!************************************************************************** !* !* Network driver interface for netboot bootrom !* !* Module: process.S !* Purpose: Process management functions for DOS simulator !* Entries: dos00, dos31, dos4C, dos4D, loadprog, runprog !* !************************************************************************** !* !* Copyright (C) 1995-2003 Gero Kuhlmann !* !* This program is free software; you can redistribute it and/or modify !* it under the terms of the GNU General Public License as published by !* the Free Software Foundation; either version 2 of the License, or !* any later version. !* !* This program is distributed in the hope that it will be useful, !* but WITHOUT ANY WARRANTY; without even the implied warranty of !* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the !* GNU General Public License for more details. !* !* You should have received a copy of the GNU General Public License !* along with this program; if not, write to the Free Software !* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. !* !* $Id: process.S,v 1.5 2003/01/25 23:29:41 gkminix Exp $ !* ! !************************************************************************** ! ! Include assembler macros: ! #include #include #include "dospriv.inc" ! !************************************************************************** ! ! Data segment ! .data psp_tmpl: ! PSP template .byte $CD, $20 ! code for INT20h psp_endmem: .word MEMEND ! first allocatable seg after program .byte 0 ! reserved .byte $9A ! far call OP-code to INT21h handler psp_call21: .word call21 ! pointer to INT21h handler .word 0 psp_int22: .long 0 ! saved interrupt 22h vector psp_int23: .long 0 ! saved interrupt 23h vector psp_int24: .long 0 ! saved interrupt 24h vector psp_parent: .word 0 ! PSP of parent process psp_handles: .byte $FF, $FF, $FF ! reserved file handles .byte $FF, $FF .space 20 - 5 ! file handle table psp_env: .word 0 ! segment of program environment psp_oldstack: .long 0 ! stack pointer of parent psp_numhndl: .word 20 ! number of file handles psp_ofshndl: .word o_psp_handles ! offset of file handle table psp_seghndl: .word 0 ! segment of file handle table .space 24 ! reserved .byte $CD, $21, $CB ! OP-codes for INT21h and RETF psp_tmpl_end: ! !************************************************************************** ! ! BSS segment ! .bss extrn dos_active ! DOS active flag extrn first_mcb ! segment of first MCB .comm curr_psp,2 ! current PSP .lcomm ret_code,2 ! program return code .lcomm prognum,2 ! number of currently loaded program .lcomm new_psp,2 ! segment of new PSP .lcomm new_prog,4 ! pointer to new program copy .lcomm new_spss,4 ! new stack pointer .lcomm new_ipcs,4 ! new execution address ! !************************************************************************** ! ! Start code segment. ! .text public dos00 public dos31 public dos4C ! define entry poimts public dos4C public dos4D public loadprog public runprog extrn dos48 extrn dos4A extrn freeall extrn call21 extrn unused extrn prnstr extrn prnword ! !************************************************************************** ! ! Get exit code. ! Input: none ! Output: AL - exit code ! AH - exit cause ! Registers changed: AX ! dos4D: mov ax,[ret_code] ret ! !************************************************************************** ! ! Terminate current process, but keep it resident in memory. ! Input: AL - return code ! DX - number of paragraphs to keep in memory (including PSP) ! Output: none (function does not return) ! Registers changed: all ! dos31: mov ah,#3 mov [ret_code],ax ! set return code mov bx,dx mov es,[curr_psp] call dos4A ! resize the memory block jmp dos4C0 ! !************************************************************************** ! ! Terminate program. ! Input: none ! Output: none (function does not return) ! Registers changed: all ! dos00: les bx,old_stack[bp] seg es mov dx,[bx + 2] ! load callers code segment mov [curr_psp],dx ! set current PSP segment xor al,al ! fall through to function 4C ! !************************************************************************** ! ! Terminate current program. ! Input: AL - return code ! Output: none (function does not return) ! Registers changed: all ! dos4C: xor ah,ah mov [ret_code],ax ! set return code mov dx,[curr_psp] call freeall ! free all memory used by this process dos4C0: jc dos4C1 xor bx,bx jmp dos4C2 dos4C1: cmp ax,#ERR_NOMEM mov bx,#errnom je dos4C2 mov bx,#errmem dos4C2: ! fall through to terminate function ! !************************************************************************** ! ! Terminate current process. This routine does the actual job, except ! memory management. ! Input: BX - offset to error message ! Output: BX - offset to error message passed to parent process ! Registers changed: all ! terminate: ! Restore the stack of the parent process, reset the PSP of the ! parent process, reset the DOS active flag, and return to the parent ! calling address. Also reset the interrupts $22 to $24. cli mov es,[curr_psp] seg es mov ax,[o_psp_parent] ! reset current PSP mov [curr_psp],ax mov byte ptr [dos_active],#0 ! reset DOS active flag seg es mov ss,word ptr [o_psp_oldstack+2] seg es mov sp,word ptr [o_psp_oldstack+0] cld xor ax,ax mov es,ax mov di,#$22 * 4 mov cx,#3 term1: mov ax,#unused ! restore interrupts $22 to $24 stosw mov ax,cs stosw loop term1 sti retf ! return to runprog ! !************************************************************************** ! ! Load a new process, but dont run it. This routine gets called by the ! network driver initialization code. It will internally store all values ! necessary to run the program for later execution by 'runprog'. ! The program image layout has to look like: ! ! Offset ! 0000 - Two byte magic cookie ! 0002 - Length of command line in bytes ! 0003 - Command line (not terminated by zero) ! var - Length of program image in bytes ! var+2 - Amount of memory to allocate in paragraphs including PSP ! var+4 - Program image ! ! This routine will print a message in case of an error, and then return ! with the carry flag set. ! ! Input: ES:SI - pointer to program code image ! Output: ES:SI - pointer to first byte after program code image ! BX - first segment of program code ! Carry flag set if error ! Registers changed: AX, BX, CX, DX, SI, DI, ES ! loadprog: cld push bp inc word ptr [prognum] ! First check that the program code image has the correct magic cookie ! at its beginning. seg es cmp word ptr [si],#COM_MAGIC mov bx,#errprg jne load9 ! Now compute the size of memory required to load the program and try to ! allocate it. For COM programs we simply try to get as much memory as ! possible, while for EXE programs we have to use the amount specified ! in the header. At the end of this part the registers are set as ! follows: ! ! AX:DI - pointer to loading area for program ! ES:SI - pointer to program image structure (see above) ! ES:BP - pointer to program image length value ! DX - PSP segment ! ! It is expected that the binary patch program has already verified that ! the EXE file header is valid. seg es #ifdef IS386 movzx bx,byte ptr [si + 2] #else mov bl,byte ptr [si + 2] xor bh,bh #endif lea bp,[si + bx + 3] seg es cmp word ptr [bp + 4 + EXE_SIG],#EXE_MAGIC je load2 mov bx,#$FFFF ! check how much memory we have call dos48 ! available jnc load1 cmp ax,#ERR_NOMEM jne load8 ! couldnt get that much, so try call dos48 ! to get as much as possible jc load6 load1: cmp bx,#COM_MIN_FREE jbe load7 ! check if the program will fit into seg es ! the reserved memory area cmp bx,word ptr [bp + 2] mov di,#PSP_SIZE ! set program start address mov dx,ax jae load3 load7: mov bx,#errnom jmp load9 load6: cmp ax,#ERR_NOMEM je load7 load8: mov bx,#errmem load9: call doserror ! print error message (returns with jmp near load99 ! carry flag set) load2: seg es ! get number of paragraphs to allocate mov bx,[bp + 2] call dos48 jc load6 mov dx,ax ! save PSP segment for later add ax,#PSP_SIZE / 16 ! compute program start address xor di,di load3: mov [new_psp],dx mov word ptr [new_prog + 0],di mov word ptr [new_prog + 2],ax ! Setup new PSP by copying the PSP template into it. The FCBs remain empty ! as they are not used. mov dx,es ! save original parameters mov bx,si mov es,[new_psp] xor ax,ax xor di,di mov cx,#PSP_SIZE / 2 ! first clear the whole PSP area rep stosw mov [psp_seghndl],es mov ax,[curr_psp] ! setup PSP template mov [psp_parent],ax mov word ptr [psp_call21 + 2],cs mov ax,[first_mcb] ! the first MCB contains an empty inc ax ! memory block which we can use as mov [psp_env],ax ! a dummy environment xor di,di mov si,#psp_tmpl mov cx,#psp_tmpl_end - psp_tmpl rep movsb ! copy PSP template into memory area push ds mov ds,dx mov di,#o_psp_cmdl lea si,[bx + 3] #ifdef IS386 movzx cx,byte ptr [bx + 2] ! get size of command line #else mov cl,[bx + 2] ! get size of command line xor ch,ch #endif jcxz load4 cmp cx,#PSP_MAXCMDL jbe load5 mov cx,#PSP_MAXCMDL load5: seg es mov [o_psp_cmdlsize],cl rep movsb ! save command line load4: mov al,#CHR_RET ! terminate command line with CR stosb xor al,al ! and with a zero byte stosb pop ds ! Copy the program code into the new memory block. At the end of this part ! the registers are set as follows: ! ! DX:BP - pointer to first byte of program image ! DX:SI - pointer to first byte after program image ! ES:DI - pointer to first byte after new program copy push ds mov ax,word ptr [new_prog + 0] mov di,ax shift (shr,ax,4) ! adjust to lowest possible offset add ax,word ptr [new_prog + 2] and di,#$000F mov es,ax mov ax,si shift (shr,ax,4) ! adjust to lowest possible offset add dx,ax and si,#$000F mov ds,dx mov cx,word ptr [si] ! copy program code lea si,[si + 4] mov bp,si cmp word ptr [si + EXE_SIG],#EXE_MAGIC jne load10 mov ax,[si + EXE_HEADSIZE] shift (shl,ax,4) add si,ax ! dont copy EXE header sub cx,ax load10: shr cx,#1 rep movsw ! use word copy for speed jnc load12 movsb ! copy the remaining byte load12: pop ds ! Now set the owner of all memory blocks. This owner ID is simply the PSP ! segment. Also set the end of stack and execution address for COM programs. ! At the end of this part the registers are set as follows: ! ! DX:BP - pointer to first byte of program image ! DX:SI - pointer to first byte after program image ! BX - segment of new program copy ! ! DX:SI and BX have to remain intact until the end of the routine. push ds mov bx,[new_psp] mov ax,bx ! set program MCB dec bx mov ds,bx mov [mcb_owner],ax ! set owner of PSP memory block add ax,[mcb_size] ! get first paragraph following MCB pop ds mov bx,word ptr [new_prog + 2] sub ax,bx ! compute program size cmp ax,#$1000 jb load13 ! dont allow a stack pointer above xor ax,ax ! $FFFE jmp load14 load13: shift (shl,ax,4) ! convert paragraph size into byte load14: dec ax ! offset for top of stack dec ax mov word ptr [new_spss + 0],ax mov word ptr [new_spss + 2],bx mov ax,word ptr [new_prog + 0] mov word ptr [new_ipcs + 0],ax mov word ptr [new_ipcs + 2],bx ! Compute the starting segment of the program and stack for EXE programs. ! Then adjust the relocation entries in the EXE header. At the end, we ! have the following register allocation: ! ! DX:SI - pointer to first byte of program image ! DX:BP - pointer to first byte after program image ! BX - code segment of new program copy shift (shr,ax,4) ! compute segment of execution address add ax,bx xchg si,bp mov es,dx ! check for EXE file seg es cmp word ptr [si + EXE_SIG],#EXE_MAGIC jne load20 seg es ! compute execution address mov ax,word ptr [si + EXE_CS] add word ptr [new_ipcs + 2],ax seg es mov ax,word ptr [si + EXE_IP] mov word ptr [new_ipcs + 0],ax seg es ! compute stack pointer mov ax,word ptr [si + EXE_SS] add word ptr [new_spss + 2],ax seg es mov ax,word ptr [si + EXE_SP] mov word ptr [new_spss + 0],ax seg es mov cx,[si + EXE_RELOCNUM] ! get number of relocation entries jcxz load20 push ds mov ds,dx add si,[si + EXE_RELOCTAB] ! get offset to relocation table load11: lodsw ! get relocation offset mov di,ax lodsw ! get relocation segment add ax,bx mov es,ax seg es add [di],bx ! adjust word to be relocated loop load11 pop ds mov ax,word ptr [new_ipcs + 2] ! Thats all. Adjust ES:SI as return value. load20: mov bx,ax ! prepare BX return value mov ax,bp shift (shr,ax,4) ! adjust to lowest possible offset add ax,dx mov es,ax and bp,#$000F ! this will also clear the carry flag mov si,bp load99: pop bp ret ! !************************************************************************** ! ! Run a recently loaded program. If the DOS termination function found an ! error, it will return the offset to the error message in BX. ! Input: none ! Output: Carry flag set if error ! Registers changed: AX, BX, CX, DX ! runprog: cli push ds push es push si push di push bp ! Push return address and save old stack pointer push cs #ifdef IS186 push #runret ! push return address #else mov ax,#runret push ax ! push return address #endif mov dx,[new_psp] mov es,dx seg es ! save old stack pointer mov word ptr [o_psp_oldstack + 0],sp seg es mov word ptr [o_psp_oldstack + 2],ss ! Make new program current, switch stack to new stack and push termination ! address. Then set the data segment registers and call the new program. mov [curr_psp],dx #ifdef IS386 lss sp,[new_spss] push #0 ! save near pointer to terminate fctn push dword ptr [new_ipcs] #else mov ss,word ptr [new_spss + 2] mov sp,word ptr [new_spss + 0] xor ax,ax push ax ! save near pointer to terminate fctn push word ptr [new_ipcs + 2] push word ptr [new_ipcs + 0] #endif sti mov ds,dx ! set data segment register correctly mov ax,#$FFFF ! indicate FCBs are invalid retf ! call the new program ! The called program will return here. Simply pop all registers from the ! stack and return to the caller. runret: pop bp pop di pop si pop es pop ds or bx,bx jz runp9 ! set carry flag in case of error call doserror ! print DOS error message runp9: ret ! !************************************************************************** ! ! Print DOS error message ! Input: BX - offset to error message in text segment ! Output: Carry flag set ! Registers changed: AX, BX ! doserror: push bx mov bx,#errmsg call prnstr ! print starting message pop bx push bx call prnstr ! print error message pop ax cmp ax,#errprg jne doser9 mov ax,[prognum] ! print program number call prnword doser9: mov bx,#crlf call prnstr stc ! return to caller ret ! Error messages errmsg: .byte $0D,$0A .asciz "DOS ERROR: " errnom: .asciz "no free mem" errmem: .asciz "mem corrupt" errprg: .asciz "corrupt prog " crlf: .byte $0D,$0A,0 ! !************************************************************************** ! end