gptboot.c revision 40404
1/* 2 * Copyright (c) 1998 Robert Nordier 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are freely 6 * permitted provided that the above copyright notice and this 7 * paragraph and the following disclaimer are duplicated in all 8 * such forms. 9 * 10 * This software is provided "AS IS" and without any express or 11 * implied warranties, including, without limitation, the implied 12 * warranties of merchantability and fitness for a particular 13 * purpose. 14 */ 15 16/* 17 * $Id: boot2.c,v 1.6 1998/10/13 23:43:38 rnordier Exp $ 18 */ 19 20#include <sys/param.h> 21#include <sys/reboot.h> 22#include <sys/diskslice.h> 23#include <sys/disklabel.h> 24#include <sys/dirent.h> 25#include <machine/bootinfo.h> 26 27#include <ufs/ffs/fs.h> 28#include <ufs/ufs/dinode.h> 29 30#include <stdarg.h> 31 32#include <a.out.h> 33#include <elf.h> 34 35#include <btxv86.h> 36 37#include "lib.h" 38 39#define RBX_ASKNAME 0x0 /* -a */ 40#define RBX_SINGLE 0x1 /* -s */ 41#define RBX_DFLTROOT 0x5 /* -r */ 42#define RBX_KDB 0x6 /* -d */ 43#define RBX_CONFIG 0xa /* -c */ 44#define RBX_VERBOSE 0xb /* -v */ 45#define RBX_SERIAL 0xc /* -h */ 46#define RBX_CDROM 0xd /* -C */ 47#define RBX_GDB 0xf /* -g */ 48#define RBX_DUAL 0x1d /* -D */ 49#define RBX_PROBEKBD 0x1e /* -P */ 50 51#define RBX_MASK 0xffff 52 53#define PATH_CONFIG "/boot.config" 54#define PATH_BOOT3 "/boot/loader" 55#define PATH_KERNEL "/kernel" 56#define PATH_HELP "boot.help" 57 58#define ARGS 0x800 59#define NOPT 11 60#define XOPT 2 61#define BSIZEMAX 8192 62#define NDEV 5 63#define MEM_BASE 0x12 64#define MEM_EXT 0x15 65#define V86_CY(x) ((x) & 1) 66#define V86_ZR(x) ((x) & 0x40) 67 68#define DRV_HARD 0x80 69#define DRV_MASK 0x7f 70 71#define MAJ_WD 0 72#define MAJ_WFD 1 73#define MAJ_FD 2 74#define MAJ_DA 4 75 76extern uint32_t _end; 77 78static const char optstr[NOPT] = "DhaCcdgPrsv"; 79static const unsigned char flags[NOPT] = { 80 RBX_DUAL, 81 RBX_SERIAL, 82 RBX_ASKNAME, 83 RBX_CDROM, 84 RBX_CONFIG, 85 RBX_KDB, 86 RBX_GDB, 87 RBX_PROBEKBD, 88 RBX_DFLTROOT, 89 RBX_SINGLE, 90 RBX_VERBOSE 91}; 92 93static const char *const dev_nm[] = {"wd", " ", "fd", " ", "da"}; 94 95static struct dsk { 96 unsigned drive; 97 unsigned type; 98 unsigned unit; 99 unsigned slice; 100 unsigned part; 101 unsigned start; 102 int init; 103 int meta; 104} dsk; 105static char cmd[512]; 106static char kname[1024]; 107static char help[2048]; 108static uint32_t opts; 109static struct bootinfo bootinfo; 110static int ls; 111static uint32_t fs_off; 112static uint8_t ioctrl = 0x1; 113 114void exit(int); 115static void load(const char *); 116static int parse(char *); 117static void readfile(const char *, void *, size_t); 118static ino_t lookup(const char *); 119static int fsfind(const char *, ino_t *); 120static int xfsread(ino_t, void *, size_t); 121static ssize_t fsread(ino_t, void *, size_t); 122static int dskread(void *, unsigned, unsigned); 123static int printf(const char *,...); 124static void getstr(char *, int); 125static int putchar(int); 126static int getchar(void); 127static void *memcpy(void *, const void *, size_t); 128static int strcmp(const char *, const char *); 129static void *malloc(size_t); 130static uint32_t memsize(int); 131static uint32_t drvinfo(int); 132static int drvread(void *, unsigned, unsigned); 133static int keyhit(unsigned); 134static int xputc(int); 135static int xgetc(int); 136static void putc(int); 137static int getc(int); 138 139int 140main(void) 141{ 142 int autoboot, helpon, i; 143 144 v86.ctl = V86_FLAGS; 145 dsk.drive = *(uint8_t *)PTOV(ARGS); 146 dsk.type = dsk.drive & DRV_HARD ? MAJ_WD : MAJ_FD; 147 dsk.unit = dsk.drive & DRV_MASK; 148 dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1; 149 bootinfo.bi_version = BOOTINFO_VERSION; 150 bootinfo.bi_size = sizeof(bootinfo); 151 bootinfo.bi_basemem = memsize(MEM_BASE); 152 bootinfo.bi_extmem = memsize(MEM_EXT); 153 bootinfo.bi_memsizes_valid++; 154 for (i = 0; i < N_BIOS_GEOM; i++) 155 bootinfo.bi_bios_geom[i] = drvinfo(i); 156 autoboot = 2; 157 helpon = 1; 158 readfile(PATH_CONFIG, cmd, sizeof(cmd)); 159 if (parse(cmd)) 160 autoboot = 0; 161 else if (!*kname) { 162 if (autoboot == 2) { 163 memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); 164 if (!keyhit(0x37)) { 165 load(kname); 166 autoboot = 1; 167 } 168 } 169 if (autoboot == 1) 170 memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); 171 } 172 readfile(PATH_HELP, help, sizeof(help)); 173 for (;;) { 174 printf(" \n>> FreeBSD/i386 BOOT\n" 175 "Default: %u:%s(%u,%c)%s\n" 176 "%s" 177 "boot: ", 178 dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 179 'a' + dsk.part, kname, helpon ? help : ""); 180 if (ioctrl & 0x2) 181 sio_flush(); 182 if (!autoboot || keyhit(0x5a)) 183 getstr(cmd, sizeof(cmd)); 184 autoboot = helpon = 0; 185 if (parse(cmd)) 186 helpon = 1; 187 else 188 load(kname); 189 } 190} 191 192void 193exit(int x) 194{ 195} 196 197static void 198load(const char *fname) 199{ 200 union { 201 struct exec ex; 202 Elf32_Ehdr eh; 203 } hdr; 204 Elf32_Phdr ep[2]; 205 Elf32_Shdr es[2]; 206 caddr_t p; 207 ino_t ino; 208 uint32_t addr, x; 209 int fmt, i, j; 210 211 if (!(ino = lookup(fname))) { 212 if (!ls) 213 printf("No %s\n", fname); 214 return; 215 } 216 if (xfsread(ino, &hdr, sizeof(hdr))) 217 return; 218 if (N_GETMAGIC(hdr.ex) == ZMAGIC) 219 fmt = 0; 220 else if (IS_ELF(hdr.eh)) 221 fmt = 1; 222 else { 223 printf("Invalid %s\n", "format"); 224 return; 225 } 226 if (fmt == 0) { 227 addr = hdr.ex.a_entry & 0xffffff; 228 p = PTOV(addr); 229 printf("%s=0x%x ", "text", (unsigned)hdr.ex.a_text); 230 fs_off = PAGE_SIZE; 231 if (xfsread(ino, p, hdr.ex.a_text)) 232 return; 233 p += roundup2(hdr.ex.a_text, PAGE_SIZE); 234 printf("%s=0x%x ", "data", (unsigned)hdr.ex.a_data); 235 if (xfsread(ino, p, hdr.ex.a_data)) 236 return; 237 p += hdr.ex.a_data; 238 printf("%s=0x%x ", "bss", (unsigned)hdr.ex.a_bss); 239 p += roundup2(hdr.ex.a_bss, PAGE_SIZE); 240 bootinfo.bi_symtab = VTOP(p); 241 memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); 242 p += sizeof(hdr.ex.a_syms); 243 printf("symbols=["); 244 printf("+0x%x", (unsigned)hdr.ex.a_syms); 245 if (hdr.ex.a_syms) { 246 if (xfsread(ino, p, hdr.ex.a_syms)) 247 return; 248 p += hdr.ex.a_syms; 249 if (xfsread(ino, p, sizeof(int))) 250 return; 251 x = *(uint32_t *)p; 252 p += sizeof(int); 253 x -= sizeof(int); 254 printf("+0x%x", x); 255 if (xfsread(ino, p, x)) 256 return; 257 p += x; 258 } 259 } else { 260 fs_off = hdr.eh.e_phoff; 261 for (j = i = 0; i < hdr.eh.e_phoff && j < 2; i++) { 262 if (xfsread(ino, ep + j, sizeof(ep[0]))) 263 return; 264 if (ep[j].p_type == PT_LOAD) 265 j++; 266 } 267 for (i = 0; i < 2; i++) { 268 p = PTOV(ep[i].p_paddr & 0xffffff); 269 printf("%s=0x%x ", !i ? "text" : "data", ep[i].p_filesz); 270 fs_off = ep[i].p_offset; 271 if (xfsread(ino, p, ep[i].p_filesz)) 272 return; 273 } 274 printf("%s=0x%x ", "bss", ep[1].p_memsz - ep[1].p_filesz); 275 p += roundup2(ep[1].p_memsz, PAGE_SIZE); 276 bootinfo.bi_symtab = VTOP(p); 277 printf("symbols=["); 278 if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 279 fs_off = hdr.eh.e_shoff + sizeof(es[0]) * 280 (hdr.eh.e_shstrndx + 1); 281 if (xfsread(ino, &es, sizeof(es))) 282 return; 283 for (i = 0; i < 2; i++) { 284 memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size)); 285 p += sizeof(es[i].sh_size); 286 printf("+0x%x", es[i].sh_size); 287 fs_off = es[i].sh_offset; 288 if (xfsread(ino, p, es[i].sh_size)) 289 return; 290 p += es[i].sh_size; 291 } 292 } 293 addr = hdr.eh.e_entry & 0xffffff; 294 } 295 bootinfo.bi_esymtab = VTOP(p); 296 printf("]\nentry=0x%x\n", addr); 297 bootinfo.bi_kernelname = VTOP(fname); 298 __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), 299 MAKEBOOTDEV(dsk.type, 0, dsk.slice, dsk.unit, dsk.part), 300 0, 0, 0, VTOP(&bootinfo)); 301} 302 303static int 304parse(char *arg) 305{ 306 char *p, *q; 307 int drv, c, i; 308 309 while ((c = *arg++)) { 310 if (c == ' ') 311 continue; 312 for (p = arg; *p && *p != '\n' && *p != ' '; p++); 313 if (*p) 314 *p++ = 0; 315 if (c == '-') { 316 while ((c = *arg++)) { 317 for (i = 0; c != optstr[i]; i++) 318 if (i == NOPT - 1) 319 return -1; 320 if (i < XOPT) 321 opts ^= 1 << flags[i]; 322 else 323 opts |= 1 << flags[i]; 324 } 325 if (opts & 1 << RBX_PROBEKBD) { 326 i = *(uint8_t *)PTOV(0x496) & 0x10; 327 printf("Keyboard: %s\n", i ? "yes" : "no"); 328 if (!i) 329 opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL; 330 } 331 ioctrl = opts & 1 << RBX_DUAL ? 0x3 : 332 opts & 1 << RBX_SERIAL ? 0x2 : 0x1; 333 if (ioctrl & 0x2) 334 sio_init(); 335 } else { 336 for (q = arg--; *q && *q != '('; q++); 337 if (*q) { 338 drv = -1; 339 if (arg[1] == ':') { 340 if (*arg < '0' || *arg > '9') 341 return -1; 342 drv = *arg - '0'; 343 arg += 2; 344 } 345 if (q - arg != 2) 346 return -1; 347 for (i = 0; arg[0] != dev_nm[i][0] || 348 arg[1] != dev_nm[i][1]; i++) 349 if (i == NDEV - 1) 350 return -1; 351 dsk.type = i; 352 arg += 3; 353 if (arg[1] != ',' || *arg < '0' || *arg > '9') 354 return -1; 355 dsk.unit = *arg - '0'; 356 arg += 2; 357 dsk.slice = WHOLE_DISK_SLICE; 358 if (arg[1] == ',') { 359 if (*arg < '0' || *arg > '0' + NDOSPART) 360 return -1; 361 if ((dsk.slice = *arg - '0')) 362 dsk.slice++; 363 arg += 2; 364 } 365 if (arg[1] != ')' || *arg < 'a' || *arg > 'p') 366 return -1; 367 dsk.part = *arg - 'a'; 368 arg += 2; 369 if (drv == -1) 370 drv = dsk.unit; 371 dsk.drive = (dsk.type == MAJ_WD || 372 dsk.type == MAJ_DA ? DRV_HARD : 0) + drv; 373 dsk.meta = 0; 374 fsread(0, NULL, 0); 375 } 376 if ((i = p - arg - !*(p - 1))) { 377 if (i >= sizeof(kname)) 378 return -1; 379 memcpy(kname, arg, i + 1); 380 } 381 } 382 arg = p; 383 } 384 return 0; 385} 386 387static void 388readfile(const char *fname, void *buf, size_t size) 389{ 390 ino_t ino; 391 392 if ((ino = lookup(fname))) 393 fsread(ino, buf, size); 394} 395 396static ino_t 397lookup(const char *path) 398{ 399 char name[MAXNAMLEN + 1]; 400 const char *s; 401 ino_t ino; 402 ssize_t n; 403 int dt; 404 405 ino = ROOTINO; 406 dt = DT_DIR; 407 for (;;) { 408 if (*path == '/') 409 path++; 410 if (!*path) 411 break; 412 for (s = path; *s && *s != '/'; s++); 413 if ((n = s - path) > MAXNAMLEN) 414 return 0; 415 ls = *path == '?' && n == 1 && !*s; 416 memcpy(name, path, n); 417 name[n] = 0; 418 if ((dt = fsfind(name, &ino)) <= 0) 419 break; 420 path = s; 421 } 422 return dt == DT_REG ? ino : 0; 423} 424 425static int 426fsfind(const char *name, ino_t * ino) 427{ 428 char buf[DEV_BSIZE]; 429 struct dirent *d; 430 char *s; 431 ssize_t n; 432 433 fs_off = 0; 434 while ((n = fsread(*ino, buf, DEV_BSIZE)) > 0) 435 for (s = buf; s < buf + DEV_BSIZE;) { 436 d = (void *)s; 437 if (ls) 438 printf("%s ", d->d_name); 439 else if (!strcmp(name, d->d_name)) { 440 *ino = d->d_fileno; 441 return d->d_type; 442 } 443 s += d->d_reclen; 444 } 445 if (n != -1 && ls) 446 putchar('\n'); 447 return 0; 448} 449 450static int 451xfsread(ino_t inode, void *buf, size_t nbyte) 452{ 453 if (fsread(inode, buf, nbyte) != nbyte) { 454 printf("Invalid %s\n", "format"); 455 return -1; 456 } 457 return 0; 458} 459 460static ssize_t 461fsread(ino_t inode, void *buf, size_t nbyte) 462{ 463 static struct fs fs; 464 static struct dinode din; 465 static char *blkbuf; 466 static ufs_daddr_t *indbuf; 467 static ino_t inomap; 468 static ufs_daddr_t blkmap, indmap; 469 static unsigned fsblks; 470 char *s; 471 ufs_daddr_t lbn, addr; 472 size_t n, nb, off; 473 474 if (!dsk.meta) { 475 if (!blkbuf) 476 blkbuf = malloc(BSIZEMAX); 477 inomap = 0; 478 if (dskread(blkbuf, SBOFF / DEV_BSIZE, SBSIZE / DEV_BSIZE)) 479 return -1; 480 memcpy(&fs, blkbuf, sizeof(fs)); 481 if (fs.fs_magic != FS_MAGIC) { 482 printf("Not ufs\n"); 483 return -1; 484 } 485 fsblks = fs.fs_bsize >> DEV_BSHIFT; 486 dsk.meta = 1; 487 } 488 if (!inode) 489 return 0; 490 if (inomap != inode) { 491 if (dskread(blkbuf, fsbtodb(&fs, ino_to_fsba(&fs, inode)), 492 fsblks)) 493 return -1; 494 din = ((struct dinode *)blkbuf)[inode % INOPB(&fs)]; 495 inomap = inode; 496 fs_off = 0; 497 blkmap = indmap = 0; 498 } 499 s = buf; 500 if (nbyte > (n = din.di_size - fs_off)) 501 nbyte = n; 502 nb = nbyte; 503 while (nb) { 504 lbn = lblkno(&fs, fs_off); 505 if (lbn < NDADDR) 506 addr = din.di_db[lbn]; 507 else { 508 if (indmap != din.di_ib[0]) { 509 if (!indbuf) 510 indbuf = malloc(BSIZEMAX); 511 if (dskread(indbuf, fsbtodb(&fs, din.di_ib[0]), 512 fsblks)) 513 return -1; 514 indmap = din.di_ib[0]; 515 } 516 addr = indbuf[(lbn - NDADDR) % NINDIR(&fs)]; 517 } 518 n = dblksize(&fs, &din, lbn); 519 if (blkmap != addr) { 520 if (dskread(blkbuf, fsbtodb(&fs, addr), n >> DEV_BSHIFT)) 521 return -1; 522 blkmap = addr; 523 } 524 off = blkoff(&fs, fs_off); 525 n -= off; 526 if (n > nb) 527 n = nb; 528 memcpy(s, blkbuf + off, n); 529 s += n; 530 fs_off += n; 531 nb -= n; 532 } 533 return nbyte; 534} 535 536static int 537dskread(void *buf, unsigned lba, unsigned nblk) 538{ 539 static char *sec; 540 struct dos_partition *dp; 541 struct disklabel *d; 542 unsigned sl, i; 543 544 if (!dsk.meta) { 545 if (!sec) 546 sec = malloc(DEV_BSIZE); 547 dsk.start = 0; 548 if (drvread(sec, DOSBBSECTOR, 1)) 549 return -1; 550 dp = (void *)(sec + DOSPARTOFF); 551 sl = dsk.slice; 552 if (sl < BASE_SLICE) { 553 for (i = 0; i < NDOSPART; i++) 554 if (dp[i].dp_typ == DOSPTYP_386BSD && 555 (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) { 556 sl = BASE_SLICE + i; 557 if (dp[i].dp_flag & 0x80 || 558 dsk.slice == COMPATIBILITY_SLICE) 559 break; 560 } 561 if (dsk.slice == WHOLE_DISK_SLICE) 562 dsk.slice = sl; 563 } 564 if (sl != WHOLE_DISK_SLICE) { 565 if (sl != COMPATIBILITY_SLICE) 566 dp += sl - BASE_SLICE; 567 if (dp->dp_typ != DOSPTYP_386BSD) { 568 printf("Invalid %s\n", "slice"); 569 return -1; 570 } 571 dsk.start = dp->dp_start; 572 } 573 if (drvread(sec, dsk.start + LABELSECTOR, 1)) 574 return -1; 575 d = (void *)(sec + LABELOFFSET); 576 if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) { 577 if (dsk.part != RAW_PART) { 578 printf("Invalid %s\n", "label"); 579 return -1; 580 } 581 } else { 582 if (!dsk.init) { 583 if (d->d_type == DTYPE_SCSI) 584 dsk.type = MAJ_DA; 585 dsk.init++; 586 } 587 if (dsk.part >= d->d_npartitions) { 588 printf("Invalid %s\n", "partition"); 589 return -1; 590 } 591 dsk.start = d->d_partitions[dsk.part].p_offset; 592 } 593 } 594 return drvread(buf, dsk.start + lba, nblk); 595} 596 597static int 598printf(const char *fmt,...) 599{ 600 static const char digits[16] = "0123456789abcdef"; 601 va_list ap; 602 char buf[10]; 603 char *s; 604 unsigned r, u; 605 int c; 606 607 va_start(ap, fmt); 608 while ((c = *fmt++)) { 609 if (c == '%') { 610 c = *fmt++; 611 switch (c) { 612 case 'c': 613 putchar(va_arg(ap, int)); 614 continue; 615 case 's': 616 for (s = va_arg(ap, char *); *s; s++) 617 putchar(*s); 618 continue; 619 case 'u': 620 case 'x': 621 r = c == 'u' ? 10U : 16U; 622 u = va_arg(ap, unsigned); 623 s = buf; 624 do 625 *s++ = digits[u % r]; 626 while (u /= r); 627 while (--s >= buf) 628 putchar(*s); 629 continue; 630 } 631 } 632 putchar(c); 633 } 634 va_end(ap); 635 return 0; 636} 637 638static void 639getstr(char *str, int size) 640{ 641 char *s; 642 int c; 643 644 s = str; 645 do { 646 switch (c = getchar()) { 647 case '\b': 648 if (s > str) 649 s--; 650 break; 651 case '\n': 652 *s = 0; 653 break; 654 default: 655 if (s - str < size - 1) 656 *s++ = c; 657 } 658 putchar(c); 659 } while (c != '\n'); 660} 661 662static int 663putchar(int c) 664{ 665 if (c == '\n') 666 xputc('\r'); 667 return xputc(c); 668} 669 670static int 671getchar(void) 672{ 673 int c; 674 675 c = xgetc(0); 676 if (c == '\r') 677 c = '\n'; 678 return c; 679} 680 681static void * 682memcpy(void *dst, const void *src, size_t size) 683{ 684 const char *s; 685 char *d; 686 687 for (d = dst, s = src; size; size--) 688 *d++ = *s++; 689 return dst; 690} 691 692static int 693strcmp(const char *s1, const char *s2) 694{ 695 for (; *s1 == *s2 && *s1; s1++, s2++); 696 return (u_char)*s1 - (u_char)*s2; 697} 698 699static void * 700malloc(size_t size) 701{ 702 static uint32_t next; 703 void *p; 704 705 if (!next) 706 next = roundup2(__base + _end, 0x10000) - __base; 707 p = (void *)next; 708 next += size; 709 return p; 710} 711 712static uint32_t 713memsize(int type) 714{ 715 v86.addr = type; 716 v86.eax = 0x8800; 717 v86int(); 718 return v86.eax; 719} 720 721static uint32_t 722drvinfo(int drive) 723{ 724 v86.addr = 0x13; 725 v86.eax = 0x800; 726 v86.edx = DRV_HARD + drive; 727 v86int(); 728 if (V86_CY(v86.efl)) 729 return 0x4f010f; 730 return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) | 731 (v86.edx & 0xff00) | (v86.ecx & 0x3f); 732} 733 734static int 735drvread(void *buf, unsigned lba, unsigned nblk) 736{ 737 static unsigned c = 0x2d5c7c2f; 738 739 printf("%c\b", c = c << 8 | c >> 24); 740 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 741 v86.addr = 0x604; 742 v86.eax = nblk; 743 v86.ebx = VTOPSEG(buf) << 16 | VTOPOFF(buf); 744 v86.ecx = lba; 745 v86.edx = 0x100 | dsk.drive; 746 v86int(); 747 v86.ctl = V86_FLAGS; 748 if (V86_CY(v86.efl)) { 749 printf("Disk error 0x%x (lba=0x%x)\n", v86.eax >> 8 & 0xff, 750 lba); 751 return -1; 752 } 753 return 0; 754} 755 756static int 757keyhit(unsigned ticks) 758{ 759 uint32_t t0, t1; 760 761 t0 = 0; 762 for (;;) { 763 if (xgetc(1)) 764 return 1; 765 t1 = *(uint32_t *)PTOV(0x46c); 766 if (!t0) 767 t0 = t1; 768 if (t1 < t0 || t1 >= t0 + ticks) 769 return 0; 770 } 771} 772 773static int 774xputc(int c) 775{ 776 if (ioctrl & 0x1) 777 putc(c); 778 if (ioctrl & 0x2) 779 sio_putc(c); 780 return c; 781} 782 783static int 784xgetc(int fn) 785{ 786 for (;;) { 787 if (ioctrl & 0x1 && getc(1)) 788 return fn ? 1 : getc(0); 789 if (ioctrl & 0x2 && sio_ischar()) 790 return fn ? 1 : sio_getc(); 791 if (fn) 792 return 0; 793 } 794} 795 796static void 797putc(int c) 798{ 799 v86.addr = 0x10; 800 v86.eax = 0xe00 | (c & 0xff); 801 v86.ebx = 0x7; 802 v86int(); 803} 804 805static int 806getc(int fn) 807{ 808 v86.addr = 0x16; 809 v86.eax = fn << 8; 810 v86int(); 811 return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl); 812} 813