; Assembly to machine code mapping for the i281 CPU.
;
;


The CPU has 26 OPCODES. Their abbreviations and full names are listed below.


MNEMONIC     LONG NAME
------------------------------------------------------------------
NOOP         NO OPeration
INPUTC       INPUT into Code memory
INPUTCF      INPUT into Code memory with oFfset
INPUTD       INPUT into Data memory
INPUTDF      INPUT into Data memory with oFfset
MOVE         MOVE (i.e., copy) the contents of one register into another
LOADI        LOAD Immediate value
LOADP        LOAD Pointer address
ADD          ADD two registers
ADDI         ADD an Immediate value to a register
SUB          SUBtract two registers
SUBI         SUBtract an Immediate value from a register
LOAD         LOAD from a data memory address into a register
LOADF        LOAD with an oFfset specified by another register
STORE        STORE a register into a data memory address
STOREF       STORE with an oFfset specified by another register
SHIFTL       SHIFT Left all bits in a register
SHIFTR       SHIFT Right all bits in a register
CMP          CoMPare the values in two registers
JUMP         JUMP unconditionally to a specified address
BRE          BRanch if Equal
BRZ          BRanch if Zero
BRNE         BRanch if Not Equal
BRNZ         BRanch if Not Zero
BRG          BRanch if Greater 
BRGE         BRanch if Greater than or Equal 



OPCODE      16-bit MACHINE CODE         MEANING
------------------------------------------------

NOOP       0000 |dd|dd||dddddddd|     ; DO NOTHING (i.e., idle for one clock cycle)

INPUTC     0001 |dd|00||CADDRESS|     ; CODE at [Address+Const]   =INPUTVAL from switches SW15 - SW0
INPUTCF    0001 |RX|01||CADDRESS|     ; CODE at [Address+RX+Const]=INPUTVAL from switches SW15 - SW0
INPUTD     0001 |dd|10||DADDRESS|     ; DATA at [Address+Const]   =INPUTVAL from switches SW7  - SW0
INPUTDF    0001 |RX|11||DADDRESS|     ; DATA at [Address+RX+Const]=INPUTVAL from switches SW7  - SW0


MOVE       0010 |RX|RY||00000000|     ; RX=RY+0  ; copies the value of RY into RX (implemented as addition with 0)

LOADI      0011 |RX|dd||IMMEDVAL|     ; RX=IMMEDVAL
LOADP      0011 |RX|dd||IMMEDVAL|     ; RX=IMMEDVAL (used for pointer operations, handled by the compiler)



ADD        0100 |RX|RY||dddddddd|     ; RX=RX+RY

ADDI       0101 |RX|dd||IMMEDVAL|     ; RX=RX+IMMEDVAL

SUB        0110 |RX|RY||dddddddd|     ; RX=RX-RY

SUBI       0111 |RX|dd||IMMEDVAL|     ; RX=RX-IMMEDVAL



LOAD       1000 |RX|dd||DADDRESS|     ; RX=[Address+Const]

LOADF      1001 |RX|RY||DADDRESS|     ; RX=[Address+RY+Const] 

STORE      1010 |RX|dd||DADDRESS|     ; [Address+Const]=RX

STOREF     1011 |RX|RY||DADDRESS|     ; [Address+RY+Const]=RX ; Note that RX and RY are swapped relative to LOADF.



SHIFTL     1100 |RX|d0||dddddddd|     ; RX=RX<<1
SHIFTR     1100 |RX|d1||dddddddd|     ; RX=RX>>1
                    
CMP        1101 |RX|RY||dddddddd|     ; IF RX-RY=0, SET ZERO-FLAG. IF RX-RY<0, SET NEGATIVE-FLAG.

JUMP       1110 |dd|dd||PCOFFSET|     ; PC = PC + 1 + PCOFFSET  (PCOFFSET is signed in 2's complement)

BRE        1111 |dd|00||PCOFFSET|     ; PC = PC + 1 + PCOFFSET if condition 'Equal' is true. Identical to BRZ.
BRZ        1111 |dd|00||PCOFFSET|     ; PC = PC + 1 + PCOFFSET if condition 'Zero' is true.  Identical to BRE.

BRNE       1111 |dd|01||PCOFFSET|     ; PC = PC + 1 + PCOFFSET if condition 'Not Equal' is true. Identical to BRNZ.
BRNZ       1111 |dd|01||PCOFFSET|     ; PC = PC + 1 + PCOFFSET if condition 'Not Zero' is true.  Identical to BRNE.

BRG        1111 |dd|10||PCOFFSET|     ; PC = PC + 1 + PCOFFSET if condition 'Greater than' is true. 

BRGE       1111 |dd|11||PCOFFSET|     ; PC = PC + 1 + PCOFFSET if condition 'Greater than or equal' is true. 





;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Some notes on the assembly to machine code mapping.


Many instructions use or modify the value of one of the CPU registers. 
For these instructions, the address of the specific register is denoted with RX.
In this case, RX is a 2-bit value that denotes one of the four registers of the CPU:
        RX=00 selects register A, 
        RX=01 selects register B, 
        RX=10 selects register C, 
        RX=11 selects register D. 


Several instructions use the values of two registers (and usually modify one of them). 
In those cases the addresses of the two registers are denoted with RX and RY. 
Similarly to RX, RY is a 2-bit value that denotes one of the four registers of the CPU:
        RY=00 selects register A, 
        RY=01 selects register B, 
        RY=10 selects register C, 
        RY=11 selects register D. 


d - Denotes a don't care bit that is ignored by the hardware. 
    The assembler, however, maps all d bits to 0.


The vertical lines | are used for better visual separation between the bits.
They are not part of the machine language. 


The six branch instructions are clustered into four groups. They all have the same 1111 opcode, 
but the two least significant bits in the first byte determine the type of branch. 
The general format for a branch instruction is the following:

BR??       1111 |dd|CC||PCOFFSET|     ; PC = PC + PCOFFSET if condition CC is true

                    CC = 00: BRE  or BRZ 
                    CC = 01: BRNE or BRNZ
                    CC = 10: BRG 
                    CC = 11: BRGE

At the machine-language level, the instruction BRE is indistinguishable from BRZ. 
These aliased instructions are handled by the assembler.  Similarly, BRNE is 
aliased with BRNZ.


The program counter offset (i.e., PCOFFSET) is an 8-bit value stored in 2's complement. 
Thus, it can be either positive or negative.  If the brach condition is true, then the 
value of PCOFFSET is added to the Program Counter (PC). Negative values move the 
PC back, positive values advance it forward. Because in the hardware implementation the PC 
is always incremented by 1 after each instruction, the offset value needs to be corrected accordingly. 
The JUMP instruction implements an unconditional jump. Its PCOFFSET value needs to be similarly adjusted. 


There are four INPUT instructions that allow the user to enter a value from the switches on the board. 
They differ only by the last two bits in the first byte:

INPUT??    0001 |dd|CC||MADDRESS|     ; [MADDRESS]=INPUTVAL from switches

                    CC = 00: INPUTC
                    CC = 01: INPUTCF
                    CC = 10: INPUTD
                    CC = 11: INPUTDF

INPUTC and INPUTCF read a 16-bit value (from SW15-SW0) and store it in the code memory at the given address.

INPUTD and INPUTDF read an 8-bit value (from SW7-SW0) and store it in the data memory at the given address.


The instructions SHIFTL and SHIFTR differ only by the least significant bit in the first byte. If it is
equal to 0, then the shift is to the left. If it is equal to 1, then the value of the register is shifted
to the right. In both cases, the bit that is shifted out is stored in the overflow flag in the flags register.


The CPU has two memories: one for data and one for code. Some of the opcodes need to store 
an address into one of these memories in the second byte of the instruction. In the 
description above these are called DADDRESS and CADDRESS, which are defined as follows: 

DADDRESS is an address into the data memory,

CADDRESS is an address into the code memory.


In the opcodes these are hardcoded values that are computed by the assembler when it emits the machine code. 
In the assembly language (but not in the machine code) there is some flexibility in the way the address
is specified. In particular, an optional constant offset can be added to the address. The assembler will perform 
the addition and will store the result in the second byte of the opcode. For example, 

   [Address] = DADDRESS.

or

   [Address + Const] = DADDRESS.

Opcodes that end in 'F' allow for another offset to be added to the address, in addition to the optional
constant offset. That variable offset value is stored in one of the registers (specified by the two RX bits). 
In this case, however, the register value offset is added at run time through the ALU.  Thus, for 

   [Address + RX]   the DADDRESS is equal to Address. The value of register RX is added at runtime.

or for

   [Address + RX + Const]   the DADDRESS is equal to [Address + Const]. The value of register RX is added at runtime.


