Wednesday, 4 June 2014

Virtual CPU - ROM & Interrupts

Interrupts are ways for hardware, and sometimes other pieces of software, to break into the flow of your CPU processing and tell it do something else.

This is my very friendly way of describing interrupts, unfortunately a lot of sources of good information drop people into the idea of these mystical beasts far too quickly.  I'm going to try not to drop you all in it.

Lets think about our CPU, and how we know it operates, our CPU has a Run function, which has a loop within.  This loop fetches an operation to carry out from the memory pointed to by the program counter and it acts on that instruction.

As we've seen from example programs we can also jump to a new memory address.

And if we took a close look at the Intel published datasheet from the prior post we'd also see that there are many other kinds of jump, jumps when things are equal, jumps when they're not, jumps for the sake of jumping...

Well, an interrupt is a special kind of jump instruction, which when the interrupt is signalled (triggers/fired - whatever term you want to use) gets inserted as the next instruction for the program to carry out.

The Interrupt instruction is special, because it tells the CPU to jump off to some piece of code elsewhere, but also remembers when that piece of code is complete to come back and pick up exactly where the CPU left off.

Like a function call in other programming languages, but at the CPU level.  In our programs therefore we now need to think about what state the CPU is in before and after an interupt is carried out (or handled).

Because the code in the interupt function might change the state of the CPU, and sometimes we don't want this.

A good example comes to mind from the very first real (i.e. not BASIC) programming language I learned, Pascal.  In Microsoft/Intel PC's running the MS DOS Operating system (16 bit) the BIOS had a set of standard interrupts, the standard interrupt number 11H (hexidecimal) was for the mouse, if this interrupt was triggered the mouse had moved, but we didn't want the state of the processor to have changed within our program, so when the interrupt handler was defined in our code we used to have to say:

"PUSH REGISTER STATE"

Before the interrupt code was run, and then when the interrupt processing was complete we would say:

"POP REGISTER STATE"

Lets look how an interrupt might work in our CPU Run function:

From left to right, we see our Program on our CPU... Then we see the Interrupt get signalled (some how) and the program counter is pointed to the new piece of code "A"... The Run function continues through the interrupt instruction codes and then returns to your program at "B", by setting the Program Counter back to point at your original programs' "next" operation.

The interrupt code could have changed any of the CPU registers or status flags, so it is at point "A" we must be able to Push, or save the current CPU state.  Then at Point "B" we must be able to Pop, or load the saved CPU state back into the machine proper.

We say "push" and "pop" as the structure we use to save and restore the CPU state is called a "stack".  I'll come back to that later, but you can check out an explanation of stacks here if you're eager to understand what we're talking about.

In our CPU however, we're not going to add a stack just yet, we're just going to add a set of mirror values for each CPU member variable.  In early silicon this would have been totally impracticle because of the cost of adding redundant duplicates of everything would have been far too costly... Those early chips might have therefore saved their registers off to memory.

Whatever they did the essence is the same as what we're now going to make our code do... it saves all the values somewhere.


So what might cause an interrupt and what might the code in an interrupt actually do?

Well, the most basic interrupt you will probably use today is the keyboard, as you press each key an interrupt is generated going off to the processor telling it that a key stroke has arrived.

The processor can then pick up and action the key stroke, adding it to data, or changing status however we instruct it to.

So where does the code for our Interrupts come from?  At the most basic level in your machine they come from the BIOS.  So the BIOS handles the key stroke, some store or queue many key strokes at once, and then they are handled by the processor as and when the Operating System software wants to accept them.

How might we add interrupt code to our CPU?  Well, from the last post we mentioned ROM's... We're going to write a ROM class and add to it key handling.


Right we've implemented our ROM class, then added the interrupt, now we can move into an interrupt and return from it, we also have the push and pop instructions added, so op codes 200, 201 and 202 are important to the interrupting process.

When we go into an interrupt the CPU changes state to start reading from the offset into the ROM, and it is up to the ROM code to return from that interrupt code.  The CPU has no idea when the interrupt is finished.  So the last instruction from our ROM function for an interrupt must be to return from it!

What might an interrupt series of byte codes in our ROM look like?  That's up to you!

In the next post in our series I'm going to write some platform specific code to let us read characters from the keyboard, until I do that however, we'll just pretend an interrupt has been called...

2 comments:

  1. Please Note, the videos for this are missing, I have had trouble up loading them to YouTube, but they will be present this evening - around midnight I hope.

    ReplyDelete
  2. You will now find the two videos uploaded to help guide your implementation of the code.

    ReplyDelete