Fix disk_1301 handler.
[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 GPLv3 license.
7
8 #include "disk.h" // floppy_13
9 #include "biosvar.h" // struct bregs
10 #include "config.h" // CONFIG_*
11 #include "cmos.h" // inb_cmos
12 #include "util.h" // debug_enter
13 #include "ata.h" // ATA_*
14
15 #define DEBUGF1(fmt, args...) bprintf(0, fmt , ##args)
16 #define DEBUGF(fmt, args...)
17
18
19 /****************************************************************
20  * Helper functions
21  ****************************************************************/
22
23 #define DISK_STUB(regs) do {                    \
24         struct bregs *__regs = (regs);          \
25         debug_stub(__regs);                     \
26         disk_ret(__regs, DISK_RET_SUCCESS);     \
27     } while (0)
28
29 static u8
30 checksum_seg(u16 seg, u16 offset, u32 len)
31 {
32     u32 i;
33     u8 sum = 0;
34     for (i=0; i<len; i++)
35         sum += GET_FARVAR(seg, *(u8*)(offset+i));
36     return sum;
37 }
38
39 static void
40 basic_access(struct bregs *regs, u8 device, u16 command)
41 {
42     u16 count       = regs->al;
43     u16 cylinder    = regs->ch | ((((u16) regs->cl) << 2) & 0x300);
44     u16 sector      = regs->cl & 0x3f;
45     u16 head        = regs->dh;
46
47     if (count > 128 || count == 0 || sector == 0) {
48         BX_INFO("int13_harddisk: function %02x, parameter out of range!\n"
49                 , regs->ah);
50         disk_ret(regs, DISK_RET_EPARAM);
51         return;
52     }
53
54     u16 nlc   = GET_EBDA(ata.devices[device].lchs.cylinders);
55     u16 nlh   = GET_EBDA(ata.devices[device].lchs.heads);
56     u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
57
58     // sanity check on cyl heads, sec
59     if (cylinder >= nlc || head >= nlh || sector > nlspt) {
60         BX_INFO("int13_harddisk: function %02x, parameters out of"
61                 " range %04x/%04x/%04x!\n"
62                 , regs->ah, cylinder, head, sector);
63         disk_ret(regs, DISK_RET_EPARAM);
64         return;
65     }
66
67     if (!command) {
68         // If verify or seek
69         disk_ret(regs, DISK_RET_SUCCESS);
70         return;
71     }
72
73     u16 segment = regs->es;
74     u16 offset  = regs->bx;
75
76     // translate lchs to lba
77     u32 lba = (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nlspt)
78                + (u32)sector - 1);
79     irq_enable();
80     u8 status = ata_cmd_data(device, command, lba, count
81                              , MAKE_32_PTR(segment, offset));
82     irq_disable();
83
84     // Set nb of sector transferred
85     regs->al = GET_EBDA(ata.trsfsectors);
86
87     if (status != 0) {
88         BX_INFO("int13_harddisk: function %02x, error %02x !\n",regs->ah,status);
89         disk_ret(regs, DISK_RET_EBADTRACK);
90     }
91     disk_ret(regs, DISK_RET_SUCCESS);
92 }
93
94 void
95 emu_access(struct bregs *regs, u8 device, u16 command)
96 {
97     u16 count    = regs->al;
98     u16 cylinder = regs->ch | ((((u16) regs->cl) << 2) & 0x300);
99     u16 sector   = regs->cl & 0x3f;
100     u16 head     = regs->dh;
101
102     if ((count > 128) || (count == 0) || (sector == 0)) {
103         BX_INFO("int13_harddisk: function %02x, parameter out of range!\n"
104                 , regs->ah);
105         disk_ret(regs, DISK_RET_EPARAM);
106         return;
107     }
108
109     u16 nlc   = GET_EBDA(cdemu.vdevice.cylinders);
110     u16 nlh   = GET_EBDA(cdemu.vdevice.heads);
111     u16 nlspt = GET_EBDA(cdemu.vdevice.spt);
112
113     // sanity check on cyl heads, sec
114     if ( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
115         BX_INFO("int13_harddisk: function %02x, parameters out of"
116                 " range %04x/%04x/%04x!\n"
117                 , regs->ah, cylinder, head, sector);
118         disk_ret(regs, DISK_RET_EPARAM);
119         return;
120     }
121
122     if (!command) {
123         // If verify or seek
124         disk_ret(regs, DISK_RET_SUCCESS);
125         return;
126     }
127
128     u32 ilba = GET_EBDA(cdemu.ilba);
129     // calculate the virtual lba inside the image
130     u32 vlba= (((((u32)cylinder*(u32)nlh)+(u32)head)*(u32)nlspt)
131                +((u32)(sector-1)));
132     // start lba on cd
133     u32 slba  = (u32)vlba/4;
134     u16 before= (u16)vlba%4;
135     u32 lba = ilba + slba;
136
137     u16 segment = regs->es;
138     u16 offset  = regs->bx;
139
140     irq_enable();
141     u8 status = cdrom_read(device, lba, count*512
142                            , MAKE_32_PTR(segment, offset), before*512);
143     irq_disable();
144     if (status != 0) {
145         BX_INFO("int13_harddisk: function %02x, error %02x !\n"
146                 , regs->ah, status);
147         regs->al = 0;
148         disk_ret(regs, DISK_RET_EBADTRACK);
149     }
150     regs->al = count;
151     disk_ret(regs, DISK_RET_SUCCESS);
152 }
153
154 static void
155 extended_access(struct bregs *regs, u8 device, u16 command)
156 {
157     u16 count = GET_INT13EXT(regs, count);
158     u16 segment = GET_INT13EXT(regs, segment);
159     u16 offset = GET_INT13EXT(regs, offset);
160
161     // Can't use 64 bits lba
162     u32 lba = GET_INT13EXT(regs, lba2);
163     if (lba != 0L) {
164         BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n"
165                  , regs->ah);
166         disk_ret(regs, DISK_RET_EPARAM);
167         return;
168     }
169
170     u8 type = GET_EBDA(ata.devices[device].type);
171
172     // Get 32 bits lba and check
173     lba = GET_INT13EXT(regs, lba1);
174     if (type == ATA_TYPE_ATA
175         && lba >= GET_EBDA(ata.devices[device].sectors)) {
176         BX_INFO("int13_harddisk: function %02x. LBA out of range\n", regs->ah);
177         disk_ret(regs, DISK_RET_EPARAM);
178         return;
179     }
180
181     if (!command) {
182         // If verify or seek
183         disk_ret(regs, DISK_RET_SUCCESS);
184         return;
185     }
186
187     irq_enable();
188
189     u8 status;
190     if (type == ATA_TYPE_ATA)
191         status = ata_cmd_data(device, command, lba, count
192                               , MAKE_32_PTR(segment, offset));
193     else
194         status = cdrom_read(device, lba, count*2048
195                             , MAKE_32_PTR(segment, offset), 0);
196
197     irq_disable();
198
199     SET_INT13EXT(regs, count, GET_EBDA(ata.trsfsectors));
200
201     if (status != 0) {
202         BX_INFO("int13_harddisk: function %02x, error %02x !\n"
203                 , regs->ah, status);
204         disk_ret(regs, DISK_RET_EBADTRACK);
205         return;
206     }
207     disk_ret(regs, DISK_RET_SUCCESS);
208 }
209
210
211 /****************************************************************
212  * Hard Drive functions
213  ****************************************************************/
214
215 // disk controller reset
216 static void
217 disk_1300(struct bregs *regs, u8 device)
218 {
219     ata_reset(device);
220 }
221
222 // read disk status
223 static void
224 disk_1301(struct bregs *regs, u8 device)
225 {
226     u8 v = GET_BDA(disk_last_status);
227     regs->ah = v;
228     set_cf(regs, v);
229     // XXX - clear disk_last_status?
230 }
231
232 // read disk sectors
233 static void
234 disk_1302(struct bregs *regs, u8 device)
235 {
236     basic_access(regs, device, ATA_CMD_READ_SECTORS);
237 }
238
239 // write disk sectors
240 static void
241 disk_1303(struct bregs *regs, u8 device)
242 {
243     basic_access(regs, device, ATA_CMD_WRITE_SECTORS);
244 }
245
246 // verify disk sectors
247 static void
248 disk_1304(struct bregs *regs, u8 device)
249 {
250     basic_access(regs, device, 0);
251     // FIXME verify
252 }
253
254 // format disk track
255 static void
256 disk_1305(struct bregs *regs, u8 device)
257 {
258     DISK_STUB(regs);
259 }
260
261 // read disk drive parameters
262 static void
263 disk_1308(struct bregs *regs, u8 device)
264 {
265     // Get logical geometry from table
266     u16 nlc = GET_EBDA(ata.devices[device].lchs.cylinders);
267     u16 nlh = GET_EBDA(ata.devices[device].lchs.heads);
268     u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
269     u16 count = GET_EBDA(ata.hdcount);
270
271     nlc = nlc - 2; /* 0 based , last sector not used */
272     regs->al = 0;
273     regs->ch = nlc & 0xff;
274     regs->cl = ((nlc >> 2) & 0xc0) | (nlspt & 0x3f);
275     regs->dh = nlh - 1;
276     regs->dl = count; /* FIXME returns 0, 1, or n hard drives */
277
278     // FIXME should set ES & DI
279     disk_ret(regs, DISK_RET_SUCCESS);
280 }
281
282 // initialize drive parameters
283 static void
284 disk_1309(struct bregs *regs, u8 device)
285 {
286     DISK_STUB(regs);
287 }
288
289 // seek to specified cylinder
290 static void
291 disk_130c(struct bregs *regs, u8 device)
292 {
293     DISK_STUB(regs);
294 }
295
296 // alternate disk reset
297 static void
298 disk_130d(struct bregs *regs, u8 device)
299 {
300     DISK_STUB(regs);
301 }
302
303 // check drive ready
304 static void
305 disk_1310(struct bregs *regs, u8 device)
306 {
307     // should look at 40:8E also???
308
309     // Read the status from controller
310     u8 status = inb(GET_EBDA(ata.channels[device/2].iobase1) + ATA_CB_STAT);
311     if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY )
312         disk_ret(regs, DISK_RET_SUCCESS);
313     else
314         disk_ret(regs, DISK_RET_ENOTREADY);
315 }
316
317 // recalibrate
318 static void
319 disk_1311(struct bregs *regs, u8 device)
320 {
321     DISK_STUB(regs);
322 }
323
324 // controller internal diagnostic
325 static void
326 disk_1314(struct bregs *regs, u8 device)
327 {
328     DISK_STUB(regs);
329 }
330
331 // read disk drive size
332 static void
333 disk_1315(struct bregs *regs, u8 device)
334 {
335     // Get logical geometry from table
336     u16 nlc   = GET_EBDA(ata.devices[device].lchs.cylinders);
337     u16 nlh   = GET_EBDA(ata.devices[device].lchs.heads);
338     u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
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
345     disk_ret(regs, DISK_RET_SUCCESS);
346     regs->ah = 3; // hard disk accessible
347 }
348
349 // IBM/MS installation check
350 static void
351 disk_1341(struct bregs *regs, u8 device)
352 {
353     regs->bx = 0xaa55;  // install check
354     regs->cx = 0x0007;  // ext disk access and edd, removable supported
355     disk_ret(regs, DISK_RET_SUCCESS);
356     regs->ah = 0x30;    // EDD 3.0
357 }
358
359 // IBM/MS extended read
360 static void
361 disk_1342(struct bregs *regs, u8 device)
362 {
363     extended_access(regs, device, ATA_CMD_READ_SECTORS);
364 }
365
366 // IBM/MS extended write
367 static void
368 disk_1343(struct bregs *regs, u8 device)
369 {
370     extended_access(regs, device, ATA_CMD_WRITE_SECTORS);
371 }
372
373 // IBM/MS verify
374 static void
375 disk_1344(struct bregs *regs, u8 device)
376 {
377     extended_access(regs, device, 0);
378 }
379
380 // IBM/MS lock/unlock drive
381 static void
382 disk_1345(struct bregs *regs, u8 device)
383 {
384     // Always success for HD
385     disk_ret(regs, DISK_RET_SUCCESS);
386 }
387
388 // IBM/MS eject media
389 static void
390 disk_1346(struct bregs *regs, u8 device)
391 {
392     // Volume Not Removable
393     disk_ret(regs, DISK_RET_ENOTREMOVABLE);
394 }
395
396 // IBM/MS extended seek
397 static void
398 disk_1347(struct bregs *regs, u8 device)
399 {
400     extended_access(regs, device, 0);
401 }
402
403 // IBM/MS get drive parameters
404 static void
405 disk_1348(struct bregs *regs, u8 device)
406 {
407     u16 size = GET_INT13DPT(regs, size);
408
409     // Buffer is too small
410     if (size < 0x1a) {
411         disk_ret(regs, DISK_RET_EPARAM);
412         return;
413     }
414
415     // EDD 1.x
416
417     u8  type    = GET_EBDA(ata.devices[device].type);
418     u16 npc     = GET_EBDA(ata.devices[device].pchs.cylinders);
419     u16 nph     = GET_EBDA(ata.devices[device].pchs.heads);
420     u16 npspt   = GET_EBDA(ata.devices[device].pchs.spt);
421     u32 lba     = GET_EBDA(ata.devices[device].sectors);
422     u16 blksize = GET_EBDA(ata.devices[device].blksize);
423
424     SET_INT13DPT(regs, size, 0x1a);
425     if (type == ATA_TYPE_ATA) {
426         if ((lba/npspt)/nph > 0x3fff) {
427             SET_INT13DPT(regs, infos, 0x00); // geometry is invalid
428             SET_INT13DPT(regs, cylinders, 0x3fff);
429         } else {
430             SET_INT13DPT(regs, infos, 0x02); // geometry is valid
431             SET_INT13DPT(regs, cylinders, (u32)npc);
432         }
433         SET_INT13DPT(regs, heads, (u32)nph);
434         SET_INT13DPT(regs, spt, (u32)npspt);
435         SET_INT13DPT(regs, sector_count1, lba);  // FIXME should be Bit64
436         SET_INT13DPT(regs, sector_count2, 0L);
437     } else {
438         // ATAPI
439         // 0x74 = removable, media change, lockable, max values
440         SET_INT13DPT(regs, infos, 0x74);
441         SET_INT13DPT(regs, cylinders, 0xffffffff);
442         SET_INT13DPT(regs, heads, 0xffffffff);
443         SET_INT13DPT(regs, spt, 0xffffffff);
444         SET_INT13DPT(regs, sector_count1, 0xffffffff);  // FIXME should be Bit64
445         SET_INT13DPT(regs, sector_count2, 0xffffffff);
446     }
447     SET_INT13DPT(regs, blksize, blksize);
448
449     if (size < 0x1e) {
450         disk_ret(regs, DISK_RET_SUCCESS);
451         return;
452     }
453
454     // EDD 2.x
455
456     SET_INT13DPT(regs, size, 0x1e);
457
458     SET_INT13DPT(regs, dpte_segment, EBDA_SEG);
459     SET_INT13DPT(regs, dpte_offset
460                  , offsetof(struct extended_bios_data_area_s, ata.dpte));
461
462     // Fill in dpte
463     u8 channel = device / 2;
464     u16 iobase1 = GET_EBDA(ata.channels[channel].iobase1);
465     u16 iobase2 = GET_EBDA(ata.channels[channel].iobase2);
466     u8 irq = GET_EBDA(ata.channels[channel].irq);
467     u8 mode = GET_EBDA(ata.devices[device].mode);
468
469     u16 options;
470     if (type == ATA_TYPE_ATA) {
471         u8 translation = GET_EBDA(ata.devices[device].translation);
472         options  = (translation==ATA_TRANSLATION_NONE?0:1)<<3; // chs translation
473         options |= (translation==ATA_TRANSLATION_LBA?1:0)<<9;
474         options |= (translation==ATA_TRANSLATION_RECHS?3:0)<<9;
475     } else {
476         // ATAPI
477         options  = (1<<5); // removable device
478         options |= (1<<6); // atapi device
479     }
480     options |= (1<<4); // lba translation
481     options |= (mode==ATA_MODE_PIO32?1:0)<<7;
482
483     SET_EBDA(ata.dpte.iobase1, iobase1);
484     SET_EBDA(ata.dpte.iobase2, iobase2 + ATA_CB_DC);
485     SET_EBDA(ata.dpte.prefix, (0xe | (device % 2))<<4 );
486     SET_EBDA(ata.dpte.unused, 0xcb );
487     SET_EBDA(ata.dpte.irq, irq );
488     SET_EBDA(ata.dpte.blkcount, 1 );
489     SET_EBDA(ata.dpte.dma, 0 );
490     SET_EBDA(ata.dpte.pio, 0 );
491     SET_EBDA(ata.dpte.options, options);
492     SET_EBDA(ata.dpte.reserved, 0);
493     if (size >= 0x42)
494         SET_EBDA(ata.dpte.revision, 0x11);
495     else
496         SET_EBDA(ata.dpte.revision, 0x10);
497
498     u8 sum = checksum_seg(EBDA_SEG
499                           , offsetof(struct extended_bios_data_area_s, ata.dpte)
500                           , 15);
501     SET_EBDA(ata.dpte.checksum, ~sum);
502
503     if (size < 0x42) {
504         disk_ret(regs, DISK_RET_SUCCESS);
505         return;
506     }
507
508     // EDD 3.x
509     channel = device / 2;
510     u8 iface = GET_EBDA(ata.channels[channel].iface);
511     iobase1 = GET_EBDA(ata.channels[channel].iobase1);
512
513     SET_INT13DPT(regs, size, 0x42);
514     SET_INT13DPT(regs, key, 0xbedd);
515     SET_INT13DPT(regs, dpi_length, 0x24);
516     SET_INT13DPT(regs, reserved1, 0);
517     SET_INT13DPT(regs, reserved2, 0);
518
519     if (iface==ATA_IFACE_ISA) {
520         SET_INT13DPT(regs, host_bus[0], 'I');
521         SET_INT13DPT(regs, host_bus[1], 'S');
522         SET_INT13DPT(regs, host_bus[2], 'A');
523         SET_INT13DPT(regs, host_bus[3], 0);
524     } else {
525         // FIXME PCI
526     }
527     SET_INT13DPT(regs, iface_type[0], 'A');
528     SET_INT13DPT(regs, iface_type[1], 'T');
529     SET_INT13DPT(regs, iface_type[2], 'A');
530     SET_INT13DPT(regs, iface_type[3], 0);
531
532     if (iface==ATA_IFACE_ISA) {
533         SET_INT13DPT(regs, iface_path[0], iobase1);
534         SET_INT13DPT(regs, iface_path[2], 0);
535         SET_INT13DPT(regs, iface_path[4], 0L);
536     } else {
537         // FIXME PCI
538     }
539     SET_INT13DPT(regs, device_path[0], device%2);
540     SET_INT13DPT(regs, device_path[1], 0);
541     SET_INT13DPT(regs, device_path[2], 0);
542     SET_INT13DPT(regs, device_path[4], 0L);
543
544     sum = checksum_seg(regs->ds, 30, 34);
545     SET_INT13DPT(regs, checksum, ~sum);
546 }
547
548 // IBM/MS extended media change
549 static void
550 disk_1349(struct bregs *regs, u8 device)
551 {
552     // Always success for HD
553     disk_ret(regs, DISK_RET_SUCCESS);
554 }
555
556 static void
557 disk_134e01(struct bregs *regs, u8 device)
558 {
559     disk_ret(regs, DISK_RET_SUCCESS);
560 }
561
562 static void
563 disk_134e03(struct bregs *regs, u8 device)
564 {
565     disk_ret(regs, DISK_RET_SUCCESS);
566 }
567
568 static void
569 disk_134e04(struct bregs *regs, u8 device)
570 {
571     disk_ret(regs, DISK_RET_SUCCESS);
572 }
573
574 static void
575 disk_134e06(struct bregs *regs, u8 device)
576 {
577     disk_ret(regs, DISK_RET_SUCCESS);
578 }
579
580 static void
581 disk_134eXX(struct bregs *regs, u8 device)
582 {
583     debug_stub(regs);
584     disk_ret(regs, DISK_RET_EPARAM);
585 }
586
587 // IBM/MS set hardware configuration
588 static void
589 disk_134e(struct bregs *regs, u8 device)
590 {
591     switch (regs->al) {
592     case 0x01: disk_134e01(regs, device); break;
593     case 0x03: disk_134e03(regs, device); break;
594     case 0x04: disk_134e04(regs, device); break;
595     case 0x06: disk_134e06(regs, device); break;
596     default:   disk_134eXX(regs, device); break;
597     }
598 }
599
600 void
601 disk_13XX(struct bregs *regs, u8 device)
602 {
603     debug_stub(regs);
604     disk_ret(regs, DISK_RET_EPARAM);
605 }
606
607 void
608 disk_13(struct bregs *regs, u8 device)
609 {
610     //debug_stub(regs);
611
612     // clear completion flag
613     SET_BDA(disk_interrupt_flag, 0);
614
615     switch (regs->ah) {
616     case 0x00: disk_1300(regs, device); break;
617     case 0x01: disk_1301(regs, device); break;
618     case 0x02: disk_1302(regs, device); break;
619     case 0x03: disk_1303(regs, device); break;
620     case 0x04: disk_1304(regs, device); break;
621     case 0x05: disk_1305(regs, device); break;
622     case 0x08: disk_1308(regs, device); break;
623     case 0x09: disk_1309(regs, device); break;
624     case 0x0c: disk_130c(regs, device); break;
625     case 0x0d: disk_130d(regs, device); break;
626     case 0x10: disk_1310(regs, device); break;
627     case 0x11: disk_1311(regs, device); break;
628     case 0x14: disk_1314(regs, device); break;
629     case 0x15: disk_1315(regs, device); break;
630     case 0x41: disk_1341(regs, device); break;
631     case 0x42: disk_1342(regs, device); break;
632     case 0x43: disk_1343(regs, device); break;
633     case 0x44: disk_1344(regs, device); break;
634     case 0x45: disk_1345(regs, device); break;
635     case 0x46: disk_1346(regs, device); break;
636     case 0x47: disk_1347(regs, device); break;
637     case 0x48: disk_1348(regs, device); break;
638     case 0x49: disk_1349(regs, device); break;
639     case 0x4e: disk_134e(regs, device); break;
640     default:   disk_13XX(regs, device); break;
641     }
642 }
643
644
645 /****************************************************************
646  * Entry points
647  ****************************************************************/
648
649 static u8
650 get_device(struct bregs *regs, u8 iscd, u8 drive)
651 {
652     // basic check : device has to be defined
653     if (drive >= CONFIG_MAX_ATA_DEVICES) {
654         disk_ret(regs, DISK_RET_EPARAM);
655         return CONFIG_MAX_ATA_DEVICES;
656     }
657
658     // Get the ata channel
659     u8 device = GET_EBDA(ata.idmap[iscd][drive]);
660
661     // basic check : device has to be valid
662     if (device >= CONFIG_MAX_ATA_DEVICES) {
663         disk_ret(regs, DISK_RET_EPARAM);
664         return CONFIG_MAX_ATA_DEVICES;
665     }
666
667     return device;
668 }
669
670 static void
671 handle_legacy_disk(struct bregs *regs, u8 drive)
672 {
673     if (drive < 0x80) {
674         floppy_13(regs, drive);
675         return;
676     }
677
678     if (! CONFIG_ATA) {
679         // XXX - old code had other disk access method.
680         disk_ret(regs, DISK_RET_EPARAM);
681         return;
682     }
683
684     if (drive >= 0xe0) {
685         u8 device = get_device(regs, 1, drive - 0xe0);
686         if (device >= CONFIG_MAX_ATA_DEVICES)
687             return;
688         cdrom_13(regs, device);
689         return;
690     }
691
692     u8 device = get_device(regs, 0, drive - 0x80);
693     if (device >= CONFIG_MAX_ATA_DEVICES)
694         return;
695     disk_13(regs, device);
696 }
697
698 void VISIBLE16
699 handle_40(struct bregs *regs)
700 {
701     debug_enter(regs);
702     handle_legacy_disk(regs, regs->dl);
703 }
704
705 // INT 13h Fixed Disk Services Entry Point
706 void VISIBLE16
707 handle_13(struct bregs *regs)
708 {
709     //debug_enter(regs);
710     u8 drive = regs->dl;
711
712     if (CONFIG_CDROM_EMU) {
713         if (regs->ah == 0x4b) {
714             cdemu_134b(regs);
715             return;
716         }
717         if (GET_EBDA(cdemu.active)) {
718             if (drive == GET_EBDA(cdemu.emulated_drive)) {
719                 cdemu_13(regs);
720                 return;
721             }
722             if (drive < 0xe0)
723                 drive--;
724         }
725     }
726     handle_legacy_disk(regs, drive);
727 }
728
729 // record completion in BIOS task complete flag
730 void VISIBLE16
731 handle_76()
732 {
733     debug_isr();
734     SET_BDA(disk_interrupt_flag, 0xff);
735     eoi_both_pics();
736 }