New improved I/O handler functions for the PDP-11
This commit is contained in:
parent
c61876a568
commit
dfe367f53d
18 changed files with 847 additions and 1059 deletions
|
|
@ -59,11 +59,6 @@ if (NODE) {
|
|||
* addMemory(). If the component needs something more than simple read/write storage,
|
||||
* it must provide a custom controller.
|
||||
*
|
||||
* All port (I/O) operations are defined by external handlers; they register with us,
|
||||
* and we manage those registrations and provide support for I/O breakpoints, but the
|
||||
* only default I/O behavior we provide is ignoring writes to any unregistered output
|
||||
* ports and returning 0xff from any unregistered input ports.
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
* @param {Object} parmsBus
|
||||
|
|
@ -80,35 +75,16 @@ function Bus(parmsBus, cpu, dbg)
|
|||
this.nBusWidth = parmsBus['busWidth'] || 16;
|
||||
|
||||
/*
|
||||
* Compute all Bus memory block parameters, based on the width of the bus.
|
||||
*
|
||||
* Regarding blockTotal, we want to avoid using block overflow expressions like:
|
||||
*
|
||||
* iBlock < this.nBlockTotal? iBlock : 0
|
||||
*
|
||||
* As long as we know that blockTotal is a power of two (eg, 256 or 0x100, in the case of
|
||||
* nBusWidth == 20 and blockSize == 4096), we can define blockMask as (blockTotal - 1) and
|
||||
* rewrite the previous expression as:
|
||||
*
|
||||
* iBlock & this.nBlockMask
|
||||
*
|
||||
* Bus Property Old hard-coded values (when nBusWidth was always 20)
|
||||
* ------------ ----------------------------------------------------
|
||||
* this.nBusLimit 0xfffff
|
||||
* this.nBusMask [same as busLimit]
|
||||
* this.nBlockSize 4096
|
||||
* this.nBlockLen (this.nBlockSize >> 2)
|
||||
* this.nBlockShift 12
|
||||
* this.nBlockLimit 0xfff
|
||||
* this.nBlockTotal ((this.nBusLimit + this.nBlockSize) / this.nBlockSize) | 0
|
||||
* this.nBlockMask (this.nBlockTotal - 1) [ie, 0xff]
|
||||
*
|
||||
* Note that we choose a nBlockShift value (and thus a physical memory block size) based on "buswidth":
|
||||
* Compute all Bus6502 memory block parameters, based on the width of the bus. The entire
|
||||
* address space is divided into blocks, using a block size that is (hopefully) appropriate to
|
||||
* the bus width. The following table summarizes our simplistic calculations.
|
||||
*
|
||||
* Bus Width Block Shift Block Size
|
||||
* --------- ----------- ----------
|
||||
* 16 bits (64Kb address space): 10 1Kb (64 maximum blocks)
|
||||
* 18 bits (256Kb address space): 11 2Kb (128 maximum blocks)
|
||||
* 20 bits (1Mb address space): 12 4Kb (256 maximum blocks)
|
||||
* 22 bits (4Mb address space): 13 8Kb (512 maximum blocks)
|
||||
* 24 bits (16Mb address space): 14 16Kb (1K maximum blocks)
|
||||
* 32 bits (4Gb address space); 15 32Kb (128K maximum blocks)
|
||||
*
|
||||
|
|
@ -119,7 +95,9 @@ function Bus(parmsBus, cpu, dbg)
|
|||
*/
|
||||
this.addrTotal = Math.pow(2, this.nBusWidth);
|
||||
this.nBusLimit = this.nBusMask = (this.addrTotal - 1) | 0;
|
||||
this.nBlockShift = (this.nBusWidth <= 16)? 10 : ((this.nBusWidth <= 20)? 12 : (this.nBusWidth <= 24? 14 : 15));
|
||||
this.nBlockShift = (this.nBusWidth >> 1) + 2;
|
||||
if (this.nBlockShift < 10) this.nBlockShift = 10;
|
||||
if (this.nBlockShift > 15) this.nBlockShift = 15;
|
||||
this.nBlockSize = 1 << this.nBlockShift;
|
||||
this.nBlockLen = this.nBlockSize >> 2;
|
||||
this.nBlockLimit = this.nBlockSize - 1;
|
||||
|
|
@ -127,39 +105,6 @@ function Bus(parmsBus, cpu, dbg)
|
|||
this.nBlockMask = this.nBlockTotal - 1;
|
||||
this.assert(this.nBlockMask <= Bus.BlockInfo.num.mask);
|
||||
|
||||
/*
|
||||
* Lists of I/O notification functions: aPortInputNotify and aPortOutputNotify are arrays, indexed by
|
||||
* port, of sub-arrays which contain:
|
||||
*
|
||||
* [0]: registered function to call for every I/O access
|
||||
*
|
||||
* The registered function is called with the port address, and if the access was triggered by the CPU,
|
||||
* the instruction pointer (IP) at the point of access.
|
||||
*
|
||||
* WARNING: Unlike the (old) read and write memory notification functions, these support only one
|
||||
* pair of input/output functions per port. A more sophisticated architecture could support a list
|
||||
* of chained functions across multiple components, but I doubt that will be necessary here.
|
||||
*
|
||||
* UPDATE: The Debugger now piggy-backs on these arrays to indicate ports for which it wants notification
|
||||
* of I/O. In those cases, the registered component/function elements may or may not be set, but the
|
||||
* following additional element will be set:
|
||||
*
|
||||
* [1]: true to break on I/O, false to ignore I/O
|
||||
*
|
||||
* The false case is important if fPortInputBreakAll and/or fPortOutputBreakAll is set, because it allows the
|
||||
* Debugger to selectively ignore specific ports.
|
||||
*/
|
||||
this.aPortInputNotify = [];
|
||||
this.aPortOutputNotify = [];
|
||||
this.fPortInputBreakAll = this.fPortOutputBreakAll = false;
|
||||
|
||||
/*
|
||||
* By default, all I/O ports are 1 byte wide; ports that are wider must add themselves to one or both of
|
||||
* these lists, using addPortInputWidth() and/or addPortOutputWidth().
|
||||
*/
|
||||
this.aPortInputWidth = [];
|
||||
this.aPortOutputWidth = [];
|
||||
|
||||
/*
|
||||
* Allocate empty Memory blocks to span the entire physical address space.
|
||||
*/
|
||||
|
|
@ -177,38 +122,6 @@ Bus.ERROR = {
|
|||
REM_MEM_BADRANGE: 5
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {number}
|
||||
*/
|
||||
var BlockInfo;
|
||||
|
||||
/**
|
||||
* This defines the BlockInfo bit fields used by scanMemory() when it creates the aBlocks array.
|
||||
*
|
||||
* @typedef {{
|
||||
* num: BitField,
|
||||
* count: BitField,
|
||||
* btmod: BitField,
|
||||
* type: BitField
|
||||
* }}
|
||||
*/
|
||||
Bus.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
|
||||
|
||||
/**
|
||||
* BusInfo object definition (returned by scanMemory())
|
||||
*
|
||||
* cbTotal: total bytes allocated
|
||||
* cBlocks: total Memory blocks allocated
|
||||
* aBlocks: array of allocated Memory block numbers
|
||||
*
|
||||
* @typedef {{
|
||||
* cbTotal: number,
|
||||
* cBlocks: number,
|
||||
* aBlocks: Array.<BlockInfo>
|
||||
* }}
|
||||
*/
|
||||
var BusInfo;
|
||||
|
||||
/**
|
||||
* initMemory()
|
||||
*
|
||||
|
|
@ -365,6 +278,42 @@ Bus.prototype.cleanMemory = function(addr, size)
|
|||
return fClean;
|
||||
};
|
||||
|
||||
/*
|
||||
* Data types used by scanMemory()
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {number}
|
||||
*/
|
||||
var BlockInfo;
|
||||
|
||||
/**
|
||||
* This defines the BlockInfo bit fields used by scanMemory() when it creates the aBlocks array.
|
||||
*
|
||||
* @typedef {{
|
||||
* num: BitField,
|
||||
* count: BitField,
|
||||
* btmod: BitField,
|
||||
* type: BitField
|
||||
* }}
|
||||
*/
|
||||
Bus.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
|
||||
|
||||
/**
|
||||
* BusInfo object definition (returned by scanMemory())
|
||||
*
|
||||
* cbTotal: total bytes allocated
|
||||
* cBlocks: total Memory blocks allocated
|
||||
* aBlocks: array of allocated Memory block numbers
|
||||
*
|
||||
* @typedef {{
|
||||
* cbTotal: number,
|
||||
* cBlocks: number,
|
||||
* aBlocks: Array.<BlockInfo>
|
||||
* }}
|
||||
*/
|
||||
var BusInfo;
|
||||
|
||||
/**
|
||||
* scanMemory(info, addr, size)
|
||||
*
|
||||
|
|
@ -741,309 +690,6 @@ Bus.prototype.restoreMemory = function(a)
|
|||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* addPortInputBreak(port)
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} [port]
|
||||
* @return {boolean} true if break on port input enabled, false if disabled
|
||||
*/
|
||||
Bus.prototype.addPortInputBreak = function(port)
|
||||
{
|
||||
if (port === undefined) {
|
||||
this.fPortInputBreakAll = !this.fPortInputBreakAll;
|
||||
return this.fPortInputBreakAll;
|
||||
}
|
||||
if (this.aPortInputNotify[port] === undefined) {
|
||||
this.aPortInputNotify[port] = [null, false];
|
||||
}
|
||||
this.aPortInputNotify[port][1] = !this.aPortInputNotify[port][1];
|
||||
return this.aPortInputNotify[port][1];
|
||||
};
|
||||
|
||||
/**
|
||||
* addPortInputNotify(start, end, fn)
|
||||
*
|
||||
* Add a port input-notification handler to the list of such handlers.
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} start port address
|
||||
* @param {number} end port address
|
||||
* @param {function(number,number)} fn is called with the port and IP values at the time of the input
|
||||
*/
|
||||
Bus.prototype.addPortInputNotify = function(start, end, fn)
|
||||
{
|
||||
if (fn !== undefined) {
|
||||
for (var port = start; port <= end; port++) {
|
||||
if (this.aPortInputNotify[port] !== undefined) {
|
||||
Component.warning("Input port " + str.toHexWord(port) + " already registered");
|
||||
continue;
|
||||
}
|
||||
this.aPortInputNotify[port] = [fn, false];
|
||||
if (MAXDEBUG) this.log("addPortInputNotify(" + str.toHexWord(port) + ")");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* addPortInputTable(component, table, offset)
|
||||
*
|
||||
* Add port input-notification handlers from the specified table (a batch version of addPortInputNotify)
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {Component} component
|
||||
* @param {Object} table
|
||||
* @param {number} [offset] is an optional port offset
|
||||
*/
|
||||
Bus.prototype.addPortInputTable = function(component, table, offset)
|
||||
{
|
||||
if (offset === undefined) offset = 0;
|
||||
for (var port in table) {
|
||||
this.addPortInputNotify(+port + offset, +port + offset, table[port].bind(component));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* addPortInputWidth(port, size)
|
||||
*
|
||||
* By default, all input ports are 1 byte wide; ports that are wider must call this function.
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} port
|
||||
* @param {number} size (1, 2 or 4)
|
||||
*/
|
||||
Bus.prototype.addPortInputWidth = function(port, size)
|
||||
{
|
||||
this.aPortInputWidth[port] = size;
|
||||
};
|
||||
|
||||
/**
|
||||
* checkPortInputNotify(port, size, addrIP)
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} port
|
||||
* @param {number} size (1, 2 or 4)
|
||||
* @param {number} [addrIP] is the IP value at the time of the input
|
||||
* @return {number} simulated port data
|
||||
*
|
||||
* NOTE: It seems that parts of the ROM BIOS (like the RS-232 probes around F000:E5D7 in the 5150 BIOS)
|
||||
* assume that ports for non-existent hardware return 0xff rather than 0x00, hence my new default (0xff) below.
|
||||
*/
|
||||
Bus.prototype.checkPortInputNotify = function(port, size, addrIP)
|
||||
{
|
||||
var data = 0, shift = 0;
|
||||
|
||||
while (size > 0) {
|
||||
|
||||
var aNotify = this.aPortInputNotify[port];
|
||||
var sizePort = this.aPortInputWidth[port] || 1;
|
||||
var maskPort = (sizePort == 1? 0xff : (sizePort == 2? 0xffff : -1));
|
||||
var dataPort = maskPort;
|
||||
|
||||
/*
|
||||
* TODO: We need to decide what to do about 8-bit I/O to a 16-bit port (ditto for 16-bit I/O
|
||||
* to a 32-bit port). We probably should pass the size through to the aNotify[0] handler,
|
||||
* and let it decide what to do, but I don't feel like changing all the I/O handlers right now.
|
||||
* The good news, at least, is that the 8-bit handlers would not have to do anything special.
|
||||
* This assert will warn us if this is a pressing need.
|
||||
*/
|
||||
this.assert(size >= sizePort);
|
||||
|
||||
if (aNotify !== undefined) {
|
||||
if (aNotify[0]) {
|
||||
dataPort = aNotify[0](port, addrIP);
|
||||
if (dataPort === undefined) {
|
||||
dataPort = maskPort;
|
||||
} else {
|
||||
dataPort &= maskPort;
|
||||
}
|
||||
}
|
||||
if (DEBUGGER && this.dbg && this.fPortInputBreakAll != aNotify[1]) {
|
||||
this.dbg.checkPortInput(port, size, dataPort);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (DEBUGGER && this.dbg) {
|
||||
this.dbg.messageIO(this, port, null, addrIP);
|
||||
if (this.fPortInputBreakAll) this.dbg.checkPortInput(port, size, dataPort);
|
||||
}
|
||||
}
|
||||
|
||||
data |= dataPort << shift;
|
||||
shift += (sizePort << 3);
|
||||
port += sizePort;
|
||||
size -= sizePort;
|
||||
}
|
||||
|
||||
this.assert(!size);
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* removePortInputNotify(start, end)
|
||||
*
|
||||
* Remove port input-notification handler(s) (to be ENABLED later if needed)
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} start address
|
||||
* @param {number} end address
|
||||
*
|
||||
Bus.prototype.removePortInputNotify = function(start, end)
|
||||
{
|
||||
for (var port = start; port < end; port++) {
|
||||
if (this.aPortInputNotify[port]) {
|
||||
delete this.aPortInputNotify[port];
|
||||
}
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
/**
|
||||
* addPortOutputBreak(port)
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} [port]
|
||||
* @return {boolean} true if break on port output enabled, false if disabled
|
||||
*/
|
||||
Bus.prototype.addPortOutputBreak = function(port)
|
||||
{
|
||||
if (port === undefined) {
|
||||
this.fPortOutputBreakAll = !this.fPortOutputBreakAll;
|
||||
return this.fPortOutputBreakAll;
|
||||
}
|
||||
if (this.aPortOutputNotify[port] === undefined) {
|
||||
this.aPortOutputNotify[port] = [null, false];
|
||||
}
|
||||
this.aPortOutputNotify[port][1] = !this.aPortOutputNotify[port][1];
|
||||
return this.aPortOutputNotify[port][1];
|
||||
};
|
||||
|
||||
/**
|
||||
* addPortOutputNotify(start, end, fn)
|
||||
*
|
||||
* Add a port output-notification handler to the list of such handlers.
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} start port address
|
||||
* @param {number} end port address
|
||||
* @param {function(number,number)} fn is called with the port and IP values at the time of the output
|
||||
*/
|
||||
Bus.prototype.addPortOutputNotify = function(start, end, fn)
|
||||
{
|
||||
if (fn !== undefined) {
|
||||
for (var port = start; port <= end; port++) {
|
||||
if (this.aPortOutputNotify[port] !== undefined) {
|
||||
Component.warning("Output port " + str.toHexWord(port) + " already registered");
|
||||
continue;
|
||||
}
|
||||
this.aPortOutputNotify[port] = [fn, false];
|
||||
if (MAXDEBUG) this.log("addPortOutputNotify(" + str.toHexWord(port) + ")");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* addPortOutputTable(component, table, offset)
|
||||
*
|
||||
* Add port output-notification handlers from the specified table (a batch version of addPortOutputNotify)
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {Component} component
|
||||
* @param {Object} table
|
||||
* @param {number} [offset] is an optional port offset
|
||||
*/
|
||||
Bus.prototype.addPortOutputTable = function(component, table, offset)
|
||||
{
|
||||
if (offset === undefined) offset = 0;
|
||||
for (var port in table) {
|
||||
this.addPortOutputNotify(+port + offset, +port + offset, table[port].bind(component));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* addPortOutputWidth(port, size)
|
||||
*
|
||||
* By default, all output ports are 1 byte wide; ports that are wider must call this function.
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} port
|
||||
* @param {number} size (1, 2 or 4)
|
||||
*/
|
||||
Bus.prototype.addPortOutputWidth = function(port, size)
|
||||
{
|
||||
this.aPortOutputWidth[port] = size;
|
||||
};
|
||||
|
||||
/**
|
||||
* checkPortOutputNotify(port, size, data, addrIP)
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} port
|
||||
* @param {number} size
|
||||
* @param {number} data
|
||||
* @param {number} [addrIP] is the IP value at the time of the output
|
||||
*/
|
||||
Bus.prototype.checkPortOutputNotify = function(port, size, data, addrIP)
|
||||
{
|
||||
var shift = 0;
|
||||
|
||||
while (size > 0) {
|
||||
|
||||
var aNotify = this.aPortOutputNotify[port];
|
||||
var sizePort = this.aPortOutputWidth[port] || 1;
|
||||
var maskPort = (sizePort == 1? 0xff : (sizePort == 2? 0xffff : -1));
|
||||
var dataPort = (data >>>= shift) & maskPort;
|
||||
|
||||
/*
|
||||
* TODO: We need to decide what to do about 8-bit I/O to a 16-bit port (ditto for 16-bit I/O
|
||||
* to a 32-bit port). We probably should pass the size through to the aNotify[0] handler,
|
||||
* and let it decide what to do, but I don't feel like changing all the I/O handlers right now.
|
||||
* The good news, at least, is that the 8-bit handlers would not have to do anything special.
|
||||
* This assert will warn us if this is a pressing need.
|
||||
*/
|
||||
this.assert(size >= sizePort);
|
||||
|
||||
if (aNotify !== undefined) {
|
||||
if (aNotify[0]) {
|
||||
aNotify[0](port, dataPort, addrIP);
|
||||
}
|
||||
if (DEBUGGER && this.dbg && this.fPortOutputBreakAll != aNotify[1]) {
|
||||
this.dbg.checkPortOutput(port, size, dataPort);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (DEBUGGER && this.dbg) {
|
||||
this.dbg.messageIO(this, port, dataPort, addrIP);
|
||||
if (this.fPortOutputBreakAll) this.dbg.checkPortOutput(port, size, dataPort);
|
||||
}
|
||||
}
|
||||
|
||||
shift += (sizePort << 3);
|
||||
port += sizePort;
|
||||
size -= sizePort;
|
||||
}
|
||||
this.assert(!size);
|
||||
};
|
||||
|
||||
/**
|
||||
* removePortOutputNotify(start, end)
|
||||
*
|
||||
* Remove port output-notification handler(s) (to be ENABLED later if needed)
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} start address
|
||||
* @param {number} end address
|
||||
*
|
||||
Bus.prototype.removePortOutputNotify = function(start, end)
|
||||
{
|
||||
for (var port = start; port < end; port++) {
|
||||
if (this.aPortOutputNotify[port]) {
|
||||
delete this.aPortOutputNotify[port];
|
||||
}
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
/**
|
||||
* reportError(op, addr, size, fQuiet)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -807,18 +807,45 @@ Memory.prototype = {
|
|||
|
||||
/*
|
||||
* This is the effective definition of afnNone, but we need not fully define it, because setAccess()
|
||||
* uses these defaults when any of the 6 handlers (ie, 3 read handlers and 3 write handlers) are undefined.
|
||||
* uses these defaults when any of the 4 handlers (ie, 2 byte handlers and 2 short handlers) are undefined.
|
||||
*
|
||||
Memory.afnNone = [Memory.prototype.readNone, Memory.prototype.readShortDefault, Memory.prototype.writeNone, Memory.prototype.writeShortDefault];
|
||||
Memory.afnNone = [
|
||||
Memory.prototype.readNone,
|
||||
Memory.prototype.writeNone,
|
||||
Memory.prototype.readShortDefault,
|
||||
Memory.prototype.writeShortDefault
|
||||
];
|
||||
*/
|
||||
Memory.afnNone = [];
|
||||
|
||||
Memory.afnNone = [];
|
||||
Memory.afnMemory = [Memory.prototype.readByteMemory, Memory.prototype.readShortMemory, Memory.prototype.writeByteMemory, Memory.prototype.writeShortMemory];
|
||||
Memory.afnChecked = [Memory.prototype.readByteChecked, Memory.prototype.readShortChecked, Memory.prototype.writeByteChecked, Memory.prototype.writeShortChecked];
|
||||
Memory.afnMemory = [
|
||||
Memory.prototype.readByteMemory,
|
||||
Memory.prototype.writeByteMemory,
|
||||
Memory.prototype.readShortMemory,
|
||||
Memory.prototype.writeShortMemory
|
||||
];
|
||||
|
||||
Memory.afnChecked = [
|
||||
Memory.prototype.readByteChecked,
|
||||
Memory.prototype.writeByteChecked,
|
||||
Memory.prototype.readShortChecked,
|
||||
Memory.prototype.writeShortChecked
|
||||
];
|
||||
|
||||
if (TYPEDARRAYS) {
|
||||
Memory.afnArrayBE = [Memory.prototype.readByteBE, Memory.prototype.readShortBE, Memory.prototype.writeByteBE, Memory.prototype.writeShortBE];
|
||||
Memory.afnArrayLE = [Memory.prototype.readByteLE, Memory.prototype.readShortLE, Memory.prototype.writeByteLE, Memory.prototype.writeShortLE];
|
||||
Memory.afnArrayBE = [
|
||||
Memory.prototype.readByteBE,
|
||||
Memory.prototype.writeByteBE,
|
||||
Memory.prototype.readShortBE,
|
||||
Memory.prototype.writeShortBE
|
||||
];
|
||||
|
||||
Memory.afnArrayLE = [
|
||||
Memory.prototype.readByteLE,
|
||||
Memory.prototype.writeByteLE,
|
||||
Memory.prototype.readShortLE,
|
||||
Memory.prototype.writeShortLE
|
||||
];
|
||||
}
|
||||
|
||||
if (NODE) module.exports = Memory;
|
||||
|
|
|
|||
|
|
@ -76,35 +76,16 @@ function Bus8080(parmsBus, cpu, dbg)
|
|||
this.nBusWidth = parmsBus['busWidth'] || 16;
|
||||
|
||||
/*
|
||||
* Compute all Bus8080 memory block parameters, based on the width of the bus.
|
||||
*
|
||||
* Regarding blockTotal, we want to avoid using block overflow expressions like:
|
||||
*
|
||||
* iBlock < this.nBlockTotal? iBlock : 0
|
||||
*
|
||||
* As long as we know that blockTotal is a power of two (eg, 256 or 0x100, in the case of
|
||||
* nBusWidth == 20 and blockSize == 4096), we can define blockMask as (blockTotal - 1) and
|
||||
* rewrite the previous expression as:
|
||||
*
|
||||
* iBlock & this.nBlockMask
|
||||
*
|
||||
* Bus Property Old hard-coded values (when nBusWidth was always 20)
|
||||
* ------------ ----------------------------------------------------
|
||||
* this.nBusLimit 0xfffff
|
||||
* this.nBusMask [same as busLimit]
|
||||
* this.nBlockSize 4096
|
||||
* this.nBlockLen (this.nBlockSize >> 2)
|
||||
* this.nBlockShift 12
|
||||
* this.nBlockLimit 0xfff
|
||||
* this.nBlockTotal ((this.nBusLimit + this.nBlockSize) / this.nBlockSize) | 0
|
||||
* this.nBlockMask (this.nBlockTotal - 1) [ie, 0xff]
|
||||
*
|
||||
* Note that we choose a nBlockShift value (and thus a physical memory block size) based on "buswidth":
|
||||
* Compute all Bus8080 memory block parameters, based on the width of the bus. The entire
|
||||
* address space is divided into blocks, using a block size that is (hopefully) appropriate to
|
||||
* the bus width. The following table summarizes our simplistic calculations.
|
||||
*
|
||||
* Bus Width Block Shift Block Size
|
||||
* --------- ----------- ----------
|
||||
* 16 bits (64Kb address space): 10 1Kb (64 maximum blocks)
|
||||
* 18 bits (256Kb address space): 11 2Kb (128 maximum blocks)
|
||||
* 20 bits (1Mb address space): 12 4Kb (256 maximum blocks)
|
||||
* 22 bits (4Mb address space): 13 8Kb (512 maximum blocks)
|
||||
* 24 bits (16Mb address space): 14 16Kb (1K maximum blocks)
|
||||
* 32 bits (4Gb address space); 15 32Kb (128K maximum blocks)
|
||||
*
|
||||
|
|
@ -115,7 +96,9 @@ function Bus8080(parmsBus, cpu, dbg)
|
|||
*/
|
||||
this.addrTotal = Math.pow(2, this.nBusWidth);
|
||||
this.nBusLimit = this.nBusMask = (this.addrTotal - 1) | 0;
|
||||
this.nBlockShift = (this.nBusWidth <= 16)? 10 : ((this.nBusWidth <= 20)? 12 : (this.nBusWidth <= 24? 14 : 15));
|
||||
this.nBlockShift = (this.nBusWidth >> 1) + 2;
|
||||
if (this.nBlockShift < 10) this.nBlockShift = 10;
|
||||
if (this.nBlockShift > 15) this.nBlockShift = 15;
|
||||
this.nBlockSize = 1 << this.nBlockShift;
|
||||
this.nBlockLen = this.nBlockSize >> 2;
|
||||
this.nBlockLimit = this.nBlockSize - 1;
|
||||
|
|
@ -173,38 +156,6 @@ Bus8080.ERROR = {
|
|||
REM_MEM_BADRANGE: 5
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {number}
|
||||
*/
|
||||
var BlockInfo;
|
||||
|
||||
/**
|
||||
* This defines the BlockInfo bit fields used by scanMemory() when it creates the aBlocks array.
|
||||
*
|
||||
* @typedef {{
|
||||
* num: BitField,
|
||||
* count: BitField,
|
||||
* btmod: BitField,
|
||||
* type: BitField
|
||||
* }}
|
||||
*/
|
||||
Bus8080.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
|
||||
|
||||
/**
|
||||
* BusInfo8080 object definition (returned by scanMemory())
|
||||
*
|
||||
* cbTotal: total bytes allocated
|
||||
* cBlocks: total Memory blocks allocated
|
||||
* aBlocks: array of allocated Memory block numbers
|
||||
*
|
||||
* @typedef {{
|
||||
* cbTotal: number,
|
||||
* cBlocks: number,
|
||||
* aBlocks: Array.<BlockInfo>
|
||||
* }}
|
||||
*/
|
||||
var BusInfo8080;
|
||||
|
||||
/**
|
||||
* initMemory()
|
||||
*
|
||||
|
|
@ -361,6 +312,42 @@ Bus8080.prototype.cleanMemory = function(addr, size)
|
|||
return fClean;
|
||||
};
|
||||
|
||||
/*
|
||||
* Data types used by scanMemory()
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {number}
|
||||
*/
|
||||
var BlockInfo;
|
||||
|
||||
/**
|
||||
* This defines the BlockInfo bit fields used by scanMemory() when it creates the aBlocks array.
|
||||
*
|
||||
* @typedef {{
|
||||
* num: BitField,
|
||||
* count: BitField,
|
||||
* btmod: BitField,
|
||||
* type: BitField
|
||||
* }}
|
||||
*/
|
||||
Bus8080.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
|
||||
|
||||
/**
|
||||
* BusInfo8080 object definition (returned by scanMemory())
|
||||
*
|
||||
* cbTotal: total bytes allocated
|
||||
* cBlocks: total Memory blocks allocated
|
||||
* aBlocks: array of allocated Memory block numbers
|
||||
*
|
||||
* @typedef {{
|
||||
* cbTotal: number,
|
||||
* cBlocks: number,
|
||||
* aBlocks: Array.<BlockInfo>
|
||||
* }}
|
||||
*/
|
||||
var BusInfo8080;
|
||||
|
||||
/**
|
||||
* scanMemory(info, addr, size)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -384,11 +384,11 @@ Memory8080.prototype = {
|
|||
setReadAccess: function(afn, fDirect) {
|
||||
if (!fDirect || !this.cReadBreakpoints) {
|
||||
this.readByte = afn[0] || this.readNone;
|
||||
this.readShort = afn[1] || this.readShortDefault;
|
||||
this.readShort = afn[2] || this.readShortDefault;
|
||||
}
|
||||
if (fDirect || fDirect === undefined) {
|
||||
this.readByteDirect = afn[0] || this.readNone;
|
||||
this.readShortDirect = afn[1] || this.readShortDefault;
|
||||
this.readShortDirect = afn[2] || this.readShortDefault;
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
|
@ -400,11 +400,11 @@ Memory8080.prototype = {
|
|||
*/
|
||||
setWriteAccess: function(afn, fDirect) {
|
||||
if (!fDirect || !this.cWriteBreakpoints) {
|
||||
this.writeByte = !this.fReadOnly && afn[2] || this.writeNone;
|
||||
this.writeByte = !this.fReadOnly && afn[1] || this.writeNone;
|
||||
this.writeShort = !this.fReadOnly && afn[3] || this.writeShortDefault;
|
||||
}
|
||||
if (fDirect || fDirect === undefined) {
|
||||
this.writeByteDirect = afn[2] || this.writeNone;
|
||||
this.writeByteDirect = afn[1] || this.writeNone;
|
||||
this.writeShortDirect = afn[3] || this.writeShortDefault;
|
||||
}
|
||||
},
|
||||
|
|
@ -807,18 +807,45 @@ Memory8080.prototype = {
|
|||
|
||||
/*
|
||||
* This is the effective definition of afnNone, but we need not fully define it, because setAccess()
|
||||
* uses these defaults when any of the 6 handlers (ie, 3 read handlers and 3 write handlers) are undefined.
|
||||
* uses these defaults when any of the 4 handlers (ie, 2 byte handlers and 2 short handlers) are undefined.
|
||||
*
|
||||
Memory8080.afnNone = [Memory8080.prototype.readNone, Memory8080.prototype.readShortDefault, Memory8080.prototype.writeNone, Memory8080.prototype.writeShortDefault];
|
||||
Memory8080.afnNone = [
|
||||
Memory8080.prototype.readNone,
|
||||
Memory8080.prototype.writeNone,
|
||||
Memory8080.prototype.readShortDefault,
|
||||
Memory8080.prototype.writeShortDefault
|
||||
];
|
||||
*/
|
||||
Memory8080.afnNone = [];
|
||||
|
||||
Memory8080.afnNone = [];
|
||||
Memory8080.afnMemory = [Memory8080.prototype.readByteMemory, Memory8080.prototype.readShortMemory, Memory8080.prototype.writeByteMemory, Memory8080.prototype.writeShortMemory];
|
||||
Memory8080.afnChecked = [Memory8080.prototype.readByteChecked, Memory8080.prototype.readShortChecked, Memory8080.prototype.writeByteChecked, Memory8080.prototype.writeShortChecked];
|
||||
Memory8080.afnMemory = [
|
||||
Memory8080.prototype.readByteMemory,
|
||||
Memory8080.prototype.writeByteMemory,
|
||||
Memory8080.prototype.readShortMemory,
|
||||
Memory8080.prototype.writeShortMemory
|
||||
];
|
||||
|
||||
Memory8080.afnChecked = [
|
||||
Memory8080.prototype.readByteChecked,
|
||||
Memory8080.prototype.writeByteChecked,
|
||||
Memory8080.prototype.readShortChecked,
|
||||
Memory8080.prototype.writeShortChecked
|
||||
];
|
||||
|
||||
if (TYPEDARRAYS) {
|
||||
Memory8080.afnArrayBE = [Memory8080.prototype.readByteBE, Memory8080.prototype.readShortBE, Memory8080.prototype.writeByteBE, Memory8080.prototype.writeShortBE];
|
||||
Memory8080.afnArrayLE = [Memory8080.prototype.readByteLE, Memory8080.prototype.readShortLE, Memory8080.prototype.writeByteLE, Memory8080.prototype.writeShortLE];
|
||||
Memory8080.afnArrayBE = [
|
||||
Memory8080.prototype.readByteBE,
|
||||
Memory8080.prototype.writeByteBE,
|
||||
Memory8080.prototype.readShortBE,
|
||||
Memory8080.prototype.writeShortBE
|
||||
];
|
||||
|
||||
Memory8080.afnArrayLE = [
|
||||
Memory8080.prototype.readByteLE,
|
||||
Memory8080.prototype.writeByteLE,
|
||||
Memory8080.prototype.readShortLE,
|
||||
Memory8080.prototype.writeShortLE
|
||||
];
|
||||
}
|
||||
|
||||
if (NODE) module.exports = Memory8080;
|
||||
|
|
|
|||
|
|
@ -259,38 +259,6 @@ Bus.ERROR = {
|
|||
REM_MEM_BADRANGE: 5
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {number}
|
||||
*/
|
||||
var BlockInfo;
|
||||
|
||||
/**
|
||||
* This defines the BlockInfo bit fields used by scanMemory() when it creates the aBlocks array.
|
||||
*
|
||||
* @typedef {{
|
||||
* num: BitField,
|
||||
* count: BitField,
|
||||
* btmod: BitField,
|
||||
* type: BitField
|
||||
* }}
|
||||
*/
|
||||
Bus.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
|
||||
|
||||
/**
|
||||
* BusInfo object definition (returned by scanMemory())
|
||||
*
|
||||
* cbTotal: total bytes allocated
|
||||
* cBlocks: total Memory blocks allocated
|
||||
* aBlocks: array of allocated Memory block numbers
|
||||
*
|
||||
* @typedef {{
|
||||
* cbTotal: number,
|
||||
* cBlocks: number,
|
||||
* aBlocks: Array.<BlockInfo>
|
||||
* }}
|
||||
*/
|
||||
var BusInfo;
|
||||
|
||||
/**
|
||||
* initMemory()
|
||||
*
|
||||
|
|
@ -463,6 +431,42 @@ Bus.prototype.cleanMemory = function(addr, size)
|
|||
return fClean;
|
||||
};
|
||||
|
||||
/*
|
||||
* Data types used by scanMemory()
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {number}
|
||||
*/
|
||||
var BlockInfo;
|
||||
|
||||
/**
|
||||
* This defines the BlockInfo bit fields used by scanMemory() when it creates the aBlocks array.
|
||||
*
|
||||
* @typedef {{
|
||||
* num: BitField,
|
||||
* count: BitField,
|
||||
* btmod: BitField,
|
||||
* type: BitField
|
||||
* }}
|
||||
*/
|
||||
Bus.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
|
||||
|
||||
/**
|
||||
* BusInfo object definition (returned by scanMemory())
|
||||
*
|
||||
* cbTotal: total bytes allocated
|
||||
* cBlocks: total Memory blocks allocated
|
||||
* aBlocks: array of allocated Memory block numbers
|
||||
*
|
||||
* @typedef {{
|
||||
* cbTotal: number,
|
||||
* cBlocks: number,
|
||||
* aBlocks: Array.<BlockInfo>
|
||||
* }}
|
||||
*/
|
||||
var BusInfo;
|
||||
|
||||
/**
|
||||
* scanMemory(info, addr, size)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -417,7 +417,19 @@ Memory.prototype = {
|
|||
/**
|
||||
* setAccess(afn, fDirect)
|
||||
*
|
||||
* If no function table is specified, a default is selected based on the Memory type.
|
||||
* The afn parameter should be a 6-entry function table containing two byte handlers, two
|
||||
* short handlers, and two long handlers. See the static afnMemory table for an example.
|
||||
*
|
||||
* If no function table is specified, a default is selected based on the Memory type;
|
||||
* similarly, any undefined entries in the table are filled with default handlers that fall
|
||||
* back to the byte handlers, and if one or both byte handlers are undefined, they default
|
||||
* to handlers that simply ignore the access.
|
||||
*
|
||||
* fDirect indicates that both the default AND the direct handlers should be updated. Direct
|
||||
* handlers normally match the default handlers, except when "checked" handlers are installed;
|
||||
* this allows "checked" handlers to know where to dispatch the call after performing checks.
|
||||
* Examples of checks are read/write breakpoints, but it's really up to the Debugger to decide
|
||||
* what the check consists of.
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {Array.<function()>} [afn] function table
|
||||
|
|
@ -448,13 +460,13 @@ Memory.prototype = {
|
|||
setReadAccess: function(afn, fDirect) {
|
||||
if (!fDirect || !this.cReadBreakpoints) {
|
||||
this.readByte = afn[0] || this.readNone;
|
||||
this.readShort = afn[1] || this.readShortDefault;
|
||||
this.readLong = afn[2] || this.readLongDefault;
|
||||
this.readShort = afn[2] || this.readShortDefault;
|
||||
this.readLong = afn[4] || this.readLongDefault;
|
||||
}
|
||||
if (fDirect || fDirect === undefined) {
|
||||
this.readByteDirect = afn[0] || this.readNone;
|
||||
this.readShortDirect = afn[1] || this.readShortDefault;
|
||||
this.readLongDirect = afn[2] || this.readLongDefault;
|
||||
this.readShortDirect = afn[2] || this.readShortDefault;
|
||||
this.readLongDirect = afn[4] || this.readLongDefault;
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
|
@ -466,13 +478,13 @@ Memory.prototype = {
|
|||
*/
|
||||
setWriteAccess: function(afn, fDirect) {
|
||||
if (!fDirect || !this.cWriteBreakpoints) {
|
||||
this.writeByte = !this.fReadOnly && afn[3] || this.writeNone;
|
||||
this.writeShort = !this.fReadOnly && afn[4] || this.writeShortDefault;
|
||||
this.writeByte = !this.fReadOnly && afn[1] || this.writeNone;
|
||||
this.writeShort = !this.fReadOnly && afn[3] || this.writeShortDefault;
|
||||
this.writeLong = !this.fReadOnly && afn[5] || this.writeLongDefault;
|
||||
}
|
||||
if (fDirect || fDirect === undefined) {
|
||||
this.writeByteDirect = afn[3] || this.writeNone;
|
||||
this.writeShortDirect = afn[4] || this.writeShortDefault;
|
||||
this.writeByteDirect = afn[1] || this.writeNone;
|
||||
this.writeShortDirect = afn[3] || this.writeShortDefault;
|
||||
this.writeLongDirect = afn[5] || this.writeLongDefault;
|
||||
}
|
||||
},
|
||||
|
|
@ -1507,25 +1519,85 @@ Memory.prototype = {
|
|||
};
|
||||
|
||||
/*
|
||||
* This is the effective definition of afnNone, but we need not fully define it, because setAccess()
|
||||
* uses these defaults when any of the 6 handlers (ie, 3 read handlers and 3 write handlers) are undefined.
|
||||
* This is the effective definition of afnNone, but we need not fully define it, because setAccess() uses these
|
||||
* defaults when any of the 6 handlers (ie, 2 byte handlers, 2 short handlers, and 2 long handlers) are undefined.
|
||||
*
|
||||
Memory.afnNone = [Memory.prototype.readNone, Memory.prototype.readShortDefault, Memory.prototype.readLongDefault, Memory.prototype.writeNone, Memory.prototype.writeShortDefault, Memory.prototype.writeLongDefault];
|
||||
Memory.afnNone = [
|
||||
Memory.prototype.readNone,
|
||||
Memory.prototype.writeNone,
|
||||
Memory.prototype.readShortDefault,
|
||||
Memory.prototype.writeShortDefault,
|
||||
Memory.prototype.readLongDefault,
|
||||
Memory.prototype.writeLongDefault
|
||||
];
|
||||
*/
|
||||
Memory.afnNone = [];
|
||||
|
||||
Memory.afnNone = [];
|
||||
Memory.afnMemory = [Memory.prototype.readByteMemory, Memory.prototype.readShortMemory, Memory.prototype.readLongMemory, Memory.prototype.writeByteMemory, Memory.prototype.writeShortMemory, Memory.prototype.writeLongMemory];
|
||||
Memory.afnChecked = [Memory.prototype.readByteChecked, Memory.prototype.readShortChecked, Memory.prototype.readLongChecked, Memory.prototype.writeByteChecked, Memory.prototype.writeShortChecked, Memory.prototype.writeLongChecked];
|
||||
Memory.afnMemory = [
|
||||
Memory.prototype.readByteMemory,
|
||||
Memory.prototype.writeByteMemory,
|
||||
Memory.prototype.readShortMemory,
|
||||
Memory.prototype.writeShortMemory,
|
||||
Memory.prototype.readLongMemory,
|
||||
Memory.prototype.writeLongMemory
|
||||
];
|
||||
|
||||
Memory.afnChecked = [
|
||||
Memory.prototype.readByteChecked,
|
||||
Memory.prototype.writeByteChecked,
|
||||
Memory.prototype.readShortChecked,
|
||||
Memory.prototype.writeShortChecked,
|
||||
Memory.prototype.readLongChecked,
|
||||
Memory.prototype.writeLongChecked
|
||||
];
|
||||
|
||||
if (PAGEBLOCKS) {
|
||||
Memory.afnPaged = [Memory.prototype.readBytePaged, Memory.prototype.readShortPaged, Memory.prototype.readLongPaged, Memory.prototype.writeBytePaged, Memory.prototype.writeShortPaged, Memory.prototype.writeLongPaged];
|
||||
Memory.afnUnpaged = [Memory.prototype.readByteUnpaged, Memory.prototype.readShortUnpaged, Memory.prototype.readLongUnpaged, Memory.prototype.writeByteUnpaged, Memory.prototype.writeShortUnpaged, Memory.prototype.writeLongUnpaged];
|
||||
Memory.afnPaged = [
|
||||
Memory.prototype.readBytePaged,
|
||||
Memory.prototype.writeBytePaged,
|
||||
Memory.prototype.readShortPaged,
|
||||
Memory.prototype.writeShortPaged,
|
||||
Memory.prototype.readLongPaged,
|
||||
Memory.prototype.writeLongPaged
|
||||
];
|
||||
|
||||
Memory.afnUnpaged = [
|
||||
Memory.prototype.readByteUnpaged,
|
||||
Memory.prototype.writeByteUnpaged,
|
||||
Memory.prototype.readShortUnpaged,
|
||||
Memory.prototype.writeShortUnpaged,
|
||||
Memory.prototype.readLongUnpaged,
|
||||
Memory.prototype.writeLongUnpaged
|
||||
];
|
||||
}
|
||||
|
||||
if (TYPEDARRAYS) {
|
||||
Memory.afnArrayBE = [Memory.prototype.readByteBE, Memory.prototype.readShortBE, Memory.prototype.readLongBE, Memory.prototype.writeByteBE, Memory.prototype.writeShortBE, Memory.prototype.writeLongBE];
|
||||
Memory.afnArrayLE = [Memory.prototype.readByteLE, Memory.prototype.readShortLE, Memory.prototype.readLongLE, Memory.prototype.writeByteLE, Memory.prototype.writeShortLE, Memory.prototype.writeLongLE];
|
||||
Memory.afnPagedLE = [Memory.prototype.readBytePLE, Memory.prototype.readShortPLE, Memory.prototype.readLongPLE, Memory.prototype.writeBytePLE, Memory.prototype.writeShortPLE, Memory.prototype.writeLongPLE];
|
||||
Memory.afnArrayBE = [
|
||||
Memory.prototype.readByteBE,
|
||||
Memory.prototype.writeByteBE,
|
||||
Memory.prototype.readShortBE,
|
||||
Memory.prototype.writeShortBE,
|
||||
Memory.prototype.readLongBE,
|
||||
Memory.prototype.writeLongBE
|
||||
];
|
||||
|
||||
Memory.afnArrayLE = [
|
||||
Memory.prototype.readByteLE,
|
||||
Memory.prototype.writeByteLE,
|
||||
Memory.prototype.readShortLE,
|
||||
Memory.prototype.writeShortLE,
|
||||
Memory.prototype.readLongLE,
|
||||
Memory.prototype.writeLongLE
|
||||
];
|
||||
|
||||
Memory.afnPagedLE = [
|
||||
Memory.prototype.readBytePLE,
|
||||
Memory.prototype.writeBytePLE,
|
||||
Memory.prototype.readShortPLE,
|
||||
Memory.prototype.writeShortPLE,
|
||||
Memory.prototype.readLongPLE,
|
||||
Memory.prototype.writeLongPLE
|
||||
];
|
||||
}
|
||||
|
||||
if (NODE) module.exports = Memory;
|
||||
|
|
|
|||
|
|
@ -2797,7 +2797,7 @@ Card.prototype.setMemoryAccess = function(nAccess)
|
|||
}
|
||||
if (!this.afnAccess) this.afnAccess = new Array(6);
|
||||
this.afnAccess[0] = fnReadByte;
|
||||
this.afnAccess[3] = fnWriteByte;
|
||||
this.afnAccess[1] = fnWriteByte;
|
||||
this.nAccess = nAccess;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -73,30 +73,9 @@ function BusPDP11(parmsBus, cpu, dbg)
|
|||
this.nBusWidth = parmsBus['busWidth'] || 16;
|
||||
|
||||
/*
|
||||
* Compute all BusPDP11 memory block parameters, based on the width of the bus.
|
||||
*
|
||||
* Regarding blockTotal, we want to avoid using block overflow expressions like:
|
||||
*
|
||||
* iBlock < this.nBlockTotal? iBlock : 0
|
||||
*
|
||||
* As long as we know that blockTotal is a power of two (eg, 256 or 0x100, in the case of
|
||||
* nBusWidth == 20 and blockSize == 4096), we can define blockMask as (blockTotal - 1) and
|
||||
* rewrite the previous expression as:
|
||||
*
|
||||
* iBlock & this.nBlockMask
|
||||
*
|
||||
* Bus Property Old hard-coded values (when nBusWidth was always 20)
|
||||
* ------------ ----------------------------------------------------
|
||||
* this.nBusLimit 0xfffff
|
||||
* this.nBusMask [same as busLimit]
|
||||
* this.nBlockSize 4096
|
||||
* this.nBlockLen (this.nBlockSize >> 2)
|
||||
* this.nBlockShift 12
|
||||
* this.nBlockLimit 0xfff
|
||||
* this.nBlockTotal ((this.nBusLimit + this.nBlockSize) / this.nBlockSize) | 0
|
||||
* this.nBlockMask (this.nBlockTotal - 1) [ie, 0xff]
|
||||
*
|
||||
* Note that we choose a nBlockShift value (and thus a physical memory block size) based on "buswidth":
|
||||
* Compute all BusPDP11 memory block parameters, based on the width of the bus. The entire
|
||||
* address space is divided into blocks, using a block size that is (hopefully) appropriate to
|
||||
* the bus width. The following table summarizes our simplistic calculations.
|
||||
*
|
||||
* Bus Width Block Shift Block Size
|
||||
* --------- ----------- ----------
|
||||
|
|
@ -125,20 +104,24 @@ function BusPDP11(parmsBus, cpu, dbg)
|
|||
this.assert(this.nBlockMask <= BusPDP11.BlockInfo.num.mask);
|
||||
|
||||
/*
|
||||
* Lists of I/O notification functions: aIONotify are arrays, indexed by address, where each entry
|
||||
* contains an array:
|
||||
* aIONotify is an array (ie, a hash) of I/O notification handlers, indexed by address, where each
|
||||
* entry contains an array:
|
||||
*
|
||||
* [0]: registered function to call for read
|
||||
* [1]: registered function to call for write
|
||||
* [0]: readByte(addr)
|
||||
* [1]: writeByte()
|
||||
* [2]: readShort()
|
||||
* [3]: writeShort()
|
||||
*
|
||||
* The registered function is called with the address, and if the access was triggered by the CPU,
|
||||
* the program counter (PC) at the point of access.
|
||||
* Each of these 4-element arrays are similar to the memory access arrays assigned to entire Memory
|
||||
* blocks, but these handlers generally target a specific address (or handful of addresses), while
|
||||
* Memory access handlers must service the entire block; see the setAccess() function in the Memory
|
||||
* component for details.
|
||||
*
|
||||
* UPDATE: The Debugger now piggy-backs on these arrays to indicate addresses for which it wants
|
||||
* UPDATE: The Debugger wants to piggy-back on these arrays to indicate addresses for which it wants
|
||||
* notification. In those cases, the registered component/function elements may or may not be set,
|
||||
* but the following additional element will be set:
|
||||
*
|
||||
* [2]: true to break on I/O, false to ignore I/O
|
||||
* [4]: true to break on I/O, false to ignore I/O
|
||||
*
|
||||
* The false case is important if fIOBreakAll is set, because it allows the Debugger to selectively
|
||||
* ignore specific addresses.
|
||||
|
|
@ -172,38 +155,6 @@ BusPDP11.ERROR = {
|
|||
REMOVE_INVALID: 5
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {number}
|
||||
*/
|
||||
var BlockInfo;
|
||||
|
||||
/**
|
||||
* This defines the BlockInfo bit fields used by scanMemory() when it creates the aBlocks array.
|
||||
*
|
||||
* @typedef {{
|
||||
* num: BitField,
|
||||
* count: BitField,
|
||||
* btmod: BitField,
|
||||
* type: BitField
|
||||
* }}
|
||||
*/
|
||||
BusPDP11.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
|
||||
|
||||
/**
|
||||
* BusInfoPDP11 object definition (returned by scanMemory())
|
||||
*
|
||||
* cbTotal: total bytes allocated
|
||||
* cBlocks: total Memory blocks allocated
|
||||
* aBlocks: array of allocated Memory block numbers
|
||||
*
|
||||
* @typedef {{
|
||||
* cbTotal: number,
|
||||
* cBlocks: number,
|
||||
* aBlocks: Array.<BlockInfo>
|
||||
* }}
|
||||
*/
|
||||
var BusInfoPDP11;
|
||||
|
||||
BusPDP11.controller = {
|
||||
|
||||
/**
|
||||
|
|
@ -211,26 +162,13 @@ BusPDP11.controller = {
|
|||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} [addr]
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readIOPageByte: function(off, addr)
|
||||
{
|
||||
return 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* readIOPageWord(off, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} [addr]
|
||||
* @return {number}
|
||||
*/
|
||||
readIOPageWord: function(off, addr)
|
||||
{
|
||||
var afn = this.controller.aIONotify[off];
|
||||
return afn? afn[0](addr) : 0;
|
||||
return afn && afn[0]? afn[0](addr) : 0;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -239,24 +177,58 @@ BusPDP11.controller = {
|
|||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} b (which should already be pre-masked to 8 bits)
|
||||
* @param {number} [addr]
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeIOPageByte: function(off, b, addr)
|
||||
{
|
||||
var afn = this.controller.aIONotify[off];
|
||||
if (afn && afn[1]) {
|
||||
afn[1](addr, b);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* writeIOPageWord(off, w, addr)
|
||||
* readIOPageShort(off, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readIOPageShort: function(off, addr)
|
||||
{
|
||||
Component.assert(!(addr & 1)); // unaligned addresses should be getting trapped at a higher level
|
||||
var afn = this.controller.aIONotify[off];
|
||||
if (afn) {
|
||||
if (afn[2]) {
|
||||
return afn[2](addr);
|
||||
} else if (afn[0]) {
|
||||
return afn[0](addr) | (afn[0](addr + 1) << 8);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* writeIOPageShort(off, w, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} w (which should already be pre-masked to 16 bits)
|
||||
* @param {number} [addr]
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeIOPageWord: function(off, w, addr)
|
||||
writeIOPageShort: function(off, w, addr)
|
||||
{
|
||||
Component.assert(!(addr & 1)); // unaligned addresses should be getting trapped at a higher level
|
||||
var afn = this.controller.aIONotify[off];
|
||||
return afn? afn[1](w, addr) : 0;
|
||||
if (afn) {
|
||||
if (afn[3]) {
|
||||
afn[3](addr, w);
|
||||
} else if (afn[1]) {
|
||||
afn[1](addr, w & 0xff);
|
||||
afn[1](addr + 1, w >> 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -277,9 +249,9 @@ BusPDP11.prototype.initMemory = function()
|
|||
}
|
||||
this.afnIOPage = new Array(4);
|
||||
this.afnIOPage[0] = BusPDP11.controller.readIOPageByte;
|
||||
this.afnIOPage[1] = BusPDP11.controller.readIOPageWord;
|
||||
this.afnIOPage[1] = BusPDP11.controller.readIOPageShort;
|
||||
this.afnIOPage[2] = BusPDP11.controller.writeIOPageByte;
|
||||
this.afnIOPage[3] = BusPDP11.controller.writeIOPageWord;
|
||||
this.afnIOPage[3] = BusPDP11.controller.writeIOPageShort;
|
||||
this.addrIOPage = this.addrTotal - BusPDP11.IOPAGE_LENGTH;
|
||||
this.addMemory(this.addrIOPage, BusPDP11.IOPAGE_LENGTH, MemoryPDP11.TYPE.CONTROLLER, this);
|
||||
};
|
||||
|
|
@ -451,16 +423,52 @@ BusPDP11.prototype.cleanMemory = function(addr, size)
|
|||
return fClean;
|
||||
};
|
||||
|
||||
/*
|
||||
* Data types used by scanMemory()
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {number} BlockInfo
|
||||
*/
|
||||
var BlockInfo;
|
||||
|
||||
/**
|
||||
* This defines the BlockInfo bit fields used by scanMemory() when it creates the aBlocks array.
|
||||
*
|
||||
* @typedef {{
|
||||
* num: BitField,
|
||||
* count: BitField,
|
||||
* btmod: BitField,
|
||||
* type: BitField
|
||||
* }}
|
||||
*/
|
||||
BusPDP11.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
|
||||
|
||||
/**
|
||||
* BusInfoPDP11 object definition (returned by scanMemory())
|
||||
*
|
||||
* cbTotal: total bytes allocated
|
||||
* cBlocks: total Memory blocks allocated
|
||||
* aBlocks: array of allocated Memory block numbers
|
||||
*
|
||||
* @typedef {{
|
||||
* cbTotal: number,
|
||||
* cBlocks: number,
|
||||
* aBlocks: Array.<BlockInfo>
|
||||
* }} BusInfoPDP11
|
||||
*/
|
||||
var BusInfoPDP11;
|
||||
|
||||
/**
|
||||
* scanMemory(info, addr, size)
|
||||
*
|
||||
* Returns a BusInfoPDP11 object for the specified address range.
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {Object} [info] previous BusInfoPDP11, if any
|
||||
* @param {BusInfoPDP11} [info] previous BusInfoPDP11, if any
|
||||
* @param {number} [addr] starting address of range (0 if none provided)
|
||||
* @param {number} [size] size of range, in bytes (up to end of address space if none provided)
|
||||
* @return {Object} updated info (or new info if no previous info provided)
|
||||
* @return {BusInfoPDP11} updated info (or new info if no previous info provided)
|
||||
*/
|
||||
BusPDP11.prototype.scanMemory = function(info, addr, size)
|
||||
{
|
||||
|
|
@ -640,7 +648,7 @@ BusPDP11.prototype.getByteDirect = function(addr)
|
|||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr is a physical address
|
||||
* @return {number} word (16-bit) value at that address
|
||||
* @return {number} short (16-bit) value at that address
|
||||
*/
|
||||
BusPDP11.prototype.getShort = function(addr)
|
||||
{
|
||||
|
|
@ -659,7 +667,7 @@ BusPDP11.prototype.getShort = function(addr)
|
|||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr is a physical address
|
||||
* @return {number} word (16-bit) value at that address
|
||||
* @return {number} short (16-bit) value at that address
|
||||
*/
|
||||
BusPDP11.prototype.getShortDirect = function(addr)
|
||||
{
|
||||
|
|
@ -703,7 +711,7 @@ BusPDP11.prototype.setByteDirect = function(addr, b)
|
|||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr is a physical address
|
||||
* @param {number} w is the word (16-bit) value to write (we truncate it to 16 bits to be safe)
|
||||
* @param {number} w is the short (16-bit) value to write (we truncate it to 16 bits to be safe)
|
||||
*/
|
||||
BusPDP11.prototype.setShort = function(addr, w)
|
||||
{
|
||||
|
|
@ -725,7 +733,7 @@ BusPDP11.prototype.setShort = function(addr, w)
|
|||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr is a physical address
|
||||
* @param {number} w is the word (16-bit) value to write (we truncate it to 16 bits to be safe)
|
||||
* @param {number} w is the short (16-bit) value to write (we truncate it to 16 bits to be safe)
|
||||
*/
|
||||
BusPDP11.prototype.setShortDirect = function(addr, w)
|
||||
{
|
||||
|
|
@ -860,18 +868,21 @@ BusPDP11.prototype.restoreMemory = function(a)
|
|||
};
|
||||
|
||||
/**
|
||||
* addIOHandlers(start, end, fnRead, fnWrite)
|
||||
* addIOHandlers(start, end, fnReadByte, fnWriteByte, fnReadShort, fnWriteShort)
|
||||
*
|
||||
* Add I/O notification handlers to the list of such handlers. The start and end addresses are typically
|
||||
* relative to the starting IOPAGE address, but they can also be absolute.
|
||||
* Add I/O notification handlers to the master list (aIONotify). The start and end addresses are typically
|
||||
* relative to the starting IOPAGE address, but they can also be absolute; we simply mask all addresses with
|
||||
* (IOPAGE_LENGTH - 1).
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} start address
|
||||
* @param {number} end address
|
||||
* @param {function(number)|null|undefined} fnRead is called with the read address whenever a read occurs
|
||||
* @param {function(number,number)|null|undefined} fnWrite is called with the data and write address whenever a write occurs
|
||||
* @param {function(number)|null|undefined} fnReadByte
|
||||
* @param {function(number,number)|null|undefined} fnWriteByte
|
||||
* @param {function(number)|null|undefined} fnReadShort
|
||||
* @param {function(number,number)|null|undefined} fnWriteShort
|
||||
*/
|
||||
BusPDP11.prototype.addIOHandlers = function(start, end, fnRead, fnWrite)
|
||||
BusPDP11.prototype.addIOHandlers = function(start, end, fnReadByte, fnWriteByte, fnReadShort, fnWriteShort)
|
||||
{
|
||||
for (var addr = start; addr <= end; addr += 2) {
|
||||
var off = addr & (BusPDP11.IOPAGE_LENGTH - 1);
|
||||
|
|
@ -883,7 +894,7 @@ BusPDP11.prototype.addIOHandlers = function(start, end, fnRead, fnWrite)
|
|||
Component.warning("I/O address already registered: " + str.toHexLong(this.addrIOPage + off));
|
||||
continue;
|
||||
}
|
||||
this.aIONotify[off] = [fnRead, fnWrite, false];
|
||||
this.aIONotify[off] = [fnReadByte, fnWriteByte, fnReadShort, fnWriteShort, false];
|
||||
if (MAXDEBUG) this.log("addIOHandlers(" + str.toHexLong(this.addrIOPage + off) + ")");
|
||||
}
|
||||
};
|
||||
|
|
@ -901,9 +912,11 @@ BusPDP11.prototype.addIOTable = function(component, table)
|
|||
{
|
||||
for (var port in table) {
|
||||
var afn = table[port];
|
||||
var fnRead = afn[0]? afn[0].bind(component) : null;
|
||||
var fnWrite = afn[1]? afn[1].bind(component) : null;
|
||||
this.addIOHandlers(+port, +port, fnRead, fnWrite);
|
||||
var fnReadByte = afn[0]? afn[0].bind(component) : null;
|
||||
var fnWriteByte = afn[1]? afn[1].bind(component) : null;
|
||||
var fnReadShort = afn[2]? afn[2].bind(component) : null;
|
||||
var fnWriteShort = afn[3]? afn[3].bind(component) : null;
|
||||
this.addIOHandlers(+port, +port, fnReadByte, fnWriteByte, fnReadShort, fnWriteShort);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ DevicePDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
* readPSW(addr)
|
||||
*
|
||||
* @this {DevicePDP11}
|
||||
* @param {number} [addr]
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
DevicePDP11.prototype.readPSW = function(addr)
|
||||
|
|
@ -102,7 +102,7 @@ DevicePDP11.prototype.readPSW = function(addr)
|
|||
*
|
||||
* @this {DevicePDP11}
|
||||
* @param {number} data
|
||||
* @param {number} [addr]
|
||||
* @param {number} addr
|
||||
*/
|
||||
DevicePDP11.prototype.writePSW = function(data, addr)
|
||||
{
|
||||
|
|
@ -110,7 +110,7 @@ DevicePDP11.prototype.writePSW = function(data, addr)
|
|||
};
|
||||
|
||||
DevicePDP11.UNIBUS_TABLE = {};
|
||||
DevicePDP11.UNIBUS_TABLE[DevicePDP11.ADDR_PSW] = [DevicePDP11.prototype.readPSW, DevicePDP11.prototype.writePSW];
|
||||
DevicePDP11.UNIBUS_TABLE[DevicePDP11.ADDR_PSW] = [null, null, DevicePDP11.prototype.readPSW, DevicePDP11.prototype.writePSW];
|
||||
|
||||
/**
|
||||
* DevicePDP11.init()
|
||||
|
|
|
|||
|
|
@ -383,7 +383,19 @@ MemoryPDP11.prototype = {
|
|||
/**
|
||||
* setAccess(afn, fDirect)
|
||||
*
|
||||
* If no function table is specified, a default is selected based on the MemoryPDP11 type.
|
||||
* The afn parameter should be a 4-entry function table containing two byte handlers and
|
||||
* two short handlers. See the static afnMemory table for an example.
|
||||
*
|
||||
* If no function table is specified, a default is selected based on the MemoryPDP11 type;
|
||||
* similarly, any undefined entries in the table are filled with default handlers that fall
|
||||
* back to the byte handlers, and if one or both byte handlers are undefined, they default
|
||||
* to handlers that simply ignore the access.
|
||||
*
|
||||
* fDirect indicates that both the default AND the direct handlers should be updated. Direct
|
||||
* handlers normally match the default handlers, except when "checked" handlers are installed;
|
||||
* this allows "checked" handlers to know where to dispatch the call after performing checks.
|
||||
* Examples of checks are read/write breakpoints, but it's really up to the Debugger to decide
|
||||
* what the check consists of.
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {Array.<function()>} [afn] function table
|
||||
|
|
@ -407,11 +419,11 @@ MemoryPDP11.prototype = {
|
|||
setReadAccess: function(afn, fDirect) {
|
||||
if (!fDirect || !this.cReadBreakpoints) {
|
||||
this.readByte = afn[0] || this.readNone;
|
||||
this.readShort = afn[1] || this.readShortDefault;
|
||||
this.readShort = afn[2] || this.readShortDefault;
|
||||
}
|
||||
if (fDirect || fDirect === undefined) {
|
||||
this.readByteDirect = afn[0] || this.readNone;
|
||||
this.readShortDirect = afn[1] || this.readShortDefault;
|
||||
this.readShortDirect = afn[2] || this.readShortDefault;
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
|
@ -423,11 +435,11 @@ MemoryPDP11.prototype = {
|
|||
*/
|
||||
setWriteAccess: function(afn, fDirect) {
|
||||
if (!fDirect || !this.cWriteBreakpoints) {
|
||||
this.writeByte = !this.fReadOnly && afn[2] || this.writeNone;
|
||||
this.writeByte = !this.fReadOnly && afn[1] || this.writeNone;
|
||||
this.writeShort = !this.fReadOnly && afn[3] || this.writeShortDefault;
|
||||
}
|
||||
if (fDirect || fDirect === undefined) {
|
||||
this.writeByteDirect = afn[2] || this.writeNone;
|
||||
this.writeByteDirect = afn[1] || this.writeNone;
|
||||
this.writeShortDirect = afn[3] || this.writeShortDefault;
|
||||
}
|
||||
},
|
||||
|
|
@ -830,43 +842,43 @@ MemoryPDP11.prototype = {
|
|||
|
||||
/*
|
||||
* This is the effective definition of afnNone, but we need not fully define it, because setAccess()
|
||||
* uses these defaults when any of the 4 handlers (ie, 2 read handlers and 2 write handlers) are undefined.
|
||||
* uses these defaults when any of the 4 handlers (ie, 2 byte handlers and 2 short handlers) are undefined.
|
||||
*
|
||||
MemoryPDP11.afnNone = [
|
||||
MemoryPDP11.afnNone = [
|
||||
MemoryPDP11.prototype.readNone,
|
||||
MemoryPDP11.prototype.readShortDefault,
|
||||
MemoryPDP11.prototype.writeNone,
|
||||
MemoryPDP11.prototype.readShortDefault,
|
||||
MemoryPDP11.prototype.writeShortDefault
|
||||
];
|
||||
*/
|
||||
MemoryPDP11.afnNone = [];
|
||||
MemoryPDP11.afnNone = [];
|
||||
|
||||
MemoryPDP11.afnMemory = [
|
||||
MemoryPDP11.afnMemory = [
|
||||
MemoryPDP11.prototype.readByteMemory,
|
||||
MemoryPDP11.prototype.readShortMemory,
|
||||
MemoryPDP11.prototype.writeByteMemory,
|
||||
MemoryPDP11.prototype.readShortMemory,
|
||||
MemoryPDP11.prototype.writeShortMemory
|
||||
];
|
||||
|
||||
MemoryPDP11.afnChecked = [
|
||||
MemoryPDP11.afnChecked = [
|
||||
MemoryPDP11.prototype.readByteChecked,
|
||||
MemoryPDP11.prototype.readShortChecked,
|
||||
MemoryPDP11.prototype.writeByteChecked,
|
||||
MemoryPDP11.prototype.readShortChecked,
|
||||
MemoryPDP11.prototype.writeShortChecked
|
||||
];
|
||||
|
||||
if (TYPEDARRAYS) {
|
||||
MemoryPDP11.afnArrayBE = [
|
||||
MemoryPDP11.afnArrayBE = [
|
||||
MemoryPDP11.prototype.readByteBE,
|
||||
MemoryPDP11.prototype.readShortBE,
|
||||
MemoryPDP11.prototype.writeByteBE,
|
||||
MemoryPDP11.prototype.readShortBE,
|
||||
MemoryPDP11.prototype.writeShortBE
|
||||
];
|
||||
|
||||
MemoryPDP11.afnArrayLE = [
|
||||
MemoryPDP11.afnArrayLE = [
|
||||
MemoryPDP11.prototype.readByteLE,
|
||||
MemoryPDP11.prototype.readShortLE,
|
||||
MemoryPDP11.prototype.writeByteLE,
|
||||
MemoryPDP11.prototype.readShortLE,
|
||||
MemoryPDP11.prototype.writeShortLE
|
||||
];
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue