Introducing the Instruction Set Part 1
This tutorial focuses primarily on introducing the CP1610's instruction set. The instructions divide up into multiple categories that we will explore briefly below. You can visit the various instruction pages for details on specific instructions.
The CP1610 has 7 primary instructions. The primary instructions have the most flexibility in terms of where they draw their inputs from. The 7 primary instructions are:
|MVI||"MoVe In": Read a value into a register|
|MVO||"MoVe Out": Write a value from a register to memory|
|ADD||Add two values together|
|SUB||Subtract two values|
|CMP||Compare two values by subtracting them|
|AND||Bitwise logical AND|
|XOR||Bitwise logical XOR|
Each instruction takes two operands. The first operand is a source operand. The second is both source and destination, except in the case of CMP, which doesn't write a result. (CMP does, however, set flags.)
The primary instructions are available in 4 forms, each with a different addressing mode. The mnemonics for each form differs slightly from the others:
|Register Mode||Direct Mode||Indirect Mode||Immediate Mode|
Register Mode Instructions
Register mode instructions operate on two different registers. For instance, "ADDR R0, R1" adds the value in R0 to the value in R1, and writes the result to R1. It's equivalent to the expression "R1 = R0 + R1".
The move instructions are the simplest. "Move" is something of a misnomer, though. Move instructions really copy values from one place into another. For example, "MOVR R0, R1" copies the value in R0 to R1. After the CPU runs this instruction, both R0 and R1 will have the same value. The CPU will also set the Sign Flag and Zero Flag based on the value of the number it copied.
Move instructions are sometimes used with the program counter. "MOVR R5, R7" will jump to the location whose address is in R5. The assembler offers a pseudonym for this, "JR". The instruction "JR R5" is equivalent to "MOVR R5, R7."
ADDR R0, R1 ; Adds R0 to R1, leaving the result in R1 MOVR R2, R3 ; Copies the value in R2 to R3 CMPR R1, R2 ; Subtracts R1 from R2 setting flags. Does not change either of R1 or R2.
Direct Mode Instructions
Direct mode instructions operate on a value in memory. For all but MVO, they read a value from the named location. The instruction "ADD $123, R0" reads the value from location $123 and adds it to R0. The MVO instruction works in the opposite direction: It copies the value from a register into a memory location. "MVO R0, $123" writes the value that's in R0 to location $123.
The address can either be a bare address, as shown above, or a label. Labeled addresses are much easier to read. For example, suppose the label "LIVES" points to the number of lives your player has remaining. You could read that value from memory into R0 with:
MVI LIVES, R0
You'll see this technique used heavily in assembly programs of appreciable size. It makes programs easier to write, read and follow.
Indirect Mode Instructions
Indirect mode instructions also operate on a value in memory. However, rather than specifying the address directly in the instruction, they instead get the address from a register. This is useful for reading through a range of memory. For example, the instruction "ADD@ R1, R2" reads from the memory location pointed to by R1, and adds that value into R2. As with direct mode, the MVO@ instruction works in the opposite direction by writing a value to memory. "MVO@ R0, R1" writes the value of R0 to the location pointed to by R1.
Indirect mode behaves specially when using R4, R5 or R6 as the pointer register. For R4 and R5, the CPU will increment their value after using it. This makes it easy to step through an array in memory. The instruction "MVI@ R4, R0" will copy the value in memory pointed to by R4 into R0, and then it will increment R4.
; Read memory pointed to by R3, ; and put the value in R0 MVI@ R3, R0
; Read memory pointed to by R2, ; and add the value to R1, leaving the result in R1 ADD@ R2, R1
; Read memory pointed to by R4, ; subtract the value from R3 writing the difference to R3. ; Then, increment R4. SUB@ R4, R3
R6 works even more specially. When reading via R6, the CPU will decrement R6 *before* using it. When writing via R6, the CPU will increment R6 *after* using it. This behavior is what makes R6 useful as a stack pointer. The instructions "PSHR" and "PULR" are just pseudonyms for MVI@ and MVO@ using R6. Thus "PSHR R0" means the same as "MVO@ R0, R6", and "PULR R0" means the same as "MVI@ R6, R0".
The stack pointer can be used with other instructions as well. The instruction "ADD@ R6, R0" reads the value from the top of stack and adds it to R0. This can eliminate many PULR instructions. If you've ever used a stack-based calculator (such as one of HP's RPN calculators), you may find this style of programming intuitive.
Indirect mode has one limitation: You can't use R0 as the indirect register. Therefore "MVI@ R0, R1" is illegal, as is "MVO@ R1, R0".
Immediate Mode Instructions
Immediate mode instructions operate on a constant value. "MVII #1234, R0" copies the number 1234 into R0. These are useful for adding or subtracting constants from registers, setting up values in registers and so on. Note that the instruction "MVOI" generally doesn't do anything useful. You can safely ignore it.
; Puts the value 1234 into R0 MVII #1234, R0
; Bitwise ANDs $FF with R1, leaving result in R1 ANDI #$FF, R1
Special Note: Compare Instructions
The CMP instruction bears special mention. Regardless of their addressing mode, the ADD, SUB and CMP instructions all set the flags based on the result of the computation. ADD and SUB also write the computed value back to a register. CMP works like SUB, but it doesn't write the result of the subtraction anywhere. Rather, it only sets flags. Its primary use is to control conditional branch instructions.