From e827ac11b5ee40bf07ab1805a044866a8485ca98 Mon Sep 17 00:00:00 2001 From: Alexander Bass Date: Sat, 24 Feb 2024 20:33:54 -0500 Subject: [PATCH] commit before ISA revamp --- TODO | 7 +++-- src/computer.ts | 61 +++++++++++++++++++++++------------------ src/etc.ts | 2 ++ src/events.ts | 10 ++++--- src/index.ts | 25 ++++++++++++----- src/instructionSet.ts | 25 ++++++++++++++--- src/num.ts | 7 +++++ src/style.scss | 49 ++++++++++++++++++++++++++++++++- src/ui/bankIndicator.ts | 22 +++++++++++++++ src/ui/memoryView.ts | 3 +- src/ui/screen.ts | 7 ----- 11 files changed, 164 insertions(+), 54 deletions(-) create mode 100644 src/ui/bankIndicator.ts diff --git a/TODO b/TODO index e021ef9..19a35ee 100644 --- a/TODO +++ b/TODO @@ -5,9 +5,10 @@ Move start/stop/auto logic into computer Speed control slider behavior Speed control slider styling -Determine how to implement 16 bit integers in code (new instructions?) - UI for screen (toggling (click an icon?)) UI for togging other UI elements -Instruction assign memory based on register +UI for showing which Memory bank is selected +VRAM select instruction + +Error log diff --git a/src/computer.ts b/src/computer.ts index 34e8826..aeb3572 100644 --- a/src/computer.ts +++ b/src/computer.ts @@ -1,7 +1,7 @@ import { CpuEvent, CpuEventHandler, UiEvent, UiEventHandler } from "./events"; import { byte_array_to_js_source, format_hex } from "./etc"; import { Instruction, ISA } from "./instructionSet"; -import { m256, u1, u3, u8 } from "./num"; +import { m256, u1, u2, u3, u8 } from "./num"; export type TempInstrState = { pos: u8; @@ -10,13 +10,21 @@ export type TempInstrState = { params: Array; }; +function init_banks(): [Uint8Array, Uint8Array, Uint8Array, Uint8Array] { + const banks = []; + for (let i = 0; i < 4; i++) { + banks.push(new Uint8Array(256)); + } + return banks as [Uint8Array, Uint8Array, Uint8Array, Uint8Array]; +} + export class Computer { - private memory: Uint8Array = new Uint8Array(256); - private vram: Uint8Array = new Uint8Array(256); + private banks: [Uint8Array, Uint8Array, Uint8Array, Uint8Array] = init_banks(); private registers: Uint8Array = new Uint8Array(8); private call_stack: Array = []; + private carry_flag: boolean = false; private program_counter: u8 = 0; - private bank: u1 = 0; + private bank: u2 = 0; private current_instr: TempInstrState | null = null; events: CpuEventHandler = new CpuEventHandler(); @@ -94,34 +102,22 @@ export class Computer { } this.events.dispatch(CpuEvent.Cycle); } - private getMemorySilent(address: u8, bank_override?: u1): u8 { - const banks = [this.memory, this.vram]; - - const bank = banks[bank_override ?? this.bank]; + private getMemorySilent(address: u8, bank_override?: u2): u8 { + const bank = this.banks[bank_override ?? this.bank]; const value = bank[address] as u8; return value; } - getMemory(address: u8, bank_override?: u1): u8 { + getMemory(address: u8, bank_override?: u2): u8 { const value = this.getMemorySilent(address, bank_override); this.events.dispatch(CpuEvent.MemoryAccessed, { address, bank: this.bank, value }); + return value; } setMemory(address: u8, value: u8): void { - let bank: Uint8Array | undefined; - if (this.bank === 0) { - bank = this.memory; - } else if (this.bank === 1) { - bank = this.vram; - } else { - const _: never = this.bank; - } - if (bank === undefined) { - throw new Error("unreachable"); - } - bank[address] = value; + this.banks[this.bank][address] = value; this.events.dispatch(CpuEvent.MemoryChanged, { address, bank: this.bank, value }); } @@ -153,18 +149,28 @@ export class Computer { return this.call_stack.pop() ?? null; } - setBank(bank_no: u1): void { + setBank(bank_no: u2): void { this.events.dispatch(CpuEvent.SwitchBank, { bank: bank_no }); this.bank = bank_no; } + setCarry(state: boolean): void { + this.carry_flag = state; + this.events.dispatch(CpuEvent.SetFlagCarry, true); + } + + getCarry(): boolean { + return this.carry_flag; + } + reset(): void { this.events.dispatch(CpuEvent.Reset); - this.memory = new Uint8Array(256); + this.banks = init_banks(); this.registers = new Uint8Array(8); this.call_stack = []; this.current_instr = null; this.program_counter = 0; + this.carry_flag = false; } init_events(ui: UiEventHandler): void { @@ -175,20 +181,21 @@ export class Computer { } load_memory(program: Array): void { + // TODO allow loading into other banks console.log(byte_array_to_js_source(program)); const max_loop: u8 = Math.min(255, program.length) as u8; - for (let i: u8 = 0; i < 255; i++) { + for (let i: u8 = 0; i < 256; i++) { // Don't fire event if no change is made - if (this.memory[i] === program[i]) continue; + if (this.banks[0][i] === program[i]) continue; - this.memory[i] = program[i]; + this.banks[0][i] = program[i]; this.events.dispatch(CpuEvent.MemoryChanged, { address: i as u8, bank: 0, value: program[i] }); } this.program_counter = 0; } dump_memory(): Uint8Array { - return this.memory; + return this.banks[0]; } private step_forward(): void { diff --git a/src/etc.ts b/src/etc.ts index 4e27ac5..d8fd97f 100644 --- a/src/etc.ts +++ b/src/etc.ts @@ -42,3 +42,5 @@ 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"; diff --git a/src/events.ts b/src/events.ts index 4858a90..64c838b 100644 --- a/src/events.ts +++ b/src/events.ts @@ -5,7 +5,7 @@ */ import { EventHandler } from "./eventHandler"; import { Instruction, ParameterType } from "./instructionSet"; -import { u1, u3, u8 } from "./num"; +import { u1, u2, u3, u8 } from "./num"; // // CPU Event Handler Definition @@ -26,6 +26,7 @@ export enum CpuEvent { // ClockStopped, MemoryAccessed, SwitchBank, + SetFlagCarry, } type VoidDataCpuEventList = CpuEvent.Halt | CpuEvent.Reset | CpuEvent.Cycle; @@ -33,16 +34,17 @@ type VoidDataCpuEventList = CpuEvent.Halt | CpuEvent.Reset | CpuEvent.Cycle; // | CpuEvent.ClockStopped; interface CpuEventMap { - [CpuEvent.MemoryChanged]: { address: u8; bank: u1; value: u8 }; - [CpuEvent.MemoryAccessed]: { address: u8; bank: u1; value: u8 }; + [CpuEvent.MemoryChanged]: { address: u8; bank: u2; value: u8 }; + [CpuEvent.MemoryAccessed]: { address: u8; bank: u2; value: u8 }; [CpuEvent.RegisterChanged]: { register_no: u3; value: u8 }; [CpuEvent.ProgramCounterChanged]: { counter: u8 }; [CpuEvent.InstructionParsed]: { pos: u8; code: u8; instr: Instruction }; [CpuEvent.ParameterParsed]: { pos: u8; code: u8; param: ParameterType }; [CpuEvent.InvalidParsed]: { pos: u8; code: u8 }; [CpuEvent.InstructionExecuted]: { instr: Instruction }; - [CpuEvent.SwitchBank]: { bank: u1 }; + [CpuEvent.SwitchBank]: { bank: u2 }; [CpuEvent.Print]: string; + [CpuEvent.SetFlagCarry]: boolean; } export interface CpuEventHandler extends EventHandler { diff --git a/src/index.ts b/src/index.ts index 4b4ce28..8ea7f35 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,6 +17,7 @@ declare global { interface Window { comp: Computer; ui: UI; + firehose: () => void; } } @@ -78,7 +79,6 @@ function main(): void { ui.init_events(computer.events); computer.load_memory(program); computer.init_events(ui.events); - window.comp = computer; window.ui = ui; @@ -112,10 +112,17 @@ function main(): void { }); reader.readAsArrayBuffer(file); }); - - // computer.events.firehose((ident, data) => { - // console.log(`New Event: ${CpuEvent[ident]}. data: `, data); - // }); + let fire = false; + window.firehose = (): void => { + if (fire === false) { + computer.events.firehose((ident, data) => { + console.log(`New Event: ${CpuEvent[ident]}. data: `, data); + }); + fire = true; + } else { + console.error("Firehose already started"); + } + }; $("save_button").addEventListener("click", () => { const memory = computer.dump_memory(); @@ -125,11 +132,15 @@ function main(): void { const link = document.createElement("a"); link.href = url; link.download = "bin.bin"; - link.style.display = "none"; link.click(); }); } document.addEventListener("DOMContentLoaded", () => { - main(); + // at least you know it's bad + try { + main(); + } catch (e) { + alert(e); + } }); diff --git a/src/instructionSet.ts b/src/instructionSet.ts index c95e6be..84e890e 100644 --- a/src/instructionSet.ts +++ b/src/instructionSet.ts @@ -5,7 +5,7 @@ */ import { CpuEvent, CpuEventHandler } from "./events"; import { format_hex } from "./etc"; -import { isU3, m256, u1, u3, u8 } from "./num"; +import { isU2, isU3, m256, u2, u3, u8 } from "./num"; export enum ParamType { Const, @@ -47,7 +47,9 @@ interface GenericComputer { setRegister: (number: u3, value: u8) => void; pushCallStack: (address: u8) => boolean; popCallStack: () => u8 | null; - setBank: (bank_no: u1) => void; + setBank: (bank_no: u2) => void; + getCarry(): boolean; + setCarry(state: boolean): void; } interface AfterExecutionComputerAction { @@ -112,7 +114,7 @@ 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 held in this memory address")], + 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"); @@ -200,6 +202,21 @@ ISA.insertInstruction(0x40, { 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", @@ -321,7 +338,7 @@ ISA.insertInstruction(0xb1, { params: [new ConstParam("Bank number")], execute(c, p) { const bank_no = p[0]; - if (!(bank_no === 1 || bank_no === 0)) { + if (!isU2(bank_no)) { throw new Error("TODO"); } c.setBank(bank_no); diff --git a/src/num.ts b/src/num.ts index e9c6083..fa348ca 100644 --- a/src/num.ts +++ b/src/num.ts @@ -30,6 +30,13 @@ export type u4 = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 export const m256 = (number: number): u8 => (number % 256) as u8; +export function isU2(n: number): n is u2 { + if (n < 4 && n >= 0) { + return true; + } + return false; +} + /** * Determines whether a number is a u3 type (unsigned 3-bit integer). * Does not check for non integers diff --git a/src/style.scss b/src/style.scss index d4301c3..35f8077 100644 --- a/src/style.scss +++ b/src/style.scss @@ -52,7 +52,7 @@ body { "title . . . ." "title . . . printout " "title . . . printout " - ". buttons buttons buttons buttons"; + ". buttons buttons . ."; #memory { grid-column: 2/4; grid-row: 2/6; @@ -198,6 +198,53 @@ label.button:hover { #controls_bar { grid-area: buttons; + display: flex; gap: 10px; } + +input[type="range"] { + background-color: transparent; + -webkit-appearance: none; + appearance: none; + margin: 18px 0; + // width: 100%; +} +input[type="range"]:focus { + outline: none; +} +// 2024 and we still have to do this +input[type="range"]::-webkit-slider-runnable-track { + height: 0.5em; + cursor: pointer; + background: yellow; + border-radius: 0px; +} +input[type="range"]::-webkit-slider-thumb { + border: 4px solid yellow; + background-color: black; + height: 42px; + width: 20px; + border-radius: 0px; + cursor: pointer; + margin-top: -18px; + -webkit-appearance: none; +} + +input[type="range"]:focus::-webkit-slider-runnable-track { + background: yellow; +} +input[type="range"]::-moz-range-track { + height: 0.5em; + cursor: pointer; + background: yellow; + border-radius: 0px; +} +input[type="range"]::-moz-range-thumb { + border: 4px solid yellow; + background-color: black; + height: 36px; + width: 16px; + border-radius: 0px; + cursor: pointer; +} diff --git a/src/ui/bankIndicator.ts b/src/ui/bankIndicator.ts new file mode 100644 index 0000000..19f429e --- /dev/null +++ b/src/ui/bankIndicator.ts @@ -0,0 +1,22 @@ +import { UiEventHandler, CpuEventHandler, CpuEvent } from "../events"; +import { u1, u2 } from "../num"; +import { UiComponent } from "./uiComponent"; + +class BankIndicator implements UiComponent { + element: HTMLElement; + events: UiEventHandler; + constructor(element: HTMLElement, events: UiEventHandler) { + this.element = element; + this.events = events; + } + + reset(): void {} + + select_bank(bank_no: u2): void {} + + init_cpu_events(c: CpuEventHandler): void { + c.listen(CpuEvent.SwitchBank, ({ bank }) => { + this.select_bank(bank); + }); + } +} diff --git a/src/ui/memoryView.ts b/src/ui/memoryView.ts index 0ed57dc..e08dc71 100644 --- a/src/ui/memoryView.ts +++ b/src/ui/memoryView.ts @@ -31,7 +31,8 @@ export class MemoryView extends CelledViewer implements UiComponent { } init_cpu_events(c: CpuEventHandler): void { - c.listen(CpuEvent.MemoryAccessed, ({ address, value }) => { + c.listen(CpuEvent.MemoryAccessed, ({ address, bank, value }) => { + if (bank !== 0) return; if (this.last_accessed_cell !== address) { if (this.last_accessed_cell !== null) { this.remove_cell_class(this.last_accessed_cell, "last_access"); diff --git a/src/ui/screen.ts b/src/ui/screen.ts index 7e3e8d7..67d96b5 100644 --- a/src/ui/screen.ts +++ b/src/ui/screen.ts @@ -43,12 +43,6 @@ export class Screen implements UiComponent { setPixel(x: u4, y: u4, value: u8): void { const point: [number, number] = [x * this.scale[0], y * this.scale[1]]; - // const RED_SCALE = 255 / 2 ** 2; - // const GREEN_SCALE = 255 / 2 ** 2; - // const BLUE_SCALE = 255 / 2 ** 2; - // const red = ((value >> 4) & 0b11) * RED_SCALE; - // const green = ((value >> 2) & 0b11) * GREEN_SCALE; - // const blue = (value & 0b11) * BLUE_SCALE; const RED_SCALE = 255 / 2 ** 3; const GREEN_SCALE = 255 / 2 ** 3; @@ -58,7 +52,6 @@ export class Screen implements UiComponent { const blue = (value & 0b11) * BLUE_SCALE; const color = `rgb(${red},${green},${blue})`; - console.log(x, y, value, color); this.ctx.fillStyle = color; this.ctx.fillRect(...point, ...this.scale); }