DOS PROGRAM SEGMENT

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:

  1. ) The segment address of the passed environment is contained at offset 2Ch in the Program Segment Prefix.

  2. ) The environment is a series of ASCII strings totalling less than 32k bytes in the form: NAME=value The default environment is 160 bytes. Each string is a maximum of 127 bytes terminated by a byte of zeroes for a total of 128 bytes, and the entire set of strings is terminated by another byte of zeroes. Following the byte of zeroes that terminates the set of environment string is a set of initial arguments passed to a program that contains a word count followed by an ASCIIZ string. The ASCIIZ string contains the drive, path, and filename.ext of the executable program. Programs may use this area to determine where the program was loaded from.

    The environment built by the command processor (and passed to all programs it invokes) contains a COMSPEC=string at a minimum (the parameter on COMSPEC is the path used by DOS to locate COMMAND.COM on disk). The last PATH and PROMPT commands issued will also be in the environment, along with any environment strings entered through the SET command.

    The environment that you are passed is actually a copy of the invoking process's environment. If your application terminates and stays resident through int 27h, you should be aware that the copy of the environment passed to you is static. That is, it will not change even if subsequent PATH, PROMPT, or SET commands are issued.

    The size of the environment may be changed from its default of 160 bytes by using the SHELL= command in the config.sys from in DOS version 3.1 up, or COMMAND.COM may be patched in earlier versions.

    The environment can be used to transfer information between processes or to store strings for later use by application programs. The environment is always located on a paragraph boundary. This is its format:
            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.

  3. ) Offset 80h in the PSP contains code to invoke the DOS function dispatcher. Thus, by placing the desired function number in AH, a program can issue a long call to PSP+50h to invoke a DOS function rather than issuing an int 21h.

  4. ) The disk transfer address (DTA) is set to 80h (default DTA in PSP).

  5. ) File Control Blocks 5Ch and 6Ch are formatted from the first two parameters entered when the command was invoked. Note that if either parameter contained a path name, then the corresponding FCB will contain only a valid drive number. The filename field will not be valid.

  6. ) An unformatted parameter area at 81h contains all the characters entered after the command name (including leading and imbedded delimiters), with 80h set to the number of characters. If the <, >, or | parameters were entered on the command line, they (and the filenames associated with them) will not appear in this area, because redirection of standard input and output is transparent to applications.

    (For EXE files only)
  7. ) DS and ES registers are set to point to the PSP.

  8. ) CS, IP, SS, and SP registers are set to the values passed by the linker.

    (For COM files only)
  9. ) For COM files, offset 6 (one word) contains the number of bytes availible in the segment.
  10. ) Register AX reflects the validity of drive specifiers entered with the first two parameters as follows:
            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.
  11. ) All four segment registers contain the segment address of the inital allocation block, that starts within the PSP control block. All of user memory is allocated to the program. If the program needs to invoke another program through the EXEC function call (4Bh), it must first free some memory through the SETBLOCK function call to provide space for the program being invoked.

  12. ) The Instruction Pointer (IP) is set to 100h.

  13. ) The SP register is set to the end of the program's segment. The segment size at offset 6 is rounded down to the paragraph size.

  14. ) A word of zeroes is placed on top of the stack.

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: