Assembly (x86-64)
Go under every language you know and meet the machine itself: registers, memory, flags, the stack, and the instructions your CPU actually executes. Taught in x86-64 with NASM syntax, with an interactive CPU you step through in every module.
Why learn Assembly (x86-64)?
Every language you have ever written ends up as the same thing: numbered instructions a processor obeys one at a time. Assembly is the readable name for those instructions, and it is the only level where you can actually see what your computer is doing. Learn it and a whole category of mystery disappears. Stack traces, segmentation faults, why one loop is ten times slower than another, what a buffer overflow really is: all of it becomes readable rather than folklore.
This course is built around an interactive CPU that lives inside the lessons. You do not read about registers changing, you press Step and watch them change, then press Back and watch it again. Every module puts you in front of that machine: moving values, indexing memory, setting flags, taking branches, pushing and popping the stack, flipping individual bits, and finally writing complete programs that print and exit. There is a hint ladder on every challenge and a worked solution behind it, so nobody gets stranded.
The dialect is fixed and never mixed: x86-64 with NASM syntax, the System V calling convention, and Linux system calls. That consistency matters more than it sounds, because the usual reason people bounce off assembly is that half the tutorials they find are written in a different syntax for a different platform.
Who this course is for
- Developers who can already write loops and functions in some language and want to know what happens underneath
- Anyone heading into security research, reverse engineering, or capture-the-flag competitions
- Embedded, systems and performance engineers who need to read compiler output and crash dumps
- Computer science students meeting architecture or compilers and wanting it to actually make sense
What you'll build and practice
- Fluency with all sixteen general-purpose registers, their sub-register windows, and the zero-extension rule that catches everyone
- The ability to read any memory operand, including base plus scaled index plus displacement, and know what data structure it implies
- Working programs: counting loops, array traversals, a search with an early exit, a bit-counting loop, and functions that obey the calling convention
- A complete mental model of the stack, from push and pop through call and return to stack frames and why overflows redirect execution
- The skill of reading compiler output at both -O0 and -O2 and mapping it back to the C that produced it
- A capstone program that composes something from all twelve modules, counts array values and prints its answer through a real system call
What you'll learn
Assembly (x86-64) is organized into 12 focused modules. By the end you'll be comfortable with:
- Welcome to the Machine
- Meet the Registers
- Memory & Addresses
- Doing Math
- Flags: How the CPU Remembers
- Making Decisions
- Loops
- The Stack
- Functions & Conventions
- Bit Magic
- Arrays, Strings & the Outside World
- Reading, Debugging & the Big Picture
Course curriculum
66 lessons across 12 modules. Lessons marked Free preview are readable without an account.
Module 1. Welcome to the Machine
What Assembly is, how it becomes machine code, and the fetch-decode-execute loop your CPU has been running this whole time
- 7mThe Language Your CPU Actually SpeaksFree preview
Every app you have ever opened runs as a river of tiny numbered commands. Assembly is the readable name for those commands, and this is your first look at one running.
- 7mFrom Your Code to the MachineFree preview
Follow one line of C all the way down: compiler, assembly, assembler, bytes. Once you have seen the trip, assembly stops feeling like a separate world.
- 8mFetch, Decode, ExecuteFree preview
The CPU runs one loop, forever, and it has only three steps. Step through it yourself and watch the instruction pointer walk down the program.
- 7mThe Desk and the Warehouse
The CPU has sixteen slots to work in and billions of bytes to work from. The gap between those two speeds explains most of what optimised code is doing.
- 7mOur Environment: x86-64, NASM, Intel
Assembly is not one language. Pinning down architecture, syntax and conventions now means every example for the rest of the course reads the same way.
- 8mPractice: You Are the CPU πͺ
No new ideas. Just you, a few short programs, and the question every assembly programmer answers a hundred times a day: what is in that register now?
Module 2. Meet the Registers
The CPU's sixteen slots, the MOV instruction, and the sub-register rule that catches everyone
- 8mThe Sixteen Slots
Meet RAX through R15 by name. Their odd letters are a forty-year-old fossil, and two of them are not really general purpose at all.
- 7mMOV: The Workhorse
One instruction accounts for roughly a third of all code your compiler emits. It is also badly named, and the name causes real bugs.
- 7mSwapping Two Values
Swapping two registers is the first problem where the copy-not-move rule bites. Solve it the classic way, then meet the instruction that does it in one line.
- 8mOne Box, Four Windows
RAX, EAX, AX and AL are not four registers. They are four differently sized windows onto the same 64 bits, and knowing that turns confusing disassembly into obvious code.
- 8mThe 32-Bit Surprise
Writing to an 8-bit or 16-bit register leaves the rest alone. Writing to a 32-bit register wipes the top half. This asymmetry is real, deliberate, and everywhere in compiled code.
- 8mPractice: Register Gymnastics πͺ
Everything from this module, in anger. Shuffle values between registers, keep track of which copy is which, and stay on the right side of the zero-extension rule.
Module 3. Memory & Addresses
Loading, storing, sizes, and the bracket notation that turns a number into a place
- 7mMemory Is One Giant Array
No types, no names, no structure. Just numbered boxes holding one byte each, from zero to enormous. Everything else is something a language built on top.
- 7mBrackets Mean "At"
One pair of square brackets is the difference between a number and a place. Get this wrong and your program reads an address as data, which is a classic and confusing bug.
- 8mStoring, and Why Size Matters
Writing to memory is a mirror image of reading from it, except for one case where the CPU genuinely cannot guess how many bytes you meant.
- 8mBuilding an Address
One bracket expression can multiply, add and offset, all for free, in the same instruction that does the load. This is how array indexing happens in a single line.
- 7mLEA: The Address, Not the Contents
One instruction uses square brackets and then deliberately does not read memory. It is the answer to "where is it?" rather than "what is in it?", and compilers love it for a second reason.
- 9mPractice: Memory Drills πͺ
Loads, stores, sizes, indexed addresses and LEA, all mixed together. Plus the byte-ordering rule that explains why memory sometimes looks backwards.
Module 4. Doing Math
Addition, subtraction, negative numbers, multiplication, and the divide instruction that breaks all the rules
- 7mADD and SUB
Two instructions, one shape, and one habit that catches everyone coming from a high-level language: the destination is also an input.
- 7mINC, DEC and NEG
Three one-operand instructions. Two of them look like shorthand for ADD and SUB but differ in one specific, occasionally important way.
- 8mWhere Negative Numbers Hide
There is no minus sign in a register, only 64 bits. Two's complement is the trick that makes subtraction and addition the same circuit, and it explains a whole family of real bugs.
- 7mMultiplication
IMUL looks like a normal two-operand instruction, and mostly is. The complication is that multiplying two 64-bit numbers can produce a 128-bit answer.
- 9mDivision, the Strange One
Division uses a fixed register pair, needs a setup instruction, returns two answers at once, and is the only arithmetic instruction that can kill your program outright.
- 9mPractice: Make the Number πͺ
A given starting value, a target, and a budget of instructions. Every challenge in this lesson has more than one right answer, and finding the short one is the fun part.
Module 5. Flags: How the CPU Remembers
Four single bits that turn arithmetic into decisions, and the instruction that exists only to set them
- 7mSticky Notes for the CPU
The CPU has no memory of what it just did, except for four single bits it sets automatically after every calculation. Those bits are where every if statement comes from.
- 7mZero and Sign
The two flags you will read most often, plus the idiom every compiler uses to ask "is this value zero?" without changing anything.
- 7mCMP: A Subtraction You Throw Away
The comparison instruction does not compute "equal" or "greater". It subtracts, keeps the flags, and discards the answer, and every comparison in every language is built on that.
- 8mCarry and Overflow
Two flags that both mean "it did not fit", for two different meanings of "fit". The CPU sets both every time, because it does not know whether you meant signed or unsigned.
- 8mPractice: Flag Detective πͺ
Given a program, what do the flags say when it stops? And given a set of flags, what could have caused them?
Module 6. Making Decisions
Labels, jumps, conditional jumps, and how an if/else is assembled out of them
- 7mLabels and JMP
A label is a name for a place in the program. JMP sends the instruction pointer there, and that is the entire machinery behind every control structure you have ever used.
- 8mJumping Only Sometimes
Combine a comparison with a jump that reads the flags, and you have every decision a computer can make. This is the lesson where the whole course clicks together.
- 7mGreater, Less, and In Between
Four more jumps cover every ordering comparison. Their names read naturally as long as you remember which operand the comparison was about.
- 8mAbove Is Not Greater
x86 has two complete sets of ordering jumps, one for signed values and one for unsigned. Picking the wrong set is a silent, security-relevant bug.
- 8mBuilding an If/Else
Take a C if/else apart and put it back together in assembly, then meet the trick compilers use to avoid branching at all when the branch is unpredictable.
- 9mPractice: Branch Tracing πͺ
Follow the instruction pointer through branching code and predict where it lands. This is the exact skill you use when reading a disassembly or stepping through a debugger.
Module 7. Loops
Counting up, counting down, walking arrays, and the four parts every loop is built from
- 8mThe Anatomy of a Loop
Every loop you have ever written has the same four parts. In assembly they stop being syntax and become four separate instructions you have to place yourself.
- 7mCounting Down
Compilers turn your counting-up loop into a counting-down loop whenever they can, and the reason is one instruction you no longer need.
- 8mWalking an Array
Combine a loop counter with a scaled memory operand and you can visit every element of an array. This is what the overwhelming majority of real loops actually do.
- 7mBreaking Out Early
Break and continue look like special keywords in a high-level language. In assembly they are just two more jumps, aimed at two different labels.
- 9mPractice: Loop Lab πͺ
Trace loops, count iterations, spot the infinite one, and write a nested loop. Everything from this module, and nothing new.
Module 8. The Stack
Push, pop, call and return: the region of memory that makes functions possible
- 8mWhat the Stack Actually Is
It is a region of ordinary memory and one register pointing into it. Everything else you have heard about the stack follows from those two facts.
- 7mPUSH and POP
Two instructions, each shorthand for two others. Once you can expand them by hand, the stack stops being mysterious.
- 8mCALL and RET
A function call is a jump that leaves a note saying where to come back to. The note goes on the stack, and everything about functions follows from that.
- 8mRecursion, Made Visible
Recursion needs no new instructions. Watch the stack grow with each call and shrink on the way back, and the whole idea stops being mysterious.
- 9mPractice: Stack Escape πͺ
Given a stack, work out what RET will do. Given an imbalance, work out where it breaks. This is the skill that makes crash dumps readable.
Module 9. Functions & Conventions
Arguments, return values, who saves what, and where local variables actually live
- 8mArguments and Return Values
The hardware has no idea what a function argument is. Passing one works because everybody agreed, in advance, which registers to use.
- 8mWho Saves What
Sixteen registers, shared by every function in the program. The convention divides them in two, and knowing which half is which explains the shape of every compiled function.
- 9mStack Frames and Local Variables
A local variable is a fixed distance from a register. Once you can read the frame diagram, C's stack behaviour and a whole category of crash become obvious.
- 9mPractice: Write a Function πͺ
Arguments in, answer out, registers preserved. Write functions that obey the convention, including one that calls another.
Module 10. Bit Magic
AND, OR, XOR, NOT and the shifts: reading, setting and moving individual bits
- 7mCounting in Twos
A register is not a number, it is sixty-four switches. Learning to see the switches is what makes the next five lessons feel easy instead of arbitrary.
- 8mAND, OR, XOR, NOT
Four instructions that work on every bit independently, in parallel, in one cycle. Each has one job, and the jobs are worth learning as verbs.
- 8mMasks: Reading and Writing One Bit
A mask is a pattern that says which bits you mean. Combine one with the right operation and you can test, set, clear or toggle any individual bit.
- 8mShifting Bits Sideways
Sliding every bit one position left doubles a number. That coincidence makes shifts the cheapest multiplication and division there is, and the distinction between the two right shifts matters.
- 8mTricks Real Code Uses
A small collection of bit patterns that appear in production code everywhere, plus honest advice about when reaching for them is a bad idea.
- 9mPractice: Bit Gym πͺ
Masks, shifts, flags and packed fields. Extract a value from the middle of a word, build one, and count bits.
Module 11. Arrays, Strings & the Outside World
How arrays, structs and strings are laid out, and how a program asks the operating system for anything at all
- 8mAn Array Is an Address and a Stride
There is no array type in the hardware. There is a starting address and a distance between elements, and every array behaviour you know follows from those two numbers.
- 8mStrings Are Just Bytes
A string is an array of bytes with a zero on the end. That single design decision explains an entire genre of security vulnerability.
- 8mStructs Are Offsets
A struct is a base address and a fixed offset per field. Field names vanish at compile time, and the padding the compiler inserts explains why struct sizes surprise people.
- 9mTalking to the Outside World
There is no print instruction. Every file read, every byte on screen and every network packet is a program asking the kernel for a favour, and here is exactly how it asks.
- 9mPractice: Data Drills πͺ
Arrays, strings, structs and one real program that prints something. Everything this module covered, applied.
Module 12. Reading, Debugging & the Big Picture
Map C onto assembly, read what the compiler produced, diagnose crashes from machine state, and put the whole course together
- 8mC to Assembly: The Basics
Put a C function and its compiled output side by side and read across. Once the mapping clicks, disassembly stops being a foreign language.
- 8mC to Assembly: If and Loops
Recognise a branch, a loop and an early return on sight. These three shapes account for most of the control flow in any program you will ever disassemble.
- 8mDebugging: Read, Do Not Guess
You have a simulator, a step button and every register on screen. That is more information than most debugging sessions give you, and the method for using it is worth making explicit.
- 9mClassic Crashes and What They Mean
Segmentation fault, divide error, stack overflow, jumped-to-nowhere. Four messages you have seen for years, and now you can read each one as a specific machine event.
- 9mPractice: Crack the Program πͺ
Programs you did not write, questions you must answer before running them. Predict, then check, then explain the gap.
- 12mCapstone: The Whole Machine
One complete program that uses something from every module, walked through end to end. Then you write the last piece yourself.
Frequently asked questions
- Do I need to know C before starting Assembly?
- No. You need to be comfortable with variables, conditions, loops and functions in any language. The course uses short C snippets when comparing high-level code with its compiled form, but every one is explained line by line, and no C is assumed beyond reading it.
- Which assembly dialect does the course teach, and why does that matter?
- x86-64 with NASM syntax, Intel operand order, the System V calling convention and Linux system calls. It matters because there is no single assembly language, and mixing dialects is the main reason self-taught learners get confused. One combination is used from the first lesson to the last, and the one place AT&T syntax appears is a short section teaching you to recognise it.
- Do I have to install an assembler or set up a Linux machine?
- No. Every lesson runs against a CPU simulator built into the course, with registers, memory, flags, a stack and an output log on screen. You can step forward, step backward and reset as often as you like. If you later want to assemble programs yourself, the course points you at the exact commands, but nothing requires it.
- Is the simulator a real emulator, or a teaching model?
- A teaching model, and it says so out loud. It implements around forty instructions, four condition flags and two Linux system calls. Everything it simplifies is listed inside the simulator itself, in a panel you can open at any time, and the lessons flag each simplification where it comes up. The goal is a correct mental model, not a machine that boots an operating system.
- Will this actually help me if I write Python or JavaScript for a living?
- It changes what you can explain rather than what you type day to day. After it you know why a hash lookup beats a linear scan in cache terms, what an integer overflow really is, why one data layout is faster than another, and what your interpreter is doing when it stalls. Those are the questions that separate people who guess about performance from people who reason about it.
Ready to start Assembly (x86-64)?
Create a free account to track progress, earn XP, and get instant help from the AI tutor as you code.
Create your free account