Fixed 5170 diskette drive detection (40-track vs. 80-track drives)
This commit is contained in:
parent
a474ddfa74
commit
367db06996
16 changed files with 1498 additions and 1263 deletions
|
|
@ -128,8 +128,8 @@ if (typeof module !== 'undefined') {
|
|||
*
|
||||
* MODEL_5170 Description
|
||||
* ---------- -----------
|
||||
* 070 [3] CMOS Address ChipSet.CMOS_ADDR.PORT
|
||||
* 071 CMOS Data ChipSet.CMOS_DATA.PORT
|
||||
* 070 [3] CMOS Address ChipSet.CMOS.ADDR.PORT
|
||||
* 071 CMOS Data ChipSet.CMOS.DATA.PORT
|
||||
* 0F0 Coprocessor Clear Busy (output 0x00)
|
||||
* 0F1 Coprocessor Reset (output 0x00)
|
||||
*
|
||||
|
|
@ -178,6 +178,10 @@ function ChipSet(parmsChipSet)
|
|||
this.cDMACs = this.cPICs = 1;
|
||||
if (this.model >= ChipSet.MODEL_5170) {
|
||||
this.cDMACs = this.cPICs = 2;
|
||||
this.regsHFCombo = {
|
||||
bCtrl: 0x00, // port 0x1F4
|
||||
bStatus: 0x7F // port 0x1F7
|
||||
};
|
||||
}
|
||||
this.fScaleTimers = parmsChipSet['scaleTimers'] || false;
|
||||
this.sRTCDate = parmsChipSet['rtcDate'];
|
||||
|
|
@ -220,7 +224,7 @@ ChipSet.MODEL_5160 = 5160;
|
|||
ChipSet.MODEL_5170 = 5170;
|
||||
|
||||
/*
|
||||
* Values returned by ChipSet.getSW1VideoMonitor()
|
||||
* Values returned by ChipSet.getSWVideoMonitor()
|
||||
*/
|
||||
ChipSet.MONITOR = {};
|
||||
ChipSet.MONITOR.NONE = 0;
|
||||
|
|
@ -514,7 +518,7 @@ ChipSet.PPI_CTRL.A_MODE = 0x60;
|
|||
/*
|
||||
* On the MODEL_5150, the following PPI_SW bits are exposed through PPI_A.
|
||||
*
|
||||
* On the MODEL_5160, either the low or high 4 bits are exposed through PPI_C_SW, if PPI_B.ENABLE_SW_HI is clear or set.
|
||||
* On the MODEL_5160, either the low or high 4 bits are exposed through PPI_C.SW, if PPI_B.ENABLE_SW_HI is clear or set.
|
||||
*/
|
||||
ChipSet.PPI_SW = {};
|
||||
ChipSet.PPI_SW.FDRIVE = {};
|
||||
|
|
@ -533,7 +537,7 @@ ChipSet.PPI_SW.MONITOR.CGA80 = 0x20;
|
|||
ChipSet.PPI_SW.MONITOR.MDA = 0x30;
|
||||
ChipSet.PPI_SW.MONITOR.MASK = 0x30;
|
||||
ChipSet.PPI_SW.MONITOR.SHIFT = 4;
|
||||
ChipSet.PPI_SW.FDRIVE.ONE = 0x00; // 1 floppy drive attached (or 0 drives if PPI_SW_FDRIVE_IPL is not set -- MODEL_5150 only)
|
||||
ChipSet.PPI_SW.FDRIVE.ONE = 0x00; // 1 floppy drive attached (or 0 drives if PPI_SW.FDRIVE_IPL is not set -- MODEL_5150 only)
|
||||
ChipSet.PPI_SW.FDRIVE.TWO = 0x40; // 2 floppy drives attached
|
||||
ChipSet.PPI_SW.FDRIVE.THREE = 0x80; // 3 floppy drives attached
|
||||
ChipSet.PPI_SW.FDRIVE.FOUR = 0xC0; // 4 floppy drives attached
|
||||
|
|
@ -558,7 +562,7 @@ ChipSet.PPI_SW.FDRIVE.SHIFT = 6;
|
|||
* the same register (bPPIB) but install different I/O handlers. It's also bi-directional: at one point, the BIOS
|
||||
* reads KBD_RWREG.REFRESH_BIT (bit 4) to verify that it's alternating.
|
||||
*
|
||||
* PPI_C and PPI_CTRL are neither documented nor used by the MODEL_5170 BIOS, so I'm assuming they're obsolete.
|
||||
* PPI_C and PPI_CTRL don't seem to be documented or used by the MODEL_5170 BIOS, so I'm assuming they're obsolete.
|
||||
*
|
||||
* NOTE: For more information on the 8042 Controller, including information on undocumented commands, refer to the
|
||||
* documents in /devices/pc/keyboard/, as well as the following websites:
|
||||
|
|
@ -566,40 +570,36 @@ ChipSet.PPI_SW.FDRIVE.SHIFT = 6;
|
|||
* http://halicery.com/8042/8042_INTERN_TXT.htm
|
||||
* http://www.os2museum.com/wp/?p=589 ("IBM PC/AT 8042 Keyboard Controller Commands")
|
||||
*/
|
||||
ChipSet.KBD_DATA = { // this.b8042OutBuff (PPI_A on previous models, still referred to as "PORT A" by the MODEL_5170 BIOS)
|
||||
PORT: 0x60
|
||||
ChipSet.KBC = {};
|
||||
ChipSet.KBC.DATA = { // this.b8042OutBuff (PPI_A on previous models, still referred to as "PORT A" by the MODEL_5170 BIOS)
|
||||
PORT: 0x60,
|
||||
SELF_TEST: { // result of ChipSet.KBC.CMD.SELF_TEST command (0xAA)
|
||||
OK: 0x55
|
||||
},
|
||||
INTF_TEST: { // result of ChipSet.KBC.CMD.INTF_TEST command (0xAB)
|
||||
OK: 0x00, // no error
|
||||
CLOCK_LO: 0x01, // keyboard clock line stuck low
|
||||
CLOCK_HI: 0x02, // keyboard clock line stuck high
|
||||
DATA_LO: 0x03, // keyboard data line stuck low
|
||||
DATA_HI: 0x04 // keyboard data line stuck high
|
||||
}
|
||||
};
|
||||
|
||||
ChipSet.KBD_DATA.CMD = { // this.b8042CmdData (KBD_DATA.CMD "data bytes" written to port 0x60, after writing a KBD_CMD byte to port 0x64)
|
||||
ChipSet.KBC.DATA.CMD = { // this.b8042CmdData (KBD_DATA.CMD "data bytes" written to port 0x60, after writing a KBD_CMD byte to port 0x64)
|
||||
PC_COMPAT: 0x40, // generate IBM PC-compatible scan codes
|
||||
PC_MODE: 0x20,
|
||||
NO_CLOCK: 0x10, // disable keyboard by driving "clock" line low
|
||||
NO_INHIBIT: 0x08, // disable inhibit function
|
||||
SYS_FLAG: 0x04, // this value is propagated to ChipSet.KBD_STATUS.SYS_FLAG
|
||||
SYS_FLAG: 0x04, // this value is propagated to ChipSet.KBC.STATUS.SYS_FLAG
|
||||
INT_ENABLE: 0x01 // generate an interrupt when the controller places data in the output buffer
|
||||
};
|
||||
|
||||
ChipSet.KBD_DATA.SELF_TEST = {
|
||||
OK: 0x55
|
||||
};
|
||||
|
||||
ChipSet.KBD_DATA.INTF_TEST = { // result of ChipSet.KBD_CMD.INTF_TEST command (0xAB)
|
||||
OK: 0x00, // no error
|
||||
KBD_CLOCK_LO: 0x01, // keyboard clock line stuck low
|
||||
KBD_CLOCK_HI: 0x02, // keyboard clock line stuck high
|
||||
KBD_DATA_LO: 0x03, // keyboard data line stuck low
|
||||
KBD_DATA_HI: 0x04 // keyboard data line stuck high
|
||||
};
|
||||
|
||||
ChipSet.KBD_DATA.INPORT = { // this.b8042InPort
|
||||
ChipSet.KBC.INPORT = { // this.b8042InPort
|
||||
UNDEFINED: 0x0F, // undefined
|
||||
ENABLE_256KB: 0x10, // enable 2nd 256Kb of system board RAM
|
||||
MFG_OFF: 0x20, // manufacturing jumper not installed
|
||||
MONO: 0x40, // monochrome monitor is primary display
|
||||
KBD_ON: 0x80 // keyboard not inhibited
|
||||
};
|
||||
|
||||
ChipSet.KBD_DATA.OUTPORT = { // this.b8042OutPort
|
||||
ChipSet.KBC.OUTPORT = { // this.b8042OutPort
|
||||
NO_RESET: 0x01, // set by default
|
||||
A20_ON: 0x02, // set by default
|
||||
OUTBUFF_FULL: 0x10, // output buffer full
|
||||
|
|
@ -607,13 +607,11 @@ ChipSet.KBD_DATA.OUTPORT = { // this.b8042OutPort
|
|||
KBD_CLOCK: 0x40, // keyboard clock (output)
|
||||
KBD_DATA: 0x80 // keyboard data (output)
|
||||
};
|
||||
|
||||
ChipSet.KBD_DATA.TESTPORT = { // generated "on the fly"
|
||||
ChipSet.KBC.TESTPORT = { // generated "on the fly"
|
||||
KBD_CLOCK: 0x01, // keyboard clock (input)
|
||||
KBD_DATA: 0x02 // keyboard data (input)
|
||||
};
|
||||
|
||||
ChipSet.KBD_RWREG = { // this.bPPIB (since CLK_TIMER2 and SPK_TIMER2 are in both PPI_B and KBD_RWREG)
|
||||
ChipSet.KBC.RWREG = { // this.bPPIB (since CLK_TIMER2 and SPK_TIMER2 are in both PPI_B and KBD_RWREG)
|
||||
PORT: 0x61,
|
||||
CLK_TIMER2: 0x01, // set to enable clock to TIMER2
|
||||
SPK_TIMER2: 0x02, // set to connect output of TIMER2 to speaker
|
||||
|
|
@ -623,17 +621,7 @@ ChipSet.KBD_RWREG = { // this.bPPIB (since CLK_TIMER2 and SPK_TIMER2 a
|
|||
PARITY_CHK: 0x80, // indicates RAM parity check
|
||||
PARITY_ERR: 0xC0
|
||||
};
|
||||
|
||||
ChipSet.KBD_DATA.CMD = { // this.b8042CmdData (KBD_DATA.CMD "data bytes" written to port 0x60, after writing a KBD_CMD byte to port 0x64)
|
||||
PC_COMPAT: 0x40, // generate IBM PC-compatible scan codes
|
||||
PC_MODE: 0x20,
|
||||
NO_CLOCK: 0x10, // disable keyboard by driving "clock" line low
|
||||
NO_INHIBIT: 0x08, // disable inhibit function
|
||||
SYS_FLAG: 0x04, // this value is propagated to ChipSet.KBD_STATUS.SYS_FLAG
|
||||
INT_ENABLE: 0x01 // generate an interrupt when the controller places data in the output buffer
|
||||
};
|
||||
|
||||
ChipSet.KBD_CMD = { // this.b8042InBuff (on write to port 0x64, interpret this as a CMD)
|
||||
ChipSet.KBC.CMD = { // this.b8042InBuff (on write to port 0x64, interpret this as a CMD)
|
||||
PORT: 0x64,
|
||||
READ_CMD: 0x20,
|
||||
WRITE_CMD: 0x60, // followed by a command byte written to KBD_DATA.PORT (see KBD_DATA.CMD)
|
||||
|
|
@ -648,8 +636,7 @@ ChipSet.KBD_CMD = { // this.b8042InBuff (on write to port 0x64, inte
|
|||
READ_TEST: 0xE0,
|
||||
PULSE_OUTPORT: 0xF0 // this is the 1st of 16 commands (0xF0-0xFF) that pulse bits 0-3 of the output port
|
||||
};
|
||||
|
||||
ChipSet.KBD_STATUS = { // this.b8042Status (on read from port 0x64)
|
||||
ChipSet.KBC.STATUS = { // this.b8042Status (on read from port 0x64)
|
||||
PORT: 0x64,
|
||||
OUTBUFF_FULL: 0x01,
|
||||
INBUFF_FULL: 0x02, // set if the controller has received but not yet read data written to the input buffer (not normally set)
|
||||
|
|
@ -665,90 +652,89 @@ ChipSet.KBD_STATUS = { // this.b8042Status (on read from port 0x64)
|
|||
/*
|
||||
* MC146818A RTC/CMOS Ports (MODEL_5170)
|
||||
*
|
||||
* Write a CMOS address to ChipSet.CMOS_ADDR.PORT, then read/write data from/to ChipSet.CMOS_DATA.PORT.
|
||||
* Write a CMOS address to ChipSet.CMOS.ADDR.PORT, then read/write data from/to ChipSet.CMOS.DATA.PORT.
|
||||
*
|
||||
* The ADDR port also controls NMI: write an address with bit 7 clear to enable NMI or set to disable NMI.
|
||||
*/
|
||||
ChipSet.CMOS_ADDR = {}; // this.bCMOSAddr
|
||||
ChipSet.CMOS_ADDR.PORT = 0x70;
|
||||
ChipSet.CMOS_ADDR.RTC_SEC = 0x00;
|
||||
ChipSet.CMOS_ADDR.RTC_SEC_ALRM = 0x01;
|
||||
ChipSet.CMOS_ADDR.RTC_MIN = 0x02;
|
||||
ChipSet.CMOS_ADDR.RTC_MIN_ALRM = 0x03;
|
||||
ChipSet.CMOS_ADDR.RTC_HOUR = 0x04;
|
||||
ChipSet.CMOS_ADDR.RTC_HOUR_ALRM = 0x05;
|
||||
ChipSet.CMOS_ADDR.RTC_WEEK_DAY = 0x06;
|
||||
ChipSet.CMOS_ADDR.RTC_MONTH_DAY = 0x07;
|
||||
ChipSet.CMOS_ADDR.RTC_MONTH = 0x08;
|
||||
ChipSet.CMOS_ADDR.RTC_YEAR = 0x09;
|
||||
ChipSet.CMOS_ADDR.RTC_STATUSA = 0x0A;
|
||||
ChipSet.CMOS_ADDR.RTC_STATUSB = 0x0B;
|
||||
ChipSet.CMOS_ADDR.RTC_STATUSC = 0x0C;
|
||||
ChipSet.CMOS_ADDR.RTC_STATUSD = 0x0D;
|
||||
ChipSet.CMOS_ADDR.DIAG = 0x0E;
|
||||
ChipSet.CMOS_ADDR.SHUTDOWN = 0x0F;
|
||||
ChipSet.CMOS_ADDR.FDRIVE = 0x10;
|
||||
ChipSet.CMOS_ADDR.HDRIVE = 0x12;
|
||||
ChipSet.CMOS_ADDR.EQUIP = 0x14;
|
||||
ChipSet.CMOS_ADDR.BASEMEM_LO = 0x15;
|
||||
ChipSet.CMOS_ADDR.BASEMEM_HI = 0x16; //the BASEMEM values indicate the total Kb of base memory, up to 0x280 (640Kb)
|
||||
ChipSet.CMOS_ADDR.EXTMEM_LO = 0x17;
|
||||
ChipSet.CMOS_ADDR.EXTMEM_HI = 0x18; //the EXTMEM values indicate the total Kb of extended memory, up to 0x3C00 (15Mb)
|
||||
ChipSet.CMOS_ADDR.CHKSUM_HI = 0x2E;
|
||||
ChipSet.CMOS_ADDR.CHKSUM_LO = 0x2F; // CMOS bytes included in the checksum calculation: 0x10-0x2D
|
||||
ChipSet.CMOS_ADDR.EXTMEM2_LO = 0x30;
|
||||
ChipSet.CMOS_ADDR.EXTMEM2_HI = 0x31;
|
||||
ChipSet.CMOS_ADDR.CENTURY_DATE = 0x32; // BCD value for the current century (eg, 0x19 for 20th century, 0x20 for 21st century)
|
||||
ChipSet.CMOS_ADDR.BOOT_INFO = 0x33; // 0x80 if 128Kb expansion memory installed, 0x40 if Setup Utility wants an initial setup message
|
||||
ChipSet.CMOS_ADDR.MASK = 0x3F;
|
||||
ChipSet.CMOS_ADDR.TOTAL = 0x40;
|
||||
ChipSet.CMOS_ADDR.NMI_DISABLE = 0x80;
|
||||
ChipSet.CMOS = {};
|
||||
ChipSet.CMOS.ADDR = {}; // this.bCMOSAddr
|
||||
ChipSet.CMOS.ADDR.PORT = 0x70;
|
||||
ChipSet.CMOS.ADDR.RTC_SEC = 0x00;
|
||||
ChipSet.CMOS.ADDR.RTC_SEC_ALRM = 0x01;
|
||||
ChipSet.CMOS.ADDR.RTC_MIN = 0x02;
|
||||
ChipSet.CMOS.ADDR.RTC_MIN_ALRM = 0x03;
|
||||
ChipSet.CMOS.ADDR.RTC_HOUR = 0x04;
|
||||
ChipSet.CMOS.ADDR.RTC_HOUR_ALRM = 0x05;
|
||||
ChipSet.CMOS.ADDR.RTC_WEEK_DAY = 0x06;
|
||||
ChipSet.CMOS.ADDR.RTC_MONTH_DAY = 0x07;
|
||||
ChipSet.CMOS.ADDR.RTC_MONTH = 0x08;
|
||||
ChipSet.CMOS.ADDR.RTC_YEAR = 0x09;
|
||||
ChipSet.CMOS.ADDR.RTC_STATUSA = 0x0A;
|
||||
ChipSet.CMOS.ADDR.RTC_STATUSB = 0x0B;
|
||||
ChipSet.CMOS.ADDR.RTC_STATUSC = 0x0C;
|
||||
ChipSet.CMOS.ADDR.RTC_STATUSD = 0x0D;
|
||||
ChipSet.CMOS.ADDR.DIAG = 0x0E;
|
||||
ChipSet.CMOS.ADDR.SHUTDOWN = 0x0F;
|
||||
ChipSet.CMOS.ADDR.FDRIVE = 0x10; // drive 0 ChipSet.FDRIVE in high nibble, drive 1 ChipSet.FDRIVE value in low nibble
|
||||
ChipSet.CMOS.ADDR.HDRIVE = 0x12;
|
||||
ChipSet.CMOS.ADDR.EQUIP = 0x14;
|
||||
ChipSet.CMOS.ADDR.BASEMEM_LO = 0x15;
|
||||
ChipSet.CMOS.ADDR.BASEMEM_HI = 0x16; // the BASEMEM values indicate the total Kb of base memory, up to 0x280 (640Kb)
|
||||
ChipSet.CMOS.ADDR.EXTMEM_LO = 0x17;
|
||||
ChipSet.CMOS.ADDR.EXTMEM_HI = 0x18; // the EXTMEM values indicate the total Kb of extended memory, up to 0x3C00 (15Mb)
|
||||
ChipSet.CMOS.ADDR.CHKSUM_HI = 0x2E;
|
||||
ChipSet.CMOS.ADDR.CHKSUM_LO = 0x2F; // CMOS bytes included in the checksum calculation: 0x10-0x2D
|
||||
ChipSet.CMOS.ADDR.EXTMEM2_LO = 0x30;
|
||||
ChipSet.CMOS.ADDR.EXTMEM2_HI = 0x31;
|
||||
ChipSet.CMOS.ADDR.CENTURY_DATE = 0x32; // BCD value for the current century (eg, 0x19 for 20th century, 0x20 for 21st century)
|
||||
ChipSet.CMOS.ADDR.BOOT_INFO = 0x33; // 0x80 if 128Kb expansion memory installed, 0x40 if Setup Utility wants an initial setup message
|
||||
ChipSet.CMOS.ADDR.MASK = 0x3F;
|
||||
ChipSet.CMOS.ADDR.TOTAL = 0x40;
|
||||
ChipSet.CMOS.ADDR.NMI_DISABLE = 0x80;
|
||||
|
||||
ChipSet.CMOS_DATA = {}; // this.abCMOSData
|
||||
ChipSet.CMOS_DATA.PORT = 0x71;
|
||||
ChipSet.CMOS.DATA = {}; // this.abCMOSData
|
||||
ChipSet.CMOS.DATA.PORT = 0x71;
|
||||
|
||||
ChipSet.CMOS_STATUSA = {}; // abCMOSData[ChipSet.CMOS_ADDR.RTC_STATUSA]
|
||||
ChipSet.CMOS_STATUSA.UIP = 0x80; // bit 7: 1 indicates Update-In-Progress, 0 indicates date/time ready to read
|
||||
ChipSet.CMOS_STATUSA.DV = 0x70; // bits 6-4 (DV2-DV0) are programmed to 010 to select a 32.768Khz time base
|
||||
ChipSet.CMOS_STATUSA.RS = 0x0F; // bits 3-0 (RS3-RS0) are programmed to 0110 to select a 976.562us interrupt rate
|
||||
ChipSet.CMOS.STATUSA = {}; // abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSA]
|
||||
ChipSet.CMOS.STATUSA.UIP = 0x80; // bit 7: 1 indicates Update-In-Progress, 0 indicates date/time ready to read
|
||||
ChipSet.CMOS.STATUSA.DV = 0x70; // bits 6-4 (DV2-DV0) are programmed to 010 to select a 32.768Khz time base
|
||||
ChipSet.CMOS.STATUSA.RS = 0x0F; // bits 3-0 (RS3-RS0) are programmed to 0110 to select a 976.562us interrupt rate
|
||||
|
||||
ChipSet.CMOS_STATUSB = {}; // abCMOSData[ChipSet.CMOS_ADDR.RTC_STATUSB]
|
||||
ChipSet.CMOS_STATUSB.SET = 0x80; // bit 7: 1 to set any/all of the 14 time-bytes
|
||||
ChipSet.CMOS_STATUSB.PIE = 0x40; // bit 6: 1 for Periodic Interrupt Enable
|
||||
ChipSet.CMOS_STATUSB.AIE = 0x20; // bit 5: 1 for Alarm Interrupt Enable
|
||||
ChipSet.CMOS_STATUSB.UIE = 0x10; // bit 4: 1 for Update-Ended Interrupt Enable
|
||||
ChipSet.CMOS_STATUSB.SQWE = 0x08; // bit 3: 1 for Square Wave Enabled (as set by the STATUSA rate selection bits)
|
||||
ChipSet.CMOS_STATUSB.BINARY = 0x04; // bit 2: 1 for binary Date Mode, 0 for BCD Date Mode
|
||||
ChipSet.CMOS_STATUSB.HOUR24 = 0x02; // bit 1: 1 for 24-hour mode, 0 for 12-hour mode
|
||||
ChipSet.CMOS_STATUSB.DST = 0x01; // bit 0: 1 for Daylight Savings Time enabled
|
||||
ChipSet.CMOS.STATUSB = {}; // abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSB]
|
||||
ChipSet.CMOS.STATUSB.SET = 0x80; // bit 7: 1 to set any/all of the 14 time-bytes
|
||||
ChipSet.CMOS.STATUSB.PIE = 0x40; // bit 6: 1 for Periodic Interrupt Enable
|
||||
ChipSet.CMOS.STATUSB.AIE = 0x20; // bit 5: 1 for Alarm Interrupt Enable
|
||||
ChipSet.CMOS.STATUSB.UIE = 0x10; // bit 4: 1 for Update-Ended Interrupt Enable
|
||||
ChipSet.CMOS.STATUSB.SQWE = 0x08; // bit 3: 1 for Square Wave Enabled (as set by the STATUSA rate selection bits)
|
||||
ChipSet.CMOS.STATUSB.BINARY = 0x04; // bit 2: 1 for binary Date Mode, 0 for BCD Date Mode
|
||||
ChipSet.CMOS.STATUSB.HOUR24 = 0x02; // bit 1: 1 for 24-hour mode, 0 for 12-hour mode
|
||||
ChipSet.CMOS.STATUSB.DST = 0x01; // bit 0: 1 for Daylight Savings Time enabled
|
||||
|
||||
ChipSet.CMOS_STATUSC = {}; // abCMOSData[ChipSet.CMOS_ADDR.RTC_STATUSC] TODO: Does reading this register clear these interrupt conditions? (see F000:01C6 in the MODEL_5170 BIOS)
|
||||
ChipSet.CMOS_STATUSC.IRQF = 0x80; // bit 7
|
||||
ChipSet.CMOS_STATUSC.PF = 0x40; // bit 6: 1 indicates Periodic Interrupt
|
||||
ChipSet.CMOS_STATUSC.AF = 0x20; // bit 5: 1 indicates Alarm Interrupt
|
||||
ChipSet.CMOS_STATUSC.UF = 0x10; // bit 4: 1 indicates Update-Ended Interrupt
|
||||
ChipSet.CMOS_STATUSC.RESERVED = 0x0F;
|
||||
ChipSet.CMOS.STATUSC = {}; // abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSC] TODO: Does reading this register clear these interrupt conditions? (see F000:01C6 in the MODEL_5170 BIOS)
|
||||
ChipSet.CMOS.STATUSC.IRQF = 0x80; // bit 7
|
||||
ChipSet.CMOS.STATUSC.PF = 0x40; // bit 6: 1 indicates Periodic Interrupt
|
||||
ChipSet.CMOS.STATUSC.AF = 0x20; // bit 5: 1 indicates Alarm Interrupt
|
||||
ChipSet.CMOS.STATUSC.UF = 0x10; // bit 4: 1 indicates Update-Ended Interrupt
|
||||
ChipSet.CMOS.STATUSC.RESERVED = 0x0F;
|
||||
|
||||
ChipSet.CMOS_STATUSD = {}; // abCMOSData[ChipSet.CMOS_ADDR.RTC_STATUSD]
|
||||
ChipSet.CMOS_STATUSD.VRB = 0x80; // bit 7: 1 indicates Valid RAM Bit (0 implies power was and/or is lost)
|
||||
ChipSet.CMOS_STATUSD.RESERVED = 0x7F;
|
||||
ChipSet.CMOS.STATUSD = {}; // abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSD]
|
||||
ChipSet.CMOS.STATUSD.VRB = 0x80; // bit 7: 1 indicates Valid RAM Bit (0 implies power was and/or is lost)
|
||||
ChipSet.CMOS.STATUSD.RESERVED = 0x7F;
|
||||
|
||||
ChipSet.CMOS_DIAG = {}; // abCMOSData[ChipSet.CMOS_ADDR.DIAG]
|
||||
ChipSet.CMOS_DIAG.RTCFAIL = 0x80; // bit 7: 1 indicates RTC lost power
|
||||
ChipSet.CMOS_DIAG.CHKSUMFAIL = 0x40; // bit 6: 1 indicates bad CMOS checksum
|
||||
ChipSet.CMOS_DIAG.CONFIGFAIL = 0x20; // bit 5: 1 indicates bad CMOS configuration info
|
||||
ChipSet.CMOS_DIAG.MEMSIZEFAIL = 0x10; // bit 4: 1 indicates memory size miscompare
|
||||
ChipSet.CMOS_DIAG.HDRIVEFAIL = 0x08; // bit 3: 1 indicates hard drive controller or drive init failure
|
||||
ChipSet.CMOS_DIAG.TIMEFAIL = 0x04; // bit 2: 1 indicates time failure
|
||||
ChipSet.CMOS_DIAG.RESERVED = 0x03;
|
||||
ChipSet.CMOS.DIAG = {}; // abCMOSData[ChipSet.CMOS.ADDR.DIAG]
|
||||
ChipSet.CMOS.DIAG.RTCFAIL = 0x80; // bit 7: 1 indicates RTC lost power
|
||||
ChipSet.CMOS.DIAG.CHKSUMFAIL = 0x40; // bit 6: 1 indicates bad CMOS checksum
|
||||
ChipSet.CMOS.DIAG.CONFIGFAIL = 0x20; // bit 5: 1 indicates bad CMOS configuration info
|
||||
ChipSet.CMOS.DIAG.MEMSIZEFAIL = 0x10; // bit 4: 1 indicates memory size miscompare
|
||||
ChipSet.CMOS.DIAG.HDRIVEFAIL = 0x08; // bit 3: 1 indicates hard drive controller or drive init failure
|
||||
ChipSet.CMOS.DIAG.TIMEFAIL = 0x04; // bit 2: 1 indicates time failure
|
||||
ChipSet.CMOS.DIAG.RESERVED = 0x03;
|
||||
|
||||
ChipSet.CMOS_FDRIVE = {}; // abCMOSData[ChipSet.CMOS_ADDR.FDRIVE]
|
||||
ChipSet.CMOS_FDRIVE.D0 = 0xF0;
|
||||
ChipSet.CMOS_FDRIVE.D0_DS = 0x10; // double-sided drive (48 TPI)
|
||||
ChipSet.CMOS_FDRIVE.D0_HC = 0x20; // high-capacity drive (96 TPI)
|
||||
ChipSet.CMOS_FDRIVE.D1 = 0x0F;
|
||||
ChipSet.CMOS_FDRIVE.D1_DS = 0x01; // double-sided drive (48 TPI)
|
||||
ChipSet.CMOS_FDRIVE.D1_HC = 0x02; // high-capacity drive (96 TPI)
|
||||
ChipSet.FDRIVE = { // abCMOSData[ChipSet.CMOS.ADDR.FDRIVE] values (drive 0 value in high nibble, drive 1 value in low nibble)
|
||||
NONE: 0, // no drive
|
||||
DSDD: 1, // double-sided double-density drive (48 TPI, 40-track, 360Kb max)
|
||||
DSHC: 2 // double-sided high-capacity drive (96 TPI, 80-track, 1.2Mb max)
|
||||
};
|
||||
|
||||
/*
|
||||
* The following HDRIVE types are supported by the MODEL_5170, where C is Cylinders, H is Heads,
|
||||
|
|
@ -772,17 +758,17 @@ ChipSet.CMOS_FDRIVE.D1_HC = 0x02; // high-capacity drive (96 TPI)
|
|||
* 14 733 7 no 733
|
||||
* 15 (reserved--all zeros)
|
||||
*/
|
||||
ChipSet.CMOS_HDRIVE = {}; // abCMOSData[ChipSet.CMOS_ADDR.HDRIVE]
|
||||
ChipSet.CMOS_HDRIVE.D0 = 0xF0;
|
||||
ChipSet.CMOS_HDRIVE.D1 = 0x0F;
|
||||
ChipSet.CMOS.HDRIVE = {}; // abCMOSData[ChipSet.CMOS.ADDR.HDRIVE]
|
||||
ChipSet.CMOS.HDRIVE.D0 = 0xF0;
|
||||
ChipSet.CMOS.HDRIVE.D1 = 0x0F;
|
||||
|
||||
/*
|
||||
* The CMOS equipment flags use the same format as the older PPI equipment flags
|
||||
*/
|
||||
ChipSet.CMOS_EQUIP = {}; // abCMOSData[ChipSet.CMOS_ADDR.EQUIP]
|
||||
ChipSet.CMOS_EQUIP.MONITOR = ChipSet.PPI_SW.MONITOR; // PPI_SW.MONITOR.MASK == 0x30
|
||||
ChipSet.CMOS_EQUIP.COPROC = ChipSet.PPI_SW.COPROC; // PPI_SW.COPROC == 0x02
|
||||
ChipSet.CMOS_EQUIP.FDRIVE = ChipSet.PPI_SW.FDRIVE; // PPI_SW.FDRIVE.IPL == 0x01 and PPI_SW.FDRIVE.MASK = 0xC0
|
||||
ChipSet.CMOS.EQUIP = {}; // abCMOSData[ChipSet.CMOS.ADDR.EQUIP]
|
||||
ChipSet.CMOS.EQUIP.MONITOR = ChipSet.PPI_SW.MONITOR; // PPI_SW.MONITOR.MASK == 0x30
|
||||
ChipSet.CMOS.EQUIP.COPROC = ChipSet.PPI_SW.COPROC; // PPI_SW.COPROC == 0x02
|
||||
ChipSet.CMOS.EQUIP.FDRIVE = ChipSet.PPI_SW.FDRIVE; // PPI_SW.FDRIVE.IPL == 0x01 and PPI_SW.FDRIVE.MASK = 0xC0
|
||||
|
||||
/*
|
||||
* Manufacturing Test Ports (MODEL_5170)
|
||||
|
|
@ -819,10 +805,24 @@ ChipSet.COPROC.PORT_RESET = 0xF1; // reset the coprocessor
|
|||
/*
|
||||
* Ports used by MODEL_5170 BIOS for "Combo Hard File/Diskette Card" check (@F000:144C)
|
||||
*
|
||||
* We're intercepting reads for this card's STATUS port simply to reduce boot time; otherwise,
|
||||
* our default unknown port response (0xFF) maximizes boot delay. The STATUS port simply needs
|
||||
* to return a byte with bit 7 clear, so that the BIOS will then attempt to write/read the CTRL
|
||||
* port, which will immediately fail (since the write will be ignored).
|
||||
* The ChipSet component provides minimal boot-time support for the "IBM Personal Computer
|
||||
* AT Fixed Disk and Diskette Drive Adapter", aka the HFCOMBO card, until we're able to fork
|
||||
* the HDC component into a new HDCombo component to deal with the "Fixed Disk" portion
|
||||
* of the HFCOMBO card. Fortunately, the "Diskette Drive Adapter" portion of the card is
|
||||
* quite compatible with the existing FDC component, so that component can be used as-is,
|
||||
* with minor tweaks.
|
||||
*
|
||||
* Initially, we intercepted reads for HFCOMBO's STATUS port simply to reduce boot time;
|
||||
* otherwise, our default "unknown port" response of 0xFF would maximize boot delay. To solve
|
||||
* that, the STATUS port simply needs to return a byte with bit 7 clear, so that the BIOS
|
||||
* will then attempt to write/read the CTRL port.
|
||||
*
|
||||
* Next, we initially treated the HFCOMBO's CTRL port as an "unknown port", because again,
|
||||
* we didn't need HDC support and it didn't seem to affect FDC support. But it turns out
|
||||
* that FDC support IS affected, because if the BIOS doesn't set the "DUAL" bit (bit 0) of the
|
||||
* "HFCNTRL" byte at 40:8F, then when it comes time later to report the diskette drive type,
|
||||
* the "DISK_TYPE" function (@F000:273D) will branch to one of two almost-identical blocks of
|
||||
* code -- specifically, the block that disallows diskette drive types >= 2 instead of >= 3.
|
||||
*/
|
||||
ChipSet.HFCOMBO = {};
|
||||
ChipSet.HFCOMBO.CTRL = {PORT: 0x1F4};
|
||||
|
|
@ -1001,21 +1001,21 @@ ChipSet.prototype.reset = function()
|
|||
* TODO: Consider a UI for the Keyboard INHIBIT switch. By default, our keyboard is never inhibited
|
||||
* (ie, locked). Also, note that the hardware changes this bit only when new data is sent to b8042OutBuff.
|
||||
*/
|
||||
this.b8042Status = ChipSet.KBD_STATUS.NO_INHIBIT;
|
||||
this.b8042Status = ChipSet.KBC.STATUS.NO_INHIBIT;
|
||||
this.b8042InBuff = 0;
|
||||
this.b8042CmdData = ChipSet.KBD_DATA.CMD.NO_CLOCK;
|
||||
this.b8042CmdData = ChipSet.KBC.DATA.CMD.NO_CLOCK;
|
||||
this.b8042OutBuff = 0;
|
||||
|
||||
/*
|
||||
* TODO: Provide more control over these 8042 "Input Port" bits (eg, the keyboard lock)
|
||||
*/
|
||||
this.b8042InPort = ChipSet.KBD_DATA.INPORT.MFG_OFF | ChipSet.KBD_DATA.INPORT.KBD_ON;
|
||||
if (this.getSWMemorySize() >= 512) this.b8042InPort |= ChipSet.KBD_DATA.INPORT.ENABLE_256KB;
|
||||
if (this.getSW1VideoMonitor() == ChipSet.MONITOR.MONO) this.b8042InPort |= ChipSet.KBD_DATA.INPORT.MONO;
|
||||
this.b8042InPort = ChipSet.KBC.INPORT.MFG_OFF | ChipSet.KBC.INPORT.KBD_ON;
|
||||
if (this.getSWMemorySize() >= 512) this.b8042InPort |= ChipSet.KBC.INPORT.ENABLE_256KB;
|
||||
if (this.getSWVideoMonitor() == ChipSet.MONITOR.MONO) this.b8042InPort |= ChipSet.KBC.INPORT.MONO;
|
||||
|
||||
this.b8042OutPort = ChipSet.KBD_DATA.OUTPORT.NO_RESET | ChipSet.KBD_DATA.OUTPORT.A20_ON;
|
||||
this.bCMOSAddr = 0; // NMI is enabled, since the ChipSet.CMOS_ADDR.NMI_DISABLE bit is not set in bCMOSAddr
|
||||
this.abCMOSData = new Array(ChipSet.CMOS_ADDR.TOTAL);
|
||||
this.b8042OutPort = ChipSet.KBC.OUTPORT.NO_RESET | ChipSet.KBC.OUTPORT.A20_ON;
|
||||
this.bCMOSAddr = 0; // NMI is enabled, since the ChipSet.CMOS.ADDR.NMI_DISABLE bit is not set in bCMOSAddr
|
||||
this.abCMOSData = new Array(ChipSet.CMOS.ADDR.TOTAL);
|
||||
this.initRTCDate(this.sRTCDate);
|
||||
this.initCMOSData();
|
||||
/*
|
||||
|
|
@ -1066,23 +1066,23 @@ ChipSet.prototype.initRTCDate = function(sDate)
|
|||
*/
|
||||
var date = sDate? new Date(sDate) : new Date();
|
||||
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_SEC] = date.getSeconds();
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_SEC_ALRM] = 0;
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_MIN] = date.getMinutes();
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_MIN_ALRM] = 0;
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_HOUR] = date.getHours();
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_HOUR_ALRM] = 0;
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_WEEK_DAY] = date.getDay() + 1;
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_MONTH_DAY] = date.getDate();
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_MONTH] = date.getMonth() + 1;
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_YEAR] = date.getFullYear() % 100;
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_SEC] = date.getSeconds();
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_SEC_ALRM] = 0;
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_MIN] = date.getMinutes();
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_MIN_ALRM] = 0;
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_HOUR] = date.getHours();
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_HOUR_ALRM] = 0;
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_WEEK_DAY] = date.getDay() + 1;
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_MONTH_DAY] = date.getDate();
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_MONTH] = date.getMonth() + 1;
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_YEAR] = date.getFullYear() % 100;
|
||||
|
||||
this.nCyclesCMOSLastUpdate = -1;
|
||||
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_STATUSA] = 0x26; // hard-coded default; refer to ChipSet.CMOS_STATUSA.DV and ChipSet.CMOS_STATUSA.RS
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_STATUSB] = ChipSet.CMOS_STATUSB.HOUR24; // default to BCD mode (ChipSet.CMOS_STATUSB.BINARY not set)
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_STATUSC] = 0x00;
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_STATUSD] = ChipSet.CMOS_STATUSD.VRB;
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSA] = 0x26; // hard-coded default; refer to ChipSet.CMOS.STATUSA.DV and ChipSet.CMOS.STATUSA.RS
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSB] = ChipSet.CMOS.STATUSB.HOUR24; // default to BCD mode (ChipSet.CMOS.STATUSB.BINARY not set)
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSC] = 0x00;
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSD] = ChipSet.CMOS.STATUSD.VRB;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1093,14 +1093,14 @@ ChipSet.prototype.initRTCDate = function(sDate)
|
|||
*/
|
||||
ChipSet.prototype.getRTCByte = function(iRTC)
|
||||
{
|
||||
Component.assert(iRTC >= 0 && iRTC <= ChipSet.CMOS_ADDR.RTC_STATUSD);
|
||||
Component.assert(iRTC >= 0 && iRTC <= ChipSet.CMOS.ADDR.RTC_STATUSD);
|
||||
|
||||
var b = this.abCMOSData[iRTC];
|
||||
|
||||
if (iRTC < ChipSet.CMOS_ADDR.RTC_STATUSA) {
|
||||
if (iRTC < ChipSet.CMOS.ADDR.RTC_STATUSA) {
|
||||
var f12HourValue = false;
|
||||
if (iRTC == ChipSet.CMOS_ADDR.RTC_HOUR || iRTC == ChipSet.CMOS_ADDR.RTC_HOUR_ALRM) {
|
||||
if (!(this.abCMOSData[ChipSet.CMOS_ADDR.RTC_STATUSB] & ChipSet.CMOS_STATUSB.HOUR24)) {
|
||||
if (iRTC == ChipSet.CMOS.ADDR.RTC_HOUR || iRTC == ChipSet.CMOS.ADDR.RTC_HOUR_ALRM) {
|
||||
if (!(this.abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSB] & ChipSet.CMOS.STATUSB.HOUR24)) {
|
||||
if (b < 12) {
|
||||
b = (!b? 12 : b);
|
||||
} else {
|
||||
|
|
@ -1110,7 +1110,7 @@ ChipSet.prototype.getRTCByte = function(iRTC)
|
|||
f12HourValue = true;
|
||||
}
|
||||
}
|
||||
if (!(this.abCMOSData[ChipSet.CMOS_ADDR.RTC_STATUSB] & ChipSet.CMOS_STATUSB.BINARY)) {
|
||||
if (!(this.abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSB] & ChipSet.CMOS.STATUSB.BINARY)) {
|
||||
/*
|
||||
* We're in BCD mode, so we must convert b from BINARY to BCD. But first:
|
||||
*
|
||||
|
|
@ -1125,12 +1125,12 @@ ChipSet.prototype.getRTCByte = function(iRTC)
|
|||
b = (b % 10) | ((b / 10) << 4);
|
||||
}
|
||||
} else {
|
||||
if (iRTC == ChipSet.CMOS_ADDR.RTC_STATUSA) {
|
||||
if (iRTC == ChipSet.CMOS.ADDR.RTC_STATUSA) {
|
||||
/*
|
||||
* HACK: Perform a mindless toggling of the "Update-In-Progress" bit, so that it's flipped
|
||||
* on the next read; this makes the MODEL_5170 BIOS ("POST2_RTCUP") happy.
|
||||
*/
|
||||
this.abCMOSData[iRTC] ^= ChipSet.CMOS_STATUSA.UIP;
|
||||
this.abCMOSData[iRTC] ^= ChipSet.CMOS.STATUSA.UIP;
|
||||
}
|
||||
}
|
||||
return b;
|
||||
|
|
@ -1145,11 +1145,11 @@ ChipSet.prototype.getRTCByte = function(iRTC)
|
|||
*/
|
||||
ChipSet.prototype.setRTCByte = function(iRTC, b)
|
||||
{
|
||||
Component.assert(iRTC >= 0 && iRTC <= ChipSet.CMOS_ADDR.RTC_STATUSD);
|
||||
Component.assert(iRTC >= 0 && iRTC <= ChipSet.CMOS.ADDR.RTC_STATUSD);
|
||||
|
||||
if (iRTC < ChipSet.CMOS_ADDR.RTC_STATUSA) {
|
||||
if (iRTC < ChipSet.CMOS.ADDR.RTC_STATUSA) {
|
||||
var fBCD = false;
|
||||
if (!(this.abCMOSData[ChipSet.CMOS_ADDR.RTC_STATUSB] & ChipSet.CMOS_STATUSB.BINARY)) {
|
||||
if (!(this.abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSB] & ChipSet.CMOS.STATUSB.BINARY)) {
|
||||
/*
|
||||
* We're in BCD mode, so we must convert b from BCD to BINARY (we assume it's valid
|
||||
* BCD; ie, that both nibbles contain only 0-9, not A-F).
|
||||
|
|
@ -1157,7 +1157,7 @@ ChipSet.prototype.setRTCByte = function(iRTC, b)
|
|||
b = (b >> 4) * 10 + (b & 0xf);
|
||||
fBCD = true;
|
||||
}
|
||||
if (iRTC == ChipSet.CMOS_ADDR.RTC_HOUR || iRTC == ChipSet.CMOS_ADDR.RTC_HOUR_ALRM) {
|
||||
if (iRTC == ChipSet.CMOS.ADDR.RTC_HOUR || iRTC == ChipSet.CMOS.ADDR.RTC_HOUR_ALRM) {
|
||||
if (fBCD) {
|
||||
/*
|
||||
* If the original BCD hour was 0x81-0x92, then the previous BINARY-to-BCD conversion
|
||||
|
|
@ -1168,7 +1168,7 @@ ChipSet.prototype.setRTCByte = function(iRTC, b)
|
|||
b += 0x30;
|
||||
}
|
||||
}
|
||||
if (!(this.abCMOSData[ChipSet.CMOS_ADDR.RTC_STATUSB] & ChipSet.CMOS_STATUSB.HOUR24)) {
|
||||
if (!(this.abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSB] & ChipSet.CMOS.STATUSB.HOUR24)) {
|
||||
if (b <= 12) {
|
||||
b = (b == 12? 0 : b);
|
||||
} else {
|
||||
|
|
@ -1209,19 +1209,19 @@ ChipSet.prototype.updateRTCDate = function()
|
|||
*/
|
||||
Component.assert(nSecondsDelta <= 1);
|
||||
if (nSecondsDelta) {
|
||||
if (++this.abCMOSData[ChipSet.CMOS_ADDR.RTC_SEC] >= 60) {
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_SEC] = 0;
|
||||
if (++this.abCMOSData[ChipSet.CMOS_ADDR.RTC_MIN] >= 60) {
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_MIN] = 0;
|
||||
if (++this.abCMOSData[ChipSet.CMOS_ADDR.RTC_HOUR] >= 24) {
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_HOUR] = 0;
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_WEEK_DAY] = (this.abCMOSData[ChipSet.CMOS_ADDR.RTC_WEEK_DAY] % 7) + 1;
|
||||
var nDayMax = usr.getMonthDays(this.abCMOSData[ChipSet.CMOS_ADDR.RTC_MONTH], this.abCMOSData[ChipSet.CMOS_ADDR.RTC_YEAR]);
|
||||
if (++this.abCMOSData[ChipSet.CMOS_ADDR.RTC_MONTH_DAY] > nDayMax) {
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_MONTH_DAY] = 1;
|
||||
if (++this.abCMOSData[ChipSet.CMOS_ADDR.RTC_MONTH] > 12) {
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_MONTH] = 1;
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.RTC_YEAR] = (this.abCMOSData[ChipSet.CMOS_ADDR.RTC_YEAR] + 1) % 100;
|
||||
if (++this.abCMOSData[ChipSet.CMOS.ADDR.RTC_SEC] >= 60) {
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_SEC] = 0;
|
||||
if (++this.abCMOSData[ChipSet.CMOS.ADDR.RTC_MIN] >= 60) {
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_MIN] = 0;
|
||||
if (++this.abCMOSData[ChipSet.CMOS.ADDR.RTC_HOUR] >= 24) {
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_HOUR] = 0;
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_WEEK_DAY] = (this.abCMOSData[ChipSet.CMOS.ADDR.RTC_WEEK_DAY] % 7) + 1;
|
||||
var nDayMax = usr.getMonthDays(this.abCMOSData[ChipSet.CMOS.ADDR.RTC_MONTH], this.abCMOSData[ChipSet.CMOS.ADDR.RTC_YEAR]);
|
||||
if (++this.abCMOSData[ChipSet.CMOS.ADDR.RTC_MONTH_DAY] > nDayMax) {
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_MONTH_DAY] = 1;
|
||||
if (++this.abCMOSData[ChipSet.CMOS.ADDR.RTC_MONTH] > 12) {
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_MONTH] = 1;
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_YEAR] = (this.abCMOSData[ChipSet.CMOS.ADDR.RTC_YEAR] + 1) % 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1249,7 +1249,7 @@ ChipSet.prototype.initCMOSData = function()
|
|||
* Make sure all the "checksummed" CMOS bytes get initialized (not just the handful we set below) to ensure
|
||||
* that the checksum will be valid.
|
||||
*/
|
||||
for (var iCMOS = ChipSet.CMOS_ADDR.DIAG; iCMOS < ChipSet.CMOS_ADDR.CHKSUM_HI; iCMOS++) {
|
||||
for (var iCMOS = ChipSet.CMOS.ADDR.DIAG; iCMOS < ChipSet.CMOS.ADDR.CHKSUM_HI; iCMOS++) {
|
||||
this.abCMOSData[iCMOS] = 0;
|
||||
}
|
||||
|
||||
|
|
@ -1263,29 +1263,21 @@ ChipSet.prototype.initCMOSData = function()
|
|||
* incomplete; for example, does the FDC component have a way of specifying the number of drives, and do we honor that?
|
||||
* I think not....
|
||||
*/
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.EQUIP] = this.sw1 & (ChipSet.PPI_SW.MONITOR.MASK | ChipSet.PPI_SW.COPROC | ChipSet.PPI_SW.FDRIVE.IPL | ChipSet.PPI_SW.FDRIVE.MASK);
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.EQUIP] = this.sw1 & (ChipSet.PPI_SW.MONITOR.MASK | ChipSet.PPI_SW.COPROC | ChipSet.PPI_SW.FDRIVE.IPL | ChipSet.PPI_SW.FDRIVE.MASK);
|
||||
|
||||
/*
|
||||
* TODO: We default all floppy diskette drives to High Capacity, but MODEL_5170 machines will need more control
|
||||
* over settings like this.
|
||||
*/
|
||||
var bDisketteTypes = 0;
|
||||
var cDisketteDrives = this.getSW1FloppyDrives();
|
||||
if (cDisketteDrives > 0) bDisketteTypes |= ChipSet.CMOS_FDRIVE.D0_HC;
|
||||
if (cDisketteDrives > 1) bDisketteTypes |= ChipSet.CMOS_FDRIVE.D1_HC;
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.FDRIVE] = bDisketteTypes;
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.FDRIVE] = (this.getSWFloppyDriveType(0) << 4) | this.getSWFloppyDriveType(1);
|
||||
|
||||
var wBaseMemKb = this.getSWMemorySize();
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.BASEMEM_LO] = wBaseMemKb & 0xff;
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.BASEMEM_HI] = wBaseMemKb >> 8;
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.BASEMEM_LO] = wBaseMemKb & 0xff;
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.BASEMEM_HI] = wBaseMemKb >> 8;
|
||||
|
||||
/*
|
||||
* The final step is calculating the CMOS checksum, which we then store into the CMOS as a courtesy, so that the
|
||||
* user doesn't get unnecessary CMOS errors.
|
||||
*/
|
||||
var wChecksum = this.getCMOSChecksum();
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.CHKSUM_LO] = wChecksum & 0xff;
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.CHKSUM_HI] = wChecksum >> 8;
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.CHKSUM_LO] = wChecksum & 0xff;
|
||||
this.abCMOSData[ChipSet.CMOS.ADDR.CHKSUM_HI] = wChecksum >> 8;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1303,7 +1295,7 @@ ChipSet.prototype.initCMOSData = function()
|
|||
ChipSet.prototype.getCMOSChecksum = function()
|
||||
{
|
||||
var wChecksum = 0;
|
||||
for (var iCMOS = ChipSet.CMOS_ADDR.FDRIVE; iCMOS < ChipSet.CMOS_ADDR.CHKSUM_HI; iCMOS++) {
|
||||
for (var iCMOS = ChipSet.CMOS.ADDR.FDRIVE; iCMOS < ChipSet.CMOS.ADDR.CHKSUM_HI; iCMOS++) {
|
||||
wChecksum += this.abCMOSData[iCMOS];
|
||||
}
|
||||
return wChecksum;
|
||||
|
|
@ -1643,26 +1635,44 @@ ChipSet.prototype.getSWMemorySize = function(fInit)
|
|||
};
|
||||
|
||||
/**
|
||||
* getSW1FloppyDrives(fInit)
|
||||
* getSWFloppyDrives(fInit)
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {boolean|undefined} [fInit] is true for init switch value(s) only, current value(s) otherwise
|
||||
* @return {number} number of floppy drives specified by SW1 (range is 0 to 4)
|
||||
*/
|
||||
ChipSet.prototype.getSW1FloppyDrives = function(fInit)
|
||||
ChipSet.prototype.getSWFloppyDrives = function(fInit)
|
||||
{
|
||||
var sw1 = (fInit? this.sw1Init : this.sw1);
|
||||
return ((this.model != ChipSet.MODEL_5150) || (sw1 & ChipSet.PPI_SW.FDRIVE.IPL))? ((sw1 & ChipSet.PPI_SW.FDRIVE.MASK) >> ChipSet.PPI_SW.FDRIVE.SHIFT) + 1 : 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* getSW1VideoMonitor(fInit)
|
||||
* getSWFloppyDriveType(iDrive)
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {number} iDrive (0-based)
|
||||
* @return {number} one of the ChipSet.FDRIVE values (ie, NONE: 0, DSDD: 1, DSHC: 2)
|
||||
*/
|
||||
ChipSet.prototype.getSWFloppyDriveType = function(iDrive)
|
||||
{
|
||||
/*
|
||||
* TODO: For MODEL_5170, we default all floppy drive types to High Capacity, but more control would be nice.
|
||||
*/
|
||||
if (iDrive < this.getSWFloppyDrives()) {
|
||||
return (this.model < ChipSet.MODEL_5170? ChipSet.FDRIVE.DSDD : ChipSet.FDRIVE.DSHC);
|
||||
}
|
||||
return ChipSet.FDRIVE.NONE;
|
||||
};
|
||||
|
||||
/**
|
||||
* getSWVideoMonitor(fInit)
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {boolean|undefined} [fInit] is true for init switch value(s) only, current value(s) otherwise
|
||||
* @return {number} one of ChipSet.MONITOR.*
|
||||
*/
|
||||
ChipSet.prototype.getSW1VideoMonitor = function(fInit)
|
||||
ChipSet.prototype.getSWVideoMonitor = function(fInit)
|
||||
{
|
||||
var sw1 = (fInit? this.sw1Init : this.sw1);
|
||||
return (sw1 & ChipSet.PPI_SW.MONITOR.MASK) >> ChipSet.PPI_SW.MONITOR.SHIFT;
|
||||
|
|
@ -1786,8 +1796,8 @@ ChipSet.prototype.updateSwitchDesc = function()
|
|||
if (controlDesc !== undefined) {
|
||||
var sHTML = "";
|
||||
sHTML += this.getSWMemorySize(true) + "Kb";
|
||||
sHTML += ", " + asMonitorTypes[this.getSW1VideoMonitor(true)] + " Monitor";
|
||||
sHTML += ", " + this.getSW1FloppyDrives(true) + " Floppy Drives";
|
||||
sHTML += ", " + asMonitorTypes[this.getSWVideoMonitor(true)] + " Monitor";
|
||||
sHTML += ", " + this.getSWFloppyDrives(true) + " Floppy Drives";
|
||||
if (this.sw1 !== undefined && this.sw1 != this.sw1Init || this.sw2 !== undefined && this.sw2 != this.sw2Init)
|
||||
sHTML += " (Reset required)";
|
||||
controlDesc.innerHTML = sHTML;
|
||||
|
|
@ -3397,7 +3407,7 @@ ChipSet.prototype.inPPIC = function(port, addrFrom)
|
|||
|
||||
/*
|
||||
* If you ever wanted to simulate I/O channel errors or R/W memory parity errors, you could
|
||||
* add either PPI_C_IO_CHANNEL_CHK (0x40) or PPI_C_RW_PARITY_CHK (0x80) to the return value (b).
|
||||
* add either PPI_C.IO_CHANNEL_CHK (0x40) or PPI_C.RW_PARITY_CHK (0x80) to the return value (b).
|
||||
*/
|
||||
if (this.model == ChipSet.MODEL_5150) {
|
||||
if (this.bPPIB & ChipSet.PPI_B.ENABLE_SW2) {
|
||||
|
|
@ -3486,7 +3496,7 @@ ChipSet.prototype.in8042OutBuff = function(port, addrFrom)
|
|||
{
|
||||
var b = this.b8042OutBuff;
|
||||
this.messagePort(port, null, addrFrom, "8042_OUTBUFF", ChipSet.MESSAGE_CHIPSET, b);
|
||||
this.b8042Status &= ~(ChipSet.KBD_STATUS.OUTBUFF_FULL | ChipSet.KBD_STATUS.OUTBUFF_DELAY);
|
||||
this.b8042Status &= ~(ChipSet.KBC.STATUS.OUTBUFF_FULL | ChipSet.KBC.STATUS.OUTBUFF_DELAY);
|
||||
var bNext = this.kbd && this.kbd.readScanCode(true);
|
||||
if (bNext) this.set8042OutBuff(bNext);
|
||||
return b;
|
||||
|
|
@ -3508,16 +3518,16 @@ ChipSet.prototype.out8042InBuffData = function(port, bOut, addrFrom)
|
|||
{
|
||||
this.messagePort(port, bOut, addrFrom, "8042_INBUF.DATA", ChipSet.MESSAGE_CHIPSET);
|
||||
|
||||
if (this.b8042Status & ChipSet.KBD_STATUS.CMD_FLAG) {
|
||||
if (this.b8042Status & ChipSet.KBC.STATUS.CMD_FLAG) {
|
||||
switch (this.b8042InBuff) {
|
||||
|
||||
case ChipSet.KBD_CMD.WRITE_CMD:
|
||||
case ChipSet.KBC.CMD.WRITE_CMD:
|
||||
this.b8042CmdData = bOut;
|
||||
Component.assert(ChipSet.KBD_DATA.CMD.SYS_FLAG === ChipSet.KBD_STATUS.SYS_FLAG);
|
||||
this.b8042Status = (this.b8042Status & ~ChipSet.KBD_STATUS.SYS_FLAG) | (bOut & ChipSet.KBD_DATA.CMD.SYS_FLAG);
|
||||
Component.assert(ChipSet.KBC.DATA.CMD.SYS_FLAG === ChipSet.KBC.STATUS.SYS_FLAG);
|
||||
this.b8042Status = (this.b8042Status & ~ChipSet.KBC.STATUS.SYS_FLAG) | (bOut & ChipSet.KBC.DATA.CMD.SYS_FLAG);
|
||||
break;
|
||||
|
||||
case ChipSet.KBD_CMD.WRITE_OUTPORT:
|
||||
case ChipSet.KBC.CMD.WRITE_OUTPORT:
|
||||
this.set8042OutPort(bOut);
|
||||
break;
|
||||
|
||||
|
|
@ -3586,13 +3596,13 @@ ChipSet.prototype.out8042InBuffData = function(port, bOut, addrFrom)
|
|||
* error, but "TEST.21" assumes that it is.
|
||||
*/
|
||||
default:
|
||||
this.b8042CmdData &= ~ChipSet.KBD_DATA.CMD.NO_CLOCK;
|
||||
this.b8042CmdData &= ~ChipSet.KBC.DATA.CMD.NO_CLOCK;
|
||||
if (this.kbd) this.set8042OutBuff(this.kbd.sendCmd(bOut));
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.b8042InBuff = bOut;
|
||||
this.b8042Status &= ~ChipSet.KBD_STATUS.CMD_FLAG;
|
||||
this.b8042Status &= ~ChipSet.KBC.STATUS.CMD_FLAG;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -3609,12 +3619,12 @@ ChipSet.prototype.in8042RWReg = function(port, addrFrom)
|
|||
* Normally, we return whatever was last written to this port, but we do need to mask the
|
||||
* two upper-most bits (KBD_RWREG.PARITY_ERR), because we never want to report a parity error.
|
||||
*/
|
||||
var b = this.bPPIB & ~ChipSet.KBD_RWREG.PARITY_ERR;
|
||||
var b = this.bPPIB & ~ChipSet.KBC.RWREG.PARITY_ERR;
|
||||
this.messagePort(port, null, addrFrom, "8042_RWREG", ChipSet.MESSAGE_CHIPSET, b);
|
||||
/*
|
||||
* "TEST.09" of the MODEL_5170 BIOS expects the following bit ("REFRESH_BIT") to alternate, so we oblige.
|
||||
*/
|
||||
this.bPPIB ^= ChipSet.KBD_RWREG.REFRESH_BIT;
|
||||
this.bPPIB ^= ChipSet.KBC.RWREG.REFRESH_BIT;
|
||||
return b;
|
||||
};
|
||||
|
||||
|
|
@ -3659,9 +3669,9 @@ ChipSet.prototype.in8042Status = function(port, addrFrom)
|
|||
* If longer delays are needed down the road, we may need to set a delay count in the upper (hidden)
|
||||
* bits of b8042Status, instead of using a single "OUTBUFF_DELAY" bit.
|
||||
*/
|
||||
if (this.b8042Status & ChipSet.KBD_STATUS.OUTBUFF_DELAY) {
|
||||
this.b8042Status |= ChipSet.KBD_STATUS.OUTBUFF_FULL;
|
||||
this.b8042Status &= ~ChipSet.KBD_STATUS.OUTBUFF_DELAY;
|
||||
if (this.b8042Status & ChipSet.KBC.STATUS.OUTBUFF_DELAY) {
|
||||
this.b8042Status |= ChipSet.KBC.STATUS.OUTBUFF_FULL;
|
||||
this.b8042Status &= ~ChipSet.KBC.STATUS.OUTBUFF_DELAY;
|
||||
}
|
||||
return b;
|
||||
};
|
||||
|
|
@ -3681,34 +3691,34 @@ ChipSet.prototype.in8042Status = function(port, addrFrom)
|
|||
ChipSet.prototype.out8042InBuffCmd = function(port, bOut, addrFrom)
|
||||
{
|
||||
this.messagePort(port, bOut, addrFrom, "8042_INBUFF.CMD", ChipSet.MESSAGE_CHIPSET);
|
||||
Component.assert(!(this.b8042Status & ChipSet.KBD_STATUS.INBUFF_FULL));
|
||||
Component.assert(!(this.b8042Status & ChipSet.KBC.STATUS.INBUFF_FULL));
|
||||
this.b8042InBuff = bOut;
|
||||
|
||||
this.b8042Status |= ChipSet.KBD_STATUS.CMD_FLAG;
|
||||
this.b8042Status |= ChipSet.KBC.STATUS.CMD_FLAG;
|
||||
|
||||
var bPulseBits = 0;
|
||||
if (this.b8042InBuff >= ChipSet.KBD_CMD.PULSE_OUTPORT) {
|
||||
if (this.b8042InBuff >= ChipSet.KBC.CMD.PULSE_OUTPORT) {
|
||||
bPulseBits = (this.b8042InBuff ^ 0xf);
|
||||
/*
|
||||
* Now that we have isolated the bit(s) to pulse, map all pulse commands to KBD_CMD.PULSE_OUTPORT
|
||||
*/
|
||||
this.b8042InBuff = ChipSet.KBD_CMD.PULSE_OUTPORT;
|
||||
this.b8042InBuff = ChipSet.KBC.CMD.PULSE_OUTPORT;
|
||||
}
|
||||
|
||||
switch (this.b8042InBuff) {
|
||||
case ChipSet.KBD_CMD.WRITE_CMD: // 0x60
|
||||
case ChipSet.KBD_CMD.WRITE_OUTPORT: // 0xD1
|
||||
case ChipSet.KBC.CMD.WRITE_CMD: // 0x60
|
||||
case ChipSet.KBC.CMD.WRITE_OUTPORT: // 0xD1
|
||||
/*
|
||||
* No further action required for this first group of commands; more data is expected via out8042InBuffData()
|
||||
*/
|
||||
break;
|
||||
|
||||
case ChipSet.KBD_CMD.READ_INPORT: // 0xC0
|
||||
case ChipSet.KBC.CMD.READ_INPORT: // 0xC0
|
||||
this.set8042OutBuff(this.b8042InPort);
|
||||
break;
|
||||
|
||||
case ChipSet.KBD_CMD.DISABLE_KBD: // 0xAD
|
||||
this.b8042CmdData |= ChipSet.KBD_DATA.CMD.NO_CLOCK;
|
||||
case ChipSet.KBC.CMD.DISABLE_KBD: // 0xAD
|
||||
this.b8042CmdData |= ChipSet.KBC.DATA.CMD.NO_CLOCK;
|
||||
if (DEBUG) this.messageDebugger("keyboard disabled", ChipSet.MESSAGE_KBD);
|
||||
/*
|
||||
* TODO: Determine where to honor KBD_DATA.CMD.NO_CLOCK; note that the MODEL_5170 BIOS calls "KBD_RESET" (F000:17D2)
|
||||
|
|
@ -3716,24 +3726,24 @@ ChipSet.prototype.out8042InBuffCmd = function(port, bOut, addrFrom)
|
|||
*/
|
||||
break;
|
||||
|
||||
case ChipSet.KBD_CMD.ENABLE_KBD: // 0xAE
|
||||
this.b8042CmdData &= ~ChipSet.KBD_DATA.CMD.NO_CLOCK;
|
||||
case ChipSet.KBC.CMD.ENABLE_KBD: // 0xAE
|
||||
this.b8042CmdData &= ~ChipSet.KBC.DATA.CMD.NO_CLOCK;
|
||||
if (DEBUG) this.messageDebugger("keyboard re-enabled", ChipSet.MESSAGE_KBD);
|
||||
break;
|
||||
|
||||
case ChipSet.KBD_CMD.SELF_TEST: // 0xAA
|
||||
case ChipSet.KBC.CMD.SELF_TEST: // 0xAA
|
||||
if (this.kbd) this.kbd.shiftScanCode(true);
|
||||
this.b8042CmdData |= ChipSet.KBD_DATA.CMD.NO_CLOCK;
|
||||
this.b8042CmdData |= ChipSet.KBC.DATA.CMD.NO_CLOCK;
|
||||
if (DEBUG) this.messageDebugger("keyboard disabled on reset", ChipSet.MESSAGE_KBD);
|
||||
this.set8042OutBuff(ChipSet.KBD_DATA.SELF_TEST.OK);
|
||||
this.set8042OutPort(ChipSet.KBD_DATA.OUTPORT.NO_RESET | ChipSet.KBD_DATA.OUTPORT.A20_ON);
|
||||
this.set8042OutBuff(ChipSet.KBC.DATA.SELF_TEST.OK);
|
||||
this.set8042OutPort(ChipSet.KBC.OUTPORT.NO_RESET | ChipSet.KBC.OUTPORT.A20_ON);
|
||||
break;
|
||||
|
||||
case ChipSet.KBD_CMD.READ_TEST: // 0xE0
|
||||
this.set8042OutBuff((this.b8042CmdData & ChipSet.KBD_DATA.CMD.NO_CLOCK)? 0 : ChipSet.KBD_DATA.TESTPORT.KBD_CLOCK);
|
||||
case ChipSet.KBC.CMD.READ_TEST: // 0xE0
|
||||
this.set8042OutBuff((this.b8042CmdData & ChipSet.KBC.DATA.CMD.NO_CLOCK)? 0 : ChipSet.KBC.TESTPORT.KBD_CLOCK);
|
||||
break;
|
||||
|
||||
case ChipSet.KBD_CMD.PULSE_OUTPORT: // 0xF0-0xFF
|
||||
case ChipSet.KBC.CMD.PULSE_OUTPORT: // 0xF0-0xFF
|
||||
if (bPulseBits & 0x1) {
|
||||
/*
|
||||
* Bit 0 of the 8042's output port is connected to RESET. If it's pulsed, the processor resets.
|
||||
|
|
@ -3763,8 +3773,8 @@ ChipSet.prototype.set8042OutBuff = function(b)
|
|||
{
|
||||
if (b >= 0) {
|
||||
this.b8042OutBuff = b;
|
||||
this.b8042Status &= ~ChipSet.KBD_STATUS.OUTBUFF_FULL;
|
||||
this.b8042Status |= ChipSet.KBD_STATUS.OUTBUFF_DELAY;
|
||||
this.b8042Status &= ~ChipSet.KBC.STATUS.OUTBUFF_FULL;
|
||||
this.b8042Status |= ChipSet.KBC.STATUS.OUTBUFF_DELAY;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -3777,8 +3787,8 @@ ChipSet.prototype.set8042OutBuff = function(b)
|
|||
ChipSet.prototype.set8042OutPort = function(b)
|
||||
{
|
||||
this.b8042OutPort = b;
|
||||
this.bus.setA20(!!(b & ChipSet.KBD_DATA.OUTPORT.A20_ON));
|
||||
if (!(b & ChipSet.KBD_DATA.OUTPORT.NO_RESET)) {
|
||||
this.bus.setA20(!!(b & ChipSet.KBC.OUTPORT.A20_ON));
|
||||
if (!(b & ChipSet.KBC.OUTPORT.NO_RESET)) {
|
||||
/*
|
||||
* Bit 0 of the 8042's output port is connected to RESET. Normally, it's "pulsed" with the
|
||||
* KBD_CMD.PULSE_OUTPORT command, so if a RESET is detected via this command, we should try to
|
||||
|
|
@ -3818,7 +3828,7 @@ ChipSet.prototype.outCMOSAddr = function(port, bOut, addrFrom)
|
|||
{
|
||||
this.messagePort(port, bOut, addrFrom, "CMOS_ADDR", ChipSet.MESSAGE_CHIPSET);
|
||||
this.bCMOSAddr = bOut;
|
||||
this.bNMI = (bOut & ChipSet.CMOS_ADDR.NMI_DISABLE)? ChipSet.NMI.DISABLE : ChipSet.NMI.ENABLE;
|
||||
this.bNMI = (bOut & ChipSet.CMOS.ADDR.NMI_DISABLE)? ChipSet.NMI.DISABLE : ChipSet.NMI.ENABLE;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -3831,8 +3841,8 @@ ChipSet.prototype.outCMOSAddr = function(port, bOut, addrFrom)
|
|||
*/
|
||||
ChipSet.prototype.inCMOSData = function(port, addrFrom)
|
||||
{
|
||||
var bAddr = this.bCMOSAddr & ChipSet.CMOS_ADDR.MASK;
|
||||
var bIn = (bAddr <= ChipSet.CMOS_ADDR.RTC_STATUSD? this.getRTCByte(bAddr) : this.abCMOSData[bAddr]);
|
||||
var bAddr = this.bCMOSAddr & ChipSet.CMOS.ADDR.MASK;
|
||||
var bIn = (bAddr <= ChipSet.CMOS.ADDR.RTC_STATUSD? this.getRTCByte(bAddr) : this.abCMOSData[bAddr]);
|
||||
this.messagePort(port, null, addrFrom, "CMOS_DATA[" + str.toHexByte(bAddr) + "]", ChipSet.MESSAGE_CHIPSET, bIn);
|
||||
return bIn;
|
||||
};
|
||||
|
|
@ -3847,9 +3857,9 @@ ChipSet.prototype.inCMOSData = function(port, addrFrom)
|
|||
*/
|
||||
ChipSet.prototype.outCMOSData = function(port, bOut, addrFrom)
|
||||
{
|
||||
var bAddr = this.bCMOSAddr & ChipSet.CMOS_ADDR.MASK;
|
||||
var bAddr = this.bCMOSAddr & ChipSet.CMOS.ADDR.MASK;
|
||||
this.messagePort(port, bOut, addrFrom, "CMOS_DATA[" + str.toHexByte(bAddr) + "]", ChipSet.MESSAGE_CHIPSET);
|
||||
this.abCMOSData[bAddr] = (bAddr <= ChipSet.CMOS_ADDR.RTC_STATUSD? this.setRTCByte(bAddr, bOut) : bOut);
|
||||
this.abCMOSData[bAddr] = (bAddr <= ChipSet.CMOS.ADDR.RTC_STATUSD? this.setRTCByte(bAddr, bOut) : bOut);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -3896,6 +3906,50 @@ ChipSet.prototype.outNMI = function(port, bOut, addrFrom)
|
|||
this.bNMI = bOut;
|
||||
};
|
||||
|
||||
/**
|
||||
* inHFCCtrl(port, addrFrom)
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {number} port (0x1F4)
|
||||
* @param {number|undefined} addrFrom (not defined if the Debugger is trying to read the specified port)
|
||||
* @return {number} simulated port value
|
||||
*/
|
||||
ChipSet.prototype.inHFCCtrl = function(port, addrFrom)
|
||||
{
|
||||
var b = this.regsHFCombo.bCtrl;
|
||||
this.messagePort(port, null, addrFrom, "HFC_CTRL", ChipSet.MESSAGE_CHIPSET, b);
|
||||
return b;
|
||||
};
|
||||
|
||||
/**
|
||||
* outHFCCtrl(port, bOut, addrFrom)
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {number} port (0x1F4)
|
||||
* @param {number} bOut
|
||||
* @param {number|undefined} addrFrom (not defined if the Debugger is trying to write the specified port)
|
||||
*/
|
||||
ChipSet.prototype.outHFCCtrl = function(port, bOut, addrFrom)
|
||||
{
|
||||
this.messagePort(port, bOut, addrFrom, "HFC_CTRL", ChipSet.MESSAGE_CHIPSET);
|
||||
this.regsHFCombo.bCtrl = bOut;
|
||||
};
|
||||
|
||||
/**
|
||||
* inHFCStatus(port, addrFrom)
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {number} port (0x1F7)
|
||||
* @param {number|undefined} addrFrom (not defined if the Debugger is trying to read the specified port)
|
||||
* @return {number} simulated port value
|
||||
*/
|
||||
ChipSet.prototype.inHFCStatus = function(port, addrFrom)
|
||||
{
|
||||
var b = this.regsHFCombo.bStatus;
|
||||
this.messagePort(port, null, addrFrom, "HFC_STATUS", ChipSet.MESSAGE_CHIPSET, b);
|
||||
return b;
|
||||
};
|
||||
|
||||
/**
|
||||
* parseSwitches(s, def)
|
||||
*
|
||||
|
|
@ -4076,7 +4130,8 @@ ChipSet.aPortInput5170 = {
|
|||
0xCC: /** @this {ChipSet} */ function(port, addrFrom) { return this.inDMAChannelAddr(ChipSet.DMA1.INDEX, 3, port, addrFrom); },
|
||||
0xCE: /** @this {ChipSet} */ function(port, addrFrom) { return this.inDMAChannelCount(ChipSet.DMA1.INDEX, 3, port, addrFrom); },
|
||||
0xD0: /** @this {ChipSet} */ function(port, addrFrom) { return this.inDMAStatus(ChipSet.DMA1.INDEX, port, addrFrom); },
|
||||
0x1F7: /** @this {ChipSet} */ function(port, addrFrom) { return 0x7F; } // refer to comments regarding HFCOMBO.STATUS
|
||||
0x1F4: ChipSet.prototype.inHFCCtrl, // refer to comments regarding HFCOMBO.CTRL
|
||||
0x1F7: ChipSet.prototype.inHFCStatus // refer to comments regarding HFCOMBO.STATUS
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -4150,7 +4205,8 @@ ChipSet.aPortOutput5170 = {
|
|||
0xD4: /** @this {ChipSet} */ function(port, bOut, addrFrom) { this.outDMAMask(ChipSet.DMA1.INDEX, port, bOut, addrFrom); },
|
||||
0xD6: /** @this {ChipSet} */ function(port, bOut, addrFrom) { this.outDMAMode(ChipSet.DMA1.INDEX, port, bOut, addrFrom); },
|
||||
0xD8: /** @this {ChipSet} */ function(port, bOut, addrFrom) { this.outDMAIndex(ChipSet.DMA1.INDEX, port, bOut, addrFrom); },
|
||||
0xDA: /** @this {ChipSet} */ function(port, bOut, addrFrom) { this.outDMAClear(ChipSet.DMA1.INDEX, port, bOut, addrFrom); }
|
||||
0xDA: /** @this {ChipSet} */ function(port, bOut, addrFrom) { this.outDMAClear(ChipSet.DMA1.INDEX, port, bOut, addrFrom); },
|
||||
0x1F4: ChipSet.prototype.outHFCCtrl // refer to comments regarding HFCOMBO.CTRL
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ Component.subclass(Component, CPU);
|
|||
*/
|
||||
CPU.YIELDS_PER_SECOND = 30;
|
||||
CPU.VIDEO_UPDATES_PER_SECOND = 60; // WARNING: if you change this, beware of side-effects in the Video component
|
||||
CPU.STATUS_UPDATES_PER_SECOND = 5;
|
||||
CPU.STATUS_UPDATES_PER_SECOND = 2;
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
|
|
|
|||
|
|
@ -1518,14 +1518,15 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* stepCPU(nCycles, fRegs)
|
||||
* stepCPU(nCycles, fRegs, fUpdateCPU)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {number} nCycles (0 for one instruction without checking breakpoints)
|
||||
* @param {boolean} [fRegs] is true to display registers after step (default is false)
|
||||
* @param {boolean} [fUpdateCPU] is false to disable calls to updateCPU() (default is true)
|
||||
* @return {boolean}
|
||||
*/
|
||||
Debugger.prototype.stepCPU = function(nCycles, fRegs)
|
||||
Debugger.prototype.stepCPU = function(nCycles, fRegs, fUpdateCPU)
|
||||
{
|
||||
if (!this.isCPUAvail()) return false;
|
||||
|
||||
|
|
@ -1556,9 +1557,11 @@ if (DEBUGGER) {
|
|||
|
||||
/*
|
||||
* Because we called cpu.stepCPU() and not cpu.runCPU(), we must nudge the cpu's update code,
|
||||
* and then update our own state.
|
||||
* and then update our own state. Normally, the only time fUpdateCPU will be false is when doStep()
|
||||
* is calling us in a loop, in which case it will perform its own updateCPU() when it's done.
|
||||
*/
|
||||
this.cpu.updateCPU();
|
||||
if (fUpdateCPU !== false) this.cpu.updateCPU();
|
||||
|
||||
this.updateStatus(!fRegs);
|
||||
return (this.nCycles > 0);
|
||||
};
|
||||
|
|
@ -1593,7 +1596,6 @@ if (DEBUGGER) {
|
|||
if (fStep || this.fProcStep == 1)
|
||||
this.doUnassemble();
|
||||
else {
|
||||
// if (fStep === false) this.println();
|
||||
this.doRegisters();
|
||||
}
|
||||
};
|
||||
|
|
@ -1768,7 +1770,9 @@ if (DEBUGGER) {
|
|||
if (this.nCycles) {
|
||||
var msTotal = ms - this.msStart;
|
||||
var nCyclesPerSecond = (msTotal > 0? Math.round(this.nCycles * 1000 / msTotal) : 0);
|
||||
sStopped += " (" + this.cInstructions + " ops, " + this.nCycles + " cycles, " + msTotal + "ms, " + nCyclesPerSecond + "hz)";
|
||||
sStopped += " (";
|
||||
if (this.checksEnabled()) sStopped += this.cInstructions + " ops, ";
|
||||
sStopped += this.nCycles + " cycles, " + msTotal + " ms, " + nCyclesPerSecond + " hz)";
|
||||
if (MAXDEBUG && this.chipset) {
|
||||
var i, c, n;
|
||||
for (i = 0; i < this.chipset.acInterrupts.length; i++) {
|
||||
|
|
@ -4069,7 +4073,9 @@ if (DEBUGGER) {
|
|||
this.println("updated registers:");
|
||||
}
|
||||
}
|
||||
this.println(this.getRegStr(fProt));
|
||||
|
||||
this.println('\n' + this.getRegStr(fProt));
|
||||
|
||||
if (fIns) {
|
||||
this.aAddrNextCode = this.newAddr(this.cpu.regIP, this.cpu.segCS.sel);
|
||||
this.doUnassemble(this.hexAddr(this.aAddrNextCode));
|
||||
|
|
@ -4219,9 +4225,15 @@ if (DEBUGGER) {
|
|||
web.onCountRepeat(
|
||||
count,
|
||||
function onCountStep() {
|
||||
return dbg.setBusy(true) && dbg.stepCPU(nCycles, fRegs);
|
||||
return dbg.setBusy(true) && dbg.stepCPU(nCycles, fRegs, false);
|
||||
},
|
||||
function onCountStepComplete() {
|
||||
/*
|
||||
* We explicitly called stepCPU() with fUpdateCPU === false, because repeatedly
|
||||
* calling updateCPU() is very slow, so once the repeat count has been exhausted,
|
||||
* we need to perform a final updateCPU().
|
||||
*/
|
||||
dbg.cpu.updateCPU();
|
||||
dbg.setBusy(false);
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -44,6 +44,62 @@ if (typeof module !== 'undefined') {
|
|||
var State = require("./state");
|
||||
}
|
||||
|
||||
/*
|
||||
* FDC Terms
|
||||
*
|
||||
* C Cylinder Number the current or selected cylinder number
|
||||
*
|
||||
* D Data the data pattern to be written to a sector
|
||||
*
|
||||
* DS Drive Select the selected driver number encoded the same as bits 0 and 1 of the Digital Output
|
||||
* Register (DOR); eg, DS0, DS1, DS2, or DS3
|
||||
*
|
||||
* DTL Data Length when N is 00, DTL is the data length to be read from or written to a sector
|
||||
*
|
||||
* EOT End Of Track the final sector number on a cylinder
|
||||
*
|
||||
* GPL Gap Length the length of gap 3 (spacing between sectors excluding the VCO synchronous field)
|
||||
*
|
||||
* H Head Address the head number, either 0 or 1, as specified in the ID field
|
||||
*
|
||||
* HD Head the selected head number, 0 or 1 (H = HD in all command words)
|
||||
*
|
||||
* HLT Head Load Time the head load time in the selected drive (2 to 256 milliseconds in 2-millisecond
|
||||
* increments for the 1.2M-byte drive and 4 to 512 milliseconds in 4 millisecond increments
|
||||
* for the 320K-byte drive)
|
||||
*
|
||||
* HUT Head Unload Time the head unload time after a read or write operation (0 to 240 milliseconds in
|
||||
* 16-millisecond increments for the 1.2M-byte drive and 0 to 480 milliseconds in
|
||||
* 32-millisecond increments for the 320K-byte drive)
|
||||
*
|
||||
* MF FM or MFM Mode 0 selects FM mode and 1 selects MFM (MFM is selected only if it is implemented)
|
||||
*
|
||||
* MT Multitrack 1 selects multitrack operation (Both HD0 and HD1 will be read or written)
|
||||
*
|
||||
* N Number the number of data bytes written in a sector
|
||||
*
|
||||
* NCN New Cylinder the new cylinder number for a seek operation
|
||||
*
|
||||
* ND Non-Data Mode indicates an operation in the non-data mode
|
||||
*
|
||||
* PCN Present Cylinder Number the cylinder number at the completion of a Sense interrupt status command
|
||||
* (present position of the head)
|
||||
*
|
||||
* R Record the sector number to be read or written
|
||||
*
|
||||
* SC Sectors Per Cylinder the number of sectors per cylinder
|
||||
*
|
||||
* SK Skip this stands for skip deleted-data address mark
|
||||
*
|
||||
* SRT Stepping Rate this 4 bit byte indicates the stepping rate for the diskette drive as follows:
|
||||
* 1.2M-Byte Diskette Drive: 1111=1ms, 1110=2ms, 1101=3ms
|
||||
* 320K-Byte Diskette Drive: 1111=2ms, 1110=4ms, 1101=6ms
|
||||
*
|
||||
* STP STP Scan Test if STP is 1, the data in contiguous sectors is compared with the data sent
|
||||
* by the processor during a scan operation; if STP is 2, then alternate sections
|
||||
* are read and compared
|
||||
*/
|
||||
|
||||
/**
|
||||
* FDC(parmsFDC)
|
||||
*
|
||||
|
|
@ -136,28 +192,29 @@ FDC.BIOS.DISKETTE_INT = 0x13;
|
|||
FDC.DEFAULT_DRIVE_NAME = "Floppy Drive";
|
||||
|
||||
/*
|
||||
* FDC Output Register (0x3F2, write-only)
|
||||
* FDC Digital Output Register (DOR) (0x3F2, write-only)
|
||||
*
|
||||
* NOTE: A drive's MOTOR bit must be ON before the the drive can be selected. Motor start time is 500ms.
|
||||
* NOTE: Reportedly, a drive's MOTOR bit had to be ON before the the drive could be selected, so outFDCOutput()
|
||||
* verifies that. Also, motor start time for early model drives was 500ms, but we make no attempt to simulate that.
|
||||
*
|
||||
* On the MODEL_5170 "PC AT Fixed Disk and Diskette Drive Adapter", this port is called the Digital Output Register
|
||||
* or DOR. It uses the same bit definitions as the original FDC Output Register, except that only two diskette drives
|
||||
* are supported, hence bit 1 is always 0 (FDC.REG_OUTPUT.SELECT_C and FDC.REG_OUTPUT.SELECT_D are not supported)
|
||||
* and bits 6 and 7 are unused (FDC.REG_OUTPUT.MOTOR_C and FDC.REG_OUTPUT.MOTOR_D are not supported).
|
||||
* are supported, hence bit 1 is always 0 (ie, FDC.REG_OUTPUT.DS2 and FDC.REG_OUTPUT.DS3 are not supported) and bits
|
||||
* 6 and 7 are unused (FDC.REG_OUTPUT.MOTOR_D2 and FDC.REG_OUTPUT.MOTOR_D3 are not supported).
|
||||
*/
|
||||
FDC.REG_OUTPUT = {};
|
||||
FDC.REG_OUTPUT.PORT = 0x3F2;
|
||||
FDC.REG_OUTPUT.SELECT = 0x03;
|
||||
FDC.REG_OUTPUT.SELECT_A = 0x00;
|
||||
FDC.REG_OUTPUT.SELECT_B = 0x01;
|
||||
FDC.REG_OUTPUT.SELECT_C = 0x02; // reserved on the MODEL_5170
|
||||
FDC.REG_OUTPUT.SELECT_D = 0x03; // reserved on the MODEL_5170
|
||||
FDC.REG_OUTPUT.DS = 0x03; // drive select bits
|
||||
FDC.REG_OUTPUT.DS0 = 0x00;
|
||||
FDC.REG_OUTPUT.DS1 = 0x01;
|
||||
FDC.REG_OUTPUT.DS2 = 0x02; // reserved on the MODEL_5170
|
||||
FDC.REG_OUTPUT.DS3 = 0x03; // reserved on the MODEL_5170
|
||||
FDC.REG_OUTPUT.ENABLE = 0x04; // clearing this bit resets the FDC
|
||||
FDC.REG_OUTPUT.INT_ENABLE = 0x08; // enables both FDC and DMA (Channel 2) interrupt requests (IRQ 6)
|
||||
FDC.REG_OUTPUT.MOTOR_A = 0x10;
|
||||
FDC.REG_OUTPUT.MOTOR_B = 0x20;
|
||||
FDC.REG_OUTPUT.MOTOR_C = 0x40; // reserved on the MODEL_5170
|
||||
FDC.REG_OUTPUT.MOTOR_D = 0x80; // reserved on the MODEL_5170
|
||||
FDC.REG_OUTPUT.MOTOR_D0 = 0x10;
|
||||
FDC.REG_OUTPUT.MOTOR_D1 = 0x20;
|
||||
FDC.REG_OUTPUT.MOTOR_D2 = 0x40; // reserved on the MODEL_5170
|
||||
FDC.REG_OUTPUT.MOTOR_D3 = 0x80; // reserved on the MODEL_5170
|
||||
|
||||
/*
|
||||
* FDC Main Status Register (0x3F4, read-only)
|
||||
|
|
@ -247,32 +304,42 @@ FDC.REG_DATA.CMD.MF = 0x40; // MF (Modified Frequency Modulation
|
|||
FDC.REG_DATA.CMD.MT = 0x80; // MT (Multi-Track; ie, data under both heads will be processed)
|
||||
|
||||
/*
|
||||
* FDC error conditions, generally assigned according to the corresponding ST0, ST1 or ST2 error bit.
|
||||
* FDC status/error results, generally assigned according to the corresponding ST0, ST1, ST2 or ST3 status bit.
|
||||
*
|
||||
* TODO: Determine when EQUIP_CHECK is *really* set; "77 step pulses" sounds suspiciously like a typo.
|
||||
*/
|
||||
FDC.REG_DATA.ERR = {};
|
||||
FDC.REG_DATA.ERR.NONE = 0x000000; // ST0 (IC): Normal termination of command (NT)
|
||||
FDC.REG_DATA.ERR.NOT_READY = 0x000008; // ST0 (NR): When the FDD is in the not-ready state and a read or write command is issued, this flag is set; if a read or write command is issued to side 1 of a single sided drive, then this flag is set
|
||||
FDC.REG_DATA.ERR.EQUIP_CHECK = 0x000010; // ST0 (EC): If a fault signal is received from the FDD, or if the track 0 signal fails to occur after 77 step pulses (recalibrate command), then this flag is set
|
||||
FDC.REG_DATA.ERR.SEEK_END = 0x000020; // ST0 (SE): When the FDC completes the Seek command, this flag is set to 1 (high)
|
||||
FDC.REG_DATA.ERR.INCOMPLETE = 0x000040; // ST0 (IC): Abnormal termination of command (AT); execution of command was started, but was not successfully completed
|
||||
FDC.REG_DATA.ERR.RESET = 0x0000C0; // ST0 (IC): Abnormal termination because during command execution the ready signal from FOO changed state
|
||||
FDC.REG_DATA.ERR.INVALID = 0x000080; // ST0 (IC): Invalid command issue (IC); command which was issued was never started
|
||||
FDC.REG_DATA.ERR.ST0 = 0x0000FF;
|
||||
FDC.REG_DATA.ERR.NO_ID_MARK = 0x000100; // ST1 (MA): If the FDC cannot detect the ID Address Mark, this flag is set; at the same time, the MD (Missing Address Mark in Data Field) of Status Register 2 is set
|
||||
FDC.REG_DATA.ERR.NOT_WRITABLE = 0x000200; // ST1 (NW): During Execution of a Write Data, Write Deleted Data, or Format a Cylinder command, if the FDC detects a write protect signal from the FDD, then this flag is set
|
||||
FDC.REG_DATA.ERR.NO_DATA = 0x000400; // ST1 (ND): FDC cannot find specified sector (or specified ID if READ_ID command)
|
||||
FDC.REG_DATA.ERR.DMA_OVERRUN = 0x001000; // ST1 (OR): If the FDC is not serviced by the main systems during data transfers within a certain time interval, this flag is set
|
||||
FDC.REG_DATA.ERR.CRC_ERROR = 0x002000; // ST1 (DE): When the FDC detects a CRC error in either the ID field or the data field, this flag is set
|
||||
FDC.REG_DATA.ERR.END_OF_CYL = 0x008000; // ST1 (EN): When the FDC tries to access a sector beyond the final sector of a cylinder, this flag is set
|
||||
FDC.REG_DATA.ERR.ST1 = 0x00FF00;
|
||||
FDC.REG_DATA.ERR.NO_DATA_MARK = 0x010000; // ST2 (MD): When data is read from the medium, if the FDC cannot find a Data Address Mark or Deleted Data Address Mark, then this flag is set
|
||||
FDC.REG_DATA.ERR.BAD_CYL = 0x020000; // ST2 (BC): This bit is related to the ND bit, and when the contents of C on the medium are different from that stored in the ID Register, and the content of C is FF, then this flag is set
|
||||
FDC.REG_DATA.ERR.SCAN_FAILED = 0x040000; // ST2 (SN): During execution of the Scan command, if the FDC cannot find a sector on the cylinder which meets the condition, then this flag is set
|
||||
FDC.REG_DATA.ERR.SCAN_EQUAL = 0x080000; // ST2 (SH): During execution of the Scan command, if the condition of "equal" is satisfied, this flag is set
|
||||
FDC.REG_DATA.ERR.WRONG_CYL = 0x100000; // ST2 (WC): This bit is related to the ND bit, and when the contents of C on the medium are different from that stored in the ID Register, this flag is set
|
||||
FDC.REG_DATA.ERR.DATA_FIELD = 0x200000; // ST2 (DD): If the FDC detects a CRC error in the data, then this flag is set
|
||||
FDC.REG_DATA.ERR.STRL_MARK = 0x400000; // ST2 (CM): During execution of the Read Data or Scan command, if the FDC encounters a sector which contains a Deleted Data Address Mark, this flag is set
|
||||
FDC.REG_DATA.ERR.ST2 = 0xFF0000;
|
||||
FDC.REG_DATA.RES = {};
|
||||
FDC.REG_DATA.RES.NONE = 0x00000000; // ST0 (IC): Normal termination of command (NT)
|
||||
FDC.REG_DATA.RES.NOT_READY = 0x00000008; // ST0 (NR): When the FDD is in the not-ready state and a read or write command is issued, this flag is set; if a read or write command is issued to side 1 of a single sided drive, then this flag is set
|
||||
FDC.REG_DATA.RES.EQUIP_CHECK = 0x00000010; // ST0 (EC): If a fault signal is received from the FDD, or if the track 0 signal fails to occur after 77 step pulses (recalibrate command), then this flag is set
|
||||
FDC.REG_DATA.RES.SEEK_END = 0x00000020; // ST0 (SE): When the FDC completes the Seek command, this flag is set to 1 (high)
|
||||
FDC.REG_DATA.RES.INCOMPLETE = 0x00000040; // ST0 (IC): Abnormal termination of command (AT); execution of command was started, but was not successfully completed
|
||||
FDC.REG_DATA.RES.RESET = 0x000000C0; // ST0 (IC): Abnormal termination because during command execution the ready signal from the drive changed state
|
||||
FDC.REG_DATA.RES.INVALID = 0x00000080; // ST0 (IC): Invalid command issue (IC); command which was issued was never started
|
||||
FDC.REG_DATA.RES.ST0 = 0x000000FF;
|
||||
FDC.REG_DATA.RES.NO_ID_MARK = 0x00000100; // ST1 (MA): If the FDC cannot detect the ID Address Mark, this flag is set; at the same time, the MD (Missing Address Mark in Data Field) of Status Register 2 is set
|
||||
FDC.REG_DATA.RES.NOT_WRITABLE = 0x00000200; // ST1 (NW): During Execution of a Write Data, Write Deleted Data, or Format a Cylinder command, if the FDC detects a write protect signal from the FDD, then this flag is set
|
||||
FDC.REG_DATA.RES.NO_DATA = 0x00000400; // ST1 (ND): FDC cannot find specified sector (or specified ID if READ_ID command)
|
||||
FDC.REG_DATA.RES.DMA_OVERRUN = 0x00001000; // ST1 (OR): If the FDC is not serviced by the main systems during data transfers within a certain time interval, this flag is set
|
||||
FDC.REG_DATA.RES.CRC_ERROR = 0x00002000; // ST1 (DE): When the FDC detects a CRC error in either the ID field or the data field, this flag is set
|
||||
FDC.REG_DATA.RES.END_OF_CYL = 0x00008000; // ST1 (EN): When the FDC tries to access a sector beyond the final sector of a cylinder, this flag is set
|
||||
FDC.REG_DATA.RES.ST1 = 0x0000FF00;
|
||||
FDC.REG_DATA.RES.NO_DATA_MARK = 0x00010000; // ST2 (MD): When data is read from the medium, if the FDC cannot find a Data Address Mark or Deleted Data Address Mark, then this flag is set
|
||||
FDC.REG_DATA.RES.BAD_CYL = 0x00020000; // ST2 (BC): This bit is related to the ND bit, and when the contents of C on the medium are different from that stored in the ID Register, and the content of C is FF, then this flag is set
|
||||
FDC.REG_DATA.RES.SCAN_FAILED = 0x00040000; // ST2 (SN): During execution of the Scan command, if the FDC cannot find a sector on the cylinder which meets the condition, then this flag is set
|
||||
FDC.REG_DATA.RES.SCAN_EQUAL = 0x00080000; // ST2 (SH): During execution of the Scan command, if the condition of "equal" is satisfied, this flag is set
|
||||
FDC.REG_DATA.RES.WRONG_CYL = 0x00100000; // ST2 (WC): This bit is related to the ND bit, and when the contents of C on the medium are different from that stored in the ID Register, this flag is set
|
||||
FDC.REG_DATA.RES.DATA_FIELD = 0x00200000; // ST2 (DD): If the FDC detects a CRC error in the data, then this flag is set
|
||||
FDC.REG_DATA.RES.STRL_MARK = 0x00400000; // ST2 (CM): During execution of the Read Data or Scan command, if the FDC encounters a sector which contains a Deleted Data Address Mark, this flag is set
|
||||
FDC.REG_DATA.RES.ST2 = 0x00FF0000;
|
||||
FDC.REG_DATA.RES.DRIVE = 0x03000000; // ST3 (Ux): Status of the "Drive Select" signals from the diskette drive
|
||||
FDC.REG_DATA.RES.HEAD = 0x04000000; // ST3 (HD): Status of the "Side Select" signal from the diskette drive
|
||||
FDC.REG_DATA.RES.TWOSIDE = 0x08000000; // ST3 (TS): Status of the "Two Side" signal from the diskette drive
|
||||
FDC.REG_DATA.RES.TRACK0 = 0x10000000; // ST3 (T0): Status of the "Track 0" signal from the diskette drive
|
||||
FDC.REG_DATA.RES.READY = 0x20000000; // ST3 (RY): Status of the "Ready" signal from the diskette drive
|
||||
FDC.REG_DATA.RES.WRITEPROT = 0x40000000; // ST3 (WP): Status of the "Write Protect" signal from the diskette drive
|
||||
FDC.REG_DATA.RES.FAULT = 0x80000000; // ST3 (FT): Status of the "Fault" signal from the diskette drive
|
||||
FDC.REG_DATA.RES.ST3 = 0xFF000000;
|
||||
|
||||
/*
|
||||
* FDC Command Sequences
|
||||
|
|
@ -466,7 +533,15 @@ FDC.prototype.powerUp = function(data, fRepower)
|
|||
if (!this.restore(data)) return false;
|
||||
}
|
||||
if (this.chipset) {
|
||||
this.nDrives = this.chipset.getSW1FloppyDrives();
|
||||
var iDrive;
|
||||
this.nDrives = this.chipset.getSWFloppyDrives();
|
||||
for (iDrive = 0; iDrive < this.nDrives; iDrive++) {
|
||||
var drive = this.aDrives[iDrive];
|
||||
drive.bType = this.chipset.getSWFloppyDriveType(iDrive);
|
||||
if (drive.bType == ChipSet.FDRIVE.DSHC) {
|
||||
drive.nCylinders = 80;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Now that we finally have the SW1 settings, we can populate the HTML control
|
||||
* to match the actual (well, um, specified) number of floppy drives in the system.
|
||||
|
|
@ -477,7 +552,7 @@ FDC.prototype.powerUp = function(data, fRepower)
|
|||
controlDrives.removeChild(controlDrives.firstChild);
|
||||
}
|
||||
controlDrives.innerHTML = "";
|
||||
for (var iDrive = 0; iDrive < this.nDrives; iDrive++) {
|
||||
for (iDrive = 0; iDrive < this.nDrives; iDrive++) {
|
||||
var controlOption = window.document.createElement("option");
|
||||
controlOption['value'] = iDrive;
|
||||
/*
|
||||
|
|
@ -507,7 +582,7 @@ FDC.prototype.powerUp = function(data, fRepower)
|
|||
*/
|
||||
FDC.prototype.powerDown = function(fSave)
|
||||
{
|
||||
return fSave && this.save ? this.save() : true;
|
||||
return fSave && this.save? this.save() : true;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -577,25 +652,25 @@ FDC.prototype.initController = function(data)
|
|||
* Selected drive (from reOutput), which can only be selected if its motor is on (see regOutput).
|
||||
*/
|
||||
this.iDrive = data[i++];
|
||||
/*
|
||||
* FDC commands select a unit, which I assume should always match the selected drive, but since they're
|
||||
* independent, we'll use independent variables.
|
||||
*/
|
||||
this.iUnit = data[i++];
|
||||
i++; // unused slot (if reused, bias by +4, since it was formerly a unit #)
|
||||
|
||||
/*
|
||||
* Defaults to FDC.REG_STATUS.RQM set (ready for command) and FDC.REG_STATUS.READ_DATA clear (data direction
|
||||
* is from processor to the FDC Data Register).
|
||||
*/
|
||||
this.regStatus = data[i++];
|
||||
|
||||
/*
|
||||
* There can be up to 9 command bytes, and 7 result bytes, so 9 data registers are sufficient for communicating
|
||||
* in both directions (hence, the new Array(9) default above).
|
||||
*/
|
||||
this.regDataArray = data[i++];
|
||||
|
||||
/*
|
||||
* Determines the next data byte to be received.
|
||||
*/
|
||||
this.regDataIndex = data[i++];
|
||||
|
||||
/*
|
||||
* Determines the next data byte to be sent (internally, we use regDataIndex to read data bytes, up to this total).
|
||||
*/
|
||||
|
|
@ -615,6 +690,7 @@ FDC.prototype.initController = function(data)
|
|||
* the SW1 switch settings.
|
||||
*/
|
||||
if (this.aDrives === undefined) {
|
||||
this.nDrives = 0; // this will be set later to the number of ACTUAL drives
|
||||
this.aDrives = new Array(4);
|
||||
}
|
||||
for (var iDrive = 0; iDrive < this.aDrives.length; iDrive++) {
|
||||
|
|
@ -653,7 +729,7 @@ FDC.prototype.saveController = function()
|
|||
var i = 0;
|
||||
var data = [];
|
||||
data[i++] = this.iDrive;
|
||||
data[i++] = this.iUnit;
|
||||
data[i++] = 0;
|
||||
data[i++] = this.regStatus;
|
||||
data[i++] = this.regDataArray;
|
||||
data[i++] = this.regDataIndex;
|
||||
|
|
@ -668,7 +744,7 @@ FDC.prototype.saveController = function()
|
|||
|
||||
/**
|
||||
* initDrive(drive, iDrive, data)
|
||||
*
|
||||
*
|
||||
* @this {FDC}
|
||||
* @param {Object} drive
|
||||
* @param {number} iDrive
|
||||
|
|
@ -684,23 +760,32 @@ FDC.prototype.initDrive = function(drive, iDrive, data)
|
|||
|
||||
if (data === undefined) {
|
||||
/*
|
||||
* We set a default of two heads (MODEL_5150 PCs originally shipped with single-sided drives only,
|
||||
* We set a default of two heads (MODEL_5150 PCs originally shipped with single-sided drives,
|
||||
* but the ROM BIOS appears to have always supported both drive types).
|
||||
*/
|
||||
data = [FDC.REG_DATA.ERR.RESET, true, 0, 2, 0];
|
||||
data = [FDC.REG_DATA.RES.RESET, true, 0, 2, 0];
|
||||
}
|
||||
|
||||
if (typeof data[1] == "boolean") {
|
||||
data[1] = [FDC.DEFAULT_DRIVE_NAME, 40, data[3], 9, 512, data[1]];
|
||||
/*
|
||||
* Note that when no data is provided (eg, when the controller is being reinitialized), we now take
|
||||
* care to use drive.nCylinders as the default, falling back to a 40-track maximum ONLY when the drive
|
||||
* hasn't been initialized. This preserves whatever maximum the powerUp() function may have obtained
|
||||
* from the ChipSet component.
|
||||
*
|
||||
* TODO: We may need to make a similar accommodation for drive.nHeads and drive.nSectors down the road;
|
||||
* they currently default to a maximum of 2 heads (see above) and 9 sectors/track (see below).
|
||||
*/
|
||||
data[1] = [FDC.DEFAULT_DRIVE_NAME, drive.nCylinders || 40, data[3], 9, 512, data[1]];
|
||||
}
|
||||
|
||||
/*
|
||||
* errorCode used to be an FDC global, but in order to insulate FDC state from the operation of various functions that operate on drive
|
||||
* objects (eg, readByte and writeByte), I've made it a per-drive variable. This choice, similar to my choice for handling PCN, is
|
||||
* probably contrary to how the actual hardware works, but I prefer this approach, as long as it doesn't expose any incompatibilities that
|
||||
* any software actually cares about.
|
||||
* resCode used to be an FDC global, but in order to insulate FDC state from the operation of various functions
|
||||
* that operate on drive objects (eg, readByte and writeByte), I've made it a per-drive variable. This choice,
|
||||
* similar to my choice for handling PCN, may be contrary to how the actual hardware works, but I prefer this
|
||||
* approach, as long as it doesn't expose any incompatibilities that any software actually cares about.
|
||||
*/
|
||||
drive.errorCode = data[i++];
|
||||
drive.resCode = data[i++];
|
||||
|
||||
/*
|
||||
* Some additional drive properties/defaults that are largely for the Disk component's benefit.
|
||||
|
|
@ -720,12 +805,36 @@ FDC.prototype.initDrive = function(drive, iDrive, data)
|
|||
* awaiting their first FDC command. We do this because the initial INT_STATUS command returns a PCN, which will also
|
||||
* be undefined unless we have at least zeroed both the current drive and the "present" cylinder on that drive.
|
||||
*
|
||||
* Alternatively, I could make PCN a global FDC variable. That's probably closer to how the actual hardware operates,
|
||||
* but I'm eschewing global FDC variables so that the FDC component can be a good client to both the CPU and other components.
|
||||
* Alternatively, I could make PCN a global FDC variable. That may be closer to how the actual hardware operates,
|
||||
* but I'm using per-drive variables so that the FDC component can be a good client to both the CPU and other components.
|
||||
*
|
||||
* COMPATIBILITY ALERT: The MODEL_5170 BIOS ("DSKETTE_SETUP") attempts to discern the drive type (double-density vs.
|
||||
* high-capacity) by "slapping" the heads around. Literally (it uses a constant named "TRK_SLAP" equal to 48).
|
||||
* After seeking to "TRK_SLAP", the BIOS performs a series of seeks, looking for the precise point where the heads
|
||||
* return to track 0.
|
||||
*
|
||||
* Here's how it works: the BIOS seeks to track 48 (which is fine on an 80-track 1.2Mb high-capacity drive, but 9 tracks
|
||||
* too far on a 40-track 360Kb double-density drive), then seeks to track 10, and then seeks in single-track increments
|
||||
* up to 10 more times until the DRIVE_STATUS command returns ST3 with the TRACK0 bit set.
|
||||
*
|
||||
* This implies that SEEK isn't really seeking to a specified cylinder, but rather it is calculating a delta from
|
||||
* the previous cylinder to the specified cylinder, and stepping over that number of tracks. Which means that SEEK
|
||||
* is updating a "logical" cylinder number, not the "physical" (actual) cylinder number. Presumably a RECALIBRATE
|
||||
* command will bring the logical and physical values into sync, but once an out-of-bounds cylinder is requested, they
|
||||
* will be out of sync.
|
||||
*
|
||||
* To simulate this, bCylinder is now treated as the "physical" cylinder (since that's how it's ALWAYS been used here),
|
||||
* and bCylinderSeek will now track (pun intended) the "logical" cylinder that's programmed via SEEK commands.
|
||||
*/
|
||||
drive.bType = ChipSet.FDRIVE.DSDD; // default; updated later once we have the actual ChipSet object
|
||||
drive.bHead = data[i++];
|
||||
i++; // skip the data[] slot where we used to store drive.nHeads (no longer used)
|
||||
drive.bCylinderSeek = data[i++]; // the data[] slot where we used to store drive.nHeads (or -1)
|
||||
drive.bCylinder = data[i++];
|
||||
if (drive.bCylinderSeek >= 100) { // verify that the saved bCylinderSeek is valid, otherwise sync it with bCylinder
|
||||
drive.bCylinderSeek -= 100;
|
||||
} else {
|
||||
drive.bCylinderSeek -= drive.bCylinder;
|
||||
}
|
||||
drive.bSector = data[i++];
|
||||
drive.bSectorEnd = data[i++]; // aka EOT
|
||||
drive.nBytes = data[i++];
|
||||
|
|
@ -735,7 +844,7 @@ FDC.prototype.initDrive = function(drive, iDrive, data)
|
|||
*
|
||||
* NOTE: I now avoid reinitializing drive.disk in order to retain any previously mounted diskette across resets.
|
||||
*
|
||||
* drive.disk = null; // when a "disk" is "inserted" into the "drive", this variable contains a Disk object
|
||||
* drive.disk = null; // when a "disk" is "inserted" into the "drive", this is a Disk object
|
||||
*/
|
||||
|
||||
/*
|
||||
|
|
@ -813,10 +922,14 @@ FDC.prototype.saveDrive = function(drive)
|
|||
{
|
||||
var i = 0;
|
||||
var data = [];
|
||||
data[i++] = drive.errorCode;
|
||||
data[i++] = drive.resCode;
|
||||
data[i++] = [drive.name, drive.nCylinders, drive.nHeads, drive.nSectors, drive.cbSector, drive.fRemovable];
|
||||
data[i++] = drive.bHead;
|
||||
data[i++] = -1; // where we used to store drive.nHeads (no longer used)
|
||||
/*
|
||||
* We used to store drive.nHeads in the next slot, but now we store bCylinderSeek,
|
||||
* and we bias it by +100 so that initDrive() can distinguish it from older values.
|
||||
*/
|
||||
data[i++] = drive.bCylinderSeek + 100;
|
||||
data[i++] = drive.bCylinder;
|
||||
data[i++] = drive.bSector;
|
||||
data[i++] = drive.bSectorEnd;
|
||||
|
|
@ -922,7 +1035,7 @@ FDC.prototype.seekDrive = function(drive, iSector, nSectors)
|
|||
* do anything with bSectorEnd at this point. Perhaps someday, when we faithfully honor/restrict requests
|
||||
* to a single track (or a single cylinder, in the case of multi-track requests).
|
||||
*/
|
||||
drive.errorCode = FDC.REG_DATA.ERR.NONE;
|
||||
drive.resCode = FDC.REG_DATA.RES.NONE;
|
||||
/*
|
||||
* At this point, we've finished simulating what an FDC.REG_DATA.CMD.READ_DATA command would have performed,
|
||||
* up through doRead(). Now it's the caller responsibility to call readByte(), just like the DMA Controller would.
|
||||
|
|
@ -1228,17 +1341,41 @@ FDC.prototype.outFDCOutput = function(port, bOut, addrFrom)
|
|||
this.messagePort(port, bOut, addrFrom, "OUTPUT");
|
||||
if (!(bOut & FDC.REG_OUTPUT.ENABLE)) {
|
||||
this.initController();
|
||||
} else if (!(this.regOutput & FDC.REG_OUTPUT.ENABLE)) {
|
||||
/*
|
||||
* When FDC.REG_OUTPUT.ENABLE transitions from 0 to 1, generate an interrupt
|
||||
* initController() resets, among other things, the selected drive (this.iDrive), so if we were
|
||||
* still updating this.iDrive below based on the "drive select" bits in regOutput, we would want
|
||||
* to make sure those bits now match what initController() set. But since we no longer do that
|
||||
* (see below), this is no longer needed either.
|
||||
*/
|
||||
// bOut = (bOut & ~FDC.REG_OUTPUT.DS) | this.iDrive;
|
||||
}
|
||||
else if (!(this.regOutput & FDC.REG_OUTPUT.ENABLE)) {
|
||||
/*
|
||||
* When FDC.REG_OUTPUT.ENABLE transitions from 0 to 1, generate an interrupt.
|
||||
*/
|
||||
if (this.regOutput & FDC.REG_OUTPUT.INT_ENABLE) {
|
||||
if (this.chipset) this.chipset.setIRR(ChipSet.IRQ.FDC);
|
||||
}
|
||||
}
|
||||
var iDrive = bOut & FDC.REG_OUTPUT.SELECT;
|
||||
if (bOut & (FDC.REG_OUTPUT.MOTOR_A << iDrive))
|
||||
this.iDrive = iDrive;
|
||||
/*
|
||||
* This no longer updates the internally selected drive (this.iDrive) based on regOutput, because (a) there seems
|
||||
* to be no point, as all drive-related commands include their own "drive select" bits, and (b) it breaks the
|
||||
* MODEL_5170 boot code. Here's why:
|
||||
*
|
||||
* Unlike previous models, the MODEL_5170 BIOS probes all installed diskette drives to determine drive type;
|
||||
* ie, DSDD (40-track) or DSHC (80-track). So if there are two drives, the last selected drive will be drive 1.
|
||||
* Immediately before booting, the BIOS issues an INT 0x3/AH=0 reset, which writes regOutput two times: first
|
||||
* with FDC.REG_OUTPUT.ENABLE clear, and then with it set. However, both times, it ALSO loads the last selected
|
||||
* drive # into regOutput's "drive select" bits.
|
||||
*
|
||||
* If we switched our selected drive to match regOutput, then the ST0 value we returned on an INT_STATUS command
|
||||
* following the regOutput reset operation would indicate drive 1 instead of drive 0. But the BIOS requires
|
||||
* the ST0 result from the INT_STATUS command ALWAYS be 0xC0, not 0xC1, so the controller must not be propagating
|
||||
* regOutput's "drive select" bits in the way I originally assumed.
|
||||
*/
|
||||
// var iDrive = bOut & FDC.REG_OUTPUT.DS;
|
||||
// if (bOut & (FDC.REG_OUTPUT.MOTOR_D0 << iDrive)) this.iDrive = iDrive;
|
||||
|
||||
this.regOutput = bOut;
|
||||
};
|
||||
|
||||
|
|
@ -1404,7 +1541,7 @@ FDC.prototype.intBIOSDisketteReturn = function(nCycles, nLevel)
|
|||
{
|
||||
if (DEBUGGER) {
|
||||
nCycles = this.cpu.getCycles() - nCycles;
|
||||
this.messageDebugger("FDC.intBIOSReturn(" + nLevel + "): C=" + (this.cpu.getCF() ? 1 : 0) + " (cycles=" + nCycles + ")");
|
||||
this.messageDebugger("FDC.intBIOSReturn(" + nLevel + "): C=" + (this.cpu.getCF()? 1 : 0) + " (cycles=" + nCycles + ")");
|
||||
// if (DEBUG && nCycles > 10000) this.cpu.haltCPU();
|
||||
}
|
||||
};
|
||||
|
|
@ -1419,7 +1556,7 @@ FDC.prototype.doCmd = function()
|
|||
var fIRQ = false;
|
||||
this.regDataIndex = 0;
|
||||
var bCmd = this.popCmd();
|
||||
var iUnitSelect, drive, bHeadSelect, bHead, n;
|
||||
var drive, bDrive, bHeadSelect, bHead, bCylinder, 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.
|
||||
|
|
@ -1428,109 +1565,129 @@ FDC.prototype.doCmd = function()
|
|||
* data without any formatting data.
|
||||
*
|
||||
* Similarly, we ignore parameters like SRT, HUT, HLT and the like, since our "motors" don't require physical delays;
|
||||
* however, if timing issues become compatibility issues, we might have to start honoring those delays. In any case,
|
||||
* the maximum speed of the simulation will still be limited by various spin-loops in the ROM BIOS that wait prescribed
|
||||
* times, so even with infinitely fast hardware, the simulation will never run as fast as it theoretically could,
|
||||
* unless we opt to identify those spin-loops and either patch them or skip over them.
|
||||
* however, if timing issues become compatibility issues, we'll have to revisit those delays. In any case, the maximum
|
||||
* speed of the simulation will still be limited by various spin-loops in the ROM BIOS that wait prescribed times, so even
|
||||
* with infinitely fast hardware, the simulation will never run as fast as it theoretically could, unless we opt to identify
|
||||
* those spin-loops and either patch them or skip over them.
|
||||
*/
|
||||
var bCmdMasked = bCmd & FDC.REG_DATA.CMD.MASK;
|
||||
|
||||
switch (bCmdMasked) {
|
||||
case FDC.REG_DATA.CMD.SPECIFY: // 0x03
|
||||
this.popSRT(); // SRT and HUT (encodings?)
|
||||
this.popHLT(); // HLT and ND (encodings?)
|
||||
this.beginResult(); // no results are provided by this command, and fIRQ should remain false
|
||||
break;
|
||||
case FDC.REG_DATA.CMD.DRIVE_STATUS: // 0x04
|
||||
iUnitSelect = this.popCmd("US");
|
||||
bHeadSelect = (iUnitSelect >> 2) & 0x1;
|
||||
this.iUnit = (iUnitSelect &= 0x3);
|
||||
drive = this.aDrives[iUnitSelect];
|
||||
this.beginResult();
|
||||
this.pushST3(drive);
|
||||
break;
|
||||
case FDC.REG_DATA.CMD.WRITE_DATA: // 0x05
|
||||
case FDC.REG_DATA.CMD.READ_DATA: // 0x06
|
||||
iUnitSelect = this.popCmd("US");
|
||||
bHeadSelect = (iUnitSelect >> 2) & 0x1;
|
||||
iUnitSelect &= 0x3;
|
||||
this.iUnit = iUnitSelect;
|
||||
drive = this.aDrives[iUnitSelect];
|
||||
drive.bHead = bHeadSelect;
|
||||
drive.bCylinder = this.popCmd("C"); // C
|
||||
bHead = this.popCmd("H"); // H
|
||||
Component.assert(bHead == bHeadSelect);
|
||||
drive.bSector = this.popCmd("R"); // R
|
||||
n = this.popCmd("N"); // N
|
||||
drive.nBytes = 128 << n; // 0 => 128, 1 => 256, 2 => 512, 3 => 1024
|
||||
drive.bSectorEnd = this.popCmd("EOT"); // EOT (final sector number on a cylinder)
|
||||
this.popCmd("GPL"); // GPL (spacing between sectors, excluding VCO Sync Field; 3)
|
||||
this.popCmd("DTL"); // DTL (when N is 0, DTL stands for the data length to read out or write into the sector)
|
||||
if (bCmdMasked == FDC.REG_DATA.CMD.READ_DATA)
|
||||
this.doRead(drive);
|
||||
else
|
||||
this.doWrite(drive);
|
||||
this.beginResult();
|
||||
this.pushST0(drive.errorCode);
|
||||
this.pushST1(drive.errorCode);
|
||||
this.pushST2(drive.errorCode);
|
||||
this.pushResult(drive.bCylinder, "C");
|
||||
this.pushResult(drive.bHead, "H");
|
||||
this.pushResult(drive.bSector, "R");
|
||||
this.pushResult(n, "N");
|
||||
fIRQ = true;
|
||||
break;
|
||||
case FDC.REG_DATA.CMD.RECALIBRATE: // 0x07
|
||||
this.iUnit = iUnitSelect = this.popCmd("US") & 0x3;
|
||||
drive = this.aDrives[iUnitSelect];
|
||||
drive.bCylinder = 0;
|
||||
drive.errorCode = FDC.REG_DATA.ERR.SEEK_END;
|
||||
this.beginResult(); // no results are provided; this command is typically followed by FDC.REG_DATA.CMD.INT_STATUS
|
||||
fIRQ = true;
|
||||
break;
|
||||
case FDC.REG_DATA.CMD.INT_STATUS: // 0x08
|
||||
this.iUnit = this.iDrive;
|
||||
drive = this.aDrives[this.iUnit];
|
||||
this.beginResult();
|
||||
this.pushST0(drive.errorCode);
|
||||
this.pushResult(drive.bCylinder, "PCN");// no interrupt is generated by this command, so fIRQ should remain false
|
||||
break;
|
||||
case FDC.REG_DATA.CMD.FORMAT_TRACK: // 0x0D
|
||||
iUnitSelect = this.popCmd("US");
|
||||
bHeadSelect = (iUnitSelect >> 2) & 0x1;
|
||||
iUnitSelect &= 0x3;
|
||||
this.iUnit = iUnitSelect;
|
||||
drive = this.aDrives[iUnitSelect];
|
||||
drive.bHead = bHeadSelect;
|
||||
n = this.popCmd("N"); // N
|
||||
drive.nBytes = 128 << n; // 0 => 128, 1 => 256, 2 => 512, 3 => 1024 (bytes/sector)
|
||||
drive.bSectorEnd = this.popCmd("SC"); // SC (sectors/track)
|
||||
this.popCmd("GPL"); // GPL (spacing between sectors, excluding VCO Sync Field; 3)
|
||||
drive.bFiller = this.popCmd("D"); // D (filler byte)
|
||||
this.doFormat(drive);
|
||||
this.beginResult();
|
||||
this.pushST0(drive.errorCode);
|
||||
this.pushST1(drive.errorCode);
|
||||
this.pushST2(drive.errorCode);
|
||||
this.pushResult(drive.bCylinder, "C");
|
||||
this.pushResult(drive.bHead, "H");
|
||||
this.pushResult(drive.bSector, "R");
|
||||
this.pushResult(n, "N");
|
||||
fIRQ = true;
|
||||
break;
|
||||
case FDC.REG_DATA.CMD.SEEK: // 0x0F
|
||||
iUnitSelect = this.popCmd("US");
|
||||
bHeadSelect = (iUnitSelect >> 2) & 0x1;
|
||||
this.iUnit = (iUnitSelect &= 0x3);
|
||||
drive = this.aDrives[iUnitSelect];
|
||||
drive.bHead = bHeadSelect;
|
||||
drive.bCylinder = this.popCmd("NCN");
|
||||
drive.errorCode = FDC.REG_DATA.ERR.SEEK_END;
|
||||
this.beginResult(); // like FDC.REG_DATA.CMD.RECALIBRATE, no results are provided
|
||||
fIRQ = true;
|
||||
break;
|
||||
default:
|
||||
if (DEBUG) this.messageDebugger("FDC operation unsupported (command=0x: " + str.toHexByte(bCmd) + ")");
|
||||
break;
|
||||
case FDC.REG_DATA.CMD.SPECIFY: // 0x03
|
||||
this.popSRT(); // SRT and HUT (encodings?)
|
||||
this.popHLT(); // HLT and ND (encodings?)
|
||||
this.beginResult(); // no results are provided by this command, and fIRQ should remain false
|
||||
break;
|
||||
case FDC.REG_DATA.CMD.DRIVE_STATUS: // 0x04 (SENSE DRIVE STATUS)
|
||||
bDrive = this.popCmd("DS");
|
||||
bHeadSelect = (bDrive >> 2) & 0x1;
|
||||
this.iDrive = (bDrive & 0x3);
|
||||
drive = this.aDrives[this.iDrive];
|
||||
this.beginResult();
|
||||
this.pushST3(drive);
|
||||
break;
|
||||
case FDC.REG_DATA.CMD.WRITE_DATA: // 0x05
|
||||
case FDC.REG_DATA.CMD.READ_DATA: // 0x06
|
||||
bDrive = this.popCmd("DS");
|
||||
bHeadSelect = (bDrive >> 2) & 0x1;
|
||||
this.iDrive = (bDrive & 0x3);
|
||||
drive = this.aDrives[this.iDrive];
|
||||
drive.bHead = bHeadSelect;
|
||||
drive.bCylinder = this.popCmd("C"); // C
|
||||
bHead = this.popCmd("H"); // H
|
||||
Component.assert(bHead == bHeadSelect);
|
||||
drive.bSector = this.popCmd("R"); // R
|
||||
n = this.popCmd("N"); // N
|
||||
drive.nBytes = 128 << n; // 0 => 128, 1 => 256, 2 => 512, 3 => 1024
|
||||
drive.bSectorEnd = this.popCmd("EOT"); // EOT (final sector number on a cylinder)
|
||||
this.popCmd("GPL"); // GPL (spacing between sectors, excluding VCO Sync Field; 3)
|
||||
this.popCmd("DTL"); // DTL (when N is 0, DTL stands for the data length to read out or write into the sector)
|
||||
if (bCmdMasked == FDC.REG_DATA.CMD.READ_DATA)
|
||||
this.doRead(drive);
|
||||
else
|
||||
this.doWrite(drive);
|
||||
this.beginResult();
|
||||
this.pushST0(drive);
|
||||
this.pushST1(drive);
|
||||
this.pushST2(drive);
|
||||
this.pushResult(drive.bCylinder, "C");
|
||||
this.pushResult(drive.bHead, "H");
|
||||
this.pushResult(drive.bSector, "R");
|
||||
this.pushResult(n, "N");
|
||||
fIRQ = true;
|
||||
break;
|
||||
case FDC.REG_DATA.CMD.RECALIBRATE: // 0x07
|
||||
bDrive = this.popCmd("DS");
|
||||
this.iDrive = (bDrive & 0x3);
|
||||
drive = this.aDrives[this.iDrive];
|
||||
drive.bCylinder = drive.bCylinderSeek = 0;
|
||||
drive.resCode = FDC.REG_DATA.RES.SEEK_END | FDC.REG_DATA.RES.TRACK0;
|
||||
this.beginResult(); // no results are provided; this command is typically followed by FDC.REG_DATA.CMD.INT_STATUS
|
||||
fIRQ = true;
|
||||
break;
|
||||
case FDC.REG_DATA.CMD.INT_STATUS: // 0x08 (SENSE INTERRUPT STATUS)
|
||||
drive = this.aDrives[this.iDrive];
|
||||
this.beginResult();
|
||||
this.pushST0(drive);
|
||||
this.pushResult(drive.bCylinder, "PCN");// no interrupt is generated by this command, so fIRQ should remain false
|
||||
break;
|
||||
case FDC.REG_DATA.CMD.FORMAT_TRACK: // 0x0D
|
||||
bDrive = this.popCmd("DS");
|
||||
bHeadSelect = (bDrive >> 2) & 0x1;
|
||||
this.iDrive = (bDrive & 0x3);
|
||||
drive = this.aDrives[this.iDrive];
|
||||
drive.bHead = bHeadSelect;
|
||||
n = this.popCmd("N"); // N
|
||||
drive.nBytes = 128 << n; // 0 => 128, 1 => 256, 2 => 512, 3 => 1024 (bytes/sector)
|
||||
drive.bSectorEnd = this.popCmd("SC"); // SC (sectors/track)
|
||||
this.popCmd("GPL"); // GPL (spacing between sectors, excluding VCO Sync Field; 3)
|
||||
drive.bFiller = this.popCmd("D"); // D (filler byte)
|
||||
this.doFormat(drive);
|
||||
this.beginResult();
|
||||
this.pushST0(drive);
|
||||
this.pushST1(drive);
|
||||
this.pushST2(drive);
|
||||
this.pushResult(drive.bCylinder, "C");
|
||||
this.pushResult(drive.bHead, "H");
|
||||
this.pushResult(drive.bSector, "R");
|
||||
this.pushResult(n, "N");
|
||||
fIRQ = true;
|
||||
break;
|
||||
case FDC.REG_DATA.CMD.SEEK: // 0x0F
|
||||
bDrive = this.popCmd("DS");
|
||||
bHeadSelect = (bDrive >> 2) & 0x1;
|
||||
this.iDrive = (bDrive & 0x3);
|
||||
drive = this.aDrives[this.iDrive];
|
||||
drive.bHead = bHeadSelect;
|
||||
/*
|
||||
* 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
|
||||
* by that amount. Then we simply move the NCN into bCylinderSeek without any range checking.
|
||||
*
|
||||
* Since bCylinder is now expressly defined as the "physical" cylinder number, it must never 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("NCN");
|
||||
drive.bCylinder += bCylinder - drive.bCylinderSeek;
|
||||
if (drive.bCylinder < 0) drive.bCylinder = 0;
|
||||
if (drive.bCylinder >= drive.nCylinders) drive.bCylinder = drive.nCylinders - 1;
|
||||
drive.bCylinderSeek = bCylinder;
|
||||
drive.resCode = FDC.REG_DATA.RES.SEEK_END;
|
||||
/*
|
||||
* TODO: To properly support ALL the ST3 result bits (not just TRACK0), we need a resCode
|
||||
* update() function that all FDC commands can use. This code is merely sufficient to get us
|
||||
* through the "DSKETTE_SETUP" gauntlet in the MODEL_5170 BIOS.
|
||||
*/
|
||||
if (drive.bCylinder == 0) {
|
||||
drive.resCode |= FDC.REG_DATA.RES.TRACK0;
|
||||
}
|
||||
this.beginResult(); // like FDC.REG_DATA.CMD.RECALIBRATE, no results are provided
|
||||
fIRQ = true;
|
||||
break;
|
||||
default:
|
||||
if (DEBUG) this.messageDebugger("FDC operation unsupported (command=0x: " + str.toHexByte(bCmd) + ")");
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.regDataTotal > 0) this.regStatus |= (FDC.REG_STATUS.READ_DATA | FDC.REG_STATUS.BUSY);
|
||||
|
|
@ -1541,10 +1698,10 @@ FDC.prototype.doCmd = function()
|
|||
* result has been read, the interrupt is cleared (see inFDCData).
|
||||
*
|
||||
* TODO: Technically, interrupt request status should be cleared by the FDC.REG_DATA.CMD.INT_STATUS command; in fact,
|
||||
* if that command is issued and no interrupt was pending, then FDC.REG_DATA.ERR.INVALID should be returned (via ST0).
|
||||
* if that command is issued and no interrupt was pending, then FDC.REG_DATA.RES.INVALID should be returned (via ST0).
|
||||
*/
|
||||
if (this.regOutput & FDC.REG_OUTPUT.INT_ENABLE) {
|
||||
if (drive && !(drive.errorCode & FDC.REG_DATA.ERR.NOT_READY) && fIRQ) {
|
||||
if (drive && !(drive.resCode & FDC.REG_DATA.RES.NOT_READY) && fIRQ) {
|
||||
if (this.chipset) this.chipset.setIRR(ChipSet.IRQ.FDC);
|
||||
}
|
||||
}
|
||||
|
|
@ -1561,10 +1718,10 @@ 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 ? this.dbg.MESSAGE_PORT : 0) | this.dbg.MESSAGE_FDC)) {
|
||||
if (DEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled((this.regDataIndex > 0? this.dbg.MESSAGE_PORT : 0) | this.dbg.MESSAGE_FDC)) {
|
||||
var bCmdMasked = bCmd & FDC.REG_DATA.CMD.MASK;
|
||||
if (!name && !this.regDataIndex && FDC.aCmdSeqs[bCmdMasked]) name = FDC.aCmdSeqs[bCmdMasked].name;
|
||||
this.dbg.message("FDC.CMD[" + (name !== undefined ? name : this.regDataIndex) + "]: 0x" + str.toHexByte(bCmd));
|
||||
this.dbg.message("FDC.CMD[" + (name || this.regDataIndex) + "]: 0x" + str.toHexByte(bCmd));
|
||||
}
|
||||
this.regDataIndex++;
|
||||
return bCmd;
|
||||
|
|
@ -1615,41 +1772,41 @@ FDC.prototype.beginResult = function()
|
|||
*/
|
||||
FDC.prototype.pushResult = function(bResult, name)
|
||||
{
|
||||
if (DEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(this.dbg.MESSAGE_PORT | this.dbg.MESSAGE_FDC)) this.dbg.message("FDC.RES[" + (name !== undefined ? name : this.regDataTotal) + "]: 0x" + str.toHexByte(bResult));
|
||||
if (DEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(this.dbg.MESSAGE_PORT | this.dbg.MESSAGE_FDC)) this.dbg.message("FDC.RES[" + (name || this.regDataTotal) + "]: 0x" + str.toHexByte(bResult));
|
||||
this.regDataArray[this.regDataTotal++] = bResult;
|
||||
};
|
||||
|
||||
/**
|
||||
* pushST0(errorCode)
|
||||
* pushST0(drive)
|
||||
*
|
||||
* @this {FDC}
|
||||
* @param {number} errorCode
|
||||
* @param {Object} drive
|
||||
*/
|
||||
FDC.prototype.pushST0 = function(errorCode)
|
||||
FDC.prototype.pushST0 = function(drive)
|
||||
{
|
||||
this.pushResult(this.iUnit | this.aDrives[this.iUnit].bHead | (errorCode & FDC.REG_DATA.ERR.ST0), "ST0");
|
||||
this.pushResult(drive.iDrive | drive.bHead | (drive.resCode & FDC.REG_DATA.RES.ST0), "ST0");
|
||||
};
|
||||
|
||||
/**
|
||||
* pushST1(errorCode)
|
||||
* pushST1(drive)
|
||||
*
|
||||
* @this {FDC}
|
||||
* @param {number} errorCode
|
||||
* @param {Object} drive
|
||||
*/
|
||||
FDC.prototype.pushST1 = function(errorCode)
|
||||
FDC.prototype.pushST1 = function(drive)
|
||||
{
|
||||
this.pushResult((errorCode & FDC.REG_DATA.ERR.ST1) >> 8, "ST1");
|
||||
this.pushResult((drive.resCode & FDC.REG_DATA.RES.ST1) >>> 8, "ST1");
|
||||
};
|
||||
|
||||
/**
|
||||
* pushST2(errorCode)
|
||||
* pushST2(drive)
|
||||
*
|
||||
* @this {FDC}
|
||||
* @param {number} errorCode
|
||||
* @param {Object} drive
|
||||
*/
|
||||
FDC.prototype.pushST2 = function(errorCode)
|
||||
FDC.prototype.pushST2 = function(drive)
|
||||
{
|
||||
this.pushResult((errorCode & FDC.REG_DATA.ERR.ST2) >> 16, "ST2");
|
||||
this.pushResult((drive.resCode & FDC.REG_DATA.RES.ST2) >>> 16, "ST2");
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1660,10 +1817,7 @@ FDC.prototype.pushST2 = function(errorCode)
|
|||
*/
|
||||
FDC.prototype.pushST3 = function(drive)
|
||||
{
|
||||
//
|
||||
// WARNING: Unimplemented
|
||||
//
|
||||
this.pushResult(0x00, "ST3");
|
||||
this.pushResult((drive.resCode & FDC.REG_DATA.RES.ST3) >>> 24, "ST3");
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1737,13 +1891,13 @@ FDC.prototype.doRead = function(drive)
|
|||
* With only NOT_READY and INCOMPLETE set, an empty drive causes DOS to report "General Failure";
|
||||
* with the addition of NO_DATA, DOS reports "Sector not found".
|
||||
*/
|
||||
drive.errorCode = FDC.REG_DATA.ERR.NOT_READY | FDC.REG_DATA.ERR.INCOMPLETE;
|
||||
drive.resCode = FDC.REG_DATA.RES.NOT_READY | FDC.REG_DATA.RES.INCOMPLETE;
|
||||
|
||||
if (DEBUG) this.messageDebugger("FDC.doRead(" + drive.bCylinder + ":" + drive.bHead + ":" + drive.bSector + ":" + drive.nBytes + ")");
|
||||
|
||||
if (drive.disk) {
|
||||
drive.sector = null;
|
||||
drive.errorCode = FDC.REG_DATA.ERR.NONE;
|
||||
drive.resCode = FDC.REG_DATA.RES.NONE;
|
||||
if (this.chipset) {
|
||||
this.chipset.connectDMA(ChipSet.DMA_FDC, this, 'dmaRead', drive);
|
||||
this.chipset.requestDMA(ChipSet.DMA_FDC);
|
||||
|
|
@ -1759,17 +1913,17 @@ FDC.prototype.doRead = function(drive)
|
|||
*/
|
||||
FDC.prototype.doWrite = function(drive)
|
||||
{
|
||||
drive.errorCode = FDC.REG_DATA.ERR.NOT_READY | FDC.REG_DATA.ERR.INCOMPLETE;
|
||||
drive.resCode = FDC.REG_DATA.RES.NOT_READY | FDC.REG_DATA.RES.INCOMPLETE;
|
||||
|
||||
if (DEBUG) this.messageDebugger("FDC.doWrite(" + drive.bCylinder + ":" + drive.bHead + ":" + drive.bSector + ":" + drive.nBytes + ")");
|
||||
|
||||
if (drive.disk) {
|
||||
if (drive.disk.fWriteProtected) {
|
||||
drive.errorCode = FDC.REG_DATA.ERR.NOT_WRITABLE | FDC.REG_DATA.ERR.INCOMPLETE;
|
||||
drive.resCode = FDC.REG_DATA.RES.NOT_WRITABLE | FDC.REG_DATA.RES.INCOMPLETE;
|
||||
return;
|
||||
}
|
||||
drive.sector = null;
|
||||
drive.errorCode = FDC.REG_DATA.ERR.NONE;
|
||||
drive.resCode = FDC.REG_DATA.RES.NONE;
|
||||
if (this.chipset) {
|
||||
this.chipset.connectDMA(ChipSet.DMA_FDC, this, 'dmaWrite', drive);
|
||||
this.chipset.requestDMA(ChipSet.DMA_FDC);
|
||||
|
|
@ -1794,13 +1948,13 @@ FDC.prototype.doWrite = function(drive)
|
|||
*/
|
||||
FDC.prototype.doFormat = function(drive)
|
||||
{
|
||||
drive.errorCode = FDC.REG_DATA.ERR.NOT_READY | FDC.REG_DATA.ERR.INCOMPLETE;
|
||||
drive.resCode = FDC.REG_DATA.RES.NOT_READY | FDC.REG_DATA.RES.INCOMPLETE;
|
||||
|
||||
//if (DEBUG) this.messageDebugger("doFormat()");
|
||||
|
||||
if (drive.disk) {
|
||||
drive.sector = null;
|
||||
drive.errorCode = FDC.REG_DATA.ERR.NONE;
|
||||
drive.resCode = FDC.REG_DATA.RES.NONE;
|
||||
if (this.chipset) {
|
||||
drive.cbFormat = 0;
|
||||
drive.abFormat = new Array(4);
|
||||
|
|
@ -1840,7 +1994,7 @@ FDC.prototype.doFormat = function(drive)
|
|||
FDC.prototype.readByte = function(drive, done)
|
||||
{
|
||||
var b = -1;
|
||||
if (!drive.errorCode && drive.disk) {
|
||||
if (!drive.resCode && drive.disk) {
|
||||
do {
|
||||
if (drive.sector) {
|
||||
if ((b = drive.disk.read(drive.sector, drive.ibSector++)) >= 0)
|
||||
|
|
@ -1851,7 +2005,7 @@ FDC.prototype.readByte = function(drive, done)
|
|||
*/
|
||||
drive.sector = drive.disk.seek(drive.bCylinder, drive.bHead, drive.bSector);
|
||||
if (!drive.sector) {
|
||||
drive.errorCode = FDC.REG_DATA.ERR.NO_DATA | FDC.REG_DATA.ERR.INCOMPLETE;
|
||||
drive.resCode = FDC.REG_DATA.RES.NO_DATA | FDC.REG_DATA.RES.INCOMPLETE;
|
||||
break;
|
||||
}
|
||||
drive.ibSector = 0;
|
||||
|
|
@ -1888,7 +2042,7 @@ FDC.prototype.readByte = function(drive, done)
|
|||
*/
|
||||
FDC.prototype.writeByte = function(drive, b)
|
||||
{
|
||||
if (drive.errorCode || !drive.disk) return -1;
|
||||
if (drive.resCode || !drive.disk) return -1;
|
||||
do {
|
||||
if (drive.sector) {
|
||||
if (drive.disk.write(drive.sector, drive.ibSector++, b))
|
||||
|
|
@ -1900,9 +2054,9 @@ FDC.prototype.writeByte = function(drive, b)
|
|||
drive.sector = drive.disk.seek(drive.bCylinder, drive.bHead, drive.bSector);
|
||||
if (!drive.sector) {
|
||||
/*
|
||||
* TODO: Determine whether this should be FDC.REG_DATA.ERR.CRC_ERROR or FDC.REG_DATA.ERR.DATA_FIELD
|
||||
* TODO: Determine whether this should be FDC.REG_DATA.RES.CRC_ERROR or FDC.REG_DATA.RES.DATA_FIELD
|
||||
*/
|
||||
drive.errorCode = FDC.REG_DATA.ERR.CRC_ERROR | FDC.REG_DATA.ERR.INCOMPLETE;
|
||||
drive.resCode = FDC.REG_DATA.RES.CRC_ERROR | FDC.REG_DATA.RES.INCOMPLETE;
|
||||
b = -1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1922,7 +2076,7 @@ FDC.prototype.writeByte = function(drive, b)
|
|||
*/
|
||||
FDC.prototype.writeFormat = function(drive, b)
|
||||
{
|
||||
if (drive.errorCode) return -1;
|
||||
if (drive.resCode) return -1;
|
||||
drive.abFormat[drive.cbFormat++] = b;
|
||||
if (drive.cbFormat == drive.abFormat.length) {
|
||||
drive.bCylinder = drive.abFormat[0]; // C
|
||||
|
|
|
|||
|
|
@ -1228,49 +1228,51 @@ HDC.prototype.doCmd = function() {
|
|||
* processed in the first group, and all the rest should be processed in the second group.
|
||||
*/
|
||||
switch (bCmd) {
|
||||
case HDC.REG_DATA.CMD.REQUEST_SENSE: // 0x03
|
||||
this.beginResult(drive? drive.errorCode : HDC.REG_DATA.ERR.NOT_READY);
|
||||
this.pushResult(b1);
|
||||
this.pushResult(b2);
|
||||
this.pushResult(b3);
|
||||
/*
|
||||
* Although not terribly clear from IBM's "Fixed Disk Adapter" documentation,
|
||||
* a data "status byte" also follows the 4 "sense bytes". Interestingly, The HDC BIOS
|
||||
* checks that data status byte for REG_DATA.STATUS_ERROR, but I have to wonder if it
|
||||
* would have ever been set for this command....
|
||||
*
|
||||
* The whole point of the HDC.REG_DATA.CMD.REQUEST_SENSE command is to obtain details about a
|
||||
* previous error, so if HDC.REG_DATA.CMD.REQUEST_SENSE itself reports an error, what would that mean?
|
||||
*/
|
||||
this.pushResult(HDC.REG_DATA.STATUS_OK | bDrive);
|
||||
bCmd = -1; // mark the command as complete
|
||||
break;
|
||||
case HDC.REG_DATA.CMD.INIT_DRIVE: // 0x0C
|
||||
/*
|
||||
* Pop off all the extra "Initialize Drive Characteristics" bytes and store them,
|
||||
* for the benefit of other functions, like verifyDrive().
|
||||
*/
|
||||
var i = 0;
|
||||
while ((bParm = this.popCmd()) >= 0) {
|
||||
if (drive && i < drive.abDriveParms.length) {
|
||||
drive.abDriveParms[i++] = bParm;
|
||||
}
|
||||
case HDC.REG_DATA.CMD.REQUEST_SENSE: // 0x03
|
||||
this.beginResult(drive? drive.errorCode : HDC.REG_DATA.ERR.NOT_READY);
|
||||
this.pushResult(b1);
|
||||
this.pushResult(b2);
|
||||
this.pushResult(b3);
|
||||
/*
|
||||
* Although not terribly clear from IBM's "Fixed Disk Adapter" documentation,
|
||||
* a data "status byte" also follows the 4 "sense bytes". Interestingly, The HDC BIOS
|
||||
* checks that data status byte for REG_DATA.STATUS_ERROR, but I have to wonder if it
|
||||
* would have ever been set for this command....
|
||||
*
|
||||
* The whole point of the HDC.REG_DATA.CMD.REQUEST_SENSE command is to obtain details about a
|
||||
* previous error, so if HDC.REG_DATA.CMD.REQUEST_SENSE itself reports an error, what would that mean?
|
||||
*/
|
||||
this.pushResult(HDC.REG_DATA.STATUS_OK | bDrive);
|
||||
bCmd = -1; // mark the command as complete
|
||||
break;
|
||||
case HDC.REG_DATA.CMD.INIT_DRIVE: // 0x0C
|
||||
/*
|
||||
* Pop off all the extra "Initialize Drive Characteristics" bytes and store them,
|
||||
* for the benefit of other functions, like verifyDrive().
|
||||
*/
|
||||
var i = 0;
|
||||
while ((bParm = this.popCmd()) >= 0) {
|
||||
if (drive && i < drive.abDriveParms.length) {
|
||||
drive.abDriveParms[i++] = bParm;
|
||||
}
|
||||
if (drive) this.verifyDrive(drive);
|
||||
bDataStatus = HDC.REG_DATA.STATUS_OK;
|
||||
if (!drive && this.iDriveAllowFail == iDrive) {
|
||||
this.iDriveAllowFail = -1;
|
||||
if (DEBUG) this.messageDebugger("HDC.doCmd(): fake failure triggered");
|
||||
bDataStatus = HDC.REG_DATA.STATUS_ERROR;
|
||||
}
|
||||
this.beginResult(bDataStatus | bDrive);
|
||||
bCmd = -1; // mark the command as complete
|
||||
break;
|
||||
case HDC.REG_DATA.CMD.RAM_DIAGNOSTIC: // 0xE0
|
||||
case HDC.REG_DATA.CMD.CTL_DIAGNOSTIC: // 0xE4
|
||||
this.beginResult(HDC.REG_DATA.STATUS_OK | bDrive);
|
||||
bCmd = -1; // mark the command as complete
|
||||
break;
|
||||
}
|
||||
if (drive) this.verifyDrive(drive);
|
||||
bDataStatus = HDC.REG_DATA.STATUS_OK;
|
||||
if (!drive && this.iDriveAllowFail == iDrive) {
|
||||
this.iDriveAllowFail = -1;
|
||||
if (DEBUG) this.messageDebugger("HDC.doCmd(): fake failure triggered");
|
||||
bDataStatus = HDC.REG_DATA.STATUS_ERROR;
|
||||
}
|
||||
this.beginResult(bDataStatus | bDrive);
|
||||
bCmd = -1; // mark the command as complete
|
||||
break;
|
||||
case HDC.REG_DATA.CMD.RAM_DIAGNOSTIC: // 0xE0
|
||||
case HDC.REG_DATA.CMD.CTL_DIAGNOSTIC: // 0xE4
|
||||
this.beginResult(HDC.REG_DATA.STATUS_OK | bDrive);
|
||||
bCmd = -1; // mark the command as complete
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (bCmd >= 0) {
|
||||
|
|
@ -1286,46 +1288,46 @@ HDC.prototype.doCmd = function() {
|
|||
drive.senseCode = 0;
|
||||
}
|
||||
switch (bCmd) {
|
||||
case HDC.REG_DATA.CMD.TEST_READY: // 0x00
|
||||
this.beginResult(HDC.REG_DATA.STATUS_OK | bDrive);
|
||||
break;
|
||||
case HDC.REG_DATA.CMD.RECALIBRATE: // 0x01
|
||||
drive.bControl = bControl;
|
||||
if (DEBUG) this.messageDebugger("HDC.doCmd(): drive " + iDrive + " control byte: 0x" + str.toHexByte(bControl));
|
||||
this.beginResult(HDC.REG_DATA.STATUS_OK | bDrive);
|
||||
break;
|
||||
case HDC.REG_DATA.CMD.READ_VERIFY: // 0x05
|
||||
/*
|
||||
* This is a non-DMA operation, so we simply pretend everything is OK for now; TODO: Revisit.
|
||||
*/
|
||||
this.beginResult(HDC.REG_DATA.STATUS_OK | bDrive);
|
||||
break;
|
||||
case HDC.REG_DATA.CMD.READ_DATA: // 0x08
|
||||
this.doRead(drive, function(bStatus) {
|
||||
hdc.beginResult(bStatus | bDrive);
|
||||
});
|
||||
break;
|
||||
case HDC.REG_DATA.CMD.WRITE_DATA: // 0x0A
|
||||
/*
|
||||
* QUESTION: The IBM TechRef (p1-188) implies that bCount is used as part of HDC.REG_DATA.CMD.WRITE_DATA command,
|
||||
* but it is omitted from the HDC.REG_DATA.CMD.READ_DATA command. Is that correct? Note that, as far as the length
|
||||
* of the transfer is concerned, we rely exclusively on the DMA controller being programmed with the
|
||||
* appropriate byte count.
|
||||
*/
|
||||
this.doWrite(drive, function(bStatus) {
|
||||
hdc.beginResult(bStatus | bDrive);
|
||||
});
|
||||
break;
|
||||
case HDC.REG_DATA.CMD.WRITE_BUFFER: // 0x0F
|
||||
this.doWriteToBuffer(drive, function(bStatus) {
|
||||
hdc.beginResult(bStatus | bDrive);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
if (DEBUG) this.messageDebugger((bCmd < 0? "HDC.doCmd(): invalid drive" : "unsupported operation") + " (command=0x" + str.toHexByte(bCmdOrig) + ",drive=" + iDrive + ")");
|
||||
this.beginResult(HDC.REG_DATA.STATUS_ERROR | bDrive);
|
||||
if (DEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(this.dbg.MESSAGE_HDC) && bCmd >= 0) this.cpu.haltCPU();
|
||||
break;
|
||||
case HDC.REG_DATA.CMD.TEST_READY: // 0x00
|
||||
this.beginResult(HDC.REG_DATA.STATUS_OK | bDrive);
|
||||
break;
|
||||
case HDC.REG_DATA.CMD.RECALIBRATE: // 0x01
|
||||
drive.bControl = bControl;
|
||||
if (DEBUG) this.messageDebugger("HDC.doCmd(): drive " + iDrive + " control byte: 0x" + str.toHexByte(bControl));
|
||||
this.beginResult(HDC.REG_DATA.STATUS_OK | bDrive);
|
||||
break;
|
||||
case HDC.REG_DATA.CMD.READ_VERIFY: // 0x05
|
||||
/*
|
||||
* This is a non-DMA operation, so we simply pretend everything is OK for now; TODO: Revisit.
|
||||
*/
|
||||
this.beginResult(HDC.REG_DATA.STATUS_OK | bDrive);
|
||||
break;
|
||||
case HDC.REG_DATA.CMD.READ_DATA: // 0x08
|
||||
this.doRead(drive, function(bStatus) {
|
||||
hdc.beginResult(bStatus | bDrive);
|
||||
});
|
||||
break;
|
||||
case HDC.REG_DATA.CMD.WRITE_DATA: // 0x0A
|
||||
/*
|
||||
* QUESTION: The IBM TechRef (p1-188) implies that bCount is used as part of HDC.REG_DATA.CMD.WRITE_DATA command,
|
||||
* but it is omitted from the HDC.REG_DATA.CMD.READ_DATA command. Is that correct? Note that, as far as the length
|
||||
* of the transfer is concerned, we rely exclusively on the DMA controller being programmed with the
|
||||
* appropriate byte count.
|
||||
*/
|
||||
this.doWrite(drive, function(bStatus) {
|
||||
hdc.beginResult(bStatus | bDrive);
|
||||
});
|
||||
break;
|
||||
case HDC.REG_DATA.CMD.WRITE_BUFFER: // 0x0F
|
||||
this.doWriteToBuffer(drive, function(bStatus) {
|
||||
hdc.beginResult(bStatus | bDrive);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
if (DEBUG) this.messageDebugger((bCmd < 0? "HDC.doCmd(): invalid drive" : "unsupported operation") + " (command=0x" + str.toHexByte(bCmdOrig) + ",drive=" + iDrive + ")");
|
||||
this.beginResult(HDC.REG_DATA.STATUS_ERROR | bDrive);
|
||||
if (DEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(this.dbg.MESSAGE_HDC) && bCmd >= 0) this.cpu.haltCPU();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2268,7 +2268,7 @@ Video.prototype.reset = function()
|
|||
* on the EGA's own switch settings instead.
|
||||
*/
|
||||
if (this.chipset) {
|
||||
nMonitorType = this.chipset.getSW1VideoMonitor();
|
||||
nMonitorType = this.chipset.getSWVideoMonitor();
|
||||
}
|
||||
|
||||
var fEGA = false;
|
||||
|
|
|
|||
Loading…
Reference in a new issue