From e2d3bae806e1ecd15df54450b380811953904d4a Mon Sep 17 00:00:00 2001 From: Alexander Bass Date: Fri, 16 Feb 2024 03:44:20 -0500 Subject: [PATCH] Add UiEvent's. Make events follow types --- HelloWorld.bin | Bin 256 -> 256 bytes index.html | 1 + src/computer.ts | 51 ++++++++------ src/etc.ts | 11 ++++ src/eventHandler.ts | 8 +-- src/events.ts | 36 +++++++++- src/index.ts | 23 +++++-- src/instructionSet.ts | 44 +++++++------ src/isaGenerator.ts | 4 +- src/ui.ts | 150 ++++++++++++++++++++++++++---------------- 10 files changed, 219 insertions(+), 109 deletions(-) diff --git a/HelloWorld.bin b/HelloWorld.bin index e14a5b35ea05a761c8078e9dfc538f53a260ae65..995036fb2289bbeed32ed4db60aa44acddb303cf 100644 GIT binary patch literal 256 scmdOA_@Kbf5WvdDF37<8kKKSlfSF-91CP|4oP34w{Gyx`MJ}L60UdM)P5=M^ delta 52 ycmZo*YG6{
+
diff --git a/src/computer.ts b/src/computer.ts index a71f1de..a202dec 100644 --- a/src/computer.ts +++ b/src/computer.ts @@ -1,5 +1,5 @@ -import { CpuEvent, MemoryCellType } from "./events"; -import { u8 } from "./etc"; +import { CpuEvent, CpuEventHandler, MemoryCellType, UiEvent, UiEventHandler } from "./events"; +import { byte_array_to_js_source, format_hex, u8 } from "./etc"; import { EventHandler } from "./eventHandler"; import { ConstParam, Instruction, ISA, MemorParam, RegisParam } from "./instructionSet"; @@ -17,21 +17,13 @@ export type ComputerState = { }; export class Computer { - private memory: Uint8Array; - private program_counter: u8; - private registers: Uint8Array; - private current_instr: TempInstrState | null; - events: EventHandler; + private memory = new Uint8Array(256); + private registers = new Uint8Array(8); + private program_counter: u8 = 0; + private current_instr: TempInstrState | null = null; + events: CpuEventHandler = new EventHandler() as CpuEventHandler; constructor() { - // 256 bytes for both program and general purpose memory. - this.memory = new Uint8Array(256); - // 8 registers - this.registers = new Uint8Array(8); - this.program_counter = 0; - this.current_instr = null; - this.events = new EventHandler(); - // Add events for (const [, e_type] of Object.entries(CpuEvent)) { this.events.register_event(e_type as CpuEvent); @@ -47,8 +39,9 @@ export class Computer { this.events.dispatch(CpuEvent.MemoryByteParsed, { type: MemoryCellType.InvalidInstruction, pos: this.program_counter, + code: current_byte, }); - console.log(`Invalid instruction: ${current_byte.toString(16).toUpperCase().padStart(2, "0")}`); + console.log(`Invalid instruction: ${format_hex(current_byte)}`); this.step_forward(); return; } @@ -62,6 +55,7 @@ export class Computer { type: MemoryCellType.Instruction, pos: this.program_counter, instr: parsed_instruction, + code: current_byte, }); } @@ -80,7 +74,15 @@ export class Computer { } else if (b instanceof MemorParam) { a = MemoryCellType.Memory; } - this.events.dispatch(CpuEvent.MemoryByteParsed, { type: a, pos: this.program_counter, param: b }); + if (a === undefined) { + throw new Error("Shouldn't"); + } + this.events.dispatch(CpuEvent.MemoryByteParsed, { + type: a, + pos: this.program_counter, + code: current_byte, + param: b, + }); this.current_instr.params[this.current_instr.params_found] = current_byte; this.current_instr.params_found += 1; if (this.current_instr.params.length !== this.current_instr.params_found) { @@ -93,10 +95,10 @@ export class Computer { noStep: function (): void { this.should_step = false; }, - event: this.events.dispatch.bind(this.events), + dispatch: this.events.dispatch.bind(this.events), }; this.current_instr.instr.execute(this, this.current_instr.params, execution_post_action_state); - + this.events.dispatch(CpuEvent.InstructionExecuted, { instr: this.current_instr.instr }); this.current_instr = null; if (execution_post_action_state.should_step) { @@ -136,9 +138,20 @@ export class Computer { this.events.dispatch(CpuEvent.Reset, null); } + init_events(ui: UiEventHandler): void { + ui.listen(UiEvent.RequestCpuCycle, () => { + this.cycle(); + }); + } + 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++) { + // 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] }); } diff --git a/src/etc.ts b/src/etc.ts index 04243c1..924d35e 100644 --- a/src/etc.ts +++ b/src/etc.ts @@ -4,6 +4,17 @@ 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 byte_array_to_js_source = (a: Array): string => { + let str = "["; + for (const b of a) { + str += `0x${format_hex(b)},`; + } + str += "]"; + return str; +}; + export function el(type: string, id?: string): HTMLElement { const element = document.createElement(type); if (id === undefined) { diff --git a/src/eventHandler.ts b/src/eventHandler.ts index 6ddead8..68972b4 100644 --- a/src/eventHandler.ts +++ b/src/eventHandler.ts @@ -1,18 +1,16 @@ export class Event { identifier: T; - callbacks: Array<(event_data: unknown) => void>; + callbacks: Array<(event_data: unknown) => void> = []; constructor(identifier: T) { this.identifier = identifier; - this.callbacks = []; } } export class EventHandler { - events: Array>; + events: Array> = []; private sealed: boolean; constructor() { this.sealed = false; - this.events = []; } seal(): void { @@ -38,7 +36,7 @@ export class EventHandler { callback(event_data); } } - add_listener(identifier: T, callback: (event_data: unknown) => void): void { + listen(identifier: T, callback: (event_data: unknown) => void): void { if (!this.sealed) throw new Error("Event handler must be sealed before adding listener"); const event = this.events.find((e) => e.identifier === identifier); if (event === undefined) { diff --git a/src/events.ts b/src/events.ts index 1df9206..0fa883a 100644 --- a/src/events.ts +++ b/src/events.ts @@ -1,8 +1,13 @@ +import { u8 } from "./etc"; +import { EventHandler } from "./eventHandler"; +import { Instruction, ParameterType } from "./instructionSet"; + export enum CpuEvent { MemoryChanged, RegisterChanged, - MemoryByteParsed, ProgramCounterChanged, + MemoryByteParsed, + InstructionExecuted, Print, Reset, } @@ -14,3 +19,32 @@ export enum MemoryCellType { Memory, Constant, } + +// Handily explained in https://www.cgjennings.ca/articles/typescript-events/ +interface CpuEventMap { + [CpuEvent.MemoryChanged]: { address: u8; value: u8 }; + [CpuEvent.RegisterChanged]: { register_no: u8; value: u8 }; + [CpuEvent.ProgramCounterChanged]: { counter: u8 }; + [CpuEvent.Reset]: null; + [CpuEvent.MemoryByteParsed]: { type: MemoryCellType; pos: u8; code: u8; param?: ParameterType; instr?: Instruction }; + [CpuEvent.InstructionExecuted]: { instr: Instruction }; + [CpuEvent.Print]: string; +} + +export interface CpuEventHandler extends EventHandler { + listen(type: E, listener: (ev: CpuEventMap[E]) => void): void; + dispatch(type: E, data: CpuEventMap[E]): void; +} + +export enum UiEvent { + RequestCpuCycle, +} + +interface UiEventMap { + [UiEvent.RequestCpuCycle]: null; +} + +export interface UiEventHandler extends EventHandler { + listen(type: E, listener: (ev: UiEventMap[E]) => void): void; + dispatch(type: E, data: UiEventMap[E]): void; +} diff --git a/src/index.ts b/src/index.ts index bde19a1..e97a127 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,8 +5,22 @@ import { generate_isa } from "./isaGenerator"; import { UI } from "./ui"; function main(): void { - // const program = [0x2f, 0x01, 0x01, 0x40, 0x00, 0x01, 0x21, 0x00, 0x02, 0x10, 0x00]; - const program = [0x2f, 0x00, 0x41, 0xfe, 0x00, 0x30, 0x00, 0x10, 0x03]; + 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 container = $("container"); if (container === null) { @@ -14,9 +28,10 @@ function main(): void { } const computer = new Computer(); - const ui = new UI(container, computer.events); + const ui = new UI(container); + ui.init_events(computer.events); computer.load_memory(program); - ui.set_step_func(computer.cycle.bind(computer)); + computer.init_events(ui.events); // eslint-disable-next-line @typescript-eslint/no-explicit-any (window).comp = computer; diff --git a/src/instructionSet.ts b/src/instructionSet.ts index 4c44927..dafec60 100644 --- a/src/instructionSet.ts +++ b/src/instructionSet.ts @@ -1,10 +1,10 @@ -import { CpuEvent } from "./events"; -import { u8 } from "./etc"; +import { CpuEvent, CpuEventHandler } from "./events"; +import { format_hex, u8 } from "./etc"; -class ParameterType { - readonly description: string; +export class ParameterType { + readonly desc: string; constructor(description: string) { - this.description = description; + this.desc = description; } } @@ -24,7 +24,7 @@ interface GenericComputer { interface AfterExecutionComputerAction { // Does not step forward the program counter noStep: () => void; - event: (e: CpuEvent, data: unknown) => void; + dispatch: CpuEventHandler["dispatch"]; } export interface Instruction { @@ -43,7 +43,7 @@ export class InstructionSet { insertInstruction(hexCode: u8, instruction: Instruction): void { if (this.instructions.has(hexCode)) { - throw new Error(`Instruction "${hexCode.toString(16)}" already exists`); + throw new Error(`Instruction "${format_hex(hexCode)}" already exists`); } this.instructions.set(hexCode, instruction); } @@ -75,17 +75,17 @@ ISA.insertInstruction(0x10, { ISA.insertInstruction(0x20, { name: "LoadToRegister", - desc: "Sets the byte in register (P1) to be the contents of memory cell (P2)", - params: [new RegisParam(""), new MemorParam("")], + 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")], execute(c, p) { const [register_no, mem_address] = p; - c.setRegister(register_no, c.getMemory(mem_address)); + c.setRegister(register_no, c.getMemory(c.getRegister(mem_address))); }, }); ISA.insertInstruction(0x21, { name: "SaveToMemory", - desc: "Writes the byte in register (P1) to the processing memory location (P2)", + desc: "Writes the byte in register (P1) to the memory cell (P2)", params: [new RegisParam(""), new MemorParam("")], execute(c, p) { const [register_no, mem_address] = p; @@ -96,7 +96,7 @@ ISA.insertInstruction(0x21, { ISA.insertInstruction(0x2f, { name: "AssignRegister", desc: "Assigns constant value (P2) to register (P1)", - params: [new RegisParam(""), new ConstParam("")], + params: [new RegisParam("Set this register"), new ConstParam("to this constant")], execute(c, p) { const [register_no, value] = p; c.setRegister(register_no, value); @@ -104,9 +104,9 @@ ISA.insertInstruction(0x2f, { }); ISA.insertInstruction(0x11, { - name: "GotoIfLowBit", + 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("new instruction counter location"), new RegisParam("Register to check")], + 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 (c.getRegister(check_register_no) % 2 === 1) { @@ -150,24 +150,28 @@ ISA.insertInstruction(0x40, { ISA.insertInstruction(0x50, { name: "Equals", - desc: "If byte in register (P1) equals byte in register (P2), set byte in register (P3) to 0x01", - params: [new RegisParam(""), new RegisParam(""), new RegisParam("")], + 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; - const truth = c.getRegister(register_no_1) === c.getRegister(register_no_2) ? 0x01 : 0x00; - c.setRegister(register_no_3, truth); + 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("")], + params: [new RegisParam("Register to print from")], execute(c, p, a) { const register_no = p[0]; const asciiByte = c.getRegister(register_no); const char = String.fromCharCode(asciiByte); - a.event(CpuEvent.Print, { data: char }); + a.dispatch(CpuEvent.Print, char); }, }); diff --git a/src/isaGenerator.ts b/src/isaGenerator.ts index 0538ac6..2b4a088 100644 --- a/src/isaGenerator.ts +++ b/src/isaGenerator.ts @@ -1,4 +1,4 @@ -import { u8 } from "./etc"; +import { format_hex, u8 } from "./etc"; import { Instruction, InstructionSet } from "./instructionSet"; export function generate_isa(iset: InstructionSet): string { @@ -13,7 +13,7 @@ export function generate_isa(iset: InstructionSet): string { max_instr_name_len = Math.max(max_instr_name_len, short_description.length); } for (const instruction of instructions) { - const hex_code = instruction[0].toString(16).toUpperCase().padStart(2, "0"); + 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 description = instruction[1].desc; diff --git a/src/ui.ts b/src/ui.ts index 4d6b844..ef7ad6e 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -1,24 +1,33 @@ import { ComputerState } from "./computer"; -import { CpuEvent, MemoryCellType } from "./events"; -import { $, el, u8 } from "./etc"; +import { CpuEvent, CpuEventHandler, MemoryCellType, UiEvent, UiEventHandler } from "./events"; +import { $, el, format_hex, u8 } from "./etc"; +import { Instruction, ParameterType } from "./instructionSet"; import { EventHandler } from "./eventHandler"; export class UI { container: HTMLElement; program_memory: HTMLElement; - program_memory_cells: Array; + program_memory_cells: Array = []; registers: HTMLElement; - register_cells: Array; - step_func: null | (() => void); - - program_counter: u8; + printout: HTMLElement; + instruction_explainer: HTMLElement; + register_cells: Array = []; + instruction_parsing_addresses: Array = []; + program_counter: u8 = 0; auto_running: boolean; - constructor(parent: HTMLElement, cpu_events: EventHandler) { + + events: UiEventHandler = new EventHandler() as UiEventHandler; + + constructor(parent: HTMLElement) { + for (const [, e_type] of Object.entries(UiEvent)) { + this.events.register_event(e_type as UiEvent); + } + this.events.seal(); this.container = parent; - this.program_counter = 0; + this.printout = $("printout"); + this.instruction_explainer = $("instruction_explainer"); const program_mem = $("memory"); - this.program_memory_cells = []; for (let i = 0; i < 256; i++) { const mem_cell = el("div", `p_${i}`); mem_cell.textContent = "00"; @@ -28,10 +37,9 @@ export class UI { this.program_memory_cells[0].classList.add("div", "program_counter"); this.program_memory = program_mem; - this.register_cells = []; const registers = $("registers"); for (let i = 0; i < 8; i++) { - const reg_cell = el("div", `R_${i}`); + const reg_cell = el("div", `r_${i}`); reg_cell.textContent = "00"; registers.appendChild(reg_cell); this.register_cells.push(reg_cell); @@ -39,7 +47,6 @@ export class UI { this.registers = registers; - this.step_func = null; this.auto_running = false; const pp_button = $("pause_play_button"); if (pp_button === null) { @@ -54,48 +61,32 @@ export class UI { pp_button.textContent = "Storp"; } }); - document.getElementById("step_button")?.addEventListener("click", () => { + $("step_button")?.addEventListener("click", () => { if (this.auto_running) { this.stop_auto(); } - if (this.step_func === null) { - return; - } - this.step_func(); - }); - cpu_events.add_listener(CpuEvent.MemoryChanged, (e) => { - const { address, value } = e as { address: u8; value: u8 }; - this.program_memory_cells[address].textContent = value.toString(16).toUpperCase().padStart(2, "0"); + this.events.dispatch(UiEvent.RequestCpuCycle, null); }); - cpu_events.add_listener(CpuEvent.RegisterChanged, (e) => { - const { register_no, value } = e as { register_no: u8; value: u8 }; - this.register_cells[register_no].textContent = value.toString(16).toUpperCase().padStart(2, "0"); + } + + init_events(cpu_events: CpuEventHandler): void { + cpu_events.listen(CpuEvent.MemoryChanged, ({ address, value }) => { + this.program_memory_cells[address].textContent = format_hex(value); }); - cpu_events.add_listener(CpuEvent.ProgramCounterChanged, (e) => { - const { counter } = e as { counter: u8 }; + cpu_events.listen(CpuEvent.RegisterChanged, ({ register_no, value }) => { + this.register_cells[register_no].textContent = format_hex(value); + }); + cpu_events.listen(CpuEvent.ProgramCounterChanged, ({ counter }) => { this.program_memory_cells[this.program_counter].classList.remove("program_counter"); this.program_memory_cells[counter].classList.add("program_counter"); this.program_counter = counter; }); - cpu_events.add_listener(CpuEvent.Print, (e) => { - const { data } = e as { data: u8 }; - const printout = $("printout"); - if (printout === null) { - throw new Error("Couldn't get printout"); - } - printout.textContent = (printout.textContent ?? "") + data; + cpu_events.listen(CpuEvent.Print, (char) => { + this.printout.textContent = (this.printout.textContent ?? "") + char; }); - cpu_events.add_listener(CpuEvent.Reset, () => { - this.stop_auto(); - this.program_memory_cells.forEach((c) => { - c.className = ""; - c.textContent = "00"; - }); - this.register_cells.forEach((r) => { - r.textContent = "00"; - }); - this.program_counter = 0; + cpu_events.listen(CpuEvent.Reset, () => { + this.reset(); }); const map: Map = new Map(); @@ -105,8 +96,8 @@ export class UI { map.set(MemoryCellType.InvalidInstruction, "invalid_instruction"); map.set(MemoryCellType.Memory, "memory"); map.set(MemoryCellType.Register, "register"); - cpu_events.add_listener(CpuEvent.MemoryByteParsed, (e) => { - const { type, pos } = e as { type: MemoryCellType; pos: u8 }; + cpu_events.listen(CpuEvent.MemoryByteParsed, (e) => { + const { type, pos, code } = e; const css_class = map.get(type); if (css_class === undefined) { throw new Error("Something went wrong"); @@ -115,27 +106,74 @@ export class UI { if (other_class === css_class) continue; this.program_memory_cells[pos].classList.remove(other_class); } + if (type === MemoryCellType.Instruction) { + while (this.instruction_parsing_addresses.length > 0) { + const num = this.instruction_parsing_addresses.pop(); + if (num === undefined) { + throw new Error("Shouldn't happen"); + } + this.program_memory_cells[num].classList.remove("instruction_argument"); + this.program_memory_cells[num].classList.remove("current_instruction"); + } + this.instruction_explainer.innerHTML = ""; + const { instr } = e as { instr: Instruction }; + this.program_memory_cells[pos].classList.add("current_instruction"); + this.instruction_parsing_addresses.push(pos); + const instr_box = el("div", "expl_box"); + const instr_icon = el("span", "expl_icon"); + instr_icon.classList.add(css_class); + instr_icon.setAttribute("title", css_class.toUpperCase()); + instr_icon.textContent = format_hex(code); + const instr_box_text = el("span", "expl_text"); + instr_box_text.textContent = `${instr.name}`; + instr_box.appendChild(instr_icon); + instr_box.appendChild(instr_box_text); + this.instruction_explainer.appendChild(instr_box); + } else if (type !== MemoryCellType.InvalidInstruction) { + const { param } = e as { param: ParameterType }; + this.program_memory_cells[pos].classList.add("instruction_argument"); + this.instruction_parsing_addresses.push(pos); + const instr_box = el("div", "expl_box"); + const instr_icon = el("span", "expl_icon"); + instr_icon.classList.add(css_class); + instr_icon.setAttribute("title", css_class.toUpperCase()); + instr_icon.textContent = format_hex(code); + const instr_box_text = el("span", "expl_text"); + instr_box_text.textContent = `${param.desc}`; + instr_box.appendChild(instr_icon); + instr_box.appendChild(instr_box_text); + this.instruction_explainer.appendChild(instr_box); + } this.program_memory_cells[pos].classList.add(css_class); }); + + cpu_events.listen(CpuEvent.InstructionExecuted, ({ instr }) => {}); + } + + reset(): void { + this.stop_auto(); + this.program_memory_cells.forEach((c) => { + c.className = ""; + c.textContent = "00"; + }); + this.register_cells.forEach((r) => { + r.textContent = "00"; + }); + this.program_counter = 0; + this.program_memory_cells[0].classList.add("program_counter"); + this.printout.textContent = ""; } start_auto(speed: number = 200): void { - if (this.step_func === null) { - return; - } if (this.auto_running) { return; } this.auto_running = true; const loop = (): void => { - if (this.step_func === null) { - this.auto_running = false; - return; - } if (this.auto_running === false) { return; } - this.step_func(); + this.events.dispatch(UiEvent.RequestCpuCycle, null); setTimeout(loop, speed); // requestAnimationFrame(loop); }; @@ -146,10 +184,6 @@ export class UI { this.auto_running = false; } - set_step_func(f: () => void): void { - this.step_func = f; - } - state_update_event(state: ComputerState): void { const current_instr = state.current_instruction; if (current_instr !== null) {