This article is for those who are not familiar with assembly languages—but would like to take a quick look. We won’t turn you into an assembly guru in 15 minutes, but we will show assembly languages for several popular microcontroller architectures (ARM32, AVR, MSP430, 8051), as well as for desktop computers (x86 under Linux and DOS), so you can see their similarities and differences—and feel less intimidated about diving deeper if something catches your interest.
Our goal is not to convince everyone to write in assembly (assemblies!)—it’s not that difficult, but for most tasks it’s not very practical. The goal is simply to introduce it! So that you won’t be afraid to occasionally peek into low-level debugging, write some inline assembly for optimization, or maybe even attempt writing a compiler or something similar.
As a bonus—for the curious—we’ll also look at assembly for the Intel 4004, a 4-bit processor that’s over 50 years old. There will also be a small interactive example for it.
---
General Notes
Assembly language is a way to write processor instructions—not as hexadecimal codes (though that’s possible too!), but as human-readable mnemonics.
Processors differ (in architecture and type), and so do their instructions. Therefore, assembly languages differ at least in their instruction mnemonics. Additionally, different assembler authors use slightly different syntax formats.
Does this mean assembly programs are completely non-portable compared to C? Not necessarily. Most assemblers support macros, defines, and similar tools that allow some level of abstraction. However, this is rarely needed.
---
About Processors and Systems
Learning any assembly language usually begins with studying the processor architecture—what capabilities and resources it has. The first thing to consider is registers—small memory cells inside the CPU. There are usually few of them, from 1 to 32, not counting special ones like the PC (Program Counter), which holds the address of the current instruction.
Many instructions manipulate registers—for example, arithmetic operations. Others handle control flow (branches, loops). A “jump” is essentially writing a new value into the PC register.
Memory access instructions are also crucial. When we write a value to an address like 0x1020BEDA, the processor interacts with the system’s address space. That space may represent RAM—or hardware like I/O ports or sound devices.
Thus, writing to memory might store data—or toggle voltages on pins or produce sound. This is how processors interact with hardware.
Another complication: even with the same CPU core, connected peripherals may differ. This affects portability and adds complexity.
---
ARM32 — The Microcontroller World
We start with mid-range systems: 32-bit ARM microcontrollers, widely used in embedded devices—from gadgets to smartphones.
ARM32 uses 32-bit registers (typically 12–16 general-purpose). It can theoretically address 4 GB of memory, though most microcontrollers have far less, leaving unused address space for peripherals.
Example: blinking an LED.
(code unchanged)
Explanation:
.equ defines constants (like #define in C)
.text marks the code section
ldr loads a value into a register
str stores a value into memory
The program writes values to GPIO registers to control pins. XOR (eors) toggles bits, making the LED blink. A loop with subs and bne creates a delay.
---
AVR and AvrAsm
AVR is an 8-bit architecture used in Arduino. It has 32 small (8-bit) registers. Peripheral addresses are separate, accessed via IN and OUT.
Example:
(code unchanged)
Key differences:
ldi loads constants
out writes to I/O
rjmp is a relative jump
Since registers are only 8-bit, delays require nested loops or multi-register arithmetic.
Also, program memory is separate from data memory.
---
MSP430 — 16-bit Controllers
These Texas Instruments chips are easy to use thanks to built-in bootloaders (programming via UART).
Example:
(code unchanged)
Features:
.org sets code address
mov handles data transfer
jnz = jump if not zero
The syntax resembles x86 and older systems like PDP-11.
---
8051 Family
These classic 8-bit controllers date back to the 1980s but are still used.
Example:
(code unchanged)
Features:
Separate memory spaces
Direct writes to peripheral registers
SJMP = short jump
They have quirky but interesting design choices.
---
x86 (DOS, TASM)
Assembly for early PCs:
(code unchanged)
Key ideas:
Segmented memory (64 KB segments)
INT 21h calls DOS functions
INT 20h exits the program
Strings must end with $.
---
x86 (Linux, GAS)
Modern version:
(code unchanged)
Differences:
Registers: EAX, EBX, etc.
$ for constants, % for registers
int 0x80 calls Linux system functions
Example: write to stdout and exit.
---
Intel 4004 (Bonus)
The first commercial microprocessor (4-bit).
Features:
16 registers (4-bit each)
Accumulator-based operations
Limited instruction set
Example:
(code unchanged)
Due to its simplicity, it’s mainly useful for educational purposes.
---
Conclusion
The goal of this article was not to teach assembly, but to show what it looks like and how different architectures compare.
If you try writing assembly yourself, you may find it surprisingly interesting—and a great mental exercise.
We didn’t cover AMD64 or ARM64, but they are conceptually similar to x86 and ARM32.
Our goal is not to convince everyone to write in assembly (assemblies!)—it’s not that difficult, but for most tasks it’s not very practical. The goal is simply to introduce it! So that you won’t be afraid to occasionally peek into low-level debugging, write some inline assembly for optimization, or maybe even attempt writing a compiler or something similar.
As a bonus—for the curious—we’ll also look at assembly for the Intel 4004, a 4-bit processor that’s over 50 years old. There will also be a small interactive example for it.
---
General Notes
Assembly language is a way to write processor instructions—not as hexadecimal codes (though that’s possible too!), but as human-readable mnemonics.
Processors differ (in architecture and type), and so do their instructions. Therefore, assembly languages differ at least in their instruction mnemonics. Additionally, different assembler authors use slightly different syntax formats.
Does this mean assembly programs are completely non-portable compared to C? Not necessarily. Most assemblers support macros, defines, and similar tools that allow some level of abstraction. However, this is rarely needed.
---
About Processors and Systems
Learning any assembly language usually begins with studying the processor architecture—what capabilities and resources it has. The first thing to consider is registers—small memory cells inside the CPU. There are usually few of them, from 1 to 32, not counting special ones like the PC (Program Counter), which holds the address of the current instruction.
Many instructions manipulate registers—for example, arithmetic operations. Others handle control flow (branches, loops). A “jump” is essentially writing a new value into the PC register.
Memory access instructions are also crucial. When we write a value to an address like 0x1020BEDA, the processor interacts with the system’s address space. That space may represent RAM—or hardware like I/O ports or sound devices.
Thus, writing to memory might store data—or toggle voltages on pins or produce sound. This is how processors interact with hardware.
Another complication: even with the same CPU core, connected peripherals may differ. This affects portability and adds complexity.
---
ARM32 — The Microcontroller World
We start with mid-range systems: 32-bit ARM microcontrollers, widely used in embedded devices—from gadgets to smartphones.
ARM32 uses 32-bit registers (typically 12–16 general-purpose). It can theoretically address 4 GB of memory, though most microcontrollers have far less, leaving unused address space for peripherals.
Example: blinking an LED.
(code unchanged)
Explanation:
.equ defines constants (like #define in C)
.text marks the code section
ldr loads a value into a register
str stores a value into memory
The program writes values to GPIO registers to control pins. XOR (eors) toggles bits, making the LED blink. A loop with subs and bne creates a delay.
---
AVR and AvrAsm
AVR is an 8-bit architecture used in Arduino. It has 32 small (8-bit) registers. Peripheral addresses are separate, accessed via IN and OUT.
Example:
(code unchanged)
Key differences:
ldi loads constants
out writes to I/O
rjmp is a relative jump
Since registers are only 8-bit, delays require nested loops or multi-register arithmetic.
Also, program memory is separate from data memory.
---
MSP430 — 16-bit Controllers
These Texas Instruments chips are easy to use thanks to built-in bootloaders (programming via UART).
Example:
(code unchanged)
Features:
.org sets code address
mov handles data transfer
jnz = jump if not zero
The syntax resembles x86 and older systems like PDP-11.
---
8051 Family
These classic 8-bit controllers date back to the 1980s but are still used.
Example:
(code unchanged)
Features:
Separate memory spaces
Direct writes to peripheral registers
SJMP = short jump
They have quirky but interesting design choices.
---
x86 (DOS, TASM)
Assembly for early PCs:
(code unchanged)
Key ideas:
Segmented memory (64 KB segments)
INT 21h calls DOS functions
INT 20h exits the program
Strings must end with $.
---
x86 (Linux, GAS)
Modern version:
(code unchanged)
Differences:
Registers: EAX, EBX, etc.
$ for constants, % for registers
int 0x80 calls Linux system functions
Example: write to stdout and exit.
---
Intel 4004 (Bonus)
The first commercial microprocessor (4-bit).
Features:
16 registers (4-bit each)
Accumulator-based operations
Limited instruction set
Example:
(code unchanged)
Due to its simplicity, it’s mainly useful for educational purposes.
---
Conclusion
The goal of this article was not to teach assembly, but to show what it looks like and how different architectures compare.
If you try writing assembly yourself, you may find it surprisingly interesting—and a great mental exercise.
We didn’t cover AMD64 or ARM64, but they are conceptually similar to x86 and ARM32.