final commit before major rework

This commit is contained in:
Alexander Bass 2024-05-15 18:26:39 -04:00
parent b550a62af8
commit f850b21869
19 changed files with 452 additions and 203 deletions

View file

@ -37,7 +37,7 @@
"quote-props" : ["warn","as-needed"], "quote-props" : ["warn","as-needed"],
"dot-notation":"warn", "dot-notation":"warn",
"one-var":["warn","never"], "one-var":["warn","never"],
"no-use-before-define":"warn", "no-use-before-define":"off",
"no-multi-assign":"warn", "no-multi-assign":"warn",
"no-else-return":"warn", "no-else-return":"warn",
"spaced-comment":"warn", "spaced-comment":"warn",

View file

@ -4,11 +4,18 @@ import { Instruction, ISA } from "./instructionSet";
import { m256, u2, u3, u8 } from "./num"; import { m256, u2, u3, u8 } from "./num";
import { DEFAULT_VRAM_BANK } from "./constants"; import { DEFAULT_VRAM_BANK } from "./constants";
interface ParsedParameter {
pos: u8;
code: u8;
valid: boolean;
}
export type TempInstrState = { export type TempInstrState = {
pos: u8; pos: u8;
params_found: number; params_found: number;
instr: Instruction; instr: Instruction;
params: Array<u8>; valid: boolean;
params: Array<ParsedParameter>;
}; };
function initBanks(): [Uint8Array, Uint8Array, Uint8Array, Uint8Array] { function initBanks(): [Uint8Array, Uint8Array, Uint8Array, Uint8Array] {
@ -22,7 +29,7 @@ function initBanks(): [Uint8Array, Uint8Array, Uint8Array, Uint8Array] {
export default class Computer { export default class Computer {
private banks: [Uint8Array, Uint8Array, Uint8Array, Uint8Array] = initBanks(); private banks: [Uint8Array, Uint8Array, Uint8Array, Uint8Array] = initBanks();
private registers: Uint8Array = new Uint8Array(8); private registers: Uint8Array = new Uint8Array(8);
private call_stack: Array<u8> = []; // private call_stack: Array<u8> = [];
private carry_flag: boolean = false; private carry_flag: boolean = false;
private program_counter: u8 = 0; private program_counter: u8 = 0;
private bank: u2 = 0; private bank: u2 = 0;
@ -36,7 +43,7 @@ export default class Computer {
if (this.current_instr === null) { if (this.current_instr === null) {
const parsed_instruction = ISA.getInstruction(current_byte); const parsed_instruction = ISA.getInstruction(current_byte);
if (parsed_instruction === null) { if (parsed_instruction === null) {
this.events.dispatch(CpuEvent.InvalidParsed, { this.events.dispatch(CpuEvent.InvalidInstructionParsed, {
pos: this.program_counter, pos: this.program_counter,
code: current_byte, code: current_byte,
}); });
@ -45,35 +52,55 @@ export default class Computer {
this.events.dispatch(CpuEvent.Cycle); this.events.dispatch(CpuEvent.Cycle);
return; return;
} }
this.current_instr = { this.current_instr = {
pos: this.program_counter, pos: this.program_counter,
instr: parsed_instruction, instr: parsed_instruction,
params_found: 0, params_found: 0,
params: new Array<u8>(parsed_instruction.params.length), valid: true,
params: new Array<ParsedParameter>(parsed_instruction.params.length),
}; };
this.events.dispatch(CpuEvent.InstructionParsed, {
this.events.dispatch(CpuEvent.InstructionParseBegin, {
pos: this.program_counter, pos: this.program_counter,
instr: parsed_instruction, instr: parsed_instruction,
code: current_byte, code: current_byte,
}); });
}
if (this.current_instr.pos === this.program_counter && this.current_instr.params.length > 0) {
this.stepForward(); this.stepForward();
this.events.dispatch(CpuEvent.Cycle); this.events.dispatch(CpuEvent.Cycle);
return; 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) { if (this.current_instr.params.length !== this.current_instr.params_found) {
const b = this.current_instr.instr.params[this.current_instr.params_found]; const b = this.current_instr.instr.params[this.current_instr.params_found];
const valid = b.validate(current_byte);
if (valid) {
this.events.dispatch(CpuEvent.ParameterParsed, { this.events.dispatch(CpuEvent.ParameterParsed, {
param: b, param: b,
pos: this.program_counter, pos: this.program_counter,
code: current_byte, code: current_byte,
}); });
this.current_instr.params[this.current_instr.params_found] = 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; this.current_instr.params_found += 1;
if (this.current_instr.params.length !== this.current_instr.params_found) { if (this.current_instr.params.length !== this.current_instr.params_found) {
this.stepForward(); this.stepForward();
this.events.dispatch(CpuEvent.Cycle); this.events.dispatch(CpuEvent.Cycle);
@ -87,8 +114,12 @@ export default class Computer {
}, },
dispatch: 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); if (this.current_instr.valid) {
const params: Array<u8> = 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.InstructionExecuted, { instr: this.current_instr.instr });
}
this.events.dispatch(CpuEvent.InstructionParseEnd);
this.current_instr = null; this.current_instr = null;
if (execution_post_action_state.should_step) { if (execution_post_action_state.should_step) {
@ -134,15 +165,15 @@ export default class Computer {
this.program_counter = new_value; this.program_counter = new_value;
} }
pushCallStack(address: u8): boolean { // pushCallStack(address: u8): boolean {
if (this.call_stack.length >= 8) return false; // if (this.call_stack.length >= 8) return false;
this.call_stack.push(address); // this.call_stack.push(address);
return true; // return true;
} // }
popCallStack(): u8 | null { // popCallStack(): u8 | null {
return this.call_stack.pop() ?? null; // return this.call_stack.pop() ?? null;
} // }
setBank(bank: u2): void { setBank(bank: u2): void {
this.events.dispatch(CpuEvent.SwitchBank, { bank: bank }); 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.RequestMemoryChange, ({ address, bank, value }) => this.setMemory(address, value, bank));
ui.listen(UiCpuSignal.RequestRegisterChange, ({ register_no, value }) => this.setRegister(register_no, value)); ui.listen(UiCpuSignal.RequestRegisterChange, ({ register_no, value }) => this.setRegister(register_no, value));
ui.listen(UiCpuSignal.RequestMemoryDump, (callback) => callback(this.dumpMemory())); ui.listen(UiCpuSignal.RequestMemoryDump, (callback) => callback(this.dumpMemory()));
ui.listen(UiCpuSignal.RequestCpuReset, () => this.reset());
ui.listen(UiCpuSignal.RequestProgramCounterChange, ({ address }) => { ui.listen(UiCpuSignal.RequestProgramCounterChange, ({ address }) => {
this.setProgramCounter(address); this.setProgramCounter(address);
}); });
ui.listen(UiCpuSignal.RequestCpuReset, () => this.reset());
ui.listen(UiCpuSignal.RequestCpuSoftReset, () => this.softReset()); ui.listen(UiCpuSignal.RequestCpuSoftReset, () => this.softReset());
} }
softReset(): void { softReset(): void {
this.events.dispatch(CpuEvent.SoftReset); this.events.dispatch(CpuEvent.SoftReset);
for (let i = 0; i < 8; i++) this.setRegister(i as u3, 0); 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.setVramBank(DEFAULT_VRAM_BANK);
this.setCarry(false); this.setCarry(false);
this.current_instr = null; this.current_instr = null;
@ -193,7 +224,7 @@ export default class Computer {
this.banks = initBanks(); this.banks = initBanks();
// Soft reset // Soft reset
for (let i = 0; i < 8; i++) this.setRegister(i as u3, 0); 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.setVramBank(DEFAULT_VRAM_BANK);
this.setCarry(false); this.setCarry(false);
this.current_instr = null; this.current_instr = null;

View file

@ -14,9 +14,11 @@ export enum CpuEvent {
MemoryChanged, MemoryChanged,
RegisterChanged, RegisterChanged,
ProgramCounterChanged, ProgramCounterChanged,
InstructionParsed, InstructionParseBegin,
InstructionParseEnd,
ParameterParsed, ParameterParsed,
InvalidParsed, InvalidParameterParsed,
InvalidInstructionParsed,
InstructionExecuted, InstructionExecuted,
Cycle, Cycle,
Print, Print,
@ -29,16 +31,22 @@ export enum CpuEvent {
SetVramBank, 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 { interface CpuEventMap {
[CpuEvent.MemoryChanged]: { address: u8; bank: u2; value: u8 }; [CpuEvent.MemoryChanged]: { address: u8; bank: u2; value: u8 };
[CpuEvent.MemoryAccessed]: { address: u8; bank: u2; value: u8 }; [CpuEvent.MemoryAccessed]: { address: u8; bank: u2; value: u8 };
[CpuEvent.RegisterChanged]: { register_no: u3; value: u8 }; [CpuEvent.RegisterChanged]: { register_no: u3; value: u8 };
[CpuEvent.ProgramCounterChanged]: { counter: 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.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.InstructionExecuted]: { instr: Instruction };
[CpuEvent.SwitchBank]: { bank: u2 }; [CpuEvent.SwitchBank]: { bank: u2 };
[CpuEvent.SetVramBank]: { bank: u2 }; [CpuEvent.SetVramBank]: { bank: u2 };

View file

@ -11,24 +11,33 @@
This computer requires JavaScript. Your browser either doesn't support it, or you have it disabled. This computer requires JavaScript. Your browser either doesn't support it, or you have it disabled.
</noscript> </noscript>
<main> <main>
<div id="left_column">
<div id="program_meta">
<div id="title_div">
<input type="text" name="" id="" value="New Program" />
</div>
<div id="btn_div">
<span id="save_load_buttons"></span>
<button>🔗</button>
<button id="trash_button">🗑</button>
</div>
</div>
<div id="grid"> <div id="grid">
<div id="title">VIRTUAL 8-BIT COMPUTER</div> <div id="title">VIRTUAL 8-BIT COMPUTER</div>
<div id="registers"></div> <div id="registers"></div>
<div id="labelcontainer"> <!-- <div id="reg_label">↯REGISTERS</div> -->
<div id="registers_label">↯REGISTERS</div> <!-- <div id="mem_label">MEMORY↯</div> -->
<div id="memory_label">MEMORY↯</div>
</div>
<div id="memory"></div> <div id="memory"></div>
<div id="controls_bar"> <div id="controls_bar">
<span id="controls_buttons"></span> <span id="controls_buttons"></span>
<span id="save_load_buttons"></span>
<button type="button" id="edit_button"></button> <button type="button" id="edit_button"></button>
<div id="reset_button"></div>
</div> </div>
<span id="cycles"></span> <span id="cycles"></span>
<div id="memory_bank_view"></div> <div id="memory_bank_view"></div>
</div>
<div id="reset_buttons"></div>
</div> </div>
<div id="window_box"> <div id="window_box">
@ -63,6 +72,20 @@
</div> </div>
</main> </main>
<article>
<details id="isa_container">
<summary>Instruction Set</summary>
<pre id="ISA"></pre> <pre id="ISA"></pre>
</details>
<h1>Virtual 8-Bit Computer</h1>
<p>
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.
</p>
<h2>How it works</h2>
<p></p>
</article>
</body> </body>
</html> </html>

View file

@ -7,7 +7,7 @@ import Computer from "./computer";
import UI from "./ui"; import UI from "./ui";
import { $ } from "./etc"; import { $ } from "./etc";
import { ISA } from "./instructionSet"; import { ISA } from "./instructionSet";
import { generateIsa } from "./isaGenerator"; import { generateIsaTable } from "./isaGenerator";
import { u8 } from "./num"; import { u8 } from "./num";
import "./style/style.scss"; import "./style/style.scss";
@ -49,7 +49,7 @@ function main(): void {
// Todo, move to ui component // Todo, move to ui component
// or move to documentation // or move to documentation
$("ISA").textContent = generateIsa(ISA); $("ISA").replaceWith(generateIsaTable(ISA));
let fire = false; let fire = false;
window.firehose = (): void => { window.firehose = (): void => {

View file

@ -10,7 +10,10 @@ import { isU2, isU3, m256, u2, u3, u8 } from "./num";
export enum ParamType { export enum ParamType {
Const, Const,
Register, Register,
Memory, ConstMemory,
Bank,
// A register which holds a memory address
RegisterAddress,
} }
export abstract class ParameterType { export abstract class ParameterType {
@ -20,6 +23,9 @@ export abstract class ParameterType {
this.desc = description; this.desc = description;
this.type = p_type; this.type = p_type;
} }
validate(n: number): boolean {
return true;
}
} }
class ConstParam extends ParameterType { class ConstParam extends ParameterType {
@ -31,10 +37,28 @@ class RegisParam extends ParameterType {
constructor(d: string) { constructor(d: string) {
super(d, ParamType.Register); super(d, ParamType.Register);
} }
validate(n: number): boolean {
return isU3(n);
} }
class MemorParam extends ParameterType { }
class BankParam extends ParameterType {
constructor(d: string) { 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; getProgramCounter: () => u8;
getRegister: (number: u3) => u8; getRegister: (number: u3) => u8;
setRegister: (number: u3, value: u8) => void; setRegister: (number: u3, value: u8) => void;
pushCallStack: (address: u8) => boolean; // pushCallStack: (address: u8) => boolean;
popCallStack: () => u8 | null; // popCallStack: () => u8 | null;
setBank: (bank_no: u2) => void; setBank: (bank_no: u2) => void;
getCarry(): boolean; getCarry(): boolean;
setCarry(state: boolean): void; setCarry(state: boolean): void;
setVramBank(bank: u2): void; setVramBank(bank: u2): void;
softReset(): void;
} }
interface AfterExecutionComputerAction { interface AfterExecutionComputerAction {
@ -129,9 +154,9 @@ ISA.addCategory(category(0xf0, 0xff, "IO"));
// COPY // COPY
ISA.insertInstruction(0x10, { ISA.insertInstruction(0x10, {
name: "Copy R -> M", name: "Copy CR -> CA",
desc: "Copy the byte in register (P1) to the memory address (P2)", 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) { execute(c, p) {
const [register_no, mem_address] = p; const [register_no, mem_address] = p;
if (!isU3(register_no)) throw new Error("TODO"); if (!isU3(register_no)) throw new Error("TODO");
@ -140,9 +165,9 @@ ISA.insertInstruction(0x10, {
}); });
ISA.insertInstruction(0x11, { ISA.insertInstruction(0x11, {
name: "Copy M -> R", name: "Copy CA -> R",
desc: "Copy the byte in memory address (P1) to the register (P2)", 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) { execute(c, p) {
const [register_no, mem_address] = p; const [register_no, mem_address] = p;
if (!isU3(register_no)) throw new Error("TODO"); if (!isU3(register_no)) throw new Error("TODO");
@ -151,9 +176,9 @@ ISA.insertInstruction(0x11, {
}); });
ISA.insertInstruction(0x12, { ISA.insertInstruction(0x12, {
name: "Copy M -> M", name: "Copy CM -> CA",
desc: "Copy the byte in memory address (P1) to memory address (P2)", 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) { execute(c, p) {
const [mem_address_1, mem_address_2] = p; const [mem_address_1, mem_address_2] = p;
c.setMemory(mem_address_2, c.getMemory(mem_address_1)); c.setMemory(mem_address_2, c.getMemory(mem_address_1));
@ -172,10 +197,10 @@ ISA.insertInstruction(0x13, {
}, },
}); });
ISA.insertInstruction(0x14, { ISA.insertInstruction(0x14, {
name: "Load RM -> R", name: "Load RA -> R",
desc: "Copy the byte in memory addressed by register (P1) to register (P2)", desc: "Copy the byte in memory addressed by register (P1) to register (P2)",
params: [ 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"), new RegisParam("To this register"),
], ],
execute(c, p) { execute(c, p) {
@ -186,11 +211,11 @@ ISA.insertInstruction(0x14, {
}, },
}); });
ISA.insertInstruction(0x15, { 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)", desc: "Copy the byte in register (P1) to the memory cell addressed in register (P2)",
params: [ params: [
new RegisParam("Copy the value in this register"), 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) { execute(c, p) {
const [register_no_1, register_no_2] = p; const [register_no_1, register_no_2] = p;
@ -213,7 +238,7 @@ ISA.insertInstruction(0x17, {
ISA.insertInstruction(0x18, { ISA.insertInstruction(0x18, {
name: "Zero Memory", name: "Zero Memory",
desc: "Set the byte in memory address (P1) to 0", 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) { execute(c, p) {
const mem_address = p[0]; const mem_address = p[0];
c.setMemory(mem_address, 0); c.setMemory(mem_address, 0);
@ -234,7 +259,7 @@ ISA.insertInstruction(0x19, {
ISA.insertInstruction(0x1f, { ISA.insertInstruction(0x1f, {
name: "Set bank", name: "Set bank",
desc: "Selects which bank of memory to write and read to", desc: "Selects which bank of memory to write and read to",
params: [new ConstParam("Bank number")], params: [new BankParam("Bank number")],
execute(c, p) { execute(c, p) {
const bank_no = p[0]; const bank_no = p[0];
if (!isU2(bank_no)) { if (!isU2(bank_no)) {
@ -255,9 +280,8 @@ ISA.insertInstruction(0x20, {
params: [new RegisParam("new instruction counter location")], params: [new RegisParam("new instruction counter location")],
execute: (c, p, a) => { execute: (c, p, a) => {
const register_no = p[0]; const register_no = p[0];
if (!isU3(register_no)) { if (!isU3(register_no)) throw new Error("todo");
throw new Error("todo");
}
const new_address = c.getRegister(register_no); const new_address = c.getRegister(register_no);
c.setProgramCounter(new_address); c.setProgramCounter(new_address);
a.noStep(); a.noStep();
@ -333,33 +357,33 @@ ISA.insertInstruction(0x29, {
}, },
}); });
ISA.insertInstruction(0x2d, { // ISA.insertInstruction(0x2d, {
name: "Call", // name: "Call",
desc: "", // desc: "",
params: [new ConstParam("the subroutine at this memory address")], // params: [new ConstParam("the subroutine at this memory address")],
execute(c, p, a) { // execute(c, p, a) {
const current_address = c.getProgramCounter(); // const current_address = c.getProgramCounter();
const success = c.pushCallStack(current_address); // const success = c.pushCallStack(current_address);
// TODO Handle success/failure // // TODO Handle success/failure
const new_address = p[0]; // const new_address = p[0];
c.setProgramCounter(new_address); // c.setProgramCounter(new_address);
a.noStep(); // a.noStep();
}, // },
}); // });
ISA.insertInstruction(0x2e, { // ISA.insertInstruction(0x2e, {
name: "Return", // name: "Return",
desc: "", // desc: "",
params: [], // params: [],
execute(c, p, a) { // execute(c, p, a) {
const new_address = c.popCallStack(); // const new_address = c.popCallStack();
if (new_address === null) throw new Error("TODO handle this"); // if (new_address === null) throw new Error("TODO handle this");
c.setProgramCounter(m256(new_address + 1)); // c.setProgramCounter(m256(new_address + 1));
a.noStep(); // a.noStep();
}, // },
}); // });
ISA.insertInstruction(0x2c, { ISA.insertInstruction(0x2c, {
name: "NoOp", name: "NoOp",
@ -369,11 +393,12 @@ ISA.insertInstruction(0x2c, {
}); });
ISA.insertInstruction(0x2f, { ISA.insertInstruction(0x2f, {
name: "Halt and Catch Fire", name: "Halt",
desc: "Stops program execu..... Fire! FIRE EVERYWHERE!", desc: "Stops CPU execution and soft resets",
params: [], params: [],
execute(c, p, a) { execute(c, p, a) {
a.dispatch(CpuEvent.Halt); a.dispatch(CpuEvent.Halt);
c.softReset();
}, },
}); });
@ -743,7 +768,7 @@ ISA.insertInstruction(0xf1, {
ISA.insertInstruction(0xff, { ISA.insertInstruction(0xff, {
name: "Set VRAM Bank", name: "Set VRAM Bank",
desc: "Set memory bank which screen gets pixels from memory bank (P1)", 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) { execute(c, p, a) {
const bank_no = p[0]; const bank_no = p[0];
if (!isU2(bank_no)) throw new Error("TO2O"); if (!isU2(bank_no)) throw new Error("TO2O");

View file

@ -3,7 +3,7 @@
* @copyright Alexander Bass 2024 * @copyright Alexander Bass 2024
* @license GPL-3.0 * @license GPL-3.0
*/ */
import { formatHex, inRange } from "./etc"; import { el, formatHex, inRange } from "./etc";
import { InstrCategory, Instruction, InstructionSet, ParameterType, ParamType } from "./instructionSet"; import { InstrCategory, Instruction, InstructionSet, ParameterType, ParamType } from "./instructionSet";
import { u8 } from "./num"; import { u8 } from "./num";
@ -13,7 +13,13 @@ function parameterDescription(params: Array<ParameterType>): string {
str += " "; str += " ";
} }
for (const p of params) { 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]; const char = p_map[p.type];
str += char; str += char;
str += " "; str += " ";
@ -21,39 +27,46 @@ function parameterDescription(params: Array<ParameterType>): string {
return str; 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]> = []; const instructions: Array<[u8, Instruction]> = [];
for (const kv of iset.instructions.entries()) instructions.push(kv); 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; let current_category: InstrCategory | null = null;
for (const [code, instr] of instructions) {
for (const instruction of instructions) { const cat = iset.category_ranges.find((i) => inRange(code, i.start, i.end));
const cat = iset.category_ranges.find((i) => inRange(instruction[0], i.start, i.end));
if (cat === undefined) { if (cat === undefined) {
throw new Error("Instruction found which is not part of category"); throw new Error("Instruction found which is not part of category");
} }
if (current_category !== cat) { 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; 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, " "); row.appendChild(
const parameters = parameterDescription(instruction[1].params); el("td")
const description = instruction[1].desc; .tx(`0x${formatHex(code)}`)
output_string += `0x${hex_code}: ${short_description}`; .fin()
if (parameters.length !== 0) { );
output_string += ` -${parameters}- `; row.appendChild(el("td").tx(parameterDescription(instr.params)).fin());
} else { row.appendChild(el("td").tx(instr_text).fin());
output_string += " - "; row.appendChild(el("td").tx(instr.desc).fin());
table.appendChild(row);
} }
output_string += `${description}\n`;
} return table;
return output_string;
} }

25
src/style/article.scss Normal file
View file

@ -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;
}

View file

@ -1,9 +1,16 @@
@use "memory_registers"; @use "memory_registers";
@use "hover_text_box"; @use "hover_text_box";
@use "article";
@use "windows"; @use "windows";
@use "top_meta";
@use "buttons"; @use "buttons";
@use "table";
@use "vars"; @use "vars";
#isa_container summary {
font-size: 1.5rem;
}
* { * {
box-sizing: border-box; box-sizing: border-box;
} }
@ -35,9 +42,9 @@ main {
grid-template-columns: min-content min-content min-content; grid-template-columns: min-content min-content min-content;
grid-template-rows: min-content min-content min-content; grid-template-rows: min-content min-content min-content;
grid-template-areas: grid-template-areas:
". regmemlabel . cycles" ". reglabel . ."
". registers . bank " ". registers . bank "
"reset memory memory memory" "memlabel memory memory memory"
"title memory memory memory" "title memory memory memory"
". buttons buttons buttons"; ". buttons buttons buttons";
@ -50,8 +57,11 @@ main {
#memory_bank_view { #memory_bank_view {
grid-area: bank; grid-area: bank;
} }
#labelcontainer { #reg_label {
grid-area: regmemlabel; grid-area: reglabel;
}
#mem_label {
grid-area: memlabel;
} }
#cycles { #cycles {
grid-area: cycles; grid-area: cycles;
@ -70,9 +80,6 @@ main {
#memory { #memory {
grid-area: memory; grid-area: memory;
} }
#reset_buttons {
grid-area: reset;
}
} }
#reset_buttons { #reset_buttons {
@ -88,15 +95,20 @@ main {
} }
} }
#labelcontainer { #reg_label,
column-gap: 18px; #mem_label {
font-size: 0.75em; font-size: 0.75em;
display: flex;
flex-direction: row;
align-items: center;
user-select: none; 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 { #memory_bank_view {
display: flex; display: flex;
#bank_boxes { #bank_boxes {

34
src/style/table.scss Normal file
View file

@ -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;
}

55
src/style/top_meta.scss Normal file
View file

@ -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;
}

View file

@ -9,7 +9,8 @@ import BankSelector from "./ui/components/bankViewSelector";
import EditButton from "./ui/components/editButton"; import EditButton from "./ui/components/editButton";
import pausePlay from "./ui/components/pausePlay"; import pausePlay from "./ui/components/pausePlay";
import SaveLoad from "./ui/components/saveLoad"; 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 // Window Components
import InstructionExplainer from "./ui/windows/instructionExplainer"; import InstructionExplainer from "./ui/windows/instructionExplainer";
import Screen from "./ui/windows/screen"; import Screen from "./ui/windows/screen";
@ -23,7 +24,7 @@ export default class UI {
constructor() { constructor() {
this.register_component(MemoryView, $("memory")); this.register_component(MemoryView, $("memory"));
this.register_component(frequencyIndicator, $("cycles")); // this.register_component(frequencyIndicator, $("cycles"));
this.register_component(InstructionExplainer, $("instruction_explainer")); this.register_component(InstructionExplainer, $("instruction_explainer"));
this.register_component(RegisterView, $("registers")); this.register_component(RegisterView, $("registers"));
this.register_component(Screen, $("tv")); this.register_component(Screen, $("tv"));
@ -33,7 +34,8 @@ export default class UI {
this.register_component(SaveLoad, $("save_load_buttons")); this.register_component(SaveLoad, $("save_load_buttons"));
this.register_component(BankSelector, $("memory_bank_view")); this.register_component(BankSelector, $("memory_bank_view"));
this.register_component(BankVisualizer, $("bank_viz")); 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 { private register_component(ctor: UiComponentConstructor, e: HTMLElement): void {

View file

@ -5,6 +5,14 @@ import UiComponent from "../uiComponent";
import { el } from "../../etc"; import { el } from "../../etc";
import CelledViewer from "../celledViewer"; 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 */ /** Only to be run once */
function createBanks( function createBanks(
element: HTMLElement, element: HTMLElement,
@ -106,26 +114,17 @@ export default class MemoryView implements UiComponent {
this.program.addCellClass(pos, "instruction_argument"); this.program.addCellClass(pos, "instruction_argument");
const t = param.type; const t = param.type;
this.program.removeCellClass(pos, "constant", "register", "memory", "instruction", "invalid"); this.program.removeCellClass(pos, "constant", "register", "memory", "instruction", "invalid");
let name: string = ""; const name = p_map[t];
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.program.addCellClass(pos, name); 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("instruction_argument");
this.program.removeAllCellClass("current_instruction"); this.program.removeAllCellClass("current_instruction");
this.program.removeCellClass(pos, "constant", "register", "memory", "invalid"); this.program.removeCellClass(pos, "constant", "register", "memory", "invalid");
this.program.addCellClass(pos, "current_instruction"); this.program.addCellClass(pos, "current_instruction");
this.program.addCellClass(pos, "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.removeCellClass(pos, "constant", "register", "memory", "instruction");
this.program.addCellClass(pos, "invalid"); this.program.addCellClass(pos, "invalid");
}); });

View file

@ -42,8 +42,12 @@ export default class pausePlay implements UiComponent {
this.events.listen(UiEvent.EditOn, () => this.disable()); this.events.listen(UiEvent.EditOn, () => this.disable());
this.events.listen(UiEvent.EditOff, () => this.enable()); 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 { disable(): void {
@ -68,7 +72,7 @@ export default class pausePlay implements UiComponent {
} else { } else {
this.on = true; this.on = true;
this.cycle(); this.cycle();
this.start_button.textContent = "Storp"; this.start_button.textContent = "Stop";
} }
} }

View file

@ -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);
}
}

View file

@ -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);
}
}
}

View file

@ -37,7 +37,7 @@ export default class SaveLoad implements UiComponent {
this.cpu_signals.dispatch(UiCpuSignal.RequestMemoryDump, (memory) => { this.cpu_signals.dispatch(UiCpuSignal.RequestMemoryDump, (memory) => {
const flattened = new Uint8Array(256 * memory.length); const flattened = new Uint8Array(256 * memory.length);
for (let x = 0; x < 4; x++) { 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]; flattened[256 * x + y] = memory[x][y];
} }
} }

View file

@ -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);
}
}
}

View file

@ -5,6 +5,14 @@ import { u8 } from "../../num";
import WindowBox from "../windowBox"; import WindowBox from "../windowBox";
import UiComponent from "../uiComponent"; 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 { export default class InstructionExplainer extends WindowBox implements UiComponent {
events: UiEventHandler; events: UiEventHandler;
cpu_signals: UiCpuSignalHandler; cpu_signals: UiCpuSignalHandler;
@ -34,33 +42,33 @@ export default class InstructionExplainer extends WindowBox implements UiCompone
addParameter(param: ParameterType, pos: u8, byte: u8): void { addParameter(param: ParameterType, pos: u8, byte: u8): void {
const t = param.type; const t = param.type;
let name;
if (t === ParamType.Const) { this.addBox(formatHex(byte), param.desc, p_map[t]);
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);
} }
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.reset();
this.addBox(formatHex(byte), "Invalid Instruction", "invalid"); this.addBox(formatHex(byte), "Invalid Instruction", "invalid");
} }
initCpuEvents(c: CpuEventHandler): void { initCpuEvents(c: CpuEventHandler): void {
c.listen(CpuEvent.InstructionParseBegin, ({ instr, code, pos }) => {
this.addInstruction(instr, pos, code);
});
c.listen(CpuEvent.ParameterParsed, ({ param, code, pos }) => { c.listen(CpuEvent.ParameterParsed, ({ param, code, pos }) => {
this.addParameter(param, pos, code); this.addParameter(param, pos, code);
}); });
c.listen(CpuEvent.InstructionParsed, ({ instr, code, pos }) => { c.listen(CpuEvent.InvalidParameterParsed, ({ param, code, pos }) => {
this.addInstruction(instr, pos, code); this.addInvalidParam(param, code, pos);
}); });
c.listen(CpuEvent.InvalidParsed, ({ code, pos }) => { c.listen(CpuEvent.InvalidInstructionParsed, ({ code, pos }) => {
this.addInvalid(pos, code); this.addInvalidInstr(pos, code);
}); });
} }