# Assembly language

*Human-readable machine code*

Assembly language is a human-readable form of machine code in which each instruction is written as a short mnemonic (such as LOAD or ADD) that an assembler translates one-to-one into the opcode byte the CPU executes.

Group: Processor
URL: https://digiwleea.wleeaf.dev/learn/assembly-intro/

You can now read and write [machine code](https://digiwleea.wleeaf.dev/learn/machine-code/) (the opcode/operand bytes) and you watched a program [run through the machine](https://digiwleea.wleeaf.dev/learn/program-trace/). But writing programs as raw hex, `0x1E`, `0x2F`, `0x3D`, is miserable and easy to get wrong. **Assembly language** fixes that: each instruction becomes a short **mnemonic** you can read, and an **assembler** turns the mnemonics back into the exact bytes. It is the thinnest possible layer over the hardware, one line per instruction.

## Mnemonics: a name per opcode

Instead of memorising that `0x1` means load, you write `LOAD`. Each opcode in the machine's [instruction set](https://digiwleea.wleeaf.dev/learn/machine-code/) gets a name, its **mnemonic**, and the operand is written as a plain number. The four instructions of our accumulator machine:

| mnemonic | opcode | what it does |
| --- | --- | --- |
| LOAD addr | 0x1 | ACC <- memory[addr] |
| ADD addr | 0x2 | ACC <- ACC + memory[addr] |
| STORE addr | 0x3 | memory[addr] <- ACC |
| HALT | 0xF | stop the clock |

_The instruction set, now with a mnemonic for each opcode. You write the left column; the assembler produces the byte from the opcode plus the operand._

## The assembler: names back to bytes

An **assembler** is a tiny program that reads your mnemonics and emits bytes. For each line it looks up the opcode nibble for the mnemonic and packs the operand nibble after it, exactly the [hand-assembly](https://digiwleea.wleeaf.dev/learn/machine-code/) you did (`ADD 14` becomes opcode `0x2`, address `0xE`, byte `0x2E`), just automated. Here is the [program you traced](https://digiwleea.wleeaf.dev/learn/program-trace/) written in assembly, next to the bytes the assembler produces:

```asm
addr  byte   assembly       meaning
 0    0x1E   LOAD 14        ; ACC = mem[14]
 1    0x2F   ADD 15         ; ACC = ACC + mem[15]
 2    0x3D   STORE 13       ; mem[13] = ACC
 3    0xF0   HALT           ; stop
```

_The same LOAD/ADD/STORE/HALT program, as assembly (right) and the bytes the assembler emits (middle). Line for line, one mnemonic becomes one byte. This is what makes it assembly, not a high-level language._

Read across a row: `LOAD 14` is opcode `0x1` (LOAD) on operand `0xE` (14), so the assembler writes the byte `0x1E`. `ADD 15` is `0x2` on `0xF`, byte `0x2F`. Every assembly line maps to exactly one byte, and you could disassemble the bytes straight back to the mnemonics. That one-to-one relationship is the defining feature of assembly language.

> **KEY:** Why this matters: assembly is the boundary between software and hardware. Above it, high-level languages (C, Python) compile down to many assembly instructions per line; below it, the assembler's bytes are what the [control unit](https://digiwleea.wleeaf.dev/learn/control/) actually decodes and runs. Everything a computer does is, at the bottom, a stream of these instructions. Writing a few by hand is how that stops being mysterious.

> **WARN:** A comment (everything after `;` here) is for you, not the machine: the assembler ignores it, so it never becomes a byte. Do not confuse the operand (a real number the assembler encodes) with a comment (ignored text). `ADD 15` encodes the `15`; `ADD 15 ; add the tax` still encodes only the `15`.

**Q (Try it):** Write the assembly line that stores the accumulator into address `9`, and give the byte the assembler produces.

**A:** The mnemonic for store is `STORE`, so the line is `STORE 9`. STORE is opcode `0x3` and `9` is `0x9`, so the assembler packs them into the byte `0x39` (`0011 1001`). One line, one byte.

### FAQ

**Q:** What is assembly language?

**A:** Assembly language is a human-readable form of machine code: each instruction is written as a short mnemonic (LOAD, ADD, STORE, HALT) with its operand, and an assembler translates each line one-to-one into the opcode byte the CPU executes. It is the thinnest layer over the hardware.

**Q:** What is an assembler?

**A:** An assembler is a program that turns assembly mnemonics into machine-code bytes. For each instruction it looks up the opcode and packs the operand after it, for example ADD 14 becomes byte 0x2E. It is the automated, error-proof version of assembling bytes by hand.

**Q:** What is the difference between assembly language and machine code?

**A:** They are the same instructions in two forms: machine code is the raw bytes the CPU runs (0x1E), assembly is the readable text a human writes (LOAD 14). The assembler converts assembly to machine code, and every assembly line corresponds to exactly one machine instruction.

**Q:** How is assembly different from a high-level language like C?

**A:** Assembly is one-to-one with machine code: each line is a single CPU instruction. A high-level language like C is many instructions per line, translated by a compiler, and hides the registers and addresses that assembly exposes directly.
