wok-current view runcom/stuff/runcom.c @ rev 12725
fox14: update bdeps
author | Pascal Bellard <pascal.bellard@slitaz.org> |
---|---|
date | Sat May 05 13:03:47 2012 +0200 (2012-05-05) |
parents | 289a2f495cd6 |
children | fd625ee963af |
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 <signal.h>
17 #include <errno.h>
18 #include <ctype.h>
19 #include <termios.h>
21 #include <sys/syscall.h>
22 #include <asm/vm86.h>
24 //#define DUMP_INT21
26 static inline int vm86(int func, struct vm86plus_struct *v86)
27 {
28 return syscall(__NR_vm86, func, v86);
29 }
31 #define CF_MASK 0x00000001
32 #define ZF_MASK 0x00000040
33 #define TF_MASK 0x00000100
34 #define IF_MASK 0x00000200
35 #define DF_MASK 0x00000400
36 #define IOPL_MASK 0x00003000
37 #define NT_MASK 0x00004000
38 #define RF_MASK 0x00010000
39 #define VM_MASK 0x00020000
40 #define AC_MASK 0x00040000
41 #define VIF_MASK 0x00080000
42 #define VIP_MASK 0x00100000
43 #define ID_MASK 0x00200000
45 void usage(void)
46 {
47 printf("runcom version 0.2-slitaz (c) 2003-2011 Fabrice Bellard\n"
48 "usage: runcom file.com [args...]\n"
49 "Run simple .com DOS executables (linux vm86 test mode)\n");
50 exit(1);
51 }
53 static inline void set_bit(uint8_t *a, unsigned int bit)
54 {
55 a[bit / 8] |= (1 << (bit % 8));
56 }
58 static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg)
59 {
60 return (uint8_t *)((seg << 4) + (reg & 0xffff));
61 }
63 static inline void pushw(struct vm86_regs *r, int val)
64 {
65 r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff);
66 *(uint16_t *)seg_to_linear(r->ss, r->esp) = val;
67 }
69 void dump_regs(struct vm86_regs *r)
70 {
71 int i;
72 uint8_t *p8;
73 uint16_t *p16;
74 fprintf(stderr,
75 "EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n"
76 "ESI=%08lx EDI=%08lx EBP=%08lx ESP=%08lx\n"
77 "EIP=%08lx EFL=%08lx "
78 "CS=%04x DS=%04x ES=%04x SS=%04x FS=%04x GS=%04x\n[SP]",
79 r->eax, r->ebx, r->ecx, r->edx, r->esi, r->edi, r->ebp, r->esp,
80 r->eip, r->eflags,
81 r->cs, r->ds, r->es, r->ss, r->fs, r->gs);
82 for (p16 = (uint16_t *) seg_to_linear(r->ss, r->esp), i = 0; i < 15; i++)
83 fprintf(stderr," %04x", *p16++);
84 fprintf(stderr,"\n[IP]");
85 for (p8 = seg_to_linear(r->cs, r->eip), i = 0; i < 25; i++)
86 fprintf(stderr," %02x", *p8++);
87 fprintf(stderr,"\n");
88 }
90 #define DOS_FD_MAX 256
91 typedef struct {
92 int fd; /* -1 means closed */
93 } DOSFile;
95 DOSFile dos_files[DOS_FD_MAX];
96 uint16_t cur_psp;
98 void dos_init(void)
99 {
100 int i;
101 for(i = 0; i < DOS_FD_MAX; i++)
102 dos_files[i].fd = (i < 3) ? i : -1;
103 }
105 static inline void set_error(struct vm86_regs *r, int val)
106 {
107 r->eflags &= ~CF_MASK;
108 if (val) {
109 r->eax = (r->eax & ~0xffff) | val;
110 r->eflags |= CF_MASK;
111 }
112 }
113 static DOSFile *get_file(int h)
114 {
115 DOSFile *fh;
117 if (h < DOS_FD_MAX) {
118 fh = &dos_files[h];
119 if (fh->fd != -1)
120 return fh;
121 }
122 return NULL;
123 }
125 /* return -1 if error */
126 static int get_new_handle(void)
127 {
128 DOSFile *fh;
129 int i;
131 for(i = 0; i < DOS_FD_MAX; i++) {
132 fh = &dos_files[i];
133 if (fh->fd == -1)
134 return i;
135 }
136 return -1;
137 }
139 static char *get_filename1(struct vm86_regs *r, char *buf, int buf_size,
140 uint16_t seg, uint16_t offset)
141 {
142 char *q;
143 int c;
144 q = buf;
145 for(;;) {
146 c = *seg_to_linear(seg, offset);
147 if (c == 0)
148 break;
149 if (q >= buf + buf_size - 1)
150 break;
151 c = tolower(c);
152 if (c == '\\')
153 c = '/';
154 *q++ = c;
155 offset++;
156 }
157 *q = '\0';
158 return buf;
159 }
161 static char *get_filename(struct vm86_regs *r, char *buf, int buf_size)
162 {
163 return get_filename1(r, buf, buf_size, r->ds, r->edx & 0xffff);
164 }
166 typedef struct __attribute__((packed)) {
167 uint8_t drive_num;
168 uint8_t file_name[8];
169 uint8_t file_ext[3];
170 uint16_t current_block;
171 uint16_t logical_record_size;
172 uint32_t file_size;
173 uint16_t date;
174 uint16_t time;
175 uint8_t reserved[8];
176 uint8_t record_in_block;
177 uint32_t record_num;
178 } FCB;
180 typedef struct __attribute__((packed)) {
181 uint16_t environ;
182 uint16_t cmdtail_off;
183 uint16_t cmdtail_seg;
184 uint32_t fcb1;
185 uint32_t fcb2;
186 uint16_t sp, ss;
187 uint16_t ip, cs;
188 } ExecParamBlock;
190 typedef struct MemBlock {
191 struct MemBlock *next;
192 uint16_t seg;
193 uint16_t size; /* in paragraphs */
194 } MemBlock;
196 /* first allocated paragraph */
197 MemBlock *first_mem_block = NULL;
199 #define MEM_START 0x1000
200 #define MEM_END 0xa000
202 /* return -1 if error */
203 int mem_malloc(int size, int *pmax_size)
204 {
205 MemBlock *m, **pm;
206 int seg_free, seg;
208 /* XXX: this is totally inefficient, but we have only 1 or 2
209 blocks ! */
210 seg_free = MEM_START;
211 for(pm = &first_mem_block; *pm != NULL; pm = &(*pm)->next) {
212 m = *pm;
213 seg = m->seg + m->size;
214 if (seg > seg_free)
215 seg_free = seg;
216 }
217 if ((seg_free + size) > MEM_END)
218 return -1;
219 if (pmax_size)
220 *pmax_size = MEM_END - seg_free;
221 /* add at the end */
222 m = malloc(sizeof(MemBlock));
223 *pm = m;
224 m->next = NULL;
225 m->seg = seg_free;
226 m->size = size;
227 #ifdef DUMP_INT21
228 printf("mem_malloc size=0x%04x: 0x%04x\n", size, seg_free);
229 #endif
230 return seg_free;
231 }
233 /* return -1 if error */
234 int mem_free(int seg)
235 {
236 MemBlock *m, **pm;
237 for(pm = &first_mem_block; *pm != NULL; pm = &(*pm)->next) {
238 m = *pm;
239 if (m->seg == seg) {
240 *pm = m->next;
241 free(m);
242 return 0;
243 }
244 }
245 return -1;
246 }
248 /* return -1 if error or the maxmium size */
249 int mem_resize(int seg, int new_size)
250 {
251 MemBlock *m, **pm, *m1;
252 int max_size;
254 for(pm = &first_mem_block; *pm != NULL; pm = &(*pm)->next) {
255 m = *pm;
256 if (m->seg == seg) {
257 m1 = m->next;
258 if (!m1)
259 max_size = MEM_END - m->seg;
260 else
261 max_size = m1->seg - m->seg;
262 if (new_size > max_size)
263 return -1;
264 m->size = new_size;
265 return max_size;
266 }
267 }
268 return -1;
269 }
271 int load_boot(const char *filename, struct vm86_regs *r)
272 {
273 int fd, ret;
275 /* load the boot sector */
276 fd = open(filename, O_RDONLY);
277 if (fd >= 0) {
278 *seg_to_linear(0x0, 0x7dff) = 0;
279 r->eax = 0x200;
280 r->ebx = r->esp = r->eip = 0x7c00;
281 r->ecx = 1;
282 r->esi = r->edi = r->ebp =
283 r->edx = 0; /* floppy disk */
284 r->cs = r->ss = r->ds = r->es = 0;
285 r->eflags = VIF_MASK;
286 ret = read(fd, seg_to_linear(0x0, 0x7c00), 0x200);
287 if (lseek(fd, 0, SEEK_END) > 4*1024*1024)
288 r->edx = 0x80; /* hard disk */
289 close(fd);
290 if (ret != 0x200 ||
291 *seg_to_linear(0x0, 0x7dfe) != 0x55 ||
292 *seg_to_linear(0x0, 0x7dff) != 0xaa) {
293 fprintf(stderr,"No boot sector.\n");
294 fd = -1;
295 }
296 }
297 return fd;
298 }
300 /* return the PSP or -1 if error */
301 int load_exe(ExecParamBlock *blk, const char *filename,
302 int psp, uint32_t *pfile_size)
303 {
304 int fd, size, base;
305 struct {
306 uint16_t signature; // 0x5A4D 'MZ'
307 uint16_t bytes_in_last_block;
308 uint16_t blocks_in_file;
309 uint16_t num_relocs;
310 uint16_t header_paragraphs; // Size of header
311 uint16_t min_extra_paragraphs; // BSS size
312 uint16_t max_extra_paragraphs;
313 uint16_t ss; // Initial (relative) SS value
314 uint16_t sp; // Initial SP value
315 uint16_t checksum;
316 uint16_t ip; // Initial IP value
317 uint16_t cs; // Initial (relative) CS value
318 uint16_t reloc_table_offset;
319 uint16_t overlay_number;
320 } header;
321 struct {
322 uint16_t offset;
323 uint16_t segment;
324 } rel;
326 /* load the MSDOS .exe executable */
327 fd = open(filename, O_RDONLY);
328 if (fd < 0) {
329 return -1;
330 }
331 if (read(fd, &header, sizeof(header)) != sizeof(header)) {
332 close(fd);
333 return -1;
334 }
336 memset(seg_to_linear(psp, 0x100), 0, 65536 - 0x100);
338 size = (header.blocks_in_file * 512) - (header.header_paragraphs * 16) +
339 (header.bytes_in_last_block ? header.bytes_in_last_block - 512 : 0);
340 header.min_extra_paragraphs += (size-1)/16;
342 /* address of last segment allocated */
343 *(uint16_t *)seg_to_linear(psp, 2) = psp + header.min_extra_paragraphs;
345 if (pfile_size)
346 *pfile_size = size;
348 if (mem_resize(psp, header.min_extra_paragraphs) < 0 ||
349 lseek(fd, header.header_paragraphs * 16, SEEK_SET) < 0 ||
350 read(fd, seg_to_linear(psp, 0x100), size) != size ||
351 lseek(fd, header.reloc_table_offset, SEEK_SET) < 0) {
352 close(fd);
353 return -1;
354 }
356 base = psp + 16;
357 while (header.num_relocs-- && read(fd, &rel, sizeof(rel)) == sizeof(rel))
358 if (rel.segment != 0 || rel.offset != 0)
359 * (uint16_t *) seg_to_linear(base + rel.segment, rel.offset) += base;
360 close(fd);
362 blk->cs = base + header.cs;
363 blk->ip = header.ip;
364 blk->ss = base + header.ss;
365 blk->sp = header.sp - 6;
367 /* push return far address */
368 *(uint16_t *)seg_to_linear(blk->ss, blk->sp + 4) = psp;
370 return psp;
371 }
373 /* return the PSP or -1 if error */
374 int load_com(ExecParamBlock *blk, const char *filename, uint32_t *pfile_size,
375 int argc, char **argv)
376 {
377 int psp, fd, ret;
379 /* load the MSDOS .com executable */
380 fd = open(filename, O_RDONLY);
381 if (fd < 0) {
382 return -1;
383 }
384 psp = mem_malloc(65536 / 16, NULL);
385 ret = read(fd, seg_to_linear(psp, 0x100), 65536 - 0x100);
386 close(fd);
387 if (ret <= 0) {
388 mem_free(psp);
389 return -1;
390 }
391 if (pfile_size)
392 *pfile_size = ret;
394 /* reset the PSP */
395 memset(seg_to_linear(psp, 0), 0, 0x100);
397 *seg_to_linear(psp, 0) = 0xcd; /* int $0x20 */
398 *seg_to_linear(psp, 1) = 0x20;
399 /* address of last segment allocated */
400 *(uint16_t *)seg_to_linear(psp, 2) = psp + 0xfff;
402 if (argc) {
403 int i, p;
404 char *s;
405 /* set the command line */
406 p = 0x81;
407 for(i = 2; i < argc; i++) {
408 if (p >= 0xff)
409 break;
410 *seg_to_linear(psp, p++) = ' ';
411 s = argv[i];
412 while (*s) {
413 if (p >= 0xff)
414 break;
415 *seg_to_linear(psp, p++) = *s++;
416 }
417 }
418 *seg_to_linear(psp, p) = '\r';
419 *seg_to_linear(psp, 0x80) = p - 0x81;
420 }
421 else {
422 int len;
423 /* copy the command line */
424 len = *seg_to_linear(blk->cmdtail_seg, blk->cmdtail_off);
425 memcpy(seg_to_linear(psp, 0x80),
426 seg_to_linear(blk->cmdtail_seg, blk->cmdtail_off), len + 2);
427 }
429 blk->sp = 0xfffc;
430 blk->ip = 0x100;
431 blk->cs = blk->ss = psp;
433 if (*(uint16_t *)seg_to_linear(psp, 0x100) == 0x5A4D)
434 psp = load_exe(blk, filename, psp, pfile_size);
436 /* push ax value */
437 *(uint16_t *)seg_to_linear(blk->ss, blk->sp) = 0;
438 /* push return address to 0 */
439 *(uint16_t *)seg_to_linear(blk->ss, blk->sp + 2) = 0;
441 return psp;
442 }
445 void unsupported_function(struct vm86_regs *r, uint8_t num, uint8_t ah)
446 {
447 fprintf(stderr, "int 0x%02x: unsupported function 0x%02x\n", num, ah);
448 dump_regs(r);
449 set_error(r, 0x01); /* function number invalid */
450 }
452 /* Open hard disk image ./hd[0-7] / floppy image ./fd[0-7] or /dev/fd[0-7] */
453 int open_disk(struct vm86_regs *r)
454 {
455 int fd = -1, drive = r->edx & 0xff;
456 char filename[9], n = '0' + (drive & 7);
457 if (drive > 127) {
458 strcpy(filename,"hd0");
459 filename[2] = n;
460 }
461 else {
462 strcpy(filename,"/dev/fd0");
463 filename[7] = n;
464 fd = open(filename+5, O_RDONLY);
465 }
466 if (fd < 0)
467 fd = open(filename, O_RDONLY);
468 return fd;
469 }
472 void read_sectors(int fd, struct vm86_regs *r, int first_sector,
473 int sector_count, void *buffer)
474 {
475 int drive = r->edx & 0xff;
476 r->eax &= ~0xff00;
477 r->eax |= 0x0400; /* sector not found */
478 r->eflags |= CF_MASK;
479 if (fd >= 0) {
480 static struct stat st;
481 first_sector <<= 9;
482 sector_count <<= 9;
483 if (drive < 8 && fstat(fd, &st) == 0) {
484 static ino_t inodes[8];
485 ino_t last = inodes[drive];
486 inodes[drive] = st.st_ino;
487 if (last && last != st.st_ino) {
488 set_error(r, 0x0600); /* floppy disk swap */
489 goto failed;
490 }
491 }
492 if (lseek(fd, first_sector, SEEK_SET) >= 0 &&
493 read(fd, buffer, sector_count) == sector_count) {
494 r->eax &= ~0xff00;
495 r->eflags &= ~CF_MASK;
496 }
497 failed:
498 close(fd);
499 }
500 }
502 void do_int10(struct vm86_regs *r)
503 {
504 uint8_t ah;
506 ah = (r->eax >> 8);
507 switch(ah) {
508 case 0x0E: /* write char */
509 {
510 uint8_t c = r->eax;
511 write(1, &c, 1);
512 }
513 break;
514 default:
515 unsupported_function(r, 0x10, ah);
516 }
517 }
519 void do_int13(struct vm86_regs *r)
520 {
521 uint8_t ah;
523 ah = (r->eax >> 8);
524 switch(ah) {
525 case 0x00: /* reset disk */
526 {
527 r->eax &= ~0xff00; /* success */
528 r->eflags &= ~CF_MASK;
529 }
530 break;
531 case 0x02: /* read disk CHS */
532 {
533 int fd, c, h, s, heads, sectors, cylinders;
534 long size;
535 fd = open_disk(r);
536 if (fd >= 0) {
537 size = lseek(fd, 0, SEEK_END) / 512;
538 if ((r->edx & 0xff) > 127) {
539 sectors = 63;
540 if (size % sectors)
541 sectors = 62;
542 if (size % sectors)
543 sectors = 32;
544 if (size % sectors)
545 sectors = 17;
546 if (size % sectors)
547 fd = -1;
548 size /= sectors;
549 for (heads = 256; size % heads; heads--);
550 cylinders = size / heads;
551 }
552 else {
553 int i;
554 heads = 1 + (size > 256*2);
555 cylinders = 40 * (1 + (size > 512*2));
556 size /= heads;
557 for (i = 0; i < 5; i++)
558 if (size % (cylinders + i) == 0) break;
559 if (i == 5)
560 fd = -1;
561 cylinders += i;
562 sectors = size / cylinders;
563 }
564 }
565 c = ((r->ecx & 0xC0) << 2) | ((r->ecx >> 8) & 0xff);
566 h = (r->edx >> 8) & 0xff;
567 s = (r->ecx & 0x3f) -1;
568 if (fd < 0 || c >= cylinders || h >= heads || s >= sectors) {
569 set_error(r, 0x0400); /* sector not found */
570 break;
571 }
572 read_sectors(fd, r, (((c * heads) + h) * sectors) + s,
573 r->eax & 0xff, seg_to_linear(r->es, r->ebx));
574 }
575 break;
576 case 0x42: /* read disk LBA */
577 {
578 uint16_t *packet = (uint16_t *) seg_to_linear(r->ds, r-> esi);
579 uint8_t *to = seg_to_linear(packet[3], packet[2]);
580 if ((packet[3] & packet[2]) == 0xffff)
581 to = * (uint8_t **) &packet[8];
582 if (packet[0] != 0x0010 && packet[0] != 0x0018)
583 goto unsupported;
584 read_sectors(open_disk(r), r, * (uint32_t *) &packet[4], packet[1], to);
585 }
586 break;
587 default:
588 unsupported:
589 unsupported_function(r, 0x13, ah);
590 }
591 }
593 void do_int15(struct vm86_regs *r)
594 {
595 uint8_t ah;
597 ah = (r->eax >> 8);
598 switch(ah) {
599 case 0x87: /* move memory */
600 /* XXX */
601 break;
602 default:
603 unsupported_function(r, 0x15, ah);
604 }
605 }
607 void do_int16(struct vm86_regs *r)
608 {
609 static uint16_t last_ax, hold_char;
610 struct termios termios_def, termios_raw;
611 uint8_t ah;
613 ah = (r->eax >> 8);
614 tcgetattr(0, &termios_def);
615 termios_raw = termios_def;
616 cfmakeraw(&termios_raw);
617 tcsetattr(0, TCSADRAIN, &termios_raw);
618 switch(ah) {
619 case 0x01: /* test keyboard */
620 {
621 int count;
622 r->eflags &= ~ZF_MASK;
623 if (hold_char) {
624 r->eax &= ~0xffff;
625 r->eax |= last_ax;
626 break;
627 }
628 if (ioctl(0, FIONREAD, &count) < 0 || count == 0) {
629 r->eflags |= ZF_MASK;
630 break;
631 }
632 hold_char = 2;
633 }
634 case 0x00: /* read keyboard */
635 {
636 uint8_t c;
637 if (hold_char)
638 hold_char--;
639 read(0, &c, 1);
640 if (c == 3) {
641 tcsetattr(0, TCSADRAIN, &termios_def);
642 exit(0);
643 }
644 if (c == 10)
645 c = 13;
646 r->eax &= ~0xffff;
647 r->eax |= last_ax = c;
648 /* XXX ah = scan code */
649 }
650 break;
651 default:
652 unsupported_function(r, 0x16, ah);
653 }
654 tcsetattr(0, TCSADRAIN, &termios_def);
655 }
657 void do_int1a(struct vm86_regs *r)
658 {
659 uint8_t ah;
661 ah = (r->eax >> 8);
662 switch(ah) {
663 case 0x00: /* GET SYSTEM TIME */
664 {
665 uint16_t *timer = (uint16_t *) seg_to_linear(0, 0x46C);
666 r->ecx &= ~0xffff;
667 r->ecx |= *timer++;
668 r->edx &= ~0xffff;
669 r->edx |= *timer;
670 r->eax &= ~0xff;
671 }
672 break;
673 default:
674 unsupported_function(r, 0x1a, ah);
675 }
676 }
678 void do_int20(struct vm86_regs *r)
679 {
680 /* terminate program */
681 exit(0);
682 }
684 void do_int21(struct vm86_regs *r)
685 {
686 uint8_t ah;
688 ah = (r->eax >> 8);
689 switch(ah) {
690 case 0x00: /* exit */
691 exit(0);
692 case 0x02: /* write char */
693 {
694 uint8_t c = r->edx;
695 write(1, &c, 1);
696 }
697 break;
698 case 0x09: /* write string */
699 {
700 uint8_t c;
701 int offset;
702 offset = r->edx;
703 for(;;) {
704 c = *seg_to_linear(r->ds, offset);
705 if (c == '$')
706 break;
707 write(1, &c, 1);
708 offset++;
709 }
710 r->eax = (r->eax & ~0xff) | '$';
711 }
712 break;
713 case 0x0a: /* buffered input */
714 {
715 int max_len, cur_len, ret;
716 uint8_t ch;
717 uint16_t off;
719 /* XXX: should use raw mode to avoid sending the CRLF to
720 the terminal */
721 off = r->edx & 0xffff;
722 max_len = *seg_to_linear(r->ds, off);
723 cur_len = 0;
724 while (cur_len < max_len) {
725 ret = read(0, &ch, 1);
726 if (ret < 0) {
727 if (errno != EINTR && errno != EAGAIN)
728 break;
729 } else if (ret == 0) {
730 break;
731 } else {
732 if (ch == '\n')
733 break;
734 }
735 *seg_to_linear(r->ds, off + 2 + cur_len++) = ch;
736 }
737 *seg_to_linear(r->ds, off + 1) = cur_len;
738 *seg_to_linear(r->ds, off + 2 + cur_len) = '\r';
739 }
740 break;
741 case 0x25: /* set interrupt vector */
742 {
743 uint16_t *ptr;
744 ptr = (uint16_t *)seg_to_linear(0, (r->eax & 0xff) * 4);
745 ptr[0] = r->edx;
746 ptr[1] = r->ds;
747 }
748 break;
749 case 0x29: /* parse filename into FCB */
750 #if 0
751 /* not really needed */
752 {
753 const uint8_t *p, *p_start;
754 uint8_t file[8], ext[3];
755 FCB *fcb;
756 int file_len, ext_len, has_wildchars, c, drive_num;
758 /* XXX: not complete at all */
759 fcb = (FCB *)seg_to_linear(r->es, r->edi);
760 printf("ds=0x%x si=0x%lx\n", r->ds, r->esi);
761 p_start = (const uint8_t *)seg_to_linear(r->ds, r->esi);
763 p = p_start;
764 has_wildchars = 0;
766 /* drive */
767 if (isalpha(p[0]) && p[1] == ':') {
768 drive_num = toupper(p[0]) - 'A' + 1;
769 p += 2;
770 } else {
771 drive_num = 0;
772 }
774 /* filename */
775 file_len = 0;
776 for(;;) {
777 c = *p;
778 if (!(c >= 33 && c <= 126))
779 break;
780 if (c == '.')
781 break;
782 if (c == '*' || c == '?')
783 has_wildchars = 1;
784 if (file_len < 8)
785 file[file_len++] = c;
786 }
787 memset(file + file_len, ' ', 8 - file_len);
789 /* extension */
790 ext_len = 0;
791 if (*p == '.') {
792 for(;;) {
793 c = *p;
794 if (!(c >= 33 && c <= 126))
795 break;
796 if (c == '*' || c == '?')
797 has_wildchars = 1;
798 ext[ext_len++] = c;
799 if (ext_len >= 3)
800 break;
801 }
802 }
803 memset(ext + ext_len, ' ', 3 - ext_len);
805 #if 0
806 {
807 printf("drive=%d file=%8s ext=%3s\n",
808 drive_num, file, ext);
809 }
810 #endif
811 if (drive_num == 0 && r->eax & (1 << 1)) {
812 /* keep drive */
813 } else {
814 fcb->drive_num = drive_num; /* default drive */
815 }
817 if (file_len == 0 && r->eax & (1 << 2)) {
818 /* keep */
819 } else {
820 memcpy(fcb->file_name, file, 8);
821 }
823 if (ext_len == 0 && r->eax & (1 << 3)) {
824 /* keep */
825 } else {
826 memcpy(fcb->file_ext, ext, 3);
827 }
828 r->eax = (r->eax & ~0xff) | has_wildchars;
829 r->esi = (r->esi & ~0xffff) | ((r->esi + (p - p_start)) & 0xffff);
830 }
831 #endif
832 break;
833 case 0x2A: /* get system date */
834 {
835 time_t t = time(NULL);
836 struct tm *now=localtime(&t);
838 r->ecx = now->tm_year;
839 r->edx = (now->tm_mon * 256) + now->tm_mday;
840 r->eax = now->tm_wday;;
841 }
842 break;
843 case 0x2C: /* get system time */
844 {
845 time_t t = time(NULL);
846 struct tm *now=localtime(&t);
847 struct timeval tim;
849 gettimeofday(&tim, NULL);
850 r->edx = (now->tm_hour * 256) + now->tm_min;
851 r->edx = (tim.tv_sec * 256) + tim.tv_usec/10000;
852 }
853 break;
854 case 0x30: /* get dos version */
855 {
856 int major, minor, serial, oem;
857 /* XXX: return correct value for FreeDOS */
858 major = 0x03;
859 minor = 0x31;
860 serial = 0x123456;
861 oem = 0x66;
862 r->eax = (r->eax & ~0xffff) | major | (minor << 8);
863 r->ecx = (r->ecx & ~0xffff) | (serial & 0xffff);
864 r->ebx = (r->ebx & ~0xffff) | (serial & 0xff) | (0x66 << 8);
865 }
866 break;
867 case 0x35: /* get interrupt vector */
868 {
869 uint16_t *ptr;
870 ptr = (uint16_t *)seg_to_linear(0, (r->eax & 0xff) * 4);
871 r->ebx = (r->ebx & ~0xffff) | ptr[0];
872 r->es = ptr[1];
873 }
874 break;
875 case 0x37:
876 {
877 switch(r->eax & 0xff) {
878 case 0x00: /* get switch char */
879 r->eax = (r->eax & ~0xff) | 0x00;
880 r->edx = (r->edx & ~0xff) | '/';
881 break;
882 default:
883 goto unsupported;
884 }
885 }
886 break;
887 case 0x3c: /* create or truncate file */
888 {
889 char filename[1024];
890 int fd, h, flags;
892 h = get_new_handle();
893 if (h < 0) {
894 set_error(r, 0x04); /* too many open files */
895 } else {
896 get_filename(r, filename, sizeof(filename));
897 if (r->ecx & 1)
898 flags = 0444; /* read-only */
899 else
900 flags = 0777;
901 fd = open(filename, O_RDWR | O_TRUNC | O_CREAT, flags);
902 #ifdef DUMP_INT21
903 printf("int21: create: file='%s' cx=0x%04x ret=%d\n",
904 filename, (int)(r->ecx & 0xffff), h);
905 #endif
906 if (fd < 0) {
907 set_error(r, 0x03); /* path not found */
908 } else {
909 dos_files[h].fd = fd;
910 set_error(r, 0);
911 r->eax = (r->eax & ~0xffff) | h;
912 }
913 }
914 }
915 break;
916 case 0x3d: /* open file */
917 {
918 char filename[1024];
919 int fd, h;
921 h = get_new_handle();
922 if (h < 0) {
923 set_error(r, 0x04); /* too many open files */
924 } else {
925 get_filename(r, filename, sizeof(filename));
926 #ifdef DUMP_INT21
927 printf("int21: open: file='%s' al=0x%02x ret=%d\n",
928 filename, (int)(r->eax & 0xff), h);
929 #endif
930 fd = open(filename, r->eax & 3);
931 if (fd < 0) {
932 set_error(r, 0x02); /* file not found */
933 } else {
934 dos_files[h].fd = fd;
935 set_error(r, 0);
936 r->eax = (r->eax & ~0xffff) | h;
937 }
938 }
939 }
940 break;
941 case 0x3e: /* close file */
942 {
943 DOSFile *fh = get_file(r->ebx & 0xffff);
944 #ifdef DUMP_INT21
945 printf("int21: close fd=%d\n", (int)(r->ebx & 0xffff));
946 #endif
947 if (!fh) {
948 set_error(r, 0x06); /* invalid handle */
949 } else {
950 close(fh->fd);
951 fh->fd = -1;
952 set_error(r, 0);
953 }
954 }
955 break;
956 case 0x3f: /* read */
957 {
958 DOSFile *fh = get_file(r->ebx & 0xffff);
959 int n, ret;
961 if (!fh) {
962 set_error(r, 0x06); /* invalid handle */
963 } else {
964 n = r->ecx & 0xffff;
965 for(;;) {
966 ret = read(fh->fd,
967 seg_to_linear(r->ds, r->edx), n);
968 if (ret < 0) {
969 if (errno != EINTR && errno != EAGAIN)
970 break;
971 } else {
972 break;
973 }
974 }
975 #ifdef DUMP_INT21
976 printf("int21: read: fd=%d n=%d ret=%d\n",
977 (int)(r->ebx & 0xffff), n, ret);
978 #endif
979 if (ret < 0) {
980 set_error(r, 0x05); /* acces denied */
981 } else {
982 r->eax = (r->eax & ~0xffff) | ret;
983 set_error(r, 0);
984 }
985 }
986 }
987 break;
988 case 0x40: /* write */
989 {
990 DOSFile *fh = get_file(r->ebx & 0xffff);
991 int n, ret, pos;
993 if (!fh) {
994 set_error(r, 0x06); /* invalid handle */
995 } else {
996 n = r->ecx & 0xffff;
997 if (n == 0) {
998 /* truncate */
999 pos = lseek(fh->fd, 0, SEEK_CUR);
1000 if (pos >= 0) {
1001 ret = ftruncate(fh->fd, pos);
1002 } else {
1003 ret = -1;
1004 }
1005 } else {
1006 for(;;) {
1007 ret = write(fh->fd,
1008 seg_to_linear(r->ds, r->edx), n);
1009 if (ret < 0) {
1010 if (errno != EINTR && errno != EAGAIN)
1011 break;
1012 } else {
1013 break;
1014 }
1015 }
1016 }
1017 #ifdef DUMP_INT21
1018 printf("int21: write: fd=%d n=%d ret=%d\n",
1019 (int)(r->ebx & 0xffff), n, ret);
1020 #endif
1021 if (ret < 0) {
1022 set_error(r, 0x05); /* acces denied */
1023 } else {
1024 r->eax = (r->eax & ~0xffff) | ret;
1025 set_error(r, 0);
1026 }
1027 }
1028 }
1029 break;
1030 case 0x41: /* unlink */
1031 {
1032 char filename[1024];
1033 get_filename(r, filename, sizeof(filename));
1034 if (unlink(filename) < 0) {
1035 set_error(r, 0x02); /* file not found */
1036 } else {
1037 set_error(r, 0);
1038 }
1039 }
1040 break;
1041 case 0x42: /* lseek */
1042 {
1043 DOSFile *fh = get_file(r->ebx & 0xffff);
1044 int pos, ret;
1046 if (!fh) {
1047 set_error(r, 0x06); /* invalid handle */
1048 } else {
1049 pos = ((r->ecx & 0xffff) << 16) | (r->edx & 0xffff);
1050 ret = lseek(fh->fd, pos, r->eax & 0xff);
1051 #ifdef DUMP_INT21
1052 printf("int21: lseek: fd=%d pos=%d whence=%d ret=%d\n",
1053 (int)(r->ebx & 0xffff), pos, (uint8_t)r->eax, ret);
1054 #endif
1055 if (ret < 0) {
1056 set_error(r, 0x01); /* function number invalid */
1057 } else {
1058 r->edx = (r->edx & ~0xffff) | ((unsigned)ret >> 16);
1059 r->eax = (r->eax & ~0xffff) | (ret & 0xffff);
1060 set_error(r, 0);
1061 }
1062 }
1063 }
1064 break;
1065 case 0x44: /* ioctl */
1066 switch(r->eax & 0xff) {
1067 case 0x00: /* get device information */
1068 {
1069 DOSFile *fh = get_file(r->ebx & 0xffff);
1070 int ret;
1072 if (!fh) {
1073 set_error(r, 0x06); /* invalid handle */
1074 } else {
1075 ret = 0;
1076 if (isatty(fh->fd)) {
1077 ret |= 0x80;
1078 if (fh->fd == 0)
1079 ret |= (1 << 0);
1080 else
1081 ret |= (1 << 1);
1082 }
1083 r->edx = (r->edx & ~0xffff) | ret;
1084 set_error(r, 0);
1085 }
1086 }
1087 break;
1088 default:
1089 goto unsupported;
1090 }
1091 break;
1092 case 0x48: /* allocate memory */
1093 {
1094 int ret, max_size;
1095 #ifdef DUMP_INT21
1096 printf("int21: allocate memory: size=0x%04x\n", (uint16_t)r->ebx);
1097 #endif
1098 ret = mem_malloc(r->ebx & 0xffff, &max_size);
1099 if (ret < 0) {
1100 set_error(r, 0x08); /* insufficient memory*/
1101 } else {
1102 r->eax = (r->eax & ~0xffff) | ret;
1103 r->ebx = (r->ebx & ~0xffff) | max_size;
1104 set_error(r, 0);
1105 }
1106 }
1107 break;
1108 case 0x49: /* free memory */
1109 {
1110 #ifdef DUMP_INT21
1111 printf("int21: free memory: block=0x%04x\n", r->es);
1112 #endif
1113 if (mem_free(r->es) < 0) {
1114 set_error(r, 0x09); /* memory block address invalid */
1115 } else {
1116 set_error(r, 0);
1117 }
1118 }
1119 break;
1120 case 0x4a: /* resize memory block */
1121 {
1122 int ret;
1123 #ifdef DUMP_INT21
1124 printf("int21: resize memory block: block=0x%04x size=0x%04x\n",
1125 r->es, (uint16_t)r->ebx);
1126 #endif
1127 ret = mem_resize(r->es, r->ebx & 0xffff);
1128 if (ret < 0) {
1129 set_error(r, 0x08); /* insufficient memory*/
1130 } else {
1131 r->ebx = (r->ebx & ~0xffff) | ret;
1132 set_error(r, 0);
1133 }
1134 }
1135 break;
1136 case 0x4b: /* load program */
1137 {
1138 char filename[1024];
1139 ExecParamBlock *blk;
1140 int ret;
1142 if ((r->eax & 0xff) != 0x01) /* only load */
1143 goto unsupported;
1144 get_filename(r, filename, sizeof(filename));
1145 blk = (ExecParamBlock *)seg_to_linear(r->es, r->ebx);
1146 ret = load_com(blk, filename, NULL, 0, NULL);
1147 if (ret < 0) {
1148 set_error(r, 0x02); /* file not found */
1149 } else {
1150 cur_psp = ret;
1151 set_error(r, 0);
1152 }
1153 }
1154 break;
1155 case 0x4c: /* exit with return code */
1156 exit(r->eax & 0xff);
1157 break;
1158 case 0x50: /* set PSP address */
1159 #ifdef DUMP_INT21
1160 printf("int21: set PSP: 0x%04x\n", (uint16_t)r->ebx);
1161 #endif
1162 cur_psp = r->ebx;
1163 break;
1164 case 0x51: /* get PSP address */
1165 #ifdef DUMP_INT21
1166 printf("int21: get PSP: ret=0x%04x\n", cur_psp);
1167 #endif
1168 r->ebx = (r->ebx & ~0xffff) | cur_psp;
1169 break;
1170 case 0x55: /* create child PSP */
1171 {
1172 uint8_t *psp_ptr;
1173 #ifdef DUMP_INT21
1174 printf("int21: create child PSP: psp=0x%04x last_seg=0x%04x\n",
1175 (uint16_t)r->edx, (uint16_t)r->esi);
1176 #endif
1177 psp_ptr = seg_to_linear(r->edx & 0xffff, 0);
1178 memset(psp_ptr, 0, 0x80);
1179 psp_ptr[0] = 0xcd; /* int $0x20 */
1180 psp_ptr[1] = 0x20;
1181 *(uint16_t *)(psp_ptr + 2) = r->esi;
1182 r->eax = (r->eax & ~0xff);
1183 }
1184 break;
1185 default:
1186 unsupported:
1187 unsupported_function(r, 0x21, ah);
1188 }
1189 }
1191 void do_int29(struct vm86_regs *r)
1192 {
1193 uint8_t c = r->eax;
1194 write(1, &c, 1);
1195 }
1197 void raise_interrupt(int number)
1198 {
1199 if (* (uint32_t *) seg_to_linear(0, number * 4) == 0)
1200 return;
1201 // FIXME VM86_SIGNAL
1202 }
1204 void biosclock()
1205 {
1206 uint32_t *timer = (uint32_t *) seg_to_linear(0, 0x46C);
1207 ++*timer;
1208 raise_interrupt(8);
1209 raise_interrupt(0x1C);
1210 }
1212 int main(int argc, char **argv)
1213 {
1214 uint8_t *vm86_mem;
1215 const char *filename;
1216 int ret;
1217 uint32_t file_size;
1218 struct sigaction sa;
1219 struct itimerval timerval;
1220 struct vm86plus_struct ctx;
1221 struct vm86_regs *r;
1222 ExecParamBlock blk1, *blk = &blk1;
1224 if (argc < 2)
1225 usage();
1226 filename = argv[1];
1228 vm86_mem = mmap((void *)0x00000000, 0x110000,
1229 PROT_WRITE | PROT_READ | PROT_EXEC,
1230 MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
1231 if (vm86_mem == MAP_FAILED) {
1232 perror("mmap");
1233 exit(1);
1234 }
1236 memset(&ctx, 0, sizeof(ctx));
1237 r = &ctx.regs;
1238 set_bit((uint8_t *)&ctx.int_revectored, 0x10);
1239 set_bit((uint8_t *)&ctx.int_revectored, 0x13);
1240 set_bit((uint8_t *)&ctx.int_revectored, 0x15);
1241 set_bit((uint8_t *)&ctx.int_revectored, 0x16);
1242 set_bit((uint8_t *)&ctx.int_revectored, 0x1a);
1243 set_bit((uint8_t *)&ctx.int_revectored, 0x20);
1244 set_bit((uint8_t *)&ctx.int_revectored, 0x21);
1245 set_bit((uint8_t *)&ctx.int_revectored, 0x29);
1247 dos_init();
1249 if (strstr(filename,".com") || strstr(filename,".exe") ||
1250 strstr(filename,".COM") || strstr(filename,".EXE")) {
1251 ret = load_com(blk, filename, &file_size, argc, argv);
1252 if (ret < 0) {
1253 perror(filename);
1254 exit(1);
1255 }
1256 cur_psp = ret;
1258 /* init basic registers */
1259 r->eip = blk->ip;
1260 r->esp = blk->sp + 2; /* pop ax value */
1261 r->cs = blk->cs;
1262 r->ss = blk->ss;
1263 r->ds = cur_psp;
1264 r->es = cur_psp;
1265 r->eflags = VIF_MASK;
1267 /* the value of these registers seem to be assumed by pi_10.com */
1268 r->esi = 0x100;
1269 #if 0
1270 r->ebx = file_size >> 16;
1271 r->ecx = file_size & 0xffff;
1272 #else
1273 r->ecx = 0xff;
1274 #endif
1275 r->ebp = 0x0900;
1276 r->edi = 0xfffe;
1277 }
1278 else {
1279 if (load_boot(filename, r) < 0) {
1280 if (errno)
1281 perror(filename);
1282 exit(1);
1283 }
1284 }
1286 sa.sa_handler = biosclock;
1287 sigemptyset(&sa.sa_mask);
1288 sa.sa_flags = SA_RESTART;
1289 if (sigaction(SIGALRM, &sa, 0) == 0) {
1290 timerval.it_interval.tv_sec = timerval.it_value.tv_sec = 0;
1291 timerval.it_interval.tv_usec = timerval.it_value.tv_usec = 10000000 / 182;
1292 setitimer (ITIMER_REAL, &timerval, NULL);
1293 }
1295 for(;;) {
1296 ret = vm86(VM86_ENTER, &ctx);
1297 switch(VM86_TYPE(ret)) {
1298 case VM86_INTx:
1299 {
1300 int int_num;
1302 int_num = VM86_ARG(ret);
1303 switch(int_num) {
1304 case 0x10:
1305 do_int10(r);
1306 break;
1307 case 0x13:
1308 do_int13(r);
1309 break;
1310 case 0x15:
1311 do_int15(r);
1312 break;
1313 case 0x16:
1314 do_int16(r);
1315 break;
1316 case 0x1a:
1317 do_int1a(r);
1318 break;
1319 case 0x20:
1320 do_int20(r);
1321 break;
1322 case 0x21:
1323 do_int21(r);
1324 break;
1325 case 0x29:
1326 do_int29(r);
1327 break;
1328 default:
1329 fprintf(stderr, "unsupported int 0x%02x\n", int_num);
1330 dump_regs(&ctx.regs);
1331 break;
1332 }
1333 }
1334 break;
1335 case VM86_SIGNAL:
1336 /* a signal came, we just ignore that */
1337 break;
1338 case VM86_STI:
1339 break;
1340 case VM86_TRAP:
1341 /* just executes the interruption */
1342 {
1343 uint16_t *int_vector;
1344 uint32_t eflags;
1346 eflags = r->eflags & ~IF_MASK;
1347 if (r->eflags & VIF_MASK)
1348 eflags |= IF_MASK;
1349 pushw(r, eflags);
1350 pushw(r, r->cs);
1351 pushw(r, r->eip);
1352 int_vector = (uint16_t *)seg_to_linear(0, VM86_ARG(ret) * 4);
1353 r->eip = int_vector[0];
1354 r->cs = int_vector[1];
1355 r->eflags &= ~(VIF_MASK | TF_MASK | AC_MASK);
1356 }
1357 break;
1358 default:
1359 fprintf(stderr, "unhandled vm86 return code (0x%x)\n", ret);
1360 dump_regs(&ctx.regs);
1361 exit(1);
1362 }
1363 }
1364 }