📄 emxldpmi.asm
字号:
;
; modified by Rainer Schnitker
;
; 1) always use RSX.EXE if there is a DPMI server
; 2) work with Watcom/wasm and Borland/tasm
;
; file is renamed to EMXLDPMI.ASM
;
; EMXL.ASM -- emx loader (emxl.exe)
;
; Copyright (c) 1991-1994 by Eberhard Mattes
;
; This file is part of emx.
;
; emx 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, or (at your option)
; any later version.
;
; emx 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 emx; see the file COPYING. If not, write to
; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
;
; <START_OF_EXCEPTION>
; As a special exception, if you bind emxl.exe to an executable file
; (using emxbind), this does not cause the resulting executable file
; to be covered by the GNU General Public License. The source code
; for emxl.exe must be distributed with the executable file. This
; exception does not however invalidate any other reasons why the
; executable file might be covered by the GNU General Public License.
; <END_OF_EXCEPTION>
;
INCLUDE EMX.INC
INCLUDE HEADERS.INC
INCLUDE VERSION.INC
.8086
;
; Data (the name SV_DATA stems from emx, we're using EMX.INC!)
;
SV_DATA SEGMENT
PSP_SEG DW ? ; Program prefix segment
ENV_SEG DW ? ; Environment segment
NAME_OFF DW ? ; Offset of prog name (ENV_SEG)
NAME_LEN DW ? ; Length of prog name
;
; Parameter block for INT 21H, AX=4B00H
;
PAR_BLOCK DW 0 ; Copy parent's environment
DW OFFSET CMD_LINE ; Command line
DW SEG CMD_LINE
DW 5CH ; FCB1
PB_SEG1 DW ?
DW 6CH ; FCB2
PB_SEG2 DW ?
;
; DPMI_FLAG is non-zero if a DPMI server has been found, but no
; VCPI server
;
DPMI_FLAG DB FALSE ; Initially FALSE
;
; Messages
;
$EMX_NOT_FOUND DB "emx not found", CR, LF, "$"
$RSX_NOT_FOUND DB "rsx not found, DPMI not supported by emx", CR, LF, "$"
$USE_EMXBIND DB "Use emxbind", CR, LF, "$"
$BAD_ENV DB "Bad environment", CR, LF, "$"
;
; Names of environment variables
;
$EMX DB "EMX", 0
$PATH DB "PATH", 0
;
; Name of emx.exe for searching in the current working directory and PATH
;
$EMX_EXE DB "emx.exe", 0
EMX_EXE_LEN = 8 ; Length of name, with 0
;
; This command line is passed to emx.exe
;
CMD_LINE DB 7 ; 7 bytes
DB "-/" ; Special marker
CMD_LINE_PSP DB "0000" ; PSP_SEG, hexadecimal
DB "/" ; Another special marker
DB CR ; End of command line
;
; Build path name here when using directory from PATH environment variable
;
PGM_NAME DB 128 DUP (?)
PGM_NAME_END LABEL BYTE
SV_DATA ENDS
;
; This area will be patched by emxbind
;
HDR_SEG SEGMENT
PATCH LABEL BIND_HEADER
DB "emx ", VERSION, 0
DB (SIZE BIND_HEADER - HDR_VERSION_LEN) DUP (0)
HDR_SEG ENDS
;
; Code
;
INIT_CODE SEGMENT
ASSUME CS:INIT_CODE
ASSUME DS:NOTHING, ES:NOTHING
;
; Setup data segment. Note: This macro modifies AX.
;
SET_DS MACRO
MOV AX, SV_DATA
MOV DS, AX
ASSUME DS:SV_DATA
ENDM
;
; The program starts here
;
ENTRY: SET_DS ; Setup data segment
MOV PSP_SEG, ES ; Save program prefix segment
CALL INIT ; Initialize
CALL CHECK_DPMI ; Check for DPMI
CALL GET_NAME ; Get program name
CALL BIND_HDR ; Check patch area
CALL TRY_EMX_ENV ; Use EMX environment variable
CALL TRY_CWD ; Search current directory
CALL TRY_PATH ; Use PATH environment variable
LEA DX, $EMX_NOT_FOUND ; Give up
CMP DPMI_FLAG, FALSE
JE FAIL
LEA DX, $RSX_NOT_FOUND
FAIL: JMP ABORT
;
; Initialization
;
INIT PROC NEAR
MOV AX, PSP_SEG
MOV PB_SEG1, AX ; Fill in parameter block
MOV PB_SEG2, AX ; for DOS function 4BH (EXEC)
MOV ES, AX
MOV AX, ES:[2CH] ; Get environment segment
MOV ENV_SEG, AX
LEA BX, CMD_LINE_PSP ; Insert PSP_SEG, hexadecimal
MOV CX, 0404H ; 4 digits, shift by 4 bits
MOV DX, PSP_SEG
INIT_1: ROL DX, CL
MOV AL, DL
AND AL, 0FH
ADD AL, "0"
CMP AL, "9"
JBE INIT_2
ADD AL, "A" - ("0" + 10)
INIT_2: MOV [BX], AL
INC BX
DEC CH
JNZ INIT_1
RET
INIT ENDP
;
; Check for DPMI server
;
CHECK_DPMI PROC NEAR
MOV AX, 1687H ; Check for DPMI server
INT 2FH
OR AX, AX ; Server present?
JNZ DPMI_RET ; No -> call emx.exe
TEST BX, 1 ; 32-bit programs supported?
JZ DPMI_RET ; No -> hope we can get VCPI
;
; DPMI (32 bit) is supported, run rsx.exe
;
NO_VCPI: MOV DPMI_FLAG, NOT FALSE ; Set flag
MOV AX, "SR" ; First 2 letters of "RSX"
MOV WORD PTR $EMX, AX ; Patch name of env. variable
MOV AX, "sr" ; First 2 letters of "rsx.exe"
MOV WORD PTR $EMX_EXE, AX ; Patch name of executable
DPMI_RET: RET
CHECK_DPMI ENDP
;
; Find program name
;
GET_NAME PROC NEAR
MOV ES, ENV_SEG ; Scan environment
XOR DI, DI ; starting at offset 0
MOV CX, 8000H ; Maximum environment size
XOR AL, AL ; Search for bytes of zeros
CLD ; Incrementing
GET_NAME_1: REPNE SCAS BYTE PTR ES:[DI] ; Skip string
JNE GET_NAME_ERR ; Count exhausted -> error
SCAS BYTE PTR ES:[DI] ; End of environment?
JE GET_NAME_9 ; Yes -> program name found
LOOP GET_NAME_1 ; Next string, 1st char skipped
GET_NAME_ERR: LEA DX, $BAD_ENV ; Bad environment
JMP ABORT
GET_NAME_9: ADD DI, 2 ; Skip count
MOV NAME_OFF, DI ; Offset of program name
CALL STRLEN ; Compute length
MOV NAME_LEN, CX
RET
GET_NAME ENDP
;
; Check emxbind patch area
;
BIND_HDR PROC NEAR
MOV AX, HDR_SEG
MOV ES, AX ; Access emxbind patch area
ASSUME ES:HDR_SEG
CMP PATCH.BND_BIND_FLAG, FALSE ; Bound?
JNE BIND_HDR_1 ; Yes -> continue
LEA DX, $USE_EMXBIND ; Must be bound to a.out
JMP ABORT
BIND_HDR_1: RET
BIND_HDR ENDP
ASSUME ES:NOTHING
;
; Display error message and abort program
;
; In: DX Offset of dollar-delimited error message in SV_DATA
;
ABORT: SET_DS ; This can't hurt
MOV AH, 09H ; Display string
INT 21H
MOV AX, 4C01H ; Terminate process, rc=1
INT 21H
;
; Try to find emx.exe using EMX environment variable
;
TRY_EMX_ENV PROC NEAR
LEA BX, $EMX
CALL GETENV
JC TRY_EMX_ENV_RET
MOV DX, DI
MOV_DS_ES
ASSUME DS:NOTHING
CALL SPAWN
ASSUME DS:SV_DATA
TRY_EMX_ENV_RET:RET
TRY_EMX_ENV ENDP
;
; Try to find emx.exe in current working directory
;
TRY_CWD PROC NEAR
LEA DX, $EMX_EXE
CALL SPAWN
RET
TRY_CWD ENDP
;
; Try to find emx.exe in the directories listed in the PATH environment
; variable
;
TRY_PATH PROC NEAR
LEA BX, $PATH
CALL GETENV
JC TRY_PATH_RET
MOV SI, DI
FIND_2: LEA DI, PGM_NAME
FIND_3: MOV AL, ES:[SI]
INC SI
OR AL, AL
JZ TRY_PATH_RET
CMP AL, " "
JE FIND_3
CMP AL, TAB
JE FIND_3
CMP AL, ";"
JE FIND_3
FIND_4: CMP DI, OFFSET PGM_NAME_END
JAE FIND_5
MOV [DI], AL
MOV AH, AL
INC DI
FIND_5: MOV AL, ES:[SI]
INC SI
OR AL, AL
JZ FIND_6
CMP AL, " "
JE FIND_5
CMP AL, TAB
JE FIND_5
CMP AL, ";"
JNE FIND_4
FIND_6: DEC SI
CMP DI, OFFSET PGM_NAME_END - EMX_EXE_LEN
JAE FIND_2
CMP AH, "\"
JE FIND_7
CMP AH, "/"
JE FIND_7
CMP AH, ":"
JE FIND_7
CMP DI, OFFSET PGM_NAME_END
JAE FIND_7
MOV BYTE PTR DS:[DI], "\"
INC DI
FIND_7: PUSH ES
PUSH SI
LEA SI, $EMX_EXE
MOV CX, EMX_EXE_LEN
FIND_8: LODSB
MOV [DI], AL
INC DI
LOOP FIND_8
LEA DX, PGM_NAME
CALL SPAWN
POP SI
POP ES
JMP FIND_2
TRY_PATH_RET: RET
TRY_PATH ENDP
;
; Find environment entry
;
; In: BX Points to zero-terminated name of environment variable
;
; Out: CY Not found
; NC Found
; DI Points to value of environment variable
;
ASSUME DS:SV_DATA
GETENV PROC NEAR
MOV ES, ENV_SEG
XOR DI, DI
CLD
GETENV_NEXT: CMP BYTE PTR ES:[DI], 0 ; Empty environment?
JE GETENV_FAILURE ; Yes -> not found
PUSH BX ; Save pointer to name
GETENV_COMPARE: MOV AL, [BX]
CMP AL, ES:[DI] ; Compare names
JNE GETENV_DIFF ; Mismatch -> try next one
OR AL, AL ; Shouldn't happen (`='!)
JZ GETENV_DIFF ; (name matches completely)
INC BX
INC DI
JMP GETENV_COMPARE ; Compare next character
GETENV_DIFF: POP BX ; Restore pointer to name
OR AL, AL ; End of name reached?
JE GETENV_EQUAL ; Yes -> candidate found
GETENV_SKIP: XOR AL, AL
MOV CX, 32767 ; Search for next entry
REPNE SCAS BYTE PTR ES:[DI]
JMP GETENV_NEXT ; Check that entry
GETENV_EQUAL: CMP BYTE PTR ES:[DI], "=" ; Exact match?
JNE GETENV_SKIP ; No -> go to next entry
INC DI ; Skip `='
CLC
JMP GETENV_RET ; Return pointer to value
GETENV_FAILURE: STC ; Not found
GETENV_RET: RET
GETENV ENDP
;
; Compute the length of a zero-terminated string
;
; In: ES:SI Points to string
;
; Out: CX Length
;
STRLEN PROC NEAR
XOR CX, CX
XOR AL, AL
STRLEN_1: SCASB
JE STRLEN_2
INC CX
JMP STRLEN_1
STRLEN_2: RET
STRLEN ENDP
;
; Try to run a program. Exit if successful, return if failed.
;
; In: DS:DX Points to path name of program
;
; Out: DS SV_DATA
;
ASSUME DS:NOTHING
SPAWN PROC NEAR
MOV AX, SV_DATA
MOV ES, AX
ASSUME ES:SV_DATA
LEA BX, PAR_BLOCK ; ES:BX -> parameter block
MOV AX, 4B00H ; Load and execute program
INT 21H
SET_DS ; Restore data segment
JNC DONE ; Success -> terminate
RET
DONE: MOV AH, 4DH ; Get return code of child
INT 21H ; process
MOV AH, 4CH ; terminate process
INT 21H
SPAWN ENDP
ASSUME ES:NOTHING
INIT_CODE ENDS
RM_STACK SEGMENT
DW 256 DUP (?)
RM_STACK ENDS
END ENTRY
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -