cardiograph-computer/simulator-sketch-v2.js

274 lines
6.4 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.

// 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");
}
function runCPU() {
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);
};
// RUN IT !
const test_lda_sto = new Uint8Array([
3, 17, // LDA lit ... Acc = 17
1, 14, // STO lit ... @14 = Acc = 17
3, 16, // LDA lit ... Acc = 16
1, 15, // STO lit ... @15 = Acc = 16
2, 15, // STO addr ... @[@15] = Acc = 16 ... mem[mem[addr]] <- Acc
4, 0, // LDA addr ... Acc = @00 = 03
0, 0, // END
0, 0, // DATA
]);
let test_add_sub_nocarry = new Uint8Array([
5, 6, // ADD lit ... acc = 6
7, 1, // SUB lit ... acc = 5
1, 15, // STO lit ... mem[15] = 5
6, 15, // ADD addr ... acc = 10
8, 15, // SUB addr ... acc = 5
0, 0, // END
0, 0,
0, 0,
]);
let test_hop = new Uint8Array([
5, 8, // ADD lit ... acc = 8
9, 8, // HOP lit ... hop over next op if acc = 8
0, 0, // END ... (hopped over)
7, 8, // SUB lit ... acc = 0
0, 0
]);
let test_jmp = new Uint8Array([
11, 8, // 0 ... JMP lit
0, 0, // 2 ... END ... JMP'd over
0, 0, // 4
0, 0, // 6
5, 8, // 8 ... ADD lit ... acc = 8
0, 0, // 10 ... END
]);
const test_chp = new Uint8Array([
5, 8, // ADD lit ... Acc = 8
14, 0, // CHP ... shouldn't hop (CF = 0)
5, 1, // ADD lit ... Acc = 9
5, 8, // ADD lit ... Acc = 1 and CF = 1
14, 0, // CHP ... hop! (CF = 1)
0, 0, // END
7, 1, // SUB lit ... Acc = 0
13, 0, // CFC ... CF = 0
0, 0, // END
]);
//CPU.loadMemory(test_lda_sto);
//CPU.loadMemory(test_add_sub_nocarry);
//CPU.loadMemory(test_hop);
CPU.loadMemory(test_jmp);
//CPU.loadMemory(test_chp);
runCPU();