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"],
"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",

View file

@ -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<u8>;
valid: boolean;
params: Array<ParsedParameter>;
};
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<u8> = [];
// private call_stack: Array<u8> = [];
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<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,
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];
const valid = b.validate(current_byte);
if (valid) {
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;
} 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);
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.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;

View file

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

View file

@ -11,24 +11,33 @@
This computer requires JavaScript. Your browser either doesn't support it, or you have it disabled.
</noscript>
<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="title">VIRTUAL 8-BIT COMPUTER</div>
<div id="registers"></div>
<div id="labelcontainer">
<div id="registers_label">↯REGISTERS</div>
<div id="memory_label">MEMORY↯</div>
</div>
<!-- <div id="reg_label">↯REGISTERS</div> -->
<!-- <div id="mem_label">MEMORY↯</div> -->
<div id="memory"></div>
<div id="controls_bar">
<span id="controls_buttons"></span>
<span id="save_load_buttons"></span>
<button type="button" id="edit_button"></button>
<div id="reset_button"></div>
</div>
<span id="cycles"></span>
<div id="memory_bank_view"></div>
<div id="reset_buttons"></div>
</div>
</div>
<div id="window_box">
@ -63,6 +72,20 @@
</div>
</main>
<article>
<details id="isa_container">
<summary>Instruction Set</summary>
<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>
</html>

View file

@ -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 => {

View file

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

View file

@ -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<ParameterType>): 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<ParameterType>): 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 += " - ";
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);
}
output_string += `${description}\n`;
}
return output_string;
return table;
}

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 "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,9 +42,9 @@ main {
grid-template-columns: min-content min-content min-content;
grid-template-rows: min-content min-content min-content;
grid-template-areas:
". regmemlabel . cycles"
". reglabel . ."
". registers . bank "
"reset memory memory memory"
"memlabel memory memory memory"
"title memory memory memory"
". buttons buttons buttons";
@ -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 {

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 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 {

View file

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

View file

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

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) => {
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];
}
}

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