Merge branch 'next-release'
This commit is contained in:
commit
4022fc03a4
303 changed files with 14860 additions and 877 deletions
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue