Simplify the Bus and Memory interfaces slightly, by enabling WORDBUS, which treats all word accesses as aligned; the CPU is has sole responsibility for detecting unaligned word addresses and trapping appropriatelu

This commit is contained in:
Jeff 2016-10-06 14:17:29 -07:00 committed by Jeff Parsons
commit 1a1614a648
12 changed files with 444 additions and 398 deletions

View file

@ -253,7 +253,7 @@ C1PComputer.power = function(computer)
/*
* C1PComputer.init()
*
* This function operates on every HTML element of class "computer", extracting the
* This function operates on every HTML element of class "c1pjs-computer", extracting the
* JSON-encoded parameters for the C1PComputer constructor from the element's "data-value"
* attribute, invoking the constructor to create a C1PComputer component, and then binding
* any associated HTML controls to the new component.
@ -263,7 +263,7 @@ C1PComputer.init = function()
/*
* In non-COMPILED builds, embedMachine() may have set XMLVERSION.
*/
if (!COMPILED && C1PJS.XMLVERSION) C1PJS.APPVERSION = C1PJS.XMLVERSION;
if (!COMPILED && XMLVERSION) C1PJS.APPVERSION = XMLVERSION;
var aeComputers = Component.getElementsByClass(document, C1PJS.APPCLASS, "computer");

View file

@ -1488,7 +1488,7 @@ Computer8080.prototype.updateVideo = function(fForced)
/**
* Computer8080.init()
*
* For every machine represented by an HTML element of class "pcjs-machine", this function
* For every machine represented by an HTML element of class "pc8080-machine", this function
* locates the HTML element of class "computer", extracting the JSON-encoded parameters for the
* Computer constructor from the element's "data-value" attribute, invoking the constructor to
* create a Computer component, and then binding any associated HTML controls to the new component.
@ -1498,7 +1498,7 @@ Computer8080.init = function()
/*
* In non-COMPILED builds, embedMachine() may have set XMLVERSION.
*/
if (!COMPILED && PC8080.XMLVERSION) PC8080.APPVERSION = PC8080.XMLVERSION;
if (!COMPILED && XMLVERSION) PC8080.APPVERSION = XMLVERSION;
var aeMachines = Component.getElementsByClass(document, PC8080.APPCLASS + "-machine");

View file

@ -1518,7 +1518,7 @@ Computer.prototype.updateVideo = function(fForce)
/**
* Computer.init()
*
* For every machine represented by an HTML element of class "pcjs-machine", this function
* For every machine represented by an HTML element of class "pcx86-machine", this function
* locates the HTML element of class "computer", extracting the JSON-encoded parameters for the
* Computer constructor from the element's "data-value" attribute, invoking the constructor to
* create a Computer component, and then binding any associated HTML controls to the new component.
@ -1528,7 +1528,7 @@ Computer.init = function()
/*
* In non-COMPILED builds, embedMachine() may have set XMLVERSION.
*/
if (!COMPILED && PCX86.XMLVERSION) PCX86.APPVERSION = PCX86.XMLVERSION;
if (!COMPILED && XMLVERSION) PCX86.APPVERSION = XMLVERSION;
var aeMachines = Component.getElementsByClass(document, PCX86.APPCLASS + "-machine");

View file

@ -133,7 +133,7 @@ function BusPDP11(parmsBus, cpu, dbg)
this.fIOBreakAll = false;
this.fnReset = null;
this.fnAccess = this.access;
this.fnIOAccess = this.unknownIO;
/*
* Allocate empty Memory blocks to span the entire physical address space.
@ -214,7 +214,7 @@ BusPDP11.IOController = {
}
return b;
}
b = bus.fnAccess(addr | BusPDP11.IOPAGE_22BIT, -1, 1);
b = bus.fnIOAccess(addr | BusPDP11.IOPAGE_22BIT, -1, 1);
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
this.dbg.printMessage("warning: unconverted read access to byte @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(b));
}
@ -279,7 +279,7 @@ BusPDP11.IOController = {
}
return;
}
bus.fnAccess(addr | BusPDP11.IOPAGE_22BIT, b, 1);
bus.fnIOAccess(addr | BusPDP11.IOPAGE_22BIT, b, 1);
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
this.dbg.printMessage("warning: unconverted write access to byte @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(b));
}
@ -312,7 +312,7 @@ BusPDP11.IOController = {
}
return w;
}
w = bus.fnAccess(addr | BusPDP11.IOPAGE_22BIT, -1, 0);
w = bus.fnIOAccess(addr | BusPDP11.IOPAGE_22BIT, -1, 0);
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
this.dbg.printMessage("warning: unconverted read access to word @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(w));
}
@ -349,7 +349,7 @@ BusPDP11.IOController = {
}
return;
}
bus.fnAccess(addr | BusPDP11.IOPAGE_22BIT, w, 0);
bus.fnIOAccess(addr | BusPDP11.IOPAGE_22BIT, w, 0);
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
this.dbg.printMessage("warning: unconverted write access to word @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(w));
}
@ -426,7 +426,7 @@ BusPDP11.prototype.reset = function()
};
/**
* access(addr, data, byteFlag)
* unknownIO(addr, data, byteFlag)
*
* This is our default I/O handler, called whenever there's an IOPAGE access without a corresponding entry
* in aIOHandlers; in the interim, our Device component will override this default handler with its own function
@ -438,10 +438,10 @@ BusPDP11.prototype.reset = function()
* @param {number} data (-1 if read, otherwise write)
* @param {number} byteFlag (true if byte I/O, otherwise word)
*/
BusPDP11.prototype.access = function(addr, data, byteFlag)
BusPDP11.prototype.unknownIO = function(addr, data, byteFlag)
{
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
this.dbg.printMessage("warning: unrecognized access(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(data) + "," + byteFlag + ")");
this.dbg.printMessage("warning: unrecognized I/O access(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(data) + "," + byteFlag + ")");
}
return 0;
};
@ -808,10 +808,10 @@ BusPDP11.prototype.getWord = function(addr)
{
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
if (off != this.nBlockLimit) {
return this.aMemBlocks[iBlock].readWord(off, addr);
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
return this.aMemBlocks[iBlock++].readByte(off, addr) | (this.aMemBlocks[iBlock & this.nBlockMask].readByte(0, addr + 1) << 8);
}
return this.aMemBlocks[iBlock++].readByte(off, addr) | (this.aMemBlocks[iBlock & this.nBlockMask].readByte(0, addr + 1) << 8);
return this.aMemBlocks[iBlock].readWord(off, addr);
};
/**
@ -827,10 +827,10 @@ BusPDP11.prototype.getWordDirect = function(addr)
{
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
if (off != this.nBlockLimit) {
return this.aMemBlocks[iBlock].readWordDirect(off, addr);
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
return this.aMemBlocks[iBlock++].readByteDirect(off, addr) | (this.aMemBlocks[iBlock & this.nBlockMask].readByteDirect(0, addr + 1) << 8);
}
return this.aMemBlocks[iBlock++].readByteDirect(off, addr) | (this.aMemBlocks[iBlock & this.nBlockMask].readByteDirect(0, addr + 1) << 8);
return this.aMemBlocks[iBlock].readWordDirect(off, addr);
};
/**
@ -871,12 +871,12 @@ BusPDP11.prototype.setWord = function(addr, w)
{
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
if (off != this.nBlockLimit) {
this.aMemBlocks[iBlock].writeWord(off, w & 0xffff, addr);
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);
return;
}
this.aMemBlocks[iBlock++].writeByte(off, w & 0xff, addr);
this.aMemBlocks[iBlock & this.nBlockMask].writeByte(0, (w >> 8) & 0xff, addr + 1);
this.aMemBlocks[iBlock].writeWord(off, w & 0xffff, addr);
};
/**
@ -893,12 +893,12 @@ BusPDP11.prototype.setWordDirect = function(addr, w)
{
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
if (off != this.nBlockLimit) {
this.aMemBlocks[iBlock].writeWordDirect(off, w & 0xffff, 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);
return;
}
this.aMemBlocks[iBlock++].writeByteDirect(off, w & 0xff, addr);
this.aMemBlocks[iBlock & this.nBlockMask].writeByteDirect(0, (w >> 8) & 0xff, addr + 1);
this.aMemBlocks[iBlock].writeWordDirect(off, w & 0xffff, addr);
};
/**
@ -1072,18 +1072,18 @@ BusPDP11.prototype.addIOTable = function(component, table)
};
/**
* addIODefaultHandlers(fnReset, fnAccess)
* addIODefaultHandlers(fnReset, fnIOAccess)
*
* Add default I/O notification handlers.
*
* @this {BusPDP11}
* @param {function()} fnReset
* @param {function(number,number,number)} fnAccess
* @param {function(number,number,number)} fnIOAccess
*/
BusPDP11.prototype.addIODefaultHandlers = function(fnReset, fnAccess)
BusPDP11.prototype.addIODefaultHandlers = function(fnReset, fnIOAccess)
{
this.fnReset = fnReset;
this.fnAccess = fnAccess;
this.fnIOAccess = fnIOAccess;
};
/**

View file

@ -1448,7 +1448,7 @@ ComputerPDP11.prototype.updateStatus = function(fForce)
/**
* ComputerPDP11.init()
*
* For every machine represented by an HTML element of class "pcjs-machine", this function
* For every machine represented by an HTML element of class "pdp11-machine", this function
* locates the HTML element of class "computer", extracting the JSON-encoded parameters for the
* Computer constructor from the element's "data-value" attribute, invoking the constructor to
* create a Computer component, and then binding any associated HTML controls to the new component.

View file

@ -1094,12 +1094,18 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
this.mmuLastVirtual = physicalAddress;
if (physicalAddress >= BusPDP11.IOPAGE_VIRT) {
physicalAddress |= BusPDP11.IOPAGE_22BIT;
} else { // no max_memory check in 16 bit mode
}
/*
* I'm moving this alignment test to the CPU->Bus interfaces that read/write physical words;
* hopefully there are no order dependencies on this test.
*
else { // no max_memory check in 16 bit mode
if ((physicalAddress & 1) && !(accessFlags & PDP11.ACCESS.BYTE)) {
this.regErr |= PDP11.CPUERR.ODDADDR;
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.REASON.ODDMEMADDR);
}
}
*/
} else {
this.mmuLastVirtual = virtualAddress;
page = (virtualAddress >> 13) & this.mmuMask[this.mmuMode];
@ -1118,10 +1124,15 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
this.regErr |= PDP11.CPUERR.NOMEMORY;
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.REASON.NOMEMORY); // KB11-EM does this after ABORT handling - KB11-CM before
}
/*
* I'm moving this alignment test to the CPU->Bus interfaces that read/write physical words;
* hopefully there are no order dependencies on this test.
*
if ((physicalAddress & 1) && !(accessFlags & PDP11.ACCESS.BYTE)) {
this.regErr |= PDP11.CPUERR.ODDADDR;
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.REASON.ODDMMUADDR);
}
*/
}
switch (pdr & 0x7) {
case 1: // read-only with trap
@ -1190,6 +1201,18 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
return physicalAddress;
};
/**
* readByteFromPhysical(physicalAddress) [formerly readByteByAddr]
*
* @this {CPUStatePDP11}
* @param {number} physicalAddress
* @return {number}
*/
CPUStatePDP11.prototype.readByteFromPhysical = function(physicalAddress)
{
return this.bus.getByte(physicalAddress);
};
/**
* readWordFromPhysical(physicalAddress) [formerly readWordByAddr]
*
@ -1199,6 +1222,10 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
*/
CPUStatePDP11.prototype.readWordFromPhysical = function(physicalAddress)
{
if (physicalAddress & 0x1) {
this.regErr |= PDP11.CPUERR.ODDADDR;
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.REASON.ODDMEMADDR);
}
return this.bus.getWord(physicalAddress);
};
@ -1213,30 +1240,6 @@ CPUStatePDP11.prototype.readWordFromVirtual = function(virtualAddress)
return this.readWordFromPhysical(this.mapVirtualToPhysical(virtualAddress, PDP11.ACCESS.READ_WORD));
};
/**
* writeWordToPhysical(physicalAddress, data) [formerly writeWordByAddr]
*
* @this {CPUStatePDP11}
* @param {number} physicalAddress
* @param {number} data
*/
CPUStatePDP11.prototype.writeWordToPhysical = function(physicalAddress, data)
{
this.bus.setWord(physicalAddress, data & 0xffff);
};
/**
* readByteFromPhysical(physicalAddress) [formerly readByteByAddr]
*
* @this {CPUStatePDP11}
* @param {number} physicalAddress
* @return {number}
*/
CPUStatePDP11.prototype.readByteFromPhysical = function(physicalAddress)
{
return this.bus.getByte(physicalAddress);
};
/**
* writeByteToPhysical(physicalAddress, data) [formerly writeByteByAddr]
*
@ -1250,6 +1253,22 @@ CPUStatePDP11.prototype.writeByteToPhysical = function(physicalAddress, data)
this.bus.setByte(physicalAddress, data & 0xff);
};
/**
* writeWordToPhysical(physicalAddress, data) [formerly writeWordByAddr]
*
* @this {CPUStatePDP11}
* @param {number} physicalAddress
* @param {number} data
*/
CPUStatePDP11.prototype.writeWordToPhysical = function(physicalAddress, data)
{
if (physicalAddress & 0x1) {
this.regErr |= PDP11.CPUERR.ODDADDR;
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.REASON.ODDMEMADDR);
}
this.bus.setWord(physicalAddress, data & 0xffff);
};
/**
* popWord()
*

View file

@ -590,7 +590,7 @@ if (DEBUGGER) {
if (addr !== PDP11.ADDR_INVALID) {
this.nDisableMessages++;
/*
* TODO: We also need a Bus interface to disable fnAccess() calls that could trigger a trap()
* TODO: We also need a Bus interface to disable fnIOAccess() calls that could trigger a trap().
*/
b = this.bus.getByteDirect(addr);
this.nDisableMessages--;
@ -614,7 +614,11 @@ if (DEBUGGER) {
if (addr !== PDP11.ADDR_INVALID) {
this.nDisableMessages++;
/*
* TODO: We also need a Bus interface to disable fnAccess() calls that could trigger a trap()
* TODO: We also need a Bus interface to disable fnIOAccess() calls that could trigger a trap().
*
* NOTE: We don't care if the word address is aligned, because 1) we assume the user knows what
* they're doing, and 2) the Bus simply ignores the low address bit anyway. Alignment checks are
* performed by the CPU, not the Bus.
*/
w = this.bus.getWordDirect(addr);
this.nDisableMessages--;
@ -637,7 +641,7 @@ if (DEBUGGER) {
if (addr !== PDP11.ADDR_INVALID) {
this.nDisableMessages++;
/*
* TODO: We also need a Bus interface to disable fnAccess() calls that could trigger a trap()
* TODO: We also need a Bus interface to disable fnIOAccess() calls that could trigger a trap().
*/
this.bus.setByteDirect(addr, b);
this.nDisableMessages--;
@ -660,7 +664,11 @@ if (DEBUGGER) {
if (addr !== PDP11.ADDR_INVALID) {
this.nDisableMessages++;
/*
* TODO: We also need a Bus interface to disable fnAccess() calls that could trigger a trap()
* TODO: We also need a Bus interface to disable fnIOAccess() calls that could trigger a trap().
*
* NOTE: We don't care if the word address is aligned, because 1) we assume the user knows what
* they're doing, and 2) the Bus simply ignores the low address bit anyway. Alignment checks are
* performed by the CPU, not the Bus.
*/
this.bus.setWordDirect(addr, w);
this.nDisableMessages--;
@ -2841,15 +2849,14 @@ if (DEBUGGER) {
var sData = "", sChars = "";
sAddr = this.toStrAddr(dbgAddr);
/*
* It's just coincidence that we want to dump 8 bytes per line when using base 8 and 16 bytes
* per line when using base 16, because octal requires more digits.
* Dump 8 bytes per line when using base 8, and dump 16 bytes when using base 16 (or when dumping dwords)
*/
var nBytes = (size == 4? 16 : this.nBase);
for (i = nBytes; i > 0 && cb > 0; i--) {
var b = this.getByte(dbgAddr, 1);
data |= (b << (iByte++ << 3));
if (iByte == size) {
sData += (this.nBase == 8? str.toOct(data, size * 3) : str.toHex(data, size * 2));
sData += this.toStrBase(data, size);
sData += (size == 1? (i == 9? '-' : ' ') : " ");
data = iByte = 0;
}

View file

@ -79,6 +79,21 @@ var BYTEARRAYS = false;
*/
var TYPEDARRAYS = (typeof ArrayBuffer !== 'undefined');
/**
* WORDBUS forces the Bus and Memory interfaces to assume even addresses when accessing words. Since PDPjs inherited
* its Bus component from PCx86, it originally supported both aligned and unaligned word accesses by default, but since
* the PDP-11 requires aligned (even) word addresses, we can turn off support for unaligned accesses and get some
* performance gains.
*
* When WORDBUS is true, the Bus and Memory components simply ignore the low bit of word addresses, because it is the
* CPU, not the Bus, that's responsible for validating addresses and generating the appropriate traps.
*
* Don't worry that the source code looks MORE complicated rather than LESS with the additional WORDBUS checks, because
* the Closure Compiler eliminates those checks and throws away the (unreachable) code blocks that deal with unaligned
* accesses.
*/
var WORDBUS = true;
/*
* Combine all the shared globals and machine-specific globals into one machine-specific global object,
* which all machine components should start using; eg: "if (PDP11.DEBUG) ..." instead of "if (DEBUG) ...".
@ -95,6 +110,7 @@ var PDP11 = {
MAXDEBUG: MAXDEBUG, // shared
PRIVATE: PRIVATE, // shared
TYPEDARRAYS:TYPEDARRAYS,
WORDBUS: WORDBUS,
SITEHOST: SITEHOST, // shared
XMLVERSION: XMLVERSION, // shared

View file

@ -782,10 +782,15 @@ MemoryPDP11.prototype = {
*/
readWordLE: function readWordLE(off, addr) {
/*
* TODO: It remains to be seen if there's any advantage to checking the offset for an aligned read
* vs. always reading the bytes separately.
* TODO: For non-WORDBUS machines, it remains to be seen if there's any advantage to checking the offset
* for an aligned read vs. always reading the bytes separately.
*/
var w = (off & 0x1)? (this.ab[off] | (this.ab[off+1] << 8)) : this.aw[off >> 1];
var w;
if (PDP11.WORDBUS || !(off & 0x1)) {
w = this.aw[off >> 1];
} else {
w = this.ab[off] | (this.ab[off+1] << 8);
}
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEM)) {
this.dbg.printMessage("Memory.readWord(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(w), true);
}
@ -840,14 +845,14 @@ MemoryPDP11.prototype = {
*/
writeWordLE: function writeWordLE(off, w, addr) {
/*
* TODO: It remains to be seen if there's any advantage to checking the offset for an aligned write
* vs. always writing the bytes separately.
* TODO: For non-WORDBUS machines, it remains to be seen if there's any advantage to checking the offset
* for an aligned write vs. always writing the bytes separately.
*/
if (off & 0x1) {
if (PDP11.WORDBUS || !(off & 0x1)) {
this.aw[off >> 1] = w;
} else {
this.ab[off] = w;
this.ab[off+1] = w >> 8;
} else {
this.aw[off >> 1] = w;
}
this.fDirty = true;
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEM)) {