From 0c339bc2806bf61263def8a2b27eaf1f1d1a7758 Mon Sep 17 00:00:00 2001 From: paul Date: Mon, 4 Jun 2012 02:26:26 +0000 Subject: [PATCH] Commit DCMCP transcription as of 2012-06-03; commit initial emulator module constructors. --- SYMBOL/DCMCP.esp_m | 233 ++++++++++++++++++++ emulator/B5500CentralControl.js | 114 ++++++++++ emulator/B5500Processor.js | 374 ++++++++++++++++++++++++++++++++ emulator/Register.js | 2 +- 4 files changed, 722 insertions(+), 1 deletion(-) create mode 100644 emulator/B5500CentralControl.js create mode 100644 emulator/B5500Processor.js diff --git a/SYMBOL/DCMCP.esp_m b/SYMBOL/DCMCP.esp_m index 6198727..0c9a50a 100644 --- a/SYMBOL/DCMCP.esp_m +++ b/SYMBOL/DCMCP.esp_m @@ -2338,3 +2338,236 @@ ARRAY MAINTBUFFER[*]; 04121950 END 04134950 ELSE GO TO DC; 04134955 END ELSE GO TO X; 04134960 + END; 04134990 + IF E = 0 THEN 04135000 + BEGIN % RECOVERED MASS STORAGE % 04137000 + MAINTBUFFER[NXDISK:=NXDISK+4 AND 15] 04137100 + := -0 & U[2:46:2] & LOCATQUE[S][4:3:5] & 04137110 + (LOIGENTRY:=LOGENTRY+1)[CTF] & 04137120 + RDCTABLE[U]{18:1:2]; 04137130 + IF FINALQUE[S] GTR 0 THEN 04137140 + BEGIN 04137150 + MAINTBUFFER[XNDISK]:=(*P(DUP)) & 04137160 + ((M[M[S1:=LOCATQUE[S] INX NOT 2] INX 4]04137170 + .[13:11] DIV ETRLNG)+1)[9:39:9]; 04137180 + M[S1].[7:1] := 1; 04137190 + END; 04137200 + P(MAINTBUFFER[NXDISK+2]:=IOQUE[S]); 04137202 + $ SET OMIT = NOT(AUXMEM) 04137203 + P(NFLAG(M[P])); 04137212 + P(P&V[1:44:4],[MAINTBUFFER[NXDISK+1]],STD); 04137215 + MAINTBUFFER[NXDISK+3]:=MAINTBUFFER[U]; 04137220 + IF (LOGHOLDER INX 0) = 0 THEN 04137230 + BEGIN 04137240 + LOGHOLDER.[CF]:=[MAINTBUFFER[NXDISK]]; 04137250 + INDEPENDENTRUNNER(P(.MAINTLOGGER),0,100); 04137260 + END ELSE M[LOGHOLDER.[FF]].[CF]:= 04137270 + [MAINTBUFFER[NXDISK]]; 04137275 + LOGHOLDER.[FF]:=[MAINTBUFFER[NXDISK]]; 04137280 + NUMAINTMESS:= NUMAINTMESS+1; 04137290 + T.[5:8] ~ 0; 04142000 + GO TO SW; 04142500 + END;% 04143000 + IF V = 0 THEN% 04144000 + $ SET OMIT = NOT(SHAREDISK) 04144099 + BEGIN % ORIGINAL ERROR ON MASS STORAGE% 04145000 + TINU[U].[18:2] ~ P(DUP).[18:12]+1;% 04146000 + MAINTBUFFER[U]:=R&TWO(C)[18:43:4]; 04146100 + RDCTABLE[U]:=(*P(DUP))&(C-1)[1:46:2]; 04146200 + V:=129; 04147000 + $ SET OMIT = NOT(SHAREDISK) 04147399 + END% 04148000 + ELSE BEGIN % RECURRENT ERROR ON MASS STORAGE% 04149000 + P(MAINTBUFFER[U]:=P(DUP,LOD) OR 04150100 + R&TWO(C)[18:43:4]); 04150200 + IF (V ~ V+1) > 137 THEN% 04151000 + BEGIN R:=P; 04151200 + IF LOCATQUE[S].[9:1] THEN % OLAY I/O 04151220 + M[LOCATQUE[S]:=R OR IOMASK; 04151230 + $ SET OMIT = NOT(AUXMEM) 04151235 + DISKERR: 04151300 + $ SET OMIT = NOT(DFX) 04151399 + T.[5:10]:=0; 04151400 + GO TO DX; 04152600 + END; 04152800 + P(DEL); 04152900 + END;% 04153000 + UNIT[U] ~ T&V[5:40:8];% 04154000 + DS:% 04155000 + CHANNEL[P(TIO)] ~ U;% 04156000 + P([IOQUE[S]],IIO);% 04157000 + GO TO EXTERNAL ;% 04158000 + X: STOP ~ (V!0)|2+1;% 04159000 + T.[5:13] ~ 32|E+8;% 04160000 + GO TO TEST; 04161000 + END; 04161500 + SW:: GO TO TYPE[T.[1:4]];% 04162000 + LP: 04163000 + IF STOP := (T := T&0[16:16:1]).[17:1] THEN 04164000 + TEST: IF FIRSTWAIT = NEXTWAIT THEN GO TO INCR ELSE% 04165000 + GO TO NEW ELSE GO TO NOWAIT;% 04166000 + DK: 04167900 + IF NOT (I:=IOQUE[S]).[24:1] THEN 04168000 + IF FINALQUE[S].[24:1] THEN% 04169000 + $ SET OMIT = DFX 04169090 + BEGIN 04169100 + $ SET OMIT = NOT DKBNODFX OR OMIT 04169190 + $ SET OMIT = DKBNODFX OR OMIT 04170750 + M[IOQUE[S]:=I&1[24:47:1]]:=*(P(DUP) INX P(0,LNG,XCH)); 04170800 + $ POP OMIT 04170900 + GO TO DS; 04171000 + END ELSE GO TO OK ELSE GO TO OK; 04171200 + $ POP OMIT 04171250 + $ SET OMIT = NOT DFX 04171350 + DC: 04174000 + $ SET OMIT = NOT(DATACOM ) 04174999 + 04176000 + $ SET OMIT = DFX 04176899 + DX: DX1: 04176900 + $ POP OMIT 04176901 + OK: IF FIRSTWAIT = NEXTWAIT THEN 04177000 + NOWAIT: IF (S1 := LOCATQUE[S].[18:15]) LSS @1777 THEN 04178000 + INITIATEIO(IOQUE[S1],LOCATQUE[S1].[3:5],U)% 04180000 + ELSE 04181000 + PROC: T := T&0[16:16:2] 04182000 + ELSE 04183000 + BEGIN% 04187000 + NEW: NEWIO;% 04188000 + IF STOP THEN GO TO INCR;% 04189000 + QUP: IF LOCATQUE[S].[FF] GTR @1777 THEN GO TO PROC; 04190000 + QUEUEUP(U);% 04191000 + T ~ T&4[13:43:5];% 04192000 + END;% 04193000 + INCR: 04194000 + IF (TIM~CLOCK+P(RTR)-TIM) LSS THEN THEN TIM~0; 04194050 + IOD:=IOQUE[S]; 04194100 + IF (U OR 1 )=19 THEN 04194200 + BEGIN 04194300 + IF (JUNK:=M[IOD].[5:7])>9 THEN 04194400 + JUNK:=NEUP.[CF]+(JUNK AND @17); 04194500 + IF JUNK PA is P1, 1=> PB is P1 + + + this.rtcTick = 1000/60; // Real-time clock period, milliseconds + this.nextTimeStamp = 0; // Next actual Date.getTime() expected +} + +/**************************************/ +B5500CentralControl.prototype.fetch(addr) { + /* Called by all modules to fetch a word from memory. /* + + // TO BE PROVIDED +} + +/**************************************/ +B5500CentralControl.prototype.store(addr, word) { + /* Called by all modules to fetch a word from memory. /* + + // TO BE PROVIDED +} + +/**************************************/ +B5500CentralControl.prototype.signalInterrupt() { + /* 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. /* + + // TO BE PROVIDED +} + +/**************************************/ +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 */ + var thisTime = new Date().getTime(); + + if (this.TM < 63) { + this.TM++; + } else { + this.TM = 0; + this.CCI03F = 1; // set timer interrupt + this.signalInterrupt(); + } + this.nextTimeStamp += this.rtcTick; + if (this.nextTimeStamp < thisTime) { + setTimeout(this.tock, 1); // try to catch up + } else { + setTimeout(this.tock, this.nextTimeStamp-thisTime); + } +} + +/**************************************/ +B5500CentralControl.prototype.halt() { + /* Halts the system */ + + // TO BE PROVIDED +} + +/**************************************/ +B5500CentralControl.prototype.load() { + /* Initiates a Load operation to start the system */ + + // TO BE PROVIDED +} \ No newline at end of file diff --git a/emulator/B5500Processor.js b/emulator/B5500Processor.js new file mode 100644 index 0000000..fd76fcc --- /dev/null +++ b/emulator/B5500Processor.js @@ -0,0 +1,374 @@ +/*********************************************************************** +* retro-b5500/emulator B5500Processor.js +************************************************************************ +* Copyright (c) 2012, Nigel Williams and Paul Kimpel. +* Licensed under the MIT License, see http://www.opensource.org/licenses/mit-license.php +************************************************************************ +* JavaScript object definition for the B5500 Processor (CPU) module. +************************************************************************ +* 2012-06-03 P.Kimpel +* Original version, from thin air. +***********************************************************************/ + +/**************************************/ +function B5500Processor() { + /* Constructor for the Processor module object */ + + this.A = 0; // Top-of-stack register 1 + this.AROF = 0; // A contents valid + this.B = 0; // Top-of-stack register 2 + this.BROF = 0; // B contents valid + this.C = 0; // Current program instruction word address + this.CCCF = 0; // Clock-count control FF (maintenance only) + this.CWMF = 0; // Character/word mode FF (1=CM) + this.E = 0; // Memory access control register + this.EIHF = 0; // ?? + this.F = 0; // Top MSCW/RCW stack address + this.G = 0; // Character index register for A + this.H = 0; // Bit index register for G (in A) + this.HLTF = 0; // Processor halt FF + this.I = 0; // Processor interrupt register + this.K = 0; // Character index register for B + this.L = 0; // Instruction syllable index in P + this.M = 0; // Memory address register (SI.w in CM) + this.MRAF = 0; // Memory read access FF + this.MROF = 0; // Memory read obtained FF + this.MSFF = 0; // Mark-stack FF (word mode: MSCW is pending RCW, physically also TFFF & Q12F) + this.MWOF = 0; // Memory write obtained FF + this.N = 0; // Octal shift counter for B + this.NCSF = 0; // Normal/control state FF (1=normal) + this.P = 0; // Current program instruction word register + this.PROF = 0; // P contents valid + this.Q = 0; // Misc. FFs (bits 1-9 only: Q07F=hardware-induced interrupt, Q09F=enable parallel adder for R-relative addressing) + this.R = 0; // PRT base address (high-order 9 bits only) + this.S = 0; // Top-of-stack memory address (DI.w in CM) + this.SALF = 0; // Program/subroutine state FF (1=subroutine) + this.T = 0; // Current program syllable register + this.TALLY = 0; // CM TALLY register (physically, low-order 6 bits of R) + this.TM = 0; // Temporary maintenance storage register + this.TROF = 0; // T contents valid + this.V = 0; // Bit index register for K (in B) + this.VARF = 0; // Variant-mode FF (enables full PRT indexing) + this.X = 0; // Mantissa extension for B (loop control in CM) + this.Y = 0; // Serial character register for A + this.Z = 0; // Serial character register for B + + this.cycleLimit = 0; // Count-down cycle limit for this.run() + this.isP1 = true; // Control processor flag +} + +/**************************************/ +B5500Processor.prototype.access(eValue) { + /* Access memory based on the E register */ + var addr; + + /**************************************************************** + HOW TO HANDLE INVALID ADDRESS INTERRUPTS DETECTED BY CENTRAL CONTROL? + ****************************************************************/ + + this.E = eValue; + switch (eValue) { + case 0x02: // A = [S] + this.A = cc.fetch(this.S); + this.AROF = 1; + break; + case 0x03: // B = [S] + this.B = cc.fetch(this.S); + this.BROF = 1; + break; + case 0x04: // A = [M] + this.A = cc.fetch(this.M); + this.AROF = 1; + break; + case 0x05: // B = [M] + this.B = cc.fetch(this.M); + this.BROF = 1; + break; + case 0x06: // M = [M].[18:15] + this.M = (cc.fetch(this.M) >>> 15) & 0x7FFF; + break; + case 0x0A: // [S] = A + cc.store(this.S, this.A); + break; + case 0x0B: // [S] = B + cc.store(this.S, this.B); + break; + case 0x0C: // [M] = A + cc.store(this.M, this.A); + break; + case 0x0D: // [M] = B + cc.store(this.M, this.B); + case 0x30: // P = [C] + this.P = cc.fetch(this.C); + this.PROF = 1; + break; + default: + throw "Invalid E register value: " + eReg.toString(2); + break; + } + this.cycleLimit -= 6; // assume 6 us memory cycle time + + if (addr < 0x0200 && this.NCSF) { // normal-state cannot address @000-@777 [?? first 512 or 1024 words ??] + this.I |= 0x0500; // set I02F & I04F + cc.signalInterrupt(); + } else { + cc.store(addr, word); + } +} + +/**************************************/ +B5500Processor.prototype.adjustAEmpty() { + /* Adjusts the A register so that it is empty pushing the prior + contents of A into B and B into memory, as necessary. */ + + if (this.AROF} { + if (this.BROF) { + this.S++; + this.access(0x0B); // [S] = B + } + this.B = this.A; + this.AROF = 0; + this.BROF = 1; + // else we're done -- A is already empty + } +} + +/**************************************/ +B5500Processor.prototype.adjustAFull() { + /* Adjusts the A register so that it is full, popping the contents of + B or [S] into A, as necessary. */ + + if (!this.AROF) { + if (this.BROF) { + this.A = this.B; + this.AROF = 1; + this.BROF = 0; + } else { + this.access(0x02); // A = [S] + this.S--; + } + // else we're done -- A is already full + } +} + +/**************************************/ +B5500Processor.prototype.adjustBEmpty() { + /* Adjusts the B register so that it is empty pushing the prior + contents of B into memory, as necessary. */ + + if (this.BROF) { + this.S++; + this.access(0x0B); // [S] = B + // else we're done -- B is already empty + } +} + +/**************************************/ +B5500Processor.prototype.adjustBFull() { + /* Adjusts the B register so that it is full popping the contents of + [S] into B, as necessary. */ + + if (!this.BROF) { + this.access(0x03); // B = [S] + this.S--; + // else we're done -- B is already full + } +} + +/**************************************/ +B5500Processor.storeForInterrupt() { + /* Implements the 3011=SFI operator */ + + if (this.CWMF) { + if (this.BROF) { + this.access(0x0B); // [S] = B, save B if valid + } + if (this.AROF) { + this.access(0x0A); // [S] = A, save A if valid + } + this.B = ((((0x30*512 + + (this.R >>> 6))*4 + + this.MSFF)*2 + + this.SALF)*32768 + + this.N)*16 + + this.M; + this.S++; + this.access(0x0B); + } else + } +} + +/**************************************/ +B5500Processor.prototype.run() { + /* 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. */ + var opcode; + + /* HOW TO ENTER, EXIT, AND RESUME CHARACTER MODE? */ + + this.cycleLimit = 5000; // max CPU cycles to run + do { + opcode = this.T; + switch (opcode & 3) { + case 0: // LITC: Literal Call + this.adjustAEmpty(); + this.A = opcode >>> 2; + this.AROF = 1; + break; + + case 2: // OPDC: Operand Call + this.adjustAEmpty(); + // TO BE PROVIDED + break; + + case 3: // DESC: Descriptor (name) Call + this.adjustAEmpty(); + // TO BE PROVIDED + break; + + case 1: // all other word-mode operators + switch (opcode & 0x3F) { + case 0x01: // XX01: single-precision numerics + break; + + case 0x05: // XX05: double-precision numerics + break; + + case 0x09: // XX11: control state and communication ops + switch (opcode >>> 6) { + case 0x01: // 0111: PRL=Program Release + break; + + case 0x10: // 1011: COM=Communicate + this.adjustAFull(); + this.M = 0x09; // address @11 + this.access(0x0C); // [M] = A + this.AROF = 0; + break; + + case 0x02: // 0211: ITI=Interrogate Interrupt + break; + + case 0x04: // 0411: RTR=Read Timer + adjustAEmpty(); + this.A = cc.CCI03F << 6 | cc.TM; + break; + + case 0x11: // 2111: IOR=I/O Release + break; + + case 0x12: // 2211: HP2=Halt Processor 2 + break; + + case 0x14: // 2411: ZPI=Conditional Halt + break; + + case 0x18: // 3011: SFI=Store for Interrupt + this.storeForInterrupt(); + break; + + case 0x1C: // 3411: SFT=Store for Test + break; + + case 0x21: // 4111: IP1=Initiate Processor 1 + break; + + case 0x22: // 4211: IP2=Initiate Processor 2 + break; + + case 0x24: // 4411: IIO=Initiate I/O + break; + + case 0x29: // 5111: IFT=Initiate For Test + break; + + default: + break; // Anything else is a no-op + } / end switch for XX11 ops + break; + + case 0x0D: // XX15: logical (bitmask) ops + break; + + case 0x11: // XX21: load & store ops + break; + + case 0x15: // XX25: comparison & misc. stack ops + break; + + case 0x19: // XX31: branch, sign-bit, interrogate ops + break; + + case 0x1D: // XX35: exit & return ops + break; + + case 0x21: // XX41: index, mark stack, etc. + break; + + case 0x25: // XX45: ISO=Variable Field Isolate op + break; + + case 0x29: // XX51: delete & conditional branch ops + break; + + case 0x2D: // XX55: NOOP & DIA=Dial A ops + if (opcode & 0xFC0) { + this.G = opcode >>> 9; + this.H = (opcode >>> 6) & 7; + // else 0055=NOOP + } + break; + + case 0x31: // XX61: XRT & DIB=Dial B ops + if (opcode & 0xFC0) { + this.K = opcode >>> 9; + this.V = (opcode >>> 6) & 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 + break; + + case 0x39: // XX71: FCL=Compare Field Low op + break; + + case 0x3D: // XX75: FCE=Compare Field Equal op + break; + + default: + break; // should never get here, but in any case it'd be a no-op + } // end switch for word-mode operators + break; + } // end switch for main opcode dispatch + + // SECL: Syllable Execution Complete Level + this.Q = 0; + this.Y = 0; + this.Z = 0; + if (this.CWMF) { + this.M = 0; + this.N = 0; + this.X = 0; + } + if (cc.IAR && this.NCSF) { // there's an interrupt and we're in normal state + this.T = 0x0609; // inject 3011=SFI into T + this.Q |= 0x40 // set Q07F=hardware-induced SFI + this.Q &= ~(0x100); // reset Q09F=adder mode for R-relative addressing + } else { + if (this.L < 3) { + this.T = (this.P >>> (36-this.L*12)) & 0x0FFF; + this.L++; + } else { + this.T = this.P & 0x0FFF; + this.L = 0; + this.C++; + this.access(0x30); // P = [C] + } + } + } while (--this.Limit > 0); +} \ No newline at end of file diff --git a/emulator/Register.js b/emulator/Register.js index 45007d6..501eee6 100644 --- a/emulator/Register.js +++ b/emulator/Register.js @@ -2,7 +2,7 @@ * retro-b5500/emulator Register.js ************************************************************************ * Copyright (c) 2012, Nigel Williams and Paul Kimpel. -* Licensed under the MIT Licensed, see http://www.opensource.org/licenses/mit-license.php +* Licensed under the MIT License, see http://www.opensource.org/licenses/mit-license.php ************************************************************************ * JavaScript object definition for the generalized Register prototype. * Maximum register width is 52 bits, since Javascript stores numbers