[comp.os.msdos.programmer] XMS/EMS/changing the master environment

shaunc@gold.gvg.tek.com (Shaun Case) (06/18/91)

Hello helpful netters!

Last week you may have seen a post from me asking about XMS memory handles.
I wanted to know if they are limited to any particular range, so I could
speed up a search through all of them, or at least set an upper bound on
the time it would take.

I received two replies, both which indicated that XMS handle numbers
are implementation dependent.  One source also indicated that EMS
handles may be as well.

It seems to be time to ask again, with more information.  I think this
will be of general interest to many people here, if not now, then
some time in the future.  (I could be wrong, of course.  Read at your
own risk.  :-)

I use a number of programs that don't restore the screen when they exit.
I want to fix this.  Right now, I have a program that runs, saves the
text screen to a disk file, then exits.  Then, the main application
is run.  After it exits, my program runs again, restoring the text screen
and cursor position from the disk file.

This is all fine and dandy, and I even released it (in the public domain.)
However, if you don't have a ramdisk, or a fast hard disk, then there
is a noticeable delay between the main application exit and the screen
restore.  I recently got copies of some excellent EMS and XMS libraries
for Turbo/Borland C by James Birdsall.  (Registration forthcoming, pending
successful completion of this problem, if you're listening, James!)

Everything in the libraries works fine.  My problem is that one my
program exits and then reloads, it doesn't know which EMS or XMS handle
it was using!  My solution now is:

EMS:  Require 4.0 or above, and use the name setting feature to indicate
      page ownership by my program.

XMS:  Copy the same string into the first n bytes of the 1st page, thus
      doing a kludgy version of the EMS setname.

For both types, my program starts scanning handles at 0 through whatever
is necessary to support 16 MB, search for valid handles, and looking
for the names whenever it finds a valid handle.  To me, this appears
really stupid, and if handle numbers are implementation dependent, 
possibly much too slow.  Bogosity.

My other possible solution was to set an environment variable
like SET MYPROG=NNNNNNNNNN, and then sprintf() in an E or an X for
the memory type, and then the handle number.  Of course, this requires
modifying the master environment, which, according to all accounts, is
a rough thing to do.  Neverthless, I gave it a shot and failed; TASM/
TC code is below, for anyone who wants to try to fix it.  (I didn't
write the ASM code, I swiped it from Simtel.)  

Anyhow, does anyone have a good idea about how (or where) I can
store 17 bits without going to disk?  This really has me stumped.

Any and all solutions appreciated.  Email or posting is fine, I do
both.  


Shaun.


The program below generates  'Abnormal program termination' in the .asm
module.

--- cur here for main.c ---

#include <stdio.h>
#include <dos.h>


extern char *FE(short psp);

int main()
{

    char *start;

    start = FE(_psp);

    while (*start != NULL)
    {
        puts(start);
        start += strlen(start);
    }

    return 0;
}
--- cut here for me.asm ---

        .MODEL small
        .STACK 100h
        .DATA
        .CODE
;
;==========
;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

                   PUBLIC C FE
FE                 PROC  C NEAR psp: WORD
                   mov   ds, psp
                   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  C 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

FE   ENDP
                   END
;
;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

a_rubin@dsg4.dse.beckman.com (Arthur Rubin) (06/22/91)

In <2556@gold.gvg.tek.com> shaunc@gold.gvg.tek.com (Shaun Case) writes:

>Hello helpful netters!

>Anyhow, does anyone have a good idea about how (or where) I can
>store 17 bits without going to disk?  This really has me stumped.

According to MEMORY.LST, attributed to Robin Walker in Ralf Brown's interrupt
lists, memory addresses 40:F0-FF are reserved to the user.

If nothing else around uses them, that should do what you are looking for.


--
2165888@mcimail.com 70707.453@compuserve.com arthur@pnet01.cts.com (personal)
a_rubin@dsg4.dse.beckman.com (work)
My opinions are my own, and do not represent those of my employer.