Skip to content
This repository has been archived by the owner on Dec 24, 2020. It is now read-only.

Commit

Permalink
Added BACKTRACK support to I/O instructions, so that ATC Disk I/O can…
Browse files Browse the repository at this point in the history
… be tracked, too
  • Loading branch information
jeffpar committed Dec 29, 2014
1 parent 572d3a7 commit 3172912
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 21 deletions.
63 changes: 61 additions & 2 deletions modules/pcjs/lib/bus.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ function Bus(parmsBus, cpu, dbg)
*
* iBlock & this.blockMask
*
* While we *could* say that we mask addresses with this.addrLimit to simulate "A20 wrap", the simple
* While we *could* say that we mask addresses with this.addrMask to simulate "A20 wrap", the simple
* fact is it relieves us from bounds-checking every aMemBlocks index. Address wrapping at the 1Mb
* boundary (ie, the A20 address line) is something we'll have to deal with more carefully on the 80286.
*
Expand Down Expand Up @@ -407,6 +407,11 @@ Bus.prototype.removeMemory = function(addr, size)
/**
* getByte(addr)
*
* Why does the CPU use its own getByte() (which is substantially similar to ours) instead of calling us?
*
* It certainly could, but the CPU also needs to update some BACKTRACK state. There may also be a slight
* performance advantage calling its own method vs. calling through another object (ie, the Bus object).
*
* @this {Bus}
* @param {number} addr is a physical (non-segmented) address
* @return {number} byte (8-bit) value at that address
Expand All @@ -419,6 +424,8 @@ Bus.prototype.getByte = function(addr)
/**
* getByteDirect(addr)
*
* This is useful for the Debugger and other components that want to bypass getByte() breakpoint detection.
*
* @this {Bus}
* @param {number} addr is a physical (non-segmented) address
* @return {number} byte (8-bit) value at that address
Expand All @@ -431,6 +438,12 @@ Bus.prototype.getByteDirect = function(addr)
/**
* getWord(addr)
*
* Why does the CPU use its own getWord() (which is substantially similar to ours) instead of calling us?
*
* It certainly could, but the CPU also needs to update its cycle count, along with some BACKTRACK state.
* There may also be a slight performance advantage calling its own method vs. calling through another object
* (ie, the Bus object).
*
* @this {Bus}
* @param {number} addr is a physical (non-segmented) address
* @return {number} word (16-bit) value at that address
Expand All @@ -448,6 +461,8 @@ Bus.prototype.getWord = function(addr)
/**
* getWordDirect(addr)
*
* This is useful for the Debugger and other components that want to bypass getWord() breakpoint detection.
*
* @this {Bus}
* @param {number} addr is a physical (non-segmented) address
* @return {number} word (16-bit) value at that address
Expand All @@ -465,6 +480,11 @@ Bus.prototype.getWordDirect = function(addr)
/**
* setByte(addr, b)
*
* Why does the CPU use its own setByte() (which is substantially similar to ours) instead of calling us?
*
* It certainly could, but the CPU also needs to update some BACKTRACK state. There may also be a slight
* performance advantage calling its own method vs. calling through another object (ie, the Bus object).
*
* @this {Bus}
* @param {number} addr is a physical (non-segmented) address
* @param {number} b is the byte (8-bit) value to write (we truncate it to 8 bits to be safe)
Expand All @@ -477,6 +497,9 @@ Bus.prototype.setByte = function(addr, b)
/**
* setByteDirect(addr, b)
*
* This is useful for the Debugger and other components that want to bypass breakpoint detection AND read-only
* memory protection (for example, this is an interface the ROM component could use to initialize ROM contents).
*
* @this {Bus}
* @param {number} addr is a physical (non-segmented) address
* @param {number} b is the byte (8-bit) value to write (we truncate it to 8 bits to be safe)
Expand All @@ -489,6 +512,12 @@ Bus.prototype.setByteDirect = function(addr, b)
/**
* setWord(addr, w)
*
* Why does the CPU use its own setWord() (which is substantially similar to ours) instead of calling us?
*
* It certainly could, but the CPU also needs to update its cycle count, along with some BACKTRACK state.
* There may also be a slight performance advantage calling its own method vs. calling through another object
* (ie, the Bus object).
*
* @this {Bus}
* @param {number} addr is a physical (non-segmented) address
* @param {number} w is the word (16-bit) value to write (we truncate it to 16 bits to be safe)
Expand All @@ -508,6 +537,9 @@ Bus.prototype.setWord = function(addr, w)
/**
* setWordDirect(addr, w)
*
* This is useful for the Debugger and other components that want to bypass breakpoint detection AND read-only
* memory protection (for example, this is an interface the ROM component could use to initialize ROM contents).
*
* @this {Bus}
* @param {number} addr is a physical (non-segmented) address
* @param {number} w is the word (16-bit) value to write (we truncate it to 16 bits to be safe)
Expand Down Expand Up @@ -541,9 +573,15 @@ Bus.prototype.setWordDirect = function(addr, w)
Bus.prototype.addBackTrackObject = function(obj, bto, off)
{
if (BACKTRACK && obj) {
var cbtObjects = this.abtObjects.length;
if (!bto) {
/*
* Try the most recently created bto, on the off-chance it's what the caller needs
*/
if (cbtObjects) bto = this.abtObjects[cbtObjects-1];
}
if (!bto || bto.obj != obj || off < bto.off || off >= bto.off + Bus.BACKTRACK.OFF_MAX) {
var slot;
var cbtObjects = this.abtObjects.length;
bto = {obj: obj, off: off, slot: 0, refs: 0};
if (!this.cbtDeletions) {
slot = cbtObjects;
Expand All @@ -569,6 +607,23 @@ Bus.prototype.addBackTrackObject = function(obj, bto, off)
return null;
};

/**
* getBackTrackIndex(bto, off)
*
* @this {Bus}
* @param {Object|null} bto
* @param {number} off
* @return {number}
*/
Bus.prototype.getBackTrackIndex = function(bto, off)
{
var bti = 0;
if (BACKTRACK && bto) {
bti = (bto.slot << Bus.BACKTRACK.SLOT_SHIFT) | (Bus.BACKTRACK.GEN_START << Bus.BACKTRACK.GEN_SHIFT) | (off - bto.off);
}
return bti;
};

/**
* writeBackTrackObject(addr, bto, off)
*
Expand Down Expand Up @@ -843,6 +898,10 @@ Bus.prototype.checkPortInputNotify = function(port, addrFrom)
{
var bIn = 0xff;
var aNotify = this.aPortInputNotify[port];

if (BACKTRACK) {
this.cpu.backTrack.btiIO = 0;
}
if (aNotify !== undefined) {
if (aNotify[1]) {
bIn = aNotify[1].call(aNotify[0], port, addrFrom);
Expand Down
2 changes: 1 addition & 1 deletion modules/pcjs/lib/fdc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2309,7 +2309,7 @@ FDC.prototype.doFormat = function(drive)
FDC.prototype.readByte = function(drive, done)
{
var b = -1;
var obj, off; // these variables are purely for BACKTRACK purposes
var obj = null, off = 0; // these variables are purely for BACKTRACK purposes

if (!drive.resCode && drive.disk) {
do {
Expand Down
44 changes: 31 additions & 13 deletions modules/pcjs/lib/hdc.js
Original file line number Diff line number Diff line change
Expand Up @@ -1394,13 +1394,25 @@ HDC.prototype.inATCData = function(port, addrFrom)
* the pump; all we can do is assert that the pump has something in it. If bIn is inexplicably negative,
* well, then the caller will get 0xff.
*/
bIn = this.readByte(this.drive);
var hdc = this;
bIn = this.readByte(this.drive, function(b, fAsync, obj, off) {
hdc.assert(!fAsync);
if (BACKTRACK) {
if (!off && obj.file) {
hdc.println("loading " + obj.file.sPath + '[' + obj.offFile + "] via port 0x" + str.toHexWord(port));
}
/*
* TODO: We could define a cached BTO that's reset prior to a new ATC command, and then pass that
* to addBackTrackObject() here instead of null; but for now, we're going to rely on that function's
* simplistic MRU cache. If that fails, the worst that will (or should) happen is we'll burn through
* more BackTrack wrapper objects than necessary, and run the risk of running out.
*/
var bto = hdc.bus.addBackTrackObject(obj, null, off);
hdc.cpu.backTrack.btiIO = hdc.bus.getBackTrackIndex(bto, off);
}
});
this.assert(bIn >= 0);

/*
* Now that we've supplied a full sector of data, see if the caller's expecting additional sectors;
* if so, prime the pump again. The caller should not poll us again until another interrupt's been delivered.
*/
if (this.drive.ibSector == 1) {
/*
* messagePort() calls, if enabled, can be overwhelming for this port, so limit them to the first byte
Expand All @@ -1411,7 +1423,10 @@ HDC.prototype.inATCData = function(port, addrFrom)
}
}
else if (this.drive.ibSector == this.drive.cbSector) {

/*
* Now that we've supplied a full sector of data, see if the caller's expecting additional sectors;
* if so, prime the pump again. The caller should not poll us again until another interrupt's been delivered.
*/
if (this.messageEnabled(Messages.DATA | Messages.HDC)) {
var sDump = this.drive.disk.dumpSector(this.drive.sector);
if (sDump) this.dbg.message(sDump);
Expand All @@ -1424,7 +1439,6 @@ HDC.prototype.inATCData = function(port, addrFrom)
* additional bytes into the inATCData() stream. And we must first set DATA_REQ in the STATUS register.
*/
if (this.drive.nBytes >= this.drive.cbSector) {
var hdc = this;
hdc.regStatus = HDC.ATC.STATUS.BUSY | HDC.ATC.STATUS.DATA_REQ;
this.readByte(this.drive, function(b, fAsync) {
if (b >= 0) {
Expand Down Expand Up @@ -2478,26 +2492,29 @@ HDC.prototype.doDMAFormat = function(drive, done)
*
* @this {HDC}
* @param {Object} drive
* @param {function(number,boolean)} [done] (number is next available byte from drive, or -1 if no more bytes available)
* @param {function(number,boolean,Object,number)} [done] (number is next available byte from drive, or -1 if no more bytes available)
* @param {boolean} [fAutoInc] (default is true to auto-increment)
* @return {number} the requested byte, or -1 if unavailable
*/
HDC.prototype.readByte = function(drive, done, fAutoInc)
{
var b = -1;
var obj = null, off = 0; // these variables are purely for BACKTRACK purposes

if (drive.errorCode) {
if (done) done(b, false);
if (done) done(b, false, obj, off);
return b;
}

var inc = (fAutoInc !== false? 1 : 0);

if (drive.sector) {
off = drive.ibSector;
b = drive.disk.read(drive.sector, drive.ibSector);
drive.ibSector += inc;
if (b >= 0) {
if (done) done(b, false);
obj = drive.sector;
if (done) done(b, false, obj, off);
return b;
}
}
Expand All @@ -2514,7 +2531,8 @@ HDC.prototype.readByte = function(drive, done, fAutoInc)
if (drive.disk) {
drive.disk.seek(drive.wCylinder, drive.bHead, drive.bSector + drive.bSectorBias, false, function(sector, fAsync) {
if ((drive.sector = sector)) {
drive.ibSector = 0;
obj = sector;
off = drive.ibSector = 0;
/*
* We "pre-advance" bSector et al now, instead of waiting to advance it right before the seek().
* This allows the initial call to readByte() to perform a seek without triggering an unwanted advance.
Expand All @@ -2525,12 +2543,12 @@ HDC.prototype.readByte = function(drive, done, fAutoInc)
} else {
drive.errorCode = HDC.XTC.DATA.ERR.NO_SECTOR;
}
done(b, fAsync);
done(b, fAsync, obj, off);
});
return b;
}
drive.errorCode = HDC.XTC.DATA.ERR.NO_SECTOR;
done(b, false);
done(b, false, obj, off);
}
return b;
};
Expand Down
3 changes: 2 additions & 1 deletion modules/pcjs/lib/x86cpu.js
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,8 @@ X86CPU.prototype.resetRegs = function()
btiDILo: 0,
btiDIHi: 0,
btiMemLo: 0,
btiMemHi: 0
btiMemHi: 0,
btiIO: 0
};
}

Expand Down
18 changes: 15 additions & 3 deletions modules/pcjs/lib/x86opxx.js
Original file line number Diff line number Diff line change
Expand Up @@ -1213,6 +1213,7 @@ var X86OpXX = {

if (nReps--) {
var b = this.bus.checkPortInputNotify(this.regDX, this.regEIP - nDelta - 1);
if (BACKTRACK) this.backTrack.btiMemLo = this.backTrack.btiIO;
this.setSOByte(this.segES, this.regDI, b);
this.regDI = (this.regDI + ((this.regPS & X86.PS.DF)? -1 : 1)) & 0xffff;
this.nStepCycles -= nCycles;
Expand Down Expand Up @@ -1258,7 +1259,10 @@ var X86OpXX = {
}
if (nReps--) {
var addrFrom = this.regEIP - nDelta - 1;
var w = this.bus.checkPortInputNotify(this.regDX, addrFrom) | (this.bus.checkPortInputNotify(this.regDX, addrFrom) << 8);
var w = this.bus.checkPortInputNotify(this.regDX, addrFrom);
if (BACKTRACK) this.backTrack.btiMemLo = this.backTrack.btiIO;
w |= (this.bus.checkPortInputNotify(this.regDX, addrFrom) << 8);
if (BACKTRACK) this.backTrack.btiMemHi = this.backTrack.btiIO;
this.setSOWord(this.segES, this.regDI, w);
this.regDI = (this.regDI + ((this.regPS & X86.PS.DF)? -2 : 2)) & 0xffff;
this.nStepCycles -= nCycles;
Expand Down Expand Up @@ -3014,6 +3018,7 @@ var X86OpXX = {
opINb: function() {
var port = this.getIPByte();
this.regAX = (this.regAX & ~0xff) | this.bus.checkPortInputNotify(port, this.regEIP - 2);
if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiIO;
this.nStepCycles -= this.CYCLES.nOpCyclesInP;
},
/**
Expand All @@ -3023,7 +3028,10 @@ var X86OpXX = {
*/
opINw: function() {
var port = this.getIPByte();
this.regAX = this.bus.checkPortInputNotify(port, this.regEIP - 1) | (this.bus.checkPortInputNotify((port + 1) & 0xffff, this.regEIP - 2) << 8);
this.regAX = this.bus.checkPortInputNotify(port, this.regEIP - 2);
if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiIO;
this.regAX |= (this.bus.checkPortInputNotify((port + 1) & 0xffff, this.regEIP - 2) << 8);
if (BACKTRACK) this.backTrack.btiAH = this.backTrack.btiIO;
this.nStepCycles -= this.CYCLES.nOpCyclesInP;
},
/**
Expand Down Expand Up @@ -3094,6 +3102,7 @@ var X86OpXX = {
*/
opINDXb: function() {
this.regAX = (this.regAX & ~0xff) | this.bus.checkPortInputNotify(this.regDX, this.regEIP - 1);
if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiIO;
this.nStepCycles -= this.CYCLES.nOpCyclesInDX;
},
/**
Expand All @@ -3102,7 +3111,10 @@ var X86OpXX = {
* op=0xED (in AX,dx)
*/
opINDXw: function() {
this.regAX = this.bus.checkPortInputNotify(this.regDX, this.regEIP - 1) | (this.bus.checkPortInputNotify((this.regDX + 1) & 0xffff, this.regEIP - 1) << 8);
this.regAX = this.bus.checkPortInputNotify(this.regDX, this.regEIP - 1);
if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiIO;
this.regAX |= (this.bus.checkPortInputNotify((this.regDX + 1) & 0xffff, this.regEIP - 1) << 8);
if (BACKTRACK) this.backTrack.btiAH = this.backTrack.btiIO;
this.nStepCycles -= this.CYCLES.nOpCyclesInDX;
},
/**
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pcjs",
"version": "1.16.4",
"version": "1.16.5",
"description": "Node-enabled version of PCjs",
"main": "server.js",
"directories": {
Expand Down

0 comments on commit 3172912

Please sign in to comment.