Computer Science › How Computers Actually Work

C & Assembly

0 of 18
  1. Arithmetic and Logical Instructions

    `add`, `sub`, `imul`, `and`, `or`, `xor`, and the shifts operate directly on registers or memory, plus `lea` — load effective address — computes an address arithmetic expression without ever touching the memory it points at.

    Read →6 sections10 min
  2. Condition Codes and Conditional Branches

    `cmp` and `test` silently set the zero, sign, overflow, and carry flags; `jcc`, `setcc`, and `cmovcc` are the instructions that actually read them back — the exact ALU flags already covered in hardware, now driving real control flow.

    Read →6 sections10 min
  3. Translating Control Flow: if, while, and for

    A compiler lowers every `if`, `while`, and `for` into the same small vocabulary of comparisons and conditional jumps — the mechanical translation ACM/IEEE CS2013 names directly as a core learning outcome for assembly language.

    Read →6 sections9 min
  4. The Process Address Space

    Every running program sees its own private layout of memory — code, initialized and uninitialized globals, a heap growing upward, a stack growing downward — a view the operating system constructs, not the raw physical RAM the machine actually has.

    Read →6 sections11 min
  5. The Stack and Automatic Storage

    A LIFO region of memory that holds every function's local variables and call bookkeeping, growing downward one frame at a time and shrinking automatically the instant a function returns — the exact hardware structure the Stack ADT was named after.

    Read →6 sections10 min
  6. The x86-64 Runtime Stack: call and ret

    `call` pushes the return address onto the stack and jumps to the callee; `ret` pops that same address back into the instruction pointer — the entire mechanism a procedure uses to hand control back to wherever it was invoked from.

    Read →6 sections10 min
  7. Stack Frames: Prologue and Epilogue

    A short, fixed sequence of instructions — the prologue — carves out a frame for a function's locals on entry, and a matching epilogue tears it down before returning, leaving the stack exactly as the caller left it.

    Read →6 sections9 min
  8. The System V AMD64 Calling Convention

    The exact, binding agreement compilers on Linux/macOS follow so independently-compiled functions can call each other correctly: which registers carry the first six arguments, which register carries the return value, which registers a callee may clobber freely, and how the stack must stay aligned at every call.

    Read →6 sections11 min
  9. Capstone: From C Source to a Traced Function Call

    A small recursive C function compiled all the way down to real x86-64 assembly, then traced call by call — every pointer, stack frame, and calling-convention rule covered in this discipline shown working together to make recursion, taken on faith since Programming & Computational Thinking, physically happen.

    Read →6 sections10 min
  10. Pointers: Addresses, Dereferencing, and the Address-Of Operator

    A pointer is an ordinary variable whose value happens to be a memory address — `&` asks an object for its address, `*` follows an address back to the object living there, and every indirect access in C reduces to just these two operators.

    Read →6 sections11 min
  11. The Heap and Dynamic Allocation

    Memory requested explicitly at runtime with `malloc` and released explicitly with `free`, for data whose size or lifetime can't be pinned to any single function call — the actual heap that a dynamic array's doubling reallocation calls down into.

    Read →6 sections10 min
  12. Common Memory Bugs: Leaks, Double Frees, and Dangling Pointers

    Manual memory management fails in three distinct, well-known shapes — forgetting to free (a leak), freeing twice (a double free), and using memory after it's been freed (a dangling pointer / use-after-free) — each with its own symptom and its own fix.

    Read →6 sections10 min
  13. void* and Generic Code

    A typeless pointer that can point at anything but be dereferenced as nothing until it's cast — the trick `malloc`, `qsort`, and `memcpy` all use to stay generic in a language without templates.

    Read →6 sections10 min
  14. Function Pointers

    A pointer whose pointee is executable code rather than data — the mechanism behind callbacks and dispatch tables in a language with no first-class functions of its own.

    Read →6 sections10 min
  15. Structs, Unions, and Memory Layout

    A struct lays its fields out contiguously in declaration order, with compiler-inserted padding to keep each field aligned; a union overlays every member at the very same starting address, one at a time.

    Read →6 sections10 min
  16. Pointer Arithmetic and Array Decay

    Adding 1 to a pointer moves it forward by one whole element, not one byte — arithmetic is scaled by the pointee's size — and in nearly every expression an array's name silently decays into a pointer to its first element.

    Read →6 sections10 min
  17. Stack Smashing and Buffer Overflows

    Writing past the end of a stack-allocated buffer doesn't just corrupt a neighboring variable — pushed far enough, it overwrites the saved return address itself, the classic first step toward hijacking a program's control flow.

    Read →6 sections10 min

C & Assembly

Part of the Computer Science curriculum.

Sort
1

x86-64 Registers and Data Movement

16 general-purpose 64-bit registers, each with addressable 32-, 16-, and 8-bit sub-portions, moved between with `mov` and its zero-/sign-extending variants — the same register file already built from flip-flops, now under its real x86-64 names.

2

Arithmetic and Logical Instructions

`add`, `sub`, `imul`, `and`, `or`, `xor`, and the shifts operate directly on registers or memory, plus `lea` — load effective address — computes an address arithmetic expression without ever touching the memory it points at.

3

Condition Codes and Conditional Branches

`cmp` and `test` silently set the zero, sign, overflow, and carry flags; `jcc`, `setcc`, and `cmovcc` are the instructions that actually read them back — the exact ALU flags already covered in hardware, now driving real control flow.

4

Translating Control Flow: if, while, and for

A compiler lowers every `if`, `while`, and `for` into the same small vocabulary of comparisons and conditional jumps — the mechanical translation ACM/IEEE CS2013 names directly as a core learning outcome for assembly language.

5

The Process Address Space

Every running program sees its own private layout of memory — code, initialized and uninitialized globals, a heap growing upward, a stack growing downward — a view the operating system constructs, not the raw physical RAM the machine actually has.

6

The Stack and Automatic Storage

A LIFO region of memory that holds every function's local variables and call bookkeeping, growing downward one frame at a time and shrinking automatically the instant a function returns — the exact hardware structure the Stack ADT was named after.

7

The x86-64 Runtime Stack: call and ret

`call` pushes the return address onto the stack and jumps to the callee; `ret` pops that same address back into the instruction pointer — the entire mechanism a procedure uses to hand control back to wherever it was invoked from.

8

Stack Frames: Prologue and Epilogue

A short, fixed sequence of instructions — the prologue — carves out a frame for a function's locals on entry, and a matching epilogue tears it down before returning, leaving the stack exactly as the caller left it.

9

The System V AMD64 Calling Convention

The exact, binding agreement compilers on Linux/macOS follow so independently-compiled functions can call each other correctly: which registers carry the first six arguments, which register carries the return value, which registers a callee may clobber freely, and how the stack must stay aligned at every call.

10

Capstone: From C Source to a Traced Function Call

A small recursive C function compiled all the way down to real x86-64 assembly, then traced call by call — every pointer, stack frame, and calling-convention rule covered in this discipline shown working together to make recursion, taken on faith since Programming & Computational Thinking, physically happen.

11

Pointers: Addresses, Dereferencing, and the Address-Of Operator

A pointer is an ordinary variable whose value happens to be a memory address — `&` asks an object for its address, `*` follows an address back to the object living there, and every indirect access in C reduces to just these two operators.

12

The Heap and Dynamic Allocation

Memory requested explicitly at runtime with `malloc` and released explicitly with `free`, for data whose size or lifetime can't be pinned to any single function call — the actual heap that a dynamic array's doubling reallocation calls down into.

13

Common Memory Bugs: Leaks, Double Frees, and Dangling Pointers

Manual memory management fails in three distinct, well-known shapes — forgetting to free (a leak), freeing twice (a double free), and using memory after it's been freed (a dangling pointer / use-after-free) — each with its own symptom and its own fix.

14

void* and Generic Code

A typeless pointer that can point at anything but be dereferenced as nothing until it's cast — the trick `malloc`, `qsort`, and `memcpy` all use to stay generic in a language without templates.

15

Function Pointers

A pointer whose pointee is executable code rather than data — the mechanism behind callbacks and dispatch tables in a language with no first-class functions of its own.

16

Structs, Unions, and Memory Layout

A struct lays its fields out contiguously in declaration order, with compiler-inserted padding to keep each field aligned; a union overlays every member at the very same starting address, one at a time.

17

Pointer Arithmetic and Array Decay

Adding 1 to a pointer moves it forward by one whole element, not one byte — arithmetic is scaled by the pointee's size — and in nearly every expression an array's name silently decays into a pointer to its first element.

18

Stack Smashing and Buffer Overflows

Writing past the end of a stack-allocated buffer doesn't just corrupt a neighboring variable — pushed far enough, it overwrites the saved return address itself, the classic first step toward hijacking a program's control flow.