----------------------------------------------------------------------
Patch name: patch.floppy-athiel
Author: Alex Thiel (uploaded by cbothamy)
Date: 8 Nov 2002
Status: Proposed 

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).

We still have no test case for the non-DMA mode and the overrun/underrun (timeout)
code fails if cpu speedups are enabled. The timeout code expects at least one DMA
cycle within 15 usec (Volker Ruppert, Mar 12th 2005). 

Patch was created with:
  cvs diff -u
Apply patch to what version:
  cvs checked out on 11 Mar 2005
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.77
diff -u -r1.77 floppy.cc
--- iodev/floppy.cc	2005-03-11 21:12:52 +0100
+++ iodev/floppy.cc	2005-03-12 15:23:40 +0200
@@ -73,11 +73,18 @@
 #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
 
 #define FLOPPY_DMA_CHAN 2
 
+#define FD_TIMEOUT  15  // for FIFO overrun/underrun
+#define FD_IRQ_DELAY   2  // delay so the system can detect a INT change
+
 typedef struct {
   unsigned id;
   Bit8u trk;
@@ -391,6 +398,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 cpu
+
+       /* 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;
@@ -527,6 +548,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 cpu 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)
@@ -670,7 +705,7 @@
       head_load_time = BX_FD_THIS s.command[2] >> 1;
       BX_FD_THIS s.non_dma = BX_FD_THIS s.command[2] & 0x01;
       if (BX_FD_THIS s.non_dma)
-        BX_ERROR(("non DMA mode not implemented yet"));
+        BX_INFO(("non DMA mode selected"));
       enter_idle_phase();
       return;
       break;
@@ -839,10 +874,14 @@
       /* 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;
@@ -957,21 +996,25 @@
         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;
-        }
-      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_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
+
+        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
+      } else
         BX_PANIC(("floppy_command(): unknown read/write command"));
 
       return;
@@ -1138,6 +1181,31 @@
       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
+        // FIXME: this code requires at least one DMA cycle within 15 usec
+        //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;
@@ -1163,9 +1231,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;
 
@@ -1174,7 +1244,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) {
@@ -1215,6 +1284,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;
@@ -1279,7 +1351,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) {
@@ -1322,6 +1393,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] ++;
@@ -1344,6 +1423,12 @@
       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
@@ -1702,14 +1787,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;
@@ -1718,6 +1812,8 @@
     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 */
+
+    bx_pc_system.deactivate_timer( BX_FD_THIS s.floppy_timer_index ); // clear pending timeout
     BX_FD_THIS raise_interrupt();
     break;
   }
@@ -1729,6 +1825,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;
   BX_FD_THIS s.command_size = 0;
