If you have a look at a processor's instruction set, there are a number of ways of grouping them. For example, all the ADD
instructions might be grouped together, and all the XOR
instructions.
Within each group of the same instruction, there may be versions that operate on memory, or on registers. It's this sub-grouping that effectively defines the number of registers that the processor has.
As an 8-bit hypothetical example, let's say the $Ax
instructions might be the ADD
instructions, and $Cx
might be the XOR
instructions. With this design, there are only four bits left to define the operands!
- One might have only four general-purpose registers, and use two bits to define one, and two bits to define the other.
- Or, one might use the first bit to distinguish "special" variants, and the other 3 bits to define which of eight registers to operate with the accumulator (
$x0
could be the accumulator itself).
- Or, one could have more than this number of registers - but then limit which registers are accessible to which instructions.
Of course, we're past 8-bit instruction sets. But still, this logic helped define register sets in the past - it will continue to do so into the future.
EDIT (as requested)
Say the top four bits are for the instruction: ADD
, SUB
, XOR
, MOV
, CMP
etc. There are 16 possibilities here. Then, for those instructions where register-to-register makes sense (e.g. ADD Rx,Ry
), you need to specify Rx
and Ry
. Say the next two bits are for x
, and the last two are for y
. Thus:
ADD R1, R2 => 'ADD' + 'R1' + 'R2' => $A0 + $04 + $02
With only two bits to define a register like this, you only have room for a total of four registers!
As an aside, you'll note that some register combinations don't make sense. For example, MOV Rx, Rx
(does nothing) and SUB Rx, Rx
(always produces 0
). These could become special-case instructions:
SUB Rx, Rx
could become NOT Rx
- a single-operand instruction.
MOV Rx, Rx
could become a MOV
instruction that takes a second byte as an immediate value, interpreted as MOV Rx, #$yy
.
In this way you can "play" with the instruction map, filling in the holes for otherwise-useless or -nonsensical instructions to provide a larger instruction set for the programmer. But ultimately, the instruction set defines the register set.