AmmAsm - Assembler that sucks less.
Author: Ammar Najafli
AmmAsm is a handwritten x86-64 assembler designed for simplicity and clarity, educational and experimental purpuse. It compiles assembly code directly to machine code and produces ELF executables, PIE binaries (Position-Independent Executables), and relocatable object files for Linux x86-64. Successfully tested on Deban12, Kyronix, Windows(experimental).
-
Added new instructions:
SSE2(45 instructions),AVX/AVX2/AVX512(~80 instructions),AVX-512 FP16(3 instructions)AVX-512 BF16(2 instructions),bsf,bsr,cmc,clc,stc,cld,std,cli,sti,lahf,sahf,pushf,popf,popfq,iret,iretq,cpuid,hlt,wait,fwait,pause,ud2,xchg,movq,rdpru,rdtsc,rdtscp,lfence -
Added Float number for
SSE1, IEEE-754 -
alignsymbol:align <scale>, <8 bit number>: align 16, 0 -
Added
XMM16-XMM31registers -
Added
YMM0-YMM31registers -
Added
ZMM0-ZMM31registers (Fully supported via EVEX prefix engine) -
Hardware check for the presence of SIMD instructions via
cpuid -
Full VEX/EVEX support (mask registers k0-k7, z, broatcast, ect.)
-
Backend refactoring
-
Added Documentation for every supported instruction in AmmAsm
AmmAsm includes a fully handwritten, high-performance EVEX prefix encoder with zero external dependencies. It supports the core features of the modern Intel/AMD AVX-512 architecture.
- 32 Vector Registers: Full access to
ZMM0-ZMM31(as well asXMM16-XMM31andYMM16-YMM31). - Predicate Masking: Dedicated hardware masking with
{k1}-{k7}register selection. - Zeroing Masking: Optional conditional zeroing via the
{z}modifier. - Embedded Broadcast: Built-in
{b}flag support for memory operands (e.g.,DWORD BCST). - Compressed Displacement: Automatic scale matching (disp8 times N) based on data type, broadcast state, and vector size.
Unlike standard assemblers, AmmAsm uses an explicit, clean, and bulletproof Key-Value syntax for memory operands to make handwritten assembly robust and easy to parse:
_start:
; 512-bit vector add with masking {k1}, zeroing {z}, SIB-addressing,
; and embedded 1-to-16 DWORD broadcast {b} enabled!
vaddps zmm20{k1}{z}, zmm10, [b=rbp, i=rcx, s=1, d=64]{b}also check tests/General/
Code generated directly by AmmAsm and disassembled using standard Linux objdump -d -Mintel:
0000000000000000 <_start>:
0: 62 e1 2c d9 58 64 0d vaddps zmm20{k1}{z},zmm10,DWORD BCST [rbp+rcx*1+0x40]
7: 10 Starting from v2.0.0, AmmAsm can generate valid ELF64 relocatable object files (.o) in addition to executables.
Generated object files are compatible with the standard Linux toolchain and can be linked using ld, gcc, or other GNU binutils-compatible linkers.
This allows AmmAsm to participate in normal C/C++ build pipelines instead of being limited to standalone executable generation.
Generated object files contain:
.text- executable code.data- initialized data.symtab- symbol table.strtab- symbol string table.shstrtab- section-name string table.rela.text- relocation records.note.GNU-stack- marks stack as non-executable
The global and extern directivies exports labels into the ELF symbol table.
global _start, strcmp
extern printf, __pthread_unregister_cancel_restoreOnly object-file generation uses exported symbols. They have no effect when producing ET_EXEC.
References that cannot be resolved during assembly automatically generate relocation entries.
Currently supported relocations include:
- RIP-relative label references
- External/global symbols
- Symbol references requiring linker resolution
Relocations are emitted into .rela.text and are resolved later by ld, gcc, or compatible ELF linkers.
./aasm hello.asm -c hello.o
gcc hello.o -o hello
./hello- Basic SSE/SSE2/AVX1/AVX2/AVX-512 support(VEX/EVEX fullsuport)
- Macro system (v2.2.0)
- Compatible with GNU ld and GCC object-file linking
- Direct x86-64 encoding - No NASM/GAS dependencies
- Multiple operand sizes - 8/16/32/64-bit registers and immediates
- Memory addressing - Full SIB/ModRM support with explicit key-value syntax
- RIP-relative addressing - Automatic for label bases (v1.6)
- Label support - Global and local labels with two-pass symbol resolution
- Inline literals - Embed strings and data directly in .text
- Control flow - jmp, call, conditional jumps with relative addressing
- Two-pass linker - Built-in symbol resolution and relocation
- Numeric literals - 0xDEADBEEF, 0b1010, 0o777, decimal, negative, float (beta)
- ELF output - Generates valid Linux x86-64 ET_EXEC, PIE and OBJ(v2.0.0) binary
-
AmmAsm supports RDPRU, an AMD-specific instruction currently not supported by NASM 3.02. (August 10 2026)
-
AmmAsm supports all form of
vcvtps2phinstruction while NASM 3.02 does not
Expression features:
mov rax, msg+5-> absolute addressjmp msg+10-> relative jump with offsetlab: dq $-msg, msg+8, msg+16-> data directivesadd rax, $-_start-> arithmetic with current addressmov rax, (((((10 * 2) << 2) + $) & 0xFF) | 0x100) - label-> mixed all
Expression examples:
_start:
mov rax, msg ; address of msg (0x401000)
mov rbx, $-_start ; length from _start to current
mov rcx, msg+5 ; address of 'W' in "Hello World"
mov rdx, msg+8 ; address of 'r' in "World"
.tmp: dq $-msg, msg+5, msg+8, 0xdeadbeef
jmp _start
msg: db "Hello World", 0Backend Refactoring
- resolve_expr() - New expression evaluator that supports:
- Label resolution (msg)
- Current address ($)
- Character literals ('A')
- Arithmetic (+, -, *, /, <<, >>, &, |, ^)
- Parentheses for grouping
- Mixed expressions with labels and constants
Assembling x86-64 code -> generating object-files -> linking via
ld-> running binary
Parse all macro and replace them in called place.
- Parse macro body, store it args, content in memory.
- Parses all macros and expands them where they are called.
- Recursively expands nested macros.
- After finishing, creates file.asm.i file and gives this file to Lexer
Converts source text to a flat token stream.
- Recognizes instructions, registers (rax), literals, labels, directives
- Comments: //, ;, /* ... */
- Number bases: hex (0x), binary (0b), octal (0o), decimal
- Label scoping: global label:, local .label: (scoped to last global)
- Character literals: 'A', '\n', '\0'
Builds the Abstract Syntax Tree.
- Validates operand combinations per instruction
- Resolves operand types: O_REG8/16/32/64, O_IMM, O_MEM, O_CHAR, O_EXPR
- Produces typed AST nodes: AST_INS, AST_LABEL, AST_U8/16/32/64, etc.
Emits x86-64 machine code per AST node.
- REX prefix construction
- ModR/M and SIB encoding via encode_inst_rm_rm()
- Displacement and immediate encoding (little-endian)
- Placeholder bytes (0x00000000) for unresolved label references
- SSE/SSE2
Two-pass symbol resolution.
- Pass 1 - Walks AST, assigns vaddr to each AST_LABEL (base 0x401000 or 0x1000(PIE))
- Pass 2 - Patches placeholders:
- MOV r64, label -> absolute 64-bit address (8 bytes at mc[2])
- JMP/CALL/JCC label -> rel32 = target - (current_pc + inst_size)
- RIP-relative -> disp32 = target - (current_pc + inst_size) + user_disp
Orchestrates all passes and writes the final binary buffer.
Unlike NASM, AmmAsm uses an explicit key-value format inside [...]:
| Key | Meaning | Example |
|---|---|---|
| b=REG | Base register | b=rbx |
| i=REG | Index register | i=rcx |
| s=N | Scale (1/2/4/8) | s=4 |
| d=N | Displacement | d=0x10 |
mov rax, [b=rbx] ; [rbx]
mov rax, [b=rbx, d=16] ; [rbx + 16]
mov rax, [b=rbx, i=rcx, s=8] ; [rbx + rcx*8]
mov rax, [b=rbx, i=rcx, s=8, d=0x10] ; [rbx + rcx*8 + 16]
mov [b=rsp, d=8], rax ; store to [rsp+8]
mov rax, [b=msg] ; load from msg
mov rax, [b=msg, d=4] ; msg + 4# Build
./build.sh
# Compile assembly
./aasm input.asm
./aasm input.asm -o output
./aasm -pie input.asm -o prog
./aasm input.asm -c prog.o -d
./aasm input.asm -c prog.o -E
# Run
chmod +x output && ./output
ld prog.o -o output && chmod +x output && ./output- Limited instruction set - Only a subset of the x86-64 instruction set is currently implemented (look at
./insn.dat) - x87 FPU not implemented
- No
ah, bh, ch, dhregisters (sorry)
Intel, what were you smoking when you designed VEX?
Intel... after implementing EVEX, I no longer want to know. :)

