Likely overzealous numeric type enforcement.

This commit is contained in:
Alexander Bass 2024-02-21 19:08:02 -05:00
parent e7cb198123
commit 0764614b66
15 changed files with 496 additions and 123 deletions

5
TODO
View file

@ -1,5 +1,4 @@
Add screen (VRAM?)
- Bank switching
Live memory and register editing (Probably should pause autostep when it reaches the cell you're modifying)
Draw lines between registers and memory when used
Save binary button
HCF flames

View file

@ -22,6 +22,7 @@
<button type="button" id="step_button">Step</button>
<label for="binary_upload" class="button">Load Binary</label>
<input id="binary_upload" name="binary_upload" id="binary_upload" style="display: none" type="file" />
<button type="button" id="save_button">Save</button>
<input type="range" name="" id="delay_range" min="0" max="1500" />
</div>
<span id="cycles"></span>

View file

@ -1,20 +1,22 @@
import { CpuEvent, CpuEventHandler, UiEvent, UiEventHandler } from "./events";
import { byte_array_to_js_source, format_hex, u8 } from "./etc";
import { byte_array_to_js_source, format_hex } from "./etc";
import { EventHandler } from "./eventHandler";
import { Instruction, ISA } from "./instructionSet";
import { m256, u8 } from "./num";
export type TempInstrState = {
pos: u8;
params_found: number;
instr: Instruction;
params: Uint8Array;
params: Array<u8>;
};
export class Computer {
private memory = new Uint8Array(256);
private registers = new Uint8Array(8);
private memory = new Array<u8>(256);
private registers = new Array<u8>(256);
private call_stack: Array<u8> = [];
private program_counter: u8 = 0;
private bank: u8 = 0;
private current_instr: TempInstrState | null = null;
events: CpuEventHandler = new EventHandler<CpuEvent>() as CpuEventHandler;
@ -44,7 +46,7 @@ export class Computer {
pos: this.program_counter,
instr: parsed_instruction,
params_found: 0,
params: new Uint8Array(parsed_instruction.params.length),
params: new Array<u8>(parsed_instruction.params.length),
};
this.events.dispatch(CpuEvent.InstructionParsed, {
pos: this.program_counter,
@ -126,10 +128,14 @@ export class Computer {
return this.call_stack.pop() ?? null;
}
setBank(bank_no: u8): void {
this.bank = bank_no;
}
reset(): void {
this.events.dispatch(CpuEvent.Reset, null);
this.memory = new Uint8Array(256);
this.registers = new Uint8Array(8);
this.memory = new Array<u8>(256);
this.registers = new Array<u8>(8);
this.call_stack = [];
this.current_instr = null;
this.program_counter = 0;
@ -144,19 +150,23 @@ export class Computer {
load_memory(program: Array<u8>): void {
console.log(byte_array_to_js_source(program));
const max_loop = Math.min(this.memory.length, program.length);
for (let i = 0; i < max_loop; i++) {
const max_loop: u8 = Math.min(255, program.length) as u8;
for (let i: u8 = 0; i < 255; i++) {
// Don't fire event if no change is made
if (this.memory[i] === program[i]) continue;
this.memory[i] = program[i];
this.events.dispatch(CpuEvent.MemoryChanged, { address: i, value: program[i] });
this.events.dispatch(CpuEvent.MemoryChanged, { address: i as u8, value: program[i] });
}
this.program_counter = 0;
}
dump_memory(): Array<u8> {
return this.memory;
}
private step_forward(): void {
this.program_counter = (this.program_counter + 1) % 256;
this.program_counter = m256(this.program_counter + 1);
this.events.dispatch(CpuEvent.ProgramCounterChanged, { counter: this.program_counter });
}
}

View file

@ -1,13 +1,13 @@
import { UiEventHandler } from "./events";
import { u8 } from "./num";
// The u8 type represents an unsigned 8bit integer: byte. It does not add any safety, other than as a hint to the programmer.
// export type u8 = 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|101|102|103|104|105|106|107|108|109|110|111|112|113|114|115|116|117|118|119|120|121|122|123|124|125|126|127|128|129|130|131|132|133|134|135|136|137|138|139|140|141|142|143|144|145|146|147|148|149|150|151|152|153|154|155|156|157|158|159|160|161|162|163|164|165|166|167|168|169|170|171|172|173|174|175|176|177|178|179|180|181|182|183|184|185|186|187|188|189|190|191|192|193|194|195|196|197|198|199|200|201|202|203|204|205|206|207|208|209|210|211|212|213|214|215|216|217|218|219|220|221|222|223|224|225|226|227|228|229|230|231|232|233|234|235|236|237|238|239|240|241|242|243|244|245|246|247|248|249|250|251|252|253|254|255;
export type u8 = number;
// export type u8 = number;
// Jquery lite
export const $ = (s: string): HTMLElement => document.getElementById(s) as HTMLElement;
export const format_hex = (n: number): string => n.toString(16).toUpperCase().padStart(2, "0");
export const format_hex = (n: u8): string => n.toString(16).toUpperCase().padStart(2, "0");
export const byte_array_to_js_source = (a: Array<u8>): string => {
let str = "[";

View file

@ -1,6 +1,6 @@
import { u8 } from "./etc";
import { EventHandler } from "./eventHandler";
import { Instruction, ParameterType } from "./instructionSet";
import { u8 } from "./num.js";
export enum CpuEvent {
MemoryChanged,

View file

@ -3,29 +3,13 @@ import { $ } from "./etc";
import { ISA } from "./instructionSet";
import { generate_isa } from "./isaGenerator";
import { UI } from "./ui";
import { u8 } from "./num";
import "./style.scss";
function main(): void {
// const program = [
// 0x2f, 0x00, 0xf0, 0x20, 0x07, 0x00, 0x50, 0x05, 0x06, 0x07, 0x11, 0x00, 0x05, 0xfe, 0x07, 0x30, 0x00, 0x10, 0x03,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57,
// 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x0a, 0x00, 0x00, 0x00,
// ];
const program = [
0x01, 0x00, 0x01, 0x00, 0xa0, 0x10, 0xff, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0xa1,
const program: Array<u8> = [
0x2f, 0x00, 0xf0, 0x20, 0x07, 0x00, 0x50, 0x05, 0x06, 0x07, 0x11, 0x00, 0x05, 0xfe, 0x07, 0x30, 0x00, 0x10, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -41,6 +25,23 @@ function main(): void {
0x6f, 0x72, 0x6c, 0x64, 0x21, 0x0a, 0x00, 0x00, 0x00,
];
// const program = [
// 0x01, 0x00, 0x01, 0x00, 0xa0, 0x10, 0xff, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0xa1,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57,
// 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x0a, 0x00, 0x00, 0x00,
// ];
const computer = new Computer();
const ui = new UI();
@ -68,7 +69,7 @@ function main(): void {
const data = e.target.result;
if (data instanceof ArrayBuffer) {
const view = new Uint8Array(data);
const array = [...view];
const array = [...view] as Array<u8>;
ui.stop_auto();
computer.reset();
computer.load_memory(array);
@ -79,6 +80,19 @@ function main(): void {
});
reader.readAsArrayBuffer(file);
});
$("save_button").addEventListener("click", () => {
const memory = computer.dump_memory();
const buffer = new Uint8Array(memory);
const blob = new Blob([buffer], { type: "application/octet-stream" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "bin.bin";
link.style.display = "none";
link.click();
});
}
document.addEventListener("DOMContentLoaded", () => {

View file

@ -1,5 +1,6 @@
import { CpuEvent, CpuEventHandler } from "./events";
import { format_hex, u8 } from "./etc";
import { format_hex } from "./etc";
import { isU3, m256, u1, u3, u8 } from "./num";
export enum ParamType {
Const,
@ -37,10 +38,11 @@ interface GenericComputer {
setMemory: (address: u8, value: u8) => void;
setProgramCounter: (address: u8) => void;
getProgramCounter: () => u8;
getRegister: (number: u8) => u8;
setRegister: (number: u8, value: u8) => void;
getRegister: (number: u3) => u8;
setRegister: (number: u3, value: u8) => void;
pushCallStack: (address: u8) => boolean;
popCallStack: () => u8 | null;
setBank: (bank_no: u1) => void;
}
interface AfterExecutionComputerAction {
@ -55,7 +57,7 @@ export interface Instruction {
readonly params: Array<ParameterType>;
readonly execute: (
computer_reference: GenericComputer,
parameters: Uint8Array,
parameters: Array<u8>,
a: AfterExecutionComputerAction
) => void;
}
@ -104,10 +106,13 @@ ISA.insertInstruction(0x10, {
ISA.insertInstruction(0x20, {
name: "LoadToRegister",
desc: "Sets the byte in register (P1) to be the contents of memory cell at address in register (P2)",
params: [new RegisParam("Set this register to"), new MemorParam("the byte held in this memory address")],
params: [new RegisParam("Set this register to"), new RegisParam("the byte held in this memory address")],
execute(c, p) {
const [register_no, mem_address] = p;
c.setRegister(register_no, c.getMemory(c.getRegister(mem_address)));
const [register_no, register_2] = p;
if (!isU3(register_no)) throw new Error("TODO");
if (!isU3(register_2)) throw new Error("TODO");
c.setRegister(register_no, c.getMemory(c.getRegister(register_2)));
},
});
@ -117,6 +122,7 @@ ISA.insertInstruction(0x21, {
params: [new RegisParam("Write the byte in this register"), new MemorParam("To this memory address")],
execute(c, p) {
const [register_no, mem_address] = p;
if (!isU3(register_no)) throw new Error("TODO");
c.setMemory(mem_address, c.getRegister(register_no));
},
});
@ -127,6 +133,7 @@ ISA.insertInstruction(0x2f, {
params: [new RegisParam("Set this register"), new ConstParam("to this constant")],
execute(c, p) {
const [register_no, value] = p;
if (!isU3(register_no)) throw new Error("TODO");
c.setRegister(register_no, value);
},
});
@ -137,6 +144,7 @@ ISA.insertInstruction(0x11, {
params: [new ConstParam("Set program counter to this constant"), new RegisParam("if this register's 1 bit is set")],
execute(c, p, a) {
const [new_address, check_register_no] = p;
if (!isU3(check_register_no)) throw new Error("TODO");
if (c.getRegister(check_register_no) % 2 === 1) {
c.setProgramCounter(new_address);
a.noStep();
@ -150,8 +158,9 @@ ISA.insertInstruction(0x30, {
params: [new RegisParam("register to be incremented")],
execute(c, p) {
const register_no = p[0];
if (!isU3(register_no)) throw new Error("TODO");
const current_value = c.getRegister(register_no);
const new_value = (current_value + 1) % 256;
const new_value = m256(current_value + 1);
c.setRegister(register_no, new_value);
},
});
@ -162,12 +171,13 @@ ISA.insertInstruction(0x31, {
params: [new RegisParam("register to be decremented")],
execute(c, p) {
const register_no = p[0];
if (!isU3(register_no)) throw new Error("TODO");
const current_value = c.getRegister(register_no);
let new_value = current_value - 1;
if (new_value === -1) {
new_value = 255;
}
c.setRegister(register_no, new_value);
c.setRegister(register_no, new_value as u8);
},
});
@ -177,7 +187,9 @@ ISA.insertInstruction(0x40, {
params: [new RegisParam("set this register to"), new RegisParam("it's sum with the value in this register")],
execute(c, p) {
const [register_no_1, register_no_2] = p;
const new_value = (c.getRegister(register_no_1) + c.getRegister(register_no_2)) % 256;
if (!isU3(register_no_1)) throw new Error("TODO");
if (!isU3(register_no_2)) throw new Error("TODO");
const new_value = m256(c.getRegister(register_no_1) + c.getRegister(register_no_2));
c.setRegister(register_no_1, new_value);
},
});
@ -192,6 +204,9 @@ ISA.insertInstruction(0x50, {
],
execute(c, p) {
const [register_no_1, register_no_2, register_no_3] = p;
if (!isU3(register_no_1)) throw new Error("TODO");
if (!isU3(register_no_2)) throw new Error("TODO");
if (!isU3(register_no_3)) throw new Error("TODO");
const truth = c.getRegister(register_no_2) === c.getRegister(register_no_3) ? 0x01 : 0x00;
c.setRegister(register_no_1, truth);
},
@ -203,6 +218,7 @@ ISA.insertInstruction(0xfe, {
params: [new RegisParam("Register to print from")],
execute(c, p, a) {
const register_no = p[0];
if (!isU3(register_no)) throw new Error("TODO");
const asciiByte = c.getRegister(register_no);
const char = String.fromCharCode(asciiByte);
@ -216,8 +232,10 @@ ISA.insertInstruction(0x48, {
params: [new RegisParam(""), new RegisParam("")],
execute(c, p) {
const [register_no_1, register_no_2] = p;
if (!isU3(register_no_1)) throw new Error("TODO");
if (!isU3(register_no_2)) throw new Error("TODO");
const new_value = c.getRegister(register_no_1) & c.getMemory(register_no_2);
c.setRegister(register_no_1, new_value);
c.setRegister(register_no_1, new_value as u8);
},
});
@ -227,6 +245,7 @@ ISA.insertInstruction(0xff, {
params: [new RegisParam("Register to print from")],
execute(c, p, a) {
const register_no = p[0];
if (!isU3(register_no)) throw new Error("TODO");
const byte = c.getRegister(register_no);
a.dispatch(CpuEvent.Print, byte.toString(10));
@ -239,6 +258,8 @@ ISA.insertInstruction(0xfd, {
params: [new RegisParam("Upper 8 bits of number"), new RegisParam("Lower 8 bits of number")],
execute(c, p, a) {
const [upper_register_no, lower_register_no] = p;
if (!isU3(upper_register_no)) throw new Error("TODO");
if (!isU3(lower_register_no)) throw new Error("TODO");
const upper = c.getRegister(upper_register_no);
const lower = c.getRegister(lower_register_no);
const sum = upper * 16 * 16 + lower;
@ -282,8 +303,14 @@ ISA.insertInstruction(0xa1, {
throw new Error("TODO handle this");
}
c.setProgramCounter(new_address + 1);
c.setProgramCounter(m256(new_address + 1));
a.noStep();
},
});
ISA.insertInstruction(0xb1, {
name: "Set bank",
desc: "Selects which bank of memory to write and read to",
params: [new ConstParam("Bank number")],
execute(c, p, a) {},
});

View file

@ -1,5 +1,6 @@
import { format_hex, u8 } from "./etc";
import { format_hex } from "./etc";
import { Instruction, InstructionSet } from "./instructionSet";
import { u8 } from "./num.js";
export function generate_isa(iset: InstructionSet): string {
const instructions: Array<[u8, Instruction]> = [];

307
src/num.ts Normal file
View file

@ -0,0 +1,307 @@
export type u8 =
| 0
| 1
| 2
| 3
| 4
| 5
| 6
| 7
| 8
| 9
| 10
| 11
| 12
| 13
| 14
| 15
| 16
| 17
| 18
| 19
| 20
| 21
| 22
| 23
| 24
| 25
| 26
| 27
| 28
| 29
| 30
| 31
| 32
| 33
| 34
| 35
| 36
| 37
| 38
| 39
| 40
| 41
| 42
| 43
| 44
| 45
| 46
| 47
| 48
| 49
| 50
| 51
| 52
| 53
| 54
| 55
| 56
| 57
| 58
| 59
| 60
| 61
| 62
| 63
| 64
| 65
| 66
| 67
| 68
| 69
| 70
| 71
| 72
| 73
| 74
| 75
| 76
| 77
| 78
| 79
| 80
| 81
| 82
| 83
| 84
| 85
| 86
| 87
| 88
| 89
| 90
| 91
| 92
| 93
| 94
| 95
| 96
| 97
| 98
| 99
| 100
| 101
| 102
| 103
| 104
| 105
| 106
| 107
| 108
| 109
| 110
| 111
| 112
| 113
| 114
| 115
| 116
| 117
| 118
| 119
| 120
| 121
| 122
| 123
| 124
| 125
| 126
| 127
| 128
| 129
| 130
| 131
| 132
| 133
| 134
| 135
| 136
| 137
| 138
| 139
| 140
| 141
| 142
| 143
| 144
| 145
| 146
| 147
| 148
| 149
| 150
| 151
| 152
| 153
| 154
| 155
| 156
| 157
| 158
| 159
| 160
| 161
| 162
| 163
| 164
| 165
| 166
| 167
| 168
| 169
| 170
| 171
| 172
| 173
| 174
| 175
| 176
| 177
| 178
| 179
| 180
| 181
| 182
| 183
| 184
| 185
| 186
| 187
| 188
| 189
| 190
| 191
| 192
| 193
| 194
| 195
| 196
| 197
| 198
| 199
| 200
| 201
| 202
| 203
| 204
| 205
| 206
| 207
| 208
| 209
| 210
| 211
| 212
| 213
| 214
| 215
| 216
| 217
| 218
| 219
| 220
| 221
| 222
| 223
| 224
| 225
| 226
| 227
| 228
| 229
| 230
| 231
| 232
| 233
| 234
| 235
| 236
| 237
| 238
| 239
| 240
| 241
| 242
| 243
| 244
| 245
| 246
| 247
| 248
| 249
| 250
| 251
| 252
| 253
| 254
| 255;
export type u1 = 0 | 1;
export type u2 = 0 | 1 | 2 | 3;
export type u3 = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;
export type u4 = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15;
export const m256 = (number: number): u8 => (number % 256) as u8;
/**
* Determines whether a number is a u3 type (unsigned 3-bit integer).
* Does not check for non integers
*
* @param n - Input number to be checked
* @returns true or false
*
*/
export function isU3(n: number): n is u3 {
if (n < 8 && n >= 0) {
return true;
}
return false;
}
/**
* Determines whether a number is a u4 type (unsigned 4-bit integer).
* Does not check for non integers
*
* @param n - Input number to be checked
* @returns true or false
*
*/
export function isU4(n: number): n is u4 {
if (n < 16 && n >= 0) {
return true;
}
return false;
}
/**
* Determines whether a number is a u8 type (unsigned 8-bit integer).
* Does not check for non integers
*
* @param n - Input number to be checked
* @returns true or false
*
*/
export function isU8(n: number): n is u8 {
if (n < 256 && n >= 0) {
return true;
}
return false;
}

View file

@ -7,16 +7,26 @@ pre {
}
:root {
--Border: Yellow;
--mem-instruction: greenyellow;
--mem-register: Purple;
--mem-constant: lightgray;
--mem-memory: lightgray;
--mem-invalid: red;
--Border: #ffff00;
// --mem-instruction: #adff2f;
// --mem-register: #800080;
// --mem-constant: #d3d3d3;
// --mem-memory: #d3d3d3;
// --mem-invalid: #ff0000;
// --mem-instruction: #2f962a;
// --mem-register: #dc21d1;
// --mem-constant: #d3d3d3;
// --mem-memory: #4d86f0;
// --mem-invalid: #bf2e2e;
--mem-instruction: #3af78f;
--mem-memory: #ff26a8;
--mem-register: #9e0ef7;
--mem-constant: #19f7f0;
--mem-invalid: #bf2e2e;
}
body {
color: gray;
color: #808080;
background-color: black;
font-size: 2.5em;
font-family: monospace;
@ -63,7 +73,7 @@ body {
}
#printout {
border: 4px dashed yellow;
border: 4px dashed var(--Border);
grid-area: printout;
padding: 10px;
word-wrap: break-word;
@ -77,7 +87,7 @@ body {
display: flex;
flex-direction: column;
gap: 5px;
border: 5px dashed yellow;
border: 5px dashed var(--Border);
#expl_box {
padding-inline: 20px;
padding-block-start: 10px;
@ -128,13 +138,10 @@ body {
.program_counter {
outline: 3px solid orange;
}
.instruction_argument {
outline: 3px dashed purple;
}
.instruction_argument,
.current_instruction {
outline: 3px dashed greenyellow;
outline: 3px dashed var(--color);
}
.invalid {
&::after {
user-select: none;

View file

@ -1,5 +1,5 @@
import { CpuEvent, CpuEventHandler, UiEvent, UiEventHandler } from "./events";
import { $, el, format_hex, u8 } from "./etc";
import { $, el, format_hex } from "./etc";
import { EventHandler } from "./eventHandler";
import { InstructionExplainer } from "./ui/instructionExplainer";
import { MemoryView } from "./ui/memoryView";
@ -75,57 +75,16 @@ export class UI {
}
init_events(cpu_events: CpuEventHandler): void {
cpu_events.listen(CpuEvent.MemoryChanged, ({ address, value }) => {
this.memory.set_cell_value(address, value);
});
cpu_events.listen(CpuEvent.RegisterChanged, ({ register_no, value }) => {
this.register_cells[register_no].textContent = format_hex(value);
});
cpu_events.listen(CpuEvent.ProgramCounterChanged, ({ counter }) => {
this.memory.set_program_counter(counter);
});
cpu_events.listen(CpuEvent.Print, (char) => {
this.printout.textContent = (this.printout.textContent ?? "") + char;
});
cpu_events.listen(CpuEvent.Reset, () => {
this.reset();
});
this.frequencyIndicator.init_cpu_events(cpu_events);
this.memory.init_cpu_events(cpu_events);
this.instruction_explainer.init_cpu_events(cpu_events);
cpu_events.listen(CpuEvent.ParameterParsed, ({ param, code, pos }) => {
this.memory.add_cell_class(pos, "instruction_argument");
this.instruction_explainer.add_param(param, pos, code);
const t = param.type;
this.memory.remove_cell_class(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");
}
this.memory.add_cell_class(pos, name);
});
cpu_events.listen(CpuEvent.InstructionParsed, ({ instr, code, pos }) => {
this.instruction_explainer.add_instruction(instr, pos, code);
this.memory.remove_all_cell_class("instruction_argument");
this.memory.remove_all_cell_class("current_instruction");
this.memory.add_cell_class(pos, "current_instruction");
this.memory.remove_cell_class(pos, "constant", "register", "memory", "invalid");
this.memory.add_cell_class(pos, "instruction");
});
cpu_events.listen(CpuEvent.InvalidParsed, ({ code, pos }) => {
this.memory.remove_cell_class(pos, "constant", "register", "memory", "instruction");
this.memory.add_cell_class(pos, "invalid");
this.instruction_explainer.add_invalid(pos, code);
});
}
reset(): void {

View file

@ -13,6 +13,7 @@ export class frequencyIndicator implements UiComponent {
}
start(): void {
this.last_time = performance.now();
if (this.running !== null) {
throw new Error("Tried starting frequencyIndicator twice!");
}
@ -26,10 +27,15 @@ export class frequencyIndicator implements UiComponent {
}
update_indicator(): void {
if (this.last_value !== this.count) {
this.element.textContent = `${this.count}hz`;
this.last_value = this.count;
const new_time = performance.now();
const dt = (new_time - this.last_time) / 1000 || 1;
const value = Math.round(this.count / dt);
if (this.last_value !== value) {
this.element.textContent = `${value}hz`;
this.last_value = value;
}
this.last_time = new_time;
this.count = 0;
}
@ -44,7 +50,7 @@ export class frequencyIndicator implements UiComponent {
this.count = 0;
this.last_value = 0;
}
init_cpu_events(c: CpuEventHandler) {
init_cpu_events(c: CpuEventHandler): void {
c.listen(CpuEvent.ClockCycle, () => {
this.count += 1;
});

View file

@ -1,6 +1,7 @@
import { el, format_hex, u8 } from "../etc";
import { el, format_hex } from "../etc";
import { CpuEvent, CpuEventHandler, UiEventHandler } from "../events";
import { Instruction, ParamType, ParameterType } from "../instructionSet";
import { u8 } from "../num";
import { UiComponent } from "./uiComponent";
export class InstructionExplainer implements UiComponent {
@ -41,13 +42,23 @@ export class InstructionExplainer implements UiComponent {
this.add_box(format_hex(byte), param.desc, name);
}
add_invalid(pos: u8, byte: u8) {
add_invalid(pos: u8, byte: u8): void {
this.reset();
this.add_box(format_hex(byte), "Invalid Instruction", "invalid");
}
init_events(eh: UiEventHandler): void {}
init_cpu_events(c: CpuEventHandler) {}
init_cpu_events(c: CpuEventHandler): void {
c.listen(CpuEvent.ParameterParsed, ({ param, code, pos }) => {
this.add_param(param, pos, code);
});
c.listen(CpuEvent.InstructionParsed, ({ instr, code, pos }) => {
this.add_instruction(instr, pos, code);
});
c.listen(CpuEvent.InvalidParsed, ({ code, pos }) => {
this.add_invalid(pos, code);
});
}
reset(): void {
this.element.innerHTML = "";

View file

@ -1,11 +1,9 @@
import { el, format_hex, u8 } from "../etc";
import { CpuEventHandler, UiEventHandler } from "../events";
import { el, format_hex } from "../etc";
import { CpuEvent, CpuEventHandler, UiEventHandler } from "../events";
import { ParamType } from "../instructionSet";
import { u8 } from "../num.js";
import { UiComponent } from "./uiComponent";
export enum CellTag {
ProgramCounter = "program_counter",
}
type MemoryCell = {
el: HTMLElement;
};
@ -74,5 +72,39 @@ export class MemoryView implements UiComponent {
init_events(eh: UiEventHandler): void {
this;
}
init_cpu_events(c: CpuEventHandler) {}
init_cpu_events(c: CpuEventHandler): void {
c.listen(CpuEvent.MemoryChanged, ({ address, value }) => {
this.set_cell_value(address, value);
});
c.listen(CpuEvent.ProgramCounterChanged, ({ counter }) => {
this.set_program_counter(counter);
});
c.listen(CpuEvent.ParameterParsed, ({ param, code, pos }) => {
this.add_cell_class(pos, "instruction_argument");
const t = param.type;
this.remove_cell_class(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");
}
this.add_cell_class(pos, name);
});
c.listen(CpuEvent.InstructionParsed, ({ instr, code, pos }) => {
this.remove_all_cell_class("instruction_argument");
this.remove_all_cell_class("current_instruction");
this.add_cell_class(pos, "current_instruction");
this.remove_cell_class(pos, "constant", "register", "memory", "invalid");
this.add_cell_class(pos, "instruction");
});
c.listen(CpuEvent.InvalidParsed, ({ code, pos }) => {
this.remove_cell_class(pos, "constant", "register", "memory", "instruction");
this.add_cell_class(pos, "invalid");
});
}
}

View file

@ -1,4 +1,3 @@
import { u8 } from "../etc";
import { CpuEventHandler, UiEventHandler } from "../events";
export interface UiComponent {