When you enter an external command or call a program through the EXEC function call, DOS determines the lowest availible address space to use as the start of available memory for the program being started. This area is called the Program Segment.
At offset 0 within the program segment, DOS builds the Program Segment Prefix control block. EXEC loads the program after the Program Segment Prefix (at offset 100h) and gives it control.
The program returns from EXEC by a jump to offset 0 in the Program Segment Prefix, by issuing an int 20h, or by issuing an int 21h with register AH=00h or 4Ch, or by calling location 50h in the PSP with AH=00h or 4Ch.
It is the responsibility of all programs to ensure that the CS register contains the segment address of the Program Segment Prefix when terminating by any of these methods except call 4Ch.
All of these methods result in returning to the program that issued the EXEC. During this returning process, interrupt vectors 22h, 23h, and 24h (Terminate, Ctrl-Break, and Critical Error Exit addresses) are restored from the values saved in the PSP of the terminating program. Control is then given to the terminate address.
When a program receives control, the following conditions are in effect:
For all programs:
byte ASCIIZ string 1 byte ASCIIZ string 2 .... byte ASCIIZ string n byte of zeros (0)
Typically the environment strings have the form:
NAME = VALUE
The length of NAME or VALUE can be anything desired as long as it still fits
into the 123 byte space (4 bytes are used by "SET ").
Following the byte of zeros in the environment, a WORD indicates the number
of other strings following.
If the environment is part of an EXECed command interpreter, it is followed
by a copy of the DS:DX filename passed to the child process. A zero value
causes the newly created process to inherit the parent's environment.
AL=0FFh is the first parameter contained an invalid drive specifier, otherwise AL=00h. AL=0FFh if the second parameter contained an invalid drive specifier, otherwise AL=00h.
Find the parent program segment prefix
comment | This program demonstrates how to read this program's copy of the parent's environment created by COMMAND.COM ... For DOS Ver 3.x, the fully justified path name of this program will also be displayed. The environment consists of null-terminated ASCII strings (ASCIIZ), with the entire list terminated by another null. A 2-byte entry at offset 2Ch in the program's program segment prefix (PSP) contains the segment of the copy of the environment. The code commenting, wording of printing messages and some of the code itself have been changed in a number of places. Written for MASM 5.0 by Hardin Brothers for PCResource. The original appeared in the April 1988 issue of their magazine. Entire contents of the April 1988 issue (C) Copyright 1988 by IDG Communications/Peterborough, Inc. Hardin Brothers is a freelance programmer and technical writer. Write to him at 280 N. Campus Ave., Upland, CA 91786. Enclose a self- addressed, stamped envelope for a reply. | LF equ 0Ah ; linefeed char CR equ 0Dh ; carriage return char STDOUT equ 1 ; standard output device EXIT macro val mov AH, 4Ch ; INT 21h service 4Ch: exit from program ; also returns a value. replaces old INT 20h. mov AL, val ; return value byte int 21h endm .MODEL SMALL .STACK .CODE start: cld ; clear direction flag: ; search from lowmem to highmem mov DS, ES:[2Ch] ; point DS to environment copy mov ES, ES:[2Ch] ; point ES there, too mov AH, 02h ; INT 21h service 02h: write a char to stdout mov DL, CR int 21h mov AH, 02h mov DL, LF int 21h mov DI, 0 ; set pointer offset from ES mov CX, 7FFFh ; max. environment size = 32768 bytes mov BX, STDOUT ; write to standard output device lp: test byte ptr ES:[DI], 0FFh ; test for end of list jz cmd_nam ; skip to cmd_nam if so sub AL, AL ; set AL = 0 mov DX, DI ; put start offset in DX repne scasb ; search for null char push CX ; save byte count mov CX, DI ; get tail + 1 sub CX, DX ; set CX to string length mov AH, 40h ; INT 21h service 40h: write to file/device int 21h mov AH, 02h mov DL, CR int 21h mov AH, 02h mov DL, LF int 21h pop CX ; restore byte count jmp lp ; continue search cmd_nam: mov AH, 30h ; INT 21h service 30h: get DOS version number int 21h cmp AL, 3 ; is this Ver 3.x ? jb endit ; skip to endit if not inc DI ; addr ES:DI points to word 0001h flag cmp word ptr ES:[DI], 1 ; check flag value jne endit ; skip to endit if unflagged add DI, 2 ; addr ES:DI points to head of name mov DX, DI ; save addr of head sub AL, AL ; set AL = 0 mov CX, 0FFh ; set search until end repne scasb ; search for null char mov CX, DI ; get addr of tail sub CX, DX ; set CX to string length mov BX, STDOUT ; write to stdout mov AH, 40h int 21h endit: mov AH, 02h mov DL, CR int 21h mov AH, 02h mov DL, LF int 21h EXIT 0 ; normal termination end start
Find the master program segment prefix
========== tech.notes/pc.code #29, from pmaupin, 3407 chars, Sat Jun 4 22:40:45 1988 ---------- TITLE: Finding DOS's master environment pointer This is a fragment of code that my SD.COM program uses to find the environment. This fragment is different than most ways of finding the environment, in that it finds the MASTER environment block, not the current process's parent's environment. This is useful in some cases, and has the added advantage that it does NOT behave differently when executing under CodeView, so you do NOT have to hard-code your system's DOS environment address into your program in order to debug it. EnvPtr EQU 2CH ; Offset in PSP CommandInterrupt EQU 2EH ; entry point into first Command.Com ; through interpreter DosSegPtr EQU CommandInterrupt * 4 + 2 ; FindEnvironment is passed: ; DS should point to program PSP ; FindEnvironment returns: ; ES points to master environment block, or program's copy if couldn't ; find the master. ; CX is length of block, or 0 if couldn't find the master. ; FindEnvironment destroys: ; AX, SI FindEnvironment PROC NEAR xor si,si ; Point to segment 0 mov es,si mov si, word ptr es:[DosSegPtr] mov ax,si call VerifyBlock ; make sure we've found COMMAND jnz GotBlock ; jump if not a good block -- ; use process's environment mov ax,es:[EnvPtr+10h] ; get COMMAND's environment ptr or ax,ax ; jump if COMMAND has a jnz MaybeGoodBlock ; subsidiary environment mov ax,si ; If no subsidiary, just use add ax,cx ; the allocation block inc ax ; immediately after COMMAND MaybeGoodBlock: call VerifyBlock ; verify that we have a good ; one, one way or another GotBlock: shl cx,1 ; multiply by 16 to get shl cx,1 ; length in bytes shl cx,1 shl cx,1 mov es,ax ret ; VerifyBlock tries to insure that we're pointing to a valid DOS ; allocation block. If not, returns the current process's environment ; block. VerifyBlock PROC NEAR dec ax ; get block header into ES mov es,ax inc ax cmp byte ptr es:[0],04Dh ; make sure signature is valid jnz UseCurrent cmp word ptr es:[1],si ; make sure owner is valid jnz UseCurrent mov cx, word ptr es:[3] ; retrieve the length ret UseCurrent: mov ax,word ptr ds:[EnvPtr] ; get current process's env xor cx,cx ; zero length ret VerifyBlock ENDP FindEnvironment ENDP So far, this seems to work. I would welcome any feedback on its efficacy, but if the feedback is negative, please give the DOS version and a detailed problem description. Thanks, Pat
Questions: