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
|
|
@ -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