Computer/src/computer.ts
2024-02-16 00:17:23 -05:00

153 lines
4.4 KiB
TypeScript

import { CpuEvent, MemoryCellType } from "./events";
import { u8 } from "./etc";
import { EventHandler } from "./eventHandler";
import { ConstParam, Instruction, ISA, MemorParam, RegisParam } from "./instructionSet";
export type TempInstrState = {
pos: u8;
params_found: number;
instr: Instruction;
params: Uint8Array;
};
export type ComputerState = {
memory: Uint8Array;
program_counter: u8;
registers: Uint8Array;
current_instruction: TempInstrState | null;
};
export class Computer {
private memory: Uint8Array;
private program_counter: u8;
private registers: Uint8Array;
private current_instr: TempInstrState | null;
events: EventHandler<CpuEvent>;
constructor() {
// 256 bytes for both program and general purpose memory.
this.memory = new Uint8Array(256);
// 8 registers
this.registers = new Uint8Array(8);
this.program_counter = 0;
this.current_instr = null;
this.events = new EventHandler<CpuEvent>();
// Add events
for (const [, e_type] of Object.entries(CpuEvent)) {
this.events.register_event(e_type as CpuEvent);
}
this.events.seal();
}
cycle(): void {
const current_byte = this.memory[this.program_counter];
if (this.current_instr === null) {
const parsed_instruction = ISA.getInstruction(current_byte);
if (parsed_instruction === null) {
this.events.dispatch(CpuEvent.MemoryByteParsed, {
type: MemoryCellType.InvalidInstruction,
pos: this.program_counter,
});
console.log(`Invalid instruction: ${current_byte.toString(16).toUpperCase().padStart(2, "0")}`);
this.step_forward();
return;
}
this.current_instr = {
pos: this.program_counter,
instr: parsed_instruction,
params_found: 0,
params: new Uint8Array(parsed_instruction.params.length),
};
this.events.dispatch(CpuEvent.MemoryByteParsed, {
type: MemoryCellType.Instruction,
pos: this.program_counter,
instr: parsed_instruction,
});
}
if (this.current_instr.pos === this.program_counter && this.current_instr.params.length > 0) {
this.step_forward();
return;
}
if (this.current_instr.params.length !== this.current_instr.params_found) {
let a;
const b = this.current_instr.instr.params[this.current_instr.params_found];
if (b instanceof ConstParam) {
a = MemoryCellType.Constant;
} else if (b instanceof RegisParam) {
a = MemoryCellType.Register;
} else if (b instanceof MemorParam) {
a = MemoryCellType.Memory;
}
this.events.dispatch(CpuEvent.MemoryByteParsed, { type: a, pos: this.program_counter, param: b });
this.current_instr.params[this.current_instr.params_found] = current_byte;
this.current_instr.params_found += 1;
if (this.current_instr.params.length !== this.current_instr.params_found) {
this.step_forward();
return;
}
}
const execution_post_action_state = {
should_step: true,
noStep: function (): void {
this.should_step = false;
},
event: this.events.dispatch.bind(this.events),
};
this.current_instr.instr.execute(this, this.current_instr.params, execution_post_action_state);
this.current_instr = null;
if (execution_post_action_state.should_step) {
this.step_forward();
}
}
getMemory(address: u8): u8 {
return this.memory[address];
}
setMemory(address: u8, value: u8): void {
this.events.dispatch(CpuEvent.MemoryChanged, { address, value });
this.memory[address] = value;
}
getRegister(register_no: u8): u8 {
return this.registers[register_no];
}
setRegister(register_no: u8, value: u8): void {
this.events.dispatch(CpuEvent.RegisterChanged, { register_no, value });
this.registers[register_no] = value;
}
getProgramCounter(): u8 {
return this.program_counter;
}
setProgramCounter(new_value: u8): void {
this.events.dispatch(CpuEvent.ProgramCounterChanged, { counter: new_value });
this.program_counter = new_value;
}
reset(): void {
this.memory = new Uint8Array(256);
this.registers = new Uint8Array(8);
this.current_instr = null;
this.program_counter = 0;
this.events.dispatch(CpuEvent.Reset, null);
}
load_memory(program: Array<u8>): void {
const max_loop = Math.min(this.memory.length, program.length);
for (let i = 0; i < max_loop; i++) {
this.memory[i] = program[i];
this.events.dispatch(CpuEvent.MemoryChanged, { address: i, value: program[i] });
}
this.program_counter = 0;
}
private step_forward(): void {
this.program_counter = (this.program_counter + 1) % 256;
this.events.dispatch(CpuEvent.ProgramCounterChanged, { counter: this.program_counter });
}
}