The 8080 CPU goes through the Bus interfaces for all memory accesses now. The PCjs CPU maintained its own copy of Bus memory structures and interfaces, partly for improved performance but also because some CPUs can turn on address translation (ie, paging), making separate interfaces a necessity. However, PC8080 has much lower performance requirements and no address translation requirements, so we should revert to the original Bus interfaces.
This commit is contained in:
parent
cba3fca260
commit
a56f1cfde1
6 changed files with 376 additions and 434 deletions
|
|
@ -135,7 +135,7 @@ function Bus(parmsBus, cpu, dbg)
|
|||
* [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 linear instruction pointer (LIP) at the point of access.
|
||||
* 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
|
||||
|
|
@ -225,7 +225,6 @@ Bus.prototype.initMemory = function()
|
|||
for (var iBlock = 0; iBlock < this.nBlockTotal; iBlock++) {
|
||||
this.aMemBlocks[iBlock] = block;
|
||||
}
|
||||
this.cpu.initMemory(this.aMemBlocks, this.nBlockShift, this.nBusMask);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -492,8 +491,6 @@ Bus.prototype.setMemoryBlocks = function(addr, size, aBlocks, type)
|
|||
/**
|
||||
* getByte(addr)
|
||||
*
|
||||
* For physical addresses only; for linear addresses, use cpu.getByte().
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} addr is a physical address
|
||||
* @return {number} byte (8-bit) value at that address
|
||||
|
|
@ -520,8 +517,6 @@ Bus.prototype.getByteDirect = function(addr)
|
|||
/**
|
||||
* getShort(addr)
|
||||
*
|
||||
* For physical addresses only; for linear addresses, use cpu.getShort().
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} addr is a physical address
|
||||
* @return {number} word (16-bit) value at that address
|
||||
|
|
@ -558,8 +553,6 @@ Bus.prototype.getShortDirect = function(addr)
|
|||
/**
|
||||
* setByte(addr, b)
|
||||
*
|
||||
* For physical addresses only; for linear addresses, use cpu.setByte().
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} addr is a physical address
|
||||
* @param {number} b is the byte (8-bit) value to write (we truncate it to 8 bits to be safe)
|
||||
|
|
@ -587,8 +580,6 @@ Bus.prototype.setByteDirect = function(addr, b)
|
|||
/**
|
||||
* setShort(addr, w)
|
||||
*
|
||||
* For physical addresses only; for linear addresses, use cpu.setShort().
|
||||
*
|
||||
* @this {Bus}
|
||||
* @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)
|
||||
|
|
@ -627,6 +618,36 @@ Bus.prototype.setShortDirect = function(addr, w)
|
|||
this.aMemBlocks[iBlock & this.nBlockMask].writeByteDirect(0, (w >> 8) & 0xff, addr + 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* addMemBreak(addr, fWrite)
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} addr
|
||||
* @param {boolean} fWrite is true for a memory write breakpoint, false for a memory read breakpoint
|
||||
*/
|
||||
Bus.prototype.addMemBreak = function(addr, fWrite)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
this.aMemBlocks[iBlock].addBreakpoint(addr & this.nBlockLimit, fWrite);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* removeMemBreak(addr, fWrite)
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} addr
|
||||
* @param {boolean} fWrite is true for a memory write breakpoint, false for a memory read breakpoint
|
||||
*/
|
||||
Bus.prototype.removeMemBreak = function(addr, fWrite)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
this.aMemBlocks[iBlock].removeBreakpoint(addr & this.nBlockLimit, fWrite);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* saveMemory(fAll)
|
||||
*
|
||||
|
|
@ -745,7 +766,7 @@ Bus.prototype.addPortInputBreak = function(port)
|
|||
* @this {Bus}
|
||||
* @param {number} start port address
|
||||
* @param {number} end port address
|
||||
* @param {function(number,number)} fn is called with the port and LIP values at the time of the input
|
||||
* @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)
|
||||
{
|
||||
|
|
@ -794,18 +815,18 @@ Bus.prototype.addPortInputWidth = function(port, size)
|
|||
};
|
||||
|
||||
/**
|
||||
* checkPortInputNotify(port, size, addrLIP)
|
||||
* checkPortInputNotify(port, size, addrIP)
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} port
|
||||
* @param {number} size (1, 2 or 4)
|
||||
* @param {number} [addrLIP] is the LIP value at the time of the input
|
||||
* @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, addrLIP)
|
||||
Bus.prototype.checkPortInputNotify = function(port, size, addrIP)
|
||||
{
|
||||
var data = 0, shift = 0;
|
||||
|
||||
|
|
@ -827,7 +848,7 @@ Bus.prototype.checkPortInputNotify = function(port, size, addrLIP)
|
|||
|
||||
if (aNotify !== undefined) {
|
||||
if (aNotify[0]) {
|
||||
dataPort = aNotify[0](port, addrLIP);
|
||||
dataPort = aNotify[0](port, addrIP);
|
||||
if (dataPort === undefined) {
|
||||
dataPort = maskPort;
|
||||
} else {
|
||||
|
|
@ -840,7 +861,7 @@ Bus.prototype.checkPortInputNotify = function(port, size, addrLIP)
|
|||
}
|
||||
else {
|
||||
if (DEBUGGER && this.dbg) {
|
||||
this.dbg.messageIO(this, port, null, addrLIP);
|
||||
this.dbg.messageIO(this, port, null, addrIP);
|
||||
if (this.fPortInputBreakAll) this.dbg.checkPortInput(port, size, dataPort);
|
||||
}
|
||||
}
|
||||
|
|
@ -902,7 +923,7 @@ Bus.prototype.addPortOutputBreak = function(port)
|
|||
* @this {Bus}
|
||||
* @param {number} start port address
|
||||
* @param {number} end port address
|
||||
* @param {function(number,number)} fn is called with the port and LIP values at the time of the output
|
||||
* @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)
|
||||
{
|
||||
|
|
@ -951,15 +972,15 @@ Bus.prototype.addPortOutputWidth = function(port, size)
|
|||
};
|
||||
|
||||
/**
|
||||
* checkPortOutputNotify(port, size, data, addrLIP)
|
||||
* checkPortOutputNotify(port, size, data, addrIP)
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} port
|
||||
* @param {number} size
|
||||
* @param {number} data
|
||||
* @param {number} [addrLIP] is the LIP value at the time of the output
|
||||
* @param {number} [addrIP] is the IP value at the time of the output
|
||||
*/
|
||||
Bus.prototype.checkPortOutputNotify = function(port, size, data, addrLIP)
|
||||
Bus.prototype.checkPortOutputNotify = function(port, size, data, addrIP)
|
||||
{
|
||||
var shift = 0;
|
||||
|
||||
|
|
@ -981,7 +1002,7 @@ Bus.prototype.checkPortOutputNotify = function(port, size, data, addrLIP)
|
|||
|
||||
if (aNotify !== undefined) {
|
||||
if (aNotify[0]) {
|
||||
aNotify[0](port, dataPort, addrLIP);
|
||||
aNotify[0](port, dataPort, addrIP);
|
||||
}
|
||||
if (DEBUGGER && this.dbg && this.fPortOutputBreakAll != aNotify[1]) {
|
||||
this.dbg.checkPortOutput(port, size, dataPort);
|
||||
|
|
@ -989,7 +1010,7 @@ Bus.prototype.checkPortOutputNotify = function(port, size, data, addrLIP)
|
|||
}
|
||||
else {
|
||||
if (DEBUGGER && this.dbg) {
|
||||
this.dbg.messageIO(this, port, dataPort, addrLIP);
|
||||
this.dbg.messageIO(this, port, dataPort, addrIP);
|
||||
if (this.fPortOutputBreakAll) this.dbg.checkPortOutput(port, size, dataPort);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,14 +92,6 @@ function CPUSim(parmsCPU)
|
|||
*/
|
||||
this.cLiveRegs = 0;
|
||||
|
||||
/*
|
||||
* We're just declaring aBusBlocks and associated Bus parameters here; they'll be initialized by initMemory()
|
||||
* when the Bus is initialized.
|
||||
*/
|
||||
this.aBusBlocks = [];
|
||||
this.nBusMask = 0;
|
||||
this.nBlockShift = this.nBlockSize = this.nBlockLimit = this.nBlockTotal = this.nBlockMask = 0;
|
||||
|
||||
/*
|
||||
* Array of halt handlers, if any (see addHaltCheck)
|
||||
*/
|
||||
|
|
@ -115,27 +107,6 @@ function CPUSim(parmsCPU)
|
|||
|
||||
Component.subclass(CPUSim, CPU);
|
||||
|
||||
/**
|
||||
* initMemory(aBusBlocks, nBlockShift, nBusMask)
|
||||
*
|
||||
* Notification from Bus.initMemory(), giving us direct access to the entire memory space.
|
||||
*
|
||||
* @this {CPUSim}
|
||||
* @param {Array} aBusBlocks
|
||||
* @param {number} nBlockShift
|
||||
* @param {number} nBusMask
|
||||
*/
|
||||
CPUSim.prototype.initMemory = function(aBusBlocks, nBlockShift, nBusMask)
|
||||
{
|
||||
this.aBusBlocks = aBusBlocks;
|
||||
this.nBlockShift = nBlockShift;
|
||||
this.nBlockSize = 1 << this.nBlockShift;
|
||||
this.nBlockLimit = this.nBlockSize - 1;
|
||||
this.nBlockTotal = aBusBlocks.length;
|
||||
this.nBlockMask = this.nBlockTotal - 1;
|
||||
this.nBusMask = nBusMask;
|
||||
};
|
||||
|
||||
/**
|
||||
* addHaltCheck(fn)
|
||||
*
|
||||
|
|
@ -149,36 +120,6 @@ CPUSim.prototype.addHaltCheck = function(fn)
|
|||
this.afnHalt.push(fn);
|
||||
};
|
||||
|
||||
/**
|
||||
* addMemBreak(addr, fWrite)
|
||||
*
|
||||
* @this {CPUSim}
|
||||
* @param {number} addr
|
||||
* @param {boolean} fWrite is true for a memory write breakpoint, false for a memory read breakpoint
|
||||
*/
|
||||
CPUSim.prototype.addMemBreak = function(addr, fWrite)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
this.aBusBlocks[iBlock].addBreakpoint(addr & this.nBlockLimit, fWrite);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* removeMemBreak(addr, fWrite)
|
||||
*
|
||||
* @this {CPUSim}
|
||||
* @param {number} addr
|
||||
* @param {boolean} fWrite is true for a memory write breakpoint, false for a memory read breakpoint
|
||||
*/
|
||||
CPUSim.prototype.removeMemBreak = function(addr, fWrite)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
this.aBusBlocks[iBlock].removeBreakpoint(addr & this.nBlockLimit, fWrite);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* initProcessor()
|
||||
*
|
||||
|
|
@ -889,7 +830,7 @@ CPUSim.prototype.xorByte = function(src)
|
|||
*/
|
||||
CPUSim.prototype.getByte = function(addr)
|
||||
{
|
||||
return this.aBusBlocks[(addr & this.nBusMask) >>> this.nBlockShift].readByte(addr & this.nBlockLimit, addr);
|
||||
return this.bus.getByte(addr);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -901,14 +842,7 @@ CPUSim.prototype.getByte = function(addr)
|
|||
*/
|
||||
CPUSim.prototype.getWord = function(addr)
|
||||
{
|
||||
var off = addr & this.nBlockLimit;
|
||||
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
|
||||
if (off < this.nBlockLimit) {
|
||||
return this.aBusBlocks[iBlock].readShort(off, addr);
|
||||
}
|
||||
var w = this.aBusBlocks[iBlock].readByte(off, addr);
|
||||
w |= this.aBusBlocks[(iBlock + 1) & this.nBlockMask].readByte(0, addr + 1) << 8;
|
||||
return w;
|
||||
return this.bus.getShort(addr);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -920,7 +854,7 @@ CPUSim.prototype.getWord = function(addr)
|
|||
*/
|
||||
CPUSim.prototype.setByte = function(addr, b)
|
||||
{
|
||||
this.aBusBlocks[(addr & this.nBusMask) >>> this.nBlockShift].writeByte(addr & this.nBlockLimit, b & 0xff, addr);
|
||||
this.bus.setByte(addr, b);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -932,14 +866,7 @@ CPUSim.prototype.setByte = function(addr, b)
|
|||
*/
|
||||
CPUSim.prototype.setWord = function(addr, w)
|
||||
{
|
||||
var off = addr & this.nBlockLimit;
|
||||
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
|
||||
if (off < this.nBlockLimit) {
|
||||
this.aBusBlocks[iBlock].writeShort(off, w & 0xffff, addr);
|
||||
return;
|
||||
}
|
||||
this.aBusBlocks[iBlock++].writeByte(off, w & 0xff, addr);
|
||||
this.aBusBlocks[iBlock & this.nBlockMask].writeByte(0, (w >> 8) & 0xff, addr + 1);
|
||||
this.bus.setShort(addr, w);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1114,14 +1114,13 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* dumpBlocks(aBlocks, sAddr, fLinear)
|
||||
* dumpBlocks(aBlocks, sAddr)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {Array} aBlocks
|
||||
* @param {string} [sAddr] (optional block address)
|
||||
* @param {boolean} [fLinear] (true if linear, physical otherwise)
|
||||
*/
|
||||
Debugger.prototype.dumpBlocks = function(aBlocks, sAddr, fLinear)
|
||||
Debugger.prototype.dumpBlocks = function(aBlocks, sAddr)
|
||||
{
|
||||
var addr = 0, i = 0, n = aBlocks.length;
|
||||
|
||||
|
|
@ -1131,11 +1130,11 @@ if (DEBUGGER) {
|
|||
this.println("invalid address: " + sAddr);
|
||||
return;
|
||||
}
|
||||
i = addr >>> this.cpu.nBlockShift;
|
||||
i = addr >>> this.bus.nBlockShift;
|
||||
n = 1;
|
||||
}
|
||||
|
||||
this.println("blockid " + (fLinear? "linear " : "physical") + " blockaddr used size type");
|
||||
this.println("blockid physical blockaddr used size type");
|
||||
this.println("-------- --------- ---------- ------ ------ ----");
|
||||
|
||||
var typePrev = -1, cPrev = 0;
|
||||
|
|
@ -1147,12 +1146,12 @@ if (DEBUGGER) {
|
|||
typePrev = block.type;
|
||||
var sType = Memory.TYPE.NAMES[typePrev];
|
||||
if (block) {
|
||||
this.println(str.toHex(block.id) + " %" + str.toHex(i << this.cpu.nBlockShift) + " %%" + str.toHex(block.addr) + " " + str.toHexWord(block.used) + " " + str.toHexWord(block.size) + " " + sType);
|
||||
this.println(str.toHex(block.id) + " %" + str.toHex(i << this.bus.nBlockShift) + " %%" + str.toHex(block.addr) + " " + str.toHexWord(block.used) + " " + str.toHexWord(block.size) + " " + sType);
|
||||
}
|
||||
if (typePrev != Memory.TYPE.NONE) typePrev = -1;
|
||||
cPrev = 0;
|
||||
}
|
||||
addr += this.cpu.nBlockSize;
|
||||
addr += this.bus.nBlockSize;
|
||||
i++;
|
||||
}
|
||||
};
|
||||
|
|
@ -1167,20 +1166,7 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.dumpBus = function(asArgs)
|
||||
{
|
||||
this.dumpBlocks(this.cpu.aBusBlocks, asArgs[0]);
|
||||
};
|
||||
|
||||
/**
|
||||
* dumpMem(asArgs)
|
||||
*
|
||||
* Dumps page allocations.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {Array.<string>} asArgs (asArgs[0] is an optional block address)
|
||||
*/
|
||||
Debugger.prototype.dumpMem = function(asArgs)
|
||||
{
|
||||
this.dumpBlocks(this.cpu.aMemBlocks, asArgs[0], this.cpu.aMemBlocks !== this.cpu.aBusBlocks);
|
||||
this.dumpBlocks(this.bus.aMemBlocks, asArgs[0]);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -2122,14 +2108,14 @@ if (DEBUGGER) {
|
|||
if (this.aBreakRead !== undefined) {
|
||||
for (i = 1; i < this.aBreakRead.length; i++) {
|
||||
dbgAddr = this.aBreakRead[i];
|
||||
this.cpu.removeMemBreak(this.getAddr(dbgAddr), false);
|
||||
this.bus.removeMemBreak(this.getAddr(dbgAddr), false);
|
||||
}
|
||||
}
|
||||
this.aBreakRead = ["br"];
|
||||
if (this.aBreakWrite !== undefined) {
|
||||
for (i = 1; i < this.aBreakWrite.length; i++) {
|
||||
dbgAddr = this.aBreakWrite[i];
|
||||
this.cpu.removeMemBreak(this.getAddr(dbgAddr), true);
|
||||
this.bus.removeMemBreak(this.getAddr(dbgAddr), true);
|
||||
}
|
||||
}
|
||||
this.aBreakWrite = ["bw"];
|
||||
|
|
@ -2192,7 +2178,7 @@ if (DEBUGGER) {
|
|||
this.println("invalid address: " + this.toHexAddr(dbgAddr));
|
||||
fSuccess = false;
|
||||
} else {
|
||||
this.cpu.addMemBreak(addr, aBreak == this.aBreakWrite);
|
||||
this.bus.addMemBreak(addr, aBreak == this.aBreakWrite);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2238,7 +2224,7 @@ if (DEBUGGER) {
|
|||
}
|
||||
aBreak.splice(i, 1);
|
||||
if (aBreak != this.aBreakExec) {
|
||||
this.cpu.removeMemBreak(addr, aBreak == this.aBreakWrite);
|
||||
this.bus.removeMemBreak(addr, aBreak == this.aBreakWrite);
|
||||
}
|
||||
/*
|
||||
* We'll mirror the logic in addBreakpoint() and leave the history buffer alone if this
|
||||
|
|
@ -2275,8 +2261,6 @@ if (DEBUGGER) {
|
|||
/**
|
||||
* printBreakpoint(aBreak, i, sAction)
|
||||
*
|
||||
* TODO: We may need to start printing linear addresses also (if any), because segmented address can be ambiguous.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {Array} aBreak
|
||||
* @param {number} i
|
||||
|
|
|
|||
|
|
@ -56,14 +56,24 @@ if (NODE) {
|
|||
* bufferCols: the width of a single frame buffer row, in pixels (eg, 256)
|
||||
* bufferRows: the number of frame buffer rows (eg, 224)
|
||||
* bufferBits: the number of bits per pixel (default is 1 if omitted)
|
||||
* interruptRate: normally the same as (or some multiple of) the refreshRate (eg, 120)
|
||||
* refreshRate: how many times updateScreen() should be called per second (eg, 60)
|
||||
* interruptRate: normally the same as (or some multiple of) refreshRate (eg, 120)
|
||||
* refreshRate: how many times updateScreen() should be performed per second (eg, 60)
|
||||
*
|
||||
* We record all the above values now, but we defer creation of the frame buffer until our initBus()
|
||||
* handler is called. At that point, we will also compute the extent of the frame buffer, determine the
|
||||
* appropriate "cell" size (ie, the number of pixels that updateScreen() will fetch and process at once),
|
||||
* and allocate our cell cache.
|
||||
*
|
||||
* Why interruptRate in addition to refreshRate? A higher interrupt rate is required for Space Invaders,
|
||||
* because even though the CRT refreshes at 60Hz, the CRT controller interrupts the CPU *twice* per
|
||||
* refresh (once after the top half of the screen has been redrawn, and again after the bottom half has
|
||||
* been redrawn), so we need an interrupt rate of 120Hz. We pass the higher rate on to the CPU, so that
|
||||
* it will call updateScreen() more frequently, but we limit our screen updates to every *other* call.
|
||||
*
|
||||
* TODO: Consider alternatives to screenRotation; it's expedient, but I'm not sure how efficient it is,
|
||||
* and it might be nice (especially for debugging) if we created our own rotated image buffer which we could
|
||||
* then blast directly to the screen canvas.
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
* @param {Object} parmsVideo
|
||||
|
|
|
|||
Loading…
Reference in a new issue