mirror of
https://github.com/pkimpel/retro-b5500.git
synced 2026-04-25 20:01:52 +00:00
1. Release emulator version 0.08.
2. Change time-slicing algorithm in Processor and implement Denicola's setImmediate() shim. 3. Find and fix normalization/rounding bugs in Processor.singlePrecisionAdd and .singlePrecisionMultiply. 4. Rework Processor.doublePrecisionAdd, but it's still not right. 5. Move read interval timer details from Processor to CentralControl. 6. Implement more centralized process halt in CentralControl and Processor. 7. CardReader: a. Fix bug when adding decks to input hopper if previous deck did not end with a line delimiter. b. Allow "?" characters as valid if not in column 1. c. "Upgrade" from 800 to 1400 cards/minute. 8. DummyLinePrinter: a. Attempt to implement double-click as a way to clear the output window (but doesn't work yet). b. "Upgrade" from 800 to 1040 lines per minute (B329 model). 9. SPOUnit: a. Tweak paper scrolling behavior. b. Rework handling of ESC/BS/Enter keys; allow Input Request while SPO is busy with output. 10. Set AUTOPRNT option by default in B5500ColdLoader.
This commit is contained in:
@@ -61,7 +61,7 @@ function B5500CentralControl() {
|
||||
/**************************************/
|
||||
/* Global constants */
|
||||
|
||||
B5500CentralControl.version = "0.07";
|
||||
B5500CentralControl.version = "0.08";
|
||||
|
||||
B5500CentralControl.memCycles = 4; // assume 4 µs memory cycle time (the other option was 6 µs)
|
||||
B5500CentralControl.rtcTick = 1000/60; // Real-time clock period, milliseconds
|
||||
@@ -573,6 +573,14 @@ B5500CentralControl.prototype.tock = function tock() {
|
||||
this.timer = setTimeout(this.boundTock, (interval < 1 ? 1 : interval));
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500CentralControl.prototype.readTimer = function readTimer() {
|
||||
/* Returns the value of the 1/60th second timer */
|
||||
var thisTime = new Date().getTime();
|
||||
|
||||
return this.CCI03F*64 + this.TM;
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500CentralControl.prototype.haltP2 = function haltP2() {
|
||||
/* Called by P1 to halt P2. storeForInterrupt() will set P2BF=0 */
|
||||
@@ -724,21 +732,11 @@ B5500CentralControl.prototype.halt = function halt() {
|
||||
}
|
||||
|
||||
if (this.PA && this.PA.busy) {
|
||||
this.PA.busy = 0;
|
||||
this.PA.cycleLimit = 0;
|
||||
if (this.PA.scheduler) {
|
||||
clearTimeout(this.PA.scheduler);
|
||||
this.PA.scheduler = null;
|
||||
}
|
||||
this.PA.halt();
|
||||
}
|
||||
|
||||
if (this.PB && this.PB.busy) {
|
||||
this.PB.busy = 0;
|
||||
this.PB.cycleLimit = 0;
|
||||
if (this.PB.scheduler) {
|
||||
clearTimeout(this.PB.scheduler);
|
||||
this.PB.scheduler = null;
|
||||
}
|
||||
this.PB.halt();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -52,7 +52,9 @@ function B5500Processor(procID, cc) {
|
||||
|
||||
/**************************************/
|
||||
|
||||
B5500Processor.timeSlice = 16000; // Standard run() timeslice in clocks (about 16ms, we hope)
|
||||
B5500Processor.timeSlice = 4000; // this.run() timeslice, clocks
|
||||
B5500Processor.minDelay = 4; // minimum schedule() delay, ms
|
||||
B5500Processor.maxDelay = 20; // maximum schedule() delay, ms
|
||||
|
||||
B5500Processor.collation = [ // index by BIC to get collation value
|
||||
53, 54, 55, 56, 57, 58, 59, 60, // @00: 0 1 2 3 4 5 6 7
|
||||
@@ -108,13 +110,13 @@ B5500Processor.prototype.clear = function clear() {
|
||||
|
||||
this.US14X = 0; // STOP OPERATOR switch
|
||||
|
||||
this.busy = 0; // Processor is running, not idle or halted
|
||||
this.cycleCount = 0; // Current cycle count for this.run()
|
||||
this.cycleLimit = 0; // Cycle limit for this.run()
|
||||
this.totalCycles = 0; // Total cycles executed on this processor
|
||||
this.procStart = 0; // Javascript time that the processor started running, ms
|
||||
this.procTime = 0; // Total processor running time, ms
|
||||
this.procTime = 0.001; // Total processor running time, ms
|
||||
this.procSlack = 0; // Total processor throttling delay, ms
|
||||
this.busy = 0; // Processor is running, not idle or halted
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
@@ -1284,17 +1286,9 @@ B5500Processor.prototype.storeForInterrupt = function storeForInterrupt(forTest)
|
||||
if (this === this.cc.P1) {
|
||||
this.T = 0x89; // inject 0211=ITI into T register
|
||||
} else {
|
||||
this.T = 0; // idle the processor
|
||||
this.TROF = 0;
|
||||
this.PROF = 0;
|
||||
this.busy = 0;
|
||||
this.cycleLimit = 0; // exit this.run()
|
||||
this.stop(); // idle the processor
|
||||
this.cc.HP2F = 1;
|
||||
this.cc.P2BF = 0; // tell P1 we've stopped
|
||||
if (this.scheduler) {
|
||||
clearTimeout(this.scheduler);
|
||||
this.scheduler = null;
|
||||
}
|
||||
}
|
||||
this.CWMF = 0;
|
||||
} else if (forTest) {
|
||||
@@ -1341,12 +1335,33 @@ B5500Processor.prototype.preset = function preset(runAddr) {
|
||||
/**************************************/
|
||||
B5500Processor.prototype.start = function start() {
|
||||
/* Initiates the processor by scheduling it on the Javascript thread */
|
||||
var stamp = new Date().getTime();
|
||||
|
||||
this.busy = 1;
|
||||
this.procStart = new Date().getTime();
|
||||
this.procStart = stamp;
|
||||
this.procTime -= stamp;
|
||||
this.scheduler = setTimeout(this.boundSchedule, 0);
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500Processor.prototype.stop = function stop() {
|
||||
/* Stops running the processor on the Javascript thread */
|
||||
var stamp = new Date().getTime();
|
||||
|
||||
this.T = 0;
|
||||
this.TROF = 0;
|
||||
this.PROF = 0;
|
||||
this.busy = 0;
|
||||
this.cycleLimit = 0; // exit this.run()
|
||||
if (this.scheduler) {
|
||||
clearTimeout(this.scheduler);
|
||||
this.scheduler = null;
|
||||
}
|
||||
while (this.procTime < 0) {
|
||||
this.procTime += stamp;
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500Processor.prototype.initiate = function initiate(forTest) {
|
||||
/* Initiates the processor from interrupt control words stored in the
|
||||
@@ -1540,15 +1555,22 @@ B5500Processor.prototype.singlePrecisionCompare = function singlePrecisionCompar
|
||||
B5500Processor.prototype.singlePrecisionAdd = function singlePrecisionAdd(adding) {
|
||||
/* Adds the contents of the A register to the B register, leaving the result
|
||||
in B and invalidating A. If "adding" is not true, the sign of A is complemented
|
||||
to accomplish subtraction instead of addition */
|
||||
to accomplish subtraction instead of addition.
|
||||
The B5500 did this by complement arithmetic, exchanging operands as necessary,
|
||||
and maintaining a bunch of Q-register flags to keep it all straight. This
|
||||
routine takes a more straightforward approach, doing algebraic arithmetic on
|
||||
the A and B mantissas and maintaining separate extensions (X registers) for
|
||||
scaling A and B. Only one register will be scaled, so the other extension will
|
||||
always be zero */
|
||||
var d = 0; // the guard (rounding) digit
|
||||
var ea; // signed exponent of A
|
||||
var eb; // signed exponent of B
|
||||
var ma; // absolute mantissa of A*8
|
||||
var mb; // absolute mantissa of B*8
|
||||
var ma; // absolute mantissa of A
|
||||
var mb; // absolute mantissa of B
|
||||
var sa; // mantissa sign of A (0=positive)
|
||||
var sb; // mantissa sign of B (ditto)
|
||||
var xx = 0; // local copy of X
|
||||
var xa = 0; // extension to A for scaling (pseudo X)
|
||||
var xb = 0; // extension to B for scaling (pseudo X)
|
||||
|
||||
this.cycleCount += 4; // estimate some general overhead
|
||||
this.adjustABFull();
|
||||
@@ -1564,9 +1586,9 @@ B5500Processor.prototype.singlePrecisionAdd = function singlePrecisionAdd(adding
|
||||
}
|
||||
} else if (mb == 0 && adding) { // otherwise, if B is zero and we're adding,
|
||||
this.B = this.A % 0x800000000000; // result is A with flag bit reset
|
||||
} else { // rats, we actually have to do this
|
||||
} else { // rats, we actually have to do this...
|
||||
ea = (this.A - ma)/0x8000000000;
|
||||
sa = ((ea >>> 7) & 0x01);
|
||||
sa = (adding ? (ea >>> 7) & 0x01 : 1-((ea >>> 7) & 0x01));
|
||||
ea = (ea & 0x40 ? -(ea & 0x3F) : (ea & 0x3F));
|
||||
|
||||
eb = (this.B - mb)/0x8000000000;
|
||||
@@ -1586,13 +1608,13 @@ B5500Processor.prototype.singlePrecisionAdd = function singlePrecisionAdd(adding
|
||||
while (ea != eb) {
|
||||
this.cycleCount++;
|
||||
d = mb % 8;
|
||||
mb = (mb - d)/8; // shift right into X
|
||||
xx = (xx - xx%8)/8 + d*0x1000000000;
|
||||
mb = (mb - d)/8; // shift right into extension
|
||||
xb = (xb - xb%8)/8 + d*0x1000000000;
|
||||
if (mb) {
|
||||
eb++;
|
||||
} else {
|
||||
eb = ea; // if B=0, result will have exponent of A
|
||||
// should we clear X at this point to prevent rounding of A?
|
||||
xb = 0; // prevent rounding of result
|
||||
}
|
||||
}
|
||||
} else if (ea < eb) {
|
||||
@@ -1606,40 +1628,56 @@ B5500Processor.prototype.singlePrecisionAdd = function singlePrecisionAdd(adding
|
||||
while (eb != ea) {
|
||||
this.cycleCount++;
|
||||
d = ma % 8;
|
||||
ma = (ma -d)/8; // shift right into X
|
||||
xx = (xx - xx%8)/8 + d*0x1000000000;
|
||||
ma = (ma - d)/8; // shift right into extension
|
||||
xa = (xa - xa%8)/8 + d*0x1000000000;
|
||||
if (ma) {
|
||||
ea++;
|
||||
} else {
|
||||
ea = eb; // if A=0, kill the scaling loop
|
||||
// should we clear X at this point to prevent rounding of B?
|
||||
xa = 0; // prevent rounding of result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, the exponents are aligned (or one of the mantissas
|
||||
// is zero), so do the actual 39-bit addition
|
||||
mb = (sb ? -mb : mb) + (sa ^ (adding ? 0 : 1) ? -ma : ma);
|
||||
// is zero), so do the actual 39-bit additions of mantissas and extensions
|
||||
|
||||
if (mb == 0) {
|
||||
this.B = 0;
|
||||
} else {
|
||||
// Determine the resulting sign
|
||||
if (mb >= 0) {
|
||||
sb = 0;
|
||||
} else {
|
||||
sb = 1;
|
||||
mb = -mb;
|
||||
xb = (sb ? -xb : xb) + (sa ? -xa : xa); // compute the extension
|
||||
if (xb < 0) {
|
||||
xb += 0x8000000000; // adjust for underflow in the extension
|
||||
mb += (sb ? 1 : -1); // adjust B for borrow into extension
|
||||
} else if (xb > 0x8000000000) {
|
||||
xb -= 0x8000000000; // adjust for overflow in the extension
|
||||
mb += (sb? -1 : 1); // adjust B for carry from extension
|
||||
}
|
||||
|
||||
mb = (sb ? -mb : mb) + (sa ? -ma : ma); // compute the mantissa
|
||||
if (mb == 0) { // if the mantissa is zero...
|
||||
this.B = 0; // the whole result is zero, and we're done
|
||||
} else { // otherwise, determine the resulting sign
|
||||
if (mb > 0) { // if positive...
|
||||
sb = 0; // reset the B sign bit
|
||||
} else { // if negative...
|
||||
sb = 1; // set the B sign bit
|
||||
mb = -mb; // negate the B mantissa
|
||||
if (xb) { // if non-zero octades have been shifted into X (and ONLY if... learned THAT the hard way...)
|
||||
xb = 0x8000000000 - xb; // negate the extension in X
|
||||
mb--; // and adjust for borrow into X
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize and round as necessary
|
||||
if (mb < 0x1000000000 && xx >= 0x800000000) { // Normalization can be required for subtract
|
||||
this.cycleCount++;
|
||||
d = (xx - xx%0x1000000000)/0x1000000000; // get the rounding digit from X
|
||||
xx = (xx%0x1000000000)*8; // shift B and X left together
|
||||
mb = mb*8 + d;
|
||||
eb--;
|
||||
d = (xx - xx%0x1000000000)/0x1000000000; // get the next rounding digit from X
|
||||
if (mb < 0x1000000000) { // Normalization can be required for subtract
|
||||
if (xb < 0x800000000) { // if first two octades in X < @04 then
|
||||
d = 0; // no rounding will take place
|
||||
} else {
|
||||
this.cycleCount++;
|
||||
d = (xb - xb%0x1000000000)/0x1000000000; // get the rounding digit from X
|
||||
xb = (xb%0x1000000000)*8; // shift B and X left together
|
||||
mb = mb*8 + d;
|
||||
eb--;
|
||||
d = (xb - xb%0x1000000000)/0x1000000000; // get the next rounding digit from X
|
||||
}
|
||||
} else if (mb >= 0x8000000000) { // Scaling can be required for add
|
||||
this.cycleCount++;
|
||||
d = mb % 8; // get the rounding digit from B
|
||||
@@ -1665,10 +1703,10 @@ B5500Processor.prototype.singlePrecisionAdd = function singlePrecisionAdd(adding
|
||||
this.cc.signalInterrupt();
|
||||
}
|
||||
} else if (eb < 0) {
|
||||
eb = (-eb) | 0x40; // set the exponent sign bit
|
||||
eb = (-eb) | 0x40; // set the exponent sign bit
|
||||
}
|
||||
|
||||
this.X = xx; // for display purposes only
|
||||
this.X = xb; // for display purposes only
|
||||
this.B = (sb*128 + eb)*0x8000000000 + mb; // Final Answer
|
||||
}
|
||||
}
|
||||
@@ -1685,7 +1723,7 @@ B5500Processor.prototype.singlePrecisionMultiply = function singlePrecisionMulti
|
||||
var ma; // absolute mantissa of A
|
||||
var mb; // absolute mantissa of B
|
||||
var mx = 0; // local copy of X for product extension
|
||||
var n = 0; // local copy of N (octade counter)
|
||||
var n; // local copy of N (octade counter)
|
||||
var sa; // mantissa sign of A (0=positive)
|
||||
var sb; // mantissa sign of B (ditto)
|
||||
var xx; // local copy of X for multiplier
|
||||
@@ -1734,7 +1772,7 @@ B5500Processor.prototype.singlePrecisionMultiply = function singlePrecisionMulti
|
||||
mb = 0; // initialize high-order part of product
|
||||
|
||||
// Now we step through the 13 octades of the multiplier, developing the product
|
||||
do {
|
||||
for (n=0; n<13; n++) {
|
||||
d = xx % 8; // extract the current multiplier digit
|
||||
xx = (xx - d)/8; // shift the multiplier right one octade
|
||||
|
||||
@@ -1748,7 +1786,7 @@ B5500Processor.prototype.singlePrecisionMultiply = function singlePrecisionMulti
|
||||
d = mb % 8; // get the low-order octade of partial product in B
|
||||
mb = (mb - d)/8; // shift B right one octade
|
||||
mx = mx/8 + d*0x1000000000; // shift B octade into high-order end of extension
|
||||
} while (++n < 13);
|
||||
} // for (n)
|
||||
|
||||
// Normalize the result
|
||||
if (this.Q & 0x10 && mb == 0) { // if it's integer multiply (Q05F) with integer result
|
||||
@@ -1770,7 +1808,7 @@ B5500Processor.prototype.singlePrecisionMultiply = function singlePrecisionMulti
|
||||
this.Q &= ~(0x10); // reset Q05F
|
||||
this.A = 0; // required by specs due to the way rounding addition worked
|
||||
|
||||
if (xx >= 0x4000000000) { // if high-order bit of remaining extension is 1
|
||||
if (mx >= 0x4000000000) { // if high-order bit of remaining extension is 1
|
||||
this.Q |= 0x01; // set Q01F (for display purposes only)
|
||||
if (mb < 0x7FFFFFFFFF) { // if the rounding would not cause overflow
|
||||
this.cycleCount++;
|
||||
@@ -1788,14 +1826,16 @@ B5500Processor.prototype.singlePrecisionMultiply = function singlePrecisionMulti
|
||||
this.I = (this.I & 0x0F) | 0xB0; // set I05/6/8: exponent-overflow
|
||||
this.cc.signalInterrupt();
|
||||
}
|
||||
} else if (eb < -63) {
|
||||
eb = ((-eb) % 64) | 0x40; // mod the exponent and set its sign
|
||||
if (this.NCSF) {
|
||||
this.I = (this.I & 0x0F) | 0xA0; // set I06/8: exponent-underflow
|
||||
this.cc.signalInterrupt();
|
||||
}
|
||||
} else if (eb < 0) {
|
||||
eb = (-eb) | 0x40; // set the exponent sign bit
|
||||
if (eb >= -63) {
|
||||
eb = (-eb) | 0x40; // set the exponent sign bit
|
||||
} else {
|
||||
eb = ((-eb) % 64) | 0x40; // mod the exponent and set its sign
|
||||
if (this.NCSF) {
|
||||
this.I = (this.I & 0x0F) | 0xA0;// set I06/8: exponent-underflow
|
||||
this.cc.signalInterrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.B = (sb*128 + eb)*0x8000000000 + mb; // Final Answer
|
||||
@@ -1894,14 +1934,16 @@ B5500Processor.prototype.singlePrecisionDivide = function singlePrecisionDivide(
|
||||
this.I = (this.I & 0x0F) | 0xB0; // set I05/6/8: exponent-overflow
|
||||
this.cc.signalInterrupt();
|
||||
}
|
||||
} else if (eb < -63) {
|
||||
eb = ((-eb) % 64) | 0x40; // mod the exponent and set its sign
|
||||
if (this.NCSF) {
|
||||
this.I = (this.I & 0x0F) | 0xA0; // set I06/8: exponent-underflow
|
||||
this.cc.signalInterrupt();
|
||||
}
|
||||
} else if (eb < 0) {
|
||||
eb = (-eb) | 0x40; // set the exponent sign bit
|
||||
if (eb >= -63) {
|
||||
eb = (-eb) | 0x40; // set the exponent sign bit
|
||||
} else {
|
||||
eb = ((-eb) % 64) | 0x40; // mod the exponent and set its sign
|
||||
if (this.NCSF) {
|
||||
this.I = (this.I & 0x0F) | 0xA0;// set I06/8: exponent-underflow
|
||||
this.cc.signalInterrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.B = (sb*128 + eb)*0x8000000000 + xx; // Final Answer
|
||||
@@ -2123,12 +2165,11 @@ B5500Processor.prototype.doublePrecisionAdd = function doublePrecisionAdd(adding
|
||||
precision contents of the top two words in the memory stack, leaving the result
|
||||
in A and B. If "adding" is not true, the sign of A is complemented to accomplish
|
||||
subtraction instead of addition */
|
||||
var carry = 0; // overflow carry flag
|
||||
var d = 0; // shifting digit between registers
|
||||
var d; // shifting digit between registers
|
||||
var ea; // signed exponent of A
|
||||
var eb; // signed exponent of B
|
||||
var ma; // absolute mantissa of A*8
|
||||
var mb; // absolute mantissa of B*8
|
||||
var ma; // absolute mantissa of A
|
||||
var mb; // absolute mantissa of B
|
||||
var sa; // mantissa sign of A (0=positive)
|
||||
var sb; // mantissa sign of B (ditto)
|
||||
var xa; // extended mantissa for A
|
||||
@@ -2141,7 +2182,7 @@ B5500Processor.prototype.doublePrecisionAdd = function doublePrecisionAdd(adding
|
||||
ma = this.A % 0x8000000000; // extract the A mantissa
|
||||
xa = this.B % 0x8000000000; // extract the A mantissa extension
|
||||
ea = (this.A - ma)/0x8000000000;
|
||||
sa = (ea >>> 7) & 0x01;
|
||||
sa = (adding ? (ea >>> 7) & 0x01 : 1-((ea >>> 7) & 0x01));
|
||||
ea = (ea & 0x40 ? -(ea & 0x3F) : (ea & 0x3F));
|
||||
|
||||
this.AROF = this.BROF = 0; // empty the TOS registers
|
||||
@@ -2210,78 +2251,49 @@ B5500Processor.prototype.doublePrecisionAdd = function doublePrecisionAdd(adding
|
||||
}
|
||||
|
||||
// At this point, the exponents are aligned (or one of the mantissas
|
||||
// is zero), so do the actual 78-bit addition as two 40-bit, signed, twos-
|
||||
// complement halves. Note that computing the twos-complement of the
|
||||
// extension requires a borrow from the high-order part, so the borrow
|
||||
// is taken from the 40-bit twos-complement base (i.e., using 0xFFFFFFFFFF
|
||||
// instead of 0x10000000000).
|
||||
if (sb) { // if B negative, compute B 2s complement
|
||||
this.cycleCount += 2;
|
||||
xb = 0x8000000000 - xb;
|
||||
mb = 0xFFFFFFFFFF - mb;
|
||||
}
|
||||
if (sa ^ (adding ? 0 : 1)) { // if A negative XOR subtracting, compute A 2s complement
|
||||
this.cycleCount += 2;
|
||||
xa = 0x8000000000 - xa;
|
||||
ma = 0xFFFFFFFFFF - ma;
|
||||
// is zero), so do the actual 78-bit addition.
|
||||
|
||||
xb = (sb ? -xb : xb) + (sa ? -xa : xa); // compute the extension
|
||||
if (xb < 0) {
|
||||
xb += 0x8000000000; // adjust for underflow in the extension
|
||||
mb += (sb ? 1 : -1); // adjust B for borrow into extension
|
||||
} else if (xb > 0x8000000000) {
|
||||
xb -= 0x8000000000; // adjust for overflow in the extension
|
||||
mb += (sb? -1 : 1); // adjust B for carry from extension
|
||||
}
|
||||
|
||||
xb += xa; // add the extension parts
|
||||
if (xb >= 0x8000000000) { // deal with carry out of extension part
|
||||
mb++; // into high-order part
|
||||
xb %= 0x8000000000;
|
||||
}
|
||||
|
||||
mb += ma; // add the high-order parts
|
||||
|
||||
// Check for overflow: if the result occupies more than 40 bits, we know
|
||||
// that overflow occurred; otherwise if both internal signs were positive
|
||||
// and we have a twos-complement negative result, overflow occurred; otherwise
|
||||
// if both internal signs were negative and we have a positive result,
|
||||
// overflow occurred. Set the carry flag and adjust the result as necessary.
|
||||
if (mb >= 0x10000000000) { // if result overflowed 40 bits
|
||||
carry = 1; // set the carry flag
|
||||
mb -= 0x8000000000; // and adjust result for the overflow
|
||||
} else if (sb == (sa ^ (adding ? 0 : 1))) { // if the signs of the internal addition are the same
|
||||
if (sb && mb < 0x8000000000) { // if signs were negative and result is positive
|
||||
carry = 1; // overflow occurred: set carry flag
|
||||
mb += 0x8000000000; // and adjust result for the overflow
|
||||
} else if (!sb && mb >= 0x8000000000) { // if signs were positive and result is negative
|
||||
carry = 1; // overflow occurred: set the carry flag
|
||||
mb -= 0x8000000000; // and adjust result for the overflow
|
||||
mb = (sb ? -mb : mb) + (sa ? -ma : ma); // compute the mantissa
|
||||
if (mb > 0) { // if positive...
|
||||
sb = 0; // reset the B sign bit
|
||||
} else { // if negative...
|
||||
sb = 1; // set the B sign bit
|
||||
mb = -mb; // negate the B mantissa
|
||||
if (xb) { // if the extension is non-zero
|
||||
xb = 0x8000000000 - xb; // negate the extension
|
||||
mb--; // and adjust for borrow into the extension
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the resulting sign and decomplement as necessary
|
||||
if (mb < 0x8000000000) {
|
||||
sb = 0; // it's positive
|
||||
} else {
|
||||
sb = 1; // it's negative
|
||||
this.cycleCount++;
|
||||
xb = 0x8000000000 - xb;
|
||||
mb = 0xFFFFFFFFFF - mb;
|
||||
}
|
||||
|
||||
// Scale or normalize as necessary
|
||||
if (carry) { // overflow occurred, so scale it in
|
||||
if (mb >= 0x8000000000) { // If overflowed 39 bits, scale result
|
||||
this.cycleCount++;
|
||||
d = mb % 8; // get the shift digit from high-order part
|
||||
mb = (mb - d)/8 + 0x1000000000; // shift right and insert the overflow bit
|
||||
xb = (xb - xb%8)/8 + d*0x1000000000; // shift the extension and insert the shift digit
|
||||
d = mb % 8; // get the rounding digit from B
|
||||
mb = (mb - d)/8; // shift mantissa right due to overflow
|
||||
xb = (xb - xb%8)/8 + d*0x1000000000; // shift extension right and insert mantissa digit
|
||||
eb++;
|
||||
} else {
|
||||
while (mb < 0x1000000000 && mb & xb) { // Normalize
|
||||
} else { // Otherwise, normalize as necessary
|
||||
while (mb < 0x1000000000 && mb && xb) {
|
||||
this.cycleCount++;
|
||||
d = (xb - xb%0x1000000000)/0x1000000000;// get the rounding digit from X
|
||||
xb = (xb%0x1000000000)*8; // shift B and X left together
|
||||
d = (xb - xb%0x1000000000)/0x1000000000; // get the high-order digit from the extension
|
||||
xb = (xb%0x1000000000)*8; // shift B and X left together
|
||||
mb = mb*8 + d;
|
||||
eb--;
|
||||
}
|
||||
}
|
||||
|
||||
if (mb == 0 && xb == 0) {
|
||||
this.A = this.B = 0;
|
||||
} else {
|
||||
if (mb == 0 && xb == 0) { // if the mantissa is zero...
|
||||
this.A = this.B = 0; // the whole result is zero, and we're done
|
||||
} else { // otherwise, determine the resulting sign
|
||||
// Check for exponent over/underflow
|
||||
if (eb > 63) {
|
||||
eb %= 64;
|
||||
@@ -2289,14 +2301,16 @@ B5500Processor.prototype.doublePrecisionAdd = function doublePrecisionAdd(adding
|
||||
this.I = (this.I & 0x0F) | 0xB0; // set I05/6/8: exponent-overflow
|
||||
this.cc.signalInterrupt();
|
||||
}
|
||||
} else if (eb < -63) {
|
||||
eb = ((-eb) % 64) | 0x40; // mod the exponent and set its sign
|
||||
if (this.NCSF) {
|
||||
this.I = (this.I & 0x0F) | 0xA0; // set I06/8: exponent-underflow
|
||||
this.cc.signalInterrupt();
|
||||
}
|
||||
} else if (eb < 0) {
|
||||
eb = (-eb) | 0x40; // set the exponent sign bit
|
||||
if (eb >= -63) {
|
||||
eb = (-eb) | 0x40; // set the exponent sign bit
|
||||
} else {
|
||||
eb = ((-eb) % 64) | 0x40; // mod the exponent and set its sign
|
||||
if (this.NCSF) {
|
||||
this.I = (this.I & 0x0F) | 0xA0;// set I06/8: exponent-underflow
|
||||
this.cc.signalInterrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.X = xb; // for display purposes only
|
||||
@@ -3672,7 +3686,7 @@ B5500Processor.prototype.run = function run() {
|
||||
case 0x04: // 0411: RTR=Read Timer
|
||||
if (!this.NCSF) { // control-state only
|
||||
this.adjustAEmpty();
|
||||
this.A = this.cc.CCI03F*64 + this.cc.TM;
|
||||
this.A = this.cc.readTimer();
|
||||
this.AROF = 1;
|
||||
}
|
||||
break;
|
||||
@@ -4561,10 +4575,9 @@ B5500Processor.prototype.schedule = function schedule() {
|
||||
this routine will reschedule itself after an appropriate delay, thereby
|
||||
throttling the performance and allowing other modules a chance at the
|
||||
Javascript execution thread */
|
||||
var clockOff; // ending time for this run() call, ms
|
||||
var delayTime; // delay until next run() for this processor, ms
|
||||
var elapsedTime; // elapsed real time for this run() call, ms
|
||||
var startTime = new Date().getTime(); // starting time for this run() call, ms
|
||||
var stopTime; // ending time for this run() call, ms
|
||||
var runTime = this.procTime; // real-world processor running time, ms
|
||||
|
||||
this.scheduler = null;
|
||||
this.cycleLimit = B5500Processor.timeSlice;
|
||||
@@ -4572,23 +4585,34 @@ B5500Processor.prototype.schedule = function schedule() {
|
||||
|
||||
this.run(); // execute syllables for the timeslice
|
||||
|
||||
stopTime = new Date().getTime();
|
||||
elapsedTime = stopTime - startTime;
|
||||
this.procTime += elapsedTime;
|
||||
clockOff = new Date().getTime();
|
||||
while (runTime < 0) {
|
||||
runTime += clockOff;
|
||||
}
|
||||
this.totalCycles += this.cycleCount;
|
||||
if (this.busy) {
|
||||
delayTime = this.cycleCount/1000 - elapsedTime;
|
||||
this.procSlack += delayTime;
|
||||
this.scheduler = setTimeout(this.boundSchedule, (delayTime > 0 ? delayTime : 1));
|
||||
// delayTime is the number of milliseconds the processor is running ahead of
|
||||
// real-world time. Web browsers have a certain minimum delay. If the delay
|
||||
// is less than that, we yield to the event loop, but otherwise continue (real
|
||||
// time should eventually catch up -- we hope)
|
||||
delayTime = this.totalCycles/1000 - runTime;
|
||||
if (delayTime < B5500Processor.minDelay) {
|
||||
setImmediate(this.boundSchedule); // just execute pending events
|
||||
} else {
|
||||
if (delayTime > B5500Processor.maxDelay) {
|
||||
delayTime = B5500Processor.maxDelay;
|
||||
}
|
||||
this.procSlack += delayTime;
|
||||
this.scheduler = setTimeout(this.boundSchedule, delayTime);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500Processor.prototype.step = function step() {
|
||||
/* Single-steps the processor. Normally this will cause one instruction to
|
||||
be executed, but note that in case of an interrupt, one or two injected
|
||||
instructions (e.g., SFI followed by ITI or char-mode CRF followed by lots of
|
||||
things) could also be executed */
|
||||
be executed, but note that in the case of an interrupt or char-mode CRF, one
|
||||
or two injected instructions (e.g., SFI followed by ITI) could also be executed */
|
||||
|
||||
this.cycleLimit = 1;
|
||||
this.cycleCount = 0;
|
||||
|
||||
@@ -186,7 +186,7 @@ B5500CardPunch.prototype.punchOnload = function punchOnload() {
|
||||
|
||||
this.stacker1Frame = this.$$("CPStacker1Frame");
|
||||
this.stacker1Frame.contentDocument.head.innerHTML += "<style>" +
|
||||
"BODY {background-color: #F0DCB0; margin: 2px} " +
|
||||
"BODY {background-color: #F7E7CE; margin: 2px} " +
|
||||
"PRE {margin: 0; font-size: 9pt; font-family: Lucida Sans Typewriter, Courier New, Courier, monospace}" +
|
||||
"</style>";
|
||||
this.stacker1 = this.doc.createElement("pre");
|
||||
@@ -196,7 +196,7 @@ B5500CardPunch.prototype.punchOnload = function punchOnload() {
|
||||
|
||||
this.stacker2Frame = this.$$("CPStacker2Frame");
|
||||
this.stacker2Frame.contentDocument.head.innerHTML += "<style>" +
|
||||
"BODY {background-color: #F0DCB0; margin: 2px} " +
|
||||
"BODY {background-color: #F7E7CE; margin: 2px} " +
|
||||
"PRE {margin: 0; font-size: 9pt; font-family: Lucida Sans Typewriter, Courier New, Courier, monospace}" +
|
||||
"</style>";
|
||||
this.stacker2 = this.doc.createElement("pre");
|
||||
|
||||
@@ -46,7 +46,7 @@ function B5500CardReader(mnemonic, unitIndex, designate, statusChange, signal) {
|
||||
|
||||
B5500CardReader.prototype.eolRex = /([^\n\r\f]*)((:?\r[\n\f]?)|\n|\f)?/g;
|
||||
|
||||
B5500CardReader.prototype.cardsPerMinute = 800;
|
||||
B5500CardReader.prototype.cardsPerMinute = 1400; // B129 card reader
|
||||
|
||||
B5500CardReader.prototype.cardFilter = [ // Filter ASCII character values to valid BIC ones
|
||||
0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, // 00-0F
|
||||
@@ -193,10 +193,19 @@ B5500CardReader.prototype.fileSelector_onChange = function fileSelector_onChange
|
||||
function fileLoader_onLoad(ev) {
|
||||
/* Handle the onload event for a Text FileReader */
|
||||
|
||||
if (that.bufIndex < that.bufLength) {
|
||||
that.buffer = that.buffer.substring(that.bufIndex) + ev.target.result;
|
||||
} else {
|
||||
if (that.bufIndex >= that.bufLength) {
|
||||
that.buffer = ev.target.result;
|
||||
} else {
|
||||
switch (that.buffer.charAt(that.buffer.length-1)) {
|
||||
case "\r":
|
||||
case "\n":
|
||||
case "\f":
|
||||
break; // do nothing -- the last card has a delimiter
|
||||
default:
|
||||
that.buffer += "\n"; // so the next deck starts on a new line
|
||||
break;
|
||||
}
|
||||
that.buffer = that.buffer.substring(that.bufIndex) + ev.target.result;
|
||||
}
|
||||
|
||||
that.bufIndex = 0;
|
||||
@@ -215,6 +224,7 @@ B5500CardReader.prototype.readCardAlpha = function readCardAlpha(buffer, length)
|
||||
image as necessary to the I/O buffer length. Invalid BCL characters are
|
||||
translated to ASCII "?" and the invalid character bit is set in the errorMask.
|
||||
Returns the raw card image as a string */
|
||||
var c; // current character
|
||||
var card; // card image
|
||||
var cardLength; // length of card image
|
||||
var match; // result of eolRex.exec()
|
||||
@@ -233,7 +243,12 @@ B5500CardReader.prototype.readCardAlpha = function readCardAlpha(buffer, length)
|
||||
cardLength = length;
|
||||
}
|
||||
for (x=0; x<cardLength; x++) {
|
||||
if ((buffer[x] = this.cardFilter[card.charCodeAt(x) & 0x7F]) == 0x3F) { // intentional assignment
|
||||
c = card.charCodeAt(x);
|
||||
if (c == 0x3F && x > 0) { // an actual "?"
|
||||
buffer[x] = 0x3F;
|
||||
} else if (c > 0x7F) { // Unicode R Us -- NOT!
|
||||
this.errorMask |= 0x08;
|
||||
} else if ((buffer[x] = this.cardFilter[c]) == 0x3F) { // intentional assignment
|
||||
this.errorMask |= 0x08;
|
||||
}
|
||||
}
|
||||
@@ -296,7 +311,7 @@ B5500CardReader.prototype.readerOnload = function readerOnload() {
|
||||
|
||||
this.outHopperFrame = this.$$("CROutHopperFrame");
|
||||
this.outHopperFrame.contentDocument.head.innerHTML += "<style>" +
|
||||
"BODY {background-color: #F0DCB0; margin: 2px} " +
|
||||
"BODY {background-color: #F7E7CE; margin: 2px} " +
|
||||
"PRE {margin: 0; font-size: 9pt; font-family: Lucida Sans Typewriter, Courier New, Courier, monospace}" +
|
||||
"</style>";
|
||||
this.outHopper = this.doc.createElement("pre");
|
||||
|
||||
@@ -1742,7 +1742,7 @@ window.onload = function() {
|
||||
// 41: initialize date @ H/L
|
||||
// 40: initialize time @ H/L
|
||||
// 39: use only one breakout tape
|
||||
// 38: automatically print pbt
|
||||
pow2[47-36] + // 38: automatically print pbt
|
||||
pow2[47-37] + // 37: clear write ready status @ terminal
|
||||
pow2[47-36] + // 36: write disc. code on terminal
|
||||
pow2[47-35] + // 35: type when compiler files open & close
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
<meta http-equiv="Content-Style-Type" content="text/css">
|
||||
<link id=defaultStyleSheet rel=stylesheet type="text/css" href="B5500Console.css">
|
||||
|
||||
<script src="./setImmediate.js"></script>
|
||||
|
||||
<script src="./B5500DummyUnit.js"></script>
|
||||
<script src="./B5500SPOUnit.js"></script>
|
||||
<script src="./B5500DiskUnit.js"></script>
|
||||
@@ -175,8 +177,11 @@ window.addEventListener("load", function() {
|
||||
aControl.className = "yellowButton yellowLit";
|
||||
}
|
||||
}
|
||||
et = new Date().getTime() - pa.procStart;
|
||||
procRate.innerHTML = (pa.procTime/et*100).toFixed(1) + "%";
|
||||
et = pa.procTime;
|
||||
while (et < 0) {
|
||||
et += new Date().getTime();
|
||||
}
|
||||
procRate.innerHTML = (pa.totalCycles/1000/et*100).toFixed(1) + "%";
|
||||
procSlack.innerHTML = (pa.procSlack/et*100).toFixed(1) + "%";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ function B5500DummyPrinter(mnemonic, unitIndex, designate, statusChange, signal)
|
||||
that.printerOnload();
|
||||
}, false);
|
||||
}
|
||||
B5500DummyPrinter.prototype.linesPerMinute = 800; // Printer speed
|
||||
B5500DummyPrinter.prototype.linesPerMinute = 1040; // B329 line printer
|
||||
B5500DummyPrinter.maxScrollLines = 150000; // Maximum printer scrollback (about a box of paper)
|
||||
|
||||
|
||||
@@ -60,10 +60,36 @@ B5500DummyPrinter.prototype.clear = function clear() {
|
||||
this.busy = false; // busy status
|
||||
this.activeIOUnit = 0; // I/O unit currently using this device
|
||||
|
||||
this.lastClickStamp = 0; // last click time for double-click detection
|
||||
|
||||
this.errorMask = 0; // error mask for finish()
|
||||
this.finish = null; // external function to call for I/O completion
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500SPOUnit.prototype.tearPaper = function tearPaper(ev) {
|
||||
/* Handles an event to clear the "paper" from the printer */
|
||||
|
||||
if (confirm("Do you want to clear the \"paper\" from the printer?")) {
|
||||
while (this.paper.firstChild) {
|
||||
this.paper.removeChild(this.paper.firstChild);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500DummyPrinter.prototype.appendLine = function appendLine(text) {
|
||||
/* Removes excess lines already printed, then appends a new <pre> element
|
||||
to the <iframe>, creating an empty text node inside the new element */
|
||||
var count = this.paper.childNodes.length;
|
||||
var line = this.doc.createTextNode(text || "");
|
||||
|
||||
while (count-- > this.maxScrollLines) {
|
||||
this.paper.removeChild(this.paper.firstChild);
|
||||
}
|
||||
this.paper.appendChild(line);
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500DummyPrinter.prototype.printerOnload = function printerOnload() {
|
||||
/* Initializes the line printer window and user interface */
|
||||
@@ -79,22 +105,21 @@ B5500DummyPrinter.prototype.printerOnload = function printerOnload() {
|
||||
this.window.moveTo(40, 40);
|
||||
this.window.resizeTo(1000, screen.availHeight*0.80);
|
||||
|
||||
this.window.addEventListener("click", function(ev) {
|
||||
var stamp = new Date().getTime();
|
||||
|
||||
// a kludge, but this is the DUMMY printer....
|
||||
if (stamp - this.lastClickStamp < 500) {
|
||||
that.ripPaper(ev);
|
||||
that.lastClickStamp = 0; // no triple-clicking allowed
|
||||
} else {
|
||||
that.lastClickStamp = stamp;
|
||||
}
|
||||
}, false);
|
||||
|
||||
this.statusChange(1);
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500DummyPrinter.prototype.appendLine = function appendLine(text) {
|
||||
/* Removes excess lines already printed, then appends a new <pre> element
|
||||
to the <iframe>, creating an empty text node inside the new element */
|
||||
var count = this.paper.childNodes.length;
|
||||
var line = this.doc.createTextNode(text || "");
|
||||
|
||||
while (count-- > this.maxScrollLines) {
|
||||
this.paper.removeChild(this.paper.firstChild);
|
||||
}
|
||||
this.paper.appendChild(line);
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500DummyPrinter.prototype.read = function read(finish, buffer, length, mode, control) {
|
||||
/* Initiates a read operation on the unit */
|
||||
|
||||
@@ -24,7 +24,7 @@ PRE {
|
||||
DIV#SPODiv {
|
||||
position: relative;
|
||||
width: 800px;
|
||||
background-color: #F0D0A0;
|
||||
background-color: #F7E7CE;
|
||||
border-radius: 32px;
|
||||
padding: 2em;
|
||||
text-align: left}
|
||||
|
||||
@@ -187,6 +187,7 @@ B5500SPOUnit.prototype.appendEmptyLine = function appendEmptyLine() {
|
||||
while (count-- > this.maxScrollLines) {
|
||||
this.paper.removeChild(this.paper.firstChild);
|
||||
}
|
||||
this.endOfPaper.scrollIntoView();
|
||||
this.paper.appendChild(line);
|
||||
};
|
||||
|
||||
@@ -216,7 +217,6 @@ B5500SPOUnit.prototype.printChar = function printChar(c) {
|
||||
|
||||
if (len < 1) {
|
||||
line.nodeValue = String.fromCharCode(c);
|
||||
that.endOfPaper.scrollIntoView();
|
||||
} else if (len < 72) {
|
||||
line.nodeValue += String.fromCharCode(c);
|
||||
} else {
|
||||
@@ -247,7 +247,7 @@ B5500SPOUnit.prototype.outputChar = function outputChar() {
|
||||
that.printCol = 72;
|
||||
setTimeout(that.outputChar, delay);
|
||||
}
|
||||
} else if (that.printCol == 72) { // delay to fake the output of a new-line
|
||||
} else if (that.printCol == 72) { // delay to fake the output of a carriage-return
|
||||
that.printCol++;
|
||||
setTimeout(that.outputChar, delay+that.charPeriod);
|
||||
} else { // actually output the CR/LF
|
||||
@@ -355,44 +355,49 @@ B5500SPOUnit.prototype.keyDown = function keyDown(ev) {
|
||||
} else {
|
||||
nextTime = stamp + this.charPeriod;
|
||||
}
|
||||
this.nextCharTime = nextTime;
|
||||
|
||||
if (this.spoState == this.spoRemote) {
|
||||
if (c == 27) {
|
||||
if (that.spoState == that.spoRemote) {
|
||||
that.signal();
|
||||
} else if (that.spoState == that.spoOutput) {
|
||||
that.signal();
|
||||
}
|
||||
switch (c) {
|
||||
case 27: // ESC
|
||||
switch (this.spoState) {
|
||||
case this.spoRemote:
|
||||
case this.spoOutput:
|
||||
this.signal();
|
||||
result = false;
|
||||
}
|
||||
} else if (this.spoState == this.spoInput) {
|
||||
switch (c) {
|
||||
case 27: // ESC
|
||||
break;
|
||||
case this.spoInput:
|
||||
this.cancelInput();
|
||||
result = false;
|
||||
break;
|
||||
case 8: // Backspace
|
||||
}
|
||||
break;
|
||||
case 8: // Backspace
|
||||
switch (this.spoState) {
|
||||
case this.spoInput:
|
||||
case this.spoLocal:
|
||||
setTimeout(this.backspaceChar, nextTime-stamp);
|
||||
this.nextCharTime = nextTime;
|
||||
result = false;
|
||||
break;
|
||||
case 13: // Enter
|
||||
}
|
||||
break;
|
||||
case 13: // Enter
|
||||
switch (this.spoState) {
|
||||
case this.spoInput:
|
||||
this.terminateInput();
|
||||
this.nextCharTime = nextTime;
|
||||
result = false;
|
||||
break
|
||||
case this.spoLocal:
|
||||
setTimeout(function() {
|
||||
that.appendEmptyLine();
|
||||
}, nextTime-stamp+this.charPeriod);
|
||||
this.nextCharTime = nextTime;
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
} else if (this.spoState == this.spoLocal) {
|
||||
switch (c) {
|
||||
case 8: // Backspace
|
||||
setTimeout(that.backspaceChar, nextTime-stamp);
|
||||
result = false;
|
||||
break;
|
||||
case 13: // Enter
|
||||
setTimeout(function() {that.appendEmptyLine()}, nextTime-stamp+this.charPeriod);
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
@@ -432,7 +437,7 @@ B5500SPOUnit.prototype.spoOnload = function spoOnload() {
|
||||
this.paper.appendChild(this.doc.createTextNode(""));
|
||||
this.$$("SPOUT").contentDocument.body.appendChild(this.paper);
|
||||
this.endOfPaper = this.doc.createElement("div");
|
||||
//this.endOfPaper.appendChild(this.doc.createTextNode("\xA0"));
|
||||
this.endOfPaper.appendChild(this.doc.createTextNode("\xA0"));
|
||||
this.$$("SPOUT").contentDocument.body.appendChild(this.endOfPaper);
|
||||
this.$$("SPOUT").contentDocument.head.innerHTML += "<style>" +
|
||||
"BODY {background-color: #FFE} " +
|
||||
@@ -444,43 +449,43 @@ B5500SPOUnit.prototype.spoOnload = function spoOnload() {
|
||||
this.window.moveTo(0, screen.availHeight-this.window.outerHeight);
|
||||
this.window.focus();
|
||||
|
||||
this.$$("SPORemoteBtn").onclick = function() {
|
||||
this.$$("SPORemoteBtn").addEventListener("click", function() {
|
||||
that.setRemote();
|
||||
};
|
||||
}, false);
|
||||
|
||||
this.$$("SPOPowerBtn").onclick = function() {
|
||||
this.$$("SPOPowerBtn").addEventListener("click", function() {
|
||||
that.window.alert("Don't DO THAT");
|
||||
};
|
||||
}, false);
|
||||
|
||||
this.$$("SPOLocalBtn").onclick = function() {
|
||||
this.$$("SPOLocalBtn").addEventListener("click", function() {
|
||||
that.setLocal();
|
||||
};
|
||||
}, false);
|
||||
|
||||
this.$$("SPOInputRequestBtn").onclick = function() {
|
||||
this.$$("SPOInputRequestBtn").addEventListener("click", function() {
|
||||
if (that.spoState == that.spoRemote) {
|
||||
that.signal();
|
||||
} else if (that.spoState == that.spoOutput) {
|
||||
that.signal();
|
||||
}
|
||||
};
|
||||
}, false);
|
||||
|
||||
this.$$("SPOErrorBtn").onclick = function() {
|
||||
this.$$("SPOErrorBtn").addEventListener("click", function() {
|
||||
that.cancelInput();
|
||||
};
|
||||
}, false);
|
||||
|
||||
this.$$("SPOEndOfMessageBtn").onclick = function() {
|
||||
this.$$("SPOEndOfMessageBtn").addEventListener("click", function() {
|
||||
that.terminateInput();
|
||||
};
|
||||
}, false);
|
||||
|
||||
this.window.onkeypress = function(ev) {
|
||||
this.window.addEventListener("keypress", function(ev) {
|
||||
if (ev.keyCode == 191) {ev.preventDefault()};
|
||||
that.keyPress(ev);
|
||||
};
|
||||
}, false);
|
||||
|
||||
this.window.onkeydown = function(ev) {
|
||||
this.window.addEventListener("keydown", function(ev) {
|
||||
if (ev.keyCode == 191) {ev.preventDefault()};
|
||||
that.keyDown(ev);
|
||||
};
|
||||
}, false);
|
||||
|
||||
for (x=0; x<32; x++) {
|
||||
this.appendEmptyLine();
|
||||
|
||||
@@ -7,17 +7,19 @@
|
||||
<meta http-equiv="Content-Style-Type" content="text/css">
|
||||
<link id=defaultStyleSheet rel=stylesheet type="text/css" href="B5500SyllableDebugger.css">
|
||||
|
||||
<script src="/B5500/webUI/B5500DummyUnit.js"></script>
|
||||
<script src="/B5500/webUI/B5500SPOUnit.js"></script>
|
||||
<script src="/B5500/webUI/B5500DiskUnit.js"></script>
|
||||
<script src="/B5500/webUI/B5500CardReader.js"></script>
|
||||
<script src="/B5500/webUI/B5500CardPunch.js"></script>
|
||||
<script src="/B5500/webUI/B5500DummyPrinter.js"></script>
|
||||
<script src="./setImmediate.js"></script>
|
||||
|
||||
<script src="/B5500/emulator/B5500SystemConfiguration.js"></script>
|
||||
<script src="/B5500/emulator/B5500CentralControl.js"></script>
|
||||
<script src="/B5500/emulator/B5500Processor.js"></script>
|
||||
<script src="/B5500/emulator/B5500IOUnit.js"></script>
|
||||
<script src="./B5500DummyUnit.js"></script>
|
||||
<script src="./B5500SPOUnit.js"></script>
|
||||
<script src="./B5500DiskUnit.js"></script>
|
||||
<script src="./B5500CardReader.js"></script>
|
||||
<script src="./B5500CardPunch.js"></script>
|
||||
<script src="./B5500DummyPrinter.js"></script>
|
||||
|
||||
<script src="../emulator/B5500SystemConfiguration.js"></script>
|
||||
<script src="../emulator/B5500CentralControl.js"></script>
|
||||
<script src="../emulator/B5500Processor.js"></script>
|
||||
<script src="../emulator/B5500IOUnit.js"></script>
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
@@ -880,6 +882,7 @@ function hardwareLoad_onClick(ev) {
|
||||
cc.P1.clear();
|
||||
cc.cardLoadSelect = $$("CardLoadSelect").checked;
|
||||
cc.load(true);
|
||||
displaySystemState();
|
||||
}
|
||||
|
||||
function checkBrowser() {
|
||||
|
||||
244
webUI/setImmediate.js
Normal file
244
webUI/setImmediate.js
Normal file
@@ -0,0 +1,244 @@
|
||||
// Domenic Denicola's implementation of the proposed setImmediate() API.
|
||||
// Downloaded 2013-06-30 from https://github.com/NobleJS/setImmediate.
|
||||
|
||||
/* License:
|
||||
Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
(function (global, undefined) {
|
||||
"use strict";
|
||||
|
||||
var tasks = (function () {
|
||||
function Task(handler, args) {
|
||||
this.handler = handler;
|
||||
this.args = args;
|
||||
}
|
||||
Task.prototype.run = function () {
|
||||
// See steps in section 5 of the spec.
|
||||
if (typeof this.handler === "function") {
|
||||
// Choice of `thisArg` is not in the setImmediate spec; `undefined` is in the setTimeout spec though:
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html
|
||||
this.handler.apply(undefined, this.args);
|
||||
} else {
|
||||
var scriptSource = "" + this.handler;
|
||||
/*jshint evil: true */
|
||||
eval(scriptSource);
|
||||
}
|
||||
};
|
||||
|
||||
var nextHandle = 1; // Spec says greater than zero
|
||||
var tasksByHandle = {};
|
||||
var currentlyRunningATask = false;
|
||||
|
||||
return {
|
||||
addFromSetImmediateArguments: function (args) {
|
||||
var handler = args[0];
|
||||
var argsToHandle = Array.prototype.slice.call(args, 1);
|
||||
var task = new Task(handler, argsToHandle);
|
||||
|
||||
var thisHandle = nextHandle++;
|
||||
tasksByHandle[thisHandle] = task;
|
||||
return thisHandle;
|
||||
},
|
||||
runIfPresent: function (handle) {
|
||||
// From the spec: "Wait until any invocations of this algorithm started before this one have completed."
|
||||
// So if we're currently running a task, we'll need to delay this invocation.
|
||||
if (!currentlyRunningATask) {
|
||||
var task = tasksByHandle[handle];
|
||||
if (task) {
|
||||
currentlyRunningATask = true;
|
||||
try {
|
||||
task.run();
|
||||
} finally {
|
||||
delete tasksByHandle[handle];
|
||||
currentlyRunningATask = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
|
||||
// "too much recursion" error.
|
||||
global.setTimeout(function () {
|
||||
tasks.runIfPresent(handle);
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
remove: function (handle) {
|
||||
delete tasksByHandle[handle];
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
||||
function canUseNextTick() {
|
||||
// Don't get fooled by e.g. browserify environments.
|
||||
return typeof process === "object" &&
|
||||
Object.prototype.toString.call(process) === "[object process]";
|
||||
}
|
||||
|
||||
function canUseMessageChannel() {
|
||||
return !!global.MessageChannel;
|
||||
}
|
||||
|
||||
function canUsePostMessage() {
|
||||
// The test against `importScripts` prevents this implementation from being installed inside a web worker,
|
||||
// where `global.postMessage` means something completely different and can't be used for this purpose.
|
||||
|
||||
if (!global.postMessage || global.importScripts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var postMessageIsAsynchronous = true;
|
||||
var oldOnMessage = global.onmessage;
|
||||
global.onmessage = function () {
|
||||
postMessageIsAsynchronous = false;
|
||||
};
|
||||
global.postMessage("", "*");
|
||||
global.onmessage = oldOnMessage;
|
||||
|
||||
return postMessageIsAsynchronous;
|
||||
}
|
||||
|
||||
function canUseReadyStateChange() {
|
||||
return "document" in global && "onreadystatechange" in global.document.createElement("script");
|
||||
}
|
||||
|
||||
function installNextTickImplementation(attachTo) {
|
||||
attachTo.setImmediate = function () {
|
||||
var handle = tasks.addFromSetImmediateArguments(arguments);
|
||||
|
||||
process.nextTick(function () {
|
||||
tasks.runIfPresent(handle);
|
||||
});
|
||||
|
||||
return handle;
|
||||
};
|
||||
}
|
||||
|
||||
function installMessageChannelImplementation(attachTo) {
|
||||
var channel = new global.MessageChannel();
|
||||
channel.port1.onmessage = function (event) {
|
||||
var handle = event.data;
|
||||
tasks.runIfPresent(handle);
|
||||
};
|
||||
attachTo.setImmediate = function () {
|
||||
var handle = tasks.addFromSetImmediateArguments(arguments);
|
||||
|
||||
channel.port2.postMessage(handle);
|
||||
|
||||
return handle;
|
||||
};
|
||||
}
|
||||
|
||||
function installPostMessageImplementation(attachTo) {
|
||||
// Installs an event handler on `global` for the `message` event: see
|
||||
// * https://developer.mozilla.org/en/DOM/window.postMessage
|
||||
// * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages
|
||||
|
||||
var MESSAGE_PREFIX = "com.bn.NobleJS.setImmediate" + Math.random();
|
||||
|
||||
function isStringAndStartsWith(string, putativeStart) {
|
||||
return typeof string === "string" && string.substring(0, putativeStart.length) === putativeStart;
|
||||
}
|
||||
|
||||
function onGlobalMessage(event) {
|
||||
// This will catch all incoming messages (even from other windows!), so we need to try reasonably hard to
|
||||
// avoid letting anyone else trick us into firing off. We test the origin is still this window, and that a
|
||||
// (randomly generated) unpredictable identifying prefix is present.
|
||||
if (event.source === global && isStringAndStartsWith(event.data, MESSAGE_PREFIX)) {
|
||||
var handle = event.data.substring(MESSAGE_PREFIX.length);
|
||||
tasks.runIfPresent(handle);
|
||||
}
|
||||
}
|
||||
if (global.addEventListener) {
|
||||
global.addEventListener("message", onGlobalMessage, false);
|
||||
} else {
|
||||
global.attachEvent("onmessage", onGlobalMessage);
|
||||
}
|
||||
|
||||
attachTo.setImmediate = function () {
|
||||
var handle = tasks.addFromSetImmediateArguments(arguments);
|
||||
|
||||
// Make `global` post a message to itself with the handle and identifying prefix, thus asynchronously
|
||||
// invoking our onGlobalMessage listener above.
|
||||
global.postMessage(MESSAGE_PREFIX + handle, "*");
|
||||
|
||||
return handle;
|
||||
};
|
||||
}
|
||||
|
||||
function installReadyStateChangeImplementation(attachTo) {
|
||||
attachTo.setImmediate = function () {
|
||||
var handle = tasks.addFromSetImmediateArguments(arguments);
|
||||
|
||||
// Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
|
||||
// into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
|
||||
var scriptEl = global.document.createElement("script");
|
||||
scriptEl.onreadystatechange = function () {
|
||||
tasks.runIfPresent(handle);
|
||||
|
||||
scriptEl.onreadystatechange = null;
|
||||
scriptEl.parentNode.removeChild(scriptEl);
|
||||
scriptEl = null;
|
||||
};
|
||||
global.document.documentElement.appendChild(scriptEl);
|
||||
|
||||
return handle;
|
||||
};
|
||||
}
|
||||
|
||||
function installSetTimeoutImplementation(attachTo) {
|
||||
attachTo.setImmediate = function () {
|
||||
var handle = tasks.addFromSetImmediateArguments(arguments);
|
||||
|
||||
global.setTimeout(function () {
|
||||
tasks.runIfPresent(handle);
|
||||
}, 0);
|
||||
|
||||
return handle;
|
||||
};
|
||||
}
|
||||
|
||||
if (!global.setImmediate) {
|
||||
// If supported, we should attach to the prototype of global, since that is where setTimeout et al. live.
|
||||
var attachTo = typeof Object.getPrototypeOf === "function" && "setTimeout" in Object.getPrototypeOf(global) ?
|
||||
Object.getPrototypeOf(global)
|
||||
: global;
|
||||
|
||||
if (canUseNextTick()) {
|
||||
// For Node.js before 0.9
|
||||
installNextTickImplementation(attachTo);
|
||||
} else if (canUsePostMessage()) {
|
||||
// For non-IE10 modern browsers
|
||||
installPostMessageImplementation(attachTo);
|
||||
} else if (canUseMessageChannel()) {
|
||||
// For web workers, where supported
|
||||
installMessageChannelImplementation(attachTo);
|
||||
} else if (canUseReadyStateChange()) {
|
||||
// For IE 6–8
|
||||
installReadyStateChangeImplementation(attachTo);
|
||||
} else {
|
||||
// For older browsers
|
||||
installSetTimeoutImplementation(attachTo);
|
||||
}
|
||||
|
||||
attachTo.clearImmediate = tasks.remove;
|
||||
}
|
||||
}(typeof global === "object" && global ? global : this));
|
||||
Reference in New Issue
Block a user