cardiograph-computer/simulator.js

203 lines
4.6 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
// TODO: check length of data
CPU.memory = data;
},
}
// FUNCTIONS THAT MODIFY STATE
const Instructions = {
end: () => {
console.log('END');
CPU.running = false;
},
store_lit: (lit) => {
console.log('STO lit#');
CPU.memory[lit] = CPU.Acc;
CPU.IP = CPU.IP += 2;
},
store_addr: (addr) => {
console.log('STO addr');
CPU.memory[CPU.memory[addr]] = CPU.Acc;
CPU.IP = CPU.IP += 2;
},
load_lit: (lit) => {
console.log('LDA lit#');
CPU.Acc = lit;
CPU.IP = CPU.IP += 2;
},
load_addr: (addr) => {
console.log('LDA addr');
console.log('mem at addr: ', CPU.memory[addr]);
CPU.Acc = CPU.memory[addr];
CPU.IP = CPU.IP += 2;
},
add_lit: (lit) => {
console.log("ADD lit");
let sum = CPU.Acc + lit;
if (sum > 15) {
CPU.CF = 1;
CPU.Acc = (sum % 15) - 1;
} else {
CPU.CF = 0;
CPU.Acc = sum;
}
CPU.IP = CPU.IP += 2;
},
add_addr: (addr) => {
console.log("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) => { // TODO: carry flag
console.log("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) => {
console.log("HOP lit");
console.log(` ↳ 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) => {
console.log("HOP addr");
if (CPU.Acc === CPU.memory[addr]) {
CPU.IP += 4;
} else {
CPU.IP += 2;
}
},
jump_lit: (lit) => {
console.log("JMP lit");
CPU.IP = lit;
},
jump_addr: (addr) => {
console.log("JMP addr");
CPU.IP = CPU.memory[addr];
},
carry_clear: () => {
console.log("CFC");
CPU.CF = 0;
CPU.IP += 2;
},
carry_hop: () => {
console.log("CHP");
console.log(` ↳ 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() {
console.group(`IP: ${CPU.IP}`);
let opcode = CPU.memory[CPU.IP];
let argument = CPU.memory[CPU.IP+1];
console.log(`OP: ${opcode} ARG: ${argument}`);
let instruction = opcodes2mnemonics[opcode];
instruction(argument);
logCPUState();
console.groupEnd(`IP:`);
}
exports.runProgram = async (code, debug = false) => {
if (debug) logRunningHeader();
CPU.loadMemory(code);
if (debug) logCPUState();
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;
if (!debug) console.clear();
console.group('Display')
display.printDisplay(CPU.memory);
console.groupEnd('Display');
logCPUState();
if (!debug) await new Promise(resolve => setTimeout(resolve, 75));
};
}
// FUNCTIONS THAT PULL INFO FROM STATE TO DISPLAY
function logCPUState() {
console.log();
console.log( `IP: $${num2hex(CPU.IP)} Acc: $${num2hex(CPU.Acc)} CF: ${CPU.CF}  ${CPU.running ? "running" : "halted" }` );
console.log();
};