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
|
// 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,8 +187,10 @@ 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
21
cpu.js
|
|
@ -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();
|
||||||
};
|
};
|
||||||
13
logging.js
13
logging.js
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -41,4 +40,6 @@ For extended commentary, see [issues](issues.md).
|
||||||
- Minimal LOGO-ish interpreter for turtle graphics
|
- 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 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);
|
||||||
}
|
}
|
||||||
10
run-cpu.js
10
run-cpu.js
|
|
@ -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);
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue