update instruction set to match new version
This commit is contained in:
parent
e827ac11b5
commit
7e4d3c8da8
143
newISA.md
Normal file
143
newISA.md
Normal file
|
@ -0,0 +1,143 @@
|
|||
# ISA V0.7
|
||||
|
||||
# Terms
|
||||
|
||||
The form of this document is a list of instructions set into categories.
|
||||
Each instruction starts with the recommended mnemonic and then the parameters for the instruction.
|
||||
|
||||
```
|
||||
<Code> <mnemonic> <parameter 1> <parameter 2> ...
|
||||
```
|
||||
|
||||
Each parameter is is abbreviated into a category
|
||||
|
||||
- R: Register
|
||||
- M: Memory address
|
||||
- C: Constant
|
||||
|
||||
Parts of this document marked with **!!** are uncertain and will likely change
|
||||
|
||||
# Instructions
|
||||
|
||||
## Memory & Register Management
|
||||
|
||||
**COPY** _from_, _to_ - Copies the byte in _from_ to the destination _to_\
|
||||
0x10 COPY R M\
|
||||
0x11 COPY M R\
|
||||
0x12 COPY M M\
|
||||
0x13 COPY R R
|
||||
|
||||
**ZERO** _to_ - Sets the value in _to_ to 0\
|
||||
0x17 ZERO R\
|
||||
0x18 ZERO M
|
||||
|
||||
0x19 **SET** R C - Sets the value in R to C
|
||||
|
||||
0x1F **SETB** R - Sets the current memory bank to the value in R
|
||||
|
||||
## Control Flow
|
||||
|
||||
0x00 **NOP** - Does nothing
|
||||
|
||||
**GOTO** _address_ - Moves instruction counter to _address_\
|
||||
0x20 GOTO R\
|
||||
0x21 GOTO C
|
||||
|
||||
**GOTRUE** R, _address_ - Moves instruction counter to _address_ if R is true\
|
||||
0x22 GOTOTRUE R R\
|
||||
0x23 GOTOTRUE R C
|
||||
|
||||
0x2A **GOCRY** _address_ - Moves the instruction counter to _address_ if the cary flag is set
|
||||
|
||||
0x2D **CALL** R - Moves the instruction counter to R and pushes the address of the call instruction
|
||||
|
||||
0x2E **RET** - Pops the address of the last call instruction off of the call stack and moves the instruction counter past it.
|
||||
|
||||
0x2F **HCF** - Halt and catch fire.
|
||||
|
||||
## Comparison
|
||||
|
||||
**EQU** _result_, _p1_, _p2_ - Sets _result_ to true if the values in _p1_ and _p2_ are the same\
|
||||
0x30 EQU R R R\
|
||||
0x31 EQU R R C
|
||||
|
||||
**LT** _result_, _p1_, _p2_ - Sets _result_ to true if the value in _p1_ is less than the value in _p2_\
|
||||
0x32 LT R R R \
|
||||
0x33 LT R R C
|
||||
|
||||
**GT** _result_, _p1_, _p2_ - Sets _result_ to true if the value in _p1_ is greater than the value in _p2_\
|
||||
0x34 GT R R R \
|
||||
0x35 GT R R C
|
||||
|
||||
Reserved for LEQ GEQ
|
||||
|
||||
0x36\
|
||||
0x37\
|
||||
0x38\
|
||||
0x39
|
||||
|
||||
## Logic / Bitwise
|
||||
|
||||
**OR** R, _value_ - Sets each bit in R to its OR with the respective bit in _value_\
|
||||
0x40 OR R R\
|
||||
0x41 OR R C
|
||||
|
||||
**AND** R, _value_ - Sets each bit in R to its AND with the respective bit in _value_\
|
||||
0x42 AND R R\
|
||||
0x43 AND R C
|
||||
|
||||
**XOR** R, _value_ - Sets each bit in R to its XOR with the respective bit in _value_\
|
||||
0x44 XOR R R\
|
||||
0x45 XOR R C
|
||||
|
||||
**LBS** R, _quantity_ - Shifts each bit in R to the left by _quantity_. Fills new bits with 0\
|
||||
0x46 LBS R R\
|
||||
0x47 LBS R C
|
||||
|
||||
**RBS** R, _quantity_ - Shifts each bit in R to the right by _quantity_. Fills new bits with 0\
|
||||
0x48 RBS R R\
|
||||
0x49 RBS R C
|
||||
|
||||
0x4A **NOT** R - Flips each bit in value R
|
||||
|
||||
## Arithmetic
|
||||
|
||||
**ADD** _to_, _from_ - Adds to the the byte in _to_ with the value in _from_\
|
||||
0x50 ADD R R\
|
||||
0x51 ADD R C
|
||||
|
||||
**SUB** _to_, _from_ - Subtracts from the value in _to_ by the value in _from_\
|
||||
0x52 SUB R R\
|
||||
0x53 SUB R C
|
||||
|
||||
0x5E **INC** R - Increments the value in R by 1
|
||||
|
||||
0x5F **DEC** R - Decrements the value in R by 1
|
||||
|
||||
## IO
|
||||
|
||||
!! **INTXT** R - Read 1 byte of input to R
|
||||
|
||||
0xF0 **OUTXT** R - Prints the value in R to output as ASCII
|
||||
|
||||
0xF1 **OUT** R - Prints the value in R to output as base 10
|
||||
|
||||
0xFF **VRB** R - Selects the bank number in R to be used as VRAM
|
||||
|
||||
# What is True?
|
||||
|
||||
True is defined as having the least significant bit in a byte set to 1. False is defined as having the least significant bit in a byte set to 0.
|
||||
|
||||
# What are flags
|
||||
|
||||
The CPU has the following flags
|
||||
|
||||
- Carry
|
||||
|
||||
Flags are set as the result of instructions.
|
||||
|
||||
## Carry
|
||||
|
||||
When the add instruction runs, the result can be greater than 8 bits in size. In this case, the carry flag is set true.
|
||||
|
||||
!! The cary flag is set to false in two cases: At the start of an **ADD** instruction, and after running a **GOCRY** instruction
|
|
@ -44,3 +44,8 @@ export function el(type: string, id?: string): HTMLElement | undefined {
|
|||
export type NonEmptyArray<T> = T[] & { 0: T };
|
||||
|
||||
export const SVG_NS = "http://www.w3.org/2000/svg";
|
||||
|
||||
export function in_range(check: number, start: number, end: number): boolean {
|
||||
if (check >= start && check <= end) return true;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* @license GPL-3.0
|
||||
*/
|
||||
import { CpuEvent, CpuEventHandler } from "./events";
|
||||
import { format_hex } from "./etc";
|
||||
import { format_hex, in_range } from "./etc";
|
||||
import { isU2, isU3, m256, u2, u3, u8 } from "./num";
|
||||
|
||||
export enum ParamType {
|
||||
|
@ -69,11 +69,18 @@ export interface Instruction {
|
|||
) => void;
|
||||
}
|
||||
|
||||
export type InstrCategory = {
|
||||
start: u8;
|
||||
end: u8;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export class InstructionSet {
|
||||
instructions: Map<u8, Instruction>;
|
||||
|
||||
category_ranges: Array<InstrCategory>;
|
||||
constructor() {
|
||||
this.instructions = new Map();
|
||||
this.category_ranges = [];
|
||||
}
|
||||
|
||||
insertInstruction(hexCode: u8, instruction: Instruction): void {
|
||||
|
@ -83,24 +90,152 @@ export class InstructionSet {
|
|||
this.instructions.set(hexCode, instruction);
|
||||
}
|
||||
|
||||
addCategory(c: InstrCategory): void {
|
||||
// Check for overlap with existing ranges
|
||||
for (const r of this.category_ranges) {
|
||||
if (in_range(c.start, r.start, r.end) || in_range(c.end, r.start, r.end)) {
|
||||
throw new Error(`Range of ${c.start}...${c.end} is already registered`);
|
||||
}
|
||||
}
|
||||
|
||||
this.category_ranges.push(c);
|
||||
}
|
||||
|
||||
getInstruction(hexCode: u8): Instruction | null {
|
||||
// console.log(format_hex(hexCode));
|
||||
return this.instructions.get(hexCode) ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
function category(start: u8, end: u8, name: string): InstrCategory {
|
||||
return { start, end, name };
|
||||
}
|
||||
|
||||
export const ISA = new InstructionSet();
|
||||
|
||||
ISA.addCategory(category(0x10, 0x1f, "Memory & Register Management"));
|
||||
ISA.addCategory(category(0x20, 0x2f, "Control Flow"));
|
||||
ISA.addCategory(category(0x30, 0x3f, "Comparison"));
|
||||
ISA.addCategory(category(0x40, 0x4f, "Logic / Bitwise"));
|
||||
ISA.addCategory(category(0x50, 0x5f, "Arithmetic"));
|
||||
ISA.addCategory(category(0xf0, 0xff, "IO"));
|
||||
|
||||
// The definitions for actual instructions.
|
||||
|
||||
ISA.insertInstruction(0x00, {
|
||||
name: "NoOp",
|
||||
desc: "No operation; do nothing",
|
||||
params: [],
|
||||
execute: () => {},
|
||||
//
|
||||
// MEMORY & REGISTER MANAGEMENT
|
||||
// 0x10 -> 0x1F
|
||||
//
|
||||
|
||||
// COPY
|
||||
ISA.insertInstruction(0x10, {
|
||||
name: "Copy R -> M",
|
||||
desc: "Copy the byte in register (P1) to the memory address (P2)",
|
||||
params: [new RegisParam("Write the byte in this register"), new MemorParam("To this memory address")],
|
||||
execute(c, p) {
|
||||
const [register_no, mem_address] = p;
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
c.setMemory(mem_address, c.getRegister(register_no));
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x10, {
|
||||
ISA.insertInstruction(0x11, {
|
||||
name: "Copy M -> R",
|
||||
desc: "Copy the byte in memory address (P1) to the register (P2)",
|
||||
params: [new MemorParam(""), new RegisParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no, mem_address] = p;
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
c.setRegister(register_no, c.getMemory(mem_address));
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x12, {
|
||||
name: "Copy M -> M",
|
||||
desc: "Copy the byte in memory address (P1) to memory address (P2)",
|
||||
params: [new MemorParam("Copy the byte in this memory address"), new MemorParam("To this memory address")],
|
||||
execute(c, p) {
|
||||
const [mem_address_1, mem_address_2] = p;
|
||||
c.setMemory(mem_address_2, c.getMemory(mem_address_1));
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x13, {
|
||||
name: "Copy R -> R",
|
||||
desc: "Copy the byte in register (P1) to register (P2)",
|
||||
params: [new RegisParam("Copy the byte in this register"), new RegisParam("To this register")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("todo");
|
||||
if (!isU3(register_no_2)) throw new Error("todo");
|
||||
c.setRegister(register_no_2, c.getRegister(register_no_1));
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x17, {
|
||||
name: "Zero Register",
|
||||
desc: "Set the byte in register (P1) to 0",
|
||||
params: [new RegisParam("Set the value in this register to 0")],
|
||||
execute(c, p) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("todo");
|
||||
c.setRegister(register_no, 0);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x18, {
|
||||
name: "Zero Memory",
|
||||
desc: "Set the byte in memory address (P1) to 0",
|
||||
params: [new RegisParam("Set the value in this memory address to 0")],
|
||||
execute(c, p) {
|
||||
const mem_address = p[0];
|
||||
c.setMemory(mem_address, 0);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x19, {
|
||||
name: "Set Register",
|
||||
desc: "Assigns constant value (P2) to register (P1)",
|
||||
params: [new RegisParam("Set this register"), new ConstParam("to this constant")],
|
||||
execute(c, p) {
|
||||
const [register_no, value] = p;
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
c.setRegister(register_no, value);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x1f, {
|
||||
name: "Set bank",
|
||||
desc: "Selects which bank of memory to write and read to",
|
||||
params: [new ConstParam("Bank number")],
|
||||
execute(c, p) {
|
||||
const bank_no = p[0];
|
||||
if (!isU2(bank_no)) {
|
||||
throw new Error("TODO");
|
||||
}
|
||||
c.setBank(bank_no);
|
||||
},
|
||||
});
|
||||
|
||||
//
|
||||
// CONTROL FLOW
|
||||
// 0x20 -> 0x2F
|
||||
//
|
||||
|
||||
ISA.insertInstruction(0x20, {
|
||||
name: "Goto",
|
||||
desc: "Moves the CPU instruction counter to the value in register (P1)",
|
||||
params: [new RegisParam("new instruction counter location")],
|
||||
execute: (c, p, a) => {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) {
|
||||
throw new Error("todo");
|
||||
}
|
||||
const new_address = c.getRegister(register_no);
|
||||
c.setProgramCounter(new_address);
|
||||
a.noStep();
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x21, {
|
||||
name: "Goto",
|
||||
desc: "Moves the CPU instruction counter to the value in (P1)",
|
||||
params: [new ConstParam("new instruction counter location")],
|
||||
|
@ -111,199 +246,67 @@ ISA.insertInstruction(0x10, {
|
|||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x20, {
|
||||
name: "LoadToRegister",
|
||||
desc: "Sets the byte in register (P1) to be the contents of memory cell at address in register (P2)",
|
||||
params: [new RegisParam("Set this register to"), new RegisParam("the byte in the memory addressed by this register")],
|
||||
execute(c, p) {
|
||||
const [register_no, register_2] = p;
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
if (!isU3(register_2)) throw new Error("TODO");
|
||||
const mem_value = c.getMemory(c.getRegister(register_2));
|
||||
|
||||
c.setRegister(register_no, mem_value);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x21, {
|
||||
name: "SaveToMemory",
|
||||
desc: "Writes the byte in register (P1) to the memory cell (P2)",
|
||||
params: [new RegisParam("Write the byte in this register"), new MemorParam("To this memory address")],
|
||||
execute(c, p) {
|
||||
const [register_no, mem_address] = p;
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
c.setMemory(mem_address, c.getRegister(register_no));
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x2f, {
|
||||
name: "AssignRegister",
|
||||
desc: "Assigns constant value (P2) to register (P1)",
|
||||
params: [new RegisParam("Set this register"), new ConstParam("to this constant")],
|
||||
execute(c, p) {
|
||||
const [register_no, value] = p;
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
c.setRegister(register_no, value);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x11, {
|
||||
name: "GotoIfLowBitHigh",
|
||||
desc: "Moves the CPU instruction counter to the value in (P1) if the value in register (P2) has the lowest bit true",
|
||||
params: [new ConstParam("Set program counter to this constant"), new RegisParam("if this register's 1 bit is set")],
|
||||
execute(c, p, a) {
|
||||
const [new_address, check_register_no] = p;
|
||||
if (!isU3(check_register_no)) throw new Error("TODO");
|
||||
if (c.getRegister(check_register_no) % 2 === 1) {
|
||||
ISA.insertInstruction(0x22, {
|
||||
name: "Goto if True",
|
||||
desc: "Moves the instruction counter to the value in register (P2) if the value in register (P1) is true",
|
||||
params: [new RegisParam(""), new RegisParam("")],
|
||||
execute: (c, p, a) => {
|
||||
const register_no_1 = p[0];
|
||||
if (!isU3(register_no_1)) throw new Error("todo");
|
||||
const bool = c.getRegister(register_no_1);
|
||||
if (!bool) return;
|
||||
const register_no_2 = p[1];
|
||||
if (!isU3(register_no_2)) throw new Error("todo");
|
||||
const new_address = c.getRegister(register_no_2);
|
||||
c.setProgramCounter(new_address);
|
||||
a.noStep();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x30, {
|
||||
name: "Increment",
|
||||
desc: "Increments the value within register (P1) by 1",
|
||||
params: [new RegisParam("register to be incremented")],
|
||||
execute(c, p) {
|
||||
ISA.insertInstruction(0x23, {
|
||||
name: "Goto if True",
|
||||
desc: "Moves the instruction counter to the value in (P2) if the value in register (P1) is true",
|
||||
params: [new RegisParam(""), new ConstParam("")],
|
||||
execute: (c, p, a) => {
|
||||
const [register_no, constant_value] = p;
|
||||
if (!isU3(register_no)) throw new Error("todo");
|
||||
const bool = c.getRegister(register_no);
|
||||
if (!bool) return;
|
||||
c.setProgramCounter(constant_value);
|
||||
a.noStep();
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x28, {
|
||||
name: "Goto if Carry Flag set",
|
||||
desc: "Moves the instruction counter to the value in register (P1) if CPU Carry flag is true",
|
||||
params: [new RegisParam("")],
|
||||
execute: (c, p, a) => {
|
||||
if (!c.getCarry()) return;
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const current_value = c.getRegister(register_no);
|
||||
const new_value = m256(current_value + 1);
|
||||
c.setRegister(register_no, new_value);
|
||||
if (!isU3(register_no)) throw new Error("todo");
|
||||
const register_value = c.getRegister(register_no);
|
||||
c.setProgramCounter(register_value);
|
||||
a.noStep();
|
||||
c.setCarry(false);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x29, {
|
||||
name: "Goto if Carry Flag set",
|
||||
desc: "Moves the instruction counter to the value in (P1) if CPU Carry flag is true",
|
||||
params: [new ConstParam("")],
|
||||
execute: (c, p, a) => {
|
||||
if (!c.getCarry()) return;
|
||||
const goto_address = p[0];
|
||||
c.setProgramCounter(goto_address);
|
||||
a.noStep();
|
||||
c.setCarry(false);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x31, {
|
||||
name: "Decrement",
|
||||
desc: "Decrements the value within register (P1) by 1",
|
||||
params: [new RegisParam("register to be decremented")],
|
||||
execute(c, p) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const current_value = c.getRegister(register_no);
|
||||
let new_value = current_value - 1;
|
||||
if (new_value === -1) {
|
||||
new_value = 255;
|
||||
}
|
||||
c.setRegister(register_no, new_value as u8);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x40, {
|
||||
name: "Add",
|
||||
desc: "Adds the contents of (P1) and (P2) and stores result to register (P1). (Overflow will be taken mod 256)",
|
||||
params: [new RegisParam("set this register to"), new RegisParam("it's sum with the value in this register")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const new_value = m256(c.getRegister(register_no_1) + c.getRegister(register_no_2));
|
||||
c.setRegister(register_no_1, new_value);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x41, {
|
||||
name: "Subtract",
|
||||
desc: "Subtracts the contents of (P2) from (P1) and stores result to register (P1).",
|
||||
params: [
|
||||
new RegisParam("set this register to"),
|
||||
new RegisParam("the difference between it in the value in this register"),
|
||||
],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const new_value = m256(c.getRegister(register_no_1) - c.getRegister(register_no_2));
|
||||
c.setRegister(register_no_1, new_value);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x50, {
|
||||
name: "Equals",
|
||||
desc: "If byte in register (P2) equals byte in register (P3), set byte in register (P1) to 0x01",
|
||||
params: [
|
||||
new RegisParam("Set this register to be 0x01"),
|
||||
new RegisParam("if this register and"),
|
||||
new RegisParam("this register are equal (else 0x00)"),
|
||||
],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2, register_no_3] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
if (!isU3(register_no_3)) throw new Error("TODO");
|
||||
const truth = c.getRegister(register_no_2) === c.getRegister(register_no_3) ? 0x01 : 0x00;
|
||||
c.setRegister(register_no_1, truth);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0xfe, {
|
||||
name: "PrintASCII",
|
||||
desc: "Prints the ASCII byte in register (P1) to console",
|
||||
params: [new RegisParam("Register to print from")],
|
||||
execute(c, p, a) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const asciiByte = c.getRegister(register_no);
|
||||
|
||||
const char = String.fromCharCode(asciiByte);
|
||||
a.dispatch(CpuEvent.Print, char);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x48, {
|
||||
name: "Bitwise And",
|
||||
desc: "Takes each bit in register (P1) and compares to the respective bit in register (P2). Each bit in register (P1) is set to 1 if the respective bit in both registers are 1",
|
||||
params: [new RegisParam(""), new RegisParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) & c.getMemory(register_no_2);
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0xff, {
|
||||
name: "Print",
|
||||
desc: "Prints the byte in register (P1) to console as base 10",
|
||||
params: [new RegisParam("Register to print from")],
|
||||
execute(c, p, a) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const byte = c.getRegister(register_no);
|
||||
|
||||
a.dispatch(CpuEvent.Print, byte.toString(10));
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0xfd, {
|
||||
name: "Print 16 bit",
|
||||
desc: "Prints the byte in register (P1) as the upper half and the byte in register (P2) as the lower half of a 16 bit number. Formats to decimal",
|
||||
params: [new RegisParam("Upper 8 bits of number"), new RegisParam("Lower 8 bits of number")],
|
||||
execute(c, p, a) {
|
||||
const [upper_register_no, lower_register_no] = p;
|
||||
if (!isU3(upper_register_no)) throw new Error("TODO");
|
||||
if (!isU3(lower_register_no)) throw new Error("TODO");
|
||||
const upper = c.getRegister(upper_register_no);
|
||||
const lower = c.getRegister(lower_register_no);
|
||||
const sum = upper * 16 * 16 + lower;
|
||||
|
||||
a.dispatch(CpuEvent.Print, sum.toString(10));
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x66, {
|
||||
name: "Halt and Catch Fire",
|
||||
desc: "Stops program execu..... Fire! FIRE EVERYWHERE!",
|
||||
params: [],
|
||||
execute(c, p, a) {
|
||||
a.dispatch(CpuEvent.Halt);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0xa0, {
|
||||
ISA.insertInstruction(0x2d, {
|
||||
name: "Call",
|
||||
desc: "Calls a subroute",
|
||||
desc: "",
|
||||
params: [new ConstParam("the subroutine at this memory address")],
|
||||
execute(c, p, a) {
|
||||
const current_address = c.getProgramCounter();
|
||||
|
@ -317,30 +320,390 @@ ISA.insertInstruction(0xa0, {
|
|||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0xa1, {
|
||||
ISA.insertInstruction(0x2e, {
|
||||
name: "Return",
|
||||
desc: "returns from a subroutine",
|
||||
desc: "",
|
||||
params: [],
|
||||
execute(c, p, a) {
|
||||
const new_address = c.popCallStack();
|
||||
if (new_address === null) {
|
||||
throw new Error("TODO handle this");
|
||||
}
|
||||
|
||||
if (new_address === null) throw new Error("TODO handle this");
|
||||
c.setProgramCounter(m256(new_address + 1));
|
||||
|
||||
a.noStep();
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0xb1, {
|
||||
name: "Set bank",
|
||||
desc: "Selects which bank of memory to write and read to",
|
||||
params: [new ConstParam("Bank number")],
|
||||
execute(c, p) {
|
||||
const bank_no = p[0];
|
||||
if (!isU2(bank_no)) {
|
||||
throw new Error("TODO");
|
||||
}
|
||||
c.setBank(bank_no);
|
||||
|
||||
ISA.insertInstruction(0x2c, {
|
||||
name: "NoOp",
|
||||
desc: "No operation; do nothing",
|
||||
params: [],
|
||||
execute: () => {},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x2f, {
|
||||
name: "Halt and Catch Fire",
|
||||
desc: "Stops program execu..... Fire! FIRE EVERYWHERE!",
|
||||
params: [],
|
||||
execute(c, p, a) {
|
||||
a.dispatch(CpuEvent.Halt);
|
||||
},
|
||||
});
|
||||
|
||||
//
|
||||
// Comparison
|
||||
// 0x30 -> 0x3F
|
||||
//
|
||||
|
||||
ISA.insertInstruction(0x30, {
|
||||
name: "Equals",
|
||||
desc: "If byte in register (P2) equals byte in register (P3), set byte in register (P1) to true",
|
||||
params: [
|
||||
new RegisParam("Set this register to true"),
|
||||
new RegisParam("if this register and"),
|
||||
new RegisParam("this register are equal (else false)"),
|
||||
],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2, register_no_3] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
if (!isU3(register_no_3)) throw new Error("TODO");
|
||||
const truth = c.getRegister(register_no_2) === c.getRegister(register_no_3) ? 0x01 : 0x00;
|
||||
c.setRegister(register_no_1, truth);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x31, {
|
||||
name: "Equals",
|
||||
desc: "If byte in register (P2) equals constant byte (P3), set byte in register (P1) to true",
|
||||
params: [
|
||||
new RegisParam("Set this register to true"),
|
||||
new RegisParam("if this register and"),
|
||||
new ConstParam("this constant are equal (else false)"),
|
||||
],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const truth = c.getRegister(register_no_2) === constant_value ? 0x01 : 0x00;
|
||||
c.setRegister(register_no_1, truth);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x32, {
|
||||
name: "Less Than",
|
||||
desc: "Sets register (P1) to true if value in register (P2) is less than the value in register (P3)",
|
||||
params: [
|
||||
new RegisParam("Set this register to true"),
|
||||
new RegisParam("if this register is less than"),
|
||||
new RegisParam("this register"),
|
||||
],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2, register_no_3] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
if (!isU3(register_no_3)) throw new Error("TODO");
|
||||
const truth = c.getRegister(register_no_2) < c.getRegister(register_no_3) ? 0x01 : 0x00;
|
||||
c.setRegister(register_no_1, truth);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x33, {
|
||||
name: "Less Than",
|
||||
desc: "Sets register (P1) to true if value in register (P2) is less than the constant value (P3)",
|
||||
params: [
|
||||
new RegisParam("Set this register to true"),
|
||||
new RegisParam("if this register is less than"),
|
||||
new ConstParam("this constant"),
|
||||
],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const truth = c.getRegister(register_no_2) < constant_value ? 0x01 : 0x00;
|
||||
c.setRegister(register_no_1, truth);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x34, {
|
||||
name: "Greater Than",
|
||||
desc: "Sets register (P1) to true if value in register (P2) is greater than the value in register (P3)",
|
||||
params: [
|
||||
new RegisParam("Set this register to true"),
|
||||
new RegisParam("if this register is greater than"),
|
||||
new RegisParam("this register"),
|
||||
],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2, register_no_3] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
if (!isU3(register_no_3)) throw new Error("TODO");
|
||||
const truth = c.getRegister(register_no_2) > c.getRegister(register_no_3) ? 0x01 : 0x00;
|
||||
c.setRegister(register_no_1, truth);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x35, {
|
||||
name: "Greater than",
|
||||
desc: "Sets register (P1) to true if value in register (P2) is greater than the constant value (P3)",
|
||||
params: [
|
||||
new RegisParam("Set this register to true"),
|
||||
new RegisParam("if this register is greater than"),
|
||||
new ConstParam("this constant"),
|
||||
],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const truth = c.getRegister(register_no_2) > constant_value ? 0x01 : 0x00;
|
||||
c.setRegister(register_no_1, truth);
|
||||
},
|
||||
});
|
||||
|
||||
//
|
||||
// Logic / Bitwise
|
||||
// 0x40 -> 0x4F
|
||||
//
|
||||
|
||||
ISA.insertInstruction(0x40, {
|
||||
name: "Bitwise OR",
|
||||
desc: "Sets each bit in register (P1) to its OR with the respective bit in register (P2)",
|
||||
params: [new RegisParam(""), new RegisParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) | c.getRegister(register_no_2);
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x41, {
|
||||
name: "Bitwise OR",
|
||||
desc: "Sets each bit in register (P1) to its OR with the respective bit in constant value (P2)",
|
||||
params: [new RegisParam(""), new ConstParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) | constant_value;
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x42, {
|
||||
name: "Bitwise AND",
|
||||
desc: "Sets each bit in register (P1) to its AND with the respective bit in register (P2)",
|
||||
params: [new RegisParam(""), new RegisParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) & c.getRegister(register_no_2);
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x43, {
|
||||
name: "Bitwise AND",
|
||||
desc: "Sets each bit in register (P1) to its AND with the respective bit in constant value (P2)",
|
||||
params: [new RegisParam(""), new ConstParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) & constant_value;
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x44, {
|
||||
name: "Bitwise XOR",
|
||||
desc: "Sets each bit in register (P1) to its XOR with the respective bit in register (P2)",
|
||||
params: [new RegisParam(""), new RegisParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) ^ c.getRegister(register_no_2);
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x45, {
|
||||
name: "Bitwise XOR",
|
||||
desc: "Sets each bit in register (P1) to its XOR with the respective bit in constant value (P2)",
|
||||
params: [new RegisParam(""), new ConstParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) ^ constant_value;
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x46, {
|
||||
name: "Left Bit Shift",
|
||||
desc: "Shifts each bit in register (P1) to the left by the amount in register (P2). Fills new bits with 0",
|
||||
params: [new RegisParam(""), new RegisParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) << c.getRegister(register_no_2);
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x47, {
|
||||
name: "Left Bit Shift",
|
||||
desc: "Shifts each bit in register (P1) to the left by the constant value (P2). Fills new bits with 0",
|
||||
params: [new RegisParam(""), new ConstParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) << constant_value;
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x48, {
|
||||
name: "Right Bit Shift",
|
||||
desc: "Shifts each bit in register (P1) to the right by the amount in register (P2). Fills new bits with 0",
|
||||
params: [new RegisParam(""), new RegisParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) >> c.getRegister(register_no_2);
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x49, {
|
||||
name: "Right Bit Shift",
|
||||
desc: "Shifts each bit in register (P1) to the right by the constant value (P2). Fills new bits with 0",
|
||||
params: [new RegisParam(""), new ConstParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) >> constant_value;
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x4a, {
|
||||
name: "Bitwise NOT",
|
||||
desc: "Flips each bit in register (P1)",
|
||||
params: [new RegisParam("")],
|
||||
execute(c, p) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const new_value = ~c.getRegister(register_no);
|
||||
c.setRegister(register_no, new_value as u8);
|
||||
},
|
||||
});
|
||||
|
||||
//
|
||||
// Arithmetic
|
||||
// 0x50 -> 0x5F
|
||||
//
|
||||
|
||||
ISA.insertInstruction(0x50, {
|
||||
name: "Add",
|
||||
desc: "Adds to the byte in register (P1) with the value in register (P2)",
|
||||
params: [new RegisParam("set this register to"), new RegisParam("it's sum with the value in this register")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const sum = c.getRegister(register_no_1) + c.getRegister(register_no_2);
|
||||
if (sum > 255) {
|
||||
c.setCarry(true);
|
||||
}
|
||||
c.setRegister(register_no_1, m256(sum));
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x51, {
|
||||
name: "Add",
|
||||
desc: "Adds to the byte in register (P1) with the value in register (P2)",
|
||||
params: [new RegisParam("set this register to"), new ConstParam("it's sum with this constant")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
const sum = c.getRegister(register_no_1) + constant_value;
|
||||
if (sum > 255) c.setCarry(true);
|
||||
c.setRegister(register_no_1, m256(sum));
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x52, {
|
||||
name: "Add",
|
||||
desc: "Subtracts from the value in register (P1) by the value in register (P2)",
|
||||
params: [new RegisParam("set this register to"), new RegisParam("it's difference with the value in this register")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const difference = c.getRegister(register_no_1) - c.getRegister(register_no_2);
|
||||
if (difference < 0) {
|
||||
c.setCarry(true);
|
||||
}
|
||||
c.setRegister(register_no_1, m256(difference));
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x53, {
|
||||
name: "Add",
|
||||
desc: "Subtracts from the value in register (P1) by the constant value (P2)",
|
||||
params: [new RegisParam("set this register to"), new ConstParam("it's difference with this constant")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
const difference = c.getRegister(register_no_1) + constant_value;
|
||||
if (difference < 0) c.setCarry(true);
|
||||
c.setRegister(register_no_1, m256(difference));
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x5e, {
|
||||
name: "Increment",
|
||||
desc: "Increments the value within register (P1) by 1",
|
||||
params: [new RegisParam("register to be incremented")],
|
||||
execute(c, p) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const current_value = c.getRegister(register_no);
|
||||
const incremented = current_value + 1;
|
||||
if (incremented > 255) c.setCarry(true);
|
||||
c.setRegister(register_no, m256(incremented));
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x5f, {
|
||||
name: "Decrement",
|
||||
desc: "Decrements the value within register (P1) by 1",
|
||||
params: [new RegisParam("register to be decremented")],
|
||||
execute(c, p) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const current_value = c.getRegister(register_no);
|
||||
const decremented = current_value - 1;
|
||||
if (decremented < 0) c.setCarry(true);
|
||||
c.setRegister(register_no, m256(decremented) as u8);
|
||||
},
|
||||
});
|
||||
|
||||
//
|
||||
// IO
|
||||
// 0xF0 -> 0xFF
|
||||
//
|
||||
|
||||
ISA.insertInstruction(0xf0, {
|
||||
name: "PrintASCII",
|
||||
desc: "Prints the ASCII byte in register (P1) to console",
|
||||
params: [new RegisParam("Register to print from")],
|
||||
execute(c, p, a) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const asciiByte = c.getRegister(register_no);
|
||||
|
||||
const char = String.fromCharCode(asciiByte);
|
||||
a.dispatch(CpuEvent.Print, char);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0xf1, {
|
||||
name: "Print",
|
||||
desc: "Prints the byte in register (P1) to console as base 10",
|
||||
params: [new RegisParam("Register to print from")],
|
||||
execute(c, p, a) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const byte = c.getRegister(register_no);
|
||||
|
||||
a.dispatch(CpuEvent.Print, byte.toString(10));
|
||||
},
|
||||
});
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
* @copyright Alexander Bass 2024
|
||||
* @license GPL-3.0
|
||||
*/
|
||||
import { format_hex } from "./etc";
|
||||
import { Instruction, InstructionSet } from "./instructionSet";
|
||||
import { format_hex, in_range } from "./etc";
|
||||
import { InstrCategory, Instruction, InstructionSet, ParameterType, ParamType } from "./instructionSet";
|
||||
import { u8 } from "./num.js";
|
||||
|
||||
export function generate_isa(iset: InstructionSet): string {
|
||||
|
@ -17,12 +17,43 @@ export function generate_isa(iset: InstructionSet): string {
|
|||
const max_instr_name_len = instructions.map((i) => i[1].name.length).reduce((acc, p) => Math.max(p, acc), 0);
|
||||
instructions.sort((a, b) => a[0] - b[0]);
|
||||
|
||||
let current_category: InstrCategory | null = null;
|
||||
|
||||
for (const instruction of instructions) {
|
||||
const cat = iset.category_ranges.find((i) => in_range(instruction[0], i.start, i.end));
|
||||
if (cat === undefined) {
|
||||
throw new Error("Instruction found which is not part of category");
|
||||
}
|
||||
if (current_category !== cat) {
|
||||
output_string += `-- ${cat.name.toUpperCase()} --\n`;
|
||||
current_category = cat;
|
||||
}
|
||||
const hex_code = format_hex(instruction[0]);
|
||||
|
||||
const short_description = instruction[1].name.padEnd(max_instr_name_len, " ");
|
||||
const parameter_count = instruction[1].params.length;
|
||||
const parameters = parameter_description(instruction[1].params);
|
||||
const description = instruction[1].desc;
|
||||
output_string += `0x${hex_code}: ${short_description} - ${parameter_count} Parameter - ${description}\n`;
|
||||
output_string += `0x${hex_code}: ${short_description}`;
|
||||
if (parameters.length !== 0) {
|
||||
output_string += ` -${parameters}- `;
|
||||
} else {
|
||||
output_string += " - ";
|
||||
}
|
||||
output_string += `${description}\n`;
|
||||
}
|
||||
return output_string;
|
||||
}
|
||||
|
||||
function parameter_description(params: Array<ParameterType>): string {
|
||||
let str = "";
|
||||
if (params.length !== 0) {
|
||||
str += " ";
|
||||
}
|
||||
for (const p of params) {
|
||||
const p_map = { [ParamType.Const]: "C", [ParamType.Memory]: "M", [ParamType.Register]: "R" };
|
||||
const char = p_map[p.type];
|
||||
str += char;
|
||||
str += " ";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue