Skip to content
FreeDOSHow-To Published Updated 4 min readViews unavailable

How to Build a TSR-Aware Batch Menu System on FreeDOS

Building a batch-file boot menu that manages memory-hungry TSRs — loading only what a chosen task needs, freeing conventional memory for everything else.

Combining batch file scripting with awareness of how TSRs consume conventional memory lets you build a menu system that loads only the memory-resident programs a specific chosen task actually needs — rather than loading everything, always, regardless of what you’re about to do.

Step 1: identify which TSRs each intended task actually needs

Task: word processing  → needs mouse driver, no network
Task: BBS/networking    → needs mTCP or packet driver TSRs
Task: gaming            → needs Sound Blaster env vars, mouse

Different tasks genuinely need different resident programs — the whole point of this menu system is avoiding loading TSRs a given session doesn’t need.

Step 2: build a CONFIG.SYS boot menu for the base-level choice

[MENU]
MENUITEM=WORK,Productivity tasks
MENUITEM=GAME,Gaming (maximum memory)
MENUITEM=NET,Networking tasks

[WORK]
DEVICE=C:\FREEDOS\HIMEM.EXE

[GAME]
DEVICE=C:\FREEDOS\HIMEM.EXE
DEVICE=C:\FREEDOS\EMM386.EXE RAM
DOS=HIGH,UMB

[NET]
DEVICE=C:\FREEDOS\HIMEM.EXE
DEVICE=C:\FREEDOS\EMM386.EXE RAM
DOS=HIGH,UMB

This chooses the memory-management foundation appropriate to the category before AUTOEXEC.BAT even runs.

Step 3: check the boot menu choice from AUTOEXEC.BAT

IF "%CONFIG%"=="WORK" GOTO WORKSETUP
IF "%CONFIG%"=="GAME" GOTO GAMESETUP
IF "%CONFIG%"=="NET" GOTO NETSETUP

FreeDOS sets an environment variable reflecting which CONFIG.SYS menu block was selected, letting AUTOEXEC.BAT branch accordingly.

Step 4: load only the TSRs relevant to the chosen branch

:WORKSETUP
LOADHIGH C:\FREEDOS\CTMOUSE.EXE
GOTO END

:GAMESETUP
SET BLASTER=A220 I5 D1 H5 P330 T6
LOADHIGH C:\FREEDOS\CTMOUSE.EXE
GOTO END

:NETSETUP
LOADHIGH C:\MTCP\ETHERDRV.COM
GOTO END

:END

Step 5: use LOADHIGH consistently to keep TSRs out of conventional memory

Every TSR loaded via LOADHIGH (rather than a plain DEVICE=/direct invocation) goes into upper memory blocks where available, directly maximizing the conventional memory left over for whatever application actually runs afterward.

Step 6: check final available memory per branch

MEM

Confirm each branch actually leaves the expected amount of conventional memory free — a branch that’s supposed to be memory-maximized but shows a disappointing free-memory figure likely has something loading low that should be LOADHIGH-ed instead.

Step 7: add an “uninstall on exit” step for TSRs that support it, for a cleaner return to the menu

Not all TSRs support clean removal (see TSR programs for why unloading is fragile) — for ones that do, calling their uninstall option before returning to a menu prompt keeps repeated menu use from stacking multiple copies of the same TSR.

Why branching TSR loading by task, rather than loading everything at boot, is worth the extra scripting

Every resident program loaded “just in case” permanently reduces conventional memory for whatever runs afterward — building a menu that loads exactly what a chosen task needs, and nothing more, is a direct, practical application of everything FreeDOS’s memory model forces you to think about, turned into a system that does the right thing automatically rather than requiring you to remember which TSRs a given session actually needs each time.

Giving the menu a sensible unattended default

If this machine sometimes boots without anyone present to make a menu selection — a headless test box, or simply a convenience for the most common case — MENUDEFAULT in the [MENU] block sets both a default option and a timeout in seconds, after which that default fires automatically if no key is pressed:

[MENU]
MENUITEM=WORK,Productivity tasks
MENUITEM=GAME,Gaming (maximum memory)
MENUITEM=NET,Networking tasks
MENUDEFAULT=WORK,10

Picking whichever branch is genuinely the safest, most conservative default (rather than the most memory-maximized one) makes sense here specifically — an unattended boot defaulting into the gaming profile’s more aggressive memory-manager configuration is a worse failure mode than one defaulting into the plainer, more universally compatible productivity profile.

Testing every branch after a genuine cold boot, not a shell restart

The same principle that applies to any multi-configuration FDCONFIG.SYS setup applies here doubly, since this system layers a batch-file branch on top of a CONFIG.SYS menu choice: confirm each combination (menu selection plus the batch logic that reads %CONFIG%) by actually power-cycling and selecting that specific option, not by assuming the batch-file logic works correctly just because the CONFIG.SYS portion loads without error. A typo in one IF "%CONFIG%"== comparison can silently fall through to an unintended branch, loading the wrong set of TSRs for whatever you actually selected.

Extending the pattern to a fourth or fifth task category

Adding another task category is mechanically identical to the first three: a new MENUITEM line, a matching [BLOCKNAME] section in FDCONFIG.SYS for anything that genuinely needs a different memory-manager foundation, and a corresponding IF "%CONFIG%"== branch in AUTOEXEC.BAT. The pattern scales cleanly precisely because each piece (menu option, CONFIG.SYS block, batch branch) maps one-to-one to the others — the discipline worth keeping as you add categories is making sure all three stay in sync, since a menu option with no matching batch branch silently falls through to whatever default behavior your AUTOEXEC.BAT happens to have for an unmatched case.

A menu that’s grown to five or six branches is also a good prompt to revisit whether some of them still earn their own separate category, or whether two branches that turned out to need nearly identical TSRs could simply be merged into one.

Related:

Sources:

Comments