I mentioned in my last post that my intention is to design with a two-address instruction set. This means that my instructions will have zero, one or two operands. There is no important reason for this. I am familiar with 6502 (accumulator, one-address) and x86 instruction sets, and I am more used to the latter, where you use two operands to move data between register/memory locations. It is just the matter of taste, not an architectural constraint. If at any point I decide to implement a three address instruction (e.g. an atomic compare and branch) and my control module will allow for this, I will go for it.
Below is an initial list of instructions I want to implement in the machine. This should be treated as a starting point. I am more than certain that the list will start expanding the moment I start writing first test programs.
Data transfer instructions
Mnemonic | Description | Example |
LD | Load memory data to register. | LD A, #01FF |
ST | Store register data to memory. | ST ($0010h), B |
PUSH | Push value onto stack. | PUSH #15 |
POP | Remove value from stack. | POP A |
MOV | Copy register values (not memory contents). | MOV B, C |
Data manipulation instructions – arithmetic
Mnemonic | Description | Example |
ADD | Add operand values. Store the result in the first operand. | ADD A, #1 |
SUB | Subtract operands. Store the results in the first operand. | SUB B, A |
ADC | Add operand values and the value of carry flag. | ADC B, (SP:5) or ADC B, 5(SP) |
SBC | Subtract operand values and the value of carry. | SBC A, (B) |
We probably need instructions to set and reset machine flags here (C, Z, V, N, plus some more machine status bits) – to be added later.
Data manipulation instructions – logic
Mnemonic | Description | Example |
AND | Bit-wise AND of two operands. Store result in the first operand. | AND A, #0000h |
OR | Bit-wise OR of two operands. Store result in the first operand. | OR BL, #00001111b |
XOR | Bit-wise XOR of two operands. Store result in the first operand. | XOR C, C |
NOT | Bit negation. One operand. | NOT B |
SHL | Binary shift left by one position. Least significant bit zero filled. | SHL A |
SHR | Binary shift right by one position. Most significant bit zero filled. | SHR A |
ROL | Binary rotate left by one position. Most significant bit value is pushed to the least significant position. | ROL B |
ROR | Binary rotate right by one position. Least significant bit value is pushed to the most significant position. | ROR B |
Still to be decided if we need binary right shift keeping the sign bit value (most significant bit). Would that be called arithmetic right shift? If it turns out necessary, it will be added as well.
Program control
Mnemonic | Description | Example |
CMP | Compare two values. Store flags. | CMP AL, #1F |
BR | Branch unconditionally | BR label |
BE | Branch if equal. | BE label |
BNE | Branch if not equal. | BNE label |
BG | Branch if greater. | BG label |
BGE | Branch if greater or equal. | BGE label |
BL | Branch if less. | BL label |
BLE | Branch if less or equal. | BLE label |
CALL | Routine call. Push PC onto stack. | CALL _printf |
RET | Return from routine. Pop PC. | RET |
NOP | Do nothing. Skip to next instruction. | NOP |
HALT | Halt the machine. Do not fetch the next instruction. | HALT |
All comparisons signed and based on two’s complement arithmetic. (Do I need unsigned comparisons?)
Traps/interrupts
I haven’t really thought about it too much but it looks like I need at least the following instructions:
Mnemonic | Description | Example |
(IRQn) | Jump to interrupt service routine. Store machine status. Pseudo-instruction not accessible for programmer. | – |
(TRAPn) | Execute trap handler. Restore machine status from before the trap-causing condition. Pseudo-instruction not accessible for programmer. | – |
IRET | Return from interrupt service routine. Restore machine status. | IRET |
SYSCALL | Generate software trap – call operating system function. | SYSCALL #05h |
Note that (IRQ) and (TRAP) are pseudo instructions, and I will have as many of them as there are, respectively, interrupt lines and trap conditions supported by hardware.
Now I will write a couple of test microcode routines for most typical instructions in each of the functional group. This should give me an idea how my microcode word struct should look like and how to organize the microcode sequencer. It is also a good moment to decide on addressing modes the instruction set will support and map them reasonably onto instructions, keeping in mind that I have 256 opcodes (one-byte opcode will be used) and that I don’t want to complicate the hardware too much. I believe this is going to be a trade-off between instruction set expressiveness and instruction set orthogonality. The examples provided below already indicate more or less what addressing modes I will be implementing. These are:
- operand implied – operand is implicitly implied by the opcode
- immediate – constant value
- direct – explicit memory address (instruction address is given as direct memory location)
- indirect – instruction address is the address stored in given memory location
- register direct – instruction address is explicitly given register
- register indirect – instruction address is memory location at the address stored in register
- register indirect with offset – instruction address is memory location at the address stored in register, plus offset (8/16 bit?)
- PC-relative – instruction address is the value of program counter, plus offset (8/16 bit?)
I’ll be posting on the details soon.