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