assembler/cpu - Add feature: Pass debug info from assembler to cpu, so that cpu can print source line

This commit is contained in:
n loewen 2023-08-24 13:16:01 +01:00
parent f0632b0969
commit 1dc535a71e
6 changed files with 54 additions and 34 deletions

View File

@ -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
View File

@ -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();
};

View File

@ -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) {

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}