From f850b218699a323748c686cbb7e811a0ec63c5c5 Mon Sep 17 00:00:00 2001 From: Alexander Bass Date: Wed, 15 May 2024 18:26:39 -0400 Subject: [PATCH] final commit before major rework --- .eslintrc.json | 2 +- src/computer.ts | 85 ++++++++++++------ src/events.ts | 18 ++-- src/include/index.html | 55 ++++++++---- src/index.ts | 4 +- src/instructionSet.ts | 119 +++++++++++++++---------- src/isaGenerator.ts | 61 ++++++++----- src/style/article.scss | 25 ++++++ src/style/style.scss | 42 +++++---- src/style/table.scss | 34 +++++++ src/style/top_meta.scss | 55 ++++++++++++ src/ui.ts | 8 +- src/ui/components/memoryView.ts | 23 +++-- src/ui/components/pausePlay.ts | 10 ++- src/ui/components/resetButton.ts | 18 ++++ src/ui/components/resetButtons.ts | 31 ------- src/ui/components/saveLoad.ts | 2 +- src/ui/components/trashButton.ts | 23 +++++ src/ui/windows/instructionExplainer.ts | 40 +++++---- 19 files changed, 452 insertions(+), 203 deletions(-) create mode 100644 src/style/article.scss create mode 100644 src/style/table.scss create mode 100644 src/style/top_meta.scss create mode 100644 src/ui/components/resetButton.ts delete mode 100644 src/ui/components/resetButtons.ts create mode 100644 src/ui/components/trashButton.ts diff --git a/.eslintrc.json b/.eslintrc.json index ddd250f..77a2e27 100755 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -37,7 +37,7 @@ "quote-props" : ["warn","as-needed"], "dot-notation":"warn", "one-var":["warn","never"], - "no-use-before-define":"warn", + "no-use-before-define":"off", "no-multi-assign":"warn", "no-else-return":"warn", "spaced-comment":"warn", diff --git a/src/computer.ts b/src/computer.ts index a65dc95..213e9dc 100644 --- a/src/computer.ts +++ b/src/computer.ts @@ -4,11 +4,18 @@ import { Instruction, ISA } from "./instructionSet"; import { m256, u2, u3, u8 } from "./num"; import { DEFAULT_VRAM_BANK } from "./constants"; +interface ParsedParameter { + pos: u8; + code: u8; + valid: boolean; +} + export type TempInstrState = { pos: u8; params_found: number; instr: Instruction; - params: Array; + valid: boolean; + params: Array; }; function initBanks(): [Uint8Array, Uint8Array, Uint8Array, Uint8Array] { @@ -22,7 +29,7 @@ function initBanks(): [Uint8Array, Uint8Array, Uint8Array, Uint8Array] { export default class Computer { private banks: [Uint8Array, Uint8Array, Uint8Array, Uint8Array] = initBanks(); private registers: Uint8Array = new Uint8Array(8); - private call_stack: Array = []; + // private call_stack: Array = []; private carry_flag: boolean = false; private program_counter: u8 = 0; private bank: u2 = 0; @@ -36,7 +43,7 @@ export default class Computer { if (this.current_instr === null) { const parsed_instruction = ISA.getInstruction(current_byte); if (parsed_instruction === null) { - this.events.dispatch(CpuEvent.InvalidParsed, { + this.events.dispatch(CpuEvent.InvalidInstructionParsed, { pos: this.program_counter, code: current_byte, }); @@ -45,35 +52,55 @@ export default class Computer { this.events.dispatch(CpuEvent.Cycle); return; } + this.current_instr = { pos: this.program_counter, instr: parsed_instruction, params_found: 0, - params: new Array(parsed_instruction.params.length), + valid: true, + params: new Array(parsed_instruction.params.length), }; - this.events.dispatch(CpuEvent.InstructionParsed, { + + this.events.dispatch(CpuEvent.InstructionParseBegin, { pos: this.program_counter, instr: parsed_instruction, code: current_byte, }); - } - - if (this.current_instr.pos === this.program_counter && this.current_instr.params.length > 0) { this.stepForward(); this.events.dispatch(CpuEvent.Cycle); return; } + // if (this.current_instr.pos === this.program_counter && this.current_instr.params.length > 0) { + // this.stepForward(); + // this.events.dispatch(CpuEvent.Cycle); + // return; + // } + if (this.current_instr.params.length !== this.current_instr.params_found) { const b = this.current_instr.instr.params[this.current_instr.params_found]; - this.events.dispatch(CpuEvent.ParameterParsed, { - param: b, - pos: this.program_counter, - code: current_byte, - }); - this.current_instr.params[this.current_instr.params_found] = current_byte; + const valid = b.validate(current_byte); + if (valid) { + this.events.dispatch(CpuEvent.ParameterParsed, { + param: b, + pos: this.program_counter, + code: current_byte, + }); + } else { + this.events.dispatch(CpuEvent.InvalidParameterParsed, { + param: b, + pos: this.program_counter, + code: current_byte, + }); + this.current_instr.valid = false; + } + + const param = { pos: this.program_counter, code: current_byte, valid }; + + this.current_instr.params[this.current_instr.params_found] = param; this.current_instr.params_found += 1; + if (this.current_instr.params.length !== this.current_instr.params_found) { this.stepForward(); this.events.dispatch(CpuEvent.Cycle); @@ -87,8 +114,12 @@ export default class Computer { }, 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 }); + if (this.current_instr.valid) { + const params: Array = this.current_instr.params.map((p) => p.code); + this.current_instr.instr.execute(this, params, execution_post_action_state); + this.events.dispatch(CpuEvent.InstructionExecuted, { instr: this.current_instr.instr }); + } + this.events.dispatch(CpuEvent.InstructionParseEnd); this.current_instr = null; if (execution_post_action_state.should_step) { @@ -134,15 +165,15 @@ export default class Computer { this.program_counter = new_value; } - pushCallStack(address: u8): boolean { - if (this.call_stack.length >= 8) return false; - this.call_stack.push(address); - return true; - } + // pushCallStack(address: u8): boolean { + // if (this.call_stack.length >= 8) return false; + // this.call_stack.push(address); + // return true; + // } - popCallStack(): u8 | null { - return this.call_stack.pop() ?? null; - } + // popCallStack(): u8 | null { + // return this.call_stack.pop() ?? null; + // } setBank(bank: u2): void { this.events.dispatch(CpuEvent.SwitchBank, { bank: bank }); @@ -170,17 +201,17 @@ export default class Computer { ui.listen(UiCpuSignal.RequestMemoryChange, ({ address, bank, value }) => this.setMemory(address, value, bank)); ui.listen(UiCpuSignal.RequestRegisterChange, ({ register_no, value }) => this.setRegister(register_no, value)); ui.listen(UiCpuSignal.RequestMemoryDump, (callback) => callback(this.dumpMemory())); - ui.listen(UiCpuSignal.RequestCpuReset, () => this.reset()); ui.listen(UiCpuSignal.RequestProgramCounterChange, ({ address }) => { this.setProgramCounter(address); }); + ui.listen(UiCpuSignal.RequestCpuReset, () => this.reset()); ui.listen(UiCpuSignal.RequestCpuSoftReset, () => this.softReset()); } softReset(): void { this.events.dispatch(CpuEvent.SoftReset); for (let i = 0; i < 8; i++) this.setRegister(i as u3, 0); - while (this.popCallStack() !== null) 0; + // while (this.popCallStack() !== null) 0; this.setVramBank(DEFAULT_VRAM_BANK); this.setCarry(false); this.current_instr = null; @@ -193,7 +224,7 @@ export default class Computer { this.banks = initBanks(); // Soft reset for (let i = 0; i < 8; i++) this.setRegister(i as u3, 0); - while (this.popCallStack() !== null) 0; + // while (this.popCallStack() !== null) 0; this.setVramBank(DEFAULT_VRAM_BANK); this.setCarry(false); this.current_instr = null; diff --git a/src/events.ts b/src/events.ts index e57fe90..c9f49a8 100644 --- a/src/events.ts +++ b/src/events.ts @@ -14,9 +14,11 @@ export enum CpuEvent { MemoryChanged, RegisterChanged, ProgramCounterChanged, - InstructionParsed, + InstructionParseBegin, + InstructionParseEnd, ParameterParsed, - InvalidParsed, + InvalidParameterParsed, + InvalidInstructionParsed, InstructionExecuted, Cycle, Print, @@ -29,16 +31,22 @@ export enum CpuEvent { SetVramBank, } -type VoidDataCpuEventList = CpuEvent.Halt | CpuEvent.Reset | CpuEvent.SoftReset | CpuEvent.Cycle; +type VoidDataCpuEventList = + | CpuEvent.Halt + | CpuEvent.Reset + | CpuEvent.SoftReset + | CpuEvent.Cycle + | CpuEvent.InstructionParseEnd; interface CpuEventMap { [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.InstructionParseBegin]: { pos: u8; code: u8; instr: Instruction }; [CpuEvent.ParameterParsed]: { pos: u8; code: u8; param: ParameterType }; - [CpuEvent.InvalidParsed]: { pos: u8; code: u8 }; + [CpuEvent.InvalidParameterParsed]: { pos: u8; code: u8; param: ParameterType }; + [CpuEvent.InvalidInstructionParsed]: { pos: u8; code: u8 }; [CpuEvent.InstructionExecuted]: { instr: Instruction }; [CpuEvent.SwitchBank]: { bank: u2 }; [CpuEvent.SetVramBank]: { bank: u2 }; diff --git a/src/include/index.html b/src/include/index.html index 5ce2782..f0f2ae7 100644 --- a/src/include/index.html +++ b/src/include/index.html @@ -11,24 +11,33 @@ This computer requires JavaScript. Your browser either doesn't support it, or you have it disabled.
-
-
VIRTUAL 8-BIT COMPUTER
-
-
-
↯REGISTERS
-
MEMORY↯
+
+
+
+ +
+
+ + + +
-
+
+
VIRTUAL 8-BIT COMPUTER
+
+ + +
-
- - - +
+ + + +
+
+ +
- -
- -
@@ -63,6 +72,20 @@
-

+		
+
+ Instruction Set +

+			
+ +

Virtual 8-Bit Computer

+

+ Above is a virtual virtual virtual Lorem ipsum dolor sit amet consectetur adipisicing elit. Provident architecto + voluptas repudiandae eligendi mollitia necessitatibus tempore autem ea pariatur ex quae eum itaque ducimus, + velit nam voluptatem deserunt ad nesciunt. +

+

How it works

+

+
diff --git a/src/index.ts b/src/index.ts index 4e1b073..64341e0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,7 +7,7 @@ import Computer from "./computer"; import UI from "./ui"; import { $ } from "./etc"; import { ISA } from "./instructionSet"; -import { generateIsa } from "./isaGenerator"; +import { generateIsaTable } from "./isaGenerator"; import { u8 } from "./num"; import "./style/style.scss"; @@ -49,7 +49,7 @@ function main(): void { // Todo, move to ui component // or move to documentation - $("ISA").textContent = generateIsa(ISA); + $("ISA").replaceWith(generateIsaTable(ISA)); let fire = false; window.firehose = (): void => { diff --git a/src/instructionSet.ts b/src/instructionSet.ts index fbea382..c5b7bcc 100644 --- a/src/instructionSet.ts +++ b/src/instructionSet.ts @@ -10,7 +10,10 @@ import { isU2, isU3, m256, u2, u3, u8 } from "./num"; export enum ParamType { Const, Register, - Memory, + ConstMemory, + Bank, + // A register which holds a memory address + RegisterAddress, } export abstract class ParameterType { @@ -20,6 +23,9 @@ export abstract class ParameterType { this.desc = description; this.type = p_type; } + validate(n: number): boolean { + return true; + } } class ConstParam extends ParameterType { @@ -31,10 +37,28 @@ class RegisParam extends ParameterType { constructor(d: string) { super(d, ParamType.Register); } + validate(n: number): boolean { + return isU3(n); + } } -class MemorParam extends ParameterType { + +class BankParam extends ParameterType { constructor(d: string) { - super(d, ParamType.Memory); + super(d, ParamType.Bank); + } + validate(n: number): boolean { + return isU2(n); + } +} + +class ConstMemorParam extends ParameterType { + constructor(d: string) { + super(d, ParamType.ConstMemory); + } +} +class RegisAddrParam extends ParameterType { + constructor(d: string) { + super(d, ParamType.RegisterAddress); } } @@ -45,12 +69,13 @@ interface GenericComputer { getProgramCounter: () => u8; getRegister: (number: u3) => u8; setRegister: (number: u3, value: u8) => void; - pushCallStack: (address: u8) => boolean; - popCallStack: () => u8 | null; + // pushCallStack: (address: u8) => boolean; + // popCallStack: () => u8 | null; setBank: (bank_no: u2) => void; getCarry(): boolean; setCarry(state: boolean): void; setVramBank(bank: u2): void; + softReset(): void; } interface AfterExecutionComputerAction { @@ -129,9 +154,9 @@ ISA.addCategory(category(0xf0, 0xff, "IO")); // COPY ISA.insertInstruction(0x10, { - name: "Copy R -> M", + name: "Copy CR -> CA", 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")], + params: [new RegisParam("Write the byte in this register"), new ConstMemorParam("To this memory address")], execute(c, p) { const [register_no, mem_address] = p; if (!isU3(register_no)) throw new Error("TODO"); @@ -140,9 +165,9 @@ ISA.insertInstruction(0x10, { }); ISA.insertInstruction(0x11, { - name: "Copy M -> R", + name: "Copy CA -> R", desc: "Copy the byte in memory address (P1) to the register (P2)", - params: [new MemorParam(""), new RegisParam("")], + params: [new ConstMemorParam(""), new RegisParam("")], execute(c, p) { const [register_no, mem_address] = p; if (!isU3(register_no)) throw new Error("TODO"); @@ -151,9 +176,9 @@ ISA.insertInstruction(0x11, { }); ISA.insertInstruction(0x12, { - name: "Copy M -> M", + name: "Copy CM -> CA", 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")], + params: [new ConstMemorParam("Copy the byte in this memory address"), new ConstMemorParam("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)); @@ -172,10 +197,10 @@ ISA.insertInstruction(0x13, { }, }); ISA.insertInstruction(0x14, { - name: "Load RM -> R", + name: "Load RA -> R", desc: "Copy the byte in memory addressed by register (P1) to register (P2)", params: [ - new RegisParam("Copy the byte in the memory cell addressed in this register"), + new RegisAddrParam("Copy the byte in the memory cell addressed in this register"), new RegisParam("To this register"), ], execute(c, p) { @@ -186,11 +211,11 @@ ISA.insertInstruction(0x14, { }, }); ISA.insertInstruction(0x15, { - name: "Save R -> RM", + name: "Save R -> RA", desc: "Copy the byte in register (P1) to the memory cell addressed in register (P2)", params: [ new RegisParam("Copy the value in this register"), - new RegisParam("To the memory cell addressed in this register"), + new RegisAddrParam("To the memory cell addressed in this register"), ], execute(c, p) { const [register_no_1, register_no_2] = p; @@ -213,7 +238,7 @@ ISA.insertInstruction(0x17, { 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")], + params: [new RegisAddrParam("Set the value in this memory address to 0")], execute(c, p) { const mem_address = p[0]; c.setMemory(mem_address, 0); @@ -234,7 +259,7 @@ ISA.insertInstruction(0x19, { ISA.insertInstruction(0x1f, { name: "Set bank", desc: "Selects which bank of memory to write and read to", - params: [new ConstParam("Bank number")], + params: [new BankParam("Bank number")], execute(c, p) { const bank_no = p[0]; if (!isU2(bank_no)) { @@ -255,9 +280,8 @@ ISA.insertInstruction(0x20, { params: [new RegisParam("new instruction counter location")], execute: (c, p, a) => { const register_no = p[0]; - if (!isU3(register_no)) { - throw new Error("todo"); - } + if (!isU3(register_no)) throw new Error("todo"); + const new_address = c.getRegister(register_no); c.setProgramCounter(new_address); a.noStep(); @@ -333,33 +357,33 @@ ISA.insertInstruction(0x29, { }, }); -ISA.insertInstruction(0x2d, { - name: "Call", - desc: "", - params: [new ConstParam("the subroutine at this memory address")], - execute(c, p, a) { - const current_address = c.getProgramCounter(); - const success = c.pushCallStack(current_address); - // TODO Handle success/failure +// ISA.insertInstruction(0x2d, { +// name: "Call", +// desc: "", +// params: [new ConstParam("the subroutine at this memory address")], +// execute(c, p, a) { +// const current_address = c.getProgramCounter(); +// const success = c.pushCallStack(current_address); +// // TODO Handle success/failure - const new_address = p[0]; - c.setProgramCounter(new_address); +// const new_address = p[0]; +// c.setProgramCounter(new_address); - a.noStep(); - }, -}); +// a.noStep(); +// }, +// }); -ISA.insertInstruction(0x2e, { - name: "Return", - desc: "", - params: [], - execute(c, p, a) { - const new_address = c.popCallStack(); - if (new_address === null) throw new Error("TODO handle this"); - c.setProgramCounter(m256(new_address + 1)); - a.noStep(); - }, -}); +// ISA.insertInstruction(0x2e, { +// name: "Return", +// desc: "", +// params: [], +// execute(c, p, a) { +// const new_address = c.popCallStack(); +// if (new_address === null) throw new Error("TODO handle this"); +// c.setProgramCounter(m256(new_address + 1)); +// a.noStep(); +// }, +// }); ISA.insertInstruction(0x2c, { name: "NoOp", @@ -369,11 +393,12 @@ ISA.insertInstruction(0x2c, { }); ISA.insertInstruction(0x2f, { - name: "Halt and Catch Fire", - desc: "Stops program execu..... Fire! FIRE EVERYWHERE!", + name: "Halt", + desc: "Stops CPU execution and soft resets", params: [], execute(c, p, a) { a.dispatch(CpuEvent.Halt); + c.softReset(); }, }); @@ -743,7 +768,7 @@ ISA.insertInstruction(0xf1, { ISA.insertInstruction(0xff, { name: "Set VRAM Bank", desc: "Set memory bank which screen gets pixels from memory bank (P1)", - params: [new ConstParam("memory bank to select")], + params: [new BankParam("memory bank to select")], execute(c, p, a) { const bank_no = p[0]; if (!isU2(bank_no)) throw new Error("TO2O"); diff --git a/src/isaGenerator.ts b/src/isaGenerator.ts index b24465c..f8cd1ea 100644 --- a/src/isaGenerator.ts +++ b/src/isaGenerator.ts @@ -3,7 +3,7 @@ * @copyright Alexander Bass 2024 * @license GPL-3.0 */ -import { formatHex, inRange } from "./etc"; +import { el, formatHex, inRange } from "./etc"; import { InstrCategory, Instruction, InstructionSet, ParameterType, ParamType } from "./instructionSet"; import { u8 } from "./num"; @@ -13,7 +13,13 @@ function parameterDescription(params: Array): string { str += " "; } for (const p of params) { - const p_map = { [ParamType.Const]: "C", [ParamType.Memory]: "M", [ParamType.Register]: "R" }; + const p_map = { + [ParamType.Const]: "C", + [ParamType.ConstMemory]: "CM", + [ParamType.Register]: "R", + [ParamType.Bank]: "B", + [ParamType.RegisterAddress]: "RA", + }; const char = p_map[p.type]; str += char; str += " "; @@ -21,39 +27,46 @@ function parameterDescription(params: Array): string { return str; } -export function generateIsa(iset: InstructionSet): string { +export function generateIsaTable(iset: InstructionSet): HTMLTableElement { + const table = el("table").fin(); + + const headings = el("tr").fin(); + + headings.appendChild(el("td").tx("Code").fin()); + headings.appendChild(el("td").tx("Parameters").fin()); + headings.appendChild(el("td").tx("Name").fin()); + headings.appendChild(el("td").tx("Description").fin()); + table.appendChild(headings); + const instructions: Array<[u8, Instruction]> = []; for (const kv of iset.instructions.entries()) instructions.push(kv); - let output_string = "INSTRUCTIONS\n"; - - 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) => inRange(instruction[0], i.start, i.end)); + for (const [code, instr] of instructions) { + const cat = iset.category_ranges.find((i) => inRange(code, 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`; + const category_row = el("tr").fin(); + category_row.appendChild(el("td").tx(cat.name.toUpperCase()).at("colspan", "4").cl("category").fin()); + table.appendChild(category_row); current_category = cat; } - const hex_code = formatHex(instruction[0]); + const row = el("tr").fin(); + const instr_text = instr.name; - const short_description = instruction[1].name.padEnd(max_instr_name_len, " "); - const parameters = parameterDescription(instruction[1].params); - const description = instruction[1].desc; - output_string += `0x${hex_code}: ${short_description}`; - if (parameters.length !== 0) { - output_string += ` -${parameters}- `; - } else { - output_string += " - "; - } - output_string += `${description}\n`; + row.appendChild( + el("td") + .tx(`0x${formatHex(code)}`) + .fin() + ); + row.appendChild(el("td").tx(parameterDescription(instr.params)).fin()); + row.appendChild(el("td").tx(instr_text).fin()); + row.appendChild(el("td").tx(instr.desc).fin()); + table.appendChild(row); } - return output_string; + + return table; } diff --git a/src/style/article.scss b/src/style/article.scss new file mode 100644 index 0000000..ba2a34f --- /dev/null +++ b/src/style/article.scss @@ -0,0 +1,25 @@ +h1, +h2, +h3 { + color: white; + font-family: charter; + margin-block: unset; + // margin-bottom: unset; +} + +h2 { + font-size: 2rem; +} + +p { + margin-top: unset; + font-size: 1.4rem; + color: lightgray; +} + +article { + margin-top: 0.5rem; + width: 1000px; + font-family: Helvetica, sans-serif; + margin-inline: auto; +} diff --git a/src/style/style.scss b/src/style/style.scss index 746a51e..fa369f5 100644 --- a/src/style/style.scss +++ b/src/style/style.scss @@ -1,9 +1,16 @@ @use "memory_registers"; @use "hover_text_box"; +@use "article"; @use "windows"; +@use "top_meta"; @use "buttons"; +@use "table"; @use "vars"; +#isa_container summary { + font-size: 1.5rem; +} + * { box-sizing: border-box; } @@ -35,11 +42,11 @@ main { grid-template-columns: min-content min-content min-content; grid-template-rows: min-content min-content min-content; grid-template-areas: - ". regmemlabel . cycles" - ". registers . bank " - "reset memory memory memory" - "title memory memory memory" - ". buttons buttons buttons"; + ". reglabel . ." + ". registers . bank " + "memlabel memory memory memory" + "title memory memory memory" + ". buttons buttons buttons"; #window_box { grid-area: windowbox; @@ -50,8 +57,11 @@ main { #memory_bank_view { grid-area: bank; } - #labelcontainer { - grid-area: regmemlabel; + #reg_label { + grid-area: reglabel; + } + #mem_label { + grid-area: memlabel; } #cycles { grid-area: cycles; @@ -70,9 +80,6 @@ main { #memory { grid-area: memory; } - #reset_buttons { - grid-area: reset; - } } #reset_buttons { @@ -88,15 +95,20 @@ main { } } -#labelcontainer { - column-gap: 18px; +#reg_label, +#mem_label { font-size: 0.75em; - display: flex; - flex-direction: row; - align-items: center; user-select: none; } +#mem_label { + writing-mode: vertical-lr; + text-align: right; + margin-top: 5px; + user-select: none; + transform: scale(-1, -1); +} + #memory_bank_view { display: flex; #bank_boxes { diff --git a/src/style/table.scss b/src/style/table.scss new file mode 100644 index 0000000..b4ab5e8 --- /dev/null +++ b/src/style/table.scss @@ -0,0 +1,34 @@ +table { + border-collapse: collapse; + // border: 2px solid lightgray; + letter-spacing: 1px; + font-size: 0.9rem; + color: lightgray; + width: 100%; +} + +td, +th { + border: 1px solid rgb(190, 190, 190); + padding: 10px 20px; +} + +th { + background-color: rgb(235, 235, 235); +} + +.category { + border-left: 2px solid yellow; +} + +tr:nth-child(even) td { + background-color: rgb(22, 22, 22); +} + +tr:nth-child(odd) td { + background-color: black; +} + +caption { + padding: 10px; +} diff --git a/src/style/top_meta.scss b/src/style/top_meta.scss new file mode 100644 index 0000000..adcfcc2 --- /dev/null +++ b/src/style/top_meta.scss @@ -0,0 +1,55 @@ +input[type="text"] { + background-color: transparent; + outline: none; + border: none; + color: white; + font-family: monospace; + font-size: 20px; +} + +div#program_meta { + align-items: center; + #title_div { + flex: 2; + } + + #btn_div { + align-items: center; + + display: flex; + gap: 10px; + #save_load_buttons { + display: flex; + gap: inherit; + } + } + justify-content: space-between; + + border-bottom: 2px solid yellow; + margin-bottom: 7px; + button, + label.button { + font-size: 28px; + + margin: unset; + padding: unset; + border: unset; + } + + #trash_button:hover { + color: orange; + } + + input[type="text"] { + text-decoration: underline; + margin-block: auto; + padding: 10px; + &:hover { + background-color: darkslategray; + } + &:focus { + background-color: darkslategray; + } + } + display: flex; +} diff --git a/src/ui.ts b/src/ui.ts index 12360b2..f5c818c 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -9,7 +9,8 @@ import BankSelector from "./ui/components/bankViewSelector"; import EditButton from "./ui/components/editButton"; import pausePlay from "./ui/components/pausePlay"; import SaveLoad from "./ui/components/saveLoad"; -import ResetButtons from "./ui/components/resetButtons"; +import ResetButton from "./ui/components/resetButton"; +import TrashButton from "./ui/components/trashButton"; // Window Components import InstructionExplainer from "./ui/windows/instructionExplainer"; import Screen from "./ui/windows/screen"; @@ -23,7 +24,7 @@ export default class UI { constructor() { this.register_component(MemoryView, $("memory")); - this.register_component(frequencyIndicator, $("cycles")); + // this.register_component(frequencyIndicator, $("cycles")); this.register_component(InstructionExplainer, $("instruction_explainer")); this.register_component(RegisterView, $("registers")); this.register_component(Screen, $("tv")); @@ -33,7 +34,8 @@ export default class UI { this.register_component(SaveLoad, $("save_load_buttons")); this.register_component(BankSelector, $("memory_bank_view")); this.register_component(BankVisualizer, $("bank_viz")); - this.register_component(ResetButtons, $("reset_buttons")); + this.register_component(ResetButton, $("reset_button")); + this.register_component(TrashButton, $("trash_button")); } private register_component(ctor: UiComponentConstructor, e: HTMLElement): void { diff --git a/src/ui/components/memoryView.ts b/src/ui/components/memoryView.ts index 3008725..ac3c7cb 100644 --- a/src/ui/components/memoryView.ts +++ b/src/ui/components/memoryView.ts @@ -5,6 +5,14 @@ import UiComponent from "../uiComponent"; import { el } from "../../etc"; import CelledViewer from "../celledViewer"; +const p_map = { + [ParamType.Const]: "constant", + [ParamType.ConstMemory]: "memory", + [ParamType.Register]: "register", + [ParamType.Bank]: "bank", + [ParamType.RegisterAddress]: "regaddr", +}; + /** Only to be run once */ function createBanks( element: HTMLElement, @@ -106,26 +114,17 @@ export default class MemoryView implements UiComponent { this.program.addCellClass(pos, "instruction_argument"); const t = param.type; this.program.removeCellClass(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"); - } + const name = p_map[t]; this.program.addCellClass(pos, name); }); - c.listen(CpuEvent.InstructionParsed, ({ instr, code, pos }) => { + c.listen(CpuEvent.InstructionParseBegin, ({ instr, code, pos }) => { this.program.removeAllCellClass("instruction_argument"); this.program.removeAllCellClass("current_instruction"); this.program.removeCellClass(pos, "constant", "register", "memory", "invalid"); this.program.addCellClass(pos, "current_instruction"); this.program.addCellClass(pos, "instruction"); }); - c.listen(CpuEvent.InvalidParsed, ({ code, pos }) => { + c.listen(CpuEvent.InvalidInstructionParsed, ({ code, pos }) => { this.program.removeCellClass(pos, "constant", "register", "memory", "instruction"); this.program.addCellClass(pos, "invalid"); }); diff --git a/src/ui/components/pausePlay.ts b/src/ui/components/pausePlay.ts index ebc754d..aa4ff76 100644 --- a/src/ui/components/pausePlay.ts +++ b/src/ui/components/pausePlay.ts @@ -42,8 +42,12 @@ export default class pausePlay implements UiComponent { this.events.listen(UiEvent.EditOn, () => this.disable()); this.events.listen(UiEvent.EditOff, () => this.enable()); - const tb = new HoverTextBox(this.start_button, el("span").tx("hover test").st("color", "yellow").fin(), "left", 10); - tb.show(); + + const s_width = this.start_button.offsetWidth; + this.start_button.style.width = `${s_width.toString()}px`; + + // const tb = new HoverTextBox(this.start_button, el("span").tx("hover test").st("color", "yellow").fin(), "left", 10); + // tb.show(); } disable(): void { @@ -68,7 +72,7 @@ export default class pausePlay implements UiComponent { } else { this.on = true; this.cycle(); - this.start_button.textContent = "Storp"; + this.start_button.textContent = "Stop"; } } diff --git a/src/ui/components/resetButton.ts b/src/ui/components/resetButton.ts new file mode 100644 index 0000000..7e35767 --- /dev/null +++ b/src/ui/components/resetButton.ts @@ -0,0 +1,18 @@ +import { el } from "../../etc"; +import { UiEventHandler, UiCpuSignalHandler, UiCpuSignal } from "../../events"; +import UiComponent from "../uiComponent"; + +export default class ResetButton implements UiComponent { + container: HTMLElement; + events: UiEventHandler; + cpu_signals: UiCpuSignalHandler; + constructor(element: HTMLElement, events: UiEventHandler, cpu_signals: UiCpuSignalHandler) { + this.container = element; + this.events = events; + this.cpu_signals = cpu_signals; + const reset_button = el("button").ti("Reset State").tx("⟳").fin(); + + reset_button.addEventListener("click", () => this.cpu_signals.dispatch(UiCpuSignal.RequestCpuSoftReset)); + this.container.appendChild(reset_button); + } +} diff --git a/src/ui/components/resetButtons.ts b/src/ui/components/resetButtons.ts deleted file mode 100644 index 0f459ad..0000000 --- a/src/ui/components/resetButtons.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { el } from "../../etc"; -import { UiEventHandler, UiCpuSignalHandler, UiCpuSignal } from "../../events"; -import UiComponent from "../uiComponent"; - -export default class ResetButtons implements UiComponent { - container: HTMLElement; - events: UiEventHandler; - cpu_signals: UiCpuSignalHandler; - constructor(element: HTMLElement, events: UiEventHandler, cpu_signals: UiCpuSignalHandler) { - this.container = element; - this.events = events; - this.cpu_signals = cpu_signals; - const reset_button = el("button").cl("nostyle").ti("Reset State").tx("⟳").fin(); - const trash_button = el("button").cl("nostyle").ti("Delete Code").tx("🗑").fin(); - - reset_button.addEventListener("click", () => this.resetClicked()); - trash_button.addEventListener("click", () => this.trashClicked()); - this.container.append(reset_button, trash_button); - } - - resetClicked(): void { - this.cpu_signals.dispatch(UiCpuSignal.RequestCpuSoftReset); - } - - trashClicked(): void { - const a = confirm("Clear all code? Irreversible"); - if (a) { - this.cpu_signals.dispatch(UiCpuSignal.RequestCpuReset); - } - } -} diff --git a/src/ui/components/saveLoad.ts b/src/ui/components/saveLoad.ts index aa9f7d2..75507c0 100644 --- a/src/ui/components/saveLoad.ts +++ b/src/ui/components/saveLoad.ts @@ -37,7 +37,7 @@ export default class SaveLoad implements UiComponent { this.cpu_signals.dispatch(UiCpuSignal.RequestMemoryDump, (memory) => { const flattened = new Uint8Array(256 * memory.length); for (let x = 0; x < 4; x++) { - for (let y = 0; y < 256; x++) { + for (let y = 0; y < 256; y++) { flattened[256 * x + y] = memory[x][y]; } } diff --git a/src/ui/components/trashButton.ts b/src/ui/components/trashButton.ts new file mode 100644 index 0000000..997bb07 --- /dev/null +++ b/src/ui/components/trashButton.ts @@ -0,0 +1,23 @@ +import { el } from "../../etc"; +import { UiEventHandler, UiCpuSignalHandler, UiCpuSignal } from "../../events"; +import UiComponent from "../uiComponent"; + +export default class TrashButton implements UiComponent { + container: HTMLElement; + events: UiEventHandler; + cpu_signals: UiCpuSignalHandler; + constructor(element: HTMLElement, events: UiEventHandler, cpu_signals: UiCpuSignalHandler) { + this.container = element; + this.events = events; + this.cpu_signals = cpu_signals; + // const trash_button = el("button").cl("nostyle").ti("Delete Code").tx("🗑").fin(); + + this.container.addEventListener("click", () => this.trashClicked()); + } + + trashClicked(): void { + if (confirm("Clear all code? Irreversible")) { + this.cpu_signals.dispatch(UiCpuSignal.RequestCpuReset); + } + } +} diff --git a/src/ui/windows/instructionExplainer.ts b/src/ui/windows/instructionExplainer.ts index 013d623..d949745 100644 --- a/src/ui/windows/instructionExplainer.ts +++ b/src/ui/windows/instructionExplainer.ts @@ -5,6 +5,14 @@ import { u8 } from "../../num"; import WindowBox from "../windowBox"; import UiComponent from "../uiComponent"; +const p_map = { + [ParamType.Const]: "constant", + [ParamType.ConstMemory]: "memory", + [ParamType.Register]: "register", + [ParamType.Bank]: "bank", + [ParamType.RegisterAddress]: "regaddr", +}; + export default class InstructionExplainer extends WindowBox implements UiComponent { events: UiEventHandler; cpu_signals: UiCpuSignalHandler; @@ -34,33 +42,33 @@ export default class InstructionExplainer extends WindowBox implements UiCompone addParameter(param: ParameterType, pos: u8, byte: u8): void { const t = param.type; - let name; - 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.addBox(formatHex(byte), param.desc, name); + + this.addBox(formatHex(byte), param.desc, p_map[t]); } - addInvalid(pos: u8, byte: u8): void { + addInvalidParam(param: ParameterType, pos: u8, byte: u8): void { + const t = param.type; + + this.addBox(formatHex(byte), param.desc, `${p_map[t]} invalid`); + } + + addInvalidInstr(pos: u8, byte: u8): void { this.reset(); this.addBox(formatHex(byte), "Invalid Instruction", "invalid"); } initCpuEvents(c: CpuEventHandler): void { + c.listen(CpuEvent.InstructionParseBegin, ({ instr, code, pos }) => { + this.addInstruction(instr, pos, code); + }); c.listen(CpuEvent.ParameterParsed, ({ param, code, pos }) => { this.addParameter(param, pos, code); }); - c.listen(CpuEvent.InstructionParsed, ({ instr, code, pos }) => { - this.addInstruction(instr, pos, code); + c.listen(CpuEvent.InvalidParameterParsed, ({ param, code, pos }) => { + this.addInvalidParam(param, code, pos); }); - c.listen(CpuEvent.InvalidParsed, ({ code, pos }) => { - this.addInvalid(pos, code); + c.listen(CpuEvent.InvalidInstructionParsed, ({ code, pos }) => { + this.addInvalidInstr(pos, code); }); }