"Fake" XDF disk support works now
What this means is that XDF disk images formatted as 80-track 23-sector-per-track images will work with XDF code that honors that format; however, I've not yet been able to test with "real" XDF disk images (ie, disk images created with DiskDump using the experimental --xdf flag). "Real" XDF disk images almost certainly need more work (eg, setting all the sector IDs properly), but the groundwork has been laid.
This commit is contained in:
parent
715e603ea2
commit
024f9fccf8
20 changed files with 1973 additions and 1809 deletions
|
|
@ -10,7 +10,7 @@
|
|||
"APPNAME": false,
|
||||
"APPVERSION": false,
|
||||
"SITEHOST": false,
|
||||
"DEBUG": false,
|
||||
"DEBUG": true,
|
||||
"MAXDEBUG": false,
|
||||
"C1PJSCLASS": true,
|
||||
"DEBUGGER": true,
|
||||
|
|
@ -36,4 +36,4 @@
|
|||
"clearTimeout": false,
|
||||
"Image": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ DiskDump.MY_OEM_STRING = "PCJS.ORG";
|
|||
|
||||
/**
|
||||
* The BPBs that buildImage() currently supports; these BPBs should be in order of smallest to largest capacity,
|
||||
* to help insure we don't select a disk format larger than necessary.
|
||||
* to help ensure we don't select a disk format larger than necessary.
|
||||
*/
|
||||
DiskDump.aDefaultBPBs = [
|
||||
[ // define BPB for 160Kb diskette
|
||||
|
|
@ -396,7 +396,7 @@ DiskDump.CLI = function()
|
|||
}
|
||||
}
|
||||
else {
|
||||
console.log("usage: diskdump --dir={dir}|--disk={disk}|--path={file}[;{file}...] [--format=json|data|hex|bytes|img] [--comments] [--output={file}] [--manifest={file}]");
|
||||
console.log("usage: diskdump --dir={dir}|--disk={disk}|--path={file}[;{file}...] [--format=json|data|hex|bytes|img] [--comments] [--output={file}] [--manifest={file}] [--xdf]");
|
||||
}
|
||||
|
||||
if (err) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
"APPNAME": false,
|
||||
"APPVERSION": false,
|
||||
"SITEHOST": false,
|
||||
"DEBUG": false,
|
||||
"DEBUG": true,
|
||||
"MAXDEBUG": false,
|
||||
"PCJSCLASS": true,
|
||||
"DEBUGGER": true,
|
||||
|
|
@ -55,4 +55,4 @@
|
|||
"webkitAudioContext": false,
|
||||
"window": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1121,7 +1121,11 @@ Computer.prototype.onReset = function()
|
|||
* wants to clutter the UI with confusing options. ;-)
|
||||
*/
|
||||
if (this.resume && !this.sResumePath) {
|
||||
var fSave = (this.resume == Computer.RESUME_AUTO || !web.confirmUser("Click OK to save the " + Computer.sAppName + " machine state.\n\nWARNING: If you CANCEL, all disk changes will be discarded."));
|
||||
/*
|
||||
* I used to bypass the prompt if this.resume == Computer.RESUME_AUTO, setting fSave to true automatically,
|
||||
* but that gives the user no means of resetting a resumable machine that contains errors in its resume state.
|
||||
*/
|
||||
var fSave = (/* this.resume == Computer.RESUME_AUTO || */ !web.confirmUser("Click OK to save the " + Computer.sAppName + " machine state.\n\nWARNING: If you CANCEL, all disk changes will be discarded."));
|
||||
this.powerOff(fSave, true);
|
||||
/*
|
||||
* Forcing the page to reload is an expedient option, but ugly. It's preferable to call powerOn()
|
||||
|
|
|
|||
|
|
@ -1388,7 +1388,7 @@ if (DEBUGGER) {
|
|||
/*
|
||||
* TODO: Filtering of interrupt numbers below should be user-definable; this is very quick-and-dirty.
|
||||
*/
|
||||
if (nInt < 0x20 && nInt != 0x10 && nInt != 0x15 && nInt != 0x16 && nInt != 0x1A && nInt != 0x1C) {
|
||||
if (nInt != 0x10 && nInt != 0x15 && nInt != 0x16 && nInt != 0x1A && nInt != 0x1C) {
|
||||
var AH = this.cpu.regAX >> 8;
|
||||
var aFuncs = Debugger.INT_FUNCS[nInt];
|
||||
var sFunc = (aFuncs && aFuncs[AH]) || "";
|
||||
|
|
@ -1556,7 +1556,7 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.intDOSCall = function(addr)
|
||||
{
|
||||
if (this.messageEnabled(this.MESSAGE_DOS)) this.messageInt(Debugger.INT_DOS, addr);
|
||||
if (this.messageEnabled(this.MESSAGE_DOS | this.MESSAGE_INT)) this.messageInt(Debugger.INT_DOS, addr);
|
||||
return true;
|
||||
};
|
||||
|
||||
|
|
@ -4485,19 +4485,36 @@ if (DEBUGGER) {
|
|||
}
|
||||
|
||||
sCmd = sCmd.toLowerCase();
|
||||
|
||||
if (this.isReady() && !this.isBusy(true) && sCmd.length > 0) {
|
||||
|
||||
if (this.fAssemble) {
|
||||
sCmd = "a " + this.hexAddr(this.aAddrAssemble) + " " + sCmd;
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Process any "whole word" commands here first (eg, "reset").
|
||||
* Process any "whole" commands here first (eg, "debug", "nodebug", "reset", etc.)
|
||||
*
|
||||
* For all other commands, if they lack a space between the command and argument portions,
|
||||
* insert a space before the first non-alpha character, so that split() will have the desired effect.
|
||||
*/
|
||||
|
||||
/*
|
||||
* These commands work great, except that they won't compile, and in fact, I don't WANT them in the
|
||||
* compiled version, but putting them inside (!COMPILED) doesn't help, so I must disable them for now.
|
||||
*
|
||||
if (!COMPILED) {
|
||||
if (sCmd == "debug") {
|
||||
DEBUG = true;
|
||||
this.println("DEBUG checks on");
|
||||
return true;
|
||||
}
|
||||
else if (sCmd == "nodebug") {
|
||||
DEBUG = false;
|
||||
this.println("DEBUG checks off");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
var ch, ch0, i;
|
||||
switch (sCmd) {
|
||||
case "reset":
|
||||
|
|
@ -4518,71 +4535,70 @@ if (DEBUGGER) {
|
|||
}
|
||||
|
||||
var asArgs = sCmd.split(" ");
|
||||
|
||||
switch (asArgs[0].charAt(0)) {
|
||||
case "a":
|
||||
this.doAssemble(asArgs);
|
||||
break;
|
||||
case "b":
|
||||
this.doBreak(asArgs[0], asArgs[1]);
|
||||
break;
|
||||
case "c":
|
||||
this.doClear(asArgs[0]);
|
||||
break;
|
||||
case "d":
|
||||
this.doDump(asArgs[0], asArgs[1], asArgs[2]);
|
||||
break;
|
||||
case "e":
|
||||
this.doEdit(asArgs);
|
||||
break;
|
||||
case "f":
|
||||
this.doFreqs(asArgs[1]);
|
||||
break;
|
||||
case "g":
|
||||
this.doRun(asArgs[1]);
|
||||
break;
|
||||
case "h":
|
||||
this.doHalt(asArgs[1]);
|
||||
break;
|
||||
case "i":
|
||||
this.doInput(asArgs[1]);
|
||||
break;
|
||||
case "l":
|
||||
this.doLoad(asArgs);
|
||||
break;
|
||||
case "m":
|
||||
this.doMessages(asArgs);
|
||||
break;
|
||||
case "o":
|
||||
this.doOutput(asArgs[1], asArgs[2]);
|
||||
break;
|
||||
case "p":
|
||||
case "pr":
|
||||
this.doProcStep(asArgs[0]);
|
||||
break;
|
||||
case "r":
|
||||
this.doRegisters(asArgs);
|
||||
break;
|
||||
case "t":
|
||||
case "tr":
|
||||
this.doStep(asArgs[0], asArgs[1]);
|
||||
break;
|
||||
case "u":
|
||||
this.doUnassemble(asArgs[1], asArgs[2], 8);
|
||||
break;
|
||||
case "x":
|
||||
this.doExecOptions(asArgs);
|
||||
break;
|
||||
case "?":
|
||||
this.doHelp();
|
||||
break;
|
||||
case "n":
|
||||
if (this.doInfo(asArgs)) break;
|
||||
/* falls through */
|
||||
default:
|
||||
if (!fQuiet) this.println("unknown command: " + sCmd);
|
||||
result = false;
|
||||
break;
|
||||
case "a":
|
||||
this.doAssemble(asArgs);
|
||||
break;
|
||||
case "b":
|
||||
this.doBreak(asArgs[0], asArgs[1]);
|
||||
break;
|
||||
case "c":
|
||||
this.doClear(asArgs[0]);
|
||||
break;
|
||||
case "d":
|
||||
this.doDump(asArgs[0], asArgs[1], asArgs[2]);
|
||||
break;
|
||||
case "e":
|
||||
this.doEdit(asArgs);
|
||||
break;
|
||||
case "f":
|
||||
this.doFreqs(asArgs[1]);
|
||||
break;
|
||||
case "g":
|
||||
this.doRun(asArgs[1]);
|
||||
break;
|
||||
case "h":
|
||||
this.doHalt(asArgs[1]);
|
||||
break;
|
||||
case "i":
|
||||
this.doInput(asArgs[1]);
|
||||
break;
|
||||
case "l":
|
||||
this.doLoad(asArgs);
|
||||
break;
|
||||
case "m":
|
||||
this.doMessages(asArgs);
|
||||
break;
|
||||
case "o":
|
||||
this.doOutput(asArgs[1], asArgs[2]);
|
||||
break;
|
||||
case "p":
|
||||
case "pr":
|
||||
this.doProcStep(asArgs[0]);
|
||||
break;
|
||||
case "r":
|
||||
this.doRegisters(asArgs);
|
||||
break;
|
||||
case "t":
|
||||
case "tr":
|
||||
this.doStep(asArgs[0], asArgs[1]);
|
||||
break;
|
||||
case "u":
|
||||
this.doUnassemble(asArgs[1], asArgs[2], 8);
|
||||
break;
|
||||
case "x":
|
||||
this.doExecOptions(asArgs);
|
||||
break;
|
||||
case "?":
|
||||
this.doHelp();
|
||||
break;
|
||||
case "n":
|
||||
if (this.doInfo(asArgs)) break;
|
||||
/* falls through */
|
||||
default:
|
||||
if (!fQuiet) this.println("unknown command: " + sCmd);
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -256,6 +256,13 @@ FDC.REG_STATUS.RQM = 0x80; // indicates FDC Data Register is ready
|
|||
FDC.REG_DATA = {};
|
||||
FDC.REG_DATA.PORT = 0x3F5;
|
||||
|
||||
/*
|
||||
* FDC "Fixed Disk" Register (0x3F6, write-only)
|
||||
*
|
||||
* Since this register's functions are all specific to the Hard Disk Controller, see the HDC component for details.
|
||||
* The fact that this HDC register is in the middle of the FDC I/O port range is an oddity of the "HFCOMBO" controller.
|
||||
*/
|
||||
|
||||
/*
|
||||
* FDC Digital Input Register (0x3F7, read-only, MODEL_5170 only)
|
||||
*
|
||||
|
|
@ -850,7 +857,17 @@ FDC.prototype.initDrive = function(drive, iDrive, data)
|
|||
* care to preserve any drive defaults that initController() already obtained for us, falling back to
|
||||
* bare minimums only when all else fails.
|
||||
*/
|
||||
data[1] = [FDC.DEFAULT_DRIVE_NAME, drive.nCylinders || 40, drive.nHeads || data[3], drive.nSectors || 9, drive.cbSector || 512, data[1]];
|
||||
data[1] = [
|
||||
FDC.DEFAULT_DRIVE_NAME, // a[0]
|
||||
drive.nCylinders || 40, // a[1]
|
||||
drive.nHeads || data[3],// a[2]
|
||||
drive.nSectors || 9, // a[3]
|
||||
drive.cbSector || 512, // a[4]
|
||||
data[1], // a[5]
|
||||
drive.nDiskCylinders, // a[6]
|
||||
drive.nDiskHeads, // a[7]
|
||||
drive.nDiskSectors // a[8]
|
||||
];
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -864,25 +881,24 @@ FDC.prototype.initDrive = function(drive, iDrive, data)
|
|||
/*
|
||||
* Some additional drive properties/defaults that are largely for the Disk component's benefit.
|
||||
*/
|
||||
drive.name = data[i][0];
|
||||
drive.nCylinders = data[i][1]; // cylinders
|
||||
drive.nHeads = data[i][2]; // heads/cylinders
|
||||
drive.nSectors = data[i][3]; // sectors/track
|
||||
drive.cbSector = data[i][4]; // bytes/sector
|
||||
drive.fRemovable = data[i][5];
|
||||
var a = data[i++];
|
||||
drive.name = a[0];
|
||||
drive.nCylinders = a[1]; // cylinders
|
||||
drive.nHeads = a[2]; // heads/cylinders
|
||||
drive.nSectors = a[3]; // sectors/track
|
||||
drive.cbSector = a[4]; // bytes/sector
|
||||
drive.fRemovable = a[5];
|
||||
/*
|
||||
* If we have current media parameters, restore them; otherwise, default to the drive's physical parameters.
|
||||
*/
|
||||
if (drive.nDiskCylinders = data[i][6]) {
|
||||
drive.nDiskHeads = data[i][7];
|
||||
drive.nDiskSectors = data[i][8];
|
||||
if (drive.nDiskCylinders = a[6]) {
|
||||
drive.nDiskHeads = a[7];
|
||||
drive.nDiskSectors = a[8];
|
||||
} else {
|
||||
drive.nDiskCylinders = drive.nCylinders;
|
||||
drive.nDiskHeads = drive.nHeads;
|
||||
drive.nDiskSectors = drive.nSectors;
|
||||
|
||||
}
|
||||
i++;
|
||||
|
||||
/*
|
||||
* The next group of properties are set by various FDC command sequences.
|
||||
|
|
|
|||
|
|
@ -218,20 +218,21 @@ HDC.ATC = {
|
|||
DATA: { PORT: 0x1F0}, // no register (read-write)
|
||||
DIAG: { // this.regError (read-only)
|
||||
PORT: 0x1F1,
|
||||
NO_ERROR: 0x01,
|
||||
CTRL_ERROR: 0x02,
|
||||
SEC_ERROR: 0x03,
|
||||
ECC_ERROR: 0x04,
|
||||
PROC_ERROR: 0x05
|
||||
NO_ERROR: 0x01,
|
||||
CTRL_ERROR: 0x02,
|
||||
SEC_ERROR: 0x03,
|
||||
ECC_ERROR: 0x04,
|
||||
PROC_ERROR: 0x05
|
||||
},
|
||||
ERROR: { // this.regError (read-only)
|
||||
PORT: 0x1F1,
|
||||
NO_DAM: 0x01, // Data Address Mark (DAM) not found
|
||||
NO_TRK0: 0x02, // Track 0 not detected
|
||||
CMD_ABORT: 0x04, // Aborted Command
|
||||
NO_CHS: 0x10, // ID field with the specified C:H:S not found
|
||||
ECC_ERR: 0x40, // Data ECC Error
|
||||
BAD_BLOCK: 0x80 // Bad Block Detect
|
||||
NONE: 0x00,
|
||||
NO_DAM: 0x01, // Data Address Mark (DAM) not found
|
||||
NO_TRK0: 0x02, // Track 0 not detected
|
||||
CMD_ABORT: 0x04, // Aborted Command
|
||||
NO_CHS: 0x10, // ID field with the specified C:H:S not found
|
||||
ECC_ERR: 0x40, // Data ECC Error
|
||||
BAD_BLOCK: 0x80 // Bad Block Detect
|
||||
},
|
||||
WPREC: { PORT: 0x1F1}, // this.regWPreC (write-only)
|
||||
SECCNT: { PORT: 0x1F2}, // this.regSecCnt (read-write; 0 implies a 256-sector request)
|
||||
|
|
@ -239,39 +240,46 @@ HDC.ATC = {
|
|||
CYLLO: { PORT: 0x1F4}, // this.regCylLo (read-write; all 8 bits are used)
|
||||
CYLHI: { // this.regCylHi (read-write; only bits 0-1 are used, for a total of 10 bits, or 1024 max cylinders)
|
||||
PORT: 0x1F5,
|
||||
MASK: 0x03
|
||||
MASK: 0x03
|
||||
},
|
||||
DRVHD: { // this.regDrvHd (read-write)
|
||||
PORT: 0x1F6,
|
||||
HEAD_MASK: 0x0F, // set this to the max number of heads before issuing a SET PARAMETERS command
|
||||
DRIVE_MASK: 0x10,
|
||||
SET_MASK: 0xE0,
|
||||
SET_BITS: 0xA0 // for whatever reason, these bits must always be set
|
||||
HEAD_MASK: 0x0F, // set this to the max number of heads before issuing a SET PARAMETERS command
|
||||
DRIVE_MASK: 0x10,
|
||||
SET_MASK: 0xE0,
|
||||
SET_BITS: 0xA0 // for whatever reason, these bits must always be set
|
||||
},
|
||||
STATUS: { // this.regStatus (read-only; reading clears IRQ.ATC)
|
||||
PORT: 0x1F7,
|
||||
BUSY: 0x80, // if this is set, no other STATUS bits are valid
|
||||
READY: 0x40, // if this is set (along with the SEEK_OK bit), the drive is ready to read/write/seek again
|
||||
WFAULT: 0x20, // write fault
|
||||
SEEK_OK: 0x10, // seek operation complete
|
||||
DATA_REQ: 0x08, // indicates that "the sector buffer requires servicing during a Read or Write command. If either bit 7 (BUSY) or this bit is active, a command is being executed. Upon receipt of any command, this bit is reset."
|
||||
CORRECTED: 0x04,
|
||||
INDEX: 0x02, // set once for every revolution of the disk
|
||||
ERROR: 0x01 // set when the previous command ended in an error; one or more bits are set in the ERROR register (the next command to the controller resets the ERROR bit)
|
||||
BUSY: 0x80, // if this is set, no other STATUS bits are valid
|
||||
READY: 0x40, // if this is set (along with the SEEK_OK bit), the drive is ready to read/write/seek again
|
||||
WFAULT: 0x20, // write fault
|
||||
SEEK_OK: 0x10, // seek operation complete
|
||||
DATA_REQ: 0x08, // indicates that "the sector buffer requires servicing during a Read or Write command. If either bit 7 (BUSY) or this bit is active, a command is being executed. Upon receipt of any command, this bit is reset."
|
||||
CORRECTED: 0x04,
|
||||
INDEX: 0x02, // set once for every revolution of the disk
|
||||
ERROR: 0x01 // set when the previous command ended in an error; one or more bits are set in the ERROR register (the next command to the controller resets the ERROR bit)
|
||||
},
|
||||
COMMAND:{ // this.regCommand (write-only)
|
||||
COMMAND: { // this.regCommand (write-only)
|
||||
PORT: 0x1F7,
|
||||
RESTORE: 0x10, // low nibble x 500us equal stepping rate (except for 0, which corresponds to 35us) (aka RECALIBRATE)
|
||||
READ_DATA: 0x20, // also supports NO_RETRIES and WITH_ECC
|
||||
WRITE_DATA: 0x30, // also supports NO_RETRIES and WITH_ECC
|
||||
READ_VERF: 0x40, // also supports NO_RETRIES
|
||||
FORMAT_TRK: 0x50,
|
||||
SEEK: 0x70, // low nibble x 500us equal stepping rate (except for 0, which corresponds to 35us)
|
||||
DIAGNOSE: 0x90,
|
||||
SETPARMS: 0x91,
|
||||
NO_RETRIES: 0x01,
|
||||
WITH_ECC: 0x02,
|
||||
MASK: 0xF0
|
||||
RESTORE: 0x10, // low nibble x 500us equal stepping rate (except for 0, which corresponds to 35us) (aka RECALIBRATE)
|
||||
READ_DATA: 0x20, // also supports NO_RETRIES and WITH_ECC
|
||||
WRITE_DATA: 0x30, // also supports NO_RETRIES and WITH_ECC
|
||||
READ_VERF: 0x40, // also supports NO_RETRIES
|
||||
FORMAT_TRK: 0x50,
|
||||
SEEK: 0x70, // low nibble x 500us equal stepping rate (except for 0, which corresponds to 35us)
|
||||
DIAGNOSE: 0x90,
|
||||
SETPARMS: 0x91,
|
||||
NO_RETRIES: 0x01,
|
||||
WITH_ECC: 0x02,
|
||||
MASK: 0xF0
|
||||
},
|
||||
FDR: { // this.regFDR
|
||||
PORT: 0x3F6,
|
||||
INT_DISABLE: 0x02, // a logical 0 enables fixed disk interrupts
|
||||
RESET: 0x04, // a logical 1 enables reset fixed disk function
|
||||
HS3: 0x08, // a logical 1 enables head select 3 (a logical 0 enables reduced write current)
|
||||
RESERVED: 0xF1
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -692,6 +700,7 @@ HDC.prototype.initController = function(data, fHard)
|
|||
this.regDrvHd = data[i++];
|
||||
this.regStatus = data[i++];
|
||||
this.regCommand = data[i++];
|
||||
this.regFDR = data[i++];
|
||||
/*
|
||||
* Additional state is maintained by the Drive object (eg, abSector, ibSector)
|
||||
*/
|
||||
|
|
@ -766,6 +775,7 @@ HDC.prototype.saveController = function()
|
|||
data[i++] = this.regDrvHd;
|
||||
data[i++] = this.regStatus;
|
||||
data[i++] = this.regCommand;
|
||||
data[i++] = this.regFDR;
|
||||
} else {
|
||||
data[i++] = this.regConfig;
|
||||
data[i++] = this.regStatus;
|
||||
|
|
@ -1410,7 +1420,7 @@ HDC.prototype.inATCData = function(port, addrFrom)
|
|||
var hdc = this;
|
||||
this.readByte(this.drive, function(b, fAsync) {
|
||||
if (b >= 0) {
|
||||
if (hdc.chipset) hdc.chipset.setIRR(ChipSet.IRQ.ATC);
|
||||
hdc.setATCIRR();
|
||||
/*
|
||||
* I shouldn't have to set BUSY (or DATA_REQ) again, because it should still be set, no?
|
||||
*/
|
||||
|
|
@ -1464,7 +1474,7 @@ HDC.prototype.outATCData = function(port, bOut, addrFrom)
|
|||
else if (this.drive.ibSector == this.drive.cbSector) {
|
||||
this.drive.nBytes -= this.drive.cbSector;
|
||||
this.regSecCnt = (this.regSecCnt - 1) & 0xff;
|
||||
if (this.chipset) this.chipset.setIRR(ChipSet.IRQ.ATC);
|
||||
this.setATCIRR();
|
||||
if (this.drive.nBytes >= this.drive.cbSector) {
|
||||
/*
|
||||
* I shouldn't have to set BUSY (or DATA_REQ) again, because it should still be set, no?
|
||||
|
|
@ -1715,6 +1725,28 @@ HDC.prototype.outATCCommand = function(port, bOut, addrFrom)
|
|||
this.doATCommand();
|
||||
};
|
||||
|
||||
/**
|
||||
* outATCFDR(port, bOut, addrFrom)
|
||||
*
|
||||
* This is referred to in IBM's docs as the "Fixed Disk Register" (write-only)
|
||||
*
|
||||
* @this {HDC}
|
||||
* @param {number} port (0x3F6)
|
||||
* @param {number} bOut
|
||||
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
|
||||
*/
|
||||
HDC.prototype.outATCFDR = function(port, bOut, addrFrom)
|
||||
{
|
||||
this.messagePort(port, bOut, addrFrom, "FDR");
|
||||
/*
|
||||
* I'm not really sure if I should set HDC.ATC.DIAG.NO_ERROR in regError after *every* write where
|
||||
* HDC.ATC.FDR.RESET is clear, or only after it has transitioned from set to clear; since the BIOS only
|
||||
* requires the latter, I'm going to be conservative and restrict regError updates to the latter.
|
||||
*/
|
||||
if ((this.regFDR & HDC.ATC.FDR.RESET) && !(bOut & HDC.ATC.FDR.RESET)) this.regError = HDC.ATC.DIAG.NO_ERROR;
|
||||
this.regFDR = bOut;
|
||||
};
|
||||
|
||||
/**
|
||||
* doATCommand()
|
||||
*
|
||||
|
|
@ -1734,7 +1766,7 @@ HDC.prototype.doATCommand = function()
|
|||
var nSectors = this.regSecCnt || 256;
|
||||
|
||||
this.drive = null;
|
||||
this.regError = 0;
|
||||
this.regError = HDC.ATC.ERROR.NONE;
|
||||
this.regStatus = HDC.ATC.STATUS.READY | HDC.ATC.STATUS.SEEK_OK;
|
||||
|
||||
var drive = this.aDrives[iDrive];
|
||||
|
|
@ -1775,7 +1807,7 @@ HDC.prototype.doATCommand = function()
|
|||
*/
|
||||
this.readByte(drive, function(b, fAsync) {
|
||||
if (b >= 0 && hdc.chipset) {
|
||||
hdc.chipset.setIRR(ChipSet.IRQ.ATC);
|
||||
hdc.setATCIRR();
|
||||
/*
|
||||
* As with the WRITE_DATA command, I'm not sure which of BUSY and DATA_REQ (or both)
|
||||
* should be set here, so I'm setting both of them for now.
|
||||
|
|
@ -1793,16 +1825,16 @@ HDC.prototype.doATCommand = function()
|
|||
break;
|
||||
|
||||
case HDC.ATC.COMMAND.WRITE_DATA:
|
||||
if (hdc.chipset) {
|
||||
hdc.chipset.setIRR(ChipSet.IRQ.ATC);
|
||||
if (this.chipset) {
|
||||
this.setATCIRR();
|
||||
/*
|
||||
* I know that DATA_REQ must be set at this point, but I'm not sure about BUSY; so I'm
|
||||
* setting both of them for now.
|
||||
*/
|
||||
hdc.regStatus = HDC.ATC.STATUS.BUSY | HDC.ATC.STATUS.DATA_REQ;
|
||||
this.regStatus = HDC.ATC.STATUS.BUSY | HDC.ATC.STATUS.DATA_REQ;
|
||||
} else {
|
||||
hdc.regStatus = HDC.ATC.STATUS.ERROR;
|
||||
hdc.regError = HDC.ATC.ERROR.CMD_ABORT;
|
||||
this.regStatus = HDC.ATC.STATUS.ERROR;
|
||||
this.regError = HDC.ATC.ERROR.CMD_ABORT;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -1853,7 +1885,19 @@ HDC.prototype.doATCommand = function()
|
|||
break;
|
||||
}
|
||||
|
||||
if (fInterrupt && this.chipset) this.chipset.setIRR(ChipSet.IRQ.ATC);
|
||||
if (fInterrupt) this.setATCIRR();
|
||||
};
|
||||
|
||||
/**
|
||||
* setATCIRR()
|
||||
*
|
||||
* Raise the ATC's IRQ, provided ATC interrupts are enabled.
|
||||
*
|
||||
* @this {HDC}
|
||||
*/
|
||||
HDC.prototype.setATCIRR = function()
|
||||
{
|
||||
if (this.chipset && !(this.regFDR & HDC.ATC.FDR.INT_DISABLE)) this.chipset.setIRR(ChipSet.IRQ.ATC);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -2723,7 +2767,8 @@ HDC.aATCPortOutput = {
|
|||
0x1F4: HDC.prototype.outATCCylLo,
|
||||
0x1F5: HDC.prototype.outATCCylHi,
|
||||
0x1F6: HDC.prototype.outATCDrvHd,
|
||||
0x1F7: HDC.prototype.outATCCommand
|
||||
0x1F7: HDC.prototype.outATCCommand,
|
||||
0x3F6: HDC.prototype.outATCFDR
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1955,8 +1955,9 @@ Video.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
|
||||
if (DEBUGGER && dbg) {
|
||||
var video = this;
|
||||
dbg.messageInit(Video);
|
||||
this.cpu.addIntNotify(Video.BIOS.INT_VIDEO, this, this.intBIOSVideo);
|
||||
dbg.messageDump(dbg.MESSAGE_VIDEO, function onDumpVideo(sParm) {
|
||||
dbg.messageDump(Video.MESSAGE_VIDEO, function onDumpVideo(sParm) {
|
||||
video.dumpVideo(sParm);
|
||||
});
|
||||
}
|
||||
|
|
@ -1989,7 +1990,7 @@ Video.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
Video.prototype.intBIOSVideo = function(addr)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
if (this.dbg && this.dbg.messageEnabled(this.dbg.MESSAGE_VIDEO)) {
|
||||
if (this.dbg && this.dbg.messageEnabled(Video.MESSAGE_VIDEO | Video.MESSAGE_INT)) {
|
||||
this.dbg.messageInt(Video.BIOS.INT_VIDEO, addr);
|
||||
this.cpu.addIntReturn(addr, function (video, nCycles) {
|
||||
return function onBIOSVideoReturn(nLevel) {
|
||||
|
|
@ -3742,7 +3743,7 @@ Video.prototype.updateChar = function(col, row, data, context)
|
|||
this.contextScreen.fillRect(xDst, yDst, this.cxScreenCell, this.cyScreenCell);
|
||||
}
|
||||
|
||||
if (MAXDEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(this.dbg.MESSAGE_VIDEO | this.dbg.MESSAGE_LOG)) {
|
||||
if (MAXDEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(Video.MESSAGE_VIDEO | Video.MESSAGE_LOG)) {
|
||||
this.log("updateCharBgnd(" + col + "," + row + "," + bChar + "): filled " + xDst + "," + yDst);
|
||||
}
|
||||
|
||||
|
|
@ -3753,7 +3754,7 @@ Video.prototype.updateChar = function(col, row, data, context)
|
|||
var xSrcFgnd = (bChar & 0xf) * font.cxCell;
|
||||
var ySrcFgnd = (bChar >> 4) * font.cyCell;
|
||||
|
||||
if (MAXDEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(this.dbg.MESSAGE_VIDEO | this.dbg.MESSAGE_LOG)) {
|
||||
if (MAXDEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(Video.MESSAGE_VIDEO | Video.MESSAGE_LOG)) {
|
||||
this.log("updateCharFgnd(" + col + "," + row + "," + bChar + "): draw from " + xSrcFgnd + "," + ySrcFgnd + " (" + font.cxCell + "," + font.cyCell + ") to " + xDst + "," + yDst);
|
||||
}
|
||||
|
||||
|
|
@ -4121,9 +4122,10 @@ Video.prototype.updateScreenGraphicsEGA = function(addrScreen, addrScreenLimit)
|
|||
* whereas bit-wise operations operate ONLY on the low 32 bits.
|
||||
*
|
||||
* This can be confirmed by looking at dwPixel.toString(16), which returns "-80000000". The solution
|
||||
* is to check for a negative dwPixel and make it positive.
|
||||
* is to add 4294967296 (0x100000000) to any negative 32-bit value for which you need the positive
|
||||
* representation instead.
|
||||
*/
|
||||
if (dwPixel < 0) dwPixel = -dwPixel;
|
||||
if (dwPixel < 0) dwPixel += 0x100000000;
|
||||
/*
|
||||
* Since assertions don't fix problems (only catch them, and only in DEBUG builds), I'm also insuring
|
||||
* that bPixel will always default to 0 if an undefined value ever slips through again.
|
||||
|
|
@ -4925,7 +4927,7 @@ Video.prototype.dumpVideo = function(sParm)
|
|||
Video.prototype.messageDebugger = function(sMessage, fForce)
|
||||
{
|
||||
if (DEBUGGER && this.dbg) {
|
||||
if (fForce || this.dbg.messageEnabled(this.dbg.MESSAGE_VIDEO)) {
|
||||
if (fForce || this.dbg.messageEnabled(Video.MESSAGE_VIDEO)) {
|
||||
this.dbg.message(sMessage);
|
||||
}
|
||||
}
|
||||
|
|
@ -4946,7 +4948,7 @@ Video.prototype.messageDebugger = function(sMessage, fForce)
|
|||
Video.prototype.messagePort = function(port, bOut, addrFrom, name, bIn)
|
||||
{
|
||||
if (DEBUGGER && this.dbg) {
|
||||
this.dbg.messagePort(this, port, bOut, addrFrom, name, this.dbg.MESSAGE_VIDEO, bIn);
|
||||
this.dbg.messagePort(this, port, bOut, addrFrom, name, Video.MESSAGE_VIDEO, bIn);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ str.isValidInt = function(s, base)
|
|||
|
||||
/**
|
||||
* parseInt(s, base)
|
||||
*
|
||||
*
|
||||
* This is a wrapper around the built-in parseInt() function, which recognizes certain prefixes (eg,
|
||||
* '$' or "0x" for hex) and suffixes (eg, 'h' for hex or '.' for decimal), and then calls isValidInt()
|
||||
* to ensure we don't get partial values (see isValidInt() for details).
|
||||
|
|
@ -107,7 +107,7 @@ str.parseInt = function(s, base)
|
|||
* NOTE: The following work-around (adapted from code found on StackOverflow) would be another solution,
|
||||
* taking care of negative values, zero-padding, and upper-casing, but not undefined/NaN values:
|
||||
*
|
||||
* s = (n < 0? (0xffffffff + n + 1) : n).toString(16);
|
||||
* s = (n < 0? n + 0x100000000 : n).toString(16);
|
||||
* s = "00000000".substr(0, 8 - s.length) + s;
|
||||
* s = s.substr(0, cch).toUpperCase();
|
||||
*
|
||||
|
|
@ -172,16 +172,16 @@ str.toHexWord = function(w)
|
|||
str.toHexAddr = function(off, sel)
|
||||
{
|
||||
if (sel !== undefined) {
|
||||
return str.toHexWord(sel) + ":" + str.toHexWord(off);
|
||||
return str.toHexWord(sel) + ":" + str.toHexWord(off);
|
||||
}
|
||||
return str.toHex(off);
|
||||
};
|
||||
|
||||
/**
|
||||
* getBaseName(sFileName, fStripExt)
|
||||
*
|
||||
*
|
||||
* This is a poor-man's version of Node's path.basename(), which Node-only components should use instead.
|
||||
*
|
||||
*
|
||||
* Note that fStripExt can be used to strip ANY extension, whereas path.basename() will strip the extension only
|
||||
* if it matches the second parameter (eg, path.basename("/foo/bar/baz/asdf/quux.html", ".html") returns "quux").
|
||||
*
|
||||
|
|
@ -260,7 +260,7 @@ str.escapeHTML = function(sHTML)
|
|||
|
||||
/**
|
||||
* replaceAll(sFind, sReplace, s)
|
||||
*
|
||||
*
|
||||
* @param {string} sFind
|
||||
* @param {string} sReplace
|
||||
* @param {string} s
|
||||
|
|
@ -284,7 +284,16 @@ str.replaceArray = function(a, s)
|
|||
{
|
||||
var sMatch = "";
|
||||
for (var k in a) {
|
||||
k = k.replace(/([\\\[\]\*\{}\(\)\.\+\?])/g, "\\$1");
|
||||
/*
|
||||
* As noted in:
|
||||
*
|
||||
* http://www.regexguru.com/2008/04/escape-characters-only-when-necessary/
|
||||
*
|
||||
* inside character classes, only backslash, caret, hyphen and the closing bracket need to be
|
||||
* escaped. And in fact, if you ensure that the closing bracket is first, the caret is not first,
|
||||
* and the hyphen is last, you can avoid escaping those as well.
|
||||
*/
|
||||
k = k.replace(/([\\[\]*{}().+?])/g, "\\$1");
|
||||
sMatch += (sMatch? '|' : '') + k;
|
||||
}
|
||||
return s.replace(new RegExp('(' + sMatch + ')', "g"), function(m) {
|
||||
|
|
@ -294,7 +303,7 @@ str.replaceArray = function(a, s)
|
|||
|
||||
/**
|
||||
* pad(s, cch)
|
||||
*
|
||||
*
|
||||
* Note that the maximum amount of padding currently supported is 40 spaces.
|
||||
*
|
||||
* @param {string} s is a string
|
||||
|
|
@ -308,7 +317,7 @@ str.pad = function(s, cch)
|
|||
|
||||
/**
|
||||
* trim(s)
|
||||
*
|
||||
*
|
||||
* @param {string} s
|
||||
* @returns {string}
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue