1
0
mirror of https://github.com/pkimpel/retro-b5500.git synced 2026-02-12 11:17:29 +00:00

Commit DCMCP transcription and emulator development as of 2012-06-10.

This commit is contained in:
paul
2012-06-11 03:00:51 +00:00
parent 91f63f67f2
commit edeca66962
3 changed files with 859 additions and 205 deletions

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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));
}
}