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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user