The Lynx 8-bit Machine Specification

# The Lynx Abstract Machine Specification

The Lynx Abstract machine is a specification for a "true" 8-bit machine. By "true" 8-bit we informally mean that all components from the registers, data bus, address bus, ALU and to the instruction set are exactly 8 bits wide. It therefore follows that the Lynx Abstract Machine is only able to perform 8-bit arithmetic while being able to address just 256 bytes of memory. Lynx is a primitive machine able to achieve simple computations. The motivation behind the project is to explore what a machine with the previously mentioned constraints would look like and experiment with the possible computational problems it is able to express.

The following is a somewhat informal yet mostly exhaustive specification of the Lynx Abstract Machine. 

## Registers

The Lynx Abstract Machine has four general purpose registers. They are referred to as r0, r1, r2, and r3. All registers are 8-bits wide. The r0 register is also called the accumulator register and serves a special purpose in some instructions. Specifically, certain instructions will imcplicitly store the result of the operation in the r0 register. For instance, the add instruction adds takes any two registers as input and stores the result in r0.

The Lynx Abstract Machine also comes with a program counter (PC). The PC is a special register which points to the next instruction to be executed in the instruction stream. The PC is incremented after executing an instruction unless the instruction itself modifies the PC as is the case for jump instructions. The PC register can not be used as an operand to instructions.

## Memory model

The Lynx Abstract Machine follows the Harvard architecture meaning there are two seperately addressable memories. We call these the instruction memory and the data memory. Both memories are 256 bytes long. The instruction memory is read-only during program execution. The instruction memory is only addressable through the PC. The data memory can be both be read from and written to during program execution. Both memories are byte-addressable.

## Instruction Set

### Encoding

A single instruction takes up 8-bits or one byte of the instruction memory. Consequently, the size of a program for the Lynx machine is limited to 256 instructions.

The instruction set for the Lynx Abstract machine comprises of 16 unique instructions. Therefore, 4 of the 8 available bits fore each instruction is taken up by the 4-bit opcode. The opcode is stored in the 4 most significant bits, i.e, the opcode `0b1111` is stored like `0b11110000` where `0b0000` is the operand.

There are two kinds of operands: registers and immediates. An instruction either expects one register, two registers or an immediate as its operand.

Since there are four registers, two bits are used to specify a single registers. The table below describes the encoding for each general purpose register.

| Register | Encoding |
|----------|----------|
| r0       | 00       |
| r1       | 01       |
| r2       | 10       |
| r3       | 11       |

When registers are used as operands we refer to them as op1 and op2. op1 is stored in the most significant two bits of the four bit operand sequence while op2 is stored as the two least significant bits.

When an immediate is used as the operand it takes up the entire four bit space of the operand. The interpretation of the immediate is instruction dependant. It is either intepreted as a four bit signed integer stored in two complements or a four bit unsigned integer.


### Table
| Mnemonic | Opcode | Operand Kind | Semantics                         |
| -------- | ------ | ------------ | --------------------------------- |
| halt     | 0000   | None         | Halts execution                   |
| add      | 0001   | Reg Reg      | r0 = op1 + op2                    |
| sub      | 0010   | Reg Reg      | r0 = op1 - op2                    |
| ge       | 0011   | Reg Reg      | r0 = op1 > op2 (1 if true else 0) |
| le       | 0100   | Reg Reg      | r0 = op1 < op2 (1 if true else 0) |
| inc      | 0101   | Reg          | op1 = op1 + 1                     |
| dec      | 0110   | Reg          | op1 = op1 - 1                     |
| shift    | 0111   | Imm          | r0 = r0 << imm                    |
| ali      | 1000   | Imm          | r0 = r0 & imm                     |
| li       | 1001   | Imm          | r0 = imm                          |
| mv       | 1010   | Reg Reg      | op1 = op2                         |
| load     | 1011   | Reg Reg      | op1 = mem\[op2\]                  |
| store    | 1100   | Reg Reg      | mem\[op2\] = op1                  |
| jmp      | 1101   | Imm          | pc = imm                          |
| jiz      | 1110   | Reg Reg      | if op2 = 0 then pc = pc + op1     |
| jaiz     | 1111   | Reg Reg      | if op2 = 0 then pc = op1          |


For the shift, the immediate must be sign extended to 8 bits.