diff --git a/emulator/B220Processor.js b/emulator/B220Processor.js index e08732c..13978c8 100644 --- a/emulator/B220Processor.js +++ b/emulator/B220Processor.js @@ -62,6 +62,9 @@ function B220Processor(config, devices) { /* Constructor for the 220 Processor module object */ + this.mnemonic = "CPU"; + B220Processor.instance = this; // externally-available object reference (for DiagMonitor) + // Emulator control this.cardatron = null; // reference to Cardatron Control Unit this.config = config; // reference to SystemConfig object @@ -83,6 +86,7 @@ function B220Processor(config, devices) { this.opTime = 0; // estimated time for current instruction, ms this.procStart = 0; // Javascript time that the processor started running, ms this.procTime = 0; // total internal running time for processor, ms + this.runStamp = 0; // timestamp of last run() slice, ms this.runTimer = 0; // elapsed run-time timer value, ms this.scheduler = 0; // current setCallback token @@ -576,13 +580,8 @@ B220Processor.prototype.updateGlow = function updateGlow(beta) { /* Updates the lamp glow for all registers and flip-flops in the system. Beta is a bias in the range (0,1). For normal update use 0; to freeze the current state in the lamps use 1 */ - var clocking = (this.execClock < 0); var gamma = (this.RUT.value ? beta || 0 : 1); - if (clocking) { - this.clockIn(); - } - // Primary Registers this.A.updateGlow(gamma); this.B.updateGlow(gamma); @@ -661,10 +660,15 @@ B220Processor.prototype.updateGlow = function updateGlow(beta) { this.SST.updateGlow(gamma); this.UET.updateGlow(gamma); } +}; - if (clocking) { - this.clockOut(); - } +/**************************************/ +B220Processor.prototype.clockIn = function clockIn() { + /* Updates the emulated processor clock during asynchronous I/O so that + glow averages can be updated based on elapsed time. Also used at the end of + and I/O to synchronize the emulated clock with real time */ + + this.execClock = performance.now(); }; @@ -791,7 +795,7 @@ B220Processor.Register.prototype.setBit = function setBit(bitNr, value) { Note that the glow is always aged by at least one clock tick */ var alpha = Math.min(Math.max(this.p.execClock-this.lastExecClock, B220Processor.tick)/ B220Processor.maxGlowTime, 1.0); - var bit = value%2; + var bit = (value ? 1 : 0); if (bitNr < this.bits) { // Update the lamp glow for the former state. @@ -965,39 +969,6 @@ B220Processor.FlipFlop.prototype.flip = function flip() { }; -/*********************************************************************** -* Timing and Statistics Functions * -***********************************************************************/ - -/**************************************/ -B220Processor.prototype.clockOut = function clockOut() { - /* Turns off the accumulation of emulated time for the current instruction - during I/O. Once the processor regains control, it should call this.clockIn - to increment this.execClock by the amount of elapsed time the I/O was - running asynchronously */ - var stamp = performance.now(); - - while (this.execClock > 0) { - this.execClock -= stamp; - } - - return stamp; -}; - -/**************************************/ -B220Processor.prototype.clockIn = function clockIn() { - /* Turns on the accumulation of emulated time for the current instruction - after an elapsed period of asynchronous I/O has completed */ - var stamp = performance.now(); - - while (this.execClock < 0) { - this.execClock += stamp; - } - - return stamp; -}; - - /*********************************************************************** * System Alarms * ***********************************************************************/ @@ -1405,10 +1376,10 @@ B220Processor.prototype.integerDivide = function integerDivide() { while (am >= dm) { am = this.bcdAdd(dm, am, 11, 1, 1); ++rd; + count += tSign; } rm += rd; // move digit into quotient - count += tSign*rd tSign = -tSign; } // for x @@ -1494,7 +1465,7 @@ B220Processor.prototype.floatingAdd = function floatingAdd(absolute) { if (am && dm) { // both mantissas are non-zero compl = (aSign^sign); - am = this.bcdAdd(am, dm, 9, compl, compl); + am = this.bcdAdd(am, dm, 11, compl, compl); dm = dSign = 0; dx = 0x10; @@ -1503,7 +1474,7 @@ B220Processor.prototype.floatingAdd = function floatingAdd(absolute) { if (this.Z.value) { // Reverse the sign toggle and recomplement the result. sign = 1-sign; - am = this.bcdAdd(am, 0, 9, 1, 1); + am = this.bcdAdd(am, 0, 11, 1, 1); timing += 0.060; } @@ -1644,7 +1615,7 @@ B220Processor.prototype.floatingMultiply = function floatingMultiply() { rd = rm % 0x10; count += B220Processor.multiplyDigitCounts[rd]; for (rc=rd; rc>0; --rc) { - am = this.bcdAdd(am, dm, 9, 0, 0); + am = this.bcdAdd(am, dm, 11, 0, 0); } // while rd ad = am % 0x10; @@ -1765,14 +1736,15 @@ B220Processor.prototype.floatingDivide = function floatingDivide() { // subtracting the divisor from the dividend, counting subtractions until // underflow occurs, and shifting the divisor left one digit. // The 220 probably did not work quite the way that it has been mechanized - // below, which is close to the way the 205 worked. + // below, which is close to the way the 205 emulator works. for (x=0; x<10; ++x) { // Repeatedly subtract D from A until we would get underflow. rd = 0; while (am >= dm) { - am = this.bcdAdd(dm, am, 9, 1, 1); + am = this.bcdAdd(dm, am, 11, 1, 1); ++rd; + count += tSign; } // Shift A & R to the left one digit, accumulating the quotient digit in R @@ -1783,23 +1755,22 @@ B220Processor.prototype.floatingDivide = function floatingDivide() { am = am*0x10 + rd; // shift into remainder except on last digit } - count += tSign*rd tSign = -tSign; } // for x // Rotate the quotient from R into A for 8 digits or until it's normalized - for (x=0; x<8 || am%0x100000000 < 0x10000000; ++x) { + for (x=0; x<8 || am < 0x10000000; ++x) { rd = (am - am%0x1000000000)/0x1000000000; rm = rm*0x10 + rd; rd = (rm - rm%0x10000000000)/0x10000000000; rm %= 0x10000000000; - am = (am%0x1000000000)*0x10 + rd; + am = (am%0x10000000)*0x10 + rd; } // Reconstruct the final product in the registers this.A.set((sign*0x100 + ax)*0x100000000 + am); this.R.set(sign*0x10000000000 + rm); - timing += 3.895 + 0.060*count; + timing += 4.075 + 0.060*count; } } @@ -1819,71 +1790,6 @@ B220Processor.prototype.floatingDivide = function floatingDivide() { * Partial-Word Operations * ***********************************************************************/ -/**************************************/ -B220Processor.prototype.partialWordAction = function partialWordAction(source, dest, action) { - /* Common control mechanism for partial-word operations. On entry, "source" - contains the value of the source register and "dest" contains the value of - the destination register. The source word will be shifted right and the - destination word will be rotated right through three stages for a total of - 11 digits, based on the "sL" partial-word designator in digits C/22: - - Stage 1: both words will be shifted/rotated without other change until the - "s" digit is positioned in the low-order position of each word. - The return value of the action() parameter is ignored. - Stage 2: both words will be shifted/rotated through "L" digits. For each - digit, the action() parameter will be called, passing the current - stage, destination word, and current source and destination - low-order digits. The action() function must return an updated - destination DIGIT. - Stage 3: the destination word will be rotated for the remainder of the 11 - digits, if any. The return value of action() is ignored. - - Returns the final destination word after having been rotated 11 digis. - Sets the Program Check alarm if L is not reduced to zero (field overflow) */ - var dd; // current destination digit - var dw = dest; // destination word (rotated right) - var L; // partial word digit counter - var s; // starting digit counter - var sd; // current source digit - var sw = source; // source word (shifted right) - var xd; // scratch destination - - s = this.CCONTROL >>> 12; - L = (this.CCONTROL >>> 8) & 0x0F; - if (L == 0) { - L = 10; - } - - this.DC.set(0x09); // set up to rotate 11 digits - while (this.DC.value < 0x20) { - sd = sw % 0x10; // get next source digit - dd = dw % 0x10; // get next destination digit - if (s > 0) { // rotate up to the "s" digit - s = (s+1)%10; // count s up until it overflows to zero - xd = action.call(this, 1, sd, dd, dw); - } else if (L > 0) { // rotate through the action digits - xd = dd; - dd = action.call(this, 2, sd, dd, dw); - dw += dd-xd; - --L; - } else { // rotate remaining digits - xd = action.call(this, 3, sd, dd, dw); - } - - sw = (sw-sd)/0x10; // shift source word - dw = (dw-dd)/0x10 + dd*0x10000000000; // rotate destination word - this.DC.inc(); - } // while DC < 20 - - this.CCONTROL = (s*0x10 + L)*0x100 + this.CCONTROL%0x100; - this.C.set((this.CCONTROL*0x100 + this.COP)*0x10000 + this.CADDR); - if (L > 0) { - this.setProgramCheck(1); - } - - return dw; -}; - /**************************************/ B220Processor.prototype.compareField = function compareField() { /* Implements CFA/CFR (18). Compares the value in either the A or R register @@ -1916,8 +1822,8 @@ B220Processor.prototype.compareField = function compareField() { this.readMemory(); if (!this.MET.value) { this.SUT.set(1); - this.D.set(this.IB.value); - dw = this.D.value; + dw = this.IB.value; + this.D.set(dw); if (this.CCONTROL%0x10 == 1) { rw = this.R.value; // CFR: Compare Field R @@ -1972,9 +1878,9 @@ B220Processor.prototype.compareField = function compareField() { this.X.set(rd); // for display only this.Y.set(dd); adder = (compl ? 9-rd : rd) + dd + carry; - if (adder < 10) { + if (adder < 10) { // decimal correct the adder carry = 0; - } else { // decimal correct the adder + } else { carry = 1; adder -= 10; } @@ -1987,7 +1893,7 @@ B220Processor.prototype.compareField = function compareField() { // Ignore any digits after L is exhausted } - // Shift both words right; increment digit counter + // Shift both words right (no need to rotate them) rw = (rw-rd)/0x10; dw = (dw-dd)/0x10; this.DC.inc(); @@ -2026,7 +1932,333 @@ B220Processor.prototype.compareField = function compareField() { if (L > 0) { this.setProgramCheck(1); } + } +}; +/**************************************/ +B220Processor.prototype.increaseFieldLocation = function increaseFieldLocation() { + /* Implements IFL (26). Increments a designated partial field in a memory + word by the two-digit value in (42) of the C register */ + var adder = 0; // current adder digit + var carry = 0; // carry flag defaults to 0, since we're adding + var dd; // current memory (D-register) digit + var dw; // memory (D-register) word + var L; // partial-word length + var rd; // current increase digit + var rw; // increase value + var s; // partial-word "s" digit + + this.opTime = 0.160; + this.SUT.set(0); + this.DST.set(0); + this.SGT.set(0); + this.E.set(this.CADDR); + this.readMemory(); + if (!this.MET.value) { + dw = this.IB.value; + this.D.set(dw); + rw = this.CCONTROL%0x100; // increase value + + s = this.CCONTROL >>> 12; + if (s == 0) { + s = 10; + } + L = (this.CCONTROL >>> 8) & 0x0F; + if (L == 0) { + L = 10; + } + + // Now go through a modified add cycle for each digit. + this.DC.set(0x09); // set up to rotate 11 digits + while (this.DC.value < 0x20) { + dd = dw%0x10; // get digit from memory value + if (s < 10) { // positition to the "s" digit + ++s; + adder = dd; // just copy the digit + } else if (L > 0) { // operate on the partial-word field + --L; + rd = rw%0x10; // get digit from increase value + this.X.set(rd); // for display only + this.Y.set(dd); + adder = rd + dd + carry; + if (adder < 10) { // decimal correct the adder + carry = 0; + } else { + carry = 1; + adder -= 10; + } + + this.Z.set(adder); // for display only + rw = (rw-rd)/0x10; // shift the increase value right + } else { + adder = dd; // copy any remaining digits after L is exhausted + } + + dw = (dw-dd)/0x10 + adder*0x10000000000; // rotate result into memory value + this.DC.inc(); + } // while DC < 20 + + this.D.set(dw); + this.IB.set(dw); + this.C10.set(carry); // set carry toggle + this.OFT.set(carry); // set overflow if there's a carry + + this.CCONTROL = ((s%10)*0x10 + L)*0x100 + this.CCONTROL%0x100; + this.C.set((this.CCONTROL*0x100 + this.COP)*0x10000 + this.CADDR); + if (L > 0) { // check whether L was decremented to zero + this.setProgramCheck(1); + } else { + this.writeMemory(); + } + } +}; + +/**************************************/ +B220Processor.prototype.decreaseFieldLocation = function decreaseFieldLocation(loadB) { + /* Implements DFL/DLB (27, 28). Decrements a designated partial field in a memory + word by the two-digit value in (42) of the C register */ + var adder = 0; // current adder digit + var carry = 1; // carry flag defaults to 1, since we're subtracting + var dd; // current memory (D-register) digit + var dw; // memory (D-register) word + var L; // partial-word length + var rd; // current decrease digit + var rw; // decrease value + var s; // partial-word "s" digit + + this.opTime = 0.160; + this.SUT.set(1); + this.DST.set(1); + this.SGT.set(0); + this.RPT.set(0); + if (loadB) { + this.B.set(0); + } + + this.E.set(this.CADDR); + this.readMemory(); + if (!this.MET.value) { + dw = this.IB.value; + this.D.set(dw); + rw = this.CCONTROL%0x100; // decrease value + + s = this.CCONTROL >>> 12; + if (s == 0) { + s = 10; + } + L = (this.CCONTROL >>> 8) & 0x0F; + if (L == 0) { + L = 10; + } + + // Now go through a modified add cycle for each digit. + this.DC.set(0x09); // set up to rotate 11 digits + while (this.DC.value < 0x20) { + dd = dw%0x10; // get digit from memory value + if (s < 10) { // positition to the "s" digit + ++s; + adder = dd; // just copy the digit + } else if (L > 0) { // operate on the partial-word field + --L; + rd = rw%0x10; // get digit from decrease value + this.X.set(rd); // for display only + this.Y.set(dd); + adder = 9 - rd + dd + carry; + if (adder < 10) { // decimal correct the adder + carry = 0; + } else { + carry = 1; + adder -= 10; + } + + this.Z.set(adder); // for display only + rw = (rw-rd)/0x10; // shift the decrease value right + if (loadB) { // shift adder digit into B if op=DLB + this.B.value = (this.B.value - this.B.value%0x10)/0x10 + adder*0x1000; + } + } else { + adder = dd; // copy any remaining digits after L is exhausted + } + + dw = (dw-dd)/0x10 + adder*0x10000000000; // rotate result into memory value + this.DC.inc(); + } // while DC < 20 + + this.D.set(dw); + this.IB.set(dw); + this.C10.set(carry); // set carry toggle + if (carry) { + this.RPT.set(1); // set repeat toggle if no underflow + } + if (loadB) { // set B register if op=DLB + this.B.set(this.B.value); + } + + this.CCONTROL = ((s%10)*0x10 + L)*0x100 + this.CCONTROL%0x100; + this.C.set((this.CCONTROL*0x100 + this.COP)*0x10000 + this.CADDR); + if (L > 0) { // check whether L was decremented to zero + this.setProgramCheck(1); + } else { + this.writeMemory(); + } + } +}; + +/**************************************/ +B220Processor.prototype.branchField = function branchField(regValue) { + /* Implements BFA/BFR (36, 37). Compares digits of a designated partial + field in the A or R register word to a rotating two-digit value in (42) of + the C register */ + var adder = 0; // current adder digit + var carry = 1; // carry flag defaults to 1, since we're subtracting + var dd; // current pattern digit + var dw; // rotating 2-digit pattern value + var equal = 1; // start out assuming equality + var L; // partial-word length + var rd; // current register digit + var rw = regValue; // register value + var s; // partial-word "s" digit + + this.opTime = 0.075; + this.SUT.set(1); + dw = this.CCONTROL%0x100; // 2-digit pattern to compare + + s = this.CCONTROL >>> 12; + if (s == 0) { + s = 10; + } + L = (this.CCONTROL >>> 8) & 0x0F; + if (L == 0) { + L = 10; + } + + // Now position the word and compare digits to the rotating pattern. + this.DC.set(0x09); // set up to rotate 11 digits + while (this.DC.value < 0x20) { + rd = rw%0x10; // get digit from register value + if (s < 10) { // positition to the "s" digit + ++s; // just ignore any initial digits + } else if (L > 0) { // operate on the partial-word field + --L; + dd = dw%0x10; // get digit from increase value + this.X.set(rd); // for display only + this.Y.set(dd); + adder = 9 - rd + dd + carry; + if (adder < 10) { // decimal correct the adder + carry = 0; + } else { + carry = 1; + adder -= 10; + } + + this.Z.set(adder); // for display only + dw = (dw-dd)/0x10 + dd*0x10;// rotate the 2-digit pattern + if (adder) { + equal = 0; // if the adder is not zero, fields are unequal + } + } else { + // just ignore any remaining digits after L is exhausted + } + + rw = (rw-rd)/0x10; // shift register word right (no need to rotate it) + this.DC.inc(); + } // while DC < 20 + + this.C10.set(carry); // set carry toggle, for display only + if (equal) { // if equality exists, branch + this.opTime += 0.020; + this.P.set(this.CADDR); + } + + this.CCONTROL = ((s%10)*0x10 + L)*0x100 + this.CCONTROL%0x100; + this.C.set((this.CCONTROL*0x100 + this.COP)*0x10000 + this.CADDR); + if (L > 0) { // check whether L was decremented to zero + this.setProgramCheck(1); + } +}; + +/**************************************/ +B220Processor.prototype.storeRegister = function storeRegister() { + /* Implements STA/STR/STB (40). Stores a whole word or a designated partial + field in a memory word based on the sL (22) digits of the C register */ + var adder; // current adder digit + var dd; // current memory (D-register) digit + var dw; // memory (D-register) word + var L; // partial-word length + var rd; // current increase digit + var rw; // increase value + var s; // partial-word "s" digit + var xd; // current D-register digit + var xw = 0; // word used to construct the D-register value + + this.opTime = 0.100; + this.E.set(this.CADDR); + this.readMemory(); + if (!this.MET.value) { + switch (this.CCONTROL%0x10) { + case 1: // STR: Store R + rw = this.R.value; + break; + case 2: // STB: Store B + rw = this.B.value; + break; + default: // STA: Store A + rw = this.A.value; + break; + } // switch + + if ((this.CCONTROL & 0x10) == 0) { // whole-word store + this.D.set(rw); + this.IB.set(rw); + s = L = 0; + } else { // partial-word store + this.D.set(0); + dw = this.IB.value; + + s = this.CCONTROL >>> 12; + if (s == 0) { + s = 10; + } + L = (this.CCONTROL >>> 8) & 0x0F; + if (L == 0) { + L = 10; + } + + // Now position the field and copy the digits + this.DC.set(0x09); // set up to rotate 11 digits + while (this.DC.value < 0x20) { + rd = rw%0x10; // get digit from register value + dd = dw%0x10; // get digit from memory value + if (s < 10) { // positition to the "s" digit + ++s; + adder = dd; // just copy the memory digit + xd = 0; + } else if (L > 0) { // operate on the partial-word field + --L; + adder = rd; // copy digit from register into memory value + xd = rd; + } else { + adder = dd; // just copy any remaining memory digits after L is exhausted + xd = 0; + } + + dw = (dw-dd)/0x10 + adder*0x10000000000; // rotate result digit into memory value + rw = (rw-rd)/0x10; // shift register value right (no need to rotate it) + xw = xw/0x10 + xd*0x10000000000; // copy zero or register digit into D-register + this.DC.inc(); + } // while DC < 20 + + this.D.set(xw); + this.IB.set(dw); + } + + this.CCONTROL = ((s%10)*0x10 + L)*0x100 + this.CCONTROL%0x100; + this.C.set((this.CCONTROL*0x100 + this.COP)*0x10000 + this.CADDR); + if (L > 0) { // check whether L was decremented to zero + this.setProgramCheck(1); + } else { + this.writeMemory(); + } } }; @@ -2208,7 +2440,6 @@ B220Processor.prototype.consoleOutputSign = function consoleOutputSign(printSign this.EWT.set(0); this.PZT.set(d == 2 && !this.HOLDPZTZEROSW); this.PA.set(0x80 + d); // translate numerically - this.clockOut(); printSign(this.PA.value, this.boundConsoleOutputChar); } } @@ -2226,12 +2457,10 @@ B220Processor.prototype.consoleOutputChar = function consoleOutputChar(printChar if (this.AST.value) { // if false, we've probably been cleared if (this.EWT.value) { if (this.CCONTROL%0x1000 < 0x10) { - this.clockOut(); printChar(0x35, this.boundConsoleOutputFinished); } else { this.C.inc(); this.CADDR = this.C.value%0x10000; - this.clockOut(); printChar(0x35, this.boundConsoleOutputSign); } } else if (this.PZT.value) { @@ -2246,7 +2475,6 @@ B220Processor.prototype.consoleOutputChar = function consoleOutputChar(printChar if (this.DC.value >= 0x20) { this.EWT.set(1); } - this.clockOut(); printChar(d, this.boundConsoleOutputChar); } else { // Output numerically @@ -2257,7 +2485,6 @@ B220Processor.prototype.consoleOutputChar = function consoleOutputChar(printChar this.DPT.set(0); this.LT1.set(0); // stop any zero-suppression this.PA.set(0x03); // decimal point code - this.clockOut(); printChar(0x03, this.boundConsoleOutputChar); return; // early exit } @@ -2278,7 +2505,6 @@ B220Processor.prototype.consoleOutputChar = function consoleOutputChar(printChar if (this.DC.value >= 0x20) { this.EWT.set(1); } - this.clockOut(); printChar(d, this.boundConsoleOutputChar); } } @@ -2915,19 +3141,16 @@ B220Processor.prototype.execute = function execute() { case 0x06: //--------------------- PWR Paper tape write this.opTime = 0.185; // just a guess... - this.AST.set(1); d = this.CCONTROL >>> 12; // get unit number if (d == 0) { d = 10; // xlate unit 0 to unit 10 } - this.clockOut(); + this.ioInitiate(); d = this.console.outputUnitSelect(d, this.boundConsoleOutputSign); if (d < 0) { // no unit available -- set alarm and quit - this.clockIn(); - this.AST.set(0); this.setPaperTapeCheck(1); - this.operationComplete(); + this.ioComplete(); } break; @@ -2936,6 +3159,7 @@ B220Processor.prototype.execute = function execute() { if (d == 0) { d = 10; // xlate unit 0 to unit 10 } + d = this.console.outputUnitSelect(d, B220Processor.emptyFunction); if (d < 0) { // if not ready, continue in sequence this.opTime = 0.015; @@ -2954,14 +3178,11 @@ B220Processor.prototype.execute = function execute() { case 0x09: //--------------------- SPO Supervisory print-out this.opTime = 0.185; // just a guess... - this.AST.set(1); - this.clockOut(); + this.ioInitiate(); d = this.console.outputUnitSelect(0, this.boundConsoleOutputSign); if (d < 0) { // no unit available -- set alarm and quit - this.clockIn(); - this.AST.set(0); this.setPaperTapeCheck(1); - this.operationComplete(); + this.ioComplete(); } break; @@ -3087,97 +3308,17 @@ B220Processor.prototype.execute = function execute() { break; case 0x26: //--------------------- IFL Increase field location - this.opTime = 0.160; - this.SUT.set(0); - this.E.set(this.CADDR); - this.readMemory(); - if (!this.MET.value) { - this.D.set(this.IB.value); - x = 0; // use as the carry bit - w = this.CCONTROL%0x100; // increase value - d = this.partialWordAction(0, this.IB.value, function IFL(stage, sd, dd, dw) { - var d; - if (stage != 2) { - d = dd; - } else { - d = w%0x10; // get current increase digit - w = (w-d)/0x10; - this.X.set(d); // for display only - this.Y.set(dd); // for display only - d += dd + x; // generate digit sum - if (d <= 9) { - x = 0; // no carry - } else { - x = 1; // set carry bit - d -= 10; // decimal adjust digit sum - } - this.Z.set(d); // for display only - } - return d; - }); - - this.D.set(d); - this.IB.set(d); - this.C10.set(x); // set carry toggle - this.OFT.set(x || w); // set overflow if carry or increase not exhausted - if (this.CCONTROL%0x1000 < 0x100) { // check whether L was decremented to zero - this.writeMemory(); - } - } + this.increaseFieldLocation(); this.operationComplete(); break; case 0x27: //--------------------- DFL Decrease field location - case 0x28: //--------------------- DLB Decrease field location, load B - this.opTime = 0.160; - this.SUT.set(1); - this.RPT.set(0); - if (this.COP == 0x28) { - this.B.set(0); - } - this.E.set(this.CADDR); - this.readMemory(); - if (!this.MET.value) { - this.D.set(this.IB.value); - x = 0; // use as the borrow bit - w = this.CCONTROL%0x100; // decrease value - d = this.partialWordAction(0, this.IB.value, function DFL_DLB(stage, sd, dd, dw) { - var d; - if (stage != 2) { - d = dd; - } else { - d = w%0x10; // get current decrease digit - w = (w-d)/0x10; - this.X.set(d); // for display only - this.Y.set(dd); // for display only - d = dd - x - d; // generate digit sum - if (d >= 0) { - x = 0; // no borrow - } else { - x = 1; // set borrow bit - d += 10; // decimal adjust digit sum - } - this.Z.set(d); // for display only - if (this.COP == 0x28) { - this.B.value = (this.B.value - this.B.value%0x10)/0x10 + d*0x1000; - } - } - return d; - }); + this.decreaseFieldLocation(false); + this.operationComplete(); + break; - this.D.set(d); - this.IB.set(d); - this.C10.set(x); // set carry/borrow toggle - if (x == 0 && w == 0) { - this.RPT.set(1); // set repeat toggle if no underflow - } - if (this.COP == 0x28) { - this.B.set(this.B.value); - } - if (this.CCONTROL%0x1000 < 0x100) { // check whether L was decremented to zero - this.writeMemory(); - } - } + case 0x28: //--------------------- DLB Decrease field location, load B + this.decreaseFieldLocation(true); this.operationComplete(); break; @@ -3288,35 +3429,12 @@ B220Processor.prototype.execute = function execute() { break; case 0x36: //--------------------- BFA Branch, field A - case 0x37: //--------------------- BFR Branch, field R - this.opTime = 0.075; - this.SUT.set(1); - x = 1; // equality flag: initialize true - w = this.CCONTROL%0x100; // 2-digit pattern to compare - this.partialWordAction(0, (this.COP == 0x36 ? this.A.value : this.R.value), - function CFA_CFR(stage, sd, dd, dw) { - var d; - if (stage == 2) { - d = w%0x10; // get current pattern digit - w = (w-d)/0x10 + d*0x10; // rotate the 2-digit pattern - this.X.set(9-d); // for display only - this.Y.set(d); // for display only - if (d == dd) { - this.Z.set(0); // for display only - this.C10.set(0); - } else { - x = 0; // inequality detected, reset flag - this.Z.set((dd - d + 10)%10); // for display only - this.C10.set(d > dd); - } - } - return dd; - }); + this.branchField(this.A.value); + this.operationComplete(); + break; - if (x) { // if equality exists, branch - this.opTime += 0.020; - this.P.set(this.CADDR); - } + case 0x37: //--------------------- BFR Branch, field R + this.branchField(this.R.value); this.operationComplete(); break; @@ -3355,43 +3473,7 @@ B220Processor.prototype.execute = function execute() { break; case 0x40: //--------------------- ST* Store A/R/B - this.opTime = 0.100; - this.E.set(this.CADDR); - this.readMemory(); - if (!this.MET.value) { - switch (this.CCONTROL%0x10) { - case 1: // STR: Store R - w = this.R.value; - break; - case 2: // STB: Store B - w = this.B.value; - break; - default: // STA: Store A - w = this.A.value; - break; - } // switch - - if ((this.CCONTROL & 0x10) == 0) { - this.D.set(w); - this.IB.set(w); - this.writeMemory(); - } else { - this.D.set(0); - w = this.partialWordAction(w, this.IB.value, function storeAction(stage, sd, dd, dw) { - if (stage == 2) { - this.D.value += sd-this.D.value%0x10; - } - d = this.D.value%0x10; - this.D.value = (this.D.value - d)/0x10 + d*0x10000000000; - return sd; - }); - this.D.set(this.D.value); - this.IB.set(w); - if (this.CCONTROL%0x1000 < 0x100) { // check whether L was decremented to zero - this.writeMemory(); - } - } - } + this.storeRegister(); this.operationComplete(); break; @@ -3829,18 +3911,30 @@ B220Processor.prototype.operationComplete = function operationComplete() { }; /**************************************/ -B220Processor.prototype.ioComplete = function operationComplete() { +B220Processor.prototype.ioComplete = function ioComplete() { /* Implements completion of the Execute cycle for an I/O instruction that has been executing asynchronously */ - this.operationComplete(); this.AST.set(0); - this.procTime += this.execClock; + this.clockIn(); + while (this.procTime < 0) { + this.procTime += this.execClock; + } + + this.operationComplete(); if (this.RUT.value) { this.schedule(); } }; +/**************************************/ +B220Processor.prototype.ioInitiate = function ioInitiate() { + /* Initiates asynchronous mode of the processor for I/O */ + + this.AST.set(1); + this.execLimit = 0; // kill the run() loop +}; + /**************************************/ B220Processor.prototype.run = function run() { /* Main execution control loop for the processor. Called from this.schedule() @@ -3880,7 +3974,7 @@ B220Processor.prototype.run = function run() { } break; } - } while (this.execClock < this.execLimit && !this.AST.value); + } while (this.execClock < this.execLimit); }; /**************************************/ @@ -3894,13 +3988,12 @@ B220Processor.prototype.schedule = function schedule() { an appropriate delay, thereby throttling the performance and allowing other modules to share the single Javascript execution thread */ var delayTime; // delay from/until next run() for this processor, ms - var runStamp; // real-world time at start of time slice, ms var stamp = performance.now(); // ending time for the delay and the run() call, ms this.scheduler = 0; // If run() has been called by a throttling delay, compute the delay stats. - if (this.lastDelayStamp > 0) { + if (this.delayLastStamp > 0) { delayTime = stamp - this.delayLastStamp; this.procSlack += delayTime; @@ -3911,25 +4004,24 @@ B220Processor.prototype.schedule = function schedule() { this.procSlackAvg*B220Processor.slackAlpha1; } - this.procTime -= this.execClock; // prepare to accumulate internal processor time - // Execute the time slice. - runStamp = stamp; // starting clock time for time slice + this.procTime -= this.execClock; // prepare to accumulate internal processor time + this.runStamp = stamp; // starting clock time for time slice this.run(); stamp = performance.now(); - this.procRunAvg = (stamp - runStamp)*B220Processor.slackAlpha + + this.procRunAvg = (stamp - this.runStamp)*B220Processor.slackAlpha + this.procRunAvg*B220Processor.slackAlpha1; // Determine what to do next. if (!this.RUT.value) { // Processor is stopped, just inhibit delay averaging on next call and exit. - this.lastDelayStamp = 0; + this.delayLastStamp = 0; this.procTime += this.execClock; // accumulate internal processor time for the slice } else if (this.AST.value) { - // Processor is still running, but idle during I/O. Do not update this.procTime. - this.lastDelayStamp = 0; + // Processor is idle during I/O, but still accumulating clocks. + this.delayLastStamp = 0; } else { this.procTime += this.execClock; // accumulate internal processor time for the slice @@ -3966,10 +4058,7 @@ B220Processor.prototype.start = function start() { this.delayRequested = 0; this.RUT.set(1); - // Start the timers - while (this.procTime >= 0) { - this.procTime -= stamp; - } + // Start the run timer while (this.runTimer >= 0) { this.runTimer -= stamp; } @@ -3990,8 +4079,9 @@ B220Processor.prototype.stop = function stop() { this.RUT.set(0); // Stop the timers + this.clockIn(); while (this.procTime < 0) { - this.procTime += stamp; + this.procTime += this.execClock; } while (this.runTimer < 0) { this.runTimer += stamp; @@ -4021,7 +4111,7 @@ B220Processor.prototype.step = function step() { /**************************************/ B220Processor.prototype.setStop = function setStop() { /* Initiates a halt of the processor. The processor will execute through - the next Execute cycle, then stop */ + the end of the Execute cycle, then stop */ if (this.poweredOn) { this.RUT.set(0); @@ -4220,91 +4310,45 @@ B220Processor.prototype.loadDefaultProgram = function loadDefaultProgram() { this.MM[ 138] = 0; this.MM[ 139] = 0x200000; - /************************************************* - // "Square Roots 100" running from the loops with R cleared for division: - // Block for the 6980 loop - this.MM[ 200] = 0x0000647039; // CAD 7039 - this.MM[ 201] = 0x0000127038; // ST 7038 - this.MM[ 202] = 0x0000647039; // CAD 7039 - this.MM[ 203] = 0x0000330000; // CR - this.MM[ 204] = 0x0000130005; // SR 5 - this.MM[ 205] = 0x0000617038; // DIV 7038 - this.MM[ 206] = 0x0000127037; // ST 7037 - this.MM[ 207] = 0x0000757038; // SUB 7038 - this.MM[ 208] = 0x0000127036; // ST 7036 - this.MM[ 209] = 0x0000667036; // CADA 7036 - this.MM[ 210] = 0x0000757035; // SUB 7035 - this.MM[ 211] = 0x0000737035; // OSGD 7035 - this.MM[ 212] = 0x0000287000; // CC 7000 - this.MM[ 213] = 0x0000647038; // CAD 7038 - this.MM[ 214] = 0x0000747037; // ADD 7037 - this.MM[ 215] = 0x0000330000; // CR - this.MM[ 216] = 0x0000130005; // SR 5 - this.MM[ 217] = 0x0000617034; // DIV 7034 - this.MM[ 218] = 0x0000127038; // ST 7038 - this.MM[ 219] = 0x0000206982; // CU 6982 - // Block for the 7000 loop - this.MM[ 220] = 0x0000647039; // CAD 7039 - this.MM[ 221] = 0x0000030505; // PTW 0505 - this.MM[ 222] = 0x0000647037; // CAD 7037 - this.MM[ 223] = 0x0000030810; // PTW 0810 - this.MM[ 224] = 0x0000647039; // CAD 7039 - this.MM[ 225] = 0x0000747033; // ADD 7033 - this.MM[ 226] = 0x0000127039; // ST 7039 - this.MM[ 227] = 0x0000206982; // CU 6982 + // "Square Roots 100" adapted for floating-point and relative precision: + this.MM[ 200] = 0x0000100239; // CAD 239 load initial argument + this.MM[ 201] = 0x0000400238; // STA 238 store as initial upper bound + this.MM[ 202] = 0x0000100239; // CAD 239 start of loop: load current argument + this.MM[ 203] = 0x0002450000; // CR clear R + this.MM[ 204] = 0x0000250238; // FDV 238 divide argument by upper bound + this.MM[ 205] = 0x0000400237; // STA 237 store as current result + this.MM[ 206] = 0x0000250238; // FDV 238 ratio to upper bound + this.MM[ 207] = 0x0000400236; // STA 236 store as current precision + this.MM[ 208] = 0x0001100235; // CAA 235 load target precision + this.MM[ 209] = 0x0000230236; // FSU 236 subtract current precision + this.MM[ 210] = 0x0001330218; // BSA 218,1 if current precision > target precision + this.MM[ 211] = 0x0000010000; // NOP we're done -- jump out to print + this.MM[ 212] = 0x0000100238; // CAD 238 load current upper bound + this.MM[ 213] = 0x0000220237; // FAD 237 add current result + this.MM[ 214] = 0x0002450000; // CR clear R + this.MM[ 215] = 0x0000250234; // FDV 234 divide by 2.0 to get new upper bound + this.MM[ 216] = 0x0000400238; // STA 238 store new upper bound + this.MM[ 217] = 0x0000300202; // BUN 202 do another iteration + this.MM[ 218] = 0x8011090239; // SPO 239 + this.MM[ 219] = 0x8011090237; // SPO 237 + this.MM[ 220] = 0x0010090232; // SPO 232 + this.MM[ 221] = 0x0000010000; // NOP + this.MM[ 222] = 0x0000100239; // CAD 239 load argument value + this.MM[ 223] = 0x0000220233; // FAD 233 add 1 to argument value + this.MM[ 224] = 0x0000400239; // STA 239 + this.MM[ 225] = 0x0000300201; // BUN 201 start sqrt for next argument value + this.MM[ 226] = 0; + this.MM[ 227] = 0; this.MM[ 228] = 0; this.MM[ 229] = 0; this.MM[ 230] = 0; this.MM[ 231] = 0; - this.MM[ 232] = 0; - this.MM[ 233] = 0x0000100000; - this.MM[ 234] = 0x0000200000; - this.MM[ 235] = 0x0000000010; - this.MM[ 236] = 0; - this.MM[ 237] = 0; - this.MM[ 238] = 0; - this.MM[ 239] = 0x0000200000; - - // "Square Roots 100" adapted for floating-point and relative precision: - this.MM[ 300] = 0x0000640339; // CAD 339 load initial argument - this.MM[ 301] = 0x0000120338; // ST 338 store as initial upper bound - this.MM[ 302] = 0x0000640339; // CAD 339 start of loop: load current argument - this.MM[ 303] = 0x0000330000; // CR clear R - this.MM[ 304] = 0x0000830338; // FDIV 338 divide argument by upper bound - this.MM[ 305] = 0x0000120337; // ST 337 store as current result - this.MM[ 306] = 0x0000830338; // FDIV 338 ratio to upper bound - this.MM[ 307] = 0x0000120336; // ST 336 store as current precision - this.MM[ 308] = 0x0000660335; // CADA 335 load target precision - this.MM[ 309] = 0x0000810336; // FSU 336 subtract current precision - this.MM[ 310] = 0x0000730335; // OSGD 335 if current precision > target precision - this.MM[ 311] = 0x0000280318; // CC 318 we're done -- jump out to print - this.MM[ 312] = 0x0000640338; // CAD 338 load current upper bound - this.MM[ 313] = 0x0000800337; // FAD 337 add current result - this.MM[ 314] = 0x0000330000; // CR clear R - this.MM[ 315] = 0x0000830334; // FDIV 334 divide by 2.0 to get new upper bound - this.MM[ 316] = 0x0000120338; // ST 338 store new upper bound - this.MM[ 317] = 0x0000200302; // CU 302 do another iteration - this.MM[ 318] = 0x0001640339; // CAD 339 - this.MM[ 319] = 0x0000030510; // PTW 0510 - this.MM[ 320] = 0x0000640337; // CAD 337 - this.MM[ 321] = 0x0000030810; // PTW 0810 - this.MM[ 322] = 0x0000640339; // CAD 339 load argument value - this.MM[ 323] = 0x0000800333; // FAD 333 add 1 to argument value - this.MM[ 324] = 0x0000120339; // ST 339 - this.MM[ 325] = 0x0000200301; // CU 301 start sqrt for next argument value - this.MM[ 326] = 0; - this.MM[ 327] = 0; - this.MM[ 328] = 0; - this.MM[ 329] = 0; - this.MM[ 330] = 0; - this.MM[ 331] = 0; - this.MM[ 332] = 0; - this.MM[ 333] = 0x05110000000; // 1.0 literal: argument increment - this.MM[ 334] = 0x05120000000; // 2.0 literal - this.MM[ 335] = 0x05099999990; // 0.99999990 literal: target precision - this.MM[ 336] = 0; // current precision - this.MM[ 337] = 0; // current sqrt result - this.MM[ 338] = 0; // current upper bound on result - this.MM[ 339] = 0x05120000000; // 2.0 sqrt argument - *************************************************/ + this.MM[ 232] = 0x20000000016; // carriage return + this.MM[ 233] = 0x05110000000; // 1.0 literal: argument increment + this.MM[ 234] = 0x05120000000; // 2.0 literal + this.MM[ 235] = 0x05099999990; // 0.99999990 literal: target precision + this.MM[ 236] = 0; // current precision + this.MM[ 237] = 0; // current sqrt result + this.MM[ 238] = 0; // current upper bound on result + this.MM[ 239] = 0x05120000000; // 2.0 sqrt argument }; \ No newline at end of file diff --git a/webUI/B220.js b/webUI/B220.js index 40fc3bd..7bea62c 100644 --- a/webUI/B220.js +++ b/webUI/B220.js @@ -15,6 +15,7 @@ window.addEventListener("load", function() { var config = new B220SystemConfig();// system configuration object var devices = {}; // hash of I/O devices for the Processor + var diagWindow = null; // handle for the diagnostic monitor panel var processor; // the Processor object var statusMsgTimer = 0; // status message timer control cookie @@ -31,6 +32,10 @@ window.addEventListener("load", function() { } } + if (diagWindow && !diagWindow.closed) { + diagWindow.close(); + } + processor = null; document.getElementById("StartUpBtn").disabled = false; document.getElementById("StartUpBtn").focus(); @@ -90,12 +95,12 @@ window.addEventListener("load", function() { /**************************************/ function openDiagPanel(ev) { - /* Opens the emulator's diagnostic panel in a new sub-window */ - var win; + /* Opens the emulator's diagnostic monitor panel in a new sub-window */ - win = window.open("B220DiagMonitor.html", "DiagPanel", - "location=no,scrollbars=no,resizable,width=300,height=500,top=0,left=0"); - win.global = window; // give it access to our globals. + diagWindow = window.open("B220DiagMonitor.html", "DiagPanel", + "resizable,width=300,height=500,left=0,top=" + screen.availHeight-500); + diagWindow.global = window; // give it access to our globals. + diagWindow.focus(); } /**************************************/ diff --git a/webUI/B220ConsolePrinter.js b/webUI/B220ConsolePrinter.js index 20d0f02..c8ce95f 100644 --- a/webUI/B220ConsolePrinter.js +++ b/webUI/B220ConsolePrinter.js @@ -510,11 +510,13 @@ B220ConsolePrinter.prototype.receiveChar = function receiveChar(char, successor) break; case 0x15: // form-feed + delay *= 4; this.suppressLZ = 0; this.printFormFeed(); break; case 0x16: // carriage-return + delay *= 2; this.suppressLZ = 0; this.emptyLine(); break; @@ -525,7 +527,6 @@ B220ConsolePrinter.prototype.receiveChar = function receiveChar(char, successor) break; case 0x35: // end-of-word - delay = 0; nextReceiver = this.boundReceiveSign; // next will be start of a new word if (this.eowAction) { switch (this.format) { @@ -536,6 +537,7 @@ B220ConsolePrinter.prototype.receiveChar = function receiveChar(char, successor) this.printTab(); break; case 2: // EOW = carriage-return + delay *= 2; this.emptyLine(); break; } diff --git a/webUI/B220ControlConsole.js b/webUI/B220ControlConsole.js index 47c4180..a8a094d 100644 --- a/webUI/B220ControlConsole.js +++ b/webUI/B220ControlConsole.js @@ -730,8 +730,8 @@ B220ControlConsole.prototype.keyboardOpen = function keyboardOpen() { B220ControlConsole.prototype.outputUnitSelect = function outputUnitSelect(unitNr, successor) { /* Prepares for paper-tape or SPO output by selecting the first ready device having a unitMask matching the unitNr parameter. If one is found, returns - that index and calls initiateOutput() for the unit. If no such unit is found, - returns -1 */ + that index and schedules initiateOutput() for the unit. If no such unit is + found, returns -1 */ var result = -1; // be pessimistic var u = null; // output unit object var x; // for loop index @@ -741,8 +741,8 @@ B220ControlConsole.prototype.outputUnitSelect = function outputUnitSelect(unitNr if (u && u.ready) { if (u.unitMask & B220Processor.pow2[unitNr]) { result = x; - u.initiateOutput(successor); - break; // out of for loop + setCallback(this.mnemonic, u, 1, u.initiateOutput, successor); + break; // out of for loop } } } diff --git a/webUI/B220DiagMonitor.html b/webUI/B220DiagMonitor.html new file mode 100644 index 0000000..515ae3a --- /dev/null +++ b/webUI/B220DiagMonitor.html @@ -0,0 +1,130 @@ + + + + +retro-220 Emulator Diagnostic Panel + + + + + + + + + + + +

retro-220
Diagnostic Monitor

+
+ + + + + + + + +
Delta (ms)Category +
Time Stamp +
Exec Clock +
Proc Time +
Delay Delta Avg +
Proc Slack Avg +
Proc Run Avg +
+ + \ No newline at end of file diff --git a/webUI/B220Manifest.appcache b/webUI/B220Manifest.appcache index 7a2886b..43dd083 100644 --- a/webUI/B220Manifest.appcache +++ b/webUI/B220Manifest.appcache @@ -1,5 +1,5 @@ CACHE MANIFEST -# retro-220 emulator 0.00e, 2017-05-09 20:15 +# retro-220 emulator 0.01, 2017-05-13 18:30 CACHE: ../emulator/B220Processor.js B220.css @@ -30,7 +30,7 @@ B220ControlConsole.js #B220DataFile.css #B220DataFile.html #B220DataFile.js -#B220DiagMonitor.html +B220DiagMonitor.html B220FramePaper.html #B220MagTapeControl.css #B220MagTapeControl.html diff --git a/webUI/B220PanelUtil.js b/webUI/B220PanelUtil.js index 5b860ce..68ec7b0 100644 --- a/webUI/B220PanelUtil.js +++ b/webUI/B220PanelUtil.js @@ -487,7 +487,7 @@ function OrganSwitch(parent, x, y, id, offImage, onImage, momentary) { OrganSwitch.topCaptionClass = "OrganSwitchTopCaption"; OrganSwitch.bottomCaptionClass = "OrganSwitchBottomCaption"; -OrganSwitch.momentaryPeriod = 150; // time for momentary switch to bounce back, ms +OrganSwitch.momentaryPeriod = 200; // time for momentary switch to bounce back, ms /**************************************/ OrganSwitch.prototype.addEventListener = function addEventListener(eventName, handler, useCapture) {