The syntax of the assembler

Intel syntax like NASM – with the bytes it becomes.

Asmonite builds a program byte by byte from the source. The syntax is the familiar one: ; for comments, mov eax, [ebx+ecx*4+8], labels, db and dd. This page shows what it can do, and next to every example the bytes the assembler writes (taken from the real assembler). Where it differs from NASM, it says so.

The examples are x86 (32 bit, .arch i386). For x64, ARM Thumb-2 and ARM64 the line layout and the calculation are the same; their instructions are explained by F1 in the program.

One line

At most one instruction per line. Everything is optional.

start:label (the colon is optional)
movinstruction
eax, [ebx+8]operands, separated by commas
; copycomment (; only)
You write
start:  mov eax, [ebx+8]    ; copy
        add eax, 1
        jmp start
The assembler produces
8B 43 08 83 C0 01 EB F8

A label is the address of the place where it stands. The case of a label does not matter (L1 and l1 are the same label). Write instructions in lower or upper case, but an instruction with a capital first letter chooses the second encoding (see below).

Numbers and expressions

Wherever a number stands you may calculate: in operands, with db, dw, dd, dq, resb, align, org.

Operators in this order of precedence, strongest first: sign and ~; * / %; + -; << >>; &; ^; |. Brackets always work. Numbers: decimal, 0x1F, 1Fh, 101b, 0b101 and a character in single quotes ('A').

ExpressionValueNote
4*16+165 (0x41)multiplication before addition
(2+3)*420 (0x14)brackets
100/714 (0xE)integer, signed
100%72the remainder
1<<5|335 (0x23)<< binds tighter than |
0x10^0x111exclusive or
~0xF & 0xFF240 (0xF0)bitwise not, then and
-5+83sign
'A'+166 (0x42)a character in quotes is a number
1Fh31 (0x1F)hexadecimal with h
101b5binary with b
0b1015binary with 0b

Constants, distances and the current address

name equ expression gives a number a name (the line writes no bytes; the constant may stand behind its use). $ is the address of the line, $$ the start of the section. The distance of two labels is a number, a size: end-start, $-msg.

You write
size    equ 4*16
        mov eax, size+1
msg:    db 'hi', 0
len     equ $-msg
        mov al, len
start:  mov ecx, end-start
        nop
end:
The assembler produces
B8 41 00 00 00 68 69 00 B0 03 B9 06
00 00 00 90

A distance needs the addresses; the assembler therefore runs more than once until they stay the same (you do not notice). Not possible are a number behind a label in a jump or call (jmp start+4) and division by zero – both are an error with the line number.

Operands and addresses

Registers, numbers, labels and memory in square brackets: [base + index*scale + number].

You writeBytesNote
mov eax, [ebx]
8B 03
a register
mov eax, [ebx+8]
8B 43 08
register plus number
mov eax, [ebx+ecx*4+8]
8B 44 8B 08
base, index times scale (1, 2, 4, 8), number
mov eax, [ebx+2*4]
8B 43 08
the constant part is calculated: like [ebx+8]
mov eax, [ebp-4]
8B 45 FC
a minus continues the address
mov byte [ebx], 1
C6 03 01
size of the access: byte, word, dword
mov word [ebx], 1
66 C7 03 01 00
as above, 16 bit: prefix 66
mov eax, [es:ebx]
26 8B 03
segment in front of the address

A label alone (mov eax, label) is its address as a number, in brackets (mov eax, [label]) the memory there. label+4 and label-4 are addresses, also as a value and in dd. The address is the virtual address in the loaded program (image base plus position), not the distance from the start of the file.

Jumps and calls

A label is the target; the assembler finds the length of the distance itself.

You writeBytesNote
l: jmp l
EB FE
without a size the assembler takes the shortest form
l: jmp near l
E9 FB FF FF FF
near: the long form (32 bit distance)
l: jz short l
74 FE
short: one byte distance
l: nop
 jz l
90 74 FD
a conditional jump backwards

Different from NASM: a number behind jmp is the distance, not the target address: jmp 5 is EB 05. With a sign it works too: jmp short -2, call near -5. loop and jecxz need short and a label or a distance.

Data and room

What is to stand in the program as bytes.

You writeBytesNote
db 'AB', 0, 10
41 42 00 0A
bytes; text in single quotes
dw 0x1234, 1+1
34 12 02 00
words (2 bytes, low byte first)
dd 1, -1
01 00 00 00 FF FF FF FF
double words (4 bytes)
dq 1
01 00 00 00 00 00 00 00
quad words: 8 bytes
du 'Hi'
48 00 69 00
text as UTF-16: 2 bytes per character
nop
align 4
nop
90 00 00 00 90
align pads with 00 to a multiple
org 4
nop
00 00 00 00 90
org pads with 00 up to the position in the file
resb 4
nop
00 00 00 00 90
resb: room for four bytes

A label in front of a data line (msg db 'hi', 0, with or without a colon) is the address of the data. With .format pe, resb may stand only at the end of a section: the room is not in the file but is made when the program is loaded.

The kind of file

Directives at the start say what the assembler writes. They stand before the first instruction.

Windows program

The assembler writes the header, the sections and the import table; you write code, data and imports.

.format pe console
.arch i386
.import msvcrt.dll cdecl printf
.import kernel32.dll ExitProcess

.code
start:
    call [printf](text)
    call [ExitProcess](0)

.data
text:   db 'Hello', 10, 0

DOS program or boot sector

With .format binary the bytes stand as you write them: a .COM file, a boot sector or a header you write yourself.

.format binary
.output hello.com
.arch x86_real

section 0x100
start:
    mov  ah, 0x09
    mov  dx, msg
    int  0x21
    mov  ax, 0x4C00
    int  0x21
msg:  db 'Hello!', 13, 10, '$'
DirectiveMeaning
.arch i386processor: i386 (32 bit), x86_real (16 bit, DOS), x64, thumb (ARM Thumb-2), arm64
.format pe [console|gui]writes a Windows program; .format binary the bytes as they stand
.output namethe name of the file that is written (next to the source)
.code, .datawith pe: the following lines are program or data
.import dll name, nameimported functions; the name is a label of the import table: call [name]. For C functions add cdecl (see func)
.entry label, .imagebase nentry point, load address
section n, segment n, org nwith binary: a section with this virtual address, a DOS segment, padding up to the file position

What is different from NASM

The source looks like NASM, but the assembler is not NASM compatible. The main points:

You writeBytesNote
mov eax, ebx
8B C3
lower case: the first encoding
MOV eax, ebx
89 D8
an instruction that starts with an upper-case letter: the second encoding where there are two
mul eax, ebx
F7 E3
the accumulator is written (mul ebx does not work)
push byte 5
6A 05
byte before the number chooses the short form
mov ax, 5
66 B8 05 00
without .arch it is 32 bit: a 16-bit register needs the prefix 66
NASMAsmonite
bits 16, section .text.arch x86_real; section <number> (the name is not evaluated)
org 0x100 sets the load addressorg pads the file with 00 up to this position
align 4 pads code with nopalign always pads with 00
times, incbin, macros, %definedo not exist; there is no preprocessor
global, externdo not exist; imports: .import
labels are case sensitivelabels are not
.local is valid until the next label.l is an ordinary label, the same one twice is an error
db "AB"single quotes only: db 'AB'
dd label is the distance from the startdd label is the virtual address; rva label the distance
db 256: a warning onlydb 256 is an error

Not included yet are the x87 FPU, MMX/SSE and some system instructions. The help (F1 on a line) in both programs explains every instruction the assembler knows and every directive.

Next

Functions

func writes the stack frame, call Name(…) pushes the arguments, with stdcall and cdecl.

The func page →

Help in the program

F1 on a line explains the instruction, the Windows function, the directive and the error message.

More about F1 →

Try it

The ZIP has examples for Windows, DOS and ARM. From the download to the first breakpoint in 60 seconds.

Quick start →