1
0
mirror of https://github.com/pkimpel/retro-b5500.git synced 2026-02-12 03:07:30 +00:00

Apply selected reversals and corrections to Nigel's revisions for

Rhino acceptability. Justifications are in email dated 2012-06-30 
1100 PDT.
This commit is contained in:
paul
2012-06-30 17:49:23 +00:00
parent 0e0b288909
commit aff4e43308

View File

@@ -2,7 +2,12 @@
* retro-b5500/emulator B5500Processor.js
************************************************************************
* Copyright (c) 2012, Nigel Williams and Paul Kimpel.
* MIT Licensed http://www.opensource.org/licenses/mit-license.php
* Licensed under the MIT License, see
* http://www.opensource.org/licenses/mit-license.php
*
* Instance variables in all caps generally refer to register or flip-flop (FF)
* entities in the processor hardware. See the Burroughs B5500 Reference Manual
* (1021326, May 1967) for details.
************************************************************************
* B5500 Processor (CPU) module.
************************************************************************
@@ -11,7 +16,9 @@
***********************************************************************/
"use strict";
/**************************************/
function B5500Processor() {
/* Constructor for the Processor module object */
this.scheduler = null; // Reference to current setTimeout id
this.accessor = { // Memory access control block
@@ -26,20 +33,23 @@ function B5500Processor() {
this.clear(); // Create and initialize the processor state
}
/**************************************/
B5500Processor.timeSlice = 5000; // Standard run() timeslice, about 5ms (we hope)
/**************************************/
B5500Processor.prototype.clear = function() {
/* Initializes the processor state, FF is FlipFlop (in modern parlance: boolean flag) */
/* Initializes (and if necessary, creates) the processor state */
this.A = 0; // Top-of-stack register A
this.AROF = 0; // A Register Occupied Flip-flop
this.AROF = 0; // A Register Occupied FF
this.B = 0; // Top-of-stack register B
this.BROF = 0; // B contents valid
this.BROF = 0; // B Register Occupied FF
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.EIHF = 0; // E-register Inhibit Address FF
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)
@@ -78,74 +88,83 @@ B5500Processor.prototype.clear = function() {
};
/**************************************/
B5500Processor.prototype.access = function(cc, 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 */
this.E = eValue; // Just to show the world what's happening
this.accessor.MAIL = (this.addr < 0x0200 && this.NCSF);
switch (eValue) {
case 0x02: // A = [S]
this.accessor.addr = this.S;
this.accessor.MAIL = (this.S < 0x0200 && this.NCSF);
cc.fetch(this);
this.A = this.accessor.word;
this.AROF = 1;
break;
case 0x03: // B = [S]
this.accessor.addr = this.S;
this.accessor.MAIL = (this.S < 0x0200 && this.NCSF);
cc.fetch(this);
this.B = this.accessor.word;
this.BROF = 1;
break;
case 0x04: // A = [M]
this.accessor.addr = this.M;
this.accessor.MAIL = (this.M < 0x0200 && this.NCSF);
cc.fetch(this);
this.A = this.accessor.word;
this.AROF = 1;
break;
case 0x05: // B = [M]
this.accessor.addr = this.M;
this.accessor.MAIL = (this.M < 0x0200 && this.NCSF);
cc.fetch(this);
this.B = this.accessor.word;
this.BROF = 1;
break;
case 0x06: // M = [M].[18:15]
this.accessor.addr = this.M;
this.accessor.MAIL = (this.M < 0x0200 && this.NCSF);
cc.fetch(this);
this.M = ((this.accessor.word % 0x40000000) >>> 15) & 0x7FFF;
break;
case 0x0A: // [S] = A
this.accessor.addr = this.S;
this.accessor.MAIL = (this.S < 0x0200 && this.NCSF);
this.accessor.word = this.A;
cc.store(this);
break;
case 0x0B: // [S] = B
this.accessor.addr = this.S;
this.accessor.MAIL = (this.S < 0x0200 && this.NCSF);
this.accessor.word = this.B;
cc.store(this);
break;
case 0x0C: // [M] = A
this.accessor.addr = this.M;
this.accessor.MAIL = (this.M < 0x0200 && this.NCSF);
this.accessor.word = this.A;
cc.store(this);
break;
case 0x0D: // [M] = B
this.accessor.addr = this.M;
this.accessor.MAIL = (this.M < 0x0200 && this.NCSF);
this.accessor.word = this.B;
cc.store(this);
break;
case 0x30: // P = [C]
this.accessor.addr = this.C;
this.accessor.MAIL = (this.C < 0x0200 && this.NCSF);
cc.fetch(this);
this.P = this.accessor.word;
this.PROF = 1;
break;
default:
throw "Invalid E register value: " + this.E.toString(2);
throw "Invalid E register value: " + eValue.toString(2);
break;
}
this.cycleCount += 6; // assume 6 us memory cycle time (the other option was 4 usec)
this.cycleCount += 6; // assume 6 us memory cycle time (the other option is 4 usec)
if (this.accessor.MAED) {
this.I |= 0x02; // set I02F - memory address/inhibit error
if (this.NCSF || this !== cc.P1) {
@@ -164,15 +183,13 @@ B5500Processor.prototype.access = function(cc, eValue) {
};
/**************************************/
B5500Processor.prototype.adjustAEmpty = function(cc) {
/*
Adjusts the A register so that it is empty pushing the prior
contents of A into B and B into memory, as necessary.
*/
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. */
if (this.AROF) {
if (this.BROF) {
if ((this.S >>> 6) === this.R || !this.NCSF) {
if ((this.S >>> 6) == this.R || !this.NCSF) {
this.I |= 0x04; // set I03F: stack overflow
cc.signalInterrupt();
} else {
@@ -206,12 +223,12 @@ B5500Processor.prototype.adjustAFull = function() {
};
/**************************************/
B5500Processor.prototype.adjustBEmpty = function(cc) {
/* Adjusts the B register so that it is empty pushing the prior
B5500Processor.prototype.adjustBEmpty = function() {
/* Adjusts the B register so that it is empty, pushing the prior
contents of B into memory, as necessary. */
if (this.BROF) {
if ((this.S >>> 6) === this.R || !this.NCSF) {
if ((this.S >>> 6) == this.R || !this.NCSF) {
this.I |= 0x04; // set I03F: stack overflow
cc.signalInterrupt();
} else {
@@ -224,7 +241,7 @@ B5500Processor.prototype.adjustBEmpty = function(cc) {
/**************************************/
B5500Processor.prototype.adjustBFull = function() {
/* Adjusts the B register so that it is full popping the contents of
/* Adjusts the B register so that it is full, popping the contents of
[S] into B, as necessary. */
if (!this.BROF) {
@@ -270,8 +287,8 @@ B5500Processor.prototype.exchangeTOS = function() {
};
/**************************************/
B5500Processor.storeForInterrupt = function(cc, forTest) {
/* Implements the 3011=SFI operator and the parts of SFT that are
B5500Processor.storeForInterrupt = function(forTest) {
/* Implements the 3011=SFI operator and the parts of 3411=SFT that are
common to it. "forTest" implies use from SFT */
var forced = this.Q & 0x0040; // Q07F: Hardware-induced SFI syllable
var saveAROF = this.AROF;
@@ -294,7 +311,9 @@ B5500Processor.storeForInterrupt = function(cc, forTest) {
this.S++;
this.access(0x0B); // [S] = B
}
this.B = this.X + saveAROF * 0x200000000000 + 0xC00000000000; // store CM loop-control word
this.B = this.X + // store CM loop-control word
saveAROF * 0x200000000000 +
0xC00000000000;
this.S++;
this.access(0x0B); // [S] = B
} else {
@@ -373,7 +392,7 @@ B5500Processor.storeForInterrupt = function(cc, forTest) {
cc.HP2F = 1;
cc.P2BF = 0;
if (cc.P2.scheduler) {
cc.cancelTimeout(cc.P2.scheduler);
clearTimeout(cc.P2.scheduler);
cc.P2.scheduler = null;
}
}
@@ -397,7 +416,7 @@ B5500Processor.storeForInterrupt = function(cc, forTest) {
cc.HP2F = 1;
cc.P2BF = 0;
if (cc.P2.scheduler) {
cc.cancelTimeout(cc.P2.scheduler);
clearTimeout(cc.P2.scheduler);
cc.P2.scheduler = null;
}
}
@@ -620,15 +639,15 @@ B5500Processor.prototype.indexDescriptor = function() {
B5500Processor.prototype.buildMSCW = function() {
/* Return a Mark Stack Control Word from current processor state */
return this.F * 0x8000 +
this.SALF * 0x40000000 +
this.MSFF * 0x80000000 +
this.R * 0x200000000 +
0xC00000000000;
};
return this.F * 0x8000 +
this.SALF * 0x40000000 +
this.MSFF * 0x80000000 +
this.R * 0x200000000 +
0xC00000000000;
}
/**************************************/
B5500Processor.prototype.enterSubroutine = function(cc, descriptorCall) {
B5500Processor.prototype.enterSubroutine(descriptorCall) {
/* Enters a subroutine via the present program descriptor in A as part
of an OPDC or DESC syllable. Also handles accidental entry */
var aw = this.A; // local copy of word in A reg
@@ -690,11 +709,9 @@ B5500Processor.prototype.enterSubroutine = function(cc, descriptorCall) {
/**************************************/
B5500Processor.prototype.operandCall = function() {
/*
OPDC, the moral equivalent of "load accumulator" on lesser
machines. Assumes the syllable has already loaded a word into A.
See Figures 6-1, 6-3, and 6-4 in the B5500 Reference Manual
*/
/* OPDC, the moral equivalent of "load accumulator" on lesser
machines. Assumes the syllable has already loaded a word into A.
See Figures 6-1, 6-3, and 6-4 in the B5500 Reference Manual */
var aw; // local copy of A reg value
var interrupted = false; // interrupt occurred
@@ -751,12 +768,10 @@ B5500Processor.prototype.operandCall = function() {
/**************************************/
B5500Processor.prototype.descriptorCall = function() {
/*
DESC, the moral equivalent of "load address" on lesser
machines. Assumes the syllable has already loaded a word into A, and
that the address of that word is in M.
See Figures 6-2, 6-3, and 6-4 in the B5500 Reference Manual
*/
/* DESC, the moral equivalent of "load address" on lesser
machines. Assumes the syllable has already loaded a word into A, and
that the address of that word is in M.
See Figures 6-2, 6-3, and 6-4 in the B5500 Reference Manual */
var aw = this.A; // local copy of A reg value
var interrupted = false; // interrupt occurred
@@ -808,13 +823,11 @@ B5500Processor.prototype.descriptorCall = function() {
/**************************************/
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
*/
/* 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 t1;
var t2;
@@ -1519,18 +1532,16 @@ B5500Processor.prototype.run = function() {
/**************************************/
B5500Processor.prototype.schedule = function schedule() {
/*
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
a timeslice in terms of a number of processor "cycles" of 1 microsecond
each and calls run() to execute at most that number of cycles. run()
counts up cycles until it reaches this limit or some terminating event
(such as a halt), then exits back here. If the processor remains active,
this routine will reschedule itself for an appropriate later time, thereby
throttling the performance and allowing other modules a chance at the
Javascript execution thread.
*/
/* 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
a timeslice in terms of a number of processor "cycles" of 1 microsecond
each and calls run() to execute at most that number of cycles. run()
counts up cycles until it reaches this limit or some terminating event
(such as a halt), then exits back here. If the processor remains active,
this routine will reschedule itself for an appropriate later time, thereby
throttling the performance and allowing other modules a chance at the
Javascript execution thread. */
var delayTime;
var that = schedule.that;