mirror of
https://github.com/pkimpel/retro-220.git
synced 2026-04-10 22:51:52 +00:00
Release retro-220 emulator version 1.02a.
1. Apply corrections to emulator/B220Processor.js uncovered by the
paper-tape diagnostic routines acquired by Al Kossow:
a. Integer divide: sign of R must be preserved on overflow,
otherwise must be set to sign of the dividend (A register).
b. Floating division: sign of R must be preserved, correct align-
ment of operands in registers before starting division cycles,
perform 11 division cycles instead of 10, reconstruct A and R
registers if exponent overflow is detected during post-divide
mantissa normalization.
2. Disable console keyboard after ADD button is pressed.
3. Correct alphanumeric character code translation used by the internal
memory dump routine to match that for console and paper-tape I/O.
4. Fix bug with backward magnetic tape positioning (MPB) for tape
blocks less than 20 words long.
This commit is contained in:
@@ -260,7 +260,7 @@ function B220Processor(config, devices) {
|
||||
* Global Constants *
|
||||
***********************************************************************/
|
||||
|
||||
B220Processor.version = "1.02";
|
||||
B220Processor.version = "1.02a";
|
||||
|
||||
B220Processor.tick = 1000/200000; // milliseconds per clock cycle (200KHz)
|
||||
B220Processor.cyclesPerMilli = 1/B220Processor.tick;
|
||||
@@ -1497,6 +1497,8 @@ B220Processor.prototype.integerDivide = function integerDivide() {
|
||||
var dSign; // sign of divisior
|
||||
var rd; // current quotient (R) digit;
|
||||
var rm = this.R.value%0x10000000000;// current quotient (R) mantissa (ignore sign)
|
||||
var rSign = (this.R.value - rm)/0x10000000000;
|
||||
// R register sign (restored later)
|
||||
var sign; // local copy of sign toggle (sign of quotient)
|
||||
var tSign = 1; // sign for timing count accumulation
|
||||
var x; // digit counter
|
||||
@@ -1531,7 +1533,7 @@ B220Processor.prototype.integerDivide = function integerDivide() {
|
||||
if (this.bcdAdd(dm, am, 11, 1, 1) < 0x10000000000) {
|
||||
this.OFT.set(1);
|
||||
this.A.set(aSign*0x10000000000 + am);
|
||||
this.R.set(aSign*0x10000000000 + rm);
|
||||
this.R.set(rSign*0x10000000000 + rm);
|
||||
this.D.set(this.IB.value);
|
||||
this.opTime = 0.090;
|
||||
} else {
|
||||
@@ -1554,7 +1556,7 @@ B220Processor.prototype.integerDivide = function integerDivide() {
|
||||
} // for x
|
||||
|
||||
this.A.set(sign*0x10000000000 + rm); // rotate final values in A & R
|
||||
this.R.set(sign*0x10000000000 + am);
|
||||
this.R.set(aSign*0x10000000000 + am);
|
||||
this.D.set(dSign*0x10000000000 + dm);
|
||||
this.opTime = 3.805 + 0.060*count;
|
||||
}
|
||||
@@ -2012,7 +2014,6 @@ B220Processor.prototype.floatingDivide = function floatingDivide() {
|
||||
point dividend in the A & R registers by the floating-point divisor in the
|
||||
D register, producing a 9- or 10-digit quotient in the A & R registers
|
||||
and a 6- or 7-digit remainder in the low-order digits of the R register.
|
||||
See the Floating Point Handbook for the gory details of the result format.
|
||||
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 the
|
||||
Digit Check alarm as necessary */
|
||||
@@ -2025,7 +2026,9 @@ B220Processor.prototype.floatingDivide = function floatingDivide() {
|
||||
var dm = 0; // divisor mantissa
|
||||
var dSign = 0; // divisor sign
|
||||
var rd = 0; // current quotient (R) digit;
|
||||
var rm = this.R.value%0x10000000000;// current quotient (R) mantissa (ignore sign)
|
||||
var rm = this.R.value%0x10000000000;// current quotient (R) mantissa (drop sign)
|
||||
var rSign = (this.R.value-rm)/0x10000000000;
|
||||
// R register sign (restore later)
|
||||
var sign = 0; // local copy of sign toggle (sign of quotient)
|
||||
var timing = 0.085; // minimum instruction timing
|
||||
var tSign = 1; // sign for timing count accumulation
|
||||
@@ -2048,7 +2051,7 @@ B220Processor.prototype.floatingDivide = function floatingDivide() {
|
||||
dm %= 0x100000000;
|
||||
|
||||
if (am < 0x10000000 && dm >= 0x10000000) {
|
||||
this.A.set(0); // A is not normalized but D is, =quotient=0
|
||||
this.A.set(0); // A is not normalized but D is, quotient=0
|
||||
this.R.set(0);
|
||||
} else if (dm < 0x10000000) {
|
||||
this.OFT.set(1); // D is not normalized, overflow (div 0)
|
||||
@@ -2071,11 +2074,11 @@ B220Processor.prototype.floatingDivide = function floatingDivide() {
|
||||
sign = 0;
|
||||
this.A.set(am);
|
||||
} else {
|
||||
// Shift A+R, D left 2 into high-order digits
|
||||
dm *= 0x100;
|
||||
rd = (rm - rm%0x100000000)/0x100000000;
|
||||
rm = (rm%0x100000000)*0x100;
|
||||
am = am*0x100 + rd;
|
||||
// Shift A+R 1 digit right (exponent adjustment occurs later
|
||||
ad = am%0x10;
|
||||
am = (am-ad)/0x10;
|
||||
rd = rm%0x10;
|
||||
rm = (rm-rd)/0x10 + ad*0x1000000000;
|
||||
|
||||
// We now have the divisor in D (dm) and the dividend in A (am) & R (rm).
|
||||
// The value in am will become the remainder; the value in rm will become
|
||||
@@ -2083,12 +2086,12 @@ B220Processor.prototype.floatingDivide = function floatingDivide() {
|
||||
// subtracting the divisor from the dividend, counting subtractions until
|
||||
// underflow occurs, and shifting the divisor left one digit.
|
||||
// The 220 probably did not work quite the way that it has been mechanized
|
||||
// below, but we don't have sufficient technical details to know for sure.
|
||||
// below, but we don't have sufficient technical details to know for sure.
|
||||
// The following is adapted from the 205 implementation.
|
||||
|
||||
for (x=0; x<10; ++x) {
|
||||
for (x=0; x<11; ++x) {
|
||||
// Repeatedly subtract D from A until we would get underflow.
|
||||
ad = 0;
|
||||
ad = 0;
|
||||
|
||||
/********** DEBUG **********
|
||||
console.log("FDV %2d Ax=%3s A=%11s R=%11s Dx=%2s D=%11s", x,
|
||||
@@ -2109,21 +2112,21 @@ B220Processor.prototype.floatingDivide = function floatingDivide() {
|
||||
rd = (rm - rm%0x1000000000)/0x1000000000;
|
||||
rm = (rm%0x1000000000)*0x10 + ad;
|
||||
// Shift into remainder except on last digit.
|
||||
if (x < 9) {
|
||||
if (x < 10) {
|
||||
am = am*0x10 + rd;
|
||||
}
|
||||
|
||||
tSign = -tSign;
|
||||
} // for x
|
||||
|
||||
/********** DEBUG **********
|
||||
|
||||
/********** DEBUG **********
|
||||
console.log("FDV %2d Ax=%3s A=%11s R=%11s Dx=%2s D=%11s", x,
|
||||
(ax+0x1000).toString(16).substring(1),
|
||||
(am+0x100000000000).toString(16).substring(1),
|
||||
(rm+0x100000000000).toString(16).substring(1),
|
||||
(dx+0x1000).toString(16).substring(1),
|
||||
(dm+0x100000000000).toString(16).substring(1));
|
||||
***************************/
|
||||
***************************/
|
||||
|
||||
// Rotate the quotient and remainder for 10 digits to exchange registers
|
||||
for (x=0; x<10; ++x) {
|
||||
@@ -2132,7 +2135,7 @@ B220Processor.prototype.floatingDivide = function floatingDivide() {
|
||||
rm = (rm - rd)/0x10 + ad*0x1000000000;
|
||||
am = (am - ad)/0x10 + rd*0x1000000000;
|
||||
}
|
||||
|
||||
|
||||
/********** DEBUG **********
|
||||
console.log("FDV %2d Ax=%3s A=%11s R=%11s Dx=%2s D=%11s", 98,
|
||||
(ax+0x1000).toString(16).substring(1),
|
||||
@@ -2144,6 +2147,8 @@ B220Processor.prototype.floatingDivide = function floatingDivide() {
|
||||
|
||||
if (am >=0x1000000000 && ax == 0x99) {
|
||||
this.OFT.set(1);
|
||||
this.A.set(am);
|
||||
this.R.set(rSign*0x10000000000 + rm);
|
||||
} else {
|
||||
if (am < 0x1000000000) {
|
||||
// Normalize one digit to the right
|
||||
@@ -2158,7 +2163,7 @@ B220Processor.prototype.floatingDivide = function floatingDivide() {
|
||||
ax = this.bcdAdd(ax, 1, 3);
|
||||
}
|
||||
|
||||
/********** DEBUG **********
|
||||
/********** DEBUG **********
|
||||
console.log("FDV %2d Ax=%3s A=%11s R=%11s Dx=%2s D=%11s", 99,
|
||||
(ax+0x1000).toString(16).substring(1),
|
||||
(am+0x100000000000).toString(16).substring(1),
|
||||
@@ -2169,7 +2174,7 @@ B220Processor.prototype.floatingDivide = function floatingDivide() {
|
||||
|
||||
// Reconstruct the final product in the registers
|
||||
this.A.set((sign*0x100 + ax)*0x100000000 + am);
|
||||
this.R.set(sign*0x10000000000 + rm);
|
||||
this.R.set(rSign*0x10000000000 + rm);
|
||||
}
|
||||
|
||||
timing += 4.075 + 0.060*count;
|
||||
|
||||
@@ -145,6 +145,7 @@ B220ConsoleKeyboard.prototype.keypress = function keypress(ev) {
|
||||
break;
|
||||
case 0x2B: // "+" = ADD
|
||||
this.animateClick(this.$$("AddBtn"));
|
||||
this.keyboardEnable(0);
|
||||
this.p.keyboardAction(-1);
|
||||
break;
|
||||
case 0x43: case 0x63: // "C", "c"
|
||||
|
||||
@@ -110,16 +110,17 @@ B220ControlConsole.offOrganSwitchImage = "./resources/Organ-Switch-Up.png"
|
||||
B220ControlConsole.onOrganSwitchImage = "./resources/Organ-Switch-Down.png"
|
||||
|
||||
B220ControlConsole.codeXlate = [ // translate internal 220 code to ANSI
|
||||
" ", "_", " ", ".", "\u00A4", "_", "_", "_", "_", "_", "!", "!", "!", "!", "!", "!", // 00-0F
|
||||
"&", "_", "_", "$", "*", "^", "~", "_", "_", "_", "!", "!", "!", "!", "!", "!", // 10-1F
|
||||
"-", "/", "_", ",", "%", "_", "|", "_", "_", "_", "!", "!", "!", "!", "!", "!", // 20-2F
|
||||
"_", "_", "_", "#", "@", "\\", "_", "_", "_", "_", "!", "!", "!", "!", "!", "!", // 30-3F
|
||||
"_", "A", "B", "C", "D", "E", "F", "G", "H", "I", "!", "!", "!", "!", "!", "!", // 40-4F
|
||||
"_", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "!", "!", "!", "!", "!", "!", // 50-5F
|
||||
"_", "_", "S", "T", "U", "V", "W", "X", "Y", "Z", "!", "!", "!", "!", "!", "!", // 60-6F
|
||||
"_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "!", "!", "!", "!", "!", "!", // 70-7F
|
||||
// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
|
||||
" ", "?", "_", ".", "\u00A4", "?", "?", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 00-0F
|
||||
"&", "?", "?", "$", "*", "^", "|", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 10-1F
|
||||
"-", "/", "?", ",", "%", "?", "~", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 20-2F
|
||||
"?", "?", "?", "#", "@", "\\", "?", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 30-3F
|
||||
"?", "A", "B", "C", "D", "E", "F", "G", "H", "I", "!", "!", "!", "!", "!", "!", // 40-4F
|
||||
"?", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "!", "!", "!", "!", "!", "!", // 50-5F
|
||||
"?", "?", "S", "T", "U", "V", "W", "X", "Y", "Z", "!", "!", "!", "!", "!", "!", // 60-6F
|
||||
"?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 70-7F
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "!", "!", "!", "!", "!", // 80-8F
|
||||
"_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "!", "!", "!", "!", "!", "!", // 90-9F
|
||||
"?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 90-9F
|
||||
"!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // A0-AF
|
||||
"!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // B0-BF
|
||||
"!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // C0-CF
|
||||
|
||||
@@ -1008,7 +1008,8 @@ B220MagTapeControl.prototype.positionBackward = function positionBackward(dReg)
|
||||
.then(spaceBlock)
|
||||
.catch(this.boundReleaseControl);
|
||||
} else { // block count exhausted
|
||||
this.currentUnit.boundReleaseDelay(this.driveState)
|
||||
this.currentUnit.reposition(this.driveState)
|
||||
.then(this.currentUnit.boundReleaseDelay)
|
||||
.then(this.currentUnit.boundReleaseUnit)
|
||||
.then(this.boundReleaseControl)
|
||||
.catch(this.boundReleaseControl);
|
||||
|
||||
@@ -192,11 +192,11 @@ B220MagTapeDrive.prototype.maxTapeWords = Math.floor(B220MagTapeDrive.prototype.
|
||||
B220MagTapeDrive.prototype.maxTapeBlocks = Math.floor(B220MagTapeDrive.prototype.maxTapeWords/
|
||||
(B220MagTapeDrive.prototype.minBlockWords+B220MagTapeDrive.prototype.startOfBlockWords+B220MagTapeDrive.prototype.endOfBlockWords));
|
||||
// max possible blocks on a tape lane
|
||||
B220MagTapeDrive.prototype.repositionWords = 5;
|
||||
B220MagTapeDrive.prototype.repositionWords = 4;
|
||||
// number of words to reposition back into the block after a turnaround
|
||||
B220MagTapeDrive.prototype.startTime = 3;
|
||||
// tape start time [ms]
|
||||
B220MagTapeDrive.prototype.startWords = 6;
|
||||
B220MagTapeDrive.prototype.startWords = 4;
|
||||
// number of words traversed during tape start time
|
||||
B220MagTapeDrive.prototype.stopTime = 3;
|
||||
// tape stop time [ms]
|
||||
@@ -1356,7 +1356,7 @@ B220MagTapeDrive.prototype.reverseDirection = function reverseDirection(driveSta
|
||||
/**************************************/
|
||||
B220MagTapeDrive.prototype.reposition = function reposition(driveState) {
|
||||
/* Reverses tape direction after a forward tape operation and repositions
|
||||
the head five words from the end of the prior block, giving room for
|
||||
the head four words from the end of the prior block, giving room for
|
||||
startup acceleration of the next forward operation. The "prior block" is
|
||||
located by the first EOB (erase gap) or flaw marker word encountered when
|
||||
moving in a backward direction. Returns a Promise that resolves when tape
|
||||
@@ -2283,9 +2283,9 @@ B220MagTapeDrive.prototype.initialWriteBlock = function initialWriteBlock(driveS
|
||||
/**************************************/
|
||||
B220MagTapeDrive.prototype.spaceForwardBlock = function spaceForwardBlock(driveState) {
|
||||
/* Positions tape one block in a forward direction. Leaves the block
|
||||
positioned at the preface word, ready to space the next block or reposition
|
||||
into the prior block at the end of the operation. Returns a Promise that
|
||||
resolves after the block is spaced */
|
||||
positioned at the next preface word, ready to space the next block or
|
||||
reposition into the prior block at the end of the operation. Returns a
|
||||
Promise that resolves after the block is spaced */
|
||||
|
||||
var spaceBlock = (resolve, reject) => {
|
||||
/* Spaces forward over the next block. Blocks are counted as their
|
||||
@@ -2318,11 +2318,11 @@ B220MagTapeDrive.prototype.spaceForwardBlock = function spaceForwardBlock(driveS
|
||||
if (w == this.markerGap) {
|
||||
++x;
|
||||
} else {
|
||||
state = 3;
|
||||
state = 3; // found preface word
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: // found preface: search for end of block (next erase-gap word)
|
||||
case 3: // search for end of block (next erase-gap word)
|
||||
if (w == this.markerEOB) {
|
||||
++x;
|
||||
state = 4;
|
||||
@@ -2356,9 +2356,8 @@ B220MagTapeDrive.prototype.spaceForwardBlock = function spaceForwardBlock(driveS
|
||||
/**************************************/
|
||||
B220MagTapeDrive.prototype.spaceBackwardBlock = function spaceBackwardBlock(driveState) {
|
||||
/* Positions tape one block in a backward direction. Leaves the block
|
||||
positioned five words into the end of the prior block, as for a normal
|
||||
reposition after a forward operation. Returns a Promise that resolves
|
||||
after the block is spaced */
|
||||
positioned before the preface word. Returns a Promise that resolves after
|
||||
the block is spaced */
|
||||
|
||||
var spaceBlock = (resolve, reject) => {
|
||||
/* Spaces backward over the current or prior block. Blocks are counted
|
||||
@@ -2377,10 +2376,9 @@ B220MagTapeDrive.prototype.spaceBackwardBlock = function spaceBackwardBlock(driv
|
||||
} else {
|
||||
w = lane[x];
|
||||
switch (state) {
|
||||
case 1: // initial state: skip over flaw and magnetic EOT words
|
||||
case 1: // initial state: skip over inter-block gap, flaw, and magnetic EOT words
|
||||
if (w == this.markerGap) {
|
||||
--x;
|
||||
state = 2;
|
||||
} else if (w == this.markerFlaw) {
|
||||
--x;
|
||||
} else if (w == this.markerMagEOT) {
|
||||
@@ -2390,17 +2388,9 @@ B220MagTapeDrive.prototype.spaceBackwardBlock = function spaceBackwardBlock(driv
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // skip initial inter-block gap words
|
||||
if (w == this.markerGap) {
|
||||
--x;
|
||||
} else {
|
||||
state = 3;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: // search for start of block (first prior inter-block gap word)
|
||||
if (w == this.markerGap) {
|
||||
state = 4;
|
||||
state = 4; // just passed the preface word
|
||||
} else {
|
||||
--x;
|
||||
}
|
||||
@@ -2410,16 +2400,8 @@ B220MagTapeDrive.prototype.spaceBackwardBlock = function spaceBackwardBlock(driv
|
||||
if (w == this.markerGap) {
|
||||
--x;
|
||||
} else {
|
||||
state = 5;
|
||||
}
|
||||
break;
|
||||
|
||||
case 5: // skip the prior block's erase-gap words
|
||||
if (w == this.markerEOB) {
|
||||
--x;
|
||||
} else { // position into end of prior block, as usual
|
||||
state = 0;
|
||||
resolve(this.moveTapeTo(x-this.repositionWords, driveState));
|
||||
resolve(this.moveTapeTo(x, driveState));
|
||||
}
|
||||
break;
|
||||
} // switch state
|
||||
@@ -2438,7 +2420,7 @@ B220MagTapeDrive.prototype.spaceBackwardBlock = function spaceBackwardBlock(driv
|
||||
/**************************************/
|
||||
B220MagTapeDrive.prototype.spaceEOIBlock = function spaceEOIBlock(driveState) {
|
||||
/* Spaces forward one block on tape, detecting end-of-information if encountered
|
||||
(i.e., gap longer and inter-block gap. Returns a Promise that resolves
|
||||
(i.e., gap longer than inter-block gap. Returns a Promise that resolves
|
||||
after the block is spaced or EOI is encountered */
|
||||
|
||||
var spaceBlock = (resolve, reject) => {
|
||||
|
||||
Reference in New Issue
Block a user