assembler/cpu - Add feature: Pass debug info from assembler to cpu, so that cpu can print source line
This commit is contained in:
parent
f0632b0969
commit
1dc535a71e
27
assembler.js
27
assembler.js
|
|
@ -10,11 +10,15 @@ const {
|
|||
// 3 = always print
|
||||
// 4 = silent
|
||||
const DEBUG_LEVEL = 2;
|
||||
let DEBUG = false; // Turn debugging on/off -- set by assemble()
|
||||
let DEBUG; // Turn debugging on/off -- set by assemble()
|
||||
|
||||
exports.assemble = (str, debug = false) => {
|
||||
/**
|
||||
* @param {string} assemblyCode
|
||||
* @param {Boolean} [debug = false]
|
||||
**/
|
||||
exports.assemble = (assemblyCode, debug = false) => {
|
||||
DEBUG = debug;
|
||||
return decodeInstructions(str);
|
||||
return decodeInstructions(assemblyCode);
|
||||
}
|
||||
|
||||
// Configure pseudo-ops:
|
||||
|
|
@ -157,7 +161,7 @@ function handleConstantDefinitions(op, arg, IP, constants) {
|
|||
* it will be assembled to the default intial value of the IP,
|
||||
* as specified in `machine.config.js`.
|
||||
* @param {string} source - Assembly source to decode
|
||||
* @return TODO
|
||||
* @return {{ debugInfo: Object, machineCode: Uint8Array }};
|
||||
**/
|
||||
function decodeInstructions(source) {
|
||||
dbg(1, 'Pre-parsing...');
|
||||
|
|
@ -183,8 +187,10 @@ function decodeInstructions(source) {
|
|||
|
||||
// Initialize arrays to collect assembled code
|
||||
|
||||
/** @type {array} - Assembled source code, as an array of bytes **/
|
||||
/** @type {Array<number>} - Assembled source code, as an array of bytes **/
|
||||
let machineCode = new Array(IP).fill(0);
|
||||
|
||||
let debugInfo = {};
|
||||
|
||||
// Initialize memory-mapped IO -- TODO this should probably be in the CPU, not here
|
||||
machineCode[POINTER_TO_DISPLAY] = DISPLAY_ADDR;
|
||||
|
|
@ -194,6 +200,7 @@ function decodeInstructions(source) {
|
|||
let labels = {};
|
||||
let constants = {};
|
||||
|
||||
|
||||
// Decode line by line...
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
let line = lines[i];
|
||||
|
|
@ -311,6 +318,14 @@ function decodeInstructions(source) {
|
|||
machineCode[IP] = decodedOp;
|
||||
machineCode[IP + 1] = decodedArg;
|
||||
|
||||
debugInfo[IP] = {
|
||||
lineNumber: line.number,
|
||||
source: line.source,
|
||||
address: IP,
|
||||
machine: [decodedOp, decodedArg]
|
||||
};
|
||||
|
||||
|
||||
dbg(3, ``);
|
||||
dbg(3, `Line ${line.number}: ${line.source}`);
|
||||
if (line.argument) {
|
||||
|
|
@ -341,7 +356,7 @@ function decodeInstructions(source) {
|
|||
}
|
||||
}
|
||||
|
||||
return new Uint8Array(machineCode);
|
||||
return { 'debugInfo': debugInfo, 'machineCode': new Uint8Array(machineCode) };
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
21
cpu.js
21
cpu.js
|
|
@ -313,7 +313,7 @@ function startCPU(code) {
|
|||
/**
|
||||
* Execute just the next instruction in memory
|
||||
**/
|
||||
async function stepCPU(debug = false) {
|
||||
async function stepCPU(debugInfo, debug = false) {
|
||||
if (CYCLE_LIMIT) { // Temporary limit as a lazy way to halt infinite loops
|
||||
if (CPU.cycleCounter > CYCLE_LIMIT) {
|
||||
console.warn('HALTING - reached cycle limit');
|
||||
|
|
@ -332,31 +332,33 @@ async function stepCPU(debug = false) {
|
|||
CPU.cycleCounter += 1;
|
||||
}
|
||||
}
|
||||
logCPUState(debug);
|
||||
logCPUState(debugInfo, debug);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} code - Machine code to run
|
||||
* @param {any} debugInfo TODO type
|
||||
* @param {Boolean} [debug] - Enable/disable debugging printouts
|
||||
* @param {Number} [clockSpeed] - CPU clock speed in milliseconds
|
||||
**/
|
||||
exports.runProgram = (code, debug = false, clockSpeed = 100) => {
|
||||
exports.runProgram = (code, debugInfo, debug = false, clockSpeed = 100) => {
|
||||
startCPU(code);
|
||||
// Animate the output by pausing between steps
|
||||
const loop = setInterval(async () => {
|
||||
stepCPU(debug);
|
||||
stepCPU(debugInfo, debug);
|
||||
if (!CPU.running) clearInterval(loop);
|
||||
}, clockSpeed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} code - Machine code to run
|
||||
* @param {any} debugInfo - TODO
|
||||
* @param {Boolean} [debug] - Enable/disable debugging printouts
|
||||
**/
|
||||
exports.singleStepProgram = (code, debug = false) => {
|
||||
exports.singleStepProgram = (code, debugInfo, debug = false) => {
|
||||
startCPU(code);
|
||||
while (CPU.running) {
|
||||
stepCPU(debug);
|
||||
stepCPU(debugInfo, debug);
|
||||
// FIXME: this prevents exiting with Ctrl+C:
|
||||
let key = readlineSync.keyIn('S to step, Q to quit > ', {
|
||||
limit: ['s', 'S', 'q', 'Q'],
|
||||
|
|
@ -372,8 +374,9 @@ exports.singleStepProgram = (code, debug = false) => {
|
|||
/**
|
||||
* @param {Boolean} [debug] - Enable/disable debugging printouts
|
||||
**/
|
||||
function logCPUState(debug = false) {
|
||||
console.group(`Step`);
|
||||
function logCPUState(debugInfo, debug = false) {
|
||||
console.group(`Step ${CPU.cycleCounter}`);
|
||||
console.log();
|
||||
if (!debug) console.clear();
|
||||
if (debug) {
|
||||
display.printDisplay(CPU.memory);
|
||||
|
|
@ -381,11 +384,13 @@ function logCPUState(debug = false) {
|
|||
display.prettyPrintDisplay(CPU.memory);
|
||||
}
|
||||
console.log();
|
||||
console.log(`Line ${debugInfo[CPU.IP-2].lineNumber}: ${debugInfo[CPU.IP-2].source}`);
|
||||
console.log('Mnemonic:', CPU.currentInstruction.mnemonic);
|
||||
console.log(`Machine: $${num2hex(CPU.currentInstruction.opcode)} $${num2hex(CPU.currentInstruction.operand)}`);
|
||||
console.log();
|
||||
console.log(`IP: $${num2hex(CPU.IP)} Acc: $${num2hex(CPU.Acc)} ONZC: ${num2bin_4bit(CPU.FLAGS)}`);
|
||||
console.log(`KEY: $${num2hex(CPU.memory[KEYPAD_ADDR])} ${CPU.running ? "running" : "halted" }`);
|
||||
console.log();
|
||||
console.log();
|
||||
console.groupEnd();
|
||||
};
|
||||
13
logging.js
13
logging.js
|
|
@ -6,18 +6,17 @@
|
|||
* @param {number} [end] - An end-index, in decimal
|
||||
**/
|
||||
const logMemory = (mem, start=0, end=mem.length) => {
|
||||
// This function can only handle
|
||||
// an even number of array entries
|
||||
if ((start % 2) === 1) { start -= 1; }
|
||||
if ((end % 2) === 1) { end += 1; }
|
||||
|
||||
mem = mem.slice(start, end);
|
||||
console.log(`┌────────┬────────┬─────────┐`);
|
||||
console.log(`│ addr │ opcode │ operand │`);
|
||||
console.log(`├────────┼────────┼─────────┤`);
|
||||
//for (let i = 0; i < mem.length; i += 2) {
|
||||
for (let i = start; i < mem.length; i +=2) {
|
||||
console.log(`│ ${num2hex(i)} │ ${num2hex(mem[i])} │ ${num2hex(mem[i+1])} │`);
|
||||
let operand = mem[i+1];
|
||||
if (typeof operand === 'undefined') {
|
||||
console.warn(' operand undefined');
|
||||
operand = -1;
|
||||
}
|
||||
console.log(`│ ${num2hex(i)} │ ${num2hex(mem[i])} │ ${num2hex(operand)} │`);
|
||||
|
||||
// Add a blank row every 4 lines:
|
||||
if (((i + 2) % 8) === 0) {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ For extended commentary, see [issues](issues.md).
|
|||
### Todo
|
||||
|
||||
- Add a flag for bank-switching to the ~zero-page
|
||||
- (assembler) Pass asm line thru to cpu to print when debugging
|
||||
- Remove run-scripts and add the ability to run `./cpu.js` and `./assembler.js` directly -- cf. [#1](issues.md#1---improve-cli-interface)
|
||||
- [fix] (cpu) Make single-stepping work with simulated keypad
|
||||
|
||||
|
|
@ -41,4 +40,6 @@ For extended commentary, see [issues](issues.md).
|
|||
- Minimal LOGO-ish interpreter for turtle graphics
|
||||
|
||||
|
||||
## Closed
|
||||
## Closed
|
||||
|
||||
- (assembler) Pass asm line thru to cpu to print when debugging
|
||||
|
|
@ -13,21 +13,21 @@ const mode = process.argv[2];
|
|||
const filename = process.argv[3];
|
||||
const inputFile_str = fs.readFileSync(filename, 'utf8');
|
||||
|
||||
let machineCode;
|
||||
let assembler_output;
|
||||
|
||||
if (mode === "debug") {
|
||||
machineCode = assembler.assemble(inputFile_str, true);
|
||||
assembler_output = assembler.assemble(inputFile_str, true);
|
||||
console.log('');
|
||||
console.group("Machine code output");
|
||||
logMemory(machineCode);
|
||||
logMemory(assembler_output.machineCode);
|
||||
console.groupEnd();
|
||||
} else {
|
||||
machineCode = assembler.assemble(inputFile_str);
|
||||
assembler_output = assembler.assemble(inputFile_str);
|
||||
let output = '';
|
||||
if (mode === 'runbin') { // print binary output
|
||||
machineCode.forEach((n) => output = `${output} ${num2bin(n)}`);
|
||||
assembler_output.machineCode.forEach((n) => output = `${output} ${num2bin(n)}`);
|
||||
} else { // print hex output
|
||||
machineCode.forEach((n) => output = `${output} ${num2hex(n)}`);
|
||||
assembler_output.machineCode.forEach((n) => output = `${output} ${num2hex(n)}`);
|
||||
}
|
||||
console.log(output);
|
||||
}
|
||||
10
run-cpu.js
10
run-cpu.js
|
|
@ -16,15 +16,15 @@ const mode = process.argv[2];
|
|||
const filename = process.argv[3];
|
||||
const inputFile_str = fs.readFileSync(filename, 'utf8');
|
||||
|
||||
let machineCode = assembler.assemble(inputFile_str);
|
||||
let assemblerOutput = assembler.assemble(inputFile_str);
|
||||
if (mode === "debug") {
|
||||
logRunningHeader();
|
||||
computer.runProgram(machineCode, true);
|
||||
computer.runProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, true);
|
||||
} else if (mode === "stepdebug") {
|
||||
logRunningHeader();
|
||||
computer.singleStepProgram(machineCode, true);
|
||||
computer.singleStepProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, true);
|
||||
} else if (mode === "step") {
|
||||
computer.singleStepProgram(machineCode, false);
|
||||
computer.singleStepProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, false);
|
||||
} else {
|
||||
computer.runProgram(machineCode, false, 200);
|
||||
computer.runProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, false, 200);
|
||||
}
|
||||
Loading…
Reference in New Issue