mirror of
https://github.com/pkimpel/retro-b5500.git
synced 2026-04-25 20:01:52 +00:00
Release emulator version 0.18:
1. Implement system state dump in B5500Console and B5500SyllableDebugger. 2. Correct reporting of extended tape drive status in Mod-III result descriptors. 3. Further fixes for tape drive status oscillation at BOT. 4. Correct Processor TRN syllable (was not clearing zone bits). 5. Correct Processor TRW syllable Invalid Address fault when source string ended at address @77777. 6. Remove extraneous parameter in cc.fieldIsolate call for Processor ISO syllable (thanks to Peter Grootswagers). 7. Implement Power Off and Dump buttons in SyllableDebugger.
This commit is contained in:
@@ -61,7 +61,7 @@ function B5500CentralControl(global) {
|
||||
/**************************************/
|
||||
/* Global constants */
|
||||
|
||||
B5500CentralControl.version = "0.17";
|
||||
B5500CentralControl.version = "0.18";
|
||||
|
||||
B5500CentralControl.memReadCycles = 2; // assume 2 µs memory read cycle time (the other option was 3 µs)
|
||||
B5500CentralControl.memWriteCycles = 4; // assume 4 µs memory write cycle time (the other option was 6 µs)
|
||||
@@ -891,6 +891,150 @@ B5500CentralControl.prototype.runTest = function runTest(runAddr) {
|
||||
this.P1.start();
|
||||
};
|
||||
|
||||
B5500CentralControl.prototype.dumpSystemState = function dumpSystemState(caption, writer) {
|
||||
/* Generates a dump of the processor states and all of memory
|
||||
"caption is an identifying string that is output in the heading line.
|
||||
"writer" is a function that is called to output lines of text to the outside
|
||||
world. It takes two parameters:
|
||||
"phase" is a numeric code indicating the type of line being output:
|
||||
0 = initialization and heading line
|
||||
1 = processor 1 state
|
||||
2 = processor 2 state
|
||||
32 = core memory
|
||||
-1 = end of dump (text parameter not valid)
|
||||
"text" is the line of text to be output.
|
||||
*/
|
||||
var addr;
|
||||
var bic;
|
||||
var dupCount = 0;
|
||||
var lastLine = "";
|
||||
var line;
|
||||
var lineAddr;
|
||||
var mod;
|
||||
var x;
|
||||
|
||||
var accessor = { // Memory access control block
|
||||
requestorID: "C", // Memory requestor ID
|
||||
addr: 0, // Memory address
|
||||
word: 0, // 48-bit data word
|
||||
MAIL: 0, // Truthy if attempt to access @000-@777 in normal state
|
||||
MPED: 0, // Truthy if memory parity error
|
||||
MAED: 0 // Truthy if memory address/inhibit error
|
||||
};
|
||||
|
||||
var BICtoANSI = [
|
||||
"0", "1", "2", "3", "4", "5", "6", "7",
|
||||
"8", "9", "#", "@", "?", ":", ">", "}",
|
||||
"+", "A", "B", "C", "D", "E", "F", "G",
|
||||
"H", "I", ".", "[", "&", "(", "<", "~",
|
||||
"|", "J", "K", "L", "M", "N", "O", "P",
|
||||
"Q", "R", "$", "*", "-", ")", ";", "{",
|
||||
" ", "/", "S", "T", "U", "V", "W", "X",
|
||||
"Y", "Z", ",", "%", "!", "=", "]", "\""];
|
||||
|
||||
function padLeft(text, minLength, char) {
|
||||
/* Pads "text" on the left to a total length of "minLength" with "char" */
|
||||
var s = text.toString();
|
||||
var len = s.length;
|
||||
var pad = char || " ";
|
||||
|
||||
while (len++ < minLength) {
|
||||
s = pad + s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function padOctal(value, octades) {
|
||||
/* Formats "value" as an octal number of "octades" length, left-padding with
|
||||
zeroes as necessary */
|
||||
var text = value.toString(8);
|
||||
|
||||
if (value >= 0) {
|
||||
return padLeft(text, octades, "0");
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
function convertWordtoANSI(value) {
|
||||
/* Converts the "value" as a B5500 word to an eight character string and returns it */
|
||||
var c; // current character
|
||||
var s = ""; // working string value
|
||||
var w = value; // working word value
|
||||
var x; // character counter
|
||||
|
||||
for (x=0; x<8; x++) {
|
||||
c = w % 64;
|
||||
w = (w-c)/64;
|
||||
s = BICtoANSI[c] + s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function dumpProcessorState(px, nr) {
|
||||
/* Dumps the register state for the specified processor */
|
||||
|
||||
writer(nr, "Processor P" + nr + " = " + px.mnemonic + ":");
|
||||
writer(nr, "NCSF=" + px.NCSF + " CWMF=" + px.CWMF + " MSFF=" + px.MSFF + " SALF=" + px.SALF +
|
||||
" VARF=" + px.VARF);
|
||||
writer(nr, "C=" + padOctal(px.C, 5) + " L=" + px.L + " P=" + padOctal(px.P, 16) + " PROF=" + px.TROF +
|
||||
" T=" + padOctal(px.T, 4) + " TROF=" + px.TROF);
|
||||
writer(nr, "I=" + padLeft(px.I.toString(2), 8, "0") + " E=" + padLeft(px.E.toString(2), 6, "0") +
|
||||
" Q=" + padLeft(px.Q.toString(2), 9, "0") + " [bit masks]");
|
||||
writer(nr, "M=" + padOctal(px.M, 5) + " G=" + px.G + " H=" + px.H);
|
||||
writer(nr, "S=" + padOctal(px.S, 5) + " K=" + px.K + " V=" + px.V);
|
||||
writer(nr, "F=" + padOctal(px.F, 5) + " R=" + padOctal(px.R, 3));
|
||||
writer(nr, "X= " + padOctal(px.X, 13) + " Y=" + padOctal(px.Y, 2) + " Z=" + padOctal(px.Z, 2) +
|
||||
" N=" + px.N);
|
||||
writer(nr, "A=" + padOctal(px.A, 16) + " AROF=" + px.AROF);
|
||||
writer(nr, "B=" + padOctal(px.B, 16) + " BROF=" + px.BROF);
|
||||
}
|
||||
|
||||
writer(0, "B5500 State Dump by " + (caption || "(unknown)") + " : " + new Date().toString());
|
||||
|
||||
// Dump the processor states
|
||||
dumpProcessorState(this.P1, 1);
|
||||
if (this.P2) {
|
||||
dumpProcessorState(this.P2, 2);
|
||||
}
|
||||
|
||||
// Dump all of memory
|
||||
for (mod=0; mod<0x8000; mod+=0x1000) {
|
||||
for (addr=0; addr<0x1000; addr+=4) {
|
||||
lineAddr = mod+addr;
|
||||
line = " ";
|
||||
bic = " ";
|
||||
for (x=0; x<4; x++) {
|
||||
accessor.addr = lineAddr+x;
|
||||
this.fetch(accessor);
|
||||
if (accessor.MPED) {
|
||||
line += " << PARITY >> ";
|
||||
bic += "????????";
|
||||
} else if (accessor.MAED) {
|
||||
line += " << INV ADDR >> ";
|
||||
bic += "????????";
|
||||
} else {
|
||||
line += " " + padOctal(accessor.word, 16);
|
||||
bic += convertWordtoANSI(accessor.word);
|
||||
}
|
||||
} // for x
|
||||
|
||||
if (line == lastLine && lineAddr < 0x7FFC) {
|
||||
dupCount++;
|
||||
} else {
|
||||
if (dupCount > 0) {
|
||||
writer(32, "..... ................ for " + dupCount*4 + " words");
|
||||
dupCount = 0;
|
||||
}
|
||||
writer(32, padOctal(lineAddr, 5) + line + bic);
|
||||
lastLine = line;
|
||||
}
|
||||
} // for addr
|
||||
} // for mod
|
||||
|
||||
writer(-1, null);
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500CentralControl.prototype.configureSystem = function configureSystem() {
|
||||
/* Establishes the hardware module configuration from the
|
||||
|
||||
@@ -70,7 +70,9 @@ function B5500IOUnit(ioUnitID, cc) {
|
||||
this.boundFinishGeneric = this.makeFinish(this.finishGeneric);
|
||||
this.boundFinishGenericRead = this.makeFinish(this.finishGenericRead);
|
||||
this.boundFinishSPORead = this.makeFinish(this.finishSPORead);
|
||||
this.boundFinishTapeIO = this.makeFinish(this.finishTapeIO);
|
||||
this.boundFinishTapeRead = this.makeFinish(this.finishTapeRead);
|
||||
this.boundFinishTapeWrite = this.makeFinish(this.finishTapeWrite);
|
||||
|
||||
this.initiateStamp = 0; // Timestamp of last I/O initiation on this unit
|
||||
|
||||
@@ -960,11 +962,41 @@ B5500IOUnit.prototype.finishSPORead = function finishSPORead(errorMask, length)
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500IOUnit.prototype.finishTapeRead = function finishDiskRead(errorMask, length) {
|
||||
B5500IOUnit.prototype.finishTapeIO = function finishTapeIO(errorMask, count) {
|
||||
/* For Mod III I/O Units, extra status bits can be reported in the word-count
|
||||
field. The driver will report these in the higher-order bits of errorMask.
|
||||
mask & 0x040000 => tape at EOT (becomes D14F)
|
||||
mask & 0x080000 => tape at BOT (becomes D13F)
|
||||
mask & 0x100000 => blank tape (becomes D12F)
|
||||
mask & 0x200000 => reserved for relocated D29F memory parity bit (becomes D11F)
|
||||
The original memory parity bit in D29F is relocated to D11F, and both
|
||||
D02F and D29F are set unconditionally.
|
||||
|
||||
In addition, the count of characters in a paritally-filled final word is
|
||||
reported in the low-order three bits of the word count field.
|
||||
For a forward read, this is the number of characters in the last partial word.
|
||||
For a backward read, this is 7 minus the number of characters */
|
||||
var partialCount = (errorMask % 0x040000) >>> 15;
|
||||
|
||||
if (errorMask & 0x1C0008) {
|
||||
partialCount += ((errorMask % 0x200000) >>> 18) * 0x08 +
|
||||
(errorMask & 0x08) * 0x08; // relocate the memory parity bit
|
||||
this.D02F = 1; // mark as a Mod III RD
|
||||
errorMask |= 0x08; // set the original mem parity bit (D29) unconditionally
|
||||
}
|
||||
|
||||
this.DwordCount = partialCount;
|
||||
this.finishGeneric(errorMask, count);
|
||||
|
||||
//console.log(this.mnemonic + " finishTapeIO: " + errorMask.toString(8) + " for " + count.toString() +
|
||||
// ", D=" + this.D.toString(8));
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500IOUnit.prototype.finishTapeRead = function finishTapeRead(errorMask, length) {
|
||||
/* Handles I/O finish for a tape drive read operation */
|
||||
var count;
|
||||
var memWords = (length+7) >>> 3;
|
||||
var partialCount = (errorMask % 0x040000) >>> 15;
|
||||
|
||||
if (this.D23F && memWords > this.DwordCount) {
|
||||
memWords = this.DwordCount;
|
||||
@@ -984,29 +1016,19 @@ B5500IOUnit.prototype.finishTapeRead = function finishDiskRead(errorMask, length
|
||||
}
|
||||
}
|
||||
|
||||
// For Mod III I/O Units, extra status bits can be reported in the word-count
|
||||
// field. The driver will report these in the higher-order bits of errorMask.
|
||||
// mask & 0x040000 => tape at EOT (becomes D14F)
|
||||
// mask & 0x080000 => tape at BOT (becomes D13F)
|
||||
// mask & 0x100000 => blank tape (becomes D12F)
|
||||
// mask & 0x200000 => reserved for relocated D29F memory parity bit (becomes D11F)
|
||||
// The original memory parity bit in D29F is relocated to D11F, and both
|
||||
// D02F and D29F are set unconditionally.
|
||||
this.finishTapeIO(errorMask, count);
|
||||
};
|
||||
|
||||
// In addition, the count of characters in a paritally-filled final word is
|
||||
// reported in the low-order three bits of the word count field. For a forward
|
||||
// read, this is the number of characters in the last partial word. For a back-
|
||||
// ward read, this is 7 minus the number of characters.
|
||||
/**************************************/
|
||||
B5500IOUnit.prototype.finishTapeWrite = function finishTapeWrite(errorMask, length) {
|
||||
/* Handles I/O finish for a tape drive write operation */
|
||||
|
||||
if ((errorMask & 0x3C0000) || this.D29F) {
|
||||
this.DwordCount = partialCount +
|
||||
((errorMask % 0x400000) >>> 18) * 0x08 +
|
||||
this.D29F * 0x40; // relocate the memory parity bit
|
||||
this.D02F = 1; // mark as a Mod III RD
|
||||
errorMask |= 0x08; // set the original mem parity bit unconditionally
|
||||
}
|
||||
/*** Temporary stub until tape write is implemented ***/
|
||||
|
||||
this.finishGeneric(errorMask, count);
|
||||
this.finishTapeIO(errorMask, length);
|
||||
|
||||
//console.log(this.mnemonic + " finishTapeWr: " + errorMask.toString(8) + " for " + length.toString() +
|
||||
// ", D=" + this.D.toString(8));
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
@@ -1021,17 +1043,17 @@ B5500IOUnit.prototype.initiateTapeIO = function initiateTapeIO(u) {
|
||||
this.D30F = 1; // (MAINTENANCE I/Os NOT YET IMPLEMENTED)
|
||||
this.finish();
|
||||
} else if (this.D23F && this.DwordCount == 0) { // forward or backward space
|
||||
u.space(this.boundFinishGeneric, 0, this.D22F);
|
||||
u.space(this.boundFinishTapeIO, 0, this.D22F);
|
||||
} else { // some sort of actual read
|
||||
memWords = (this.D23F ? this.DwordCount : this.buffer.length);
|
||||
u.read(this.boundFinishTapeRead, this.buffer, memWords*8, this.D21F, this.D22F);
|
||||
}
|
||||
} else { // tape write operation
|
||||
if (this.D23F && this.DwordCount == 0) {// interrogate drive status
|
||||
u.writeInterrogate(this.boundFinishGeneric, 0);
|
||||
u.writeInterrogate(this.boundFinishTapeIO, 0);
|
||||
} else if (this.D18F) { // memory inhibit
|
||||
if (this.D22F) { // backward write => rewind
|
||||
u.rewind(this.boundFinishGeneric);
|
||||
u.rewind(this.boundFinishTapeIO);
|
||||
} else {
|
||||
this.D30F = 1; // (ERASE NOT YET IMPLEMENTED)
|
||||
this.finish();
|
||||
|
||||
@@ -1031,8 +1031,9 @@ B5500Processor.prototype.streamCharacterToDest = function streamCharacterToDest(
|
||||
|
||||
/**************************************/
|
||||
B5500Processor.prototype.streamNumericToDest = function streamNumericToDest(count, zones) {
|
||||
/* Transfers character transfers from source to destination for the TRS syllable.
|
||||
"count" is the number of source characters to transfer */
|
||||
/* Transfers from source to destination for the TRN and TRZ syllables. "count"
|
||||
is the number of source characters to transfer. If transferring numerics and the
|
||||
low-order character has a negative sign (BA=10), sets MSFF=1 */
|
||||
var aBit; // A register bit nr
|
||||
var aw; // current A register word
|
||||
var bBit; // B register bit nr
|
||||
@@ -1062,11 +1063,8 @@ B5500Processor.prototype.streamNumericToDest = function streamNumericToDest(coun
|
||||
c = this.cc.fieldIsolate(aw, aBit, 6);
|
||||
if (zones) { // transfer only the zone portion of the char
|
||||
bw = this.cc.fieldInsert(bw, bBit, 2, c >>> 4);
|
||||
} else { // transfer only the numeric portion of the char
|
||||
bw = this.cc.fieldInsert(bw, bBit+2, 4, c);
|
||||
if (count == 1 && (c & 0x30) == 0x20) {
|
||||
this.MSFF = 1; // neg. sign
|
||||
}
|
||||
} else { // transfer the numeric portion with a zero zone
|
||||
bw = this.cc.fieldInsert(bw, bBit, 6, (c & 0x0F));
|
||||
}
|
||||
count--;
|
||||
if (bBit < 42) {
|
||||
@@ -1102,6 +1100,9 @@ B5500Processor.prototype.streamNumericToDest = function streamNumericToDest(coun
|
||||
} while (count);
|
||||
this.B = bw;
|
||||
this.Y = c; // for display purposes only
|
||||
if (!zones && (c & 0x30) == 0x20) {
|
||||
this.MSFF = 1; // last char had a negative sign
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3087,9 +3088,14 @@ B5500Processor.prototype.run = function run() {
|
||||
this.storeAviaS(); // [S] = A
|
||||
this.S++;
|
||||
this.M++;
|
||||
this.loadAviaM(); // A = [M]
|
||||
} while (--variant);
|
||||
if (--variant) {
|
||||
this.loadAviaM(); // A = [M]
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
this.AROF = 0;
|
||||
break;
|
||||
|
||||
case 0x06: // XX06: SED=Set destination address
|
||||
@@ -3629,7 +3635,7 @@ B5500Processor.prototype.run = function run() {
|
||||
break;
|
||||
|
||||
case 0x3D: // XX75: TRN=Transfer source numerics
|
||||
this.MSFF = 0; // initialize true-false FF
|
||||
this.MSFF = 0; // initialize for negative sign test
|
||||
this.streamNumericToDest(variant, false);
|
||||
break;
|
||||
|
||||
@@ -4484,7 +4490,7 @@ B5500Processor.prototype.run = function run() {
|
||||
this.A = cc.fieldIsolate(this.A, t1, t2);
|
||||
} else { // handle wrap-around in the source value
|
||||
this.A = cc.fieldInsert(
|
||||
cc.fieldIsolate(this.A, 0, t2-48+t1, t1+t2-48), 48-t2, 48-t1,
|
||||
cc.fieldIsolate(this.A, 0, t2-48+t1), 48-t2, 48-t1,
|
||||
cc.fieldIsolate(this.A, t1, 48-t1));
|
||||
}
|
||||
// approximate the shift cycle counts
|
||||
|
||||
@@ -84,6 +84,7 @@ window.addEventListener("load", function() {
|
||||
$$("LoadSelectBtn").disabled = false;
|
||||
$$("LoadBtn").disabled = false;
|
||||
$$("HaltBtn").disabled = true;
|
||||
$$("MemoryCheckBtn").disabled = false;
|
||||
window.focus();
|
||||
if (showAnnunciators) {
|
||||
$$("CentralControl").style.visibility = "visible";
|
||||
@@ -108,6 +109,7 @@ window.addEventListener("load", function() {
|
||||
$$("LoadSelectBtn").disabled = true;
|
||||
$$("LoadBtn").disabled = true;
|
||||
$$("HaltBtn").disabled = true;
|
||||
$$("MemoryCheckBtn").disabled = true;
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
@@ -165,6 +167,85 @@ window.addEventListener("load", function() {
|
||||
}
|
||||
}
|
||||
|
||||
function dumpState(caption) {
|
||||
/* Generates a dump of the processor states and all of memory */
|
||||
var doc;
|
||||
var lastPhase = -2;
|
||||
var win = window.open("", "", "resizable,scrollbars,status");
|
||||
var x;
|
||||
|
||||
var htmlMatch = /[<>&"]/g; // regular expression for escaping HTML text
|
||||
|
||||
function escapeHTML(text) {
|
||||
/* Returns "text" as escaped HTML */
|
||||
|
||||
function htmlFilter(char) {
|
||||
/* Used to escape HTML-sensitive characters in a string */
|
||||
switch (char) {
|
||||
case "&":
|
||||
return "&";
|
||||
case "<":
|
||||
return "<";
|
||||
case ">":
|
||||
return ">";
|
||||
case "\"":
|
||||
return """;
|
||||
default:
|
||||
return char;
|
||||
}
|
||||
}
|
||||
|
||||
return text.replace(htmlMatch, htmlFilter);
|
||||
}
|
||||
|
||||
function writer(phase, text) {
|
||||
/* Call-back function for cc.dumpSystemState */
|
||||
|
||||
switch (phase) {
|
||||
case 0:
|
||||
lastPhase = phase;
|
||||
doc.writeln(escapeHTML(text));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
if (phase == lastPhase) {
|
||||
doc.writeln(escapeHTML(text));
|
||||
} else {
|
||||
lastPhase = phase;
|
||||
doc.writeln();
|
||||
doc.writeln(escapeHTML(text));
|
||||
doc.writeln();
|
||||
}
|
||||
break;
|
||||
|
||||
case 32:
|
||||
if (phase != lastPhase) {
|
||||
lastPhase = phase;
|
||||
doc.writeln();
|
||||
}
|
||||
doc.writeln();
|
||||
doc.writeln(escapeHTML(text));
|
||||
break;
|
||||
|
||||
case -1:
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
doc = win.document;
|
||||
doc.open();
|
||||
doc.writeln("<html><head><title>B5500 Console State Dump</title>");
|
||||
doc.writeln("</head><body>");
|
||||
doc.write("<pre>");
|
||||
|
||||
cc.dumpSystemState(caption, writer);
|
||||
|
||||
doc.writeln("</pre></body></html>")
|
||||
doc.close();
|
||||
win.focus();
|
||||
}
|
||||
|
||||
function displayCallbacks() {
|
||||
/* Builds a table of outstanding callbacks */
|
||||
var cb;
|
||||
@@ -479,6 +560,10 @@ window.addEventListener("load", function() {
|
||||
$$("RetroVersion").innerHTML = B5500CentralControl.version;
|
||||
if (!checkBrowser()) {
|
||||
$$("BurroughsLogo").addEventListener("click", BurroughsLogo_Click, false);
|
||||
$$("B5500Logo").addEventListener("click", function(ev) {
|
||||
alert("Dynamic configuration management is not yet implemented");
|
||||
});
|
||||
|
||||
$$("PowerOnBtn").addEventListener("click", PowerOnBtn_Click, false);
|
||||
$$("PowerOffBtn").addEventListener("click", PowerOffBtn_Click, false);
|
||||
$$("HaltBtn").addEventListener("click", HaltBtn_Click, false);
|
||||
@@ -490,6 +575,9 @@ window.addEventListener("load", function() {
|
||||
B5500SystemConfiguration.PB ^= true;
|
||||
$$("RetroVersion").style.color = (B5500SystemConfiguration.PB ? "yellow" : "white");
|
||||
});
|
||||
$$("MemoryCheckBtn").addEventListener("click", function(ev) {
|
||||
dumpState("Memory Check Button");
|
||||
});
|
||||
|
||||
aControl = $$("AControlBtn");
|
||||
aNormal = $$("ANormalBtn");
|
||||
@@ -510,7 +598,7 @@ window.addEventListener("load", function() {
|
||||
<button id=HaltBtn class="redButton" DISABLED>HALT</button>
|
||||
|
||||
<button id=NotReadyBtn class=whiteButton>NOT READY</button>
|
||||
<button id=MemoryCheckBtn class=redButton>MEMORY CHECK</button>
|
||||
<button id=MemoryCheckBtn class=redButton DISABLED>MEMORY CHECK</button>
|
||||
<button id=LoadBtn class="blackButton blackLit" DISABLED>LOAD</button>
|
||||
|
||||
<button id=LoadSelectBtn class="yellowButton" DISABLED>CARD LOAD SELECT</button>
|
||||
|
||||
@@ -274,10 +274,6 @@ B5500MagTapeDrive.prototype.setTapeRemote = function setTapeRemote(ready) {
|
||||
this.statusChange(0);
|
||||
this.removeClass(this.$$("MTRemoteBtn"), "yellowLit");
|
||||
this.addClass(this.$$("MTLocalBtn"), "yellowLit");
|
||||
if (this.timer) {
|
||||
clearCallback(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -319,7 +315,7 @@ B5500MagTapeDrive.prototype.tapeRewind = function tapeRewind(makeReady) {
|
||||
this.timer = null;
|
||||
this.busy = false;
|
||||
this.removeClass(this.$$("MTRewindingLight"), "whiteLit");
|
||||
if (makeReady) {
|
||||
if (makeReady && this.tapeState == this.tapeRemote) {
|
||||
this.ready = true;
|
||||
this.statusChange(1);
|
||||
}
|
||||
@@ -515,7 +511,7 @@ B5500MagTapeDrive.prototype.bcdSpaceBackward = function bcdSpaceBackward(checkEO
|
||||
|
||||
if (imgIndex <= 0) {
|
||||
this.setAtBOT(true);
|
||||
this.errorMask |= 0x100010; // set blank-tape and parity bits
|
||||
this.errorMask |= 0x100000; // set blank-tape bit
|
||||
} else {
|
||||
if (this.atEOT) {
|
||||
this.setAtEOT(false);
|
||||
@@ -652,7 +648,7 @@ B5500MagTapeDrive.prototype.bcdReadBackward = function bcdReadBackward(oddParity
|
||||
|
||||
if (imgIndex <= 0) {
|
||||
this.setAtBOT(true);
|
||||
this.errorMask |= 0x100010; // set blank-tape and parity bits
|
||||
this.errorMask |= 0x100000; // set blank-tape bit
|
||||
} else {
|
||||
if (this.atEOT) {
|
||||
this.setAtEOT(false);
|
||||
@@ -930,9 +926,10 @@ B5500MagTapeDrive.prototype.writeInterrogate = function writeInterrogate(finish,
|
||||
} else if (!this.ready) {
|
||||
finish(0x04, 0); // report unit not ready
|
||||
} else {
|
||||
this.buildErrorMask(0, true);
|
||||
if (!this.writeRing) {
|
||||
this.errorMask |= 0x50; // RD bits 26 & 28 => no write ring
|
||||
if (this.writeRing) {
|
||||
this.buildErrorMask(0, true);
|
||||
} else {
|
||||
this.errorMask |= 0x50; // RD bits 26 & 28 => no write ring, don't return Mod III bits
|
||||
}
|
||||
finish(this.errorMask, 0);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ var running = false; // true if in run mode
|
||||
var runSilently = false; // true if system state is not to be refreshed after each step
|
||||
var stopAddress = 0; // run-until-address stop point
|
||||
|
||||
var htmlMatch = /[<>&"]/g; // regular expression for escaping HTML text
|
||||
|
||||
var accessor = { // Memory access control block
|
||||
requestorID: "A", // Memory requestor ID
|
||||
addr: 0, // Memory address
|
||||
@@ -265,6 +267,52 @@ function setText(id, text) {
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHTML(text) {
|
||||
/* Returns "text" as escaped HTML */
|
||||
|
||||
function htmlFilter(char) {
|
||||
/* Used to escape HTML-sensitive characters in a string */
|
||||
switch (char) {
|
||||
case "&":
|
||||
return "&";
|
||||
case "<":
|
||||
return "<";
|
||||
case ">":
|
||||
return ">";
|
||||
case "\"":
|
||||
return """;
|
||||
default:
|
||||
return char;
|
||||
}
|
||||
}
|
||||
|
||||
return text.replace(htmlMatch, htmlFilter);
|
||||
}
|
||||
|
||||
function padLeft(text, minLength, char) {
|
||||
/* Pads "text" on the left to a total length of "minLength" with "char" */
|
||||
var s = text.toString();
|
||||
var len = s.length;
|
||||
var pad = char || " ";
|
||||
|
||||
while (len++ < minLength) {
|
||||
s = pad + s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function padOctal(value, octades) {
|
||||
/* Formats "value" as an octal number of "octades" length, left-padding with
|
||||
zeroes as necessary */
|
||||
var text = value.toString(8);
|
||||
|
||||
if (value >= 0) {
|
||||
return padLeft(text, octades, "0");
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
function parseToOctal(e) {
|
||||
/* Obtains the .value from the element "e", parses it, and returns the
|
||||
result as a B5500 numeric word. If the element text contains any of "-+eE."
|
||||
@@ -320,20 +368,6 @@ function parseToOctal(e) {
|
||||
return v;
|
||||
}
|
||||
|
||||
function padOctal(value, octades) {
|
||||
/* Formats "value" as an octal number of "octades" length, left-padding with
|
||||
zeroes as necessary */
|
||||
var text = value.toString(8);
|
||||
var len = text.length;
|
||||
|
||||
if (value >= 0) {
|
||||
while (len++ < octades) {
|
||||
text = "0" + text;
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function decodeSyllable(syllable, mode, level, msff) {
|
||||
/* Decodes the B5500 operator "syllable" and returns a string formatted with the
|
||||
mnemonic description of that syllable.
|
||||
@@ -660,6 +694,171 @@ function displaySystemState() {
|
||||
//window.focus();
|
||||
}
|
||||
|
||||
function dumpState__OLD(caption) {
|
||||
/* Generates a dump of the processor states and all of memory */
|
||||
var addr;
|
||||
var bic;
|
||||
var doc;
|
||||
var dupCount = 0;
|
||||
var lastLine = "";
|
||||
var line;
|
||||
var lineAddr;
|
||||
var mod;
|
||||
var win = window.open("", "", "resizable,scrollbars,status");
|
||||
var x;
|
||||
|
||||
function convertWordtoANSI(value) {
|
||||
/* Converts the "value" as a B5500 word to an eight character string and returns it */
|
||||
var c; // current character
|
||||
var s = ""; // working string value
|
||||
var w = value; // working word value
|
||||
var x; // character counter
|
||||
|
||||
for (x=0; x<8; x++) {
|
||||
c = w % 64;
|
||||
w = (w-c)/64;
|
||||
s = BICtoANSI[c] + s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function dumpProcessorState(px) {
|
||||
/* Dumps the register state for the specified processor */
|
||||
var procNr = (px === cc.P1 ? "1" : "2");
|
||||
|
||||
doc.writeln();
|
||||
doc.writeln("Processor P" + procNr + " = " + px.mnemonic + ":");
|
||||
|
||||
doc.writeln();
|
||||
doc.writeln("NCSF=" + px.NCSF + " CWMF=" + px.CWMF + " MSFF=" + px.MSFF + " SALF=" + px.SALF +
|
||||
" VARF=" + px.VARF);
|
||||
doc.writeln("C=" + padOctal(px.C, 5) + " L=" + px.L + " P=" + padOctal(px.P, 16) + " PROF=" + px.TROF +
|
||||
" T=" + padOctal(px.T, 4) + " TROF=" + px.TROF);
|
||||
doc.writeln("I=" + padLeft(px.I.toString(2), 8, "0") + " E=" + padLeft(px.E.toString(2), 6, "0") +
|
||||
" Q=" + padLeft(px.Q.toString(2), 12, "0") + " [bit masks]");
|
||||
doc.writeln("M=" + padOctal(px.M, 5) + " G=" + px.G + " H=" + px.H);
|
||||
doc.writeln("S=" + padOctal(px.S, 5) + " K=" + px.K + " V=" + px.V);
|
||||
doc.writeln("F=" + padOctal(px.F, 5) + " R=" + padOctal(px.R, 3));
|
||||
doc.writeln();
|
||||
doc.writeln("X= " + padOctal(px.X, 13) + " Y=" + padOctal(px.Y, 2) + " Z=" + padOctal(px.Z, 2) +
|
||||
" N=" + px.N);
|
||||
doc.writeln("A=" + padOctal(px.A, 16) + " AROF=" + px.AROF);
|
||||
doc.writeln("B=" + padOctal(px.B, 16) + " BROF=" + px.BROF);
|
||||
}
|
||||
|
||||
doc = win.document;
|
||||
doc.open();
|
||||
doc.writeln("<html><head><title>B5500 Syllable Debugger State Dump</title>");
|
||||
doc.writeln("</head><body>");
|
||||
doc.writeln("<pre>Dump by " + escapeHTML(caption || "(unknown)") + " : " + new Date().toString());
|
||||
|
||||
dumpProcessorState(cc.P1);
|
||||
if (cc.P2) {
|
||||
dumpProcessorState(cc.P2);
|
||||
}
|
||||
|
||||
doc.writeln();
|
||||
for (mod=0; mod<0x8000; mod+=0x1000) {
|
||||
for (addr=0; addr<0x1000; addr+=4) {
|
||||
lineAddr = mod+addr;
|
||||
line = bic = "";
|
||||
for (x=0; x<4; x++) {
|
||||
accessor.addr = lineAddr+x;
|
||||
cc.fetch(accessor);
|
||||
if (accessor.MAIL) {
|
||||
line += escapeHTML(" << ADDR INH >> ");
|
||||
bic += "????????";
|
||||
} else if (accessor.MPED) {
|
||||
line += escapeHTML(" << PARITY >> ");
|
||||
bic += "????????";
|
||||
} else if (accessor.MAED) {
|
||||
line += escapeHTML(" << INV ADDR >> ");
|
||||
bic += "????????";
|
||||
} else {
|
||||
line += " " + padOctal(accessor.word, 16);
|
||||
bic += convertWordtoANSI(accessor.word);
|
||||
}
|
||||
} // for x
|
||||
|
||||
if (line == lastLine && lineAddr < 0x7FFC) {
|
||||
dupCount++;
|
||||
} else {
|
||||
if (dupCount > 0) {
|
||||
doc.writeln();
|
||||
doc.writeln("..... ................ for " + dupCount*4 + " words");
|
||||
dupCount = 0;
|
||||
}
|
||||
doc.writeln();
|
||||
doc.write(padOctal(lineAddr, 5));
|
||||
doc.write(" ");
|
||||
doc.write(line);
|
||||
doc.write(" ");
|
||||
doc.writeln(escapeHTML(bic));
|
||||
lastLine = line;
|
||||
}
|
||||
} // for addr
|
||||
} // for mod
|
||||
|
||||
doc.writeln("</pre></body></html>")
|
||||
doc.close();
|
||||
win.focus();
|
||||
}
|
||||
|
||||
function dumpState(caption) {
|
||||
/* Generates a dump of the processor states and all of memory */
|
||||
var doc;
|
||||
var lastPhase = -2;
|
||||
var win = window.open("", "", "resizable,scrollbars,status");
|
||||
var x;
|
||||
|
||||
function writer(phase, text) {
|
||||
/* Call-back function for cc.dumpSystemState */
|
||||
|
||||
switch (phase) {
|
||||
case 0:
|
||||
lastPhase = phase;
|
||||
doc.writeln(escapeHTML(text));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
if (phase == lastPhase) {
|
||||
doc.writeln(escapeHTML(text));
|
||||
} else {
|
||||
lastPhase = phase;
|
||||
doc.writeln();
|
||||
doc.writeln(escapeHTML(text));
|
||||
doc.writeln();
|
||||
}
|
||||
break;
|
||||
|
||||
case 32:
|
||||
if (phase != lastPhase) {
|
||||
lastPhase = phase;
|
||||
doc.writeln();
|
||||
}
|
||||
doc.writeln();
|
||||
doc.writeln(escapeHTML(text));
|
||||
break;
|
||||
|
||||
case -1:
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
doc = win.document;
|
||||
doc.open();
|
||||
doc.writeln("<html><head><title>B5500 Syllable Debugger State Dump</title>");
|
||||
doc.writeln("</head><body>");
|
||||
doc.write("<pre>");
|
||||
|
||||
cc.dumpSystemState(caption, writer);
|
||||
|
||||
doc.writeln("</pre></body></html>")
|
||||
doc.close();
|
||||
win.focus();
|
||||
}
|
||||
|
||||
function establishSilence(beQuiet) {
|
||||
/* Maintains synchronization between the global "runSilently" flag, the
|
||||
RunSilently checkbox, and cc.inhCCI03F */
|
||||
@@ -762,19 +961,14 @@ function runIt(ev) {
|
||||
alert("P1.S out of range");
|
||||
stopAddress = 0;
|
||||
}
|
||||
/***************************
|
||||
if (px.C < 0x40) {
|
||||
// There is an interrupt
|
||||
switch (px.C) {
|
||||
case 0x31: // P1 invalid address
|
||||
case 0x32: // P1 stack overflow
|
||||
case 0x3C: // P1 integer overflow
|
||||
alert("Unexpected Processor Interrupt occurred");
|
||||
stopAddress = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/********************
|
||||
if (stopAddress == 0x31 && (px.I & 0x02)) { // There is an Invalid Address interrupt
|
||||
dumpState("Invalid Address");
|
||||
stopAddress = 0;
|
||||
}
|
||||
***************************/
|
||||
********************/
|
||||
|
||||
if (--count <= 0) {
|
||||
count = runningCycles;
|
||||
break; // exit loop to pick up events
|
||||
@@ -971,6 +1165,18 @@ function selectProcessor_onChange(ev) {
|
||||
}
|
||||
}
|
||||
|
||||
function powerOff_onClick(ev) {
|
||||
/* Power off the system in response to the Power Off button */
|
||||
|
||||
cc.powerOff();
|
||||
}
|
||||
|
||||
function dump_onClick(ev) {
|
||||
/* Dumps the system state to a new window */
|
||||
|
||||
dumpState("User Requested");
|
||||
}
|
||||
|
||||
function checkBrowser() {
|
||||
/* Checks whether this browser can support the necessary stuff */
|
||||
var missing = "";
|
||||
@@ -1147,6 +1353,8 @@ function initialize() {
|
||||
$$("FileSelector").addEventListener("change", fileSelector_onChange, false);
|
||||
$$("HardwareLoad").addEventListener("click", hardwareLoad_onClick, false);
|
||||
$$("SelectProcessor").addEventListener("change", selectProcessor_onChange, false);
|
||||
$$("PowerOff").addEventListener("click", powerOff_onClick, false);
|
||||
$$("Dump").addEventListener("click", dump_onClick, false);
|
||||
$$("GoBtn").addEventListener("click", goIt, false);
|
||||
$$("StepBtn").addEventListener("click", stepIt, false);
|
||||
$$("RunBtn").addEventListener("click", runIt, false);
|
||||
@@ -1203,6 +1411,10 @@ window.onload = function() {
|
||||
<option value=1 selected>Show P1
|
||||
<option value=2>Show P2
|
||||
</select>
|
||||
|
||||
<input id=PowerOff type=button value="Power Off">
|
||||
|
||||
<input id=Dump type=button value="Dump">
|
||||
</p>
|
||||
|
||||
<table id=RegisterBank1 class="normal border">
|
||||
|
||||
Reference in New Issue
Block a user