wok-next diff runcom/stuff/runcom.c @ rev 11674

add runcom
author Pascal Bellard <pascal.bellard@slitaz.org>
date Sat Feb 18 09:00:05 2012 +0100 (2012-02-18)
parents
children b6347c31b319
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/runcom/stuff/runcom.c	Sat Feb 18 09:00:05 2012 +0100
     1.3 @@ -0,0 +1,1364 @@
     1.4 +/*
     1.5 + * Simple example of use of vm86: launch a basic .com DOS executable
     1.6 + */
     1.7 +#include <stdlib.h>
     1.8 +#include <stdio.h>
     1.9 +#include <string.h>
    1.10 +#include <inttypes.h>
    1.11 +#include <unistd.h>
    1.12 +#include <fcntl.h>
    1.13 +#include <time.h>
    1.14 +#include <sys/mman.h>
    1.15 +#include <sys/ioctl.h>
    1.16 +#include <sys/time.h>
    1.17 +#include <sys/types.h>
    1.18 +#include <sys/stat.h>
    1.19 +#include <signal.h>
    1.20 +#include <errno.h>
    1.21 +#include <ctype.h>
    1.22 +#include <termios.h>
    1.23 +
    1.24 +#include <sys/syscall.h>
    1.25 +#include <asm/vm86.h>
    1.26 +
    1.27 +//#define DUMP_INT21
    1.28 +
    1.29 +static inline int vm86(int func, struct vm86plus_struct *v86)
    1.30 +{
    1.31 +    return syscall(__NR_vm86, func, v86);
    1.32 +}
    1.33 +
    1.34 +#define CF_MASK 		0x00000001
    1.35 +#define ZF_MASK 		0x00000040
    1.36 +#define TF_MASK 		0x00000100
    1.37 +#define IF_MASK 		0x00000200
    1.38 +#define DF_MASK 		0x00000400
    1.39 +#define IOPL_MASK		0x00003000
    1.40 +#define NT_MASK	         	0x00004000
    1.41 +#define RF_MASK			0x00010000
    1.42 +#define VM_MASK			0x00020000
    1.43 +#define AC_MASK			0x00040000
    1.44 +#define VIF_MASK                0x00080000
    1.45 +#define VIP_MASK                0x00100000
    1.46 +#define ID_MASK                 0x00200000
    1.47 +
    1.48 +void usage(void)
    1.49 +{
    1.50 +    printf("runcom version 0.2-slitaz (c) 2003-2011 Fabrice Bellard\n"
    1.51 +           "usage: runcom file.com [args...]\n"
    1.52 +           "Run simple .com DOS executables (linux vm86 test mode)\n");
    1.53 +    exit(1);
    1.54 +}
    1.55 +
    1.56 +static inline void set_bit(uint8_t *a, unsigned int bit)
    1.57 +{
    1.58 +    a[bit / 8] |= (1 << (bit % 8));
    1.59 +}
    1.60 +
    1.61 +static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg)
    1.62 +{
    1.63 +    return (uint8_t *)((seg << 4) + (reg & 0xffff));
    1.64 +}
    1.65 +
    1.66 +static inline void pushw(struct vm86_regs *r, int val)
    1.67 +{
    1.68 +    r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff);
    1.69 +    *(uint16_t *)seg_to_linear(r->ss, r->esp) = val;
    1.70 +}
    1.71 +
    1.72 +void dump_regs(struct vm86_regs *r)
    1.73 +{
    1.74 +    int i;
    1.75 +    uint8_t *p8;
    1.76 +    uint16_t *p16;
    1.77 +    fprintf(stderr,
    1.78 +            "EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n"
    1.79 +            "ESI=%08lx EDI=%08lx EBP=%08lx ESP=%08lx\n"
    1.80 +            "EIP=%08lx EFL=%08lx "
    1.81 +            "CS=%04x DS=%04x ES=%04x SS=%04x FS=%04x GS=%04x\n[SP]",
    1.82 +            r->eax, r->ebx, r->ecx, r->edx, r->esi, r->edi, r->ebp, r->esp,
    1.83 +            r->eip, r->eflags,
    1.84 +            r->cs, r->ds, r->es, r->ss, r->fs, r->gs);
    1.85 +    for (p16 = (uint16_t *) seg_to_linear(r->ss, r->esp), i = 0; i < 15; i++)
    1.86 +    	fprintf(stderr," %04x", *p16++);
    1.87 +    fprintf(stderr,"\n[IP]");
    1.88 +    for (p8 = seg_to_linear(r->cs, r->eip), i = 0; i < 25; i++)
    1.89 +    	fprintf(stderr," %02x", *p8++);
    1.90 +    fprintf(stderr,"\n");
    1.91 +}
    1.92 +
    1.93 +#define DOS_FD_MAX 256
    1.94 +typedef struct {
    1.95 +    int fd; /* -1 means closed */
    1.96 +} DOSFile;
    1.97 +
    1.98 +DOSFile dos_files[DOS_FD_MAX];
    1.99 +uint16_t cur_psp;
   1.100 +
   1.101 +void dos_init(void)
   1.102 +{
   1.103 +    int i;
   1.104 +    for(i = 0; i < DOS_FD_MAX; i++)
   1.105 +        dos_files[i].fd = (i < 3) ? i : -1;
   1.106 +}
   1.107 +
   1.108 +static inline void set_error(struct vm86_regs *r, int val)
   1.109 +{
   1.110 +    r->eflags &= ~CF_MASK;
   1.111 +    if (val) {
   1.112 +        r->eax = (r->eax & ~0xffff) | val;
   1.113 +        r->eflags |= CF_MASK;
   1.114 +    }
   1.115 +}
   1.116 +static DOSFile *get_file(int h)
   1.117 +{
   1.118 +    DOSFile *fh;
   1.119 +
   1.120 +    if (h < DOS_FD_MAX) {
   1.121 +        fh = &dos_files[h];
   1.122 +        if (fh->fd != -1)
   1.123 +            return fh;
   1.124 +    }
   1.125 +    return NULL;
   1.126 +}
   1.127 +
   1.128 +/* return -1 if error */
   1.129 +static int get_new_handle(void)
   1.130 +{
   1.131 +    DOSFile *fh;
   1.132 +    int i;
   1.133 +
   1.134 +    for(i = 0; i < DOS_FD_MAX; i++) {
   1.135 +        fh = &dos_files[i];
   1.136 +        if (fh->fd == -1)
   1.137 +            return i;
   1.138 +    }
   1.139 +    return -1;
   1.140 +}
   1.141 +
   1.142 +static char *get_filename1(struct vm86_regs *r, char *buf, int buf_size,
   1.143 +                           uint16_t seg, uint16_t offset)
   1.144 +{
   1.145 +    char *q;
   1.146 +    int c;
   1.147 +    q = buf;
   1.148 +    for(;;) {
   1.149 +        c = *seg_to_linear(seg, offset);
   1.150 +        if (c == 0)
   1.151 +            break;
   1.152 +        if (q >= buf + buf_size - 1)
   1.153 +            break;
   1.154 +        c = tolower(c);
   1.155 +        if (c == '\\')
   1.156 +            c = '/';
   1.157 +        *q++ = c;
   1.158 +        offset++;
   1.159 +    }
   1.160 +    *q = '\0';
   1.161 +    return buf;
   1.162 +} 
   1.163 +
   1.164 +static char *get_filename(struct vm86_regs *r, char *buf, int buf_size)
   1.165 +{
   1.166 +    return get_filename1(r, buf, buf_size, r->ds, r->edx & 0xffff);
   1.167 +}
   1.168 +
   1.169 +typedef struct __attribute__((packed)) {
   1.170 +    uint8_t drive_num;
   1.171 +    uint8_t file_name[8];
   1.172 +    uint8_t file_ext[3];
   1.173 +    uint16_t current_block;
   1.174 +    uint16_t logical_record_size;
   1.175 +    uint32_t file_size;
   1.176 +    uint16_t date;
   1.177 +    uint16_t time;
   1.178 +    uint8_t reserved[8];
   1.179 +    uint8_t record_in_block;
   1.180 +    uint32_t record_num;
   1.181 +} FCB;
   1.182 +
   1.183 +typedef struct __attribute__((packed)) {
   1.184 +    uint16_t environ;
   1.185 +    uint16_t cmdtail_off;
   1.186 +    uint16_t cmdtail_seg;
   1.187 +    uint32_t fcb1;
   1.188 +    uint32_t fcb2;
   1.189 +    uint16_t sp, ss;
   1.190 +    uint16_t ip, cs;
   1.191 +} ExecParamBlock;
   1.192 +
   1.193 +typedef struct MemBlock {
   1.194 +    struct MemBlock *next;
   1.195 +    uint16_t seg;
   1.196 +    uint16_t size; /* in paragraphs */
   1.197 +} MemBlock;
   1.198 +
   1.199 +/* first allocated paragraph */
   1.200 +MemBlock *first_mem_block = NULL;
   1.201 +
   1.202 +#define MEM_START 0x1000
   1.203 +#define MEM_END   0xa000
   1.204 +
   1.205 +/* return -1 if error */
   1.206 +int mem_malloc(int size, int *pmax_size)
   1.207 +{
   1.208 +    MemBlock *m, **pm;
   1.209 +    int seg_free, seg;
   1.210 +    
   1.211 +    /* XXX: this is totally inefficient, but we have only 1 or 2
   1.212 +       blocks ! */
   1.213 +    seg_free = MEM_START;
   1.214 +    for(pm = &first_mem_block; *pm != NULL; pm = &(*pm)->next) {
   1.215 +        m = *pm;
   1.216 +        seg = m->seg + m->size;
   1.217 +        if (seg > seg_free)
   1.218 +            seg_free = seg;
   1.219 +    }
   1.220 +    if ((seg_free + size) > MEM_END)
   1.221 +        return -1;
   1.222 +    if (pmax_size)
   1.223 +        *pmax_size = MEM_END - seg_free;
   1.224 +    /* add at the end */
   1.225 +    m = malloc(sizeof(MemBlock));
   1.226 +    *pm = m;
   1.227 +    m->next = NULL;
   1.228 +    m->seg = seg_free;
   1.229 +    m->size = size;
   1.230 +#ifdef DUMP_INT21
   1.231 +    printf("mem_malloc size=0x%04x: 0x%04x\n", size, seg_free);
   1.232 +#endif
   1.233 +    return seg_free;
   1.234 +}
   1.235 +
   1.236 +/* return -1 if error */
   1.237 +int mem_free(int seg)
   1.238 +{
   1.239 +    MemBlock *m, **pm;
   1.240 +    for(pm = &first_mem_block; *pm != NULL; pm = &(*pm)->next) {
   1.241 +        m = *pm;
   1.242 +        if (m->seg == seg) {
   1.243 +            *pm = m->next;
   1.244 +            free(m);
   1.245 +            return 0;
   1.246 +        }
   1.247 +    }
   1.248 +    return -1;
   1.249 +}
   1.250 +
   1.251 +/* return -1 if error or the maxmium size */
   1.252 +int mem_resize(int seg, int new_size)
   1.253 +{
   1.254 +    MemBlock *m, **pm, *m1;
   1.255 +    int max_size;
   1.256 +
   1.257 +    for(pm = &first_mem_block; *pm != NULL; pm = &(*pm)->next) {
   1.258 +        m = *pm;
   1.259 +        if (m->seg == seg) {
   1.260 +            m1 = m->next;
   1.261 +            if (!m1)
   1.262 +                max_size = MEM_END - m->seg;
   1.263 +            else
   1.264 +                max_size = m1->seg - m->seg;
   1.265 +            if (new_size > max_size)
   1.266 +                return -1;
   1.267 +            m->size = new_size;
   1.268 +            return max_size;
   1.269 +        }
   1.270 +    }
   1.271 +    return -1;
   1.272 +}
   1.273 +
   1.274 +int load_boot(const char *filename, struct vm86_regs *r)
   1.275 +{
   1.276 +    int fd, ret;
   1.277 +
   1.278 +    /* load the boot sector */
   1.279 +    fd = open(filename, O_RDONLY);
   1.280 +    if (fd >= 0) {
   1.281 +    	*seg_to_linear(0x0, 0x7dff) = 0;
   1.282 +        r->eax = 0x200;
   1.283 +        r->ebx = r->esp = r->eip = 0x7c00;
   1.284 +        r->ecx = 1;
   1.285 +        r->esi = r->edi = r->ebp =
   1.286 +        r->edx = 0; /* floppy disk */
   1.287 +        r->cs = r->ss = r->ds = r->es = 0;
   1.288 +        r->eflags = VIF_MASK;
   1.289 +        ret = read(fd, seg_to_linear(0x0, 0x7c00), 0x200);
   1.290 +        if (lseek(fd, 0, SEEK_END) > 4*1024*1024)
   1.291 +            r->edx = 0x80; /* hard disk */
   1.292 +        close(fd);
   1.293 +        if (ret != 0x200 ||
   1.294 +            *seg_to_linear(0x0, 0x7dfe) != 0x55 ||
   1.295 +            *seg_to_linear(0x0, 0x7dff) != 0xaa) {
   1.296 +            fprintf(stderr,"No boot sector.\n");
   1.297 +            fd = -1;
   1.298 +        }
   1.299 +    }
   1.300 +    return fd;
   1.301 +}
   1.302 +
   1.303 +/* return the PSP or -1 if error */
   1.304 +int load_exe(ExecParamBlock *blk, const char *filename,
   1.305 +             int psp, uint32_t *pfile_size)
   1.306 +{
   1.307 +    int fd, size, base;
   1.308 +    struct {
   1.309 +    	uint16_t signature;		// 0x5A4D 'MZ'
   1.310 +    	uint16_t bytes_in_last_block;
   1.311 +    	uint16_t blocks_in_file;	
   1.312 +    	uint16_t num_relocs;	
   1.313 +    	uint16_t header_paragraphs;	// Size of header
   1.314 +    	uint16_t min_extra_paragraphs;	// BSS size
   1.315 +    	uint16_t max_extra_paragraphs;
   1.316 +    	uint16_t ss;			// Initial (relative) SS value
   1.317 +    	uint16_t sp;			// Initial SP value
   1.318 +    	uint16_t checksum;
   1.319 +    	uint16_t ip;			// Initial IP value
   1.320 +    	uint16_t cs;			// Initial (relative) CS value
   1.321 +    	uint16_t reloc_table_offset;
   1.322 +    	uint16_t overlay_number;
   1.323 +    } header;
   1.324 +    struct {
   1.325 +    	uint16_t offset;
   1.326 +    	uint16_t segment;
   1.327 +    } rel;
   1.328 +
   1.329 +    /* load the MSDOS .exe executable */
   1.330 +    fd = open(filename, O_RDONLY);
   1.331 +    if (fd < 0) {
   1.332 +        return -1;
   1.333 +    }
   1.334 +    if (read(fd, &header, sizeof(header)) != sizeof(header)) {
   1.335 +        close(fd);
   1.336 +        return -1;
   1.337 +    }
   1.338 +    
   1.339 +    memset(seg_to_linear(psp, 0x100), 0, 65536 - 0x100);
   1.340 +  
   1.341 +    size = (header.blocks_in_file * 512) - (header.header_paragraphs * 16) + 
   1.342 +    	(header.bytes_in_last_block ? header.bytes_in_last_block - 512 : 0);
   1.343 +    header.min_extra_paragraphs += (size-1)/16;
   1.344 +    
   1.345 +    /* address of last segment allocated */
   1.346 +    *(uint16_t *)seg_to_linear(psp, 2) = psp + header.min_extra_paragraphs;
   1.347 +    
   1.348 +    if (pfile_size)
   1.349 +        *pfile_size = size;
   1.350 +
   1.351 +    if (mem_resize(psp, header.min_extra_paragraphs) < 0 ||
   1.352 +    	lseek(fd, header.header_paragraphs * 16, SEEK_SET) < 0 ||
   1.353 +        read(fd, seg_to_linear(psp, 0x100), size) != size ||
   1.354 +        lseek(fd, header.reloc_table_offset, SEEK_SET) < 0) {
   1.355 +        close(fd);
   1.356 +        return -1;
   1.357 +    }
   1.358 +
   1.359 +    base = psp + 16;
   1.360 +    while (header.num_relocs-- && read(fd, &rel, sizeof(rel)) == sizeof(rel))
   1.361 +    	if (rel.segment != 0 || rel.offset != 0)
   1.362 +    	    * (uint16_t *) seg_to_linear(base + rel.segment, rel.offset) += base;
   1.363 +    close(fd);
   1.364 +
   1.365 +    blk->cs = base + header.cs;
   1.366 +    blk->ip = header.ip;
   1.367 +    blk->ss = base + header.ss;
   1.368 +    blk->sp = header.sp - 6;
   1.369 +
   1.370 +    /* push return far address */
   1.371 +    *(uint16_t *)seg_to_linear(blk->ss, blk->sp + 4) = psp;
   1.372 +
   1.373 +    return psp;
   1.374 +}
   1.375 +
   1.376 +/* return the PSP or -1 if error */
   1.377 +int load_com(ExecParamBlock *blk, const char *filename, uint32_t *pfile_size,
   1.378 +             int argc, char **argv)
   1.379 +{
   1.380 +    int psp, fd, ret;
   1.381 +
   1.382 +    /* load the MSDOS .com executable */
   1.383 +    fd = open(filename, O_RDONLY);
   1.384 +    if (fd < 0) {
   1.385 +        return -1;
   1.386 +    }
   1.387 +    psp = mem_malloc(65536 / 16, NULL);
   1.388 +    ret = read(fd, seg_to_linear(psp, 0x100), 65536 - 0x100);
   1.389 +    close(fd);
   1.390 +    if (ret <= 0) {
   1.391 +        mem_free(psp);
   1.392 +        return -1;
   1.393 +    }
   1.394 +    if (pfile_size)
   1.395 +        *pfile_size = ret;
   1.396 +    
   1.397 +    /* reset the PSP */
   1.398 +    memset(seg_to_linear(psp, 0), 0, 0x100);
   1.399 +
   1.400 +    *seg_to_linear(psp, 0) = 0xcd; /* int $0x20 */
   1.401 +    *seg_to_linear(psp, 1) = 0x20;
   1.402 +    /* address of last segment allocated */
   1.403 +    *(uint16_t *)seg_to_linear(psp, 2) = psp + 0xfff;
   1.404 +
   1.405 +    if (argc) {
   1.406 +        int i, p;
   1.407 +        char *s;
   1.408 +        /* set the command line */
   1.409 +        p = 0x81;
   1.410 +        for(i = 2; i < argc; i++) {
   1.411 +            if (p >= 0xff)
   1.412 +                break;
   1.413 +            *seg_to_linear(psp, p++) = ' ';
   1.414 +            s = argv[i];
   1.415 +            while (*s) {
   1.416 +                if (p >= 0xff)
   1.417 +                    break;
   1.418 +                *seg_to_linear(psp, p++) = *s++;
   1.419 +            }
   1.420 +        }
   1.421 +        *seg_to_linear(psp, p) = '\r';
   1.422 +        *seg_to_linear(psp, 0x80) = p - 0x81;
   1.423 +    }
   1.424 +    else {
   1.425 +        int len;
   1.426 +        /* copy the command line */
   1.427 +        len = *seg_to_linear(blk->cmdtail_seg, blk->cmdtail_off);
   1.428 +        memcpy(seg_to_linear(psp, 0x80), 
   1.429 +               seg_to_linear(blk->cmdtail_seg, blk->cmdtail_off), len + 2);
   1.430 +    }
   1.431 +
   1.432 +    blk->sp = 0xfffc;
   1.433 +    blk->ip = 0x100;
   1.434 +    blk->cs = blk->ss = psp;
   1.435 +
   1.436 +    if (*(uint16_t *)seg_to_linear(psp, 0x100) == 0x5A4D)
   1.437 +        psp = load_exe(blk, filename, psp, pfile_size);
   1.438 +    
   1.439 +    /* push ax value */
   1.440 +    *(uint16_t *)seg_to_linear(blk->ss, blk->sp) = 0;
   1.441 +    /* push return address to 0 */
   1.442 +    *(uint16_t *)seg_to_linear(blk->ss, blk->sp + 2) = 0;
   1.443 + 
   1.444 +    return psp;
   1.445 +}
   1.446 +
   1.447 +
   1.448 +void unsupported_function(struct vm86_regs *r, uint8_t num, uint8_t ah)
   1.449 +{
   1.450 +    fprintf(stderr, "int 0x%02x: unsupported function 0x%02x\n", num, ah);
   1.451 +    dump_regs(r);
   1.452 +    set_error(r, 0x01); /* function number invalid */
   1.453 +}
   1.454 +
   1.455 +/* Open hard disk image ./hd[0-7] / floppy image ./fd[0-7] or /dev/fd[0-7] */
   1.456 +int open_disk(struct vm86_regs *r)
   1.457 +{
   1.458 +    int fd = -1, drive = r->edx & 0xff;
   1.459 +    char filename[9], n = '0' + (drive & 7);
   1.460 +    if (drive > 127) {
   1.461 +        strcpy(filename,"hd0");
   1.462 +        filename[2] = n;
   1.463 +    }
   1.464 +    else {
   1.465 +        strcpy(filename,"/dev/fd0");
   1.466 +        filename[7] = n;
   1.467 +        fd = open(filename+5, O_RDONLY);
   1.468 +    }
   1.469 +    if (fd < 0)
   1.470 +        fd = open(filename, O_RDONLY);
   1.471 +    return fd;
   1.472 +}
   1.473 +
   1.474 +
   1.475 +void read_sectors(int fd, struct vm86_regs *r, int first_sector, 
   1.476 +                  int sector_count, void *buffer) 
   1.477 +{
   1.478 +    int drive = r->edx & 0xff;
   1.479 +    r->eax &= ~0xff00;
   1.480 +    r->eax |= 0x0400; /* sector not found */
   1.481 +    r->eflags |= CF_MASK;
   1.482 +    if (fd >= 0) {
   1.483 +        static struct stat st;
   1.484 +        first_sector <<= 9;
   1.485 +        sector_count <<= 9;
   1.486 +    	if (drive < 8 && fstat(fd, &st) == 0) {
   1.487 +            static ino_t inodes[8];
   1.488 +            ino_t last = inodes[drive];
   1.489 +            inodes[drive] = st.st_ino;
   1.490 +            if (last && last != st.st_ino) {
   1.491 +                set_error(r, 0x0600); /* floppy disk swap */
   1.492 +                goto failed;
   1.493 +            }
   1.494 +    	}
   1.495 +        if (lseek(fd, first_sector, SEEK_CUR) >= 0 &&
   1.496 +            read(fd, buffer, sector_count) == sector_count) {
   1.497 +            r->eax &= ~0xff00;
   1.498 +            r->eflags &= ~CF_MASK;
   1.499 +        }
   1.500 +    failed:
   1.501 +        close(fd);
   1.502 +    }
   1.503 +}
   1.504 +
   1.505 +void do_int10(struct vm86_regs *r)
   1.506 +{
   1.507 +    uint8_t ah;
   1.508 +    
   1.509 +    ah = (r->eax >> 8);
   1.510 +    switch(ah) {
   1.511 +    case 0x0E: /* write char */
   1.512 +        {
   1.513 +            uint8_t c = r->eax;
   1.514 +            write(1, &c, 1);
   1.515 +        }
   1.516 +        break;
   1.517 +    default:
   1.518 +        unsupported_function(r, 0x10, ah);
   1.519 +    }
   1.520 +}
   1.521 +
   1.522 +void do_int13(struct vm86_regs *r)
   1.523 +{
   1.524 +    uint8_t ah;
   1.525 +    
   1.526 +    ah = (r->eax >> 8);
   1.527 +    switch(ah) {
   1.528 +    case 0x00: /* reset disk */
   1.529 +        {
   1.530 +            r->eax &= ~0xff00; /* success */
   1.531 +            r->eflags &= ~CF_MASK;
   1.532 +        }
   1.533 +        break;
   1.534 +    case 0x02: /* read disk CHS */
   1.535 +        {
   1.536 +            int fd, c, h, s, heads, sectors, cylinders;
   1.537 +            long size;
   1.538 +            fd = open_disk(r);
   1.539 +            if (fd >= 0) {
   1.540 +                size = lseek(fd, 0, SEEK_END) / 512;
   1.541 +                if ((r->edx & 0xff) > 127) {
   1.542 +        	    sectors = 63;
   1.543 +                    if (size % sectors)
   1.544 +        	        sectors = 62;
   1.545 +                    if (size % sectors)
   1.546 +        	        sectors = 32;
   1.547 +                    if (size % sectors)
   1.548 +        	        sectors = 17;
   1.549 +                    if (size % sectors)
   1.550 +                        fd = -1;
   1.551 +                    size /= sectors;
   1.552 +        	    for (heads = 256; size % heads; heads--);
   1.553 +        	    cylinders = size / heads;
   1.554 +                }
   1.555 +                else {
   1.556 +                    int i;
   1.557 +        	    heads = 1 + (size > 256*2);
   1.558 +        	    cylinders = 40 * (1 + (size > 512*2));
   1.559 +        	    size /= heads;
   1.560 +        	    for (i = 0; i < 5; i++)
   1.561 +        	        if (size % (cylinders + i) == 0) break;
   1.562 +        	    if (i == 5)
   1.563 +        	    	fd = -1;
   1.564 +        	    cylinders += i;
   1.565 +        	    sectors = size / cylinders;
   1.566 +                }
   1.567 +            }
   1.568 +            c = ((r->ecx & 0xC0) << 2) | ((r->ecx >> 8) & 0xff);
   1.569 +            h = (r->edx >> 8) & 0xff;
   1.570 +            s = (r->ecx & 0x3f) -1;
   1.571 +            if (fd < 0 || c >= cylinders || h >= heads || s >= sectors) {
   1.572 +                set_error(r, 0x0400); /* sector not found */
   1.573 +                break;
   1.574 +            }
   1.575 +            read_sectors(fd, r, (((c * heads) + h) * sectors) + s,
   1.576 +                         r->eax & 0xff, seg_to_linear(r->es, r->ebx));
   1.577 +        }
   1.578 +        break;
   1.579 +    case 0x42: /* read disk LBA */
   1.580 +        {
   1.581 +            uint16_t *packet = (uint16_t *) seg_to_linear(r->ds, r-> esi);
   1.582 +            uint8_t *to = seg_to_linear(packet[3], packet[2]);
   1.583 +            if ((packet[3] & packet[2]) == 0xffff)
   1.584 +            	to = * (uint8_t **) &packet[8]; 
   1.585 +            if (packet[0] != 0x0010 && packet[0] != 0x0018)
   1.586 +                goto unsupported;
   1.587 +            read_sectors(open_disk(r), r, * (uint32_t *) &packet[4], packet[1], to);
   1.588 +        }
   1.589 +        break;
   1.590 +    default:
   1.591 +    unsupported:
   1.592 +        unsupported_function(r, 0x13, ah);
   1.593 +    }
   1.594 +}
   1.595 +
   1.596 +void do_int15(struct vm86_regs *r)
   1.597 +{
   1.598 +    uint8_t ah;
   1.599 +    
   1.600 +    ah = (r->eax >> 8);
   1.601 +    switch(ah) {
   1.602 +    case 0x87: /* move memory */
   1.603 +    	/* XXX */
   1.604 +        break;
   1.605 +    default:
   1.606 +        unsupported_function(r, 0x15, ah);
   1.607 +    }
   1.608 +}
   1.609 +
   1.610 +void do_int16(struct vm86_regs *r)
   1.611 +{
   1.612 +    static uint16_t last_ax, hold_char;
   1.613 +    struct termios termios_def, termios_raw;
   1.614 +    uint8_t ah;
   1.615 +    
   1.616 +    ah = (r->eax >> 8);
   1.617 +    tcgetattr(0, &termios_def);
   1.618 +    termios_raw = termios_def;
   1.619 +    cfmakeraw(&termios_raw);
   1.620 +    tcsetattr(0, TCSADRAIN, &termios_raw);
   1.621 +    switch(ah) {
   1.622 +    case 0x01: /* test keyboard */
   1.623 +        {
   1.624 +            int count;
   1.625 +            r->eflags &= ~ZF_MASK;
   1.626 +            if (hold_char) {
   1.627 +                r->eax &= ~0xffff;
   1.628 +                r->eax |= last_ax;
   1.629 +                break;
   1.630 +            }
   1.631 +    	    if (ioctl(0, FIONREAD, &count) < 0 || count == 0) {
   1.632 +                r->eflags |= ZF_MASK;
   1.633 +                break;
   1.634 +            }
   1.635 +            hold_char = 2;
   1.636 +        }
   1.637 +    case 0x00: /* read keyboard */
   1.638 +        {
   1.639 +            uint8_t c;
   1.640 +            if (hold_char)
   1.641 +            	hold_char--;
   1.642 +            read(0, &c, 1);
   1.643 +            if (c == 3) {
   1.644 +                tcsetattr(0, TCSADRAIN, &termios_def);
   1.645 +                exit(0);
   1.646 +            }
   1.647 +            if (c == 10)
   1.648 +            	c = 13;
   1.649 +            r->eax &= ~0xffff;
   1.650 +            r->eax |= last_ax = c;
   1.651 +            /* XXX ah = scan code */
   1.652 +        }
   1.653 +        break;
   1.654 +    default:
   1.655 +        unsupported_function(r, 0x16, ah);
   1.656 +    }
   1.657 +    tcsetattr(0, TCSADRAIN, &termios_def);
   1.658 +}
   1.659 +
   1.660 +void do_int1a(struct vm86_regs *r)
   1.661 +{
   1.662 +    uint8_t ah;
   1.663 +    
   1.664 +    ah = (r->eax >> 8);
   1.665 +    switch(ah) {
   1.666 +    case 0x00: /* GET SYSTEM TIME */
   1.667 +        {
   1.668 +            uint16_t *timer = (uint16_t *) seg_to_linear(0, 0x46C);
   1.669 +            r->ecx &= ~0xffff;
   1.670 +            r->ecx |= *timer++;
   1.671 +            r->edx &= ~0xffff;
   1.672 +            r->edx |= *timer;
   1.673 +            r->eax &= ~0xff;
   1.674 +        }
   1.675 +        break;
   1.676 +    default:
   1.677 +        unsupported_function(r, 0x1a, ah);
   1.678 +    }
   1.679 +}
   1.680 +
   1.681 +void do_int20(struct vm86_regs *r)
   1.682 +{
   1.683 +    /* terminate program */
   1.684 +    exit(0);
   1.685 +}
   1.686 +
   1.687 +void do_int21(struct vm86_regs *r)
   1.688 +{
   1.689 +    uint8_t ah;
   1.690 +    
   1.691 +    ah = (r->eax >> 8);
   1.692 +    switch(ah) {
   1.693 +    case 0x00: /* exit */
   1.694 +        exit(0);
   1.695 +    case 0x02: /* write char */
   1.696 +        {
   1.697 +            uint8_t c = r->edx;
   1.698 +            write(1, &c, 1);
   1.699 +        }
   1.700 +        break;
   1.701 +    case 0x09: /* write string */
   1.702 +        {
   1.703 +            uint8_t c;
   1.704 +            int offset;
   1.705 +            offset = r->edx;
   1.706 +            for(;;) {
   1.707 +                c = *seg_to_linear(r->ds, offset);
   1.708 +                if (c == '$')
   1.709 +                    break;
   1.710 +                write(1, &c, 1);
   1.711 +                offset++;
   1.712 +            }
   1.713 +            r->eax = (r->eax & ~0xff) | '$';
   1.714 +        }
   1.715 +        break;
   1.716 +    case 0x0a: /* buffered input */
   1.717 +        {
   1.718 +            int max_len, cur_len, ret;
   1.719 +            uint8_t ch;
   1.720 +            uint16_t off;
   1.721 +
   1.722 +            /* XXX: should use raw mode to avoid sending the CRLF to
   1.723 +               the terminal */
   1.724 +            off = r->edx & 0xffff;
   1.725 +            max_len = *seg_to_linear(r->ds, off);
   1.726 +            cur_len = 0;
   1.727 +            while (cur_len < max_len) {
   1.728 +                ret = read(0, &ch, 1);
   1.729 +                if (ret < 0) {
   1.730 +                    if (errno != EINTR && errno != EAGAIN)
   1.731 +                        break;
   1.732 +                } else if (ret == 0) {
   1.733 +                    break;
   1.734 +                } else {
   1.735 +                    if (ch == '\n')
   1.736 +                        break;
   1.737 +                }
   1.738 +                *seg_to_linear(r->ds, off + 2 + cur_len++) = ch;
   1.739 +            }
   1.740 +            *seg_to_linear(r->ds, off + 1) = cur_len;
   1.741 +            *seg_to_linear(r->ds, off + 2 + cur_len) = '\r';
   1.742 +        }
   1.743 +        break;
   1.744 +    case 0x25: /* set interrupt vector */
   1.745 +        {
   1.746 +            uint16_t *ptr;
   1.747 +            ptr = (uint16_t *)seg_to_linear(0, (r->eax & 0xff) * 4);
   1.748 +            ptr[0] = r->edx;
   1.749 +            ptr[1] = r->ds;
   1.750 +        }
   1.751 +        break;
   1.752 +    case 0x29: /* parse filename into FCB */
   1.753 +#if 0
   1.754 +        /* not really needed */
   1.755 +        {
   1.756 +            const uint8_t *p, *p_start;
   1.757 +            uint8_t file[8], ext[3]; 
   1.758 +            FCB *fcb;
   1.759 +            int file_len, ext_len, has_wildchars, c, drive_num;
   1.760 +            
   1.761 +            /* XXX: not complete at all */
   1.762 +            fcb = (FCB *)seg_to_linear(r->es, r->edi);
   1.763 +            printf("ds=0x%x si=0x%lx\n", r->ds, r->esi);
   1.764 +            p_start = (const uint8_t *)seg_to_linear(r->ds, r->esi);
   1.765 +
   1.766 +            p = p_start;
   1.767 +            has_wildchars = 0;
   1.768 +
   1.769 +            /* drive */
   1.770 +            if (isalpha(p[0]) && p[1] == ':') {
   1.771 +                drive_num = toupper(p[0]) - 'A' + 1;
   1.772 +                p += 2;
   1.773 +            } else {
   1.774 +                drive_num = 0;
   1.775 +            }
   1.776 +
   1.777 +            /* filename */
   1.778 +            file_len = 0;
   1.779 +            for(;;) {
   1.780 +                c = *p;
   1.781 +                if (!(c >= 33 && c <= 126))
   1.782 +                    break;
   1.783 +                if (c == '.')
   1.784 +                    break;
   1.785 +                if (c == '*' || c == '?')
   1.786 +                    has_wildchars = 1;
   1.787 +                if (file_len < 8)
   1.788 +                    file[file_len++] = c;
   1.789 +            }
   1.790 +            memset(file + file_len, ' ', 8 - file_len);
   1.791 +
   1.792 +            /* extension */
   1.793 +            ext_len = 0;
   1.794 +            if (*p == '.') {
   1.795 +                for(;;) {
   1.796 +                    c = *p;
   1.797 +                    if (!(c >= 33 && c <= 126))
   1.798 +                        break;
   1.799 +                    if (c == '*' || c == '?')
   1.800 +                        has_wildchars = 1;
   1.801 +                    ext[ext_len++] = c;
   1.802 +                    if (ext_len >= 3)
   1.803 +                        break;
   1.804 +                }
   1.805 +            }
   1.806 +            memset(ext + ext_len, ' ', 3 - ext_len);
   1.807 +
   1.808 +#if 0
   1.809 +            {
   1.810 +                printf("drive=%d file=%8s ext=%3s\n",
   1.811 +                       drive_num, file, ext);
   1.812 +            }
   1.813 +#endif
   1.814 +            if (drive_num == 0 && r->eax & (1 << 1)) {
   1.815 +                /* keep drive */
   1.816 +            } else {
   1.817 +                fcb->drive_num = drive_num; /* default drive */
   1.818 +            }
   1.819 +
   1.820 +            if (file_len == 0 && r->eax & (1 << 2)) {
   1.821 +                /* keep */
   1.822 +            } else {
   1.823 +                memcpy(fcb->file_name, file, 8);
   1.824 +            }
   1.825 +
   1.826 +            if (ext_len == 0 && r->eax & (1 << 3)) {
   1.827 +                /* keep */
   1.828 +            } else {
   1.829 +                memcpy(fcb->file_ext, ext, 3);
   1.830 +            }
   1.831 +            r->eax = (r->eax & ~0xff) | has_wildchars;
   1.832 +            r->esi = (r->esi & ~0xffff) | ((r->esi + (p - p_start)) & 0xffff);
   1.833 +        }
   1.834 +#endif
   1.835 +        break;
   1.836 +    case 0x2A: /* get system date */
   1.837 +        {
   1.838 +            time_t t = time(NULL);
   1.839 +            struct tm *now=localtime(&t);
   1.840 +            
   1.841 +            r->ecx = now->tm_year;
   1.842 +            r->edx = (now->tm_mon * 256) + now->tm_mday;
   1.843 +            r->eax = now->tm_wday;;
   1.844 +        }
   1.845 +        break;
   1.846 +    case 0x2C: /* get system time */
   1.847 +        {
   1.848 +            time_t t = time(NULL);
   1.849 +            struct tm *now=localtime(&t);
   1.850 +            struct timeval tim;
   1.851 +            
   1.852 +            gettimeofday(&tim, NULL);
   1.853 +            r->edx = (now->tm_hour * 256) + now->tm_min;
   1.854 +            r->edx = (tim.tv_sec * 256) + tim.tv_usec/10000;
   1.855 +        }
   1.856 +        break;
   1.857 +    case 0x30: /* get dos version */
   1.858 +        {
   1.859 +            int major, minor, serial, oem;
   1.860 +            /* XXX: return correct value for FreeDOS */
   1.861 +            major = 0x03;
   1.862 +            minor = 0x31;
   1.863 +            serial = 0x123456;
   1.864 +            oem = 0x66;
   1.865 +            r->eax = (r->eax & ~0xffff) | major | (minor << 8);
   1.866 +            r->ecx = (r->ecx & ~0xffff) | (serial & 0xffff);
   1.867 +            r->ebx = (r->ebx & ~0xffff) | (serial & 0xff) | (0x66 << 8);
   1.868 +        }
   1.869 +        break;
   1.870 +    case 0x35: /* get interrupt vector */
   1.871 +        {
   1.872 +            uint16_t *ptr;
   1.873 +            ptr = (uint16_t *)seg_to_linear(0, (r->eax & 0xff) * 4);
   1.874 +            r->ebx = (r->ebx & ~0xffff) | ptr[0];
   1.875 +            r->es = ptr[1];
   1.876 +        }
   1.877 +        break;
   1.878 +    case 0x37: 
   1.879 +        {
   1.880 +            switch(r->eax & 0xff) {
   1.881 +            case 0x00: /* get switch char */
   1.882 +                r->eax = (r->eax & ~0xff) | 0x00;
   1.883 +                r->edx = (r->edx & ~0xff) | '/';
   1.884 +                break;
   1.885 +            default:
   1.886 +                goto unsupported;
   1.887 +            }
   1.888 +        }
   1.889 +        break;
   1.890 +    case 0x3c: /* create or truncate file */
   1.891 +        {
   1.892 +            char filename[1024];
   1.893 +            int fd, h, flags;
   1.894 +
   1.895 +            h = get_new_handle();
   1.896 +            if (h < 0) {
   1.897 +                set_error(r, 0x04); /* too many open files */
   1.898 +            } else {
   1.899 +                get_filename(r, filename, sizeof(filename));
   1.900 +                if (r->ecx & 1)
   1.901 +                    flags = 0444; /* read-only */
   1.902 +                else
   1.903 +                    flags = 0777;
   1.904 +                fd = open(filename, O_RDWR | O_TRUNC | O_CREAT, flags);
   1.905 +#ifdef DUMP_INT21
   1.906 +                printf("int21: create: file='%s' cx=0x%04x ret=%d\n", 
   1.907 +                       filename, (int)(r->ecx & 0xffff), h);
   1.908 +#endif
   1.909 +                if (fd < 0) {
   1.910 +                    set_error(r, 0x03); /* path not found */
   1.911 +                } else {
   1.912 +                    dos_files[h].fd = fd;
   1.913 +                    set_error(r, 0);
   1.914 +                    r->eax = (r->eax & ~0xffff) | h;
   1.915 +                }
   1.916 +            }
   1.917 +        }
   1.918 +        break;
   1.919 +    case 0x3d: /* open file */
   1.920 +        {
   1.921 +            char filename[1024];
   1.922 +            int fd, h;
   1.923 +
   1.924 +            h = get_new_handle();
   1.925 +            if (h < 0) {
   1.926 +                set_error(r, 0x04); /* too many open files */
   1.927 +            } else {
   1.928 +                get_filename(r, filename, sizeof(filename));
   1.929 +#ifdef DUMP_INT21
   1.930 +                printf("int21: open: file='%s' al=0x%02x ret=%d\n", 
   1.931 +                       filename, (int)(r->eax & 0xff), h);
   1.932 +#endif
   1.933 +                fd = open(filename, r->eax & 3);
   1.934 +                if (fd < 0) {
   1.935 +                    set_error(r, 0x02); /* file not found */
   1.936 +                } else {
   1.937 +                    dos_files[h].fd = fd;
   1.938 +                    set_error(r, 0);
   1.939 +                    r->eax = (r->eax & ~0xffff) | h;
   1.940 +                }
   1.941 +            }
   1.942 +        }
   1.943 +        break;
   1.944 +    case 0x3e: /* close file */
   1.945 +        {
   1.946 +            DOSFile *fh = get_file(r->ebx & 0xffff);
   1.947 +#ifdef DUMP_INT21
   1.948 +            printf("int21: close fd=%d\n", (int)(r->ebx & 0xffff));
   1.949 +#endif
   1.950 +            if (!fh) {
   1.951 +                set_error(r, 0x06); /* invalid handle */
   1.952 +            } else {
   1.953 +                close(fh->fd);
   1.954 +                fh->fd = -1;
   1.955 +                set_error(r, 0);
   1.956 +            }
   1.957 +        }
   1.958 +        break;
   1.959 +    case 0x3f: /* read */
   1.960 +        {
   1.961 +            DOSFile *fh = get_file(r->ebx & 0xffff);
   1.962 +            int n, ret;
   1.963 +
   1.964 +            if (!fh) {
   1.965 +                set_error(r, 0x06); /* invalid handle */
   1.966 +            } else {
   1.967 +                n = r->ecx & 0xffff;
   1.968 +                for(;;) {
   1.969 +                    ret = read(fh->fd, 
   1.970 +                               seg_to_linear(r->ds, r->edx), n);
   1.971 +                    if (ret < 0) {
   1.972 +                        if (errno != EINTR && errno != EAGAIN)
   1.973 +                            break;
   1.974 +                    } else {
   1.975 +                        break;
   1.976 +                    }
   1.977 +                }
   1.978 +#ifdef DUMP_INT21
   1.979 +                printf("int21: read: fd=%d n=%d ret=%d\n", 
   1.980 +                       (int)(r->ebx & 0xffff), n, ret);
   1.981 +#endif
   1.982 +                if (ret < 0) {
   1.983 +                    set_error(r, 0x05); /* acces denied */
   1.984 +                } else {
   1.985 +                    r->eax = (r->eax & ~0xffff) | ret;
   1.986 +                    set_error(r, 0);
   1.987 +                }
   1.988 +            }
   1.989 +        }
   1.990 +        break;
   1.991 +    case 0x40: /* write */
   1.992 +        {
   1.993 +            DOSFile *fh = get_file(r->ebx & 0xffff);
   1.994 +            int n, ret, pos;
   1.995 +            
   1.996 +            if (!fh) {
   1.997 +                set_error(r, 0x06); /* invalid handle */
   1.998 +            } else {
   1.999 +                n = r->ecx & 0xffff;
  1.1000 +                if (n == 0) {
  1.1001 +                    /* truncate */
  1.1002 +                    pos = lseek(fh->fd, 0, SEEK_CUR);
  1.1003 +                    if (pos >= 0) {
  1.1004 +                        ret = ftruncate(fh->fd, pos);
  1.1005 +                    } else {
  1.1006 +                        ret = -1;
  1.1007 +                    }
  1.1008 +                } else {
  1.1009 +                    for(;;) {
  1.1010 +                        ret = write(fh->fd, 
  1.1011 +                                    seg_to_linear(r->ds, r->edx), n);
  1.1012 +                        if (ret < 0) {
  1.1013 +                            if (errno != EINTR && errno != EAGAIN)
  1.1014 +                                break;
  1.1015 +                        } else {
  1.1016 +                            break;
  1.1017 +                        }
  1.1018 +                    }
  1.1019 +                }
  1.1020 +#ifdef DUMP_INT21
  1.1021 +                printf("int21: write: fd=%d n=%d ret=%d\n", 
  1.1022 +                       (int)(r->ebx & 0xffff), n, ret);
  1.1023 +#endif
  1.1024 +                if (ret < 0) {
  1.1025 +                    set_error(r, 0x05); /* acces denied */
  1.1026 +                } else {
  1.1027 +                    r->eax = (r->eax & ~0xffff) | ret;
  1.1028 +                    set_error(r, 0);
  1.1029 +                }
  1.1030 +            }
  1.1031 +        }
  1.1032 +        break;
  1.1033 +    case 0x41: /* unlink */
  1.1034 +        {
  1.1035 +            char filename[1024];
  1.1036 +            get_filename(r, filename, sizeof(filename));
  1.1037 +            if (unlink(filename) < 0) {
  1.1038 +                set_error(r, 0x02); /* file not found */
  1.1039 +            } else {
  1.1040 +                set_error(r, 0);
  1.1041 +            }
  1.1042 +        }
  1.1043 +        break;
  1.1044 +    case 0x42: /* lseek */
  1.1045 +        {
  1.1046 +            DOSFile *fh = get_file(r->ebx & 0xffff);
  1.1047 +            int pos, ret;
  1.1048 +            
  1.1049 +            if (!fh) {
  1.1050 +                set_error(r, 0x06); /* invalid handle */
  1.1051 +            } else {
  1.1052 +                pos = ((r->ecx & 0xffff) << 16) | (r->edx & 0xffff);
  1.1053 +                ret = lseek(fh->fd, pos, r->eax & 0xff);
  1.1054 +#ifdef DUMP_INT21
  1.1055 +                printf("int21: lseek: fd=%d pos=%d whence=%d ret=%d\n", 
  1.1056 +                       (int)(r->ebx & 0xffff), pos, (uint8_t)r->eax, ret);
  1.1057 +#endif
  1.1058 +                if (ret < 0) {
  1.1059 +                    set_error(r, 0x01); /* function number invalid */
  1.1060 +                } else {
  1.1061 +                    r->edx = (r->edx & ~0xffff) | ((unsigned)ret >> 16);
  1.1062 +                    r->eax = (r->eax & ~0xffff) | (ret & 0xffff);
  1.1063 +                    set_error(r, 0);
  1.1064 +                }
  1.1065 +            }
  1.1066 +        }
  1.1067 +        break;
  1.1068 +    case 0x44: /* ioctl */
  1.1069 +        switch(r->eax & 0xff) {
  1.1070 +        case 0x00: /* get device information */
  1.1071 +            {
  1.1072 +                DOSFile *fh = get_file(r->ebx & 0xffff);
  1.1073 +                int ret;
  1.1074 +                
  1.1075 +                if (!fh) {
  1.1076 +                    set_error(r, 0x06); /* invalid handle */
  1.1077 +                } else {
  1.1078 +                    ret = 0;
  1.1079 +                    if (isatty(fh->fd)) {
  1.1080 +                        ret |= 0x80;
  1.1081 +                        if (fh->fd == 0)
  1.1082 +                            ret |= (1 << 0);
  1.1083 +                        else
  1.1084 +                            ret |= (1 << 1);
  1.1085 +                    }
  1.1086 +                    r->edx = (r->edx & ~0xffff) | ret;
  1.1087 +                    set_error(r, 0);
  1.1088 +                }
  1.1089 +            }
  1.1090 +            break;
  1.1091 +        default:
  1.1092 +            goto unsupported;
  1.1093 +        }
  1.1094 +        break;
  1.1095 +    case 0x48: /* allocate memory */
  1.1096 +        {
  1.1097 +            int ret, max_size;
  1.1098 +#ifdef DUMP_INT21
  1.1099 +            printf("int21: allocate memory: size=0x%04x\n", (uint16_t)r->ebx);
  1.1100 +#endif
  1.1101 +            ret = mem_malloc(r->ebx & 0xffff, &max_size);
  1.1102 +            if (ret < 0) {
  1.1103 +                set_error(r, 0x08); /* insufficient memory*/
  1.1104 +            } else {
  1.1105 +                r->eax = (r->eax & ~0xffff) | ret;
  1.1106 +                r->ebx = (r->ebx & ~0xffff) | max_size;
  1.1107 +                set_error(r, 0);
  1.1108 +            }
  1.1109 +        }
  1.1110 +        break;
  1.1111 +    case 0x49: /* free memory */
  1.1112 +        {
  1.1113 +#ifdef DUMP_INT21
  1.1114 +            printf("int21: free memory: block=0x%04x\n", r->es);
  1.1115 +#endif
  1.1116 +            if (mem_free(r->es) < 0) {
  1.1117 +                set_error(r, 0x09); /* memory block address invalid */
  1.1118 +            } else {
  1.1119 +                set_error(r, 0);
  1.1120 +            }
  1.1121 +        }
  1.1122 +        break;
  1.1123 +    case 0x4a: /* resize memory block */
  1.1124 +        {
  1.1125 +            int ret;
  1.1126 +#ifdef DUMP_INT21
  1.1127 +            printf("int21: resize memory block: block=0x%04x size=0x%04x\n", 
  1.1128 +                   r->es, (uint16_t)r->ebx);
  1.1129 +#endif
  1.1130 +            ret = mem_resize(r->es, r->ebx & 0xffff);
  1.1131 +            if (ret < 0) {
  1.1132 +                set_error(r, 0x08); /* insufficient memory*/
  1.1133 +            } else {
  1.1134 +                r->ebx = (r->ebx & ~0xffff) | ret;
  1.1135 +                set_error(r, 0);
  1.1136 +            }
  1.1137 +        }
  1.1138 +        break;
  1.1139 +    case 0x4b: /* load program */
  1.1140 +        {
  1.1141 +            char filename[1024];
  1.1142 +            ExecParamBlock *blk;
  1.1143 +            int ret;
  1.1144 +
  1.1145 +            if ((r->eax & 0xff) != 0x01) /* only load */
  1.1146 +                goto unsupported;
  1.1147 +            get_filename(r, filename, sizeof(filename));
  1.1148 +            blk = (ExecParamBlock *)seg_to_linear(r->es, r->ebx);
  1.1149 +            ret = load_com(blk, filename, NULL, 0, NULL);
  1.1150 +            if (ret < 0) {
  1.1151 +                set_error(r, 0x02); /* file not found */
  1.1152 +            } else {
  1.1153 +                cur_psp = ret;
  1.1154 +                set_error(r, 0);
  1.1155 +            }
  1.1156 +        }
  1.1157 +        break;
  1.1158 +    case 0x4c: /* exit with return code */
  1.1159 +        exit(r->eax & 0xff);
  1.1160 +        break;
  1.1161 +    case 0x50: /* set PSP address */
  1.1162 +#ifdef DUMP_INT21
  1.1163 +        printf("int21: set PSP: 0x%04x\n", (uint16_t)r->ebx);
  1.1164 +#endif
  1.1165 +        cur_psp = r->ebx;
  1.1166 +        break;
  1.1167 +    case 0x51: /* get PSP address */
  1.1168 +#ifdef DUMP_INT21
  1.1169 +        printf("int21: get PSP: ret=0x%04x\n", cur_psp);
  1.1170 +#endif
  1.1171 +        r->ebx = (r->ebx & ~0xffff) | cur_psp;
  1.1172 +        break;
  1.1173 +    case 0x55: /* create child PSP */
  1.1174 +        {
  1.1175 +            uint8_t *psp_ptr;
  1.1176 +#ifdef DUMP_INT21
  1.1177 +            printf("int21: create child PSP: psp=0x%04x last_seg=0x%04x\n", 
  1.1178 +                   (uint16_t)r->edx, (uint16_t)r->esi);
  1.1179 +#endif
  1.1180 +            psp_ptr = seg_to_linear(r->edx & 0xffff, 0);
  1.1181 +            memset(psp_ptr, 0, 0x80);
  1.1182 +            psp_ptr[0] = 0xcd; /* int $0x20 */
  1.1183 +            psp_ptr[1] = 0x20;
  1.1184 +            *(uint16_t *)(psp_ptr + 2) = r->esi;
  1.1185 +            r->eax = (r->eax & ~0xff);
  1.1186 +        }
  1.1187 +        break;
  1.1188 +    default:
  1.1189 +    unsupported:
  1.1190 +        unsupported_function(r, 0x21, ah);
  1.1191 +    }
  1.1192 +}
  1.1193 +    
  1.1194 +void do_int29(struct vm86_regs *r)
  1.1195 +{
  1.1196 +    uint8_t c = r->eax;
  1.1197 +    write(1, &c, 1);
  1.1198 +}
  1.1199 +
  1.1200 +void raise_interrupt(int number)
  1.1201 +{
  1.1202 +    if (* (uint32_t *) seg_to_linear(0, number * 4) == 0)
  1.1203 +        return;
  1.1204 +    // FIXME VM86_SIGNAL
  1.1205 +}
  1.1206 +
  1.1207 +void biosclock()
  1.1208 +{
  1.1209 +    uint32_t *timer = (uint32_t *) seg_to_linear(0, 0x46C);
  1.1210 +    ++*timer;
  1.1211 +    raise_interrupt(8);
  1.1212 +    raise_interrupt(0x1C);
  1.1213 +}
  1.1214 +
  1.1215 +int main(int argc, char **argv)
  1.1216 +{
  1.1217 +    uint8_t *vm86_mem;
  1.1218 +    const char *filename;
  1.1219 +    int ret;
  1.1220 +    uint32_t file_size;
  1.1221 +    struct sigaction sa; 
  1.1222 +    struct itimerval timerval;
  1.1223 +    struct vm86plus_struct ctx;
  1.1224 +    struct vm86_regs *r;
  1.1225 +    ExecParamBlock blk1, *blk = &blk1;
  1.1226 +
  1.1227 +    if (argc < 2)
  1.1228 +        usage();
  1.1229 +    filename = argv[1];
  1.1230 +
  1.1231 +    vm86_mem = mmap((void *)0x00000000, 0x110000,
  1.1232 +                    PROT_WRITE | PROT_READ | PROT_EXEC,
  1.1233 +                    MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
  1.1234 +    if (vm86_mem == MAP_FAILED) {
  1.1235 +        perror("mmap");
  1.1236 +        exit(1);
  1.1237 +    }
  1.1238 +
  1.1239 +    memset(&ctx, 0, sizeof(ctx));
  1.1240 +    r = &ctx.regs;
  1.1241 +    set_bit((uint8_t *)&ctx.int_revectored, 0x10);
  1.1242 +    set_bit((uint8_t *)&ctx.int_revectored, 0x13);
  1.1243 +    set_bit((uint8_t *)&ctx.int_revectored, 0x15);
  1.1244 +    set_bit((uint8_t *)&ctx.int_revectored, 0x16);
  1.1245 +    set_bit((uint8_t *)&ctx.int_revectored, 0x1a);
  1.1246 +    set_bit((uint8_t *)&ctx.int_revectored, 0x20);
  1.1247 +    set_bit((uint8_t *)&ctx.int_revectored, 0x21);
  1.1248 +    set_bit((uint8_t *)&ctx.int_revectored, 0x29);
  1.1249 +
  1.1250 +    dos_init();
  1.1251 +
  1.1252 +    if (strstr(filename,".com") || strstr(filename,".exe") ||
  1.1253 +        strstr(filename,".COM") || strstr(filename,".EXE")) {
  1.1254 +        ret = load_com(blk, filename, &file_size, argc, argv);
  1.1255 +        if (ret < 0) {
  1.1256 +            perror(filename);
  1.1257 +            exit(1);
  1.1258 +        }
  1.1259 +        cur_psp = ret;
  1.1260 +
  1.1261 +        /* init basic registers */
  1.1262 +        r->eip = blk->ip;
  1.1263 +        r->esp = blk->sp + 2; /* pop ax value */
  1.1264 +        r->cs = blk->cs;
  1.1265 +        r->ss = blk->ss;
  1.1266 +        r->ds = cur_psp;
  1.1267 +        r->es = cur_psp;
  1.1268 +        r->eflags = VIF_MASK;
  1.1269 +    
  1.1270 +        /* the value of these registers seem to be assumed by pi_10.com */
  1.1271 +        r->esi = 0x100;
  1.1272 +#if 0
  1.1273 +        r->ebx = file_size >> 16;
  1.1274 +        r->ecx = file_size & 0xffff;
  1.1275 +#else
  1.1276 +        r->ecx = 0xff;
  1.1277 +#endif
  1.1278 +        r->ebp = 0x0900;
  1.1279 +        r->edi = 0xfffe;
  1.1280 +    }
  1.1281 +    else {
  1.1282 +        if (load_boot(filename, r) < 0) {
  1.1283 +            if (errno)
  1.1284 +                perror(filename);
  1.1285 +            exit(1);
  1.1286 +        }
  1.1287 +    }
  1.1288 +
  1.1289 +    sa.sa_handler = biosclock;
  1.1290 +    sigemptyset(&sa.sa_mask);
  1.1291 +    sa.sa_flags = SA_RESTART;
  1.1292 +    if (sigaction(SIGALRM, &sa, 0) == 0) {
  1.1293 +        timerval.it_interval.tv_sec  = timerval.it_value.tv_sec  = 0;
  1.1294 +        timerval.it_interval.tv_usec = timerval.it_value.tv_usec = 10000000 / 182;
  1.1295 +        setitimer (ITIMER_REAL, &timerval, NULL);
  1.1296 +    }
  1.1297 +
  1.1298 +    for(;;) {
  1.1299 +        ret = vm86(VM86_ENTER, &ctx);
  1.1300 +        switch(VM86_TYPE(ret)) {
  1.1301 +        case VM86_INTx:
  1.1302 +            {
  1.1303 +                int int_num;
  1.1304 +
  1.1305 +                int_num = VM86_ARG(ret);
  1.1306 +                switch(int_num) {
  1.1307 +                case 0x10:
  1.1308 +                    do_int10(r);
  1.1309 +                    break;
  1.1310 +                case 0x13:
  1.1311 +                    do_int13(r);
  1.1312 +                    break;
  1.1313 +                case 0x15:
  1.1314 +                    do_int15(r);
  1.1315 +                    break;
  1.1316 +                case 0x16:
  1.1317 +                    do_int16(r);
  1.1318 +                    break;
  1.1319 +                case 0x1a:
  1.1320 +                    do_int1a(r);
  1.1321 +                    break;
  1.1322 +                case 0x20:
  1.1323 +                    do_int20(r);
  1.1324 +                    break;
  1.1325 +                case 0x21:
  1.1326 +                    do_int21(r);
  1.1327 +                    break;
  1.1328 +                case 0x29:
  1.1329 +                    do_int29(r);
  1.1330 +                    break;
  1.1331 +                default:
  1.1332 +                    fprintf(stderr, "unsupported int 0x%02x\n", int_num);
  1.1333 +                    dump_regs(&ctx.regs);
  1.1334 +                    break;
  1.1335 +                }
  1.1336 +            }
  1.1337 +            break;
  1.1338 +        case VM86_SIGNAL:
  1.1339 +            /* a signal came, we just ignore that */
  1.1340 +            break;
  1.1341 +        case VM86_STI:
  1.1342 +            break;
  1.1343 +        case VM86_TRAP:
  1.1344 +            /* just executes the interruption */
  1.1345 +            {
  1.1346 +                uint16_t *int_vector;
  1.1347 +                uint32_t eflags;
  1.1348 +                
  1.1349 +                eflags = r->eflags & ~IF_MASK;
  1.1350 +                if (r->eflags & VIF_MASK)
  1.1351 +                    eflags |= IF_MASK;
  1.1352 +                pushw(r, eflags);
  1.1353 +                pushw(r, r->cs);
  1.1354 +                pushw(r, r->eip);
  1.1355 +                int_vector = (uint16_t *)seg_to_linear(0, VM86_ARG(ret) * 4);
  1.1356 +                r->eip = int_vector[0];
  1.1357 +                r->cs = int_vector[1];
  1.1358 +                r->eflags &= ~(VIF_MASK | TF_MASK | AC_MASK);
  1.1359 +            }
  1.1360 +            break;
  1.1361 +        default:
  1.1362 +            fprintf(stderr, "unhandled vm86 return code (0x%x)\n", ret);
  1.1363 +            dump_regs(&ctx.regs);
  1.1364 +            exit(1);
  1.1365 +        }
  1.1366 +    }
  1.1367 +}