diff --git a/emulator/B5500CentralControl.js b/emulator/B5500CentralControl.js index 0684e48..9ddaa0c 100644 --- a/emulator/B5500CentralControl.js +++ b/emulator/B5500CentralControl.js @@ -62,7 +62,7 @@ function B5500CentralControl() { /**************************************/ /* Global constants */ -B5500CentralControl.version = "0.05"; +B5500CentralControl.version = "0.06"; B5500CentralControl.rtcTick = 1000/60; // Real-time clock period, milliseconds @@ -119,8 +119,8 @@ B5500CentralControl.unitSpecs = { SPO: {unitIndex: 22, designate: 30, unitClass: B5500SPOUnit}, 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}, + CRA: {unitIndex: 24, designate: 10, unitClass: B5500CardReader}, + CRB: {unitIndex: 23, designate: 14, unitClass: B5500CardReader}, CPA: {unitIndex: 25, designate: 10, unitClass: null}, LPA: {unitIndex: 27, designate: 22, unitClass: null}, LPB: {unitIndex: 26, designate: 26, unitClass: null}, @@ -150,7 +150,7 @@ B5500CentralControl.unitSpecs = { /**************************************/ -B5500CentralControl.bindMethod = function(f, context) { +B5500CentralControl.bindMethod = function bindMethod(f, context) { /* Returns a new function that binds the function "f" to the object "context". Note that this is a constructor property function, NOT an instance method of the CC object */ @@ -159,7 +159,7 @@ B5500CentralControl.bindMethod = function(f, context) { }; /**************************************/ -B5500CentralControl.prototype.clear = function() { +B5500CentralControl.prototype.clear = function clear() { /* Initializes (and if necessary, creates) the system and starts the real-time clock */ @@ -218,7 +218,7 @@ B5500CentralControl.prototype.clear = function() { }; /**************************************/ -B5500CentralControl.prototype.bit = function(word, bit) { +B5500CentralControl.prototype.bit = function bit(word, bit) { /* Extracts and returns the specified bit from the word */ var e = 47-bit; // word lower power exponent var p; // bottom portion of word power of 2 @@ -231,7 +231,7 @@ B5500CentralControl.prototype.bit = function(word, bit) { }; /**************************************/ -B5500CentralControl.prototype.bitSet = function(word, bit) { +B5500CentralControl.prototype.bitSet = function bitSet(word, bit) { /* Sets the specified bit in word and returns the updated word */ var ue = 48-bit; // word upper power exponent var le = ue-1; // word lower power exponent @@ -245,7 +245,7 @@ B5500CentralControl.prototype.bitSet = function(word, bit) { }; /**************************************/ -B5500CentralControl.prototype.bitReset = function(word, bit) { +B5500CentralControl.prototype.bitReset = function bitReset(word, bit) { /* Resets the specified bit in word and returns the updated word */ var ue = 48-bit; // word upper power exponent var le = ue-1; // word lower power exponent @@ -258,7 +258,7 @@ B5500CentralControl.prototype.bitReset = function(word, bit) { }; /**************************************/ -B5500CentralControl.prototype.fieldIsolate = function(word, start, width) { +B5500CentralControl.prototype.fieldIsolate = function fieldIsolate(word, start, width) { /* Extracts a bit field [start:width] from word and returns the field */ var le = 48-start-width; // lower power exponent var p; // bottom portion of word power of 2 @@ -269,7 +269,7 @@ B5500CentralControl.prototype.fieldIsolate = function(word, start, width) { }; /**************************************/ -B5500CentralControl.prototype.fieldInsert = function(word, start, width, value) { +B5500CentralControl.prototype.fieldInsert = function fieldInsert(word, start, width, value) { /* Inserts a bit field from the low-order bits of value ([48-width:width]) into word.[start:width] and returns the updated word */ var ue = 48-start; // word upper power exponent @@ -284,7 +284,7 @@ B5500CentralControl.prototype.fieldInsert = function(word, start, width, value) }; /**************************************/ -B5500CentralControl.prototype.fieldTransfer = function(word, wstart, width, value, vstart) { +B5500CentralControl.prototype.fieldTransfer = function fieldTransfer(word, wstart, width, value, vstart) { /* Inserts a bit field from value.[vstart:width] into word.[wstart:width] and returns the updated word */ var ue = 48-wstart; // word upper power exponent @@ -304,7 +304,7 @@ B5500CentralControl.prototype.fieldTransfer = function(word, wstart, width, valu }; /**************************************/ -B5500CentralControl.prototype.fetch = function(acc) { +B5500CentralControl.prototype.fetch = function fetch(acc) { /* Called by a requestor module passing accessor object "acc" to fetch a word from memory. */ var addr = acc.addr; @@ -348,7 +348,7 @@ B5500CentralControl.prototype.fetch = function(acc) { }; /**************************************/ -B5500CentralControl.prototype.store = function(acc) { +B5500CentralControl.prototype.store = function store(acc) { /* Called by requestor module passing accessor object "acc" to store a word into memory. */ var addr = acc.addr; @@ -392,7 +392,7 @@ B5500CentralControl.prototype.store = function(acc) { }; /**************************************/ -B5500CentralControl.prototype.signalInterrupt = function() { +B5500CentralControl.prototype.signalInterrupt = function signalInterrupt() { /* Called by all modules to signal that an interrupt has occurred and to invoke the interrupt prioritization mechanism. This will result in an updated vector address in the IAR. Can also be called to reprioritize @@ -430,7 +430,7 @@ B5500CentralControl.prototype.signalInterrupt = function() { }; /**************************************/ -B5500CentralControl.prototype.clearInterrupt = function() { +B5500CentralControl.prototype.clearInterrupt = function clearInterrupt() { /* Resets an interrupt based on the current setting of this.IAR, then reprioritizes any remaining interrupts, leaving the new vector address in this.IAR. */ @@ -555,7 +555,7 @@ B5500CentralControl.prototype.tock = function tock() { }; /**************************************/ -B5500CentralControl.prototype.haltP2 = function() { +B5500CentralControl.prototype.haltP2 = function haltP2() { /* Called by P1 to halt P2. storeForInterrupt() will set P2BF=0 */ this.HP2F = 1; @@ -566,7 +566,7 @@ B5500CentralControl.prototype.haltP2 = function() { }; /**************************************/ -B5500CentralControl.prototype.initiateP2 = function() { +B5500CentralControl.prototype.initiateP2 = function initiateP2() { /* 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 @@ -583,7 +583,7 @@ B5500CentralControl.prototype.initiateP2 = function() { }; /**************************************/ -B5500CentralControl.prototype.initiateIO = function() { +B5500CentralControl.prototype.initiateIO = function initiateIO() { /* Selects an I/O unit and initiates an I/O */ if (this.IO1 && this.IO1.REMF && !this.AD1F) { @@ -605,7 +605,7 @@ B5500CentralControl.prototype.initiateIO = function() { }; /**************************************/ -B5500CentralControl.prototype.interrogateIOChannel = function() { +B5500CentralControl.prototype.interrogateIOChannel = function interrogateIOChannel() { /* Returns a value as for the processor TIO syllable indicating the first available and non-busy I/O Unit */ @@ -623,7 +623,7 @@ B5500CentralControl.prototype.interrogateIOChannel = function() { }; /**************************************/ -B5500CentralControl.prototype.interrogateUnitStatus = function() { +B5500CentralControl.prototype.interrogateUnitStatus = function interrogateUnitStatus() { /* Returns a bitmask as for the processor TUS syllable indicating the ready status of all peripheral units */ @@ -631,7 +631,7 @@ B5500CentralControl.prototype.interrogateUnitStatus = function() { }; /**************************************/ -B5500CentralControl.prototype.testUnitReady = function(index) { +B5500CentralControl.prototype.testUnitReady = function testUnitReady(index) { /* Determines whether the unit index "index" is currently in ready status. Returns 1 if ready, 0 if not ready */ @@ -639,7 +639,7 @@ B5500CentralControl.prototype.testUnitReady = function(index) { }; /**************************************/ -B5500CentralControl.prototype.testUnitBusy = function(index) { +B5500CentralControl.prototype.testUnitBusy = function testUnitBusy(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 */ @@ -647,7 +647,7 @@ B5500CentralControl.prototype.testUnitBusy = function(index) { }; /**************************************/ -B5500CentralControl.prototype.setUnitBusy = function(index, busy) { +B5500CentralControl.prototype.setUnitBusy = function setUnitBusy(index, busy) { /* Sets or resets the unit-busy mask bit for unit index "index" */ if (index) { @@ -657,7 +657,7 @@ B5500CentralControl.prototype.setUnitBusy = function(index, busy) { }; /**************************************/ -B5500CentralControl.prototype.halt = function() { +B5500CentralControl.prototype.halt = function halt() { /* Halts the processors. Any in-process I/Os are allowed to complete */ if (this.timer) { @@ -723,10 +723,10 @@ B5500CentralControl.prototype.loadComplete = function loadComplete(dontStart) { }; /**************************************/ -B5500CentralControl.prototype.load = function(dontStart) { +B5500CentralControl.prototype.load = function load(dontStart) { /* Initiates a Load operation to start the system. If "dontStart" is truthy, then only the MCP bootstrap is loaded into memory -- P1 is not started */ - var boundLoadComplete = (function(that, dontStart) { + var boundLoadComplete = (function boundLoadComplete(that, dontStart) { return function() {return that.loadComplete(dontStart)} })(this, dontStart); @@ -754,7 +754,7 @@ B5500CentralControl.prototype.load = function(dontStart) { }; /**************************************/ -B5500CentralControl.prototype.loadTest = function(buf, loadAddr) { +B5500CentralControl.prototype.loadTest = function loadTest(buf, loadAddr) { /* Loads a test codestream into memory starting at B5500 word address "loadAddr" from the ArrayBuffer "buf". Returns the number of B5500 words loaded into memory. Note that when loading an ESPOL "DISK" file, @@ -809,7 +809,7 @@ B5500CentralControl.prototype.loadTest = function(buf, loadAddr) { }; /**************************************/ -B5500CentralControl.prototype.runTest = function(runAddr) { +B5500CentralControl.prototype.runTest = function runTest(runAddr) { /* Executes a test program previously loaded by this.loadTest on processor P1. "runAddr" is the B5500 word address at which execution will begin (typically 0x10 [octal 20]) */ @@ -822,7 +822,7 @@ B5500CentralControl.prototype.runTest = function(runAddr) { }; /**************************************/ -B5500CentralControl.prototype.configureSystem = function() { +B5500CentralControl.prototype.configureSystem = function configureSystem() { /* Establishes the hardware module configuration from the B5500SystemConfiguration module */ var cfg = B5500SystemConfiguration; @@ -834,7 +834,7 @@ B5500CentralControl.prototype.configureSystem = function() { var x; function makeChange(cc, maskBit) { - return function(ready) { + return function statusChange(ready) { cc.unitStatusMask = (ready ? cc.bitSet(cc.unitStatusMask, maskBit) : cc.bitReset(cc.unitStatusMask, maskBit)); }; @@ -843,41 +843,41 @@ B5500CentralControl.prototype.configureSystem = function() { function makeSignal(cc, mnemonic) { switch (mnemonic) { case "SPO": - return function() { + return function signalSPO() { cc.CCI05F = 1; cc.signalInterrupt(); }; break; case "LPA": - return function() { + return function signalLPA() { cc.setUnitBusy(27, 0); cc.CCI06F = 1; cc.signalInterrupt(); }; break; case "LPB": - return function() { + return function signalLPB() { cc.setUnitBusy(26, 0); cc.CCI07F = 1; cc.signalInterrupt(); }; break; case "DKA": - return function() { + return function signalDKA() { cc.setUnitBusy(29, 0); cc.CCI15F = 1; cc.signalInterrupt(); }; break; case "DKB": - return function() { + return function signalDKB() { cc.setUnitBusy(28, 0); cc.CCI16F = 1; cc.signalInterrupt(); }; break; default: - return function() {}; + return function signalDefault() {}; break; } } @@ -924,7 +924,7 @@ B5500CentralControl.prototype.configureSystem = function() { }; /**************************************/ -B5500CentralControl.prototype.powerOn = function() { +B5500CentralControl.prototype.powerOn = function powerOn() { /* Powers up the system and establishes the hardware module configuration. Redundant power-ons are ignored. */ @@ -935,7 +935,7 @@ B5500CentralControl.prototype.powerOn = function() { }; /**************************************/ -B5500CentralControl.prototype.powerOff = function() { +B5500CentralControl.prototype.powerOff = function powerOff() { /* Powers down the system and deallocates the hardware modules. Redundant power-offs are ignored. */ var x; diff --git a/emulator/B5500IOUnit.js b/emulator/B5500IOUnit.js index 6555dcf..7bf5335 100644 --- a/emulator/B5500IOUnit.js +++ b/emulator/B5500IOUnit.js @@ -63,6 +63,7 @@ function B5500IOUnit(ioUnitID, cc) { // Establish contexts for asynchronously-called methods this.boundForkIO = B5500CentralControl.bindMethod(this.forkIO, this); this.boundFinishGeneric = this.makeFinish(this.finishGeneric); + this.boundFinishGenericRead = this.makeFinish(this.finishGenericRead); this.boundFinishBusy = this.makeFinish(this.finishBusy); this.boundFinishDiskRead = this.makeFinish(this.finishDiskRead); this.boundFinishSPORead = this.makeFinish(this.finishSPORead); @@ -132,7 +133,7 @@ B5500IOUnit.BCLANSItoBIC = [ // Index by 8-bit BCL-as-ANSI to get 6-b 0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C]; // F0-FF /**************************************/ -B5500IOUnit.prototype.clear = function() { +B5500IOUnit.prototype.clear = function clear() { /* Initializes (and if necessary, creates) the I/O Unit state */ this.W = 0; // Memory buffer register @@ -187,7 +188,7 @@ B5500IOUnit.prototype.clear = function() { }; /**************************************/ -B5500IOUnit.prototype.clearD = function() { +B5500IOUnit.prototype.clearD = function clearD() { /* Clears the D-register and the exploded field variables used internally */ this.D = 0; @@ -209,7 +210,7 @@ B5500IOUnit.prototype.clearD = function() { }; /**************************************/ -B5500IOUnit.prototype.fetch = function(addr) { +B5500IOUnit.prototype.fetch = function fetch(addr) { /* Fetch a word from memory at address "addr" and leave it in the W register. Returns 1 if a memory access error occurred, 0 if no error */ var acc = this.accessor; // get a local reference to the accessor object @@ -231,7 +232,7 @@ B5500IOUnit.prototype.fetch = function(addr) { }; /**************************************/ -B5500IOUnit.prototype.store = function(addr) { +B5500IOUnit.prototype.store = function store(addr) { /* Store a word in memory at address "addr" from the W register. Returns 1 if a memory access error occurred, 0 if no error */ var acc = this.accessor; // get a local reference to the accessor object @@ -250,7 +251,7 @@ B5500IOUnit.prototype.store = function(addr) { }; /**************************************/ -B5500IOUnit.prototype.fetchBuffer = function(mode, words) { +B5500IOUnit.prototype.fetchBuffer = function fetchBuffer(mode, words) { /* Fetches words from memory starting at this.Daddress and coverts the BIC characters to ANSI or BCLANSI in this.buffer. "mode": 0=BCLANSI, 1=ANSI; "words": maximum number of words to transfer. At exit, updates this.Daddress @@ -300,7 +301,7 @@ B5500IOUnit.prototype.fetchBuffer = function(mode, words) { }; /**************************************/ -B5500IOUnit.prototype.fetchBufferWithGM = function(mode, words) { +B5500IOUnit.prototype.fetchBufferWithGM = function fetchBufferWithGM(mode, words) { /* Fetches words from memory starting at this.Daddress and coverts the BIC characters to ANSI or BCLANSI in this.buffer. "mode": 0=BCLANSI, 1=ANSI; "words": maximum number of words to transfer. The transfer can be terminated @@ -356,7 +357,7 @@ B5500IOUnit.prototype.fetchBufferWithGM = function(mode, words) { }; /**************************************/ -B5500IOUnit.prototype.storeBuffer = function(chars, offset, mode, words) { +B5500IOUnit.prototype.storeBuffer = function storeBuffer(chars, offset, mode, words) { /* Converts characters in this.buffer from ANSI or BCLANSI to BIC, assembles them into words, and stores the words into memory starting at this.Daddress. "chars": the number of characters to store, starting at "offset" in the buffer; @@ -428,7 +429,7 @@ B5500IOUnit.prototype.storeBuffer = function(chars, offset, mode, words) { }; /**************************************/ -B5500IOUnit.prototype.storeBufferWithGM = function(chars, offset, mode, words) { +B5500IOUnit.prototype.storeBufferWithGM = function storeBufferWithGM(chars, offset, mode, words) { /* Converts characters in this.buffer from ANSI to BIC, assembles them into words, and stores the words into memory starting at this.Daddress. "chars": the number of characters to store, starting at "offset" in the buffer; @@ -507,7 +508,7 @@ B5500IOUnit.prototype.storeBufferWithGM = function(chars, offset, mode, words) { }; /**************************************/ -B5500IOUnit.prototype.finish = function () { +B5500IOUnit.prototype.finish = function finish() { /* Called to finish an I/O operation on this I/O Unit. Constructs and stores the result descriptor, sets the appropriate I/O Finished interrupt in CC */ @@ -553,7 +554,7 @@ B5500IOUnit.prototype.finish = function () { }; /**************************************/ -B5500IOUnit.prototype.makeFinish = function(f) { +B5500IOUnit.prototype.makeFinish = function makeFinish(f) { /* Utility function to create a closure for I/O finish handlers */ var that = this; @@ -561,7 +562,7 @@ B5500IOUnit.prototype.makeFinish = function(f) { }; /**************************************/ -B5500IOUnit.prototype.decodeErrorMask = function(errorMask) { +B5500IOUnit.prototype.decodeErrorMask = function decodeErrorMask(errorMask) { /* Decodes the errorMask returned by the device drivers and ORs it into the D-register error bits */ @@ -575,7 +576,7 @@ B5500IOUnit.prototype.decodeErrorMask = function(errorMask) { }; /**************************************/ -B5500IOUnit.prototype.finishBusy = function(errorMask, length) { +B5500IOUnit.prototype.finishBusy = function finishBusy(errorMask, length) { /* Handles a generic I/O finish when no word-count update or input data transfer is needed, but leaves the unit marked as busy in CC. This is needed by device operations that have a separate completion signal, such as line @@ -587,7 +588,7 @@ B5500IOUnit.prototype.finishBusy = function(errorMask, length) { }; /**************************************/ -B5500IOUnit.prototype.finishGeneric = function(errorMask, length) { +B5500IOUnit.prototype.finishGeneric = function finishGeneric(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. Note that this turns off the @@ -599,7 +600,16 @@ B5500IOUnit.prototype.finishGeneric = function(errorMask, length) { }; /**************************************/ -B5500IOUnit.prototype.finishSPORead = function(errorMask, length) { +B5500IOUnit.prototype.finishGenericRead = function finishGenericRead(errorMask, length) { + /* Handles a generic I/O finish when input data transfer, and optionally, + word-count update, is needed. Note that this turns off the busyUnit mask bit in CC */ + + this.storeBuffer(length, 0, 1, (this.D23F ? this.DwordCount : 0x7FFF)); + this.finishGeneric(errorMask, length); +}; + +/**************************************/ +B5500IOUnit.prototype.finishSPORead = function finishSPORead(errorMask, length) { /* Handles I/O finish for a SPO keyboard input operation */ this.storeBufferWithGM(length, 0, 1, 0x7FFF); @@ -607,7 +617,7 @@ B5500IOUnit.prototype.finishSPORead = function(errorMask, length) { }; /**************************************/ -B5500IOUnit.prototype.finishDiskRead = function(errorMask, length) { +B5500IOUnit.prototype.finishDiskRead = function finishDiskRead(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); @@ -620,7 +630,7 @@ B5500IOUnit.prototype.finishDiskRead = function(errorMask, length) { }; /**************************************/ -B5500IOUnit.prototype.initiateDiskIO = function(u) { +B5500IOUnit.prototype.initiateDiskIO = function initiateDiskIO(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 @@ -678,7 +688,7 @@ B5500IOUnit.prototype.initiateDiskIO = function(u) { }; /**************************************/ -B5500IOUnit.prototype.initiatePrinterIO = function(u) { +B5500IOUnit.prototype.initiatePrinterIO = function initiatePrinterIO(u) { /* Initiates an I/O to a Line Printer unit */ var addr = this.Daddress; // initial data transfer address var cc; // carriage control value to driver @@ -766,12 +776,22 @@ B5500IOUnit.prototype.forkIO = function forkIO() { // card #1 reader/punch case 10: - this.D30F = 1; this.finish(); // >>> temp until implemented <<< + if (this.D24F) { // CRA + u.read(this.boundFinishGenericRead, this.buffer, (this.D21F ? 160 : 80), this.D21F, 0); + } else { // CPA + this.D30F = 1; // >>> temp until CPA implemented <<< + this.finish(); + } break; // card #2 reader case 14: - this.D30F = 1; this.finish(); // >>> temp until implemented <<< + if (this.D24F) { + u.read(this.boundFinishGenericRead, this.buffer, (this.D21F ? 160 : 80), this.D21F, 0); + } else { + this.D30F = 1; // can't write to CRB, report as not ready + this.finish(); + } break; // SPO designate @@ -812,7 +832,7 @@ B5500IOUnit.prototype.forkIO = function forkIO() { }; /**************************************/ -B5500IOUnit.prototype.initiate = function() { +B5500IOUnit.prototype.initiate = function initiate() { /* 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 @@ -842,7 +862,7 @@ B5500IOUnit.prototype.initiate = function() { }; /**************************************/ -B5500IOUnit.prototype.initiateLoad = function(cardLoadSelect, loadComplete) { +B5500IOUnit.prototype.initiateLoad = function initiateLoad(cardLoadSelect, loadComplete) { /* Initiates a hardware load operation on this I/O unit. "cardLoadSelect" is true if the load is to come from the card reader, otherwise it will come from sector 1 of DKA EU0. "loadComplete" is called on completion of the I/O. @@ -850,10 +870,11 @@ B5500IOUnit.prototype.initiateLoad = function(cardLoadSelect, loadComplete) { pressed and released. Since there is no IOD in memory, we must generate one in the D register and initiate the I/O intrinsically. Note that if the I/O has an error, the RD is not stored and the I/O finish interrupt is not set in CC */ + var chars; var index; var u; - function finishDiskLoad(errorMask, length) { + function finishLoad(errorMask, length) { var memWords = Math.floor((length+7)/8); this.storeBuffer(length, 0, this.D21F, memWords); @@ -870,22 +891,28 @@ B5500IOUnit.prototype.initiateLoad = function(cardLoadSelect, loadComplete) { this.clearD(); if (cardLoadSelect) { - alert("Card Load Select is not yet implemented"); - } else { - this.D = 0x0600009F8010; // unit 6, read, 63 segs, addr @00020 - this.Dunit = 6; // 6=DKA + this.D = 0x0A0004800010; // unit 10, read, binary mode, addr @00020 + this.Dunit = 10; // 10=CRA this.D24F = 1; // read + chars = 20*8; + } else { + this.D = 0x0600009F8010; // unit 6, read, alpha mode, 63 segs, addr @00020 + this.Dunit = 6; // 6=DKA + this.D21F = 0; // alpha mode this.LP = 63; // 63 sectors - this.Daddress = 0x10; // memory address @20 - this.busyUnit = index = B5500CentralControl.unitIndex[this.D24F & 1][this.Dunit & 0x1F]; - if (this.cc.testUnitBusy(index)) { - this.D32F = 1; // set unit busy error - } else if (!this.cc.testUnitReady(index)) { - this.D30F = 1; // set unit not-ready error - } else { - this.cc.setUnitBusy(index, 1); - u = this.cc.unit[index]; - u.read(this.makeFinish(finishDiskLoad), this.buffer, 63*240, this.D21F, 1); - } + chars = 63*240; + } + + this.D24F = 1; // read + this.Daddress = 0x10; // memory address @20 + this.busyUnit = index = B5500CentralControl.unitIndex[this.D24F & 1][this.Dunit & 0x1F]; + if (this.cc.testUnitBusy(index)) { + this.D32F = 1; // set unit busy error + } else if (!this.cc.testUnitReady(index)) { + this.D30F = 1; // set unit not-ready error + } else { + this.cc.setUnitBusy(index, 1); + u = this.cc.unit[index]; + u.read(this.makeFinish(finishLoad), this.buffer, chars, this.D21F, 1); } }; \ No newline at end of file diff --git a/emulator/B5500SystemConfiguration.js b/emulator/B5500SystemConfiguration.js index 0e2a4d2..0853924 100644 --- a/emulator/B5500SystemConfiguration.js +++ b/emulator/B5500SystemConfiguration.js @@ -41,7 +41,7 @@ var B5500SystemConfiguration = { SPO: true, // SPO keyboard/printer DKA: true, // Disk File Control A DKB: false, // Disk File Control B - CRA: false, // Card Reader A + CRA: true, // Card Reader A CRB: false, // Card Reader B CPA: false, // Card Punch A LPA: false, // Line Printer A diff --git a/webUI/B5500CardReader.css b/webUI/B5500CardReader.css new file mode 100644 index 0000000..e95cace --- /dev/null +++ b/webUI/B5500CardReader.css @@ -0,0 +1,99 @@ +/*********************************************************************** +* retro-b5500/emulator B5500CardReader.css +************************************************************************ +* Copyright (c) 2013, Nigel Williams and Paul Kimpel. +* Licensed under the MIT License, see http://www.opensource.org/licenses/mit-license.php +************************************************************************ +* B5500 emulator Card Reader web interface style sheet. +************************************************************************ +* 2013-06-09 P.Kimpel +* Original version, from B5500SPOUnit.css. +***********************************************************************/ + +BODY { + position: relative; + background-color: black; + margin: 4px} + +DIV#CRDiv { + position: relative; + background-color: #666; + width: 280px; + height: 112px; + border: 1px solid black; + border-radius: 8px; + padding: 0; + vertical-align: top} + +BUTTON.greenButton { + background-color: #060; + color: white; + font-family: Arial Rounded, Arial, Helvetica, sans-serif; + font-size: 10px; + font-weight: bold; + width: 60px; + height: 40px; + border: 1px solid #DDD; + border-radius: 4px} + +BUTTON.blackButton { + background-color: black; + color: white; + font-family: Arial Rounded, Arial, Helvetica, sans-serif; + font-size: 10px; + font-weight: bold; + width: 60px; + height: 40px; + border: 1px solid #DDD; + border-radius: 4px} + +BUTTON.redButton { + background-color: #900; + color: white; + font-family: Arial Rounded, Arial, Helvetica, sans-serif; + font-size: 10px; + font-weight: bold; + width: 60px; + height: 40px; + border: 1px solid #DDD; + border-radius: 4px} + +BUTTON.greenLit { + background-color: green} + +BUTTON.redLit { + background-color: #F00} + +#CRNotReadyLight { + position: absolute; + top: 8px; + left: 8px} + +#CREOFBtn { + position: absolute; + top: 8px; + left: 76px} + +#CRStopBtn { + position: absolute; + top: 8px; + left: 144px} + +#CRStartBtn { + position: absolute; + top: 8px; + left: 212px;} + +#CRFileSelector { + position: absolute; + border: 1px solid white; + top: 56px; + left: 8px; + width: 120px} + +#CRProgressBar { + position: absolute; + border: 1px solid white; + top: 84px; + left: 8px; + width: 262px} diff --git a/webUI/B5500CardReader.html b/webUI/B5500CardReader.html new file mode 100644 index 0000000..5cb1dd5 --- /dev/null +++ b/webUI/B5500CardReader.html @@ -0,0 +1,26 @@ + + +B5500 Emulator Card Reader + + + + + + + + + + +
+ + + + + + + + +
+ + + \ No newline at end of file diff --git a/webUI/B5500CardReader.js b/webUI/B5500CardReader.js new file mode 100644 index 0000000..bba8841 --- /dev/null +++ b/webUI/B5500CardReader.js @@ -0,0 +1,398 @@ +/*********************************************************************** +* retro-b5500/emulator B5500CardReader.js +************************************************************************ +* Copyright (c) 2013, Nigel Williams and Paul Kimpel. +* Licensed under the MIT License, see +* http://www.opensource.org/licenses/mit-license.php +************************************************************************ +* B5500 Card Reader Peripheral Unit module. +* +* Defines a card reader peripheral unit type. +* +************************************************************************ +* 2013-06-05 P.Kimpel +* Original version, from B5500SPOUnit.js. +***********************************************************************/ +"use strict"; + +/**************************************/ +function B5500CardReader(mnemonic, unitIndex, designate, statusChange, signal) { + /* Constructor for the CardReader object */ + var that = this; + + this.mnemonic = mnemonic; // Unit mnemonic + this.unitIndex = unitIndex; // 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(); + + this.window = window.open("", mnemonic); + if (this.window) { + this.window.close(); // destroy the previously-existing window + this.window = null; + } + this.doc = null; + this.window = window.open("/B5500/webUI/B5500CardReader.html", mnemonic, + "scrollbars=no,resizable,width=200,height=200"); + this.window.onload = function() { + that.readerOnload(); + }; +} +B5500CardReader.prototype.eolRex = /([^\n\r\f]*)((:?\r[\n\f]?)|\n|\f)?/g; + +B5500CardReader.prototype.cardsPerMinute = 800; + +B5500CardReader.prototype.cardFilter = [ // Filter ASCII character values to valid BIC ones + 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, // 00-0F + 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, // 10-1F + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x3F,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, // 20-2F + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, // 30-3F + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, // 40-4F + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x3F,0x5D,0x3F,0x3F, // 50-5F + 0x3F,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, // 60-6F + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x7B,0x7C,0x7D,0x7E,0x3F]; // 70-7F + +/**************************************/ +B5500CardReader.prototype.$$ = function $$(e) { + return this.doc.getElementById(e); +}; + +/**************************************/ +B5500CardReader.prototype.clear = function clear() { + /* Initializes (and if necessary, creates) the reader unit 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.buffer = ""; // Card reader "input hopper" + this.bufLength = 0; // Current input buffer length (characters) + this.bufIndex = 0; // 0-relative offset to next "card" to be read + this.eofArmed = false; // EOF button: armed state + this.stopCount = 0; // stopCount for clearing the input buffer +}; + +/**************************************/ +B5500CardReader.prototype.hasClass = function hasClass(e, name) { + /* returns true if element "e" has class "name" in its class list */ + var classes = e.className; + + if (!e) { + return false; + } else if (classes == name) { + return true; + } else { + return (classes.search("\\b" + name + "\\b") >= 0); + } +}; + +/**************************************/ +B5500CardReader.prototype.addClass = function addClass(e, name) { + /* Adds a class "name" to the element "e"s class list */ + + if (!this.hasClass(e, name)) { + e.className += (" " + name); + } +}; + +/**************************************/ +B5500CardReader.prototype.removeClass = function removeClass(e, name) { + /* Removes the class "name" from the element "e"s class list */ + + e.className = e.className.replace(new RegExp("\\b" + name + "\\b\\s*", "g"), ""); +}; + +/**************************************/ +B5500CardReader.prototype.setReaderReady = function setReaderReady(ready) { + /* Controls the ready-state of the card reader */ + + this.$$("CRFileSelector").disabled = ready; + this.ready = ready; + if (ready) { + this.statusChange(1); + this.removeClass(this.$$("CRNotReadyLight"), "redLit"); + } else { + this.statusChange(0); + this.addClass(this.$$("CRNotReadyLight"), "redLit"); + } +}; + +/**************************************/ +B5500CardReader.prototype.armEOF = function armEOF(armed) { + /* Controls the arming/disarming of the EOF signal when starting with + an empty input hopper */ + + this.eofArmed = armed; + if (armed) { + this.addClass(this.$$("CREOFBtn"), "redLit"); + } else { + this.removeClass(this.$$("CREOFBtn"), "redLit"); + } +}; + +/**************************************/ +B5500CardReader.prototype.CRStartBtn_onclick = function CRStartBtn_onclick(ev) { + /* Handle the click event for the START button */ + var that = this; + + if (!this.ready) { + this.stopCount = 0; + if (this.bufIndex < this.bufLength) { + this.setReaderReady(true); + } + } +}; + +/**************************************/ +B5500CardReader.prototype.CRStopBtn_onclick = function CRStopBtn_onclick(ev) { + /* Handle the click event for the STOP button */ + + if (this.ready) { + this.setReaderReady(false); + } else if (this.eofArmed) { + this.armEOF(false); + } else if (this.bufIndex < this.bufLength) { + if (this.stopCount < 3) { + this.stopCount++; + } else { + if (confirm("Empty the " + this.mnemonic + " input hopper?")) { + this.stopCount = 0; + this.buffer = ""; + this.bufLength = 0; + this.bufIndex = 0; + this.$$("CRProgressBar").value = 0; + } + } + } +}; + +/**************************************/ +B5500CardReader.prototype.CREOFBtn_onclick = function CREOFBtn_onclick(ev) { + /* Handle the click event for the EOF button */ + + this.armEOF(!this.eofArmed); +}; + +/**************************************/ +B5500CardReader.prototype.fileSelector_onChange = function fileSelector_onChange(ev) { + /* Handle the onchange event when a file is selected */ + var f = ev.target.files[0]; + var reader = new FileReader(); + var that = this; + + function fileLoader_onLoad(ev) { + /* Handle the onload event for a Text FileReader */ + + if (that.bufIndex < that.bufLength) { + that.buffer = that.buffer.substring(that.bufIndex) + ev.target.result; + } else { + that.buffer = ev.target.result; + } + + that.bufIndex = 0; + that.bufLength = that.buffer.length; + that.$$("CRProgressBar").value = that.bufLength; + that.$$("CRProgressBar").max = that.bufLength; + } + + /******************** + alert("File selected: " + f.name + + "\nModified " + f.lastModifiedDate + + "\nType=" + f.type + ", Size=" + f.size + " octets"); + ********************/ + + reader.onload = fileLoader_onLoad; + reader.readAsText(f); +}; + +/**************************************/ +B5500CardReader.prototype.readCardAlpha = function readCardAlpha(buffer, length) { + /* Reads one card image from the buffer in alpha mode; pads or trims the + image as necessary to the I/O buffer length. Invalid BCL characters are + translated to ASCII "?" and the invalid character bit is set in the errorMask */ + var card; // card image + var cardLength; // length of card image + var match; // result of eolRex.exec() + var x; // for loop index + + this.eolRex.lastIndex = this.bufIndex; + match = this.eolRex.exec(this.buffer); + if (!match) { + cardLength = 0; + } else { + this.bufIndex += match[0].length; + card = match[1]; + cardLength = card.length; + if (length < cardLength) { + cardLength = length; + } + for (x=0; x= this.bufLength) { + this.armEOF(false); + finish(0x24, 0); // report unit EOF + not ready + } else { + finish(0x04, 0); // report unit not ready + } + } else { + this.busy = true; + if (mode == 0) { + this.readCardAlpha(buffer, length); + } else { + this.readCardBinary(buffer, length); + } + if (this.bufIndex < this.bufLength) { + this.$$("CRProgressBar").value = this.bufLength-this.bufIndex; + } else { + this.$$("CRProgressBar").value = 0; + this.buffer = ""; // discard the input buffer + this.bufLength = 0; + this.bufIndex = 0; + this.setReaderReady(false); + } + + setTimeout(function() { + that.busy = false; + finish(that.errorMask, length); + }, 60000/this.cardsPerMinute); + } +}; + +/**************************************/ +B5500CardReader.prototype.space = function space(finish, length, control) { + /* Initiates a space operation on the unit */ + + finish(0x04, 0); // report unit not ready +}; + +/**************************************/ +B5500CardReader.prototype.write = function write(finish, buffer, length, mode, control) { + /* Initiates a write operation on the unit */ + + finish(0x04); // report unit not ready +}; + +/**************************************/ +B5500CardReader.prototype.erase = function erase(finish, length) { + /* Initiates an erase operation on the unit */ + + finish(0x04, 0); // report unit not ready +}; + +/**************************************/ +B5500CardReader.prototype.rewind = function rewind(finish) { + /* Initiates a rewind operation on the unit */ + + finish(0x04, 0); // report unit not ready +}; + +/**************************************/ +B5500CardReader.prototype.readCheck = function readCheck(finish, length, control) { + /* Initiates a read check operation on the unit */ + + finish(0x04, 0); // report unit not ready +}; + +/**************************************/ +B5500CardReader.prototype.readInterrogate = function readInterrogate(finish, control) { + /* Initiates a read interrogate operation on the unit */ + + finish(0x04, 0); // report unit not ready +}; + +/**************************************/ +B5500CardReader.prototype.writeInterrogate = function writeInterrogate(finish, control) { + /* Initiates a write interrogate operation on the unit */ + + finish(0x04, 0); // report unit not ready +}; diff --git a/webUI/B5500Console.html b/webUI/B5500Console.html index 52aeb7e..3390019 100644 --- a/webUI/B5500Console.html +++ b/webUI/B5500Console.html @@ -10,6 +10,7 @@ + diff --git a/webUI/B5500DummyUnit.js b/webUI/B5500DummyUnit.js index 08e7d83..04cdef4 100644 --- a/webUI/B5500DummyUnit.js +++ b/webUI/B5500DummyUnit.js @@ -27,75 +27,75 @@ 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.clear(); } /**************************************/ -B5500DummyUnit.prototype.clear = function() { +B5500DummyUnit.prototype.clear = function clear() { /* 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.buffer = null; // + this.buffer = null; // }; /**************************************/ -B5500DummyUnit.prototype.read = function(finish, buffer, length, mode, control) { +B5500DummyUnit.prototype.read = function read(finish, buffer, length, mode, control) { /* Initiates a read operation on the unit */ - + finish(0x04, 0); // report unit not ready }; /**************************************/ -B5500DummyUnit.prototype.space = function(finish, length, control) { +B5500DummyUnit.prototype.space = function space(finish, length, control) { /* Initiates a space operation on the unit */ finish(0x04, 0); // report unit not ready }; /**************************************/ -B5500DummyUnit.prototype.write = function(finish, buffer, length, mode, control) { +B5500DummyUnit.prototype.write = function write(finish, buffer, length, mode, control) { /* Initiates a write operation on the unit */ finish(0x04, 0); // report unit not ready }; /**************************************/ -B5500DummyUnit.prototype.erase = function(finish, length) { +B5500DummyUnit.prototype.erase = function erase(finish, length) { /* Initiates an erase operation on the unit */ - + finish(0x04, 0); // report unit not ready }; /**************************************/ -B5500DummyUnit.prototype.rewind = function(finish) { +B5500DummyUnit.prototype.rewind = function rewind(finish) { /* Initiates a rewind operation on the unit */ - + finish(0x04, 0); // report unit not ready }; /**************************************/ -B5500DummyUnit.prototype.readCheck = function(finish, length, control) { +B5500DummyUnit.prototype.readCheck = function readCheck(finish, length, control) { /* Initiates a read check operation on the unit */ - + finish(0x04, 0); // report unit not ready }; /**************************************/ -B5500DummyUnit.prototype.readInterrogate = function(finish, control) { +B5500DummyUnit.prototype.readInterrogate = function readInterrogate(finish, control) { /* Initiates a read interrogate operation on the unit */ - + finish(0x04, 0); // report unit not ready }; /**************************************/ -B5500DummyUnit.prototype.writeInterrogate = function (finish, control) { +B5500DummyUnit.prototype.writeInterrogate = function writeInterrogate(finish, control) { /* Initiates a write interrogate operation on the unit */ - + finish(0x04, 0); // report unit not ready }; diff --git a/webUI/B5500SPOUnit.html b/webUI/B5500SPOUnit.html index 918a2f5..4dc4a29 100644 --- a/webUI/B5500SPOUnit.html +++ b/webUI/B5500SPOUnit.html @@ -1,8 +1,9 @@ B5500 Emulator SPO Unit - + + diff --git a/webUI/B5500SPOUnit.js b/webUI/B5500SPOUnit.js index fe9f8e3..75adce4 100644 --- a/webUI/B5500SPOUnit.js +++ b/webUI/B5500SPOUnit.js @@ -36,12 +36,13 @@ function B5500SPOUnit(mnemonic, unitIndex, designate, statusChange, signal) { this.printChar.that = this; this.outputChar.that = this; - this.window = window.open("", "SPOWin"); + this.window = window.open("", mnemonic); if (this.window) { this.window.close(); // destroy the previously-existing window this.window = null; } - this.window = window.open("/B5500/webUI/B5500SPOUnit.html", "SPOWin", "scrollbars,resizable,width=600,height=500"); + this.doc = null; + this.window = window.open("/B5500/webUI/B5500SPOUnit.html", mnemonic, "scrollbars,resizable,width=600,height=500"); this.window.onload = function() { that.spoOnload(); }; @@ -54,7 +55,7 @@ B5500SPOUnit.prototype.spoRemote = 2; B5500SPOUnit.prototype.spoInput = 3; B5500SPOUnit.prototype.spoOutput = 4; -B5500SPOUnit.prototype.keyFilter = [ // Filter keyCode values to valid B5500 ones +B5500SPOUnit.prototype.keyFilter = [ // Filter keyCode values to valid BIC ones 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, // 00-0F 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, // 10-1F 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x3F,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, // 20-2F @@ -65,11 +66,12 @@ B5500SPOUnit.prototype.keyFilter = [ // Filter keyCode values to vali 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x7B,0x7C,0x7D,0x7E,0x3F]; // 70-7F /**************************************/ -B5500SPOUnit.prototype.$$ = function(e) { - return this.doc.getElementById(e)}; +B5500SPOUnit.prototype.$$ = function $$(e) { + return this.doc.getElementById(e); +}; /**************************************/ -B5500SPOUnit.prototype.clear = function() { +B5500SPOUnit.prototype.clear = function clear() { /* Initializes (and if necessary, creates) the SPO unit state */ this.ready = false; // ready status @@ -89,7 +91,7 @@ B5500SPOUnit.prototype.clear = function() { }; /**************************************/ -B5500SPOUnit.prototype.hasClass = function(e, name) { +B5500SPOUnit.prototype.hasClass = function hasClass(e, name) { /* returns true if element "e" has class "name" in its class list */ var classes = e.className; @@ -103,7 +105,7 @@ B5500SPOUnit.prototype.hasClass = function(e, name) { }; /**************************************/ -B5500SPOUnit.prototype.addClass = function(e, name) { +B5500SPOUnit.prototype.addClass = function addClass(e, name) { /* Adds a class "name" to the element "e"s class list */ if (!this.hasClass(e, name)) { @@ -112,14 +114,14 @@ B5500SPOUnit.prototype.addClass = function(e, name) { }; /**************************************/ -B5500SPOUnit.prototype.removeClass = function(e, name) { +B5500SPOUnit.prototype.removeClass = function removeClass(e, name) { /* Removes the class "name" from the element "e"s class list */ e.className = e.className.replace(new RegExp("\\b" + name + "\\b\\s*", "g"), ""); }; /**************************************/ -B5500SPOUnit.prototype.setNotReady = function() { +B5500SPOUnit.prototype.setNotReady = function setNotReady() { /* Sets the status of the SPO to Not Ready */ if (this.spoState == this.spoLocal) { @@ -130,7 +132,7 @@ B5500SPOUnit.prototype.setNotReady = function() { }; /**************************************/ -B5500SPOUnit.prototype.setReady = function() { +B5500SPOUnit.prototype.setReady = function setReady() { /* Sets the status of the SPO to Ready */ if (this.spoState == this.spoNotReady) { @@ -140,7 +142,7 @@ B5500SPOUnit.prototype.setReady = function() { }; /**************************************/ -B5500SPOUnit.prototype.setLocal = function() { +B5500SPOUnit.prototype.setLocal = function setLocal() { /* Sets the status of the SPO to Local */ if (this.spoState == this.spoRemote) { @@ -160,7 +162,7 @@ B5500SPOUnit.prototype.setLocal = function() { }; /**************************************/ -B5500SPOUnit.prototype.setRemote = function() { +B5500SPOUnit.prototype.setRemote = function setRemote() { /* Sets the status of the SPO to Remote */ if (this.spoState == this.spoLocal) { @@ -172,7 +174,7 @@ B5500SPOUnit.prototype.setRemote = function() { }; /**************************************/ -B5500SPOUnit.prototype.appendEmptyLine = function() { +B5500SPOUnit.prototype.appendEmptyLine = function appendEmptyLine() { /* Removes excess lines already printed, then appends a new
 element
     to the