cpu - WIP - Finish refactoring into a library
This commit is contained in:
parent
81617dfe42
commit
18d77086df
646
src/cpu.lib.js
646
src/cpu.lib.js
|
|
@ -1,352 +1,352 @@
|
|||
const readline = require('readline');
|
||||
const readlineSync = require('readline-sync');
|
||||
const { num2hex } = require('./logging.js');
|
||||
|
||||
const {
|
||||
INITIAL_IP_ADDRESS,
|
||||
DEFAULT_CYCLE_LIMIT: CYCLE_LIMIT,
|
||||
KEYPAD_ADDR,
|
||||
KEY_MAP,
|
||||
} = require('./machine.config');
|
||||
module.exports = class CPU {
|
||||
|
||||
const {
|
||||
num2hex,
|
||||
bool2bit,
|
||||
} = require('./logging.js');
|
||||
const display = require('./display.js');
|
||||
/**
|
||||
* @arg {number} initialIP
|
||||
**/
|
||||
constructor(initialIP, cycleLimit) {
|
||||
this.running = false;
|
||||
this.IP = initialIP;
|
||||
this.acc = 0;
|
||||
this.flags = {'C': false, 'Z': false, 'N': false, 'O': false};
|
||||
this.flagNums = {0: 'C', 1: 'Z', 2: 'N', 3: 'O'};
|
||||
this.instruction = { opcode: null, operand: null };
|
||||
this.memory = null;
|
||||
|
||||
// STATE
|
||||
const CPU = {
|
||||
this._cycleLimit = cycleLimit;
|
||||
|
||||
/** Core state **/
|
||||
state: {
|
||||
running: false,
|
||||
IP: INITIAL_IP_ADDRESS,
|
||||
flags: {'C': false, 'Z': false, 'N': false, 'O': false},
|
||||
FLAGNUMS2NAMES: {0: 'C', 1: 'Z', 2: 'N', 3: 'O'},
|
||||
acc: 0,
|
||||
memory: null,
|
||||
},
|
||||
this.dbg = {
|
||||
sourceInfo: null,
|
||||
currentMnemonic: null,
|
||||
previousIP: initialIP,
|
||||
cycleCounter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/** Debug info **/
|
||||
debug: {
|
||||
sourceInfo: null,
|
||||
previousIP: 0,
|
||||
currentInstruction: {
|
||||
opcode: null,
|
||||
operand: null,
|
||||
mnemonic: null,
|
||||
},
|
||||
cycleCounter: 0,
|
||||
},
|
||||
|
||||
/** Functions that update state **/
|
||||
/** Public interface **/
|
||||
|
||||
/** @param {Uint8Array} data **/
|
||||
loadMemory(data) {
|
||||
this.state.memory = new Uint8Array(256);
|
||||
this.state.memory.set(data, 0);
|
||||
},
|
||||
/**
|
||||
* @param {Uint8Array} machineCode
|
||||
**/
|
||||
loadMemory(machineCode) {
|
||||
this.memory = new Uint8Array(256);
|
||||
this.memory.set(machineCode, 0);
|
||||
}
|
||||
|
||||
peek() { return; } // TODO
|
||||
|
||||
poke() { return; } // TODO
|
||||
|
||||
/** @param {Array} info **/ // TODO type info
|
||||
loadSourceInfo(info) {
|
||||
this.debug.sourceInfo = info;
|
||||
},
|
||||
this.dbg.sourceInfo = info;
|
||||
}
|
||||
|
||||
incrementIP(offset) {
|
||||
this.state.previousIP = this.state.IP;
|
||||
this.state.IP = this.state.IP + offset;
|
||||
},
|
||||
|
||||
setIP(address) {
|
||||
this.state.previousIP = this.state.IP;
|
||||
this.state.IP = address;
|
||||
},
|
||||
|
||||
updateFlagZero() { this.state.flags.Z = this.state.acc === 0; },
|
||||
updateFlagNegative() { this.state.acc & 128 ? this.state.flags.N = true : this.state.flags.N = false },
|
||||
|
||||
/**
|
||||
* Load code into memory and set CPU state to "state.running"
|
||||
* @param {Uint8Array} code - Machine code to load
|
||||
**/
|
||||
startCPU(code) {
|
||||
CPU.loadMemory(code);
|
||||
CPU.state.running = true;
|
||||
},
|
||||
/** Set CPU state to "state.running" **/
|
||||
start() {
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
/** Execute the next instruction in memory **/
|
||||
stepCPU() {
|
||||
CPU.cycleStartCallbacks.forEach((fn) => fn());
|
||||
step() {
|
||||
this._cycleStartCallbacks.forEach((fn) => fn());
|
||||
|
||||
if (CPU.state.IP >= CPU.state.memory.length) {
|
||||
if (this.IP >= this.memory.length) {
|
||||
console.error('HALTING - IP greater than memory size'); // TODO -- should this throw an error instead?
|
||||
CPU.state.running = false;
|
||||
this.running = false;
|
||||
} else {
|
||||
CPU.debug.currentInstruction.opcode = CPU.state.memory[CPU.state.IP];
|
||||
CPU.debug.currentInstruction.operand = CPU.state.memory[CPU.state.IP+1];
|
||||
let executeInstruction = opcodes2mnemonics[CPU.debug.currentInstruction.opcode];
|
||||
|
||||
if (typeof executeInstruction === 'undefined') { failInvalidOpcode(); }
|
||||
|
||||
executeInstruction(CPU.debug.currentInstruction.operand);
|
||||
CPU.debug.cycleCounter += 1;
|
||||
this.instruction.opcode = this.memory[this.IP];
|
||||
this.instruction.operand = this.memory[this.IP+1];
|
||||
let mnem = this._nums2mnems[this.instruction.opcode];
|
||||
let op = this._ops[mnem];
|
||||
if (typeof op === 'undefined') { this._failInvalidOpcode(); }
|
||||
op(this.instruction.operand);
|
||||
this.dbg.cycleCounter += 1;
|
||||
}
|
||||
|
||||
// Temporary limit as a lazy way to halt infinite loops
|
||||
if ((CYCLE_LIMIT > 0) && CPU.debug.cycleCounter >= CYCLE_LIMIT) {
|
||||
if ((this._cycleLimit > 0) && this.dbg.cycleCounter >= this._cycleLimit) {
|
||||
console.warn(' HALTING - reached cycle limit'); // TODO -- throw error?
|
||||
CPU.state.running = false;
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
if (!CPU.state.running) process.exit();
|
||||
CPU.cycleEndCallbacks.forEach((fn) => fn());
|
||||
},
|
||||
this._cycleEndCallbacks.forEach((fn) => fn());
|
||||
if (!this.running) process.exit();
|
||||
}
|
||||
|
||||
|
||||
/** Private methods **/
|
||||
|
||||
_incrementIP(offset) {
|
||||
this.dbg.previousIP = this.IP;
|
||||
this.IP = this.IP + offset;
|
||||
}
|
||||
|
||||
_setIP(address) {
|
||||
this.dbg.previousIP = this.IP;
|
||||
this.IP = address;
|
||||
}
|
||||
|
||||
_updateFlagZero() {
|
||||
this.flags.Z = this.acc === 0;
|
||||
}
|
||||
|
||||
_updateFlagNegative() {
|
||||
if (this.acc & 128)
|
||||
{ this.flags.N = true; }
|
||||
else
|
||||
{ this.flags.N = false; }
|
||||
}
|
||||
|
||||
|
||||
/** Hooks **/
|
||||
|
||||
cycleStartCallbacks: [],
|
||||
cycleEndCallbacks: [],
|
||||
/** @type Array<Function> **/
|
||||
_cycleStartCallbacks = [];
|
||||
|
||||
/** @type Array<Function> **/
|
||||
_cycleEndCallbacks = [];
|
||||
|
||||
/** @param {function} fn **/
|
||||
onCycleStart(fn) { this.cycleStartCallbacks.push(fn) },
|
||||
onCycleEnd(fn) { this.cycleEndCallbacks.push(fn) },
|
||||
onCycleStart(fn) { this._cycleStartCallbacks.push(fn) };
|
||||
|
||||
/** @param {function} fn **/
|
||||
onCycleEnd(fn) { this._cycleEndCallbacks.push(fn) };
|
||||
|
||||
_ops = {
|
||||
end: () => {
|
||||
this.dbg.currentMnemonic = 'END';
|
||||
this.running = false;
|
||||
this._incrementIP(2);
|
||||
},
|
||||
|
||||
store_lit: (lit) => {
|
||||
this.dbg.currentMnemonic = 'STO lit';
|
||||
this.memory[lit] = this.acc;
|
||||
this._incrementIP(2);
|
||||
},
|
||||
|
||||
store_addr: (addr) => {
|
||||
this.dbg.currentMnemonic = `STO addr; @addr: ${num2hex(this.memory[addr])}`;
|
||||
this.memory[this.memory[addr]] = this.acc;
|
||||
this._incrementIP(2);
|
||||
},
|
||||
|
||||
load_lit: (lit) => {
|
||||
this.dbg.currentMnemonic = 'LDA lit';
|
||||
this.acc = lit;
|
||||
this._updateFlagNegative();
|
||||
this._updateFlagZero();
|
||||
this._incrementIP(2);
|
||||
},
|
||||
|
||||
load_addr: (addr) => {
|
||||
this.dbg.currentMnemonic = `LDA addr; @ addr: ${num2hex(this.memory[addr])}`;
|
||||
this.acc = this.memory[addr];
|
||||
this._updateFlagNegative();
|
||||
this._updateFlagZero();
|
||||
this._incrementIP(2);
|
||||
},
|
||||
|
||||
add_lit: (lit) => {
|
||||
this.dbg.currentMnemonic = 'ADD lit';
|
||||
// Calculate sum
|
||||
let sum = this.acc + lit;
|
||||
if (sum > 255) {
|
||||
this.flags.C = true;
|
||||
sum = (sum % 255) - 1;
|
||||
} else {
|
||||
this.flags.C = false;
|
||||
}
|
||||
// Calculate overflow flag status
|
||||
let bitSixCarry = 0;
|
||||
if ((this.acc & 64) && (lit & 64)) { bitSixCarry = 1; }
|
||||
// let overflow = bitSixCarry ^ (this.flags & 8);
|
||||
// FIXME FIXME FIXME
|
||||
// I'm on a plane and can't remember how this works
|
||||
let overflow = 0;
|
||||
if (overflow) {
|
||||
this.flags.O = true;
|
||||
} else {
|
||||
this.flags.O = false;
|
||||
}
|
||||
this.acc = sum;
|
||||
this._updateFlagNegative();
|
||||
this._updateFlagZero();
|
||||
this._incrementIP(2);
|
||||
},
|
||||
|
||||
add_addr: (addr) => {
|
||||
this.dbg.currentMnemonic = 'ADD addr';
|
||||
// Calculate sum
|
||||
let sum = this.acc + this.memory[addr];
|
||||
if (sum > 255) {
|
||||
this.flags.C = true;
|
||||
sum = (sum % 255) - 1;
|
||||
} else {
|
||||
this.flags.C = false;
|
||||
}
|
||||
// Calculate overflow flag status
|
||||
let bitSixCarry = 0;
|
||||
if ((this.acc & 64) && (addr & 64)) { bitSixCarry = 1; }
|
||||
// let overflow = bitSixCarry ^ (this.flags & 8);
|
||||
// FIXME FIXME FIXME
|
||||
// I'm on a plane and can't remember how this works
|
||||
let overflow = 0;
|
||||
if (overflow) {
|
||||
this.flags.O = true;
|
||||
} else {
|
||||
this.flags.O = false;
|
||||
}
|
||||
this.acc = sum;
|
||||
this._updateFlagNegative();
|
||||
this._updateFlagZero();
|
||||
this._incrementIP(2);
|
||||
},
|
||||
|
||||
sub_lit: (lit) => {
|
||||
this.dbg.currentMnemonic = 'SUB lit';
|
||||
// Calculate sum
|
||||
let sum = this.acc - lit;
|
||||
if (sum < 0) {
|
||||
this.flags.C = true;
|
||||
sum = sum + 256;
|
||||
} else {
|
||||
this.flags.C = false;
|
||||
}
|
||||
// Calculate overflow flag status
|
||||
let bitSixCarry = 0;
|
||||
if ((this.acc & 64) && (lit & 64)) { bitSixCarry = 1; }
|
||||
// let overflow = bitSixCarry ^ (this.flags & 8);
|
||||
// FIXME FIXME FIXME
|
||||
// I'm on a plane and can't remember how this works
|
||||
let overflow = 0;
|
||||
if (overflow) {
|
||||
this.flags.O = true;
|
||||
} else {
|
||||
this.flags.O = false;
|
||||
}
|
||||
this.acc = sum;
|
||||
this._updateFlagNegative();
|
||||
this._updateFlagZero();
|
||||
this._incrementIP(2);
|
||||
},
|
||||
|
||||
sub_addr: (addr) => {
|
||||
this.dbg.currentMnemonic = 'SUB addr';
|
||||
// Calculate sum
|
||||
let sum = this.acc - this.memory[addr];
|
||||
if (sum < 0) {
|
||||
this.flags.C = true;
|
||||
sum = sum + 256;
|
||||
} else {
|
||||
this.flags.C = false;
|
||||
}
|
||||
// Calculate overflow flag status
|
||||
let bitSixCarry = 0;
|
||||
if ((this.acc & 64) && (addr & 64)) { bitSixCarry = 1; }
|
||||
// let overflow = bitSixCarry ^ (this.flags & 8);
|
||||
// FIXME FIXME FIXME
|
||||
// I'm on a plane and can't remember how this works
|
||||
let overflow = 0;
|
||||
if (overflow) {
|
||||
this.flags.O = true;
|
||||
} else {
|
||||
this.flags.O = false;
|
||||
}
|
||||
this.acc = sum;
|
||||
this._updateFlagNegative();
|
||||
this._updateFlagZero();
|
||||
this._incrementIP(2);
|
||||
},
|
||||
|
||||
hop_lit: (lit) => {
|
||||
this.dbg.currentMnemonic = `HOP lit; IP+2: ${this.memory[this.IP+2]}, IP+3: ${this.memory[this.IP+3]}`;
|
||||
if (this.acc === lit) {
|
||||
this._incrementIP(4);
|
||||
} else {
|
||||
this._incrementIP(2);
|
||||
}
|
||||
},
|
||||
|
||||
hop_addr: (addr) => {
|
||||
this.dbg.currentMnemonic = 'HOP addr';
|
||||
if (this.acc === this.memory[addr]) {
|
||||
this._incrementIP(4);
|
||||
} else {
|
||||
this._incrementIP(2);
|
||||
}
|
||||
},
|
||||
|
||||
jump_lit: (lit) => {
|
||||
this.dbg.currentMnemonic = 'JMP lit';
|
||||
this._setIP(lit);
|
||||
},
|
||||
|
||||
jump_addr: (addr) => {
|
||||
this.dbg.currentMnemonic = 'JMP addr';
|
||||
this._setIP(this.memory[addr]);
|
||||
},
|
||||
|
||||
flag_toggle: (flagNum) => {
|
||||
if (flagNum === null) {
|
||||
console.error('Invalid flag number');
|
||||
process.exit(); // TODO review
|
||||
}
|
||||
const flagName = this.flagNums[flagNum];
|
||||
this.dbg.currentMnemonic = `FTG ${flagName}`;
|
||||
this.flags[flagName] = !this.flags[flagName];
|
||||
this._incrementIP(2);
|
||||
},
|
||||
|
||||
flag_hop: (flagNum) => {
|
||||
if (flagNum === null) {
|
||||
console.error('Invalid flag number');
|
||||
process.exit();
|
||||
}
|
||||
const flagName = this.flagNums[flagNum];
|
||||
this.dbg.currentMnemonic =
|
||||
`FHP ${flagName}; IP+2: ${this.memory[this.IP+2]}, IP+3: ${this.memory[this.IP+3]}`;
|
||||
if (this.flags[this.flagNums[flagNum]]) {
|
||||
this._incrementIP(4);
|
||||
} else {
|
||||
this._incrementIP(2);
|
||||
}
|
||||
},
|
||||
|
||||
no_op: () => {
|
||||
this.dbg.currentMnemonic = `NOP`;
|
||||
this._incrementIP(2);
|
||||
},
|
||||
}
|
||||
|
||||
_nums2mnems = {
|
||||
0: "end",
|
||||
1: "store_lit",
|
||||
2: "store_addr",
|
||||
3: "load_lit",
|
||||
4: "load_addr",
|
||||
5: "add_lit",
|
||||
6: "add_addr",
|
||||
7: "sub_lit",
|
||||
8: "sub_addr",
|
||||
9: "hop_lit",
|
||||
10: "hop_addr",
|
||||
11: "jump_lit",
|
||||
12: "jump_addr",
|
||||
13: "flag_toggle",
|
||||
14: "flag_hop",
|
||||
15: "no_op",
|
||||
}
|
||||
|
||||
_failInvalidOpcode() {
|
||||
let info = this.dbg.sourceInfo[this.dbg.previousIP];
|
||||
console.error();
|
||||
console.error(`Error: Invalid opcode`);
|
||||
console.error(` Executing $${num2hex(info.machine[0])} $${num2hex(info.machine[1])}`);
|
||||
console.error(` from line ${info.lineNumber}: ${info.source}`);
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FUNCTIONS THAT MODIFY STATE
|
||||
|
||||
const Instructions = {
|
||||
end: () => {
|
||||
CPU.debug.currentInstruction.mnemonic = 'END';
|
||||
CPU.state.running = false;
|
||||
CPU.incrementIP(2);
|
||||
},
|
||||
|
||||
store_lit: (lit) => {
|
||||
CPU.debug.currentInstruction.mnemonic = 'STO lit';
|
||||
CPU.state.memory[lit] = CPU.state.acc;
|
||||
CPU.incrementIP(2);
|
||||
},
|
||||
|
||||
store_addr: (addr) => {
|
||||
CPU.debug.currentInstruction.mnemonic = `STO addr; @addr: ${num2hex(CPU.state.memory[addr])}`;
|
||||
CPU.state.memory[CPU.state.memory[addr]] = CPU.state.acc;
|
||||
CPU.incrementIP(2);
|
||||
},
|
||||
|
||||
load_lit: (lit) => {
|
||||
CPU.debug.currentInstruction.mnemonic = 'LDA lit';
|
||||
CPU.state.acc = lit;
|
||||
CPU.updateFlagNegative();
|
||||
CPU.updateFlagZero();
|
||||
CPU.incrementIP(2);
|
||||
},
|
||||
|
||||
load_addr: (addr) => {
|
||||
CPU.debug.currentInstruction.mnemonic = `LDA addr; @ addr: ${num2hex(CPU.state.memory[addr])}`;
|
||||
CPU.state.acc = CPU.state.memory[addr];
|
||||
CPU.updateFlagNegative();
|
||||
CPU.updateFlagZero();
|
||||
CPU.incrementIP(2);
|
||||
},
|
||||
|
||||
add_lit: (lit) => {
|
||||
CPU.debug.currentInstruction.mnemonic = 'ADD lit';
|
||||
// Calculate sum
|
||||
let sum = CPU.state.acc + lit;
|
||||
if (sum > 255) {
|
||||
CPU.state.flags.C = true;
|
||||
sum = (sum % 255) - 1;
|
||||
} else {
|
||||
CPU.state.flags.C = false;
|
||||
}
|
||||
// Calculate overflow flag status
|
||||
let bitSixCarry = 0;
|
||||
if ((CPU.state.acc & 64) && (lit & 64)) { bitSixCarry = 1; }
|
||||
// let overflow = bitSixCarry ^ (CPU.state.flags & 8);
|
||||
// FIXME FIXME FIXME
|
||||
// I'm on a plane and can't remember how this works
|
||||
let overflow = 0;
|
||||
if (overflow) {
|
||||
CPU.state.flags.O = true;
|
||||
} else {
|
||||
CPU.state.flags.O = false;
|
||||
}
|
||||
CPU.state.acc = sum;
|
||||
CPU.updateFlagNegative();
|
||||
CPU.updateFlagZero();
|
||||
CPU.incrementIP(2);
|
||||
},
|
||||
|
||||
add_addr: (addr) => {
|
||||
CPU.debug.currentInstruction.mnemonic = 'ADD addr';
|
||||
// Calculate sum
|
||||
let sum = CPU.state.acc + CPU.state.memory[addr];
|
||||
if (sum > 255) {
|
||||
CPU.state.flags.C = true;
|
||||
sum = (sum % 255) - 1;
|
||||
} else {
|
||||
CPU.state.flags.C = false;
|
||||
}
|
||||
// Calculate overflow flag status
|
||||
let bitSixCarry = 0;
|
||||
if ((CPU.state.acc & 64) && (addr & 64)) { bitSixCarry = 1; }
|
||||
// let overflow = bitSixCarry ^ (CPU.state.flags & 8);
|
||||
// FIXME FIXME FIXME
|
||||
// I'm on a plane and can't remember how this works
|
||||
let overflow = 0;
|
||||
if (overflow) {
|
||||
CPU.state.flags.O = true;
|
||||
} else {
|
||||
CPU.state.flags.O = false;
|
||||
}
|
||||
CPU.state.acc = sum;
|
||||
CPU.updateFlagNegative();
|
||||
CPU.updateFlagZero();
|
||||
CPU.incrementIP(2);
|
||||
},
|
||||
|
||||
sub_lit: (lit) => {
|
||||
CPU.debug.currentInstruction.mnemonic = 'SUB lit';
|
||||
// Calculate sum
|
||||
let sum = CPU.state.acc - lit;
|
||||
if (sum < 0) {
|
||||
CPU.state.flags.C = true;
|
||||
sum = sum + 256;
|
||||
} else {
|
||||
CPU.state.flags.C = false;
|
||||
}
|
||||
// Calculate overflow flag status
|
||||
let bitSixCarry = 0;
|
||||
if ((CPU.state.acc & 64) && (lit & 64)) { bitSixCarry = 1; }
|
||||
// let overflow = bitSixCarry ^ (CPU.state.flags & 8);
|
||||
// FIXME FIXME FIXME
|
||||
// I'm on a plane and can't remember how this works
|
||||
let overflow = 0;
|
||||
if (overflow) {
|
||||
CPU.state.flags.O = true;
|
||||
} else {
|
||||
CPU.state.flags.O = false;
|
||||
}
|
||||
CPU.state.acc = sum;
|
||||
CPU.updateFlagNegative();
|
||||
CPU.updateFlagZero();
|
||||
CPU.incrementIP(2);
|
||||
},
|
||||
|
||||
sub_addr: (addr) => {
|
||||
CPU.debug.currentInstruction.mnemonic = 'SUB addr';
|
||||
// Calculate sum
|
||||
let sum = CPU.state.acc - CPU.state.memory[addr];
|
||||
if (sum < 0) {
|
||||
CPU.state.flags.C = true;
|
||||
sum = sum + 256;
|
||||
} else {
|
||||
CPU.state.flags.C = false;
|
||||
}
|
||||
// Calculate overflow flag status
|
||||
let bitSixCarry = 0;
|
||||
if ((CPU.state.acc & 64) && (addr & 64)) { bitSixCarry = 1; }
|
||||
// let overflow = bitSixCarry ^ (CPU.state.flags & 8);
|
||||
// FIXME FIXME FIXME
|
||||
// I'm on a plane and can't remember how this works
|
||||
let overflow = 0;
|
||||
if (overflow) {
|
||||
CPU.state.flags.O = true;
|
||||
} else {
|
||||
CPU.state.flags.O = false;
|
||||
}
|
||||
CPU.state.acc = sum;
|
||||
CPU.updateFlagNegative();
|
||||
CPU.updateFlagZero();
|
||||
CPU.incrementIP(2);
|
||||
},
|
||||
|
||||
hop_lit: (lit) => {
|
||||
CPU.debug.currentInstruction.mnemonic = `HOP lit; IP+2: ${CPU.state.memory[CPU.state.IP+2]}, IP+3: ${CPU.state.memory[CPU.state.IP+3]}`;
|
||||
if (CPU.state.acc === lit) {
|
||||
CPU.incrementIP(4);
|
||||
} else {
|
||||
CPU.incrementIP(2);
|
||||
}
|
||||
},
|
||||
|
||||
hop_addr: (addr) => {
|
||||
CPU.debug.currentInstruction.mnemonic = 'HOP addr';
|
||||
if (CPU.state.acc === CPU.state.memory[addr]) {
|
||||
CPU.incrementIP(4);
|
||||
} else {
|
||||
CPU.incrementIP(2);
|
||||
}
|
||||
},
|
||||
|
||||
jump_lit: (lit) => {
|
||||
CPU.debug.currentInstruction.mnemonic = 'JMP lit';
|
||||
CPU.setIP(lit);
|
||||
},
|
||||
|
||||
jump_addr: (addr) => {
|
||||
CPU.debug.currentInstruction.mnemonic = 'JMP addr';
|
||||
CPU.setIP(CPU.state.memory[addr]);
|
||||
},
|
||||
|
||||
flag_toggle: (flagNum) => {
|
||||
if (flagNum === null) {
|
||||
console.error('Invalid flag number');
|
||||
process.exit();
|
||||
}
|
||||
const flagName = CPU.FLAGNUMS2NAMES[flagNum];
|
||||
CPU.debug.currentInstruction.mnemonic = `FTG ${flagName}`;
|
||||
CPU.state.flags[flagName] = !CPU.state.flags[flagName];
|
||||
CPU.incrementIP(2);
|
||||
},
|
||||
|
||||
flag_hop: (flagNum) => {
|
||||
if (flagNum === null) {
|
||||
console.error('Invalid flag number');
|
||||
process.exit();
|
||||
}
|
||||
const flagName = CPU.FLAGNUMS2NAMES[flagNum];
|
||||
CPU.debug.currentInstruction.mnemonic = `FHP ${flagName}; IP+2: ${CPU.state.memory[CPU.state.IP+2]}, IP+3: ${CPU.state.memory[CPU.state.IP+3]}`;
|
||||
if (CPU.state.flags[CPU.FLAGNUMS2NAMES[flagNum]]) {
|
||||
CPU.incrementIP(4);
|
||||
} else {
|
||||
CPU.incrementIP(2);
|
||||
}
|
||||
},
|
||||
|
||||
no_op: () => {
|
||||
CPU.debug.currentInstruction.mnemonic = `NOP`;
|
||||
CPU.incrementIP(2);
|
||||
},
|
||||
}
|
||||
|
||||
const opcodes2mnemonics = {
|
||||
0: (operand) => Instructions.end(),
|
||||
1: (operand) => Instructions.store_lit(operand),
|
||||
2: (operand) => Instructions.store_addr(operand),
|
||||
3: (operand) => Instructions.load_lit(operand),
|
||||
4: (operand) => Instructions.load_addr(operand),
|
||||
5: (operand) => Instructions.add_lit(operand),
|
||||
6: (operand) => Instructions.add_addr(operand),
|
||||
7: (operand) => Instructions.sub_lit(operand),
|
||||
8: (operand) => Instructions.sub_addr(operand),
|
||||
9: (operand) => Instructions.hop_lit(operand),
|
||||
10: (operand) => Instructions.hop_addr(operand),
|
||||
11: (operand) => Instructions.jump_lit(operand),
|
||||
12: (operand) => Instructions.jump_addr(operand),
|
||||
13: (operand) => Instructions.flag_toggle(operand),
|
||||
14: (operand) => Instructions.flag_hop(operand),
|
||||
15: (operand) => Instructions.no_op(),
|
||||
};
|
||||
|
||||
|
||||
function failInvalidOpcode() {
|
||||
let info = CPU.debug.sourceInfo[CPU.previousIP];
|
||||
console.error();
|
||||
console.error(`Error: Invalid opcode`);
|
||||
console.error(` Executing $${num2hex(info.machine[0])} $${num2hex(info.machine[1])}`);
|
||||
console.error(` from line ${info.lineNumber}: ${info.source}`);
|
||||
process.exit();
|
||||
}
|
||||
Loading…
Reference in New Issue