Capstone — A Real x86-64 Program
x86-64 Assembly
Chapter 12 · Capstone — A Real x86-64 Program
One real, working NASM program, closing not just this course but the entire three-course Assembly/Machine Language arc: a function call using a genuine calling convention, an array walked with full SIB addressing, and output produced through a real Linux syscall.
The Program
Sums a small array of five integers using a real function (not inlined code), then prints the single-digit result.
section .data array dq 1, 2, 1, 3, 1 ; 5 qwords — kept small so the sum stays a single digit outbuf db 0, 10 ; byte 0: the digit (filled in below); byte 1: newline section .text global _start ; --- Function: sum_array --- ; Input: RDI = array pointer, RSI = element count (System V AMD64 ABI, assembly2-5) ; Output: RAX = sum sum_array: PUSH RBP MOV RBP, RSP ; a real stable frame pointer (assembly2-5) XOR RAX, RAX ; RAX = running sum = 0 XOR RCX, RCX ; RCX = index = 0 (caller-saved — safe to clobber freely) .loop: CMP RCX, RSI JE .done ADD RAX, [RDI + RCX*8] ; full SIB addressing (assembly2-4): Base + Index*Scale INC RCX JMP .loop .done: POP RBP RET _start: LEA RDI, [rel array] ; 1st argument — RIP-relative addressing (assembly2-4) MOV RSI, 5 ; 2nd argument — element count CALL sum_array ; a real function call, System V convention (assembly2-5) ADD AL, '0' ; digit -> ASCII, assembly1-10's own single-digit trick, echoed here MOV [rel outbuf], AL MOV RAX, 1 ; syscall number for write() (assembly2-10) MOV RDI, 1 ; fd = stdout LEA RSI, [rel outbuf] MOV RDX, 2 ; length — digit + newline SYSCALL MOV RAX, 60 ; syscall number for exit() XOR RDI, RDI ; exit code 0 SYSCALL
Assembled and linked exactly the way assembly2-11 described: nasm -f elf64 program.asm -o program.o, then ld program.o -o program. Running it prints 8 followed by a newline.
Chapter Attribution — All Three Courses
| Capstone piece | Concept | From |
|---|---|---|
| The fetch-decode-execute cycle running the whole program | The universal execution model every chip in this arc shares | assembly1-2 |
| outbuf's single-digit ASCII trick (ADD AL, '0') | Converting a small integer into a printable character | assembly1-10 |
| [RDI + RCX*8] — full SIB addressing | Base + Index×Scale, the richest addressing mode this whole arc covered | assembly2-4, previewed by cpu8bit1-4's own zero-page,X and cpu8bit1-7's own (IX+d) |
| PUSH RBP / MOV RBP, RSP / POP RBP | A real stack frame — the modern descendant of assembly1-7's manually-built LC-3 stack and cpu8bit1-3/cpu8bit1-7's own real hardware stacks | assembly2-5 |
| RDI/RSI argument passing, CALL/RET | A real, external calling-convention contract, not a self-invented one | assembly2-5, contrasted with assembly1-7's and cpu8bit1-12's own single informal conventions |
| CMP/JE inside the loop | Condition-code-driven branching, unchanged in spirit since Chapter 6 of assembly1 | assembly1-6, assembly2-3, assembly2-6 |
| SYSCALL, syscall numbers in RAX, arguments in RDI/RSI/RDX | The real, OS-specific modern replacement for assembly1-8's own unified TRAP | assembly2-10 |
| [rel array] / [rel outbuf] | RIP-relative addressing — PC-relative addressing's own conceptual return | assembly2-4, tracing back to assembly1-3's own LC-3 PC-relative LD |
Closing the Full Arc
Three courses, one throughline, stated first in assembly1-1: strip away real-hardware history and teach the universal concepts cleanly (LC-3), then meet two real, historically important 1970s chips shaped by genuinely opposite founding constraints (the 6502 and Z80), then meet the real, modern architecture carrying every one of those forces forward, compounded, for four more decades (x86-64). Registers grew from LC-3's uniform eight, to the 6502's cost-starved three and the Z80's shadow-doubled fourteen, to x86-64's sixteen — each carrying its own real history in its own names. Addressing grew from a single PC-relative formula, to zero page and (IX+d), to a single instruction computing Base+Index×Scale+Displacement. Stacks grew from entirely hand-built, to real-but-page-locked, to real-and-fully-flexible, to a modern frame pointer inside a real function. And the RISC-vs-CISC question cpu8bit1-1 only previewed got a full, honest answer: confirmed at modern scale, and then honestly complicated by the discovery that a CISC instruction set can run on a genuinely RISC-like microarchitecture underneath. This capstone is the last, concrete proof that every one of those threads was real — not just described, but written, and run.
Hands-On Exercises
Trace sum_array's own loop for the array [1, 2, 1, 3, 1], stating RAX's value after each iteration, and state the final ASCII character written to outbuf.
📄 View solutionUsing this chapter's own [RDI + RCX*8] instruction, identify exactly which register or value plays the role of Base, Index, Scale, and Displacement in assembly2-4's own SIB formula.
Pick three rows from this chapter's own chapter-attribution table and explain, in one or two sentences each, exactly which piece of this capstone's code draws on that chapter's material and why it was needed here.
📄 View solutionChapter 12 Quick Reference — Course & Arc Recap
- assembly2-1 to -3 — the 8086-to-x86-64 lineage, register naming as history made visible, RFLAGS and the two real syntaxes
- assembly2-4 to -5 — full SIB addressing, RIP-relative addressing's return, real calling conventions as a genuine external contract
- assembly2-6 to -7 — Jcc/LOOP/CMOV, and the instruction set's real scale confirming (and complicating) cpu8bit1-11's RISC/CISC framing
- assembly2-8 — genuinely new ground: rings, paging, and page faults
- assembly2-9 to -11 — SIMD previewed honestly, syscalls vs. the Windows API, real assemblers and linkers
- assembly2-12 — all of the above, combined into one real, working program
- This closes the full Assembly/Machine Language arc: assembly1 (LC-3) → cpu8bit1 (6502/Z80) → assembly2 (x86-64)