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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -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.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.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.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,12 +842,12 @@ 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.prototype.readNone,
|
||||
MemoryPDP11.prototype.readShortDefault,
|
||||
MemoryPDP11.prototype.writeNone,
|
||||
MemoryPDP11.prototype.readShortDefault,
|
||||
MemoryPDP11.prototype.writeShortDefault
|
||||
];
|
||||
*/
|
||||
|
|
@ -843,30 +855,30 @@ MemoryPDP11.afnNone = [];
|
|||
|
||||
MemoryPDP11.afnMemory = [
|
||||
MemoryPDP11.prototype.readByteMemory,
|
||||
MemoryPDP11.prototype.readShortMemory,
|
||||
MemoryPDP11.prototype.writeByteMemory,
|
||||
MemoryPDP11.prototype.readShortMemory,
|
||||
MemoryPDP11.prototype.writeShortMemory
|
||||
];
|
||||
|
||||
MemoryPDP11.afnChecked = [
|
||||
MemoryPDP11.prototype.readByteChecked,
|
||||
MemoryPDP11.prototype.readShortChecked,
|
||||
MemoryPDP11.prototype.writeByteChecked,
|
||||
MemoryPDP11.prototype.readShortChecked,
|
||||
MemoryPDP11.prototype.writeShortChecked
|
||||
];
|
||||
|
||||
if (TYPEDARRAYS) {
|
||||
MemoryPDP11.afnArrayBE = [
|
||||
MemoryPDP11.prototype.readByteBE,
|
||||
MemoryPDP11.prototype.readShortBE,
|
||||
MemoryPDP11.prototype.writeByteBE,
|
||||
MemoryPDP11.prototype.readShortBE,
|
||||
MemoryPDP11.prototype.writeShortBE
|
||||
];
|
||||
|
||||
MemoryPDP11.afnArrayLE = [
|
||||
MemoryPDP11.prototype.readByteLE,
|
||||
MemoryPDP11.prototype.readShortLE,
|
||||
MemoryPDP11.prototype.writeByteLE,
|
||||
MemoryPDP11.prototype.readShortLE,
|
||||
MemoryPDP11.prototype.writeShortLE
|
||||
];
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue