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 pieceConceptFrom
The fetch-decode-execute cycle running the whole programThe universal execution model every chip in this arc sharesassembly1-2
outbuf's single-digit ASCII trick (ADD AL, '0')Converting a small integer into a printable characterassembly1-10
[RDI + RCX*8] — full SIB addressingBase + Index×Scale, the richest addressing mode this whole arc coveredassembly2-4, previewed by cpu8bit1-4's own zero-page,X and cpu8bit1-7's own (IX+d)
PUSH RBP / MOV RBP, RSP / POP RBPA real stack frame — the modern descendant of assembly1-7's manually-built LC-3 stack and cpu8bit1-3/cpu8bit1-7's own real hardware stacksassembly2-5
RDI/RSI argument passing, CALL/RETA real, external calling-convention contract, not a self-invented oneassembly2-5, contrasted with assembly1-7's and cpu8bit1-12's own single informal conventions
CMP/JE inside the loopCondition-code-driven branching, unchanged in spirit since Chapter 6 of assembly1assembly1-6, assembly2-3, assembly2-6
SYSCALL, syscall numbers in RAX, arguments in RDI/RSI/RDXThe real, OS-specific modern replacement for assembly1-8's own unified TRAPassembly2-10
[rel array] / [rel outbuf]RIP-relative addressing — PC-relative addressing's own conceptual returnassembly2-4, tracing back to assembly1-3's own LC-3 PC-relative LD
Honest scope note
This capstone deliberately stays within what this course actually taught. Left out, on purpose: any Windows-side build of this same program (assembly2-10's own Windows API path would need an entirely different, non-syscall-based ending); any use of SIMD in the capstone itself (assembly2-9 was deliberately scoped light, and nothing here needed it); any kernel-mode or privileged code (everything here runs entirely in ring 3, per assembly2-8); and no multi-threading of any kind. None of these are gaps in what was taught — they're deliberate boundaries of a single, focused capstone, the same honest-scoping precedent assembly1-10 and cpu8bit1-12 both set before it.

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

Exercise 1

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 solution
Exercise 2

Using 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.

📄 View solution
Exercise 3

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 solution

Chapter 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)