[mod.computers.ibm-pc] Info-IBMPC Digest V5 #90

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