Likely overzealous numeric type enforcement.
This commit is contained in:
parent
e7cb198123
commit
0764614b66
5
TODO
5
TODO
|
@ -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
|
|
@ -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>
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = "[";
|
||||
|
|
|
@ -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,
|
||||
|
|
54
src/index.ts
54
src/index.ts
|
@ -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", () => {
|
||||
|
|
|
@ -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) {},
|
||||
});
|
||||
|
|
|
@ -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
307
src/num.ts
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
|
|
43
src/ui.ts
43
src/ui.ts
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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 = "";
|
||||
|
|
|
@ -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");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { u8 } from "../etc";
|
||||
import { CpuEventHandler, UiEventHandler } from "../events";
|
||||
|
||||
export interface UiComponent {
|
||||
|
|
Loading…
Reference in a new issue