Two programs stand between a source file and something DOS can run. The assembler reads one .ASM file and writes an object module (.OBJ): the machine code, the names the module defines for others and the names it needs from them, and notes on every place that holds an address not yet known. The linker reads object modules and libraries, matches each needed name with its definition, lays the segments out one after another, fills in the addresses, and writes the program.

The program is a .EXE or a .COM. A .EXE starts with a header: where the code starts (CS:IP), where the stack is (SS:SP), and a table of the words DOS must adjust because the program is loaded at a segment nobody knew in advance. It can be as large as memory allows. A .COM is a plain memory image, loaded at offset 100h of one segment and started there; it has no header and no relocations, so it fits in 64 KB and uses no segment addresses of its own. Both assemblers and linkers can also write a listing (.LST: each source line with its offset and bytes) and a map (.MAP: where every segment and public name ended up).

The cheat sheet

ToMASM 6.11JWasmTASM
make a one-file .COMML /AT HELLO.ASMJWASMR -bin -Fo=HELLO.COM HELLO.ASMTASM HELLO, then TLINK /t HELLO
make a one-file .EXEML HELLOX.ASMJWASMR -mz HELLOX.ASMTASM HELLOX, then TLINK HELLOX
only assembleML /c MAIN.ASM PRINT.ASMJWASMR MAIN.ASM PRINT.ASMTASM MAIN, then TASM PRINT
link modulesLINK MAIN+PRINT,PROG;JWLINK format dos file MAIN.OBJ,PRINT.OBJ name PROG.EXETLINK MAIN PRINT, PROG
build a libraryLIB UTIL.LIB +PRINT.OBJ;JWLIB -n UTIL.LIB +PRINT.OBJTLIB UTIL +PRINT
link with a libraryLINK MAIN,PROG,,UTIL.LIB;JWLINK format dos file MAIN.OBJ library UTIL.LIB name PROG.EXETLINK MAIN, PROG,, UTIL
write a listingML /Fl ...JWASMR -Fl ...TASM /l ...
write a mapML /Fm ..., or LINK's third fieldJWLINK ... option mapTLINK writes one (/x: none)
debug with symbolsML /Zi, then CV PROGDEBUG (no symbols)TASM /zi, TLINK /v, then TD PROG
step in the consoleDEBUG PROG.EXEDEBUG PROG.EXEDEBUG PROG.EXE

Every MASM and JWasm line above was run on a dosbench box, and so were TLINK and TLIB; TASM itself was not (see its article). The sources are the two below and the pair in the article on several modules.

The two kinds of program

HELLO.ASM: a .COM. The tiny model puts code and data in one segment; ORG 100h leaves room for the PSP.
.MODEL TINY
.CODE
ORG 100h
START: MOV DX, OFFSET MSG
MOV AH, 09h
INT 21h
MOV AX, 4C00h
INT 21h
MSG DB 'Hello from a .COM', 13, 10, '$'
END START
HELLOX.ASM: a .EXE. The small model has a code segment and a data segment (with the stack); the program points DS at its data itself.
.MODEL SMALL
.STACK 256
.DATA
MSG DB 'Hello from an .EXE', 13, 10, '$'
.CODE
START: MOV AX, @DATA
MOV DS, AX
MOV DX, OFFSET MSG
MOV AH, 09h
INT 21h
MOV AX, 4C00h
INT 21h
END START

On a dosbench box

The workbench's Build button assembles the open file for you: with MASM 6.11 when the box has it in C:\MASM611, otherwise with JWasm from C:\JWASM, and keeps the listing so the debugger can show your source lines. Add JWasm, JWlink and DOS-debug to a box from the catalog; they are free. MASM and TASM are not, so bring your own copy in the box's files if you have one.