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