]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - sys/boot/pc98/boot2/boot2.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.git] / sys / boot / pc98 / boot2 / boot2.c
1 /*-
2  * Copyright (c) 2008-2009 TAKAHASHI Yoshihiro
3  * Copyright (c) 1998 Robert Nordier
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms are freely
7  * permitted provided that the above copyright notice and this
8  * paragraph and the following disclaimer are duplicated in all
9  * such forms.
10  *
11  * This software is provided "AS IS" and without any express or
12  * implied warranties, including, without limitation, the implied
13  * warranties of merchantability and fitness for a particular
14  * purpose.
15  */
16
17 #include <sys/cdefs.h>
18 __FBSDID("$FreeBSD$");
19
20 #include <sys/param.h>
21 #include <sys/disklabel.h>
22 #include <sys/diskpc98.h>
23 #include <sys/dirent.h>
24 #include <sys/reboot.h>
25
26 #include <machine/bootinfo.h>
27 #include <machine/cpufunc.h>
28 #include <machine/elf.h>
29
30 #include <stdarg.h>
31
32 #include <a.out.h>
33
34 #include <btxv86.h>
35
36 #include "boot2.h"
37 #include "lib.h"
38 #include "paths.h"
39 #include "rbx.h"
40
41 /* Define to 0 to omit serial support */
42 #ifndef SERIAL
43 #define SERIAL 0
44 #endif
45
46 #define IO_KEYBOARD     1
47 #define IO_SERIAL       2
48
49 #if SERIAL
50 #define DO_KBD (ioctrl & IO_KEYBOARD)
51 #define DO_SIO (ioctrl & IO_SERIAL)
52 #else
53 #define DO_KBD (1)
54 #define DO_SIO (0)
55 #endif
56
57 #define SECOND          1       /* Circa that many ticks in a second. */
58
59 #define ARGS            0x900
60 #define NOPT            14
61 #define NDEV            3
62
63 #define DRV_DISK        0xf0
64 #define DRV_UNIT        0x0f
65
66 #define TYPE_AD         0
67 #define TYPE_DA         1
68 #define TYPE_FD         2
69
70 extern uint32_t _end;
71
72 static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
73 static const unsigned char flags[NOPT] = {
74     RBX_DUAL,
75     RBX_SERIAL,
76     RBX_ASKNAME,
77     RBX_CDROM,
78     RBX_CONFIG,
79     RBX_KDB,
80     RBX_GDB,
81     RBX_MUTE,
82     RBX_NOINTR,
83     RBX_PAUSE,
84     RBX_QUIET,
85     RBX_DFLTROOT,
86     RBX_SINGLE,
87     RBX_VERBOSE
88 };
89
90 static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
91 static const unsigned char dev_maj[NDEV] = {30, 4, 2};
92 static const unsigned char dev_daua[NDEV] = {0x80, 0xa0, 0x90};
93
94 static struct dsk {
95     unsigned daua;
96     unsigned type;
97     unsigned disk;
98     unsigned unit;
99     unsigned head;
100     unsigned sec;
101     uint8_t slice;
102     uint8_t part;
103     unsigned start;
104 } dsk;
105 static char cmd[512], cmddup[512], knamebuf[1024];
106 static const char *kname;
107 uint32_t opts;
108 static struct bootinfo bootinfo;
109 #if SERIAL
110 static int comspeed = SIOSPD;
111 static uint8_t ioctrl = IO_KEYBOARD;
112 #endif
113
114 void exit(int);
115 static void load(void);
116 static int parse(void);
117 static int dskread(void *, unsigned, unsigned);
118 static void printf(const char *,...);
119 static void putchar(int);
120 static int drvread(void *, unsigned);
121 static int keyhit(unsigned);
122 static int xputc(int);
123 static int xgetc(int);
124 static inline int getc(int);
125
126 static void memcpy(void *, const void *, int);
127 static void
128 memcpy(void *dst, const void *src, int len)
129 {
130     const char *s = src;
131     char *d = dst;
132
133     while (len--)
134         *d++ = *s++;
135 }
136
137 static inline int
138 strcmp(const char *s1, const char *s2)
139 {
140     for (; *s1 == *s2 && *s1; s1++, s2++);
141     return (unsigned char)*s1 - (unsigned char)*s2;
142 }
143
144 #define UFS_SMALL_CGBASE
145 #include "ufsread.c"
146
147 static inline int
148 xfsread(ufs_ino_t inode, void *buf, size_t nbyte)
149 {
150     if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
151         printf("Invalid %s\n", "format");
152         return -1;
153     }
154     return 0;
155 }
156
157 static inline void
158 getstr(void)
159 {
160     char *s;
161     int c;
162
163     s = cmd;
164     for (;;) {
165         switch (c = xgetc(0)) {
166         case 0:
167             break;
168         case '\177':
169         case '\b':
170             if (s > cmd) {
171                 s--;
172                 printf("\b \b");
173             }
174             break;
175         case '\n':
176         case '\r':
177             *s = 0;
178             return;
179         default:
180             if (s - cmd < sizeof(cmd) - 1)
181                 *s++ = c;
182             putchar(c);
183         }
184     }
185 }
186
187 static inline void
188 putc(int c)
189 {
190
191     v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
192     v86.addr = PUTCORG;         /* call to putc in boot1 */
193     v86.eax = c;
194     v86int();
195     v86.ctl = V86_FLAGS;
196 }
197
198 static inline int
199 is_scsi_hd(void)
200 {
201
202     if ((*(u_char *)PTOV(0x482) >> dsk.unit) & 0x01)
203         return 1;
204
205     return 0;
206 }
207
208 static inline void
209 fix_sector_size(void)
210 {
211     u_char *p;
212
213     p = (u_char *)PTOV(0x460 + dsk.unit * 4);   /* SCSI equipment parameter */
214
215     if ((p[0] & 0x1f) == 7) {           /* SCSI MO */
216         if (!(p[3] & 0x30)) {           /* 256B / sector */
217             p[3] |= 0x10;               /* forced set 512B / sector */
218             p[3 + 0xa1000] |= 0x10;
219         }
220     }
221 }
222
223 static inline uint32_t
224 get_diskinfo(void)
225 {
226
227     if (dsk.disk == 0x30) {                             /* 1440KB FD */
228         /* 80 cylinders, 2 heads, 18 sectors */
229         return (80 << 16) | (2 << 8) | 18;
230     } else if (dsk.disk == 0x90) {                      /* 1200KB FD */
231         /* 80 cylinders, 2 heads, 15 sectors */
232         return (80 << 16) | (2 << 8) | 15;
233     } else if (dsk.disk == 0x80 || is_scsi_hd()) {      /* IDE or SCSI HDD */
234         v86.addr = 0x1b;
235         v86.eax = 0x8400 | dsk.daua;
236         v86int();
237         return (v86.ecx << 16) | v86.edx;
238     }
239
240     /* SCSI MO or CD */
241     fix_sector_size();  /* SCSI MO */
242
243     /* other SCSI devices */
244     return (65535 << 16) | (8 << 8) | 32;
245 }
246
247 static void
248 set_dsk(void)
249 {
250     uint32_t di;
251
252     di = get_diskinfo();
253
254     dsk.head = (di >> 8) & 0xff;
255     dsk.sec = di & 0xff;
256     dsk.start = 0;
257 }
258
259 #ifdef GET_BIOSGEOM
260 static uint32_t
261 bd_getbigeom(int bunit)
262 {
263     int hds = 0;
264     int unit = 0x80;            /* IDE HDD */
265     u_int addr = 0x55d;
266
267     while (unit < 0xa7) {
268         if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f)))
269             if (hds++ == bunit)
270                 break;
271
272         if (unit >= 0xA0) {
273             int media = ((unsigned *)PTOV(0x460))[unit & 0x0F] & 0x1F;
274
275             if (media == 7 && hds++ == bunit)   /* SCSI MO */
276                 return(0xFFFE0820); /* C:65535 H:8 S:32 */
277         }
278         if (++unit == 0x84) {
279             unit = 0xA0;        /* SCSI HDD */
280             addr = 0x482;
281         }
282     }
283     if (unit == 0xa7)
284         return 0x4F020F;        /* 1200KB FD C:80 H:2 S:15 */
285     v86.addr = 0x1b;
286     v86.eax = 0x8400 | unit;
287     v86int();
288     if (V86_CY(v86.efl))
289         return 0x4F020F;        /* 1200KB FD C:80 H:2 S:15 */
290     return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff);
291 }
292 #endif
293
294 static int
295 check_slice(void)
296 {
297     struct pc98_partition *dp;
298     char *sec;
299     unsigned i, cyl;
300
301     sec = dmadat->secbuf;
302     cyl = *(uint16_t *)PTOV(ARGS);
303     set_dsk();
304
305     if (dsk.type == TYPE_FD)
306         return (WHOLE_DISK_SLICE);
307     if (drvread(sec, PC98_BBSECTOR))
308         return (WHOLE_DISK_SLICE);      /* Read error */
309     dp = (void *)(sec + PC98_PARTOFF);
310     for (i = 0; i < PC98_NPARTS; i++) {
311         if (dp[i].dp_mid == DOSMID_386BSD) {
312             if (dp[i].dp_scyl <= cyl && cyl <= dp[i].dp_ecyl)
313                 return (BASE_SLICE + i);
314         }
315     }
316
317     return (WHOLE_DISK_SLICE);
318 }
319
320 int
321 main(void)
322 {
323 #ifdef GET_BIOSGEOM
324     int i;
325 #endif
326     uint8_t autoboot;
327     ufs_ino_t ino;
328     size_t nbyte;
329
330     dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
331     v86.ctl = V86_FLAGS;
332     v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
333     dsk.daua = *(uint8_t *)PTOV(0x584);
334     dsk.disk = dsk.daua & DRV_DISK;
335     dsk.unit = dsk.daua & DRV_UNIT;
336     if (dsk.disk == 0x80)
337         dsk.type = TYPE_AD;
338     else if (dsk.disk == 0xa0)
339         dsk.type = TYPE_DA;
340     else /* if (dsk.disk == 0x30 || dsk.disk == 0x90) */
341         dsk.type = TYPE_FD;
342     dsk.slice = check_slice();
343 #ifdef GET_BIOSGEOM
344     for (i = 0; i < N_BIOS_GEOM; i++)
345         bootinfo.bi_bios_geom[i] = bd_getbigeom(i);
346 #endif
347     bootinfo.bi_version = BOOTINFO_VERSION;
348     bootinfo.bi_size = sizeof(bootinfo);
349
350     /* Process configuration file */
351
352     autoboot = 1;
353
354     if ((ino = lookup(PATH_CONFIG)) ||
355         (ino = lookup(PATH_DOTCONFIG))) {
356         nbyte = fsread(ino, cmd, sizeof(cmd) - 1);
357         cmd[nbyte] = '\0';
358     }
359
360     if (*cmd) {
361         memcpy(cmddup, cmd, sizeof(cmd));
362         if (parse())
363             autoboot = 0;
364         if (!OPT_CHECK(RBX_QUIET))
365             printf("%s: %s", PATH_CONFIG, cmddup);
366         /* Do not process this command twice */
367         *cmd = 0;
368     }
369
370     /*
371      * Try to exec stage 3 boot loader. If interrupted by a keypress,
372      * or in case of failure, try to load a kernel directly instead.
373      */
374
375     if (!kname) {
376         kname = PATH_LOADER;
377         if (autoboot && !keyhit(3*SECOND)) {
378             load();
379             kname = PATH_KERNEL;
380         }
381     }
382
383     /* Present the user with the boot2 prompt. */
384
385     for (;;) {
386         if (!autoboot || !OPT_CHECK(RBX_QUIET))
387             printf("\nFreeBSD/pc98 boot\n"
388                    "Default: %u:%s(%u,%c)%s\n"
389                    "boot: ",
390                    dsk.unit, dev_nm[dsk.type], dsk.unit,
391                    'a' + dsk.part, kname);
392         if (DO_SIO)
393             sio_flush();
394         if (!autoboot || keyhit(3*SECOND))
395             getstr();
396         else if (!autoboot || !OPT_CHECK(RBX_QUIET))
397             putchar('\n');
398         autoboot = 0;
399         if (parse())
400             putchar('\a');
401         else
402             load();
403     }
404 }
405
406 /* XXX - Needed for btxld to link the boot2 binary; do not remove. */
407 void
408 exit(int x)
409 {
410 }
411
412 static void
413 load(void)
414 {
415     union {
416         struct exec ex;
417         Elf32_Ehdr eh;
418     } hdr;
419     static Elf32_Phdr ep[2];
420     static Elf32_Shdr es[2];
421     caddr_t p;
422     ufs_ino_t ino;
423     uint32_t addr;
424     int i, j;
425
426     if (!(ino = lookup(kname))) {
427         if (!ls)
428             printf("No %s\n", kname);
429         return;
430     }
431     if (xfsread(ino, &hdr, sizeof(hdr)))
432         return;
433
434     if (N_GETMAGIC(hdr.ex) == ZMAGIC) {
435         addr = hdr.ex.a_entry & 0xffffff;
436         p = PTOV(addr);
437         fs_off = PAGE_SIZE;
438         if (xfsread(ino, p, hdr.ex.a_text))
439             return;
440         p += roundup2(hdr.ex.a_text, PAGE_SIZE);
441         if (xfsread(ino, p, hdr.ex.a_data))
442             return;
443     } else if (IS_ELF(hdr.eh)) {
444         fs_off = hdr.eh.e_phoff;
445         for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
446             if (xfsread(ino, ep + j, sizeof(ep[0])))
447                 return;
448             if (ep[j].p_type == PT_LOAD)
449                 j++;
450         }
451         for (i = 0; i < 2; i++) {
452             p = PTOV(ep[i].p_paddr & 0xffffff);
453             fs_off = ep[i].p_offset;
454             if (xfsread(ino, p, ep[i].p_filesz))
455                 return;
456         }
457         p += roundup2(ep[1].p_memsz, PAGE_SIZE);
458         bootinfo.bi_symtab = VTOP(p);
459         if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
460             fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
461                 (hdr.eh.e_shstrndx + 1);
462             if (xfsread(ino, &es, sizeof(es)))
463                 return;
464             for (i = 0; i < 2; i++) {
465                 *(Elf32_Word *)p = es[i].sh_size;
466                 p += sizeof(es[i].sh_size);
467                 fs_off = es[i].sh_offset;
468                 if (xfsread(ino, p, es[i].sh_size))
469                     return;
470                 p += es[i].sh_size;
471             }
472         }
473         addr = hdr.eh.e_entry & 0xffffff;
474         bootinfo.bi_esymtab = VTOP(p);
475     } else {
476         printf("Invalid %s\n", "format");
477         return;
478     }
479
480     bootinfo.bi_kernelname = VTOP(kname);
481     bootinfo.bi_bios_dev = dsk.daua;
482     __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
483            MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part),
484            0, 0, 0, VTOP(&bootinfo));
485 }
486
487 static int
488 parse()
489 {
490     char *arg = cmd;
491     char *ep, *p, *q;
492     const char *cp;
493     unsigned int drv;
494     int c, i, j;
495
496     while ((c = *arg++)) {
497         if (c == ' ' || c == '\t' || c == '\n')
498             continue;
499         for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
500         ep = p;
501         if (*p)
502             *p++ = 0;
503         if (c == '-') {
504             while ((c = *arg++)) {
505                 if (c == 'P') {
506                     if (*(uint8_t *)PTOV(0x481) & 0x48) {
507                         cp = "yes";
508                     } else {
509                         opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL);
510                         cp = "no";
511                     }
512                     printf("Keyboard: %s\n", cp);
513                     continue;
514 #if SERIAL
515                 } else if (c == 'S') {
516                     j = 0;
517                     while ((unsigned int)(i = *arg++ - '0') <= 9)
518                         j = j * 10 + i;
519                     if (j > 0 && i == -'0') {
520                         comspeed = j;
521                         break;
522                     }
523                     /* Fall through to error below ('S' not in optstr[]). */
524 #endif
525                 }
526                 for (i = 0; c != optstr[i]; i++)
527                     if (i == NOPT - 1)
528                         return -1;
529                 opts ^= OPT_SET(flags[i]);
530             }
531 #if SERIAL
532             ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
533                      OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
534             if (DO_SIO) {
535                 if (sio_init(115200 / comspeed) != 0)
536                     ioctrl &= ~IO_SERIAL;
537             }
538 #endif
539         } else {
540             for (q = arg--; *q && *q != '('; q++);
541             if (*q) {
542                 drv = -1;
543                 if (arg[1] == ':') {
544                     drv = *arg - '0';
545                     if (drv > 9)
546                         return (-1);
547                     arg += 2;
548                 }
549                 if (q - arg != 2)
550                     return -1;
551                 for (i = 0; arg[0] != dev_nm[i][0] ||
552                             arg[1] != dev_nm[i][1]; i++)
553                     if (i == NDEV - 1)
554                         return -1;
555                 dsk.type = i;
556                 arg += 3;
557                 dsk.unit = *arg - '0';
558                 if (arg[1] != ',' || dsk.unit > 9)
559                     return -1;
560                 arg += 2;
561                 dsk.slice = WHOLE_DISK_SLICE;
562                 if (arg[1] == ',') {
563                     dsk.slice = *arg - '0' + 1;
564                     if (dsk.slice > PC98_NPARTS + 1)
565                         return -1;
566                     arg += 2;
567                 }
568                 if (arg[1] != ')')
569                     return -1;
570                 dsk.part = *arg - 'a';
571                 if (dsk.part > 7)
572                     return (-1);
573                 arg += 2;
574                 if (drv == -1)
575                     drv = dsk.unit;
576                 dsk.disk = dev_daua[dsk.type];
577                 dsk.daua = dsk.disk | dsk.unit;
578                 dsk_meta = 0;
579             }
580             if ((i = ep - arg)) {
581                 if ((size_t)i >= sizeof(knamebuf))
582                     return -1;
583                 memcpy(knamebuf, arg, i + 1);
584                 kname = knamebuf;
585             }
586         }
587         arg = p;
588     }
589     return 0;
590 }
591
592 static int
593 dskread(void *buf, unsigned lba, unsigned nblk)
594 {
595     struct pc98_partition *dp;
596     struct disklabel *d;
597     char *sec;
598     unsigned i;
599     uint8_t sl;
600     u_char *p;
601
602     if (!dsk_meta) {
603         sec = dmadat->secbuf;
604         set_dsk();
605         if (dsk.type == TYPE_FD)
606             goto unsliced;
607         if (drvread(sec, PC98_BBSECTOR))
608             return -1;
609         dp = (void *)(sec + PC98_PARTOFF);
610         sl = dsk.slice;
611         if (sl < BASE_SLICE) {
612             for (i = 0; i < PC98_NPARTS; i++)
613                 if (dp[i].dp_mid == DOSMID_386BSD) {
614                     sl = BASE_SLICE + i;
615                     break;
616                 }
617             dsk.slice = sl;
618         }
619         if (sl != WHOLE_DISK_SLICE) {
620             dp += sl - BASE_SLICE;
621             if (dp->dp_mid != DOSMID_386BSD) {
622                 printf("Invalid %s\n", "slice");
623                 return -1;
624             }
625             dsk.start = dp->dp_scyl * dsk.head * dsk.sec +
626                 dp->dp_shd * dsk.sec + dp->dp_ssect;
627         }
628         if (drvread(sec, dsk.start + LABELSECTOR))
629                 return -1;
630         d = (void *)(sec + LABELOFFSET);
631         if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
632             if (dsk.part != RAW_PART) {
633                 printf("Invalid %s\n", "label");
634                 return -1;
635             }
636         } else {
637             if (dsk.part >= d->d_npartitions ||
638                 !d->d_partitions[dsk.part].p_size) {
639                 printf("Invalid %s\n", "partition");
640                 return -1;
641             }
642             dsk.start += d->d_partitions[dsk.part].p_offset;
643             dsk.start -= d->d_partitions[RAW_PART].p_offset;
644         }
645     unsliced: ;
646     }
647     for (p = buf; nblk; p += 512, lba++, nblk--) {
648         if ((i = drvread(p, dsk.start + lba)))
649             return i;
650     }
651     return 0;
652 }
653
654 static void
655 printf(const char *fmt,...)
656 {
657     va_list ap;
658     static char buf[10];
659     char *s;
660     unsigned u;
661     int c;
662
663     va_start(ap, fmt);
664     while ((c = *fmt++)) {
665         if (c == '%') {
666             c = *fmt++;
667             switch (c) {
668             case 'c':
669                 putchar(va_arg(ap, int));
670                 continue;
671             case 's':
672                 for (s = va_arg(ap, char *); *s; s++)
673                     putchar(*s);
674                 continue;
675             case 'u':
676                 u = va_arg(ap, unsigned);
677                 s = buf;
678                 do
679                     *s++ = '0' + u % 10U;
680                 while (u /= 10U);
681                 while (--s >= buf)
682                     putchar(*s);
683                 continue;
684             }
685         }
686         putchar(c);
687     }
688     va_end(ap);
689     return;
690 }
691
692 static void
693 putchar(int c)
694 {
695     if (c == '\n')
696         xputc('\r');
697     xputc(c);
698 }
699
700 static int
701 drvread(void *buf, unsigned lba)
702 {
703     static unsigned c = 0x2d5c7c2f;
704     unsigned bpc, x, cyl, head, sec;
705
706     bpc = dsk.sec * dsk.head;
707     cyl = lba / bpc;
708     x = lba % bpc;
709     head = x / dsk.sec;
710     sec = x % dsk.sec;
711
712     if (!OPT_CHECK(RBX_QUIET))
713         printf("%c\b", c = c << 8 | c >> 24);
714     v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
715     v86.addr = READORG;         /* call to read in boot1 */
716     v86.ecx = cyl;
717     v86.edx = (head << 8) | sec;
718     v86.edi = lba;
719     v86.ebx = 512;
720     v86.es = VTOPSEG(buf);
721     v86.ebp = VTOPOFF(buf);
722     v86int();
723     v86.ctl = V86_FLAGS;
724     if (V86_CY(v86.efl)) {
725         printf("error %u c/h/s %u/%u/%u lba %u\n", v86.eax >> 8 & 0xff,
726                cyl, head, sec, lba);
727         return -1;
728     }
729     return 0;
730 }
731
732 static inline void
733 delay(void)
734 {
735     int i;
736
737     i = 800;
738     do {
739         outb(0x5f, 0);  /* about 600ns */
740     } while (--i >= 0);
741 }
742
743 static int
744 keyhit(unsigned sec)
745 {
746     unsigned i;
747
748     if (OPT_CHECK(RBX_NOINTR))
749         return 0;
750     for (i = 0; i < sec * 1000; i++) {
751         if (xgetc(1))
752             return 1;
753         delay();
754     }
755     return 0;
756 }
757
758 static int
759 xputc(int c)
760 {
761     if (DO_KBD)
762         putc(c);
763     if (DO_SIO)
764         sio_putc(c);
765     return c;
766 }
767
768 static int
769 getc(int fn)
770 {
771     v86.addr = 0x18;
772     v86.eax = fn << 8;
773     v86int();
774     if (fn)
775         return (v86.ebx >> 8) & 0x01;
776     else
777         return v86.eax & 0xff;
778 }
779
780 static int
781 xgetc(int fn)
782 {
783     if (OPT_CHECK(RBX_NOINTR))
784         return 0;
785     for (;;) {
786         if (DO_KBD && getc(1))
787             return fn ? 1 : getc(0);
788         if (DO_SIO && sio_ischar())
789             return fn ? 1 : sio_getc();
790         if (fn)
791             return 0;
792     }
793 }