1
$\begingroup$

I've recently been designing a simple 8-bit microprocessor, similar to the Intel 8008. It doesn't make use of anything advanced as pipelining, as my knowledge isn't at that level yet to know how to implement it.

The address length of 14 bits, and an instruction lengths ranging from 1 to 3 bytes.

I've been researching, and I found that instructions generally span across multiple addresses in memory. For example, a 2 byte instruction could store the opcode-byte in one address, and a one-byte data value in the following.

However, when fetching an instruction from memory, does this mean that the processor has to make up to 3 memory calls just for the fetch stage?

I.e. - loading the MAR with the PC address, incrementing the PC, and loading from memory into part of the instruction register, repeated up to three times.

Or, is there a different way to store the instructions to get around this? Just to note, my processor also doesn't use any caching, meaning it has to get instructions and data exclusively from RAM.

Also, how would this work for jump instructions? If I want to jump to a specific instruction in the program, but don't know the address in memory because of all the other multi-length instructions.

I would appreciate feedback on any of these questions.

$\endgroup$
2
  • $\begingroup$ As far as I know, instructions are always stored contiguously in RAM. You can't know how many bytes to load before you have partially decoded the instruction, so per-byte fetches seem logical. Anyway, loading three bytes every time (and dropping the extraneous ones) might make sense if this speeds-up the memory access. $\endgroup$
    – user16034
    Commented Oct 10, 2022 at 11:24
  • $\begingroup$ Regarding the jumps, well, you simply adjust the PC after you have computed the address. Anyway, in the case of conditional jumps not taken, you don't even have to compute the address. $\endgroup$
    – user16034
    Commented Oct 10, 2022 at 11:27

1 Answer 1

4
$\begingroup$

Some processors - generally, 32-bit RISC ones - make sure that all instructions fit in one location in memory. This works for them because 32 bits is enough to fit plenty of instructions, and it makes it easy for the processor to load instructions. All the instruction operands are included as part of the instruction. The instructions are often bigger than they need to be, but the simplicity can be worth it.

8-bit processors don't do this, because you can't really fit enough instructions in only 8 bits. You can only fit 256 instructions, in fact. If they have 4-bit operands (not enough) you can only fit 16 instructions (also not enough). So multi-byte instructions are a necessity.

However, 256 is plenty of opcodes. In the 8-bit era it was typical that a CPU would read a 1-byte opcode, and then the operands (if any) would be in the next 1 or 2 bytes.

I.e. - loading the MAR with the PC address, incrementing the PC, and loading from memory into part of the instruction register, repeated up to three times.

Almost. You could do this. But 8-bit CPUs generally do not have 3-byte instruction registers. Instead, the instructions are designed so only the opcode needs to be stored in the opcode register is needed, and the operand bytes can go directly to their final destination. An instruction like ADD BC, 1234 might process the bytes like this:

  1. load the MAR with the PC address, increment the PC, and load from memory into the opcode register
  2. load the MAR with the PC address, increment the PC, load from memory into the ALU right input, load from register C into the ALU left input, set the ALU mode to ADD, store from the ALU output into register C
  3. load the MAR with the PC address, increment the PC, load from memory into the ALU right input, load from register B into the ALU left input, set the ALU mode to ADC (add-with-carry), store from the ALU output into register B

To keep track of the steps, you might find it helpful to add a step counter "mini-register" which increments each cycle, except at the end of the instruction when it resets. Then you can use the opcode register and the step counter and combinational logic to select what the CPU should do in the current clock cycle. If you use a ROM chip instead of combinational logic, the contents of the ROM are called microcode.


For jump and call instructions specifically (with 16-bit addresses), the Z80 CPU uses an extra, hidden 16-bit register called WZ. Normally you would load 16 bits from memory into a register by loading one half, then the other half. But when you are loading into the program counter, you can't do this, because if you load the first half of the program counter then it messes up the address you load the second half from. So it loads the halves into WZ and then transfers WZ to PC.

To save having a WZ register, a different idea is to make the programmer load the address into a register, then jump to the address in the register. You can still have relative jumps (add an 8-bit value to the PC) and page-relative jumps (loading the lower 8 bits only) without needing any extra registers.


Also, how would this work for jump instructions? If I want to jump to a specific instruction in the program, but don't know the address in memory because of all the other multi-length instructions

You do know the address. Each instruction has an address, and you tell the CPU to jump to the address of the instruction.

$\endgroup$
8
  • $\begingroup$ Thanks for this information, much appreciated :) $\endgroup$ Commented Oct 11, 2022 at 22:54
  • $\begingroup$ One other approach is a stack machine, which, due to the implicit operands, can often fit its entire instruction in 8 (or even less) bits. Stack machines often have terrible performance for larger processors for various reasons... but for a small microprocessor this approach can be useful. $\endgroup$
    – TLW
    Commented Oct 15, 2022 at 23:31
  • $\begingroup$ Another approach for 16-bit calls and jumps is... to ignore it entirely. This then restricts you to only jumping to the same address (+1) in a different page, which is somewhat terrible, but can be workable. (E.g. put a stub at the end of each page that $\endgroup$
    – TLW
    Commented Oct 16, 2022 at 0:19
  • $\begingroup$ moves the upper half from one registers to the pc then the lower half from another register to the pc) $\endgroup$
    – TLW
    Commented Oct 16, 2022 at 0:20
  • $\begingroup$ @TLW Even stack machines use load-literal instructions which occupy multiple bytes $\endgroup$ Commented Oct 16, 2022 at 18:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.