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 styling
Determine how to implement 16 bit integers in code (new instructions?)
UI for screen (toggling (click an icon?))
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 { byte_array_to_js_source, format_hex } from "./etc";
import { Instruction, ISA } from "./instructionSet";
import { m256, u1, u3, u8 } from "./num";
import { m256, u1, u2, u3, u8 } from "./num";
export type TempInstrState = {
pos: u8;
@ -10,13 +10,21 @@ export type TempInstrState = {
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 {
private memory: Uint8Array = new Uint8Array(256);
private vram: Uint8Array = new Uint8Array(256);
private banks: [Uint8Array, Uint8Array, Uint8Array, Uint8Array] = init_banks();
private registers: Uint8Array = new Uint8Array(8);
private call_stack: Array<u8> = [];
private carry_flag: boolean = false;
private program_counter: u8 = 0;
private bank: u1 = 0;
private bank: u2 = 0;
private current_instr: TempInstrState | null = null;
events: CpuEventHandler = new CpuEventHandler();
@ -94,34 +102,22 @@ export class Computer {
}
this.events.dispatch(CpuEvent.Cycle);
}
private getMemorySilent(address: u8, bank_override?: u1): u8 {
const banks = [this.memory, this.vram];
const bank = banks[bank_override ?? this.bank];
private getMemorySilent(address: u8, bank_override?: u2): u8 {
const bank = this.banks[bank_override ?? this.bank];
const value = bank[address] as u8;
return value;
}
getMemory(address: u8, bank_override?: u1): u8 {
getMemory(address: u8, bank_override?: u2): u8 {
const value = this.getMemorySilent(address, bank_override);
this.events.dispatch(CpuEvent.MemoryAccessed, { address, bank: this.bank, value });
return value;
}
setMemory(address: u8, value: u8): void {
let bank: Uint8Array | undefined;
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.banks[this.bank][address] = value;
this.events.dispatch(CpuEvent.MemoryChanged, { address, bank: this.bank, value });
}
@ -153,18 +149,28 @@ export class Computer {
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.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 {
this.events.dispatch(CpuEvent.Reset);
this.memory = new Uint8Array(256);
this.banks = init_banks();
this.registers = new Uint8Array(8);
this.call_stack = [];
this.current_instr = null;
this.program_counter = 0;
this.carry_flag = false;
}
init_events(ui: UiEventHandler): void {
@ -175,20 +181,21 @@ export class Computer {
}
load_memory(program: Array<u8>): void {
// TODO allow loading into other banks
console.log(byte_array_to_js_source(program));
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
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.program_counter = 0;
}
dump_memory(): Uint8Array {
return this.memory;
return this.banks[0];
}
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 const SVG_NS = "http://www.w3.org/2000/svg";

View file

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

View file

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

View file

@ -5,7 +5,7 @@
*/
import { CpuEvent, CpuEventHandler } from "./events";
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 {
Const,
@ -47,7 +47,9 @@ interface GenericComputer {
setRegister: (number: u3, value: u8) => void;
pushCallStack: (address: u8) => boolean;
popCallStack: () => u8 | null;
setBank: (bank_no: u1) => void;
setBank: (bank_no: u2) => void;
getCarry(): boolean;
setCarry(state: boolean): void;
}
interface AfterExecutionComputerAction {
@ -112,7 +114,7 @@ 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 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) {
const [register_no, register_2] = p;
if (!isU3(register_no)) throw new Error("TODO");
@ -200,6 +202,21 @@ ISA.insertInstruction(0x40, {
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, {
name: "Equals",
@ -321,7 +338,7 @@ ISA.insertInstruction(0xb1, {
params: [new ConstParam("Bank number")],
execute(c, p) {
const bank_no = p[0];
if (!(bank_no === 1 || bank_no === 0)) {
if (!isU2(bank_no)) {
throw new Error("TODO");
}
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 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).
* Does not check for non integers

View file

@ -52,7 +52,7 @@ body {
"title . . . ."
"title . . . printout "
"title . . . printout "
". buttons buttons buttons buttons";
". buttons buttons . .";
#memory {
grid-column: 2/4;
grid-row: 2/6;
@ -198,6 +198,53 @@ label.button:hover {
#controls_bar {
grid-area: buttons;
display: flex;
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 {
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 !== null) {
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 {
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 GREEN_SCALE = 255 / 2 ** 3;
@ -58,7 +52,6 @@ export class Screen implements UiComponent {
const blue = (value & 0b11) * BLUE_SCALE;
const color = `rgb(${red},${green},${blue})`;
console.log(x, y, value, color);
this.ctx.fillStyle = color;
this.ctx.fillRect(...point, ...this.scale);
}