Learn assembly language on a simulated 8-bit CPU. 24 instructions, 256 bytes of memory, 2 registers.
Write assembly, tap Asm & Run, watch the CPU execute
The Environment
The left side of the screen is your code editor. This is where you type assembly language mnemonics — short abbreviations like LDI, ADD, OUT, and HLT that represent instructions the CPU understands.
Each line holds one instruction. Some instructions need an argument (a number), some don't. The editor highlights your code and shows line numbers so you can track where you are.
The Mem view shows all 256 bytes of the CPU's memory, laid out in a grid. Each cell shows a two-digit hex value (00 to FF). When you assemble your code, you can watch the machine code bytes appear in memory. During execution, the current instruction is highlighted so you can follow along.
Memory addresses run from 00 (top-left) to FF (bottom-right). Your program starts at address 00 and grows upward. Data can be stored anywhere you like.
Below the editor you'll find a hex keypad with keys 0-9 and A-F — the sixteen digits of the hexadecimal number system. Use this for entering hex values directly. There are also letter keys for typing mnemonics.
A built-in tool that converts between decimal, hexadecimal, and binary. Type a number in any format and see it in all three. Essential while you're learning to think in hex.
Example: type 42 in decimal and see that it's 2A in hex and 00101010 in binary.
The registers panel shows the current state of the CPU at all times:
In Step mode, watch these values change with each instruction. This is the fastest way to understand what assembly code actually does.
Concepts
A register is a tiny piece of memory built directly into the CPU. It's the fastest storage there is — the CPU can read and write registers in a single clock cycle.
The Hex Machine has two general-purpose registers, A and B. Each holds one byte — a number from 0 to 255 (or 00 to FF in hex). Most arithmetic happens in register A (which is why it's sometimes called the accumulator). Register B holds a second value when you need one.
The Program Counter (PC) is a special register that holds the memory address of the next instruction to execute. When the CPU finishes one instruction, it looks at the PC to find the next one.
Normally the PC increments automatically — after executing the instruction at address 00, the PC moves to 01 (or 02, if the instruction was two bytes long). Jump instructions (JMP, JZ, JNZ, JC) change the PC to a completely different address, which is how you create loops and branches.
The Hex Machine has 256 bytes of memory, addressed from 00 to FF. That's it — your entire world. Your program lives in memory. Your data lives in memory. Everything is just bytes.
Each byte can hold a value from 0 to 255. A byte might be an instruction (like LDI), an argument to an instruction (like the number 05), or data your program stores. The CPU doesn't know the difference — it just executes whatever the PC points to.
Flags are single-bit indicators that report what happened during the last operation. The Hex Machine has two flags:
Zero Flag (Z) — set to 1 when the result of an operation is exactly zero. For example, if register A holds 01 and you decrement it (DEC A), the result is 00, so the Zero flag is set. This is how the CPU knows when a countdown reaches zero.
Carry Flag (C) — set to 1 when an arithmetic operation overflows past 255 or underflows below 0. For example, 255 + 1 wraps around to 0 and sets the Carry flag. This is how the CPU detects overflow.
Flags are what make conditional jumps possible. JZ (Jump if Zero) only jumps when the Zero flag is set. JNZ (Jump if Not Zero) only jumps when it's clear. JC (Jump if Carry) only jumps on overflow. Without flags, the CPU could never make decisions.
Every CPU in every computer follows the same fundamental loop:
When you use Step mode, you're manually triggering one pass through this cycle. Each tap of Step runs exactly one fetch-decode-execute, then pauses so you can inspect the result.
Reference
| Mnemonic | Args | Description |
|---|---|---|
| NOP | — | No operation. Does nothing. Advances PC by 1. |
| LDA addr | 1 byte | Load register A from memory address. A = MEM[addr] |
| LDI val | 1 byte | Load immediate value into A. A = val |
| STA addr | 1 byte | Store register A to memory address. MEM[addr] = A |
| LDB addr | 1 byte | Load register B from memory address. B = MEM[addr] |
| LBI val | 1 byte | Load immediate value into B. B = val |
| STB addr | 1 byte | Store register B to memory address. MEM[addr] = B |
| MOV A,B | — | Copy B into A. A = B (B unchanged) |
| Mnemonic | Args | Description |
|---|---|---|
| ADD | — | Add B to A. A = A + B. Sets Zero and Carry flags. |
| SUB | — | Subtract B from A. A = A - B. Sets Zero and Carry flags. |
| INC | — | Increment A by 1. A = A + 1. Sets Zero flag if result is 00. |
| DEC | — | Decrement A by 1. A = A - 1. Sets Zero flag if result is 00. |
| ADI val | 1 byte | Add immediate value to A. A = A + val. Sets flags. |
| Mnemonic | Args | Description |
|---|---|---|
| AND | — | Bitwise AND: A = A AND B |
| OR | — | Bitwise OR: A = A OR B |
| XOR | — | Bitwise XOR: A = A XOR B |
| NOT | — | Bitwise NOT: A = NOT A (flips all bits) |
| SHL | — | Shift A left 1 bit. Equivalent to multiply by 2. |
| SHR | — | Shift A right 1 bit. Equivalent to divide by 2. |
| Mnemonic | Args | Description |
|---|---|---|
| CMP | — | Compare A and B (A - B) without storing result. Sets flags only. |
| JMP addr | 1 byte | Jump unconditionally. PC = addr. |
| JZ addr | 1 byte | Jump if Zero flag is set. Branch when last result was 0. |
| JNZ addr | 1 byte | Jump if Zero flag is NOT set. Branch when last result was not 0. |
| JC addr | 1 byte | Jump if Carry flag is set. Branch on overflow/underflow. |
| Mnemonic | Args | Description |
|---|---|---|
| OUT | — | Output the value in register A to the display. |
| INP | — | Read input into register A. Pauses for user input. |
| HLT | — | Halt the CPU. Program stops executing. |
Number Systems
Hexadecimal (hex for short) is a base-16 number system. While decimal uses 10 digits (0-9), hex uses 16: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F.
A = 10, B = 11, C = 12, D = 13, E = 14, F = 15.
Computers work in binary (base-2), but binary numbers are long and hard for humans to read. The number 255 in binary is 11111111 — eight digits. In hex, it's just FF — two digits.
Hex is a compact way to represent binary. Each hex digit maps perfectly to exactly 4 binary digits (bits). Two hex digits represent one byte (8 bits). That's why hex is everywhere in low-level computing: it's human-friendly binary.
| Hex | Decimal | Binary |
|---|---|---|
| 00 | 0 | 00000000 |
| 01 | 1 | 00000001 |
| 05 | 5 | 00000101 |
| 0A | 10 | 00001010 |
| 0F | 15 | 00001111 |
| 10 | 16 | 00010000 |
| 1A | 26 | 00011010 |
| 20 | 32 | 00100000 |
| 2A | 42 | 00101010 |
| 40 | 64 | 01000000 |
| 64 | 100 | 01100100 |
| 80 | 128 | 10000000 |
| C8 | 200 | 11001000 |
| FF | 255 | 11111111 |
To convert a decimal number to hex by hand, divide repeatedly by 16:
To go the other direction, multiply each hex digit by its place value:
Or just use the built-in Hex Converter. That's what it's there for.
Deep Dive
Before assemblers existed, programmers would convert assembly language to machine code by hand on paper. Here's how it works:
Step 1: Write your assembly program with addresses:
Step 2: Look up each mnemonic's hex opcode and write the machine code:
Step 3: The final machine code bytes, in order, are:
That's seven bytes. That's your entire program. On the original hardware trainers, you'd toggle these bytes in one at a time using switches. With the Hex Machine, you type the assembly and the assembler does the conversion for you — but understanding the process helps you understand what's really happening.
Tutorials
The simplest possible program — load a value and display it.
LDI 42 — this is two bytes in memory. The first byte is the LDI opcode (which tells the CPU "load the next byte into register A"). The second byte is 42 (the value to load). After this instruction, register A contains 42 (hex) which is 66 in decimal.
OUT — one byte. Tells the CPU to take whatever is in register A and send it to the output display. You'll see "42" appear.
HLT — one byte. Stops the CPU. Without this, the CPU would keep fetching bytes from memory and trying to execute whatever random data is there.
Type the three lines into the editor. Tap "Asm & Run." You'll see 42 on the output display. Now try changing 42 to FF, or 0A, or 01. Each time, the display shows the new value.
Tap "Assemble" (not Asm & Run). Then tap "Step" three times. Watch the PC advance, register A fill with your value, and the output appear. This is the fetch-execute cycle in action.
A loop that counts from 5 down to 0, displaying each number.
The key is the DEC and JNZ pair. DEC subtracts 1 from register A. If the result is not zero, the Zero flag stays clear, and JNZ jumps back to the OUT instruction. When A finally reaches 00, the DEC sets the Zero flag, JNZ sees it, and falls through to the next instruction instead of jumping.
Use Step mode to walk through every iteration. Watch register A count down and the Zero flag flip on the last decrement. Change the starting value from 05 to 0A (that's 10 in decimal) and run it again.
Use both registers to add two numbers together.
LDI 15 — puts hex 15 into register A. Register B is still 00.
LBI 0A — puts hex 0A into register B. Now A = 15, B = 0A.
ADD — the CPU adds A and B together (15 + 0A = 1F) and stores the result back in A. The Zero flag is clear (result is not zero). The Carry flag is clear (no overflow — the result fits in one byte).
OUT — displays 1F.
Change the numbers and predict the result before running. Try LDI FF and LBI 01 — what happens when the sum exceeds FF? (Answer: it wraps to 00 and the Carry flag is set.) This is overflow, and it's how the CPU signals that the result didn't fit in one byte.
Replace ADD with SUB. Now it computes A - B instead. If B is larger than A, the result wraps around and the Carry flag is set (indicating an underflow / borrow).
Examples
The Hex Machine comes with 10 built-in example programs. Load any one from the examples menu, step through it, study it, and modify it.
History
In the early 1980s, Radio Shack sold a remarkable little kit: the Science Fair Microcomputer Trainer (catalog number 28-259). It was a bare circuit board with a simple CPU, 256 bytes of RAM, a hexadecimal keypad, and a row of LED displays.
There was no screen. No keyboard. No operating system. You programmed it by pressing hex keys to enter machine code directly into memory — one byte at a time. You'd press the address, then the data byte, then advance to the next address. When you pressed RUN, the LEDs would blink and the tiny speaker would beep as your program executed.
The Microcomputer Trainer stripped away every abstraction. There was no compiler, no editor, no file system. Just you, raw hex bytes, and a CPU. You had to understand exactly what every byte did because there was nowhere to hide from the machine.
It was frustrating, educational, and deeply satisfying. When your program finally worked — when those LEDs blinked in the pattern you intended — you understood something fundamental about how computers operate. Not as theory, but as physical reality.
The Hex Machine gives you the same hands-on learning with modern convenience. You get an assembler (so you don't have to look up opcodes by hand), a memory view (instead of squinting at LEDs), and Step mode (instead of guessing what went wrong). But the core experience is the same: a tiny CPU, a small instruction set, 256 bytes of memory, and the joy of making the machine do exactly what you tell it.
Every CPU in every computer and phone works this way at the lowest level. The instructions are more complex, the memory is vastly larger, and there are many more registers — but the fetch-execute cycle, the flags, the jumps, and the logic are all the same concepts you learn here.
Strategy