kernel: Use hashdestroy() to free hash tables allocated with hashinit().
[dragonfly.git] / usr.bin / doscmd / int13.c
1 /*
2  * Copyright (c) 1992, 1993, 1996
3  *      Berkeley Software Design, Inc.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Berkeley Software
16  *      Design, Inc.
17  *
18  * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  *      BSDI int13.c,v 2.3 1996/04/08 19:32:43 bostic Exp
31  *
32  * $FreeBSD: src/usr.bin/doscmd/int13.c,v 1.4.2.2 2002/04/25 11:04:51 tg Exp $
33  */
34
35 #include <sys/ioctl.h>
36 #include <sys/types.h>
37 #include <sys/uio.h>
38 #include <unistd.h>
39
40 #include "doscmd.h"
41
42 #define FDCHANGED       _IOR('F', 64, int)
43
44 #define INT13_ERR_NONE               0x00
45 #define INT13_ERR_BAD_COMMAND        0x01
46 #define INT13_ERR_BAD_ADDRESS_MARK   0x02
47 #define INT13_ERR_WRITE_PROTECT      0x03
48 #define INT13_ERR_SECTOR_ID_BAD      0x04
49 #define INT13_ERR_RESET_FAILURE      0x05
50 #define INT13_ERR_CLL_ACTIVE         0x06
51 #define INT13_ERR_ACT_FAILED         0x07
52 #define INT13_ERR_DMA_OVERRUN        0x08
53 #define INT13_ERR_DMA_BOUNDARY       0x09
54 #define INT13_ERR_BAD_TRACK_FLAG     0x0B
55 #define INT13_ERR_MEDIA_TYP_UNKNOWN  0x0C
56 #define INT13_ERR_CRC                0x10
57 #define INT13_ERR_CORRECTED          0x11
58 #define INT13_ERR_CTRLR_FAILURE      0x20
59 #define INT13_ERR_SEEK               0x40
60 #define INT13_ERR_TIME_OUT           0x80
61 #define INT13_ERR_NOT_READY          0xAA
62 #define INT13_ERR_UNDEFINED          0xBB
63 #define INT13_ERR_SENSE_OPERATION    0xFF
64
65 typedef struct {
66     u_char      bootIndicator;
67     u_char      beginHead;
68     u_char      beginSector;
69     u_char      beginCyl;
70     u_char      systemID;
71     u_char      endHead;
72     u_char      endSector;
73     u_char      endCyl;
74     u_long      relSector;
75     u_long      numSectors;
76 } PTAB;
77
78 struct diskinfo {
79     int         type;
80     int         sectors;
81     int         cylinders;
82     int         sides;
83     int         secsize;
84     int         fd;
85     char        *path;
86     u_long      location;
87     u_char      *sector0;
88     u_long      offset;
89     char        *list[4];               /* Up to 4 devices allowed */
90     unsigned    multi:2;
91     int         read_only:1;
92     int         removeable:1;
93     int         changed:1;              /* Set if we change format */
94 };
95
96 #define hd_status       (*(u_char *)0x474)
97 #define fd_status       (*(u_char *)0x441)
98
99 static inline int
100 disize(struct diskinfo *di)
101 {
102     return(di->sectors * di->cylinders * di->sides);
103 }
104
105 static inline int
106 cylsize(struct diskinfo *di)
107 {
108     return(di->sectors * di->sides);
109 }
110
111 static u_long ftable = 0xF1000; /* Floppy table */
112 static u_long htable = 0xF1020; /* Hard disk table */
113
114 static struct diskinfo diskinfo[26];
115
116 static struct diskinfo floppyinfo[] = {
117     {0,  9, 40, 1, 512, -1, NULL, 0, NULL, 0,
118      {NULL, NULL, NULL, NULL}, 0, 0, 0, 0}, /* Probably not correct */
119     {1,  9, 40, 2, 512, -1, NULL, 0, NULL, 0,
120      {NULL, NULL, NULL, NULL}, 0, 0, 0, 0},
121     {2,  9, 80, 2, 512, -1, NULL, 0, NULL, 0,
122      {NULL, NULL, NULL, NULL}, 0, 0, 0, 0},
123     {3, 15, 80, 2, 512, -1, NULL, 0, NULL, 0,
124      {NULL, NULL, NULL, NULL}, 0, 0, 0, 0},
125     {4, 18, 80, 2, 512, -1, NULL, 0, NULL, 0,
126      {NULL, NULL, NULL, NULL}, 0, 0, 0, 0},
127     {6, 36, 80, 2, 512, -1, NULL, 0, NULL, 0,
128      {NULL, NULL, NULL, NULL}, 0, 0, 0, 0},
129     {-1, 0,  0, 0,   0,  0, NULL, 0, NULL, 0,
130      {NULL, NULL, NULL, NULL}, 0, 0, 0, 0}
131 };
132
133 static struct diskinfo *
134 getdisk(int drive)
135 {
136     struct diskinfo *di;
137
138     if (drive >= 2 && drive < 0x80) {
139         return(0);
140     }
141     if (drive >= 0x80) {
142         drive -= 0x80;
143         drive += 2;
144     }
145
146     if (drive > 25 || diskinfo[drive].path == NULL) {
147         return(0);
148     }
149     di = &diskinfo[drive];
150     if (di->fd < 0) {
151         if (di->removeable) {
152             di->read_only = 0;
153             if (!(di->path = di->list[di->multi]))
154                 di->path = di->list[di->multi = 0];
155             }   
156         if ((di->fd = open(di->path, di->read_only ? O_RDONLY
157                            : O_RDWR|O_FSYNC)) < 0 &&
158             (di->read_only = 1) &&
159             (di->fd = open(di->path, O_RDONLY)) < 0) {
160             return(0);
161         }
162         di->fd = squirrel_fd(di->fd);
163     }
164     return(di);
165 }
166
167 int
168 disk_fd(int drive)
169 {
170     struct diskinfo *di;
171
172     if (drive > 1)
173         drive += 0x80 - 2;
174     di = getdisk(drive);
175     if (!di)
176         return(-1);
177     return(di->fd);
178 }
179
180 void
181 make_readonly(int drive)
182 {
183     if (drive < 0 || drive >= 26)
184         return;
185     diskinfo[drive].read_only = 1;
186 }
187
188 int
189 init_hdisk(int drive, int cyl, int head, int tracksize, char *file, char *fake_ptab)
190 {
191     struct diskinfo *di;
192     u_long table = 0;
193
194     if (drive < 0) {
195         for (drive = 2; drive < 26; ++drive) {
196             if (diskinfo[drive].path == NULL)
197                 break;
198         }
199     }
200     if (drive < 2) {
201         fprintf(stderr, "Only floppies may be assigned to A: or B:\n");
202         return(-1);
203     }
204
205     if (drive >= 26) {
206         fprintf(stderr, "Too many disk drives (only 24 allowed)\n");
207         return(-1);
208     }
209
210     di = &diskinfo[drive];
211
212     if (di->path) {
213         fprintf(stderr, "Drive %c: already assigned to %s\n", drntol(drive),
214                          di->path);
215         return(-1);
216     }
217     di->fd = -1;
218     di->sectors = tracksize;
219     di->cylinders = cyl;
220     di->sides = head;
221     di->sector0 = NULL;
222     di->offset = 0;
223
224     if (fake_ptab) {
225         u_char buf[512];
226         int fd;
227         PTAB *ptab;
228         int clusters;
229
230         if ((fd = open(fake_ptab, 0)) < 0) {
231                 perror(fake_ptab);
232                 return(-1);
233         }
234         di->sector0 = malloc(512);
235         if (!di->sector0) {
236                 perror("malloc in init_hdisk");
237                 quit(1);
238         }
239
240         read(fd, di->sector0, 512);
241         close(fd);
242
243         ptab = (PTAB *)(di->sector0 + 0x01BE);
244
245         for (fd = 0; fd < 4; ++fd) {
246             if (*(u_short *)(di->sector0 + 0x1FE) == 0xAA55 &&
247                 ptab[fd].numSectors == (u_long)(head * tracksize * cyl) &&
248                 (ptab[fd].systemID == 1 || ptab[fd].systemID == 4 ||
249                  ptab[fd].systemID == 6))
250                     break;
251         }
252         if (fd < 4) {
253             if (fd)
254                 memcpy(ptab, ptab + fd, sizeof(PTAB));
255             memset(ptab + 1, 0, sizeof(PTAB) * 3);
256             di->offset = ptab[0].relSector;
257             di->cylinders += di->offset / cylsize(di);
258         } else {
259             memset(ptab, 0, sizeof(PTAB) * 4);
260
261             ptab->beginHead = 0;
262             ptab->beginSector = 1;      /* this is 1 based */
263             ptab->beginCyl = 1;
264
265             ptab->endHead = head - 1;
266             ptab->endSector = tracksize;        /* this is 1 based */
267             ptab->endCyl = cyl & 0xff;
268             ptab->endSector |= (cyl & 0x300) >> 2;
269
270             ptab->relSector = head * tracksize;
271             ptab->numSectors = head * tracksize * cyl;
272             
273             *(u_short *)(di->sector0 + 0x1FE) = 0xAA55;
274
275             fd = open(file, 0);
276             if (fd < 0) {
277                 perror(file);
278                 return(-1);
279             }
280             memset(buf, 0, 512);
281             read(fd, buf, 512);
282             close(fd);
283             if ((clusters = buf[0x0D]) == 0) {
284                 if (disize(di) <= 128 * 2048)
285                     clusters = 4;
286                 else if (disize(di) <= 256 * 2048)
287                     clusters = 8;
288                 else if (disize(di) <= 8 * 1024 * 2048)
289                     clusters = 16;
290                 else if (disize(di) <= 16 * 1024 * 2048)
291                     clusters = 32;
292                 else
293                     clusters = 64;
294             }
295             if ((disize(di) / clusters) <= 4096) {
296                 ptab->systemID = 0x01;
297             } else {
298                 ptab->systemID = 0x04;
299             }
300
301             di->cylinders += 1; /* Extra cylinder for partition table, etc. */
302         }
303         ptab->bootIndicator = 0x80;
304     }
305     di->type = 0xf8;
306     di->path = file;
307     di->secsize = 512;
308     di->path = strdup(file);
309
310     di->location = ((table & 0xf0000) << 12) | (table & 0xffff);
311
312     if (drive == 0) {
313         ivec[0x41] = di->location;
314     } else if (drive == 1) {
315         ivec[0x46] = di->location;
316     }
317
318     table = htable + (drive - 2) * 0x10;
319     *(u_short *)(table+0x00) = di->cylinders-1; /* Cylinders */
320     *(u_char  *)(table+0x02) = di->sides;       /* Heads */
321     *(u_short *)(table+0x03) = 0;               /* 0 */
322     *(u_short *)(table+0x05) = 0xffff;          /* write pre-comp */
323     *(u_char  *)(table+0x07) = 0;               /* ECC Burst length */
324     *(u_char  *)(table+0x08) = 0;               /* Control Byte */
325     *(u_char  *)(table+0x09) = 0;               /* standard timeout */
326     *(u_char  *)(table+0x0a) = 0;               /* formatting timeout */
327     *(u_char  *)(table+0x0b) = 0;               /* timeout for checking drive */
328     *(u_short *)(table+0x0c) = di->cylinders-1; /* landing zone */
329     *(u_char  *)(table+0x0e) = di->sectors;     /* sectors/track */
330     *(u_char  *)(table+0x0f) = 0;
331
332     if ((drive - 1) >= ndisks)
333         ndisks = drive - 1;
334     return(drive);
335 }
336
337 static inline int
338 bps(int size)
339 {
340     switch (size) {
341     case 128:   return(0);
342     case 256:   return(1);
343     case 512:   return(2);
344     case 1024:  return(3);
345     default:
346         fprintf(stderr, "Invalid sector size: %d\n", size);
347         quit(1);
348     }
349     /* keep `gcc -Wall' happy */
350     return(0);
351 }
352
353 int
354 init_floppy(int drive, int type, char *file)
355 {
356     struct diskinfo *di = floppyinfo;
357     u_long table = 0;
358     struct stat sb;
359
360     while (di->type >= 0 && di->type != type && disize(di)/2 != type)
361         ++di;
362
363     if (!di->type) {
364         fprintf(stderr, "Invalid floppy type: %d\n", type);
365         return(-1);
366     }
367
368     if (drive < 0) {
369         if (diskinfo[0].path == NULL) {
370             drive = 0;
371         } else if (diskinfo[1].path == NULL) {
372             drive = 1;
373         } else {
374             fprintf(stderr, "Too many floppy drives (only 2 allowed)\n");
375             return(-1);
376         }
377     }
378     if (drive > 1) {
379         fprintf(stderr, "Floppies must be either drive A: or B:\n");
380         return(-1);
381     }
382
383     if (drive >= nfloppies)
384         nfloppies = drive + 1;
385
386     if (diskinfo[drive].path == NULL) {
387         diskinfo[drive] = *di;
388     }
389
390     di = &diskinfo[drive];
391
392     if (stat(file, &sb) < 0) {
393         fprintf(stderr, "Drive %c: Could not stat %s\n", drntol(drive), file);
394         return(-1);
395     }
396
397     if (drive < 2 && (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode))) {
398         if (di->path && !di->removeable) {
399             fprintf(stderr, "Drive %c: is not removeable and hence can only have one assignment\n", drntol(drive));
400             return(-1);
401         }
402         di->removeable = 1;
403     } else if (di->removeable) {
404         fprintf(stderr, "Drive %c: already assigned to %s\n", drntol(drive),
405                          di->path);
406         return(-1);
407     }
408
409     if (di->removeable) {
410 #if 0 /*XXXXX*/
411         if (di->multi == 4) {
412             fprintf(stderr, "Drive %c: already assigned 4 devices\n",
413                              drntol(drive));
414             return(-1);
415         }
416 #endif
417         di->path = di->list[di->multi++] = strdup(file);
418     } else {
419         if (di->path) {
420             fprintf(stderr, "Drive %c: already assigned to %s\n",
421                              drntol(drive), di->path);
422             return(-1);
423         }
424
425         di->path = strdup(file);
426     }
427     di->fd = -1;
428     di->location = ((table & 0xf0000) << 12) | (table & 0xffff);
429     di->sector0 = NULL;
430     di->offset = 0;
431
432     ivec[0x1e] = ((ftable & 0xf0000) << 12) | (ftable & 0xffff);
433
434     table = ftable + drive * 0x0a;
435
436     *(u_char *)(table+0x00) = 0xdf;     /* First Specify Byte */
437     *(u_char *)(table+0x01) = 0x02;     /* Second Specify Byte */
438     *(u_char *)(table+0x02) = 0x25;     /* Timer ticks to wait 'til motor OFF */
439     *(u_char *)(table+0x03) = bps(di->secsize); /* Number of bytes/sector */
440     *(u_char *)(table+0x04) = di->sectors;      /* Number of sectors/track */
441     *(u_char *)(table+0x05) = 0x1b;     /* Gap length, in bytes */
442     *(u_char *)(table+0x06) = 0xff;     /* Data length, in bytes */
443     *(u_char *)(table+0x07) = 0x6c;     /* Gap length for format */
444     *(u_char *)(table+0x09) = 0xf6;     /* Fill byte for formatting */
445     *(u_char *)(table+0x09) = 0x0f;     /* Head settle time, in milliseconds */
446     *(u_char *)(table+0x0a) = 0x08;     /* Motor startup time, in 1/8 seconds */
447     return(drive);
448 }
449
450 int
451 search_floppy(int i)
452 {
453     return(i < nfloppies ? diskinfo[i].type : 0);
454 }
455
456 #define seterror(err)   {                       \
457     if (drive & 0x80)                           \
458         hd_status = err;                        \
459     else                                        \
460         fd_status = err;                        \
461     R_AH = err;                                 \
462     R_FLAGS |= PSL_C;                           \
463 }
464             
465 static int
466 trynext(struct diskinfo *di)
467 {
468     close(di->fd);
469     di->fd = -1;
470     di->changed = 1;
471 #if 0 /*XXXXX*/
472     if (di->multi++ >= 4)
473         return(0);
474 #endif
475     if (di->list[di->multi] && (di = getdisk(di - diskinfo))) {
476         di->multi = 0;
477         return(1);
478     }
479     return(0);
480 }
481
482 static int
483 diread(struct diskinfo *di, regcontext_t *REGS,
484        off_t start, char *addr, int sectors)
485 {
486     off_t res;
487
488     int drive = di - diskinfo;
489     di->multi = -1;
490
491     if (drive > 1) {
492         drive -= 2;
493         drive |= 0x80;
494     }
495
496 again:
497     res = lseek(di->fd, start * di->secsize, 0);
498
499     if (res < 0 && di->removeable && trynext(di))
500         goto again;
501
502     if (res < 0) {
503         seterror(INT13_ERR_SEEK);
504         return(-1);
505     }
506
507     res = read(di->fd, addr, sectors * di->secsize);
508
509     if (res < 0 && di->removeable && trynext(di))
510         goto again;
511
512     if (di->removeable) {
513         if (res < 0) {
514             seterror(INT13_ERR_NOT_READY);
515             return(-1);
516         }
517         return(res / di->secsize);
518     }
519
520     /*
521      * reads always work, if if they don't.
522      * Just pretend any byte not read was actually a 0
523      */
524     if (res < 0)
525         memset(addr, 0, sectors * di->secsize);
526     else if (res < sectors * di->secsize)
527         memset(addr + res, 0, sectors * di->secsize - res);
528         
529     return(sectors);
530 }
531
532 static int
533 diwrite(struct diskinfo *di, regcontext_t *REGS,
534        off_t start, char *addr, int sectors)
535 {
536     off_t res;
537     int drive = di - diskinfo;
538     di->multi = -1;
539
540     if (drive > 1) {
541         drive -= 2;
542         drive |= 0x80;
543     }
544
545 again:
546     res = lseek(di->fd, start * di->secsize, 0);
547     
548     if (res < 0 && di->removeable && trynext(di))
549         goto again;
550
551     if (res < 0) {
552         seterror(INT13_ERR_SEEK);
553         return(-1);
554     }
555
556     res = write(di->fd, addr, sectors * di->secsize);
557     
558     if (res < 0 && di->removeable && trynext(di))
559         goto again;
560     
561     if (di->removeable) {
562         if (res < 0) {
563             seterror(INT13_ERR_NOT_READY);
564             return(-1);
565         }
566     } else if (res < 0) {
567         seterror(INT13_ERR_WRITE_PROTECT);
568         return(-1);
569     }
570     return(res / di->secsize);
571 }
572
573 static void
574 int13(regcontext_t *REGS)
575 {
576     char *addr;
577     int sectors;
578     struct diskinfo *di;
579     off_t start;
580     int did;
581
582     int cyl;
583     int sector;
584     int side;
585     int drive;
586     
587     reset_poll();
588
589     R_FLAGS &= ~PSL_C;
590
591     drive = R_DL;
592
593     if (R_AX != 0x01) {
594         if (drive & 0x80) 
595             hd_status = 0;
596         else
597             fd_status = 0;
598     }
599
600     switch (R_AH) {
601     case 0x00:  /* Reset */
602         break;
603     case 0x01:  /* Read disk status */ 
604         if (drive & 0x80) 
605             R_AH = hd_status;
606         else
607             R_AH = fd_status;
608         if (R_AH)
609             R_FLAGS |= PSL_C;
610         break;
611         case 0x02:      /* Read */
612             R_AH = 0;
613             addr = (char *)MAKEPTR(R_ES, R_BX);
614             sectors = R_AL;
615             side = R_DH;
616             R_AL = 0;           /* Start out with nothing read */
617
618             if (drive & 0x80) {
619                 cyl = R_CH | ((R_CL & 0xc0) << 2);
620                 sector = (R_CL & 0x3f) - 1;
621             } else {
622                 sector = R_CL - 1;
623                 cyl = R_CH;
624             }
625
626             if ((di = getdisk(drive)) == NULL) {
627                 debug(D_DISK, "Bad drive: %02x (%d : %d : %d)\n",
628                       drive, cyl, side, sector);
629                 seterror(INT13_ERR_BAD_COMMAND);
630                 break;
631             }
632             start = cyl * di->sectors * di->sides +
633                 side * di->sectors +
634                 sector;
635
636             if (start >= disize(di)) {
637                 debug(D_DISK, "Read past end of disk\n");
638                 seterror(INT13_ERR_SEEK);
639                 break;
640             }
641             if (sectors + start >= disize(di)) {
642                 sectors = disize(di) - start;
643             }
644
645             if (di->sector0) {
646                 if (start < di->offset) {
647                     R_AL = sectors;
648                     if (start == 0) {
649                         memcpy(addr, di->sector0, di->secsize);
650                         addr += di->secsize;
651                         --sectors;
652                     }
653                     memset(addr, 0, sectors * di->secsize);
654                     break;
655                 } else {
656                     start -= di->offset;
657                 }
658             }
659             debug(D_DISK, "%02x: Read %2d sectors from %qd to %04x:%04x\n",
660                   drive, sectors, start, R_ES, R_BX);
661
662             if ((did = diread(di, REGS, start, addr, sectors)) >= 0)
663                 R_AL = did;
664 #if 0
665             callint(0x0d);
666             callint(0x76);
667 #endif
668             break;
669
670         case 0x03:      /* Write */
671             R_AH = 0;
672             addr = (char *)MAKEPTR(R_ES, R_BX);
673             sectors = R_AL;
674             side = R_DH;
675             R_AL = 0;   /* Start out with nothing written */
676
677             if (drive & 0x80) {
678                 cyl = R_CH | ((R_CL & 0xc0) << 2);
679                 sector = (R_CL & 0x3f) - 1;
680             } else {
681                 sector = R_CL - 1;
682                 cyl = R_CH;
683             }
684
685             if ((di = getdisk(drive)) == NULL) {
686                 debug(D_DISK, "Bad drive: %d (%d : %d : %d)\n",
687                       drive, cyl, side, sector);
688                 seterror(INT13_ERR_BAD_COMMAND);
689                 break;
690             }
691             if (di->read_only) {
692                 debug(D_DISK, "%02x: Attempt to write readonly disk\n", drive);
693                 seterror(INT13_ERR_WRITE_PROTECT);
694                 break;
695             }
696             start = cyl * di->sectors * di->sides +
697                 side * di->sectors +
698                 sector;
699             
700             if (start >= disize(di)) {
701                 debug(D_DISK, "Write past end of disk\n");
702                 seterror(INT13_ERR_SEEK);
703                 break;
704             }
705
706             if (sectors + start >= disize(di))
707                 sectors = disize(di) - start;
708
709             if (di->sector0) {
710                 if (start < di->offset) {
711                     R_AL = sectors;
712                     break;
713                 } else {
714                     start -= di->offset;
715                 }
716             }
717
718             debug(D_DISK, "%02x: Write %2d sectors from %qd to %04x:%04x\n",
719                   drive, sectors, start, R_ES, R_BX);
720
721             if ((did = diwrite(di, REGS, start, addr, sectors)) >= 0)
722                 R_AL = did;
723 #if 0
724             callint(0x0d);
725             callint(0x76);
726 #endif
727             break;
728
729         case 0x04:      /* Verify */
730             R_AH = 0;
731             sectors = R_AL;
732             side = R_DH;
733
734             if (drive & 0x80) {
735                 cyl = R_CH | ((R_CL & 0xc0) << 2);
736                 sector = (R_CL & 0x3f) - 1;
737             } else {
738                 sector = R_CL - 1;
739                 cyl = R_CH;
740             }
741
742             if ((di = getdisk(drive)) == NULL) {
743                 debug(D_DISK, "Bad drive: %d (%d : %d : %d)\n",
744                       drive, cyl, side, sector);
745                 seterror(INT13_ERR_BAD_COMMAND);
746                 break;
747             }
748             start = cyl * di->sectors * di->sides +
749                 side * di->sectors +
750                 sector;
751             
752             if (start >= disize(di)) {
753                 debug(D_DISK, "Verify past end of disk\n");
754                 seterror(INT13_ERR_SEEK);
755                 break;
756             }
757
758             if (sectors + start >= disize(di))
759                 sectors = disize(di) - start;
760
761             if (di->sector0) {
762                 if (start < di->offset)
763                     break;
764                 else
765                     start -= di->offset;
766             }
767
768             debug(D_DISK, "Verify %2d sectors from %qd\n", sectors, start);
769             if (lseek(di->fd, start * di->secsize, 0) < 0) {
770                 debug(D_DISK, "Seek error\n");
771                 seterror(INT13_ERR_SEEK);
772                 break;
773             }
774             while (sectors > 0) {
775                 char buf[512];
776                 if (read(di->fd, buf, di->secsize) != di->secsize) {
777                     debug(D_DISK, "Verify error\n");
778                     seterror(0x04);
779                     break;
780                 }
781                 --sectors;
782             }
783 #if 0
784             callint(0x0d);
785             callint(0x76);
786 #endif
787             break;
788     
789     case 0x05:  /* Format track */
790         seterror(INT13_ERR_BAD_COMMAND);
791         break;
792
793     case 0x08:  /* Status */
794         R_AH = 0;
795
796         if ((di = getdisk(drive)) == NULL) {
797             debug(D_DISK, "Bad drive: %d\n", drive);
798             seterror(INT13_ERR_BAD_COMMAND);
799             break;
800         }
801         R_AX = 0;
802
803         R_BX = di->type;
804         if ((drive & 0x80) == 0)
805             PUTVEC(R_ES, R_DI, di->location);
806
807         R_CL = di->sectors | ((di->cylinders >> 2) & 0xc0);
808         R_CH = di->cylinders & 0xff;
809         R_DL = (drive & 0x80) ? ndisks : nfloppies;
810         R_DH = di->sides - 1;
811         debug(D_DISK, "%02x: Status requested: sec %d cyl %d side %d drive %d\n", 
812               drive, R_CL, R_CH, R_DH, R_DL);
813 #if 0
814         callint(0x0d);
815         callint(0x76);
816 #endif
817         break;
818
819     case 0x0c:  /* Move read/write head */
820     case 0x0d:  /* Reset */
821         break;
822
823     case 0x10:  /* check for disk ready */
824         R_AH = 0;       /* always open for business */
825         break;
826             
827     case 0x15:
828         if ((di = getdisk(drive)) == NULL) {
829             R_AH = 0;
830             R_FLAGS |= PSL_C;
831             break;
832         }
833
834         if (drive & 0x80) {
835             start = di->sectors * di->cylinders * di->sides;
836             R_CX = start >> 16;
837             R_DX = start;
838             R_AH = 3;
839         } else {
840             R_AH = 1;   /* Non-changeable disk */
841         }
842         break;
843
844     case 0x16:  /* Media change */
845         R_AH = 0;
846         if ((di = getdisk(drive)) && di->changed) {
847             di->changed = 0;
848             R_AH = 6;
849         }
850         break;
851
852     case 0x17:  /* Determine floppy disk format */
853         seterror(INT13_ERR_BAD_COMMAND);
854         break;
855         
856     case 0x18:  /* Determine disk format */
857         if ((di = getdisk(drive)) == NULL) {
858             R_AH = 0;
859             R_FLAGS |= PSL_C;
860             break;
861         }
862         /* XXX incomplete? */
863         break;
864
865     default:
866         unknown_int2(0x13, R_AH, REGS);
867         break;
868     }
869 }
870
871 void
872 disk_bios_init(void)
873 {
874     u_long vec;
875
876     vec = insert_softint_trampoline();
877     ivec[0x13] = vec;
878     register_callback(vec, int13, "int 13");
879     
880     vec = insert_null_trampoline();
881     ivec[0x76] = vec;
882 }