[Edit: Ooops, that question already been answered a few years ago on Stack Overflow]
I'd expect there to be some reference (either a file handle or an old-fashioned FCB) in the PSP, but there's nothing I can find.
Almost. While there is no file handle or FCB to the program file, as it is already closed before execution starts, DOS (starting with 3.0) stores the program name as ASCIZ within the environment memory block, right after all environment variables.
To retrieve
- Test for min DOS version 3.0
- Get PSP address from DOS
- Get the environment segment from offset 002Ch of the PSP
- Advance to after the last environment variable
- marked by two consecutive null bytes
- There's a word with the number of strings following
- If at least 1, the program name follows
- Fetch the program name (a full ASCIZ path name)
- Open that file
- Do whatever you want to do :))
This works for .COM and .EXE programs - but only with DOS 3.0 and above.
In Assembly (COM or EXE) this may look a bit like:
MOV AH,30h ; Get DOS version
INT 21h
JC LOWDOS ; Call failed -> DOS 1.x ?
CMP AL,3 ; At least DOS 3
JAE DOSOK ; Yes -> continue
LOWDOS: ; DOS version too low: terminate with exit code = 126
MOV AX,4C7Eh ; Try to do it DOS 2 style
INT 21h
INT 20h ; Just in case this is really DOS 1.x
DOSOK:
MOV AH,51h ; Get active PSP
INT 21h ; Will never return an error
MOV ES,BX ; Segment of PSP
MOV AX,ES:[2Ch] ; Get Environment Segment from PSP
MOV ES,AX ; to ES
XOR DI,DI ; Point ES:DI at start of environment vars
XOR AX,AX ; Search for 0-bytes
CLD ; Upward
MOV CX,32767 ; Maximum size of an environment segment
SKIPVAR:
;Loop skipping all environment variables
REPNE SCASB ; Skip a string
JCXZ NFND ; Searched max.length without finding a zero
CMP AL,BYTE ES:[DI] ; next byte as well Zero ?
JNE SKIPVAR ; No -> Skip next var
INC DI ; Skip over zero byte
CMP WORD ES:[DI],AX; String count not zero ?
JE NFND ; No -> Not Found
INC DI ; Skip string count
INC DI
FOUND:
; Full Program Name at ES:DI
; Do whatever is to be done
; like opening it via 3Dh:
PUSH DS
MOV AX,ES
MOV DS,AX
MOV DX,DI ; Filename in DS:DX
MOV AL,<openmode> ; Read/Write/etc...
MOV CL,0
MOV AH,3Dh ; Open File
INT 21h
POP DS
JNC FILE_OPENED
; Open Error: terminate with exit code = openerror code
MOV AH,4Ch
INT 21h
NFND:
; Name Not Found: terminate with exit code = 127
MOV AX,4C7Fh
INT 21h
FILE_OPENED:
; Read or write from/to the program file
; File Handle in AX
...
END
Rather easy, isn't it?