%if 0 Example of a DPMI fake child process returning Public Domain %endif ;%include "CMMACROS.MAC" %define _4digitshex(h) ((((h)/1000h)% 10h)+'0' +(('A'-'9'-1)*((((h)/1000h)% 10h)/0Ah))), \ ((((h)/100h)% 10h)+'0' +(('A'-'9'-1)*((((h)/100h)% 10h)/0Ah))), \ ((((h)/10h)% 10h)+'0' +(('A'-'9'-1)*((((h)/10h)% 10h)/0Ah))), \ (((h)% 10h)+'0' +(('A'-'9'-1)*(((h)% 10h)/0Ah))) ; ASCIZ string ; ; %1+ = Optional string %imacro asciz 0-1+.nolist %if %0 >= 1 db %1 %endif db 0 %endmacro cpu 386 org 100h bits 16 start: pop ax ; get word saved on stack for COM files mov bx, sp shr bx, 4 jnz .smallstack mov bx, 1000h ; it was a full 64 KiB stack .smallstack: mov ah, 4Ah ; free unused memory int 21h xor ax, ax xchg ax, word [2Ch] mov es, ax mov ah, 49h int 21h ; free environment if any mov bx, 80h >> 4 mov ah, 48h int 21h ; allocate memory for child process jc nomemory ; ax:0-> DOS memory allocated for child process mov es, ax xor di, di ; es:di-> xor si, si ; cs:si = ds:si -> our PSP mov cx, 128 >> 1 ; PSP size rep movsw ; copy process segment prefix into newly allocated block dec ax mov ds, ax ; ds = MCB of allocated block inc ax ; ax = allocated block! mov word [ 8 ], "DP" mov word [ 8+2 ], "MI" mov word [ 8+4 ], "PS" mov word [ 8+6 ], "PC" ; Force MCB string to "DPMIPSPC" ; mov word [ 1 ], ax ; Set owner to itself ; (leave owner as us) mov es, ax ; es = new PSP mov dx, cs mov ds, dx ; ds = old PSP mov di, 18h mov cx, 20 mov word [ es:32h ], cx mov word [ es:34h ], di mov word [ es:34h+2 ], ax ; fix the new PSP's PHT pointer mov word [ es:0Ah ], childterminated mov word [ es:0Ah+2 ], dx ; set termination address mov word [ es:16h ], dx ; set parent PSP to current one ; The stack address set on the child's termination ; is that of the last Int21 call to a usual function ; (such as Int21.48 or .45) from the parent process. ; All registers will be lost though, because we don't ; build a fake stack frame. push di mov al, -1 rep stosb ; initialize new PHT with empty entries pop di mov cx, word [ 32h ] ; = number of current process handles jcxz .phtdone ; (weird, but handle this correctly) cmp cx, 20 jbe .normalpht mov cx, 20 .normalpht: lds si, [ 34h ] ; -> current process handle table xor bx, bx .phtloop: mov dl, -1 cmp byte [si+bx], dl ; source PHT handle used? je .phtnext ; no --> mov ah, 45h int 21h ; DUP jc nohandle xchg ax, bx xchg dl, byte [si+bx] ; get DUPed handle and kill PHT entry xchg ax, bx mov byte [es:di+bx], dl ; write into PHT of new process .phtnext: inc bx ; next handle loop .phtloop .phtdone: push cs pop ds mov bx, es mov ah, 50h int 21h ; set current PSP to new ; mov ah, 1Ah ; mov ds, ; mov dx, 80h ; int 21h ; set DTA ; Now executing in the child process's environment. ; Terminating will return to label childterminated. mov ax, cs mov word [rmcallstruc0.cs], ax mov word [rmcallstruc0.ds], ax mov word [rmcallstruc0.es], ax mov word [rmcallstruc0.ss], ax pushf pop word [rmcallstruc0.flags] ; setup with RM values mov ax, 1687h int 2Fh test ax, ax ; DPMI host installed? jnz nohost push es ; save DPMI entry address push di test si, si ; host requires client-specific DOS memory? jz .nomemneeded ; no --> mov bx, si mov ah, 48h ; alloc DOS memory int 21h jc nomemory mov es, ax .nomemneeded: mov si, msg.debuginfo call printstring int3 mov bp, sp mov ax, 0001h ; start a 32-bit client call far [bp] ; initial switch to protected-mode jnc initsuccessful initfailed: mov si, msg.initfailed jmp short rmerror nohandle: cmp cx, 20 je .no_handles_duplicated_yet ; In this case, there may be some already duplicated ; file handles. Switch to the child process and then ; terminate it, in order to close the handles. mov word [ es:0Ah ], .half_initialised_child_terminated ; set Parent Return Address to ours mov bx, es mov ah, 50h int 21h mov ax, 4C00h int 21h .half_initialised_child_terminated: cld .no_handles_duplicated_yet: push cs pop ds mov si, msg.nohandle jmp short rmerror nohost: mov si, msg.nohost jmp short rmerror nomemory: mov si, msg.nomemory rmerror: call printstring mov ax, 4CFFh int 21h initsuccessful: initsuccessful_ofs equ (initsuccessful-$$+100h) ; now in protected mode mov bx, cs mov cx, cs lar cx, cx shr cx, 8 or ch, 40h ; make a 32-bit cs mov ax, 9 int 31h ; now in 32-bit PM bits 32 now32bit: now32bit_ofs equ (now32bit-$$+100h) mov word [ data.pspsel ], es push ds pop es mov esi, msg.welcome call printstring mov ax, 0303h push cs pop ds mov esi, callback ; ds:esi-> called procedure mov edi, rmcallstruc1 ; es:edi-> (inreentrant) real mode call structure int 31h ; allocate RM callback push ss pop ds jc nocallback mov dword [callrm.callback], edx mov word [callrm.callback+2], cx mov ax, 0302h xor ebx, ebx xor ecx, ecx mov edi, rmcallstruc0 callingrm: callingrm_ofs equ (callingrm-$$+100h) int 31h ; call RM procedure with interrupt stack jc callrmfailed mov ecx, dword [callrm.callback+2] mov edx, dword [callrm.callback] mov ax, 0304h int 31h ; free RM callback mov esi, msg.bye call printstring mov ax, 4C00h ; normal client exit int 21h callrmfailed: mov esi, msg.callrmfailed jmp short pmerror nocallback: mov esi, msg.nocallback pmerror: call printstring movzx ecx, word [callrm.callback+2] movzx edx, word [callrm.callback] mov eax, edx and eax, ecx inc ax jnz .done mov ax, 0304h int 31h ; free RM callback .done: mov ax, 4CFFh int 21h ; This is a RM callback. Called from RM, executed in PM. ; ; INP: es:edi-> RM call structure ; ds:esi-> RM stack ; ss:esp-> DPMI host internal stack ; CHG: all (?) except es:edi ; read/write RM call structure to communicate with RM code callback: push dword [esi] pop dword [es:edi+2Ah] ; set RM cs:ip add word [es:edi+2Eh], byte 4 ; pop 4 byte from the stack ; The int3 trap used not to work here in the then-current ; lDebugX development version. int 03h however did. Note that ; both might crash the DPMI host if no debugger is installed. int strict byte 03h nop int3 nop iret bits 16 callrm: ; int3 ; int 03h or int3 traps (in real or V86 mode) don't cause any problems. int strict byte 03h xor ax, ax push ax push ax call -1:-1 .callback equ $-4 pop ax pop ax ; int3 int strict byte 03h iret ; Print a string with simple instructions. Don't use ; pointers or instructions depending on the default operation ; size, this is called in both 16- and 32-bit modes. printstring.next: mov dl, al mov ah, 2 int 21h printstring: lodsb test al, al jnz .next retn ; The (DPMIed) child process has terminated. childterminated: childterminated_ofs equ (childterminated-$$+100h) cld push cs pop ds mov ah, 4Dh int 21h mov si, msg.backsuccess test al, al jz .success mov si, msg.backerror .success: call printstring mov ax, 4C00h int 21h ; terminate DOS process align 2 data: .pspsel: dw 0 align 4 ; This one is used to call down to our RM part ; from the 32-bit PM code. All values are filled ; in either here or in our RM initialization. rmcallstruc0: .edi: dd 0 .esi: dd 0 .ebp: dd 0 dd 0 .ebx: dd 0 .edx: dd 0 .ecx: dd 0 .eax: dd 0 .flags: dw 0 .es: dw 0 .ds: dw 0 .fs: dw 0 .gs: dw 0 .ip: dw callrm .cs: dw 0 .sp: dw rmcallsp .ss: dw 0 align 4 ; This one is utilized by the DPMI host and ; our RM callback when called from RM. All ; values are filled in by the DPMI host. rmcallstruc1: .edi: dd 0 .esi: dd 0 .ebp: dd 0 dd 0 .ebx: dd 0 .edx: dd 0 .ecx: dd 0 .eax: dd 0 .flags: dw 0 .es: dw 0 .ds: dw 0 .fs: dw 0 .gs: dw 0 .ip: dw 0 .cs: dw 0 .sp: dw 0 .ss: dw 0 msg: .callrmfailed: asciz "Calling real mode procedure failed.",13,10 .bye: asciz "Calling real mode procedure which called callback successful.",13,10 .backsuccess: asciz "Child process terminated okay, back in real mode.",13,10 .backerror: asciz "Child process terminated with error, back in real mode.",13,10 .nocallback: asciz "Could not allocate real mode callback.",13,10 .nohost: asciz "No DPMI host installed.",13,10 .nohandle: asciz "No free DOS file handle for child process creation.",13,10 .nomemory: asciz "Not enough DOS memory for child process creation or client initialization.",13,10 .initfailed: asciz "DPMI initialization failed.",13,10 .debuginfo: db "Protected mode breakpoint at ",_4digitshex(initsuccessful_ofs),"h.",13,10 db "32-bit code segment breakpoint at ",_4digitshex(now32bit_ofs),"h.",13,10 db "Real mode procedure called at ",_4digitshex(callingrm_ofs),"h.",13,10 db "Return from child process at ",_4digitshex(childterminated_ofs),"h.",13,10 asciz 13,10 .welcome: asciz "Welcome in 32-bit protected mode.",13,10 align 2 rmcallsp: