memdisk: install a DPT if needed in INT 1Eh; better zero-drive detection
authorH. Peter Anvin <hpa@zytor.com>
Thu, 3 Jul 2008 22:22:39 +0000 (15:22 -0700)
committerH. Peter Anvin <hpa@zytor.com>
Thu, 3 Jul 2008 22:22:39 +0000 (15:22 -0700)
Per the Interrupt list, treat INT 13 08 returning with CL=0 as a
failure, meaning single drive only.

If we find ourselves the only floppy drive, install a DPT into INT
1Eh.  This appears to be needed for PC-DOS 7.0 to boot.  This can be
overridden with the "nodpt" option, and forced with the "dpt" option.

memdisk/memdisk.asm
memdisk/memdisk.h
memdisk/setup.c

index f175ef4..15d662d 100644 (file)
@@ -917,7 +917,7 @@ Mover_dummy2:       dd 0, 0, 0, 0           ; More space for the BIOS
 
                alignb 4, db 0
 MemDisk_Info   equ $                   ; Pointed to by installation check
-MDI_Bytes      dw 27                   ; Total bytes in MDI structure
+MDI_Bytes      dw MDI_Len              ; Total bytes in MDI structure
 MDI_Version    db VER_MINOR, VER_MAJOR ; MEMDISK version
 
 PatchArea      equ $                   ; This gets filled in by the installer
@@ -931,10 +931,18 @@ OldInt15  dd 0                    ; INT 15h in chain
 
 OldDosMem      dw 0                    ; Old position of DOS mem end
 BootLoaderID   db 0                    ; Boot loader ID from header
+               db 0                    ; pad
+
+DPT_ptr                dw 0                    ; If nonzero, pointer to DPT
+                                       ; Original DPT pointer follows
+
+MDI_Len                equ $-MemDisk_Info
+
 ; ---- MDI structure ends here ---
 Int13MaxFunc   db Int13FuncsCnt-1      ; Max INT 13h function (to disable EDD)
+               db 0                    ; pad
 
-               db 0, 0                 ; pad
+               dw 0                    ; pad
 MemInt1588     dw 0                    ; 1MB-65MB memory amount (1K)
 
 Cylinders      dw 0                    ; Cylinder count
@@ -954,6 +962,8 @@ MyStack             dw 0                    ; Offset of stack
 StatusPtr      dw 0                    ; Where to save status (zeroseg ptr)
 
 DPT            times 16 db 0           ; BIOS parameter table pointer (floppies)
+OldInt1E       dd 0                    ; Previous INT 1E pointer (DPT)
+
 %if EDD
 EDD_DPT:
 .length                dw 30
index bb2057d..615e3d1 100644 (file)
@@ -19,6 +19,8 @@
 #ifndef MEMDISK_H
 #define MEMDISK_H
 
+#include <stddef.h>
+
 /* We use the com32 interface for calling 16-bit code */
 #include <com32.h>
 
index ba02582..32dd8ed 100644 (file)
@@ -55,7 +55,11 @@ typedef union {
     uint8_t ffill;             /* Format fill byte */
     uint8_t settle;            /* Head settle time (ms) */
     uint8_t mstart;            /* Motor start time */
-    uint8_t _pad1;             /* Padding */
+    uint8_t maxtrack;          /* Maximum track number */
+
+    uint8_t rate;              /* Data transfer rate */
+    uint8_t cmos;              /* CMOS type */
+    uint8_t pad[2];
 
     uint32_t old_fd_dpt;       /* Extension: pointer to old INT 1Eh */
   } fd;
@@ -83,10 +87,15 @@ struct patch_area {
 
   uint16_t olddosmem;
   uint8_t  bootloaderid;
+  uint8_t  _pad1;
+
+  uint16_t dpt_ptr;
+  /* End of the official MemDisk_Info */
   uint8_t  maxint13func;
 #define MAXINT13_NOEDD 0x16
+  uint8_t  _pad2;
 
-  uint8_t  _pad[2];
+  uint16_t _pad3;
   uint16_t memint1588;
 
   uint16_t cylinders;
@@ -597,6 +606,7 @@ __cdecl void setup(__cdecl syscall_t cs_syscall, void *cs_bounce)
   uint32_t ramdisk_image, ramdisk_size;
   int bios_drives;
   int do_edd = -1;             /* -1 = default, 0 = no, 1 = yes */
+  int no_bpt;                  /* No valid BPT presented */
 
   /* Set up global variables */
   syscall = cs_syscall;
@@ -714,6 +724,8 @@ __cdecl void setup(__cdecl syscall_t cs_syscall, void *cs_bounce)
     pptr->dpt.fd.ffill    = 0xf6;
     pptr->dpt.fd.settle   = 0x0f;
     pptr->dpt.fd.mstart   = 0x05;
+    pptr->dpt.fd.maxtrack = geometry->c-1;
+    pptr->dpt.fd.cmos     = geometry->type > 5 ? 5 : geometry->type;
 
     pptr->dpt.fd.old_fd_dpt = rdz_32(BIOS_INT1E);
   }
@@ -798,6 +810,7 @@ __cdecl void setup(__cdecl syscall_t cs_syscall, void *cs_bounce)
     bios_drives = 0;
     pptr->drivecnt = 0;
     pptr->oldint13 = driverptr+hptr->iret_offs;
+    no_bpt = 1;
   } else {
     /* Query drive parameters of this type */
     memset(&regs, 0, sizeof regs);
@@ -806,13 +819,17 @@ __cdecl void setup(__cdecl syscall_t cs_syscall, void *cs_bounce)
     regs.edx.b[0] = geometry->driveno & 0x80;
     syscall(0x13, &regs, &regs);
 
-    if ( regs.eflags.l & 1 ) {
+    /* Note: per suggestion from the Interrupt List, consider
+       INT 13 08 to have failed if the sector count in CL is zero. */
+    if ((regs.eflags.l & 1) || !(regs.ecx.b[0] & 0x3f)) {
       printf("INT 13 08: Failure, assuming this is the only drive\n");
       pptr->drivecnt = 0;
+      no_bpt = 1;
     } else {
       printf("INT 13 08: Success, count = %u, BPT = %04x:%04x\n",
             regs.edx.b[0], regs.es, regs.edi.w[0]);
       pptr->drivecnt = regs.edx.b[0];
+      no_bpt = !(regs.es|regs.edi.w[0]);
     }
 
     /* Compare what INT 13h returned with the appropriate equipment byte */
@@ -848,21 +865,18 @@ __cdecl void setup(__cdecl syscall_t cs_syscall, void *cs_bounce)
   /* Copy driver followed by E820 table followed by command line */
   {
     unsigned char *dpp = (unsigned char *)(driverseg << 4);
+
+    /* Adjust these pointers to point to the installed image */
+    /* Careful about the order here... the image isn't copied yet! */
+    pptr = (struct patch_area *)(dpp + hptr->patch_offs);
+    hptr = (struct memdisk_header *)dpp;
+
+    /* Actually copy to low memory */
     dpp = memcpy_endptr(dpp, &_binary_memdisk_bin_start, bin_size);
     dpp = memcpy_endptr(dpp, ranges, (nranges+1)*sizeof(ranges[0]));
     dpp = memcpy_endptr(dpp, shdr->cmdline, cmdlinelen+1);
   }
 
-  /* Install the interrupt handlers */
-  printf("old: int13 = %08x  int15 = %08x\n",
-        rdz_32(BIOS_INT13), rdz_32(BIOS_INT15));
-
-  wrz_32(BIOS_INT13, driverptr+hptr->int13_offs);
-  wrz_32(BIOS_INT15, driverptr+hptr->int15_offs);
-
-  printf("new: int13 = %08x  int15 = %08x\n",
-        rdz_32(BIOS_INT13), rdz_32(BIOS_INT15));
-
   /* Update various BIOS magic data areas (gotta love this shit) */
 
   if ( geometry->driveno & 0x80 ) {
@@ -886,8 +900,28 @@ __cdecl void setup(__cdecl syscall_t cs_syscall, void *cs_bounce)
       equip |= ((nflop-1) << 6) | 0x01;
 
     wrz_8(BIOS_EQUIP, equip);
+
+    /* Install DPT pointer if this was the only floppy */
+    if (getcmditem("dpt") != CMD_NOTFOUND ||
+       ((nflop == 1 || no_bpt)
+        && getcmditem("nodpt") == CMD_NOTFOUND)) {
+      /* Do install a replacement DPT into INT 1Eh */
+      pptr->dpt_ptr = hptr->patch_offs + offsetof(struct patch_area, dpt);
+    }
   }
 
+  /* Install the interrupt handlers */
+  printf("old: int13 = %08x  int15 = %08x  int1e = %08x\n",
+        rdz_32(BIOS_INT13), rdz_32(BIOS_INT15), rdz_32(BIOS_INT1E));
+
+  wrz_32(BIOS_INT13, driverptr+hptr->int13_offs);
+  wrz_32(BIOS_INT15, driverptr+hptr->int15_offs);
+  if (pptr->dpt_ptr)
+    wrz_32(BIOS_INT1E, driverptr+pptr->dpt_ptr);
+
+  printf("new: int13 = %08x  int15 = %08x  int1e = %08x\n",
+        rdz_32(BIOS_INT13), rdz_32(BIOS_INT15), rdz_32(BIOS_INT1E));
+
   /* Reboot into the new "disk"; this is also a test for the interrupt hooks */
   puts("Loading boot sector... ");