PDP-11/20 test machine can load BASIC from paper tape, but still has some trouble running it
This commit is contained in:
parent
fbb36c1d1b
commit
16b8b59f12
14 changed files with 383 additions and 274 deletions
|
|
@ -52,7 +52,7 @@ if (NODE) {
|
|||
*
|
||||
* For example, opADD() passes the helper function fnADD() to the appropriate update method. This
|
||||
* allows the update method to perform the entire read/modify/write operation, because the modify
|
||||
* step is performed internally via the fnXXX() helper function.
|
||||
* step is performed internally, via the fnXXX() helper function.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -1117,7 +1117,24 @@ PDP11.opHALT = function(opCode)
|
|||
this.regErr |= PDP11.CPUERR.BADHALT;
|
||||
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.REASON.HALT);
|
||||
} else {
|
||||
this.stopCPU();
|
||||
if (!this.dbg) {
|
||||
/*
|
||||
* This will leave the PC exactly where it's supposed to be: at the address of the HALT + 2.
|
||||
*/
|
||||
this.stopCPU();
|
||||
} else {
|
||||
/*
|
||||
* When the Debugger is present, this call will rewind PC by 2 so that the HALT instruction is
|
||||
* displayed, making it clear why the processor stopped; the user could also use the "dh" command
|
||||
* to dump the Debugger's instruction history buffer to see why it stopped, assuming the history
|
||||
* buffer is enabled, but that's more work.
|
||||
*
|
||||
* Because rewinding is not normal CPU behavior, attempting to Run again (or use the Debugger's
|
||||
* "g" command) will cause an immediate HALT again; the work-around is simple: either set the PC to
|
||||
* a new address (eg, "r pc=pc+2") or single-step the HALT instruction ("t").
|
||||
*/
|
||||
this.dbg.stopInstruction();
|
||||
}
|
||||
}
|
||||
this.nStepCycles -= 7;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -997,10 +997,16 @@ if (DEBUGGER) {
|
|||
|
||||
var sComment = "history";
|
||||
var nSequence = nPrev--;
|
||||
|
||||
/*
|
||||
* TODO: Need to some UI to control whether cycle counts are displayed as part of the history.
|
||||
* It's currently disabled in checkInstruction(), so it's disable here, too.
|
||||
*
|
||||
if (DEBUG && dbgAddr.cycleCount != null) {
|
||||
sComment = "cycles";
|
||||
nSequence = dbgAddr.cycleCount;
|
||||
}
|
||||
*/
|
||||
|
||||
var sInstruction = this.getInstruction(dbgAddrNew, sComment, nSequence);
|
||||
|
||||
|
|
@ -1623,7 +1629,7 @@ if (DEBUGGER) {
|
|||
if (opCode != null) {
|
||||
var dbgAddr = this.aOpcodeHistory[this.iOpcodeHistory];
|
||||
this.setAddr(dbgAddr, cpu.getPC());
|
||||
if (DEBUG) dbgAddr.cycleCount = cpu.getCycles();
|
||||
// if (DEBUG) dbgAddr.cycleCount = cpu.getCycles();
|
||||
if (++this.iOpcodeHistory == this.aOpcodeHistory.length) this.iOpcodeHistory = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -425,8 +425,10 @@ var PDP11 = {
|
|||
|
||||
LKS: 0o177546, // KW11-L Clock Status
|
||||
|
||||
PRS: 0o177550, // PC11/PR11 Reader Status Register
|
||||
PRB: 0o177552, // PC11/PR11 Reader Buffer Register
|
||||
PRS: 0o177550, // PC11 (and PR11) Reader Status Register
|
||||
PRB: 0o177552, // PC11 (and PR11) Reader Buffer Register
|
||||
PPS: 0o177554, // PC11 Punch Status Register
|
||||
PPB: 0o177556, // PC11 Punch Buffer Register
|
||||
|
||||
RCSR: 0o177560, // Display Terminal: Receiver Status Register
|
||||
RBUF: 0o177562, // Display Terminal: Receiver Data Buffer Register
|
||||
|
|
@ -511,6 +513,9 @@ var PDP11 = {
|
|||
PSW: 0o177776 // 777776 17777776 0x3FFFFE Processor Status Word
|
||||
},
|
||||
PC11: { // High Speed Reader & Punch (PR11 is a Reader-only unit)
|
||||
PRI: 4, // NOTE: reader has precedence over punch
|
||||
RVEC: 0o70, // reader vector
|
||||
PVEC: 0o74, // punch vector
|
||||
PRS: {
|
||||
RE: 0x0001, // Reader Enable (W/O)
|
||||
RIE: 0x0040, // Reader Interrupt Enable (allows the DONE and ERROR bits to trigger an interrupt)
|
||||
|
|
@ -518,6 +523,7 @@ var PDP11 = {
|
|||
BUSY: 0x0800, // Busy (R/O)
|
||||
ERROR: 0x8000, // Error (R/O)
|
||||
CLEAR: 0x08C0, // bits cleared on INIT
|
||||
RMASK: 0xFFFE, // bits readable (TODO: All I know for sure is that bit 0 is NOT readable; see PC11.readPRS())
|
||||
WMASK: 0x0041 // bits writable
|
||||
},
|
||||
PRB: {
|
||||
|
|
|
|||
|
|
@ -206,6 +206,8 @@ PC11.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
|
||||
var pc11 = this;
|
||||
|
||||
this.configMount = this.cmp.getMachineParm('autoMount') || this.configMount;
|
||||
|
||||
if (this.configMount) {
|
||||
|
|
@ -223,6 +225,12 @@ PC11.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
}
|
||||
}
|
||||
|
||||
this.triggerReaderInterrupt = this.cpu.addTrigger(PDP11.PC11.RVEC, PDP11.PC11.PRI);
|
||||
|
||||
this.timerReaderAdvance = this.cpu.addTimer(function readyReader() {
|
||||
pc11.advanceReader();
|
||||
});
|
||||
|
||||
bus.addIOTable(this, PC11.UNIBUS_IOTABLE);
|
||||
|
||||
this.addTape("None", PC11.LOADSTATE.NONE, true);
|
||||
|
|
@ -623,16 +631,57 @@ PC11.prototype.restore = function(data)
|
|||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* advanceReader()
|
||||
*
|
||||
* If the reader is enabled (RE is set) and there is no exceptional condition (ie, ERROR is set),
|
||||
* and if the buffer register is empty (DONE is clear), then if we have more data in our internal buffer,
|
||||
* store it in the buffer register, and optionally trigger an interrupt if device interrupts are enabled.
|
||||
*
|
||||
* @this {PC11}
|
||||
*/
|
||||
PC11.prototype.advanceReader = function()
|
||||
{
|
||||
if ((this.prs & (PDP11.PC11.PRS.RE | PDP11.PC11.PRS.ERROR)) == PDP11.PC11.PRS.RE) {
|
||||
if (!(this.prs & PDP11.PC11.PRS.DONE)) {
|
||||
if (this.iTapeData < this.aTapeData.length) {
|
||||
this.prb = this.aTapeData[this.iTapeData++];
|
||||
this.prs |= PDP11.PC11.PRS.DONE;
|
||||
this.prs &= ~PDP11.PC11.PRS.BUSY;
|
||||
if (this.prs & PDP11.PC11.PRS.RIE) {
|
||||
this.cpu.setTrigger(this.triggerReaderInterrupt);
|
||||
}
|
||||
/*
|
||||
* The PC11, by virtue of its "high speed", is supposed to deliver characters at 300 CPS,
|
||||
* so for now, that's what we're going to deliver (ie, 1000ms / 300).
|
||||
*
|
||||
* TODO: Review this code. If we don't set the fReset parameter to true, the timer will eventually
|
||||
* fire while the "Absolute Loader" tape is still reading bytes from, say, the "BASIC (Single User)"
|
||||
* tape, causing an EXTRA advance to occur and causing a byte to be skipped. Passing true ensures
|
||||
* that the timer cannot fire for AT LEAST 3ms after each advance. But again, I need to understand
|
||||
* the reader's actual behavior.
|
||||
*/
|
||||
this.cpu.setTimer(this.timerReaderAdvance, 1000/300, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* readPRS(addr)
|
||||
*
|
||||
* NOTE: We use the PRS RMASK to honor the "write-only" behavior of bit 0, the reader enable bit (RE), because
|
||||
* DEC's tiny Bootstrap Loader (/apps/pdp11/boot/bootstrap/BOOTSTRAP-16KB.lst) repeatedly enables the reader using
|
||||
* the INC instruction, which causes the PRS to be read, incremented, and written, so if bit 0 isn't always read
|
||||
* as zero, the INC instruction would clear RE instead of setting it.
|
||||
*
|
||||
* @this {PC11}
|
||||
* @param {number} addr (eg, PDP11.UNIBUS.PRS or 177550)
|
||||
* @return {number}
|
||||
*/
|
||||
PC11.prototype.readPRS = function(addr)
|
||||
{
|
||||
return this.prs;
|
||||
return this.prs & PDP11.PC11.PRS.RMASK; // RMASK honors the "write-only" nature of the RE bit by returning zero on reads
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -648,14 +697,16 @@ PC11.prototype.writePRS = function(data, addr)
|
|||
if (this.prs & PDP11.PC11.PRS.ERROR) {
|
||||
data &= ~PDP11.PC11.PRS.RE;
|
||||
// if (this.prs & PDP11.PC11.PRS.RIE) {
|
||||
// TODO: Generate an interrupt
|
||||
// TODO: Generate an interrupt (error condition)
|
||||
// }
|
||||
} else {
|
||||
this.prs &= ~PDP11.PC11.PRS.DONE;
|
||||
this.prs |= PDP11.PC11.PRS.BUSY;
|
||||
this.prb = 0;
|
||||
}
|
||||
}
|
||||
this.prs = (this.prs & ~PDP11.PC11.PRS.WMASK) | (data & PDP11.PC11.PRS.WMASK);
|
||||
this.advanceReader();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -667,6 +718,12 @@ PC11.prototype.writePRS = function(data, addr)
|
|||
*/
|
||||
PC11.prototype.readPRB = function(addr)
|
||||
{
|
||||
/*
|
||||
* I'm guessing that the DONE and BUSY bits always remain more-or-less inverses of each other. They definitely
|
||||
* start out that way when writePRS() sets the reader enable (RE) bit, and so that's how we treat them elsewhere, too.
|
||||
*/
|
||||
this.prs &= ~PDP11.PC11.PRS.DONE;
|
||||
this.prs |= PDP11.PC11.PRS.BUSY;
|
||||
return this.prb;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -475,10 +475,6 @@ SerialPortPDP11.prototype.receiveData = function(data)
|
|||
return true; // for now, return true regardless, since we're buffering everything anyway
|
||||
};
|
||||
|
||||
SerialPortPDP11.prototype.advanceRBUF = function()
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* transmitByte(b)
|
||||
*
|
||||
|
|
|
|||
Loading…
Reference in a new issue