mirror of
https://github.com/pkimpel/retro-220.git
synced 2026-03-27 02:14:45 +00:00
Commit 220 emulator version 0.3c:
1. Rework magnetic tape drive implementation to use Promises; refactor and consolidate drive operation routines. 2. Implement remaining magnetic tape operations: MOW, MOR, MRD, MRR, MTS, MFS, MTC, MFC. 3. Implement better error status reporting in mag tape control MISC register. 4. Implement memory and Processor-state dump by clicking the Burroughs "meatball" logo. 5. Implement preliminary run-time statistics on Console. 6. Use Function.bind() to bind context in Processor and mag tape scripts.
This commit is contained in:
@@ -229,22 +229,22 @@ function B220Processor(config, devices) {
|
||||
this.rightPanelOpen = false;
|
||||
|
||||
// Context-bound routines
|
||||
this.boundUpdateLampGlow = B220Processor.bindMethod(this, B220Processor.prototype.updateLampGlow);
|
||||
this.boundConsoleOutputSign = B220Processor.prototype.consoleOutputSign.bind(this);
|
||||
this.boundConsoleOutputChar = B220Processor.prototype.consoleOutputChar.bind(this);
|
||||
this.boundConsoleOutputFinished = B220Processor.prototype.consoleOutputFinished.bind(this);
|
||||
this.boundConsoleInputReceiveChar = B220Processor.prototype.consoleInputReceiveChar.bind(this);
|
||||
this.boundConsoleInputInitiateNormal = B220Processor.prototype.consoleInputInitiateNormal.bind(this);
|
||||
this.boundConsoleInputInitiateInverse = B220Processor.prototype.consoleInputInitiateInverse.bind(this);
|
||||
|
||||
this.boundConsoleOutputSign = B220Processor.bindMethod(this, B220Processor.prototype.consoleOutputSign);
|
||||
this.boundConsoleOutputChar = B220Processor.bindMethod(this, B220Processor.prototype.consoleOutputChar);
|
||||
this.boundConsoleOutputFinished = B220Processor.bindMethod(this, B220Processor.prototype.consoleOutputFinished);
|
||||
this.boundConsoleInputReceiveChar = B220Processor.bindMethod(this, B220Processor.prototype.consoleInputReceiveChar);
|
||||
this.boundConsoleInputInitiateNormal = B220Processor.bindMethod(this, B220Processor.prototype.consoleInputInitiateNormal);
|
||||
this.boundConsoleInputInitiateInverse = B220Processor.bindMethod(this, B220Processor.prototype.consoleInputInitiateInverse);
|
||||
this.boundCardatronOutputWord= B220Processor.prototype.cardatronOutputWord.bind(this);
|
||||
this.boundCardatronOutputFinished = B220Processor.prototype.cardatronOutputFinished.bind(this);
|
||||
this.boundCardatronReceiveWord = B220Processor.prototype.cardatronReceiveWord.bind(this);
|
||||
|
||||
this.boundCardatronOutputWord= B220Processor.bindMethod(this, B220Processor.prototype.cardatronOutputWord);
|
||||
this.boundCardatronOutputFinished = B220Processor.bindMethod(this, B220Processor.prototype.cardatronOutputFinished);
|
||||
this.boundCardatronReceiveWord = B220Processor.bindMethod(this, B220Processor.prototype.cardatronReceiveWord);
|
||||
this.boundMagTapeComplete = B220Processor.prototype.magTapeComplete.bind(this);
|
||||
this.boundMagTapeReceiveWord = B220Processor.prototype.magTapeReceiveWord.bind(this);
|
||||
this.boundMagTapeSendWord = B220Processor.prototype.magTapeSendWord.bind(this);
|
||||
|
||||
this.boundMagTapeComplete = B220Processor.bindMethod(this, B220Processor.prototype.magTapeComplete);
|
||||
this.boundMagTapeReceiveWord = B220Processor.bindMethod(this, B220Processor.prototype.magTapeReceiveWord);
|
||||
this.boundMagTapeSendWord = B220Processor.bindMethod(this, B220Processor.prototype.magTapeSendWord);
|
||||
this.boundIoComplete = B220Processor.prototype.ioComplete.bind(this);
|
||||
|
||||
this.clear(); // Create and initialize the processor state
|
||||
|
||||
@@ -256,13 +256,13 @@ function B220Processor(config, devices) {
|
||||
* Global Constants *
|
||||
***********************************************************************/
|
||||
|
||||
B220Processor.version = "0.03b";
|
||||
B220Processor.version = "0.03c";
|
||||
|
||||
B220Processor.tick = 1000/200000; // milliseconds per clock cycle (200KHz)
|
||||
B220Processor.cyclesPerMilli = 1/B220Processor.tick;
|
||||
// clock cycles per millisecond (200 => 200KHz)
|
||||
B220Processor.timeSlice = 10; // maximum processor time slice, ms
|
||||
B220Processor.delayAlpha = 0.001; // decay factor for exponential weighted average delay
|
||||
B220Processor.delayAlpha = 0.0001; // decay factor for exponential weighted average delay
|
||||
B220Processor.delayAlpha1 = 1-B220Processor.delayAlpha;
|
||||
B220Processor.slackAlpha = 0.0001; // decay factor for exponential weighted average slack
|
||||
B220Processor.slackAlpha1 = 1-B220Processor.slackAlpha;
|
||||
@@ -322,15 +322,6 @@ B220Processor.emptyFunction = function emptyFunction() {
|
||||
return;
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B220Processor.bindMethod = function bindMethod(context, f) {
|
||||
/* Returns a new function that binds the function "f" to the object "context".
|
||||
Note that this is a static constructor property function, NOT an instance
|
||||
method of the CC object */
|
||||
|
||||
return function bindMethodAnon() {return f.apply(context, arguments)};
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B220Processor.bcdBinary = function bcdBinary(v) {
|
||||
/* Converts the BCD value "v" to a binary number and returns it. If the
|
||||
@@ -2834,21 +2825,19 @@ B220Processor.prototype.cardatronReceiveWord = function cardatronReceiveWord(wor
|
||||
***********************************************************************/
|
||||
|
||||
/**************************************/
|
||||
B220Processor.prototype.magTapeComplete = function magTapeComplete(alarm, control, word) {
|
||||
B220Processor.prototype.magTapeComplete = function magTapeComplete(control, word) {
|
||||
/* Call-back routine to signal completion of a magnetic tape operation. If
|
||||
"alarm" is true, the Magnetic Tape Alarm will be set. If "control" is true,
|
||||
the contents of "word" are processed as a tape control word and an appropriate
|
||||
branch is set up. Unconditionally terminates the tape I/O instruction */
|
||||
"control" is true, the contents of "word" are processed as a tape control
|
||||
word and an appropriate branch is set up. Unconditionally terminates the
|
||||
tape I/O instruction */
|
||||
var aaaa;
|
||||
var bbbb;
|
||||
|
||||
if (alarm) {
|
||||
this.setMagneticTapeCheck(true);
|
||||
} else if (control) {
|
||||
if (control) {
|
||||
this.D.set(word);
|
||||
bbbb = word%0x10000;
|
||||
aaaa = ((word - bbbb)/0x10000)%0x10000;
|
||||
if ((word - word%0x10000000000)%2) { // if sign bit is 1,
|
||||
if (word%0x20000000000 >= 0x10000000000) { // if sign bit is 1,
|
||||
bbbb = this.bcdAdd(bbbb, this.B.value, 4); // B-adjust the low-order 4 digits
|
||||
}
|
||||
|
||||
@@ -2862,13 +2851,13 @@ B220Processor.prototype.magTapeComplete = function magTapeComplete(alarm, contro
|
||||
}
|
||||
}
|
||||
|
||||
this.ioComplete(true);
|
||||
Promise.resolve(true).then(this.boundIoComplete);
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B220Processor.prototype.magTapeSendWord = function magTapeSendWord(initial) {
|
||||
B220Processor.prototype.magTapeSendWord = function magTapeSendWord(initialFetch) {
|
||||
/* Sends the next of data from memory to the tape control unit, starting at
|
||||
the current operand address in the C register. "initial" is true if this
|
||||
the current operand address in the C register. "initialFetch" is true if this
|
||||
call is the first to fetch words for a block. This causes the routine to
|
||||
save the current operand address in the control digits of C. Returns
|
||||
binary -1 if the processor has been cleared or a memory address error
|
||||
@@ -2878,7 +2867,7 @@ B220Processor.prototype.magTapeSendWord = function magTapeSendWord(initial) {
|
||||
if (!this.AST.value) {
|
||||
result = -1; // we've probably been cleared
|
||||
} else {
|
||||
if (initial) {
|
||||
if (initialFetch) {
|
||||
this.clockIn();
|
||||
this.CCONTROL = this.CADDR; // copy C address into control digits
|
||||
}
|
||||
@@ -2900,9 +2889,9 @@ B220Processor.prototype.magTapeSendWord = function magTapeSendWord(initial) {
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B220Processor.prototype.magTapeReceiveWord = function magTapeReceiveWord(initial, word) {
|
||||
B220Processor.prototype.magTapeReceiveWord = function magTapeReceiveWord(initialStore, word) {
|
||||
/* Stores the next of data from the tape control unit to memory, starting at
|
||||
the current operand address in the C register. "initial" is true if this
|
||||
the current operand address in the C register. "initialStore" is true if this
|
||||
call is the first to store words for a block. This causes the routine to
|
||||
save the current operand address in the control digits of C. Returns
|
||||
binary -1 if the processor has been cleared or a memory address error
|
||||
@@ -2913,7 +2902,7 @@ B220Processor.prototype.magTapeReceiveWord = function magTapeReceiveWord(initial
|
||||
if (!this.AST.value) {
|
||||
result = -1; // we've probably been cleared
|
||||
} else {
|
||||
if (initial) {
|
||||
if (initialStore) {
|
||||
this.clockIn();
|
||||
this.CCONTROL = this.CADDR; // copy C address into control digits
|
||||
}
|
||||
@@ -2923,7 +2912,7 @@ B220Processor.prototype.magTapeReceiveWord = function magTapeReceiveWord(initial
|
||||
this.C.set((this.CCONTROL*0x100 + this.COP)*0x10000 + this.CADDR);
|
||||
this.D.set(word);
|
||||
if (this.vDigit & 0x08) { // B-adjustment of words is enabled
|
||||
sign = (word - word%0x10000000000);
|
||||
sign = (word - word%0x10000000000)/0x10000000000;
|
||||
if (sign & 0x08) { // this word is to be B-adjusted
|
||||
word = (sign&0x07)*0x10000000000 + word%0x10000000000 -
|
||||
word%0x100000 + this.bcdAdd(word, this.B.value, 4);
|
||||
@@ -3601,22 +3590,33 @@ B220Processor.prototype.execute = function execute() {
|
||||
this.vDigit = this.CCONTROL%0x10;
|
||||
this.ioInitiate();
|
||||
if (this.vDigit & 0x08) { // MRW/MDA: rewind, with or without lockout
|
||||
this.magTape.rewind(this.D.value, this.boundMagTapeComplete, this.boundMagTapeSendWord);
|
||||
this.magTape.rewind(this.D.value);
|
||||
} else if (this.vDigit & 0x04) { // MLS: lane select
|
||||
this.magTape.laneSelect(this.D.value, this.boundMagTapeComplete, this.boundMagTapeSendWord);
|
||||
this.magTape.laneSelect(this.D.value);
|
||||
} else { // MTS/MFS: search or field search
|
||||
if (this.D.value%0x80000000000 < 0x40000000000) { // full-word search
|
||||
this.magTape.search(this.D.value, this.boundMagTapeComplete, 0, this.boundMagTapeSendWord);
|
||||
} else { // partial-word search based on sL in B
|
||||
this.magTape.search(this.D.value, this.boundMagTapeComplete, this.B.value, this.boundMagTapeSendWord);
|
||||
if (this.D.value%0x80000000000 < 0x40000000000) { // sign 4-bit = 0: full-word search
|
||||
this.magTape.search(this.D.value, 0);
|
||||
} else { // partial-word search based on sL00 in B
|
||||
this.magTape.search(this.D.value, this.B.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x51: //--------------------- MTC/MFC Magnetic tape scan/field scan
|
||||
this.setProgramCheck(1);
|
||||
this.operationComplete();
|
||||
this.opTime = 0.160;
|
||||
if (!this.magTape) {
|
||||
this.setMagneticTapeCheck(true); // no tape control
|
||||
this.operationComplete();
|
||||
} else {
|
||||
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
|
||||
this.ioInitiate();
|
||||
if (this.D.value%0x80000000000 < 0x40000000000) { // sign 4-bit = 0: full-word search
|
||||
this.magTape.scan(this.D.value, 0);
|
||||
} else { // partial-word search based on sL00 in B
|
||||
this.magTape.scan(this.D.value, this.B.value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x52: //--------------------- MRD Magnetic tape read
|
||||
@@ -3626,9 +3626,9 @@ B220Processor.prototype.execute = function execute() {
|
||||
this.operationComplete();
|
||||
} else {
|
||||
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
|
||||
this.vDigit = this.CCONTROL%0x10;
|
||||
this.vDigit = this.CCONTROL%0x10; // controlword and B-mod bits
|
||||
this.ioInitiate();
|
||||
this.magTape.read(this.D.value, this.boundMagTapeComplete, false, this.boundMagTapeReceiveWord);
|
||||
this.magTape.read(this.D.value, false);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -3639,9 +3639,9 @@ B220Processor.prototype.execute = function execute() {
|
||||
this.operationComplete();
|
||||
} else {
|
||||
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
|
||||
this.vDigit = this.CCONTROL%0x10;
|
||||
this.vDigit = this.CCONTROL%0x10; // controlword and B-mod bits
|
||||
this.ioInitiate();
|
||||
this.magTape.read(this.D.value, this.boundMagTapeComplete, true, this.boundMagTapeReceiveWord);
|
||||
this.magTape.read(this.D.value, true);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -3653,7 +3653,7 @@ B220Processor.prototype.execute = function execute() {
|
||||
} else {
|
||||
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
|
||||
this.ioInitiate();
|
||||
this.magTape.initialWrite(this.D.value, this.boundMagTapeComplete, false, this.boundMagTapeSendWord);
|
||||
this.magTape.initialWrite(this.D.value, false);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -3665,7 +3665,7 @@ B220Processor.prototype.execute = function execute() {
|
||||
} else {
|
||||
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
|
||||
this.ioInitiate();
|
||||
this.magTape.initialWrite(this.D.value, this.boundMagTapeComplete, true, this.boundMagTapeSendWord);
|
||||
this.magTape.initialWrite(this.D.value, true);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -3677,7 +3677,7 @@ B220Processor.prototype.execute = function execute() {
|
||||
} else {
|
||||
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
|
||||
this.ioInitiate();
|
||||
this.magTape.overwrite(this.D.value, this.boundMagTapeComplete, false, this.boundMagTapeSendWord);
|
||||
this.magTape.overwrite(this.D.value, false);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -3689,7 +3689,7 @@ B220Processor.prototype.execute = function execute() {
|
||||
} else {
|
||||
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
|
||||
this.ioInitiate();
|
||||
this.magTape.overwrite(this.D.value, this.boundMagTapeComplete, true, this.boundMagTapeSendWord);
|
||||
this.magTape.overwrite(this.D.value, true);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -3703,13 +3703,13 @@ B220Processor.prototype.execute = function execute() {
|
||||
this.ioInitiate();
|
||||
switch (this.CCONTROL%0x10) {
|
||||
case 1: // MPB: position tape backward
|
||||
this.magTape.positionBackward(this.D.value, this.boundMagTapeComplete);
|
||||
this.magTape.positionBackward(this.D.value);
|
||||
break;
|
||||
case 2: // MPE: position tape at end
|
||||
this.magTape.positionAtEnd(this.D.value, this.boundMagTapeComplete);
|
||||
this.magTape.positionAtEnd(this.D.value);
|
||||
break;
|
||||
default: // MPF: position tape forward
|
||||
this.magTape.positionForward(this.D.value, this.boundMagTapeComplete);
|
||||
this.magTape.positionForward(this.D.value);
|
||||
break;
|
||||
} // switch on operation variant
|
||||
}
|
||||
@@ -3723,7 +3723,7 @@ B220Processor.prototype.execute = function execute() {
|
||||
} else {
|
||||
opTime = 0.14;
|
||||
if (this.CCONTROL%0x10 == 1) { // MIE
|
||||
if (this.magTape.testUnitAtMagneticEOT(this.D.value)) {
|
||||
if (this.magTape.testUnitAtEOT(this.D.value)) {
|
||||
this.P.set(this.CADDR);
|
||||
this.opTime += 0.020;
|
||||
}
|
||||
@@ -4257,19 +4257,20 @@ B220Processor.prototype.loadDefaultProgram = function loadDefaultProgram() {
|
||||
this.MM[ 2] = 0x1000540000; // MIW 0,1,10,100
|
||||
this.MM[ 3] = 0x1750540100; // MIW 100,1,7,50
|
||||
this.MM[ 4] = 0x1500550079; // MIR 79,1,5,00
|
||||
this.MM[ 5] = 0x1101542000; // MIW 2000,1,1,1 // write control block
|
||||
this.MM[ 5] = 0x1101542000; // MIW 2000,1,1,1 // write an EOT block
|
||||
|
||||
this.MM[ 6] = 0x1008500000; // MRW 1
|
||||
this.MM[ 7] = 0x1000560000; // MOW 0,1,10,100
|
||||
this.MM[ 8] = 0x1750560100; // MOW 100,1,7,50
|
||||
this.MM[ 9] = 0x1500570079; // MOR 79,1,5,00
|
||||
this.MM[ 10] = 0x1101012000; // MOW 2000,1,1,1 // TEMP: changed to a NOP
|
||||
//this.MM[ 10] = 0x1101562000; // MOW 2000,1,1,1
|
||||
this.MM[ 10] = 0x1110562000; // MOW 2000,1,1,10 // TEMP: block-length=10, should fire EOT control word
|
||||
|
||||
this.MM[ 11] = 0x1008500000; // MRW 1
|
||||
this.MM[ 12] = 0x1000523000; // MRD 3000,1,10,0
|
||||
this.MM[ 13] = 0x1700524000; // MRD 4000,1,7,0
|
||||
this.MM[ 14] = 0x1500534350; // MRR 4350,1,5,0
|
||||
this.MM[ 15] = 0x1100534800; // MRR 4800,1,1,0 // should be a control block
|
||||
this.MM[ 15] = 0x1100534800; // MRR 4800,1,1,0 // should be an EOT block
|
||||
|
||||
this.MM[ 16] = 0x1009500000; // MDA 1
|
||||
this.MM[ 17] = 0x7777009999; // HLT 9999,7777
|
||||
@@ -4283,6 +4284,7 @@ B220Processor.prototype.loadDefaultProgram = function loadDefaultProgram() {
|
||||
this.MM[2000] = 0x9920012002; // end-of-tape control word
|
||||
this.MM[2001] = 0x9999999999; // storage for end-of-tape block state
|
||||
this.MM[2002] = 0x9999008421; // HLT: target for end-of-tape control branch
|
||||
this.MM[2003] = 0x0000300011; // branch to read test sequence
|
||||
|
||||
// Simple counter speed test
|
||||
this.MM[ 80] = 0x0000120082; // ADD 82
|
||||
|
||||
@@ -66,16 +66,22 @@ B220ConsolePrinter.maxScrollLines = 15000;
|
||||
// Maximum amount of paper scrollback
|
||||
|
||||
B220ConsolePrinter.codeXlate = [ // translate internal B220 code to ANSI
|
||||
" ", "?", " ", ".", "\u00A4", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 00-0F
|
||||
"&", "?", "?", "$", "*", "\f", "\n", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 10-1F
|
||||
"-", "/", "?", ",", "%", "?", "\t", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 20-2F
|
||||
"?", "?", "?", "#", "@", "!", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 30-3F
|
||||
"?", "A", "B", "C", "D", "E", "F", "G", "H", "I", "?", "?", "?", "?", "?", "?", // 40-4F
|
||||
"?", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "?", "?", "?", "?", "?", "?", // 50-5F
|
||||
"?", "?", "S", "T", "U", "V", "W", "X", "Y", "Z", "?", "?", "?", "?", "?", "?", // 60-6F
|
||||
"?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 70-7F
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "?", "?", "?", "?", "?", "?", // 80-8F
|
||||
"?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?"]; // 90-9F
|
||||
" ", "?", " ", ".", "\u00A4", "?", "?", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 00-0F
|
||||
"&", "?", "?", "$", "*", "\f", "\n", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 10-1F
|
||||
"-", "/", "?", ",", "%", "?", "\t", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 20-2F
|
||||
"?", "?", "?", "#", "@", "\\", "?", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 30-3F
|
||||
"?", "A", "B", "C", "D", "E", "F", "G", "H", "I", "!", "!", "!", "!", "!", "!", // 40-4F
|
||||
"?", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "!", "!", "!", "!", "!", "!", // 50-5F
|
||||
"?", "?", "S", "T", "U", "V", "W", "X", "Y", "Z", "!", "!", "!", "!", "!", "!", // 60-6F
|
||||
"?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 70-7F
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "!", "!", "!", "!", "!", // 80-8F
|
||||
"?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 90-9F
|
||||
"!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // A0-AF
|
||||
"!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // B0-BF
|
||||
"!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // C0-CF
|
||||
"!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // D0-DF
|
||||
"!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // E0-EF
|
||||
"!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!"]; // F0-FF
|
||||
|
||||
|
||||
/**************************************/
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
font-weight: bold}
|
||||
|
||||
#IntervalTimerResetCaption {
|
||||
bottom: 21px;
|
||||
left: calc(50% - 206px);
|
||||
bottom: 20px;
|
||||
width: 120px;
|
||||
text-align: right}
|
||||
#IntervalTimerResetBtn {
|
||||
@@ -69,7 +69,7 @@
|
||||
bottom: 12px}
|
||||
#PowerOffCaption {
|
||||
left: calc(50% + 92px);
|
||||
bottom: 21px}
|
||||
bottom: 20px}
|
||||
|
||||
#VersionDiv {
|
||||
right: 150px;
|
||||
@@ -85,6 +85,25 @@
|
||||
right: 12px;
|
||||
bottom: 12px}
|
||||
|
||||
#ProcDelta {
|
||||
position: absolute;
|
||||
width: 64px;
|
||||
text-align: right;
|
||||
left: 120px;
|
||||
bottom: 12px}
|
||||
#ProcSlack {
|
||||
position: absolute;
|
||||
width: 64px;
|
||||
text-align: right;
|
||||
left: 190px;
|
||||
bottom: 12px}
|
||||
#ProcRun {
|
||||
position: absolute;
|
||||
width: 64px;
|
||||
text-align: right;
|
||||
left: 260px;
|
||||
bottom: 12px}
|
||||
|
||||
#PanelSurface {
|
||||
height: 100%;
|
||||
width: 100%}
|
||||
|
||||
@@ -148,17 +148,16 @@
|
||||
<button id=LeftPanelBtn class=plainButton title="Some day there may be a Left Panel">Left Panel</button>
|
||||
<button id=RightPanelBtn class=plainButton title="Some day there may be a Right Panel, too">Right Panel</button>
|
||||
|
||||
<div id=ProcDelta title="Average Processor delay delta (ms)"></div>
|
||||
<div id=ProcSlack title="Average Processor slack time (ms)"></div>
|
||||
<div id=ProcRun title="Average Processor run time (ms)"></div>
|
||||
|
||||
<div id=VersionDiv class=caption>
|
||||
retro-220 <span id=EmulatorVersion>?.??</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id=Diagnostics>
|
||||
<br>
|
||||
Proc Delta: <input id=ProcDelta class="rj" type=text size=10>
|
||||
|
||||
Latency: <input id=LastLatency class="rj" type=text size=10>
|
||||
|
||||
<table id=CallbackTable>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -17,7 +17,7 @@ function B220ControlConsole(p, systemShutdown) {
|
||||
/* Constructor for the ControlConsole object */
|
||||
var h = 600;
|
||||
var w = 1064;
|
||||
var mnemonic = "ControlConsole";
|
||||
var mnemonic = "Console";
|
||||
var inputConfig = p.config.getNode("ConsoleInput");
|
||||
var outputConfig = p.config.getNode("ConsoleOutput");
|
||||
var u;
|
||||
@@ -30,12 +30,12 @@ function B220ControlConsole(p, systemShutdown) {
|
||||
|
||||
this.keyboard = new B220ConsoleKeyboard(p);
|
||||
|
||||
this.boundLamp_Click = B220Util.bindMethod(this, B220ControlConsole.prototype.lamp_Click);
|
||||
this.boundPowerBtn_Click = B220Util.bindMethod(this, B220ControlConsole.prototype.powerBtn_Click);
|
||||
this.boundSwitch_Click = B220Util.bindMethod(this, B220ControlConsole.prototype.switch_Click);
|
||||
this.boundStartBtn_Click = B220Util.bindMethod(this, B220ControlConsole.prototype.startBtn_Click);
|
||||
this.boundResetTimer = B220Util.bindMethod(this, B220ControlConsole.prototype.resetTimer);
|
||||
this.boundUpdatePanel = B220Util.bindMethod(this, B220ControlConsole.prototype.updatePanel);
|
||||
this.boundMeatballMemdump = B220ControlConsole.prototype.meatballMemdump.bind(this);
|
||||
this.boundLamp_Click = B220ControlConsole.prototype.lamp_Click.bind(this);
|
||||
this.boundPowerBtn_Click = B220ControlConsole.prototype.powerBtn_Click.bind(this);
|
||||
this.boundSwitch_Click = B220ControlConsole.prototype.switch_Click.bind(this);
|
||||
this.boundResetTimer = B220ControlConsole.prototype.resetTimer.bind(this);
|
||||
this.boundUpdatePanel = B220ControlConsole.prototype.updatePanel.bind(this);
|
||||
|
||||
// Configure the console input unit objects. These are paper-tape readers.
|
||||
this.inputUnit = [
|
||||
@@ -109,6 +109,24 @@ B220ControlConsole.onSwitchImage = "./resources/ToggleUp.png";
|
||||
B220ControlConsole.offOrganSwitchImage = "./resources/Organ-Switch-Up.png"
|
||||
B220ControlConsole.onOrganSwitchImage = "./resources/Organ-Switch-Down.png"
|
||||
|
||||
B220ControlConsole.codeXlate = [ // translate internal B220 code to ANSI
|
||||
" ", "_", " ", ".", "\u00A4", "_", "_", "_", "_", "_", "!", "!", "!", "!", "!", "!", // 00-0F
|
||||
"&", "_", "_", "$", "*", "^", "~", "_", "_", "_", "!", "!", "!", "!", "!", "!", // 10-1F
|
||||
"-", "/", "_", ",", "%", "_", "|", "_", "_", "_", "!", "!", "!", "!", "!", "!", // 20-2F
|
||||
"_", "_", "_", "#", "@", "\\", "_", "_", "_", "_", "!", "!", "!", "!", "!", "!", // 30-3F
|
||||
"_", "A", "B", "C", "D", "E", "F", "G", "H", "I", "!", "!", "!", "!", "!", "!", // 40-4F
|
||||
"_", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "!", "!", "!", "!", "!", "!", // 50-5F
|
||||
"_", "_", "S", "T", "U", "V", "W", "X", "Y", "Z", "!", "!", "!", "!", "!", "!", // 60-6F
|
||||
"_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "!", "!", "!", "!", "!", "!", // 70-7F
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "!", "!", "!", "!", "!", // 80-8F
|
||||
"_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "!", "!", "!", "!", "!", "!", // 90-9F
|
||||
"!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // A0-AF
|
||||
"!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // B0-BF
|
||||
"!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // C0-CF
|
||||
"!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // D0-DF
|
||||
"!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // E0-EF
|
||||
"!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!"]; // F0-FF
|
||||
|
||||
/**************************************/
|
||||
B220ControlConsole.prototype.$$ = function $$(e) {
|
||||
return this.doc.getElementById(e);
|
||||
@@ -150,6 +168,234 @@ B220ControlConsole.prototype.beforeUnload = function beforeUnload(ev) {
|
||||
return msg;
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B220ControlConsole.prototype.meatballMemdump = function meatballMemdump() {
|
||||
/* Opens a temporary window and formats the current processor and memory
|
||||
state to it */
|
||||
var doc = null; // loader window.document
|
||||
var p = this.p; // local copy of Processor object
|
||||
var paper = null; // <pre> element to receive dump lines
|
||||
var trimRightRex = /[\s\uFEFF\xA0]+$/;
|
||||
var win = this.window.open("./B220FramePaper.html", this.mnemonic + "-MEMDUMP",
|
||||
"location=no,scrollbars=yes,resizable,width=800,height=600");
|
||||
var xlate = B220ControlConsole.codeXlate; // local copy
|
||||
|
||||
function formatWord(w) {
|
||||
/* Formats a 220 numeric word as "S DDDDDDDDDD" and returns it */
|
||||
var s = padBCD(w, 11);
|
||||
|
||||
return s.substring(0, 1) + " " + s.substring(1);
|
||||
}
|
||||
|
||||
function padBCD(value, digits) {
|
||||
/* Formats "value" as a BCD number of "digits" length, left-padding with
|
||||
zeroes as necessary */
|
||||
var text = value.toString(16);
|
||||
|
||||
if (value < 0) {
|
||||
return text;
|
||||
} else {
|
||||
return padLeft(text, digits, "0");
|
||||
}
|
||||
}
|
||||
|
||||
function padLeft(text, minLength, c) {
|
||||
/* Pads "text" on the left to a total length of "minLength" with "c" */
|
||||
var s = text.toString();
|
||||
var len = s.length;
|
||||
var pad = c || " ";
|
||||
|
||||
while (len++ < minLength) {
|
||||
s = pad + s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function trimRight(text) {
|
||||
/* Returns the string with all terminating whitespace removed from "text" */
|
||||
|
||||
return text.replace(trimRightRex, '');
|
||||
}
|
||||
|
||||
function wordToANSI(value) {
|
||||
/* Converts the "value" as a 220 word to a five-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<5; ++x) {
|
||||
c = w % 256;
|
||||
w = (w-c)/256;
|
||||
s = xlate[c] + s;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
function writer(text) {
|
||||
/* Outputs one line of text to the dump window */
|
||||
|
||||
paper.appendChild(doc.createTextNode(trimRight(text) + "\n"));
|
||||
}
|
||||
|
||||
function dumpProcessorState() {
|
||||
/* Dumps the register state for the Processor */
|
||||
var s = "";
|
||||
var x = 0;
|
||||
|
||||
writer("");
|
||||
writer("Processor:");
|
||||
writer("");
|
||||
writer("A: " + formatWord(p.A.value) + " R: " + formatWord(p.R.value) +
|
||||
" D: " + formatWord(p.D.value));
|
||||
writer("");
|
||||
s = padBCD(p.C.value, 10);
|
||||
s = s.substring(0, 4) + " " + s.substring(4, 6) + " " + s.substring(6);
|
||||
writer("B: " + padBCD(p.B.value, 4) + " P: " + padBCD(p.P.value, 4) + " C: " + s +
|
||||
" E: " + padBCD(p.E.value, 4) + " S: " + padBCD(p.S.value, 4));
|
||||
|
||||
writer("");
|
||||
s = "Program Switches:";
|
||||
for (x=1; x<=10; ++x) {
|
||||
if (p["PC" + x%10 + "SW"]) {
|
||||
s += " " + x%10;
|
||||
} else {
|
||||
s += " .";
|
||||
}
|
||||
}
|
||||
|
||||
if (p.SONSW) {s += " S-ON"}
|
||||
if (p.SUNITSSW) {s += " S-UNITS"}
|
||||
if (p.STOCSW) {s += " S-TO-C"}
|
||||
if (p.STOPSW) {s += " S-TO-P"}
|
||||
writer(s);
|
||||
|
||||
s = "Flip-Flops:";
|
||||
if (p.digitCheckAlarm.value) {s += " DCK"}
|
||||
if (p.ALT.value) {s += " ALT"}
|
||||
if (p.AST.value) {s += " AST"}
|
||||
if (p.CCT.value) {s += " CCT"}
|
||||
if (p.CRT.value) {s += " CRT"}
|
||||
if (p.DPT.value) {s += " DPT"}
|
||||
if (p.EWT.value) {s += " EWT"}
|
||||
if (p.EXT.value) {s += " EXT"}
|
||||
if (p.HAT.value) {s += " HAT"}
|
||||
if (p.HCT.value) {s += " HCT"}
|
||||
if (p.HIT.value) {s += " HIT"}
|
||||
if (p.MAT.value) {s += " MAT"}
|
||||
if (p.MET.value) {s += " MET"}
|
||||
if (p.MNT.value) {s += " MNT"}
|
||||
if (p.OFT.value) {s += " OFT"}
|
||||
if (p.PAT.value) {s += " PAT"}
|
||||
if (p.PRT.value) {s += " PRT"}
|
||||
if (p.PZT.value) {s += " PZT"}
|
||||
if (p.RPT.value) {s += " RPT"}
|
||||
if (p.RUT.value) {s += " RUT"}
|
||||
if (p.SST.value) {s += " SST"}
|
||||
if (p.TAT.value) {s += " TAT"}
|
||||
if (p.UET.value) {s += " UET"}
|
||||
writer(s);
|
||||
s = " ";
|
||||
if (p.systemNotReady.value) {s += " SNR"}
|
||||
if (p.computerNotReady.value) {s += " CNR"}
|
||||
if (p.compareLowLamp.value) {s += " LOW"}
|
||||
if (p.compareEqualLamp.value) {s += " EQUAL"}
|
||||
if (p.compareLowLamp.value) {s += " HIGH"}
|
||||
if (p.C10.value) {s += " C10"}
|
||||
if (p.DST.value) {s += " DST"}
|
||||
if (p.LT1.value) {s += " LT1"}
|
||||
if (p.LT2.value) {s += " LT2"}
|
||||
if (p.LT3.value) {s += " LT3"}
|
||||
if (p.SCI.value) {s += " SCI"}
|
||||
if (p.SGT.value) {s += " SGT"}
|
||||
if (p.SUT.value) {s += " SUT"}
|
||||
if (p.TBT.value) {s += " TBT"}
|
||||
if (p.TCT.value) {s += " TCT"}
|
||||
if (p.TPT.value) {s += " TPT"}
|
||||
if (p.TWT.value) {s += " TWT"}
|
||||
writer(s);
|
||||
}
|
||||
|
||||
function memdumpDriver() {
|
||||
/* Driver for formatting the memory and Processor state dump */
|
||||
var addr = 0;
|
||||
var dupCount = 0;
|
||||
var lastLine = "";
|
||||
var line = "";
|
||||
var top = p.memorySize-1; // max memory address
|
||||
var x = 0; // image data index
|
||||
|
||||
function dumpDupes() {
|
||||
/* Outputs the duplicate-line message, if any */
|
||||
|
||||
if (dupCount > 0) {
|
||||
writer(".... ..... DUP FOR " + dupCount + " LINE" + (dupCount>1 ? "S" : "") +
|
||||
" THRU " + padLeft(addr-1, 4, "0") + " .....");
|
||||
dupCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
while (paper.firstChild) { // delete any existing <pre> content
|
||||
paper.removeChild(paper.firstChild);
|
||||
}
|
||||
|
||||
writer("retro-220 Processor State and Memory Dump : " + new Date().toString());
|
||||
|
||||
dumpProcessorState();
|
||||
|
||||
// Dump all of memory
|
||||
writer("");
|
||||
writer("Memory: ");
|
||||
writer("");
|
||||
addr = 0;
|
||||
while (addr <= top) {
|
||||
// Format the next five words
|
||||
line = "";
|
||||
for (x=0; x<5; ++x) {
|
||||
line += " " + formatWord(p.MM[addr+x]);
|
||||
} // for x
|
||||
|
||||
// Check for duplicate lines; write a non-duplicate
|
||||
if (line == lastLine) {
|
||||
++dupCount;
|
||||
} else {
|
||||
dumpDupes();
|
||||
lastLine = line;
|
||||
line = padLeft(addr, 4, "0") + line + " ";
|
||||
for (x=0; x<5; ++x) {
|
||||
line += wordToANSI(p.MM[addr+x]);
|
||||
} // for x
|
||||
|
||||
writer(line);
|
||||
}
|
||||
|
||||
addr += 5;
|
||||
} // for addr
|
||||
|
||||
dumpDupes();
|
||||
writer("");
|
||||
writer("End dump, memory size: " + (top+1).toString() + " words");
|
||||
}
|
||||
|
||||
function memdumpSetup() {
|
||||
/* Loads a status message into the "paper" rendering area, then calls
|
||||
dumpDriver after a short wait to allow the message to appear */
|
||||
|
||||
win.removeEventListener("load", memdumpSetup, false);
|
||||
doc = win.document;
|
||||
doc.title = "retro-220 Console: Meatball Memdump";
|
||||
paper = doc.getElementById("Paper");
|
||||
writer("Rendering the dump... please wait...");
|
||||
setTimeout(memdumpDriver, 50);
|
||||
}
|
||||
|
||||
// Outer block of meatBallMemdump
|
||||
win.moveTo((screen.availWidth-win.outerWidth)/2, (screen.availHeight-win.outerHeight)/2);
|
||||
win.focus();
|
||||
win.addEventListener("load", memdumpSetup, false);
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B220ControlConsole.prototype.displayCallbackState = function displayCallbackState() {
|
||||
/* Builds a table of outstanding callback state */
|
||||
@@ -259,9 +505,10 @@ B220ControlConsole.prototype.updatePanel = function updatePanel() {
|
||||
this.equalLamp.set(p.compareEqualLamp.glow);
|
||||
this.highLamp.set(p.compareHighLamp.glow);
|
||||
|
||||
this.$$("ProcDelta").textContent = p.delayDeltaAvg.toFixed(2) + " D";
|
||||
this.$$("ProcSlack").textContent = p.procSlackAvg.toFixed(2) + " S";
|
||||
this.$$("ProcRun").textContent = p.procRunAvg.toFixed(2) + " R";
|
||||
/********** DEBUG **********
|
||||
this.$$("ProcDelta").value = p.procSlackAvg.toFixed(2);
|
||||
this.$$("LastLatency").value = p.delayDeltaAvg.toFixed(2);
|
||||
this.displayCallbackState();
|
||||
***************************/
|
||||
};
|
||||
@@ -737,6 +984,7 @@ B220ControlConsole.prototype.consoleOnLoad = function consoleOnLoad() {
|
||||
this.resetTransferSwitch.addEventListener("click", this.boundSwitch_Click);
|
||||
this.tcuClearSwitch.addEventListener("click", this.boundSwitch_Click);
|
||||
|
||||
this.$$("BurroughsMeatball").addEventListener("click", this.boundMeatballMemdump);
|
||||
this.$$("IntervalTimerResetBtn").addEventListener("click", this.boundResetTimer);
|
||||
this.$$("PowerOffBtn").addEventListener("click", this.boundPowerBtn_Click);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -197,6 +197,13 @@ SELECT {
|
||||
right: 16px}
|
||||
|
||||
#MTUnloadedLight {
|
||||
top: 36px;
|
||||
right: 8px;
|
||||
width: 64px}
|
||||
|
||||
#MTLaneNrLight {
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
top: 60px;
|
||||
right: 8px;
|
||||
width: 64px}
|
||||
|
||||
@@ -87,6 +87,7 @@
|
||||
<img id=MTReel src="./resources/MagTapeReel.jpg">
|
||||
|
||||
<div id=MTUnloadedLight class=annunciator>UNLOADED</div>
|
||||
<div id=MTLaneNrLight class=annunciator>LANE 0</div>
|
||||
<div id=MTAtBOTLight class=annunciator>AT BOT</div>
|
||||
<div id=MTAtEOTLight class=annunciator>AT EOT</div>
|
||||
<div id=MTRewindingLight class=annunciator>REWINDING</div>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -49,7 +49,7 @@
|
||||
<option value="100">100-word blocks
|
||||
</select>
|
||||
|
||||
<input id=MTLoadWriteEnableCheck type=checkbox value="1">
|
||||
<input id=MTLoadWriteEnableCheck type=checkbox value="1" checked>
|
||||
<label for=MTLoadWriteEnableCheck>Write Enabled</label>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
CACHE MANIFEST
|
||||
# retro-220 emulator 0.03b, 2017-11-02 12:30
|
||||
# retro-220 emulator 0.03c, 2017-11-17 06:15
|
||||
CACHE:
|
||||
../emulator/B220Processor.js
|
||||
B220.css
|
||||
|
||||
Reference in New Issue
Block a user