mirror of
https://github.com/pkimpel/retro-b5500.git
synced 2026-04-26 12:18:08 +00:00
Finish coding Character Mode ops and start on single-precision Add/Subtract.
This commit is contained in:
@@ -496,7 +496,7 @@ B5500Processor.prototype.compareSourceWithDest = function(count) {
|
||||
this.M++;
|
||||
}
|
||||
}
|
||||
} else { // check this character
|
||||
} else { // strings still equal -- check this character
|
||||
if ((this.Y = this.cc.fieldIsolate(this.A, aBit, 6)) != (this.Z = this.cc.fieldIsolate(this.B, bBit, 6))) {
|
||||
this.Q |= 0x04; // set Q03F to stop further comparison
|
||||
this.MSFF = (B5500Processor.collate[this.Y] > B5500Processor.collate[this.Z] ? 1 : 0);
|
||||
@@ -673,6 +673,51 @@ B5500Processor.prototype.fieldArithmetic = function(count, subtract) {
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500Processor.prototype.streamBitsToDest = function(count, mask) {
|
||||
/* Streams a pattern of bits to the destination specified by S, K, and V,
|
||||
as supplied by the 48-bit "mask" argument. Partial words are filled from
|
||||
the low-order bits of the mask. Implements the guts of Character-Mode
|
||||
Bit Set (XX64) and Bit Reset (XX65). Leaves the registers pointing at the
|
||||
next bit in sequence */
|
||||
var bn; // field starting bit number
|
||||
var fl; // field length in bits
|
||||
|
||||
if (count) {
|
||||
this.cycleCount += count;
|
||||
if (!this.BROF) {
|
||||
this.access(0x03); // B = [S]
|
||||
}
|
||||
do {
|
||||
bn = this.K*6 + this.V; // starting bit nr.
|
||||
fl = 48-bn; // bits remaining in the word
|
||||
if (count < fl) {
|
||||
fl = count;
|
||||
}
|
||||
if (fl < 48) {
|
||||
this.B = this.cc.fieldInsert(this.B, bn, fl, mask);
|
||||
} else {
|
||||
this.B = mask; // set the whole word
|
||||
}
|
||||
count -= fl; // decrement by number of bits modified
|
||||
bn += fl; // increment the starting bit nr.
|
||||
if (bn < 48) {
|
||||
this.V = bn % 6;
|
||||
this.K = (bn - this.V)/6;
|
||||
} else {
|
||||
this.K = this.V = 0;
|
||||
this.access(0x0B); // [S] = B, save the updated word
|
||||
this.S++;
|
||||
if (count > 0) {
|
||||
this.access(0x03); // B = [S], fetch next word in sequence
|
||||
} else {
|
||||
this.BROF = 0;
|
||||
}
|
||||
}
|
||||
} while (count);
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500Processor.prototype.streamSourceToDest = function(count, transform) {
|
||||
/* General driver for character-mode character transfers from source to
|
||||
@@ -765,7 +810,155 @@ B5500Processor.prototype.streamToDest = function(count, transform) {
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500Processor.storeForInterrupt = function(forTest) {
|
||||
B5500Processor.prototype.streamInputConvert = function(count) {
|
||||
/* Converts a signed-numeric character field at the source M & G address
|
||||
from decimal to binary, storing the resulting word at the S address and then
|
||||
incrementing S. Normally, decimal to binary conversion shouldn't be this
|
||||
complex, so we must do it more or less the way the B5500 hardware did, by
|
||||
repeated remainder division (i.e., shifting right) and adjusting the
|
||||
low-order digit by -3 when a one was shifted into the high-order bit of the
|
||||
low-order digit from the higher digit locations. The problem with doing it a
|
||||
more direct and efficient way is with digits that are not in the range 0-9.
|
||||
Doing it the hardware way should yield the same (albeit questionable)
|
||||
result. See Section 2.6 in the B5281 Training Manual for details. This
|
||||
process took at least 27 clocks on the B5500, so we can afford to be slow
|
||||
here, too. Note that a maximum of 8 characters are converted */
|
||||
var a = 0; // local working copy of A
|
||||
var b = 0; // local working copy of B
|
||||
var power = 1; // A-register shif factor
|
||||
|
||||
this.streamAdjustSourceChar();
|
||||
if (this.BROF) {
|
||||
this.access(0x0B); // [S] = B
|
||||
this.BROF = 0;
|
||||
}
|
||||
if (this.K || this.V) { // adjust dest to word boundary
|
||||
this.K = this.V = 0;
|
||||
this.S++;
|
||||
}
|
||||
if (count) { // count > 0
|
||||
this.cycleCount += count*2 + 27;
|
||||
count = ((count-1) & 0x07) + 1; // limit the count to 8
|
||||
if (!this.AROF) {
|
||||
this.access(0x04); // A = [M]
|
||||
}
|
||||
|
||||
// First, assemble the digits into B
|
||||
do {
|
||||
b = b << 4 | ((this.Y = this.cc.fieldIsolate(this.A, this.G*6, 6)) & 0x0F);
|
||||
if (this.G < 7) {
|
||||
this.G++;
|
||||
} else {
|
||||
this.G = 0;
|
||||
this.M++;
|
||||
if (count > 1) {
|
||||
this.access(0x04); // A = [M], only if more chars are needed
|
||||
} else {
|
||||
this.AROF = 0;
|
||||
}
|
||||
}
|
||||
} while (--count);
|
||||
|
||||
// Then do the artful shifting to form the binary value in A
|
||||
this.AROF = 0;
|
||||
this.B = b; // for display purposes only
|
||||
while (b) {
|
||||
if (b & 0x01) {
|
||||
a += power;
|
||||
}
|
||||
power *= 2;
|
||||
b >>>= 1;
|
||||
if (b & 0x08) {
|
||||
b -= 3; // since the low-order digit is >= 8, don't worry about borrow
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, fix up the binary sign and store the result
|
||||
if (a) { // zero results have sign bit reset
|
||||
if (this.Y & 0x30 == 0x20) {
|
||||
a += 0x400000000000; // set the sign bit
|
||||
}
|
||||
}
|
||||
this.A = a;
|
||||
this.access(0x0A); // [S] = A
|
||||
this.S++;
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500Processor.prototype.streamOutputConvert = function(count) {
|
||||
/* Converts the binary word addressed by M (after word-boundary adjustment)
|
||||
to decimal BIC at the destination address of S & K. The maximum number of
|
||||
digits to convert is 8. If the binary value can be represented in "count"
|
||||
digits (or the count is zero), the true-false FF, MSFF, is set; otherwise it
|
||||
is reset. The sign is stored in low-order character of the result */
|
||||
var a; // local working copy of A
|
||||
var b = 0; // local working copy of B
|
||||
var c; // converted decimal character
|
||||
var d = 0; // digit counter
|
||||
var power = 1; // power-of-64 factor for result digits
|
||||
|
||||
this.MSFF = 1; // set TFFF unless there's overflow
|
||||
this.streamAdjustDestChar();
|
||||
if (this.BROF) {
|
||||
this.access(0x0B); // [S] = B, but leave BROF set
|
||||
}
|
||||
if (this.G || this.H) { // adjust source to word boundary
|
||||
this.G = this.H = 0;
|
||||
this.AROF = 0;
|
||||
this.M++;
|
||||
}
|
||||
if (count) { // count > 0
|
||||
this.cycleCount += count*2 + 27;
|
||||
if (!this.AROF) {
|
||||
this.access(0x04); // A = [M]
|
||||
}
|
||||
count = ((count-1) & 0x07) + 1; // limit the count to 8
|
||||
a = this.A % 0x8000000000; // get absolute mantissa value, ignore exponent
|
||||
if (a) { // mantissa is non-zero, so conversion is required
|
||||
if ((this.A % 800000000000) >= 0x400000000000) {
|
||||
b = 0x20; // result is negative, so preset the sign in the low-order digit
|
||||
}
|
||||
do { // Convert the binary value in A to BIC digits in B
|
||||
c = a % 10;
|
||||
a = (a-c)/10;
|
||||
if (c) {
|
||||
b += c*power;
|
||||
}
|
||||
power *= 64;
|
||||
} while (a && ++d < count);
|
||||
if (a) {
|
||||
this.MSFF = 0; // overflow occurred, so reset TFFF
|
||||
}
|
||||
}
|
||||
this.AROF = 0; // invalidate A
|
||||
this.M++; // and advance to the next source word
|
||||
|
||||
// Finally, stream the digits from A (whose value is still in local b) to the destination
|
||||
this.A = b; // for display purposes only
|
||||
this.access(0x03) // B = [S], restore original value of B
|
||||
d = 48 - count*6; // starting bit in A
|
||||
do {
|
||||
this.B = this.cc.fieldTransfer(this.B, this.K*6, 6, b, d);
|
||||
d += 6;
|
||||
if (this.K < 7) {
|
||||
this.K++;
|
||||
} else {
|
||||
this.access(0x0B); // [S] = B
|
||||
this.K = 0;
|
||||
this.S++;
|
||||
if (count > 1) {
|
||||
this.access(0x03); // B = [S]
|
||||
} else
|
||||
this.BROF = 0;
|
||||
}
|
||||
}
|
||||
} while (--count);
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500Processor.prototype.storeForInterrupt = function(forTest) {
|
||||
/* Implements the 3011=SFI operator and the parts of 3411=SFT that are
|
||||
common to it. "forTest" implies use from SFT */
|
||||
var forced = this.Q & 0x0040; // Q07F: Hardware-induced SFI syllable
|
||||
@@ -902,7 +1095,7 @@ B5500Processor.storeForInterrupt = function(forTest) {
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500Processor.initiate = function(forTest) {
|
||||
B5500Processor.prototype.initiate = function(forTest) {
|
||||
/* Initiates the processor from interrupt control words stored in the
|
||||
stack. Assumes the INCW is in A. "forTest" implies use from IFT */
|
||||
var saveAROF;
|
||||
@@ -998,6 +1191,106 @@ B5500Processor.initiate = function(forTest) {
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500Processor.prototype.singePrecisionAdd = function(subtract) {
|
||||
/* Adds the contents of the A register to the B register, leaving the result
|
||||
in B and invalidating A. If "subtract" is true, the sign of A is complemented
|
||||
to accomplish subtraction instead of addition. 42-bit arithmetic is done on
|
||||
the mantissas to allow for a rounding digit in the low-order octade (in the
|
||||
hardware, shifts off the low-order end of the word went into the X register) */
|
||||
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 sa; // mantissa sign of A (0=positive)
|
||||
var sb; // mantissa sign of B (ditto)
|
||||
|
||||
this.adjustABFull();
|
||||
ma = this.A % 0x8000000000;
|
||||
mb = this.B % 0x8000000000;
|
||||
if (ma == 0) { // if A is zero, result is B
|
||||
this.B %= 0x800000000000; // reset the flag bit
|
||||
} else if (mb == 0) { // otherwise, if B is zero, result is A
|
||||
this.B = this.A % 0x800000000000;
|
||||
} else { // rats, we actually have to do this
|
||||
ea = (this.A - ma)/0x8000000000;
|
||||
sa = ((ea >>> 7) & 0x01) ^ (subtract ? 1 : 0);
|
||||
ea = (ea & 0x40 ? -(ea & 0x3F) : (ea & 0x3F));
|
||||
ma *= 8; // shift A left an octade to provide a guard digit
|
||||
|
||||
eb = (this.B - mb)/0x8000000000;
|
||||
sb = (eb >>> 7) & 0x01;
|
||||
eb = (eb & 0x40 ? -(eb & 0x3F) : (eb & 0x3F));
|
||||
mb *= 8; // shift B left an octade to provide a guard digit
|
||||
|
||||
// If the exponents are unequal, normalize the larger and scale the smaller
|
||||
// until they are in alignment, or one of the mantissas (mantissae?) becomes zero
|
||||
if (ea > eb) {
|
||||
// Normalize A
|
||||
while (ma < 0x8000000000 && ea != eb) {
|
||||
ma *= 8;
|
||||
ea--;
|
||||
}
|
||||
// Scale B until its exponent matches or mantissa goes to zero
|
||||
while (ea != eb && mb) {
|
||||
mb = (mb - mb%8)/8;
|
||||
eb++;
|
||||
}
|
||||
} else if (ea < eb) {
|
||||
// Normalize B
|
||||
while (mb < 0x8000000000 && eb != ea) {
|
||||
mb *= 8;
|
||||
eb--;
|
||||
}
|
||||
// Scale A until its exponent matches or mantissa goes to zero
|
||||
while (eb != ea && ma) {
|
||||
ma = (ma - ma%8)/8;
|
||||
ea++;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, the exponents are aligned (or one of the mantissas
|
||||
// is zero), so do the actual 42-bit addition
|
||||
mb = (sb ? -mb : mb) + (sa ? -ma : ma);
|
||||
|
||||
if (mb == 0) {
|
||||
this.B = 0;
|
||||
} else {
|
||||
// Determine the resulting sign
|
||||
if (mb >= 0) {
|
||||
sb = 0;
|
||||
} else {
|
||||
sb = 1;
|
||||
mb = -mb;
|
||||
}
|
||||
|
||||
// Round and normalize as necessary
|
||||
ma = mb % 8; // reuse ma for the guard digit;
|
||||
mb = (mb - ma)/8; // shift back to a 39-bit mantissa
|
||||
if (mb >= 0x8000000000) {
|
||||
ma = mb % 8;
|
||||
mb = (mb - ma)/8; // renormalize due to overflow
|
||||
eb++;
|
||||
}
|
||||
mb += (ma >>> 2); // add in the rounding bit (what about overflow ??)
|
||||
|
||||
// Check for exponent overflow
|
||||
if (eb > 63) {
|
||||
eb %= 64;
|
||||
if (this.NCSF) {
|
||||
this.I = (this.I & 0x0F) | 0xB0; // set I05/6/8: exponent-overflow
|
||||
this.cc.signalInterrupt();
|
||||
}
|
||||
} else if (eb < 0) {
|
||||
eb = (-eb) | 0x40; // set the exponent sign bit
|
||||
}
|
||||
|
||||
this.B = (sb*64 + eb)*0x8000000000 + mb; // Final Answer
|
||||
}
|
||||
}
|
||||
this.AROF = 0;
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500Processor.prototype.computeRelativeAddr = function(offset, cEnabled) {
|
||||
/* Computes an absolute memory address from the relative "offset" parameter
|
||||
@@ -1064,7 +1357,7 @@ B5500Processor.prototype.indexDescriptor = function() {
|
||||
|
||||
// Normalize the index, if necessary
|
||||
if (xe > 0) { // index is not an integer
|
||||
if (this.cc.bit(bw, 2)) { // index exponent is negative
|
||||
if (this.cc.bit(bw, 2)) { // index exponent is negative
|
||||
do {
|
||||
xo = xm % 8;
|
||||
xm = (xm - xo)/8;
|
||||
@@ -1710,9 +2003,8 @@ B5500Processor.prototype.run = function() {
|
||||
if (!this.AROF) {
|
||||
this.access(0x04); // A = [M]
|
||||
}
|
||||
t1 = B5500Processor.collate[this.cc.fieldIsolate(this.A, this.G*6, 6)];
|
||||
t2 = B5500Processor.collate[variant];
|
||||
this.MSFF = (t1 == t2 ? 1 : 0);
|
||||
t1 = this.cc.fieldIsolate(this.A, this.G*6, 6);
|
||||
this.MSFF = (t1 == variant ? 1 : 0);
|
||||
break;
|
||||
|
||||
case 0x15: // XX25: TNE=Test for not equal
|
||||
@@ -1720,9 +2012,8 @@ B5500Processor.prototype.run = function() {
|
||||
if (!this.AROF) {
|
||||
this.access(0x04); // A = [M]
|
||||
}
|
||||
t1 = B5500Processor.collate[this.cc.fieldIsolate(this.A, this.G*6, 6)];
|
||||
t2 = B5500Processor.collate[variant];
|
||||
this.MSFF = (t1 != t2 ? 1 : 0);
|
||||
t1 = this.cc.fieldIsolate(this.A, this.G*6, 6);
|
||||
this.MSFF = (t1 != variant ? 1 : 0);
|
||||
break;
|
||||
|
||||
case 0x16: // XX26: TEG=Test for equal or greater
|
||||
@@ -1768,9 +2059,11 @@ B5500Processor.prototype.run = function() {
|
||||
break;
|
||||
|
||||
case 0x1A: // XX32: ---=Field subtract (aux) !! ??
|
||||
this.fieldArithmetic(variant, true);
|
||||
break;
|
||||
|
||||
case 0x1B: // XX33: ---=Field add (aux) !! ??
|
||||
this.fieldArithmetic(variant, false);
|
||||
break;
|
||||
|
||||
case 0x1C: // XX34: TEL=Test for equal or less
|
||||
@@ -1824,6 +2117,22 @@ B5500Processor.prototype.run = function() {
|
||||
break;
|
||||
|
||||
case 0x21: // XX41: STC=Store TALLY
|
||||
this.cycleCount += variant;
|
||||
this.A = this.B; // save B
|
||||
this.AROF = 0; // invalidate A
|
||||
this.B = this.F; // save RCW address in B (why??)
|
||||
if (this.BROF) {
|
||||
this.access(0x0A); // [S] = A, save original B contents
|
||||
this.BROF = 0;
|
||||
}
|
||||
this.A = this.B; // move saved F address to A (why??)
|
||||
this.B = this.R; // copy the TALLY value to B
|
||||
t1 = this.S; // save S (not the way the hardware did it)
|
||||
this.S = this.F - variant;
|
||||
this.access(0x0B); // [S] = B, store the TALLY value
|
||||
this.B = this.A; // restore F address from A (why??)
|
||||
this.S = t1; // restore S
|
||||
this.BROF = 0; // invalidate B
|
||||
break;
|
||||
|
||||
case 0x22: // XX42: SEC=Set TALLY
|
||||
@@ -2038,15 +2347,18 @@ B5500Processor.prototype.run = function() {
|
||||
break;
|
||||
|
||||
case 0x34: // XX64: BIS=Set bit
|
||||
this.streamBitsToDest(variant, 0xFFFFFFFFFFFF);
|
||||
break;
|
||||
|
||||
case 0x35: // XX65: BIR=Reset bit
|
||||
this.streamBitsToDest(variant, 0);
|
||||
break;
|
||||
|
||||
case 0x36: // XX66: OCV=Output convert
|
||||
break;
|
||||
|
||||
case 0x37: // XX67: ICV=Input convert
|
||||
this.streamInputConvert(variant);
|
||||
break;
|
||||
|
||||
case 0x38: // XX70: CEL=Compare equal or less
|
||||
@@ -2170,9 +2482,11 @@ B5500Processor.prototype.run = function() {
|
||||
case 0x01: // XX01: single-precision numerics
|
||||
switch (variant) {
|
||||
case 0x01: // 0101: ADD=single-precision add
|
||||
this.singlePrecisionAdd(false);
|
||||
break;
|
||||
|
||||
case 0x03: // 0301: SUB=single-precision subtract
|
||||
this.singlePrecisionAdd(true);
|
||||
break;
|
||||
|
||||
case 0x04: // 0401: MUL=single-precision multiply
|
||||
|
||||
Reference in New Issue
Block a user