Info-IBMPC@B.ISI.EDU (Info-IBMPC Digest) (10/08/86)
Info-IBMPC Digest Tuesday, October 7, 1986 Volume 5 : Issue 90 This Week's Editor: Phyllis O'Neil Today's Topics: Undocumented DOS Environment Info Increasing Environment Space ---------------------------------------------------------------------- Date: Tue, 30 Sep 86 08:44:22 PDT From: csustan!smdev@lll-crg.arpa To: nike!B.ISI.EDU!Info-IBMPC@ucbvax.Berkeley.EDU Subject: Undocumented DOS Environment Info I've been following the discussion on the DOS environment and thought that I would dig out this old paper. The paper applies specifically to MsDos 2.11 on an AT&T PC6300, but it is my understanding that it covers 2.x in general. Feel free to edit, archive, ignore, etc. (Note that there are 2 programs at the tail.) \scott [EXENV.C and MORENV.C have been added to the library. - ed] Scott Hazen Mueller lll-crg.arpa!csustan!smdev City of Turlock work: (209) 668-5590 -or- 5628 901 South Walnut Avenue home: (209) 527-1203 Turlock, CA 95380 <Insert pithy saying here...> DOS Memory Blocks and the Environment MS-DOS provides what it calls an environment. The environment is a block of memory with null-terminated strings of the form name=value. The last string in the environment is followed by an additional null. In theory, the environment can be up to 32K long; in practice, however, its size is severely limited. The limitation is brought about by two factors: 1) any program or batch file that is executed is loaded up immediately above the environment so that no extra space is available between the environment and the program for expansion; 2) no tools are available within MS-DOS for increasing the environment size except by manually entering new environment strings. An application can find its local copy of the environment by referring to a specific location in its Program Segment Prefix that contains the segment address of the local environment. When a program is running under Debug, it is not given a new copy of the environment, but rather, shares the copy that Debug uses. This can be verified by invoking Debug and looking at the PSPs of Debug and of the program being debugged. DOS provides primitive functions for the creation and manipulation of blocks of memory. It tags memory blocks by assigning the paragraph preceding the block as a block header. A block header consists of an identification flag value, either 'M' (hex 4D) or 'Z' (hex 5A). The 'M' is used as a flag to indicate that the memory block chain continues; the 'Z' is the end-of-list flag. Following the identification flag come a pointer to the next item, which is a segment value, and a size in paragraphs. Both are words. The rest of the block header paragraph appears to be unused. The initial approach to the question of increasing the size of the environment involved three steps: 1) increment the size field in the block header; 2) create a new header for the following block; 3) decrement the size field in the following block header. In order to minimize possible adverse effects, the increment size was limited to one paragraph. A program to perform this task, exenv.c, is given as an appendix. It was found that this program performed acceptably in an interactive mode, but when it was run from a batch file, the system would execute the program and then state that the batch file could no longer be found. Obviously, some sort of information about which batch file was being used had been overwritten. In order to determine precisely what the difference between interactive and batch use was, a program consisting of a breakpoint interrupt was created and run out of a batch file (which was executed by a copy of command.com that was being run under Debug). When the program interrupted, memory was examined and it was determined that a block of memory had been allocated to hold some information on the batch file, including the full name, and that, under ordinary circumstances, this block of memory would immediately follow the root environment. Therefore, since the original procedure for the addition of memory to the environment had overwritten part of the following block, it had destroyed the batch file information, and, furthermore, could not be run under a batch file since it would always do so. The second approximation to a solution assumed that the environment block was a real memory block, and that the Setblock call could increase the size of this block once the block immediately following it had been deallocated; however, it was at this point that it became apparent that the root environment block was not a real memory block as this entirely straightforward procedure did not work. The procedure that does work is given in the program morenv.c. In this procedure, the size of the root block is increased by the size of the following block (plus one for the header) explicitly, and the header of the following block is destroyed. In a sensible system, this would fail when one block header got destroyed without repairing the chain. However, in MS-DOS, the chain is not started with an explicit pointer, but rather as an offset from the start of the root environment block, which is why the root environment block's chain pointer can point at the PSP template, but the length field must yield an offset to a new block (which can be shown experimentally). So, the header of the first non-system block can be destroyed, since the start of non-system memory is found by using the length field of the root environment block, which must therefore be kept updated. This works correctly with batch files because it does not destroy any of the information that is stored in the memory block. When the program is run, it concatenates the following block onto the system memory area; however, the routine that executes the batch file only cares about whether or not it can find its data; it does not check between commands to see if its data area has been stolen away from it. Since it is possible to overwrite this work area with a set command, it is recommended that no set commands be issued immediately after increasing the environment size, but rather that the environment size be increased in one batch file, and the environment strings be added to in another. It is unfortunately not possible to call the second batch file from the first; however, if the environment size is increased in autoexec.bat, individual command procedures that need additional environment strings can add and delete their own material; such a system is better than manual entry, at the very least. /* exenv v1.0 Expand the original parent environment by one paragraph. Use this program very carefully. Note that this makes use of undocumented features of DOS. Written in DeSmet C by Scott Hazen Mueller Water Quality Control 901 S. Walnut Rd. Turlock, CA 95380 */ #include <stdio.h> #define A_BLOCK 8 #define COPY_LEN 7 unsigned i; char buf[COPY_LEN+1]; main() { buf[7] = NULL; for (i=0; i<0xffff; i++ ) if ( testmem() == A_BLOCK ) { show7(); if ( !strcmp( buf, "COMSPEC" ) ) { addpar(); exit( 0 ); } } } testmem() /* Test a segment to see if it is the start of a memory block by attempting to modify blocksize at that segment to 64K paragraphs. */ { #asm mov ax,word i_ mov es,ax mov bx,0FFFFH mov ah,04AH stc int 021H #endasm } show7() /* Copy the first seven bytes (enough to spell "COMSPEC") from the segment located by i into buf. */ { #asm lea di,buf_ ;get location of buf push ds pop es ;move to es:di push es mov ax,word i_ ;move from ds:si mov ds,ax mov si,0 mov cx,7 ;copy 7 bytes cld lab: movsb loop lab pop ds #endasm } addpar() /* Add a paragraph to a block by increasing this block's size indicator, moving the block header for the next block forward one paragraph, and shrink- ing that block by one paragraph. */ { #asm mov ax,word i_ ;get block segment dec ax ;block header is one paragraph back mov es,ax ;get a segment register set mov ax,es:[0003] ;get the block size (in paragraphs) inc ax ;increase the size mov es:[0003],ax ;save the new size mov bx,es add ax,bx ;get the next block (current position+new size) push ds ;save ds for later mov ds,ax ;need ds for block move instruction inc ax ;es will point to new header of next block mov es,ax mov si,0 ;start at offset 0 mov di,0 mov cx,7 ;move 7 bytes cld ;in the forward direction flab: movsb loop flab mov ax,es:[0003] ;resize the next block dec ax mov es:[0003],ax ;save new size pop ds ;restore #endasm } /* morenv v1.0 Expand the memory available to the parent environment segment by finding and adding the next memory block onto the segment. Written in DeSmet C by Scott Hazen Mueller Water Quality Control 901 S. Walnut Rd. Turlock, CA 95380 */ #include <stdio.h> #define A_BLOCK 8 #define COPY_LEN 7 unsigned seg, eseg, nexseg; char buf[COPY_LEN+1]; main() { buf[7] = NULL; seg = 0; while ( ++seg < 0xFFFF ) if ( testmem() == A_BLOCK ) { show7(); if ( !strcmp( buf, "COMSPEC" ) ) { eseg = seg; while ( ++seg < 0xFFFF ) if ( testmem() == A_BLOCK ) coalesc(); } } } testmem() /* Test a segment to see if it is the start of a memory block by attempting to modify blocksize at that segment to 64K paragraphs. */ { #asm mov ax,word seg_ mov es,ax mov bx,0FFFFH mov ah,04AH stc int 021H #endasm } show7() /* Copy the first seven bytes (enough to spell "COMSPEC") from the segment located by i into buf. */ { #asm lea di,buf_ ;get location of buf push ds pop es ;move to es:di push es mov ax,word seg_ ;move from ds:si mov ds,ax mov si,0 mov cx,7 ;copy 7 bytes cld lab: movsb loop lab pop ds #endasm } coalesc() /* Concatenate the block pointed at by seg onto the end of the block pointed at by eseg. */ { eseg--; seg--; #asm push ds mov ax,word eseg_ ;point es at eseg mov es,ax mov ax,word seg_ ;point ds at seg mov ds,ax mov ax,[0003] ;get the length of seg mov bx,es:[0003] ;get the length of eseg add ax,bx ;add the lengths inc ax ;plus one for the block header mov es:[0003],ax ;save the new length mov byte [0000],0 ;zap the M in the block header of the tail block pop ds #endasm exit( 0 ); } ------------------------------ Date: Mon, 6 Oct 1986 15:31 EDT From: LENOIL@XX.LCS.MIT.EDU To: Raymond Chen <6101778%PUCC.BITNET@WISCVM.WISC.EDU> Cc: info-ibmpc@B.ISI.EDU Subject: Increasing Environment Space, Options to COMMAND /P makes the options selected on this invokation of COMMAND [P]ermanent. (otherwise, they vanish when you type EXIT. Also if you don't make them permanent, you end up gobbling 17K of memory because DOS keeps a copy of the "old" COMMAND.COM around just in case you do type EXIT.) Not true, I'm afraid. The /P command simply tells COMMAND.COM to terminate-and-stay-resident, and set the EXIT command to do nothing. There is no memory savings over running COMMAND.COM without the /P option. In fact, there is a memory loss, since you end up creating another resident copy of COMMAND.COM. Furthermore, /P has the additional side-effect of running the AUTOEXEC.BAT file. DOS invokes the first COMMAND.COM with the /P option. ------------------------------ End of Info-IBMPC Digest ************************ -------