Create a version of the CPU as a JS module -- otherwise the same as the previous version

This commit is contained in:
n loewen 2023-07-26 15:03:23 +01:00
parent ba35470c3c
commit 75b6916666
1 changed files with 213 additions and 0 deletions

View File

@ -0,0 +1,213 @@
// NOTES:
//
// - instructions are two bytes long:
// one byte for the opcode, one for the argument
// STATE
const CPU = {
running: false,
IP: 0,
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;
logTableTitled(CPU.memory, 'Current memory');
CPU.IP = CPU.IP += 2;
},
store_addr: (addr) => {
console.log('STO addr');
CPU.memory[CPU.memory[addr]] = CPU.Acc;
logTableTitled(CPU.memory, 'Memory');
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");
CPU.Acc = CPU.Acc - lit;
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]}`);
console.table(CPU.memory);
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("Step CPU");
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("Step CPU");
}
exports.runProgram = (code) => {
CPU.loadMemory(code);
const initialMemory = JSON.parse(JSON.stringify(CPU.memory)); // Hack to make a copy-by-value -- https://stackoverflow.com/questions/18829099/copy-a-variables-value-into-another
console.log();
console.log("————————————————————————————————————————");
let time = new Date();
console.log( `Running at ${time.toLocaleTimeString('en-US')}` );
console.log("————————————————————————————————————————");
logCPUState();
CPU.running = true;
for (let i = 0; i < 255; i++) { // FIXME: temporary limit as a lazy way to halt infinite loops
if (!CPU.running) break;
if (CPU.IP >= CPU.memory.length) break;
stepCPU();
};
}
// FUNCTIONS THAT PULL INFO FROM STATE TO DISPLAY
function logCPUState() {
console.log();
console.group('CPU state');
console.log( `Acc: ${CPU.Acc} IP: ${CPU.IP} CF: ${CPU.CF}  ${CPU.running ? "running" : "halted" }` );
console.log();
console.groupEnd('CPU state');
};
// FUNCTIONS FOR DISPLAYING DATA
function num2hex(num) { return num.toString(16) };
function hex2num(hex) { return parseInt(hex, 16) };
logTableTitled = (memory, tableTitle) => {
console.log();
console.group(tableTitle);
console.table(memory);
console.groupEnd(tableTitle);
};