# HG changeset patch # User Pascal Bellard # Date 1329552005 -3600 # Node ID 289a2f495cd6f948ae508d7e2f32677d240e0b1e # Parent 8eec3c4a2a202275491b219ca41edbcbe0ad1a50 add runcom diff -r 8eec3c4a2a20 -r 289a2f495cd6 runcom/description.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runcom/description.txt Sat Feb 18 09:00:05 2012 +0100 @@ -0,0 +1,41 @@ +Runcom support DOS .com binary files and boot sector files. + +1- The DOS .com support + +Runcom provides few BIOS and DOS (int 21H) interrupt handlers. Many .com files +may not work. DOS .exe are also supported. +You can test it with the file /usr/bin/debug.com, with the command line : +$ debug.com + +2- The boot sector image support + +A boot sector image is a 512 bytes file ending with the 0xAA and 0x55 bytes +with the .bin extension. +Bios disk (int 13H) are amulated (CHS or LBA) with image file : +- hard disk are image ./hd0, ./hd1, ... for disk 0x80, 0x81... +- floppy disk are image ./fd0, ./fd1 ... or /dev/fd0, /dev/fd1 if not found. +You can test it with the file /usr/bin/debug.bin, with the command line : +$ debug.bin + +3- The boot sector debugger /usr/bin/debug.bin + +Usage: + + f DX:CX load one CHS sector to 0000:7C00 + t trace one step + g
go to adrs + d
display 16 bytes, CR for next 16 bytes... + e
... enter memory byte/word/dword + m self move + + default segment offset + +seqment and offset are hexadecimal values in 0..FFFF range +address is linear hexadecimal value in 0..FFFFF range or seqment:offset +words are bytes in 00..FF range or words in 0000..FFFF range or double words +CX and DX are used by INT13H/AL=01 BIOS interrupt. + +Example: + m 0FC0 move debugger to 0FC0:0000 0FC0:01FF + f 1 read floppy boot sector to 0000:7C00 + f 80 1 read hard disk master boot sector to 0000:7C00 + g 7C0E ... diff -r 8eec3c4a2a20 -r 289a2f495cd6 runcom/receipt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runcom/receipt Sat Feb 18 09:00:05 2012 +0100 @@ -0,0 +1,58 @@ +# SliTaz package receipt. + +PACKAGE="runcom" +VERSION="1.0" +CATEGORY="system-tools" +SHORT_DESC="DOS .com binary format support" +MAINTAINER="dev@slitaz.org" +WEB_SITE="http://bellard.org/jslinux" + +# Rules to configure and make the package. +compile_rules() +{ + mkdir -p $src + cd $src + #tarball=$(wget -O - $WEB_SITE/tech.html | \ + # sed '/linuxstart/!d;s/.*href="\([^"]*\)".*/\1/') + #wget $WEB_SITE/$tarball + #tar xzf $tarball + mkdir -p $DESTDIR/usr/bin + cc -o $DESTDIR/usr/bin/runcom $stuff/runcom.c + cc -o $src/debug.o -Wa,-a=$src/debug.lst -c $stuff/debug.S + objcopy -O binary $src/debug.o $DESTDIR/usr/bin/debug.bin + cp $stuff/debug.com $DESTDIR/usr/bin + chmod +x $DESTDIR/usr/bin/debug.* +} + +# Rules to gen a SliTaz package suitable for Tazpkg. +genpkg_rules() +{ + cp -a $_pkg/* $fs +} + +# Post install command for Tazpkg. +post_install() +{ + fmt="binfmt_misc" + proc="/proc/sys/fs/binfmt_misc" + bin=":BOOTBIN:E::bin::/usr/bin/runcom:" + com=":DOSCOM:E::com::/usr/bin/runcom:" + rc="$1/etc/init.d/local.sh" + grep -q "$com" $rc || cat >> $rc <$proc/register +echo "$com" >$proc/register +EOT + [ -n "$1" ] && return + [ ! -e $proc/register ] && modprobe $fmt && mount -t $fmt $fmt $proc + echo "$bin" >$proc/register + echo "$com" >$proc/register +} + +# Pre remove command for Tazpkg. +pre_remove() +{ + echo -1 > $1/proc/sys/fs/binfmt_misc/BOOTBIN + echo -1 > $1/proc/sys/fs/binfmt_misc/DOSCOM + sed -i '/binfmt_misc/{NN;/DOSCOM:E::com/d}' $1/etc/init.d/local.sh +} diff -r 8eec3c4a2a20 -r 289a2f495cd6 runcom/stuff/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runcom/stuff/Makefile Sat Feb 18 09:00:05 2012 +0100 @@ -0,0 +1,20 @@ +CC=gcc -m32 +CFLAGS= -O2 -Wall -fno-builtin -fno-stack-protector #-march=i386 +HOST_CFLAGS=-O2 -Wall +# modify to set the kernel path +KERNEL_PATH=../linux-2.6.20 + +all: debug.bin debug8086.bin dbg8086.bin new.bin dbg.bin runcom + +%.bin: %.o + objcopy -O binary $< $@ + chmod +x $@ + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +%.o: %.S + $(CC) -D__ASSEMBLY__ -Wa,-acghlnm=$*.lst -c -o $@ $< + +clean: + rm -f *.bin runcom *.o *~ diff -r 8eec3c4a2a20 -r 289a2f495cd6 runcom/stuff/debug.S --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runcom/stuff/debug.S Sat Feb 18 09:00:05 2012 +0100 @@ -0,0 +1,493 @@ +// Usage: +// +// f DX:CX load one CHS sector to 0000:7C00 +// t trace one step +// g
go to adrs +// d
display 16 bytes, CR for next 16 bytes... +// e
... enter memory byte/word/dword +// m self move +// + default segment offset +// +// Example: +// m 0FC0 move debugger to 0FC0:0000 0FC0:01FF +// f 1 read floppy boot sector to 0000:7C00 +// f 80 1 read hard disk master boot sector to 0000:7C00 +// g 7C0E ... + +#define REGS32 28 bytes display FS, GS and 32 bits datas for AX..DI +#define ADJESDI 16 bytes add segment overflow support for e and d +#define ASCIIDUMP 20 bytes display hexa and ascii datas +#define INPUTBUFFER 3 bytes overload init code with a 32+ bytes input buffer + +.macro pushib val + .byte 0x6A, \val-_start +.endm + +#ifdef REGS32 +#define SEGREGSZ 10 +#define REGSZ 32 +#define USER_SP REGSZ+SEGREGSZ-28(%bp) +#define FIXSP 14 +#else +#define SEGREGSZ 6 +#define REGSZ 16 +#define USER_SP REGSZ+SEGREGSZ-14(%bp) +#define FIXSP 10 +#endif +#define USER_FLAGS REGSZ+SEGREGSZ+4(%bp) +#define USER_FLAGS_HIGH REGSZ+SEGREGSZ+5(%bp) +#define USER_IP REGSZ+SEGREGSZ(%bp) +#define USER_CS REGSZ+SEGREGSZ+2(%bp) +#define USER_CSIP REGSZ+SEGREGSZ(%bp) + +#ifdef INPUTBUFFER +//#define ABS(x) (x-(setvectors-_start)) +#define ABS(x) (x-32) +#else +#define ABS(x) (x) +#endif +.macro initcode + movw $0x0FC0, %di # move (and jump) to 0FC0:0000 + subw $_startz-_start, USER_IP + movw USER_IP, %ax + shrw $4, %ax # _start MUST be aligned on paragraph + addw USER_CS, %ax # normalize %cs to have _start=0 + movw %ax, %ds +.endm + + .text + .code16 + .org 0 + + .globl _start +_start: + pushf + pushw %cs + stc + call init # the start code will be overwritten by the input buffer +_startz: + +#ifdef INPUTBUFFER +isinit: + initcode + addw $FIXSP, USER_SP # adjust SP with [FLAGS CS IP DS ES [FS GS]] size + pushib setvectors + jmp moveself +#endif + +setvectors: + xorw %si, %si # set interrupt vectors in 0 segment + movw %si, %ds + movb $0x7D, %cl # skip nmi +hooklp: # interrupts: 0=div0 1=step 2=nmi 3=brk 4=ov 5=bound 6=invalid + pushw %cs + pushib ABS(dbgstart) # set %cs:dbgstart + popl (%si) # to interrupt vector +skiphook: + lodsl # %si += 4 + shrb $1,%cl + jnc skiphook + jnz hooklp # note %cx will be cleared: SP will be untouched + decw (3-7)*4(%si) # update int3 vector + jmp dbgstartz # registers are already pushed by startup code + +regs: + .ascii "ss" + .ascii "di" + .ascii "si" + .ascii "bp" + .ascii "sp" + .ascii "bx" + .ascii "dx" + .ascii "cx" + .ascii "ax" +#ifdef REGS32 + .ascii "gs" + .ascii "fs" +#endif + .ascii "es" + .ascii "ds" + .ascii "ip" + .ascii "cs" +# Bit Label Desciption +# --------------------------- +# 0 CF Carry flag +# 2 PF Parity flag +# 4 AF Auxiliary carry flag +# 6 ZF Zero flag +# 7 SF Sign flag +# 8 TF Trap flag +# 9 IF Interrupt enable flag +# 10 DF Direction flag +# 11 OF Overflow flag +#ifdef REGS32 + .ascii "odi|sz|a|p|c" # flags bits +#else + .ascii "oditsz?a?p c=" # flags bits +#endif +# 12-13 IOPL I/O Priviledge level +# 14 NT Nested task flag +# 16 RF Resume flag +# 17 VM Virtual 8086 mode flag +# 18 AC Alignment check flag (486+) +# 19 VIF Virutal interrupt flag +# 20 VIP Virtual interrupt pending flag +# 21 ID ID flag + +#ifdef INPUTBUFFER +ismove: + pushw %ax +moveself: + popw %si +#else +isinit: + initcode +ismove: + xorw %si, %si +#endif + movw %di, %es # move code to %di:0 + pushw %di +#ifdef INPUTBUFFER + xorw %di, %di # and jmp into (%di:setvectors) with retf +#else + movw $setvectors, %di # and jmp into (%di:setvectors) with retf + movw %di, %si +#endif + movw $_end-setvectors, %cx + pushw %di + rep movsb + retf + +int3: + .byte 0x68 # push $0x086A OV UP DI NT PL ZR - NA - PO - NC +# interrupt entry point: the registers [FLAGS CS IP] are already pushed +dbgstart: + .byte 0x6A, 0x08 # push $0x08 NV UP DI NT PL NZ - NA - PO - NC + popf +init: + pushw %ds + pushw %es +#ifdef REGS32 + pushw %fs + pushw %gs + pushal # [FLAGS CS IP DS ES FS GS] EAX ECX EDX EBX ESP EBP ESI EDI [SS] +#else + pushaw # [FLAGS CS IP DS ES] AX CX DX BX SP BP SI DI [SS] +#endif + pushw %ss + movw %sp, %bp +#ifndef INPUTBUFFER + pushf + addw $FIXSP, USER_SP # adjust SP with [FLAGS CS IP DS ES [FS GS]] size + popf +#endif + jc isinit + jnz notint3 + decw USER_IP + lesw USER_CSIP, %di +#define OPCODE_BRK 0xCC + .byte 0xB0 # movb $IM, %al +break: + .byte 0xCC + stosb +notint3: +#ifdef INPUTBUFFER + addw $FIXSP, USER_SP # adjust SP with [FLAGS CS IP DS ES [FS GS]] size +#endif +dbgstartz: +dbgregslp: + pushw %cs + popw %ds + movw $ABS(regs), %si +#ifdef REGS32 + subw %si, %bp + movw $15, %cx +#else + movw $13, %cx +#endif +regslp: + call putreg # display register name and value + loop regslp +#ifdef REGS32 + movw (%bp,%si), %dx # get flags +#else + movw USER_FLAGS, %dx + pushw %si + stc # add trailing = +#endif + movb $13, %cl + rcrw %cl, %dx +nextbit: + lodsb + shlw $1, %dx +#ifdef REGS32 + jnc skipflag + cmpb $'|', %al # remove system flags + je skipflag + call dbgputc +skipflag: +#else + call dbgputcbit # display active flags bits +#endif + loop nextbit +#ifdef REGS32 + movw %sp, %bp +#else + popw %si + movb $8, %cl +stacklp: + lodsw # si += 2 + call putr16 # display flags and the beginning of the stack + loop stacklp +#endif + call getline + lodsb + xchgw %ax, %di + call getval + .byte 0x81, 0xC3 # addw $0, %bx +offset_value: + .word 0 + movw %bx, %es + xchgw %ax, %di + subb $'m', %al + je ismove + subb $'+'-'m', %al + jne not_offset + movw %di, ABS(offset_value) +not_offset: + orb $1, USER_FLAGS_HIGH # set TF + subb $'t'-'+', %al + je done + subb $'d'-'t', %al + xchgw %ax, %cx + jcxz dump # 'd' ? + loop noenter # 'e' ? +nextval: + call getval + jcxz dbgregslp + xchgb %dl, %dh +mextmsb: + stosb + xchgw %ax, %dx + xchgb %al, %dh +#ifdef ADJESDI + call adjustESDI +#endif + decw %cx + loopne mextmsb + jmp nextval +noenter: + loop not_floppy_load # f DX:CX ? + movw %es, %dx + movw %cx, %es + movw %di, %cx + movw $0x0201, %ax + movw $0x7C00, %bx + pushw %bx + int $0x13 + popw %di +godbgregslpifc: + jc dbgregslp +dump: + movw %es, %ax + call putax + movw %di, %ax + call putax + movw $16, %cx +dhex: + movb %es:(%di), %ah +#ifdef ASCIIDUMP + movb %ah, (%si) + incw %si +#endif +#ifdef ADJESDI + call incESDI +#else + incw %di +#endif +#ifdef REGS32 + movb $0x30, %dh # the data has 2 digits +#else + movb $0x01, %dh # the data has 2 digits +#endif + call putx + loop dhex +#ifdef ASCIIDUMP + movb $16, %cl + subw %cx, %si +dascii: + lodsb + cmpb $0x7F, %al + jnc skipascii + cmpb $0x20, %al + cmc +skipascii: + call dbgputcbit + loop dascii +#endif + call dbgputcr + int $0x16 + cmpb $13, %al + je dump +notdump: +not_floppy_load: + stc + loop godbgregslpifc # g ? +isgo: + andb $0xfe, USER_FLAGS_HIGH # clear TF + xchgw %ax, %cx + jcxz done +setbreak: + movb $OPCODE_BRK, %al + xchgb %al, %es:(%di) + movb %al, ABS(break) +done: + popw %ax # %ss +#ifdef REGS32 + popal + popw %gs + popw %fs +#else + popaw +#endif + popw %es + popw %ds + iret + +#ifdef ADJESDI +adjustESDI: + decw %di +incESDI: + incw %di + jnz esok + pushw %es + addb $0x10,-3(%bp) + popw %es +esok: + ret +#endif + +putreg: + call dbgput2c + movb $'=', %al + call dbgputc +putr16: +#ifdef REGS32 + movl -2(%bp,%si), %eax + movw $0x3FC0, %dx # check bits 7..14 + shrw %cl, %dx +putax: + movb $0xF0, %dh # the data has 4 digits + jnc putx # 16 bits register ? + incw %bp # a 32 bits register, not 16 bits + incw %bp + movb $0xFF, %dh # the data has 8 digits + jmp putx +putxlp: +#else +# movw _start-ABS(regs)-2(%bp,%si), %ax + .byte 0x8b, 0x42, _start-ABS(regs)-2 +putax: + movb $0x07, %dh # the data has 4 digits +putx: +putxlp: + rolw $4, %ax +#endif + pushw %ax + andb $0xf, %al + addb $0x90, %al + daa + adcb $0x40, %al + daa + call dbgputc + popw %ax +#ifdef REGS32 +putx: + roll $4, %eax +#endif + shrb $1, %dh + jc putxlp +#ifdef REGS32 + jnz putx +#endif +dbgputcbit: + jc dbgputc + mov $0x20, %al +dbgputc: + movw $7, %bx + mov $0xE, %ah + int $0x10 + xchgw %ax, %bx +# clc # for putax + ret + +# get value in DX:AX, BX is segment CX is digits count. +getval: + xorw %ax, %ax + xorw %bx, %bx + xorw %cx, %cx +getvalz: + xchgw %ax, %bx + cwd + decw %cx +isx: + shll $4, %edx + orb %al, %dl + incw %cx +gotspc: +getvallp: + lodsb + cmpb $0x20, %al # heat heading spaces + jne notspc + jcxz gotspc +notspc: + subb $'0', %al + cmpb $10, %al # in 0..9 ? + jb isx + subb $'a'-'0'-10, %al + cmpb $16, %al # in a..f ? + jb isx + cmpb $':'-'a'+10, %al + pushl %edx + popw %ax + popw %dx + je getvalz # store segment in %bx + pushw %dx + shlw $12, %dx + orw %dx, %bx + popw %dx + ret + +getline: + call dbgputcr +getlinebs: + cmpw $ABS(buffer), %si + je getc + decw %si +getlinelp: + call dbgputc +getc: + int $0x16 + cmpb $8, %al + je getlinebs + orb $0x20, %al + movb %al, (%si) + inc %si + cmpb $0x2D, %al + jne getlinelp +dbgputcr: + movw $ABS(crlf), %si +dbgput2c: + call dbgput1c +dbgput1c: + lodsb + jmp dbgputc + +crlf: + .byte 13,10 +_end: +buffer: + + .org 510 + .byte 0x55, 0xAA + diff -r 8eec3c4a2a20 -r 289a2f495cd6 runcom/stuff/debug.com Binary file runcom/stuff/debug.com has changed diff -r 8eec3c4a2a20 -r 289a2f495cd6 runcom/stuff/debug8086.S --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runcom/stuff/debug8086.S Sat Feb 18 09:00:05 2012 +0100 @@ -0,0 +1,477 @@ +// Usage: +// +// f DX:CX load one CHS sector to 0000:7C00 +// t trace one step +// g
go to adrs +// d
display 16 bytes, CR for next 16 bytes... +// e
... enter memory byte/word/dword +// m self move +// + default segment offset +// +// Example: +// m 0FC0 move debugger to 0FC0:0000 0FC0:01FF +// f 1 read floppy boot sector to 0000:7C00 +// f 80 1 read hard disk master boot sector to 0000:7C00 +// g 7C0E ... + +#define ADJESDI 16 bytes add segment overflow support for e and d +#define ASCIIDUMP 20 bytes display hexa and ascii datas +#define INPUTBUFFER 2 bytes overload init code with a 32+ bytes input buffer +//#define CPU186 -26 bytes 80186+ CPU + +.macro pusham + pushw %ax + pushw %cx + pushw %dx + pushw %bx + movw %sp, %bx + leaw 14(%bx), %bx # adjust SP with [FLAGS CS IP AX CX DX BX] size + pushw %bx # %sp + pushw %bp + pushw %si + pushw %di +.endm + +.macro popam + popw %di + popw %si + popw %bp + popw %ax # %sp + popw %bx + popw %dx + popw %cx + popw %ax +.endm + +#ifdef INPUTBUFFER +//#define ABS(x) (x-(setvectors-_start)) +#define ABS(x) (x-30) +#else +#define ABS(x) (x) +#endif + +#define SEGREGSZ 6 +#define REGSZ 16 +#define USER_SP REGSZ+SEGREGSZ-10(%bp) +#define USER_FLAGS REGSZ+SEGREGSZ+4(%bp) +#define USER_FLAGS_HIGH REGSZ+SEGREGSZ+5(%bp) +#define USER_IP REGSZ+SEGREGSZ(%bp) +#define USER_CS REGSZ+SEGREGSZ+2(%bp) +#define USER_CSIP REGSZ+SEGREGSZ(%bp) + +.macro initcode + movw $0x0FC0, %di # move (and jump) to 0FC0:0000 + subw $_startz-_start, USER_IP + movw USER_IP, %ax +#ifdef CPU186 + cld # ensure movsb will work + shrw $4, %ax # _start MUST be aligned on paragraph +#else + movb $4, %cl + shrw %cl, %ax # _start MUST be aligned on paragraph +#endif + addw USER_CS, %ax # normalize %cs to have _start=0 + movw %ax, %ds +.endm + .text + .code16 +#ifdef CPU186 + .arch i186 +#else + .arch i8086 +#endif + .org 0 + + .globl _start +_start: + pushf + pushw %cs + stc + call init # the start code will be overwritten by the input buffer +_startz: + +#ifdef INPUTBUFFER +isinit: + initcode + movw $setvectors, %si + jmp moveself +#endif + +setvectors: + xorw %si, %si # set interrupt vectors in 0 segment + movw %si, %ds + movb $0xF9, %ch # skip nmi +hooklp: # interrupts: 0=div0 1=step 2=nmi 3=brk 4=ov 5=bound 6=invalid + movw $ABS(dbgstart), (%si) # set %cs:dbgstart + lodsw # %si += 2 + movw %cs, (%si) # to interrupt vector +skiphook: + lodsw # %si += 2 + shrb $1,%ch + jnc skiphook + jnz hooklp # note %cx will be cleared: SP will be untouched +#ifdef CPU186 + decw (3-7)*4(%si) # update int3 vector +#else + movb $ABS(int3), (3-7)*4(%si) # update int3 vector +#endif + jmp dbgstartz # registers are already pushed by startup code + +regs: + .ascii "ss" + .ascii "es" + .ascii "ds" + .ascii "di" + .ascii "si" + .ascii "bp" + .ascii "sp" + .ascii "bx" + .ascii "dx" + .ascii "cx" + .ascii "ax" + .ascii "ip" + .ascii "cs" +# Bit Label Desciption +# --------------------------- +# 0 CF Carry flag +# 2 PF Parity flag +# 4 AF Auxiliary carry flag +# 6 ZF Zero flag +# 7 SF Sign flag +# 8 TF Trap flag +# 9 IF Interrupt enable flag +# 10 DF Direction flag +# 11 OF Overflow flag + .ascii "oditsz?a?p c=" # flags bits + +int3: +#ifdef CPU186 + .byte 0x68 # push $0x086A OV UP DI NT PL ZR - NA - PO - NC +# interrupt entry point: the registers [FLAGS CS IP] are already pushed +dbgstart: + .byte 0x6A, 0x08 # push $0x08 NV UP DI NT PL NZ - NA - PO - NC + popf +init: + pushaw # [FLAGS CS IP] AX CX DX BX SP BP SI DI [DS ES SS] +#else + stc + .byte 0x73 # jnc +# interrupt entry point: the registers [FLAGS CS IP] are already pushed +dbgstart: + clc + pushw %ax + sbbw %ax,%ax # copy CF to SF + clc + popw %ax +init: + cld # ensure movsb will work + pusham # [FLAGS CS IP] AX CX DX BX SP BP SI DI [DS ES SS] +#endif + pushw %ds + pushw %es + pushw %ss + movw %sp, %bp +#ifdef CPU186 + pushf + addw $6, USER_SP # adjust SP with [FLAGS CS IP] size + popf +#endif + jc isinit + jns notint3 + decw USER_IP + lesw USER_CSIP, %di +#define OPCODE_BRK 0xCC + .byte 0xB0 # movb $IM, %al +break: + .byte 0xCC + stosb +notint3: +dbgstartz: +dbgregslp: + call getcmd + .byte 0x81, 0xC3 # addw $0, %bx +offset_value: + .word 0 + movw %bx, %es + xchgw %ax, %di + subb $'m', %al + jne isinotmove +#ifdef INPUTBUFFER +ismove: + xchgw %ax, %si +moveself: +#else +isinit: + jmp ismove + initcode +ismove: +#endif + movw %di, %es # move code to %di:0 + pushw %di +#ifdef INPUTBUFFER + xorw %di, %di # and jmp into (%di:setvectors) with retf +#else + movw $setvectors, %di # and jmp into (%di:setvectors) with retf + movw %di, %si +#endif + movw $_end-setvectors, %cx + pushw %di + rep movsb + retf + +isinotmove: + subb $'+'-'m', %al + jne not_offset + movw %di, ABS(offset_value) +not_offset: + orb $1, USER_FLAGS_HIGH # set TF + subb $'t'-'+', %al + je done + subb $'d'-'t', %al + xchgw %ax, %cx + jcxz dump # 'd' ? + loop noenter # 'e' ? +nextval: + call getval + jcxz dbgregslp + xchgb %dl, %dh +mextmsb: + stosb + xchgw %ax, %dx + xchgb %al, %dh +#ifdef ADJESDI + call adjustESDI +#endif + decw %cx + loopne mextmsb + jmp nextval +noenter: + loop not_floppy_load # f DX:CX ? + movw %es, %dx + movw %cx, %es + movw %di, %cx + movw $0x0201, %ax + movw $0x7C00, %bx + pushw %bx + int $0x13 + popw %di +godbgregslpifc: + jc dbgregslp +dump: + movw %es, %ax + call putax + movw %di, %ax + call putax + movw $16, %cx +dhex: + movb %es:(%di), %ah +#ifdef ASCIIDUMP + movb %ah, (%si) + incw %si +#endif +#ifdef ADJESDI + call incESDI +#else + incw %di +#endif + movb $0x01, %dh # the data has 2 digits + call putx + loop dhex +#ifdef ASCIIDUMP + movb $16, %cl + subw %cx, %si +dascii: + lodsb + cmpb $0x7F, %al + jnc skipascii + cmpb $0x20, %al + cmc +skipascii: + call dbgputcbit + loop dascii +#endif + call dbgputcr + int $0x16 + cmpb $13, %al + je dump +notdump: +not_floppy_load: + stc + loop godbgregslpifc # g ? +isgo: + andb $0xfe, USER_FLAGS_HIGH # clear TF + xchgw %ax, %cx + jcxz done +setbreak: + movb $OPCODE_BRK, %al + xchgb %al, %es:(%di) + movb %al, ABS(break) +done: + popw %ax # %ss + popw %es + popw %ds +#ifdef CPU186 + popaw +#else + popam +#endif + iret + +#ifdef ADJESDI +adjustESDI: + decw %di +incESDI: + incw %di + jnz esok + pushw %es + addb $0x10,-3(%bp) + popw %es +esok: + ret +#endif + +putreg: + call dbgput2c + movb $'=', %al + call dbgputc +putr16: +# movw _start-ABS(regs)-2(%bp,%si), %ax + .byte 0x8b, 0x42, _start-ABS(regs)-2 +putax: + movb $0x07, %dh # the data has 4 digits +putx: +putxlp: +#ifdef CPU186 + rolw $4, %ax +#else + pushw %cx + movb $4, %cl + rolw %cl, %ax + popw %cx +#endif + pushw %ax + andb $0xf, %al + addb $0x90, %al + daa + adcb $0x40, %al + daa + call dbgputc + popw %ax + shrb $1, %dh + jc putxlp +dbgputcbit: + jc dbgputc + mov $0x20, %al +dbgputc: + movw $7, %bx + mov $0xE, %ah + int $0x10 + xchgw %ax, %bx + ret + +getline: + movw $ABS(regs), %si + movw $13, %cx +regslp: + call putreg # display register name and value + loop regslp + movw USER_FLAGS, %dx + pushw %si + movb $13, %cl + stc # add trailing = + rcrw %cl, %dx +nextbit: + lodsb + shlw $1, %dx + call dbgputcbit # display active flags bits + loop nextbit + popw %si + movb $8, %cl +stacklp: + lodsw # si += 2 + call putr16 # display flags and the beginning of the stack + loop stacklp + call dbgputcr +getlinebs: + cmpw $ABS(buffer), %si + je getc + decw %si +getlinelp: + call dbgputc +getc: + int $0x16 + cmpb $8, %al + je getlinebs + orb $0x20, %al + movb %al, (%si) + inc %si + cmpb $0x2D, %al + jne getlinelp +dbgputcr: + movw $ABS(crlf), %si +dbgput2c: + call dbgput1c +dbgput1c: + lodsb + jmp dbgputc + +getcmd: + pushw %cs + popw %ds + call getline + lodsb + xchgw %ax, %di +# get value in DX:AX, BX is segment CX is digits count. +getval: + xorw %bx, %bx + xorw %cx, %cx +getvalz: + pushw %bx # save segment + xorw %bx, %bx + mul %bx # clear %dx:%ax + decw %cx +isx: + incw %cx + orb $0xE0, %dh +getvalbit: + shlw $1, %bx + rclw $1, %dx + jc getvalbit + orb %al, %bl +gotspc: + lodsb + cmpb $0x20, %al # space ? + jne notspc + jcxz gotspc +notspc: + sub $'0', %al + cmpb $10, %al # in 0..9 ? + jb isx + sub $'a'-'0'-10, %al + cmpb $16, %al # in a..f ? + jb isx + cmpb $':'-'a'+10, %al + popw %ax + je getvalz # store segment in %bx + xchgw %ax, %bx + pushw %dx +#ifdef CPU186 + shlw $12, %dx +#else + pushw %cx + movb $12, %cl + shlw %cl, %dx + popw %cx +#endif + addw %dx, %bx + popw %dx + ret + +crlf: + .byte 13,10 +_end: +buffer: + + .org 510 + .byte 0x55, 0xAA + diff -r 8eec3c4a2a20 -r 289a2f495cd6 runcom/stuff/runcom.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runcom/stuff/runcom.c Sat Feb 18 09:00:05 2012 +0100 @@ -0,0 +1,1364 @@ +/* + * Simple example of use of vm86: launch a basic .com DOS executable + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//#define DUMP_INT21 + +static inline int vm86(int func, struct vm86plus_struct *v86) +{ + return syscall(__NR_vm86, func, v86); +} + +#define CF_MASK 0x00000001 +#define ZF_MASK 0x00000040 +#define TF_MASK 0x00000100 +#define IF_MASK 0x00000200 +#define DF_MASK 0x00000400 +#define IOPL_MASK 0x00003000 +#define NT_MASK 0x00004000 +#define RF_MASK 0x00010000 +#define VM_MASK 0x00020000 +#define AC_MASK 0x00040000 +#define VIF_MASK 0x00080000 +#define VIP_MASK 0x00100000 +#define ID_MASK 0x00200000 + +void usage(void) +{ + printf("runcom version 0.2-slitaz (c) 2003-2011 Fabrice Bellard\n" + "usage: runcom file.com [args...]\n" + "Run simple .com DOS executables (linux vm86 test mode)\n"); + exit(1); +} + +static inline void set_bit(uint8_t *a, unsigned int bit) +{ + a[bit / 8] |= (1 << (bit % 8)); +} + +static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg) +{ + return (uint8_t *)((seg << 4) + (reg & 0xffff)); +} + +static inline void pushw(struct vm86_regs *r, int val) +{ + r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff); + *(uint16_t *)seg_to_linear(r->ss, r->esp) = val; +} + +void dump_regs(struct vm86_regs *r) +{ + int i; + uint8_t *p8; + uint16_t *p16; + fprintf(stderr, + "EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n" + "ESI=%08lx EDI=%08lx EBP=%08lx ESP=%08lx\n" + "EIP=%08lx EFL=%08lx " + "CS=%04x DS=%04x ES=%04x SS=%04x FS=%04x GS=%04x\n[SP]", + r->eax, r->ebx, r->ecx, r->edx, r->esi, r->edi, r->ebp, r->esp, + r->eip, r->eflags, + r->cs, r->ds, r->es, r->ss, r->fs, r->gs); + for (p16 = (uint16_t *) seg_to_linear(r->ss, r->esp), i = 0; i < 15; i++) + fprintf(stderr," %04x", *p16++); + fprintf(stderr,"\n[IP]"); + for (p8 = seg_to_linear(r->cs, r->eip), i = 0; i < 25; i++) + fprintf(stderr," %02x", *p8++); + fprintf(stderr,"\n"); +} + +#define DOS_FD_MAX 256 +typedef struct { + int fd; /* -1 means closed */ +} DOSFile; + +DOSFile dos_files[DOS_FD_MAX]; +uint16_t cur_psp; + +void dos_init(void) +{ + int i; + for(i = 0; i < DOS_FD_MAX; i++) + dos_files[i].fd = (i < 3) ? i : -1; +} + +static inline void set_error(struct vm86_regs *r, int val) +{ + r->eflags &= ~CF_MASK; + if (val) { + r->eax = (r->eax & ~0xffff) | val; + r->eflags |= CF_MASK; + } +} +static DOSFile *get_file(int h) +{ + DOSFile *fh; + + if (h < DOS_FD_MAX) { + fh = &dos_files[h]; + if (fh->fd != -1) + return fh; + } + return NULL; +} + +/* return -1 if error */ +static int get_new_handle(void) +{ + DOSFile *fh; + int i; + + for(i = 0; i < DOS_FD_MAX; i++) { + fh = &dos_files[i]; + if (fh->fd == -1) + return i; + } + return -1; +} + +static char *get_filename1(struct vm86_regs *r, char *buf, int buf_size, + uint16_t seg, uint16_t offset) +{ + char *q; + int c; + q = buf; + for(;;) { + c = *seg_to_linear(seg, offset); + if (c == 0) + break; + if (q >= buf + buf_size - 1) + break; + c = tolower(c); + if (c == '\\') + c = '/'; + *q++ = c; + offset++; + } + *q = '\0'; + return buf; +} + +static char *get_filename(struct vm86_regs *r, char *buf, int buf_size) +{ + return get_filename1(r, buf, buf_size, r->ds, r->edx & 0xffff); +} + +typedef struct __attribute__((packed)) { + uint8_t drive_num; + uint8_t file_name[8]; + uint8_t file_ext[3]; + uint16_t current_block; + uint16_t logical_record_size; + uint32_t file_size; + uint16_t date; + uint16_t time; + uint8_t reserved[8]; + uint8_t record_in_block; + uint32_t record_num; +} FCB; + +typedef struct __attribute__((packed)) { + uint16_t environ; + uint16_t cmdtail_off; + uint16_t cmdtail_seg; + uint32_t fcb1; + uint32_t fcb2; + uint16_t sp, ss; + uint16_t ip, cs; +} ExecParamBlock; + +typedef struct MemBlock { + struct MemBlock *next; + uint16_t seg; + uint16_t size; /* in paragraphs */ +} MemBlock; + +/* first allocated paragraph */ +MemBlock *first_mem_block = NULL; + +#define MEM_START 0x1000 +#define MEM_END 0xa000 + +/* return -1 if error */ +int mem_malloc(int size, int *pmax_size) +{ + MemBlock *m, **pm; + int seg_free, seg; + + /* XXX: this is totally inefficient, but we have only 1 or 2 + blocks ! */ + seg_free = MEM_START; + for(pm = &first_mem_block; *pm != NULL; pm = &(*pm)->next) { + m = *pm; + seg = m->seg + m->size; + if (seg > seg_free) + seg_free = seg; + } + if ((seg_free + size) > MEM_END) + return -1; + if (pmax_size) + *pmax_size = MEM_END - seg_free; + /* add at the end */ + m = malloc(sizeof(MemBlock)); + *pm = m; + m->next = NULL; + m->seg = seg_free; + m->size = size; +#ifdef DUMP_INT21 + printf("mem_malloc size=0x%04x: 0x%04x\n", size, seg_free); +#endif + return seg_free; +} + +/* return -1 if error */ +int mem_free(int seg) +{ + MemBlock *m, **pm; + for(pm = &first_mem_block; *pm != NULL; pm = &(*pm)->next) { + m = *pm; + if (m->seg == seg) { + *pm = m->next; + free(m); + return 0; + } + } + return -1; +} + +/* return -1 if error or the maxmium size */ +int mem_resize(int seg, int new_size) +{ + MemBlock *m, **pm, *m1; + int max_size; + + for(pm = &first_mem_block; *pm != NULL; pm = &(*pm)->next) { + m = *pm; + if (m->seg == seg) { + m1 = m->next; + if (!m1) + max_size = MEM_END - m->seg; + else + max_size = m1->seg - m->seg; + if (new_size > max_size) + return -1; + m->size = new_size; + return max_size; + } + } + return -1; +} + +int load_boot(const char *filename, struct vm86_regs *r) +{ + int fd, ret; + + /* load the boot sector */ + fd = open(filename, O_RDONLY); + if (fd >= 0) { + *seg_to_linear(0x0, 0x7dff) = 0; + r->eax = 0x200; + r->ebx = r->esp = r->eip = 0x7c00; + r->ecx = 1; + r->esi = r->edi = r->ebp = + r->edx = 0; /* floppy disk */ + r->cs = r->ss = r->ds = r->es = 0; + r->eflags = VIF_MASK; + ret = read(fd, seg_to_linear(0x0, 0x7c00), 0x200); + if (lseek(fd, 0, SEEK_END) > 4*1024*1024) + r->edx = 0x80; /* hard disk */ + close(fd); + if (ret != 0x200 || + *seg_to_linear(0x0, 0x7dfe) != 0x55 || + *seg_to_linear(0x0, 0x7dff) != 0xaa) { + fprintf(stderr,"No boot sector.\n"); + fd = -1; + } + } + return fd; +} + +/* return the PSP or -1 if error */ +int load_exe(ExecParamBlock *blk, const char *filename, + int psp, uint32_t *pfile_size) +{ + int fd, size, base; + struct { + uint16_t signature; // 0x5A4D 'MZ' + uint16_t bytes_in_last_block; + uint16_t blocks_in_file; + uint16_t num_relocs; + uint16_t header_paragraphs; // Size of header + uint16_t min_extra_paragraphs; // BSS size + uint16_t max_extra_paragraphs; + uint16_t ss; // Initial (relative) SS value + uint16_t sp; // Initial SP value + uint16_t checksum; + uint16_t ip; // Initial IP value + uint16_t cs; // Initial (relative) CS value + uint16_t reloc_table_offset; + uint16_t overlay_number; + } header; + struct { + uint16_t offset; + uint16_t segment; + } rel; + + /* load the MSDOS .exe executable */ + fd = open(filename, O_RDONLY); + if (fd < 0) { + return -1; + } + if (read(fd, &header, sizeof(header)) != sizeof(header)) { + close(fd); + return -1; + } + + memset(seg_to_linear(psp, 0x100), 0, 65536 - 0x100); + + size = (header.blocks_in_file * 512) - (header.header_paragraphs * 16) + + (header.bytes_in_last_block ? header.bytes_in_last_block - 512 : 0); + header.min_extra_paragraphs += (size-1)/16; + + /* address of last segment allocated */ + *(uint16_t *)seg_to_linear(psp, 2) = psp + header.min_extra_paragraphs; + + if (pfile_size) + *pfile_size = size; + + if (mem_resize(psp, header.min_extra_paragraphs) < 0 || + lseek(fd, header.header_paragraphs * 16, SEEK_SET) < 0 || + read(fd, seg_to_linear(psp, 0x100), size) != size || + lseek(fd, header.reloc_table_offset, SEEK_SET) < 0) { + close(fd); + return -1; + } + + base = psp + 16; + while (header.num_relocs-- && read(fd, &rel, sizeof(rel)) == sizeof(rel)) + if (rel.segment != 0 || rel.offset != 0) + * (uint16_t *) seg_to_linear(base + rel.segment, rel.offset) += base; + close(fd); + + blk->cs = base + header.cs; + blk->ip = header.ip; + blk->ss = base + header.ss; + blk->sp = header.sp - 6; + + /* push return far address */ + *(uint16_t *)seg_to_linear(blk->ss, blk->sp + 4) = psp; + + return psp; +} + +/* return the PSP or -1 if error */ +int load_com(ExecParamBlock *blk, const char *filename, uint32_t *pfile_size, + int argc, char **argv) +{ + int psp, fd, ret; + + /* load the MSDOS .com executable */ + fd = open(filename, O_RDONLY); + if (fd < 0) { + return -1; + } + psp = mem_malloc(65536 / 16, NULL); + ret = read(fd, seg_to_linear(psp, 0x100), 65536 - 0x100); + close(fd); + if (ret <= 0) { + mem_free(psp); + return -1; + } + if (pfile_size) + *pfile_size = ret; + + /* reset the PSP */ + memset(seg_to_linear(psp, 0), 0, 0x100); + + *seg_to_linear(psp, 0) = 0xcd; /* int $0x20 */ + *seg_to_linear(psp, 1) = 0x20; + /* address of last segment allocated */ + *(uint16_t *)seg_to_linear(psp, 2) = psp + 0xfff; + + if (argc) { + int i, p; + char *s; + /* set the command line */ + p = 0x81; + for(i = 2; i < argc; i++) { + if (p >= 0xff) + break; + *seg_to_linear(psp, p++) = ' '; + s = argv[i]; + while (*s) { + if (p >= 0xff) + break; + *seg_to_linear(psp, p++) = *s++; + } + } + *seg_to_linear(psp, p) = '\r'; + *seg_to_linear(psp, 0x80) = p - 0x81; + } + else { + int len; + /* copy the command line */ + len = *seg_to_linear(blk->cmdtail_seg, blk->cmdtail_off); + memcpy(seg_to_linear(psp, 0x80), + seg_to_linear(blk->cmdtail_seg, blk->cmdtail_off), len + 2); + } + + blk->sp = 0xfffc; + blk->ip = 0x100; + blk->cs = blk->ss = psp; + + if (*(uint16_t *)seg_to_linear(psp, 0x100) == 0x5A4D) + psp = load_exe(blk, filename, psp, pfile_size); + + /* push ax value */ + *(uint16_t *)seg_to_linear(blk->ss, blk->sp) = 0; + /* push return address to 0 */ + *(uint16_t *)seg_to_linear(blk->ss, blk->sp + 2) = 0; + + return psp; +} + + +void unsupported_function(struct vm86_regs *r, uint8_t num, uint8_t ah) +{ + fprintf(stderr, "int 0x%02x: unsupported function 0x%02x\n", num, ah); + dump_regs(r); + set_error(r, 0x01); /* function number invalid */ +} + +/* Open hard disk image ./hd[0-7] / floppy image ./fd[0-7] or /dev/fd[0-7] */ +int open_disk(struct vm86_regs *r) +{ + int fd = -1, drive = r->edx & 0xff; + char filename[9], n = '0' + (drive & 7); + if (drive > 127) { + strcpy(filename,"hd0"); + filename[2] = n; + } + else { + strcpy(filename,"/dev/fd0"); + filename[7] = n; + fd = open(filename+5, O_RDONLY); + } + if (fd < 0) + fd = open(filename, O_RDONLY); + return fd; +} + + +void read_sectors(int fd, struct vm86_regs *r, int first_sector, + int sector_count, void *buffer) +{ + int drive = r->edx & 0xff; + r->eax &= ~0xff00; + r->eax |= 0x0400; /* sector not found */ + r->eflags |= CF_MASK; + if (fd >= 0) { + static struct stat st; + first_sector <<= 9; + sector_count <<= 9; + if (drive < 8 && fstat(fd, &st) == 0) { + static ino_t inodes[8]; + ino_t last = inodes[drive]; + inodes[drive] = st.st_ino; + if (last && last != st.st_ino) { + set_error(r, 0x0600); /* floppy disk swap */ + goto failed; + } + } + if (lseek(fd, first_sector, SEEK_CUR) >= 0 && + read(fd, buffer, sector_count) == sector_count) { + r->eax &= ~0xff00; + r->eflags &= ~CF_MASK; + } + failed: + close(fd); + } +} + +void do_int10(struct vm86_regs *r) +{ + uint8_t ah; + + ah = (r->eax >> 8); + switch(ah) { + case 0x0E: /* write char */ + { + uint8_t c = r->eax; + write(1, &c, 1); + } + break; + default: + unsupported_function(r, 0x10, ah); + } +} + +void do_int13(struct vm86_regs *r) +{ + uint8_t ah; + + ah = (r->eax >> 8); + switch(ah) { + case 0x00: /* reset disk */ + { + r->eax &= ~0xff00; /* success */ + r->eflags &= ~CF_MASK; + } + break; + case 0x02: /* read disk CHS */ + { + int fd, c, h, s, heads, sectors, cylinders; + long size; + fd = open_disk(r); + if (fd >= 0) { + size = lseek(fd, 0, SEEK_END) / 512; + if ((r->edx & 0xff) > 127) { + sectors = 63; + if (size % sectors) + sectors = 62; + if (size % sectors) + sectors = 32; + if (size % sectors) + sectors = 17; + if (size % sectors) + fd = -1; + size /= sectors; + for (heads = 256; size % heads; heads--); + cylinders = size / heads; + } + else { + int i; + heads = 1 + (size > 256*2); + cylinders = 40 * (1 + (size > 512*2)); + size /= heads; + for (i = 0; i < 5; i++) + if (size % (cylinders + i) == 0) break; + if (i == 5) + fd = -1; + cylinders += i; + sectors = size / cylinders; + } + } + c = ((r->ecx & 0xC0) << 2) | ((r->ecx >> 8) & 0xff); + h = (r->edx >> 8) & 0xff; + s = (r->ecx & 0x3f) -1; + if (fd < 0 || c >= cylinders || h >= heads || s >= sectors) { + set_error(r, 0x0400); /* sector not found */ + break; + } + read_sectors(fd, r, (((c * heads) + h) * sectors) + s, + r->eax & 0xff, seg_to_linear(r->es, r->ebx)); + } + break; + case 0x42: /* read disk LBA */ + { + uint16_t *packet = (uint16_t *) seg_to_linear(r->ds, r-> esi); + uint8_t *to = seg_to_linear(packet[3], packet[2]); + if ((packet[3] & packet[2]) == 0xffff) + to = * (uint8_t **) &packet[8]; + if (packet[0] != 0x0010 && packet[0] != 0x0018) + goto unsupported; + read_sectors(open_disk(r), r, * (uint32_t *) &packet[4], packet[1], to); + } + break; + default: + unsupported: + unsupported_function(r, 0x13, ah); + } +} + +void do_int15(struct vm86_regs *r) +{ + uint8_t ah; + + ah = (r->eax >> 8); + switch(ah) { + case 0x87: /* move memory */ + /* XXX */ + break; + default: + unsupported_function(r, 0x15, ah); + } +} + +void do_int16(struct vm86_regs *r) +{ + static uint16_t last_ax, hold_char; + struct termios termios_def, termios_raw; + uint8_t ah; + + ah = (r->eax >> 8); + tcgetattr(0, &termios_def); + termios_raw = termios_def; + cfmakeraw(&termios_raw); + tcsetattr(0, TCSADRAIN, &termios_raw); + switch(ah) { + case 0x01: /* test keyboard */ + { + int count; + r->eflags &= ~ZF_MASK; + if (hold_char) { + r->eax &= ~0xffff; + r->eax |= last_ax; + break; + } + if (ioctl(0, FIONREAD, &count) < 0 || count == 0) { + r->eflags |= ZF_MASK; + break; + } + hold_char = 2; + } + case 0x00: /* read keyboard */ + { + uint8_t c; + if (hold_char) + hold_char--; + read(0, &c, 1); + if (c == 3) { + tcsetattr(0, TCSADRAIN, &termios_def); + exit(0); + } + if (c == 10) + c = 13; + r->eax &= ~0xffff; + r->eax |= last_ax = c; + /* XXX ah = scan code */ + } + break; + default: + unsupported_function(r, 0x16, ah); + } + tcsetattr(0, TCSADRAIN, &termios_def); +} + +void do_int1a(struct vm86_regs *r) +{ + uint8_t ah; + + ah = (r->eax >> 8); + switch(ah) { + case 0x00: /* GET SYSTEM TIME */ + { + uint16_t *timer = (uint16_t *) seg_to_linear(0, 0x46C); + r->ecx &= ~0xffff; + r->ecx |= *timer++; + r->edx &= ~0xffff; + r->edx |= *timer; + r->eax &= ~0xff; + } + break; + default: + unsupported_function(r, 0x1a, ah); + } +} + +void do_int20(struct vm86_regs *r) +{ + /* terminate program */ + exit(0); +} + +void do_int21(struct vm86_regs *r) +{ + uint8_t ah; + + ah = (r->eax >> 8); + switch(ah) { + case 0x00: /* exit */ + exit(0); + case 0x02: /* write char */ + { + uint8_t c = r->edx; + write(1, &c, 1); + } + break; + case 0x09: /* write string */ + { + uint8_t c; + int offset; + offset = r->edx; + for(;;) { + c = *seg_to_linear(r->ds, offset); + if (c == '$') + break; + write(1, &c, 1); + offset++; + } + r->eax = (r->eax & ~0xff) | '$'; + } + break; + case 0x0a: /* buffered input */ + { + int max_len, cur_len, ret; + uint8_t ch; + uint16_t off; + + /* XXX: should use raw mode to avoid sending the CRLF to + the terminal */ + off = r->edx & 0xffff; + max_len = *seg_to_linear(r->ds, off); + cur_len = 0; + while (cur_len < max_len) { + ret = read(0, &ch, 1); + if (ret < 0) { + if (errno != EINTR && errno != EAGAIN) + break; + } else if (ret == 0) { + break; + } else { + if (ch == '\n') + break; + } + *seg_to_linear(r->ds, off + 2 + cur_len++) = ch; + } + *seg_to_linear(r->ds, off + 1) = cur_len; + *seg_to_linear(r->ds, off + 2 + cur_len) = '\r'; + } + break; + case 0x25: /* set interrupt vector */ + { + uint16_t *ptr; + ptr = (uint16_t *)seg_to_linear(0, (r->eax & 0xff) * 4); + ptr[0] = r->edx; + ptr[1] = r->ds; + } + break; + case 0x29: /* parse filename into FCB */ +#if 0 + /* not really needed */ + { + const uint8_t *p, *p_start; + uint8_t file[8], ext[3]; + FCB *fcb; + int file_len, ext_len, has_wildchars, c, drive_num; + + /* XXX: not complete at all */ + fcb = (FCB *)seg_to_linear(r->es, r->edi); + printf("ds=0x%x si=0x%lx\n", r->ds, r->esi); + p_start = (const uint8_t *)seg_to_linear(r->ds, r->esi); + + p = p_start; + has_wildchars = 0; + + /* drive */ + if (isalpha(p[0]) && p[1] == ':') { + drive_num = toupper(p[0]) - 'A' + 1; + p += 2; + } else { + drive_num = 0; + } + + /* filename */ + file_len = 0; + for(;;) { + c = *p; + if (!(c >= 33 && c <= 126)) + break; + if (c == '.') + break; + if (c == '*' || c == '?') + has_wildchars = 1; + if (file_len < 8) + file[file_len++] = c; + } + memset(file + file_len, ' ', 8 - file_len); + + /* extension */ + ext_len = 0; + if (*p == '.') { + for(;;) { + c = *p; + if (!(c >= 33 && c <= 126)) + break; + if (c == '*' || c == '?') + has_wildchars = 1; + ext[ext_len++] = c; + if (ext_len >= 3) + break; + } + } + memset(ext + ext_len, ' ', 3 - ext_len); + +#if 0 + { + printf("drive=%d file=%8s ext=%3s\n", + drive_num, file, ext); + } +#endif + if (drive_num == 0 && r->eax & (1 << 1)) { + /* keep drive */ + } else { + fcb->drive_num = drive_num; /* default drive */ + } + + if (file_len == 0 && r->eax & (1 << 2)) { + /* keep */ + } else { + memcpy(fcb->file_name, file, 8); + } + + if (ext_len == 0 && r->eax & (1 << 3)) { + /* keep */ + } else { + memcpy(fcb->file_ext, ext, 3); + } + r->eax = (r->eax & ~0xff) | has_wildchars; + r->esi = (r->esi & ~0xffff) | ((r->esi + (p - p_start)) & 0xffff); + } +#endif + break; + case 0x2A: /* get system date */ + { + time_t t = time(NULL); + struct tm *now=localtime(&t); + + r->ecx = now->tm_year; + r->edx = (now->tm_mon * 256) + now->tm_mday; + r->eax = now->tm_wday;; + } + break; + case 0x2C: /* get system time */ + { + time_t t = time(NULL); + struct tm *now=localtime(&t); + struct timeval tim; + + gettimeofday(&tim, NULL); + r->edx = (now->tm_hour * 256) + now->tm_min; + r->edx = (tim.tv_sec * 256) + tim.tv_usec/10000; + } + break; + case 0x30: /* get dos version */ + { + int major, minor, serial, oem; + /* XXX: return correct value for FreeDOS */ + major = 0x03; + minor = 0x31; + serial = 0x123456; + oem = 0x66; + r->eax = (r->eax & ~0xffff) | major | (minor << 8); + r->ecx = (r->ecx & ~0xffff) | (serial & 0xffff); + r->ebx = (r->ebx & ~0xffff) | (serial & 0xff) | (0x66 << 8); + } + break; + case 0x35: /* get interrupt vector */ + { + uint16_t *ptr; + ptr = (uint16_t *)seg_to_linear(0, (r->eax & 0xff) * 4); + r->ebx = (r->ebx & ~0xffff) | ptr[0]; + r->es = ptr[1]; + } + break; + case 0x37: + { + switch(r->eax & 0xff) { + case 0x00: /* get switch char */ + r->eax = (r->eax & ~0xff) | 0x00; + r->edx = (r->edx & ~0xff) | '/'; + break; + default: + goto unsupported; + } + } + break; + case 0x3c: /* create or truncate file */ + { + char filename[1024]; + int fd, h, flags; + + h = get_new_handle(); + if (h < 0) { + set_error(r, 0x04); /* too many open files */ + } else { + get_filename(r, filename, sizeof(filename)); + if (r->ecx & 1) + flags = 0444; /* read-only */ + else + flags = 0777; + fd = open(filename, O_RDWR | O_TRUNC | O_CREAT, flags); +#ifdef DUMP_INT21 + printf("int21: create: file='%s' cx=0x%04x ret=%d\n", + filename, (int)(r->ecx & 0xffff), h); +#endif + if (fd < 0) { + set_error(r, 0x03); /* path not found */ + } else { + dos_files[h].fd = fd; + set_error(r, 0); + r->eax = (r->eax & ~0xffff) | h; + } + } + } + break; + case 0x3d: /* open file */ + { + char filename[1024]; + int fd, h; + + h = get_new_handle(); + if (h < 0) { + set_error(r, 0x04); /* too many open files */ + } else { + get_filename(r, filename, sizeof(filename)); +#ifdef DUMP_INT21 + printf("int21: open: file='%s' al=0x%02x ret=%d\n", + filename, (int)(r->eax & 0xff), h); +#endif + fd = open(filename, r->eax & 3); + if (fd < 0) { + set_error(r, 0x02); /* file not found */ + } else { + dos_files[h].fd = fd; + set_error(r, 0); + r->eax = (r->eax & ~0xffff) | h; + } + } + } + break; + case 0x3e: /* close file */ + { + DOSFile *fh = get_file(r->ebx & 0xffff); +#ifdef DUMP_INT21 + printf("int21: close fd=%d\n", (int)(r->ebx & 0xffff)); +#endif + if (!fh) { + set_error(r, 0x06); /* invalid handle */ + } else { + close(fh->fd); + fh->fd = -1; + set_error(r, 0); + } + } + break; + case 0x3f: /* read */ + { + DOSFile *fh = get_file(r->ebx & 0xffff); + int n, ret; + + if (!fh) { + set_error(r, 0x06); /* invalid handle */ + } else { + n = r->ecx & 0xffff; + for(;;) { + ret = read(fh->fd, + seg_to_linear(r->ds, r->edx), n); + if (ret < 0) { + if (errno != EINTR && errno != EAGAIN) + break; + } else { + break; + } + } +#ifdef DUMP_INT21 + printf("int21: read: fd=%d n=%d ret=%d\n", + (int)(r->ebx & 0xffff), n, ret); +#endif + if (ret < 0) { + set_error(r, 0x05); /* acces denied */ + } else { + r->eax = (r->eax & ~0xffff) | ret; + set_error(r, 0); + } + } + } + break; + case 0x40: /* write */ + { + DOSFile *fh = get_file(r->ebx & 0xffff); + int n, ret, pos; + + if (!fh) { + set_error(r, 0x06); /* invalid handle */ + } else { + n = r->ecx & 0xffff; + if (n == 0) { + /* truncate */ + pos = lseek(fh->fd, 0, SEEK_CUR); + if (pos >= 0) { + ret = ftruncate(fh->fd, pos); + } else { + ret = -1; + } + } else { + for(;;) { + ret = write(fh->fd, + seg_to_linear(r->ds, r->edx), n); + if (ret < 0) { + if (errno != EINTR && errno != EAGAIN) + break; + } else { + break; + } + } + } +#ifdef DUMP_INT21 + printf("int21: write: fd=%d n=%d ret=%d\n", + (int)(r->ebx & 0xffff), n, ret); +#endif + if (ret < 0) { + set_error(r, 0x05); /* acces denied */ + } else { + r->eax = (r->eax & ~0xffff) | ret; + set_error(r, 0); + } + } + } + break; + case 0x41: /* unlink */ + { + char filename[1024]; + get_filename(r, filename, sizeof(filename)); + if (unlink(filename) < 0) { + set_error(r, 0x02); /* file not found */ + } else { + set_error(r, 0); + } + } + break; + case 0x42: /* lseek */ + { + DOSFile *fh = get_file(r->ebx & 0xffff); + int pos, ret; + + if (!fh) { + set_error(r, 0x06); /* invalid handle */ + } else { + pos = ((r->ecx & 0xffff) << 16) | (r->edx & 0xffff); + ret = lseek(fh->fd, pos, r->eax & 0xff); +#ifdef DUMP_INT21 + printf("int21: lseek: fd=%d pos=%d whence=%d ret=%d\n", + (int)(r->ebx & 0xffff), pos, (uint8_t)r->eax, ret); +#endif + if (ret < 0) { + set_error(r, 0x01); /* function number invalid */ + } else { + r->edx = (r->edx & ~0xffff) | ((unsigned)ret >> 16); + r->eax = (r->eax & ~0xffff) | (ret & 0xffff); + set_error(r, 0); + } + } + } + break; + case 0x44: /* ioctl */ + switch(r->eax & 0xff) { + case 0x00: /* get device information */ + { + DOSFile *fh = get_file(r->ebx & 0xffff); + int ret; + + if (!fh) { + set_error(r, 0x06); /* invalid handle */ + } else { + ret = 0; + if (isatty(fh->fd)) { + ret |= 0x80; + if (fh->fd == 0) + ret |= (1 << 0); + else + ret |= (1 << 1); + } + r->edx = (r->edx & ~0xffff) | ret; + set_error(r, 0); + } + } + break; + default: + goto unsupported; + } + break; + case 0x48: /* allocate memory */ + { + int ret, max_size; +#ifdef DUMP_INT21 + printf("int21: allocate memory: size=0x%04x\n", (uint16_t)r->ebx); +#endif + ret = mem_malloc(r->ebx & 0xffff, &max_size); + if (ret < 0) { + set_error(r, 0x08); /* insufficient memory*/ + } else { + r->eax = (r->eax & ~0xffff) | ret; + r->ebx = (r->ebx & ~0xffff) | max_size; + set_error(r, 0); + } + } + break; + case 0x49: /* free memory */ + { +#ifdef DUMP_INT21 + printf("int21: free memory: block=0x%04x\n", r->es); +#endif + if (mem_free(r->es) < 0) { + set_error(r, 0x09); /* memory block address invalid */ + } else { + set_error(r, 0); + } + } + break; + case 0x4a: /* resize memory block */ + { + int ret; +#ifdef DUMP_INT21 + printf("int21: resize memory block: block=0x%04x size=0x%04x\n", + r->es, (uint16_t)r->ebx); +#endif + ret = mem_resize(r->es, r->ebx & 0xffff); + if (ret < 0) { + set_error(r, 0x08); /* insufficient memory*/ + } else { + r->ebx = (r->ebx & ~0xffff) | ret; + set_error(r, 0); + } + } + break; + case 0x4b: /* load program */ + { + char filename[1024]; + ExecParamBlock *blk; + int ret; + + if ((r->eax & 0xff) != 0x01) /* only load */ + goto unsupported; + get_filename(r, filename, sizeof(filename)); + blk = (ExecParamBlock *)seg_to_linear(r->es, r->ebx); + ret = load_com(blk, filename, NULL, 0, NULL); + if (ret < 0) { + set_error(r, 0x02); /* file not found */ + } else { + cur_psp = ret; + set_error(r, 0); + } + } + break; + case 0x4c: /* exit with return code */ + exit(r->eax & 0xff); + break; + case 0x50: /* set PSP address */ +#ifdef DUMP_INT21 + printf("int21: set PSP: 0x%04x\n", (uint16_t)r->ebx); +#endif + cur_psp = r->ebx; + break; + case 0x51: /* get PSP address */ +#ifdef DUMP_INT21 + printf("int21: get PSP: ret=0x%04x\n", cur_psp); +#endif + r->ebx = (r->ebx & ~0xffff) | cur_psp; + break; + case 0x55: /* create child PSP */ + { + uint8_t *psp_ptr; +#ifdef DUMP_INT21 + printf("int21: create child PSP: psp=0x%04x last_seg=0x%04x\n", + (uint16_t)r->edx, (uint16_t)r->esi); +#endif + psp_ptr = seg_to_linear(r->edx & 0xffff, 0); + memset(psp_ptr, 0, 0x80); + psp_ptr[0] = 0xcd; /* int $0x20 */ + psp_ptr[1] = 0x20; + *(uint16_t *)(psp_ptr + 2) = r->esi; + r->eax = (r->eax & ~0xff); + } + break; + default: + unsupported: + unsupported_function(r, 0x21, ah); + } +} + +void do_int29(struct vm86_regs *r) +{ + uint8_t c = r->eax; + write(1, &c, 1); +} + +void raise_interrupt(int number) +{ + if (* (uint32_t *) seg_to_linear(0, number * 4) == 0) + return; + // FIXME VM86_SIGNAL +} + +void biosclock() +{ + uint32_t *timer = (uint32_t *) seg_to_linear(0, 0x46C); + ++*timer; + raise_interrupt(8); + raise_interrupt(0x1C); +} + +int main(int argc, char **argv) +{ + uint8_t *vm86_mem; + const char *filename; + int ret; + uint32_t file_size; + struct sigaction sa; + struct itimerval timerval; + struct vm86plus_struct ctx; + struct vm86_regs *r; + ExecParamBlock blk1, *blk = &blk1; + + if (argc < 2) + usage(); + filename = argv[1]; + + vm86_mem = mmap((void *)0x00000000, 0x110000, + PROT_WRITE | PROT_READ | PROT_EXEC, + MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); + if (vm86_mem == MAP_FAILED) { + perror("mmap"); + exit(1); + } + + memset(&ctx, 0, sizeof(ctx)); + r = &ctx.regs; + set_bit((uint8_t *)&ctx.int_revectored, 0x10); + set_bit((uint8_t *)&ctx.int_revectored, 0x13); + set_bit((uint8_t *)&ctx.int_revectored, 0x15); + set_bit((uint8_t *)&ctx.int_revectored, 0x16); + set_bit((uint8_t *)&ctx.int_revectored, 0x1a); + set_bit((uint8_t *)&ctx.int_revectored, 0x20); + set_bit((uint8_t *)&ctx.int_revectored, 0x21); + set_bit((uint8_t *)&ctx.int_revectored, 0x29); + + dos_init(); + + if (strstr(filename,".com") || strstr(filename,".exe") || + strstr(filename,".COM") || strstr(filename,".EXE")) { + ret = load_com(blk, filename, &file_size, argc, argv); + if (ret < 0) { + perror(filename); + exit(1); + } + cur_psp = ret; + + /* init basic registers */ + r->eip = blk->ip; + r->esp = blk->sp + 2; /* pop ax value */ + r->cs = blk->cs; + r->ss = blk->ss; + r->ds = cur_psp; + r->es = cur_psp; + r->eflags = VIF_MASK; + + /* the value of these registers seem to be assumed by pi_10.com */ + r->esi = 0x100; +#if 0 + r->ebx = file_size >> 16; + r->ecx = file_size & 0xffff; +#else + r->ecx = 0xff; +#endif + r->ebp = 0x0900; + r->edi = 0xfffe; + } + else { + if (load_boot(filename, r) < 0) { + if (errno) + perror(filename); + exit(1); + } + } + + sa.sa_handler = biosclock; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + if (sigaction(SIGALRM, &sa, 0) == 0) { + timerval.it_interval.tv_sec = timerval.it_value.tv_sec = 0; + timerval.it_interval.tv_usec = timerval.it_value.tv_usec = 10000000 / 182; + setitimer (ITIMER_REAL, &timerval, NULL); + } + + for(;;) { + ret = vm86(VM86_ENTER, &ctx); + switch(VM86_TYPE(ret)) { + case VM86_INTx: + { + int int_num; + + int_num = VM86_ARG(ret); + switch(int_num) { + case 0x10: + do_int10(r); + break; + case 0x13: + do_int13(r); + break; + case 0x15: + do_int15(r); + break; + case 0x16: + do_int16(r); + break; + case 0x1a: + do_int1a(r); + break; + case 0x20: + do_int20(r); + break; + case 0x21: + do_int21(r); + break; + case 0x29: + do_int29(r); + break; + default: + fprintf(stderr, "unsupported int 0x%02x\n", int_num); + dump_regs(&ctx.regs); + break; + } + } + break; + case VM86_SIGNAL: + /* a signal came, we just ignore that */ + break; + case VM86_STI: + break; + case VM86_TRAP: + /* just executes the interruption */ + { + uint16_t *int_vector; + uint32_t eflags; + + eflags = r->eflags & ~IF_MASK; + if (r->eflags & VIF_MASK) + eflags |= IF_MASK; + pushw(r, eflags); + pushw(r, r->cs); + pushw(r, r->eip); + int_vector = (uint16_t *)seg_to_linear(0, VM86_ARG(ret) * 4); + r->eip = int_vector[0]; + r->cs = int_vector[1]; + r->eflags &= ~(VIF_MASK | TF_MASK | AC_MASK); + } + break; + default: + fprintf(stderr, "unhandled vm86 return code (0x%x)\n", ret); + dump_regs(&ctx.regs); + exit(1); + } + } +}