In a cpu, why are/were Index Registers needed? You can of course live without them, but why would you want them? Wikipedia says that they are used for vector/array operations, but I'm not really sure what that means. (I am deciding on the architecture of my home-brew cpu)
Why are Index Registers needed
cpuregister
Related Solutions
Turns out I missed a crucial detail in the accompanying text, and the registers are indeed composed out of two (master-slave) sub-registers:
The Use of Master–Slave Registers
Note that the contents of the PC are incremented within the same clock pulse. As a direct consequence, the PC must be implemented as a master–slave flip–flop; one that responds to its input only during the positive phase of the clock. In the design of this computer, all registers in the CPU will be implemented as master–slave flip–flops.
The zero-register on RISC CPUs is useful for two reasons:
It's a useful constant
Depending on restrictions of the ISA, you can't use a literal in some instructions encoding, but you can be sure you can use that r0
to get 0.
It can be used to synthesize other instructions
This is perhaps the most important point. As a ISA designer, you can trade-off a general purpose register to a zero-register to be able to synthesize other useful instructions. Synthesizing instructions is good because by having less actual instructions, you need less bits to encode an operation in a opcode, which frees-up space in the instruction encoding space. You can use that space to have e.g. bigger address offsets and/or literals.
The semantics of the zero-register is like /dev/zero
on *nix systems: everything written to it is discarded, and you always read back 0.
Let's see a few examples of how we can make pseudo-instructions with the help of the r0
zero-register:
; ### Hypothetical CPU ###
; Assembler with syntax:
; op rd, rm, rn
; => rd: destination, rm: 1st operand, rn: 2nd operand
; literal as #lit
; On an CPU architecture with a status register (which contains arithmetic status
; flags), `sub` can be used, with r0 as destination to discard result.
cmp rn, rm ; => sub r0, rn, rm
; `add` instruction can be used as a `mov` instruction:
mov rd, rm ; => add rd, rm, r0
mov rd, #lit ; => add rd, r0, #lit
; Negate:
neg rd, rm ; => sub rd, r0, rm
; On CPU without status flags,
nop ; => add r0, r0, r0
; RISC-V's `jal` instruction -- Jump and Link: Jump to PC-relative instruction,
; save return address into rd; we can synthesize a `jmp` instruction out of it.
jmp dest ; => jal r0, dest
; You can even load from an absolute (direct) address, for a usually small range
; of addresses by using a literal offset as an address.
ld rd, addr ; => ld rd, [r0, #addr]
The case of MIPS
I looked more closely at the MIPS instruction set. There are a handful of pseudo-instructions that uses $zero
; they are mainly used for branches. Here are some examples of what I've found:
move $rt, $rs => add $rt, $rs, $zero
not $rt, $rs => nor $rt, $rs, $zero
b Label => beq $zero, $zero, Label ; a small relative branch
bgt $rs, $rt, Label => slt $at, $rt, $rs
bne $at, $zero, Label
blt $rs, $rt, Label => slt $at, $rs, $rt
bne $at, $zero, Label
bge $rs, $rt, Label => slt $at, $rs, $rt
beq $at, $zero, Label
ble $rs, $rt, Label => slt $at, $rt, $rs
beq $at, $zero, Label
As for why you have found only one instance of the $zero
register in your disassembly, perhaps it's your disassembler that is smart enough to transform known sequences of instructions into their equivalent pseudo-instruction.
Is the zero-register really useful?
Well, apparently, ARM finds having a zero-register useful enough that in their (somewhat) new ARMv8-A core, which implement AArch64, there's now a zero-register in 64-bit mode; there wasn't a zero-register before. (The register is a bit special though, in some encoding contexts it's a zero-register, in others it instead designates the stack pointer)
Best Answer
(where index is a variable who's value is unknown at compile time) is a common thing to do in C. You can do something similar in x86 assembly:
Here, eax holds the value of the index variable, and we call eax an index register in this instruction. If ebx holds the address of array, and array is of a type which has a size of 4 bytes, then eax = n will store array[n] into edx.
Without index variables, this would be cumbersome to do.