7.1 Assembly Source, Toolchain, and Debugging
Chapter 6 examined one instruction at a time: operands entered the processor, a result changed, and flags recorded properties of that result. A useful program needs one more layer. It gives instructions names and storage, turns those names into addresses, combines separately written parts, and provides a way to observe a wrong execution.
This chapter uses 8086 Intel-style assembly. Exact directive spelling varies among MASM, TASM, NASM, and teaching assemblers, but the underlying jobs remain the same. When a directive is tool-specific, we use it to explain the job rather than claim that every assembler accepts identical source.
A source file mixes instructions with directions for the tools
An assembly source file is plain text. An instruction such as MOV AX,BX requests processor work. A directive such as .CODE, DB, or END tells the assembler how to organize or emit bytes; it is not executed by the processor.
A symbol is a name associated with a value. A label is a symbol attached to a location. In the following small module, start, again, and done name instruction addresses, while count names a data byte:
.DATA
count DB 3
.CODE
start:
MOV CL,count
XOR AX,AX
again:
INC AX
DEC CL
JNZ again
done:
HLT
END startDB means define byte: reserve one byte and initialize it. .DATA and .CODE identify logical data and code regions. END start tells this assembler which symbol is the program entry. None of those directives reaches the 8086 as an instruction.
The assembler normally performs at least two conceptual passes. It first discovers symbols and their offsets, then encodes instructions using those values. That is why a forward jump may name done before the done: line appears: the assembler can fill the displacement after it has built the symbol table.
Assembly, linking, and loading answer different questions
An object file contains encoded bytes plus metadata that is not yet final: exported symbols, external references, and locations that still need address repair. A linker combines object files, chooses a final layout, resolves cross-module names, and produces an executable image. A loader places that image in memory and establishes the initial machine state.
An invalid mnemonic or missing local label can be diagnosed while one module is assembled. An external symbol may be legal in that module yet remain without a provider; that is a link error. A program that builds successfully but computes the wrong value is a runtime logic error and needs observation.
A debugger turns hidden execution into evidence
A debugger controls a loaded program and exposes processor state. Its most important beginner operations are:
- A breakpoint pauses execution before the instruction at a chosen address runs.
- Single-step executes one instruction and pauses again.
- A register view shows values such as IP, AX, SP, and FLAGS.
- A memory view shows the bytes or words at selected addresses.
- A watch repeatedly displays a chosen register or memory location as execution changes it.
Suppose a loop should calculate :
MOV AX,3
XOR BX,BX
again:
ADD BX,AX
DEC AX
JNZ again
MOV [0200H],BXA breakpoint on DEC AX is reached after the preceding ADD but before DEC itself. At the first hit, AX is still 3 and BX is already 3. Confusing “the highlighted instruction” with “the last completed instruction” causes many incorrect traces.
Debugging should follow a hypothesis:
1. State the expected invariant, such as “before every ADD, AX is the next positive addend and BX is the sum accumulated so far.”
2. Place a breakpoint where the invariant can be inspected.
3. Compare expected and observed registers, flags, memory, and IP.
4. Single-step only far enough to find the first divergence.
5. Repair the source, rebuild it, and rerun the same test.
The first divergence matters more than the final bad result. If a mistaken JZ exits after the first iteration, the later memory write is only where the damage becomes visible; the wrong branch is where the program first departs from its intended path.
You can now distinguish source organization, assembly errors, link errors, and runtime evidence. Section 7.2 uses labels and conditional jumps deliberately to build whole decisions, loops, and event-driven state machines.