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