1
0
mirror of https://github.com/pkimpel/retro-b5500.git synced 2026-02-12 03:07:30 +00:00

Continue initial I/O Unit development.

This commit is contained in:
paul
2012-12-10 00:57:17 +00:00
parent a55a6779ab
commit 37b41cb0fb
3 changed files with 323 additions and 72 deletions

View File

@@ -87,6 +87,20 @@ B5500CentralControl.mask2 = [ // (2**n)-1 For n From 0 to 52
0x0FFFFFFFFFFFF, 0x1FFFFFFFFFFFF, 0x3FFFFFFFFFFFF, 0x7FFFFFFFFFFFF,
0x0FFFFFFFFFFFFF] ;
// The following two-dimensional array translates unit designates to a unique 1-relative
// peripheral unit index. This index is the same as the unit's ready-status bit number,
// which is why they are in the range 17..47. The [0] dimension determines the index
// when writing; the [1] dimension determines the index when reading. This approach
// is necessary since some unit designates map to two different devices depending
// on IOD.[24:1], e.g. designate 14=CPA/CRA (status bits 23/24).
B5500CentralControl.unitIndex = [
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
[null, 47,null, 46, 31, 45, 29, 44, 30, 43, 25, 42, 28, 41,null, 40,
17, 39, 21, 38, 18, 37, 27, 36,null, 35, 26, 34,null, 33, 22, 32],
[null, 47,null, 46, 31, 45, 29, 44, 30, 43, 24, 42, 28, 41, 23, 40,
17, 39, 20, 38, 19, 37,null, 36,null, 35,null, 34,null, 33, 22, 32]];
/**************************************/
B5500CentralControl.prototype.clear = function() {
/* Initializes (and if necessary, creates) the system and starts the
@@ -478,36 +492,37 @@ B5500CentralControl.prototype.tock = function tock() {
} else {
that.TM = 0;
that.CCI03F = 1; // set timer interrupt
// inhibit for now // that.signalInterrupt();
// >>>>> inhibit for now >>>>> that.signalInterrupt();
}
interval = (that.nextTimeStamp += B5500CentralControl.rtcTick) - thisTime;
that.timer = setTimeout(function() {that.tock()}, (interval < 0 ? 1 : interval));
};
/**************************************/
B5500CentralControl.prototype.haltP2 = function() {
/* Called by P1 to halt P2. storeForInterrupt() will set P2BF=0 */
this.HP2F = 1;
// We know P2 is not currently running on this thread, so save its registers
if (this.P2 && this.P2BF) {
this.P2.storeForInterrupt(0);
}
};
/**************************************/
B5500CentralControl.prototype.initiateP2 = function() {
/* Called by P1 to initiate P2. Assumes that an INCW has been stored at
memory location @10. If P2 is busy or not present, sets the P2 busy
interrupt. Otherwise, loads the INCW into P2's A register and initiates
the processor. */
var p2 = this.P2;
if (!this.P2 || this.P2BF) {
this.CCI12F = 1; // set P2 busy interrupt
this.signalInterrupt();
} else {
p2.M = 8; // Now have P2 pick up the INCW
p2.access(0x04); // A = [M]
p2.AROF = 1;
p2.T = 0x849; // inject 4111=IP1 into P2's T register
p2.TROF = 1;
p2.NCSF = 0; // make sure P2 is in control state
this.P2BF = 1;
this.HP2F = 0;
// Now start scheduling P2 on the Javascript thread
p2.procTime = new Date().getTime()*1000;
p2.scheduler = setTimeout(p2.schedule, 0);
this.P2.initiateAsP2();
}
};
@@ -578,21 +593,13 @@ B5500CentralControl.prototype.testUnitBusy = function(ioUnit, unit) {
};
/**************************************/
B5500CentralControl.prototype.testUnitReady = function(unit) {
B5500CentralControl.prototype.testUnitReady = function(rw, unit) {
/* Determines whether the unit designate "unit" is currently in ready status.
Returns 0 if not ready */
"rw" indicates whether the designate is for writing (0) or reading (1).
Returns 1 if ready, 0 if not ready */
var index = B5500CentralControl.unitIndex[rw & 1][unit & 0x1F];
if (ioUnit != "1" && this.IO1 && this.IO1.REMF && this.AD1F && this.IO1.Dunit == unit) {
return 1;
} else if (ioUnit != "2" && this.IO2 && this.IO2.REMF && this.AD2F && this.IO2.Dunit == unit) {
return 2;
} else if (ioUnit != "3" && this.IO3 && this.IO3.REMF && this.AD3F && this.IO3.Dunit == unit) {
return 3;
} else if (ioUnit != "4" && this.IO4 && this.IO4.REMF && this.AD4F && this.IO4.Dunit == unit) {
return 4;
} else {
return 0; // peripheral unit not in use by any other I/O Unit
}
return (index ? this.bit(this.unitStatusMask, index) : 0);
};
/**************************************/
@@ -653,15 +660,7 @@ B5500CentralControl.prototype.loadComplete = function loadComplete() {
if (completed) {
that.loadTimer = null;
that.LOFF = 0;
that.P1.C = 0x10; // execute from address @20
that.P1.access(0x30); // P = [C]
that.P1.T = that.fieldIsolate(that.P, 0, 12);
that.P1.TROF = 1;
that.P1.L = 1; // advance L to the next syllable
// Now start scheduling P1 on the Javascript thread
that.P1.procTime = new Date().getTime()*1000;
that.P1.scheduler = setTimeout(that.P1.schedule, 0);
that.P1.start();
}
};
@@ -752,16 +751,7 @@ B5500CentralControl.prototype.runTest = function(runAddr) {
this.clear();
this.loadTimer = null;
this.LOFF = 0;
this.P1.C = runAddr; // starting execution address
this.P1.access(0x30); // P = [C]
this.P1.T = this.fieldIsolate(this.P1.P, 0, 12);
this.P1.TROF = 1;
this.P1.L = 1; // advance L to the next syllable
// Now start scheduling P1 on the Javascript thread
this.busy = 1;
this.P1.procTime = new Date().getTime()*1000;
this.P1.scheduler = setTimeout(this.P1.schedule, 0);
this.P1.start();
};
/**************************************/

View File

@@ -44,11 +44,10 @@ function B5500IOUnit(ioUnitID, cc) {
this.ioUnitID = ioUnitID; // I/O Unit ID ("1", "2", "3", or "4")
this.cc = cc; // Reference back to Central Control module
this.rdAddress = {"1": 0x0C, "2": 0x0D, "3": 0x0E, "4": 0x0F}[ioUnitID];
this.scheduler = null; // Reference to current setTimeout id
this.accessor = { // Memory access control block
requestorID: procID, // Memory requestor ID
requestorID: ioUnitID, // Memory requestor ID
addr: 0, // Memory address
word: 0, // 48-bit data word
MAIL: 0, // Truthy if attempt to access @000-@777 in normal state
@@ -61,7 +60,7 @@ function B5500IOUnit(ioUnitID, cc) {
// Establish a buffer for the peripheral modules to use during their I/O.
// The size is sufficient for 63 disk sectors, rounded up to the next 8KB.
this.bufferArea = new ArrayBuffer(16384);
this.buffer = new UInt8Array(bufferArea);
this.buffer = new Uint8Array(this.bufferArea);
this.clear(); // Create and initialize the processor state
}
@@ -217,6 +216,180 @@ B5500IOUnit.prototype.store = function(addr) {
}
};
/**************************************/
B5500IOUnit.prototype.fetchBuffer = function(mode, words) {
/* Fetches words from memory starting at this.Daddress and coverts the
BIC characters to ANSI in this.buffer. "mode": 0=alpha, 1=binary;
"words": maximum number of words to transfer. In alpha mode, the transfer
can be terminated by a group-mark code in memory. At exit, updates this.Daddress
with the final transfer address+1. If this.D23F, updates this.wordCount
with any remaining count.
Returns the number of characters fetched into the buffer */
var addr = this.Daddress; // local copy of memory address
var buf = this.buffer; // local pointer to buffer
var c; // current character code
var count = 0; // number of characters fetched
var done = false; // loop control
var s; // character shift counter
var w; // local copy of this.W
do { // loop through the words
if (words <= 0) {
done = true;
} else {
words--;
if (addr > 0x7FFF) {
this.D26F = 1; // address overflow: set invalid address error
done = true;
} else if (this.fetch(addr)) { // fetch the next word from memory
if (this.accessor.MAED) {
this.D26F = 1; // set invalid address error
}
if (this.accessor.MPED) {
this.D29F = 1; // set memory parity error
}
} else { // fill the buffer with this word's characters
w = this.W;
for (s=0; s<8; s++) {
c = (w - (w %= 0x40000000000))/0x40000000000;
if (mode || c != 0x1F) { // if binary mode or not a group-mark
buf[count++] = B5500IOUnit.BICtoANSI[c];
w *= 64; // shift word left 6 bits
} else {
done = true; // group-mark detected in alpha mode
break;
}
} // for s
}
addr++;
}
} while (!done);
this.Daddress = addr % 0x7FFF;
if (this.D23F) {
this.DwordCount = words % 0x1FF;
}
return count;
};
/**************************************/
B5500IOUnit.prototype.storeBuffer = function(chars, offset, mode, words) {
/* Converts characters in this.buffer from ANSI to BIC, assembles them into
words, and stores the words into memory starting at this.Daddress.
BIC characters to ANSI in this.buffer. "chars": the number of characters to
stort, starting at "offset" in the buffer; "mode": 0=alpha, 1=binary;
"words": maximum number of words to transfer. In alpha mode, the final character
stored from the buffer is followed by a group-mark, assuming the word count is
not exhausted. At exit, updates this.Daddress with the final transfer address+1.
If this.D23F, updates this.wordCount with any remaining count.
Returns the number of characters stored into memory from the buffer */
var addr = this.Daddress; // local copy of memory address
var buf = this.buffer; // local pointer to buffer
var c; // current character code
var count = 0; // number of characters fetched
var done = (words > 0); // loop control
var power = 0x40000000000; // factor for character shifting into a word
var s = 8; // character shift counter
var w = 0; // local copy of this.W
while (!done) { // loop through the words
if (count >= chars) {
done = true;
} else {
c = B5500IOUnit.ANSItoBIC[buf[offset+(count++)]];
w += c*power;
power /= 64;
if (--s <= 0) {
this.W = w;
if (addr > 0x7FFF) {
this.D26F = 1; // address overflow: set invalid address error
done = true;
} else if (this.store(addr)) { // store the word in memory
if (this.accessor.MAED) {
this.D26F = 1; // set invalid address error
}
}
addr++;
s = 8;
w = 0;
power = 0x40000000000;
if (--words <= 0) {
done = true;
}
}
}
} // while !done
if (!mode) { // alpha transfer terminates with a group-mark
w += 0x1F*power; // set group mark in register
s--;
count++;
}
if (s < 8 && words > 0) { // partial word left to be stored
this.W = w;
if (addr > 0x7FFF) {
this.D26F = 1; // address overflow: set invalid address error
done = true;
} else if (this.store(addr)) { // store the word in memory
if (this.accessor.MAED) {
this.D26F = 1; // set invalid address error
}
}
addr++;
words--;
}
this.Daddress = addr % 0x7FFF;
if (this.D23F) {
this.DwordCount = words % 0x1FF;
}
return count;
};
/**************************************/
B5500IOUnit.prototype.finish = function () {
/* Called to finish an I/O operation on this I/O Unit. Constructs and stores
the result descriptor, sets the appropriate I/O Finished interrupt in CC */
this.W = this.D =
this.Dunit * 0x10000000000 +
this.DwordCount * 0x40000000 +
this.D18F * 0x20000000 +
this.D21F * 0x4000000 +
this.D22F * 0x2000000 +
this.D23F * 0x1000000 +
this.D24F * 0x800000 +
this.D26F * 0x200000 +
this.D27F * 0x100000 +
this.D28F * 0x80000 +
this.D29F * 0x40000 +
this.D30F * 0x20000 +
this.D31F * 0x10000 +
this.D32F * 0x8000 +
this.Daddressf;
switch(ioUnitID) {
case "1":
this.store(0x0C);
this.cc.CCI08F = 1; // set I/O Finished #1
break;
case "2":
this.store(0x0D);
this.cc.CCI08F = 1; // set I/O Finished #2
break;
case "3":
this.store(0x0E);
this.cc.CCI08F = 1; // set I/O Finished #3
break;
case "4":
this.store(0x0F);
this.cc.CCI08F = 1; // set I/O Finished #4
break;
}
this.Dunit = 0; // zero so CC won't think unit is busy
};
/**************************************/
B5500IOUnit.prototype.initiate = function() {
/* Initiates an I/O operation on this I/O Unit */
@@ -227,32 +400,89 @@ B5500IOUnit.prototype.initiate = function() {
this.EXNF = 0;
this.D31F = 1; // preset IOD fetch error condition (cleared if successful)
if (this.fetch(0x08)) { // fetch the IOD address from @10
... return descriptor ...
this.finish();
} else {
this.EXNF = 1;
this.Daddress = addr = this.W % 0x7FFF;
if (this.fetch(addr)) { // fetch the IOD from that address
... return descriptor ...
this.finish();
} else {
this.D31F = 0; // reset the IOD-fetch error condition
this.D = x = this.W; // explode the D-register into its fields
this.Dunit = this.cc.fieldIsolate(x, 3, 5);
this.DwordCount = this.cc.fieldIsolate(x, 8, 10);
x = x % 0x40000000;
this.D18F = (x >>> 29) & 1;
this.D21F = (x >>> 26) & 1;
this.D22F = (x >>> 25) & 1;
this.D23F = (x >>> 24) & 1;
this.D24F = (x >>> 23) & 1;
x = x % 0x40000000; // isolate low-order 30 bits
this.D18F = (x >>> 29) & 1; // memory inhibit
this.D21F = (x >>> 26) & 1; // mode
this.D22F = (x >>> 25) & 1; // direction (for tapes)
this.D23F = (x >>> 24) & 1; // use word counter
this.D24F = (x >>> 23) & 1; // write/read
this.LP = (x >>> 15) & 0x3F;// save control bits for drum and printer
this.Daddress = x % 0x7FFF;
if (this.cc.testUnitBusy(this.ioUnitID, this.Dunit)) {
this.D32F = 1; // set unit busy error
... return descriptor ...
} else if (!this.cc.testUnitReady(this.Dunit)) {
this.finish();
} else if (!this.cc.testUnitReady(this.D24F, this.Dunit)) {
this.D30F = 1; // set unit not-ready error
... return descriptor ...
this.finish();
} else {
switch(this.Dunit) {
// disk designates
case 6:
case 12:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// printer designates
case 22:
case 26:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// datacom designate
case 16:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// card #1 reader/punch
case 10:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// card #2 reader
case 14:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// SPO designate
case 30:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// magnetic tape designates
case 1: case 3: case 5: case 7: case 9: case 11: case 13: case 15:
case 17: case 19: case 21: case 23: case 25: case 27: case 29: case 31:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// drum designates
case 4:
case 8:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// paper tape designates
case 18:
case 20:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// illegal designates
default:
this.D30F = 1; // report invalid unit as not ready
this.finish();
break;
} // switch this.Dunit
}
}
}

View File

@@ -1061,11 +1061,11 @@ B5500Processor.prototype.storeForInterrupt = function(forTest) {
this.TROF = 0;
this.PROF = 0;
this.busy = 0;
this.cc.HP2F = 1;
this.cc.P2BF = 0;
if (this.cc.P2 && this.cc.P2.scheduler) {
clearTimeout(this.cc.P2.scheduler);
this.cc.P2.scheduler = null;
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;
@@ -1086,15 +1086,31 @@ B5500Processor.prototype.storeForInterrupt = function(forTest) {
this.PROF = 0;
this.busy = 0;
this.cc.HP2F = 1;
this.cc.P2BF = 0;
if (this.cc.P2 && this.cc.P2.scheduler) {
clearTimeout(this.cc.P2.scheduler);
this.cc.P2.scheduler = null;
this.cc.P2BF = 0; // tell P1 we've stopped
if (this.scheduler) {
clearTimeout(this.scheduler);
this.scheduler = null;
}
}
}
};
/**************************************/
B5500Processor.prototype.start = function() {
/* Initiates the processor from a load condition at P=@20 */
this.C = runAddr; // starting execution address
this.access(0x30); // P = [C]
this.T = this.fieldIsolate(this.P, 0, 12);
this.TROF = 1;
this.L = 1; // advance L to the next syllable
// Now start scheduling the processor on the Javascript thread
this.busy = 1;
this.procTime = new Date().getTime()*1000;
this.scheduler = setTimeout(this.schedule, 0);
};
/**************************************/
B5500Processor.prototype.initiate = function(forTest) {
/* Initiates the processor from interrupt control words stored in the
@@ -1192,6 +1208,23 @@ B5500Processor.prototype.initiate = function(forTest) {
}
};
/**************************************/
B5500Processor.prototype.initiateAsP2 = function() {
/* Called from Central Control to initiate the processor as P2. Fetches the
INCW from @10 and calls initiate() */
this.M = 0x08; // address of the INCW
this.access(0x04); // A = [M]
this.AROF = 1;
this.T = 0x849; // inject 4111=IP1 into P2's T register
this.TROF = 1;
this.NCSF = 0; // make sure P2 is in control state
// Now start scheduling P2 on the Javascript thread
this.procTime = new Date().getTime()*1000;
this.scheduler = setTimeout(this.schedule, 0);
};
/**************************************/
B5500Processor.prototype.singlePrecisionCompare = function() {
/* Algebraically compares the B register to the A register. Function returns
@@ -3322,10 +3355,8 @@ B5500Processor.prototype.run = function() {
break;
case 0x12: // 2211: HP2=Halt Processor 2
if (!this.NCSF && this.cc.P2 && this.cc.P2BF) { // control-state only
this.cc.HP2F = 1;
// We know P2 is not currently running on this thread, so save its registers
this.cc.P2.storeForInterrupt(0);
if (!this.NCSF) { // control-state only
this.cc.haltP2();
}
break;