How to Build a TSR-Aware Batch Menu System on FreeDOS
A complete walkthrough building a batch-file boot menu that correctly manages memory-hungry TSRs — loading only what a chosen task actually 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.