diff --git a/assembler.js b/assembler.js index 58c3f3c..4901daf 100644 --- a/assembler.js +++ b/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} - 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) }; } diff --git a/cpu.js b/cpu.js index d79f5f8..f71bb6a 100644 --- a/cpu.js +++ b/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(); }; \ No newline at end of file diff --git a/logging.js b/logging.js index 9922e61..2e1d581 100644 --- a/logging.js +++ b/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) { diff --git a/notes/issues/todo.md b/notes/issues/todo.md index 4de1ae3..59ecb67 100644 --- a/notes/issues/todo.md +++ b/notes/issues/todo.md @@ -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 \ No newline at end of file +## Closed + +- (assembler) Pass asm line thru to cpu to print when debugging \ No newline at end of file diff --git a/run-assembler.js b/run-assembler.js index e8671ac..12a22f4 100755 --- a/run-assembler.js +++ b/run-assembler.js @@ -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); } \ No newline at end of file diff --git a/run-cpu.js b/run-cpu.js index ec93b14..eb07704 100755 --- a/run-cpu.js +++ b/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); } \ No newline at end of file