4

I've seen that many TSRs installs themselves at the top of conventional memory. To do so they rely on the value at 0:413h/int 12h to tell how much conventional memory is available, and then update this value accordingly after accommodating in that region.

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.

How did these TSRs deal with this problem?

  • 3
    Where have you seen this? TSRs loaded after DOS allocate their memory from DOS, they don’t lop off part of conventional memory... – Stephen Kitt Mar 23 '18 at 12:24
  • Not related: The problem that I found with TSRs is the clock gets slower and slower. I've seen particularly badly behaved TSRs losing over an hour a week. – cup Mar 23 '18 at 12:47
  • I remember writing a TSR which moved its code into its own PSP, from offset 80h, which was the area designated for the command line. This method saved a little memory. But I think that you were alluding to saving TSRs in the high memory area, which was possible only in DOS 5 and upwards. – No'am Newman Mar 23 '18 at 16:16
  • @cup: That's pretty normal. Every TSR seemed to hook the clock interrupt to check its hotkey; add up all of these polling routines and soon your clock interrupt is taking significantly more time to service each clock tick! – ErikF Mar 24 '18 at 7:29
  • @ErikF IIRC (and it was a long time ago) TSRs tended to check their hotkey by hooking the appropriate BIOS call, but – because doing much from inside any/most BIOS calls wasn't safe – they simply set a flag and activated themselves from inside the timer call. – TripeHound Mar 26 '18 at 9:31
8

Allocating memory by reducing the amount of installed memory as indicated by 0x00413 is normally only used by resident software loaded before DOS. When DOS initialises itself, it sets up memory arenas covering all the available conventional memory starting with the DOS data segment. COMMAND.COM uses the arena structure to determine where to place its transient portion. TSRs tell DOS how much memory to keep for them when they terminate and stay resident using interrupt 0x27 or service 0x31; that memory is permanently allocated to them (unless they remove themselves from memory). When COMMAND.COM re-installs its transient portion, it will also preserve those allocations...

A more intricate TSR could always manipulate the DOS memory arenas to reduce the overall amount of memory DOS could use, but that’s fraught with danger. Some DOS versions did have support for something like this — for example, DR DOS’s MEMMAX could add unused graphics buffers to the arenas, and remove them again.

(Curious readers can trace the memory control blocks themselves by calling interrupt 0x21 service 0x52 to get a pointer to the list of lists, and looking at ES:[BX-2] which is the first MCB.)

  • In fact, even DOS itself is placed into a (pro forma) memory block. Usually the sequence something like that goes |BIOS memory including IO.SYS (No MCB)| MCB1 - MSDOS.SYS| MCBs by CONFIG.SYS drivers| COMMAN.COM| Master Environment| Programm Environment| Programm| ... – Raffzahn Mar 23 '18 at 13:15
  • typically, a TSR with manual memory allocation would fetch the start of the memory chain, and then walk the list until the 'Z' entry was seen. If large enough to hold the TSR code, then the size of the 'Z' entry would be reduced and the memory beyond it would become invisible to DOS, and thus free for the TSR to use. – peter ferrie Mar 23 '18 at 18:32
  • Hihi ... following (and printing) the MCB chain was one of the very first programs I wrote under DOS 2. The next was to maipulate it to include memory I had added above 640 Ki - something that worked quite well on an XT. – Raffzahn Oct 9 '18 at 11:21
3

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 0:413h/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.

  • 1
    Yes, it does (not much else it can do). – Stephen Kitt Mar 23 '18 at 13:00
  • 1
    Also, the first releases of COMMAND.COM handled program execution themselves (serious nit-picking here), there was no DOS service for that. – Stephen Kitt Mar 23 '18 at 13:02
  • 1
    Yeah, I know what you mean — something like Wikipedia’s support would be nice (with hyperlinks). – Stephen Kitt Mar 23 '18 at 13:06
  • 1
    Indeed, indeed! And DOS 1 didn’t even have directories, so there’s definitely not much point discussing it unless one is specifically interested in DOS 1 on a 5150... INT 0x27 on the other hand was used with DOS 2+, alongside INT 0x21 AH 0x31. But again that’s going into too much detail really. Let’s just tell people to read DOS Internals and Undocumented DOS ;-). – Stephen Kitt Mar 23 '18 at 13:14
  • 1
    @Raffzahn Post a feature request on meta. I'll support it; currently I use a combination of sup and escaping that's really kludgey. – wizzwizz4 Mar 27 '18 at 6:24
2

Going by (dim and fading) memory, it might be that some TSRs temporarily moved themselves towards the top of normal memory before moving just the parts that needed to remain resident back down to the bottom of memory...

Many/most TSRs (as they exist on disk) have two major components:

  1. A resident bit that needs to stay in memory to do whatever it is meant to do, and:
  2. A transient bit that needs to install/configure the resident bit but can be thrown away afterwards.

If you wrote a TSR directly in assembler, you (can) have full control over how these bits are laid out on disk, so that the resident part is loaded first in memory but execution transfers to the transient part that is loaded after the resident part. When the transient part has done its job, it reports the end of the resident part to DOS and the memory the TSR consumes is minimised.

If a TSR is written in a higher-level language, it may not be possible (or as easy) to achieve the "ideal" layout. I'm pretty sure I've seen / heard of TSRs that either:

  • on loading relocate most of their code towards higher memory (freeing the original load address), perform initialisation/configuration and move the resident-only parts back to low memory, or:

  • directly load only a small loader. This could then load the initialise/config code into higher memory, which could then load the resident part into low memory.

In either case, the end result is that only the resident part is left in low memory to minimise the TSR's footprint as before.


It's also conceivable (my memory even more faded here) that the more complex TSRs only loaded a bare-minimum of their code into low memory – just enough to monitor anything they needed to, and to detect whatever condition should make them "active". When they needed to activate, they then load the appropriate "module" into memory. It might be that these "temporary" bits of code were loaded into high memory (possibly not with complete safety, but where it is least likely to cause problems).

  • This was indeed fairly common, at least for larger TSRs. Even TSRs with a nice layout could benefit from moving themselves around in memory, if only so they could free the environment MCB... (Not that the latter was a huge issue, since it got reused.) – Stephen Kitt Oct 9 '18 at 12:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.