mirror of
https://github.com/pkimpel/retro-220.git
synced 2026-01-11 23:52:46 +00:00
Commit retro-220 emulator version 0.06:
1. Correct scaling and normalization limiter errors in floating Add/Subtract uncovered by BALGOL testing; set Single-Step instead of Stop if normalization limiter is exceeded. 2. Correct improper comparison of sign digits by CFA/CFR uncovered by BALGOL testing. 3. Change method of lamp glow update in Processor and ControlConsole to provide smoother, more realistic display. 4. Implement diagnostic trace facility in Processor; toggle on and off by double-clicking "220" logo on ControlConsole. 5. Implement temporary, experimental floatingAdd() routine in Processor that does rounding for use in BALGOL validity checking (the 220 did not round, and it is disabled in this commit). 6. Correct printer carriage control handling in CardatronOutput. 7. Improve timing for ConsolePrinter TTY "Whippet mode". 8. Implement compression of consecutive duplicate blocks in tape image files for MagTapeDrive -- see comments in source. 9. Remove inappropriate SPO selection from unit designate on PaperTapeReader panel.
This commit is contained in:
parent
0fe12839a7
commit
b80b0f8c5f
@ -74,6 +74,7 @@ function B220Processor(config, devices) {
|
||||
this.magTape = null; // reference to Magnetic Tape Control Unit
|
||||
this.poweredOn = 0; // system is powered on and initialized
|
||||
this.successor = null; // current delayed-action successor function
|
||||
this.tracing = false; // emulator diagnostic tracing flag
|
||||
|
||||
// Memory
|
||||
this.memorySize = config.getNode("memorySize"); // memory size, words
|
||||
@ -256,7 +257,7 @@ function B220Processor(config, devices) {
|
||||
* Global Constants *
|
||||
***********************************************************************/
|
||||
|
||||
B220Processor.version = "0.05";
|
||||
B220Processor.version = "0.06";
|
||||
|
||||
B220Processor.tick = 1000/200000; // milliseconds per clock cycle (200KHz)
|
||||
B220Processor.cyclesPerMilli = 1/B220Processor.tick;
|
||||
@ -359,6 +360,35 @@ B220Processor.binaryBCD = function binaryBCD(v) {
|
||||
return result;
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B220Processor.padLeft = function padLeft(v, digits, pad) {
|
||||
/* Converts "v" to a string if necessary and formats to a total length of
|
||||
"digits," padding with the "pad" character on the left. Used only for debug */
|
||||
var padChar = (pad || "0").toString();
|
||||
var s = v.toString();
|
||||
var len = s.length;
|
||||
|
||||
if (len > digits) {
|
||||
s = s.substring(len-digits);
|
||||
} else {
|
||||
while (len < digits) {
|
||||
s = padChar + s;
|
||||
++len;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B220Processor.formatWord = function formatWord(w) {
|
||||
/* Formats the BCD value of 220 word "w" as the customary "9 9999 99 9999" */
|
||||
var s = B220Processor.padLeft(w.toString(16), 11);
|
||||
|
||||
return s.substring(0, 1) + " " + s.substring(1, 5) + " " +
|
||||
s.substring(5, 7) + " " + s.substring(7);
|
||||
};
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* Bit and Field Manipulation Functions *
|
||||
@ -1504,7 +1534,9 @@ B220Processor.prototype.floatingAdd = function floatingAdd(absolute) {
|
||||
augend (A), placing the result in A and clearing D. The R register is not
|
||||
affected. All values are BCD with the sign in the 11th digit position.
|
||||
The floating exponent is in the first two digit positions, biased by 50.
|
||||
Sets Overflow and the Digit Check alarm as necessary */
|
||||
Sets Overflow and the Digit Check alarm as necessary. For more on the use
|
||||
of the limiter digit in C/11 and the mechanization of floating add/subtract
|
||||
on the 220, see United States Patent 3,022,006, 1962-02-20 */
|
||||
var ax; // augend exponent (binary)
|
||||
var am = this.A.value % 0x10000000000; // augend mantissa (BCD)
|
||||
var aSign = ((this.A.value - am)/0x10000000000)%2;
|
||||
@ -1538,102 +1570,101 @@ B220Processor.prototype.floatingAdd = function floatingAdd(absolute) {
|
||||
|
||||
// If the exponents are unequal, scale the smaller
|
||||
// until they are in alignment, or one mantissa becomes zero.
|
||||
if (ax > dx) {
|
||||
// Scale D until its exponent matches or the mantissa goes to zero.
|
||||
while (ax > dx) {
|
||||
if (++shifts < 8) {
|
||||
timing += 0.010;
|
||||
dx = this.bcdAdd(1, dx, 2, 0, 0); // ++dx
|
||||
d = dm % 0x10;
|
||||
dm = (dm - d)/0x10; // shift right
|
||||
} else {
|
||||
sign = aSign; // result is value in A
|
||||
dm = dSign = limiter = 0;
|
||||
dx = 0x10;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (ax < dx) {
|
||||
// Scale A until its exponent matches or the mantissa goes to zero.
|
||||
while (ax < dx) {
|
||||
if (++shifts < 8) {
|
||||
timing += 0.010;
|
||||
ax = this.bcdAdd(1, ax, 3, 0, 0); // ++ax
|
||||
d = am % 0x10;
|
||||
am = (am - d)/0x10; // shift right
|
||||
} else {
|
||||
am = dm; // result is value in D with adjusted sign
|
||||
ax = dx;
|
||||
dm = dSign = limiter = 0;
|
||||
dx = 0x10;
|
||||
break;
|
||||
}
|
||||
|
||||
// Scale D until its exponent matches or the mantissa goes to zero.
|
||||
while (ax > dx) {
|
||||
if (++shifts < 8) {
|
||||
timing += 0.010;
|
||||
dx = this.bcdAdd(1, dx, 2, 0, 0); // ++dx
|
||||
d = dm % 0x10;
|
||||
dm = (dm - d)/0x10; // shift right
|
||||
} else {
|
||||
sign = aSign; // result is value in A
|
||||
limiter = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (am && dm) { // both mantissas are non-zero
|
||||
// Scale A until its exponent matches or the mantissa goes to zero.
|
||||
while (ax < dx) {
|
||||
if (++shifts < 8) {
|
||||
timing += 0.010;
|
||||
ax = this.bcdAdd(1, ax, 3, 0, 0); // ++ax
|
||||
d = am % 0x10;
|
||||
am = (am - d)/0x10; // shift right
|
||||
} else {
|
||||
am = dm; // result is value in D with adjusted sign
|
||||
ax = dx;
|
||||
limiter = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the mantissas
|
||||
if (shifts < 8) {
|
||||
compl = (aSign^sign);
|
||||
am = this.bcdAdd(am, dm, 11, compl, compl);
|
||||
dm = dSign = 0;
|
||||
dx = 0x10;
|
||||
|
||||
// Now examine the resulting sign (still in the adder) to see if we
|
||||
// need to recomplement the result.
|
||||
// Now examine the resulting sign (still in the adder) to see if there
|
||||
// is a carry and we need to recomplement the result and sign.
|
||||
if (this.Z.value) {
|
||||
// Reverse the sign toggle and recomplement the result.
|
||||
sign = 1-sign;
|
||||
am = this.bcdAdd(am, 0, 11, 1, 1);
|
||||
timing += 0.060;
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize or scale the result as necessary
|
||||
if (am >= 0x100000000) {
|
||||
// Mantissa overflow: add/subtract can produce at most one digit of
|
||||
// overflow, so shift right and increment the exponent, checking for
|
||||
// overflow in the exponent.
|
||||
limiter = 0;
|
||||
if (ax < 0x99) {
|
||||
timing += 0.005;
|
||||
ax = this.bcdAdd(1, ax, 3, 0, 0); // ++ax
|
||||
d = am % 0x10;
|
||||
am = (am - d)/0x10; // shift right
|
||||
dm = dSign = 0; // Set D to its strange result value
|
||||
dx = 0x10;
|
||||
|
||||
// Normalize or scale the result as necessary
|
||||
if (am >= 0x100000000) {
|
||||
// Mantissa overflow: add/subtract can produce at most one digit of
|
||||
// overflow, so scale by shifting right and incrementing the exponent,
|
||||
// checking for overflow in the exponent.
|
||||
limiter = 0;
|
||||
if (ax < 0x99) {
|
||||
timing += 0.005;
|
||||
ax = this.bcdAdd(1, ax, 3, 0, 0); // ++ax
|
||||
d = am % 0x10;
|
||||
am = (am - d)/0x10; // shift right
|
||||
} else {
|
||||
// A scaling shift would overflow the exponent, so set the overflow
|
||||
// toggle and leave the mantissa as it was from the add, without the
|
||||
// exponent inserted back into it. Since the A register gets reassembled
|
||||
// below, we need to set up the mantissa and exponent so the reconstruct
|
||||
// will effectively do nothing.
|
||||
this.OFT.set(1);
|
||||
sign = ax = dx = limiter = 0;
|
||||
}
|
||||
} else if (am == 0) { // mantissa is zero
|
||||
ax = sign = limiter = 0;
|
||||
timing += 0.065;
|
||||
} else { // normalize the result as necessary
|
||||
shifts = 0;
|
||||
while (am < 0x10000000) {
|
||||
if (ax > 0) {
|
||||
++shifts;
|
||||
timing += 0.010;
|
||||
ax = this.bcdAdd(1, ax, 3, 1, 1); // --ax
|
||||
am *= 0x10; // shift left
|
||||
} else {
|
||||
// A scaling shift would overflow the exponent, so set the overflow
|
||||
// toggle and leave the mantissa as it was from the add, without the
|
||||
// exponent inserted back into it. Since the A register gets reassembled
|
||||
// below, we need to set up the mantissa and exponent so the reconstruct
|
||||
// will effectively do nothing.
|
||||
this.OFT.set(1);
|
||||
sign = ax = dx = limiter = 0;
|
||||
}
|
||||
} else if (am == 0) { // mantissa is zero
|
||||
ax = sign = limiter = 0;
|
||||
timing += 0.065;
|
||||
} else { // normalize the result as necessary
|
||||
shifts = 0;
|
||||
while (am < 0x10000000) {
|
||||
if (ax > 0) {
|
||||
++shifts;
|
||||
timing += 0.010;
|
||||
ax = this.bcdAdd(1, ax, 3, 1, 1); // --ax
|
||||
am *= 0x10; // shift left
|
||||
} else {
|
||||
// Exponent underflow: set the reconstructed A to zero.
|
||||
am = ax = sign = 0;
|
||||
break;
|
||||
}
|
||||
// Exponent underflow: set the reconstructed A to zero.
|
||||
am = ax = sign = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine whether normalizing shifts exceed the limiter value
|
||||
if (limiter > 0) {
|
||||
if (limiter >= 8) {
|
||||
limiter = 0;
|
||||
} else if (shifts > limiter) {
|
||||
limiter = 10 - (shifts-limiter);
|
||||
this.setStop();
|
||||
} else {
|
||||
limiter = 0;
|
||||
}
|
||||
// Determine whether normalizing shifts exceed the limiter value
|
||||
if (limiter > 0) {
|
||||
if (limiter >= 8) {
|
||||
limiter = 0;
|
||||
} else if (shifts > limiter) {
|
||||
limiter = 10 - (shifts-limiter);
|
||||
this.SST.set(1); // limiter exceeded: set Single-Step
|
||||
} else {
|
||||
limiter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1652,6 +1683,177 @@ B220Processor.prototype.floatingAdd = function floatingAdd(absolute) {
|
||||
this.opTime = timing;
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B220Processor.prototype.floatingAdd__WITH_ROUND = function floatingAdd(absolute) {
|
||||
/* Algebraically add the floating-point addend (IB) to the floating-point
|
||||
augend (A), placing the result in A and clearing D. The R register is not
|
||||
affected. All values are BCD with the sign in the 11th digit position.
|
||||
The floating exponent is in the first two digit positions, biased by 50.
|
||||
Sets Overflow and the Digit Check alarm as necessary. For more on the use
|
||||
of the limiter digit in C/11 and the mechanization of floating add/subtract
|
||||
on the 220, see United States Patent 3,022,006, 1962-02-20 */
|
||||
|
||||
/* THIS IS AN EXPERIMENTAL VERSION THAT ROUNDS RESULTS */
|
||||
|
||||
var ax; // augend exponent (binary)
|
||||
var am = this.A.value % 0x10000000000; // augend mantissa (BCD)
|
||||
var aSign = ((this.A.value - am)/0x10000000000)%2;
|
||||
var compl; // complement addition required
|
||||
var d; // scratch digit;
|
||||
var dx; // addend exponent (binary)
|
||||
var dm; // addend mantissa (BCD)
|
||||
var dSign; // addend sign
|
||||
var limiter = (this.CCONTROL - this.CCONTROL%0x1000)/0x1000; // normalizing limiter
|
||||
var shifts = 0; // number of scaling/normalization shifts done
|
||||
var sign; // local copy of sign toggle
|
||||
var timing = 0.125; // minimum instruction timing
|
||||
|
||||
this.E.set(this.CADDR);
|
||||
this.readMemory();
|
||||
if (this.MET.value) { // invalid address
|
||||
return; // exit to Operation Complete
|
||||
}
|
||||
|
||||
dm = this.IB.value % 0x10000000000;
|
||||
dSign = ((this.IB.value - dm)/0x10000000000)%2;
|
||||
sign = (absolute ? 0 : dSign);
|
||||
if (this.SUT.value) {
|
||||
sign = 1-sign; // complement sign for subtraction
|
||||
}
|
||||
|
||||
ax = (am - am%0x100000000)/0x100000000;
|
||||
am %= 0x100000000;
|
||||
dx = (dm - dm%0x100000000)/0x100000000;
|
||||
dm %= 0x100000000;
|
||||
|
||||
am *= 0x100; // insert two low-order rounding digits
|
||||
dm *= 0x100;
|
||||
|
||||
// If the exponents are unequal, scale the smaller
|
||||
// until they are in alignment, or one mantissa becomes zero.
|
||||
|
||||
// Scale D until its exponent matches or the mantissa goes to zero.
|
||||
while (ax > dx) {
|
||||
if (++shifts < 8) {
|
||||
timing += 0.010;
|
||||
dx = this.bcdAdd(1, dx, 2, 0, 0); // ++dx
|
||||
d = dm % 0x10;
|
||||
dm = (dm - d)/0x10; // shift right
|
||||
} else {
|
||||
sign = aSign; // result is value in A
|
||||
limiter = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Scale A until its exponent matches or the mantissa goes to zero.
|
||||
while (ax < dx) {
|
||||
if (++shifts < 8) {
|
||||
timing += 0.010;
|
||||
ax = this.bcdAdd(1, ax, 3, 0, 0); // ++ax
|
||||
d = am % 0x10;
|
||||
am = (am - d)/0x10; // shift right
|
||||
} else {
|
||||
am = dm; // result is value in D with adjusted sign
|
||||
ax = dx;
|
||||
limiter = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the mantissas
|
||||
if (shifts < 8) {
|
||||
compl = (aSign^sign);
|
||||
am = this.bcdAdd(am, dm, 13, compl, compl);
|
||||
|
||||
// Now examine the resulting sign (still in the adder) to see if there
|
||||
// is a carry and we need to recomplement the result and sign.
|
||||
if (this.Z.value) {
|
||||
// Reverse the sign toggle and recomplement the result.
|
||||
sign = 1-sign;
|
||||
am = this.bcdAdd(am, 0, 13, 1, 1);
|
||||
timing += 0.060;
|
||||
}
|
||||
}
|
||||
|
||||
dm = dSign = 0; // Set D to its strange result value
|
||||
dx = 0x10;
|
||||
|
||||
// Normalize or scale the result as necessary
|
||||
if (am >= 0x10000000000) {
|
||||
// Mantissa overflow: add/subtract can produce at most one digit of
|
||||
// overflow, so scale by shifting right and incrementing the exponent,
|
||||
// checking for overflow in the exponent.
|
||||
limiter = 0;
|
||||
if (ax < 0x99) {
|
||||
timing += 0.005;
|
||||
ax = this.bcdAdd(1, ax, 3, 0, 0); // ++ax
|
||||
d = am % 0x10;
|
||||
am = (am - d)/0x10; // shift right
|
||||
} else {
|
||||
// A scaling shift would overflow the exponent, so set the overflow
|
||||
// toggle and leave the mantissa as it was from the add, without the
|
||||
// exponent inserted back into it. Since the A register gets reassembled
|
||||
// below, we need to set up the mantissa and exponent so the reconstruct
|
||||
// will effectively do nothing.
|
||||
this.OFT.set(1);
|
||||
sign = ax = dx = limiter = 0;
|
||||
}
|
||||
} else if (am < 0x50) { // mantissa is zero
|
||||
ax = sign = limiter = 0;
|
||||
timing += 0.065;
|
||||
} else { // normalize the result as necessary
|
||||
shifts = 0;
|
||||
while (am < 0x1000000000) { // NOTE: THIS INCLUDES THE ROUNDING DIGITS
|
||||
if (ax > 0) {
|
||||
++shifts;
|
||||
timing += 0.010;
|
||||
ax = this.bcdAdd(1, ax, 3, 1, 1); // --ax
|
||||
am *= 0x10; // shift left
|
||||
} else {
|
||||
// Exponent underflow: set the reconstructed A to zero.
|
||||
am = ax = sign = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine whether normalizing shifts exceed the limiter value
|
||||
if (limiter > 0) {
|
||||
if (limiter >= 8) {
|
||||
limiter = 0;
|
||||
} else if (shifts > limiter) {
|
||||
limiter = 10 - (shifts-limiter);
|
||||
this.SST.set(1); // limiter exceeded: set Single-Step
|
||||
} else {
|
||||
limiter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild the C register with the final normalization limiter
|
||||
this.CCONTROL = this.CCONTROL%0x1000 + limiter*0x1000;
|
||||
this.C.set((this.CCONTROL*0x100 + this.COP)*0x10000 + this.CADDR);
|
||||
|
||||
// Set toggles for display purposes and set the result.
|
||||
d = am%0x100; // get the rounding digits
|
||||
am = (am - am%0x100)/0x100; // scale back to 8 digits
|
||||
if (d > 0x50) { // round required
|
||||
am = this.bcdAdd(1, am, 11, 0, 0);
|
||||
if (am >= 0x100000000) {
|
||||
am = (am - am%0x10)/0x10;
|
||||
ax = this.bcdAdd(1, ax, 3, 0, 0); // ignore exponent overflow, for now
|
||||
}
|
||||
}
|
||||
|
||||
this.AX.set(ax);
|
||||
this.DX.set(dx);
|
||||
this.DST.set(dSign);
|
||||
this.SGT.set(sign);
|
||||
this.A.set((sign*0x100 + ax)*0x100000000 + am);
|
||||
this.D.set((dSign*0x100 + dx)*0x100000000 + dm);
|
||||
this.opTime = timing;
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B220Processor.prototype.floatingMultiply = function floatingMultiply() {
|
||||
/* Algebraically multiply the floating-point multiplicand in the IB register
|
||||
@ -1739,7 +1941,6 @@ B220Processor.prototype.floatingMultiply = function floatingMultiply() {
|
||||
am = (am-ad)/0x100;
|
||||
rd = rm % 0x100;
|
||||
rm = (rm-rd)/0x100 + ad*0x100000000;
|
||||
ax = this.bcdAdd(0x02, ax, 3, 1, 1); // decrement exponent
|
||||
} else if (ax > 0) {
|
||||
// Shift product one place right
|
||||
timing += 0.010;
|
||||
@ -1955,33 +2156,44 @@ B220Processor.prototype.compareField = function compareField() {
|
||||
}
|
||||
|
||||
// If the sign digit is included in the comparison, set up for algebraic
|
||||
// comparison and the strange sign ordering.
|
||||
// comparison and the strange sign ordering. This is tricky. Basically,
|
||||
// the non-signed digits need to be compared in a signed, algebraic manner,
|
||||
// but the transformed sign digits need to be compared unsigned. Since the
|
||||
// compare is based on a signed subtraction, then if the original sign of
|
||||
// either of the operands is negative, we use the 9s-complement of the
|
||||
// transformed sign digit for that operand, converting the unsigned
|
||||
// compare of the transformed sign digits into a signed one.
|
||||
if (L > s) { // sign digit is included
|
||||
rSign = (rw - rw%0x10000000000)/0x10000000000;
|
||||
dSign = (dw - dw%0x10000000000)/0x10000000000;
|
||||
sign = 1-dSign%2;
|
||||
compl = (rSign^sign)%2;
|
||||
compl = ((rSign%2)^sign);
|
||||
carry = compl;
|
||||
if (rSign < 8) {
|
||||
rSign ^= 3; // complement two low bits of sign
|
||||
if (rSign%2) { // complement the transformed sign
|
||||
rSign = 9 - (rSign<8 ? rSign^3 : rSign);
|
||||
} else if (rSign < 8) { // just transform the sign
|
||||
rSign ^= 3;
|
||||
}
|
||||
rw = rw%0x10000000000 + rSign*0x10000000000;
|
||||
|
||||
if (dSign < 8) {
|
||||
dSign ^= 3; // complement two low bits of sign
|
||||
rw = rw%0x10000000000 + rSign*0x10000000000;
|
||||
if (dSign%2) { // complement the transformed sign
|
||||
dSign = 9 - (dSign<8 ? dSign^3 : dSign);
|
||||
} else if (dSign < 8) {
|
||||
dSign ^= 3; // just transform the sign
|
||||
}
|
||||
|
||||
dw = dw%0x10000000000 + dSign*0x10000000000;
|
||||
}
|
||||
|
||||
// Now go through a modified add cycle, subtracting the digit pairs using
|
||||
// 9s-complement addition, and updating the comparison toggles for each digit.
|
||||
this.DC.set(0x09); // set up to rotate 11 digits
|
||||
while (this.DC.value < 0x20) {
|
||||
// 10s-complement addition, and marking the result unequal if any digits differ.
|
||||
this.DC.set(0x09); // set up to rotate through 11 digits
|
||||
do {
|
||||
rd = rw%0x10;
|
||||
dd = dw%0x10;
|
||||
if (s < 10) { // positition to the "s" digit
|
||||
++s;
|
||||
} else if (L > 0) {
|
||||
} else if (L > 0) { // compare digits in the sL field
|
||||
--L;
|
||||
this.X.set(rd); // for display only
|
||||
this.Y.set(dd);
|
||||
@ -1997,25 +2209,24 @@ B220Processor.prototype.compareField = function compareField() {
|
||||
if (adder) { // if the adder is not zero,
|
||||
unequal = 1; // result will be unequal, determined by sign
|
||||
}
|
||||
} else {
|
||||
// Ignore any digits after L is exhausted
|
||||
} else { // Ignore any digits after L is exhausted
|
||||
this.DC.set(0x19); // (the 220 didn't quit early like this, though)
|
||||
}
|
||||
|
||||
// Shift both words right (no need to rotate them)
|
||||
rw = (rw-rd)/0x10;
|
||||
dw = (dw-dd)/0x10;
|
||||
this.DC.inc();
|
||||
} // while DC < 20
|
||||
} while (this.DC.value < 0x20)
|
||||
|
||||
// If we are complementing and there is no final carry, we would normally
|
||||
// decomplement the result and reverse the sign, but decomp is not needed.
|
||||
// If we are not complementing and there is a final carry, we have overflow.
|
||||
if (compl ^ carry) {
|
||||
if (carry) {
|
||||
unequal = 1; // overflow, so force unequality
|
||||
} else {
|
||||
sign = 1-sign; // reverse sign (pseudo decomplement)
|
||||
// If there is a final carry, we keep the original sign; if we are not complementing,
|
||||
// force an unequal result. If there is no final carry, we complement the result sign.
|
||||
if (carry) {
|
||||
if (!compl) {
|
||||
unequal = 1;
|
||||
}
|
||||
} else {
|
||||
sign = 1-sign;
|
||||
}
|
||||
|
||||
// Set the console lamps and toggles to the result.
|
||||
@ -2030,7 +2241,7 @@ B220Processor.prototype.compareField = function compareField() {
|
||||
this.compareHighLamp.set(0);
|
||||
}
|
||||
|
||||
this.DST.set(dSign);
|
||||
this.DST.set(dSign%2);
|
||||
this.SGT.set(sign);
|
||||
this.HIT.set(high);
|
||||
this.UET.set(unequal);
|
||||
@ -4015,10 +4226,28 @@ B220Processor.prototype.ioInitiate = function ioInitiate() {
|
||||
/* Initiates asynchronous mode of the processor for I/O */
|
||||
|
||||
this.AST.set(1);
|
||||
this.updateLampGlow(1); // update the console lamps
|
||||
this.updateLampGlow(0); // update the console lamps
|
||||
this.execLimit = 0; // kill the run() loop
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B220Processor.prototype.traceState = function traceState() {
|
||||
/* Logs a subset of the Processor state to the Javascript console for
|
||||
debugging purposes */
|
||||
|
||||
console.log("P=" + B220Processor.padLeft(this.P.value.toString(16), 4) +
|
||||
" | B=" + B220Processor.padLeft(this.B.value.toString(16), 4) +
|
||||
" | C=" + B220Processor.formatWord(this.C.value).substring(2) +
|
||||
" | A=" + B220Processor.formatWord(this.A.value) +
|
||||
" | R=" + B220Processor.formatWord(this.R.value) +
|
||||
" | D=" + B220Processor.formatWord(this.D.value) +
|
||||
" | E=" + B220Processor.padLeft(this.E.value.toString(16), 4) +
|
||||
" | UET=" + this.UET.value +
|
||||
" | HIT=" + this.HIT.value +
|
||||
" | OFT=" + this.OFT.value +
|
||||
" | RPT=" + this.RPT.value);
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B220Processor.prototype.run = function run() {
|
||||
/* Main execution control loop for the processor. Called from this.schedule()
|
||||
@ -4040,6 +4269,10 @@ B220Processor.prototype.run = function run() {
|
||||
if (this.EXT.value) { // enter EXECUTE cycle
|
||||
this.execute();
|
||||
} else { // enter FETCH cycle
|
||||
if (this.tracing) {
|
||||
this.traceState(); // DEBUG ONLY
|
||||
}
|
||||
|
||||
if (this.SONSW) { // check for post-fetch S-to-P stop
|
||||
if (this.STOPSW) { // must check before P is incremented in fetch()
|
||||
if (this.SUNITSSW) {
|
||||
@ -4147,7 +4380,7 @@ B220Processor.prototype.start = function start() {
|
||||
this.runTimer -= stamp;
|
||||
}
|
||||
|
||||
this.updateLampGlow(1);
|
||||
this.updateLampGlow(1); // freeze state in the lamps
|
||||
this.schedule();
|
||||
}
|
||||
};
|
||||
@ -4219,7 +4452,7 @@ B220Processor.prototype.setCycle = function setCycle(cycle) {
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B220Processor.prototype.toggleCompare = function toggleCompare(condition) {
|
||||
B220Processor.prototype.toggleCompareLamps = function toggleCompareLamps(condition) {
|
||||
/* Toggles the comparison lamps and sets the processor UET and HIT toggles
|
||||
according to the condition: <0=LOW, 0=EQUAL, >0=HIGH */
|
||||
|
||||
|
||||
@ -433,7 +433,7 @@ B220CardatronOutput.prototype.initiateWrite = function initiateWrite() {
|
||||
case 1: // Relay 1 (eject page after printing)
|
||||
case 9: // same as 1
|
||||
this.printLine(line, this.pendingSpaceBefore);
|
||||
this.pendingSpaceBefore = 0;
|
||||
this.pendingSpaceBefore = -99;
|
||||
break;
|
||||
case 2: // Relay 2 (single space before and after printing)
|
||||
this.printLine(line, this.pendingSpaceBefore+1);
|
||||
|
||||
@ -33,6 +33,7 @@ function B220ConsolePrinter(mnemonic, unitIndex, config) {
|
||||
this.tabStop = []; // 0-relative tab stop positions
|
||||
this.zeroSuppress = 0; // zero-suppression switch setting
|
||||
this.charPeriod = 0; // printer speed, ms/char
|
||||
this.newLinePeriod = 0; // delay for carriage-returns
|
||||
|
||||
this.boundButton_Click = B220ConsolePrinter.prototype.button_Click.bind(this);
|
||||
this.boundText_OnChange = B220ConsolePrinter.prototype.text_OnChange.bind(this);
|
||||
@ -60,8 +61,11 @@ B220ConsolePrinter.offSwitchImage = "./resources/ToggleDown.png";
|
||||
B220ConsolePrinter.onSwitchImage = "./resources/ToggleUp.png";
|
||||
|
||||
B220ConsolePrinter.ttySpeed = 10; // TTY printer speed, char/sec
|
||||
B220ConsolePrinter.whippetSpeed = 200; // Whippet printer speed, char/sec
|
||||
// Inter-character period, ms
|
||||
B220ConsolePrinter.ttyNewLine = 200; // TTY carriage-return delay, ms
|
||||
B220ConsolePrinter.whippetSpeed = 5000; // Whippet printer speed, char/sec
|
||||
B220ConsolePrinter.whippetNewLine = 200;// Whippet carriage-return delay, ms
|
||||
B220ConsolePrinter.formFeedPeriod = 500;// form-feed average delay, ms
|
||||
|
||||
B220ConsolePrinter.pageSize = 66; // lines/page for form-feed
|
||||
B220ConsolePrinter.maxScrollLines = 15000;
|
||||
// Maximum amount of paper scrollback
|
||||
@ -282,8 +286,10 @@ B220ConsolePrinter.prototype.flipSwitch = function flipSwitch(ev) {
|
||||
prefs.printerSpeed = this.speedSwitch.state;
|
||||
if (this.speedSwitch.state) {
|
||||
this.charPeriod = 1000/B220ConsolePrinter.whippetSpeed;
|
||||
this.newLinePeriod = B220ConsolePrinter.whippetNewLine;
|
||||
} else {
|
||||
this.charPeriod = 1000/B220ConsolePrinter.ttySpeed;
|
||||
this.newLinePeriod = B220ConsolePrinter.ttyNewLine;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -409,8 +415,10 @@ B220ConsolePrinter.prototype.printerOnLoad = function printerOnLoad() {
|
||||
this.speedSwitch.set(prefs.printerSpeed);
|
||||
if (this.speedSwitch.state) {
|
||||
this.charPeriod = 1000/B220ConsolePrinter.whippetSpeed;
|
||||
this.newLinePeriod = B220ConsolePrinter.whippetNewLine;
|
||||
} else {
|
||||
this.charPeriod = 1000/B220ConsolePrinter.ttySpeed;
|
||||
this.newLinePeriod = B220ConsolePrinter.ttyNewLine;
|
||||
}
|
||||
|
||||
mask = 0x001;
|
||||
@ -538,13 +546,13 @@ B220ConsolePrinter.prototype.receiveChar = function receiveChar(char, successor)
|
||||
break;
|
||||
|
||||
case 0x15: // form-feed
|
||||
delay *= 4;
|
||||
delay = B220ConsolePrinter.formFeedPeriod;
|
||||
this.suppressLZ = 0;
|
||||
this.printFormFeed();
|
||||
break;
|
||||
|
||||
case 0x16: // carriage-return
|
||||
delay *= 2;
|
||||
delay = this.newLinePeriod;
|
||||
this.suppressLZ = 0;
|
||||
this.emptyLine();
|
||||
break;
|
||||
@ -565,7 +573,7 @@ B220ConsolePrinter.prototype.receiveChar = function receiveChar(char, successor)
|
||||
this.printTab();
|
||||
break;
|
||||
case 2: // EOW = carriage-return
|
||||
delay *= 2;
|
||||
delay = this.newLinePeriod;
|
||||
this.emptyLine();
|
||||
break;
|
||||
}
|
||||
|
||||
@ -33,6 +33,9 @@
|
||||
font-size: 36px;
|
||||
font-weight: bold}
|
||||
|
||||
#B220Logo.tracing {
|
||||
color: red}
|
||||
|
||||
#IntervalTimerResetCaption {
|
||||
left: calc(50% - 206px);
|
||||
bottom: 20px;
|
||||
|
||||
@ -29,13 +29,15 @@
|
||||
<div id=PanelSurface class=panelSurface>
|
||||
<img id=BurroughsMeatball src="resources/Burroughs-Meatball.png">
|
||||
<img id=BurroughsLogo src="resources/Burroughs-Logo.png">
|
||||
<div id=B220Logo>2 2 0</div>
|
||||
<div id=B220Logo
|
||||
title="Double-click to toggle emulator tracking">2 2 0</div>
|
||||
|
||||
<div id=IntervalTimerResetCaption class=caption>ZERO TIMER</div>
|
||||
<div id=IntervalTimerResetBtn class=blackButton3> </div>
|
||||
<div id=IntervalTimer>0000.0</div>
|
||||
|
||||
<div id=PowerOffBtn class=redButton3> </div>
|
||||
<div id=PowerOffBtn class=redButton3
|
||||
title="Double-click to shut down emulator"> </div>
|
||||
<div id=PowerOffCaption class=caption>POWER OFF</div>
|
||||
|
||||
<div id=ARegPanel class=panelRegister></div>
|
||||
|
||||
@ -469,7 +469,7 @@ B220ControlConsole.prototype.updatePanel = function updatePanel() {
|
||||
text = (timer/1000 + 10000).toFixed(1);
|
||||
this.intervalTimer.textContent = text.substring(text.length-6);
|
||||
|
||||
p.updateLampGlow(0);
|
||||
p.updateLampGlow(p.AST.value ? 0.25 : 0);
|
||||
eLevel = (p.RUT.value ? p.EXT.glow : p.EXT.value);
|
||||
|
||||
// Primary Registers
|
||||
@ -762,13 +762,23 @@ B220ControlConsole.prototype.switch_Click = function switch_Click(ev) {
|
||||
p.RPT.flip();
|
||||
break;
|
||||
case "LowLamp":
|
||||
p.toggleCompare(-1);
|
||||
p.toggleCompareLamps(-1);
|
||||
break;
|
||||
case "EqualLamp":
|
||||
p.toggleCompare(0);
|
||||
p.toggleCompareLamps(0);
|
||||
break;
|
||||
case "HighLamp":
|
||||
p.toggleCompare(+1);
|
||||
p.toggleCompareLamps(+1);
|
||||
break;
|
||||
|
||||
case "B220Logo":
|
||||
p.tracing = !p.tracing;
|
||||
this.$$("LeftPanelBtn").focus(); // release any selection by the click
|
||||
if (p.tracing) {
|
||||
B220Util.addClass(ev.target, "tracing");
|
||||
} else {
|
||||
B220Util.removeClass(ev.target, "tracing");
|
||||
}
|
||||
break;
|
||||
} // switch ev.target.id
|
||||
}
|
||||
@ -985,6 +995,7 @@ B220ControlConsole.prototype.consoleOnLoad = function consoleOnLoad() {
|
||||
this.tcuClearSwitch.addEventListener("click", this.boundSwitch_Click);
|
||||
|
||||
this.$$("BurroughsMeatball").addEventListener("click", this.boundMeatballMemdump, false);
|
||||
this.$$("B220Logo").addEventListener("dblclick", this.boundSwitch_Click);
|
||||
this.$$("IntervalTimerResetBtn").addEventListener("click", this.boundResetTimer, false);
|
||||
this.$$("PowerOffBtn").addEventListener("dblclick", this.boundPowerBtn_Click, false);
|
||||
|
||||
|
||||
@ -51,29 +51,47 @@
|
||||
* represents the magnetic end-of-tape area written to newly-edited tape.
|
||||
*
|
||||
* External tape images are ordinary text files in comma-separated variable (CSV)
|
||||
* format. Each line in the file represents one block for one lane. Each field
|
||||
* on the line represents one word for the tape image. The first field on the
|
||||
* line represents the lane number -- only its low-order bit is significant (0/1).
|
||||
* The second field is the preface word indicating the length of the data. A
|
||||
* block length of 100 is represented as either 0 or 100. Values greater than 100
|
||||
* or less than zero will be treated as 100.
|
||||
* format. Each line in the file represents one block for one lane. Lines may be
|
||||
* delimited by ASCII line-feed (hex 0A), carriage-return (hex 0D) or a carriage-
|
||||
* return/line-feed pair.
|
||||
*
|
||||
* Note that with this representation, it is possible that the count in the
|
||||
* preface may not match the actual number of words on the rest of the line.
|
||||
* The first field on the line contains one or two integers, formatted as "L" or
|
||||
* "R*L". L represents the lane number. Only its low-order bit (0/1) is used.
|
||||
* R is a repeat factor used to compress the size of tape image files. It
|
||||
* indicates the number of copies of this block that exist consecutively at this
|
||||
* point in the tape image. If R and its delimiting "*" are not present, R is
|
||||
* assumed to be one. If R is not present, the "*" must also be not present.
|
||||
*
|
||||
* The second field on a line is the preface word indicating the length of the
|
||||
* data. A block length of 100 may be represented as either 0 or 100. Values
|
||||
* greater than 100 or less than zero will be treated as 100.
|
||||
*
|
||||
* The remaining fields on a line represent the data words of the block. Since
|
||||
* words are stored internally in 4-bit BCD, these fields are interpreted as
|
||||
* hexadecimal values, although normally they should be composed only using the
|
||||
* decimal digits. Leading zero digits may be omitted. The digits of a word may
|
||||
* be preceded by a hyphen ("-"), which will cause a 1 to be OR-ed into the sign
|
||||
* digit of the word. Spaces may precede or follow the digits of a field, but may
|
||||
* not appear within the digits or between any leading "-" and the first digit.
|
||||
*
|
||||
* Note that with this representation, it is possible that the word count in
|
||||
* the preface may not match the actual number of words on the rest of the line.
|
||||
* While this might be useful at some point to allow construction of invalid
|
||||
* tape blocks that generate Tape Preface Failure (TPF) halts, at present the
|
||||
* block is stored in the internal tape image with exactly the number of words
|
||||
* specified by the preface. The remaining words on the line will be truncated
|
||||
* specified by the preface. The words specified on the line will be truncated
|
||||
* or padded with zero words as necessary in the internal image to achieve this
|
||||
* block length.
|
||||
*
|
||||
* Also note that the arrangement of blocks with respect to their lane is
|
||||
* arbitrary. Blocks for a lane can be arranged on the external image separately
|
||||
* or intermingled with the blocks for the other lane. When exporting a tape
|
||||
* image that has been modified, the drive will always dump all of lane 0 and
|
||||
* then all of lane 1. To save space in the external image, trailing words of
|
||||
* zeroes will be trimmed from each block. The lane number and preface word will
|
||||
* written, however.
|
||||
* or intermingled with the blocks for the other lane. The only requirement is
|
||||
* that blocks for a lane be in sequence. When exporting a tape image that has
|
||||
* been modified, the drive will always dump all of lane 0 and then all of
|
||||
* lane 1. To save space in the external image, trailing words of zeroes will
|
||||
* be trimmed from each block, and consecutive blocks containing the same data
|
||||
* will be compressed using the R*L notation discussed above. The lane number
|
||||
* and preface fields will always be written, however.
|
||||
*
|
||||
************************************************************************
|
||||
* 2017-07-09 P.Kimpel
|
||||
@ -610,9 +628,9 @@ B220MagTapeDrive.prototype.loadTape = function loadTape() {
|
||||
}
|
||||
|
||||
function editedLoader() {
|
||||
/* Loads an edited (blank) tape image into both lanes of the drive. If
|
||||
a block length was chosen on the tape-load panel, initializes the image
|
||||
with blocks of that size, followed by an end-of-tape block with
|
||||
/* Loads an edited (blank) tape image into the drive. If a block length
|
||||
was chosen on the tape-load panel, initializes the image with blocks of
|
||||
that size, followed by an end-of-tape block in both lanes having the
|
||||
controlWord (aaaa=0000, bbbb=0001) */
|
||||
var blockLen;
|
||||
var fmt = $$$("MTLoadFormatSelect").selectedIndex;
|
||||
@ -656,25 +674,49 @@ B220MagTapeDrive.prototype.loadTape = function loadTape() {
|
||||
/* Event handler for tape image file onLoad. Loads a text image as
|
||||
comma-delimited decimal word values. No end-of-tape block is written
|
||||
unless it is present in the text image */
|
||||
var blockLen; // length of current tape block
|
||||
var blockWords; // words in current tape block
|
||||
var chunk; // ANSI text of current chunk
|
||||
var chunkLength; // length of current ASCII chunk
|
||||
var buf = ev.target.result; // ANSI tape image buffer
|
||||
var bufLength = buf.length; // length of ANSI tape image buffer
|
||||
var dups; // repeat factor for consecutive blocks
|
||||
var eolRex = /([^\n\r\f]*)((:?\r[\n\f]?)|\n|\f)?/g;
|
||||
var index = 0; // char index into tape image buffer for next chunk
|
||||
var lane; // current tape lane image
|
||||
var lx = [0,0]; // word indexes for each lane
|
||||
var match; // result of eolRex.exec()
|
||||
var preface; // preface word: block length in words
|
||||
var repeatRex = /\s*(\d+)\s*\*\s*/; // regex to detect and parse repeat factor
|
||||
var tx; // char index into ANSI chunk text
|
||||
var wx; // word index within current block
|
||||
|
||||
function parseWord() {
|
||||
/* Parses the next word from the chunk text and returns its value as BCD */
|
||||
function parseRepeatFactor() {
|
||||
/* Parses the repeat factor, if any, from the first field on the
|
||||
line and returns its value. If there is no repeat factor, returns 1.
|
||||
Leaves "tx" (the index into the line) pointing to the lane number */
|
||||
var match; // result of regex match
|
||||
var v; // parsed numeric value
|
||||
|
||||
match = repeatRex.exec(chunk);
|
||||
if (!match) {
|
||||
v = 1; // default if no repeat present
|
||||
} else {
|
||||
tx += match[0].length;
|
||||
v = parseInt(match[1], 10);
|
||||
if (isNaN(v)) {
|
||||
v = 1; // default if repeat is non-numeric
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
function parseWord(radix) {
|
||||
/* Parses the next word from the chunk text and returns its value as
|
||||
determined by "radix" */
|
||||
var cx; // offset to next comma
|
||||
var text; // text of parsed word
|
||||
var v; // parsed decimal value
|
||||
var v; // parsed numeric value
|
||||
var w = 0; // result BCD word
|
||||
|
||||
if (tx < chunkLength) {
|
||||
@ -684,7 +726,7 @@ B220MagTapeDrive.prototype.loadTape = function loadTape() {
|
||||
}
|
||||
text = chunk.substring(tx, cx).trim();
|
||||
if (text.length > 0) {
|
||||
v = parseInt(text, 16); // parse as hex (BCD)
|
||||
v = parseInt(text, radix);
|
||||
if (!isNaN(v)) {
|
||||
if (v > 0) {
|
||||
w = v % 0x100000000000;
|
||||
@ -716,36 +758,43 @@ B220MagTapeDrive.prototype.loadTape = function loadTape() {
|
||||
chunk = match[1].trim();
|
||||
chunkLength = chunk.length;
|
||||
if (chunkLength > 0) { // ignore empty lines
|
||||
tx = wx = 0;
|
||||
mt.laneNr = parseWord()%2; // get the lane number
|
||||
preface = parseWord(); // get the preface word as hex BCD
|
||||
mt.imgIndex = lx[mt.laneNr]; // restore current offset for this lane
|
||||
if (preface > 0x100) {
|
||||
blockLen = 100; // limit blocks to 100 words
|
||||
} else if (preface > 0) {
|
||||
blockLen = B220Processor.bcdBinary(preface);
|
||||
} else { // if the block length is 0, make it 100
|
||||
blockLen = 100;
|
||||
tx = 0;
|
||||
dups = parseRepeatFactor(); // get the repeat factor, if any
|
||||
mt.laneNr = parseWord(10)%2; // get the lane number
|
||||
preface = parseWord(10); // get the preface word as decimal
|
||||
if (preface > 100) { // limit blocks to 100 words
|
||||
preface = 100;
|
||||
} else if (preface < 1) { // if block length <= 0, make it 100
|
||||
preface = 100;
|
||||
}
|
||||
|
||||
writeBlockStart(blockLen);
|
||||
blockWords = (preface == 1 ? 10 : preface); // pad out end-of-tape blocks to 10 words
|
||||
lane = mt.image[mt.laneNr];
|
||||
while (tx < chunkLength && wx < blockLen) {
|
||||
lane[mt.imgIndex+wx] = parseWord();
|
||||
mt.imgIndex = lx[mt.laneNr]; // restore internal offset for this lane
|
||||
|
||||
writeBlockStart(preface);
|
||||
wx = 0; // load data words from tape image
|
||||
while (tx < chunkLength && wx < preface) {
|
||||
lane[mt.imgIndex+wx] = parseWord(16);
|
||||
++wx;
|
||||
} // while
|
||||
} // while tx:wx
|
||||
|
||||
if (blockLen == 1) {
|
||||
blockLen = 10; // pad out end-of-tape blocks to 10 words
|
||||
}
|
||||
|
||||
while (wx < blockLen) {
|
||||
while (wx < blockWords) { // pad block with zero words, if needed
|
||||
lane[mt.imgIndex+wx] = 0;
|
||||
++wx;
|
||||
} // while
|
||||
} // while wx
|
||||
|
||||
mt.imgIndex += blockLen;
|
||||
mt.imgIndex += blockWords; // update the internal image offset
|
||||
writeBlockEnd();
|
||||
|
||||
wx = lx[mt.laneNr]; // starting offset of block
|
||||
blockWords = mt.imgIndex - wx; // total words in block, including overhead
|
||||
while (dups > 1) { // repeat the block as necessary
|
||||
--dups;
|
||||
lane.copyWithin(mt.imgIndex, wx, wx+blockWords);
|
||||
mt.imgIndex += blockWords;
|
||||
} // while dups
|
||||
|
||||
lx[mt.laneNr] = mt.imgIndex; // save current offset for this lane
|
||||
}
|
||||
}
|
||||
@ -757,9 +806,10 @@ B220MagTapeDrive.prototype.loadTape = function loadTape() {
|
||||
mt.imgIndex = lx[mt.laneNr];
|
||||
for (wx=0; wx<mt.startOfBlockWords*2; ++wx) {
|
||||
lane[mt.imgIndex+wx] = mt.markerGap;
|
||||
}
|
||||
} // for wx
|
||||
|
||||
lx[mt.laneNr] = mt.imgIndex + mt.startOfBlockWords*2;
|
||||
} // for tx (lane number)
|
||||
} // for mt.laneNr
|
||||
|
||||
mt.imgTopWordNr = Math.max(lx[0], lx[1]);
|
||||
mt.imgWritten = false;
|
||||
@ -939,9 +989,11 @@ B220MagTapeDrive.prototype.unloadTape = function unloadTape() {
|
||||
/* Converts the tape image to ASCII once the window has displayed the
|
||||
waiting message */
|
||||
var buf; // ANSI tape image buffer
|
||||
var dups = 0; // block repeat count
|
||||
var imgLength = mt.imgLength; // active words in tape image
|
||||
var imgTop = mt.imgTopWordNr; // tape image last block number
|
||||
var lane; // lane image buffer
|
||||
var lastBuf = ""; // last block image output
|
||||
var lx; // lane index
|
||||
var nzw; // number of consecutive zero words
|
||||
var state; // lane processing state variable
|
||||
@ -958,7 +1010,7 @@ B220MagTapeDrive.prototype.unloadTape = function unloadTape() {
|
||||
lane = mt.image[lx];
|
||||
state = 1;
|
||||
x = 0;
|
||||
while (x < imgLength) {
|
||||
do {
|
||||
switch (state) {
|
||||
case 1: // Search for start of block
|
||||
nzw = 0;
|
||||
@ -966,25 +1018,36 @@ B220MagTapeDrive.prototype.unloadTape = function unloadTape() {
|
||||
w = findBlockStart();
|
||||
if (w < 0) { // done with this lane
|
||||
x = imgLength; // kill the loop
|
||||
} else { // format the preface word
|
||||
} else { // format the lane number and preface word
|
||||
buf = lx.toString(10) + "," + w.toString(10);
|
||||
x = mt.imgIndex;
|
||||
state = 2;
|
||||
state = 2; // switch state to blocking data words
|
||||
}
|
||||
break;
|
||||
case 2: // Record the block data words
|
||||
w = lane[x++];
|
||||
if (w == 0) {
|
||||
++nzw; // suppress trailing zero words
|
||||
} else if (w >= 0) {
|
||||
if (w == 0) { // suppress trailing zero words
|
||||
++nzw;
|
||||
} else if (w >= 0) {// buffer the current word
|
||||
while (nzw > 0) {
|
||||
--nzw; // restore non-trailing zero words
|
||||
--nzw; // restore non-trailing zero words
|
||||
buf += ",0";
|
||||
}
|
||||
buf += "," + w.toString(16);
|
||||
} else {
|
||||
image.appendChild(doc.createTextNode(buf + "\n"));
|
||||
state = 1; // reset for next block
|
||||
} else { // output the last block image(s)
|
||||
state = 1; // reset state for next block
|
||||
if (buf == lastBuf) { // compress consecutive duplicate blocks
|
||||
++dups;
|
||||
} else {
|
||||
if (dups > 1) {
|
||||
image.appendChild(doc.createTextNode(dups.toString(10) + "*" + lastBuf + "\n"));
|
||||
} else if (dups > 0) {
|
||||
image.appendChild(doc.createTextNode(lastBuf + "\n"));
|
||||
}
|
||||
|
||||
lastBuf = buf;
|
||||
dups = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -992,7 +1055,17 @@ B220MagTapeDrive.prototype.unloadTape = function unloadTape() {
|
||||
throw new Error("Invalid state: B220MagTapeDrive.unloadTape, " + state);
|
||||
break;
|
||||
} // switch state
|
||||
} // while not at end of lane
|
||||
} while (x < imgLength);
|
||||
|
||||
// Output the final block(s) for the lane.
|
||||
if (dups > 1) {
|
||||
image.appendChild(doc.createTextNode(dups.toString(10) + "*" + lastBuf + "\n"));
|
||||
} else if (dups > 0) {
|
||||
image.appendChild(doc.createTextNode(lastBuf + "\n"));
|
||||
}
|
||||
|
||||
dups = 0;
|
||||
lastBuf = "";
|
||||
} // for lx
|
||||
|
||||
mt.setTapeUnloaded();
|
||||
|
||||
@ -38,7 +38,6 @@
|
||||
|
||||
<div id=UnitDesignateKnobCaption class=caption>UNIT DESIGNATE</div>
|
||||
<select id=UnitDesignateKnob>
|
||||
<option value= 0>SPO
|
||||
<option value= 1>1
|
||||
<option value= 2>2
|
||||
<option value= 3>3
|
||||
|
||||
@ -197,7 +197,7 @@ B220PaperTapeReader.prototype.flipSwitch = function flipSwitch(ev) {
|
||||
/* Handler for switch clicks */
|
||||
var id = ev.target.id;
|
||||
var prefs = this.config.getNode("ConsoleInput.units", this.unitIndex);
|
||||
var x;
|
||||
var x = 0;
|
||||
|
||||
switch (id) {
|
||||
case "RemoteSwitch":
|
||||
@ -214,12 +214,12 @@ B220PaperTapeReader.prototype.flipSwitch = function flipSwitch(ev) {
|
||||
case "UnitDesignateKnob":
|
||||
x = this.unitDesignateKnob.selectedIndex;
|
||||
if (x < 0) {
|
||||
x = this.unitDesignateKnob.length-1;
|
||||
this.unitMask = 0;
|
||||
} else {
|
||||
this.unitMask = B220Processor.pow2[x];
|
||||
prefs.unitMask = this.unitMask
|
||||
this.unitMask = B220Processor.pow2[x+1];
|
||||
}
|
||||
|
||||
prefs.unitMask = this.unitMask;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -258,11 +258,11 @@ B220PaperTapeReader.prototype.readerOnload = function readerOnload() {
|
||||
this.charPeriod = (this.speedSwitch.state ? B220PaperTapeReader.highSpeedPeriod : B220PaperTapeReader.lowSpeedPeriod);
|
||||
|
||||
this.unitDesignateKnob = this.$$("UnitDesignateKnob");
|
||||
mask = 0x001;
|
||||
this.unitMask = prefs.unitMask;
|
||||
if (this.unitMask == 0) {
|
||||
this.unitDesignateKnob.selectedIndex = this.unitDesignateKnob.length-1; // OFF
|
||||
} else {
|
||||
mask = 0x002; // ignore the 1-bit (used for SPO on output devices)
|
||||
for (x=0; x<this.unitDesignateKnob.length; ++x) {
|
||||
if (this.unitMask & mask) {
|
||||
this.unitDesignateKnob.selectedIndex = x;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user