A COM file carries nothing but the program, so everything about how it runs is decided by DOS: where it goes in memory, what the registers hold, and how much room it has. This page goes through it in the order DOS does.
COM or EXE
DOS looks at the first two bytes of the file. If they are MZ (or
ZM), it loads the file as an MZ
.exe, with its header and relocations; otherwise it loads it as a COM
program. The name plays no part, so a .com file can be an EXE inside, and
many later ones are: DR DOS 6.0’s COMMAND.COM, and the
.com tools of Windows NT such as more.com,
tree.com, format.com and chcp.com, which are
Windows programs.
The extension matters only for finding the file. When a command is typed without one
and both NAME.COM and NAME.EXE exist, DOS runs the COM file.
On Windows NT the default PATHEXT list still puts .COM before
.EXE.
Memory
A COM program gets the largest free block of memory, which is normally all of it. DOS
builds the Program Segment Prefix in the first 256
bytes of the block and copies the file in straight after it, at offset
0x100. Nothing in the file is changed on the way: there are no relocations,
because every address in the program is an offset within this one segment, whatever
segment it is.
0x0000PSP built by DOS256 bytes0x0100the file, byte for bytefile size…free: the program’s uninitialised data, buffers and heapthe rest0xFFFEstack, a zero word on top, growing downas used
The word at offset 0x02 of the
PSP gives the segment just past the end of the block. Because the program owns the
whole block, it has to give memory back before it can allocate more or run another
program: INT 21h service 4Ah shrinks the block, usually to the end of the
program and its stack.
The size limit
Code, data and stack all live in one 64 KB segment, and the first 256 bytes of it are
the PSP, so a COM file can be at most 65,536 − 256 = 65,280 bytes
(0xFF00). In practice it must be smaller, to leave room for the stack and
any data the program does not store in the file.
Larger files with a .com name and no MZ do exist, among
them a few DOS games. The part past 0xFF00 cannot be in the program’s
segment, so such a program can only reach it by reading its own file. The explorer
shows it as a
separate block.
Registers at the start
| Register | Value |
|---|---|
CS, DS, ES, SS | The segment of the PSP, which is also the program’s segment |
IP | 0100h: the first byte of the file |
SP | FFFEh, the last word of the segment, or less when the block is smaller than 64 KB. The word on top of the stack is zero. |
AL | FFh if the first command-line argument starts with a drive letter that is not valid, otherwise 00h |
AH | The same for the second argument |
DOS does not promise anything for the other registers, and a program should not
depend on them. The zero word on the stack is there so that a plain RET
ends the program (see below).
Ending the program
| Instruction | What happens |
|---|---|
RET | Pops the zero word into IP, so the program jumps to offset 0 of its segment, where the PSP starts with INT 20h. This works only when SP is back where it started. |
INT 20h | Ends the program. CS must be the PSP’s segment, which in a COM program it always is. |
INT 21h, AH = 00h | The same, as a DOS service. |
INT 21h, AH = 4Ch | Ends the program with a return code in AL, which a batch file can test with IF ERRORLEVEL. From DOS 2.0, and the way Microsoft recommends. |
INT 21h, AH = 31h | Ends the program but keeps the first DX paragraphs of it in memory, with a return code in AL: terminate and stay resident (TSR). |
INT 27h | The older TSR call: keeps everything up to offset DX of the segment. |
Resident programs are why many COM files put their data and the code that stays in memory at the front, and the code that sets them up at the end: the set-up code then falls outside the part that is kept. The file starts with a jump to it, which the explorer shows as the entry jump.
From CP/M
CP/M, the 8-bit system that DOS followed, also loaded its .com programs
at 0100h, with a 256-byte area in front. The PSP keeps that area’s layout
where it can: it starts with an exit call, it has an entry into the system at offset
5, a default file control block at 5Ch, and the command line
at 80h, all where CP/M had them. That is what made it practical to
translate CP/M programs to DOS, though the two systems’ COM files are not compatible:
CP/M’s hold 8080 or Z80 code.
Under Windows
32-bit versions of Windows run DOS COM programs in NTVDM, an emulated DOS machine.
64-bit Windows has no NTVDM, so it cannot run them without an emulator such as
DOSBox. The .com tools it still has
are Windows programs and run normally.