; ; COM file loader ; ; David Lindauer, March 25, 1997 ; ; This should run MOST com files, if not all of them. The only ; especially tricky part is marking the newly allocated memory ; segments with an appropriate owner so that the memory will be ; released properly when the program exits. ; ; Note: there are some aspects of DOS compatability I haven't taken ; care of here; for example the program name should show up right after ; the environment with a fully qualified path; and it should show up ; again right after the command line without the path. I may have missed ; other stuff as well... ; ; .model tiny .386 .code org 100h start: jmp go db 0C4h ; word aligning the stack ;_____________________________________________________________________ ; ; Internal stack ; dw 128 DUP ('?') tos: ;_____________________________________________________________________ ; ; Internal variables ; query db 10,13,"Please enter name of file to execute: ",'$' response db 40,0,40 DUP (?) ;_____________________________________________________________________ ; ; Failure comes here ; errmsg db 10,13,"failed.",10,13,'$' failure: push cs ; print a message if something fails pop ds mov dx,offset errmsg mov ah,9 int 21h mov ax,4c01h int 21h ;_____________________________________________________________________ ; ; Prepare an empty environment. COMMAND.COM copies the environment of ; the parent to a new hunk of memory and throws some data in after it; ; We just make an empty environment ; prepare_env: mov bx,1 ; need one paragraph mov ah,48h int 21h jc failure mov es,ax ; Zero it out sub di,di sub ax,ax mov cx,8 rep stosw ret ;_____________________________________________________________________ ; ; Allocate all useable memory to our new prog ; Actually not 100% compatible with what DOS does but close enough ; for IBM PC computers. ; allocate_allmem: mov bx,-1 ; find out how much mem mov ah,48h int 21h cmp bx,1000h ; At least 64K? jc failure push bx ; Allocate it mov ah,48h int 21h mov es,ax pop bx ; returning size of block jc failure ret ;_____________________________________________________________________ ; ; Initialize the new PSP ; ; Note: this should handle most DOS .COM files. Of course it ; makes little sense to go to all this trouble if we aren't running DOS ; programs :). BTW this should let them run, but it may keep them from ; important information such as command lines and environments ; prepare_psp: push ax ; start by zeroing it out mov cx,128 sub ax,ax sub di,di rep stosw pop ax mov word ptr es:[2ch],ax ; Pointer to our environment mov word ptr es:[0],20cdh ; null return to DOS mov ax,es add ax,bx mov word ptr es:[2],ax ; Next free block (usually A000) mov byte ptr es:[5],9ah ; CALLF ; ; Now we grab stuff from the original PSP. Usually DOS initializes ; this stuff with values out of the appropriate vectors but I got lazy... ; mov si,6 ; vectors 21h-24h copy from old PSP mov di,6 mov cx,8 rep movsw mov ax,ds:[16h] ; ; This next field would typically be set to point at itself or ; the parent which did the spawning; I set it to the parent of ; this loader program ; mov word ptr es:[16h],ax ; Common parent mov byte ptr es:[18h],1 ; Handle table mov byte ptr es:[19h],1 mov byte ptr es:[1ah],1 mov byte ptr es:[1bh],0 mov byte ptr es:[1ch],2 mov di, 1dh ; blank out unused entries (handles) mov al,0ffh mov cx,15 rep stosb mov word ptr es:[32h],20 ; 20 handles available mov word ptr es:[34h],18h ; Point at handle table mov word ptr es:[36h],ES ; mov dword ptr es:[38h],-1 ; reserved mov word ptr es:[50h],21CDh ; dummy DOS call mov byte ptr es:[52h],0CBH ; ; The rest of this stuff has to be modified if you put in command line support ; mov di,5dh ; Now blank out the FCB file names mov al,20h mov cx,8 rep stosb mov di,6dh mov cx,8 rep stosb mov word ptr es:[80h],0D00h ; Indicate no command line ret ;_____________________________________________________________________ ; ; Reclassify the memory for the PSP and ENV as being owned by the PSP ; If we don't do this the memory may not get freed up... which typically ; results in a lockup. ; rename_arena: push es push es mov ax,es:[2ch] dec ax mov es,ax pop ax mov es:[1],ax dec ax mov es,ax pop ax mov es:[1],ax mov es,ax ret ;_____________________________________________________________________ ; ; Program entry point ; go: mov sp,offset tos ; Switch to internal stack ; ; This is the standard ASM prelude for freeing up memory the program ; isn't using ; mov bx,offset eop ; Free up unused memory add bx,15 shr bx,4 mov ah,4ah int 21h ; ; Everything up to here is to adjust for the fact we are already a com file ; ; ; First we make the environment. We are making an empty one but it should ; get copied either from the master environment or from the parent's ; environment call prepare_env ; Memory initialize ; ; Allocate program memory. We are going to allocate anything we can ; get our hands on. We HAVE to allocate this rather than just default ; to the pre-allocated memory because some programs depend on the PSP ; being at the beginning of an arena block ; push es call allocate_allmem pop ax ; ; Now initialize all relevant fields of the PSP ; call prepare_psp ; ; Retag the arena entries for the memory we have allocated so it will ; be freed properly ; call rename_arena ; rename the arena entries ; ; Put a prompt and get file name ; mov dx,offset query ; Get the file name mov ah,9 int 21h mov dx,offset response mov ah,10 int 21h mov bh,0 mov bl,[response+1] mov [bx+response+2],0 ; ; Read in the file ; mov ax,3d00h ; Open the file mov dx,offset response+2 int 21h mov bx,ax jc failure mov ax,3f00h ; Read the file mov dx,100h push es pop ds mov cx,0FF00h int 21h jc failure mov cx,ax ; Szie of prog in CX mov ax,3e00h ; close the file int 21h jc failure push cx push es ; ; Now we free the resources of this loader program. Note this will ; leave a memory gap- a better solution would to have been to free it ; BEFORE allocating memory. Of course then we have to move ourselves ; out of the way ; mov es,cs:[2ch] ; free our environment mov ah,49h int 21h push cs ; Free our program seg pop es mov ah,49h int 21h ; ; Now we have to inform DOS that the active PSP has changed. It is normal ; to eventually restore the PSP in TSRs but we won't be coming back ; later. ; mov bx,ds ; Inform DOS about the new PSP mov ah,50h int 21h pop es pop cx ; ; Now we set up the segment registers and such. The registers we ; are initializing are as follows: ; ; DS,ES,SS,Sp,CS,IP ; BX:CX ; ; AFAIK that is all that has to be initialized. But maybe you ought to ; write a short program to dump the registers at startup of a com file ; and see? Don't depend on DEBUG... it zeros the registers but DOS ; doesn't do that. cli ; Set stack pointer to new process push ds pop ss mov sp,0fff0h sti sub bx,bx ; Put the null in case they push bx ; use RET to exit push ds ; set the CS:IP pair to run prog push 100h retf eop: end start