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
************************
-------