Est. 1949 Advanced

Assembly

The lowest-level programming language, directly controlling hardware through processor instructions.

Also known as: Assembler

Created by Various (David Wheeler wrote the first assembler under Maurice Wilkes)

Paradigm Imperative, Low-level
Typing None (untyped)
First Appeared 1949
Latest Version Architecture-specific (x86-64, ARM64, RISC-V)

Assembly language is the thin layer between human-readable code and raw machine instructions. Every program, regardless of what language it’s written in, eventually becomes assembly (or the machine code it represents). Understanding assembly means understanding how computers actually work at the processor level.

History & Origins

The concept of assembly language emerged from the need to make machine code programming more manageable. In the earliest days of computing, programmers wrote programs directly in binary or numeric codes representing machine instructions. This was tedious and error-prone.

The First Assembly Notation

The history of assembly language begins before the first working assembler. In 1947, Kathleen Booth (nee Britten), working with her husband Andrew Donald Booth at Birkbeck, University of London, created the first known assembly language mnemonic notation in their work “Coding for A.R.C.” (Automatic Relay Calculator). The notation was influenced by her visit to John von Neumann and Herman Goldstine at the Institute for Advanced Study at Princeton. It was a notational system rather than an automated translation program, but it established the idea of representing machine instructions with human-readable symbols.

The Birth of Symbolic Assembly

In 1949, David Wheeler, working under Maurice Wilkes at Cambridge University, wrote the first assembler – the “Initial Orders” – for the EDSAC computer. Instead of writing raw numeric codes, programmers could use symbolic names for operations (like ADD or STORE) and labels for memory addresses. The assembler would translate these symbols into the actual machine codes.

This was revolutionary. A programmer could write:

ADD X
STORE Y

Instead of memorizing and typing numeric operation codes and absolute memory addresses.

EDSAC and the First Textbook

The EDSAC ran its first program on May 6, 1949, calculating a table of square numbers and a list of prime numbers. The Initial Orders were stored in read-only memory formed from rotary telephone selector switches and occupied just 31 words. A second version, installed in approximately August-September 1949, expanded to 41 words and added relocation facilities for subroutines.

In 1951, Wilkes, Wheeler, and Stanley Gill published The Preparation of Programs for an Electronic Digital Computer, widely recognized as the first programming textbook. It introduced the term “assembly” in a programming context, describing the process of assembling a program from component subroutines.

Symbolic Assembly and the IBM Era

Nathaniel Rochester, who joined IBM in 1948 and co-designed the IBM 701, let programmers on that machine use symbolic labels and addresses, making programs more readable and maintainable. In 1955, Stan Poley at IBM’s Watson Lab wrote SOAP (Symbolic Optimal Assembly Program) for the IBM 650. SOAP automatically optimized instruction placement on the 650’s rotating drum memory, an early example of an assembler performing optimization beyond simple translation.

The IBM System/360 brought Basic Assembly Language (BAL) in 1964, which established a mainframe assembly standard. Its descendants, culminating in IBM’s High-Level Assembler (HLASM), are still used on IBM Z mainframes.

Macro Assemblers

Macros let programmers define reusable patterns of instructions that the assembler expands before translation. IBM’s Autocoder, developed for the IBM 702/705 in the late 1950s, is credited as one of the earliest assemblers to support them, and macro facilities became a standard feature of assemblers from the 1960s onward. In 1970, Harlan Mills proposed Concept-14, a set of structured programming macros (IF/ELSE/ENDIF blocks) for the OS/360 assembler, implemented by Marvin Kessler at IBM’s Federal Systems Division.

Evolution Across Architectures

As different computer architectures emerged, each developed its own assembly language:

  • IBM mainframes: Had their own assembly syntax still used in legacy systems
  • PDP-11: Influenced the development of C at Bell Labs
  • Intel x86: Became the dominant PC architecture from 1978 onwards
  • ARM: Rose to dominate mobile devices and now challenges x86 in desktops and servers
  • RISC-V: Open-source architecture gaining momentum for embedded and beyond

High-Level Languages and Cross-Assemblers

FORTRAN arrived in 1957, developed by John Backus and his team at IBM as an alternative to assembly language for the IBM 704, and it began a gradual shift away from assembly as the primary programming language. COBOL (1959) and later C (1972) expanded the domains where high-level languages could replace it. Dennis Ritchie and Ken Thompson developed C to rewrite Unix, originally implemented in PDP-7 assembly, in a portable language that kept the low-level access typical of assembly.

As microprocessors proliferated in the 1970s and 1980s, cross-assemblers became essential: tools that run on one architecture and produce code for a different target processor. Embedded targets often lacked the resources to run development tools, and cross-assembly remains the standard development model for embedded systems.

Design Philosophy

Assembly language provides a human-readable representation of exactly what the processor will do.

One-to-One Correspondence

Each assembly instruction typically maps to exactly one machine instruction. When you write mov eax, 42, the processor will execute precisely that operation - moving the value 42 into the EAX register. There’s no hidden runtime, no garbage collection, no abstraction layers.

Total Control

Assembly gives complete control over:

  • Registers: The processor’s fastest storage locations
  • Memory: Direct addressing, pointer arithmetic, stack manipulation
  • Instructions: Access to every operation the CPU supports
  • Timing: Predictable instruction timing for real-time systems

Architecture Dependence

Unlike high-level languages, assembly is tied to a specific processor architecture. x86 assembly won’t run on ARM, and ARM assembly won’t run on RISC-V. This limits portability but gives access to architecture-specific capabilities.

Programmer Responsibility

Assembly provides no type system, no automatic memory management, and no enforced calling conventions. The programmer is responsible for every register allocation, every stack frame, and every memory access.

Key Features

Labels and Symbolic Addressing

A programmer can write JMP loop_start rather than calculating and hard-coding a memory offset, and the assembler resolves the label to the correct address.

Directives and Pseudo-Instructions

All assemblers support directives, also called pseudo-instructions: commands to the assembler itself rather than the processor. These control data definition (DB, DW, DD), memory alignment, section organization (.text, .data, .bss), and conditional assembly.

Macro Systems

Modern assemblers let you define reusable instruction sequences with parameters. Macros are expanded at assembly time, providing code reuse without runtime overhead. Macro systems range from simple text substitution to Turing-complete macro languages, as in FASM.

Common File Extensions

  • .asm: the most common generic extension, used with MASM, NASM, FASM, TASM, and others
  • .s: standard on Unix/Linux systems (GCC/GAS toolchain); lowercase indicates the file is assembled directly
  • .S: on Unix/Linux, uppercase indicates the file is run through the C preprocessor before assembly
  • .inc: include files containing macro definitions, constants, and shared declarations

Why Learn Assembly Today?

In an era of Python and JavaScript, why would anyone learn assembly?

Understanding Computer Architecture

Assembly reveals how computers actually work. Concepts like:

  • The stack and function calling conventions
  • Register allocation and memory hierarchy
  • Instruction pipelining and optimization
  • System calls and operating system interfaces

These become concrete rather than abstract when you work in assembly.

Performance Optimization

While modern compilers are excellent, they can’t always match hand-tuned assembly. Critical inner loops in:

  • Video codecs (x264, AV1 encoders)
  • Cryptographic libraries (OpenSSL and others)
  • Mathematical libraries (OpenBLAS, GotoBLAS)
  • Game engines (physics, rendering pipelines)

Often contain hand-written assembly for maximum performance.

Security and Reverse Engineering

Security professionals need assembly to:

  • Analyze malware behavior
  • Develop and understand exploits
  • Perform binary analysis and reverse engineering
  • Write secure, vulnerability-free low-level code

Embedded and Systems Programming

When working close to hardware:

  • Bootloaders must be written in assembly
  • Interrupt handlers often need precise assembly control
  • Real-time systems require predictable timing
  • Resource-constrained microcontrollers demand efficiency

Assembly Variants

Several assemblers exist for x86 architecture:

NASM (Netwide Assembler)

NASM uses Intel syntax and is popular for its clean, consistent design. It’s free, open-source, and runs on multiple platforms. This is what we use in our examples.

GAS (GNU Assembler)

Part of the GNU toolchain, GAS uses AT&T syntax by default (though it supports Intel syntax too). It’s what GCC uses internally.

MASM (Microsoft Macro Assembler)

Microsoft’s assembler for Windows development, tightly integrated with Visual Studio.

FASM (Flat Assembler)

A self-hosted assembler known for its macro capabilities and being written entirely in assembly itself.

TASM (Turbo Assembler)

Borland’s assembler, released in 1989 and popular in the DOS era. Its last version dates from approximately 1996.

Modern Assembly

Today’s assembly programming is different from the past:

Interfacing with High-Level Code

Modern assembly often exists as optimized functions called from C, C++, or Rust. You write the hot path in assembly and let the compiler handle the rest.

SIMD Programming

Modern CPUs have SIMD (Single Instruction, Multiple Data) extensions like SSE, AVX, and NEON that process multiple data elements simultaneously. These are often easiest to access through assembly or intrinsics.

Platform-Specific Optimization

While the basic operations are similar, each architecture has unique instructions. ARM (in its 32-bit mode) historically featured conditional execution of most instructions, though this was removed in ARM64/AArch64 in favor of specialized conditional instructions. x86 has CISC complexity, and RISC-V has extensibility.

Learning Assembly

To learn assembly, start with:

  1. A specific architecture (x86-64 is common for desktop learning)
  2. A good assembler (NASM is beginner-friendly)
  3. Understanding of basic computer architecture
  4. Practice with small programs before tackling complex ones

Timeline

1947
Kathleen Booth creates the first assembly language mnemonic notation for the ARC (Automatic Relay Calculator) at Birkbeck, University of London, influenced by her visit to John von Neumann at Princeton
1949
David Wheeler, working under Maurice Wilkes at Cambridge, writes the first assembler (Initial Orders) for EDSAC
1951
Maurice Wilkes, David Wheeler, and Stanley Gill publish 'The Preparation of Programs for an Electronic Digital Computer', the first programming textbook, introducing the term 'assembly' in a programming context
1953
Nathaniel Rochester writes the first symbolic assembler for IBM 701
1954
IBM introduces assembly language for the 704
1955
Stan Poley writes SOAP (Symbolic Optimal Assembly Program) for the IBM 650 at IBM Watson Lab, notable for optimizing instruction placement on the drum memory
1964
IBM System/360 launched with Basic Assembly Language (BAL), establishing a mainframe assembly standard that evolves into IBM's High-Level Assembler (HLASM)
1978
Intel 8086 processor establishes x86 architecture
1981
Microsoft releases MASM (Microsoft Macro Assembler) version 1.00 alongside the IBM PC launch, becoming the primary assembler for DOS and Windows x86 development
1996
NASM (Netwide Assembler) first released by Simon Tatham and Julian Hall
2000
FASM (Flat Assembler) publicly released by Tomasz Grysztar, a self-hosting assembler written entirely in assembly language
2003
AMD64/x86-64 extends x86 to 64-bit
2005
ARM processors dominate mobile phone market, with reported share exceeding 90%
2020
Apple Silicon (ARM-based) Macs debut; RISC-V gains momentum

Notable Uses & Legacy

Operating System Kernels

Critical bootloaders, interrupt handlers, and context switching code in Linux, Windows, and macOS are written in assembly.

Device Drivers

Hardware-level communication often requires assembly for precise timing and register manipulation.

Embedded Systems

Microcontrollers with limited resources frequently use assembly for maximum efficiency.

Security Research

Reverse engineering, exploit development, and malware analysis require deep assembly knowledge.

Performance-Critical Code

Video codecs, cryptographic algorithms, and game engines use hand-optimized assembly for hot paths.

Compilers

All compilers ultimately generate assembly or machine code; understanding assembly aids compiler development and debugging.

RollerCoaster Tycoon (1999)

Chris Sawyer wrote approximately 99% of RollerCoaster Tycoon in x86 assembly using MASM, with only about 1% in C for Windows and DirectX interfacing.

MenuetOS and KolibriOS

MenuetOS (by Ville Turjanmaa) and its fork KolibriOS are complete operating systems with graphical user interfaces written entirely in FASM assembly language.

Language Influence

Influenced By

Machine Code

Influenced

Running Today

Run examples using the official Docker image:

docker pull esolang/x86asm-nasm:latest

Example usage:

docker run --rm -v $(pwd):/code -w /code esolang/x86asm-nasm x86asm-nasm hello.asm

Topics Covered

Related Languages

Sources & Further Reading

Last updated: