update instruction set to match new version

This commit is contained in:
Alexander Bass 2024-02-25 00:38:09 -05:00
parent e827ac11b5
commit 7e4d3c8da8
4 changed files with 756 additions and 214 deletions

143
newISA.md Normal file
View 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

View file

@ -44,3 +44,8 @@ export function el(type: string, id?: string): HTMLElement | undefined {
export type NonEmptyArray<T> = T[] & { 0: T }; export type NonEmptyArray<T> = T[] & { 0: T };
export const SVG_NS = "http://www.w3.org/2000/svg"; 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;
}

View file

@ -4,7 +4,7 @@
* @license GPL-3.0 * @license GPL-3.0
*/ */
import { CpuEvent, CpuEventHandler } from "./events"; 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"; import { isU2, isU3, m256, u2, u3, u8 } from "./num";
export enum ParamType { export enum ParamType {
@ -69,11 +69,18 @@ export interface Instruction {
) => void; ) => void;
} }
export type InstrCategory = {
start: u8;
end: u8;
name: string;
};
export class InstructionSet { export class InstructionSet {
instructions: Map<u8, Instruction>; instructions: Map<u8, Instruction>;
category_ranges: Array<InstrCategory>;
constructor() { constructor() {
this.instructions = new Map(); this.instructions = new Map();
this.category_ranges = [];
} }
insertInstruction(hexCode: u8, instruction: Instruction): void { insertInstruction(hexCode: u8, instruction: Instruction): void {
@ -83,24 +90,152 @@ export class InstructionSet {
this.instructions.set(hexCode, instruction); 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 { getInstruction(hexCode: u8): Instruction | null {
// console.log(format_hex(hexCode));
return this.instructions.get(hexCode) ?? null; return this.instructions.get(hexCode) ?? null;
} }
} }
function category(start: u8, end: u8, name: string): InstrCategory {
return { start, end, name };
}
export const ISA = new InstructionSet(); 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. // The definitions for actual instructions.
ISA.insertInstruction(0x00, { //
name: "NoOp", // MEMORY & REGISTER MANAGEMENT
desc: "No operation; do nothing", // 0x10 -> 0x1F
params: [], //
execute: () => {},
// 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", name: "Goto",
desc: "Moves the CPU instruction counter to the value in (P1)", desc: "Moves the CPU instruction counter to the value in (P1)",
params: [new ConstParam("new instruction counter location")], params: [new ConstParam("new instruction counter location")],
@ -111,199 +246,67 @@ ISA.insertInstruction(0x10, {
}, },
}); });
ISA.insertInstruction(0x20, { ISA.insertInstruction(0x22, {
name: "LoadToRegister", name: "Goto if True",
desc: "Sets the byte in register (P1) to be the contents of memory cell at address in register (P2)", desc: "Moves the instruction counter to the value in register (P2) if the value in register (P1) is true",
params: [new RegisParam("Set this register to"), new RegisParam("the byte in the memory addressed by this register")], params: [new RegisParam(""), new RegisParam("")],
execute(c, p) { execute: (c, p, a) => {
const [register_no, register_2] = p; const register_no_1 = p[0];
if (!isU3(register_no)) throw new Error("TODO"); if (!isU3(register_no_1)) throw new Error("todo");
if (!isU3(register_2)) throw new Error("TODO"); const bool = c.getRegister(register_no_1);
const mem_value = c.getMemory(c.getRegister(register_2)); if (!bool) return;
const register_no_2 = p[1];
c.setRegister(register_no, mem_value); if (!isU3(register_no_2)) throw new Error("todo");
}, const new_address = c.getRegister(register_no_2);
});
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) {
c.setProgramCounter(new_address); c.setProgramCounter(new_address);
a.noStep(); a.noStep();
}
}, },
}); });
ISA.insertInstruction(0x30, { ISA.insertInstruction(0x23, {
name: "Increment", name: "Goto if True",
desc: "Increments the value within register (P1) by 1", desc: "Moves the instruction counter to the value in (P2) if the value in register (P1) is true",
params: [new RegisParam("register to be incremented")], params: [new RegisParam(""), new ConstParam("")],
execute(c, p) { 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]; const register_no = p[0];
if (!isU3(register_no)) throw new Error("TODO"); if (!isU3(register_no)) throw new Error("todo");
const current_value = c.getRegister(register_no); const register_value = c.getRegister(register_no);
const new_value = m256(current_value + 1); c.setProgramCounter(register_value);
c.setRegister(register_no, new_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, { ISA.insertInstruction(0x2d, {
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, {
name: "Call", name: "Call",
desc: "Calls a subroute", desc: "",
params: [new ConstParam("the subroutine at this memory address")], params: [new ConstParam("the subroutine at this memory address")],
execute(c, p, a) { execute(c, p, a) {
const current_address = c.getProgramCounter(); const current_address = c.getProgramCounter();
@ -317,30 +320,390 @@ ISA.insertInstruction(0xa0, {
}, },
}); });
ISA.insertInstruction(0xa1, { ISA.insertInstruction(0x2e, {
name: "Return", name: "Return",
desc: "returns from a subroutine", desc: "",
params: [], params: [],
execute(c, p, a) { execute(c, p, a) {
const new_address = c.popCallStack(); const new_address = c.popCallStack();
if (new_address === null) { if (new_address === null) throw new Error("TODO handle this");
throw new Error("TODO handle this");
}
c.setProgramCounter(m256(new_address + 1)); c.setProgramCounter(m256(new_address + 1));
a.noStep(); a.noStep();
}, },
}); });
ISA.insertInstruction(0xb1, {
name: "Set bank", ISA.insertInstruction(0x2c, {
desc: "Selects which bank of memory to write and read to", name: "NoOp",
params: [new ConstParam("Bank number")], desc: "No operation; do nothing",
execute(c, p) { params: [],
const bank_no = p[0]; execute: () => {},
if (!isU2(bank_no)) { });
throw new Error("TODO");
} ISA.insertInstruction(0x2f, {
c.setBank(bank_no); 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));
}, },
}); });

View file

@ -3,8 +3,8 @@
* @copyright Alexander Bass 2024 * @copyright Alexander Bass 2024
* @license GPL-3.0 * @license GPL-3.0
*/ */
import { format_hex } from "./etc"; import { format_hex, in_range } from "./etc";
import { Instruction, InstructionSet } from "./instructionSet"; import { InstrCategory, Instruction, InstructionSet, ParameterType, ParamType } from "./instructionSet";
import { u8 } from "./num.js"; import { u8 } from "./num.js";
export function generate_isa(iset: InstructionSet): string { 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); 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]); instructions.sort((a, b) => a[0] - b[0]);
let current_category: InstrCategory | null = null;
for (const instruction of instructions) { 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 hex_code = format_hex(instruction[0]);
const short_description = instruction[1].name.padEnd(max_instr_name_len, " "); 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; 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; 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;
}