wok view runcom/stuff/runcom.c @ rev 17602
appdata-tools: update bdeps
author | Pascal Bellard <pascal.bellard@slitaz.org> |
---|---|
date | Thu Feb 12 11:09:07 2015 +0100 (2015-02-12) |
parents | b6347c31b319 |
children | ac7aeefa3415 |
line source
1 /*
2 * Simple example of use of vm86: launch a basic .com DOS executable
3 */
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <string.h>
7 #include <inttypes.h>
8 #include <unistd.h>
9 #include <fcntl.h>
10 #include <time.h>
11 #include <sys/mman.h>
12 #include <sys/ioctl.h>
13 #include <sys/time.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <sys/statfs.h>
17 #include <signal.h>
18 #include <errno.h>
19 #include <ctype.h>
20 #include <termios.h>
21 #include <dirent.h>
22 #include <fnmatch.h>
24 #include <sys/syscall.h>
25 #include <asm/vm86.h>
27 //#define DUMP_INT21
29 static inline int vm86(int func, struct vm86plus_struct *v86)
30 {
31 return syscall(__NR_vm86, func, v86);
32 }
34 #define CF_MASK 0x00000001
35 #define ZF_MASK 0x00000040
36 #define TF_MASK 0x00000100
37 #define IF_MASK 0x00000200
38 #define DF_MASK 0x00000400
39 #define IOPL_MASK 0x00003000
40 #define NT_MASK 0x00004000
41 #define RF_MASK 0x00010000
42 #define VM_MASK 0x00020000
43 #define AC_MASK 0x00040000
44 #define VIF_MASK 0x00080000
45 #define VIP_MASK 0x00100000
46 #define ID_MASK 0x00200000
48 void usage(void)
49 {
50 printf("runcom version 0.2-slitaz (c) 2003-2011 Fabrice Bellard\n"
51 "usage: runcom file.com [args...]\n"
52 "Run simple .com DOS executables (linux vm86 test mode)\n");
53 exit(1);
54 }
56 static inline void set_bit(uint8_t *a, unsigned int bit)
57 {
58 a[bit / 8] |= (1 << (bit % 8));
59 }
61 static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg)
62 {
63 return (uint8_t *)((seg << 4) + (reg & 0xffff));
64 }
66 static inline void pushw(struct vm86_regs *r, int val)
67 {
68 r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff);
69 *(uint16_t *)seg_to_linear(r->ss, r->esp) = val;
70 }
72 void dump_regs(struct vm86_regs *r)
73 {
74 int i;
75 uint8_t *p8;
76 uint16_t *p16;
77 fprintf(stderr,
78 "EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n"
79 "ESI=%08lx EDI=%08lx EBP=%08lx ESP=%08lx\n"
80 "EIP=%08lx EFL=%08lx "
81 "CS=%04x DS=%04x ES=%04x SS=%04x FS=%04x GS=%04x\n[SP]",
82 r->eax, r->ebx, r->ecx, r->edx, r->esi, r->edi, r->ebp, r->esp,
83 r->eip, r->eflags,
84 r->cs, r->ds, r->es, r->ss, r->fs, r->gs);
85 for (p16 = (uint16_t *) seg_to_linear(r->ss, r->esp), i = 0; i < 15; i++)
86 fprintf(stderr," %04x", *p16++);
87 fprintf(stderr,"\n[IP]");
88 for (p8 = seg_to_linear(r->cs, r->eip), i = 0; i < 25; i++)
89 fprintf(stderr," %02x", *p8++);
90 fprintf(stderr,"\n");
91 }
93 static int argflags;
94 #define DEBUG 1
96 #define DOS_FD_MAX 256
97 typedef struct {
98 int fd; /* -1 means closed */
99 } DOSFile;
101 DOSFile dos_files[DOS_FD_MAX];
102 uint16_t cur_psp;
103 uint16_t cur_dta_seg;
104 uint16_t cur_dta_ofs;
106 void dos_init(void)
107 {
108 int i;
109 for(i = 0; i < DOS_FD_MAX; i++)
110 dos_files[i].fd = (i < 3) ? i : -1;
111 }
113 static inline void set_error(struct vm86_regs *r, int val)
114 {
115 r->eflags &= ~CF_MASK;
116 if (val) {
117 r->eax = (r->eax & ~0xffff) | val;
118 r->eflags |= CF_MASK;
119 }
120 }
121 static DOSFile *get_file(int h)
122 {
123 DOSFile *fh;
125 if (h < DOS_FD_MAX) {
126 fh = &dos_files[h];
127 if (fh->fd != -1)
128 return fh;
129 }
130 return NULL;
131 }
133 /* return -1 if error */
134 static int get_new_handle(void)
135 {
136 DOSFile *fh;
137 int i;
139 for(i = 0; i < DOS_FD_MAX; i++) {
140 fh = &dos_files[i];
141 if (fh->fd == -1)
142 return i;
143 }
144 return -1;
145 }
147 static char *get_filename1(struct vm86_regs *r, char *buf, int buf_size,
148 uint16_t seg, uint16_t offset)
149 {
150 char *q;
151 int c;
152 q = buf;
153 for(;;) {
154 c = *seg_to_linear(seg, offset);
155 if (c == 0)
156 break;
157 if (q >= buf + buf_size - 1)
158 break;
159 c = tolower(c);
160 if (c == '\\')
161 c = '/';
162 *q++ = c;
163 offset++;
164 }
165 *q = '\0';
166 if (buf[1] == ':')
167 strcpy(buf, buf+2);
168 return buf;
169 }
171 static char *get_filename(struct vm86_regs *r, char *buf, int buf_size)
172 {
173 return get_filename1(r, buf, buf_size, r->ds, r->edx & 0xffff);
174 }
176 static char *upcase(const char *s)
177 {
178 static char buffer[80];
179 int i;
180 for (i = 0; i < sizeof(buffer)-1; i++, s++)
181 buffer[i] = (*s >= 'a' && *s <= 'z') ? *s + 'A' - 'a' : *s;
182 return buffer;
183 }
185 typedef struct __attribute__((packed)) {
186 uint8_t drive_num;
187 uint8_t file_name[8];
188 uint8_t file_ext[3];
189 uint16_t current_block;
190 uint16_t logical_record_size;
191 uint32_t file_size;
192 uint16_t date;
193 uint16_t time;
194 uint8_t reserved[8];
195 uint8_t record_in_block;
196 uint32_t record_num;
197 } FCB;
199 typedef struct __attribute__((packed)) {
200 uint16_t environ;
201 uint16_t cmdtail_off;
202 uint16_t cmdtail_seg;
203 uint32_t fcb1;
204 uint32_t fcb2;
205 uint16_t sp, ss;
206 uint16_t ip, cs;
207 } ExecParamBlock;
209 typedef struct __attribute__((packed)) {
210 /* internals */
211 uint8_t attr; /* 00 */
212 uint8_t drive_letter; /* 01 */
213 uint8_t template[11]; /* 02 */
214 uint16_t entry_count; /* 0D */
215 uint32_t dta_address; /* 0F */
216 uint16_t cluster_parent_dir; /* 13 */
217 /* output */
218 uint8_t attr_found; /* 15 */
219 uint16_t file_time; /* 16 */
220 uint16_t file_date; /* 18 */
221 uint32_t file_size; /* 1A */
222 uint8_t filename[13]; /* 1E */
223 } dirdta;
225 typedef struct MemBlock {
226 struct MemBlock *next;
227 uint16_t seg;
228 uint16_t size; /* in paragraphs */
229 } MemBlock;
231 /* first allocated paragraph */
232 MemBlock *first_mem_block = NULL;
234 #define MEM_START 0x1000
235 #define MEM_END 0xa000
237 /* return -1 if error */
238 int mem_malloc(int size, int *pmax_size)
239 {
240 MemBlock *m, **pm;
241 int seg_free, seg;
243 /* XXX: this is totally inefficient, but we have only 1 or 2
244 blocks ! */
245 seg_free = MEM_START;
246 for(pm = &first_mem_block; *pm != NULL; pm = &(*pm)->next) {
247 m = *pm;
248 seg = m->seg + m->size;
249 if (seg > seg_free)
250 seg_free = seg;
251 }
252 if ((seg_free + size) > MEM_END)
253 return -1;
254 if (pmax_size)
255 *pmax_size = MEM_END - seg_free;
256 /* add at the end */
257 m = malloc(sizeof(MemBlock));
258 *pm = m;
259 m->next = NULL;
260 m->seg = seg_free;
261 m->size = size;
262 #ifdef DUMP_INT21
263 printf("mem_malloc size=0x%04x: 0x%04x\n", size, seg_free);
264 #endif
265 return seg_free;
266 }
268 /* return -1 if error */
269 int mem_free(int seg)
270 {
271 MemBlock *m, **pm;
272 for(pm = &first_mem_block; *pm != NULL; pm = &(*pm)->next) {
273 m = *pm;
274 if (m->seg == seg) {
275 *pm = m->next;
276 free(m);
277 return 0;
278 }
279 }
280 return -1;
281 }
283 /* return -1 if error or the maxmium size */
284 int mem_resize(int seg, int new_size)
285 {
286 MemBlock *m, **pm, *m1;
287 int max_size;
289 for(pm = &first_mem_block; *pm != NULL; pm = &(*pm)->next) {
290 m = *pm;
291 if (m->seg == seg) {
292 m1 = m->next;
293 if (!m1)
294 max_size = MEM_END - m->seg;
295 else
296 max_size = m1->seg - m->seg;
297 if (new_size > max_size)
298 return -1;
299 m->size = new_size;
300 return max_size;
301 }
302 }
303 return -1;
304 }
306 int load_boot(const char *filename, struct vm86_regs *r)
307 {
308 int fd, ret;
310 /* load the boot sector */
311 fd = open(filename, O_RDONLY);
312 if (fd >= 0) {
313 *seg_to_linear(0x0, 0x7dff) = 0;
314 r->eax = 0x200;
315 r->ebx = r->esp = r->eip = 0x7c00;
316 r->ecx = 1;
317 r->esi = r->edi = r->ebp =
318 r->edx = 0; /* floppy disk */
319 r->cs = r->ss = r->ds = r->es = 0;
320 r->eflags = VIF_MASK;
321 ret = read(fd, seg_to_linear(0x0, 0x7c00), 0x200);
322 if (lseek(fd, 0, SEEK_END) > 4*1024*1024)
323 r->edx = 0x80; /* hard disk */
324 close(fd);
325 if (ret != 0x200 ||
326 *seg_to_linear(0x0, 0x7dfe) != 0x55 ||
327 *seg_to_linear(0x0, 0x7dff) != 0xaa) {
328 fprintf(stderr,"No boot sector.\n");
329 fd = -1;
330 }
331 }
332 return fd;
333 }
335 /* return the PSP or -1 if error */
336 int load_exe(ExecParamBlock *blk, const char *filename,
337 int psp, uint32_t *pfile_size)
338 {
339 int fd, size, base;
340 struct {
341 uint16_t signature; // 0x5A4D 'MZ'
342 uint16_t bytes_in_last_block;
343 uint16_t blocks_in_file;
344 uint16_t num_relocs;
345 uint16_t header_paragraphs; // Size of header
346 uint16_t min_extra_paragraphs; // BSS size
347 uint16_t max_extra_paragraphs;
348 uint16_t ss; // Initial (relative) SS value
349 uint16_t sp; // Initial SP value
350 uint16_t checksum;
351 uint16_t ip; // Initial IP value
352 uint16_t cs; // Initial (relative) CS value
353 uint16_t reloc_table_offset;
354 uint16_t overlay_number;
355 } header;
356 struct {
357 uint16_t offset;
358 uint16_t segment;
359 } rel;
361 /* load the MSDOS .exe executable */
362 fd = open(filename, O_RDONLY);
363 if (fd < 0) {
364 return -1;
365 }
366 if (read(fd, &header, sizeof(header)) != sizeof(header)) {
367 close(fd);
368 return -1;
369 }
371 memset(seg_to_linear(psp, 0x100), 0, 65536 - 0x100);
373 size = (header.blocks_in_file * 512) - (header.header_paragraphs * 16) +
374 (header.bytes_in_last_block ? header.bytes_in_last_block - 512 : 0);
375 header.min_extra_paragraphs += (size-1)/16;
377 /* address of last segment allocated */
378 //*(uint16_t *)seg_to_linear(psp, 2) = psp + header.min_extra_paragraphs;
379 *(uint16_t *)seg_to_linear(psp, 2) = 0x9fff;
381 if (pfile_size)
382 *pfile_size = size;
384 if (mem_resize(psp, header.min_extra_paragraphs) < 0 ||
385 lseek(fd, header.header_paragraphs * 16, SEEK_SET) < 0 ||
386 read(fd, seg_to_linear(psp, 0x100), size) != size ||
387 lseek(fd, header.reloc_table_offset, SEEK_SET) < 0) {
388 close(fd);
389 return -1;
390 }
392 base = psp + 16;
393 while (header.num_relocs-- && read(fd, &rel, sizeof(rel)) == sizeof(rel))
394 if (rel.segment != 0 || rel.offset != 0)
395 * (uint16_t *) seg_to_linear(base + rel.segment, rel.offset) += base;
396 close(fd);
398 blk->cs = base + header.cs;
399 blk->ip = header.ip;
400 blk->ss = base + header.ss;
401 blk->sp = header.sp - 6;
403 /* push return far address */
404 *(uint16_t *)seg_to_linear(blk->ss, blk->sp + 4) = psp;
406 return psp;
407 }
409 /* return the PSP or -1 if error */
410 int load_com(ExecParamBlock *blk, const char *filename, uint32_t *pfile_size,
411 int argc, char **argv)
412 {
413 int psp, fd, ret;
415 /* load the MSDOS .com executable */
416 fd = open(filename, O_RDONLY);
417 if (fd < 0)
418 fd = open(upcase(filename), O_RDONLY);
419 if (fd < 0) {
420 return -1;
421 }
422 psp = mem_malloc(65536 / 16, NULL);
423 ret = read(fd, seg_to_linear(psp, 0x100), 65536 - 0x100);
424 close(fd);
425 if (ret <= 0) {
426 mem_free(psp);
427 return -1;
428 }
429 if (pfile_size)
430 *pfile_size = ret;
432 /* reset the PSP */
433 memset(seg_to_linear(psp, 0), 0, 0x100);
435 * (uint16_t *) seg_to_linear(psp, 0) = 0x20CD; /* int $0x20 */
436 /* address of last segment allocated */
437 //*(uint16_t *)seg_to_linear(psp, 2) = psp + 0xfff;
438 *(uint16_t *)seg_to_linear(psp, 2) = 0x9fff;
440 if (argc) {
441 int i, p;
442 char *s;
443 /* set the command line */
444 p = 0x81;
445 for(i = 2; i < argc; i++) {
446 if (p >= 0xff)
447 break;
448 *seg_to_linear(psp, p++) = ' ';
449 s = argv[i];
450 while (*s) {
451 if (p >= 0xff)
452 break;
453 *seg_to_linear(psp, p++) = *s++;
454 }
455 }
456 *seg_to_linear(psp, p) = '\r';
457 *seg_to_linear(psp, 0x80) = p - 0x81;
458 }
459 else {
460 int len;
461 /* copy the command line */
462 len = *seg_to_linear(blk->cmdtail_seg, blk->cmdtail_off);
463 memcpy(seg_to_linear(psp, 0x80),
464 seg_to_linear(blk->cmdtail_seg, blk->cmdtail_off), len + 2);
465 }
467 blk->sp = 0xfffc;
468 blk->ip = 0x100;
469 blk->cs = blk->ss = psp;
471 if (*(uint16_t *)seg_to_linear(psp, 0x100) == 0x5A4D)
472 psp = load_exe(blk, filename, psp, pfile_size);
474 /* push ax value */
475 *(uint16_t *)seg_to_linear(blk->ss, blk->sp) = 0;
476 /* push return address to 0 */
477 *(uint16_t *)seg_to_linear(blk->ss, blk->sp + 2) = 0;
479 return psp;
480 }
483 void unsupported_function(struct vm86_regs *r, uint8_t num, uint8_t ah)
484 {
485 fprintf(stderr, "int 0x%02x: unsupported function 0x%02x\n", num, ah);
486 dump_regs(r);
487 set_error(r, 0x01); /* function number invalid */
488 }
490 /* Open hard disk image ./hd[0-7] / floppy image ./fd[0-7] or /dev/fd[0-7] */
491 int open_disk(struct vm86_regs *r)
492 {
493 int fd = -1, drive = r->edx & 0xff;
494 char filename[9], n = '0' + (drive & 7);
495 if (drive > 127) {
496 strcpy(filename,"hd0");
497 filename[2] = n;
498 }
499 else {
500 strcpy(filename,"/dev/fd0");
501 filename[7] = n;
502 fd = open(filename+5, O_RDONLY);
503 }
504 if (fd < 0)
505 fd = open(filename, O_RDONLY);
506 return fd;
507 }
510 void read_sectors(int fd, struct vm86_regs *r, int first_sector,
511 int sector_count, void *buffer)
512 {
513 int drive = r->edx & 0xff;
514 r->eax &= ~0xff00;
515 r->eax |= 0x0400; /* sector not found */
516 r->eflags |= CF_MASK;
517 if (fd >= 0) {
518 static struct stat st;
519 first_sector <<= 9;
520 sector_count <<= 9;
521 if (drive < 8 && fstat(fd, &st) == 0) {
522 static ino_t inodes[8];
523 ino_t last = inodes[drive];
524 inodes[drive] = st.st_ino;
525 if (last && last != st.st_ino) {
526 set_error(r, 0x0600); /* floppy disk swap */
527 goto failed;
528 }
529 }
530 if (lseek(fd, first_sector, SEEK_SET) >= 0 &&
531 read(fd, buffer, sector_count) == sector_count) {
532 r->eax &= ~0xff00;
533 r->eflags &= ~CF_MASK;
534 }
535 failed:
536 close(fd);
537 }
538 }
540 #define ESC "\033"
541 void do_int10(struct vm86_regs *r)
542 {
543 uint8_t ah;
544 char buf[20];
545 static unsigned cursorlines = 0x0607;
546 static unsigned activepage = 0;
547 static uint8_t cursrow, curscol;
549 ah = (r->eax >> 8);
550 switch(ah) {
551 case 0x02: /* set cursor position (BH == page number) */
552 cursrow = r->edx >> 8;
553 curscol = r->edx;
554 * (uint16_t *) seg_to_linear(0x40, 0x50 + 2*((r->ebx >> 8) & 0xFF)) = r->edx;
555 sprintf(buf,ESC"[%u;%uH",cursrow + 1, curscol + 1);
556 write(1, buf, strlen(buf));
557 break;
558 case 0x03: /* get cursor position (BH == page number) */
559 r->eax = 0;
560 r->ecx = cursorlines;
561 r->edx &= ~0xFFFF;
562 r->edx |= * (uint16_t *) seg_to_linear(0x40, 0x50 + 2*((r->ebx >> 8) & 0xFF));
563 sprintf(buf,ESC"[%u;%uH",cursrow + 1, curscol + 1);
564 write(1, buf, strlen(buf));
565 break;
566 case 0x05: /* set active page */
567 activepage = r->eax & 0xFF;
568 break;
569 case 0x06: /* scroll up */
570 case 0x07: /* scroll down */
571 {
572 int i = r->eax & 0xFF;
573 if (i == 0) i = 50;
574 /* FIXME assume full row, ignore colums in CL, DL */
575 sprintf(buf,ESC"[%u;%ur",1+(r->ecx >> 8) & 0xFF, 1+(r->edx >> 8) & 0xFF);
576 write(1, buf, strlen(buf));
577 buf[2] = (ah != 6) ? 'T' : 'S';
578 while (i--) write(1,buf,3);
579 }
580 break;
581 case 0x09: /* write char and attribute at cursor position (BH == page number) */
582 {
583 static char color[8] = "04261537";
584 char extra[5], *s = extra;
585 uint8_t c = r->eax;
586 uint16_t n = r->ecx;
587 int i;
589 if (r->ebx & 0x8) { *s++ = '1'; *s++ = ';'; } // bold
590 if (r->ebx & 0x80) { *s++ = '5'; *s++ = ';'; } // blink
591 *s = 0;
592 sprintf(buf,ESC"[0;%s4%c;3%cm",extra,
593 color[(r->ebx & 0x70) >> 4],color[r->ebx & 0x7]);
594 write(1, buf, strlen(buf));
595 for (i = 0; i < n; i++)
596 write(1, &c, 1);
597 write(1, ESC"[0m", 4); /* restore attributes */
598 }
599 break;
600 case 0x0E: /* write char */
601 {
602 uint8_t c = r->eax;
603 write(1, &c, 1);
604 }
605 break;
606 case 0x0F: /* get current video mode */
607 {
608 r->eax &= ~0xFFFF;
609 r->eax |= 0x5003; /* color or 5007 mono */
610 r->ebx &= ~0xFF00;
611 r->ebx |= activepage << 8;
612 }
613 break;
614 case 0x11: /* get window coordonates */
615 r->ecx &= ~0xFFFF;
616 r->edx &= ~0xFFFF;
617 r->edx |= ~0x1950; /* 80x25 */
618 break;
619 case 0x12: /* get blanking attribute (for scroll) */
620 r->ebx &= ~0xFF00;
621 break;
622 case 0x1A: /* get display combination code */
623 #if 0
624 set_error(r, 1);
625 #else
626 r->eax &= ~0xFF;
627 r->eax |= ~0x1A;
628 r->ebx &= ~0xFFFF;
629 r->ebx |= ~0x0202; // CGA + color display
630 #endif
631 break;
632 default:
633 unsupported_function(r, 0x10, ah);
634 }
635 }
637 void do_int13(struct vm86_regs *r)
638 {
639 uint8_t ah;
641 ah = (r->eax >> 8);
642 switch(ah) {
643 case 0x00: /* reset disk */
644 {
645 r->eax &= ~0xff00; /* success */
646 r->eflags &= ~CF_MASK;
647 }
648 break;
649 case 0x02: /* read disk CHS */
650 {
651 int fd, c, h, s, heads, sectors, cylinders;
652 long size;
653 fd = open_disk(r);
654 if (fd >= 0) {
655 size = lseek(fd, 0, SEEK_END) / 512;
656 if ((r->edx & 0xff) > 127) {
657 sectors = 63;
658 if (size % sectors)
659 sectors = 62;
660 if (size % sectors)
661 sectors = 32;
662 if (size % sectors)
663 sectors = 17;
664 if (size % sectors)
665 fd = -1;
666 size /= sectors;
667 for (heads = 256; size % heads; heads--);
668 cylinders = size / heads;
669 }
670 else {
671 int i;
672 heads = 1 + (size > 256*2);
673 cylinders = 40 * (1 + (size > 512*2));
674 size /= heads;
675 for (i = 0; i < 5; i++)
676 if (size % (cylinders + i) == 0) break;
677 if (i == 5)
678 fd = -1;
679 cylinders += i;
680 sectors = size / cylinders;
681 }
682 }
683 c = ((r->ecx & 0xC0) << 2) | ((r->ecx >> 8) & 0xff);
684 h = (r->edx >> 8) & 0xff;
685 s = (r->ecx & 0x3f) -1;
686 if (fd < 0 || c >= cylinders || h >= heads || s >= sectors) {
687 set_error(r, 0x0400); /* sector not found */
688 break;
689 }
690 read_sectors(fd, r, (((c * heads) + h) * sectors) + s,
691 r->eax & 0xff, seg_to_linear(r->es, r->ebx));
692 }
693 break;
694 case 0x42: /* read disk LBA */
695 {
696 uint16_t *packet = (uint16_t *) seg_to_linear(r->ds, r-> esi);
697 uint8_t *to = seg_to_linear(packet[3], packet[2]);
698 if ((packet[3] & packet[2]) == 0xffff)
699 to = * (uint8_t **) &packet[8];
700 if (packet[0] != 0x0010 && packet[0] != 0x0018)
701 goto unsupported;
702 read_sectors(open_disk(r), r, * (uint32_t *) &packet[4], packet[1], to);
703 }
704 break;
705 default:
706 unsupported:
707 unsupported_function(r, 0x13, ah);
708 }
709 }
711 void do_int15(struct vm86_regs *r)
712 {
713 uint8_t ah;
715 ah = (r->eax >> 8);
716 switch(ah) {
717 case 0x87: /* move memory */
718 /* XXX */
719 break;
720 default:
721 unsupported_function(r, 0x15, ah);
722 }
723 }
725 void do_int16(struct vm86_regs *r)
726 {
727 static uint16_t last_ax, hold_char;
728 struct termios termios_def, termios_raw;
729 uint8_t ah;
731 ah = (r->eax >> 8);
732 tcgetattr(0, &termios_def);
733 termios_raw = termios_def;
734 cfmakeraw(&termios_raw);
735 tcsetattr(0, TCSADRAIN, &termios_raw);
736 switch(ah) {
737 case 0x01: /* test keyboard */
738 {
739 int count;
740 r->eflags &= ~ZF_MASK;
741 if (hold_char) {
742 r->eax &= ~0xffff;
743 r->eax |= last_ax;
744 break;
745 }
746 if (ioctl(0, FIONREAD, &count) < 0 || count == 0) {
747 r->eflags |= ZF_MASK;
748 break;
749 }
750 hold_char = 2;
751 }
752 case 0x00: /* read keyboard */
753 {
754 uint8_t c;
755 if (hold_char)
756 hold_char--;
757 read(0, &c, 1);
758 if (c == 3) {
759 tcsetattr(0, TCSADRAIN, &termios_def);
760 exit(0);
761 }
762 if (c == 10)
763 c = 13;
764 r->eax &= ~0xffff;
765 r->eax |= last_ax = c;
766 /* XXX ah = scan code */
767 }
768 break;
769 default:
770 unsupported_function(r, 0x16, ah);
771 }
772 tcsetattr(0, TCSADRAIN, &termios_def);
773 }
775 void do_int1a(struct vm86_regs *r)
776 {
777 uint8_t ah;
779 ah = (r->eax >> 8);
780 switch(ah) {
781 case 0x00: /* GET SYSTEM TIME */
782 {
783 uint16_t *timer = (uint16_t *) seg_to_linear(0, 0x46C);
784 r->ecx &= ~0xffff;
785 r->ecx |= *timer++;
786 r->edx &= ~0xffff;
787 r->edx |= *timer;
788 r->eax &= ~0xff;
789 }
790 break;
791 default:
792 unsupported_function(r, 0x1a, ah);
793 }
794 }
796 void do_int20(struct vm86_regs *r)
797 {
798 /* terminate program */
799 exit(0);
800 }
802 void do_int21(struct vm86_regs *r)
803 {
804 uint8_t ah;
805 DIR *dirp;
806 dirdta *dta;
808 ah = (r->eax >> 8);
809 switch(ah) {
810 case 0x00: /* exit */
811 exit(0);
812 case 0x02: /* write char */
813 {
814 uint8_t c = r->edx;
815 write(1, &c, 1);
816 }
817 break;
818 case 0x08: /* read stdin */
819 {
820 read(0,&r->eax,1);
821 }
822 break;
823 case 0x09: /* write string */
824 {
825 uint8_t c;
826 int offset;
827 offset = r->edx;
828 for(;;) {
829 c = *seg_to_linear(r->ds, offset);
830 if (c == '$')
831 break;
832 write(1, &c, 1);
833 offset++;
834 }
835 r->eax = (r->eax & ~0xff) | '$';
836 }
837 break;
838 case 0x0a: /* buffered input */
839 {
840 int max_len, cur_len, ret;
841 uint8_t ch;
842 uint16_t off;
844 /* XXX: should use raw mode to avoid sending the CRLF to
845 the terminal */
846 off = r->edx & 0xffff;
847 max_len = *seg_to_linear(r->ds, off);
848 cur_len = 0;
849 while (cur_len < max_len) {
850 ret = read(0, &ch, 1);
851 if (ret < 0) {
852 if (errno != EINTR && errno != EAGAIN)
853 break;
854 } else if (ret == 0) {
855 break;
856 } else {
857 if (ch == '\n')
858 break;
859 }
860 *seg_to_linear(r->ds, off + 2 + cur_len++) = ch;
861 }
862 *seg_to_linear(r->ds, off + 1) = cur_len;
863 *seg_to_linear(r->ds, off + 2 + cur_len) = '\r';
864 }
865 break;
866 case 0x0b: /* get stdin status */
867 {
868 r->eax &= ~0xFF; /* no character available */
869 }
870 break;
871 case 0x0d: /* disk reset */
872 {
873 sync();
874 }
875 break;
876 case 0x0e: /* select default disk */
877 {
878 r->eax &= ~0xFF;
879 r->eax |= 3; /* A: B: & C: valid */
880 }
881 break;
882 case 0x19: /* get current default drive */
883 {
884 r->eax &= ~0xFF;
885 r->eax |= 2; /* C: */
886 }
887 break;
888 case 0x1a: /* set DTA (disk transfert address) */
889 {
890 cur_dta_seg = r->ds;
891 cur_dta_ofs = r->edx;
892 }
893 break;
894 case 0x25: /* set interrupt vector */
895 {
896 uint16_t *ptr;
897 ptr = (uint16_t *)seg_to_linear(0, (r->eax & 0xff) * 4);
898 ptr[0] = r->edx;
899 ptr[1] = r->ds;
900 }
901 break;
902 case 0x29: /* parse filename into FCB */
903 #if 0
904 /* not really needed */
905 {
906 const uint8_t *p, *p_start;
907 uint8_t file[8], ext[3];
908 FCB *fcb;
909 int file_len, ext_len, has_wildchars, c, drive_num;
911 /* XXX: not complete at all */
912 fcb = (FCB *)seg_to_linear(r->es, r->edi);
913 printf("ds=0x%x si=0x%lx\n", r->ds, r->esi);
914 p_start = (const uint8_t *)seg_to_linear(r->ds, r->esi);
916 p = p_start;
917 has_wildchars = 0;
919 /* drive */
920 if (isalpha(p[0]) && p[1] == ':') {
921 drive_num = toupper(p[0]) - 'A' + 1;
922 p += 2;
923 } else {
924 drive_num = 0;
925 }
927 /* filename */
928 file_len = 0;
929 for(;;) {
930 c = *p;
931 if (!(c >= 33 && c <= 126))
932 break;
933 if (c == '.')
934 break;
935 if (c == '*' || c == '?')
936 has_wildchars = 1;
937 if (file_len < 8)
938 file[file_len++] = c;
939 }
940 memset(file + file_len, ' ', 8 - file_len);
942 /* extension */
943 ext_len = 0;
944 if (*p == '.') {
945 for(;;) {
946 c = *p;
947 if (!(c >= 33 && c <= 126))
948 break;
949 if (c == '*' || c == '?')
950 has_wildchars = 1;
951 ext[ext_len++] = c;
952 if (ext_len >= 3)
953 break;
954 }
955 }
956 memset(ext + ext_len, ' ', 3 - ext_len);
958 #if 0
959 {
960 printf("drive=%d file=%8s ext=%3s\n",
961 drive_num, file, ext);
962 }
963 #endif
964 if (drive_num == 0 && r->eax & (1 << 1)) {
965 /* keep drive */
966 } else {
967 fcb->drive_num = drive_num; /* default drive */
968 }
970 if (file_len == 0 && r->eax & (1 << 2)) {
971 /* keep */
972 } else {
973 memcpy(fcb->file_name, file, 8);
974 }
976 if (ext_len == 0 && r->eax & (1 << 3)) {
977 /* keep */
978 } else {
979 memcpy(fcb->file_ext, ext, 3);
980 }
981 r->eax = (r->eax & ~0xff) | has_wildchars;
982 r->esi = (r->esi & ~0xffff) | ((r->esi + (p - p_start)) & 0xffff);
983 }
984 #endif
985 break;
986 case 0x2A: /* get system date */
987 {
988 time_t t = time(NULL);
989 struct tm *now=localtime(&t);
991 r->ecx = now->tm_year;
992 r->edx = (now->tm_mon * 256) + now->tm_mday;
993 r->eax = now->tm_wday;;
994 }
995 break;
996 case 0x2C: /* get system time */
997 {
998 time_t t = time(NULL);
999 struct tm *now=localtime(&t);
1000 struct timeval tim;
1002 gettimeofday(&tim, NULL);
1003 r->edx = (now->tm_hour * 256) + now->tm_min;
1004 r->edx = (tim.tv_sec * 256) + tim.tv_usec/10000;
1005 }
1006 break;
1007 case 0x2f: /* get DTA (disk transfert address */
1008 {
1009 r->es = cur_dta_seg;
1010 r->ebx = cur_dta_ofs;
1011 }
1012 break;
1013 case 0x30: /* get dos version */
1014 {
1015 int major, minor, serial, oem;
1016 /* XXX: return correct value for FreeDOS */
1017 major = 0x03;
1018 minor = 0x31;
1019 serial = 0x123456;
1020 oem = 0x66;
1021 r->eax = (r->eax & ~0xffff) | major | (minor << 8);
1022 r->ecx = (r->ecx & ~0xffff) | (serial & 0xffff);
1023 r->ebx = (r->ebx & ~0xffff) | (serial & 0xff) | (0x66 << 8);
1024 }
1025 break;
1026 case 0x33: /* extended break checking */
1027 {
1028 r->edx &= ~0xFFFF;
1029 }
1030 break;
1031 case 0x35: /* get interrupt vector */
1032 {
1033 uint16_t *ptr;
1034 ptr = (uint16_t *)seg_to_linear(0, (r->eax & 0xff) * 4);
1035 r->ebx = (r->ebx & ~0xffff) | ptr[0];
1036 r->es = ptr[1];
1037 }
1038 break;
1039 case 0x36: /* get free disk space */
1040 {
1041 struct statfs buf;
1043 if (statfs(".", &buf)) {
1044 r->eax |= 0xFFFF;
1045 }
1046 else {
1047 r->eax &= ~0xFFFF;
1048 r->eax |= buf.f_bsize / 512; /* sectors per cluster */
1049 r->ebx &= ~0xFFFF;
1050 r->ebx |= buf.f_bavail;
1051 r->ecx &= ~0xFFFF;
1052 r->ecx |= 512; /* bytes per sector */
1053 r->edx &= ~0xFFFF;
1054 r->edx |= buf.f_blocks;
1055 }
1056 }
1057 break;
1058 case 0x37:
1059 {
1060 switch(r->eax & 0xff) {
1061 case 0x00: /* get switch char */
1062 r->eax = (r->eax & ~0xff) | 0x00;
1063 r->edx = (r->edx & ~0xff) | '/';
1064 break;
1065 default:
1066 goto unsupported;
1067 }
1068 }
1069 break;
1070 case 0x3B:
1071 {
1072 char filename[1024];
1074 get_filename(r, filename, sizeof(filename));
1075 if (chdir(filename))
1076 set_error(r, 0x03); /* path not found */
1077 }
1078 break;
1079 case 0x3c: /* create or truncate file */
1080 {
1081 char filename[1024];
1082 int fd, h, flags;
1084 h = get_new_handle();
1085 if (h < 0) {
1086 set_error(r, 0x04); /* too many open files */
1087 } else {
1088 get_filename(r, filename, sizeof(filename));
1089 if (r->ecx & 1)
1090 flags = 0444; /* read-only */
1091 else
1092 flags = 0777;
1093 fd = open(filename, O_RDWR | O_TRUNC | O_CREAT, flags);
1094 if (fd < 0)
1095 fd = open(upcase(filename), O_RDWR | O_TRUNC | O_CREAT, flags);
1096 #ifdef DUMP_INT21
1097 printf("int21: create: file='%s' cx=0x%04x ret=%d\n",
1098 filename, (int)(r->ecx & 0xffff), h);
1099 #endif
1100 if (fd < 0) {
1101 set_error(r, 0x03); /* path not found */
1102 } else {
1103 dos_files[h].fd = fd;
1104 set_error(r, 0);
1105 r->eax = (r->eax & ~0xffff) | h;
1106 }
1107 }
1108 }
1109 break;
1110 case 0x3d: /* open file */
1111 {
1112 char filename[1024];
1113 int fd, h;
1115 h = get_new_handle();
1116 if (h < 0) {
1117 set_error(r, 0x04); /* too many open files */
1118 } else {
1119 get_filename(r, filename, sizeof(filename));
1120 fd = open(filename, r->eax & 3);
1121 if (fd < 1)
1122 fd = open(upcase(filename), r->eax & 3);
1123 if (fd < 0) {
1124 set_error(r, 0x02); /* file not found */
1125 } else {
1126 dos_files[h].fd = fd;
1127 set_error(r, 0);
1128 r->eax = (r->eax & ~0xffff) | h;
1129 }
1130 }
1131 }
1132 break;
1133 case 0x3e: /* close file */
1134 {
1135 DOSFile *fh = get_file(r->ebx & 0xffff);
1136 #ifdef DUMP_INT21
1137 printf("int21: close fd=%d\n", (int)(r->ebx & 0xffff));
1138 #endif
1139 if (!fh) {
1140 set_error(r, 0x06); /* invalid handle */
1141 } else {
1142 close(fh->fd);
1143 fh->fd = -1;
1144 set_error(r, 0);
1145 }
1146 }
1147 break;
1148 case 0x3f: /* read */
1149 {
1150 DOSFile *fh = get_file(r->ebx & 0xffff);
1151 int n, ret;
1153 if (!fh) {
1154 set_error(r, 0x06); /* invalid handle */
1155 } else {
1156 n = r->ecx & 0xffff;
1157 for(;;) {
1158 ret = read(fh->fd,
1159 seg_to_linear(r->ds, r->edx), n);
1160 if (ret < 0) {
1161 if (errno != EINTR && errno != EAGAIN)
1162 break;
1163 } else {
1164 break;
1165 }
1166 }
1167 #ifdef DUMP_INT21
1168 printf("int21: read: fd=%d n=%d ret=%d\n",
1169 (int)(r->ebx & 0xffff), n, ret);
1170 #endif
1171 if (ret < 0) {
1172 set_error(r, 0x05); /* acces denied */
1173 } else {
1174 r->eax = (r->eax & ~0xffff) | ret;
1175 set_error(r, 0);
1176 }
1177 }
1178 }
1179 break;
1180 case 0x40: /* write */
1181 {
1182 DOSFile *fh = get_file(r->ebx & 0xffff);
1183 int n, ret, pos;
1185 if (!fh) {
1186 set_error(r, 0x06); /* invalid handle */
1187 } else {
1188 n = r->ecx & 0xffff;
1189 if (n == 0) {
1190 /* truncate */
1191 pos = lseek(fh->fd, 0, SEEK_CUR);
1192 if (pos >= 0) {
1193 ret = ftruncate(fh->fd, pos);
1194 } else {
1195 ret = -1;
1196 }
1197 } else {
1198 for(;;) {
1199 ret = write(fh->fd,
1200 seg_to_linear(r->ds, r->edx), n);
1201 if (ret < 0) {
1202 if (errno != EINTR && errno != EAGAIN)
1203 break;
1204 } else {
1205 break;
1206 }
1207 }
1208 }
1209 #ifdef DUMP_INT21
1210 printf("int21: write: fd=%d n=%d ret=%d\n",
1211 (int)(r->ebx & 0xffff), n, ret);
1212 #endif
1213 if (ret < 0) {
1214 set_error(r, 0x05); /* acces denied */
1215 } else {
1216 r->eax = (r->eax & ~0xffff) | ret;
1217 set_error(r, 0);
1218 }
1219 }
1220 }
1221 break;
1222 case 0x41: /* unlink */
1223 {
1224 char filename[1024];
1225 get_filename(r, filename, sizeof(filename));
1226 if (unlink(filename) < 0 && unlink(upcase(filename))) {
1227 set_error(r, 0x02); /* file not found */
1228 } else {
1229 set_error(r, 0);
1230 }
1231 }
1232 break;
1233 case 0x42: /* lseek */
1234 {
1235 DOSFile *fh = get_file(r->ebx & 0xffff);
1236 int pos, ret;
1238 if (!fh) {
1239 set_error(r, 0x06); /* invalid handle */
1240 } else {
1241 pos = ((r->ecx & 0xffff) << 16) | (r->edx & 0xffff);
1242 ret = lseek(fh->fd, pos, r->eax & 0xff);
1243 #ifdef DUMP_INT21
1244 printf("int21: lseek: fd=%d pos=%d whence=%d ret=%d\n",
1245 (int)(r->ebx & 0xffff), pos, (uint8_t)r->eax, ret);
1246 #endif
1247 if (ret < 0) {
1248 set_error(r, 0x01); /* function number invalid */
1249 } else {
1250 r->edx = (r->edx & ~0xffff) | ((unsigned)ret >> 16);
1251 r->eax = (r->eax & ~0xffff) | (ret & 0xffff);
1252 set_error(r, 0);
1253 }
1254 }
1255 }
1256 break;
1257 case 0x43: /* get attribute */
1258 {
1259 struct stat statbuf;
1260 char filename[1024];
1261 get_filename(r, filename, sizeof(filename));
1262 if (stat(filename, &statbuf) && stat(upcase(filename), &statbuf)) {
1263 set_error(r, 5);
1264 }
1265 else {
1266 r->ecx &= ~0xFFFF;
1267 if (S_ISDIR(statbuf.st_mode)) r->ecx |= 0x10;
1268 }
1269 }
1270 break;
1271 case 0x44: /* ioctl */
1272 switch(r->eax & 0xff) {
1273 case 0x00: /* get device information */
1274 {
1275 DOSFile *fh = get_file(r->ebx & 0xffff);
1276 int ret;
1278 if (!fh) {
1279 set_error(r, 0x06); /* invalid handle */
1280 } else {
1281 ret = 0;
1282 if (isatty(fh->fd)) {
1283 ret |= 0x80;
1284 if (fh->fd == 0)
1285 ret |= (1 << 0);
1286 else
1287 ret |= (1 << 1);
1288 }
1289 r->edx = (r->edx & ~0xffff) | ret;
1290 set_error(r, 0);
1291 }
1292 }
1293 case 0x01: /* set device information */
1294 break;
1295 default:
1296 goto unsupported;
1297 }
1298 break;
1299 case 0x47: /* get current directory (DL drive)*/
1300 {
1301 char *s = seg_to_linear(r->ds, r->esi);
1302 getcwd(s,64);
1303 strcpy(s,s+1);
1304 while (*s)
1305 if (*s++ == '/')
1306 s[-1] = '\\';
1307 r->eax = 0x100;
1308 }
1309 break;
1310 case 0x48: /* allocate memory */
1311 {
1312 int ret, max_size;
1313 #ifdef DUMP_INT21
1314 printf("int21: allocate memory: size=0x%04x\n", (uint16_t)r->ebx);
1315 #endif
1316 ret = mem_malloc(r->ebx & 0xffff, &max_size);
1317 if (ret < 0) {
1318 set_error(r, 0x08); /* insufficient memory*/
1319 } else {
1320 r->eax = (r->eax & ~0xffff) | ret;
1321 r->ebx = (r->ebx & ~0xffff) | max_size;
1322 set_error(r, 0);
1323 }
1324 }
1325 break;
1326 case 0x49: /* free memory */
1327 {
1328 #ifdef DUMP_INT21
1329 printf("int21: free memory: block=0x%04x\n", r->es);
1330 #endif
1331 if (mem_free(r->es) < 0) {
1332 set_error(r, 0x09); /* memory block address invalid */
1333 } else {
1334 set_error(r, 0);
1335 }
1336 }
1337 break;
1338 case 0x4a: /* resize memory block */
1339 {
1340 int ret;
1341 #ifdef DUMP_INT21
1342 printf("int21: resize memory block: block=0x%04x size=0x%04x\n",
1343 r->es, (uint16_t)r->ebx);
1344 #endif
1345 ret = mem_resize(r->es, r->ebx & 0xffff);
1346 if (ret < 0) {
1347 set_error(r, 0x08); /* insufficient memory*/
1348 } else {
1349 r->ebx = (r->ebx & ~0xffff) | ret;
1350 set_error(r, 0);
1351 }
1352 }
1353 break;
1354 case 0x4b: /* load program */
1355 {
1356 char filename[1024];
1357 ExecParamBlock *blk;
1358 int ret;
1360 if ((r->eax & 0xff) != 0x01) /* only load */
1361 goto unsupported;
1362 get_filename(r, filename, sizeof(filename));
1363 blk = (ExecParamBlock *)seg_to_linear(r->es, r->ebx);
1364 ret = load_com(blk, filename, NULL, 0, NULL);
1365 if (ret < 0)
1366 ret = load_com(blk, upcase(filename), NULL, 0, NULL);
1367 if (ret < 0) {
1368 set_error(r, 0x02); /* file not found */
1369 } else {
1370 cur_dta_seg = cur_psp = ret;
1371 cur_dta_ofs = 0x80;
1372 set_error(r, 0);
1373 }
1374 }
1375 break;
1376 case 0x4c: /* exit with return code */
1377 exit(r->eax & 0xff);
1378 break;
1379 case 0x4e: /* find first matching file */
1380 // TODO AL input support
1381 dirp = opendir(".");
1382 if (dirp == NULL) {
1383 set_error(r, (errno == ENOTDIR) ? 0x03 /* path not found */
1384 : 0x02 /* file not found */ );
1385 goto pattern_found;
1386 }
1387 else {
1388 struct dirent *dp;
1389 char *s;
1391 dta = (dirdta *) seg_to_linear(cur_dta_seg, cur_dta_ofs);
1392 dta->attr = r->ecx;
1393 * (DIR **) &dta->entry_count = dirp;
1394 s = seg_to_linear(r->ds, r->edx);
1395 if (s[1] == ':') s+= 2;
1396 if (s[0] == '\\') s++;
1397 strncpy(dta->template, s, 11);
1398 // NO break;
1399 case 0x4f: /* find next matching file */
1400 dta = (dirdta *) seg_to_linear(cur_dta_seg, cur_dta_ofs);
1401 dirp = * (DIR **) &dta->entry_count;
1402 while ((dp = readdir(dirp)) != NULL) {
1403 if (!fnmatch(dta->template, dp->d_name, 0)) {
1404 struct stat statbuf;
1406 r->eflags &= ~CF_MASK;
1407 strncpy(dta->filename, dp->d_name, 13);
1408 stat(dp->d_name, &statbuf);
1409 dta->file_size = statbuf.st_size;
1410 dta->file_date = 0; //DOSDATE(statbuf.st_mtime);
1411 dta->file_time = 0; //DOSIME(statbuf.st_mtime);
1412 dta->attr_found = S_ISDIR(statbuf.st_mode) ?
1413 0x10 /*aDvshr*/ : 0x20 /*Advshr*/;
1414 #if 0
1415 if ((dta->attr_found ^ dta->attr) & 0x16)
1416 continue;
1417 #endif
1418 goto pattern_found;
1419 }
1420 }
1421 }
1422 closedir(dirp);
1423 set_error(r, 0x12 /* no more files */);
1424 pattern_found:
1425 break;
1426 case 0x50: /* set PSP address */
1427 #ifdef DUMP_INT21
1428 printf("int21: set PSP: 0x%04x\n", (uint16_t)r->ebx);
1429 #endif
1430 cur_psp = r->ebx;
1431 break;
1432 case 0x51: /* get PSP address */
1433 #ifdef DUMP_INT21
1434 printf("int21: get PSP: ret=0x%04x\n", cur_psp);
1435 #endif
1436 r->ebx = (r->ebx & ~0xffff) | cur_psp;
1437 break;
1438 case 0x55: /* create child PSP */
1439 {
1440 uint8_t *psp_ptr;
1441 #ifdef DUMP_INT21
1442 printf("int21: create child PSP: psp=0x%04x last_seg=0x%04x\n",
1443 (uint16_t)r->edx, (uint16_t)r->esi);
1444 #endif
1445 psp_ptr = seg_to_linear(r->edx & 0xffff, 0);
1446 memset(psp_ptr, 0, 0x80);
1447 psp_ptr[0] = 0xcd; /* int $0x20 */
1448 psp_ptr[1] = 0x20;
1449 *(uint16_t *)(psp_ptr + 2) = r->esi;
1450 r->eax = (r->eax & ~0xff);
1451 }
1452 break;
1453 case 0x56: /* rename file (CL attribute mask) */
1454 if (rename((char *) seg_to_linear(r->ds, r->edx),
1455 (char *) seg_to_linear(r->es, r->edi)))
1456 set_error(r, 0x5 /* access denied or 2,3,0x11 */);
1457 break;
1458 default:
1459 unsupported:
1460 unsupported_function(r, 0x21, ah);
1461 }
1462 }
1464 void do_int29(struct vm86_regs *r)
1465 {
1466 uint8_t c = r->eax;
1467 write(1, &c, 1);
1468 }
1470 static int int8pending;
1472 void raise_interrupt(int number)
1473 {
1474 if (* (uint32_t *) seg_to_linear(0, number * 4) == 0)
1475 return;
1476 int8pending++;
1477 }
1479 void biosclock()
1480 {
1481 //uint32_t *timer = (uint32_t *) seg_to_linear(0, 0x46C);
1482 //++*timer;
1483 raise_interrupt(8);
1484 //raise_interrupt(0x1C);
1485 }
1487 static void exec_int(struct vm86_regs *r, unsigned num)
1488 {
1489 uint16_t *int_vector;
1490 uint32_t eflags;
1492 eflags = r->eflags & ~IF_MASK;
1493 if (r->eflags & VIF_MASK)
1494 eflags |= IF_MASK;
1495 pushw(r, eflags);
1496 pushw(r, r->cs);
1497 pushw(r, r->eip);
1498 int_vector = (uint16_t *)seg_to_linear(0, num * 4);
1499 r->eip = int_vector[0];
1500 r->cs = int_vector[1];
1501 r->eflags &= ~(VIF_MASK | TF_MASK | AC_MASK);
1502 }
1504 int main(int argc, char **argv)
1505 {
1506 uint8_t *vm86_mem;
1507 const char *filename;
1508 int i, ret;
1509 uint32_t file_size;
1510 struct sigaction sa;
1511 struct itimerval timerval;
1512 struct vm86plus_struct ctx;
1513 struct vm86_regs *r;
1514 ExecParamBlock blk1, *blk = &blk1;
1516 for (argflags = 0; *argv[1] == '-'; argv++) {
1517 char *s = argv[1];
1519 while (1)
1520 switch (*++s) {
1521 case 'd' : argflags |= DEBUG; break;
1522 case 0 : goto nextargv;
1523 }
1524 nextargv:;
1525 }
1526 if (argc < 2)
1527 usage();
1528 filename = argv[1];
1530 vm86_mem = mmap((void *)0x00000000, 0x110000,
1531 PROT_WRITE | PROT_READ | PROT_EXEC,
1532 MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
1533 if (vm86_mem == MAP_FAILED) {
1534 perror("mmap");
1535 exit(1);
1536 }
1538 memset(&ctx, 0, sizeof(ctx));
1539 r = &ctx.regs;
1540 set_bit((uint8_t *)&ctx.int_revectored, 0x10);
1541 set_bit((uint8_t *)&ctx.int_revectored, 0x13);
1542 set_bit((uint8_t *)&ctx.int_revectored, 0x15);
1543 set_bit((uint8_t *)&ctx.int_revectored, 0x16);
1544 set_bit((uint8_t *)&ctx.int_revectored, 0x1a);
1545 set_bit((uint8_t *)&ctx.int_revectored, 0x20);
1546 set_bit((uint8_t *)&ctx.int_revectored, 0x21);
1547 set_bit((uint8_t *)&ctx.int_revectored, 0x29);
1549 dos_init();
1551 if (strstr(filename,".com") || strstr(filename,".exe") ||
1552 strstr(filename,".COM") || strstr(filename,".EXE")) {
1553 ret = load_com(blk, filename, &file_size, argc, argv);
1554 if (ret < 0) {
1555 perror(filename);
1556 exit(1);
1557 }
1558 cur_dta_seg = cur_psp = ret;
1559 cur_dta_ofs = 0x80;
1561 /* init basic registers */
1562 r->eip = blk->ip;
1563 r->esp = blk->sp + 2; /* pop ax value */
1564 r->cs = blk->cs;
1565 r->ss = blk->ss;
1566 r->ds = cur_psp;
1567 r->es = cur_psp;
1568 r->eflags = VIF_MASK;
1570 /* the value of these registers seem to be assumed by pi_10.com */
1571 r->esi = 0x100;
1572 #if 0
1573 r->ebx = file_size >> 16;
1574 r->ecx = file_size & 0xffff;
1575 #else
1576 r->ecx = 0xff;
1577 #endif
1578 r->ebp = 0x0900;
1579 r->edi = 0xfffe;
1580 }
1581 else {
1582 if (load_boot(filename, r) < 0) {
1583 if (errno)
1584 perror(filename);
1585 exit(1);
1586 }
1587 }
1589 sa.sa_handler = biosclock;
1590 sigemptyset(&sa.sa_mask);
1591 sa.sa_flags = SA_RESTART;
1592 if (sigaction(SIGALRM, &sa, 0) == 0) {
1593 timerval.it_interval.tv_sec = timerval.it_value.tv_sec = 0;
1594 timerval.it_interval.tv_usec = timerval.it_value.tv_usec = 10000000 / 182;
1595 setitimer (ITIMER_REAL, &timerval, NULL);
1596 }
1597 *(uint8_t *)seg_to_linear(0xF000, 0) = 0xCF;
1598 for (i = 0; i < 16; i++)
1599 *(uint32_t *)seg_to_linear(0, i * 4) = 0xF0000000;
1600 *(uint32_t *)seg_to_linear(0, 0x18 * 4) = 0xF0000000; /* Basic */
1601 *(uint32_t *)seg_to_linear(0, 0x1B * 4) = 0xF0000000; /* Keyboard Ctrl-Break */
1602 *(uint32_t *)seg_to_linear(0, 0x23 * 4) = 0xF0000000; /* DOS Ctrl-Break */
1603 *(uint32_t *)seg_to_linear(0, 0x24 * 4) = 0xF0000000; /* Critical error */
1605 for(;;) {
1606 ret = vm86(VM86_ENTER, &ctx);
1607 switch(VM86_TYPE(ret)) {
1608 case VM86_INTx:
1609 {
1610 int int_num;
1612 int_num = VM86_ARG(ret);
1613 if (argflags & 1)
1614 fprintf(stderr,"Int%02X: CS:IP=%04X:%04X AX=%04X\n",
1615 int_num, r->cs, r->eip, r->eax);
1616 switch(int_num) {
1617 case 0x10:
1618 do_int10(r);
1619 break;
1620 case 0x13:
1621 do_int13(r);
1622 break;
1623 case 0x15:
1624 do_int15(r);
1625 break;
1626 case 0x16:
1627 do_int16(r);
1628 break;
1629 case 0x1a:
1630 do_int1a(r);
1631 break;
1632 case 0x20:
1633 do_int20(r);
1634 break;
1635 case 0x21:
1636 do_int21(r);
1637 break;
1638 case 0x29:
1639 do_int29(r);
1640 break;
1641 default:
1642 fprintf(stderr, "unsupported int 0x%02x\n", int_num);
1643 dump_regs(&ctx.regs);
1644 break;
1645 }
1646 }
1647 break;
1648 case VM86_SIGNAL:
1649 /* a signal came, we just ignore that */
1650 if (int8pending) {
1651 int8pending--;
1652 exec_int(r, 8);
1653 }
1654 break;
1655 case VM86_STI:
1656 break;
1657 case VM86_TRAP:
1658 /* just executes the interruption */
1659 exec_int(r, VM86_ARG(ret));
1660 break;
1661 case VM86_UNKNOWN:
1662 switch ( *(uint8_t *)seg_to_linear(r->cs, r->eip) ) {
1663 case 0xE4: /* inx portb,al */
1664 case 0xE5: /* in portb,ax */
1665 case 0xE6: /* out al,portb */
1666 case 0xE7: /* out ax,portb */
1667 r->eip += 2;
1668 continue;
1669 case 0xEC: /* in dx,al */
1670 case 0xED: /* in dx,ax */
1671 case 0xEE: /* out al,dx */
1672 case 0xEF: /* out ax,dx */
1673 r->eip++;
1674 continue;
1675 }
1676 default:
1677 fprintf(stderr, "unhandled vm86 return code (0x%x)\n", ret);
1678 dump_regs(&ctx.regs);
1679 exit(1);
1680 }
1681 }
1682 }