1
0
mirror of https://github.com/pkimpel/retro-b5500.git synced 2026-04-25 20:01:52 +00:00

Implement initial B5500DiskUnit and fix miscelleaneous other minor issues in I/O subsystem.

This commit is contained in:
paul
2013-01-21 01:02:02 +00:00
parent a4ba006748
commit 5c99019b0a
11 changed files with 773 additions and 221 deletions

View File

@@ -42,7 +42,8 @@ function B5500CentralControl() {
// Instance variables and flags
this.poweredUp = 0; // System power indicator
this.unitStatusMask = 0; // Peripheral unit ready-status bitmask
this.unitStatusMask = 0; // Peripheral unit ready-status bitmask
this.unitBusyMask = 0; // Peripheral unit busy-status bitmask
this.PB1L = 0; // 0=> PA is P1, 1=> PB is P1
this.cardLoadSelect = 0; // 0=> load from disk/drum; 1=> load from cards
@@ -115,8 +116,8 @@ B5500CentralControl.unitIndex = [
B5500CentralControl.unitSpecs = {
SPO: {unitIndex: 22, designate: 30, unitClass: B5500SPOUnit},
DKA: {unitIndex: 29, designate: 6, unitClass: null},
DKB: {unitIndex: 28, designate: 12, unitClass: null},
DKA: {unitIndex: 29, designate: 6, unitClass: B5500DiskUnit},
DKB: {unitIndex: 28, designate: 12, unitClass: B5500DiskUnit},
CRA: {unitIndex: 24, designate: 10, unitClass: null},
CRB: {unitIndex: 23, designate: 14, unitClass: null},
CPA: {unitIndex: 25, designate: 10, unitClass: null},
@@ -623,32 +624,29 @@ B5500CentralControl.prototype.interrogateUnitStatus = function() {
};
/**************************************/
B5500CentralControl.prototype.testUnitBusy = function(ioUnit, rw, unit) {
/* Determines whether the unit designate "unit" is currently in use by any other
I/O Unit than the one designated by "ioUnit". "rw" indicates whether the designate
is for writing (0) or reading (1). Returns 0 if not busy */
B5500CentralControl.prototype.testUnitReady = function(index) {
/* Determines whether the unit index "index" is currently in ready status.
Returns 1 if ready, 0 if not ready */
if (ioUnit != "1" && this.IO1 && this.IO1.REMF && this.AD1F && this.IO1.busyUnit == unit) {
return 1;
} else if (ioUnit != "2" && this.IO2 && this.IO2.REMF && this.AD2F && this.IO2.busyUnit == unit) {
return 2;
} else if (ioUnit != "3" && this.IO3 && this.IO3.REMF && this.AD3F && this.IO3.busyUnit == unit) {
return 3;
} else if (ioUnit != "4" && this.IO4 && this.IO4.REMF && this.AD4F && this.IO4.busyUnit == unit) {
return 4;
} else {
return 0; // peripheral unit not in use by any other I/O Unit
}
return (index ? this.bit(this.unitStatusMask, index) : 0);
};
/**************************************/
B5500CentralControl.prototype.testUnitReady = function(rw, unit) {
/* Determines whether the unit designate "unit" is currently in ready status.
"rw" indicates whether the designate is for writing (0) or reading (1).
Returns 1 if ready, 0 if not ready */
var index = B5500CentralControl.unitIndex[rw & 1][unit & 0x1F];
B5500CentralControl.prototype.testUnitBusy = function(index) {
/* Determines whether the unit index "index" is currently in use by any other
I/O Unit. Returns 1 if busy, 0 if not busy */
return (index ? this.bit(this.unitStatusMask, index) : 0);
return (index ? this.bit(this.unitBusyMask, index) : 0);
};
/**************************************/
B5500CentralControl.prototype.setUnitBusy = function(index, busy) {
/* Sets or resets the unit-busy mask bit for unit index "index" */
if (index) {
this.unitBusyMask = (busy ? this.bitSet(this.unitBusyMask, index)
: this.bitReset(this.unitBusyMask, index));
}
};
/**************************************/
@@ -825,19 +823,38 @@ B5500CentralControl.prototype.configureSystem = function() {
function makeSignal(cc, mnemonic) {
switch (mnemonic) {
case "SPO":
return function() {cc.CCI05F = 1; cc.signalInterrupt()};
return function() {
cc.CCI05F = 1;
cc.signalInterrupt();
};
break;
case "LPA":
return function() {cc.CCI06F = 1; cc.signalInterrupt()};
return function() {
cc.setUnitBusy(27, 0);
cc.CCI06F = 1;
cc.signalInterrupt();
};
break;
case "LPB":
return function() {cc.CCI07F = 1; cc.signalInterrupt()};
return function() {
cc.setUnitBusy(26, 0);
cc.CCI07F = 1;
cc.signalInterrupt();
};
break;
case "DKA":
return function() {cc.CCI15F = 1; cc.signalInterrupt()};
return function() {
cc.setUnitBusy(29, 0);
cc.CCI15F = 1;
cc.signalInterrupt();
};
break;
case "DKB":
return function() {cc.CCI16F = 1; cc.signalInterrupt()};
return function() {
cc.setUnitBusy(28, 0);
cc.CCI16F = 1;
cc.signalInterrupt();
};
break;
default:
return function() {};

View File

@@ -45,7 +45,7 @@ function B5500IOUnit(ioUnitID, cc) {
this.ioUnitID = ioUnitID; // I/O Unit ID ("1", "2", "3", or "4")
this.cc = cc; // Reference back to Central Control module
this.scheduler = null; // Reference to current setTimeout id
this.forkHandle = null; // Reference to current setTimeout id
this.accessor = { // Memory access control block
requestorID: ioUnitID, // Memory requestor ID
addr: 0, // Memory address
@@ -55,8 +55,6 @@ function B5500IOUnit(ioUnitID, cc) {
MAED: 0 // Truthy if memory address/inhibit error
};
this.schedule.that = this; // Establish context for when called from setTimeout()
// Establish a buffer for the peripheral modules to use during their I/O.
// The size is sufficient for 63 disk sectors, rounded up to the next 8KB.
this.bufferArea = new ArrayBuffer(16384);
@@ -78,7 +76,7 @@ B5500IOUnit.BICtoANSI = [ // Index by 6-bit BIC to get 8-bit ANSI
0x7C,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50, // 20-27, @40-47
0x51,0x52,0x24,0x2A,0x2D,0x29,0x3B,0x7B, // 28-2F, @50-57
0x20,0x2F,0x53,0x54,0x55,0x56,0x57,0x58, // 30-37, @60-67
0x59,0x5A,0x2C,0x25,0x21,0x3D,0x5D,0x23]; // 38-3F, @70-77
0x59,0x5A,0x2C,0x25,0x21,0x3D,0x5D,0x22]; // 38-3F, @70-77
B5500IOUnit.BICtoBCLANSI = [ // Index by 6-bit BIC to get 8-bit BCL-as-ANSI code
0x23,0x31,0x32,0x33,0x34,0x35,0x36,0x37, // 00-07, @00-07
@@ -88,7 +86,7 @@ B5500IOUnit.BICtoBCLANSI = [ // Index by 6-bit BIC to get 8-bit BCL-a
0x24,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50, // 20-27, @40-47
0x51,0x52,0x2A,0x2D,0x7C,0x29,0x3B,0x7B, // 28-2F, @50-57
0x2B,0x41,0x42,0x43,0x44,0x45,0x46,0x47, // 30-37, @60-67
0x48,0x49,0x5B,0x26,0x2E,0x28,0x3C,0X7E]; // 38-3F, @70-77
0x48,0x49,0x5B,0x26,0x2E,0x28,0x3C,0x7E]; // 38-3F, @70-77
B5500IOUnit.ANSItoBIC = [ // Index by 8-bit ANSI to get 6-bit BIC (upcased, invalid=>"?")
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // 00-0F
@@ -175,6 +173,10 @@ B5500IOUnit.prototype.clear = function() {
this.totalCycles = 0; // Total cycles executed on this I/O Unit
this.ioUnitTime = 0; // Total I/O Unit running time, based on cycles executed
this.ioUnitSlack = 0; // Total I/O Unit throttling delay, milliseconds
if (this.forkHandle) {
clearTimeout(this.forkHandle);
}
};
/**************************************/
@@ -538,7 +540,8 @@ B5500IOUnit.prototype.finish = function () {
break;
}
this.busyUnit = 0; // zero so CC won't think unit is busy
this.busy = 0; // zero so CC won't think I/O unit is busy
this.busyUnit = 0;
this.cc.signalInterrupt();
};
@@ -554,7 +557,8 @@ B5500IOUnit.prototype.makeFinish = function(f) {
B5500IOUnit.prototype.finishGeneric = function(errorMask, length) {
/* Handles a generic I/O finish when no word-count update or input data
transfer is needed. Can also be used to apply common error mask posting
at the end of specialized finish handlers */
at the end of specialized finish handlers. Note that this turns off the
busyUnit mask bit in CC */
if (errorMask & 0x01) {this.D32F = 1}
if (errorMask & 0x02) {this.D31F = 1}
@@ -563,26 +567,198 @@ B5500IOUnit.prototype.finishGeneric = function(errorMask, length) {
if (errorMask & 0x10) {this.D28F = 1}
if (errorMask & 0x20) {this.D27F = 1}
if (errorMask & 0x40) {this.D26F = 1}
this.cc.setUnitBusy(this.busyUnit, 0);
this.finish();
};
/**************************************/
B5500IOUnit.prototype.finishSPORead = function(errorMask, length) {
/* Handles I/O finish for a SPO keyboard input operation */
var count;
count = this.storeBufferWithGM(length, 0, 1, 0x7FFF);
this.storeBufferWithGM(length, 0, 1, 0x7FFF);
this.finishGeneric(errorMask, length);
};
/**************************************/
B5500IOUnit.prototype.initiate = function() {
/* Initiates an I/O operation on this I/O Unit */
B5500IOUnit.prototype.finishDiskRead = function(errorMask, length) {
/* Handles I/O finish for a DFCU data read operation */
var segWords = Math.floor((length+7)/8);
var memWords = (this.D23F ? this.DwordCount : segWords);
if (segWords < memWords) {
memWords = segWords;
}
this.storeBuffer(length, 0, this.D21F, memWords);
this.finishGeneric(errorMask, length);
};
/**************************************/
B5500IOUnit.prototype.initiateDiskIO = function(u) {
/* Initiates an I/O to the Disk File Control Unit. The disk address is fetched from
the first word of the memory area and converted to binary for the DFCU module. Read
check and interrogate operations are determined from their respective IOD bits. If
it's a read data operation, we request the specified number of segments from the disk
and will sort out word count issues in finishDiskRead(). If it's a write data operation,
we truncate or pad the data from memory as appropriate and request a write of the
specified number of segments */
var c; // address char
var memWords; // number of memory words to transfer
var p = 1; // address digit power
var w; // current memory word value
var x; // temp variable
var segAddr = 0; // disk segment address
var segs = this.LP; // I/O size in segments
var segWords = segs*30; // I/O size in words
var segChars = segWords*8; // I/O size in characters
if (this.fetch(this.Daddress)) { // fetch the segment address from first buffer word
this.finish();
} else {
this.Daddress++; // bump memory address past the seg address word
w = this.W; // convert address word to binary
for (x=0; x<7; x++) {
c = w % 0x40; // get low-order six bits of word
segAddr += (c % 0x10)*p; // isolate the numeric portion and accumulate
w = (w-c)/0x40; // shift word right six bits
p *= 10; // bump power for next digit
}
if (this.D18F) { // mem inhibit => read check operation
u.readCheck(this.makeFinish(this.finishGeneric), segChars, segAddr);
} else if (this.D23F && this.DwordCount == 0) {
if (this.D24F) { // read interrogate operation
u.readInterrogate(this.makeFinish(this.finishGeneric), segAddr);
} else { // write interrogate operation
u.writeInterrogate(this.makeFinish(this.finishGeneric), segAddr);
}
} else if (this.D24F) { // it's a read data operation
u.read(this.makeFinish(this.finishDiskRead), this.buffer, segChars, this.D21F, segAddr);
} else { // it's a write data operation
memWords = (this.D23F ? this.DwordCount : segWords);
if (segWords <= memWords) { // transfer size is limited by number of segs
this.fetchBuffer(this.D21F, segWords);
} else { // transfer size is limited by word count
x = this.fetchBuffer(this.D21F, memWords);
c = (this.D21F ? 0x00 : 0x2B); // pad "0" if binary, " " if alpha (as BCL)
while (x < segChars) { // pad remainder of buffer up to seg count
this.buffer[x++] = c;
}
}
u.write(this.makeFinish(this.finishGeneric), this.buffer, segChars, this.D21F, segAddr);
}
}
};
/**************************************/
B5500IOUnit.prototype.forkIO = function forkIO() {
/* Asychrounously nitiates an I/O operation on this I/O Unit for a peripheral device */
var addr; // memory address
var chars; // I/O memory transfer length
var index; // unit index
var u; // peripheral unit object
var x;
var x; // temp number variable
this.forkHandle = null; // clear the setTimeout() handle
x = this.D; // explode the D-register into its fields
this.Dunit = this.cc.fieldIsolate(x, 3, 5);
this.DwordCount = this.cc.fieldIsolate(x, 8, 10);
x = x % 0x40000000; // isolate low-order 30 bits
this.D18F = (x >>> 29) & 1; // memory inhibit
this.D21F = (x >>> 26) & 1; // mode
this.D22F = (x >>> 25) & 1; // direction (for tapes)
this.D23F = (x >>> 24) & 1; // use word counter
this.D24F = (x >>> 23) & 1; // write/read
this.LP = (x >>> 15) & 0x3F; // save control bits for disk, drum, and printer
this.Daddress = x % 0x8000;
this.busyUnit = index = B5500CentralControl.unitIndex[this.D24F & 1][this.Dunit & 0x1F];
if (this.cc.testUnitBusy(index)) {
this.D32F = 1; // set unit busy error
this.finish();
} else if (!this.cc.testUnitReady(index)) {
this.D30F = 1; // set unit not-ready error
this.finish();
} else {
this.cc.setUnitBusy(index, 1);
u = this.cc.unit[index];
switch(this.Dunit) {
// disk designates
case 6:
case 12:
this.initiateDiskIO(u);
break;
// printer designates
case 22:
case 26:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// datacom designate
case 16:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// card #1 reader/punch
case 10:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// card #2 reader
case 14:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// SPO designate
case 30:
if (this.D24F) {
u.read(this.makeFinish(this.finishSPORead), this.buffer, 0x7FFF, 0, 0);
} else {
chars = this.fetchBufferWithGM(1, 0x7FFF);
u.write(this.makeFinish(this.finishGeneric), this.buffer, chars, 0, 0);
}
break;
// magnetic tape designates
case 1: case 3: case 5: case 7: case 9: case 11: case 13: case 15:
case 17: case 19: case 21: case 23: case 25: case 27: case 29: case 31:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// drum designates
case 4:
case 8:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// paper tape designates
case 18:
case 20:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// illegal designates
default:
this.D30F = 1; // report invalid unit as not ready
this.finish();
break;
} // switch this.Dunit
}
};
/**************************************/
B5500IOUnit.prototype.initiate = function() {
/* Initiates an I/O operation on this I/O Unit. When P1 executes an IIO instruction,
it calls the CentralControl.initiateIO() function, which selects an idle I/O Unit and
calls this function for that unit. Thus, at entry we are still running on top of the
processor. This routine merely fetches the IOD from memory and then schedules forkIO()
to run asynchronously. Then we exit back through CC and into P1, thus allowing the
actual I/O operation to run asynchronously from the processor. Of course, in a browser
environment, all of the Javascript action occurs on one thread, so this allows us to
multiplex what are supposed to be asynchronous operations on that thread */
var that = this; // Establish object context for the callback
this.clearD();
this.AOFF = 0;
@@ -592,129 +768,13 @@ B5500IOUnit.prototype.initiate = function() {
this.finish();
} else {
this.EXNF = 1;
this.Daddress = addr = this.W % 0x8000;
if (this.fetch(addr)) { // fetch the IOD from that address
this.Daddress = this.W % 0x8000;
if (this.fetch(this.Daddress)) {// fetch the IOD from that address
this.finish();
} else {
this.D31F = 0; // reset the IOD-fetch error condition
this.D = x = this.W; // explode the D-register into its fields
this.Dunit = this.cc.fieldIsolate(x, 3, 5);
this.DwordCount = this.cc.fieldIsolate(x, 8, 10);
x = x % 0x40000000; // isolate low-order 30 bits
this.D18F = (x >>> 29) & 1; // memory inhibit
this.D21F = (x >>> 26) & 1; // mode
this.D22F = (x >>> 25) & 1; // direction (for tapes)
this.D23F = (x >>> 24) & 1; // use word counter
this.D24F = (x >>> 23) & 1; // write/read
this.LP = (x >>> 15) & 0x3F;// save control bits for drum and printer
this.Daddress = x % 0x8000;
this.busyUnit = index = B5500CentralControl.unitIndex[this.D24F & 1][this.Dunit & 0x1F];
if (this.cc.testUnitBusy(this.ioUnitID, this.busyUnit)) {
this.D32F = 1; // set unit busy error
this.finish();
} else if (!this.cc.testUnitReady(this.D24F, this.Dunit)) {
this.D30F = 1; // set unit not-ready error
this.finish();
} else {
u = this.cc.unit[index];
switch(this.Dunit) {
// disk designates
case 6:
case 12:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// printer designates
case 22:
case 26:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// datacom designate
case 16:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// card #1 reader/punch
case 10:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// card #2 reader
case 14:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// SPO designate
case 30:
if (this.D24F) {
u.read(this.makeFinish(this.finishSPORead), this.buffer, 0x7FFF, 0, 0);
} else {
chars = this.fetchBufferWithGM(1, 0x7FFF);
u.write(this.makeFinish(this.finishGeneric), this.buffer, chars, 0, 0);
}
break;
// magnetic tape designates
case 1: case 3: case 5: case 7: case 9: case 11: case 13: case 15:
case 17: case 19: case 21: case 23: case 25: case 27: case 29: case 31:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// drum designates
case 4:
case 8:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// paper tape designates
case 18:
case 20:
this.D30F = 1; this.finish(); // >>> temp until implemented <<<
break;
// illegal designates
default:
this.D30F = 1; // report invalid unit as not ready
this.finish();
break;
} // switch this.Dunit
}
this.D = this.W;
this.forkHandle = setTimeout(function() {that.forkIO()}, 0);
}
}
};
/**************************************/
B5500IOUnit.prototype.run = function() {
};
/**************************************/
B5500IOUnit.prototype.schedule = function schedule() {
/* Schedules the I/O Unit 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 I/O Unit "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 I/O Unit 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;
that.scheduler = null;
that.cycleLimit = B5500IOUnit.timeSlice;
that.cycleCount = 0;
that.run();
that.totalCycles += that.cycleCount
that.ioUnitTime += that.cycleCount;
if (that.busy) {
delayTime = that.ioUnitTime/1000 - new Date().getTime();
that.ioUnitSlack += delayTime;
that.scheduler = setTimeout(that.schedule, (delayTime < 0 ? 1 : delayTime));
}
};

View File

@@ -39,7 +39,7 @@ var B5500SystemConfiguration = {
units: {
SPO: true, // SPO keyboard/printer
DKA: false, // Disk File Control A
DKA: true, // Disk File Control A
DKB: false, // Disk File Control B
CRA: false, // Card Reader A
CRB: false, // Card Reader B

View File

@@ -44,13 +44,6 @@
*
* To use, select the .bcd file using the file selection control on the page.
* The script writes a log of activity to the web page.
*
* This version outputs the converted data by opening a browser window for
* each file and inserting the converted text into a <textarea> element in
* that window. From there you can copy the text and paste into another
* program that can save the data to a local filesystem. This approach is
* being used until we can figure out a better way to get data out of a
* browser environment and into a local filesystem. Ugh.
************************************************************************
* 2012-12-29 P.Kimpel
* Original version, from B5500LibMaintExtract.html.
@@ -1824,8 +1817,8 @@ window.onload = function() {
/**************************************/
function openDatabase(name, version) {
/* Attempts to open the disk subsystem database for the specified "name"
and "version". Returns the IDB database object if successful, or null if
unsuccessful */
and "version". Stores the IDB database object in "disk" if successful, or
stores null if unsuccessful */
var db = null;
var req;
@@ -1848,11 +1841,11 @@ window.onload = function() {
$$("ColdstartBtn").disabled = false;
$$("FileSelector").disabled = false;
dumpDisk(); // <<<<<<<<<<<<<< DEBUG <<<<<<<<<<<<<<<<<
// dumpDisk(); // <<<<<<<<<<<<<< DEBUG <<<<<<<<<<<<<<<<<
disk.transaction("CONFIG").objectStore("CONFIG").get(0).onsuccess = function(ev) {
config = ev.target.result;
}
};
};
}

View File

@@ -7,12 +7,14 @@
<meta http-equiv="Content-Style-Type" content="text/css">
<link id=defaultStyleSheet rel=stylesheet type="text/css" href="B5500Console.css">
<script src="B5500SPOUnit.js"></script>
<script src="/B5500/B5500DummyUnit.js"></script>
<script src="/B5500/B5500SPOUnit.js"></script>
<script src="/B5500/B5500DiskUnit.js"></script>
<script src="../emulator/B5500SystemConfiguration.js"></script>
<script src="../emulator/B5500CentralControl.js"></script>
<script src="../emulator/B5500Processor.js"></script>
<script src="../emulator/B5500IOUnit.js"></script>
<script src="/B5500EMU/B5500SystemConfiguration.js"></script>
<script src="/B5500EMU/B5500CentralControl.js"></script>
<script src="/B5500EMU/B5500Processor.js"></script>
<script src="/B5500EMU/B5500IOUnit.js"></script>
<script>
window.onload = function() {
@@ -33,7 +35,7 @@ window.onload = function() {
aNormal.className = "yellowButton";
aControl.className = "yellowButton yellowLit";
delay = Math.log(delay+1)*250;
} else {
} else {
paState = 1;
aNormal.className = "yellowButton yellowLit";
aControl.className = "yellowButton";
@@ -96,13 +98,13 @@ window.onload = function() {
}
return true;
};
/***** window.onload() outer block *****/
document.getElementById("PowerOnBtn").onclick = function() {
return PowerOnBtn_Click();
};
document.getElementById("PowerOffBtn").onclick = function() {
return PowerOffBtn_Click();
};
@@ -124,7 +126,7 @@ window.onload = function() {
<button id=AControlBtn class=yellowButton>A CONTROL</button>
<button id=BNormalBtn class=yellowButton>B NORMAL</button>
<button id=BControlBtn class=yellowButton>B CONTROL</button>
<button id=PowerOnBtn class=whiteButton>POWER<br>ON</button>
<button id=PowerOffBtn class=blackButton>POWER OFF</button>

470
webUI/B5500DiskUnit.js Normal file
View File

@@ -0,0 +1,470 @@
/***********************************************************************
* retro-b5500/emulator B5500DiskUnit.js
************************************************************************
* Copyright (c) 2013, Nigel Williams and Paul Kimpel.
* Licensed under the MIT License, see
* http://www.opensource.org/licenses/mit-license.php
************************************************************************
* B5500 Disk File Control Unit module.
*
* Defines a peripheral unit type for the Disk File Control Unit used with
* Burroughs Model I and Model IB Head-per-Track disk units. The DFCU is the
* addressable unit to the B5500. This module manages the Electronic Units (EU)
* and Storage Units (SU) that make up the physical disk storage facility.
*
* Physical storage in this implementation is provided by a W3C IndexedDB
* database local to the browser in which the emulator is running. This database
* must have been previously initialized; see tools/B5500ColdLoader.html.
*
* The database consists of a CONFIG object store and some number of EUn object
* stores, where n is in 0..9. The CONFIG store contains an "eus" member that
* specifies the total number of EU objects, plus an "EUn" member that specicies
* the size of that EU in 240-character segments. There may be gaps in the EU
* numbering.
*
* Within an EU, segments are represented in the database as 240-byte Uint8Array
* objects, each with a database key corresponding to its numeric segment address.
* The segments are an EU are not pre-allocated, but are created as they are
* written to by IDB put() methods. When reading, any unallocated segments are
* returned with their bytes set to binary zero, which will be translated to
* BIC "?" by the IOU for both binary and alpha modes.
*
* At present, disk write lockout is not supported. When there are two DFCUs in
* the system, the presence of a Disk File Exchange is assumed, allowing either
* DFCU to access any EU. Thus this implementation is currently limited to a
* maximum of 10 EUs (480 million characters).
*
* Note that all disk I/O is done in units of 240-character segments. The
* interface with the I/O Unit uses lengths in terms of characters, however.
* All lengths SHOULD be multiples of 240 characters; any other values will be
* rounded up to the next multiple of 240. The I/O Unit is responsible for any
* padding or truncation to account for differences between the segment count
* and word count in the IOD. This implementation ignores binary vs. alpha mode
* and assumes the IOU does any necessary translation.
*
* The starting disk segment address for an I/O is passed in the "control"
* parameter to each of the I/O methods. This is an an alphanumeric value in
* the B5500 memory and I/O Unit. The I/O unit translates this value to binary
* for the "control" parameter. The low-order six decimal digits of the value
* comprise the segment address within the EU. The seventh decimal digit is the
* EU number. Any other portion of the value is ignored.
*
* The DFCU's "read check" operation is asynchronous with respect to the IOU, and
* the I/O operation itself completes almost immediately. Because of this, any
* error reporting for the read check is deferred until the next I/O operation
* (typically an interrogate) against the unit. Therefore, the error mask is
* cleared at the end of each disk I/O operation (except for read check) instead
* of at the beginning, and new errors are OR-ed with any errors persisting from
* the prior operation.
*
* This module attempts to simulate actual disk activity times by delaying the
* finish() call by an amount of time computed from the numbers of sectors
* requested and the Model I SU's average 96KC transfer rate, plus a random
* distribution across its 40ms max rotational latency time.
************************************************************************
* 2013-01-19 P.Kimpel
* Original version, cloned from B5500DummyUnit.js.
***********************************************************************/
"use strict";
/**************************************/
function B5500DiskUnit(mnemonic, index, designate, statusChange, signal) {
/* Constructor for the DiskUnit object */
this.mnemonic = mnemonic; // Unit mnemonic
this.index = index; // Ready-mask bit number
this.designate = designate; // IOD unit designate number
this.statusChange = statusChange; // external function to call for ready-status change
this.signal = signal; // external function to call for special signals (e.g,. SPO input request)
this.clear();
}
B5500DiskUnit.prototype.dbName = "B5500DiskUnit"; // IndexedDB database name
B5500DiskUnit.prototype.dbVersion = 1; // current IDB database version
B5500DiskUnit.prototype.configName = "CONFIG"; // database configuration store name
B5500DiskUnit.prototype.euPrefix = "EU"; // prefix for EU object store names
B5500DiskUnit.prototype.charXferRate = 96000; // avg. transfer rate, characters/sec (Model I SU)
B5500DiskUnit.prototype.maxLatency = 0.040; // max rotational latency, sec (Model I SU)
/**************************************/
B5500DiskUnit.prototype.clear = function() {
/* Initializes (and if necessary, creates) the processor state */
this.ready = false; // ready status
this.busy = false; // busy status
this.activeIOUnit = 0; // I/O unit currently using this device
this.errorMask = 0; // error mask for finish()
this.finish = null; // external function to call for I/O completion
this.startStamp = null; // I/O starting timestamp
this.config = null; // copy of CONFIG store contents
this.disk = null; // the IDB database object
this.openDatabase(); // attempt to open the IDB database
};
/**************************************/
B5500DiskUnit.genericDBError = function(ev) {
/* Formats a generic alert when otherwise-unhandled database errors occur */
var disk = ev.currentTarget.result;
this.errorMask |= 0x20; // set a generic disk-parity error
alert("Database for \"" + this.mnemonic + "\" error: " + ev.target.result.error);
this.finish(this.errorMask, 0);
this.errorMask = 0;
};
/**************************************/
B5500DiskUnit.prototype.copySegment = function(seg, buffer, offset) {
/* Copies the bytes from a single segment Uint8Array object to "buffer" starting
at "offset" for 240 bytes. If "seg" is undefined, copies zero bytes instead */
var x;
if (seg) {
for (x=0; x<240; x++) {
buffer[offset+x] = seg[x];
}
} else {
for (x=offset+239; x>=0; x--) {
buffer[x] = 0;
}
}
};
/**************************************/
B5500DiskUnit.prototype.openDatabase = function() {
/* Attempts to open the disk subsystem database specified by this.dbName and
this.dbVersion. If successful, sets this.disk to the IDB object and sets the
DFCU to ready status */
var req;
var db = null;
var that = this;
that.statusChange(0); // initially force DFCU status to not ready
req = window.indexedDB.open(that.dbName, that.dbVersion);
req.onerror = function(ev) {
alert("Cannot open " + that.mnemonic + " database: " + ev.target.error);
};
req.onblocked = function(ev) {
alert("Database.open is blocked -- cannot continue");
};
req.onupgradeneeded = function(ev) {
alert("Database requires version upgrade -- cannot continue");
};
req.onsuccess = function(ev) {
that.disk = ev.target.result; // save the object reference globally for later use
that.disk.onerror = function(ev) {
alert("Database for \"" + this.mnemonic + "\" open error: " + ev.target.result.error);
};
that.disk.transaction("CONFIG").objectStore("CONFIG").get(0).onsuccess = function(ev) {
that.config = ev.target.result;
that.statusChange(1); // report the DFCU as ready to Central Control
// Set up the generic error handler
that.disk.onerror = function(ev) {
that.genericIDBError(ev);
};
};
};
};
/**************************************/
B5500DiskUnit.prototype.read = function(finish, buffer, length, mode, control) {
/* Initiates a read operation on the unit. "length" is in characters; segment address
is in "control". "mode" is ignored (any translation would have been done by IOU) */
var bx = 0; // current buffer offset
var euSize; // max seg size for EU
var finishTime; // predicted time of I/O completion, ms
var range; // key range for multi-segment read
var req; // IDB request object
var that = this; // local object context
var txn; // IDB transaction object
this.finish = finish; // for global error handler
var segs = Math.floor((length+239)/240);
var segAddr = control % 1000000; // starting seg address
var euNumber = (control % 10000000 - segAddr)/1000000;
var euName = this.euPrefix + euNumber;
var endAddr = segAddr+segs-1; // ending seg address
euSize = this.config[euName];
if (!euSize) { // EU does not exist
finish(this.errorMask | 0x20, 0); // set D27F for EU not ready
this.errorMask = 0;
} else if (segAddr < 0) {
finish(this.errorMask | 0x20, 0); // set D27F for invalid starting seg address
this.errorMask = 0;
} else {
if (endAddr >= euSize) { // if read is past end of disk
this.errorMask |= 0x20; // set D27F for invalid seg address
segs = euSize-segAddr; // compute number of segs possible to read
length = segs*240; // recompute length and ending seg address
endAddr = euSize-1;
}
finishTime = new Date().getTime() +
(Math.random()*this.maxLatency + segs*240/this.charXferRate)*1000;
// No length specified, so just finish the I/O
if (segs < 1) {
finish(this.errorMask, 0);
this.errorMask = 0;
// A single-segment read
} else if (segs < 2) {
req = this.disk.transaction(euName).objectStore(euName).get(segAddr);
req.onsuccess = function(ev) {
that.copySegment(ev.target.result, buffer, 0);
setTimeout(function() {
finish(that.errorMask, length);
that.errorMask = 0;
}, finishTime - new Date().getTime());
};
// A multi-segment read
} else {
range = window.IDBKeyRange.bound(segAddr, endAddr);
txn = this.disk.transacation(euName);
req = txn.objectStore(euName).openCursor(range);
req.onsuccess = function(ev) {
var cursor = ev.target.result;
if (cursor) { // found a segment at some address in range
// fill buffer with zeroes for any unallocated segments
while (cursor.key > segAddr) {
that.copySegment(null, buffer, bx);
bx += 240;
segAddr++;
}
// copy the segment data to the buffer and request next seg
that.copySegment(cursor.value, buffer, bx);
bx += 240;
segAddr++;
cursor.continue();
} else { // at end of range
// fill buffer with zeroes for any remaining segments in range
while (endAddr > segAddr) {
that.copySegment(null, buffer, bx);
bx += 240;
segAddr++;
}
setTimeout(function() {
finish(that.errorMask, length);
that.errorMask = 0;
}, finishTime - new Date().getTime());
}
};
}
}
};
/**************************************/
B5500DiskUnit.prototype.space = function(finish, length, control) {
/* Initiates a space operation on the unit */
finish(0x04, 0); // report unit not ready
};
/**************************************/
B5500DiskUnit.prototype.write = function(finish, buffer, length, mode, control) {
/* Initiates a write operation on the unit. "length" is in characters; segment address
is in "control". "mode" is ignored (any translation will done by the IOU) */
var bx = 0; // current buffer offset
var eu; // IDB object store for EU
var euSize; // max seg size for EU
var finishTime; // predicted time of I/O completion, ms
var req; // IDB request object
var that = this; // local object context
var txn; // IDB transaction object
this.finish = finish; // for global error handler
var segs = Math.floor((length+239)/240);
var segAddr = control % 1000000; // starting seg address
var euNumber = (control % 10000000 - segAddr)/1000000;
var euName = this.euPrefix + euNumber;
var endAddr = segAddr+segs-1; // ending seg address
euSize = this.config[euName];
if (!euSize) { // EU does not exist
finish(this.errorMask | 0x20, 0); // set D27F for EU not ready
this.errorMask = 0;
} else if (segAddr < 0) {
finish(this.errorMask | 0x20, 0); // set D27F for invalid starting seg address
this.errorMask = 0;
} else {
if (endAddr >= euSize) { // if read is past end of disk
this.errorMask |= 0x20; // set D27F for invalid seg address
segs = euSize-segAddr; // compute number of segs possible to read
length = segs*240; // recompute length and ending seg address
endAddr = euSize-1;
}
finishTime = new Date().getTime() +
(Math.random()*this.maxLatency + segs*240/this.charXferRate)*1000;
// No length specified, so just finish the I/O
if (segs < 1) {
finish(this.errorMask, 0);
this.errorMask = 0;
// Do the write
} else {
txn = this.disk.transaction(euName, "readwrite")
txn.oncomplete = function(ev) {
setTimeout(function() {
finish(that.errorMask, length);
that.errorMask = 0;
}, finishTime - new Date().getTime());
};
eu = txn.objectStore(euName);
for (; segAddr<=endAddr; segAddr++) {
eu.put(buffer.subarray(bx, bx+240), segAddr);
bx += 240;
}
}
}
};
/**************************************/
B5500DiskUnit.prototype.erase = function(finish, length) {
/* Initiates an erase operation on the unit */
finish(0x04, 0); // report unit not ready
};
/**************************************/
B5500DiskUnit.prototype.rewind = function(finish) {
/* Initiates a rewind operation on the unit */
finish(0x04, 0); // report unit not ready
};
/**************************************/
B5500DiskUnit.prototype.readCheck = function(finish, length, control) {
/* Initiates a read check operation on the unit. "length" is in characters;
segment address is in "control". "mode" is ignored. This is essentially a
read without any data transfer to memory. Note that the errorMask is NOT
zeroed at the end of the I/O -- it will be reported with the next I/O */
var euSize; // max seg size for EU
var finishTime; // predicted time of I/O completion, ms
var range; // key range for multi-segment read
var req; // IDB request object
var that = this; // local object context
var txn; // IDB transaction object
this.finish = finish; // for global error handler
var segs = Math.floor((length+239)/240);
var segAddr = control % 1000000; // starting seg address
var euNumber = (control % 10000000 - segAddr)/1000000;
var euName = this.euPrefix + euNumber;
var endAddr = segAddr+segs-1; // ending seg address
this.errorMask = 0; // clear any prior error mask
euSize = this.config[euName];
if (!euSize) { // EU does not exist
finish(this.errorMask | 0x20, 0); // set D27F for EU not ready
// do NOT clear the error mask
} else if (segAddr < 0) {
finish(this.errorMask | 0x20, 0); // set D27F for invalid starting seg address
// do NOT clear the error mask
} else {
if (endAddr >= euSize) { // if read is past end of disk
this.errorMask |= 0x20; // set D27F for invalid seg address
segs = euSize-segAddr; // compute number of segs possible to read
length = segs*240; // recompute length and ending seg address
endAddr = euSize-1;
}
finishTime = new Date().getTime() +
(Math.random()*this.maxLatency + segs*240/this.charXferRate)*1000;
// No length specified, so just finish the I/O
if (segs < 1) {
finish(this.errorMask, 0);
// do NOT clear the error mask
// A multi-segment read
} else {
range = window.IDBKeyRange.bound(segAddr, endAddr);
txn = this.disk.transacation(euName);
req = txn.objectStore(euName).openCursor(range);
req.onsuccess = function(ev) {
var cursor = ev.target.result;
if (cursor) { // found a segment at some address in range
cursor.continue();
} else { // at end of range
setTimeout(function() {
finish(that.errorMask, length);
// do NOT clear the error mask
}, finishTime - new Date().getTime());
}
};
}
}
};
/**************************************/
B5500DiskUnit.prototype.readInterrogate = function(finish, control) {
/* Initiates a read interrogate operation on the unit. This serves only to
check the addresss for validity and to return any errorMask from a prior
read check operation. This implementation assumes completion will be delayed
by a random amount of time based on rotational latency for the EU to search for
the address */
var euSize; // max seg size for EU
var segAddr = control % 1000000; // starting seg address
var euNumber = (control % 10000000 - segAddr)/1000000;
var euName = this.euPrefix + euNumber;
this.finish = finish; // for global error handler
euSize = this.config[euName];
if (!euSize) { // EU does not exist
finish(this.errorMask | 0x20, 0); // set D27F for EU not ready
this.errorMask = 0;
} else {
if (segAddr < 0 || segAddr >= euSize) { // if read is past end of disk
this.errorMask |= 0x20; // set D27F for invalid seg address
}
setTimeout(function() {
finish(that.errorMask, length);
this.errorMask = 0;
}, Math.random()*this.maxLatency*1000);
}
};
/**************************************/
B5500DiskUnit.prototype.writeInterrogate = function (finish, control) {
/* Initiates a write interrogate operation on the unit. This serves only to
check the addresss for validity and to return any errorMask from a prior
read check operation. This implementation assumes completion will be delayed
by a random amount of time based on rotational latency for the EU to search for
the address */
/* Note: until disk write lockout is implemented, this operation is identical
to readInterrogate() */
var euSize; // max seg size for EU
var segAddr = control % 1000000; // starting seg address
var euNumber = (control % 10000000 - segAddr)/1000000;
var euName = this.euPrefix + euNumber;
this.finish = finish; // for global error handler
euSize = this.config[euName];
if (!euSize) { // EU does not exist
finish(this.errorMask | 0x20, 0); // set D27F for EU not ready
this.errorMask = 0;
} else {
if (segAddr < 0 || segAddr >= euSize) { // if read is past end of disk
this.errorMask |= 0x20; // set D27F for invalid seg address
}
setTimeout(function() {
finish(that.errorMask, length);
this.errorMask = 0;
}, Math.random()*this.maxLatency*1000);
}
};

View File

@@ -27,7 +27,6 @@ function B5500DummyUnit(mnemonic, index, designate, statusChange, signal) {
this.designate = designate; // IOD unit designate number
this.statusChange = statusChange; // external function to call for ready-status change
this.signal = signal; // external function to call for special signals (e.g,. SPO input request)
this.finish = null; // external function to call for I/O completion
this.clear();
}
@@ -39,6 +38,10 @@ B5500DummyUnit.prototype.clear = function() {
this.ready = false; // ready status
this.busy = false; // busy status
this.activeIOUnit = 0; // I/O unit currently using this device
this.errorMask = 0; // error mask for finish()
this.finish = null; // external function to call for I/O completion
this.buffer = null; //
};
/**************************************/
@@ -77,21 +80,21 @@ B5500DummyUnit.prototype.rewind = function(finish) {
};
/**************************************/
B5500DummyUnit.prototype.readCheck = function(finish, length) {
B5500DummyUnit.prototype.readCheck = function(finish, length, control) {
/* Initiates a read check operation on the unit */
finish(0x04, 0); // report unit not ready
};
/**************************************/
B5500DummyUnit.prototype.readInterrogate = function(finish) {
B5500DummyUnit.prototype.readInterrogate = function(finish, control) {
/* Initiates a read interrogate operation on the unit */
finish(0x04, 0); // report unit not ready
};
/**************************************/
B5500DummyUnit.prototype.writeInterrogate = function (finish) {
B5500DummyUnit.prototype.writeInterrogate = function (finish, control) {
/* Initiates a write interrogate operation on the unit */
finish(0x04, 0); // report unit not ready

View File

@@ -29,7 +29,6 @@ function B5500SPOUnit(mnemonic, unitIndex, designate, statusChange, signal) {
this.designate = designate; // IOD unit designate number
this.statusChange = statusChange; // external function to call for ready-status change
this.signal = signal; // external function to call for special signals (e.g,. SPO input request)
this.finish = null; // external function to call for I/O completion
this.clear();
@@ -76,15 +75,17 @@ B5500SPOUnit.prototype.clear = function() {
this.ready = false; // ready status
this.busy = false; // busy status
this.activeIOUnit = 0; // I/O unit currently using this device
this.spoState = this.spoNotReady; // Current state of SPO interface
this.spoLocalRequested = false; // LOCAL button pressed while active
this.errorMask = 0; // error mask for finish()
this.errorMask = 0; // error mask for finish()
this.finish = null; // external function to call for I/O completion
this.buffer = null;
this.bufLength = 0;
this.bufIndex = 0;
this.printCol = 0;
this.nextCharTime = 0;
this.spoState = this.spoNotReady; // Current state of SPO interface
this.spoLocalRequested = false; // LOCAL button pressed while active
};
/**************************************/
@@ -551,21 +552,21 @@ B5500SPOUnit.prototype.rewind = function(finish) {
};
/**************************************/
B5500SPOUnit.prototype.readCheck = function(finish, length) {
B5500SPOUnit.prototype.readCheck = function(finish, length, control) {
/* Initiates a read check operation on the unit */
finish(0x04, 0); // report unit not ready
};
/**************************************/
B5500SPOUnit.prototype.readInterrogate = function(finish) {
B5500SPOUnit.prototype.readInterrogate = function(finish, control) {
/* Initiates a read interrogate operation on the unit */
finish(0x04, 0); // report unit not ready
};
/**************************************/
B5500SPOUnit.prototype.writeInterrogate = function (finish) {
B5500SPOUnit.prototype.writeInterrogate = function (finish, control) {
/* Initiates a write interrogate operation on the unit */
finish(0x04, 0); // report unit not ready

View File

@@ -7,7 +7,9 @@
<meta http-equiv="Content-Style-Type" content="text/css">
<link id=defaultStyleSheet rel=stylesheet type="text/css" href="B5500SyllableDebugger.css">
<script src="/B5500/B5500DummyUnit.js"></script>
<script src="/B5500/B5500SPOUnit.js"></script>
<script src="/B5500/B5500DiskUnit.js"></script>
<script src="/B5500EMU/B5500SystemConfiguration.js"></script>
<script src="/B5500EMU/B5500CentralControl.js"></script>

View File

@@ -7,12 +7,14 @@
<meta http-equiv="Content-Style-Type" content="text/css">
<link id=defaultStyleSheet rel=stylesheet type="text/css" href="B5500DistributionAndDisplay.css">
<script src="../B5500SPOUnit.js"></script>
<script src="/B5500/B5500DummyUnit.js"></script>
<script src="/B5500/B5500SPOUnit.js"></script>
<script src="/B5500/B5500DiskUnit.js"></script>
<script src="../../emulator/B5500SystemConfiguration.js"></script>
<script src="../../emulator/B5500CentralControl.js"></script>
<script src="../../emulator/B5500Processor.js"></script>
<script src="../../emulator/B5500IOUnit.js"></script>
<script src="/B5500EMU/emulator/B5500SystemConfiguration.js"></script>
<script src="/B5500EMU/emulator/B5500CentralControl.js"></script>
<script src="/B5500EMU/emulator/B5500Processor.js"></script>
<script src="/B5500EMU/emulator/B5500IOUnit.js"></script>
<script>
"use strict";
@@ -23,7 +25,7 @@ function setText(id, text) {
/* Replaces the children of the node having id="id" with the "text" */
var e = document.getElementById(id);
var f;
if (!e) {
alert("Invalid node id \"" + id + "\"");
} else {
@@ -35,7 +37,7 @@ function setText(id, text) {
}
function getOctal(id) {
/* Obtains the .value from the element "id", parses it, and returns the
/* Obtains the .value from the element "id", parses it, and returns the
result as a B5500 numeric word. If the element text contains any of "-+eE."
the text is parsed as a decimal integer or floating point number, otherwise
it is parsed as an octal value */
@@ -46,7 +48,7 @@ function getOctal(id) {
var tv = 0; // sign of parsed value exponent
var text = e.value; // text of element
var v; // parsed value of element text
if (text.search(/\S/) < 0) {
text = "0";
}
@@ -62,7 +64,7 @@ function getOctal(id) {
while (v < 0x1000000000) {
v *= 8;
ev--;
}
}
while (v >= 0x8000000000) {
v /= 8;
ev++;
@@ -81,7 +83,7 @@ function getOctal(id) {
v = (((sv*2) + tv)*64 + ev%64)*0x8000000000 + mv; // to B5500 format
}
}
if (isNaN(v)) {
e.style.backgroundColor = "red";
} else {
@@ -93,7 +95,7 @@ function getOctal(id) {
function putOctal(id, value) {
/* Formats the "value" as octal and set the "id".value property with the result */
var e = document.getElementById(id);
e.value = value.toString(8);
}
@@ -104,7 +106,7 @@ function putNumber(id, value) {
var e = (value - m)/0x8000000000; // get the exponent and sign bits
var s = (e & 0x80) >>> 7; // get the mantissa sign
var t = (e & 0x40) >>> 6; // get the exponent sign
e = (t ? -(e & 0x3F) : (e & 0x3F)); // get signed value of exponent
setText(id, (Math.pow(8, e)*(s ? -m : m)).toPrecision(12));
}
@@ -115,7 +117,7 @@ function reg_onChange(ev) {
var id = e.id;
var result = true;
var value;
value = getOctal(id);
if (isNaN(value)) {
e.style.backgroundColor = "red";
@@ -140,7 +142,7 @@ function stepIt() {
title = opList.options[opList.selectedIndex].text
value = opList.options[opList.selectedIndex].value;
opcode = parseInt(value, 8);
value = getOctal("AReg");
if (!isNaN(value)) {
cc.P1.A = value;
@@ -151,10 +153,10 @@ function stepIt() {
cc.P1.B = value;
cc.P1.BROF = 1;
setText("BRegOrig", value.toString(8));
cc.P1.I = 0; // reset any interrupts
cc.IAR = 0;
cc.P1.X = 0;
cc.P1.T = opcode;
cc.P1.step();
@@ -190,7 +192,7 @@ window.onload = function() {
cc.P1.T = cc.fieldIsolate(cc.P1.P, 0, 12);
cc.P1.TROF = 1;
cc.P1.L = 1; // point to the next instruction
cc.P1.NCSF = 1; // run test in normal state
}
@@ -203,7 +205,7 @@ window.onload = function() {
<div id=BurroughsLogo>
<img id=BurroughsLogoImage src="../Burroughs-Logo-Neg.jpg">
</div>
<div id=B5500Logo>B 5500
<div id=B5500Logo>B 5500
</div>
</div>
@@ -223,19 +225,19 @@ window.onload = function() {
<input id=AReg name=AReg type=text class=number size=16 maxlength=16>
<td id=ARegValue class=number>
<td id=ARegOrig class=number>
<tr>
<tr>
<td class=center>B
<td class=number>
<input id=BReg name=BReg type=text class=number size=16 maxlength=16>
<td id=BRegValue class=number>
<td id=BRegOrig class=number>
<tr>
<tr>
<td class=center>X
<td class=number>
<input id=XReg name=XReg type=text class=number size=13 maxlength=13>
<td id=XRegValue class=number>
<td id=XRegOrig class=number>
<tr>
<tr>
<td class=center>I
<td class=number>
<input id=IReg name=IReg type=text class=number size=3 maxlength=3>
@@ -253,7 +255,7 @@ window.onload = function() {
<input name=Remide type=button value="Rem Div" onclick="return testIt(6)">
-->
Syllable:
Syllable:
<select id=OpList name=OpList>
<option value="0055" selected>NOP : No Operation
<option value="0101">ADD : Add

View File

@@ -7,12 +7,14 @@
<meta http-equiv="Content-Style-Type" content="text/css">
<link id=defaultStyleSheet rel=stylesheet type="text/css" href="B5500DistributionAndDisplay.css">
<script src="../B5500SPOUnit.js"></script>
<script src="/B5500/B5500DummyUnit.js"></script>
<script src="/B5500/B5500SPOUnit.js"></script>
<script src="/B5500/B5500DiskUnit.js"></script>
<script src="../../emulator/B5500SystemConfiguration.js"></script>
<script src="../../emulator/B5500CentralControl.js"></script>
<script src="../../emulator/B5500Processor.js"></script>
<script src="../../emulator/B5500IOUnit.js"></script>
<script src="/B5500EMU/emulator/B5500SystemConfiguration.js"></script>
<script src="/B5500EMU/emulator/B5500CentralControl.js"></script>
<script src="/B5500EMU/emulator/B5500Processor.js"></script>
<script src="/B5500EMU/emulator/B5500IOUnit.js"></script>
<script>
"use strict";