Introduction: The 64-Bit Revolution
If you’ve ever wondered why x86-64 assembly feels both familiar and completely different from 32-bit assembly, you’re not alone. The transition from 32-bit to 64-bit x86 (AMD64/Intel 64) wasn’t just about wider registers—it was a complete rethinking of the calling convention, the register set, and how we interact with the operating system.
I remember the first time I wrote a 64-bit assembly program. It was 2008, and I had been writing 32-bit shellcode for a few years. When I discovered that system calls used the syscall instruction instead of int 0x80, and that arguments were passed in registers instead of on the stack, it felt like learning assembly all over again.
In this article, we’re going to master x86-64 assembly programming. We’ll explore the expanded register set (RAX-R15), the new calling conventions (System V ABI for Linux, Microsoft x64 ABI for Windows), RIP-relative addressing, the syscall interface, and how to write complete, working programs that leverage the full power of 64-bit mode.
Whether you’re writing high-performance code, reverse-engineering 64-bit malware, or just want to understand how modern programs really work, this article will give you the deep technical knowledge you need.
The x86-64 Evolution – AMD’s Brilliant Move
The story of x86-64 is one of the most fascinating chapters in computer architecture history. It’s a tale of how AMD outmaneuvered Intel by designing a better 64-bit architecture, forcing Intel to abandon their own failed approach and adopt AMD’s design.
The Pre-64-bit Era: Intel’s IA-64 (Itanium) Disaster
In the late 1990s, it was clear that 32-bit addressing (4GB limit) would become a constraint for servers and workstations. Intel decided to design a completely new 64-bit architecture called IA-64 (Intel Architecture 64-bit), also known as Itanium.
IA-64 was based on EPIC (Explicitly Parallel Instruction Computing), a radically different approach from x86. It wasn’t backward-compatible with x86. To run x86 programs, you needed to emulate them (slowly).
IA-64 was a commercial disaster. It was complex, expensive, and performed poorly on many workloads. It nearly bankrupted Intel.
AMD’s Brilliant Move: AMD64
While Intel was struggling with IA-64, AMD decided to extend x86 to 64 bits. They designed AMD64, which:
- Extended existing x86 registers to 64 bits (RAX, RBX, etc.)
- Added 8 new general-purpose registers (R8-R15)
- Maintained backward compatibility with 32-bit x86
- Improved the calling convention (pass arguments in registers, not on the stack)
AMD64 was a massive success. It was faster, simpler, and fully backward-compatible. Intel was forced to adopt it (calling it “Intel 64” or “EM64T”).
Today, all x86 processors are 64-bit, and they all use the AMD64 architecture.
What Changed in x86-64?
The key changes from 32-bit to 64-bit x86:
| Feature | 32-bit (x86) | 64-bit (x86-64) |
|---|---|---|
| Register width | 32 bits | 64 bits |
| General-purpose registers | 8 (EAX-EBP, ESI, EDI) | 16 (RAX-R15) |
| Address width | 32 bits (4GB) | 64 bits (256TB theoretical) |
| Calling convention | Arguments on stack (mostly) | Arguments in registers (System V) |
| System calls | int 0x80 | syscall instruction |
| RIP-relative addressing | No | Yes ([RIP + offset]) |
| Red zone | No | Yes (128 bytes below RSP) |
The Register Explosion: RAX-R15
The most visible change in x86-64 is the increase in the number of general-purpose registers. We go from 8 registers (EAX-EBP, ESI, EDI) to 16 registers (RAX-R15).
32-bit (x86) 64-bit (x86-64)
───────────── ───────────────────
EAX (32-bit) → RAX (64-bit)
EBX (32-bit) → RBX (64-bit)
ECX (32-bit) → RCX (64-bit)
EDX (32-bit) → RDX (64-bit)
ESP (32-bit) → RSP (64-bit)
EBP (32-bit) → RBP (64-bit)
ESI (32-bit) → RSI (64-bit)
EDI (32-bit) → RDI (64-bit)
R8 (64-bit) ← NEW
R9 (64-bit) ← NEW
R10 (64-bit) ← NEW
R11 (64-bit) ← NEW
R12 (64-bit) ← NEW
R13 (64-bit) ← NEW
R14 (64-bit) ← NEW
R15 (64-bit) ← NEW
And just like in 32-bit mode, you can access the lower bits of each register:
; RAX (64-bit)
; ├── EAX (lower 32 bits)
; ├── AX (lower 16 bits)
; ├── AL (lower 8 bits)
; └── AH (upper 8 bits of AX)
mov rax, 0x1122334455667788 ; RAX = full 64-bit value
mov eax, 0xAABBCCDD ; Overwrite lower 32 bits of RAX
mov ax, 0xEEFF ; Overwrite lower 16 bits
mov al, 0x12 ; Overwrite lower 8 bits
mov ah, 0x34 ; Overwrite bits 8-15
Note: When you write to a 32-bit register (EAX, EBX, etc.) in 64-bit mode, the upper 32 bits are zeroed. This is a change from 16-bit and 32-bit modes, where writing to a partial register doesn’t affect the upper bits.



