commit before ISA revamp
This commit is contained in:
parent
f290b836cf
commit
e827ac11b5
7
TODO
7
TODO
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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> {
|
||||
|
|
25
src/index.ts
25
src/index.ts
|
@ -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", () => {
|
||||
main();
|
||||
// at least you know it's bad
|
||||
try {
|
||||
main();
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
22
src/ui/bankIndicator.ts
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue