From 37b41cb0fb2d2ae564170ea8476d78a402274cc9 Mon Sep 17 00:00:00 2001 From: paul Date: Mon, 10 Dec 2012 00:57:17 +0000 Subject: [PATCH] Continue initial I/O Unit development. --- emulator/B5500CentralControl.js | 78 +++++----- emulator/B5500IOUnit.js | 260 ++++++++++++++++++++++++++++++-- emulator/B5500Processor.js | 57 +++++-- 3 files changed, 323 insertions(+), 72 deletions(-) diff --git a/emulator/B5500CentralControl.js b/emulator/B5500CentralControl.js index 2953f72..47bd03d 100644 --- a/emulator/B5500CentralControl.js +++ b/emulator/B5500CentralControl.js @@ -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(); }; /**************************************/ diff --git a/emulator/B5500IOUnit.js b/emulator/B5500IOUnit.js index bb00a3a..30ab0ac 100644 --- a/emulator/B5500IOUnit.js +++ b/emulator/B5500IOUnit.js @@ -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 } } } diff --git a/emulator/B5500Processor.js b/emulator/B5500Processor.js index 1a018a3..f553a9b 100644 --- a/emulator/B5500Processor.js +++ b/emulator/B5500Processor.js @@ -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;