Skip to content

Commit

Permalink
Allow single-stepping the program. (#3)
Browse files Browse the repository at this point in the history
- Add two buttons to change the steps accordingly. This is a O(n^2) algorithm (every step we restart from the beginning) but probably good enough for now.
  • Loading branch information
yunxing authored Apr 21, 2021
1 parent 474e524 commit 1e2e439
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 45 deletions.
6 changes: 5 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,11 @@
<body>
<table id="control">
<tr>
<td><button id="run">Run</button> Max steps: <input size="6" id="maxsteps"></input></td>
<td>
<button id="run" title="Run the program untill it reaches given steps.">Run</button> Max steps: <input size="6" id="maxsteps"></input>
<button id="prev" title="Go backward in time for a single machine instruction.">Step prev</button>
<button id="next" title="Execute a single machine instruction from current step.">Step next</button>
</td>
<td class="spacerheader"></td>
<td>Code sample:</td>
<td>
Expand Down
121 changes: 77 additions & 44 deletions ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ const maxsteps = document.querySelector('#maxsteps');
const ramstart = document.querySelector('#ramstart');
const ramshowmode = document.querySelector('#ramshowmode');
codetext.addEventListener('keydown', onCodeTextKey);
document.querySelector("#run").addEventListener("mousedown", onRunCode);
document.querySelector("#run").addEventListener("mousedown", () => dispatchStep("run"));
document.querySelector("#prev").addEventListener("mousedown", () => dispatchStep("prev"));
document.querySelector("#next").addEventListener("mousedown", () => dispatchStep("next"));
document.querySelector("#setsample").addEventListener("mousedown", onSetSample);
document.querySelector("#showramstart").addEventListener("mousedown", onShowRamStart);
document.querySelector("#ramstart").addEventListener("keyup", onRamStartKey);
Expand Down Expand Up @@ -271,52 +273,27 @@ function setStatusReady() {
// RAM per the user's request in the RAM table.
let memFromLastRun = new Array(65536).fill(0);

function onRunCode() {
saveUiState();
function checkSteps() {
if (maxsteps.value === 'undefined' || isNaN(parseInt(maxsteps.value))
|| parseInt(maxsteps.value) < 0) {
throw new Error(`Steps value is invalid`);
}
}

function dispatchStep(event) {
try {
let prog = codetext.value;

if (maxsteps.value === 'undefined' || isNaN(parseInt(maxsteps.value))) {
throw new Error(`Max steps value is invalid`);
}
let [state, mem, labelToAddr] = runProg(prog, parseInt(maxsteps.value));
memFromLastRun = mem;

// Populate CPU state / registers.
for (let regName of Object.keys(state)) {
if (cpuStateValues.hasOwnProperty(regName)) {
let valueElement = cpuStateValues[regName];
let width = registerWidths[regName];
valueElement.textContent = formatNum(state[regName], width);
} else if (regName === 'f') {
let regval = state[regName];
flagsStateValues.Sign.textContent = formatNum((regval >> 7) & 0x01, 2);
flagsStateValues.Zero.textContent = formatNum((regval >> 6) & 0x01, 2);
flagsStateValues.Parity.textContent = formatNum((regval >> 2) & 0x01, 2);
flagsStateValues.Carry.textContent = formatNum(regval & 0x01, 2);
} else {
console.log('cannot find state value for', regName);
}
}

// Populate RAM table.
ramstart.value = "0000";
populateRamTable();

// Populate labels table.
const labelTable = document.querySelector('#labels');
labelTable.innerHTML = '';
for (let [key, value] of labelToAddr.entries()) {
let row = elt("tr");
let keyCol = elt("td", key + ':');
keyCol.classList.add("labelName");
let valCol = elt("td", formatNum(value, 4));
row.append(keyCol, valCol);
labelTable.appendChild(row);
checkSteps();
switch (event) {
case "run":
onRunCode();
break;
case "next":
onNextStep();
break;
case "prev":
onPrevStep();
break;
}

setStatusSuccess();
} catch (e) {
if (e instanceof js8080sim.ParseError ||
e instanceof js8080sim.AssemblyError) {
Expand All @@ -328,6 +305,62 @@ function onRunCode() {
}
}

function onNextStep() {
let step = parseInt(maxsteps.value);
maxsteps.value = step + 1;
onRunCode();
}

function onPrevStep() {
let step = parseInt(maxsteps.value);
maxsteps.value = step - 1;
onRunCode();
}

function onRunCode() {
saveUiState();

let prog = codetext.value;

let [state, mem, labelToAddr] = runProg(prog, parseInt(maxsteps.value));
memFromLastRun = mem;

// Populate CPU state / registers.
for (let regName of Object.keys(state)) {
if (cpuStateValues.hasOwnProperty(regName)) {
let valueElement = cpuStateValues[regName];
let width = registerWidths[regName];
valueElement.textContent = formatNum(state[regName], width);
} else if (regName === 'f') {
let regval = state[regName];
flagsStateValues.Sign.textContent = formatNum((regval >> 7) & 0x01, 2);
flagsStateValues.Zero.textContent = formatNum((regval >> 6) & 0x01, 2);
flagsStateValues.Parity.textContent = formatNum((regval >> 2) & 0x01, 2);
flagsStateValues.Carry.textContent = formatNum(regval & 0x01, 2);
} else {
console.log('cannot find state value for', regName);
}
}

// Populate RAM table.
ramstart.value = "0000";
populateRamTable();

// Populate labels table.
const labelTable = document.querySelector('#labels');
labelTable.innerHTML = '';
for (let [key, value] of labelToAddr.entries()) {
let row = elt("tr");
let keyCol = elt("td", key + ':');
keyCol.classList.add("labelName");
let valCol = elt("td", formatNum(value, 4));
row.append(keyCol, valCol);
labelTable.appendChild(row);
}

setStatusSuccess();
}

function onRamStartKey(event) {
if (event.keyCode == 13) {
onShowRamStart();
Expand Down

0 comments on commit 1e2e439

Please sign in to comment.