Merge branch 'next-release'

This commit is contained in:
Jeff Parsons 2016-11-30 13:09:10 -08:00
commit 4022fc03a4
303 changed files with 14860 additions and 877 deletions

View file

@ -661,8 +661,26 @@ MarkOut.prototype.convertMDBlocks = function(sMD, sIndent)
* Explode the given Markdown sequence into blocks based purely on double-linefeed sequences.
*/
var asBlocks = sMD.split("\n\n");
for (var iBlock = 0; iBlock < asBlocks.length; iBlock++) {
sHTML += this.convertMDBlock(asBlocks[iBlock], sIndent);
/*
* Also, for any block that begins with triple-backtick but doesn't end with one, try to find the matching
* end block, and then merge them all into a single block. TODO: This is a hack; fenced blocks need to be
* processed earlier, not in convertMDBlock(), but this is OK for now.
*/
var iBlock = 0;
while (iBlock < asBlocks.length) {
var fCodeBlock = false;
var sBlock = asBlocks[iBlock++];
if (sBlock.substr(0,3) == "```") {
fCodeBlock = true;
var sBlockEnd = sBlock;
while (sBlockEnd.slice(-3) != "```" && iBlock < asBlocks.length) {
sBlockEnd = asBlocks[iBlock++];
sBlock += "\n\n" + sBlockEnd;
}
}
sHTML += this.convertMDBlock(sBlock, sIndent);
if (fCodeBlock) sHTML += this.convertMDBlock("", sIndent);
}
return sHTML;
};

View file

@ -93,7 +93,8 @@ Keyboard8080.STATE = {
CAPS_LOCK: 0x0200,
NUM_LOCK: 0x0400,
SCROLL_LOCK: 0x0800,
ALL_LOCKS: 0x0E00 // CAPS_LOCK | NUM_LOCK | SCROLL_LOCK
ALL_LOCKS: 0x0E00, // CAPS_LOCK | NUM_LOCK | SCROLL_LOCK
REMAPPED: 0x1000
};
Keyboard8080.MINPRESSTIME = 50; // minimum milliseconds to wait before auto-releasing keys
@ -187,7 +188,7 @@ Keyboard8080.VT100 = {
[Keys.KEYCODE.F4]: 0x41, // aka PF4
[Keys.KEYCODE.F2]: 0x42, // aka PF2
[Keys.KEYCODE.NUM_0]: 0x43,
[Keys.KEYCODE.F7]: 0x44, // aka LINE FEED
[Keys.KEYCODE.F7]: 0x44, // aka LINE-FEED
[Keys.KEYCODE.BSLASH]: 0x45,
[Keys.ASCII.L]: 0x46,
[Keys.ASCII.K]: 0x47,
@ -214,7 +215,7 @@ Keyboard8080.VT100 = {
[Keys.ASCII.N]: 0x67,
[Keys.ASCII.B]: 0x68,
[Keys.ASCII.X]: 0x69,
[Keys.KEYCODE.F8]: 0x6A, // aka NO SCROLL
[Keys.KEYCODE.F8]: 0x6A, // aka NO-SCROLL
[Keys.KEYCODE.NUM_9]: 0x70,
[Keys.KEYCODE.NUM_3]: 0x71,
[Keys.KEYCODE.NUM_6]: 0x72,
@ -233,7 +234,11 @@ Keyboard8080.VT100 = {
ALTCODES: {},
LEDCODES: {},
SOFTCODES: {
'setup': Keys.KEYCODE.F9
'num-comma': Keys.KEYCODE.F5, // since modern keypads don't typically have a comma...
'break': Keys.KEYCODE.F6,
'line-feed': Keys.KEYCODE.F7,
'no-scroll': Keys.KEYCODE.F8,
'setup': Keys.KEYCODE.F9
},
/*
* Reading port 0x82 returns a key address from the VT100 keyboard's UART data output.
@ -606,17 +611,17 @@ Keyboard8080.prototype.updateLEDs = function(bLEDs)
};
/**
* checkModifierKeys(softCode, fDown, fRight)
* checkModifierKeys(keyCode, fDown, fRight)
*
* @this {Keyboard8080}
* @param {number|string} softCode (ie, either a keycode or string ID)
* @param {number} keyCode (ie, either a keycode or string ID)
* @param {boolean} fDown (true if key going down, false if key going up)
* @param {boolean} fRight (true if key is on the right, false if not or unknown or n/a)
*/
Keyboard8080.prototype.checkModifierKeys = function(softCode, fDown, fRight)
Keyboard8080.prototype.checkModifierKeys = function(keyCode, fDown, fRight)
{
var bit = 0;
switch(softCode) {
switch(keyCode) {
case Keys.KEYCODE.SHIFT:
bit = fRight? Keyboard8080.STATE.RSHIFT : Keyboard8080.STATE.SHIFT;
break;
@ -676,22 +681,63 @@ Keyboard8080.prototype.onKeyDown = function(event, fDown)
{
var fPass = true;
var keyCode = event.keyCode;
var softCode = this.getSoftCode(keyCode);
/*
* We now keep track of physical keyboard modifier keys. This makes it possible for new services
* to eventually be implemented (simulateKeysDown() and simulateKeysUp()), to map special ALT-key
* combinations to VT100 keys, etc.
*/
this.checkModifierKeys(keyCode, fDown, event.location == Keys.LOCATION.RIGHT);
var softCode = this.getSoftCode(keyCode);
if (softCode) {
/*
* We now keep track of any physical keyboard modifier keys that also exist on the simulated
* keyboard; in the case of the VT100, that means CTRL and SHIFT. This will make it possible
* for a new pair of services to eventually be implemented: simulateKeysDown() and simulateKeysUp().
* Key combinations involving the "meta" key (ie, the Windows or Command key) are meaningless to
* the VT100, so we ignore them. The "meta" key itself is already effectively ignored, because it's
* not acknowledged by getSoftCode(), but we also don't want any of the keys combined with "meta"
* slipping through either.
*/
this.checkModifierKeys(softCode, fDown, event.location == Keys.LOCATION.RIGHT);
if (!event.metaKey) {
/*
* The LINE-FEED key is an important key on the VT100, and while we DO map a host function key
* to it (F7), I like the idea of making ALT-ENTER an alias for LINE-FEED as well. Ditto for
* making ALT-DELETE an alias for BACKSPACE (and no, I don't mean ALT-BACKSPACE as an alias for
* DELETE; see my earlier discussion involving BACKSPACE and DELETE).
*
* Of course, as experienced VT100 users know, it's always possible to type CTRL-J for LINE-FEED
* and CTRL-H for BACKSPACE, too. But not all our users are that experienced.
*
* I was also tempted to use CTRL-ENTER or SHIFT-ENTER, but those are composable VT100 key
* sequences, so it's best not to muck with those.
*
* Finally, this hack is complicated by the fact that if the ALT key is released first, we run
* the risk of the remapped key being stuck "down". Hence the new REMAPPED bit, which should
* remain set (as a "proxy" for the ALT bit) as long as a remapped key is down.
*/
var fRemapped = false;
if (this.bitsState & (Keyboard8080.STATE.ALTS | Keyboard8080.STATE.REMAPPED)) {
if (softCode == Keys.KEYCODE.CR) {
softCode = Keys.KEYCODE.F7;
fRemapped = true;
}
else if (softCode == Keys.KEYCODE.BS) {
softCode = Keys.KEYCODE.DEL;
fRemapped = true;
}
if (fRemapped) {
if (fDown) {
this.bitsState |= Keyboard8080.STATE.REMAPPED;
} else {
this.bitsState &= ~Keyboard8080.STATE.REMAPPED;
}
}
}
fPass = this.onSoftKeyDown(softCode, fDown);
/*
* As onKeyPress() explains, the only key presses we're interested in are letters, which provide
* an important clue regarding the CAPS-LOCK state. For all other keys, we call preventDefault(),
* which "suppresses" the keyPress event.
* which normally "suppresses" the keyPress event, as well as other unwanted browser behaviors
* (eg, the SPACE key, which browsers interpret as a desire to scroll the entire web page down).
*/
if (!(softCode >= Keys.ASCII.A && softCode <= Keys.ASCII.Z)) {
event.preventDefault();

View file

@ -75,14 +75,6 @@ function BusPDP11(parmsBus, cpu, dbg)
*/
this.nBusWidth = parmsBus['busWidth'] || 16;
/*
* This controls the location of the IOPAGE (ie, at the top of 16-bit, 18-bit, or 22-bit address range).
* It is managed by setIOPageRange(). reset() establishes the default (16).
*/
this.nIOPageRange = 0; // zero means no IOPAGE access (yet)
this.aIOPrevBlocks = []; // this saves any previous blocks we had to replace with IOPAGE blocks
this.aIOPageBlocks = null; // this saves the memory blocks allocated for IOPAGE, so we can reuse them
/*
* Compute all BusPDP11 memory block parameters now, based on the width of the bus.
*
@ -135,7 +127,6 @@ function BusPDP11(parmsBus, cpu, dbg)
this.fIOBreakAll = false;
this.nDisableFaults = 0;
this.fFault = false;
this.cbRAM = 0;
/*
* Array of RESET notification handlers registered by Device components.
@ -171,7 +162,6 @@ BusPDP11.IOPAGE_LENGTH = 0x2000; // ie, 8Kb
BusPDP11.IOPAGE_MASK = BusPDP11.IOPAGE_LENGTH - 1;
BusPDP11.UNIBUS_22BIT = 0x3C0000; /*017000000*/
BusPDP11.MAX_MEMORY = BusPDP11.UNIBUS_22BIT - 16384; // Maximum memory address (need less memory for BSD 2.9 boot)
BusPDP11.ERROR = {
RANGE_INUSE: 1,
@ -458,16 +448,34 @@ BusPDP11.IOController = {
*
* Allocate enough (empty) Memory blocks to span the entire physical address space.
*
* Note that we now maintain two parallel arrays of these Memory blocks: aBusBlocks is for use by
* devices (or any component using the "direct" interfaces), while aMemBlocks is for use by the CPU.
*
* Whereas the Bus memory map is fixed at init time, the CPU's memory map will vary depending on MMU
* settings. The CPU will call setIOPageRange() as needed to update the range of addressible memory,
* which in turn will determine where the IOPAGE can be accessed.
*
* @this {BusPDP11}
*/
BusPDP11.prototype.initMemory = function()
{
var block = new MemoryPDP11(this);
block.copyBreakpoints(this.dbg);
this.aBusBlocks = new Array(this.nBlockTotal);
this.aMemBlocks = new Array(this.nBlockTotal);
for (var iBlock = 0; iBlock < this.nBlockTotal; iBlock++) {
this.aMemBlocks[iBlock] = block;
this.aBusBlocks[iBlock] = this.aMemBlocks[iBlock] = block;
}
var addrIOPage = this.addrTotal - BusPDP11.IOPAGE_LENGTH;
this.addMemory(addrIOPage, BusPDP11.IOPAGE_LENGTH, MemoryPDP11.TYPE.CONTROLLER, this);
this.iBlockIOPageBus = (addrIOPage & this.nBusMask) >>> this.nBlockShift;
this.iBlockIOPageMem = this.iBlockIOPageBus;
this.nIOPageRange = 0;
this.nMemMask = this.nBusMask;
};
/**
@ -476,34 +484,24 @@ BusPDP11.prototype.initMemory = function()
* We can define the IOPAGE address range with a single number, because the size of the IOPAGE is fixed at 8Kb.
* The bottom of the range is (2 ^ nRange) - IOPAGE_LENGTH, and the top is (2 ^ nRange) - 1.
*
* Note that we defer our initial call to this function as long as possible (ie, at the end of reset()) so that
* other components have first shot at adding their own memory blocks (if any), because addMemory() only allows
* installing memory on top of empty memory blocks.
*
* @this {BusPDP11}
* @param {number} nRange (16, 18 or 22; 0 removes the IOPAGE altogether)
*/
BusPDP11.prototype.setIOPageRange = function(nRange)
{
if (nRange != this.nIOPageRange) {
var addr;
if (this.nIOPageRange) {
addr = (1 << this.nIOPageRange) - BusPDP11.IOPAGE_LENGTH;
this.setMemoryBlocks(addr, BusPDP11.IOPAGE_LENGTH, this.aIOPrevBlocks);
this.nIOPageRange = 0;
for (var iBlock = 0; iBlock < this.nBlockTotal; iBlock++) {
this.aMemBlocks[iBlock] = this.aBusBlocks[iBlock];
}
this.nIOPageRange = 0;
this.nMemMask = this.nBusMask;
if (nRange) {
this.nIOPageRange = nRange;
addr = (1 << nRange);
this.nBusMask = (addr - 1);
var addr = (1 << nRange);
this.nMemMask = (addr - 1);
addr -= BusPDP11.IOPAGE_LENGTH;
this.aIOPrevBlocks = this.getMemoryBlocks(addr, BusPDP11.IOPAGE_LENGTH);
if (this.aIOPageBlocks) {
this.setMemoryBlocks(addr, BusPDP11.IOPAGE_LENGTH, this.aIOPageBlocks);
} else {
this.addMemory(addr, BusPDP11.IOPAGE_LENGTH, MemoryPDP11.TYPE.CONTROLLER, this);
this.aIOPageBlocks = this.getMemoryBlocks(addr, BusPDP11.IOPAGE_LENGTH);
}
this.iBlockIOPageMem = (addr & this.nMemMask) >>> this.nBlockShift;
this.aMemBlocks[this.iBlockIOPageMem] = this.aBusBlocks[this.iBlockIOPageBus];
}
}
};
@ -623,9 +621,9 @@ BusPDP11.prototype.addMemory = function(addr, size, type, controller)
var sizeLeft = size;
var iBlock = addrNext >>> this.nBlockShift;
while (sizeLeft > 0 && iBlock < this.aMemBlocks.length) {
while (sizeLeft > 0 && iBlock < this.aBusBlocks.length) {
var block = this.aMemBlocks[iBlock];
var block = this.aBusBlocks[iBlock];
var addrBlock = iBlock * this.nBlockSize;
var sizeBlock = this.nBlockSize - (addrNext - addrBlock);
if (sizeBlock > sizeLeft) sizeBlock = sizeLeft;
@ -663,16 +661,13 @@ BusPDP11.prototype.addMemory = function(addr, size, type, controller)
var blockNew = new MemoryPDP11(this, addrNext, sizeBlock, this.nBlockSize, type, controller);
blockNew.copyBreakpoints(this.dbg, block);
this.aMemBlocks[iBlock++] = blockNew;
this.aBusBlocks[iBlock++] = blockNew;
addrNext = addrBlock + this.nBlockSize;
sizeLeft -= sizeBlock;
}
if (sizeLeft <= 0) {
if (type == MemoryPDP11.TYPE.RAM) {
this.cbRAM += size;
}
this.status((size >> 10) + "Kb " + MemoryPDP11.TYPE_NAMES[type] + " at " + str.toOct(addr));
return true;
}
@ -692,10 +687,10 @@ BusPDP11.prototype.cleanMemory = function(addr, size)
{
var fClean = true;
var iBlock = addr >>> this.nBlockShift;
while (size > 0 && iBlock < this.aMemBlocks.length) {
if (this.aMemBlocks[iBlock].fDirty) {
this.aMemBlocks[iBlock].fDirty = fClean = false;
this.aMemBlocks[iBlock].fDirtyEver = true;
while (size > 0 && iBlock < this.aBusBlocks.length) {
if (this.aBusBlocks[iBlock].fDirty) {
this.aBusBlocks[iBlock].fDirty = fClean = false;
this.aBusBlocks[iBlock].fDirtyEver = true;
}
size -= this.nBlockSize;
iBlock++;
@ -715,15 +710,8 @@ BusPDP11.prototype.zeroMemory = function(addr, size, pattern)
{
var off = addr & this.nBlockLimit;
var iBlock = addr >>> this.nBlockShift;
while (size > 0 && iBlock < this.aMemBlocks.length) {
var block = this.aMemBlocks[iBlock];
if (block.controller) {
if (this.aIOPageBlocks && this.aIOPageBlocks.length == this.aIOPrevBlocks.length) {
var i = this.aIOPageBlocks.indexOf(block);
if (i >= 0) block = this.aIOPrevBlocks[i];
}
}
if (block) block.zero(off, size, pattern);
while (size > 0 && iBlock < this.aBusBlocks.length) {
this.aBusBlocks[iBlock].zero(off, size, pattern);
size -= this.nBlockSize;
iBlock++;
off = 0;
@ -789,7 +777,7 @@ BusPDP11.prototype.scanMemory = function(info, addr, size)
info.cbTotal = 0;
info.cBlocks = 0;
while (iBlock <= iBlockMax) {
var block = this.aMemBlocks[iBlock];
var block = this.aBusBlocks[iBlock];
info.cbTotal += block.size;
if (block.size) {
info.aBlocks.push(usr.initBitFields(BusPDP11.BlockInfo, iBlock, 0, 0, block.type));
@ -817,10 +805,10 @@ BusPDP11.prototype.removeMemory = function(addr, size)
if (!(addr & this.nBlockLimit) && size && !(size & this.nBlockLimit)) {
var iBlock = addr >>> this.nBlockShift;
while (size > 0) {
var blockOld = this.aMemBlocks[iBlock];
var blockOld = this.aBusBlocks[iBlock];
var blockNew = new MemoryPDP11(this, addr);
blockNew.copyBreakpoints(this.dbg, blockOld);
this.aMemBlocks[iBlock++] = blockNew;
this.aBusBlocks[iBlock++] = blockNew;
addr = iBlock * this.nBlockSize;
size -= this.nBlockSize;
}
@ -841,8 +829,8 @@ BusPDP11.prototype.getMemoryBlocks = function(addr, size)
{
var aBlocks = [];
var iBlock = addr >>> this.nBlockShift;
while (size > 0 && iBlock < this.aMemBlocks.length) {
aBlocks.push(this.aMemBlocks[iBlock++]);
while (size > 0 && iBlock < this.aBusBlocks.length) {
aBlocks.push(this.aBusBlocks[iBlock++]);
size -= this.nBlockSize;
}
return aBlocks;
@ -867,7 +855,7 @@ BusPDP11.prototype.setMemoryAccess = function(addr, size, afn, fQuiet)
if (!(addr & this.nBlockLimit) && size && !(size & this.nBlockLimit)) {
var iBlock = addr >>> this.nBlockShift;
while (size > 0) {
var block = this.aMemBlocks[iBlock];
var block = this.aBusBlocks[iBlock];
if (!block.controller) {
return this.reportError(BusPDP11.ERROR.NO_CONTROLLER, addr, size, fQuiet);
}
@ -899,7 +887,7 @@ BusPDP11.prototype.setMemoryBlocks = function(addr, size, aBlocks, type)
{
var i = 0;
var iBlock = addr >>> this.nBlockShift;
while (size > 0 && iBlock < this.aMemBlocks.length) {
while (size > 0 && iBlock < this.aBusBlocks.length) {
var block = aBlocks[i++];
this.assert(block);
if (!block) break;
@ -908,7 +896,7 @@ BusPDP11.prototype.setMemoryBlocks = function(addr, size, aBlocks, type)
blockNew.clone(block, type, this.dbg);
block = blockNew;
}
this.aMemBlocks[iBlock++] = block;
this.aBusBlocks[iBlock++] = block;
size -= this.nBlockSize;
}
};
@ -922,21 +910,25 @@ BusPDP11.prototype.setMemoryBlocks = function(addr, size, aBlocks, type)
*/
BusPDP11.prototype.getByte = function(addr)
{
/*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
* UNIBUS relocation map.
*/
if (addr >= BusPDP11.UNIBUS_22BIT) {
addr = this.cpu.mapUnibus(addr);
}
return this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].readByte(addr & this.nBlockLimit, addr);
return this.aMemBlocks[(addr & this.nMemMask) >>> this.nBlockShift].readByte(addr & this.nBlockLimit, addr);
};
/**
* getBlockDirect(addr)
*
* @this {BusPDP11}
* @param {number} addr is a physical address
* @return {MemoryPDP11}
*/
BusPDP11.prototype.getBlockDirect = function(addr)
{
return this.aBusBlocks[(addr & this.nBusMask) >>> this.nBlockShift];
};
/**
* getByteDirect(addr)
*
* This is useful for the Debugger and other components that want to access physical memory without side-effects.
* This is used for device I/O and Debugger physical memory requests, not the CPU.
*
* @this {BusPDP11}
* @param {number} addr is a physical address
@ -944,17 +936,9 @@ BusPDP11.prototype.getByte = function(addr)
*/
BusPDP11.prototype.getByteDirect = function(addr)
{
/*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
* UNIBUS relocation map.
*/
if (addr >= BusPDP11.UNIBUS_22BIT) {
addr = this.cpu.mapUnibus(addr);
}
this.fFault = false;
this.nDisableFaults++;
var b = this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].readByteDirect(addr & this.nBlockLimit, addr);
var b = this.getBlockDirect(addr).readByteDirect(addr & this.nBlockLimit, addr);
this.nDisableFaults--;
return b;
};
@ -968,16 +952,8 @@ BusPDP11.prototype.getByteDirect = function(addr)
*/
BusPDP11.prototype.getWord = function(addr)
{
/*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
* UNIBUS relocation map.
*/
if (addr >= BusPDP11.UNIBUS_22BIT) {
addr = this.cpu.mapUnibus(addr);
}
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
var iBlock = (addr & this.nMemMask) >>> this.nBlockShift;
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
return this.aMemBlocks[iBlock++].readByte(off, addr) | (this.aMemBlocks[iBlock & this.nBlockMask].readByte(0, addr + 1) << 8);
}
@ -987,7 +963,7 @@ BusPDP11.prototype.getWord = function(addr)
/**
* getWordDirect(addr)
*
* This is useful for the Debugger and other components that want to bypass getWord() breakpoint detection.
* This is used for device I/O and Debugger physical memory requests, not the CPU.
*
* @this {BusPDP11}
* @param {number} addr is a physical address
@ -995,23 +971,15 @@ BusPDP11.prototype.getWord = function(addr)
*/
BusPDP11.prototype.getWordDirect = function(addr)
{
/*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
* UNIBUS relocation map.
*/
if (addr >= BusPDP11.UNIBUS_22BIT) {
addr = this.cpu.mapUnibus(addr);
}
var w;
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
this.fFault = false;
this.nDisableFaults++;
var off = addr & this.nBlockLimit;
var block = this.getBlockDirect(addr);
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
w = this.aMemBlocks[iBlock++].readByteDirect(off, addr) | (this.aMemBlocks[iBlock & this.nBlockMask].readByteDirect(0, addr + 1) << 8);
w = block.readByteDirect(off, addr) | (this.getBlockDirect(addr + 1).readByteDirect(0, addr + 1) << 8);
} else {
w = this.aMemBlocks[iBlock].readWordDirect(off, addr);
w = block.readWordDirect(off, addr);
}
this.nDisableFaults--;
return w;
@ -1026,22 +994,13 @@ BusPDP11.prototype.getWordDirect = function(addr)
*/
BusPDP11.prototype.setByte = function(addr, b)
{
/*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
* UNIBUS relocation map.
*/
if (addr >= BusPDP11.UNIBUS_22BIT) {
addr = this.cpu.mapUnibus(addr);
}
this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].writeByte(addr & this.nBlockLimit, b & 0xff, addr);
this.aMemBlocks[(addr & this.nMemMask) >>> this.nBlockShift].writeByte(addr & this.nBlockLimit, b & 0xff, addr);
};
/**
* setByteDirect(addr, b)
*
* This is useful for the Debugger and other components that want to bypass breakpoint detection AND read-only
* memory protection (for example, this is an interface the ROM component could use to initialize ROM contents).
* This is used for device I/O and Debugger physical memory requests, not the CPU.
*
* @this {BusPDP11}
* @param {number} addr is a physical address
@ -1049,17 +1008,9 @@ BusPDP11.prototype.setByte = function(addr, b)
*/
BusPDP11.prototype.setByteDirect = function(addr, b)
{
/*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
* UNIBUS relocation map.
*/
if (addr >= BusPDP11.UNIBUS_22BIT) {
addr = this.cpu.mapUnibus(addr);
}
this.fFault = false;
this.nDisableFaults++;
this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].writeByteDirect(addr & this.nBlockLimit, b & 0xff, addr);
this.getBlockDirect(addr).writeByteDirect(addr & this.nBlockLimit, b & 0xff, addr);
this.nDisableFaults--;
};
@ -1072,16 +1023,8 @@ BusPDP11.prototype.setByteDirect = function(addr, b)
*/
BusPDP11.prototype.setWord = function(addr, w)
{
/*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
* UNIBUS relocation map.
*/
if (addr >= BusPDP11.UNIBUS_22BIT) {
addr = this.cpu.mapUnibus(addr);
}
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
var iBlock = (addr & this.nMemMask) >>> this.nBlockShift;
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
this.aMemBlocks[iBlock++].writeByte(off, w & 0xff, addr);
this.aMemBlocks[iBlock & this.nBlockMask].writeByte(0, (w >> 8) & 0xff, addr + 1);
@ -1093,8 +1036,7 @@ BusPDP11.prototype.setWord = function(addr, w)
/**
* setWordDirect(addr, w)
*
* This is useful for the Debugger and other components that want to bypass breakpoint detection AND read-only
* memory protection (for example, this is an interface the ROM component could use to initialize ROM contents).
* This is used for device I/O and Debugger physical memory requests, not the CPU.
*
* @this {BusPDP11}
* @param {number} addr is a physical address
@ -1102,23 +1044,15 @@ BusPDP11.prototype.setWord = function(addr, w)
*/
BusPDP11.prototype.setWordDirect = function(addr, w)
{
/*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
* UNIBUS relocation map.
*/
if (addr >= BusPDP11.UNIBUS_22BIT) {
addr = this.cpu.mapUnibus(addr);
}
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
this.fFault = false;
this.nDisableFaults++;
var off = addr & this.nBlockLimit;
var block = this.getBlockDirect(addr);
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
this.aMemBlocks[iBlock++].writeByteDirect(off, w & 0xff, addr);
this.aMemBlocks[iBlock & this.nBlockMask].writeByteDirect(0, (w >> 8) & 0xff, addr + 1);
block.writeByteDirect(off, w & 0xff, addr);
this.getBlockDirect(addr + 1).writeByteDirect(0, (w >> 8) & 0xff, addr + 1);
} else {
this.aMemBlocks[iBlock].writeWordDirect(off, w & 0xffff, addr);
block.writeWordDirect(off, w & 0xffff, addr);
}
this.nDisableFaults--;
};
@ -1134,7 +1068,7 @@ BusPDP11.prototype.addMemBreak = function(addr, fWrite)
{
if (DEBUGGER) {
var iBlock = addr >>> this.nBlockShift;
this.aMemBlocks[iBlock].addBreakpoint(addr & this.nBlockLimit, fWrite);
this.aBusBlocks[iBlock].addBreakpoint(addr & this.nBlockLimit, fWrite);
}
};
@ -1149,7 +1083,7 @@ BusPDP11.prototype.removeMemBreak = function(addr, fWrite)
{
if (DEBUGGER) {
var iBlock = addr >>> this.nBlockShift;
this.aMemBlocks[iBlock].removeBreakpoint(addr & this.nBlockLimit, fWrite);
this.aBusBlocks[iBlock].removeBreakpoint(addr & this.nBlockLimit, fWrite);
}
};
@ -1188,7 +1122,7 @@ BusPDP11.prototype.saveMemory = function(fAll)
var a = [];
for (var iBlock = 0; iBlock < this.nBlockTotal; iBlock++) {
var block = this.aMemBlocks[iBlock];
var block = this.aBusBlocks[iBlock];
/*
* We have to check both fDirty and fDirtyEver, because we may have called cleanMemory() on some of
* the memory blocks (eg, video memory), and while cleanMemory() will clear a dirty block's fDirty flag,
@ -1229,7 +1163,7 @@ BusPDP11.prototype.restoreMemory = function(a)
if (adw && adw.length < this.nBlockLen) {
adw = State.decompress(adw, this.nBlockLen);
}
var block = this.aMemBlocks[iBlock];
var block = this.aBusBlocks[iBlock];
if (!block || !block.restore(adw)) {
/*
* Either the block to restore hasn't been allocated, indicating a change in the machine
@ -1244,24 +1178,22 @@ BusPDP11.prototype.restoreMemory = function(a)
};
/**
* getMemorySize(type)
*
* NOTE: The original pdp11.js defined MAX_MEMORY as IOBASE_UNIBUS - 16384, where IOBASE_UNIBUS
* is 4Mb less 256Kb, and then it subtracted another 16Kb so that BSD 2.9 could boot.
* getMemoryLimit(type)
*
* @this {BusPDP11}
* @param {number} type is one of the MemoryPDP11.TYPE constants (only RAM is currently supported)
* @return {number} (size of initial allocation, in bytes)
* @param {number} type is one of the MemoryPDP11.TYPE constants
* @return {number} (the limiting address of the specified memory type, zero if none)
*/
BusPDP11.prototype.getMemorySize = function(type)
BusPDP11.prototype.getMemoryLimit = function(type)
{
var cb = 0;
switch(type) {
case MemoryPDP11.TYPE.RAM:
cb = this.cbRAM;
break;
var addr = 0;
for (var iBlock = 0; iBlock < this.aBusBlocks.length; iBlock++) {
var block = this.aBusBlocks[iBlock];
if (block.type == type) {
addr = block.addr + block.used;
}
}
return cb;
return addr;
};
/**

View file

@ -832,7 +832,6 @@ PDP11.opBPL = function(opCode)
PDP11.opBPT = function(opCode)
{
this.trap(PDP11.TRAP.BPT, 0, PDP11.REASON.OPCODE);
this.nStepCycles -= (4 + 1);
};
/**
@ -1106,7 +1105,7 @@ PDP11.opDIV = function(opCode)
PDP11.opEMT = function(opCode)
{
this.trap(PDP11.TRAP.EMT, 0, PDP11.REASON.OPCODE);
this.nStepCycles -= (22 + 3);
this.nStepCycles -= (22 + 3 - 5);
};
/**
@ -1206,7 +1205,7 @@ PDP11.opINCB = function(opCode)
PDP11.opIOT = function(opCode)
{
this.trap(PDP11.TRAP.IOT, 0, PDP11.REASON.OPCODE);
this.nStepCycles -= (22 + 3);
this.nStepCycles -= (22 + 3 - 5);
};
PDP11.JMP_CYCLES = [
@ -1789,7 +1788,6 @@ PDP11.opSXT = function(opCode)
PDP11.opTRAP = function(opCode)
{
this.trap(PDP11.TRAP.TRAP, 0, PDP11.REASON.OPCODE);
this.nStepCycles -= (4 + 1);
};
/**

View file

@ -216,7 +216,7 @@ CPUStatePDP11.prototype.initRegs = function()
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // mode 2 (not used)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // USER (8 UIPDR regs followed by 8 UDPDR regs)
];
this.unibusMap = [ // 32 unibus map registers
this.regsUniMap = [ // 32 UNIBUS map registers
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
this.regsControl = [ // various control registers (177740-177756) we don't really care about
@ -271,12 +271,15 @@ CPUStatePDP11.prototype.resetMMU = function()
this.mmuLastMode = 0;
this.mmuMask = 0x3ffff;
this.lastAddr = 0; // this is queried by the Panel when it's not using its own ADDRESS register
this.lastOp = 0; // stores the PC and any auto-incs or auto-decs from the last opcode; used to update MMR1 and MMR2
this.addrLast = 0; // this is queried by the Panel when it's not using its own ADDRESS register
this.opLast = 0; // stores the PC and any auto-incs or auto-decs from the last opcode; used to update MMR1 and MMR2
this.resetIRQs();
if (this.bus) this.setMemoryAccess();
if (this.bus) {
this.setMemoryAccess();
this.addrInvalid = this.bus.getMemoryLimit(MemoryPDP11.TYPE.RAM);
}
};
/**
@ -356,11 +359,11 @@ CPUStatePDP11.prototype.setMMR0 = function(newMMR0)
if (newMMR0 & PDP11.MMR0.ABORT) {
/*
* If updates to MMR0[1-7], MMR1, and MMR2 are being shut off (ie, MMR0.ABORT bits are transitioning
* from clear to set), then do one final sync with their real-time counterparts in lastOp.
* from clear to set), then do one final sync with their real-time counterparts in opLast.
*/
if (!(this.regMMR0 & PDP11.MMR0.ABORT)) {
this.regMMR1 = (this.lastOp >> 16) & 0xffff;
this.regMMR2 = this.lastOp & 0xffff;
this.regMMR1 = (this.opLast >> 16) & 0xffff;
this.regMMR2 = this.opLast & 0xffff;
}
}
/*
@ -393,10 +396,10 @@ CPUStatePDP11.prototype.getMMR1 = function()
{
/*
* If updates to MMR1 have not been shut off (ie, MMR0.ABORT bits are clear),
* then we are allowed to sync MMR1 with its real-time counterpart in lastOp.
* then we are allowed to sync MMR1 with its real-time counterpart in opLast.
*/
if (!(this.regMMR0 & PDP11.MMR0.ABORT)) {
this.regMMR1 = (this.lastOp >> 16) & 0xffff;
this.regMMR1 = (this.opLast >> 16) & 0xffff;
}
var result = this.regMMR1;
if (result & 0xff00) {
@ -415,10 +418,10 @@ CPUStatePDP11.prototype.getMMR2 = function()
{
/*
* If updates to MMR2 have not been shut off (ie, MMR0.ABORT bits are clear),
* then we are allowed to sync MMR2 with its real-time counterpart in lastOp.
* then we are allowed to sync MMR2 with its real-time counterpart in opLast.
*/
if (!(this.regMMR0 & PDP11.MMR0.ABORT)) {
this.regMMR2 = this.lastOp & 0xffff;
this.regMMR2 = this.opLast & 0xffff;
}
return this.regMMR2;
};
@ -662,7 +665,7 @@ CPUStatePDP11.prototype.setNF = function()
CPUStatePDP11.prototype.getOpcode = function()
{
var pc = this.regsGen[PDP11.REG.PC];
this.lastOp = pc;
this.opLast = pc;
/*
* If PC is unaligned, a BUS trap will be generated, and because it will generate an
* exception, the next line (the equivalent of advancePC(2)) will not be executed, ensuring that
@ -710,7 +713,7 @@ CPUStatePDP11.prototype.getPC = function()
*/
CPUStatePDP11.prototype.getLastAddr = function()
{
return this.lastAddr;
return this.addrLast;
};
/**
@ -721,7 +724,7 @@ CPUStatePDP11.prototype.getLastAddr = function()
*/
CPUStatePDP11.prototype.getLastPC = function()
{
return this.lastOp & 0xffff;
return this.opLast & 0xffff;
};
/**
@ -1365,14 +1368,14 @@ CPUStatePDP11.prototype.trap = function(vector, flag, reason)
* diagnostic (PC 056710), which tests what happens when a misaligned read triggers a BUS trap,
* and that trap then triggers an MMU trap during the first pushWord() below.
*
* One would think it would be fine to zero those bits by setting lastOp to vector alone,
* One would think it would be fine to zero those bits by setting opLast to vector alone,
* and then letting each of the pushWord() calls below shift their own 0xF6 auto-dec value into
* lastOp. When the first pushWord() triggers an MMU trap, we obviously won't get to the second
* opLast. When the first pushWord() triggers an MMU trap, we obviously won't get to the second
* pushWord(), yet the diagnostic expects TWO auto-decs to be recorded. I'm puzzled why the
* hardware apparently indicates TWO auto-decs, if SP wasn't actually decremented twice, but who
* am I to judge.
*/
this.lastOp = vector | 0xf6f60000;
this.opLast = vector | 0xf6f60000;
/*
* Read from kernel D space
@ -1390,6 +1393,13 @@ CPUStatePDP11.prototype.trap = function(vector, flag, reason)
this.pushWord(this.regsGen[7], fRed);
this.setPC(newPC);
/*
* TODO: Determine the appropriate number of cycles for traps; all I've done for now is move the
* cycle charge from opTrap() to here, and reduced the amount the other opcode handlers that call
* trap() charge by a corresponding amount (5).
*/
this.nStepCycles -= (4 + 1);
/*
* DEC's "TRAP TEST" (MAINDEC-11-D0NA-PB) triggers a RESERVED trap with an invalid opcode and the
* stack deliberately set too low, and expects the stack overflow trap to be "sprung" immediately
@ -1476,7 +1486,7 @@ CPUStatePDP11.prototype.getTrapStatus = function()
/**
* mapUnibus(addr)
*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
* If bits 18-21 of addr are all set (which callers check using addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so if the UNIBUS relocation map is enabled,
* we must pass the lower 18 bits of that address through the map.
*
@ -1516,7 +1526,7 @@ CPUStatePDP11.prototype.mapUnibus = function(addr)
/*
* The UNIBUS map relocation is enabled
*/
addr = (this.unibusMap[idx] + (addr & 0x1ffe)) & 0x3ffffe;
addr = (this.regsUniMap[idx] + (addr & 0x1ffe)) & 0x3ffffe;
this.assert(addr < BusPDP11.UNIBUS_22BIT || addr >= BusPDP11.IOPAGE_22BIT);
} else {
/*
@ -1532,7 +1542,7 @@ CPUStatePDP11.prototype.mapUnibus = function(addr)
};
/**
* mapVirtualToPhysical(virtualAddress, access)
* mapVirtualToPhysical(addrVirtual, access)
*
* mapVirtualToPhysical() does memory management. It converts a 17-bit I/D virtual address to a
* 22-bit physical address. A real PDP 11/70 memory management unit can be enabled separately
@ -1556,7 +1566,7 @@ CPUStatePDP11.prototype.mapUnibus = function(addr)
* A PDP-11/70 is different from other PDP-11s in that the highest 18 bit space (017000000 & above)
* maps directly to UNIBUS space - including low memory. This doesn't appear to be particularly
* useful as it restricts maximum system memory - although it does appear to allow software
* testing of the unibus map. This feature also appears to confuse some OSes which test consecutive
* testing of the UNIBUS map. This feature also appears to confuse some OSes which test consecutive
* memory locations to find maximum memory -- and on a full memory system find themselves accessing
* low memory again at high addresses.
*
@ -1582,60 +1592,51 @@ CPUStatePDP11.prototype.mapUnibus = function(addr)
* physical address to the next 8Kb page.
*
* @this {CPUStatePDP11}
* @param {number} virtualAddress
* @param {number} addrVirtual
* @param {number} access
* @return {number}
*/
CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, access)
CPUStatePDP11.prototype.mapVirtualToPhysical = function(addrVirtual, access)
{
var page, pdr, physicalAddress;
this.assert(!(virtualAddress & ~0x1ffff) && access);
var page, pdr, addr;
/*
* This can happen when the DSTMODE (MAINT) bit of MMR0 is set but *not* the ENABLED bit.
* This can happen when the DSTMODE (MAINT) bit of MMR0 is set but not the ENABLED bit.
*/
if (!(access & this.mmuEnable)) {
physicalAddress = virtualAddress & 0xffff;
if (physicalAddress >= BusPDP11.IOPAGE_16BIT) {
physicalAddress |= this.addrIOPage;
}
return physicalAddress;
addr = addrVirtual & 0xffff;
if (addr >= BusPDP11.IOPAGE_16BIT) addr |= this.addrIOPage;
return addr;
}
page = virtualAddress >> 13;
page = addrVirtual >> 13;
if (!(this.regMMR3 & this.mapMMR3[this.mmuMode])) page &= 7;
pdr = this.mmuPDR[this.mmuMode][page];
physicalAddress = ((this.mmuPAR[this.mmuMode][page] << 6) + (virtualAddress & 0x1fff)) & this.mmuMask;
addr = ((this.mmuPAR[this.mmuMode][page] << 6) + (addrVirtual & 0x1fff)) & this.mmuMask;
if (this.nDisableTraps) return physicalAddress;
if (addr >= BusPDP11.UNIBUS_22BIT) addr = this.mapUnibus(addr);
if (this.nDisableTraps) return addr;
/*
* This next bit is the weirdness that Paul mentions in the function description above:
*
* As an aside it turns out that it is the memory management unit that does odd address and
* non-existent memory trapping: who knew? :-) I thought these would have been handled at access time.
*
* TEST #122 ("KT BEND") in the "EKBEE1" diagnostic (PC 076060) triggers an ODDADDR error using this
* instruction:
*
* 076356: 005037 140001 CLR @#140001
*
* and it expects that instruction to generate a BUS error rather than an MMU error, so we deal with that
* next. However, the test also claims to check for an NEXM (non-existent memory) error, using this
* instruction:
* TEST #122 ("KT BEND") in the "EKBEE1" diagnostic (PC 076060) triggers a NOMEMORY error
* using this instruction:
*
* 076170: 005037 140100 CLR @#140100
*
* but that error never materializes, so I've NOT included any NEXM checks yet. I assume that any such
* checks would be limited to whatever's recorded in the Memory Size registers (177760), because actually
* checking every physical address at this stage -- even in a real MMU -- seems prohibitively expensive.
* It also triggers an ODDADDR error using this instruction:
*
* TODO: Investigate why the test's NEXM error doesn't occur.
* 076356: 005037 140001 CLR @#140001
*
* These tests exercise the MMU checks that Paul mentions in the function description above.
*/
if ((physicalAddress & 0x1) && !(access & PDP11.ACCESS.BYTE)) {
if (addr >= this.addrInvalid && addr < this.addrIOPage) {
this.regErr |= PDP11.CPUERR.NOMEMORY;
this.trap(PDP11.TRAP.BUS, 0, addr);
}
else if ((addr & 0x1) && !(access & PDP11.ACCESS.BYTE)) {
this.regErr |= PDP11.CPUERR.ODDADDR;
this.trap(PDP11.TRAP.BUS, 0, physicalAddress);
this.trap(PDP11.TRAP.BUS, 0, addr);
}
var newMMR0 = 0;
@ -1679,12 +1680,12 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, access)
*/
if (pdr & PDP11.PDR.ED) {
if (pdr & PDP11.PDR.PLF) {
if ((virtualAddress & 0x1FC0) < ((pdr >> 2) & 0x1FC0)) {
if ((addrVirtual & 0x1FC0) < ((pdr >> 2) & 0x1FC0)) {
newMMR0 |= PDP11.MMR0.ABORT_PL;
}
}
} else {
if ((virtualAddress & 0x1FC0) > ((pdr >> 2) & 0x1FC0)) {
if ((addrVirtual & 0x1FC0) > ((pdr >> 2) & 0x1FC0)) {
newMMR0 |= PDP11.MMR0.ABORT_PL;
}
}
@ -1695,7 +1696,7 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, access)
*/
this.mmuPDR[this.mmuMode][page] = pdr;
if (physicalAddress != ((BusPDP11.IOPAGE_22BIT | PDP11.UNIBUS.MMR0) & this.mmuMask) || this.mmuMode) {
if (addr != ((BusPDP11.IOPAGE_22BIT | PDP11.UNIBUS.MMR0) & this.mmuMask) || this.mmuMode) {
this.mmuLastMode = this.mmuMode;
this.mmuLastPage = page;
}
@ -1721,8 +1722,8 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, access)
/*
* TODO: Review the code below, because the address range seems over-inclusive.
*/
if (physicalAddress < ((BusPDP11.IOPAGE_22BIT | PDP11.UNIBUS.SIPDR0) & this.mmuMask) ||
physicalAddress > ((BusPDP11.IOPAGE_22BIT | PDP11.UNIBUS.UDPAR7 | 0x1) & this.mmuMask)) {
if (addr < ((BusPDP11.IOPAGE_22BIT | PDP11.UNIBUS.SIPDR0) & this.mmuMask) ||
addr > ((BusPDP11.IOPAGE_22BIT | PDP11.UNIBUS.UDPAR7 | 0x1) & this.mmuMask)) {
this.regMMR0 |= PDP11.MMR0.TRAP_MMU;
if (this.regMMR0 & PDP11.MMR0.MMU_TRAPS) {
this.opFlags |= PDP11.OPFLAG.TRAP_MMU;
@ -1730,32 +1731,32 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, access)
}
}
}
return physicalAddress;
return addr;
};
/**
* readByteFromPhysical(physicalAddress)
* readByteFromPhysical(addr)
*
* @this {CPUStatePDP11}
* @param {number} physicalAddress
* @param {number} addr
* @return {number}
*/
CPUStatePDP11.prototype.readByteFromPhysical = function(physicalAddress)
CPUStatePDP11.prototype.readByteFromPhysical = function(addr)
{
return this.bus.getByte(physicalAddress);
return this.bus.getByte(addr);
};
/**
* writeByteToPhysical(physicalAddress, data)
* writeByteToPhysical(addr, data)
*
* @this {CPUStatePDP11}
* @param {number} physicalAddress
* @param {number} addr
* @param {number} data
*/
CPUStatePDP11.prototype.writeByteToPhysical = function(physicalAddress, data)
CPUStatePDP11.prototype.writeByteToPhysical = function(addr, data)
{
if (physicalAddress & 1) this.nStepCycles--;
this.bus.setByte(physicalAddress, data);
if (addr & 1) this.nStepCycles--;
this.bus.setByte(addr, data);
};
/**
@ -1780,11 +1781,11 @@ CPUStatePDP11.prototype.popWord = function()
*/
CPUStatePDP11.prototype.pushWord = function(data, fRed)
{
var virtualAddress = (this.regsGen[6] - 2) & 0xffff;
this.regsGen[6] = virtualAddress; // BSD needs SP updated before any fault :-(
this.lastOp = (this.lastOp & 0xffff) | ((this.lastOp & ~0xffff) << 8) | (0x00f6 << 16);
if (!fRed) this.checkStackLimit(PDP11.ACCESS.WRITE_WORD, -2, virtualAddress);
this.writeWord(virtualAddress, data);
var addrVirtual = (this.regsGen[6] - 2) & 0xffff;
this.regsGen[6] = addrVirtual; // BSD needs SP updated before any fault :-(
this.opLast = (this.opLast & 0xffff) | ((this.opLast & ~0xffff) << 8) | (0x00f6 << 16);
if (!fRed) this.checkStackLimit(PDP11.ACCESS.WRITE_WORD, -2, addrVirtual);
this.writeWord(addrVirtual, data);
};
/**
@ -1835,7 +1836,7 @@ CPUStatePDP11.prototype.pushWord = function(data, fRed)
*/
CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, access)
{
var virtualAddress, step;
var addrVirtual, step;
var addrDSpace = (access & PDP11.ACCESS.VIRT)? 0 : this.addrDSpace;
/*
@ -1868,10 +1869,10 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, access)
*/
case 2:
step = 2;
virtualAddress = this.regsGen[reg];
if (reg == 6) this.checkStackLimit(access, step, virtualAddress);
addrVirtual = this.regsGen[reg];
if (reg == 6) this.checkStackLimit(access, step, addrVirtual);
if (reg != 7) {
virtualAddress |= addrDSpace;
addrVirtual |= addrDSpace;
if (reg < 6 && (access & PDP11.ACCESS.BYTE)) step = 1;
}
this.nStepCycles -= (2 + 1);
@ -1882,10 +1883,10 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, access)
*/
case 3:
step = 2;
virtualAddress = this.regsGen[reg];
if (reg != 7) virtualAddress |= addrDSpace;
virtualAddress = this.readWord(virtualAddress);
virtualAddress |= addrDSpace;
addrVirtual = this.regsGen[reg];
if (reg != 7) addrVirtual |= addrDSpace;
addrVirtual = this.readWord(addrVirtual);
addrVirtual |= addrDSpace;
this.nStepCycles -= (5 + 2);
break;
@ -1895,9 +1896,9 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, access)
case 4:
step = -2;
if (reg < 6 && (access & PDP11.ACCESS.BYTE)) step = -1;
virtualAddress = (this.regsGen[reg] + step) & 0xffff;
if (reg == 6) this.checkStackLimit(access, step, virtualAddress);
if (reg != 7) virtualAddress |= addrDSpace;
addrVirtual = (this.regsGen[reg] + step) & 0xffff;
if (reg == 6) this.checkStackLimit(access, step, addrVirtual);
if (reg != 7) addrVirtual |= addrDSpace;
this.nStepCycles -= (3 + 1);
break;
@ -1906,9 +1907,9 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, access)
*/
case 5:
step = -2;
virtualAddress = (this.regsGen[reg] - 2) & 0xffff;
if (reg != 7) virtualAddress |= addrDSpace;
virtualAddress = this.readWord(virtualAddress) | addrDSpace;
addrVirtual = (this.regsGen[reg] - 2) & 0xffff;
if (reg != 7) addrVirtual |= addrDSpace;
addrVirtual = this.readWord(addrVirtual) | addrDSpace;
this.nStepCycles -= (6 + 2);
break;
@ -1916,27 +1917,27 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, access)
* Mode 6: d(R)
*/
case 6:
virtualAddress = this.readWord(this.advancePC(2));
virtualAddress = (virtualAddress + this.regsGen[reg]) & 0xffff;
if (reg == 6) this.checkStackLimit(access, 0, virtualAddress);
addrVirtual = this.readWord(this.advancePC(2));
addrVirtual = (addrVirtual + this.regsGen[reg]) & 0xffff;
if (reg == 6) this.checkStackLimit(access, 0, addrVirtual);
this.nStepCycles -= (4 + 2);
return virtualAddress | addrDSpace;
return addrVirtual | addrDSpace;
/*
* Mode 7: @d(R)
*/
case 7:
virtualAddress = this.readWord(this.advancePC(2));
virtualAddress = (virtualAddress + this.regsGen[reg]) & 0xffff;
virtualAddress = this.readWord(virtualAddress | this.addrDSpace);
addrVirtual = this.readWord(this.advancePC(2));
addrVirtual = (addrVirtual + this.regsGen[reg]) & 0xffff;
addrVirtual = this.readWord(addrVirtual | this.addrDSpace);
this.nStepCycles -= (7 + 3);
return virtualAddress | addrDSpace;
return addrVirtual | addrDSpace;
}
this.regsGen[reg] = (this.regsGen[reg] + step) & 0xffff;
this.lastOp = (this.lastOp & 0xffff) | ((this.lastOp & ~0xffff) << 8) | ((((step << 3) & 0xf8) | reg) << 16);
this.opLast = (this.opLast & 0xffff) | ((this.opLast & ~0xffff) << 8) | ((((step << 3) & 0xf8) | reg) << 16);
return virtualAddress;
return addrVirtual;
};
/**
@ -2011,20 +2012,16 @@ CPUStatePDP11.prototype.checkStackLimit1145 = function(access, step, addr)
};
/**
* getByteDirect(addr)
* getByteSafe(addr)
*
* This interface is expressly for the Debugger, to access virtual memory without faulting;
* note that if the MMU is not enabled, this is effectively the same as using the Bus interface.
* This interface is expressly for the Debugger, to access virtual memory without faulting.
*
* @this {CPUStatePDP11}
* @param {number} addr
* @return {number}
*/
CPUStatePDP11.prototype.getByteDirect = function(addr)
CPUStatePDP11.prototype.getByteSafe = function(addr)
{
if (!this.mmuEnable) {
return this.bus.getByteDirect(addr);
}
this.nDisableTraps++;
var b = this.readByteFromPhysical(this.mapVirtualToPhysical(addr, PDP11.ACCESS.READ_BYTE));
this.nDisableTraps--;
@ -2032,20 +2029,16 @@ CPUStatePDP11.prototype.getByteDirect = function(addr)
};
/**
* getWordDirect(addr)
* getWordSafe(addr)
*
* This interface is expressly for the Debugger, to access virtual memory without faulting;
* note that if the MMU is not enabled, this is effectively the same as using the Bus interface.
* This interface is expressly for the Debugger, to access virtual memory without faulting.
*
* @this {CPUStatePDP11}
* @param {number} addr
* @return {number}
*/
CPUStatePDP11.prototype.getWordDirect = function(addr)
CPUStatePDP11.prototype.getWordSafe = function(addr)
{
if (!this.mmuEnable) {
return this.bus.getWordDirect(addr);
}
this.nDisableTraps++;
var w = this.readWordFromPhysical(this.mapVirtualToPhysical(addr, PDP11.ACCESS.READ_WORD));
this.nDisableTraps--;
@ -2053,42 +2046,32 @@ CPUStatePDP11.prototype.getWordDirect = function(addr)
};
/**
* setByteDirect(addr, data)
* setByteSafe(addr, data)
*
* This interface is expressly for the Debugger, to access virtual memory without faulting;
* note that if the MMU is not enabled, this is effectively the same as using the Bus interface.
* This interface is expressly for the Debugger, to access virtual memory without faulting.
*
* @this {CPUStatePDP11}
* @param {number} addr
* @param {number} data
*/
CPUStatePDP11.prototype.setByteDirect = function(addr, data)
CPUStatePDP11.prototype.setByteSafe = function(addr, data)
{
if (!this.mmuEnable) {
this.bus.setByteDirect(addr, data);
return;
}
this.nDisableTraps++;
this.writeByteToPhysical(this.mapVirtualToPhysical(addr, PDP11.ACCESS.WRITE_BYTE), data);
this.nDisableTraps--;
};
/**
* setWordDirect(addr, data)
* setWordSafe(addr, data)
*
* This interface is expressly for the Debugger, to access virtual memory without faulting;
* note that if the MMU is not enabled, this is effectively the same as using the Bus interface.
* This interface is expressly for the Debugger, to access virtual memory without faulting.
*
* @this {CPUStatePDP11}
* @param {number} addr
* @param {number} data
*/
CPUStatePDP11.prototype.setWordDirect = function(addr, data)
CPUStatePDP11.prototype.setWordSafe = function(addr, data)
{
if (!this.mmuEnable) {
this.bus.setWordDirect(addr, data);
return;
}
this.nDisableTraps++;
this.writeWordToPhysical(this.mapVirtualToPhysical(addr, PDP11.ACCESS.WRITE_WORD), data);
this.nDisableTraps--;
@ -2107,7 +2090,9 @@ CPUStatePDP11.prototype.setWordDirect = function(addr, data)
*/
CPUStatePDP11.prototype.getPhysicalAddrByMode = function(mode, reg, access)
{
return this.getAddrByMode(mode, reg, access);
var addr = this.getAddrByMode(mode, reg, access);
if (addr >= BusPDP11.UNIBUS_22BIT) addr = this.mapUnibus(addr);
return addr;
};
/**
@ -2127,59 +2112,61 @@ CPUStatePDP11.prototype.getVirtualAddrByMode = function(mode, reg, access)
};
/**
* readWordFromPhysical(physicalAddress)
* readWordFromPhysical(addr)
*
* This is a handler set up by setMemoryAccess(). All calls should go through readWord().
*
* @this {CPUStatePDP11}
* @param {number} physicalAddress
* @param {number} addr
* @return {number}
*/
CPUStatePDP11.prototype.readWordFromPhysical = function(physicalAddress)
CPUStatePDP11.prototype.readWordFromPhysical = function(addr)
{
return this.bus.getWord(this.lastAddr = physicalAddress);
if (addr >= BusPDP11.UNIBUS_22BIT) addr = this.mapUnibus(addr);
return this.bus.getWord(this.addrLast = addr);
};
/**
* readWordFromVirtual(virtualAddress)
* readWordFromVirtual(addrVirtual)
*
* This is a handler set up by setMemoryAccess(). All calls should go through readWord().
*
* @this {CPUStatePDP11}
* @param {number} virtualAddress (input address is 17 bit (I&D))
* @param {number} addrVirtual (input address is 17 bit (I&D))
* @return {number}
*/
CPUStatePDP11.prototype.readWordFromVirtual = function(virtualAddress)
CPUStatePDP11.prototype.readWordFromVirtual = function(addrVirtual)
{
return this.bus.getWord(this.lastAddr = this.mapVirtualToPhysical(virtualAddress, PDP11.ACCESS.READ_WORD));
return this.bus.getWord(this.addrLast = this.mapVirtualToPhysical(addrVirtual, PDP11.ACCESS.READ_WORD));
};
/**
* writeWordToPhysical(physicalAddress, data)
* writeWordToPhysical(addr, data)
*
* This is a handler set up by setMemoryAccess(). All calls should go through writeWord().
*
* @this {CPUStatePDP11}
* @param {number} physicalAddress
* @param {number} addr
* @param {number} data
*/
CPUStatePDP11.prototype.writeWordToPhysical = function(physicalAddress, data)
CPUStatePDP11.prototype.writeWordToPhysical = function(addr, data)
{
this.bus.setWord(this.lastAddr = physicalAddress, data & 0xffff);
if (addr >= BusPDP11.UNIBUS_22BIT) addr = this.mapUnibus(addr);
this.bus.setWord(this.addrLast = addr, data & 0xffff);
};
/**
* writeWordToVirtual(virtualAddress, data)
* writeWordToVirtual(addrVirtual, data)
*
* This is a handler set up by setMemoryAccess(). All calls should go through writeWord().
*
* @this {CPUStatePDP11}
* @param {number} virtualAddress (input address is 17 bit (I&D))
* @param {number} addrVirtual (input address is 17 bit (I&D))
* @param {number} data
*/
CPUStatePDP11.prototype.writeWordToVirtual = function(virtualAddress, data)
CPUStatePDP11.prototype.writeWordToVirtual = function(addrVirtual, data)
{
this.bus.setWord(this.lastAddr = this.mapVirtualToPhysical(virtualAddress, PDP11.ACCESS.WRITE_WORD), data);
this.bus.setWord(this.addrLast = this.mapVirtualToPhysical(addrVirtual, PDP11.ACCESS.WRITE_WORD), data);
};
/**
@ -2223,7 +2210,7 @@ CPUStatePDP11.prototype.readWordFromPrevSpace = function(opCode, access)
*/
CPUStatePDP11.prototype.writeWordToPrevSpace = function(opCode, access, data)
{
this.lastOp = (this.lastOp & 0xffff) | (0x0016 << 16);
this.opLast = (this.opLast & 0xffff) | (0x0016 << 16);
var reg = this.dstReg = opCode & PDP11.OPREG.MASK;
var mode = this.dstMode = (opCode & PDP11.OPMODE.MASK) >> PDP11.OPMODE.SHIFT;
if (!mode) {

View file

@ -42,6 +42,7 @@ if (DEBUGGER) {
var Keys = require("../../shared/lib/keys");
var State = require("../../shared/lib/state");
var PDP11 = require("./defines");
var BusPDP11 = require("./bus");
var CPUPDP11 = require("./cpu");
var MemoryPDP11 = require("./memory");
var MessagesPDP11 = require("./messages");
@ -644,6 +645,18 @@ if (DEBUGGER) {
return addr;
};
/**
* mapUnibus(addr)
*
* @this {DebuggerPDP11}
* @param {number} addr
* @return {number}
*/
DebuggerPDP11.prototype.mapUnibus = function(addr)
{
return (addr >= BusPDP11.UNIBUS_22BIT)? this.cpu.mapUnibus(addr) : addr;
};
/**
* getByte(dbgAddr, inc)
*
@ -659,7 +672,7 @@ if (DEBUGGER) {
var b = 0xff;
var addr = this.getAddr(dbgAddr, false, 1);
if (addr !== PDP11.ADDR_INVALID) {
b = dbgAddr.fPhysical? this.bus.getByteDirect(addr) : this.cpu.getByteDirect(addr);
b = (dbgAddr.fPhysical || addr > 0xffff)? this.bus.getByteDirect(this.mapUnibus(addr)) : this.cpu.getByteSafe(addr);
if (inc) this.incAddr(dbgAddr, inc);
}
return b;
@ -678,7 +691,7 @@ if (DEBUGGER) {
var w = 0xffff;
var addr = this.getAddr(dbgAddr, false, 2);
if (addr !== PDP11.ADDR_INVALID) {
w = dbgAddr.fPhysical? this.bus.getWordDirect(addr) : this.cpu.getWordDirect(addr);
w = (dbgAddr.fPhysical || addr > 0xffff)? this.bus.getWordDirect(this.mapUnibus(addr)) : this.cpu.getWordSafe(addr);
if (inc) this.incAddr(dbgAddr, inc);
}
return w;
@ -696,10 +709,10 @@ if (DEBUGGER) {
{
var addr = this.getAddr(dbgAddr, true, 1);
if (addr !== PDP11.ADDR_INVALID) {
if (dbgAddr.fPhysical) {
this.bus.setByteDirect(addr, b);
if (dbgAddr.fPhysical || addr > 0xffff) {
this.bus.setByteDirect(this.mapUnibus(addr), b);
} else {
this.cpu.setByteDirect(addr, b);
this.cpu.setByteSafe(addr, b);
}
if (inc) this.incAddr(dbgAddr, inc);
this.cmp.updateDisplays(-1);
@ -718,10 +731,10 @@ if (DEBUGGER) {
{
var addr = this.getAddr(dbgAddr, true, 2);
if (addr !== PDP11.ADDR_INVALID) {
if (dbgAddr.fPhysical) {
this.bus.setWordDirect(addr, w);
if (dbgAddr.fPhysical || addr > 0xffff) {
this.bus.setWordDirect(this.mapUnibus(addr), w);
} else {
this.cpu.setWordDirect(addr, w);
this.cpu.setWordSafe(addr, w);
}
if (inc) this.incAddr(dbgAddr, inc);
this.cmp.updateDisplays(-1);
@ -960,7 +973,7 @@ if (DEBUGGER) {
*/
DebuggerPDP11.prototype.dumpBus = function(asArgs)
{
this.dumpBlocks(this.bus.aMemBlocks, asArgs[0]);
this.dumpBlocks(this.bus.aBusBlocks, asArgs[0]);
};
/**
@ -1706,7 +1719,7 @@ if (DEBUGGER) {
* for that here by skipping over the HALT if/when the machine starts up again.
*/
if (!nState) {
opCode = this.cpu.getWordDirect(addr);
opCode = this.cpu.getWordSafe(addr);
if (opCode == PDP11.OPCODE.HALT) {
addr = this.cpu.advancePC(2);
}
@ -1733,7 +1746,7 @@ if (DEBUGGER) {
if (nState >= 0 && this.aInstructionHistory.length) {
this.cInstructions++;
if (opCode < 0) {
opCode = this.cpu.getWordDirect(addr);
opCode = this.cpu.getWordSafe(addr);
}
if ((opCode & 0xffff) != PDP11.OPCODE.INVALID) {
var dbgAddr = this.aInstructionHistory[this.iInstructionHistory];
@ -3118,7 +3131,7 @@ if (DEBUGGER) {
data = shift = 0;
}
i -= n; nBytes -= n;
while (n--) {
while (size == 1 && n--) {
var c = v & 0xff;
sChars += (c >= 32 && c < 128? String.fromCharCode(c) : '.');
v >>= 8;
@ -3128,7 +3141,7 @@ if (DEBUGGER) {
if (fJSON) {
sDump += sData + ",";
} else {
sDump += sAddr + " " + sData + ((i == 0)? (' ' + sChars) : "");
sDump += sAddr + ": " + sData + ((i == 0)? (' ' + sChars) : "");
}
}
@ -3859,12 +3872,22 @@ if (DEBUGGER) {
var dbg = this;
var fRegs = (sCmd != "t");
var nCount = this.parseValue(sCount, null, true) || 1;
var nCycles = (nCount == 1? 0 : 1);
/*
* We used to set nCycles to 1 when a count > 1 was specified, because nCycles set
* to 0 used to mean "execute the next instruction without checking for interrupts".
* Well, this machine's stepCPU() doesn't do that; it ALWAYS checks for interrupts,
* so we should leave nCycles set to 0, so that if an interrupt is dispatched, we will
* get to see the first instruction of the interrupt handler.
*/
var nCycles = 0; // (nCount == 1? 0 : 1);
if (sCmd == "tc") {
nCycles = nCount;
nCount = 1;
}
this.sTraceCmdPrev = sCmd;
web.onCountRepeat(
nCount,
function onCountStep() {

View file

@ -301,7 +301,7 @@ DevicePDP11.prototype.writeMMR3 = function(data, addr)
DevicePDP11.prototype.readUNIMAP = function(addr)
{
var word = (addr >> 1) & 0x3f, reg = word >> 1;
var data = this.cpu.unibusMap[reg];
var data = this.cpu.regsUniMap[reg];
return (word & 1)? (data >> 16) : (data & 0xffff);
};
@ -318,9 +318,9 @@ DevicePDP11.prototype.writeUNIMAP = function(data, addr)
{
var word = (addr >> 1) & 0x3f, reg = word >> 1;
if (word & 1) {
this.cpu.unibusMap[reg] = (this.cpu.unibusMap[reg] & 0xffff) | ((data & 0x003f) << 16);
this.cpu.regsUniMap[reg] = (this.cpu.regsUniMap[reg] & 0xffff) | ((data & 0x003f) << 16);
} else {
this.cpu.unibusMap[reg] = (this.cpu.unibusMap[reg] & ~0xffff) | (data & 0xfffe);
this.cpu.regsUniMap[reg] = (this.cpu.regsUniMap[reg] & ~0xffff) | (data & 0xfffe);
}
};
@ -454,10 +454,7 @@ DevicePDP11.prototype.readKIPDR = function(addr)
DevicePDP11.prototype.writeKIPDR = function(data, addr)
{
var reg = (addr >> 1) & 7;
this.cpu.mmuPDR[0][reg] = data &= 0xff0f;
if (this.messageEnabled(MessagesPDP11.MMU)) {
this.printMessage("writeKIPDR[" + reg + "]: " + str.toOct(data), true);
}
this.cpu.mmuPDR[0][reg] = data & 0xff0f;
};
/**
@ -892,13 +889,7 @@ DevicePDP11.prototype.writeCTRL = function(data, addr)
*/
DevicePDP11.prototype.readSIZE = function(addr)
{
/*
* TODO: getMemorySize() returns an aggregate total, so if there are multiple discontiguous
* chunks of RAM, this could return the wrong result; another interface, getHighestAddress(),
* might be required. Then again, perhaps multiple discontiguous chunks of RAM are not permitted
* in PDP-11 machines.
*/
return addr == PDP11.UNIBUS.LSIZE? ((this.bus.getMemorySize(MemoryPDP11.TYPE.RAM) >> 6) - 1) : 0;
return addr == PDP11.UNIBUS.LSIZE? ((this.bus.getMemoryLimit(MemoryPDP11.TYPE.RAM) >> 6) - 1) : 0;
};
/**

View file

@ -657,6 +657,9 @@ DiskPDP11.prototype.initSector = function(sector, iCylinder, iHead, iSector, cbS
/**
* info()
*
* TODO: Decide whether deprecate this in favor of accessing the nCylinders, nHeads, nSectors, and cbSector
* properties of the Disk object directly.
*
* @this {DiskPDP11}
* @return {Array} containing: [nCylinders, nHeads, nSectorsPerTrack, nBytesPerSector]
*/

View file

@ -760,12 +760,15 @@ PanelPDP11.prototype.processDeposit = function(value, index)
if (this.fDeposit) this.advanceAddr();
var w = this.updateData(this.regSwitches);
if (this.nAddrSel == PanelPDP11.ADDRSEL.CONS_PHY) {
/*
* TODO: Determine if this needs to take the UNIBUS map into consideration.
*/
this.bus.setWordDirect(this.regAddr, w);
} else {
/*
* TODO: This code is obviously incomplete, since it doesn't take into account the precise ADDRSEL mode.
*/
this.cpu.setWordDirect(this.regAddr, w);
this.cpu.setWordSafe(this.regAddr, w);
}
}
};
@ -783,12 +786,15 @@ PanelPDP11.prototype.processExamine = function(value, index)
var w;
if (this.fExamine) this.advanceAddr();
if (this.nAddrSel == PanelPDP11.ADDRSEL.CONS_PHY) {
/*
* TODO: Determine if this needs to take the UNIBUS map into consideration.
*/
w = this.bus.getWordDirect(this.regAddr);
} else {
/*
* TODO: This code is obviously incomplete, since it doesn't take into account the precise ADDRSEL mode.
*/
w = this.cpu.getWordDirect(this.regAddr);
w = this.cpu.getWordSafe(this.regAddr);
}
this.updateData(w);
}

View file

@ -75,8 +75,8 @@ function PC11(parms)
this.cAutoMount = 0;
this.nBaudReceive = parms['baudReceive'] || PDP11.PC11.PRS.BAUD;
this.prs = 0; // PRS register
this.prb = 0; // PRB register
this.regPRS = 0; // PRS register
this.regPRB = 0; // PRB register
this.iTapeData = 0; // buffer index
this.aTapeData = []; // buffer for the PRB register
this.sTapeSource = PC11.SOURCE.NONE;
@ -339,8 +339,8 @@ PC11.prototype.powerDown = function(fSave, fShutdown)
*/
PC11.prototype.reset = function()
{
this.prs &= ~PDP11.PC11.PRS.CLEAR;
this.prb = 0;
this.regPRS &= ~PDP11.PC11.PRS.CLEAR;
this.regPRB = 0;
};
/**
@ -806,8 +806,8 @@ PC11.prototype.getBaudTimeout = function(nBaud)
*/
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.regPRS & (PDP11.PC11.PRS.RE | PDP11.PC11.PRS.ERROR)) == PDP11.PC11.PRS.RE) {
if (!(this.regPRS & PDP11.PC11.PRS.DONE)) {
if (this.iTapeData < this.aTapeData.length) {
/*
* Here, as elsewhere (eg, the DL11 component), even if I trusted all incoming data
@ -815,11 +815,11 @@ PC11.prototype.advanceReader = function()
* (eg, -128 to 127, instead of 0 to 255). Both risks are good reasons to always mask
* the data assigned to PRB with 0xff.
*/
this.prb = this.aTapeData[this.iTapeData++] & 0xff;
this.regPRB = this.aTapeData[this.iTapeData++] & 0xff;
this.displayProgress(this.iTapeData / this.aTapeData.length * 100);
this.prs |= PDP11.PC11.PRS.DONE;
this.prs &= ~PDP11.PC11.PRS.BUSY;
if (this.prs & PDP11.PC11.PRS.RIE) {
this.regPRS |= PDP11.PC11.PRS.DONE;
this.regPRS &= ~PDP11.PC11.PRS.BUSY;
if (this.regPRS & PDP11.PC11.PRS.RIE) {
this.cpu.setIRQ(this.irqReader);
}
}
@ -841,7 +841,7 @@ PC11.prototype.advanceReader = function()
*/
PC11.prototype.readPRS = function(addr)
{
return this.prs & PDP11.PC11.PRS.RMASK; // RMASK honors the "write-only" nature of the RE bit by returning zero on reads
return this.regPRS & PDP11.PC11.PRS.RMASK; // RMASK honors the "write-only" nature of the RE bit by returning zero on reads
};
/**
@ -861,15 +861,15 @@ PC11.prototype.writePRS = function(data, addr)
* sets Busy, and clears the Reader Buffer (PRB). Operation of this bit is disabled if Error = 1;
* attempting to set it when Error = 1 will cause an immediate interrupt if Interrupt Enable = 1.
*/
if (this.prs & PDP11.PC11.PRS.ERROR) {
if (this.regPRS & PDP11.PC11.PRS.ERROR) {
data &= ~PDP11.PC11.PRS.RE;
if (this.prs & PDP11.PC11.PRS.RIE) {
if (this.regPRS & PDP11.PC11.PRS.RIE) {
this.cpu.setIRQ(this.irqReader);
}
} else {
this.prs &= ~PDP11.PC11.PRS.DONE;
this.prs |= PDP11.PC11.PRS.BUSY;
this.prb = 0;
this.regPRS &= ~PDP11.PC11.PRS.DONE;
this.regPRS |= PDP11.PC11.PRS.BUSY;
this.regPRB = 0;
/*
* The PC11, by virtue of its "high speed", is supposed to deliver characters at 300 CPS, so
* that's the rate we'll choose as well (ie, 1000ms / 300). As an aside, the original "low speed"
@ -878,7 +878,7 @@ PC11.prototype.writePRS = function(data, addr)
this.cpu.setTimer(this.timerReader, this.getBaudTimeout(this.nBaudReceive));
}
}
this.prs = (this.prs & ~PDP11.PC11.PRS.WMASK) | (data & PDP11.PC11.PRS.WMASK);
this.regPRS = (this.regPRS & ~PDP11.PC11.PRS.WMASK) | (data & PDP11.PC11.PRS.WMASK);
};
/**
@ -894,9 +894,9 @@ 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;
this.regPRS &= ~PDP11.PC11.PRS.DONE;
this.regPRS |= PDP11.PC11.PRS.BUSY;
return this.regPRB;
};
/**

View file

@ -57,6 +57,11 @@ if (NODE) {
* NOTE: We make a note of the specified size, but no memory is initially allocated for the RAM until the
* Computer component calls powerUp().
*
* TODO: I seem to recall a PDP-11 diagnostic that failed if total RAM wasn't a multiple of 16Kb; our Bus
* component defaults to a block size that matches BusPDP11.IOPAGE_LENGTH (ie, 8Kb), and we even allow partial
* block allocations, so internally, we don't have that requirement, but for better compatibility, perhaps we
* should display a non-fatal warning if addr or size don't fall on 16Kb boundaries.
*
* @constructor
* @extends Component
* @param {Object} parmsRAM
@ -344,7 +349,7 @@ RAMPDP11.prototype.loadImage = function(aBytes, addrLoad, addrExec, addrInit, fR
} else {
this.printMessage("loading " + str.toHexWord(cbData) + " bytes at " + str.toHexWord(addr) + "-" + str.toHexWord(addr + cbData - 1), MessagesPDP11.PAPER);
while (cbData--) {
this.cpu.setByteDirect(addr++, aBytes[offData++] & 0xff);
this.bus.setByteDirect(addr++, aBytes[offData++] & 0xff);
}
}
fLoaded = true;
@ -354,7 +359,7 @@ RAMPDP11.prototype.loadImage = function(aBytes, addrLoad, addrExec, addrInit, fR
if (addrLoad == null) addrLoad = addrInit;
if (addrLoad != null) {
for (var i = 0; i < aBytes.length; i++) {
this.cpu.setByteDirect(addrLoad + i, aBytes[i]);
this.bus.setByteDirect(addrLoad + i, aBytes[i]);
}
fLoaded = true;
}

View file

@ -46,6 +46,13 @@ if (NODE) {
*
* autoMount: one or more JSON-encoded objects, each containing 'name' and 'path' properties
*
* The RK11 Disk Controller controls up to eight RK05 disk drives, which in turn read/write RK03-KA
* disk cartridges. See [RK11 Disk Controller Configuration Files](/devices/pdp11/rk11/).
*
* RK03 (or more precisely, RK03-KA) disks are single-platter cartridges with 203 tracks per side,
* 12 sectors per track, and a sector size of 256 words (512 bytes), for a total capacity of 2.38Mb
* (2,494,464 bytes). See [RK03-KA Disk Images](/disks/dec/rk03/).
*
* @constructor
* @extends Component
* @param {Object} parms
@ -274,7 +281,6 @@ RK11.prototype.initBus = function(cmp, bus, cpu, dbg)
/*
* Add only drives from the machine-wide autoMount configuration that match drives managed by this component.
*
*/
if (configMount) {
for (var sDrive in configMount) {
@ -585,10 +591,10 @@ RK11.prototype.finishLoadDrive = function onLoadDrive(drive, disk, sDiskName, sD
if (disk) {
/*
* We shouldn't mount the disk unless we're sure the drive is able to handle it.
* TODO: While this is a perfectly reasonable thing to do, one wonders if the Disk object shouldn't
* have done this itself, since we passed our Drive object to it (it already knows the drive's limits).
*/
aDiskInfo = disk.info();
if (disk && aDiskInfo[0] > drive.nCylinders || aDiskInfo[1] > drive.nHeads /* || aDiskInfo[2] > drive.nSectors */) {
if (disk.nCylinders > drive.nCylinders || disk.nHeads > drive.nHeads /* || disk.nSectors > drive.nSectors */) {
this.notice("Disk \"" + sDiskName + "\" too large for drive " + this.getDriveName(drive.iDrive));
disk = null;
}
@ -611,7 +617,6 @@ RK11.prototype.finishLoadDrive = function onLoadDrive(drive, disk, sDiskName, sD
* of a local disk image, it will map all such disks to "Local Disk", and any attempt to "Mount" such
* a disk, will essentially result in a "Disk not found" error.
*/
// this.addDiskHistory(sDiskName, sDiskPath, disk);
/*
@ -628,7 +633,6 @@ RK11.prototype.finishLoadDrive = function onLoadDrive(drive, disk, sDiskName, sD
*
* Successful unmounts are a different story, however; those *do* trigger a change. See unloadDrive().
*/
// this.regInput |= FDC.REG_INPUT.DISK_CHANGE;
/*
@ -813,13 +817,13 @@ RK11.prototype.initController = function(data)
{
var i = 0;
if (!data) data = [];
this.dsr = data[i++] || (PDP11.RK11.RKDS.RK05 | PDP11.RK11.RKDS.SOK | PDP11.RK11.RKDS.DRDY | PDP11.RK11.RKDS.RRDY);
this.err = data[i++] || 0;
this.csr = data[i++] || (PDP11.RK11.RKCS.CRDY);
this.wcr = data[i++] || 0;
this.bar = data[i++] || 0;
this.dar = data[i++] || 0;
this.dbr = data[i] || 0; // TODO: Determine if there's anything we should be doing to the RKDB register
this.regRKDS = data[i++] || (PDP11.RK11.RKDS.RK05 | PDP11.RK11.RKDS.SOK | PDP11.RK11.RKDS.DRDY | PDP11.RK11.RKDS.RRDY);
this.regRKER = data[i++] || 0;
this.regRKCS = data[i++] || (PDP11.RK11.RKCS.CRDY);
this.regRKWC = data[i++] || 0;
this.regRKBA = data[i++] || 0;
this.regRKDA = data[i++] || 0;
this.regRKDB = data[i] || 0; // TODO: Determine if there's anything we should be doing to the RKDB register
var fSuccess = true;
for (var iDrive = 0; iDrive < this.aDrives.length; iDrive++) {
@ -853,9 +857,13 @@ RK11.prototype.initDrive = function(drive, iDrive, data)
var fSuccess = true;
drive.iDrive = iDrive;
drive.name = this.idComponent;
drive.fBusy = drive.fLocal = false;
drive.name = this.idComponent;
/*
* NOTE: We initialize the following drive properties to their MAXIMUMs; disks may have
* these or SMALLER values (subject to the limits of what the controller supports, of course).
*/
drive.nCylinders = 203;
drive.nHeads = 2;
drive.nSectors = 12;
@ -877,9 +885,7 @@ RK11.prototype.initDrive = function(drive, iDrive, data)
drive.ibSector = 0;
drive.sector = null;
if (!drive.disk) {
drive.sDiskPath = ""; // ensure this is initialized to a default that displayDisk() can deal with
}
if (!drive.disk) drive.sDiskPath = ""; // ensure this is initialized to a default that displayDisk() can deal with
drive.status = PDP11.RK11.RKDS.RK05 | PDP11.RK11.RKDS.SOK | PDP11.RK11.RKDS.DRDY | PDP11.RK11.RKDS.RRDY;
@ -895,19 +901,19 @@ RK11.prototype.processCommand = function()
{
var fnReadWrite;
var fInterrupt = true;
var iDrive = (this.dar & PDP11.RK11.RKDA.DS) >> PDP11.RK11.RKDA.SHIFT.DS;
var iDrive = (this.regRKDA & PDP11.RK11.RKDA.DS) >> PDP11.RK11.RKDA.SHIFT.DS;
var drive = this.aDrives[iDrive];
var iCylinder, iHead, iSector, nWords, addr;
this.csr &= ~PDP11.RK11.RKCS.CRDY;
this.regRKCS &= ~PDP11.RK11.RKCS.CRDY;
switch(this.csr & PDP11.RK11.RKCS.FUNC) {
switch(this.regRKCS & PDP11.RK11.RKCS.FUNC) {
case PDP11.RK11.FUNC.CRESET:
this.dsr = drive.status;
this.err = 0;
this.csr = PDP11.RK11.RKCS.CRDY;
this.dar = 0;
this.regRKDS = drive.status;
this.regRKER = 0;
this.regRKCS = PDP11.RK11.RKCS.CRDY;
this.regRKDA = 0;
break;
case PDP11.RK11.FUNC.READ:
@ -916,24 +922,24 @@ RK11.prototype.processCommand = function()
case PDP11.RK11.FUNC.WRITE:
if (!fnReadWrite) fnReadWrite = this.writeData;
iCylinder = (this.dar & PDP11.RK11.RKDA.CA) >> PDP11.RK11.RKDA.SHIFT.CA;
iHead = (this.dar & PDP11.RK11.RKDA.HS) >> PDP11.RK11.RKDA.SHIFT.HS;
iSector = this.dar & PDP11.RK11.RKDA.SA;
iCylinder = (this.regRKDA & PDP11.RK11.RKDA.CA) >> PDP11.RK11.RKDA.SHIFT.CA;
iHead = (this.regRKDA & PDP11.RK11.RKDA.HS) >> PDP11.RK11.RKDA.SHIFT.HS;
iSector = this.regRKDA & PDP11.RK11.RKDA.SA;
if (iCylinder >= drive.nCylinders) {
this.err |= PDP11.RK11.RKER.DRE | PDP11.RK11.RKER.NXC;
this.csr |= PDP11.RK11.RKCS.HE | PDP11.RK11.RKCS.ERR;
this.regRKER |= PDP11.RK11.RKER.DRE | PDP11.RK11.RKER.NXC;
this.regRKCS |= PDP11.RK11.RKCS.HE | PDP11.RK11.RKCS.ERR;
break;
}
if (iSector >= drive.nSectors) {
this.err |= PDP11.RK11.RKER.DRE | PDP11.RK11.RKER.NXS;
this.csr |= PDP11.RK11.RKCS.HE | PDP11.RK11.RKCS.ERR;
this.regRKER |= PDP11.RK11.RKER.DRE | PDP11.RK11.RKER.NXS;
this.regRKCS |= PDP11.RK11.RKCS.HE | PDP11.RK11.RKCS.ERR;
break;
}
addr = (((this.csr & PDP11.RK11.RKCS.MEX)) << (16 - PDP11.RK11.RKCS.SHIFT.MEX)) | this.bar;
nWords = (0x10000 - this.wcr) & 0xffff;
if (DEBUG && this.messageEnabled(MessagesPDP11.READ)) {
addr = (((this.regRKCS & PDP11.RK11.RKCS.MEX)) << (16 - PDP11.RK11.RKCS.SHIFT.MEX)) | this.regRKBA;
nWords = (0x10000 - this.regRKWC) & 0xffff;
if (DEBUG && (this.messageEnabled(MessagesPDP11.READ) || this.messageEnabled(MessagesPDP11.WRITE))) {
var pos = ((((iCylinder << 1) + iHead) * drive.nSectors) + iSector) * 256;
this.printMessage((fnReadWrite == this.readData? "readData" : "writeData") + "(pos=" + pos + ",addr=" + str.toOct(addr) + ",bytes=" + (nWords * 2) + ")", true, true);
console.log((fnReadWrite == this.readData? "readData" : "writeData") + "(pos=" + pos + ",addr=" + str.toOct(addr) + ",bytes=" + (nWords * 2) + ")");
}
fInterrupt = fnReadWrite.call(this, drive, iCylinder, iHead, iSector, nWords, addr, this.endReadWrite.bind(this));
break;
@ -943,14 +949,14 @@ RK11.prototype.processCommand = function()
}
/*
* TODO: Determine what's up with the "dar % 9"....
* TODO: Determine what's up with the "regRKDA % 9"....
*/
this.dsr = drive.status | (iDrive << PDP11.RK11.RKDS.SHIFT.ID) | ((this.dar % 9) & PDP11.RK11.RKDS.SC);
this.regRKDS = drive.status | (iDrive << PDP11.RK11.RKDS.SHIFT.ID) | ((this.regRKDA % 9) & PDP11.RK11.RKDS.SC);
if (fInterrupt) {
this.csr &= ~PDP11.RK11.RKCS.GO;
this.csr |= PDP11.RK11.RKCS.CRDY;
if (this.csr & PDP11.RK11.RKCS.IE) this.cpu.setIRQ(this.irq);
this.regRKCS &= ~PDP11.RK11.RKCS.GO;
this.regRKCS |= PDP11.RK11.RKCS.CRDY;
if (this.regRKCS & PDP11.RK11.RKCS.IE) this.cpu.setIRQ(this.irq);
}
};
@ -968,12 +974,12 @@ RK11.prototype.processCommand = function()
*/
RK11.prototype.endReadWrite = function(err, iCylinder, iHead, iSector, nWords, addr)
{
this.bar = addr & 0xffff;
this.csr = (this.csr & ~PDP11.RK11.RKCS.MEX) | ((addr >> (16 - PDP11.RK11.RKCS.SHIFT.MEX)) & PDP11.RK11.RKCS.MEX);
this.wcr = (0x10000 - nWords) & 0xffff;
this.regRKBA = addr & 0xffff;
this.regRKCS = (this.regRKCS & ~PDP11.RK11.RKCS.MEX) | ((addr >> (16 - PDP11.RK11.RKCS.SHIFT.MEX)) & PDP11.RK11.RKCS.MEX);
this.regRKWC = (0x10000 - nWords) & 0xffff;
if (err) {
this.err |= err | PDP11.RK11.RKER.DRE;
this.csr |= PDP11.RK11.RKCS.HE | PDP11.RK11.RKCS.ERR;
this.regRKER |= err | PDP11.RK11.RKER.DRE;
this.regRKCS |= PDP11.RK11.RKCS.HE | PDP11.RK11.RKCS.ERR;
}
return true;
};
@ -1005,7 +1011,7 @@ RK11.prototype.readData = function(drive, iCylinder, iHead, iSector, nWords, add
var sWords = "";
while (nWords--) {
if (!sector) {
sector = drive.disk.seek(iCylinder, iHead, iSector + 1);
sector = disk.seek(iCylinder, iHead, iSector + 1);
if (!sector) {
err = PDP11.RK11.RKER.SKE;
break;
@ -1013,14 +1019,18 @@ RK11.prototype.readData = function(drive, iCylinder, iHead, iSector, nWords, add
ibSector = 0;
}
var b0, b1, data;
if ((b0 = drive.disk.read(sector, ibSector++)) < 0 || (b1 = drive.disk.read(sector, ibSector++)) < 0) {
if ((b0 = disk.read(sector, ibSector++)) < 0 || (b1 = disk.read(sector, ibSector++)) < 0) {
err = PDP11.RK11.RKER.NXS;
break;
}
this.bus.setWordDirect(addr, data = b0 | (b1 << 8));
if (DEBUG && this.messageEnabled(MessagesPDP11.READ) && sWords.length < 56) {
if (DEBUG && this.messageEnabled(MessagesPDP11.READ)) {
if (!sWords) sWords = str.toOct(addr) + ": ";
sWords += str.toOct(data) + ' ';
if (sWords.length >= 56) this.printMessage(sWords + '\n', true);
if (sWords.length >= 64) {
console.log(sWords);
sWords = "";
}
}
if (this.bus.checkFault()) {
err = PDP11.RK11.RKER.NXM;
@ -1076,14 +1086,14 @@ RK11.prototype.writeData = function(drive, iCylinder, iHead, iSector, nWords, ad
}
addr += 2;
if (!sector) {
sector = drive.disk.seek(iCylinder, iHead, iSector + 1, true);
sector = disk.seek(iCylinder, iHead, iSector + 1, true);
if (!sector) {
err = PDP11.RK11.RKER.SKE;
break;
}
ibSector = 0;
}
if (!drive.disk.write(sector, ibSector++, data & 0xff) || !drive.disk.write(sector, ibSector++, data >> 8)) {
if (!disk.write(sector, ibSector++, data & 0xff) || !disk.write(sector, ibSector++, data >> 8)) {
err = PDP11.RK11.RKER.NXS;
break;
}
@ -1113,7 +1123,7 @@ RK11.prototype.writeData = function(drive, iCylinder, iHead, iSector, nWords, ad
*/
RK11.prototype.readRKDS = function(addr)
{
return this.dsr;
return this.regRKDS;
};
/**
@ -1139,7 +1149,7 @@ RK11.prototype.writeRKDS = function(data, addr)
*/
RK11.prototype.readRKER = function(addr)
{
return this.err;
return this.regRKER;
};
/**
@ -1165,7 +1175,7 @@ RK11.prototype.writeRKER = function(data, addr)
*/
RK11.prototype.readRKCS = function(addr)
{
return this.csr & PDP11.RK11.RKCS.RMASK;
return this.regRKCS & PDP11.RK11.RKCS.RMASK;
};
/**
@ -1177,9 +1187,9 @@ RK11.prototype.readRKCS = function(addr)
*/
RK11.prototype.writeRKCS = function(data, addr)
{
this.csr = (this.csr & ~PDP11.RK11.RKCS.WMASK) | (data & PDP11.RK11.RKCS.WMASK);
this.regRKCS = (this.regRKCS & ~PDP11.RK11.RKCS.WMASK) | (data & PDP11.RK11.RKCS.WMASK);
if (this.csr & PDP11.RK11.RKCS.GO) this.processCommand();
if (this.regRKCS & PDP11.RK11.RKCS.GO) this.processCommand();
};
/**
@ -1191,7 +1201,7 @@ RK11.prototype.writeRKCS = function(data, addr)
*/
RK11.prototype.readRKWC = function(addr)
{
return this.wcr;
return this.regRKWC;
};
/**
@ -1203,7 +1213,7 @@ RK11.prototype.readRKWC = function(addr)
*/
RK11.prototype.writeRKWC = function(data, addr)
{
this.wcr = data;
this.regRKWC = data;
};
/**
@ -1215,7 +1225,7 @@ RK11.prototype.writeRKWC = function(data, addr)
*/
RK11.prototype.readRKBA = function(addr)
{
return this.bar;
return this.regRKBA;
};
/**
@ -1227,7 +1237,7 @@ RK11.prototype.readRKBA = function(addr)
*/
RK11.prototype.writeRKBA = function(data, addr)
{
this.bar = data;
this.regRKBA = data;
};
/**
@ -1239,7 +1249,7 @@ RK11.prototype.writeRKBA = function(data, addr)
*/
RK11.prototype.readRKDA = function(addr)
{
return this.dar;
return this.regRKDA;
};
/**
@ -1251,7 +1261,7 @@ RK11.prototype.readRKDA = function(addr)
*/
RK11.prototype.writeRKDA = function(data, addr)
{
this.dar = data;
this.regRKDA = data;
};
/**
@ -1263,7 +1273,7 @@ RK11.prototype.writeRKDA = function(data, addr)
*/
RK11.prototype.readRKDB = function(addr)
{
return this.dbr;
return this.regRKDB;
};
/**
@ -1275,7 +1285,7 @@ RK11.prototype.readRKDB = function(addr)
*/
RK11.prototype.writeRKDB = function(data, addr)
{
this.dbr = data;
this.regRKDB = data;
};
/*

View file

@ -46,6 +46,15 @@ if (NODE) {
*
* autoMount: one or more JSON-encoded objects, each containing 'name' and 'path' properties
*
* The RL11 Disk Controller controls up to four RL01 or RL02 disk drives, which in turn read/write RL01K or
* RL02K disk cartridges. See [RL11 Disk Controller Configuration Files](/devices/pdp11/rl11/).
*
* RL01K disks are single-platter cartridges with 256 tracks per side, 40 sectors per track, and a sector size
* of 256 bytes, for a total capacity of 5Mb (5,242,880 bytes). See [RL01K Disk Images](/disks/dec/rl01k/).
*
* RL02K disks are single-platter cartridges with 512 tracks per side, 40 sectors per track, and a sector size
* of 256 bytes, for a total capacity of 10Mb (10,485,760 bytes). See [RL02K Disk Images](/disks/dec/rl02k/).
*
* @constructor
* @extends Component
* @param {Object} parms
@ -274,7 +283,6 @@ RL11.prototype.initBus = function(cmp, bus, cpu, dbg)
/*
* Add only drives from the machine-wide autoMount configuration that match drives managed by this component.
*
*/
if (configMount) {
for (var sDrive in configMount) {
@ -579,16 +587,14 @@ RL11.prototype.loadDrive = function(iDrive, sDiskName, sDiskPath, fAutoMount, fi
*/
RL11.prototype.finishLoadDrive = function onLoadDrive(drive, disk, sDiskName, sDiskPath, fAutoMount)
{
var aDiskInfo;
drive.fBusy = false;
if (disk) {
/*
* We shouldn't mount the disk unless we're sure the drive is able to handle it.
* TODO: While this is a perfectly reasonable thing to do, one wonders if the Disk object shouldn't
* have done this itself, since we passed our Drive object to it (it already knows the drive's limits).
*/
aDiskInfo = disk.info();
if (disk && aDiskInfo[0] > drive.nCylinders || aDiskInfo[1] > drive.nHeads /* || aDiskInfo[2] > drive.nSectors */) {
if (disk.nCylinders > drive.nCylinders || disk.nHeads > drive.nHeads /* || disk.nSectors > drive.nSectors */) {
this.notice("Disk \"" + sDiskName + "\" too large for drive " + this.getDriveName(drive.iDrive));
disk = null;
}
@ -611,7 +617,6 @@ RL11.prototype.finishLoadDrive = function onLoadDrive(drive, disk, sDiskName, sD
* of a local disk image, it will map all such disks to "Local Disk", and any attempt to "Mount" such
* a disk, will essentially result in a "Disk not found" error.
*/
// this.addDiskHistory(sDiskName, sDiskPath, disk);
/*
@ -628,7 +633,6 @@ RL11.prototype.finishLoadDrive = function onLoadDrive(drive, disk, sDiskName, sD
*
* Successful unmounts are a different story, however; those *do* trigger a change. See unloadDrive().
*/
// this.regInput |= FDC.REG_INPUT.DISK_CHANGE;
/*
@ -813,12 +817,12 @@ RL11.prototype.initController = function(data)
{
var i = 0;
if (!data) data = [];
this.csr = data[i++] || 0;
this.bar = data[i++] || 0;
this.dar = data[i++] || 0;
this.darInternal = data[i++] || 0;
this.mpr = data[i++] || 0;
this.ber = data[i] || 0;
this.regRLCS = data[i++] || 0;
this.regRLBA = data[i++] || 0;
this.regRLDA = data[i++] || 0;
this.tmpRLDA = data[i++] || 0;
this.regRLMP = data[i++] || 0;
this.regRLBE = data[i] || 0;
var fSuccess = true;
for (var iDrive = 0; iDrive < this.aDrives.length; iDrive++) {
@ -852,9 +856,13 @@ RL11.prototype.initDrive = function(drive, iDrive, data)
var fSuccess = true;
drive.iDrive = iDrive;
drive.name = this.idComponent;
drive.fBusy = drive.fLocal = false;
drive.name = this.idComponent;
/*
* NOTE: We initialize the following drive properties to their MAXIMUMs; disks may have
* these or SMALLER values (subject to the limits of what the controller supports, of course).
*/
drive.nCylinders = 512;
drive.nHeads = 2;
drive.nSectors = 40;
@ -876,14 +884,12 @@ RL11.prototype.initDrive = function(drive, iDrive, data)
drive.ibSector = 0;
drive.sector = null;
if (!drive.disk) {
drive.sDiskPath = ""; // ensure this is initialized to a default that displayDisk() can deal with
}
if (!drive.disk) drive.sDiskPath = ""; // ensure this is initialized to a default that displayDisk() can deal with
/*
* Default drive status bits returned via the controller's Get Status command via the RLMP register
*/
drive.status = PDP11.RL11.RLMP.GS_ST.LOCKON | PDP11.RL11.RLMP.GS_BH | PDP11.RL11.RLMP.GS_HO | (drive.nCylinders == 512? PDP11.RL11.RLMP.GS_DT : 0);
drive.status = PDP11.RL11.RLMP.GS_ST.LOCKON | PDP11.RL11.RLMP.GS_BH | PDP11.RL11.RLMP.GS_HO;
return fSuccess;
};
@ -897,8 +903,9 @@ RL11.prototype.processCommand = function()
{
var fnReadWrite;
var fInterrupt = true;
var iDrive = (this.csr & PDP11.RL11.RLCS.DS) >> PDP11.RL11.RLCS.SHIFT.DS;
var iDrive = (this.regRLCS & PDP11.RL11.RLCS.DS) >> PDP11.RL11.RLCS.SHIFT.DS;
var drive = this.aDrives[iDrive];
var disk = drive.disk;
var iCylinder, iHead, iSector, nWords, addr;
/*
@ -908,9 +915,9 @@ RL11.prototype.processCommand = function()
* 2) CRDY is cleared to process a command
* 3) DRDY is cleared to command is in process
*/
this.csr &= ~PDP11.RL11.RLCS.DRDY;
this.regRLCS &= ~PDP11.RL11.RLCS.DRDY;
switch(this.csr & PDP11.RL11.RLCS.FUNC) {
switch(this.regRLCS & PDP11.RL11.RLCS.FUNC) {
case PDP11.RL11.FUNC.NOP:
case PDP11.RL11.FUNC.WCHK:
@ -918,27 +925,32 @@ RL11.prototype.processCommand = function()
break;
case PDP11.RL11.FUNC.STATUS:
if (this.dar & PDP11.RL11.RLDA.GS_RST) {
this.csr &= 0x3F; // TODO: Review
if (this.regRLMP & PDP11.RL11.RLMP.GS_BH) {
this.regRLCS &= (PDP11.RL11.RLCS.DRDY | PDP11.RL11.RLCS.FUNC | PDP11.RL11.RLCS.BAE); // TODO: Review
}
this.mpr = drive.status | (this.darInternal & PDP11.RL11.RLDA.RW_HS); // bit 6: Head Select, bit 7: Drive Type (1=RL02)
/*
* The bit indicating whether or not the disk contains 256 or 512 cylinders is critical;
* for example, the first RSTS/E disk image we tried was an RL01K, which has only 256 cylinders,
* and the operating system would crash mysteriously if we didn't report the correct geometry.
*/
this.regRLMP = drive.status | (this.tmpRLDA & PDP11.RL11.RLDA.RW_HS) | (disk && disk.nCylinders == 512? PDP11.RL11.RLMP.GS_DT : 0);
break;
case PDP11.RL11.FUNC.SEEK:
if ((this.dar & PDP11.RL11.RLDA.GS_CMD) == PDP11.RL11.RLDA.SEEK_CMD) {
var darCA = (this.dar & PDP11.RL11.RLDA.RW_CA);
var darHS = (this.dar & PDP11.RL11.RLDA.SEEK_HS) << 2;
if (this.dar & PDP11.RL11.RLDA.SEEK_DIR) {
this.darInternal += darCA;
if ((this.regRLDA & PDP11.RL11.RLDA.GS_CMD) == PDP11.RL11.RLDA.SEEK_CMD) {
var darCA = (this.regRLDA & PDP11.RL11.RLDA.RW_CA);
var darHS = (this.regRLDA & PDP11.RL11.RLDA.SEEK_HS) << 2;
if (this.regRLDA & PDP11.RL11.RLDA.SEEK_DIR) {
this.tmpRLDA += darCA;
} else {
this.darInternal -= darCA;
this.tmpRLDA -= darCA;
}
this.dar = this.darInternal = (this.darInternal & PDP11.RL11.RLDA.RW_CA) | darHS;
this.regRLDA = this.tmpRLDA = (this.tmpRLDA & PDP11.RL11.RLDA.RW_CA) | darHS;
}
break;
case PDP11.RL11.FUNC.RHDR:
this.mpr = this.darInternal;
this.regRLMP = this.tmpRLDA;
break;
case PDP11.RL11.FUNC.RDATA:
@ -947,15 +959,19 @@ RL11.prototype.processCommand = function()
case PDP11.RL11.FUNC.WDATA:
if (!fnReadWrite) fnReadWrite = this.writeData;
iCylinder = this.dar >> PDP11.RL11.RLDA.SHIFT.RW_CA;
iHead = (this.dar & PDP11.RL11.RLDA.RW_HS)? 1 : 0;
iSector = this.dar & PDP11.RL11.RLDA.RW_SA;
if (iCylinder >= drive.nCylinders || iSector >= drive.nSectors) {
this.csr |= PDP11.RL11.ERRC.HNF | PDP11.RL11.RLCS.ERR;
iCylinder = this.regRLDA >> PDP11.RL11.RLDA.SHIFT.RW_CA;
iHead = (this.regRLDA & PDP11.RL11.RLDA.RW_HS)? 1 : 0;
iSector = this.regRLDA & PDP11.RL11.RLDA.RW_SA;
if (!disk || iCylinder >= disk.nCylinders || iSector >= disk.nSectors) {
this.regRLCS |= PDP11.RL11.ERRC.HNF | PDP11.RL11.RLCS.ERR;
break;
}
addr = (((this.ber & PDP11.RL11.RLBE.MASK)) << 16) | this.bar; // 22 bit mode
nWords = (0x10000 - this.mpr) & 0xffff;
addr = (((this.regRLBE & PDP11.RL11.RLBE.MASK)) << 16) | this.regRLBA; // 22 bit mode
nWords = (0x10000 - this.regRLMP) & 0xffff;
var pos = ((((iCylinder << 1) + iHead) * drive.nSectors) + iSector) * 128;
if (DEBUG && (this.messageEnabled(MessagesPDP11.READ) || this.messageEnabled(MessagesPDP11.WRITE))) {
console.log((fnReadWrite == this.readData? "readData" : "writeData") + "(pos=" + pos + ",addr=" + str.toOct(addr) + ",bytes=" + (nWords * 2) + ")");
}
fInterrupt = fnReadWrite.call(this, drive, iCylinder, iHead, iSector, nWords, addr, this.endReadWrite.bind(this));
break;
@ -964,13 +980,11 @@ RL11.prototype.processCommand = function()
}
if (fInterrupt) {
this.csr |= PDP11.RL11.RLCS.DRDY | PDP11.RL11.RLCS.CRDY;
if (this.csr & PDP11.RL11.RLCS.IE) this.cpu.setIRQ(this.irq);
this.regRLCS |= PDP11.RL11.RLCS.DRDY | PDP11.RL11.RLCS.CRDY;
if (this.regRLCS & PDP11.RL11.RLCS.IE) this.cpu.setIRQ(this.irq);
}
};
/**
* endReadWrite(err, iCylinder, iHead, iSector, nWords, addr)
*
@ -985,14 +999,14 @@ RL11.prototype.processCommand = function()
*/
RL11.prototype.endReadWrite = function(err, iCylinder, iHead, iSector, nWords, addr)
{
this.bar = addr & 0xffff;
this.csr = (this.csr & ~PDP11.RL11.RLCS.BAE) | ((addr >> (16 - PDP11.RL11.RLCS.SHIFT.BAE)) & PDP11.RL11.RLCS.BAE);
this.ber = (addr >> 16) & PDP11.RL11.RLBE.MASK; // 22 bit mode
this.dar = (iCylinder << PDP11.RL11.RLDA.SHIFT.RW_CA) | (iHead? PDP11.RL11.RLDA.RW_HS : 0) | (iSector & PDP11.RL11.RLDA.RW_SA);
this.darInternal = this.dar;
this.mpr = (0x10000 - nWords) & 0xffff;
this.regRLBA = addr & 0xffff;
this.regRLCS = (this.regRLCS & ~PDP11.RL11.RLCS.BAE) | ((addr >> (16 - PDP11.RL11.RLCS.SHIFT.BAE)) & PDP11.RL11.RLCS.BAE);
this.regRLBE = (addr >> 16) & PDP11.RL11.RLBE.MASK; // 22 bit mode
this.regRLDA = (iCylinder << PDP11.RL11.RLDA.SHIFT.RW_CA) | (iHead? PDP11.RL11.RLDA.RW_HS : 0) | (iSector & PDP11.RL11.RLDA.RW_SA);
this.tmpRLDA = this.regRLDA;
this.regRLMP = (0x10000 - nWords) & 0xffff;
if (err) {
this.csr |= err | PDP11.RL11.RLCS.ERR;
this.regRLCS |= err | PDP11.RL11.RLCS.ERR;
}
return true;
};
@ -1013,6 +1027,7 @@ RL11.prototype.endReadWrite = function(err, iCylinder, iHead, iSector, nWords, a
RL11.prototype.readData = function(drive, iCylinder, iHead, iSector, nWords, addr, done)
{
var err = 0;
var checksum = 0;
var disk = drive.disk;
var sector = null, ibSector;
@ -1021,26 +1036,42 @@ RL11.prototype.readData = function(drive, iCylinder, iHead, iSector, nWords, add
nWords = 0;
}
var sWords = "";
while (nWords--) {
if (!sector) {
sector = drive.disk.seek(iCylinder, iHead, iSector + 1);
sector = disk.seek(iCylinder, iHead, iSector + 1);
if (!sector) {
err = PDP11.RL11.ERRC.HNF;
break;
}
ibSector = 0;
}
var b0, b1;
if ((b0 = drive.disk.read(sector, ibSector++)) < 0 || (b1 = drive.disk.read(sector, ibSector++)) < 0) {
var b0, b1, data;
if ((b0 = disk.read(sector, ibSector++)) < 0 || (b1 = disk.read(sector, ibSector++)) < 0) {
err = PDP11.RL11.ERRC.HNF;
break;
}
var data = this.bus.setWordDirect(addr, b0 | (b1 << 8));
/*
* Apparently (unlike the RK and RP controllers), this controller treats all 22-bit addresses as
* being in the UNIBUS address space (ie, the top 256Kb), which means we must call mapUnibus() on
* the address REGARDLESS whether it is actually >= BusPDP11.UNIBUS_22BIT. TODO: This is inherited
* code, so let's review the documentation on this.
*/
this.bus.setWordDirect(this.cpu.mapUnibus(addr), data = b0 | (b1 << 8));
if (DEBUG && this.messageEnabled(MessagesPDP11.READ)) {
if (!sWords) sWords = str.toOct(addr) + ": ";
sWords += str.toOct(data) + ' ';
if (sWords.length >= 64) {
console.log(sWords);
sWords = "";
}
}
if (this.bus.checkFault()) {
err = PDP11.RL11.ERRC.NXM;
break;
}
addr += 2;
checksum += data;
if (ibSector >= disk.cbSector) {
sector = null;
if (++iSector >= disk.nSectors) {
@ -1055,6 +1086,11 @@ RL11.prototype.readData = function(drive, iCylinder, iHead, iSector, nWords, add
}
}
}
if (DEBUG && this.messageEnabled(MessagesPDP11.READ)) {
console.log("checksum: " + (checksum|0));
}
return done(err, iCylinder, iHead, iSector, nWords, addr);
};
@ -1074,6 +1110,7 @@ RL11.prototype.readData = function(drive, iCylinder, iHead, iSector, nWords, add
RL11.prototype.writeData = function(drive, iCylinder, iHead, iSector, nWords, addr, done)
{
var err = 0;
var checksum = 0;
var disk = drive.disk;
var sector = null, ibSector;
@ -1082,22 +1119,38 @@ RL11.prototype.writeData = function(drive, iCylinder, iHead, iSector, nWords, ad
nWords = 0;
}
var sWords = "";
while (nWords--) {
var data = this.bus.getWordDirect(addr);
/*
* Apparently (unlike the RK and RP controllers), this controller treats all 22-bit addresses as
* being in the UNIBUS address space (ie, the top 256Kb), which means we must call mapUnibus() on
* the address REGARDLESS whether it is actually >= BusPDP11.UNIBUS_22BIT. TODO: This is inherited
* code, so let's review the documentation on this.
*/
var data = this.bus.getWordDirect(this.cpu.mapUnibus(addr));
if (this.bus.checkFault()) {
err = PDP11.RL11.ERRC.NXM;
break;
}
if (DEBUG && this.messageEnabled(MessagesPDP11.WRITE)) {
if (!sWords) sWords = str.toOct(addr) + ": ";
sWords += str.toOct(data) + ' ';
if (sWords.length >= 64) {
console.log(sWords);
sWords = "";
}
}
addr += 2;
checksum += data;
if (!sector) {
sector = drive.disk.seek(iCylinder, iHead, iSector + 1, true);
sector = disk.seek(iCylinder, iHead, iSector + 1, true);
if (!sector) {
err = PDP11.RL11.ERRC.HNF;
break;
}
ibSector = 0;
}
if (!drive.disk.write(sector, ibSector++, data & 0xff) || !drive.disk.write(sector, ibSector++, data >> 8)) {
if (!disk.write(sector, ibSector++, data & 0xff) || !disk.write(sector, ibSector++, data >> 8)) {
err = PDP11.RL11.ERRC.HNF;
break;
}
@ -1115,6 +1168,11 @@ RL11.prototype.writeData = function(drive, iCylinder, iHead, iSector, nWords, ad
}
}
}
if (DEBUG && this.messageEnabled(MessagesPDP11.WRITE)) {
console.log("checksum: " + (checksum|0));
}
return done(err, iCylinder, iHead, iSector, nWords, addr);
};
@ -1127,7 +1185,7 @@ RL11.prototype.writeData = function(drive, iCylinder, iHead, iSector, nWords, ad
*/
RL11.prototype.readRLCS = function(addr)
{
return this.csr & PDP11.RL11.RLCS.RMASK;
return this.regRLCS & PDP11.RL11.RLCS.RMASK;
};
/**
@ -1139,11 +1197,9 @@ RL11.prototype.readRLCS = function(addr)
*/
RL11.prototype.writeRLCS = function(data, addr)
{
this.csr = (this.csr & ~PDP11.RL11.RLCS.WMASK) | (data & PDP11.RL11.RLCS.WMASK);
this.bae = (this.bae & ~0x3) | ((data & PDP11.RL11.RLCS.BAE) >> 4);
if (!(this.csr & PDP11.RL11.RLCS.CRDY)) this.processCommand();
this.regRLCS = (this.regRLCS & ~PDP11.RL11.RLCS.WMASK) | (data & PDP11.RL11.RLCS.WMASK);
this.regRLBE = (this.regRLBE & 0x3C) | ((data & PDP11.RL11.RLCS.BAE) >> PDP11.RL11.RLCS.SHIFT.BAE);
if (!(this.regRLCS & PDP11.RL11.RLCS.CRDY)) this.processCommand();
};
/**
@ -1155,7 +1211,7 @@ RL11.prototype.writeRLCS = function(data, addr)
*/
RL11.prototype.readRLBA = function(addr)
{
return this.bar;
return this.regRLBA;
};
/**
@ -1167,7 +1223,7 @@ RL11.prototype.readRLBA = function(addr)
*/
RL11.prototype.writeRLBA = function(data, addr)
{
this.bar = data & PDP11.RL11.RLBA.WMASK;
this.regRLBA = data & PDP11.RL11.RLBA.WMASK;
};
/**
@ -1179,7 +1235,7 @@ RL11.prototype.writeRLBA = function(data, addr)
*/
RL11.prototype.readRLDA = function(addr)
{
return this.dar;
return this.regRLDA;
};
/**
@ -1191,7 +1247,7 @@ RL11.prototype.readRLDA = function(addr)
*/
RL11.prototype.writeRLDA = function(data, addr)
{
this.dar = data;
this.regRLDA = data;
};
/**
@ -1203,7 +1259,7 @@ RL11.prototype.writeRLDA = function(data, addr)
*/
RL11.prototype.readRLMP = function(addr)
{
return this.mpr;
return this.regRLMP;
};
/**
@ -1215,7 +1271,7 @@ RL11.prototype.readRLMP = function(addr)
*/
RL11.prototype.writeRLMP = function(data, addr)
{
this.mpr = data;
this.regRLMP = data;
};
/**
@ -1227,19 +1283,26 @@ RL11.prototype.writeRLMP = function(data, addr)
*/
RL11.prototype.readRLBE = function(addr)
{
return this.ber;
return this.regRLBE;
};
/**
* writeRLBE(data, addr)
*
* Curiously, we see RSTS/E v7.0 writing RLBE bits that aren't documented:
*
* R0=000000 R1=000000 R2=174410 R3=000000 R4=102076 R5=045166
* SP=052662 PC=067624 PS=034344 IR=000000 SL=000377 T0 N0 Z1 V0 C0
* 067624: 012712 000300 MOV #300,@R2
*
* @this {RL11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.RLBE or 174410)
*/
RL11.prototype.writeRLBE = function(data, addr)
{
this.ber = data & PDP11.RL11.RLBE.MASK;
this.regRLBE = data & PDP11.RL11.RLBE.MASK;
this.regRLCS = (this.regRLCS & ~PDP11.RL11.RLCS.BAE) | ((this.regRLBE & 0x3) << PDP11.RL11.RLCS.SHIFT.BAE);
};
/*

View file

@ -199,34 +199,58 @@ SerialPortPDP11.prototype.setBinding = function(sType, sBinding, control, sValue
* An onkeydown handler is required for certain keys that browsers tend to consume themselves;
* for example, BACKSPACE is often defined as going back to the previous web page, and certain
* CTRL keys are often used for browser shortcuts (usually on Windows-based browsers).
*
* NOTE: We don't bother with a keyUp handler, because for the most part, we're only intercepting
* keys that require special treatment; in general, we're content with keyPress events.
*/
control.onkeydown = function onKeyDown(event) {
event = event || window.event;
var fProcess = false;
var bASCII = 0;
var keyCode = event.keyCode;
/*
* Perform the same remapping of BACKSPACE and DELETE that our VT100 emulation performs,
* for PCjs-wide consistency; see the KEYMAP table in /modules/pc8080/lib/keyboard.js for
* the rationale. Ditto for ALT-DELETE; see onKeyDown() in /modules/pc8080/lib/keyboard.js
* for details.
*
* NOTE: keyDown (and keyUp) events supply us with KEYCODE values, which are NOT the same as
* ASCII values, which is why we are comparing with KEYCODE values but assigning ASCII values,
* because receiveData() requires ASCII values.
*/
if (keyCode == Keys.KEYCODE.BS) {
fProcess = true;
keyCode = Keys.ASCII.DEL;
bASCII = event.altKey? Keys.ASCII.CTRL_H : Keys.ASCII.DEL;
}
else if (keyCode == Keys.KEYCODE.DEL) {
bASCII = Keys.ASCII.CTRL_H;
}
else if (event.ctrlKey && keyCode >= Keys.ASCII.A && keyCode <= Keys.ASCII.Z) {
fProcess = true;
keyCode -= (Keys.ASCII.A - Keys.ASCII.CTRL_A);
bASCII = keyCode - (Keys.ASCII.A - Keys.ASCII.CTRL_A);
}
if (fProcess) {
if (bASCII) {
if (event.preventDefault) event.preventDefault();
serial.receiveData(keyCode);
serial.receiveData(bASCII);
}
return true;
};
control.onkeypress = function onKeyPress(event) {
/*
* Browser-independent keyCode extraction; refer to onKeyPress() and the other key event
* handlers in keyboard.js.
* NOTE: Unlike keyDown events, keyPress events generally supply us with ASCII values,
* despite the fact that, as above, they come to us via the keyCode property. Yes, it's
* brilliant (or rather, the opposite of brilliant), but that's life.
*/
event = event || window.event;
var keyCode = event.which || event.keyCode;
serial.receiveData(keyCode);
var bASCII = event.which || event.keyCode;
/*
* Perform the same remapping of ALT-ENTER (to LINE-FEED) that our VT100 emulation performs,
* for PCjs-wide consistency; see onKeyDown() in /modules/pc8080/lib/keyboard.js for details.
*/
if (event.altKey) {
if (bASCII == Keys.ASCII.CTRL_M) {
bASCII = Keys.ASCII.CTRL_J;
}
}
serial.receiveData(bASCII);
/*
* Since we're going to remove the "readonly" attribute from the <textarea> control
* (so that the soft keyboard activates on iOS), instead of calling preventDefault() for
@ -289,13 +313,13 @@ SerialPortPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
this.timerReceiveInterrupt = this.cpu.addTimer(function readyReceiver() {
var b = serial.receiveByte();
if (b >= 0) {
serial.rbuf = b;
if (!(serial.rcsr & PDP11.DL11.RCSR.RD)) {
serial.rcsr |= PDP11.DL11.RCSR.RD;
serial.regRBUF = b;
if (!(serial.regRCSR & PDP11.DL11.RCSR.RD)) {
serial.regRCSR |= PDP11.DL11.RCSR.RD;
} else {
serial.rbuf |= PDP11.DL11.RBUF.OE | PDP11.DL11.RBUF.ERROR;
serial.regRBUF |= PDP11.DL11.RBUF.OE | PDP11.DL11.RBUF.ERROR;
}
if (serial.rcsr & PDP11.DL11.RCSR.RIE) {
if (serial.regRCSR & PDP11.DL11.RCSR.RIE) {
cpu.setIRQ(serial.irqReceiver);
}
}
@ -304,8 +328,8 @@ SerialPortPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
this.irqTransmitter = this.cpu.addIRQ(PDP11.DL11.XVEC, PDP11.DL11.PRI, MessagesPDP11.DL11);
this.timerTransmitInterrupt = this.cpu.addTimer(function readyTransmitter() {
serial.xcsr |= PDP11.DL11.XCSR.READY;
if (serial.xcsr & PDP11.DL11.XCSR.TIE) {
serial.regXCSR |= PDP11.DL11.XCSR.READY;
if (serial.regXCSR & PDP11.DL11.XCSR.TIE) {
cpu.setIRQ(serial.irqTransmitter);
}
});
@ -458,9 +482,9 @@ SerialPortPDP11.prototype.restore = function(data)
*/
SerialPortPDP11.prototype.initState = function(data)
{
this.rbuf = 0;
this.rcsr = 0;
this.xcsr = PDP11.DL11.XCSR.READY;
this.regRBUF = 0;
this.regRCSR = 0;
this.regXCSR = PDP11.DL11.XCSR.READY;
this.abReceive = [];
return true;
};
@ -512,20 +536,20 @@ SerialPortPDP11.prototype.receiveData = function(data)
this.abReceive.push(data);
}
else if (typeof data == "string") {
var b = 0, bPrev;
var bASCII = 0, bASCIIPrev;
for (var i = 0; i < data.length; i++) {
bPrev = b;
b = data.charCodeAt(i);
bASCIIPrev = bASCII;
bASCII = data.charCodeAt(i);
/*
* NOTE: Multiple lines of pasted text will (at least on macOS) contain LFs instead of CRs;
* we convert them to CRs below. Windows may do something different, but in the worst case,
* even if we receive CR/LF pairs, this code should keep the CRs and lose the LFs.
*/
if (b == str.ASCII.LF) {
if (bPrev == str.ASCII.CR) continue;
b = str.ASCII.CR;
if (bASCII == str.ASCII.LF) {
if (bASCIIPrev == str.ASCII.CR) continue;
bASCII = str.ASCII.CR;
}
this.abReceive.push(b);
this.abReceive.push(bASCII);
}
}
else {
@ -657,7 +681,7 @@ SerialPortPDP11.prototype.transmitByte = function(b)
*/
SerialPortPDP11.prototype.readRCSR = function(addr)
{
return this.rcsr & PDP11.DL11.RCSR.RMASK;
return this.regRCSR & PDP11.DL11.RCSR.RMASK;
};
/**
@ -669,7 +693,7 @@ SerialPortPDP11.prototype.readRCSR = function(addr)
*/
SerialPortPDP11.prototype.writeRCSR = function(data, addr)
{
this.rcsr = (this.rcsr & ~PDP11.DL11.RCSR.WMASK) | (data & PDP11.DL11.RCSR.WMASK);
this.regRCSR = (this.regRCSR & ~PDP11.DL11.RCSR.WMASK) | (data & PDP11.DL11.RCSR.WMASK);
};
/**
@ -681,8 +705,8 @@ SerialPortPDP11.prototype.writeRCSR = function(data, addr)
*/
SerialPortPDP11.prototype.readRBUF = function(addr)
{
this.rcsr &= ~PDP11.DL11.RCSR.RD;
return this.rbuf;
this.regRCSR &= ~PDP11.DL11.RCSR.RD;
return this.regRBUF;
};
/**
@ -705,7 +729,7 @@ SerialPortPDP11.prototype.writeRBUF = function(data, addr)
*/
SerialPortPDP11.prototype.readXCSR = function(addr)
{
return this.xcsr;
return this.regXCSR;
};
/**
@ -726,14 +750,14 @@ SerialPortPDP11.prototype.writeXCSR = function(data, addr)
* this fix also requires a complementary change in setIRQ(), to request hardware interrupts with
* IRQ_DELAY rather than IRQ.
*/
if (this.xcsr & PDP11.DL11.XCSR.READY) {
if (this.regXCSR & PDP11.DL11.XCSR.READY) {
if (data & PDP11.DL11.XCSR.TIE) {
this.cpu.setIRQ(this.irqTransmitter);
} else {
this.cpu.clearIRQ(this.irqTransmitter);
}
}
this.xcsr = (this.xcsr & ~PDP11.DL11.XCSR.WMASK) | (data & PDP11.DL11.XCSR.WMASK);
this.regXCSR = (this.regXCSR & ~PDP11.DL11.XCSR.WMASK) | (data & PDP11.DL11.XCSR.WMASK);
};
/**
@ -758,7 +782,7 @@ SerialPortPDP11.prototype.readXBUF = function(addr)
SerialPortPDP11.prototype.writeXBUF = function(data, addr)
{
this.transmitByte(data & PDP11.DL11.XBUF.DATA);
this.xcsr &= ~PDP11.DL11.XCSR.READY;
this.regXCSR &= ~PDP11.DL11.XCSR.READY;
};
/*

View file

@ -37,7 +37,10 @@ var Keys = {
* is why I've 'unquoted' as many of them as possible.
*/
ASCII: {
CTRL_A: 1, CTRL_C: 3, CTRL_Z: 26,
BREAK: 0, CTRL_A: 1, CTRL_B: 2, CTRL_C: 3, CTRL_D: 4, CTRL_E: 5, CTRL_F: 6, CTRL_G: 7,
CTRL_H: 8, CTRL_I: 9, CTRL_J: 10, CTRL_K: 11, CTRL_L: 12, CTRL_M: 13, CTRL_N: 14, CTRL_O: 15,
CTRL_P: 16, CTRL_Q: 17, CTRL_R: 18, CTRL_S: 19, CTRL_T: 20, CTRL_U: 21, CTRL_V: 22, CTRL_W: 23,
CTRL_X: 24, CTRL_Y: 25, CTRL_Z: 26,
' ': 32, '!': 33, '"': 34, '#': 35, '$': 36, '%': 37, '&': 38, "'": 39,
'(': 40, ')': 41, '*': 42, '+': 43, ',': 44, '-': 45, '.': 46, '/': 47,
'0': 48, '1': 49, '2': 50, '3': 51, '4': 52, '5': 53, '6': 54, '7': 55,