Exercise 1: Why a Single-Pass Assembler Can't Handle LD R1, N — Possible Solution ==================================================================== WHY A SINGLE TOP-TO-BOTTOM PASS FAILS ------------------------------ If an assembler tried to generate machine code immediately, line by line, in a single pass, it would reach LD R1, N at address x3001 before it has ever seen N's own line (x3007) later in the file. To encode LD correctly, the assembler needs N's actual memory address RIGHT NOW, in order to compute the PC-relative offset per assembly1-3's formula (offset = target address - (this instruction's address + 1)). But in a single pass, N's address genuinely doesn't exist yet as far as the assembler knows -- it hasn't been assigned one, because the assembler hasn't reached that line of the file yet. There's no way to compute a real number for something that hasn't been determined. A single-pass assembler would have exactly two bad options at that point: guess a placeholder value and hope to fix it later (with no defined mechanism for going back and fixing it), or simply fail with an error the moment it encounters any forward-referenced label -- which would make forward references impossible to write at all, even though they're completely ordinary and necessary (loops that branch backward still need to reference forward-defined data like N). WHY PASS 1 SOLVES IT ------------------------------ Per the chapter's own two-pass algorithm, Pass 1's entire job is to scan the WHOLE file first, purely to build the symbol table, before Pass 2 ever tries to generate real machine code for anything. By the time Pass 2 reaches LD R1, N, Pass 1 has already scanned past N's own line (x3007) and already recorded "N -> x3007" in the symbol table. Pass 2 doesn't need to guess or defer anything -- it simply looks N up in a table that's already complete, computes the real offset, and encodes the instruction correctly on the first (and only) attempt at generating code. WHY THIS WORKS AS AN ANSWER ------------------------------ It explains concretely why a single pass has no way to know N's address at the moment it's needed (the address genuinely doesn't exist yet in that pass's own scan), and explains that Pass 1 solves the problem by fully separating "figure out where every label lives" from "generate code that needs to know where labels live," so Pass 2 never has an unresolved forward reference to deal with.