---------------------------------------------------------------------- Patch name: patch.floppy-athiel Author: Alex Thiel (uploaded by cbothamy) Date: 8 Nov 2002 Detailed description: This patch introduces the implicit termination of data transfer via end-of-track and data overrun/underrun conditions, as well as non-DMA mode. The code cleanup is present in CVS now (Volker Ruppert, Dec 1st 2002). Patch was created with: cvs diff -u Apply patch to what version: cvs checked out on 8 Nov 2002 Instructions: To patch, go to main bochs directory. Type "patch -p0 < THIS_PATCH_FILE". ---------------------------------------------------------------------- Index: iodev/floppy.cc =================================================================== RCS file: /cvsroot/bochs/bochs/iodev/floppy.cc,v retrieving revision 1.58 diff -u -r1.58 floppy.cc --- iodev/floppy.cc Sat Nov 30 10:39:29 2002 +++ iodev/floppy.cc Sat Nov 30 10:53:16 2002 @@ -61,6 +61,10 @@ #define FD_MS_ACTB 0x02 #define FD_MS_ACTA 0x01 +/* for status registers */ +#define FD_ST_EOT 0x80 +#define FD_ST_OVERRUN 0x10 + #define FROM_FLOPPY 10 #define TO_FLOPPY 11 @@ -220,7 +224,8 @@ } } - +#define FD_TIMEOUT 15 // for FIFO overrun/underrun +#define FD_IRQ_DELAY 2 // delay so the system can detect a INT change /* CMOS Equipment Byte register */ if (BX_FD_THIS s.num_supported_floppies > 0) @@ -325,6 +330,20 @@ break; case 0x3F5: /* diskette controller data */ + + /* data transfer in non-DMA mode */ + if (BX_FD_THIS s.main_status_reg & FD_MS_NDMA) { + BX_FD_THIS dma_write(&value); // write: from controller to memory + + /* This simulates the FIFO latency, see comment in timer() below. */ + BX_FD_THIS lower_interrupt(); + BX_FD_THIS s.main_status_reg &= ~FD_MS_MRQ; + // overrides the timer set in dma_write() + bx_pc_system.activate_timer(BX_FD_THIS s.floppy_timer_index, + FD_IRQ_DELAY, 0); + return(value); + } + if (BX_FD_THIS s.result_size == 0) { BX_ERROR(("port 0x3f5: no results to read")); BX_FD_THIS s.main_status_reg = 0; @@ -456,6 +475,20 @@ break; case 0x3F5: /* diskette controller data */ + + /* data transfer in non-DMA mode */ + if (BX_FD_THIS s.main_status_reg & FD_MS_NDMA) { + BX_FD_THIS dma_read((Bit8u *) &value); // read: from memory to controller + + /* This simulates the FIFO latency, see comment in timer() below. */ + BX_FD_THIS lower_interrupt(); + BX_FD_THIS s.main_status_reg &= ~FD_MS_MRQ; + // overrides the timer set in dma_read() + bx_pc_system.activate_timer(BX_FD_THIS s.floppy_timer_index, + FD_IRQ_DELAY, 0); + break; + } + BX_DEBUG(("command = %02x", (unsigned) value)); if (BX_FD_THIS s.command_complete) { if (BX_FD_THIS s.pending_command!=0) @@ -589,11 +622,6 @@ for (i=0; i> 4; head_unload_time = BX_FD_THIS s.command[1] & 0x0f; head_load_time = BX_FD_THIS s.command[2] >> 1; - if (BX_FD_THIS s.command[2] & 0x01) - BX_ERROR(("non DMA mode selected")); + BX_FD_THIS s.non_dma = BX_FD_THIS s.command[2] & 0x01; + if (BX_FD_THIS s.non_dma) + BX_INFO(("non DMA mode selected")); enter_idle_phase(); return; break; @@ -771,10 +800,15 @@ /* 4 header bytes per sector are required */ BX_FD_THIS s.format_count <<= 2; - DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1); - - /* data reg not ready, controller busy */ - BX_FD_THIS s.main_status_reg = FD_MS_BUSY; + if (BX_FD_THIS s.non_dma) { + BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_NDMA | FD_MS_BUSY; + BX_FD_THIS raise_interrupt(); + } + else { + /* data reg not ready, controller busy */ + BX_FD_THIS s.main_status_reg = FD_MS_BUSY; + DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1); + } BX_DEBUG(("format track")); return; break; @@ -891,19 +925,27 @@ floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer, 512, FROM_FLOPPY); - DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1); - - /* data reg not ready, controller busy */ - BX_FD_THIS s.main_status_reg = FD_MS_BUSY; - return; + if (BX_FD_THIS s.non_dma) { + BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_NDMA | FD_MS_DIO | FD_MS_BUSY; + BX_FD_THIS raise_interrupt(); + } + else { + /* data reg not ready, controller busy */ + BX_FD_THIS s.main_status_reg = FD_MS_BUSY; + DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1); + } } else if ((BX_FD_THIS s.command[0] & 0x7f) == 0x45) { // write - DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1); - - /* data reg not ready, controller busy */ - BX_FD_THIS s.main_status_reg = FD_MS_BUSY; - return; + if (BX_FD_THIS s.non_dma) { + BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_NDMA | FD_MS_BUSY; + BX_FD_THIS raise_interrupt(); + } + else { + /* data reg not ready, controller busy */ + BX_FD_THIS s.main_status_reg = FD_MS_BUSY; + DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1); + } } else BX_PANIC(("floppy_command(): unknown read/write command")); @@ -1015,18 +1057,43 @@ BX_FD_THIS s.DIR[drive] &= ~0x80; // clear disk change line enter_idle_phase(); - raise_interrupt(); + BX_FD_THIS raise_interrupt(); break; case 0x4a: /* read ID */ enter_result_phase(); break; + case 0x4d: // format track + case 0x46: // read normal data + case 0x66: + case 0xc6: + case 0xe6: + case 0x45: // write normal data + case 0xc5: + /* During non-DMA operation, the state of the FDC oscillates + between IRQ low/MRQ clear (set after data is transferred via 0x3f5) + and IRQ high/MRQ set. + Whenever the timer is triggered in DMA mode, or in non-DMA mode with + MRQ set, we have a data overrun/underrun. */ + if ((BX_FD_THIS s.main_status_reg & (FD_MS_MRQ | FD_MS_NDMA)) + == FD_MS_NDMA) { // NDMA & !MRQ + BX_FD_THIS raise_interrupt(); + BX_FD_THIS s.main_status_reg |= FD_MS_MRQ; + bx_pc_system.activate_timer(BX_FD_THIS s.floppy_timer_index, + FD_TIMEOUT, 0 ); + } + else { // timeout + BX_FD_THIS s.status_reg1 |= FD_ST_OVERRUN; + enter_result_phase(); + } + break; + case 0xfe: // (contrived) RESET theFloppyController->reset(BX_RESET_SOFTWARE); BX_FD_THIS s.pending_command = 0; BX_FD_THIS s.status_reg0 = 0xc0; - raise_interrupt(); + BX_FD_THIS raise_interrupt(); BX_FD_THIS s.reset_sensei = 4; break; @@ -1047,9 +1114,11 @@ // We need to return then next data byte from the floppy buffer // to be transfered via the DMA to memory. (read block from floppy) - *data_byte = BX_FD_THIS s.floppy_buffer[BX_FD_THIS s.floppy_buffer_index++]; + // reschedule timeout + bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index, FD_TIMEOUT, 0 ); + if (BX_FD_THIS s.floppy_buffer_index >= 512) { Bit8u drive; @@ -1058,7 +1127,6 @@ BX_FD_THIS s.floppy_buffer_index = 0; if (DEV_dma_get_tc()) { // Terminal Count line, done BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive] << 2) | drive; - BX_FD_THIS s.status_reg1 = 0; BX_FD_THIS s.status_reg2 = 0; if (bx_dbg.floppy) { @@ -1096,6 +1164,9 @@ Bit8u drive; Bit32u logical_sector; + // reschedule timeout + bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index, FD_TIMEOUT, 0 ); + drive = BX_FD_THIS s.DOR & 0x03; if (BX_FD_THIS s.pending_command == 0x4d) { // format track in progress --BX_FD_THIS s.format_count; @@ -1158,7 +1229,6 @@ BX_FD_THIS s.floppy_buffer_index = 0; if (DEV_dma_get_tc()) { // Terminal Count line, done BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive] << 2) | drive; - BX_FD_THIS s.status_reg1 = 0; BX_FD_THIS s.status_reg2 = 0; if (bx_dbg.floppy) { @@ -1186,6 +1256,13 @@ BX_FD_THIS s.reset_sensei = 0; } + void +bx_floppy_ctrl_c::lower_interrupt(void) +{ + DEV_pic_lower_irq(6); + BX_FD_THIS s.pending_irq = 0; +} + void bx_floppy_ctrl_c::increment_sector(void) @@ -1194,6 +1271,14 @@ drive = BX_FD_THIS s.DOR & 0x03; + if (BX_FD_THIS s.status_reg1 & FD_ST_EOT) { + /* increment past EOT: abnormal termination */ + BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; + DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0); + enter_result_phase(); + return; + } + // values after completion of data xfer // ??? calculation depends on base_count being multiple of 512 BX_FD_THIS s.sector[drive] ++; @@ -1216,6 +1301,11 @@ BX_INFO(("increment_sector: clamping cylinder to max")); } } + + /* check end-of-track condition */ + if ((BX_FD_THIS s.multi_track == BX_FD_THIS s.head[drive]) && + (BX_FD_THIS s.sector[drive] == BX_FD_THIS s.media[drive].sectors_per_track)) + BX_FD_THIS s.status_reg1 |= FD_ST_EOT; } unsigned @@ -1517,14 +1607,23 @@ BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg0; BX_FD_THIS s.result[1] = BX_FD_THIS s.cylinder[drive]; break; - case 0x4a: // read ID - case 0x4d: // format track case 0x46: // read normal data case 0x66: case 0xc6: case 0xe6: case 0x45: // write normal data case 0xc5: + /* increment sector once more if we terminated normally at EOT */ + if ((BX_FD_THIS s.status_reg0 & 0xc0) == 0x00 && + (BX_FD_THIS s.status_reg1 & FD_ST_EOT)) { + BX_FD_THIS s.status_reg1 &= ~FD_ST_EOT; // clear EOT flag + increment_sector(); + // reset the head bit + BX_FD_THIS s.status_reg0 &= 0xfb; + BX_FD_THIS s.status_reg0 |= (BX_FD_THIS s.head[drive] << 2); + } + case 0x4a: // read ID + case 0x4d: // format track BX_FD_THIS s.result_size = 7; BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg0; BX_FD_THIS s.result[1] = BX_FD_THIS s.status_reg1; @@ -1533,7 +1632,9 @@ BX_FD_THIS s.result[4] = BX_FD_THIS s.head[drive]; BX_FD_THIS s.result[5] = BX_FD_THIS s.sector[drive]; BX_FD_THIS s.result[6] = 2; /* sector size code */ - raise_interrupt(); + + bx_pc_system.deactivate_timer( BX_FD_THIS s.floppy_timer_index ); // clear pending timeout + BX_FD_THIS raise_interrupt(); break; } } @@ -1543,6 +1644,11 @@ { BX_FD_THIS s.main_status_reg &= 0x0f; // leave drive status untouched BX_FD_THIS s.main_status_reg |= FD_MS_MRQ; // data register ready + + /* do not touch ST0 and ST3 since these may be queried later via + commands 0x08 and 0x04, respectively. */ + BX_FD_THIS s.status_reg1 = 0; + BX_FD_THIS s.status_reg2 = 0; BX_FD_THIS s.command_complete = 1; /* waiting for new command */ BX_FD_THIS s.command_index = 0; Index: iodev/floppy.h =================================================================== RCS file: /cvsroot/bochs/bochs/iodev/floppy.h,v retrieving revision 1.16 diff -u -r1.16 floppy.h --- iodev/floppy.h Sat Nov 30 10:39:29 2002 +++ iodev/floppy.h Sat Nov 30 10:49:21 2002 @@ -114,6 +114,7 @@ Bit8u DIR[4]; // Digital Input Register: // b7: 0=diskette is present and has not been changed // 1=diskette missing or changed + Bit8u non_dma; } s; // state information static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); @@ -127,9 +128,10 @@ BX_FD_SMF void floppy_command(void); BX_FD_SMF void floppy_xfer(Bit8u drive, Bit32u offset, Bit8u *buffer, Bit32u bytes, Bit8u direction); BX_FD_SMF void raise_interrupt(void); + BX_FD_SMF void lower_interrupt(void); BX_FD_SMF void enter_idle_phase(void); BX_FD_SMF void enter_result_phase(void); - static void timer_handler(void *); + static void timer_handler(void *); public: BX_FD_SMF void timer(void);