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