Implement cdrom disk emulation at high-level instead of in low-level ATA.
authorKevin O'Connor <kevin@koconnor.net>
Sun, 9 Aug 2009 21:25:19 +0000 (17:25 -0400)
committerKevin O'Connor <kevin@koconnor.net>
Sun, 9 Aug 2009 21:25:19 +0000 (17:25 -0400)
Add a 2K buffer to the ebda to allow for cdrom 512 byte vs 2048 byte
    sector emulation.
For unaliagned cdemu reads, just make multiple cdrom reads instead of
    using ata specific code for short reads.
Also, define cdemu virtual sectors using struct chs_s, and update
    legacy_lba() func to take pointer to a chs_s struct.

src/biosvar.h
src/cdrom.c
src/disk.c

index 0d175efd9f01989d66d23f55735f93d4d6db230e..90cb7032ed7c650f1e144f58aa652ce60f179363 100644 (file)
@@ -9,20 +9,26 @@
 #include "types.h" // u8
 #include "farptr.h" // GET_FARVAR
 #include "config.h" // CONFIG_*
+#include "disk.h" // struct chs_s
 
-
-/****************************************************************
- * Interupt vector table
- ****************************************************************/
-
-struct rmode_IVT {
+struct segoff_s {
     union {
         struct {
             u16 offset;
             u16 seg;
         };
         u32 segoff;
-    } ivec[256];
+    };
+};
+#define SEGOFF(s,o) ({struct segoff_s __so; __so.offset=(o); __so.seg=(s); __so;})
+
+
+/****************************************************************
+ * Interupt vector table
+ ****************************************************************/
+
+struct rmode_IVT {
+    struct segoff_s ivec[256];
 };
 
 #define GET_IVT(vector)                                         \
@@ -178,9 +184,7 @@ struct cdemu_s {
     u16 sector_count;
 
     // Virtual device
-    u16 heads;
-    u16 cylinders;
-    u16 spt;
+    struct chs_s lchs;
 };
 
 struct fdpt_s {
@@ -233,6 +237,8 @@ struct extended_bios_data_area_s {
 
     // Stack space available for code that needs it.
     u8 extra_stack[512] __aligned(8);
+
+    u8 cdemu_buf[2048 * !!CONFIG_CDROM_EMU];
 } PACKED;
 
 // The initial size and location of EBDA
index b96afa070eda8803f796894e3be107503ce705cf..443ce14bb490f44053046e0709859d6edaa84854 100644 (file)
@@ -197,9 +197,9 @@ static void
 cdemu_1308(struct bregs *regs, u8 device)
 {
     u16 ebda_seg = get_ebda_seg();
-    u16 nlc   = GET_EBDA2(ebda_seg, cdemu.cylinders) - 1;
-    u16 nlh   = GET_EBDA2(ebda_seg, cdemu.heads) - 1;
-    u16 nlspt = GET_EBDA2(ebda_seg, cdemu.spt);
+    u16 nlc   = GET_EBDA2(ebda_seg, cdemu.lchs.cylinders) - 1;
+    u16 nlh   = GET_EBDA2(ebda_seg, cdemu.lchs.heads) - 1;
+    u16 nlspt = GET_EBDA2(ebda_seg, cdemu.lchs.spt);
 
     regs->al = 0x00;
     regs->bl = 0x00;
@@ -287,9 +287,9 @@ cdemu_134b(struct bregs *regs)
     SET_INT13ET(regs, buffer_segment, GET_EBDA2(ebda_seg, cdemu.buffer_segment));
     SET_INT13ET(regs, load_segment, GET_EBDA2(ebda_seg, cdemu.load_segment));
     SET_INT13ET(regs, sector_count, GET_EBDA2(ebda_seg, cdemu.sector_count));
-    SET_INT13ET(regs, cylinders, GET_EBDA2(ebda_seg, cdemu.cylinders));
-    SET_INT13ET(regs, sectors, GET_EBDA2(ebda_seg, cdemu.spt));
-    SET_INT13ET(regs, heads, GET_EBDA2(ebda_seg, cdemu.heads));
+    SET_INT13ET(regs, cylinders, GET_EBDA2(ebda_seg, cdemu.lchs.cylinders));
+    SET_INT13ET(regs, sectors, GET_EBDA2(ebda_seg, cdemu.lchs.spt));
+    SET_INT13ET(regs, heads, GET_EBDA2(ebda_seg, cdemu.lchs.heads));
 
     // If we have to terminate emulation
     if (regs->al == 0x00) {
@@ -497,19 +497,19 @@ cdrom_boot(int cdid)
 
         switch (media) {
         case 0x01:  // 1.2M floppy
-            SET_EBDA2(ebda_seg, cdemu.spt, 15);
-            SET_EBDA2(ebda_seg, cdemu.cylinders, 80);
-            SET_EBDA2(ebda_seg, cdemu.heads, 2);
+            SET_EBDA2(ebda_seg, cdemu.lchs.spt, 15);
+            SET_EBDA2(ebda_seg, cdemu.lchs.cylinders, 80);
+            SET_EBDA2(ebda_seg, cdemu.lchs.heads, 2);
             break;
         case 0x02:  // 1.44M floppy
-            SET_EBDA2(ebda_seg, cdemu.spt, 18);
-            SET_EBDA2(ebda_seg, cdemu.cylinders, 80);
-            SET_EBDA2(ebda_seg, cdemu.heads, 2);
+            SET_EBDA2(ebda_seg, cdemu.lchs.spt, 18);
+            SET_EBDA2(ebda_seg, cdemu.lchs.cylinders, 80);
+            SET_EBDA2(ebda_seg, cdemu.lchs.heads, 2);
             break;
         case 0x03:  // 2.88M floppy
-            SET_EBDA2(ebda_seg, cdemu.spt, 36);
-            SET_EBDA2(ebda_seg, cdemu.cylinders, 80);
-            SET_EBDA2(ebda_seg, cdemu.heads, 2);
+            SET_EBDA2(ebda_seg, cdemu.lchs.spt, 36);
+            SET_EBDA2(ebda_seg, cdemu.lchs.cylinders, 80);
+            SET_EBDA2(ebda_seg, cdemu.lchs.heads, 2);
             break;
         }
     } else {
@@ -523,9 +523,10 @@ cdrom_boot(int cdid)
         u8 cyllow = GET_FARVAR(boot_segment, mbr->partitions[0].last.cyllow);
         u8 heads = GET_FARVAR(boot_segment, mbr->partitions[0].last.heads);
 
-        SET_EBDA2(ebda_seg, cdemu.spt, sptcyl & 0x3f);
-        SET_EBDA2(ebda_seg, cdemu.cylinders, ((sptcyl<<2)&0x300) + cyllow + 1);
-        SET_EBDA2(ebda_seg, cdemu.heads, heads + 1);
+        SET_EBDA2(ebda_seg, cdemu.lchs.spt, sptcyl & 0x3f);
+        SET_EBDA2(ebda_seg, cdemu.lchs.cylinders
+                  , ((sptcyl<<2)&0x300) + cyllow + 1);
+        SET_EBDA2(ebda_seg, cdemu.lchs.heads, heads + 1);
     }
 
     // everything is ok, so from now on, the emulation is active
index e56bcd0bc355a8e806cbdf2385411cc566da6a28..441db5ccbf45798ecb15eb3d7034f5844b76193e 100644 (file)
@@ -59,9 +59,7 @@ __send_disk_op(struct disk_op_s *op_far, u16 op_seg)
     if (!dop.command)
         // If verify or seek
         status = 0;
-    if (dop.command == CMD_CDEMU_READ)
-        status = cdrom_read_512(&dop);
-    else if (dop.command == CMD_CDROM_READ)
+    if (dop.command == CMD_CDROM_READ)
         status = cdrom_read(&dop);
     else
         status = ata_cmd_data(&dop);
@@ -69,7 +67,8 @@ __send_disk_op(struct disk_op_s *op_far, u16 op_seg)
     irq_disable();
 
     // Update count with total sectors transferred.
-    SET_FARVAR(op_seg, op_far->count, GET_EBDA(sector_count));
+    if (dop.command)
+        SET_FARVAR(op_seg, op_far->count, GET_EBDA(sector_count));
 
     if (status)
         dprintf(1, "disk_op cmd %d error %d!\n", dop.command, status);
@@ -89,20 +88,24 @@ send_disk_op(struct disk_op_s *op)
 
 // Obtain the requested disk lba from an old-style chs request.
 static int
-legacy_lba(struct bregs *regs, struct disk_op_s *op, u16 nlc, u16 nlh, u16 nlspt)
+legacy_lba(struct bregs *regs, u16 lchs_seg, struct chs_s *lchs_far)
 {
-    op->count = regs->al;
+    u8 count = regs->al;
     u16 cylinder = regs->ch | ((((u16)regs->cl) << 2) & 0x300);
     u16 sector = regs->cl & 0x3f;
     u16 head = regs->dh;
 
-    if (op->count > 128 || op->count == 0 || sector == 0) {
+    if (count > 128 || count == 0 || sector == 0) {
         dprintf(1, "int13_harddisk: function %02x, parameter out of range!\n"
                 , regs->ah);
         disk_ret(regs, DISK_RET_EPARAM);
         return -1;
     }
 
+    u16 nlc = GET_FARVAR(lchs_seg, lchs_far->cylinders);
+    u16 nlh = GET_FARVAR(lchs_seg, lchs_far->heads);
+    u16 nlspt = GET_FARVAR(lchs_seg, lchs_far->spt);
+
     // sanity check on cyl heads, sec
     if (cylinder >= nlc || head >= nlh || sector > nlspt) {
         dprintf(1, "int13_harddisk: function %02x, parameters out of"
@@ -113,14 +116,8 @@ legacy_lba(struct bregs *regs, struct disk_op_s *op, u16 nlc, u16 nlh, u16 nlspt
     }
 
     // translate lchs to lba
-    op->lba = (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nlspt)
-               + (u32)sector - 1);
-
-    u16 segment = regs->es;
-    u16 offset  = regs->bx;
-    op->buf_fl = MAKE_FLATPTR(segment, offset);
-
-    return 0;
+    return (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nlspt)
+            + (u32)sector - 1);
 }
 
 // Perform read/write/verify using old-style chs accesses
@@ -130,12 +127,12 @@ basic_access(struct bregs *regs, u8 device, u16 command)
     struct disk_op_s dop;
     dop.driveid = device;
     dop.command = command;
-    u16 nlc = GET_GLOBAL(ATA.devices[device].lchs.cylinders);
-    u16 nlh = GET_GLOBAL(ATA.devices[device].lchs.heads);
-    u16 nlspt = GET_GLOBAL(ATA.devices[device].lchs.spt);
-    int ret = legacy_lba(regs, &dop, nlc, nlh, nlspt);
-    if (ret)
+    int lba = legacy_lba(regs, get_global_seg(), &ATA.devices[device].lchs);
+    if (lba < 0)
         return;
+    dop.lba = lba;
+    dop.count = regs->al;
+    dop.buf_fl = MAKE_FLATPTR(regs->es, regs->bx);
 
     int status = send_disk_op(&dop);
 
@@ -154,25 +151,65 @@ cdemu_access(struct bregs *regs, u8 device, u16 command)
 {
     struct disk_op_s dop;
     dop.driveid = device;
-    dop.command = (command == ATA_CMD_READ_SECTORS ? CMD_CDEMU_READ : 0);
+    dop.command = (command == ATA_CMD_READ_SECTORS ? CMD_CDROM_READ : 0);
     u16 ebda_seg = get_ebda_seg();
-    u16 nlc = GET_EBDA2(ebda_seg, cdemu.cylinders);
-    u16 nlh = GET_EBDA2(ebda_seg, cdemu.heads);
-    u16 nlspt = GET_EBDA2(ebda_seg, cdemu.spt);
-    int ret = legacy_lba(regs, &dop, nlc, nlh, nlspt);
-    if (ret)
+    int vlba = legacy_lba(
+        regs, ebda_seg
+        , (void*)offsetof(struct extended_bios_data_area_s, cdemu.lchs));
+    if (vlba < 0)
         return;
-    dop.lba += GET_EBDA2(ebda_seg, cdemu.ilba) * 4;
+    dop.lba = GET_EBDA2(ebda_seg, cdemu.ilba) + vlba / 4;
+    u8 count = regs->al;
+    u8 *cdbuf_far = (void*)offsetof(struct extended_bios_data_area_s, cdemu_buf);
+    u8 *dest_far = (void*)(regs->bx+0);
+    regs->al = 0;
 
-    int status = send_disk_op(&dop);
+    if (vlba & 3) {
+        dop.count = 1;
+        dop.buf_fl = MAKE_FLATPTR(ebda_seg, cdbuf_far);
+        int status = send_disk_op(&dop);
+        if (status)
+            goto fail;
+        u8 thiscount = 4 - (vlba & 3);
+        if (thiscount > count)
+            thiscount = count;
+        count -= thiscount;
+        memcpy_far(regs->es, dest_far
+                   , ebda_seg, cdbuf_far + (vlba & 3) * 512
+                   , thiscount * 512);
+        dest_far += thiscount * 512;
+        regs->al += thiscount;
+        dop.lba++;
+    }
 
-    regs->al = dop.count;
+    if (count > 3) {
+        dop.count = count / 4;
+        dop.buf_fl = MAKE_FLATPTR(regs->es, dest_far);
+        int status = send_disk_op(&dop);
+        regs->al += dop.count * 4;
+        if (status)
+            goto fail;
+        u8 thiscount = count & ~3;
+        count &= 3;
+        dest_far += thiscount * 512;
+        dop.lba += thiscount / 4;
+    }
 
-    if (status) {
-        disk_ret(regs, DISK_RET_EBADTRACK);
-        return;
+    if (count) {
+        dop.count = 1;
+        dop.buf_fl = MAKE_FLATPTR(ebda_seg, cdbuf_far);
+        int status = send_disk_op(&dop);
+        if (status)
+            goto fail;
+        u8 thiscount = count;
+        memcpy_far(regs->es, dest_far, ebda_seg, cdbuf_far, thiscount * 512);
+        regs->al += thiscount;
     }
+
     disk_ret(regs, DISK_RET_SUCCESS);
+    return;
+fail:
+    disk_ret(regs, DISK_RET_EBADTRACK);
 }
 
 // Perform read/write/verify using new-style "int13ext" accesses.