Tuesday 2 August 2016

The program counter

The program counter 

Let's look at a short program in a hypothetical assembly language:

0000    ldx #7
0002    call 0x100
0005    dex
0006    jnz 0x0002

What this program does is simple; it calls a subroutine at address 0x100 seven times (it doesn't matter what happens at 0x100; sufficient for the example to know that it does something and then returns to the instruction after it was called).

The numbers on the left are the addresses of the first byte of the instruction - not all the instructions take the same amount of space since some of them carry data as well: the immediate value 7 for the ldx instruction, so it takes two bytes, and a two-byte address for the call and jump instructions, so they each take three bytes. The dex has no parameters so takes only one byte.

From this it should be clear that there are a number of things the program counter has to do.
  • it must increment between each memory access as the instruction is executed.
  • it must be possible to set a non-sequential value in the case of a jump or call.
  • and not illustrated but essential - it has to be possible to preset a value from which everything must start when the system is reset.
It's possible to do all this by running everything in a loop through the arithmetic logic unit (ALU), but I think there's a more elegant way: the 74HC161 pre-settable, resettable, binary counter datasheet (pdf).

Here's the internal connections of the HC161, courtesy the NXP datasheet:


(I chose the NXP datasheet since it can be simulated directly in Logisim; the Texas version is functionally identical but implemented with a slightly more complex flip-flop).

The Logisim circuit is here: right-click to download and save it. With it saved, you can either open it directly in Logisim, or you can import it and then use it as a component. Place it and double click to see the interior view.

If you replace the CK input with a clock component (from the wires section) you can let Logisim drive this counter on your behalf. You'll find that you need both enable lines (CEP and CET) high, and also the load input ~PE and the reset input ~MR.

Taking either enable line high will make the thing stop counting, remembering the count until they're both high again. Taking the ~MR line low at any time will force the output to all zeros, and it will remain at zero until the ~MR line is raised again. But the ~PE input is a bit different; if it is taken low, the values on the input will be transferred to the output - but only when the clock next ticks. This is exactly the behaviour we need for our program counter.

One small problem though: this counter is only four bits wide, and we need sixteen to cover our full address range. We need to tie four of these in series, but we'll do it in two groups of eight since we also need to access these in eight-bit bytes.

That's easy enough to implement:

Once again, it's implemented as a single component you can download here.

No comments:

Post a Comment