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