diff --git a/SYMBOL/DCMCP.esp_m b/SYMBOL/DCMCP.esp_m index d57a7e2..1376436 100644 --- a/SYMBOL/DCMCP.esp_m +++ b/SYMBOL/DCMCP.esp_m @@ -2960,7 +2960,7 @@ START: 04372200 MAKEMESS; 04376400 SPOUTER(KEY,UNITNO,35); 04376600 END; 04376800 - MAKEMEMLOG(IF TYPE=2 THEN 10 ELSE 5); 04377000 + MAKEMLOG(IF TYPE=2 THEN 10 ELSE 5); 04377000 L1: DO BEGIN 04377200 SLEEP([CLOCK],NOT CLOCK); 04377400 UNIT[U]:=(*P(DUP))&P(T,XCH)[CTC]; 04377600 @@ -3220,3 +3220,49 @@ LEAVE: 04427600 FINISHOFFIO(U); 04428800 END; 04429000 IF ( T1:= FIN) LSS 0 THEN 04429200 + P(R&E[25:40:8]&IOD[3:3:5] OR IOMASK,LOCATQUE[S],~); 04429400 + ELSE 04429600 + BEGIN 04429800 + IF E NEQ 0 THEN 04430000 + BEGIN 04430200 + P(.T1,PRL); 04430400 + T1 := T1&E25:40:8]; 04430600 + END 04430800 + ELSE P(.T,IOR); 04431000 + LOCN := [M[LOCATQUE[S]]; 04431200 + IOD := IOD.[33:15]; 04431400 + WHILE LOCN[0].[33:15] NEQ IOD DO LOCN := 1 INX LOCN; 04431600 + LOCN[0] := P(.T1,LOD); 04431800 + END; 04432000 + UNIT[U] := T; 04432200 +CLEAR: 04432400 + UNIT[U] := (*P(DUP))&F[5:20:13]; 04432600 + STARTIO(U); 04432800 +KILLL: 04433000 + LOCATQUE[S].[11:1]:=0; 04433200 +KILLER: 04433400 +KILL([MSCW]); 04433600 + END; 04433800 + $ SET OMIT = NOT DEBUGGING 04544999 +REAL PROCEDURE TAPEPARITYRETRY(R,U,KEY);% 04548000 + VALUE R,U,KEY;% 04549000 + REAL R,U,KEY;% 04550000 + BEGIN REAL T1,T2,T3; INTEGER I= T1;% 04551000 + REAL RESULT,IOD,OIOD,SPACEMASK,SPACEIOD,M,N,W,MODE;% 04552000 + REAL J,K;% 04553000 + REAL ERASEIOD=SPACEMASK;% 04554000 + REAL Z,Y,MIX,BSIZE; 04554100 + LABEL XIO,GIVEUP; 04554200 + LABEL RP,LX; 04554300 + REAL SIZE,T4,LIMIT; 04554500 + REAL PTR,BUFFER,BUFFERSIZE,% 04554600 + PATTERN,PATTERN1,PATTERN2,PATTERNWORD;% DON"T CHANGE ORDER04554700 + BOOLEAN TESTING,SPACING,FLAGGER; 04554800 + $ SET OMIT = NOT(PACKETS) 04554899 + DEFINE UNITNO = PSEUDOMIX[MIX]#; 04554900 + $ POP OMIT 04554901 + LABEL XXIT,EXIT,ENDIT,XEXIT; 04555000 + SUBROUTINE RECORDRETRY;% 04555050 + BEGIN% 04555100 + IF PTR-KEY = TAPEBUFFERSIZE-1 THEN% 04555150 + BEGIN% 04555200 diff --git a/emulator/B5500CentralControl.js b/emulator/B5500CentralControl.js index 1903e3a..18e09b9 100644 --- a/emulator/B5500CentralControl.js +++ b/emulator/B5500CentralControl.js @@ -16,69 +16,179 @@ function B5500CentralControl() { /* Global system modules */ - this.PA = null; // Processor A (PA) - this.PB = null; // Processor B (PB) - this.IO1 = null; // I/O unit 1 - this.IO2 = null; // I/O unit 2 - this.IO3 = null; // I/O unit 3 - this.IO4 = null; // I/O unit 4 + this.PA = null; // Processor A (PA) + this.PB = null; // Processor B (PB) + this.IO1 = null; // I/O unit 1 + this.IO2 = null; // I/O unit 2 + this.IO3 = null; // I/O unit 3 + this.IO4 = null; // I/O unit 4 - this.P1 = null; // Reference for Processor 1 (control) [PA or PB] - this.P1 = null; // Reference for Processor 2 (slave) [PA or PB] + this.P1 = null; // Reference for Processor 1 (control) [PA or PB] + this.P1 = null; // Reference for Processor 2 (slave) [PA or PB] - this.AddressSpace = []; // Array of memory module address spaces (8 x 32KB each) - this.Memory = []; // Array of memory module words as Float64s (8 x 4KW each) + this.AddressSpace = []; // Array of memory module address spaces (8 x 32KB each) + this.Memory = []; // Array of memory module words as Float64s (8 x 4KW each) // This memory instantiation should be done in configuration, but here's the idea... this.AddressSpace[0] = new ArrayBuffer(32768); this.Memory[0] = new Float64Array(this.AddressSpace[0]); - /* Central Control registers and flip flops */ + this.PB1L = 0; // 0=> PA is P1, 1=> PB is P1 + this.cardLoadSelect = 0; // 0=> load from disk/drum; 1=> load from cards - this.IAR = 0; // Interrupt address register - this.TM = 0; // Real-time clock (6 bits, 60 ticks per second) + this.rtcTick = 1000/60; // Real-time clock period, milliseconds + this.nextTimeStamp = 0; // Next actual Date.getTime() expected + this.timer = null; // Reference to the RTC setTimeout id. + this.loadTimer = null; // Reference to the load setTimeout id. - this.CCI03F = 0; // Time interval interrupt - this.CCI04F = 0; // I/O busy interrupt - this.CCI05F = 0; // Keyboard request interrupt - this.CCI06F = 0; // Printer 1 finished interrupt - this.CCI07F = 0; // Printer 2 finished interrupt - this.CCI08F = 0; // I/O unit 1 finished interrupt (RD in @14) - this.CCI09F = 0; // I/O unit 2 finished interrupt (RD in @15) - this.CCI10F = 0; // I/O unit 3 finished interrupt (RD in @16) - this.CCI11F = 0; // I/O unit 4 finished interrupt (RD in @17) - this.CCI12F = 0; // P2 busy interrupt - this.CCI13F = 0; // Remote inquiry request interrupt - this.CCI14F = 0; // Special interrupt #1 (not used) - this.CCI15F = 0; // Disk file #1 read check finished - this.CCI16F = 0; // Disk file #2 read check finished - - this.MCYF = 0; // Memory cycle FFs (one bit per M0..M7) - this.PAXF = 0; // PA memory exchange select (M0..M7) - this.PBXF = 0; // PB memory exchange select (M0..M7) - this.I1XF = 0; // I/O unit 1 exchange select (M0..M7) - this.I2XF = 0; // I/O unit 2 exchange select (M0..M7) - this.I3XF = 0; // I/O unit 3 exchange select (M0..M7) - this.I4XF = 0; // I/O unit 4 exchange select (M0..M7) - - this.AD1F = 0; // I/O unit 1 busy - this.AD2F = 0; // I/O unit 2 busy - this.AD3F = 0; // I/O unit 3 busy - this.AD4F = 0; // I/O unit 4 busy - - this.LOFF = 0; // Load button pressed on console - this.CTMF = 0; // Commence timing FF - this.P2BF = 0; // Processor 2 busy FF - this.HP2F = 0; // Halt processor 2 FF - this.PB1L = 0; // 0=> PA is P1, 1=> PB is P1 - - - this.rtcTick = 1000/60; // Real-time clock period, milliseconds - this.nextTimeStamp = 0; // Next actual Date.getTime() expected + this.clear(); // Create and initialize the Central Control state } /**************************************/ -B5500CentralControl.prototype.fetch(r) { +B5500CentralControl.prototype.clear = function() { + /* Initializes the system and starts the real-time clock */ + + if (this.timer) { + clearTimeout(this.timer); + } + + this.nextTimeStamp = new Date().getTime() + this.rtcTick; + this.timer = setTimeout(this.tock, this.rtcTick); + + this.IAR = 0; // Interrupt address register + this.TM = 0; // Real-time clock (6 bits, 60 ticks per second) + + this.CCI03F = 0; // Time interval interrupt + this.CCI04F = 0; // I/O busy interrupt + this.CCI05F = 0; // Keyboard request interrupt + this.CCI06F = 0; // Printer 1 finished interrupt + this.CCI07F = 0; // Printer 2 finished interrupt + this.CCI08F = 0; // I/O unit 1 finished interrupt (RD in @14) + this.CCI09F = 0; // I/O unit 2 finished interrupt (RD in @15) + this.CCI10F = 0; // I/O unit 3 finished interrupt (RD in @16) + this.CCI11F = 0; // I/O unit 4 finished interrupt (RD in @17) + this.CCI12F = 0; // P2 busy interrupt + this.CCI13F = 0; // Remote inquiry request interrupt + this.CCI14F = 0; // Special interrupt #1 (not used) + this.CCI15F = 0; // Disk file #1 read check finished + this.CCI16F = 0; // Disk file #2 read check finished + + this.MCYF = 0; // Memory cycle FFs (one bit per M0..M7) + this.PAXF = 0; // PA memory exchange select (M0..M7) + this.PBXF = 0; // PB memory exchange select (M0..M7) + this.I1XF = 0; // I/O unit 1 exchange select (M0..M7) + this.I2XF = 0; // I/O unit 2 exchange select (M0..M7) + this.I3XF = 0; // I/O unit 3 exchange select (M0..M7) + this.I4XF = 0; // I/O unit 4 exchange select (M0..M7) + + this.AD1F = 0; // I/O unit 1 busy + this.AD2F = 0; // I/O unit 2 busy + this.AD3F = 0; // I/O unit 3 busy + this.AD4F = 0; // I/O unit 4 busy + + this.LOFF = 0; // Load button pressed on console + this.CTMF = 0; // Commence timing FF + this.P2BF = 0; // Processor 2 busy FF + this.HP2F = 1; // Halt processor 2 FF + + if (this.PA) { + this.PA.clear(); + } + if (this.PB) { + this.PB.clear(); + } + this.P1 = (this.PB1L ? PB : PA); + this.P2 = (this.PB1L ? PA : PB); + if (!this.P2) { + this.P2BF = 1; // mark non-existent P2 as busy + } +} + +/**************************************/ +B5500CentralControl.prototype.pow2 = [ // powers of 2 from 0 to 52 + 1, 2, 4, 8, + 16, 32, 64, 128, + 256, 512, 1024, 2048, + 4096, 8192, 16384, 32768, + 65536, 131072, 262144, 524288, + 1048576, 2097152, 4194304, 8388608, + 16777216, 33554432, 67108864, 134217728, + 268435456, 536870912, 1073741824, 2147483648, + 4294967296, 8589934592, 17179869184, 34359738368, + 68719476736, 137438953472, 274877906944, 549755813888, + 1099511627776, 2199023255552, 4398046511104, 8796093022208, + 17592186044416, 35184372088832, 70368744177664, 140737488355328, + 281474976710656, 562949953421312, 1125899906842624, 2251799813685248, + 4503599627370496]; + +B5500CentralControl.prototype.mask2 = [ // (2**n)-1 for n from 0 to 52 + 0, 1, 3, 7, + 15, 31, 63, 127, + 255, 511, 1023, 2047, + 4095, 8191, 16383, 32767, + 65535, 131071, 262143, 524287, + 1048575, 2097151, 4194303, 8388607, + 16777215, 33554431, 67108863, 134217727, + 268435455, 536870911, 1073741823, 2147483647, + 4294967295, 8589934591, 17179869183, 34359738367, + 68719476735, 137438953471, 274877906943, 549755813887, + 1099511627775, 2199023255551, 4398046511103, 8796093022207, + 17592186044415, 35184372088831, 70368744177663, 140737488355327, + 281474976710655, 562949953421311, 1125899906842623, 2251799813685247, + 4503599627370495]; + +/**************************************/ +B5500CentralControl.prototype.bit = function(word, bit) { + /* Extracts and returns the specified bit from the word */ + var e = 47-bit; + + return (e > 0 ? Math.floor(word/this.pow2[e]) : word) % 2; +} + +/**************************************/ +B5500CentralControl.prototype.bitSet = function(word, bit) { + /* Sets the specified bit in word and returns the updated word */ + + return this.insert(word, bit, 1, 1); +} + +/**************************************/ +B5500CentralControl.prototype.bitReset = function(word, bit) { + /* Resets the specified bit in word and returns the updated word */ + + return this.insert(word, bit, 1, 0); +} + +/**************************************/ +B5500CentralControl.prototype.isolate = function(word, start, width) { + /* Extracts a bit field [start:width] from word and returns the field */ + var ue = 48-start; // upper power exponent + var le = ue-width; // lower power exponent + + return (le > 0 ? Math.floor(word/this.pow2[le]) : word) % this.pow2[width]; +} + +/**************************************/ +B5500CentralControl.prototype.insert = function(word, start, width, value) { + /* Inserts a value into word.[start:width] and returns the updated word */ + var ue = 48-start; // upper power exponent + var le = ue-width; // lower power exponent + var bpower = 1; // bottom portion power of 2 + var bottom = 0; // unaffected bottom portion of word + var top = 0; // unaffected top portion of word + + if (start > 0) { + top = word - (word % this.pow2[ue]); + } + if (le > 0) { + bpower = this.pow2[le]; + bottom = word % bpower; + } + return (value % this.pow2[width])*bpower + top + bottom; +} + +/**************************************/ +B5500CentralControl.prototype.fetch = function(r) { /* Called by requestor module "r" to fetch a word from memory. */ var acer = r.accessor; var addr = acer.addr; @@ -87,7 +197,7 @@ B5500CentralControl.prototype.fetch(r) { var modMask = 1 << modNr; this.MCYF |= modMask; // !! need to figure out when to turn this off for display purposes - // (odd/even addresses? fetch vs. store?) + // (odd/even addresses? fetch vs. store? XOR the mask?) switch (r) { case PA: this.PAXF = modMask; @@ -111,18 +221,18 @@ B5500CentralControl.prototype.fetch(r) { // For now, we assume memory parity can never happen if (acer.MAIL || !this.Memory[modNr]) { - // acer.MPED = 0; - acer.MAED = 1; - acer.word = 0; - } else ( - // acer.MPED = 0; acer.MPED = 0; + acer.MAED = 1; + // no .word value is returned in this case + } else ( + acer.MPED = 0; + acer.MAED = 0; acer.word = this.Memory[memMod][modAddr]; } } /**************************************/ -B5500CentralControl.prototype.store(r, addr, word) { +B5500CentralControl.prototype.store = function(r, addr, word) { /* Called by requestor module "r" to store a word into memory. */ var acer = r.accessor var addr = acer.addr; @@ -131,7 +241,7 @@ B5500CentralControl.prototype.store(r, addr, word) { var modMask = 1 << modNr; this.MCYF |= modMask; // !! need to figure out when to turn this off for display purposes - // (odd/even addresses? fetch vs. store?) + // (odd/even addresses? fetch vs. store? XOR the mask?) switch (r) { case this.PA: this.PAXF = modMask; @@ -155,17 +265,17 @@ B5500CentralControl.prototype.store(r, addr, word) { // For now, we assume memory parity can never happen if (acer.MAIL || !this.Memory[modNr]) { - // acer.MPED = 0; + acer.MPED = 0; acer.MAED = 1; } else ( - // acer.MPED = 0; + acer.MPED = 0; acer.MAED = 0; this.Memory[memMod][modAddr] = acer.word; } } /**************************************/ -B5500CentralControl.prototype.signalInterrupt() { +B5500CentralControl.prototype.signalInterrupt = function() { /* Called by all modules to signal that an interrupt has occurred and to invoke the interrupt prioritization mechanism. This will result in an updated vector address in the IAR. Can also be called to reprioritize @@ -200,7 +310,7 @@ B5500CentralControl.prototype.signalInterrupt() { } /**************************************/ -B5500CentralControl.prototype.clearInterrupt(); +B5500CentralControl.prototype.clearInterrupt = function(); /* Resets an interrupt based on the current setting of this.IAR, then reprioritizes any remaining interrupts, leaving the new vector address in this.IAR. */ @@ -302,16 +412,8 @@ B5500CentralControl.prototype.clearInterrupt(); } /**************************************/ -B5500CentralControl.prototype.clear() { - /* Initializes the system and starts the real-time clock */ - - this.nextTimeStamp = new Date().getTime() + this.rtcTick; - setTimeout(this.tock, this.rtcTick); -} - -/**************************************/ -B5500CentralControl.prototype.tock() { - /* Handles the 1/60th second real-time clock increment */ +B5500CentralControl.prototype.tock = function() { + /* Handles the 1/60th second real-time clock tick */ var thisTime = new Date().getTime(); if (this.TM < 63) { @@ -323,22 +425,122 @@ B5500CentralControl.prototype.tock() { } this.nextTimeStamp += this.rtcTick; if (this.nextTimeStamp < thisTime) { - setTimeout(this.tock, 1); // try to catch up + this.timer = setTimeout(this.tock, 0); // try to catch up } else { - setTimeout(this.tock, this.nextTimeStamp-thisTime); + this.timer = setTimeout(this.tock, this.nextTimeStamp-thisTime); } } /**************************************/ -B5500CentralControl.prototype.halt() { - /* Halts the system */ +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. */ - // TO BE PROVIDED + if (!this.P2 || this.P2BF) { + this.CCI12F = 1; // set P2 busy interrupt + this.signalInterrupt(); + } else { + this.P2.M = 8; // Now have P2 pick up the INCW + this.P2.access(0x04); // A = [M] + this.P2.AROF = 1; + this.P2.T = 0x849; // inject 4111=IP1 into P2's T register + this.P2.TROF = 1; + this.P2.NCSF = 0; // make sure P2 is in control state + this.P2BF = 1; + this.HP2F = 0; + + // Now start scheduling P2 on the Javascript thread + this.P2.procTime = new Date().getTime()*1000; + this.P2.scheduler = setTimeout(this.P2.schedule, 0); + } } /**************************************/ -B5500CentralControl.prototype.load() { +B5500CentralControl.prototype.initiateIO = function() { + /* Selects an I/O unit and initiates an I/O */ + + if (this.IO1) { + this.AD1F = 1; + IO1.initiate(); + } else if (this.IO2) { + this.AD2F = 1; + IO2.initiate(); + } else if (this.IO3) { + this.AD3F = 1; + IO3.initiate(); + } else if (this.IO4) { + this.AD4F = 1; + IO4.initiate(); + } else { + this.CCI04F = 1; // set I/O busy interrupt + this.signalInterrupt(); + } +} + +/**************************************/ +B5500CentralControl.prototype.halt = function() { + /* Halts the processors. Any in-process I/Os are allowed to complete */ + + if (this.PA && this.PA.busy) { + this.PA.busy = false; + this.PA.cycleLimit = 0; + if (this.PA.scheduler) { + clearTimeout(this.PA.scheduler); + this.PA.scheduler = null; + } + } + + if (this.PB && this.PB.busy) { + this.PB.busy = false; + this.PB.cycleLimit = 0; + if (this.PB.scheduler) { + clearTimeout(this.PB.scheduler); + this.PB.scheduler = null; + } + } + + if (this.loadTimer) { + cancelTimeout(this.loadTimer); + this.loadTimer = null; + } +} + +/**************************************/ +B5500CentralControl.prototype.load = function() { /* Initiates a Load operation to start the system */ - // TO BE PROVIDED + if ((this.PA && this.PA.busy) || (this.PB && this.PB.busy)) { + this.clear(); + if (this.P1) { + this.LOFF = 1; + if (this.IO1) { // !! not sure about I/O selection here + IO1.initiateLoad(this.cardLoadSelect); + this.loadComplete(); + } + } + } +} + +/**************************************/ +B5500CentralControl.prototype.loadComplete = function() { + /* Monitors an initial load I/O operation for complete status. + When complete, initiates P1 */ + + if (!this.CCI08F) { + this.loadTimer = setTimeout(this.loadComplete, 10); + else { + this.loadTimer = null + this.LOFF = 0; + this.P1.C = 0x10; // execute from address @20 + this.P1.L = 0; + this.P1.access(0x30); // P = [C] + this.P1.T = Math.floor(this.P / 0x1000000000) % 0x1000; + this.P1.TROF = 1; + + // Now start scheduling P1 on the Javascript thread + this.P1.procTime = new Date().getTime()*1000; + this.P1.scheduler = setTimeout(this.P1.schedule, 0); + } } \ No newline at end of file diff --git a/emulator/B5500Processor.js b/emulator/B5500Processor.js index 9e59dc4..dd62feb 100644 --- a/emulator/B5500Processor.js +++ b/emulator/B5500Processor.js @@ -16,6 +16,21 @@ function B5500Processor() { this.timeSlice = 5000; // Standard run() timeslice, about 5ms (we hope) + this.scheduler = null; // Reference to current setTimeout id + this.accessor = { // Memory access control block + addr: 0, // Memory address + word: 0, // 48-bit data word + MAIL: 0, // Truthy if attempt to access @000-@777 in normal state + MPED: 0, // Truthy if memory parity error + MAED: 0}; // Truthy if memory address/inhibit error + + this.clear(); // Create and initialize the processor state +} + +/**************************************/ +B5500Processor.prototype.clear = function() { + /* Initializes the processor state */ + this.A = 0; // Top-of-stack register 1 this.AROF = 0; // A contents valid this.B = 0; // Top-of-stack register 2 @@ -56,19 +71,14 @@ function B5500Processor() { this.cycleCount = 0; // Current cycle count for this.run() this.cycleLimit = 0; // Cycle limit for this.run() + this.totalCycles = 0; // Total cycles executed on this processor this.procTime = 0; // Current processor running time, based on cycles executed + this.scheduleSlack = 0; // Total processor throttling delay, milliseconds this.busy = false; // Proessor is running, not idle or halted - - this.accessor = { // Memory access control block - addr: 0, // Memory address - word: 0, // 48-bit data word - MAIL: 0, // Truthy if attempt to access @000-@777 in normal state - MPED: 0, // Truthy if memory parity error - MAED: 0}; // Truthy if memory address/inhibit error } /**************************************/ -B5500Processor.prototype.access(eValue) { +B5500Processor.prototype.access = function(eValue) { /* Access memory based on the E register. If the processor is in normal state, it cannot access the first 512 words of memory => invalid address */ @@ -154,7 +164,7 @@ B5500Processor.prototype.access(eValue) { } /**************************************/ -B5500Processor.prototype.adjustAEmpty() { +B5500Processor.prototype.adjustAEmpty = function() { /* Adjusts the A register so that it is empty pushing the prior contents of A into B and B into memory, as necessary. */ @@ -176,7 +186,7 @@ B5500Processor.prototype.adjustAEmpty() { } /**************************************/ -B5500Processor.prototype.adjustAFull() { +B5500Processor.prototype.adjustAFull = function() { /* Adjusts the A register so that it is full, popping the contents of B or [S] into A, as necessary. */ @@ -194,7 +204,7 @@ B5500Processor.prototype.adjustAFull() { } /**************************************/ -B5500Processor.prototype.adjustBEmpty() { +B5500Processor.prototype.adjustBEmpty = function() { /* Adjusts the B register so that it is empty pushing the prior contents of B into memory, as necessary. */ @@ -211,7 +221,7 @@ B5500Processor.prototype.adjustBEmpty() { } /**************************************/ -B5500Processor.prototype.adjustBFull() { +B5500Processor.prototype.adjustBFull = function() { /* Adjusts the B register so that it is full popping the contents of [S] into B, as necessary. */ @@ -223,119 +233,122 @@ B5500Processor.prototype.adjustBFull() { } /**************************************/ -B5500Processor.storeForInterrupt(p, forTest) { +B5500Processor.storeForInterrupt = function(forTest) { /* Implements the 3011=SFI operator and the parts of SFT that are - common to it for the processor referenced as "p". "forTest" implies use - from SFT */ - var forced = p.Q & 0x0040; // Q07F: Hardware-induced SFI syllable + common to it. "forTest" implies use from SFT */ + var forced = this.Q & 0x0040; // Q07F: Hardware-induced SFI syllable + var saveAROF = this.AROF; + var saveBROF = this.BROF; var temp; if (forced || forTest) { - p.NCSF = 0; // switch to control state + this.NCSF = 0; // switch to control state } - if (p.CWMF) { - temp = p.S; // get the correct TOS address from X - p.S = (p.X % 0x40000000) >>> 15; - p.X = p.X % 0x8000 + + if (this.CWMF) { + temp = this.S; // get the correct TOS address from X + this.S = (this.X % 0x40000000) >>> 15; + this.X = this.X % 0x8000 + temp * 0x8000 + - Math.floor(p.X / 0x40000000) * 0x40000000; - if (p.AROF || forTest) { - p.access(0x0A); // [S] = A + Math.floor(this.X / 0x40000000) * 0x40000000; + if (this.AROF || forTest) { + this.access(0x0A); // [S] = A } - if (p.BROF || forTest) { - p.access(0x0B); // [S] = B + if (this.BROF || forTest) { + this.access(0x0B); // [S] = B } - p.B = p.X + // store CM loop-control word - p.AROF * 0x200000000000 + + this.B = this.X + // store CM loop-control word + saveAROF * 0x200000000000 + 0xC00000000000; - p.access(0x0B); // [S] = B + this.access(0x0B); // [S] = B } else { - if (p.BROF || forTest) { - p.access(0x0B); // [S] = B + if (this.BROF || forTest) { + this.access(0x0B); // [S] = B } - if (p.AROF || forTest) { - p.access(0x0A); // [S] = A + if (this.AROF || forTest) { + this.access(0x0A); // [S] = A } } - p.B = p.M + // store interrupt control word (ICW) - p.N * 0x8000 + - p.VARF * 0x1000000 + - p.SALF * 0x40000000 + - p.MSFF * 0x80000000 + - (p.R >>> 6) * 0x200000000 + + this.B = this.M + // store interrupt control word (ICW) + this.N * 0x8000 + + this.VARF * 0x1000000 + + this.SALF * 0x40000000 + + this.MSFF * 0x80000000 + + (this.CWMF ? this.R : this.R >>> 6) * 0x200000000 + 0xC00000000000; - p.access(0x0B); // [S] = B + this.access(0x0B); // [S] = B - p.B = p.C + // store interrupt return control word (IRCW) - p.F * 0x8000 + - p.K * 0x40000000 + - p.G * 0x200000000 + - p.L * 0x1000000000 + - p.V * 0x4000000000 + - p.H * 0x20000000000 + - p.BROF * 0x200000000000 + + this.B = this.C + // store interrupt return control word (IRCW) + this.F * 0x8000 + + this.K * 0x40000000 + + this.G * 0x200000000 + + this.L * 0x1000000000 + + this.V * 0x4000000000 + + this.H * 0x20000000000 + + saveBROF * 0x200000000000 + 0xC00000000000; - p.access(0x0B); // [S] = B + this.access(0x0B); // [S] = B - if (p.CWMF) { - temp = p.F; // if CM, get correct R value from last MSCW - p.F = p.S; - p.S = temp; - p.access(0x03); // B = [S]: get last RCW - p.S = ((p.B % 0x40000000) >>> 15) & 0x7FFF; - p.access(0x03); // B = [S]: get last MSCW - p.R = (Math.Floor(p.B / 0x200000000) % 0x200) << 6; - p.S = p.F; + if (this.CWMF) { + temp = this.F; // if CM, get correct R value from last MSCW + this.F = this.S; + this.S = temp; + this.access(0x03); // B = [S]: get last RCW + this.S = ((this.B % 0x40000000) >>> 15) & 0x7FFF; + this.access(0x03); // B = [S]: get last MSCW + this.R = (Math.Floor(this.B / 0x200000000) % 0x200) << 6; + this.S = this.F; } - p.B = p.S + // store the initiate control word (INCW) - p.CWMF * 0x8000 + + this.B = this.S + // store the initiate control word (INCW) + this.CWMF * 0x8000 + 0xC00000000000; if (forTest) { - p.B += (p.TM & 0x1F) * 0x10000 + - p.Z * 0x400000 + - p.Y * 0x10000000 + - (p.Q & 0x1FF) * 0x400000000; - p.TM = 0; + this.B += (this.TM & 0x1F) * 0x10000 + + this.Z * 0x400000 + + this.Y * 0x10000000 + + (this.Q & 0x1FF) * 0x400000000; + this.TM = 0; + this.MROF = 0; + this.MWOF = 0; } - p.M = p.R + 8; // store initiate word at R+@10 - p.access(0x0D); // [M] = B + this.M = this.R + 0x08; // store initiate word at R+@10 + this.access(0x0D); // [M] = B - p.M = 0; - p.R = 0; - p.MSFF = 0; - p.SALF = 0; - p.BROF = 0; - p.AROF = 0; + this.M = 0; + this.R = 0; + this.MSFF = 0; + this.SALF = 0; + this.BROF = 0; + this.AROF = 0; if (forced) { - if (p === cc.P1) { - p.T = 0x89; // inject 0211=ITI into T register + if (this === cc.P1) { + this.T = 0x89; // inject 0211=ITI into T register } else { - p.T = 0; // idle the processor - p.TROF = 0; - p.PROF = 0; + this.T = 0; // idle the processor + this.TROF = 0; + this.PROF = 0; cc.HP2F = 1; cc.P2BF = 0; this.busy = false; } - p.CWMF = 0; + this.CWMF = 0; } else if (forTest) { - p.CWMF = 0; - if (p === cc.P1) { - p.access(0x05); // B = [M]: load DD for test - p.C = p.B % 0x7FFF; - p.L = 0; - p.access(0x30); // P = [C]: first word of test routine - p.G = 0; - p.H = 0; - p.K = 0; - p.V = 0; + this.CWMF = 0; + if (this === cc.P1) { + this.access(0x05); // B = [M]: load DD for test + this.C = this.B % 0x7FFF; + this.L = 0; + this.access(0x30); // P = [C]: first word of test routine + this.G = 0; + this.H = 0; + this.K = 0; + this.V = 0; } else { - p.T = 0; // idle the processor - p.TROF = 0; - p.PROF = 0; + this.T = 0; // idle the processor + this.TROF = 0; + this.PROF = 0; cc.HP2F = 1; cc.P2BF = 0; this.busy = false; @@ -344,14 +357,114 @@ B5500Processor.storeForInterrupt(p, forTest) { } /**************************************/ -B5500Processor.prototype.run() { +B5500Processor.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; + var saveBROF; + var temp; + + // restore the Initiate Control Word or Initiate Test Control Word + this.S = this.A % 0x8000; + this.CWMF = Math.floor(this.A / 0x8000) % 0x02; + if (forTest) { + this.TM = Math.floor(this.A / 0x10000) % 0x20; + this.Z = Math.floor(this.A / 0x400000) % 0x40; + this.Y = Math.floor(this.A / 0x10000000) % 0x40; + this.Q = Math.floor(this.A / 0x400000000) % 0x200; + this.TM |= Math.floor(this.A / 0x200000( % 0x02 << 5; // CCCF + this.TM |= Math.floor(this.A / 0x80000000000) % 0x02 << 6; // MWOF + this.TM |= Math.floor(this.A / 0x400000000000) % 0x02 << 7; // MROF + // Emulator doesn't support J register, so can't set that from TM + } + this.AROF = 0; + this.BROF = 0; + + // restore the Interrupt Return Control Word + this.access(0x03); // B = [S] + this.S--; + this.C = this.B % 0x8000; + this.F = Math.floor(this.B / 0x8000) % 0x8000; + this.K = Math.floor(this.B / 0x40000000) % 0x08; + this.G = Math.floor(this.B / 0x200000000) % 0x08; + this.L = Math.floor(this.B / 0x1000000000) % 0x04; + this.V = Math.floor(this.B / 0x4000000000) % 0x08; + this.H = Math.floor(this.B / 0x20000000000) % 0x08; + this.access(0x30); // P = [C] + if (this.CWMF || forTest) { + saveBROF = Math.floor(this.B / 200000000000) % 0x02; + } + + // restore the Interrupt Control Word + this.access(0x03); // B = [S] + this.S--; + this.VARF = Math.floor(this.B / 0x1000000) % 0x02; + this.SALF = Math.floor(this.B / 0x40000000) % 0x02; + this.MSFF = Math.floor(this.B / 0x80000000) % 0x02; + temp = (Math.floor(this.B / 0x200000000) % 0x200); + this.R = (this.CWMF ? temp & 0x3F : temp << 6); + + if (this.CWMF || forTest) { + this.M = this.B % 0x8000; + this.N = Math.floor(this.B / 0x8000) % 0x10; + + // restore the CM Interrupt Loop Control Word + this.access(0x03); // B = [S] + this.S--; + this.X = this.B % 0x8000000000; + saveAROF = Math.floor(this.B / 0x400000000000) % 0x02; + + // restore the B register + if (saveBROF || forTest) { + this.access(0x03); // B = [S] + this.S--; + } + + // restore the A register + if (saveAROF || forTest) { + this.access(0x02); // A = [S] + this.S--; + } + + if (this.CWMF) { + // exchange S with its field in X + temp = this.S; + this.S = (this.X % 0x40000000) >>> 15; + this.X = this.X % 0x8000 + + temp * 0x8000 + + Math.floor(this.X / 0x40000000) * 0x40000000; + } + // else don't restore A or B for word mode -- will pop up as necessary + } + + this.T = Math.floor(this.P / Math.pow(2, 36-this.L*12)) % 0x1000; // ugly + this.TROF = 1; + if (forTest) { + this.NCSF = (this.TM >>> 4) & 0x01; + this.CCCF = (this.TM >>> 5) & 0x01; + this.MWOF = (this.TM >>> 6) & 0x01; + this.MROF = (this.TM >>> 7) & 0x01; + this.S--; + if (!this.CCCF) { + this.TM |= 0x80; + } + } else { + this.NCSF = 1; + this.busy = true; + } +} + +/**************************************/ +B5500Processor.prototype.run = function() { /* Instruction execution driver for the B5500 processor. This function is an artifact of the emulator design and does not represent any physical process or state of the processor. This routine assumes the registers are set up, and in particular a syllable is in T with TROF set. It will run until cycleCount >= cycleLimit or !this.busy */ var opcode; - var repeat; + var t1; + var t2; + var variant; do { this.Q = 0; @@ -362,10 +475,7 @@ B5500Processor.prototype.run() { /*********************************************************** * Character Mode Syllables * ***********************************************************/ - this.M = 0; - this.N = 0; - this.X = 0; - repeat = opcode >>> 6; + variant = opcode >>> 6; switch (opcode & 0x3F) { case 0x00: // XX00: CMX, EXC: Exit character mode break; @@ -404,16 +514,16 @@ B5500Processor.prototype.run() { break; case 0x11: // XX11: control state ops - switch (repeat) { + switch (variant) { case 0x14: // 2411: ZPI=Conditional Halt break; case 0x18: // 3011: SFI=Store for Interrupt - this.storeForInterrupt(this, false); + this.storeForInterrupt(false); break; case 0x1C: // 3411: SFT=Store for Test - this.storeForInterrupt(this, true); + this.storeForInterrupt(true); break; default: // Anything else is a no-op @@ -461,8 +571,8 @@ B5500Processor.prototype.run() { break; case 0x20: // XX40: INC=Increase TALLY - if (repeat) { - this.R = (this.R + repeat) & 0x3F; + if (variant) { + this.R = (this.R + variant) & 0x3F; // else it's a character-mode no-op } break; @@ -471,10 +581,10 @@ B5500Processor.prototype.run() { break; case 0x22: // XX42: SEC=Set TALLY - this.R = repeat; + this.R = variant; break; - case 0x23: // XX43: CRF=Call repeat field + case 0x23: // XX43: CRF=Call variant field break; case 0x24: // XX44: JNC=Jump out of loop conditional @@ -568,6 +678,9 @@ B5500Processor.prototype.run() { /*********************************************************** * Word Mode Syllables * ***********************************************************/ + this.M = 0; + this.N = 0; + this.X = 0; switch (opcode & 3) { case 0: // LITC: Literal Call this.adjustAEmpty(); @@ -586,22 +699,55 @@ B5500Processor.prototype.run() { break; case 1: // all other word-mode operators + variant = opcode >>> 6; switch (opcode & 0x3F) { case 0x01: // XX01: single-precision numerics + switch (variant) { + case 0x01: // 0101: ADD=single-precision add + break; + + case 0x03: // 0301: SUB=single-precision subtract + break; + + case 0x04: // 0401: MUL=single-precision multiply + break; + + case 0x08: // 1001: DIV=single-precision floating divide + break; + + case 0x18: // 3001: IDV=integer divide + break; + + case 0x38: // 7001: RDV=remainder divide + break; + } break; case 0x05: // XX05: double-precision numerics + switch (variant) { + case 0x01: // 0105: DLA=double-precision add + break; + + case 0x03: // 0305: DLS=double-precision subtract + break; + + case 0x04: // 0405: DLM=double-precision multiply + break; + + case 0x08: // 1005: DLD=double-precision floating divide + break; + } break; case 0x09: // XX11: control state and communication ops - switch (opcode >>> 6) { + switch (variant) { case 0x01: // 0111: PRL=Program Release break; case 0x10: // 1011: COM=Communicate if (this.NCSF) { // no-op in control state this.adjustAFull(); - this.M = 0x09; // address = @11 + this.M = this.R + 0x09; // address = R+@11 this.access(0x0C); // [M] = A this.AROF = 0; this.I = (this.I & 0x0F) | 0x40; // set I07 @@ -630,23 +776,43 @@ B5500Processor.prototype.run() { break; case 0x12: // 2211: HP2=Halt Processor 2 + if (!this.NCSF & cc.P2 && cc.P2BF) { + cc.HP2F = 1; + // We know P2 is not currently running on this thread, so save its registers + cc.P2.storeForInterrupt(false); + cc.P2BF = 0; + if (cc.P2.scheduler) { + cancelTimeout(cc.P2.scheduler); + } + } break; case 0x14: // 2411: ZPI=Conditional Halt break; case 0x18: // 3011: SFI=Store for Interrupt - this.storeForInterrupt(this, false); + this.storeForInterrupt(false); break; case 0x1C: // 3411: SFT=Store for Test - this.storeForInterrupt(this, true); + this.storeForInterrupt(true); break; case 0x21: // 4111: IP1=Initiate Processor 1 + if (!this.NCSF) { + this.initiate(false); + } break; case 0x22: // 4211: IP2=Initiate Processor 2 + if (!this.NCSF) { + this.adjustAFull(); + this.M = 8; // INCW is stored in @10 + this.access(0x0C); // [M] = A + this.AROF = 0; + cc.initiateP2(); + this.cycleLimit = 0; // give P2 a chance to run + } break; case 0x24: // 4411: IIO=Initiate I/O @@ -654,61 +820,296 @@ B5500Processor.prototype.run() { case 0x29: // 5111: IFT=Initiate For Test break; - - default: // Anything else is a no-op - break; } // end switch for XX11 ops break; case 0x0D: // XX15: logical (bitmask) ops + switch (variant) { + case 0x01: // 0115: LNG=logical negate + break; + + case 0x02: // 0215: LOR=logical OR + break; + + case 0x04: // 0415: LND=logical AND + break; + + case 0x08: // 1015: LQV=logical EQV + break; + + case 0x10: // 2015: MOP=reset flag bit (make operand) + break; + + case 0x20: // 4015: MDS=set flag bit (make descriptor) + break; + } break; case 0x11: // XX21: load & store ops + switch (variant) { + case 0x01: // 0121: CID=Conditional integer store descructive + break; + + case 0x02: // 0221: CIN=Conditional integer store nondestructive + break; + + case 0x04: // 0421: STD=Store destructive + break; + + case 0x08: // 1021: SND=Store nondestructive + break; + + case 0x10: // 2021: LOD=Load operand + break; + + case 0x21: // 4121: ISD=Integer store destructive + break; + + case 0x22: // 4221: ISN=Integer store nondestructive + break; + } break; case 0x15: // XX25: comparison & misc. stack ops + switch (variant) { + case 0x01: // 0125: CEQ=compare B greater or equal to A + break; + + case 0x02: // 0225: CGR=compare B greater to A + break; + + case 0x04: // 0425: NEQ=compare B not equal to A + break; + + case 0x08: // 1025: XCH=exchange B with A + this.adjustAFull(); + this.adjustBFull(); + t1 = this.A; + this.A = this.B; + this.B = t1; + break; + + case 0x0C: // 1425: FTC=F field to core field + break; + + case 0x10: // 2025: DUP=Duplicate TOS + this.adjustAEmpty(); + this.adjustBFull(); + this.A = this.B; + this.AROF = 1; + break; + + case 0x1C: // 3425: FTF=F field to F field + break; + + case 0x21: // 4125: LEQ=compare B less or equal to A + break; + + case 0x22: // 4225: LSS=compare B less to A + break; + + case 0x24: // 4425: EQL=compare B equal to A + break; + + case 0x2C: // 5425: CTC=core field to C field + break; + + case 0x3C: // 7425: CTF=cre field to F field + break; + } break; case 0x19: // XX31: branch, sign-bit, interrogate ops + switch (variant) { + case 0x01: // 0131: BBC=branch backward conditional + break; + + case 0x02: // 0231: BFC=branch forward conditional + break; + + case 0x04: // 0431: SSN=set sign bit (set negative) + break; + + case 0x08: // 1031: CHS=change sign bit + break; + + case 0x10: // 2031: TOP=test flag bit (test for operand) + break; + + case 0x11: // 2131: LBC=branch backward word conditional + break; + + case 0x12: // 2231: LFC=branch forward word conditional + break; + + case 0x14: // 2431: TUS=interrogate peripheral status + break; + + case 0x21: // 4131: BBW=branch backward unconditional + break; + + case 0x22: // 4231: BFW=branch forward unconditional + break; + + case 0x24: // 4431: SSP=reset sign bit (set positive) + break; + + case 0x31: // 6131: LBU=branch backward word unconditional + break; + + case 0x32: // 6231: LFU=branch forward word unconditional + break; + + case 0x34: // 6431: TIO=interrogate I/O channel + break; + + case 0x38: // 7031: FBS=stack search for flag + break; + } break; case 0x1D: // XX35: exit & return ops + switch (variant) { + case 0x01: // 0135: BRT=branch return + break; + + case 0x02: // 0235: RTN=return normal + break; + + case 0x04: // 0435: XIT=exit procedure + break; + + case 0x0A: // 1235: RTS=return special + break; + } break; case 0x21: // XX41: index, mark stack, etc. + switch (variant) { + case 0x01: // 0141: INX=index + break; + + case 0x02: // 0241: COC=construct operand call + break; + + case 0x04: // 0441: MKS=mark stack + break; + + case 0x0A: // 1241: CDC=construct descriptor call + break; + + case 0x11: // 2141: SSF=F & S register set/store + break; + + case 0x15: // 2541: LLL=link list lookup + break; + + case 0x24: // 4441: CMN=enter character mode inline + break; + } break; case 0x25: // XX45: ISO=Variable Field Isolate op break; case 0x29: // XX51: delete & conditional branch ops + if (variant == 0) { // 0065=DEL: delete TOS + if (this.AROF) { + this.AROF = 0; + } else if (this.BROF) { + this.BROF = 0; + } else { + this.S--; + } + } else { + switch (variant & 0x03) { + case 0x00: // X051/X451: CFN=non-zero field branch forward nondestructive + break; + + case 0x01: // X151/X551: CBN=non-zero field branch backward nondestructive + break; + + case 0x02: // X251/X651: CFD=non-zero field branch forward destructive + break; + + case 0x03: // X351/X751: CBD=non-zero field branch backward destructive + break; + } + } break; - case 0x2D: // XX55: NOOP & DIA=Dial A ops + case 0x2D: // XX55: NOP & DIA=Dial A ops if (opcode & 0xFC0) { - this.G = opcode >>> 9; - this.H = (opcode >>> 6) & 7; - // else 0055=NOOP + this.G = variant >>> 3; + this.H = (variant) & 7; + // else // 0055: NOP=no operation (the official one, at least) } break; case 0x31: // XX61: XRT & DIB=Dial B ops if (opcode & 0xFC0) { - this.K = opcode >>> 9; - this.V = (opcode >>> 6) & 7; + this.K = variant >>> 3; + this.V = (variant) & 7; } else { // 0061=XRT: temporarily set full PRT addressing mode this.VARF = this.SALF; this.SALF = 0; } break; - case 0x35: // XX65: TRB=Transfer Bits op + case 0x35: // XX65: TRB=Transfer Bits + this.adjustAFull(); + this.adjustBFull(); + t1 = this.G <<< 3 | this.H; // A register starting bit nr + if (t1+variant > 48) { + variant = 48-t1; + } + t2 = this.K <<< 3 | this.V; // B register starting bit nr + if (t2+variant > 48) { + variant = 48-t2; + } + if (variant > 0) { + this.B = cc.insert(this.B, t2, variant, cc.isolate(this.A, t1, variant)); + } + this.AROF = 0; + this.cycleCount += variant + this.G + this.K; // approximate the shift counts break; - case 0x39: // XX71: FCL=Compare Field Low op + case 0x39: // XX71: FCL=Compare Field Low + this.adjustAFull(); + this.adjustBFull(); + t1 = this.G <<< 3 | this.H; // A register starting bit nr + if (t1+variant > 48) { + variant = 48-t1; + } + t2 = this.K <<< 3 | this.V; // B register starting bit nr + if (t2+variant > 48) { + variant = 48-t2; + } + if (variant > 0 && cc.isolate(this.B, t2, variant) < cc.isolate(this.A, t1, variant)) { + this.A = 1; + } else { + this.A = 0; + } + this.cycleCount += variant + this.G + this.K; // approximate the shift counts break; - case 0x3D: // XX75: FCE=Compare Field Equal op + case 0x3D: // XX75: FCE=Compare Field Equal + this.adjustAFull(); + this.adjustBFull(); + t1 = this.G <<< 3 | this.H; // A register starting bit nr + if (t1+variant > 48) { + variant = 48-t1; + } + t2 = this.K <<< 3 | this.V; // B register starting bit nr + if (t2+variant > 48) { + variant = 48-t2; + } + if (variant > 0 && cc.isolate(this.B, t2, variant) == cc.isolate(this.A, t1, variant)) { + this.A = 1; + } else { + this.A = 0; + } + this.cycleCount += variant + this.G + this.K; // approximate the shift counts break; default: @@ -745,11 +1146,11 @@ B5500Processor.prototype.run() { this.access(0x30); // P = [C] } } - } while (++this.cycleCount < this.cycleLimit && this.busy); + } while ((this.cycleCount += 2) < this.cycleLimit && this.busy); } /**************************************/ -B5500Processor.prototype.schedule() { +B5500Processor.prototype.schedule = function() { /* Schedules the processor run time and attempts to throttle performance to approximate that of a real B5500. Well, at least we hope this will run fast enough that the performance will need to be throttled. It establishes @@ -762,12 +1163,17 @@ B5500Processor.prototype.schedule() { Javascript execution thread. */ var delayTime; + this.scheduler = null; this.cycleLimit = this.timeSlice; this.cycleCount = 0; + this.run(); + + this.totalCycles += this.cycleCount this.procTime += this.cycleCount; if (this.busy) { delayTime = this.procTime/1000 - new Date().getTime(); - setTimer(this.schedule, (delayTime < 0 ? 0 : delayTime)); + this.scheduleSlack += delayTime; + this.scheduler = setTimeout(this.schedule, (delayTime < 0 ? 0 : delayTime)); } } \ No newline at end of file