From 0764614b66c14284b171425ab100733a5f27a5da Mon Sep 17 00:00:00 2001 From: Alexander Bass Date: Wed, 21 Feb 2024 19:08:02 -0500 Subject: [PATCH] Likely overzealous numeric type enforcement. --- TODO | 5 +- index.html | 1 + src/computer.ts | 32 ++-- src/etc.ts | 8 +- src/events.ts | 2 +- src/index.ts | 54 +++--- src/instructionSet.ts | 51 ++++-- src/isaGenerator.ts | 3 +- src/num.ts | 307 +++++++++++++++++++++++++++++++++ src/style.scss | 35 ++-- src/ui.ts | 43 +---- src/ui/frequencyIndicator.ts | 14 +- src/ui/instructionExplainer.ts | 17 +- src/ui/memoryView.ts | 46 ++++- src/ui/uiComponent.ts | 1 - 15 files changed, 496 insertions(+), 123 deletions(-) create mode 100644 src/num.ts diff --git a/TODO b/TODO index 72c4d95..a58b191 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,4 @@ Add screen (VRAM?) +- Bank switching Live memory and register editing (Probably should pause autostep when it reaches the cell you're modifying) -Draw lines between registers and memory when used - -Save binary button \ No newline at end of file +HCF flames \ No newline at end of file diff --git a/index.html b/index.html index 9fb742b..3ce56be 100644 --- a/index.html +++ b/index.html @@ -22,6 +22,7 @@ + diff --git a/src/computer.ts b/src/computer.ts index 4c07eaf..682e29b 100644 --- a/src/computer.ts +++ b/src/computer.ts @@ -1,20 +1,22 @@ import { CpuEvent, CpuEventHandler, UiEvent, UiEventHandler } from "./events"; -import { byte_array_to_js_source, format_hex, u8 } from "./etc"; +import { byte_array_to_js_source, format_hex } from "./etc"; import { EventHandler } from "./eventHandler"; import { Instruction, ISA } from "./instructionSet"; +import { m256, u8 } from "./num"; export type TempInstrState = { pos: u8; params_found: number; instr: Instruction; - params: Uint8Array; + params: Array; }; export class Computer { - private memory = new Uint8Array(256); - private registers = new Uint8Array(8); + private memory = new Array(256); + private registers = new Array(256); private call_stack: Array = []; private program_counter: u8 = 0; + private bank: u8 = 0; private current_instr: TempInstrState | null = null; events: CpuEventHandler = new EventHandler() as CpuEventHandler; @@ -44,7 +46,7 @@ export class Computer { pos: this.program_counter, instr: parsed_instruction, params_found: 0, - params: new Uint8Array(parsed_instruction.params.length), + params: new Array(parsed_instruction.params.length), }; this.events.dispatch(CpuEvent.InstructionParsed, { pos: this.program_counter, @@ -126,10 +128,14 @@ export class Computer { return this.call_stack.pop() ?? null; } + setBank(bank_no: u8): void { + this.bank = bank_no; + } + reset(): void { this.events.dispatch(CpuEvent.Reset, null); - this.memory = new Uint8Array(256); - this.registers = new Uint8Array(8); + this.memory = new Array(256); + this.registers = new Array(8); this.call_stack = []; this.current_instr = null; this.program_counter = 0; @@ -144,19 +150,23 @@ export class Computer { load_memory(program: Array): void { console.log(byte_array_to_js_source(program)); - const max_loop = Math.min(this.memory.length, program.length); - for (let i = 0; i < max_loop; i++) { + const max_loop: u8 = Math.min(255, program.length) as u8; + for (let i: u8 = 0; i < 255; i++) { // Don't fire event if no change is made if (this.memory[i] === program[i]) continue; this.memory[i] = program[i]; - this.events.dispatch(CpuEvent.MemoryChanged, { address: i, value: program[i] }); + this.events.dispatch(CpuEvent.MemoryChanged, { address: i as u8, value: program[i] }); } this.program_counter = 0; } + dump_memory(): Array { + return this.memory; + } + private step_forward(): void { - this.program_counter = (this.program_counter + 1) % 256; + this.program_counter = m256(this.program_counter + 1); this.events.dispatch(CpuEvent.ProgramCounterChanged, { counter: this.program_counter }); } } diff --git a/src/etc.ts b/src/etc.ts index 47ae339..ffdd553 100644 --- a/src/etc.ts +++ b/src/etc.ts @@ -1,13 +1,13 @@ -import { UiEventHandler } from "./events"; +import { u8 } from "./num"; // The u8 type represents an unsigned 8bit integer: byte. It does not add any safety, other than as a hint to the programmer. -// export type u8 = 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|101|102|103|104|105|106|107|108|109|110|111|112|113|114|115|116|117|118|119|120|121|122|123|124|125|126|127|128|129|130|131|132|133|134|135|136|137|138|139|140|141|142|143|144|145|146|147|148|149|150|151|152|153|154|155|156|157|158|159|160|161|162|163|164|165|166|167|168|169|170|171|172|173|174|175|176|177|178|179|180|181|182|183|184|185|186|187|188|189|190|191|192|193|194|195|196|197|198|199|200|201|202|203|204|205|206|207|208|209|210|211|212|213|214|215|216|217|218|219|220|221|222|223|224|225|226|227|228|229|230|231|232|233|234|235|236|237|238|239|240|241|242|243|244|245|246|247|248|249|250|251|252|253|254|255; -export type u8 = number; + +// export type u8 = number; // Jquery lite export const $ = (s: string): HTMLElement => document.getElementById(s) as HTMLElement; -export const format_hex = (n: number): string => n.toString(16).toUpperCase().padStart(2, "0"); +export const format_hex = (n: u8): string => n.toString(16).toUpperCase().padStart(2, "0"); export const byte_array_to_js_source = (a: Array): string => { let str = "["; diff --git a/src/events.ts b/src/events.ts index f2d1251..5e16025 100644 --- a/src/events.ts +++ b/src/events.ts @@ -1,6 +1,6 @@ -import { u8 } from "./etc"; import { EventHandler } from "./eventHandler"; import { Instruction, ParameterType } from "./instructionSet"; +import { u8 } from "./num.js"; export enum CpuEvent { MemoryChanged, diff --git a/src/index.ts b/src/index.ts index 4859439..d88c835 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,29 +3,13 @@ import { $ } from "./etc"; import { ISA } from "./instructionSet"; import { generate_isa } from "./isaGenerator"; import { UI } from "./ui"; +import { u8 } from "./num"; import "./style.scss"; function main(): void { - // const program = [ - // 0x2f, 0x00, 0xf0, 0x20, 0x07, 0x00, 0x50, 0x05, 0x06, 0x07, 0x11, 0x00, 0x05, 0xfe, 0x07, 0x30, 0x00, 0x10, 0x03, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, - // 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x0a, 0x00, 0x00, 0x00, - // ]; - - const program = [ - 0x01, 0x00, 0x01, 0x00, 0xa0, 0x10, 0xff, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0xa1, + const program: Array = [ + 0x2f, 0x00, 0xf0, 0x20, 0x07, 0x00, 0x50, 0x05, 0x06, 0x07, 0x11, 0x00, 0x05, 0xfe, 0x07, 0x30, 0x00, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -41,6 +25,23 @@ function main(): void { 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x0a, 0x00, 0x00, 0x00, ]; + // const program = [ + // 0x01, 0x00, 0x01, 0x00, 0xa0, 0x10, 0xff, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0xa1, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, + // 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x0a, 0x00, 0x00, 0x00, + // ]; + const computer = new Computer(); const ui = new UI(); @@ -68,7 +69,7 @@ function main(): void { const data = e.target.result; if (data instanceof ArrayBuffer) { const view = new Uint8Array(data); - const array = [...view]; + const array = [...view] as Array; ui.stop_auto(); computer.reset(); computer.load_memory(array); @@ -79,6 +80,19 @@ function main(): void { }); reader.readAsArrayBuffer(file); }); + + $("save_button").addEventListener("click", () => { + const memory = computer.dump_memory(); + const buffer = new Uint8Array(memory); + const blob = new Blob([buffer], { type: "application/octet-stream" }); + const url = URL.createObjectURL(blob); + + const link = document.createElement("a"); + link.href = url; + link.download = "bin.bin"; + link.style.display = "none"; + link.click(); + }); } document.addEventListener("DOMContentLoaded", () => { diff --git a/src/instructionSet.ts b/src/instructionSet.ts index 52726da..2af2b42 100644 --- a/src/instructionSet.ts +++ b/src/instructionSet.ts @@ -1,5 +1,6 @@ import { CpuEvent, CpuEventHandler } from "./events"; -import { format_hex, u8 } from "./etc"; +import { format_hex } from "./etc"; +import { isU3, m256, u1, u3, u8 } from "./num"; export enum ParamType { Const, @@ -37,10 +38,11 @@ interface GenericComputer { setMemory: (address: u8, value: u8) => void; setProgramCounter: (address: u8) => void; getProgramCounter: () => u8; - getRegister: (number: u8) => u8; - setRegister: (number: u8, value: u8) => void; + getRegister: (number: u3) => u8; + setRegister: (number: u3, value: u8) => void; pushCallStack: (address: u8) => boolean; popCallStack: () => u8 | null; + setBank: (bank_no: u1) => void; } interface AfterExecutionComputerAction { @@ -55,7 +57,7 @@ export interface Instruction { readonly params: Array; readonly execute: ( computer_reference: GenericComputer, - parameters: Uint8Array, + parameters: Array, a: AfterExecutionComputerAction ) => void; } @@ -104,10 +106,13 @@ 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 MemorParam("the byte held in this memory address")], + params: [new RegisParam("Set this register to"), new RegisParam("the byte held in this memory address")], execute(c, p) { - const [register_no, mem_address] = p; - c.setRegister(register_no, c.getMemory(c.getRegister(mem_address))); + const [register_no, register_2] = p; + if (!isU3(register_no)) throw new Error("TODO"); + if (!isU3(register_2)) throw new Error("TODO"); + + c.setRegister(register_no, c.getMemory(c.getRegister(register_2))); }, }); @@ -117,6 +122,7 @@ ISA.insertInstruction(0x21, { 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)); }, }); @@ -127,6 +133,7 @@ ISA.insertInstruction(0x2f, { 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); }, }); @@ -137,6 +144,7 @@ ISA.insertInstruction(0x11, { 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(); @@ -150,8 +158,9 @@ ISA.insertInstruction(0x30, { 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 = (current_value + 1) % 256; + const new_value = m256(current_value + 1); c.setRegister(register_no, new_value); }, }); @@ -162,12 +171,13 @@ ISA.insertInstruction(0x31, { 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); + c.setRegister(register_no, new_value as u8); }, }); @@ -177,7 +187,9 @@ ISA.insertInstruction(0x40, { 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; - const new_value = (c.getRegister(register_no_1) + c.getRegister(register_no_2)) % 256; + 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); }, }); @@ -192,6 +204,9 @@ ISA.insertInstruction(0x50, { ], 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); }, @@ -203,6 +218,7 @@ ISA.insertInstruction(0xfe, { 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); @@ -216,8 +232,10 @@ ISA.insertInstruction(0x48, { 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); + c.setRegister(register_no_1, new_value as u8); }, }); @@ -227,6 +245,7 @@ ISA.insertInstruction(0xff, { 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)); @@ -239,6 +258,8 @@ ISA.insertInstruction(0xfd, { 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; @@ -282,8 +303,14 @@ ISA.insertInstruction(0xa1, { throw new Error("TODO handle this"); } - c.setProgramCounter(new_address + 1); + 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, a) {}, +}); diff --git a/src/isaGenerator.ts b/src/isaGenerator.ts index 5a9d630..d14147e 100644 --- a/src/isaGenerator.ts +++ b/src/isaGenerator.ts @@ -1,5 +1,6 @@ -import { format_hex, u8 } from "./etc"; +import { format_hex } from "./etc"; import { Instruction, InstructionSet } from "./instructionSet"; +import { u8 } from "./num.js"; export function generate_isa(iset: InstructionSet): string { const instructions: Array<[u8, Instruction]> = []; diff --git a/src/num.ts b/src/num.ts new file mode 100644 index 0000000..d1ec48c --- /dev/null +++ b/src/num.ts @@ -0,0 +1,307 @@ +export type u8 = + | 0 + | 1 + | 2 + | 3 + | 4 + | 5 + | 6 + | 7 + | 8 + | 9 + | 10 + | 11 + | 12 + | 13 + | 14 + | 15 + | 16 + | 17 + | 18 + | 19 + | 20 + | 21 + | 22 + | 23 + | 24 + | 25 + | 26 + | 27 + | 28 + | 29 + | 30 + | 31 + | 32 + | 33 + | 34 + | 35 + | 36 + | 37 + | 38 + | 39 + | 40 + | 41 + | 42 + | 43 + | 44 + | 45 + | 46 + | 47 + | 48 + | 49 + | 50 + | 51 + | 52 + | 53 + | 54 + | 55 + | 56 + | 57 + | 58 + | 59 + | 60 + | 61 + | 62 + | 63 + | 64 + | 65 + | 66 + | 67 + | 68 + | 69 + | 70 + | 71 + | 72 + | 73 + | 74 + | 75 + | 76 + | 77 + | 78 + | 79 + | 80 + | 81 + | 82 + | 83 + | 84 + | 85 + | 86 + | 87 + | 88 + | 89 + | 90 + | 91 + | 92 + | 93 + | 94 + | 95 + | 96 + | 97 + | 98 + | 99 + | 100 + | 101 + | 102 + | 103 + | 104 + | 105 + | 106 + | 107 + | 108 + | 109 + | 110 + | 111 + | 112 + | 113 + | 114 + | 115 + | 116 + | 117 + | 118 + | 119 + | 120 + | 121 + | 122 + | 123 + | 124 + | 125 + | 126 + | 127 + | 128 + | 129 + | 130 + | 131 + | 132 + | 133 + | 134 + | 135 + | 136 + | 137 + | 138 + | 139 + | 140 + | 141 + | 142 + | 143 + | 144 + | 145 + | 146 + | 147 + | 148 + | 149 + | 150 + | 151 + | 152 + | 153 + | 154 + | 155 + | 156 + | 157 + | 158 + | 159 + | 160 + | 161 + | 162 + | 163 + | 164 + | 165 + | 166 + | 167 + | 168 + | 169 + | 170 + | 171 + | 172 + | 173 + | 174 + | 175 + | 176 + | 177 + | 178 + | 179 + | 180 + | 181 + | 182 + | 183 + | 184 + | 185 + | 186 + | 187 + | 188 + | 189 + | 190 + | 191 + | 192 + | 193 + | 194 + | 195 + | 196 + | 197 + | 198 + | 199 + | 200 + | 201 + | 202 + | 203 + | 204 + | 205 + | 206 + | 207 + | 208 + | 209 + | 210 + | 211 + | 212 + | 213 + | 214 + | 215 + | 216 + | 217 + | 218 + | 219 + | 220 + | 221 + | 222 + | 223 + | 224 + | 225 + | 226 + | 227 + | 228 + | 229 + | 230 + | 231 + | 232 + | 233 + | 234 + | 235 + | 236 + | 237 + | 238 + | 239 + | 240 + | 241 + | 242 + | 243 + | 244 + | 245 + | 246 + | 247 + | 248 + | 249 + | 250 + | 251 + | 252 + | 253 + | 254 + | 255; + +export type u1 = 0 | 1; +export type u2 = 0 | 1 | 2 | 3; +export type u3 = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; +export type u4 = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15; + +export const m256 = (number: number): u8 => (number % 256) as u8; + +/** + * Determines whether a number is a u3 type (unsigned 3-bit integer). + * Does not check for non integers + * + * @param n - Input number to be checked + * @returns true or false + * + */ +export function isU3(n: number): n is u3 { + if (n < 8 && n >= 0) { + return true; + } + return false; +} +/** + * Determines whether a number is a u4 type (unsigned 4-bit integer). + * Does not check for non integers + * + * @param n - Input number to be checked + * @returns true or false + * + */ +export function isU4(n: number): n is u4 { + if (n < 16 && n >= 0) { + return true; + } + return false; +} +/** + * Determines whether a number is a u8 type (unsigned 8-bit integer). + * Does not check for non integers + * + * @param n - Input number to be checked + * @returns true or false + * + */ +export function isU8(n: number): n is u8 { + if (n < 256 && n >= 0) { + return true; + } + return false; +} diff --git a/src/style.scss b/src/style.scss index ea3f147..08758bb 100644 --- a/src/style.scss +++ b/src/style.scss @@ -7,16 +7,26 @@ pre { } :root { - --Border: Yellow; - --mem-instruction: greenyellow; - --mem-register: Purple; - --mem-constant: lightgray; - --mem-memory: lightgray; - --mem-invalid: red; + --Border: #ffff00; + // --mem-instruction: #adff2f; + // --mem-register: #800080; + // --mem-constant: #d3d3d3; + // --mem-memory: #d3d3d3; + // --mem-invalid: #ff0000; + // --mem-instruction: #2f962a; + // --mem-register: #dc21d1; + // --mem-constant: #d3d3d3; + // --mem-memory: #4d86f0; + // --mem-invalid: #bf2e2e; + --mem-instruction: #3af78f; + --mem-memory: #ff26a8; + --mem-register: #9e0ef7; + --mem-constant: #19f7f0; + --mem-invalid: #bf2e2e; } body { - color: gray; + color: #808080; background-color: black; font-size: 2.5em; font-family: monospace; @@ -63,7 +73,7 @@ body { } #printout { - border: 4px dashed yellow; + border: 4px dashed var(--Border); grid-area: printout; padding: 10px; word-wrap: break-word; @@ -77,7 +87,7 @@ body { display: flex; flex-direction: column; gap: 5px; - border: 5px dashed yellow; + border: 5px dashed var(--Border); #expl_box { padding-inline: 20px; padding-block-start: 10px; @@ -128,13 +138,10 @@ body { .program_counter { outline: 3px solid orange; } - .instruction_argument { - outline: 3px dashed purple; - } + .instruction_argument, .current_instruction { - outline: 3px dashed greenyellow; + outline: 3px dashed var(--color); } - .invalid { &::after { user-select: none; diff --git a/src/ui.ts b/src/ui.ts index 4709e19..d1f3eb5 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -1,5 +1,5 @@ import { CpuEvent, CpuEventHandler, UiEvent, UiEventHandler } from "./events"; -import { $, el, format_hex, u8 } from "./etc"; +import { $, el, format_hex } from "./etc"; import { EventHandler } from "./eventHandler"; import { InstructionExplainer } from "./ui/instructionExplainer"; import { MemoryView } from "./ui/memoryView"; @@ -75,57 +75,16 @@ export class UI { } init_events(cpu_events: CpuEventHandler): void { - cpu_events.listen(CpuEvent.MemoryChanged, ({ address, value }) => { - this.memory.set_cell_value(address, value); - }); cpu_events.listen(CpuEvent.RegisterChanged, ({ register_no, value }) => { this.register_cells[register_no].textContent = format_hex(value); }); - cpu_events.listen(CpuEvent.ProgramCounterChanged, ({ counter }) => { - this.memory.set_program_counter(counter); - }); cpu_events.listen(CpuEvent.Print, (char) => { this.printout.textContent = (this.printout.textContent ?? "") + char; }); - cpu_events.listen(CpuEvent.Reset, () => { - this.reset(); - }); this.frequencyIndicator.init_cpu_events(cpu_events); this.memory.init_cpu_events(cpu_events); this.instruction_explainer.init_cpu_events(cpu_events); - - cpu_events.listen(CpuEvent.ParameterParsed, ({ param, code, pos }) => { - this.memory.add_cell_class(pos, "instruction_argument"); - this.instruction_explainer.add_param(param, pos, code); - const t = param.type; - this.memory.remove_cell_class(pos, "constant", "register", "memory", "instruction", "invalid"); - let name: string = ""; - if (t === ParamType.Const) { - name = "constant"; - } else if (t === ParamType.Memory) { - name = "memory"; - } else if (t === ParamType.Register) { - name = "register"; - } else { - throw new Error("unreachable"); - } - this.memory.add_cell_class(pos, name); - }); - cpu_events.listen(CpuEvent.InstructionParsed, ({ instr, code, pos }) => { - this.instruction_explainer.add_instruction(instr, pos, code); - - this.memory.remove_all_cell_class("instruction_argument"); - this.memory.remove_all_cell_class("current_instruction"); - this.memory.add_cell_class(pos, "current_instruction"); - this.memory.remove_cell_class(pos, "constant", "register", "memory", "invalid"); - this.memory.add_cell_class(pos, "instruction"); - }); - cpu_events.listen(CpuEvent.InvalidParsed, ({ code, pos }) => { - this.memory.remove_cell_class(pos, "constant", "register", "memory", "instruction"); - this.memory.add_cell_class(pos, "invalid"); - this.instruction_explainer.add_invalid(pos, code); - }); } reset(): void { diff --git a/src/ui/frequencyIndicator.ts b/src/ui/frequencyIndicator.ts index 3a689d0..b61d013 100644 --- a/src/ui/frequencyIndicator.ts +++ b/src/ui/frequencyIndicator.ts @@ -13,6 +13,7 @@ export class frequencyIndicator implements UiComponent { } start(): void { + this.last_time = performance.now(); if (this.running !== null) { throw new Error("Tried starting frequencyIndicator twice!"); } @@ -26,10 +27,15 @@ export class frequencyIndicator implements UiComponent { } update_indicator(): void { - if (this.last_value !== this.count) { - this.element.textContent = `${this.count}hz`; - this.last_value = this.count; + const new_time = performance.now(); + const dt = (new_time - this.last_time) / 1000 || 1; + const value = Math.round(this.count / dt); + + if (this.last_value !== value) { + this.element.textContent = `${value}hz`; + this.last_value = value; } + this.last_time = new_time; this.count = 0; } @@ -44,7 +50,7 @@ export class frequencyIndicator implements UiComponent { this.count = 0; this.last_value = 0; } - init_cpu_events(c: CpuEventHandler) { + init_cpu_events(c: CpuEventHandler): void { c.listen(CpuEvent.ClockCycle, () => { this.count += 1; }); diff --git a/src/ui/instructionExplainer.ts b/src/ui/instructionExplainer.ts index ede412e..c3bc75f 100644 --- a/src/ui/instructionExplainer.ts +++ b/src/ui/instructionExplainer.ts @@ -1,6 +1,7 @@ -import { el, format_hex, u8 } from "../etc"; +import { el, format_hex } from "../etc"; import { CpuEvent, CpuEventHandler, UiEventHandler } from "../events"; import { Instruction, ParamType, ParameterType } from "../instructionSet"; +import { u8 } from "../num"; import { UiComponent } from "./uiComponent"; export class InstructionExplainer implements UiComponent { @@ -41,13 +42,23 @@ export class InstructionExplainer implements UiComponent { this.add_box(format_hex(byte), param.desc, name); } - add_invalid(pos: u8, byte: u8) { + add_invalid(pos: u8, byte: u8): void { this.reset(); this.add_box(format_hex(byte), "Invalid Instruction", "invalid"); } init_events(eh: UiEventHandler): void {} - init_cpu_events(c: CpuEventHandler) {} + init_cpu_events(c: CpuEventHandler): void { + c.listen(CpuEvent.ParameterParsed, ({ param, code, pos }) => { + this.add_param(param, pos, code); + }); + c.listen(CpuEvent.InstructionParsed, ({ instr, code, pos }) => { + this.add_instruction(instr, pos, code); + }); + c.listen(CpuEvent.InvalidParsed, ({ code, pos }) => { + this.add_invalid(pos, code); + }); + } reset(): void { this.element.innerHTML = ""; diff --git a/src/ui/memoryView.ts b/src/ui/memoryView.ts index a1de6e0..616be61 100644 --- a/src/ui/memoryView.ts +++ b/src/ui/memoryView.ts @@ -1,11 +1,9 @@ -import { el, format_hex, u8 } from "../etc"; -import { CpuEventHandler, UiEventHandler } from "../events"; +import { el, format_hex } from "../etc"; +import { CpuEvent, CpuEventHandler, UiEventHandler } from "../events"; +import { ParamType } from "../instructionSet"; +import { u8 } from "../num.js"; import { UiComponent } from "./uiComponent"; -export enum CellTag { - ProgramCounter = "program_counter", -} - type MemoryCell = { el: HTMLElement; }; @@ -74,5 +72,39 @@ export class MemoryView implements UiComponent { init_events(eh: UiEventHandler): void { this; } - init_cpu_events(c: CpuEventHandler) {} + init_cpu_events(c: CpuEventHandler): void { + c.listen(CpuEvent.MemoryChanged, ({ address, value }) => { + this.set_cell_value(address, value); + }); + c.listen(CpuEvent.ProgramCounterChanged, ({ counter }) => { + this.set_program_counter(counter); + }); + c.listen(CpuEvent.ParameterParsed, ({ param, code, pos }) => { + this.add_cell_class(pos, "instruction_argument"); + const t = param.type; + this.remove_cell_class(pos, "constant", "register", "memory", "instruction", "invalid"); + let name: string = ""; + if (t === ParamType.Const) { + name = "constant"; + } else if (t === ParamType.Memory) { + name = "memory"; + } else if (t === ParamType.Register) { + name = "register"; + } else { + throw new Error("unreachable"); + } + this.add_cell_class(pos, name); + }); + c.listen(CpuEvent.InstructionParsed, ({ instr, code, pos }) => { + this.remove_all_cell_class("instruction_argument"); + this.remove_all_cell_class("current_instruction"); + this.add_cell_class(pos, "current_instruction"); + this.remove_cell_class(pos, "constant", "register", "memory", "invalid"); + this.add_cell_class(pos, "instruction"); + }); + c.listen(CpuEvent.InvalidParsed, ({ code, pos }) => { + this.remove_cell_class(pos, "constant", "register", "memory", "instruction"); + this.add_cell_class(pos, "invalid"); + }); + } } diff --git a/src/ui/uiComponent.ts b/src/ui/uiComponent.ts index c7d281e..da43929 100644 --- a/src/ui/uiComponent.ts +++ b/src/ui/uiComponent.ts @@ -1,4 +1,3 @@ -import { u8 } from "../etc"; import { CpuEventHandler, UiEventHandler } from "../events"; export interface UiComponent {