cardiograph-computer/simulator.js

205 lines
5.1 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { INITIAL_IP_ADDRESS, CYCLE_LIMIT } = require('./machine.config');
const { logRunningHeader, num2hex } = require('./logging.js');
const display = require('./display.js');
// STATE
const CPU = {
running: false,
IP: INITIAL_IP_ADDRESS,
CF: 0,
Acc: 0,
memory: null,
loadMemory: (data) => { // data: Uint8Array
if (data.length > 256) { throw new Error("Out of memory error (program too long)"); }
CPU.memory = data;
},
currentInstruction: {
opcode: null,
argument: null,
mnemonic: null,
}
}
// FUNCTIONS THAT MODIFY STATE
const Instructions = {
end: () => {
CPU.currentInstruction.mnemonic = 'END';
CPU.running = false;
},
store_lit: (lit) => {
CPU.currentInstruction.mnemonic = 'STO lit';
CPU.memory[lit] = CPU.Acc;
CPU.IP = CPU.IP += 2;
},
store_addr: (addr) => {
CPU.currentInstruction.mnemonic = 'STO addr';
CPU.memory[CPU.memory[addr]] = CPU.Acc;
CPU.IP = CPU.IP += 2;
},
load_lit: (lit) => {
CPU.currentInstruction.mnemonic = 'LDA lit';
CPU.Acc = lit;
CPU.IP = CPU.IP += 2;
},
load_addr: (addr) => {
CPU.currentInstruction.mnemonic = 'LDA addr';
console.log('mem at addr: ', CPU.memory[addr]);
CPU.Acc = CPU.memory[addr];
CPU.IP = CPU.IP += 2;
},
add_lit: (lit) => {
CPU.currentInstruction.mnemonic = 'ADD lit';
let sum = CPU.Acc + lit;
if (sum > 255) {
CPU.CF = 1;
CPU.Acc = (sum % 255) - 1;
} else {
CPU.CF = 0;
CPU.Acc = sum;
}
CPU.IP = CPU.IP += 2;
},
add_addr: (addr) => {
CPU.currentInstruction.mnemonic = 'ADD addr';
let sum = CPU.Acc + CPU.memory[addr];
if (sum > 15) {
CPU.CF = 1;
CPU.Acc = (sum % 15) - 1;
} else {
CPU.CF = 0;
CPU.Acc = sum;
}
CPU.IP = CPU.IP += 2;
},
sub_lit: (lit) => {
CPU.currentInstruction.mnemonic = 'SUB lit';
let sum = CPU.Acc - lit;
if (sum < 0) {
CPU.CF = 1;
CPU.Acc = (sum % 15) + 1; // FIXME ???
} else {
CPU.CF = 0;
CPU.Acc = sum;
}
CPU.IP = CPU.IP += 2;
},
sub_addr: (addr) => { // TODO: carry flag
console.log("SUB addr");
CPU.Acc = CPU.Acc - CPU.memory[addr];
CPU.IP = CPU.IP += 2;
},
hop_lit: (lit) => {
CPU.currentInstruction.mnemonic = `HOP lit \n ↳ Memory at IP+2 and +3: ${CPU.memory[CPU.IP+2]}, ${CPU.memory[CPU.IP+3]}`;
if (CPU.Acc === lit) {
CPU.IP += 4;
} else {
CPU.IP += 2;
}
},
hop_addr: (addr) => {
CPU.currentInstruction.mnemonic = 'HOP addr';
if (CPU.Acc === CPU.memory[addr]) {
CPU.IP += 4;
} else {
CPU.IP += 2;
}
},
jump_lit: (lit) => {
CPU.currentInstruction.mnemonic = 'JMP lit';
CPU.IP = lit;
},
jump_addr: (addr) => {
CPU.currentInstruction.mnemonic = 'JMP addr';
CPU.IP = CPU.memory[addr];
},
carry_clear: () => {
CPU.currentInstruction.mnemonic = 'CFC';
CPU.CF = 0;
CPU.IP += 2;
},
carry_hop: () => {
CPU.currentInstruction.mnemonic = `CHP \n ↳ Memory at IP+2 and +3: ${CPU.memory[CPU.IP+2]}, ${CPU.memory[CPU.IP+3]}`;
if (CPU.CF != 0) {
CPU.IP += 4;
} else {
CPU.IP += 2;
}
},
}
const opcodes2mnemonics = {
0: (arg) => Instructions.end(arg),
1: (arg) => Instructions.store_lit(arg),
2: (arg) => Instructions.store_addr(arg),
3: (arg) => Instructions.load_lit(arg),
4: (arg) => Instructions.load_addr(arg),
5: (arg) => Instructions.add_lit(arg),
6: (arg) => Instructions.add_addr(arg),
7: (arg) => Instructions.sub_lit(arg),
8: (arg) => Instructions.sub_addr(arg),
9: (arg) => Instructions.hop_lit(arg),
10: (arg) => Instructions.hop_addr(arg),
11: (arg) => Instructions.jump_lit(arg),
12: (arg) => Instructions.jump_addr(arg),
13: (arg) => Instructions.carry_clear(arg),
14: (arg) => Instructions.carry_hop(arg),
};
function stepCPU() {
CPU.currentInstruction.opcode = CPU.memory[CPU.IP];
CPU.currentInstruction.argument = CPU.memory[CPU.IP+1];
let executeInstruction = opcodes2mnemonics[CPU.currentInstruction.opcode];
executeInstruction(CPU.currentInstruction.argument);
}
exports.runProgram = async (code, debug = false) => {
if (debug) logRunningHeader();
CPU.loadMemory(code);
CPU.running = true;
let step = 0;
while (true) {
step = step + 1;
if (CYCLE_LIMIT && (step > CYCLE_LIMIT)) { break; } // Temporary limit as a lazy way to halt infinite loops:
if (!CPU.running) break;
if (CPU.IP >= CPU.memory.length) break;
stepCPU();
console.group(`Step`);
if (!debug) console.clear();
display.printDisplay(CPU.memory);
logCPUState();
if (!debug) await new Promise(resolve => setTimeout(resolve, 75));
console.groupEnd('Step');
};
}
// FUNCTIONS THAT PULL INFO FROM STATE TO DISPLAY
function logCPUState() {
console.log();
console.log('Mnemonic:', CPU.currentInstruction.mnemonic);
console.log(`Machine: $${num2hex(CPU.currentInstruction.opcode)} $${num2hex(CPU.currentInstruction.argument)}`);
console.log();
console.log( `IP: $${num2hex(CPU.IP)} Acc: $${num2hex(CPU.Acc)} CF: ${CPU.CF}  ${CPU.running ? "running" : "halted" }` );
console.log();
};