# AmmAsm — x86-64 Assembler

**Version:** 1.8 
**Author:** Ammar Najafli 
**License:** MIT 

AmmAsm is handwritten x86-64 assembler designed for simplicity and clarity. It compiles assembly code directly to machine code and produces ELF executables and PIE(Position-Independent Executable) for Linux x86-64.

---

## What's New in v1.8

### PIE(Position-Independent Executable)

1) AmmAsm v1.8 introduces full PIE(Position-Independent Executable) binary. In computing, it is a specialized binary file that randomizes its memory  location every time it runs, virtual memory == offset in file, nevertheless in PIE mode instruction `mov r64, [label]` will not be executed how you expect becouse in this mode virt is 0x55XXXXXXXXXXXX0000, not 0x400000. Instead of this you should use RIP-REL adressing => `lea r64, [label]`.

2) New instructions: `sub`, `imul`, `lea`(full implemented)

3) Fixed Linker

4) Added tests with ScreenShots


**IMUL (Signed Multiply) — full support:**
- `imul r64` — RAX = RAX * r64
- `imul r64, r64` — dest = dest * src
- `imul r64, imm` — rax = rax * imm (sugar)
- `imul r64, r64, imm` — dest = src * imm
- All sizes (8/16/32/64), imm8/imm16/imm32 optimization


**Hello, World in PIE via pointer**

```ASM
ptr: dq msg - $
msg: db "Hello, World\n", 
len: dq $ - msg

_start:
    mov rax, 1
    mov rdi, 1
    lea rsi, [b=ptr]
    add rsi, [b=rsi]
    mov rdx, [b=len]
    syscall

    mov rax, 60
    syscall
```


**All expression features work in:**

- `mov rax, msg+5` -> absolute address
- `mov rax, [b=msg+5]` -> RIP-relative address
- `jmp msg+10` -> relative jump with offset
- `lab: dq $-msg, msg+8, msg+16` -> data directives
- `add rax, $-_start` -> arithmetic with current address
- `mov rax, (((((10 * 2) << 2) + $) & 0xFF) | 0x100) - label` -> mixed all

**Expression examples:**

```asm
_start:
    mov rax, msg           ; address of msg (0x401000)
    mov rbx, $-_start      ; length from _start to current (negative)
    mov rcx, msg+5         ; address of 'W' in "Hello World"
    mov rdx, msg+8         ; address of 'r' in "World"
    
    dq $-msg, msg+5, msg+8, 0xdeadbeef
    
    jmp _start
    
msg: db "Hello World", 0
```

---

**Backend 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 machine code → running binary

[![Demo](https://img.youtube.com/vi/P-bdMZXXyVg/0.jpg)](https://youtube.com/watch?v=P-bdMZXXyVg)

---


## Features

- 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
- ELF output — Generates valid Linux x86-64 ET_EXEC and PIE binary

---

## Pipeline Stages

### 1. Lexer (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'

### 2. Parser (PARSE)

Builds the Abstract Syntax Tree.

- Validates operand combinations per instruction
- Resolves operand types: O_REG8/16/32/64, O_IMM, O_MEM, O_LABEL, O_CHAR
- Produces typed AST nodes: AST_INS, AST_LABEL, AST_U8/16/32/64, etc.

### 3. Code Generator (parseInst)

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

### 4. Linker (collect_labels + resolve_labels)

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

### 5. Compiler (compiler)

Orchestrates all passes and writes the final binary buffer.

---

## Syntax Reference

### Registers

```asm
mov rax, 42        ; 64-bit
mov eax, 42        ; 32-bit
mov ax,  42        ; 16-bit
mov al,  42        ; 8-bit
mov r8,  r15      ; Extended regs r8-r15
```

### Numeric Literals

```asm
mov rax, 42            ; Decimal
mov rax, 0xff          ; Hexadecimal
mov rax, 0b1010        ; Binary
mov rax, 0o777         ; Octal
mov rax, -10           ; Negative
mov al,  'A'           ; Character literal
```

### Memory Addressing

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 |

```asm
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
```

### Data Directives

```asm
msg:    db  "Hello, World!", 0x0A, 0
bytes:  db  0x01, 0x02, 0x03, 'A', 'B'
words:  dw 100, 200, 300, 0x1234
dwords: dd 0xDEADBEEF, 1000000
qwords: dq 0x123456789ABCDEF0, 0
```

### Comparison
```asm
cmp rax, 42        ; rax vs immediate (64-bit)
cmp eax, ebx       ; register vs register (32-bit)
cmp al,  'A'       ; 8-bit with char literal
```

### Conditional Jumps
```asm
cmp rax, 0
je  done            ; jump if equal
jne loop            ; jump if not equal
jl  less            ; jump if less (signed)
jg  greater         ; jump if greater (signed)
jb  below           ; jump if below (unsigned)
ja  above           ; jump if above (unsigned)
```

### Unconditional Control Flow
```asm
jmp  label         ; relative jump  (E9 rel32)
jmp  rax           ; indirect jump  (FF /4)
call func          ; relative call  (E8 rel32)
call rbx           ; indirect call  (FF /2)
```

### Load Label Address
```asm
mov rax, msg      ; load virtual address of 'msg' into rax(Does not work in PIE)
```

---

## Building & Usage

```bash
# Build
gcc -std=c99 Aasm.c -o aasm

# Compile assembly
./aasm input.asm
./aasm input.asm -o output
./aasm -pie input.asm -o prog

# Run
chmod +x output && ./output
```

---

## Known Limitations

- Limited instruction set — `mov`, `add`, `sub`, `imul`, `cmp`, `jmp`, `lea`, `call`, `jcc`, `syscall` (more coming)
- No multi-file linking
- No `.data` / `.bss` sections (everything in `.text` + RWX)
- No macro system
- No floating-point (FPU/SSE)
- No optimization passes (coming in 2.0-2.1)

---

## Resources

- [Intel SDM — IA-32/x86-64 Developer Manual](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html)
- [x86-64 Instruction Encoding — OSDev Wiki](https://wiki.osdev.org/X86-64_Instruction_Encoding)
- [ELF Specification](https://refspecs.linuxfoundation.org/elf/elf.pdf)
- [System V AMD64 ABI](https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf)

---

**Made by a 15 y.o. systems programmer.**