Assembly Language and Assembler Design: Two-Pass Translation with a Complete Worked Example

Follow a 16-bit Basic Computer program through pass one, pass two, hexadecimal encoding and execution, with every symbol, address and result shown.

KnowledgeGate Team

Exam prep & CS education

Updated 15 Oct 20266 min read

Reading a short assembly program is manageable until one question asks you to track the location counter, forward references, symbol addresses, instruction bits and signed data together. Then a simple program becomes five linked problems. A small 16-bit Basic Computer program goes through both assembler passes before execution. Use GATE CS Exam Preparation as the broader study hub around this lesson.

Assembly language and assembler design: source, translator and output

Machine language is the pattern of bits that the CPU executes. Assembly language gives those patterns symbolic mnemonics, labels and operands. An assembler translates each assembly statement into a machine word or performs the requested directive. It is not the CPU, which executes the result, and it is not a compiler, which normally translates a higher-level language.

The teaching machine is a 16-bit Basic Computer with 4096 = 2^12 word locations, addressed from 0x000 to 0xFFF. In a memory-reference instruction, bit 15 is the indirect bit I, bits 14 to 12 hold a 3-bit opcode, and bits 11 to 0 hold the address. A memory cell is denoted as word[address].

The encoding rule is:

word = (I << 15) | (opcode << 12) | address

For LDA, opcode 010 is hexadecimal 2. Direct LDA 104 becomes 0x2104. Indirect LDA 104 I sets bit 15, adding 0x8000, so it becomes 0xA104. Addressing Modes and Instruction Formats explains why the same address field can be interpreted directly or indirectly.

16-bit instruction word for LDA 104: bit 15 is the indirect bit, opcode 010 in bits 14 to 12, address 104 in bits 11 to 0, giving 0x2104.

Basic Computer assembly statements: mnemonics, labels and directives

The teaching assembler recognises four statement classes:

Statement class

Examples

What the assembler does

Consumes one memory word?

Memory-reference instruction

AND, ADD, LDA, STA, BUN, BSA, ISZ

Combines I, opcode and address

Yes

Register-reference instruction

CLA, CMA, INC, SZA, HLT

Emits the fixed register-operation pattern

Yes

I/O instruction

INP, OUT, SKI, SKO, ION, IOF

Emits the fixed I/O-operation pattern

Yes

Directive

ORG, DEC, HEX, END

Controls placement, emits data, or stops processing

Only DEC and HEX

In FIRST, DEC 25, FIRST is the optional label, DEC is the directive and 25 is its operand. The comma belongs to the source syntax, not to the symbol name. Locations and instruction operands in the worked program are hexadecimal. DEC explicitly introduces a signed decimal constant, while HEX explicitly introduces a hexadecimal word.

ORG 100 sets the location counter to LC = 0x100 and emits no word. Every instruction, DEC line and HEX line occupies one 16-bit word and increments LC by one. END stops source processing and emits nothing. This teaching assembler uses one-word instructions; real instruction sets may use variable-length instructions.

Two-pass assembler design: build the symbol table first

The earlier Programming the Basic Computer (COA): Instruction Set, Assembly Language and Worked Examples owns the full 25-instruction model, loops, subroutines and I/O. The assembler-specific task is narrower: run the location counter, build SYMTAB and produce an address-object listing for one source program.

The assembler uses three tables. The mnemonic opcode table (MOT) includes LDA -> 2, ADD -> 1, STA -> 3, BUN -> 4 and the fixed word HLT -> 0x7001. A pseudo-op table describes ORG, DEC, HEX and END. The symbol table (SYMTAB) maps labels to addresses. Both passes read the source and maintain a location counter.

Pass one scans from the beginning. It applies ORG, enters each label with the current LC, rejects duplicate labels and increments LC only for statements that produce words. An operand such as FIRST may be used before it is defined because pass one records definitions instead of trying to emit its final address.

Pass two resets and rescans. It gets each mnemonic's bit pattern from the MOT, resolves symbolic operands through SYMTAB, converts DEC and HEX constants to 16-bit words, then emits address-object pairs. It must flag an undefined symbol, duplicate definition, illegal mnemonic, address outside 0x000 to 0xFFF, or constant that cannot fit the required word. A one-pass assembler can handle forward references only by keeping fix-up or backpatch records. Two complete scans keep forward-reference handling explicit.

Assembler pass one worked example: addresses and SYMTAB

The fixed source program is:

Code
ORG 100
START,  LDA FIRST
        ADD SECOND
        STA RESULT
        BUN DONE
FIRST,  DEC 25
SECOND, DEC -7
RESULT, HEX 0000
DONE,   HLT
END

All entries in this pass-one table are hexadecimal:

Source statement

LC before

Label entered

LC after

ORG 100

none

none

100

START, LDA FIRST

100

START -> 100

101

ADD SECOND

101

none

102

STA RESULT

102

none

103

BUN DONE

103

none

104

FIRST, DEC 25

104

FIRST -> 104

105

SECOND, DEC -7

105

SECOND -> 105

106

RESULT, HEX 0000

106

RESULT -> 106

107

DONE, HLT

107

DONE -> 107

108

END

108

none

108

Thus ORG and END add no words, while the eight producing lines occupy 100 through 107. When first used, FIRST, SECOND, RESULT and DONE are forward references. The completed SYMTAB is START -> 100, FIRST -> 104, SECOND -> 105, RESULT -> 106, DONE -> 107.

Assembler pass two worked example: object code and execution

Pass two now resolves every operand and emits four hexadecimal digits per word:

Address

Source

Object word

100

LDA FIRST

2104

101

ADD SECOND

1105

102

STA RESULT

3106

103

BUN DONE

4107

104

DEC 25

0019

105

DEC -7

FFF9

106

HEX 0000

0000

107

HLT

7001

For LDA FIRST, I = 0, opcode 2, and SYMTAB[FIRST] = 0x104:

(0 << 15) | (2 << 12) | 0x104 = 0x2104

For BUN DONE, opcode 4 combined with 0x107 gives 0x4107. Decimal 25 is hexadecimal 0x0019. For -7, 16-bit two's complement is 0x10000 - 0x0007 = 0xFFF9. Review Number Systems and Base Conversions Explained if those conversions need a refresher.

Execution begins with PC = 0x100. LDA loads AC = 0x0019, which is 25. ADD computes 0x0019 + 0xFFF9 = 0x10012; discarding the carry beyond 16 bits leaves AC = 0x0012, which is 18. STA writes 0x0012 into word[0x106]. BUN loads PC = 0x107, and HLT stops. The final state is AC = 0x0012, word[0x106] = 0x0012, decimal result 18.

Two-pass assembler flow for the ORG 100 program: pass one builds SYMTAB, pass two emits the object words 2104 through 7001.

Assembly and assembler questions: solve by columns and avoid six traps

Common question forms ask you to calculate a word from opcode, I and address, build SYMTAB in pass one, emit object code in pass two, identify an undefined or duplicate symbol, translate a negative DEC constant, or trace the final register or memory value. More than 10 questions on assembly and assembler design are available in our practice bank.

Trap

Corrective check

Treating a symbol's address as its stored value

Ask whether the field needs the address or word[address].

Incrementing LC for ORG or END

Mark each line as word-producing or non-producing first.

Counting a label as an extra word

A label names the current location; it occupies no separate location.

Forgetting the indirect bit

If I = 1, verify that the word includes 0x8000.

Writing decimal 25 as 0025

DEC 25 converts to hexadecimal 0019; HEX 0025 would be different.

Keeping a seventeenth carry bit

Mask an addition to 16 bits before reading the final value.

Use three scratch columns: LC/source, SYMTAB and object word. Finish the symbol table before encoding, then trace execution separately. For broader timed practice across the syllabus, the GATE Test Series is a good next stop.

Assembly and assembler design: the short version and next step

Use four steps: fix the machine format; run the location counter and build SYMTAB; encode each line after resolving its symbols; execute only if the question asks for the program result. The program's key values are FIRST = 104, DONE = 107, LDA FIRST = 2104, and the final decimal result is 18.

Keep the three work areas separate on paper, and assembler questions become a sequence of checkable transformations rather than one crowded calculation.