I've seen that many TSRs installs themselves at the top of conventional memory.
I can't remember any and I'm not so sure why they should, as that's against DOS' memory strategy and brings no advantage. In fact, it may even invoke many problems, depending on the way it's done. Then again, there are many strange people in DOS lands.
To do so they rely on the value at
int 12h to tell how much conventional memory is available, and then update this value accordingly after accommodating in that region.
That's a strategy programs may use on (BASIC) home computers to protect themself. Under DOS it's useless, as DOS only checks this value once during boot to determite the available memory when seting up its memory management.
But it seems that the transient part of command.com doesn't check this value and will overwrite whatever it's there when it regains control, thus overwriting the TSR.
That's not related to
COMMAND.COM at all not the transient nor the resident. Memory management is handled by DOS.
COMMAND.COM is just another application requesting or releasing memory via the usual management functions. Memory is handled by DOS as a series of blocks, linked via a series of MCB's (Memory Control Blocks). DOS offers a series offunctions (Allocate/ Free/ Resize)
When loading (*1) a programm, and a TSR isn't anything else, a memory block, according to the requirements is allocated. This is the first one (lowest address) available, which is equal or greater than the amount of memory requested in the program header (*2). For
.COMPrograms the program size is used.
How did these TSRs deal with this problem?
It isn't a problem at all. There is no advantage in moving code up in a DOS system, thus there is no need.
When started, the (TSR) program is loaded at the lowest possible address Usually right after
COMMAND. Now it (should) frees all non essential memory blocks (*3), then do whatever setup is needed and terminate via Terminate and Stay Resident, (Function 31h) - hence the name. This function is much like the regular exit, except it also return the length of memory to be retained in the current program block.
Job done, TSR stays right above the resident part of
COMMAND.COM. There is no need to move it anywhere else, as it doesn't matter if memory is reservd for a TSR from the beginning or the end.
To do what the question implies a TSR must either do a somewhat weired memory dance in requesting all memory, then shrinking the last (and highest) block by the amount of Memory needed (plus 16 bytes for the MCB), then requesting this block again, moving its routine there and then terminating with with that block resident (IIRC it still needs patching the MCBs PID). Or just screwing the memory management by manipulation the MCBs to create such an area.
But again, it's total useless, and I can not remember any TSR acting that way.
As an Afterthought, could it be, that you either mixed this in (your) memory with the steps needed to build a safe area for assembly programms within a BASIC environment (aka home computer)?
Or did you maybe think of 'high loading' a programm/TSR into UMB or HMA? Before DOS 4 the usage of these areas did require 'some nudgeing' to the DOS. (Heavy nudging before DOS 3)
*1 - BTW, not COMMAND is loading the program, but DOS is, as COMMAND is just parsing the command line and then issueing a Function 40h call to let DOS do it's work - the same way any program can load any other.
*2 - Starting with DOS 3 Function 58h allowed to set the strategy used. Either First Free block of memory large enough to satisfy the request (Like before and default) or Best Fit Low by searching for the smallest block large enough to satisfy the request or Last Fit Low by searching for the smallest block large enough to satisfy the request form the end of memory. Later (DOS 4) two modifiers to include UMB in the search or only search in UMB.
*3- Every programm gets two blocks by default: An environment block and a programm block. It's good style to free the environment block when no longer needed, as that hole can be used by the next program load again.