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 // 3 = always print
// 4 = silent // 4 = silent
const DEBUG_LEVEL = 2; 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; DEBUG = debug;
return decodeInstructions(str); return decodeInstructions(assemblyCode);
} }
// Configure pseudo-ops: // 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, * it will be assembled to the default intial value of the IP,
* as specified in `machine.config.js`. * as specified in `machine.config.js`.
* @param {string} source - Assembly source to decode * @param {string} source - Assembly source to decode
* @return TODO * @return {{ debugInfo: Object, machineCode: Uint8Array }};
**/ **/
function decodeInstructions(source) { function decodeInstructions(source) {
dbg(1, 'Pre-parsing...'); dbg(1, 'Pre-parsing...');
@ -183,9 +187,11 @@ function decodeInstructions(source) {
// Initialize arrays to collect assembled code // 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 machineCode = new Array(IP).fill(0);
let debugInfo = {};
// Initialize memory-mapped IO -- TODO this should probably be in the CPU, not here // Initialize memory-mapped IO -- TODO this should probably be in the CPU, not here
machineCode[POINTER_TO_DISPLAY] = DISPLAY_ADDR; machineCode[POINTER_TO_DISPLAY] = DISPLAY_ADDR;
@ -194,6 +200,7 @@ function decodeInstructions(source) {
let labels = {}; let labels = {};
let constants = {}; let constants = {};
// Decode line by line... // Decode line by line...
for (let i = 0; i < lines.length; i++) { for (let i = 0; i < lines.length; i++) {
let line = lines[i]; let line = lines[i];
@ -311,6 +318,14 @@ function decodeInstructions(source) {
machineCode[IP] = decodedOp; machineCode[IP] = decodedOp;
machineCode[IP + 1] = decodedArg; machineCode[IP + 1] = decodedArg;
debugInfo[IP] = {
lineNumber: line.number,
source: line.source,
address: IP,
machine: [decodedOp, decodedArg]
};
dbg(3, ``); dbg(3, ``);
dbg(3, `Line ${line.number}: ${line.source}`); dbg(3, `Line ${line.number}: ${line.source}`);
if (line.argument) { 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 * 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 (CYCLE_LIMIT) { // Temporary limit as a lazy way to halt infinite loops
if (CPU.cycleCounter > CYCLE_LIMIT) { if (CPU.cycleCounter > CYCLE_LIMIT) {
console.warn('HALTING - reached cycle limit'); console.warn('HALTING - reached cycle limit');
@ -332,31 +332,33 @@ async function stepCPU(debug = false) {
CPU.cycleCounter += 1; CPU.cycleCounter += 1;
} }
} }
logCPUState(debug); logCPUState(debugInfo, debug);
} }
/** /**
* @param {Uint8Array} code - Machine code to run * @param {Uint8Array} code - Machine code to run
* @param {any} debugInfo TODO type
* @param {Boolean} [debug] - Enable/disable debugging printouts * @param {Boolean} [debug] - Enable/disable debugging printouts
* @param {Number} [clockSpeed] - CPU clock speed in milliseconds * @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); startCPU(code);
// Animate the output by pausing between steps // Animate the output by pausing between steps
const loop = setInterval(async () => { const loop = setInterval(async () => {
stepCPU(debug); stepCPU(debugInfo, debug);
if (!CPU.running) clearInterval(loop); if (!CPU.running) clearInterval(loop);
}, clockSpeed); }, clockSpeed);
} }
/** /**
* @param {Uint8Array} code - Machine code to run * @param {Uint8Array} code - Machine code to run
* @param {any} debugInfo - TODO
* @param {Boolean} [debug] - Enable/disable debugging printouts * @param {Boolean} [debug] - Enable/disable debugging printouts
**/ **/
exports.singleStepProgram = (code, debug = false) => { exports.singleStepProgram = (code, debugInfo, debug = false) => {
startCPU(code); startCPU(code);
while (CPU.running) { while (CPU.running) {
stepCPU(debug); stepCPU(debugInfo, debug);
// FIXME: this prevents exiting with Ctrl+C: // FIXME: this prevents exiting with Ctrl+C:
let key = readlineSync.keyIn('S to step, Q to quit > ', { let key = readlineSync.keyIn('S to step, Q to quit > ', {
limit: ['s', 'S', 'q', 'Q'], limit: ['s', 'S', 'q', 'Q'],
@ -372,8 +374,9 @@ exports.singleStepProgram = (code, debug = false) => {
/** /**
* @param {Boolean} [debug] - Enable/disable debugging printouts * @param {Boolean} [debug] - Enable/disable debugging printouts
**/ **/
function logCPUState(debug = false) { function logCPUState(debugInfo, debug = false) {
console.group(`Step`); console.group(`Step ${CPU.cycleCounter}`);
console.log();
if (!debug) console.clear(); if (!debug) console.clear();
if (debug) { if (debug) {
display.printDisplay(CPU.memory); display.printDisplay(CPU.memory);
@ -381,11 +384,13 @@ function logCPUState(debug = false) {
display.prettyPrintDisplay(CPU.memory); display.prettyPrintDisplay(CPU.memory);
} }
console.log(); console.log();
console.log(`Line ${debugInfo[CPU.IP-2].lineNumber}: ${debugInfo[CPU.IP-2].source}`);
console.log('Mnemonic:', CPU.currentInstruction.mnemonic); console.log('Mnemonic:', CPU.currentInstruction.mnemonic);
console.log(`Machine: $${num2hex(CPU.currentInstruction.opcode)} $${num2hex(CPU.currentInstruction.operand)}`); console.log(`Machine: $${num2hex(CPU.currentInstruction.opcode)} $${num2hex(CPU.currentInstruction.operand)}`);
console.log(); console.log();
console.log(`IP: $${num2hex(CPU.IP)} Acc: $${num2hex(CPU.Acc)} ONZC: ${num2bin_4bit(CPU.FLAGS)}`); 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(`KEY: $${num2hex(CPU.memory[KEYPAD_ADDR])}  ${CPU.running ? "running" : "halted" }`);
console.log(); console.log();
console.log();
console.groupEnd(); console.groupEnd();
}; };

View File

@ -6,18 +6,17 @@
* @param {number} [end] - An end-index, in decimal * @param {number} [end] - An end-index, in decimal
**/ **/
const logMemory = (mem, start=0, end=mem.length) => { 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); mem = mem.slice(start, end);
console.log(`┌────────┬────────┬─────────┐`); console.log(`┌────────┬────────┬─────────┐`);
console.log(`│ addr │ opcode │ operand │`); console.log(`│ addr │ opcode │ operand │`);
console.log(`├────────┼────────┼─────────┤`); console.log(`├────────┼────────┼─────────┤`);
//for (let i = 0; i < mem.length; i += 2) {
for (let i = start; 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: // Add a blank row every 4 lines:
if (((i + 2) % 8) === 0) { if (((i + 2) % 8) === 0) {

View File

@ -9,7 +9,6 @@ For extended commentary, see [issues](issues.md).
### Todo ### Todo
- Add a flag for bank-switching to the ~zero-page - 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) - 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 - [fix] (cpu) Make single-stepping work with simulated keypad
@ -42,3 +41,5 @@ For extended commentary, see [issues](issues.md).
## 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 filename = process.argv[3];
const inputFile_str = fs.readFileSync(filename, 'utf8'); const inputFile_str = fs.readFileSync(filename, 'utf8');
let machineCode; let assembler_output;
if (mode === "debug") { if (mode === "debug") {
machineCode = assembler.assemble(inputFile_str, true); assembler_output = assembler.assemble(inputFile_str, true);
console.log(''); console.log('');
console.group("Machine code output"); console.group("Machine code output");
logMemory(machineCode); logMemory(assembler_output.machineCode);
console.groupEnd(); console.groupEnd();
} else { } else {
machineCode = assembler.assemble(inputFile_str); assembler_output = assembler.assemble(inputFile_str);
let output = ''; let output = '';
if (mode === 'runbin') { // print binary 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 } else { // print hex output
machineCode.forEach((n) => output = `${output} ${num2hex(n)}`); assembler_output.machineCode.forEach((n) => output = `${output} ${num2hex(n)}`);
} }
console.log(output); console.log(output);
} }

View File

@ -16,15 +16,15 @@ const mode = process.argv[2];
const filename = process.argv[3]; const filename = process.argv[3];
const inputFile_str = fs.readFileSync(filename, 'utf8'); const inputFile_str = fs.readFileSync(filename, 'utf8');
let machineCode = assembler.assemble(inputFile_str); let assemblerOutput = assembler.assemble(inputFile_str);
if (mode === "debug") { if (mode === "debug") {
logRunningHeader(); logRunningHeader();
computer.runProgram(machineCode, true); computer.runProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, true);
} else if (mode === "stepdebug") { } else if (mode === "stepdebug") {
logRunningHeader(); logRunningHeader();
computer.singleStepProgram(machineCode, true); computer.singleStepProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, true);
} else if (mode === "step") { } else if (mode === "step") {
computer.singleStepProgram(machineCode, false); computer.singleStepProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, false);
} else { } else {
computer.runProgram(machineCode, false, 200); computer.runProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, false, 200);
} }