commit before ISA revamp

This commit is contained in:
Alexander Bass 2024-02-24 20:33:54 -05:00
parent f290b836cf
commit e827ac11b5
11 changed files with 164 additions and 54 deletions

7
TODO
View file

@ -5,9 +5,10 @@ Move start/stop/auto logic into computer
Speed control slider behavior Speed control slider behavior
Speed control slider styling Speed control slider styling
Determine how to implement 16 bit integers in code (new instructions?)
UI for screen (toggling (click an icon?)) UI for screen (toggling (click an icon?))
UI for togging other UI elements UI for togging other UI elements
Instruction assign memory based on register UI for showing which Memory bank is selected
VRAM select instruction
Error log

View file

@ -1,7 +1,7 @@
import { CpuEvent, CpuEventHandler, UiEvent, UiEventHandler } from "./events"; import { CpuEvent, CpuEventHandler, UiEvent, UiEventHandler } from "./events";
import { byte_array_to_js_source, format_hex } from "./etc"; import { byte_array_to_js_source, format_hex } from "./etc";
import { Instruction, ISA } from "./instructionSet"; import { Instruction, ISA } from "./instructionSet";
import { m256, u1, u3, u8 } from "./num"; import { m256, u1, u2, u3, u8 } from "./num";
export type TempInstrState = { export type TempInstrState = {
pos: u8; pos: u8;
@ -10,13 +10,21 @@ export type TempInstrState = {
params: Array<u8>; params: Array<u8>;
}; };
function init_banks(): [Uint8Array, Uint8Array, Uint8Array, Uint8Array] {
const banks = [];
for (let i = 0; i < 4; i++) {
banks.push(new Uint8Array(256));
}
return banks as [Uint8Array, Uint8Array, Uint8Array, Uint8Array];
}
export class Computer { export class Computer {
private memory: Uint8Array = new Uint8Array(256); private banks: [Uint8Array, Uint8Array, Uint8Array, Uint8Array] = init_banks();
private vram: Uint8Array = new Uint8Array(256);
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 program_counter: u8 = 0; private program_counter: u8 = 0;
private bank: u1 = 0; private bank: u2 = 0;
private current_instr: TempInstrState | null = null; private current_instr: TempInstrState | null = null;
events: CpuEventHandler = new CpuEventHandler(); events: CpuEventHandler = new CpuEventHandler();
@ -94,34 +102,22 @@ export class Computer {
} }
this.events.dispatch(CpuEvent.Cycle); this.events.dispatch(CpuEvent.Cycle);
} }
private getMemorySilent(address: u8, bank_override?: u1): u8 { private getMemorySilent(address: u8, bank_override?: u2): u8 {
const banks = [this.memory, this.vram]; const bank = this.banks[bank_override ?? this.bank];
const bank = banks[bank_override ?? this.bank];
const value = bank[address] as u8; const value = bank[address] as u8;
return value; return value;
} }
getMemory(address: u8, bank_override?: u1): u8 { getMemory(address: u8, bank_override?: u2): u8 {
const value = this.getMemorySilent(address, bank_override); const value = this.getMemorySilent(address, bank_override);
this.events.dispatch(CpuEvent.MemoryAccessed, { address, bank: this.bank, value }); this.events.dispatch(CpuEvent.MemoryAccessed, { address, bank: this.bank, value });
return value; return value;
} }
setMemory(address: u8, value: u8): void { setMemory(address: u8, value: u8): void {
let bank: Uint8Array | undefined; this.banks[this.bank][address] = value;
if (this.bank === 0) {
bank = this.memory;
} else if (this.bank === 1) {
bank = this.vram;
} else {
const _: never = this.bank;
}
if (bank === undefined) {
throw new Error("unreachable");
}
bank[address] = value;
this.events.dispatch(CpuEvent.MemoryChanged, { address, bank: this.bank, value }); this.events.dispatch(CpuEvent.MemoryChanged, { address, bank: this.bank, value });
} }
@ -153,18 +149,28 @@ export class Computer {
return this.call_stack.pop() ?? null; return this.call_stack.pop() ?? null;
} }
setBank(bank_no: u1): void { setBank(bank_no: u2): void {
this.events.dispatch(CpuEvent.SwitchBank, { bank: bank_no }); this.events.dispatch(CpuEvent.SwitchBank, { bank: bank_no });
this.bank = bank_no; this.bank = bank_no;
} }
setCarry(state: boolean): void {
this.carry_flag = state;
this.events.dispatch(CpuEvent.SetFlagCarry, true);
}
getCarry(): boolean {
return this.carry_flag;
}
reset(): void { reset(): void {
this.events.dispatch(CpuEvent.Reset); this.events.dispatch(CpuEvent.Reset);
this.memory = new Uint8Array(256); this.banks = init_banks();
this.registers = new Uint8Array(8); this.registers = new Uint8Array(8);
this.call_stack = []; this.call_stack = [];
this.current_instr = null; this.current_instr = null;
this.program_counter = 0; this.program_counter = 0;
this.carry_flag = false;
} }
init_events(ui: UiEventHandler): void { init_events(ui: UiEventHandler): void {
@ -175,20 +181,21 @@ export class Computer {
} }
load_memory(program: Array<u8>): void { load_memory(program: Array<u8>): void {
// TODO allow loading into other banks
console.log(byte_array_to_js_source(program)); console.log(byte_array_to_js_source(program));
const max_loop: u8 = Math.min(255, program.length) as u8; const max_loop: u8 = Math.min(255, program.length) as u8;
for (let i: u8 = 0; i < 255; i++) { for (let i: u8 = 0; i < 256; i++) {
// Don't fire event if no change is made // Don't fire event if no change is made
if (this.memory[i] === program[i]) continue; if (this.banks[0][i] === program[i]) continue;
this.memory[i] = program[i]; this.banks[0][i] = program[i];
this.events.dispatch(CpuEvent.MemoryChanged, { address: i as u8, bank: 0, value: program[i] }); this.events.dispatch(CpuEvent.MemoryChanged, { address: i as u8, bank: 0, value: program[i] });
} }
this.program_counter = 0; this.program_counter = 0;
} }
dump_memory(): Uint8Array { dump_memory(): Uint8Array {
return this.memory; return this.banks[0];
} }
private step_forward(): void { private step_forward(): void {

View file

@ -42,3 +42,5 @@ export function el(type: string, id?: string): HTMLElement | undefined {
} }
export type NonEmptyArray<T> = T[] & { 0: T }; export type NonEmptyArray<T> = T[] & { 0: T };
export const SVG_NS = "http://www.w3.org/2000/svg";

View file

@ -5,7 +5,7 @@
*/ */
import { EventHandler } from "./eventHandler"; import { EventHandler } from "./eventHandler";
import { Instruction, ParameterType } from "./instructionSet"; import { Instruction, ParameterType } from "./instructionSet";
import { u1, u3, u8 } from "./num"; import { u1, u2, u3, u8 } from "./num";
// //
// CPU Event Handler Definition // CPU Event Handler Definition
@ -26,6 +26,7 @@ export enum CpuEvent {
// ClockStopped, // ClockStopped,
MemoryAccessed, MemoryAccessed,
SwitchBank, SwitchBank,
SetFlagCarry,
} }
type VoidDataCpuEventList = CpuEvent.Halt | CpuEvent.Reset | CpuEvent.Cycle; type VoidDataCpuEventList = CpuEvent.Halt | CpuEvent.Reset | CpuEvent.Cycle;
@ -33,16 +34,17 @@ type VoidDataCpuEventList = CpuEvent.Halt | CpuEvent.Reset | CpuEvent.Cycle;
// | CpuEvent.ClockStopped; // | CpuEvent.ClockStopped;
interface CpuEventMap { interface CpuEventMap {
[CpuEvent.MemoryChanged]: { address: u8; bank: u1; value: u8 }; [CpuEvent.MemoryChanged]: { address: u8; bank: u2; value: u8 };
[CpuEvent.MemoryAccessed]: { address: u8; bank: u1; 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.InstructionParsed]: { 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.InvalidParsed]: { pos: u8; code: u8 };
[CpuEvent.InstructionExecuted]: { instr: Instruction }; [CpuEvent.InstructionExecuted]: { instr: Instruction };
[CpuEvent.SwitchBank]: { bank: u1 }; [CpuEvent.SwitchBank]: { bank: u2 };
[CpuEvent.Print]: string; [CpuEvent.Print]: string;
[CpuEvent.SetFlagCarry]: boolean;
} }
export interface CpuEventHandler extends EventHandler<CpuEvent> { export interface CpuEventHandler extends EventHandler<CpuEvent> {

View file

@ -17,6 +17,7 @@ declare global {
interface Window { interface Window {
comp: Computer; comp: Computer;
ui: UI; ui: UI;
firehose: () => void;
} }
} }
@ -78,7 +79,6 @@ function main(): void {
ui.init_events(computer.events); ui.init_events(computer.events);
computer.load_memory(program); computer.load_memory(program);
computer.init_events(ui.events); computer.init_events(ui.events);
window.comp = computer; window.comp = computer;
window.ui = ui; window.ui = ui;
@ -112,10 +112,17 @@ function main(): void {
}); });
reader.readAsArrayBuffer(file); reader.readAsArrayBuffer(file);
}); });
let fire = false;
// computer.events.firehose((ident, data) => { window.firehose = (): void => {
// console.log(`New Event: ${CpuEvent[ident]}. data: `, data); if (fire === false) {
// }); computer.events.firehose((ident, data) => {
console.log(`New Event: ${CpuEvent[ident]}. data: `, data);
});
fire = true;
} else {
console.error("Firehose already started");
}
};
$("save_button").addEventListener("click", () => { $("save_button").addEventListener("click", () => {
const memory = computer.dump_memory(); const memory = computer.dump_memory();
@ -125,11 +132,15 @@ function main(): void {
const link = document.createElement("a"); const link = document.createElement("a");
link.href = url; link.href = url;
link.download = "bin.bin"; link.download = "bin.bin";
link.style.display = "none";
link.click(); link.click();
}); });
} }
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
// at least you know it's bad
try {
main(); main();
} catch (e) {
alert(e);
}
}); });

View file

@ -5,7 +5,7 @@
*/ */
import { CpuEvent, CpuEventHandler } from "./events"; import { CpuEvent, CpuEventHandler } from "./events";
import { format_hex } from "./etc"; import { format_hex } from "./etc";
import { isU3, m256, u1, u3, u8 } from "./num"; import { isU2, isU3, m256, u2, u3, u8 } from "./num";
export enum ParamType { export enum ParamType {
Const, Const,
@ -47,7 +47,9 @@ interface GenericComputer {
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: u1) => void; setBank: (bank_no: u2) => void;
getCarry(): boolean;
setCarry(state: boolean): void;
} }
interface AfterExecutionComputerAction { interface AfterExecutionComputerAction {
@ -112,7 +114,7 @@ ISA.insertInstruction(0x10, {
ISA.insertInstruction(0x20, { ISA.insertInstruction(0x20, {
name: "LoadToRegister", name: "LoadToRegister",
desc: "Sets the byte in register (P1) to be the contents of memory cell at address in register (P2)", 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 RegisParam("the byte held in this memory address")], params: [new RegisParam("Set this register to"), new RegisParam("the byte in the memory addressed by this register")],
execute(c, p) { execute(c, p) {
const [register_no, register_2] = p; const [register_no, register_2] = p;
if (!isU3(register_no)) throw new Error("TODO"); if (!isU3(register_no)) throw new Error("TODO");
@ -200,6 +202,21 @@ ISA.insertInstruction(0x40, {
c.setRegister(register_no_1, new_value); c.setRegister(register_no_1, new_value);
}, },
}); });
ISA.insertInstruction(0x41, {
name: "Subtract",
desc: "Subtracts the contents of (P2) from (P1) and stores result to register (P1).",
params: [
new RegisParam("set this register to"),
new RegisParam("the difference between it in the value in this register"),
],
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 = m256(c.getRegister(register_no_1) - c.getRegister(register_no_2));
c.setRegister(register_no_1, new_value);
},
});
ISA.insertInstruction(0x50, { ISA.insertInstruction(0x50, {
name: "Equals", name: "Equals",
@ -321,7 +338,7 @@ ISA.insertInstruction(0xb1, {
params: [new ConstParam("Bank number")], params: [new ConstParam("Bank number")],
execute(c, p) { execute(c, p) {
const bank_no = p[0]; const bank_no = p[0];
if (!(bank_no === 1 || bank_no === 0)) { if (!isU2(bank_no)) {
throw new Error("TODO"); throw new Error("TODO");
} }
c.setBank(bank_no); c.setBank(bank_no);

View file

@ -30,6 +30,13 @@ export type u4 = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14
export const m256 = (number: number): u8 => (number % 256) as u8; export const m256 = (number: number): u8 => (number % 256) as u8;
export function isU2(n: number): n is u2 {
if (n < 4 && n >= 0) {
return true;
}
return false;
}
/** /**
* Determines whether a number is a u3 type (unsigned 3-bit integer). * Determines whether a number is a u3 type (unsigned 3-bit integer).
* Does not check for non integers * Does not check for non integers

View file

@ -52,7 +52,7 @@ body {
"title . . . ." "title . . . ."
"title . . . printout " "title . . . printout "
"title . . . printout " "title . . . printout "
". buttons buttons buttons buttons"; ". buttons buttons . .";
#memory { #memory {
grid-column: 2/4; grid-column: 2/4;
grid-row: 2/6; grid-row: 2/6;
@ -198,6 +198,53 @@ label.button:hover {
#controls_bar { #controls_bar {
grid-area: buttons; grid-area: buttons;
display: flex; display: flex;
gap: 10px; gap: 10px;
} }
input[type="range"] {
background-color: transparent;
-webkit-appearance: none;
appearance: none;
margin: 18px 0;
// width: 100%;
}
input[type="range"]:focus {
outline: none;
}
// 2024 and we still have to do this
input[type="range"]::-webkit-slider-runnable-track {
height: 0.5em;
cursor: pointer;
background: yellow;
border-radius: 0px;
}
input[type="range"]::-webkit-slider-thumb {
border: 4px solid yellow;
background-color: black;
height: 42px;
width: 20px;
border-radius: 0px;
cursor: pointer;
margin-top: -18px;
-webkit-appearance: none;
}
input[type="range"]:focus::-webkit-slider-runnable-track {
background: yellow;
}
input[type="range"]::-moz-range-track {
height: 0.5em;
cursor: pointer;
background: yellow;
border-radius: 0px;
}
input[type="range"]::-moz-range-thumb {
border: 4px solid yellow;
background-color: black;
height: 36px;
width: 16px;
border-radius: 0px;
cursor: pointer;
}

22
src/ui/bankIndicator.ts Normal file
View file

@ -0,0 +1,22 @@
import { UiEventHandler, CpuEventHandler, CpuEvent } from "../events";
import { u1, u2 } from "../num";
import { UiComponent } from "./uiComponent";
class BankIndicator implements UiComponent {
element: HTMLElement;
events: UiEventHandler;
constructor(element: HTMLElement, events: UiEventHandler) {
this.element = element;
this.events = events;
}
reset(): void {}
select_bank(bank_no: u2): void {}
init_cpu_events(c: CpuEventHandler): void {
c.listen(CpuEvent.SwitchBank, ({ bank }) => {
this.select_bank(bank);
});
}
}

View file

@ -31,7 +31,8 @@ export class MemoryView extends CelledViewer implements UiComponent {
} }
init_cpu_events(c: CpuEventHandler): void { init_cpu_events(c: CpuEventHandler): void {
c.listen(CpuEvent.MemoryAccessed, ({ address, value }) => { c.listen(CpuEvent.MemoryAccessed, ({ address, bank, value }) => {
if (bank !== 0) return;
if (this.last_accessed_cell !== address) { if (this.last_accessed_cell !== address) {
if (this.last_accessed_cell !== null) { if (this.last_accessed_cell !== null) {
this.remove_cell_class(this.last_accessed_cell, "last_access"); this.remove_cell_class(this.last_accessed_cell, "last_access");

View file

@ -43,12 +43,6 @@ export class Screen implements UiComponent {
setPixel(x: u4, y: u4, value: u8): void { setPixel(x: u4, y: u4, value: u8): void {
const point: [number, number] = [x * this.scale[0], y * this.scale[1]]; const point: [number, number] = [x * this.scale[0], y * this.scale[1]];
// const RED_SCALE = 255 / 2 ** 2;
// const GREEN_SCALE = 255 / 2 ** 2;
// const BLUE_SCALE = 255 / 2 ** 2;
// const red = ((value >> 4) & 0b11) * RED_SCALE;
// const green = ((value >> 2) & 0b11) * GREEN_SCALE;
// const blue = (value & 0b11) * BLUE_SCALE;
const RED_SCALE = 255 / 2 ** 3; const RED_SCALE = 255 / 2 ** 3;
const GREEN_SCALE = 255 / 2 ** 3; const GREEN_SCALE = 255 / 2 ** 3;
@ -58,7 +52,6 @@ export class Screen implements UiComponent {
const blue = (value & 0b11) * BLUE_SCALE; const blue = (value & 0b11) * BLUE_SCALE;
const color = `rgb(${red},${green},${blue})`; const color = `rgb(${red},${green},${blue})`;
console.log(x, y, value, color);
this.ctx.fillStyle = color; this.ctx.fillStyle = color;
this.ctx.fillRect(...point, ...this.scale); this.ctx.fillRect(...point, ...this.scale);
} }