From 7e4d3c8da83c5c46b940c47c2fc2033416b5cfdc Mon Sep 17 00:00:00 2001 From: Alexander Bass Date: Sun, 25 Feb 2024 00:38:09 -0500 Subject: [PATCH] update instruction set to match new version --- newISA.md | 143 ++++++++ src/etc.ts | 5 + src/instructionSet.ts | 783 +++++++++++++++++++++++++++++++----------- src/isaGenerator.ts | 39 ++- 4 files changed, 756 insertions(+), 214 deletions(-) create mode 100644 newISA.md diff --git a/newISA.md b/newISA.md new file mode 100644 index 0000000..4e522c9 --- /dev/null +++ b/newISA.md @@ -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. + +``` + ... +``` + +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 diff --git a/src/etc.ts b/src/etc.ts index d8fd97f..8cc0a10 100644 --- a/src/etc.ts +++ b/src/etc.ts @@ -44,3 +44,8 @@ export function el(type: string, id?: string): HTMLElement | undefined { export type NonEmptyArray = 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; +} diff --git a/src/instructionSet.ts b/src/instructionSet.ts index 84e890e..ab1d3d0 100644 --- a/src/instructionSet.ts +++ b/src/instructionSet.ts @@ -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; - + category_ranges: Array; 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) { - 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) { - 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); - }, -}); - -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", +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) { - 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); + 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(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) { +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 byte = c.getRegister(register_no); - - a.dispatch(CpuEvent.Print, byte.toString(10)); + 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(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)); }, }); diff --git a/src/isaGenerator.ts b/src/isaGenerator.ts index d7888af..10c4387 100644 --- a/src/isaGenerator.ts +++ b/src/isaGenerator.ts @@ -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): 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; +}