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