Improve compatibility of FDC command responses

This commit is contained in:
Jeff Parsons 2014-10-29 12:45:06 -07:00 • committed by jeffpar
commit 234d6bbfd8
7 changed files with 819 additions and 786 deletions

View file

@ -328,7 +328,7 @@ or (at your option) any later version.
You are required to include a current copyright notice, with a link to [http://pcjs.org](), such as:
> [PCjs](http://pcjs.org) © 2012-2014 by [@jeffpar](mailto:Jeff@pcjs.org)
> [PCjs](http://pcjs.org) © 2012-2014 by [Jeff Parsons](mailto:Jeff@pcjs.org) ([@jeffpar](http://twitter.com/jeffpar))
in every source code file of every copy or modified version of this work, and to display that notice on every screen
that loads or runs any version of this software.

View file

@ -125,7 +125,7 @@ I still tend to follow Charles Simonyi's "[Hungarian](http://en.wikipedia.org/wi
conventions -- or rather, a naming convention loosely inspired by Hungarian.
For example, if I need a string or numeric variable representing a "thing," I will name it "sThing" if it's a
string or "iThing" if it's a number (or possibly "nThings" if it represents a total of Things or or "cThings"
string or "iThing" if it's a number (or possibly "nThings" if it represents a total of Things or "cThings"
if it's a counter of Things). If a string or numeric variable has a very short-term use, I'll probably just name
it "s" or "i".

View file

@ -18,8 +18,10 @@ To verify, type the following into any JavaScript REPL (eg, Node):
-1526726656
> n &= 0x80808080
-2147483648
> n.toString(16)
'-80000000'
> n == 0x80000000
false
> n == -0x80000000
true
So the notion that bitwise operators yield 32-bit results isn't exactly right; the sign (bit 31) of every 32-bit
result is always extended into the entire 52 "significand" bits of the underlying 64-bit float. And it's
@ -42,19 +44,19 @@ an operation as innocuous-looking as:
> n.toString(16)
'-80000000'
Viola: instant negative number! To continue the fun, now "or" one into bit 0:
Viola: instant negative number! To continue the fun, set bit 0 of 0x80000000, which should give you 0x80000001:
> n |= 1
-2147483647
> n.toString(16)
'-7fffffff'
Viola: all low 32 bits have instantly flipped!
WTF? Have all the low 32 bits flipped instead?
Actually, no, this time, I'm pulling your leg. The low 32 bits of the internal value are exactly what you would
expect: 0x80000001 (the internal representation looks more like 0xFFFFF80000001). The toString() method is
just a little misleading. As the MDN docs explain, for a negative number, toString() returns the positive
representation of the number, preceded by a - sign, *not* the "two's complement" of the number.
expect: 0x80000001 (the internal representation is more like 0xFFFFF80000001). But as the [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString)
explain, for a negative number, toString() returns the positive representation of the number, preceded by a - sign,
*not* the "two's complement" of the number.
*[@jeffpar](http://twitter.com/jeffpar)*
*October 26, 2014*

View file

@ -71,7 +71,7 @@ or (at your option) any later version.
You are required to include a current copyright notice, with a link to [http://pcjs.org](), such as:
> [PCjs](http://pcjs.org) © 2012-2014 by [@jeffpar](mailto:Jeff@pcjs.org)
> [PCjs](http://pcjs.org) © 2012-2014 by [Jeff Parsons](mailto:Jeff@pcjs.org) ([@jeffpar](http://twitter.com/jeffpar))
in every source code file of every copy or modified version of this work, and to display that notice on every screen
that loads or runs any version of this software.

View file

@ -1620,7 +1620,7 @@ FDC.prototype.doCmd = function()
var fIRQ = false;
this.regDataIndex = 0;
var bCmd = this.popCmd();
var drive, bDrive, bHeadSelect, bHead, bCylinder, n;
var drive, bDrive, bHead, c, h, r, n;
/*
* NOTE: We currently ignore the FDC.REG_DATA.CMD.SK, FDC.REG_DATA.CMD.MF and FDC.REG_DATA.CMD.MT bits of every command.
@ -1648,7 +1648,7 @@ FDC.prototype.doCmd = function()
case FDC.REG_DATA.CMD.SENSE_DRIVE: // 0x04
bDrive = this.popCmd(FDC.TERMS.DS);
bHeadSelect = (bDrive >> 2) & 0x1;
bHead = (bDrive >> 2) & 0x1;
this.iDrive = (bDrive & 0x3);
drive = this.aDrives[this.iDrive];
this.beginResult();
@ -1657,15 +1657,19 @@ FDC.prototype.doCmd = function()
case FDC.REG_DATA.CMD.WRITE_DATA: // 0x05
case FDC.REG_DATA.CMD.READ_DATA: // 0x06
bDrive = this.popCmd(FDC.TERMS.DS);
bHeadSelect = (bDrive >> 2) & 0x1;
this.iDrive = (bDrive & 0x3);
bDrive = this.popCmd(FDC.TERMS.DS); // Drive Select
bHead = (bDrive >> 2) & 0x1; // isolate HD (Head Select) bits
this.iDrive = (bDrive & 0x3); // isolate DS (Drive Select, aka Unit Select) bits
drive = this.aDrives[this.iDrive];
drive.bHead = bHeadSelect;
drive.bCylinder = this.popCmd(FDC.TERMS.C); // C
bHead = this.popCmd(FDC.TERMS.H); // H
Component.assert(bHead == bHeadSelect);
drive.bSector = this.popCmd(FDC.TERMS.R); // R
drive.bHead = bHead;
c = drive.bCylinder = this.popCmd(FDC.TERMS.C); // C
h = this.popCmd(FDC.TERMS.H); // H
/*
* Controller docs say that H should always match HD, so I assert that, but what if someone
* made a mistake and didn't program them identically -- what would happen? Which should we honor?
*/
Component.assert(h == bHead);
r = drive.bSector = this.popCmd(FDC.TERMS.R); // R
n = this.popCmd(FDC.TERMS.N); // N
drive.nBytes = 128 << n; // 0 => 128, 1 => 256, 2 => 512, 3 => 1024
drive.bSectorEnd = this.popCmd(FDC.TERMS.EOT); // EOT (final sector number on a cylinder)
@ -1675,14 +1679,7 @@ FDC.prototype.doCmd = function()
this.doRead(drive);
else
this.doWrite(drive);
this.beginResult();
this.pushST0(drive);
this.pushST1(drive);
this.pushST2(drive);
this.pushResult(drive.bCylinder, FDC.TERMS.C);
this.pushResult(drive.bHead, FDC.TERMS.H);
this.pushResult(drive.bSector, FDC.TERMS.R);
this.pushResult(n, FDC.TERMS.N);
this.pushResults(drive, bCmd, bHead, c, h, r, n);
fIRQ = true;
break;
@ -1731,11 +1728,12 @@ FDC.prototype.doCmd = function()
* To start, we'll focus on making this work in the normal case (80-track diskette in 80-track drive).
*/
bDrive = this.popCmd(FDC.TERMS.DS);
bHeadSelect = (bDrive >> 2) & 0x1;
bHead = (bDrive >> 2) & 0x1;
this.iDrive = (bDrive & 0x3);
drive = this.aDrives[this.iDrive];
drive.bHead = bHeadSelect;
drive.bSector = 1;
c = drive.bCylinder;
h = drive.bHead = bHead;
r = drive.bSector = 1;
n = 0;
drive.resCode = FDC.REG_DATA.RES.NONE;
if (drive.disk && (drive.sector = drive.disk.seek(drive.bCylinder, drive.bHead, drive.bSector))) {
@ -1746,45 +1744,34 @@ FDC.prototype.doCmd = function()
*/
drive.resCode = FDC.REG_DATA.RES.NO_DATA | FDC.REG_DATA.RES.INCOMPLETE;
}
this.pushST0(drive);
this.pushST1(drive);
this.pushST2(drive);
this.pushResult(drive.bCylinder, FDC.TERMS.C);
this.pushResult(drive.bHead, FDC.TERMS.H);
this.pushResult(drive.bSector, FDC.TERMS.R);
this.pushResult(n, FDC.TERMS.N);
this.pushResults(drive, bCmd, bHead, c, h, r, n);
fIRQ = true;
break;
case FDC.REG_DATA.CMD.FORMAT_TRACK: // 0x0D
bDrive = this.popCmd(FDC.TERMS.DS);
bHeadSelect = (bDrive >> 2) & 0x1;
bHead = (bDrive >> 2) & 0x1;
this.iDrive = (bDrive & 0x3);
drive = this.aDrives[this.iDrive];
drive.bHead = bHeadSelect;
c = drive.bCylinder;
h = drive.bHead = bHead;
r = 1;
n = this.popCmd(FDC.TERMS.N); // N
drive.nBytes = 128 << n; // 0 => 128, 1 => 256, 2 => 512, 3 => 1024 (bytes/sector)
drive.bSectorEnd = this.popCmd(FDC.TERMS.SC); // SC (sectors/track)
this.popCmd(FDC.TERMS.GPL); // GPL (spacing between sectors, excluding VCO Sync Field; 3)
drive.bFiller = this.popCmd(FDC.TERMS.D); // D (filler byte)
this.doFormat(drive);
this.beginResult();
this.pushST0(drive);
this.pushST1(drive);
this.pushST2(drive);
this.pushResult(drive.bCylinder, FDC.TERMS.C);
this.pushResult(drive.bHead, FDC.TERMS.H);
this.pushResult(drive.bSector, FDC.TERMS.R);
this.pushResult(n, FDC.TERMS.N);
this.pushResults(drive, bCmd, bHead, c, h, r, n);
fIRQ = true;
break;
case FDC.REG_DATA.CMD.SEEK: // 0x0F
bDrive = this.popCmd(FDC.TERMS.DS);
bHeadSelect = (bDrive >> 2) & 0x1;
bHead = (bDrive >> 2) & 0x1;
this.iDrive = (bDrive & 0x3);
drive = this.aDrives[this.iDrive];
drive.bHead = bHeadSelect;
drive.bHead = bHead;
/*
* As discussed in initDrive(), we can no longer simply set bCylinder to the specified NCN;
* instead, we must calculate the delta between bCylinderSeek and the NCN, and adjust bCylinder
@ -1794,11 +1781,11 @@ FDC.prototype.doCmd = function()
* be allowed to exceed the physical boundaries of the drive (ie, never lower than 0, and never
* greater than or equal to nCylinders).
*/
bCylinder = this.popCmd(FDC.TERMS.NCN);
drive.bCylinder += bCylinder - drive.bCylinderSeek;
c = this.popCmd(FDC.TERMS.NCN);
drive.bCylinder += c - drive.bCylinderSeek;
if (drive.bCylinder < 0) drive.bCylinder = 0;
if (drive.bCylinder >= drive.nCylinders) drive.bCylinder = drive.nCylinders - 1;
drive.bCylinderSeek = bCylinder;
drive.bCylinderSeek = c;
drive.resCode = FDC.REG_DATA.RES.SEEK_END;
/*
* TODO: To properly support ALL the ST3 result bits (not just TRACK0), we need a resCode
@ -1837,6 +1824,50 @@ FDC.prototype.doCmd = function()
}
};
/**
* pushResults(drive, bCmd, bHead, c, h, r, n)
*
* @param {Object} drive
* @param {number} bCmd
* @param {number} bHead
* @param {number} c
* @param {number} h
* @param {number} r
* @param {number} n
*/
FDC.prototype.pushResults = function(drive, bCmd, bHead, c, h, r, n)
{
this.beginResult();
this.pushST0(drive);
this.pushST1(drive);
this.pushST2(drive);
/*
* NOTE: I used to set the following C/H/R/N results using the values that advanceSector() had "advanced"
* them to, which seemed logical but was technically incorrect. For non-multi-track reads, they should match
* the programmed C/H/R/N values, except when EOT has been reached, in which case C = C + 1 and R = 1.
*
* For multi-track, the LSB of H should be complemented whenever EOT has been reached, which I "informally"
* detect by testing if the drive's current bCylinder and/or bHead positions advanced to a new cylinder or head,
* and apparently, C should never be advanced if H was initially 0.
*
* I don't do strict EOT comparisons here or elsewhere, because it allows the controller to work with a wider
* range of disks (eg, "fake" XDF disk images that contain 23 512-byte sectors/track).
*/
var i = 0;
if (c != drive.bCylinder || h != drive.bHead) {
i = r = 1;
}
if (bCmd & FDC.REG_DATA.CMD.MT) {
h ^= i;
if (!bHead) i = 0;
}
c += i;
this.pushResult(c, FDC.TERMS.C); // formerly drive.bCylinder
this.pushResult(h, FDC.TERMS.H); // formerly drive.bHead
this.pushResult(r, FDC.TERMS.R); // formerly drive.bSector
this.pushResult(n, FDC.TERMS.N);
};
/**
* popCmd(name)
*
@ -1848,7 +1879,7 @@ FDC.prototype.popCmd = function(name)
{
Component.assert((!this.regDataIndex || name !== undefined) && this.regDataIndex < this.regDataTotal);
var bCmd = this.regDataArray[this.regDataIndex];
if (DEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled((this.regDataIndex > 0? FDC.MESSAGE_PORT : 0) | FDC.MESSAGE_FDC)) {
if (DEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(FDC.MESSAGE_FDC)) {
var bCmdMasked = bCmd & FDC.REG_DATA.CMD.MASK;
if (!name && !this.regDataIndex && FDC.aCmdInfo[bCmdMasked]) name = FDC.aCmdInfo[bCmdMasked].name;
this.dbg.message("FDC.CMD[" + (name || this.regDataIndex) + "]: 0x" + str.toHexByte(bCmd));
@ -1902,7 +1933,7 @@ FDC.prototype.beginResult = function()
*/
FDC.prototype.pushResult = function(bResult, name)
{
if (DEBUG) this.messageDebugger("FDC.RES[" + (name || this.regDataTotal) + "]: 0x" + str.toHexByte(bResult), FDC.MESSAGE_PORT | FDC.MESSAGE_FDC);
if (DEBUG) this.messageDebugger("FDC.RES[" + (name || this.regDataTotal) + "]: 0x" + str.toHexByte(bResult), FDC.MESSAGE_FDC);
this.regDataArray[this.regDataTotal++] = bResult;
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long