Support ISA ATA devices for qemu's "-M isapc" mode.
[seabios.git] / src / disk.c
1 // 16bit code to access hard drives.
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002  MandrakeSoft S.A.
5 //
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
7
8 #include "disk.h" // floppy_13
9 #include "biosvar.h" // SET_BDA
10 #include "config.h" // CONFIG_*
11 #include "util.h" // debug_enter
12 #include "pic.h" // eoi_pic2
13 #include "bregs.h" // struct bregs
14 #include "pci.h" // pci_bdf_to_bus
15 #include "ata.h" // ATA_CB_DC
16
17
18 /****************************************************************
19  * Helper functions
20  ****************************************************************/
21
22 void
23 __disk_ret(struct bregs *regs, u32 linecode, const char *fname)
24 {
25     u8 code = linecode;
26     if (regs->dl < EXTSTART_HD)
27         SET_BDA(floppy_last_status, code);
28     else
29         SET_BDA(disk_last_status, code);
30     if (code)
31         __set_code_fail(regs, linecode, fname);
32     else
33         set_code_success(regs);
34 }
35
36 static void
37 __disk_stub(struct bregs *regs, int lineno, const char *fname)
38 {
39     __debug_stub(regs, lineno, fname);
40     __disk_ret(regs, DISK_RET_SUCCESS | (lineno << 8), fname);
41 }
42
43 #define DISK_STUB(regs)                         \
44     __disk_stub((regs), __LINE__, __func__)
45
46 static void
47 fillLCHS(struct drive_s *drive_g, u16 *nlc, u16 *nlh, u16 *nlspt)
48 {
49     if (CONFIG_CDROM_EMU && drive_g == GET_GLOBAL(cdemu_drive)) {
50         // Emulated drive - get info from ebda.  (It's not possible to
51         // populate the geometry directly in the driveid because the
52         // geometry is only known after the bios segment is made
53         // read-only).
54         u16 ebda_seg = get_ebda_seg();
55         *nlc = GET_EBDA2(ebda_seg, cdemu.lchs.cylinders);
56         *nlh = GET_EBDA2(ebda_seg, cdemu.lchs.heads);
57         *nlspt = GET_EBDA2(ebda_seg, cdemu.lchs.spt);
58         return;
59     }
60     *nlc = GET_GLOBAL(drive_g->lchs.cylinders);
61     *nlh = GET_GLOBAL(drive_g->lchs.heads);
62     *nlspt = GET_GLOBAL(drive_g->lchs.spt);
63 }
64
65 // Perform read/write/verify using old-style chs accesses
66 static void
67 basic_access(struct bregs *regs, struct drive_s *drive_g, u16 command)
68 {
69     struct disk_op_s dop;
70     dop.drive_g = drive_g;
71     dop.command = command;
72
73     u8 count = regs->al;
74     u16 cylinder = regs->ch | ((((u16)regs->cl) << 2) & 0x300);
75     u16 sector = regs->cl & 0x3f;
76     u16 head = regs->dh;
77
78     if (count > 128 || count == 0 || sector == 0) {
79         dprintf(1, "int13_harddisk: function %02x, parameter out of range!\n"
80                 , regs->ah);
81         disk_ret(regs, DISK_RET_EPARAM);
82         return;
83     }
84     dop.count = count;
85
86     u16 nlc, nlh, nlspt;
87     fillLCHS(drive_g, &nlc, &nlh, &nlspt);
88
89     // sanity check on cyl heads, sec
90     if (cylinder >= nlc || head >= nlh || sector > nlspt) {
91         dprintf(1, "int13_harddisk: function %02x, parameters out of"
92                 " range %04x/%04x/%04x!\n"
93                 , regs->ah, cylinder, head, sector);
94         disk_ret(regs, DISK_RET_EPARAM);
95         return;
96     }
97
98     // translate lchs to lba
99     dop.lba = (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nlspt)
100                + (u32)sector - 1);
101
102     dop.buf_fl = MAKE_FLATPTR(regs->es, regs->bx);
103
104     int status = send_disk_op(&dop);
105
106     regs->al = dop.count;
107
108     disk_ret(regs, status);
109 }
110
111 // Perform read/write/verify using new-style "int13ext" accesses.
112 static void
113 extended_access(struct bregs *regs, struct drive_s *drive_g, u16 command)
114 {
115     struct disk_op_s dop;
116     // Get lba and check.
117     dop.lba = GET_INT13EXT(regs, lba);
118     dop.command = command;
119     dop.drive_g = drive_g;
120     if (dop.lba >= GET_GLOBAL(drive_g->sectors)) {
121         dprintf(1, "int13_harddisk: function %02x. LBA out of range\n"
122                 , regs->ah);
123         disk_ret(regs, DISK_RET_EPARAM);
124         return;
125     }
126
127     dop.buf_fl = SEGOFF_TO_FLATPTR(GET_INT13EXT(regs, data));
128     dop.count = GET_INT13EXT(regs, count);
129
130     int status = send_disk_op(&dop);
131
132     SET_INT13EXT(regs, count, dop.count);
133
134     disk_ret(regs, status);
135 }
136
137
138 /****************************************************************
139  * Hard Drive functions
140  ****************************************************************/
141
142 // disk controller reset
143 static void
144 disk_1300(struct bregs *regs, struct drive_s *drive_g)
145 {
146     struct disk_op_s dop;
147     dop.drive_g = drive_g;
148     dop.command = CMD_RESET;
149     int status = send_disk_op(&dop);
150     disk_ret(regs, status);
151 }
152
153 // read disk status
154 static void
155 disk_1301(struct bregs *regs, struct drive_s *drive_g)
156 {
157     u8 v;
158     if (regs->dl < EXTSTART_HD)
159         // Floppy
160         v = GET_BDA(floppy_last_status);
161     else
162         v = GET_BDA(disk_last_status);
163     regs->ah = v;
164     set_cf(regs, v);
165     // XXX - clear disk_last_status?
166 }
167
168 // read disk sectors
169 static void
170 disk_1302(struct bregs *regs, struct drive_s *drive_g)
171 {
172     basic_access(regs, drive_g, CMD_READ);
173 }
174
175 // write disk sectors
176 static void
177 disk_1303(struct bregs *regs, struct drive_s *drive_g)
178 {
179     basic_access(regs, drive_g, CMD_WRITE);
180 }
181
182 // verify disk sectors
183 static void
184 disk_1304(struct bregs *regs, struct drive_s *drive_g)
185 {
186     basic_access(regs, drive_g, CMD_VERIFY);
187 }
188
189 // format disk track
190 static void
191 disk_1305(struct bregs *regs, struct drive_s *drive_g)
192 {
193     debug_stub(regs);
194
195     u16 nlc, nlh, nlspt;
196     fillLCHS(drive_g, &nlc, &nlh, &nlspt);
197
198     u8 num_sectors = regs->al;
199     u8 head        = regs->dh;
200
201     if (head >= nlh || num_sectors == 0 || num_sectors > nlspt) {
202         disk_ret(regs, DISK_RET_EPARAM);
203         return;
204     }
205
206     struct disk_op_s dop;
207     dop.drive_g = drive_g;
208     dop.command = CMD_FORMAT;
209     dop.lba = head;
210     dop.count = num_sectors;
211     dop.buf_fl = MAKE_FLATPTR(regs->es, regs->bx);
212     int status = send_disk_op(&dop);
213     disk_ret(regs, status);
214 }
215
216 // read disk drive parameters
217 static void
218 disk_1308(struct bregs *regs, struct drive_s *drive_g)
219 {
220     u16 ebda_seg = get_ebda_seg();
221     // Get logical geometry from table
222     u16 nlc, nlh, nlspt;
223     fillLCHS(drive_g, &nlc, &nlh, &nlspt);
224     nlc--;
225     nlh--;
226     u8 count;
227     if (regs->dl < EXTSTART_HD) {
228         // Floppy
229         count = GET_GLOBAL(Drives.floppycount);
230
231         if (CONFIG_CDROM_EMU && drive_g == GET_GLOBAL(cdemu_drive))
232             regs->bx = GET_EBDA2(ebda_seg, cdemu.media) * 2;
233         else
234             regs->bx = GET_GLOBAL(drive_g->floppy_type);
235
236         // set es & di to point to 11 byte diskette param table in ROM
237         regs->es = SEG_BIOS;
238         regs->di = (u32)&diskette_param_table2;
239     } else if (regs->dl < EXTSTART_CD) {
240         // Hard drive
241         count = GET_BDA(hdcount);
242         nlc--;  // last sector reserved
243     } else {
244         // Not supported on CDROM
245         disk_ret(regs, DISK_RET_EPARAM);
246         return;
247     }
248
249     if (CONFIG_CDROM_EMU && GET_EBDA2(ebda_seg, cdemu.active)) {
250         u8 emudrive = GET_EBDA2(ebda_seg, cdemu.emulated_extdrive);
251         if (((emudrive ^ regs->dl) & 0x80) == 0)
252             // Note extra drive due to emulation.
253             count++;
254         if (regs->dl < EXTSTART_HD && count > 2)
255             // Max of two floppy drives.
256             count = 2;
257     }
258
259     regs->al = 0;
260     regs->ch = nlc & 0xff;
261     regs->cl = ((nlc >> 2) & 0xc0) | (nlspt & 0x3f);
262     regs->dh = nlh;
263
264     disk_ret(regs, DISK_RET_SUCCESS);
265     regs->dl = count;
266 }
267
268 // initialize drive parameters
269 static void
270 disk_1309(struct bregs *regs, struct drive_s *drive_g)
271 {
272     DISK_STUB(regs);
273 }
274
275 // seek to specified cylinder
276 static void
277 disk_130c(struct bregs *regs, struct drive_s *drive_g)
278 {
279     DISK_STUB(regs);
280 }
281
282 // alternate disk reset
283 static void
284 disk_130d(struct bregs *regs, struct drive_s *drive_g)
285 {
286     DISK_STUB(regs);
287 }
288
289 // check drive ready
290 static void
291 disk_1310(struct bregs *regs, struct drive_s *drive_g)
292 {
293     // should look at 40:8E also???
294
295     struct disk_op_s dop;
296     dop.drive_g = drive_g;
297     dop.command = CMD_ISREADY;
298     int status = send_disk_op(&dop);
299     disk_ret(regs, status);
300 }
301
302 // recalibrate
303 static void
304 disk_1311(struct bregs *regs, struct drive_s *drive_g)
305 {
306     DISK_STUB(regs);
307 }
308
309 // controller internal diagnostic
310 static void
311 disk_1314(struct bregs *regs, struct drive_s *drive_g)
312 {
313     DISK_STUB(regs);
314 }
315
316 // read disk drive size
317 static void
318 disk_1315(struct bregs *regs, struct drive_s *drive_g)
319 {
320     disk_ret(regs, DISK_RET_SUCCESS);
321     if (regs->dl < EXTSTART_HD || regs->dl >= EXTSTART_CD) {
322         // Floppy or cdrom
323         regs->ah = 1;
324         return;
325     }
326     // Hard drive
327
328     // Get logical geometry from table
329     u16 nlc, nlh, nlspt;
330     fillLCHS(drive_g, &nlc, &nlh, &nlspt);
331
332     // Compute sector count seen by int13
333     u32 lba = (u32)(nlc - 1) * (u32)nlh * (u32)nlspt;
334     regs->cx = lba >> 16;
335     regs->dx = lba & 0xffff;
336     regs->ah = 3; // hard disk accessible
337 }
338
339 static void
340 disk_1316(struct bregs *regs, struct drive_s *drive_g)
341 {
342     if (regs->dl >= EXTSTART_HD) {
343         // Hard drive
344         disk_ret(regs, DISK_RET_EPARAM);
345         return;
346     }
347     disk_ret(regs, DISK_RET_ECHANGED);
348 }
349
350 // IBM/MS installation check
351 static void
352 disk_1341(struct bregs *regs, struct drive_s *drive_g)
353 {
354     regs->bx = 0xaa55;  // install check
355     regs->cx = 0x0007;  // ext disk access and edd, removable supported
356     disk_ret(regs, DISK_RET_SUCCESS);
357     regs->ah = 0x30;    // EDD 3.0
358 }
359
360 // IBM/MS extended read
361 static void
362 disk_1342(struct bregs *regs, struct drive_s *drive_g)
363 {
364     extended_access(regs, drive_g, CMD_READ);
365 }
366
367 // IBM/MS extended write
368 static void
369 disk_1343(struct bregs *regs, struct drive_s *drive_g)
370 {
371     extended_access(regs, drive_g, CMD_WRITE);
372 }
373
374 // IBM/MS verify
375 static void
376 disk_1344(struct bregs *regs, struct drive_s *drive_g)
377 {
378     extended_access(regs, drive_g, CMD_VERIFY);
379 }
380
381 // lock
382 static void
383 disk_134500(struct bregs *regs, struct drive_s *drive_g)
384 {
385     u16 ebda_seg = get_ebda_seg();
386     int cdid = regs->dl - EXTSTART_CD;
387     u8 locks = GET_EBDA2(ebda_seg, cdrom_locks[cdid]);
388     if (locks == 0xff) {
389         regs->al = 1;
390         disk_ret(regs, DISK_RET_ETOOMANYLOCKS);
391         return;
392     }
393     SET_EBDA2(ebda_seg, cdrom_locks[cdid], locks + 1);
394     regs->al = 1;
395     disk_ret(regs, DISK_RET_SUCCESS);
396 }
397
398 // unlock
399 static void
400 disk_134501(struct bregs *regs, struct drive_s *drive_g)
401 {
402     u16 ebda_seg = get_ebda_seg();
403     int cdid = regs->dl - EXTSTART_CD;
404     u8 locks = GET_EBDA2(ebda_seg, cdrom_locks[cdid]);
405     if (locks == 0x00) {
406         regs->al = 0;
407         disk_ret(regs, DISK_RET_ENOTLOCKED);
408         return;
409     }
410     locks--;
411     SET_EBDA2(ebda_seg, cdrom_locks[cdid], locks);
412     regs->al = (locks ? 1 : 0);
413     disk_ret(regs, DISK_RET_SUCCESS);
414 }
415
416 // status
417 static void
418 disk_134502(struct bregs *regs, struct drive_s *drive_g)
419 {
420     int cdid = regs->dl - EXTSTART_CD;
421     u8 locks = GET_EBDA(cdrom_locks[cdid]);
422     regs->al = (locks ? 1 : 0);
423     disk_ret(regs, DISK_RET_SUCCESS);
424 }
425
426 static void
427 disk_1345XX(struct bregs *regs, struct drive_s *drive_g)
428 {
429     disk_ret(regs, DISK_RET_EPARAM);
430 }
431
432 // IBM/MS lock/unlock drive
433 static void
434 disk_1345(struct bregs *regs, struct drive_s *drive_g)
435 {
436     if (regs->dl < EXTSTART_CD) {
437         // Always success for HD
438         disk_ret(regs, DISK_RET_SUCCESS);
439         return;
440     }
441
442     switch (regs->al) {
443     case 0x00: disk_134500(regs, drive_g); break;
444     case 0x01: disk_134501(regs, drive_g); break;
445     case 0x02: disk_134502(regs, drive_g); break;
446     default:   disk_1345XX(regs, drive_g); break;
447     }
448 }
449
450 // IBM/MS eject media
451 static void
452 disk_1346(struct bregs *regs, struct drive_s *drive_g)
453 {
454     if (regs->dl < EXTSTART_CD) {
455         // Volume Not Removable
456         disk_ret(regs, DISK_RET_ENOTREMOVABLE);
457         return;
458     }
459
460     int cdid = regs->dl - EXTSTART_CD;
461     u8 locks = GET_EBDA(cdrom_locks[cdid]);
462     if (locks != 0) {
463         disk_ret(regs, DISK_RET_ELOCKED);
464         return;
465     }
466
467     // FIXME should handle 0x31 no media in device
468     // FIXME should handle 0xb5 valid request failed
469
470     // Call removable media eject
471     struct bregs br;
472     memset(&br, 0, sizeof(br));
473     br.ah = 0x52;
474     call16_int(0x15, &br);
475
476     if (br.ah || br.flags & F_CF) {
477         disk_ret(regs, DISK_RET_ELOCKED);
478         return;
479     }
480     disk_ret(regs, DISK_RET_SUCCESS);
481 }
482
483 // IBM/MS extended seek
484 static void
485 disk_1347(struct bregs *regs, struct drive_s *drive_g)
486 {
487     extended_access(regs, drive_g, CMD_SEEK);
488 }
489
490 // IBM/MS get drive parameters
491 static void
492 disk_1348(struct bregs *regs, struct drive_s *drive_g)
493 {
494     u16 size = GET_INT13DPT(regs, size);
495
496     // Buffer is too small
497     if (size < 26) {
498         disk_ret(regs, DISK_RET_EPARAM);
499         return;
500     }
501
502     // EDD 1.x
503
504     u8  type    = GET_GLOBAL(drive_g->type);
505     u16 npc     = GET_GLOBAL(drive_g->pchs.cylinders);
506     u16 nph     = GET_GLOBAL(drive_g->pchs.heads);
507     u16 npspt   = GET_GLOBAL(drive_g->pchs.spt);
508     u64 lba     = GET_GLOBAL(drive_g->sectors);
509     u16 blksize = GET_GLOBAL(drive_g->blksize);
510
511     dprintf(DEBUG_HDL_13, "disk_1348 size=%d t=%d chs=%d,%d,%d lba=%d bs=%d\n"
512             , size, type, npc, nph, npspt, (u32)lba, blksize);
513
514     SET_INT13DPT(regs, size, 26);
515     if (type == DTYPE_ATAPI) {
516         // 0x74 = removable, media change, lockable, max values
517         SET_INT13DPT(regs, infos, 0x74);
518         SET_INT13DPT(regs, cylinders, 0xffffffff);
519         SET_INT13DPT(regs, heads, 0xffffffff);
520         SET_INT13DPT(regs, spt, 0xffffffff);
521         SET_INT13DPT(regs, sector_count, (u64)-1);
522     } else {
523         if (lba > (u64)npspt*nph*0x3fff) {
524             SET_INT13DPT(regs, infos, 0x00); // geometry is invalid
525             SET_INT13DPT(regs, cylinders, 0x3fff);
526         } else {
527             SET_INT13DPT(regs, infos, 0x02); // geometry is valid
528             SET_INT13DPT(regs, cylinders, (u32)npc);
529         }
530         SET_INT13DPT(regs, heads, (u32)nph);
531         SET_INT13DPT(regs, spt, (u32)npspt);
532         SET_INT13DPT(regs, sector_count, lba);
533     }
534     SET_INT13DPT(regs, blksize, blksize);
535
536     if (size < 30 || (type != DTYPE_ATA && type != DTYPE_ATAPI)) {
537         disk_ret(regs, DISK_RET_SUCCESS);
538         return;
539     }
540
541     // EDD 2.x
542
543     u16 ebda_seg = get_ebda_seg();
544     SET_INT13DPT(regs, size, 30);
545
546     SET_INT13DPT(regs, dpte_segment, ebda_seg);
547     SET_INT13DPT(regs, dpte_offset
548                  , offsetof(struct extended_bios_data_area_s, dpte));
549
550     // Fill in dpte
551     u8 ataid = GET_GLOBAL(drive_g->cntl_id);
552     u8 channel = ataid / 2;
553     u8 slave = ataid % 2;
554     u16 iobase1 = GET_GLOBAL(ATA_channels[channel].iobase1);
555     u16 iobase2 = GET_GLOBAL(ATA_channels[channel].iobase2);
556     u8 irq = GET_GLOBAL(ATA_channels[channel].irq);
557
558     u16 options = 0;
559     if (type == DTYPE_ATA) {
560         u8 translation = GET_GLOBAL(drive_g->translation);
561         if (translation != TRANSLATION_NONE) {
562             options |= 1<<3; // CHS translation
563             if (translation == TRANSLATION_LBA)
564                 options |= 1<<9;
565             if (translation == TRANSLATION_RECHS)
566                 options |= 3<<9;
567         }
568     } else {
569         // ATAPI
570         options |= 1<<5; // removable device
571         options |= 1<<6; // atapi device
572     }
573     options |= 1<<4; // lba translation
574     if (CONFIG_ATA_PIO32)
575         options |= 1<<7;
576
577     SET_EBDA2(ebda_seg, dpte.iobase1, iobase1);
578     SET_EBDA2(ebda_seg, dpte.iobase2, iobase2 + ATA_CB_DC);
579     SET_EBDA2(ebda_seg, dpte.prefix, ((slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0)
580                                       | ATA_CB_DH_LBA));
581     SET_EBDA2(ebda_seg, dpte.unused, 0xcb);
582     SET_EBDA2(ebda_seg, dpte.irq, irq);
583     SET_EBDA2(ebda_seg, dpte.blkcount, 1);
584     SET_EBDA2(ebda_seg, dpte.dma, 0);
585     SET_EBDA2(ebda_seg, dpte.pio, 0);
586     SET_EBDA2(ebda_seg, dpte.options, options);
587     SET_EBDA2(ebda_seg, dpte.reserved, 0);
588     SET_EBDA2(ebda_seg, dpte.revision, 0x11);
589
590     u8 sum = checksum_far(
591         ebda_seg, (void*)offsetof(struct extended_bios_data_area_s, dpte), 15);
592     SET_EBDA2(ebda_seg, dpte.checksum, -sum);
593
594     if (size < 66) {
595         disk_ret(regs, DISK_RET_SUCCESS);
596         return;
597     }
598
599     // EDD 3.x
600     SET_INT13DPT(regs, key, 0xbedd);
601     SET_INT13DPT(regs, dpi_length, 36);
602     SET_INT13DPT(regs, reserved1, 0);
603     SET_INT13DPT(regs, reserved2, 0);
604
605     int bdf = GET_GLOBAL(ATA_channels[channel].pci_bdf);
606     if (bdf != -1) {
607         SET_INT13DPT(regs, host_bus[0], 'P');
608         SET_INT13DPT(regs, host_bus[1], 'C');
609         SET_INT13DPT(regs, host_bus[2], 'I');
610         SET_INT13DPT(regs, host_bus[3], 0);
611
612         u32 path = (pci_bdf_to_bus(bdf) | (pci_bdf_to_dev(bdf) << 8)
613                     | (pci_bdf_to_fn(bdf) << 16));
614         SET_INT13DPT(regs, iface_path, path);
615     } else {
616         // ISA
617         SET_INT13DPT(regs, host_bus[0], 'I');
618         SET_INT13DPT(regs, host_bus[1], 'S');
619         SET_INT13DPT(regs, host_bus[2], 'A');
620         SET_INT13DPT(regs, host_bus[3], 0);
621
622         SET_INT13DPT(regs, iface_path, iobase1);
623     }
624
625     SET_INT13DPT(regs, iface_type[0], 'A');
626     SET_INT13DPT(regs, iface_type[1], 'T');
627     SET_INT13DPT(regs, iface_type[2], 'A');
628     SET_INT13DPT(regs, iface_type[3], 0);
629     SET_INT13DPT(regs, iface_type[4], 0);
630     SET_INT13DPT(regs, iface_type[5], 0);
631     SET_INT13DPT(regs, iface_type[6], 0);
632     SET_INT13DPT(regs, iface_type[7], 0);
633
634     SET_INT13DPT(regs, device_path, slave);
635
636     SET_INT13DPT(regs, checksum
637                  , -checksum_far(regs->ds, (void*)(regs->si+30), 35));
638
639     disk_ret(regs, DISK_RET_SUCCESS);
640 }
641
642 // IBM/MS extended media change
643 static void
644 disk_1349(struct bregs *regs, struct drive_s *drive_g)
645 {
646     if (regs->dl < EXTSTART_CD) {
647         // Always success for HD
648         disk_ret(regs, DISK_RET_SUCCESS);
649         return;
650     }
651     set_fail(regs);
652     // always send changed ??
653     regs->ah = DISK_RET_ECHANGED;
654 }
655
656 static void
657 disk_134e01(struct bregs *regs, struct drive_s *drive_g)
658 {
659     disk_ret(regs, DISK_RET_SUCCESS);
660 }
661
662 static void
663 disk_134e03(struct bregs *regs, struct drive_s *drive_g)
664 {
665     disk_ret(regs, DISK_RET_SUCCESS);
666 }
667
668 static void
669 disk_134e04(struct bregs *regs, struct drive_s *drive_g)
670 {
671     disk_ret(regs, DISK_RET_SUCCESS);
672 }
673
674 static void
675 disk_134e06(struct bregs *regs, struct drive_s *drive_g)
676 {
677     disk_ret(regs, DISK_RET_SUCCESS);
678 }
679
680 static void
681 disk_134eXX(struct bregs *regs, struct drive_s *drive_g)
682 {
683     disk_ret(regs, DISK_RET_EPARAM);
684 }
685
686 // IBM/MS set hardware configuration
687 static void
688 disk_134e(struct bregs *regs, struct drive_s *drive_g)
689 {
690     switch (regs->al) {
691     case 0x01: disk_134e01(regs, drive_g); break;
692     case 0x03: disk_134e03(regs, drive_g); break;
693     case 0x04: disk_134e04(regs, drive_g); break;
694     case 0x06: disk_134e06(regs, drive_g); break;
695     default:   disk_134eXX(regs, drive_g); break;
696     }
697 }
698
699 static void
700 disk_13XX(struct bregs *regs, struct drive_s *drive_g)
701 {
702     disk_ret(regs, DISK_RET_EPARAM);
703 }
704
705 static void
706 disk_13(struct bregs *regs, struct drive_s *drive_g)
707 {
708     //debug_stub(regs);
709
710     // clear completion flag
711     SET_BDA(disk_interrupt_flag, 0);
712
713     switch (regs->ah) {
714     case 0x00: disk_1300(regs, drive_g); break;
715     case 0x01: disk_1301(regs, drive_g); break;
716     case 0x02: disk_1302(regs, drive_g); break;
717     case 0x03: disk_1303(regs, drive_g); break;
718     case 0x04: disk_1304(regs, drive_g); break;
719     case 0x05: disk_1305(regs, drive_g); break;
720     case 0x08: disk_1308(regs, drive_g); break;
721     case 0x09: disk_1309(regs, drive_g); break;
722     case 0x0c: disk_130c(regs, drive_g); break;
723     case 0x0d: disk_130d(regs, drive_g); break;
724     case 0x10: disk_1310(regs, drive_g); break;
725     case 0x11: disk_1311(regs, drive_g); break;
726     case 0x14: disk_1314(regs, drive_g); break;
727     case 0x15: disk_1315(regs, drive_g); break;
728     case 0x16: disk_1316(regs, drive_g); break;
729     case 0x41: disk_1341(regs, drive_g); break;
730     case 0x42: disk_1342(regs, drive_g); break;
731     case 0x43: disk_1343(regs, drive_g); break;
732     case 0x44: disk_1344(regs, drive_g); break;
733     case 0x45: disk_1345(regs, drive_g); break;
734     case 0x46: disk_1346(regs, drive_g); break;
735     case 0x47: disk_1347(regs, drive_g); break;
736     case 0x48: disk_1348(regs, drive_g); break;
737     case 0x49: disk_1349(regs, drive_g); break;
738     case 0x4e: disk_134e(regs, drive_g); break;
739     default:   disk_13XX(regs, drive_g); break;
740     }
741 }
742
743 static void
744 floppy_13(struct bregs *regs, struct drive_s *drive_g)
745 {
746     // Only limited commands are supported on floppies.
747     switch (regs->ah) {
748     case 0x00:
749     case 0x01:
750     case 0x02:
751     case 0x03:
752     case 0x04:
753     case 0x05:
754     case 0x08:
755     case 0x15:
756     case 0x16:
757         disk_13(regs, drive_g);
758         break;
759     default:   disk_13XX(regs, drive_g); break;
760     }
761 }
762
763
764 /****************************************************************
765  * Entry points
766  ****************************************************************/
767
768 static void
769 handle_legacy_disk(struct bregs *regs, u8 extdrive)
770 {
771     if (! CONFIG_DRIVES) {
772         // XXX - support handle_1301 anyway?
773         disk_ret(regs, DISK_RET_EPARAM);
774         return;
775     }
776
777     if (extdrive < EXTSTART_HD) {
778         struct drive_s *drive_g = getDrive(EXTTYPE_FLOPPY, extdrive);
779         if (!drive_g)
780             goto fail;
781         floppy_13(regs, drive_g);
782         return;
783     }
784
785     struct drive_s *drive_g;
786     if (extdrive >= EXTSTART_CD)
787         drive_g = getDrive(EXTTYPE_CD, extdrive - EXTSTART_CD);
788     else
789         drive_g = getDrive(EXTTYPE_HD, extdrive - EXTSTART_HD);
790     if (!drive_g)
791         goto fail;
792     disk_13(regs, drive_g);
793     return;
794
795 fail:
796     // XXX - support 1301/1308/1315 anyway?
797     disk_ret(regs, DISK_RET_EPARAM);
798 }
799
800 void VISIBLE16
801 handle_40(struct bregs *regs)
802 {
803     debug_enter(regs, DEBUG_HDL_40);
804     handle_legacy_disk(regs, regs->dl);
805 }
806
807 // INT 13h Fixed Disk Services Entry Point
808 void VISIBLE16
809 handle_13(struct bregs *regs)
810 {
811     debug_enter(regs, DEBUG_HDL_13);
812     u8 extdrive = regs->dl;
813
814     if (CONFIG_CDROM_EMU) {
815         if (regs->ah == 0x4b) {
816             cdemu_134b(regs);
817             return;
818         }
819         u16 ebda_seg = get_ebda_seg();
820         if (GET_EBDA2(ebda_seg, cdemu.active)) {
821             u8 emudrive = GET_EBDA2(ebda_seg, cdemu.emulated_extdrive);
822             if (extdrive == emudrive) {
823                 // Access to an emulated drive.
824                 struct drive_s *cdemu = GET_GLOBAL(cdemu_drive);
825                 if (regs->ah > 0x16) {
826                     // Only old-style commands supported.
827                     disk_13XX(regs, cdemu);
828                     return;
829                 }
830                 disk_13(regs, cdemu);
831                 return;
832             }
833             if (extdrive < EXTSTART_CD && ((emudrive ^ extdrive) & 0x80) == 0)
834                 // Adjust id to make room for emulated drive.
835                 extdrive--;
836         }
837     }
838     handle_legacy_disk(regs, extdrive);
839 }
840
841 // record completion in BIOS task complete flag
842 void VISIBLE16
843 handle_76()
844 {
845     debug_isr(DEBUG_ISR_76);
846     SET_BDA(disk_interrupt_flag, 0xff);
847     eoi_pic2();
848 }
849
850 // Old Fixed Disk Parameter Table (newer tables are in the ebda).
851 struct fdpt_s OldFDPT VAR16FIXED(0xe401);