Compare commits
57 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4e833ac46 | ||
|
|
ca9fd58bed | ||
|
|
4690b3c7ec | ||
|
|
2f7282577a | ||
|
|
c48c4261da | ||
|
|
c4f9eb0e55 | ||
|
|
f87ab3afa4 | ||
|
|
9228b7949b | ||
|
|
75d765c905 | ||
|
|
dd8d005e5a | ||
|
|
26c219a2f7 | ||
|
|
e287ace328 | ||
|
|
02551fe706 | ||
|
|
224174a418 | ||
|
|
4e0719f891 | ||
|
|
52950c255a | ||
|
|
2b447b5517 | ||
|
|
8d4769ad2b | ||
|
|
87be0c0c33 | ||
|
|
17baf01a9e | ||
|
|
bb7a81265b | ||
|
|
728549c046 | ||
|
|
6eeeca1266 | ||
|
|
f9767b9f72 | ||
|
|
927b8517c5 | ||
|
|
b66b4f6668 | ||
|
|
5ee33f8cf6 | ||
|
|
f809090299 | ||
|
|
13f4e2bfe4 | ||
|
|
6df1d2ac9b | ||
|
|
07435f5839 | ||
|
|
9bd36905af | ||
|
|
10a45ff7f2 | ||
|
|
45ce196938 | ||
|
|
a78d06a216 | ||
|
|
aacf009a14 | ||
|
|
fbffd9f654 | ||
|
|
f49939b72b | ||
|
|
d54b7ba1a9 | ||
|
|
ff22a96106 | ||
|
|
c9c3f239c8 | ||
|
|
654c14d92b | ||
|
|
79a4c65df3 | ||
|
|
52d2466eb5 | ||
|
|
f6ffba2460 | ||
|
|
0c81162553 | ||
|
|
0d6ed8a318 | ||
|
|
c2934ffd50 | ||
|
|
7472eb1279 | ||
|
|
44d5e95970 | ||
|
|
6f76735710 | ||
|
|
6b6bacd9dd | ||
|
|
6f516bbb93 | ||
|
|
a9d96063f6 | ||
|
|
855893ee09 | ||
|
|
bce8e8356f | ||
|
|
00b56875f9 |
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@
|
|||
lang/target
|
||||
lang/libcompiler/target
|
||||
/target/
|
||||
/stages/build/
|
||||
|
|
|
|||
127
flake.lock
127
flake.lock
|
|
@ -20,11 +20,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1760524057,
|
||||
"narHash": "sha256-EVAqOteLBFmd7pKkb0+FIUyzTF61VKi7YmvP1tw4nEw=",
|
||||
"lastModified": 1780243769,
|
||||
"narHash": "sha256-x5UQuRsH3MqI0U9afaXSNqzTPSeZlRLvFAav2Ux1pNw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "544961dfcce86422ba200ed9a0b00dd4b1486ec5",
|
||||
"rev": "331800de5053fcebacf6813adb5db9c9dca22a0c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -35,6 +35,22 @@
|
|||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1769861202,
|
||||
"narHash": "sha256-JwhbEHmbFFH754loOPbpjPHf9EAi+oMm2RsFXnlbt1g=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "f214de98544a6acf0d9917ba265ac50849048fcb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1744536153,
|
||||
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
|
||||
|
|
@ -50,23 +66,95 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pwndbg": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"pyproject-build-systems": "pyproject-build-systems",
|
||||
"pyproject-nix": "pyproject-nix",
|
||||
"uv2nix": "uv2nix"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1780751340,
|
||||
"narHash": "sha256-MPZ+qTH0xK0zCWefximm2rAje6Zr8outa6Sj5uP9bLw=",
|
||||
"owner": "pwndbg",
|
||||
"repo": "pwndbg",
|
||||
"rev": "0ad76cb7cc74fc80d64520596429f2fff9865c62",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "pwndbg",
|
||||
"repo": "pwndbg",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pyproject-build-systems": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"pwndbg",
|
||||
"nixpkgs"
|
||||
],
|
||||
"pyproject-nix": [
|
||||
"pwndbg",
|
||||
"pyproject-nix"
|
||||
],
|
||||
"uv2nix": [
|
||||
"pwndbg",
|
||||
"uv2nix"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1763662255,
|
||||
"narHash": "sha256-4bocaOyLa3AfiS8KrWjZQYu+IAta05u3gYZzZ6zXbT0=",
|
||||
"owner": "pyproject-nix",
|
||||
"repo": "build-system-pkgs",
|
||||
"rev": "042904167604c681a090c07eb6967b4dd4dae88c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "pyproject-nix",
|
||||
"repo": "build-system-pkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pyproject-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"pwndbg",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1769936401,
|
||||
"narHash": "sha256-kwCOegKLZJM9v/e/7cqwg1p/YjjTAukKPqmxKnAZRgA=",
|
||||
"owner": "pyproject-nix",
|
||||
"repo": "pyproject.nix",
|
||||
"rev": "b0d513eeeebed6d45b4f2e874f9afba2021f7812",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "pyproject-nix",
|
||||
"repo": "pyproject.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pwndbg": "pwndbg",
|
||||
"rust-overlays": "rust-overlays"
|
||||
}
|
||||
},
|
||||
"rust-overlays": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1760582142,
|
||||
"narHash": "sha256-RSLRjAoS75szOc9fFzRi9/jzPbYsiqPISSLZTloaKtM=",
|
||||
"lastModified": 1780456895,
|
||||
"narHash": "sha256-CvRZn3Ut0scqLJ1xwQFkZwKGVBUUNBPrFVXRTMZpbfU=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "9ea094253b9389ba7dd4f18637f66b5824276d1d",
|
||||
"rev": "7cc96a6a3fd6613cafd633250a3934483479b9a1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -89,6 +177,31 @@
|
|||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"uv2nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"pwndbg",
|
||||
"nixpkgs"
|
||||
],
|
||||
"pyproject-nix": [
|
||||
"pwndbg",
|
||||
"pyproject-nix"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1769957392,
|
||||
"narHash": "sha256-6PkqwwYf5K2CHi2V+faI/9pqjfz/HxUkI/MVid6hlOY=",
|
||||
"owner": "pyproject-nix",
|
||||
"repo": "uv2nix",
|
||||
"rev": "d18bc50ae1c3d4be9c41c2d94ea765524400af75",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "pyproject-nix",
|
||||
"repo": "uv2nix",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
|
|
|||
12
flake.nix
12
flake.nix
|
|
@ -5,12 +5,16 @@
|
|||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
rust-overlays.url = "github:oxalica/rust-overlay";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
pwndbg.url = "github:pwndbg/pwndbg";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils, rust-overlays, ...}:
|
||||
outputs = { self, nixpkgs, flake-utils, rust-overlays, pwndbg, ...}:
|
||||
flake-utils.lib.eachDefaultSystem (system: let
|
||||
overlays = [
|
||||
(import rust-overlays)
|
||||
(final: prev: {
|
||||
pwndbg = pwndbg.packages.${system}.default;
|
||||
})
|
||||
];
|
||||
pkgs = import nixpkgs {
|
||||
inherit system overlays;
|
||||
|
|
@ -31,11 +35,17 @@
|
|||
just-formatter
|
||||
just-lsp
|
||||
gdb
|
||||
gdbgui
|
||||
seer
|
||||
rr
|
||||
nasm
|
||||
nasmfmt
|
||||
git
|
||||
rust
|
||||
pkgs.pwndbg
|
||||
|
||||
radare2
|
||||
iaito
|
||||
];
|
||||
};
|
||||
});
|
||||
|
|
|
|||
65
lang/src/typeck.asm
Normal file
65
lang/src/typeck.asm
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
default rel
|
||||
%include "src/ast.inc"
|
||||
|
||||
section .text
|
||||
extern vec_init_with
|
||||
extern vec_push
|
||||
extern vec_get
|
||||
extern vec_insert_sorted
|
||||
extern vec_get_or
|
||||
extern panic
|
||||
extern memcpy
|
||||
extern strcmp
|
||||
extern vec_binary_search_by
|
||||
extern vec_insert
|
||||
|
||||
extern bump_alloc
|
||||
|
||||
;; rdi: *Ctx
|
||||
;; rsi: *Ast
|
||||
;; rdx: node
|
||||
;; rcx: scope
|
||||
;; define-fn fn typeck_for_each(ctx: *mut TypeCheckCtx, ast: *mut Ast, node_index: u64, scope: u64)
|
||||
typeck_for_each:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
; start-structs
|
||||
; struct TypeCheckCtx {
|
||||
; type_graph: TCGraph,
|
||||
; ast_to_type_map: Vec<(u64, u64)>,
|
||||
; }
|
||||
; struct TCEdge<E> {
|
||||
; nodes: [u64; 2],
|
||||
; next_edge: [u64; 2],
|
||||
; weight: E,
|
||||
; }
|
||||
; struct TCNode<N> {
|
||||
; next_edge: [u64; 2],
|
||||
; weight: N,
|
||||
; }
|
||||
; struct TCGraph {
|
||||
; types: Vec<TCNode<Type>>,
|
||||
; type_vars: Vec<TCNode<()>>,
|
||||
; edges: Vec<TCEdge<bool>>,
|
||||
; }
|
||||
; end-structs
|
||||
|
||||
; In the pre-pass, the typechecker graph is built according to the typing rules of each ast node.
|
||||
; E.g., for a binary expression, both operands must be of the same type, and the result type must be a supertype of the operand type:
|
||||
; E1(Add, E2(..), E3(..))
|
||||
; E1: T1, E2: T2, E3: T3
|
||||
; T2 == T3, T1 >= T2, T1 >= T3
|
||||
|
||||
; In a subsequent pass, the type-variable graph is simplified and equal type nodes are merged to make traversal easier.
|
||||
; The resulting type graph should be a DAG.
|
||||
|
||||
sub rsp, 32
|
||||
mov [rsp], rsi ; Ast
|
||||
mov [rsp + 8], rdi ; TypeCheckCtx
|
||||
mov [rsp + 16], rdx ; node_index
|
||||
mov [rsp + 24], rcx ; scope
|
||||
|
||||
pop rbp
|
||||
ret
|
||||
;; } typeck_for_each
|
||||
45
stages/Makefile
Normal file
45
stages/Makefile
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
BUILD = build
|
||||
|
||||
all: $(BUILD)/stage1-hex1 $(BUILD)/stage1-elf $(BUILD)/stage0
|
||||
|
||||
stage0-hex0: $(BUILD)/stage0-hex0
|
||||
|
||||
# We need an initial compiler to convert the hex0.in file into an executable that can be used to generate the stage0 compiler.
|
||||
$(BUILD)/stage0-bootstrap: hex0/hex0.in
|
||||
for x in `sed '/^;/d;s/;.*//;s/ *//' hex0/hex0.in`; do printf \\x$$x; done > $@
|
||||
chmod +x $@
|
||||
|
||||
# We use the stage0-bootstrap compiler to generate the stage0-hex0 compiler.
|
||||
$(BUILD)/stage0-hex0: $(BUILD)/stage0-bootstrap
|
||||
$< < hex0/hex0.in > $@
|
||||
chmod +x $@
|
||||
|
||||
# Finally, we use the stage0-hex0 compiler to generate the stage0 compiler, which should be identical to the stage0-hex0 compiler.
|
||||
$(BUILD)/stage0: $(BUILD)/stage0-hex0
|
||||
$< < hex0/hex0.in > $@
|
||||
chmod +x $@
|
||||
|
||||
verify-hex0: $(BUILD)/stage0
|
||||
cmp $(BUILD)/stage0 $(BUILD)/stage0-hex0
|
||||
|
||||
$(BUILD)/stage1-elf: $(BUILD)/stage0
|
||||
$< < hex1/elf.in > $@
|
||||
chmod +x $@
|
||||
|
||||
$(BUILD)/stage1-hex1.text: $(BUILD)/stage0
|
||||
$< < hex1/hex1.in > $@
|
||||
|
||||
$(BUILD)/stage1-hex1: $(BUILD)/stage1-elf $(BUILD)/stage1-hex1.text
|
||||
$(BUILD)/stage1-elf $(BUILD)/stage1-hex1 $(BUILD)/stage1-hex1.text
|
||||
chmod +x $@
|
||||
|
||||
verify-hex1: $(BUILD)/stage1-elf $(BUILD)/stage1-hex1
|
||||
$(BUILD)/stage1-hex1 < hex1/helloworld.h1 > $(BUILD)/helloworld.text
|
||||
$(BUILD)/stage1-elf $(BUILD)/helloworld.elf $(BUILD)/helloworld.text
|
||||
chmod +x $(BUILD)/helloworld.elf
|
||||
$(BUILD)/helloworld.elf
|
||||
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD)
|
||||
mkdir -p $(BUILD)
|
||||
18
stages/as0/Makefile
Normal file
18
stages/as0/Makefile
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
test.bin: test.rs as0.o
|
||||
rustc -Clink-arg=-fuse-ld=mold -Clink-arg=as0.o --edition=2024 --test -g $< -o $@
|
||||
|
||||
as0.o: as0.asm
|
||||
nasm -g -f elf64 -o as0.o as0.asm
|
||||
|
||||
test: test.bin
|
||||
./test.bin
|
||||
|
||||
as0: as0.o
|
||||
ld -m elf_x86_64 -e _entry_as0 -o as0 as0.o
|
||||
|
||||
print_table: as0.o
|
||||
ld -m elf_x86_64 -e _entry_print_table -o print_table as0.o
|
||||
|
||||
clean:
|
||||
rm -f as0.o test.bin as0 print_table
|
||||
2723
stages/as0/as0.asm
Normal file
2723
stages/as0/as0.asm
Normal file
File diff suppressed because it is too large
Load diff
385
stages/as0/as1.asm
Normal file
385
stages/as0/as1.asm
Normal file
|
|
@ -0,0 +1,385 @@
|
|||
section .bss
|
||||
token_buf resb 0x100
|
||||
buf resb 0x100
|
||||
labels resb 0x1000
|
||||
|
||||
section .text
|
||||
extern panic_abort
|
||||
;; returns the next character from the source without advancing.
|
||||
extern peekc
|
||||
;; returns the next character from the source and advances.
|
||||
extern getc
|
||||
;; advances past the next character and peeks the next.
|
||||
extern consuming_peekc
|
||||
|
||||
;; calculates a quick hash of a byte sequence given by rdi (pointer) and rsi (length)
|
||||
fasthash:
|
||||
mov eax, 0x811c9dc5 ; FNV offset basis
|
||||
.loop:
|
||||
test rsi, rsi
|
||||
jz .done
|
||||
movzx ecx, byte [rdi] ; get next byte
|
||||
xor eax, ecx
|
||||
imul eax, 0x1000193 ; FNV prime
|
||||
inc rdi
|
||||
dec rsi
|
||||
jmp .loop
|
||||
.done:
|
||||
ret
|
||||
|
||||
;; @param lhs: (rdi, rsi)
|
||||
;; @param rhs: (rdx, rcx)
|
||||
;; @return al
|
||||
strcmp:
|
||||
cmp rcx, rsi
|
||||
cmovb rsi, rcx ; if rhs is shorter, use its length for the loop
|
||||
xor eax, eax
|
||||
.loop:
|
||||
cmp rsi, rax
|
||||
je .equal
|
||||
movzx ecx, byte [rdx + rax]
|
||||
cmp byte [rdi + rax], cl
|
||||
lea rax, [rax + 1]
|
||||
je .loop
|
||||
seta al ; al = lhs > rhs
|
||||
sbb al, 0 ; al = al - CF
|
||||
ret
|
||||
.equal:
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
memcpy:
|
||||
xor rax, rax
|
||||
.loop:
|
||||
cmp rax, rdx
|
||||
je .done
|
||||
mov al, byte [rdi + rax]
|
||||
mov byte [rsi + rax], al
|
||||
lea rax, [rax + 1]
|
||||
jmp .loop
|
||||
.done:
|
||||
ret
|
||||
|
||||
;; returns 1 if the result of `peekc()` is $sil
|
||||
;; treats all characters less than ' ' as spaces.
|
||||
is_ch:
|
||||
push rsi
|
||||
call peekc
|
||||
pop rsi
|
||||
cmp al, ' '
|
||||
setbe cl ; cl = al <= ' '
|
||||
movzx ecx, cl
|
||||
mov edx, ' '
|
||||
mul ecx, edx ; cl = (al < ' ') ? ' ' : 0
|
||||
cmp sil, ' '
|
||||
cmove ax, cx ; al = (sil < ' ') ? cl : al
|
||||
cmp al, sil
|
||||
setz al ; al = al == sil
|
||||
ret
|
||||
|
||||
;; struct packed LabelEntry {
|
||||
;; hash: u64,
|
||||
;; file_offset: Option<NonMaxU32>,
|
||||
;; }
|
||||
;; push the label pointed at by rdi with length rsi, and the current file offset onto the labels array.
|
||||
push_lbl:
|
||||
push r12
|
||||
lea r12, [rel labels]
|
||||
mov rax, dword [r12 + 0x1000 - 4] ; get current label count
|
||||
cmp rax, 341
|
||||
jge panic_abort
|
||||
lea rax, [rax + rax*2]
|
||||
shl rax, 2 ; $rax = num_labels + size_of::<label_entry>()
|
||||
add r12, rax
|
||||
call fasthash
|
||||
mov qword [r12], rax ; store hash in labels array
|
||||
mov dword [r12 + 8], -1 ; initialize file offset to -1 (invalid)
|
||||
inc dword [r12 + 0x1000 - 4] ; increment label count
|
||||
pop r12
|
||||
ret
|
||||
|
||||
;; searches for the offset of the label with the name pointed at by rdi with length rsi, and returns it in rax. If the label is not found, returns 0 in rax.
|
||||
find_lbl_ptr:
|
||||
call fasthash
|
||||
lea rdx, [r15 + 0x5010] ; rdx = pointer to start of labels array
|
||||
mov ebx, dword [r15 + 0x6000]
|
||||
.loop:
|
||||
test rbx, rbx
|
||||
jz .not_found
|
||||
dec rbx
|
||||
cmp qword [rdx], rax
|
||||
je .found
|
||||
add rdx, 12 ; move to next label entry
|
||||
jmp .loop
|
||||
.found:
|
||||
mov rax, rdx
|
||||
ret
|
||||
.not_found:
|
||||
xor rax, rax
|
||||
ret
|
||||
|
||||
push_or_find_lbl:
|
||||
call fasthash
|
||||
push rax
|
||||
mov rdi, rax
|
||||
call find_lbl_ptr
|
||||
test rax, rax
|
||||
jz .push
|
||||
add rsp, 8
|
||||
ret
|
||||
.push:
|
||||
pop rdi
|
||||
call push_lbl
|
||||
ret
|
||||
|
||||
find_lbl_offset:
|
||||
call find_lbl_ptr
|
||||
test rax, rax
|
||||
jz .not_found
|
||||
mov eax, dword [rax + 8] ; get file offset of label
|
||||
ret
|
||||
.not_found:
|
||||
xor rdi, rdi
|
||||
call panic_abort
|
||||
|
||||
set_lbl_offset:
|
||||
push rsi
|
||||
call find_lbl_ptr
|
||||
pop rsi
|
||||
mov dword [rax + 8], esi ; set file offset of label
|
||||
ret
|
||||
|
||||
is_alpha:
|
||||
movzx edi, dil
|
||||
and edi, 0x1fffdf ; ignore bit 5 (case)
|
||||
add edi, -65 ; subtract 'A'
|
||||
cmp edi, 26 ; check if in range 0-25
|
||||
setb al
|
||||
ret
|
||||
|
||||
is_digit:
|
||||
movzx edi, dil
|
||||
lea eax, [rdi - 48] ; subtract '0'
|
||||
cmp eax, 10 ; check if in range 0-9
|
||||
setb al
|
||||
ret
|
||||
|
||||
;; returns 1 if $dil is a valid identifier character (alphanumeric, '-' or '_'), and 0 otherwise.
|
||||
;; clobbers rdi, rax, rcx
|
||||
is_id_cont:
|
||||
movzx rdi, dil
|
||||
; is_digit {
|
||||
lea eax, [rdi - 48]
|
||||
cmp eax, 10
|
||||
setb al
|
||||
; } || is_alpha {
|
||||
mov ecx, edi
|
||||
and ecx, 0x1fffdf
|
||||
add ecx, -65
|
||||
cmp ecx, 26
|
||||
setb cl
|
||||
; } is_alpha
|
||||
or al, cl
|
||||
cmp edi, 45 ; || '-'
|
||||
sete cl
|
||||
or al, cl
|
||||
cmp edi, 95 ; || '_'
|
||||
sete cl
|
||||
or al, cl
|
||||
ret
|
||||
|
||||
;; converts char $dil to a digit with radix $rsi, returning it in $edx.
|
||||
;; $al is set to 1 if the char is a valid digit, and 0 otherwise.
|
||||
to_digit:
|
||||
lea eax, [rsi - 2]
|
||||
cmp eax, 35
|
||||
jae .invalid
|
||||
movzx rdi, dil
|
||||
lea edx, [rdi - 65] ; 'A' = 65
|
||||
and edx, -33 ; convert to uppercase
|
||||
add edx, 10 ; 'A' should map to 10
|
||||
lea eax, [rdi - 48] ; '0' = 48
|
||||
cmp esi, 11
|
||||
cmovb edx, eax ; if radix <= 10, then take the difference from '0'
|
||||
cmp edi, 58
|
||||
cmovb edx, eax ; or if char < '9', then take the difference from '0'
|
||||
xor eax, eax
|
||||
cmp edx, esi
|
||||
setb al ; al = edx < radix
|
||||
ret
|
||||
.invalid:
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
;; reads from $rdi and parses digits with radix $rsi until a non-digit is encountered.
|
||||
parse_digits:
|
||||
sub rsp, 24
|
||||
mov qword [rsp + 16], rsi ; save the radix
|
||||
mov qword [rsp + 8], rdi ; save the source iterator
|
||||
mov qword [rsp], 0
|
||||
.loop:
|
||||
mov rdi, qword [rsp + 8] ; restore the source iterator
|
||||
call peekc
|
||||
mov dil, al
|
||||
mov rsi, qword [rsp + 16] ; restore the radix
|
||||
call to_digit
|
||||
test al, al
|
||||
jz .done
|
||||
mov rax, qword [rsp]
|
||||
mov rsi, qword [rsp + 16] ; restore the radix
|
||||
imul rax, rsi
|
||||
add rax, rdx
|
||||
mov qword [rsp], rax
|
||||
mov rdi, qword [rsp + 8] ; restore the source iterator
|
||||
call getc
|
||||
jmp .loop
|
||||
|
||||
.done:
|
||||
mov rax, [rsp]
|
||||
add rsp, 24
|
||||
ret
|
||||
|
||||
parse_num:
|
||||
sub rsp, 24
|
||||
mov qword [rsp], 0 ; acc
|
||||
mov qword [rsp + 8], rdi ; source iterator
|
||||
mov dword [rsp + 16], 10 ; radix
|
||||
call peekc
|
||||
cmp al, `-`
|
||||
jne .skip_sign
|
||||
mov qword [rsp], -1 ; acc = -1
|
||||
call consuming_peekc
|
||||
.skip_sign:
|
||||
cmp al, `0`
|
||||
jne .skip_radix
|
||||
call consuming_peekc
|
||||
cmp al, `x`
|
||||
jne .skip_radix
|
||||
mov dword [rsp + 16], 16 ; radix = 16
|
||||
call consuming_peekc
|
||||
.skip_radix:
|
||||
mov dil, al
|
||||
call to_digit
|
||||
test al, al
|
||||
jz .done
|
||||
mov rax, qword [rsp] ; acc
|
||||
mov rsi, qword [rsp + 16] ; radix
|
||||
mov rcx, rdx
|
||||
imul rax, rsi
|
||||
add rax, rcx
|
||||
mov qword [rsp], rax ; acc = acc * radix + digit
|
||||
mov rdi, qword [rsp + 8] ; source iterator
|
||||
call consuming_peekc
|
||||
jmp .skip_radix
|
||||
.done:
|
||||
mov rax, qword [rsp] ; move the result into rax
|
||||
add rsp, 24
|
||||
ret
|
||||
|
||||
parse_label:
|
||||
push r12
|
||||
lea r12, [rel buf]
|
||||
sub rsp, 8
|
||||
mov qword [rsp], rdi ; source iterator
|
||||
call consuming_peekc
|
||||
mov dil, al
|
||||
call is_alpha
|
||||
test al, al
|
||||
jz .done
|
||||
mov byte [r12], al
|
||||
inc r12
|
||||
mov rdi, qword [rsp] ; restore source iterator
|
||||
call consuming_peekc
|
||||
mov dil, al
|
||||
call is_id_cont
|
||||
test al, al
|
||||
jz .done
|
||||
jmp .loop
|
||||
.done:
|
||||
lea rdi, [rel buf]
|
||||
mov rsi, r12
|
||||
sub rsi, rdi
|
||||
call push_or_find_lbl
|
||||
add rsp, 8
|
||||
pop r12
|
||||
ret
|
||||
|
||||
;; reads the next token into `token_buf`
|
||||
next_token:
|
||||
sub rsp, 8
|
||||
mov qword [rsp], 0
|
||||
.skip_spaces:
|
||||
mov rsi, ' '
|
||||
call is_ch
|
||||
test al, al
|
||||
jz .skipped_spaces:
|
||||
call getc
|
||||
jmp .skip_spaces
|
||||
.skipped_spaces:
|
||||
; leading [ is a memory access
|
||||
mov rsi, `[`
|
||||
call is_ch
|
||||
test al, al
|
||||
jnz .mem_access
|
||||
; leading @ is label-def
|
||||
mov rsi, '@'
|
||||
call is_ch
|
||||
test al, al
|
||||
jnz .label_def
|
||||
; leading ' is a label-ref
|
||||
mov rsi, `'`
|
||||
call is_ch
|
||||
test al, al
|
||||
jnz .label_ref
|
||||
; leading digit is a number
|
||||
call peekc
|
||||
mov rdi, rax
|
||||
call is_digit
|
||||
test al, al
|
||||
jnz .number
|
||||
; leading ; or # is a comment
|
||||
mov rsi, `;`
|
||||
call is_ch
|
||||
test al, al
|
||||
jnz .comment
|
||||
mov rsi, `#`
|
||||
call is_ch
|
||||
test al, al
|
||||
jnz .comment
|
||||
; leading , is a separator
|
||||
mov rsi, `,`
|
||||
call is_ch
|
||||
test al, al
|
||||
jz .ident
|
||||
; just skip the comma since its not important for parsing
|
||||
call getc
|
||||
jmp .skip_spaces
|
||||
.ident:
|
||||
; now we have some identifier
|
||||
|
||||
; (mov rax (byte rdi))
|
||||
|
||||
|
||||
|
||||
;; registers:
|
||||
;; r0/rax
|
||||
;; r1/rcx
|
||||
;; r2/rdx
|
||||
;; r3/rbx
|
||||
;; r4/rsp
|
||||
;; r5/rbp
|
||||
;; r6/rsi
|
||||
;; r7/rdi
|
||||
;; r8
|
||||
;; r9
|
||||
;; r10
|
||||
;; r11
|
||||
;; r12
|
||||
;; r13
|
||||
;; r14
|
||||
;; r15
|
||||
;;
|
||||
;; registers of the form `rNUM` may have an optional suffix b, w, d, or q
|
||||
;; legacy registers may also be referenced by their 8-bit, 16-bit, or 32-bit names (e.g. al, ax, eax, rax or spl, sp, esp, rsp)
|
||||
;; high-byte registers (ah, bh, ch, dh) are not supported
|
||||
|
||||
415
stages/as0/test.rs
Normal file
415
stages/as0/test.rs
Normal file
|
|
@ -0,0 +1,415 @@
|
|||
unsafe extern "C" {
|
||||
// unsafe fn parse_gpr(src: *mut Source, prefix: u8) -> (u64, u64);
|
||||
// unsafe fn parse_reg(src: *mut Source) -> (u64, u64);
|
||||
|
||||
unsafe fn try_parse_reg(_: *const u8) -> (usize, Register);
|
||||
unsafe fn try_parse_mem(_: *const u8, _: *mut Operand) -> (usize, *const Operand);
|
||||
#[link_name = "try_parse_operand"]
|
||||
unsafe fn try_parse_operand_impl(_: *const u8, _: *mut Operand) -> (usize, *const Operand);
|
||||
|
||||
#[link_name = "try_parse_inst"]
|
||||
unsafe fn try_parse_inst_impl(_: *const u8, _: *mut Instruction)
|
||||
-> (usize, *const Instruction);
|
||||
|
||||
#[link_name = "encode_inst"]
|
||||
unsafe fn encode_inst_impl(_: *mut Instruction) -> usize;
|
||||
|
||||
unsafe fn init_label_tables();
|
||||
|
||||
unsafe fn fasthash(_: *const u8, _: usize) -> u32;
|
||||
|
||||
#[link_name = "buf"]
|
||||
static mut BUF: [u8; 0x100];
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct Register {
|
||||
size: u32,
|
||||
num: u32,
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum OperandKind {
|
||||
None = 0,
|
||||
Reg = 1,
|
||||
Mem = 2,
|
||||
Imm = 4,
|
||||
MemLabel = 2 | 8,
|
||||
ImmLabel = 4 | 8,
|
||||
}
|
||||
|
||||
#[repr(C, align(8))]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct Operand {
|
||||
kind: OperandKind,
|
||||
size: u32,
|
||||
reg: Register,
|
||||
index: Register,
|
||||
scale: u32,
|
||||
disp: i32,
|
||||
}
|
||||
|
||||
impl Operand {
|
||||
fn parse(bytes: &[u8]) -> Option<(usize, Self)> {
|
||||
let mut op = core::mem::MaybeUninit::<Operand>::uninit();
|
||||
let (n, ptr) = unsafe { try_parse_operand_impl(bytes.as_ptr(), op.as_mut_ptr()) };
|
||||
if n == 0 {
|
||||
None
|
||||
} else {
|
||||
Some((n, unsafe { op.assume_init() }))
|
||||
}
|
||||
}
|
||||
|
||||
fn label(name: &[u8]) -> Self {
|
||||
let mut this = Self {
|
||||
kind: OperandKind::ImmLabel,
|
||||
size: 2,
|
||||
reg: Register::invalid(),
|
||||
index: Register::invalid(),
|
||||
scale: 0,
|
||||
disp: 0,
|
||||
};
|
||||
unsafe {
|
||||
let hash = fasthash(name.as_ptr(), name.len());
|
||||
(&raw mut this).byte_add(8).cast::<u32>().write(hash as u32);
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
fn imm(value: i64) -> Self {
|
||||
let size = if value.abs() == 0 {
|
||||
value as u32
|
||||
} else {
|
||||
unsafe {
|
||||
(value.abs() as u64)
|
||||
.checked_ilog2()
|
||||
.unwrap_unchecked()
|
||||
.saturating_sub(1)
|
||||
.checked_ilog2()
|
||||
.unwrap_or(0)
|
||||
+ 1
|
||||
}
|
||||
};
|
||||
|
||||
let mut this = Self {
|
||||
kind: OperandKind::Imm,
|
||||
size,
|
||||
reg: Register::invalid(),
|
||||
index: Register::invalid(),
|
||||
scale: 0,
|
||||
disp: 0,
|
||||
};
|
||||
unsafe {
|
||||
(&raw mut this).byte_add(8).cast::<i64>().write(value);
|
||||
}
|
||||
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct Instruction {
|
||||
mnemonic_offset: i32,
|
||||
num_operands: u32,
|
||||
operands: [Operand; 2],
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
fn parse(bytes: &[u8]) -> Option<(usize, Self)> {
|
||||
let mut inst = core::mem::MaybeUninit::<Instruction>::uninit();
|
||||
let (n, ptr) = unsafe { try_parse_inst_impl(bytes.as_ptr(), inst.as_mut_ptr()) };
|
||||
if n == 0 {
|
||||
None
|
||||
} else {
|
||||
Some((n, unsafe { inst.assume_init() }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Register {
|
||||
fn new(num: u32, size: u32) -> Self {
|
||||
Self {
|
||||
num,
|
||||
size: size.ilog2(),
|
||||
}
|
||||
}
|
||||
fn invalid() -> Self {
|
||||
Self { num: !0, size: !0 }
|
||||
}
|
||||
|
||||
fn rbp() -> Self {
|
||||
Self { num: 5, size: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
static mut TABLES_INIT: std::cell::LazyCell<()> =
|
||||
std::cell::LazyCell::new(|| unsafe { init_label_tables() });
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
fn write_to_buf(b: &[u8]) {
|
||||
assert!(b.len() <= 0x100, "Buffer overflow");
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(b.as_ptr(), (&raw mut BUF).cast(), b.len());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_inst() {
|
||||
_ = unsafe { *TABLES_INIT };
|
||||
let res = Instruction::parse(b"mov byte ['rax], 4\0");
|
||||
// assert_eq!(
|
||||
// res,
|
||||
// Some((
|
||||
// 12,
|
||||
// Instruction {
|
||||
// mnemonic_offset: 0,
|
||||
// num_operands: 2,
|
||||
// operands: [
|
||||
// Operand {
|
||||
// kind: OperandKind::Reg,
|
||||
// size: 3,
|
||||
// reg: Register::new(0, 8),
|
||||
// index: Register::invalid(),
|
||||
// scale: 0,
|
||||
// disp: 0,
|
||||
// },
|
||||
// Operand {
|
||||
// kind: OperandKind::Reg,
|
||||
// size: 3,
|
||||
// reg: Register::new(3, 8),
|
||||
// index: Register::invalid(),
|
||||
// scale: 0,
|
||||
// disp: 0,
|
||||
// },
|
||||
// ],
|
||||
// }
|
||||
// ))
|
||||
// );
|
||||
|
||||
unsafe {
|
||||
let mut inst = res.unwrap().1;
|
||||
encode_inst_impl((&raw mut inst).cast());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_mem() {
|
||||
let mut op = MaybeUninit::<Operand>::uninit();
|
||||
let (n, _) =
|
||||
unsafe { try_parse_mem(b"qword [rax + rbx * 2 + 0x10]\0".as_ptr(), op.as_mut_ptr()) };
|
||||
assert_eq!(
|
||||
(n, unsafe { op.assume_init_ref() }),
|
||||
(
|
||||
28,
|
||||
&Operand {
|
||||
kind: OperandKind::Mem,
|
||||
size: 3,
|
||||
reg: Register::new(0, 8),
|
||||
index: Register::new(3, 8),
|
||||
scale: 2,
|
||||
disp: 0x10,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
let (n, _) =
|
||||
unsafe { try_parse_mem(b"byte [r12 + r13 * 4 - 0x20]\0".as_ptr(), op.as_mut_ptr()) };
|
||||
assert_eq!(
|
||||
(n, unsafe { op.assume_init_ref() }),
|
||||
(
|
||||
27,
|
||||
&Operand {
|
||||
kind: OperandKind::Mem,
|
||||
size: 0,
|
||||
reg: Register::new(12, 8),
|
||||
index: Register::new(13, 8),
|
||||
scale: 4,
|
||||
disp: -0x20,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
let (n, _) = unsafe { try_parse_mem(b"word [r8 * 8 + 5]\0".as_ptr(), op.as_mut_ptr()) };
|
||||
assert_eq!(
|
||||
(n, unsafe { op.assume_init_ref() }),
|
||||
(
|
||||
17,
|
||||
&Operand {
|
||||
kind: OperandKind::Mem,
|
||||
size: 1,
|
||||
reg: Register::rbp(),
|
||||
index: Register::new(8, 8),
|
||||
scale: 8,
|
||||
disp: 5,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_operand() {
|
||||
let cases = [
|
||||
(
|
||||
&b"rax\0"[..],
|
||||
Operand {
|
||||
kind: OperandKind::Reg,
|
||||
size: 3,
|
||||
reg: Register::new(0, 8),
|
||||
index: Register::invalid(),
|
||||
scale: 0,
|
||||
disp: 0,
|
||||
},
|
||||
),
|
||||
(
|
||||
&b"qword [rax + rbx * 2 + 0x10]\0"[..],
|
||||
Operand {
|
||||
kind: OperandKind::Mem,
|
||||
size: 3,
|
||||
reg: Register::new(0, 8),
|
||||
index: Register::new(3, 8),
|
||||
scale: 2,
|
||||
disp: 0x10,
|
||||
},
|
||||
),
|
||||
(
|
||||
&b"byte [r12 + r13 * 4 - 0x20]\0"[..],
|
||||
Operand {
|
||||
kind: OperandKind::Mem,
|
||||
size: 0,
|
||||
reg: Register::new(12, 8),
|
||||
index: Register::new(13, 8),
|
||||
scale: 4,
|
||||
disp: -0x20,
|
||||
},
|
||||
),
|
||||
(
|
||||
&b"word [r8 * 8 + 5]\0"[..],
|
||||
Operand {
|
||||
kind: OperandKind::Mem,
|
||||
size: 1,
|
||||
reg: Register::rbp(),
|
||||
index: Register::new(8, 8),
|
||||
scale: 8,
|
||||
disp: 5,
|
||||
},
|
||||
),
|
||||
(&b"0x12345678\0"[..], Operand::imm(0x12345678)),
|
||||
(&b"-0x12345678\0"[..], Operand::imm(-0x12345678)),
|
||||
(&b"0\0"[..], Operand::imm(0)),
|
||||
(&b"-1\0"[..], Operand::imm(-1)),
|
||||
(&b"1\0"[..], Operand::imm(1)),
|
||||
(&b"255\0"[..], Operand::imm(255)),
|
||||
(&b"-255\0"[..], Operand::imm(-255)),
|
||||
(&b"'wazzaah\0"[..], Operand::label(b"wazzaah")),
|
||||
(
|
||||
&b"qword [rsp + 'hii]"[..],
|
||||
Operand {
|
||||
kind: OperandKind::MemLabel,
|
||||
size: 3,
|
||||
reg: Register::new(4, 8),
|
||||
index: Register::invalid(),
|
||||
scale: 0,
|
||||
disp: unsafe { fasthash(b"hii".as_ptr(), 3) as i32 },
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
for (text, expected) in cases {
|
||||
let (n, result) = Operand::parse(text).expect("Failed to parse operand");
|
||||
assert_eq!(
|
||||
result,
|
||||
expected,
|
||||
"Failed to parse '{}'",
|
||||
std::str::from_utf8(text).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_reg() {
|
||||
let cases = [
|
||||
(&b"rax\0"[..], (0, 8)),
|
||||
(&b"rcx\0"[..], (1, 8)),
|
||||
(&b"rdx\0"[..], (2, 8)),
|
||||
(&b"rbx\0"[..], (3, 8)),
|
||||
(&b"rsp\0"[..], (4, 8)),
|
||||
(&b"rbp\0"[..], (5, 8)),
|
||||
(&b"rsi\0"[..], (6, 8)),
|
||||
(&b"rdi\0"[..], (7, 8)),
|
||||
(&b"r8\0"[..], (8, 8)),
|
||||
(&b"r9\0"[..], (9, 8)),
|
||||
(&b"r10\0"[..], (10, 8)),
|
||||
(&b"r11\0"[..], (11, 8)),
|
||||
(&b"r12\0"[..], (12, 8)),
|
||||
(&b"r13\0"[..], (13, 8)),
|
||||
(&b"r14\0"[..], (14, 8)),
|
||||
(&b"r15\0"[..], (15, 8)),
|
||||
(&b"rip\0"[..], (!0, 8)),
|
||||
(&b"eax\0"[..], (0, 4)),
|
||||
(&b"ecx\0"[..], (1, 4)),
|
||||
(&b"edx\0"[..], (2, 4)),
|
||||
(&b"ebx\0"[..], (3, 4)),
|
||||
(&b"esp\0"[..], (4, 4)),
|
||||
(&b"ebp\0"[..], (5, 4)),
|
||||
(&b"esi\0"[..], (6, 4)),
|
||||
(&b"edi\0"[..], (7, 4)),
|
||||
(&b"r8d\0"[..], (8, 4)),
|
||||
(&b"r9d\0"[..], (9, 4)),
|
||||
(&b"r10d\0"[..], (10, 4)),
|
||||
(&b"r11d\0"[..], (11, 4)),
|
||||
(&b"r12d\0"[..], (12, 4)),
|
||||
(&b"r13d\0"[..], (13, 4)),
|
||||
(&b"r14d\0"[..], (14, 4)),
|
||||
(&b"r15d\0"[..], (15, 4)),
|
||||
(&b"r8w\0"[..], (8, 2)),
|
||||
(&b"r9w\0"[..], (9, 2)),
|
||||
(&b"r10w\0"[..], (10, 2)),
|
||||
(&b"r11w\0"[..], (11, 2)),
|
||||
(&b"r12w\0"[..], (12, 2)),
|
||||
(&b"r13w\0"[..], (13, 2)),
|
||||
(&b"r14w\0"[..], (14, 2)),
|
||||
(&b"r15w\0"[..], (15, 2)),
|
||||
(&b"r8b\0"[..], (8, 1)),
|
||||
(&b"r9b\0"[..], (9, 1)),
|
||||
(&b"r10b\0"[..], (10, 1)),
|
||||
(&b"r11b\0"[..], (11, 1)),
|
||||
(&b"r12b\0"[..], (12, 1)),
|
||||
(&b"r13b\0"[..], (13, 1)),
|
||||
(&b"r14b\0"[..], (14, 1)),
|
||||
(&b"r15b\0"[..], (15, 1)),
|
||||
(&b"ax\0"[..], (0, 2)),
|
||||
(&b"cx\0"[..], (1, 2)),
|
||||
(&b"dx\0"[..], (2, 2)),
|
||||
(&b"bx\0"[..], (3, 2)),
|
||||
(&b"sp\0"[..], (4, 2)),
|
||||
(&b"bp\0"[..], (5, 2)),
|
||||
(&b"si\0"[..], (6, 2)),
|
||||
(&b"di\0"[..], (7, 2)),
|
||||
(&b"al\0"[..], (0, 1)),
|
||||
(&b"cl\0"[..], (1, 1)),
|
||||
(&b"dl\0"[..], (2, 1)),
|
||||
(&b"bl\0"[..], (3, 1)),
|
||||
(&b"spl\0"[..], (4, 1)),
|
||||
(&b"bpl\0"[..], (5, 1)),
|
||||
(&b"sil\0"[..], (6, 1)),
|
||||
(&b"dil\0"[..], (7, 1)),
|
||||
];
|
||||
|
||||
for (text, (reg, size)) in cases {
|
||||
let (n, result) = unsafe { try_parse_reg(text.as_ptr()) };
|
||||
assert_eq!(
|
||||
(n, result),
|
||||
(text.len() - 1, Register::new(reg, size)),
|
||||
"Failed to parse '{}' n={n}",
|
||||
std::str::from_utf8(text).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
stages/as0/tests/helloworld.as0
Normal file
16
stages/as0/tests/helloworld.as0
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
@_start:
|
||||
mov rdi, 0xa21444C52
|
||||
push rdi
|
||||
mov rdi, 0x4F57204F
|
||||
shl rdi, 32
|
||||
mov rsi, 0x4C4C4548
|
||||
or rdi, rsi
|
||||
push rdi
|
||||
@init:
|
||||
lea rsi, qword [rsp]
|
||||
mov edx, 13
|
||||
mov eax, 1
|
||||
mov edi, 1
|
||||
syscall
|
||||
jmp 'init
|
||||
jmp '_start
|
||||
45
stages/as0/types.zig
Normal file
45
stages/as0/types.zig
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
const PackedOperandKind = enum(u3) {
|
||||
reg = 0,
|
||||
imm = 1,
|
||||
mem = 2,
|
||||
label = 3,
|
||||
mem_label = 4,
|
||||
|
||||
};
|
||||
|
||||
const Register = enum(u4) {
|
||||
rax = 0,
|
||||
rbx = 1,
|
||||
rcx = 2,
|
||||
rdx = 3,
|
||||
rsi = 4,
|
||||
rdi = 5,
|
||||
rsp = 6,
|
||||
rbp = 7,
|
||||
r8 = 8,
|
||||
r9 = 9,
|
||||
r10 = 10,
|
||||
r11 = 11,
|
||||
r12 = 12,
|
||||
r13 = 13,
|
||||
r14 = 14,
|
||||
r15 = 15,
|
||||
};
|
||||
|
||||
const PackedRegister = packed struct {
|
||||
reg: Register,
|
||||
size: u4,
|
||||
};
|
||||
|
||||
const PackedOperand = union(PackedOperandKind) {
|
||||
reg: PackedRegister,
|
||||
imm: i64,
|
||||
label: u32,
|
||||
mem_label: u32,
|
||||
mem: struct {
|
||||
base: PackedRegister,
|
||||
index: PackedRegister,
|
||||
scale: u2,
|
||||
disp: i32,
|
||||
},
|
||||
};
|
||||
15
stages/hex0/Makefile
Normal file
15
stages/hex0/Makefile
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
BUILD = ../build
|
||||
|
||||
all: $(BUILD)/stage0
|
||||
|
||||
stage0: $(BUILD)/stage0
|
||||
|
||||
stage0-bootstrap: $(BUILD)/stage0-bootstrap
|
||||
|
||||
$(BUILD)/stage0-bootstrap: hex0.in
|
||||
for x in `sed '/^;/d;s/;.*//;s/ *//' hex0.in`; do printf \\x$$x; done > $@
|
||||
chmod +x $@
|
||||
|
||||
$(BUILD)/stage0: $(BUILD)/stage0-bootstrap
|
||||
$< < hex0.in > $@
|
||||
chmod +x $@
|
||||
221
stages/hex0/hex0.in
Normal file
221
stages/hex0/hex0.in
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
; ELF header
|
||||
; e_ident: u128
|
||||
; Magic number: 0x7f454c46 = "\x7fELF"
|
||||
7f 45 4c 46
|
||||
; EI_CLASS: u8 = ELFCLASS64
|
||||
02
|
||||
; EI_DATA: u8 = ELFDATA2LSB
|
||||
01
|
||||
; EI_VERSION: u8 = EV_CURRENT
|
||||
01
|
||||
; EI_OSABI: u8 = ELFOSABI_NONE
|
||||
00
|
||||
; EI_ABIVERSION: u8 = 0
|
||||
00
|
||||
; EI_PAD: [u8; 7]
|
||||
00 00 00 00 00 00 00
|
||||
; e_type: u16 = E_EXEC
|
||||
02 00
|
||||
; e_machine: u16 = EM_X86_64
|
||||
3e 00
|
||||
; e_version: u32 = EV_CURRENT
|
||||
01 00 00 00
|
||||
; e_entry: u64 = 0x400078
|
||||
78 00 40 00 00 00 00 00
|
||||
; e_phoff: u64 = 0x40
|
||||
40 00 00 00 00 00 00 00
|
||||
; e_shoff: u64 = 0x0
|
||||
00 00 00 00 00 00 00 00
|
||||
; e_flags: u32 = 0
|
||||
00 00 00 00
|
||||
; e_ehsize: u16 = 64
|
||||
40 00
|
||||
; e_phentsize: u16 = 56
|
||||
38 00
|
||||
; e_phnum: u16 = 1
|
||||
01 00
|
||||
; e_shentsize: u16 = 0
|
||||
00 00
|
||||
; e_shnum: u16 = 0
|
||||
00 00
|
||||
; e_shstrndx: u16 = 0
|
||||
00 00
|
||||
; End of ELF header
|
||||
;
|
||||
; Program header
|
||||
; p_type: u32 = PT_LOAD
|
||||
01 00 00 00
|
||||
; p_flags: u32 = PF_R | PF_X
|
||||
05 00 00 00
|
||||
; p_offset: u64 = 0
|
||||
00 00 00 00 00 00 00 00
|
||||
; p_vaddr: u64 = 0x400000
|
||||
00 00 40 00 00 00 00 00
|
||||
; p_paddr: u64 = 0x0
|
||||
00 00 00 00 00 00 00 00
|
||||
; p_filesz: u64 = 0x167
|
||||
67 01 00 00 00 00 00 00
|
||||
; p_memsz: u64 = 0x167
|
||||
67 01 00 00 00 00 00 00
|
||||
; p_align: u64 = 0x1000
|
||||
00 10 00 00 00 00 00 00
|
||||
; End of program header
|
||||
;
|
||||
; Code segment (offset 0x40, virtual address 0x400000)
|
||||
;
|
||||
; _start=120:
|
||||
; ; alloca 0x1000 bytes on the stack
|
||||
; mov rbp, rsp
|
||||
48 89 e5
|
||||
; sub rsp, 0x10000, increase this if you want to read more than 0x10000 bytes
|
||||
48 81 ec 00 00 01 00
|
||||
; ; read 0x10000 bytes from stdin into the stack
|
||||
; .READ: (130)
|
||||
; xor rax, rax
|
||||
48 31 c0
|
||||
; mov edi, 0 (stdin)
|
||||
48 c7 c7 00 00 00 00
|
||||
; mov rsi, rsp
|
||||
48 89 e6
|
||||
; mov rdx, 0x10000, increase this if you want to read more than 0x10000 bytes
|
||||
48 c7 c2 00 00 01 00
|
||||
; syscall (sys_read)
|
||||
0f 05
|
||||
; cmp rax, 0
|
||||
48 83 f8 00
|
||||
; jle die
|
||||
0f 8e b3 00 00 00
|
||||
;# (162)
|
||||
; ; end = r8
|
||||
; mov r8, rax
|
||||
49 89 c0
|
||||
; add r8, rsp
|
||||
49 01 e0
|
||||
; ; pos = r9
|
||||
; mov r9, rsp
|
||||
49 89 e1
|
||||
; ; dst = r10
|
||||
; mov r10, rsp
|
||||
49 89 e2
|
||||
; add r10, 0x0000
|
||||
49 81 c2 00 00 00 00
|
||||
; ; while (pos < end) {
|
||||
; ; .L0: (181)
|
||||
; cmp r9, r8
|
||||
4d 39 c1
|
||||
; jge .L3
|
||||
0f 8d 72 00 00 00
|
||||
; (190)
|
||||
;
|
||||
; ; if (*pos == ';') {
|
||||
; mov al , byte [r9]
|
||||
41 8a 41 00
|
||||
; cmp al, '\n'
|
||||
3c 0a
|
||||
; je .L2.1:
|
||||
0f 84 15 00 00 00
|
||||
; (202)
|
||||
; cmp al, ';'
|
||||
3c 3b
|
||||
; jne .L1
|
||||
0f 85 15 00 00 00
|
||||
; ; while (*pos != '\n') pos++;
|
||||
; ; .L2: (210)
|
||||
; inc r9
|
||||
49 ff c1
|
||||
; cmp byte [r9], '\n'
|
||||
41 80 39 0a
|
||||
; jne .L2
|
||||
0f 85 f3 ff ff ff
|
||||
; .L2.1: (223)
|
||||
; inc r9
|
||||
49 ff c1
|
||||
; jmp .L0
|
||||
e9 ce ff ff ff
|
||||
; (231)
|
||||
; ; .L1: (231)
|
||||
; ; convert *pos to high word
|
||||
; mov al, byte [r9]
|
||||
41 8a 01
|
||||
; mov bl, al
|
||||
88 c3
|
||||
; sub bl, 'a'
|
||||
80 eb 61
|
||||
; add bl, 10
|
||||
80 c3 0a
|
||||
; sub al, '0'
|
||||
2c 30
|
||||
; cmp al, 10
|
||||
3c 0a
|
||||
; cmovge eax, ebx
|
||||
0f 43 c3
|
||||
; shl al, 4
|
||||
c0 e0 04
|
||||
; ; pos++
|
||||
; inc r9
|
||||
49 ff c1
|
||||
; ; convert *pos to low word
|
||||
; mov bl, byte [r9]
|
||||
41 8a 19
|
||||
; mov cl, bl
|
||||
88 d9
|
||||
; sub cl, 'a'
|
||||
80 e9 61
|
||||
; add cl, 10
|
||||
80 c1 0a
|
||||
; sub bl, '0'
|
||||
80 eb 30
|
||||
; cmp bl, 10
|
||||
80 fb 0a
|
||||
; cmovge ebx, ecx
|
||||
0f 4d d9
|
||||
; ; combine high and low word
|
||||
; or al, bl
|
||||
08 d8
|
||||
; ; write the byte to dst
|
||||
; mov byte [r10], al
|
||||
41 88 02
|
||||
; inc r10
|
||||
49 ff c2
|
||||
; inc r9
|
||||
49 ff c1
|
||||
; ; if (*pos++ == '\n') goto .L1;
|
||||
; cmp byte [r9], '\n'
|
||||
41 80 39 0a
|
||||
; je .L0
|
||||
0f 84 8d ff ff ff
|
||||
;# (296)
|
||||
; inc r9
|
||||
49 ff c1
|
||||
; jmp .L1 ;
|
||||
e9 b7 ff ff ff
|
||||
;# (304)
|
||||
; ; }
|
||||
; .L3: (304)
|
||||
; ; write the decoded bytes to stdout
|
||||
; mov rax, 1 (sys_write)
|
||||
48 c7 c0 01 00 00 00
|
||||
; mov rdi, 1 (stdout)
|
||||
48 c7 c7 01 00 00 00
|
||||
; mov rsi, rsp
|
||||
48 89 e6
|
||||
; add rsi, 0x000
|
||||
48 81 c6 00 00 00 00
|
||||
; mov rdx, r10
|
||||
4c 89 d2
|
||||
; sub rdx, rsi
|
||||
48 29 f2
|
||||
; syscall
|
||||
0f 05
|
||||
; jmp .READ ;
|
||||
e9 2d ff ff ff
|
||||
;# (341)
|
||||
;
|
||||
; die: (341)
|
||||
; mov rax, 60 (sys_exit)
|
||||
48 c7 c0 3c 00 00 00
|
||||
; xor rdi, rdi
|
||||
48 31 ff
|
||||
; syscall
|
||||
0f 05
|
||||
; _start+215
|
||||
150
stages/hex1/elf.asm
Normal file
150
stages/hex1/elf.asm
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
;; Program that takes an output file to which it writes elf64 headers and the contents of a second file which is assumed to be a text section.
|
||||
|
||||
die:
|
||||
mov rax, 60 ; syscall: exit
|
||||
mov rdi, 0 ; status: 0
|
||||
syscall
|
||||
ret
|
||||
|
||||
fopen:
|
||||
mov rax, 2 ; syscall: open
|
||||
mov rsi, 0 ; flags: O_RDONLY
|
||||
mov rdx, 0 ; mode
|
||||
syscall
|
||||
ret
|
||||
|
||||
fopen_write:
|
||||
mov rax, 2 ; syscall: open
|
||||
mov rsi, 0x241 ; flags: O_WRONLY | O_CREAT | O_TRUNC
|
||||
mov rdx, 0o644 ; mode: rw-r--r--
|
||||
syscall
|
||||
ret
|
||||
|
||||
;; rdi: fd
|
||||
;; rsi: buffer
|
||||
fstat:
|
||||
mov rax, 5 ; syscall: fstat
|
||||
syscall
|
||||
ret
|
||||
|
||||
fsize:
|
||||
push rdi
|
||||
push rsi
|
||||
call fstat
|
||||
pop rsi
|
||||
pop rdi
|
||||
mov rax, [rsi + 48] ; st_size
|
||||
ret
|
||||
|
||||
;; rdi: src
|
||||
;; rsi: dst
|
||||
;; rdx: len
|
||||
memcpy:
|
||||
.memcpy_loop:
|
||||
test rdx, rdx
|
||||
jz .memcpy_done
|
||||
mov al, byte [rdi]
|
||||
mov byte [rsi], al
|
||||
inc rsi
|
||||
inc rdi
|
||||
dec rdx
|
||||
jmp .memcpy_loop
|
||||
|
||||
.memcpy_done:
|
||||
ret
|
||||
|
||||
;; variables:
|
||||
;; +0 buffer: [u8; 0x1000]
|
||||
;; +0x1000 bytes_read: u32
|
||||
;; +0x1004 in_size: u32
|
||||
;; +0x1008 in_fd: u32
|
||||
;; +0x100c out_fd: u32
|
||||
;; +0x1010 argc: u32
|
||||
;; +0x1018 argv: *const *const u8
|
||||
|
||||
global _start
|
||||
_start:
|
||||
mov eax, dword [rsp] ; argc
|
||||
lea rbx, [rsp + 8] ; argv
|
||||
sub rsp, 0x4000 ; reserve stack space
|
||||
mov r15, rsp ; r15 = pointer to our state
|
||||
mov dword [r15 + 0x1010], eax ; store argc
|
||||
mov qword [r15 + 0x1018], rbx ; store argv
|
||||
cmp eax, 3
|
||||
jl die
|
||||
|
||||
;; open output file
|
||||
mov rdi, [r15 + 0x1018] ; rdi = argv
|
||||
add rdi, 8 ; rdi = argv[1]
|
||||
mov rdi, [rdi] ; rdi = pointer to argv[1]
|
||||
call fopen_write
|
||||
mov dword [r15 + 0x100c], eax ; store out_fd
|
||||
|
||||
;; open input file and get size
|
||||
mov rdi, [r15 + 0x1018] ; rdi = argv
|
||||
add rdi, 16 ; rdi = argv[2]
|
||||
mov rdi, [rdi] ; rdi = pointer to argv[2]
|
||||
call fopen
|
||||
mov dword [r15 + 0x1008], eax ; store in_fd
|
||||
mov edi, eax ; rdi = in_fd
|
||||
lea rsi, [r15] ; rsi = pointer to statbuf
|
||||
call fsize
|
||||
mov dword [r15 + 0x1004], eax ; store in_size
|
||||
|
||||
;; copy elf header to scratch space
|
||||
mov rdi, 0x400000
|
||||
mov rsi, r15
|
||||
mov rdx, 0x40
|
||||
call memcpy
|
||||
|
||||
;; copy program header to scratch space
|
||||
mov rdi, 0x400040
|
||||
lea rsi, [r15 + 0x40]
|
||||
mov rdx, 0x38
|
||||
call memcpy
|
||||
|
||||
;; write p_filesz and p_memsz to 0x78 + in_size
|
||||
mov rax, 0x78
|
||||
add eax, dword [r15 + 0x1004] ; rax = 0x78 + in_size
|
||||
mov dword [r15 + 0x60], eax ; write p_filesz
|
||||
mov dword [r15 + 0x68], eax ; write p_memsz
|
||||
|
||||
;; write e_entry to point at 0x40 + 0x38 + in_size - 5
|
||||
sub rax, 5
|
||||
add rax, 0x400000
|
||||
mov dword [r15 + 24], eax ; write e_entry
|
||||
|
||||
;; write Ehdr and Phdr to output file
|
||||
mov edi, dword [r15 + 0x100c] ; rdi = out_fd
|
||||
mov rsi, r15 ; rsi = buffer
|
||||
mov rdx, 0x78 ; size of Ehdr + Phdr
|
||||
mov rax, 1 ; syscall: write
|
||||
syscall
|
||||
|
||||
;; copy text section from input file to output file
|
||||
.copy_loop:
|
||||
mov edi, dword [r15 + 0x1008] ; rdi = in_fd
|
||||
mov rsi, r15 ; rsi = pointer to buffer
|
||||
mov rdx, 0x1000 ; rdx = size of buffer
|
||||
mov rax, 0 ; syscall: read
|
||||
syscall
|
||||
test rax, rax
|
||||
jle die
|
||||
mov dword [r15 + 0x1000], eax ; store number of bytes read
|
||||
mov edi, dword [r15 + 0x100c] ; rdi = out_fd
|
||||
mov rsi, r15 ; rsi = pointer to buffer
|
||||
mov edx, dword [r15 + 0x1000] ; rdx = number of bytes read
|
||||
mov rax, 1 ; syscall: write
|
||||
syscall
|
||||
cmp dword [r15 + 0x1000], 0x1000 ; if we read less than 0x1000 bytes, we are done
|
||||
je .copy_loop
|
||||
call die
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
269
stages/hex1/elf.in
Normal file
269
stages/hex1/elf.in
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
; ELF header
|
||||
; e_ident: u128
|
||||
; Magic number: 0x7f454c46 = "\x7fELF"
|
||||
7f 45 4c 46
|
||||
; EI_CLASS: u8 = ELFCLASS64
|
||||
02
|
||||
; EI_DATA: u8 = ELFDATA2LSB
|
||||
01
|
||||
; EI_VERSION: u8 = EV_CURRENT
|
||||
01
|
||||
; EI_OSABI: u8 = ELFOSABI_NONE
|
||||
00
|
||||
; EI_ABIVERSION: u8 = 0
|
||||
00
|
||||
; EI_PAD: [u8; 7]
|
||||
00 00 00 00 00 00 00
|
||||
; e_type: u16 = E_EXEC
|
||||
02 00
|
||||
; e_machine: u16 = EM_X86_64
|
||||
3e 00
|
||||
; e_version: u32 = EV_CURRENT
|
||||
01 00 00 00
|
||||
; e_entry: u64 = 0x400078 + 0x5c
|
||||
d4 00 40 00 00 00 00 00
|
||||
; e_phoff: u64 = 0x40
|
||||
40 00 00 00 00 00 00 00
|
||||
; e_shoff: u64 = 0x0
|
||||
00 00 00 00 00 00 00 00
|
||||
; e_flags: u32 = 0
|
||||
00 00 00 00
|
||||
; e_ehsize: u16 = 64
|
||||
40 00
|
||||
; e_phentsize: u16 = 56
|
||||
38 00
|
||||
; e_phnum: u16 = 1
|
||||
01 00
|
||||
; e_shentsize: u16 = 0
|
||||
00 00
|
||||
; e_shnum: u16 = 0
|
||||
00 00
|
||||
; e_shstrndx: u16 = 0
|
||||
00 00
|
||||
; End of ELF header
|
||||
;
|
||||
; Program header
|
||||
; p_type: u32 = PT_LOAD
|
||||
01 00 00 00
|
||||
; p_flags: u32 = PF_R | PF_X
|
||||
05 00 00 00
|
||||
; p_offset: u64 = 0
|
||||
00 00 00 00 00 00 00 00
|
||||
; p_vaddr: u64 = 0x400000
|
||||
00 00 40 00 00 00 00 00
|
||||
; p_paddr: u64 = 0x0
|
||||
00 00 00 00 00 00 00 00
|
||||
; p_filesz: u64 = 0x167
|
||||
ef 01 00 00 00 00 00 00
|
||||
; p_memsz: u64 = 0x167
|
||||
ef 01 00 00 00 00 00 00
|
||||
; p_align: u64 = 0x1000
|
||||
00 10 00 00 00 00 00 00
|
||||
; End of program header
|
||||
;
|
||||
; @die: +0
|
||||
; mov rax, 60 (exit)
|
||||
b8 3c 00 00 00
|
||||
; mov rdi, 0 (exit code)
|
||||
bf 00 00 00 00
|
||||
; syscall
|
||||
0f 05
|
||||
; ret
|
||||
c3
|
||||
;
|
||||
; @fopen:
|
||||
; mov rax, 2 (sys_open)
|
||||
b8 02 00 00 00
|
||||
; mov rsi, 0
|
||||
be 00 00 00 00
|
||||
; mov rdx, 0
|
||||
ba 00 00 00 00
|
||||
; syscall
|
||||
0f 05
|
||||
; ret
|
||||
c3
|
||||
;
|
||||
; fopen_write:
|
||||
; mov rax, 2 (sys_open)
|
||||
b8 02 00 00 00
|
||||
; mov rsi, 0x241 (O_WRONLY | O_CREAT | O_TRUNC)
|
||||
be 41 02 00 00
|
||||
; mov rdx, 0o644 (file mode)
|
||||
ba a4 01 00 00
|
||||
; syscall
|
||||
0f 05
|
||||
; ret
|
||||
c3
|
||||
;
|
||||
; @fstat:
|
||||
; mov rax, 5 (sys_fstat)
|
||||
b8 05 00 00 00
|
||||
; syscall
|
||||
0f 05
|
||||
; ret
|
||||
c3
|
||||
;
|
||||
; @fsize:
|
||||
; push rdi
|
||||
57
|
||||
; push rsi
|
||||
56
|
||||
; call 'fstat
|
||||
e8 f1 ff ff ff
|
||||
; pop rsi
|
||||
5e
|
||||
; pop rdi
|
||||
5f
|
||||
; mov rax, [rsi+48] (st_size)
|
||||
48 8b 46 30
|
||||
; ret
|
||||
c3
|
||||
;
|
||||
; @memcpy:
|
||||
; test rdx, rdx
|
||||
48 85 d2
|
||||
; jz 'memcpy_exit
|
||||
74 0f
|
||||
; mov al, byte [rdi]
|
||||
8a 07
|
||||
; mov byte [rsi], al
|
||||
88 06
|
||||
; inc rsi
|
||||
48 ff c6
|
||||
; inc rdi
|
||||
48 ff c7
|
||||
; dec rdx
|
||||
48 ff ca
|
||||
; jmp 'memcpy
|
||||
eb ec
|
||||
; @memcpy_exit:
|
||||
c3
|
||||
;
|
||||
; @_start:
|
||||
; mov eax, dword [rsp] (argc)
|
||||
8b 04 24
|
||||
; lea rbx, [rsp+8] (argv)
|
||||
48 8d 5c 24 08
|
||||
; sub rsp, 0x4000
|
||||
48 81 ec 00 40 00 00
|
||||
; mov r15, rsp
|
||||
49 89 e7
|
||||
; mov dword [r15+0x1010], eax (store argc)
|
||||
41 89 87 10 10 00 00
|
||||
; mov qword [r15+0x1018], rbx (store argv)
|
||||
49 89 9f 18 10 00 00
|
||||
; cmp eax, 3
|
||||
83 f8 03
|
||||
; jl 'die
|
||||
0f 8c 7b ff ff ff
|
||||
;
|
||||
; mov rdi, qword [r15+0x1018] (argv)
|
||||
49 8b bf 18 10 00 00
|
||||
; add rdi, 8 (argv[1])
|
||||
48 83 c7 08
|
||||
; mov rdi, [rdi] (argv[1])
|
||||
48 8b 3f
|
||||
; call 'fopen_write
|
||||
e8 87 ff ff ff
|
||||
;
|
||||
; mov dword [r15+0x101c], eax (store file descriptor)
|
||||
41 89 87 0c 10 00 00
|
||||
; mov rdi, qword [r15+0x1018] (argv)
|
||||
49 8b bf 18 10 00 00
|
||||
; add rdi, 16 (argv[2])
|
||||
48 83 c7 10
|
||||
; mov rdi, [rdi] (argv[2])
|
||||
48 8b 3f
|
||||
; call 'fopen
|
||||
e8 5b ff ff ff
|
||||
;
|
||||
; mov dword [r15+0x1008], eax (store file descriptor)
|
||||
41 89 87 08 10 00 00
|
||||
; mov edi, eax (fd)
|
||||
89 c7
|
||||
; lea rsi, [r15]
|
||||
49 8d 37
|
||||
; call 'fsize
|
||||
e8 76 ff ff ff
|
||||
;
|
||||
; mov dword [r15+0x1004], eax (store file size)
|
||||
41 89 87 04 10 00 00
|
||||
; mov edi, 0x400000
|
||||
bf 00 00 40 00
|
||||
; mov rsi, r15
|
||||
4c 89 fe
|
||||
; mov edx, 0x40 (size)
|
||||
ba 40 00 00 00
|
||||
; call 'memcpy
|
||||
e8 6b ff ff ff
|
||||
;
|
||||
; mov edi, 0x400040
|
||||
bf 40 00 40 00
|
||||
; lea rsi, [r15+0x40]
|
||||
49 8d 77 40
|
||||
; mov edx, 0x38 (size)
|
||||
ba 38 00 00 00
|
||||
; call 'memcpy
|
||||
e8 58 ff ff ff
|
||||
;
|
||||
; mov eax, 0x78
|
||||
b8 78 00 00 00
|
||||
; add eax, dword [r15+0x1004] (file size)
|
||||
41 03 87 04 10 00 00
|
||||
; mov dword [r15+0x60], eax (store file end)
|
||||
41 89 47 60
|
||||
; mov dword [r15+0x68], eax (store file end)
|
||||
41 89 47 68
|
||||
;
|
||||
; sub rax, 5
|
||||
48 83 e8 05
|
||||
; add rax, 0x400000
|
||||
48 05 00 00 40 00
|
||||
; mov dword [r15+0x18], eax (store file end - 5)
|
||||
41 89 47 18
|
||||
;
|
||||
; mov edi, dword [r15+0x100c] (fd)
|
||||
41 8b bf 0c 10 00 00
|
||||
; mov rsi, r15
|
||||
4c 89 fe
|
||||
; mov edx, 0x78 (size)
|
||||
ba 78 00 00 00
|
||||
; mov eax, 1 (sys_write)
|
||||
b8 01 00 00 00
|
||||
; syscall
|
||||
0f 05
|
||||
; @write_loop:
|
||||
; mov edi, dword [r15+0x1008] (fd)
|
||||
41 8b bf 08 10 00 00
|
||||
; mov rsi, r15
|
||||
4c 89 fe
|
||||
; mov edx, 0x1000 (size)
|
||||
ba 00 10 00 00
|
||||
; mov eax, 0 (sys_read)
|
||||
b8 00 00 00 00
|
||||
; syscall
|
||||
0f 05
|
||||
; test rax, rax
|
||||
48 85 c0
|
||||
; jle 'die
|
||||
0f 8e ba fe ff ff
|
||||
;
|
||||
; mov dword [r15+0x1000], eax (store bytes read)
|
||||
41 89 87 00 10 00 00
|
||||
; mov edi, dword [r15+0x100c] (fd)
|
||||
41 8b bf 0c 10 00 00
|
||||
; mov rsi, r15
|
||||
4c 89 fe
|
||||
; mov edx, dword [r15+0x1000] (bytes read)
|
||||
41 8b 97 00 10 00 00
|
||||
; mov eax, 1 (sys_write)
|
||||
b8 01 00 00 00
|
||||
; syscall
|
||||
0f 05
|
||||
; cmp dword [r15+0x1000], 0x1000
|
||||
41 81 bf 00 10 00 00 00 10 00 00
|
||||
; jz 'write_loop
|
||||
74 b5
|
||||
; call 'die
|
||||
e8 89 fe ff ff
|
||||
; end
|
||||
22
stages/hex1/helloworld.h1
Normal file
22
stages/hex1/helloworld.h1
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
; Code segment
|
||||
;
|
||||
; _start
|
||||
@hello_world:
|
||||
68 65 6c 6c 6f 20 77 6f 72 6c 64 21
|
||||
@_start:
|
||||
; mov rax, 1
|
||||
48 c7 c0 01 00 00 00
|
||||
; mov rdi, 1
|
||||
48 c7 c7 01 00 00 00
|
||||
; mov rsi, [@hello_world]
|
||||
48 8d 35 'hello_world
|
||||
; mov rdx, 12
|
||||
48 c7 c2 0c 00 00 00
|
||||
; syscall
|
||||
0f 05
|
||||
; mov rax, 60 (sys_exit)
|
||||
48 c7 c0 3c 00 00 00
|
||||
; xor rdi, rdi
|
||||
48 31 ff
|
||||
; syscall
|
||||
0f 05
|
||||
319
stages/hex1/hex1.asm
Normal file
319
stages/hex1/hex1.asm
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
;; This is the 2nd stage of the hex compiler.
|
||||
;; It still requires hex input, but it can automatically calculate rip-relative offsets to labels
|
||||
|
||||
;; we need the following variables:
|
||||
;; +0 = buf: [u8; 0x1000]
|
||||
;; +0x1000 = write_buf: [u8; 0x4000]
|
||||
;; +0x5000 = read_cursor: u32
|
||||
;; +0x5004 write_cursor: u32
|
||||
;; +0x5008 read_end: u32
|
||||
;; +0x500c file_offset: u32 (the offset of the current file in the overall input, used for calculating label offsets)
|
||||
;; +0x5010 labels: [(u64, u32); 340]
|
||||
;; +0x6000 num_labels: u32
|
||||
;; +0x6004 scratch space
|
||||
|
||||
section .data
|
||||
_start_lbl db "_start", 0
|
||||
|
||||
section .text
|
||||
;; exits the program
|
||||
panic:
|
||||
mov rax, 60 ; syscall: exit
|
||||
mov rdi, 1 ; status: 1
|
||||
syscall
|
||||
|
||||
;; exits the program
|
||||
die:
|
||||
mov rax, 60 ; syscall: exit
|
||||
mov rdi, 0 ; status: 0
|
||||
syscall
|
||||
|
||||
;; calculates a quick hash of a byte sequence given by rdi (pointer) and rsi (length)
|
||||
fasthash:
|
||||
mov eax, 0x811c9dc5 ; FNV offset basis
|
||||
.fashhash_loop:
|
||||
test rsi, rsi
|
||||
jz .fashhash_exit
|
||||
movzx ecx, byte [rdi] ; get next byte
|
||||
xor eax, ecx
|
||||
imul eax, 0x1000193 ; FNV prime
|
||||
inc rdi
|
||||
dec rsi
|
||||
jmp .fashhash_loop
|
||||
|
||||
.fashhash_exit:
|
||||
ret
|
||||
|
||||
;; push the label pointed at by rdi with length rsi, and the current file offset onto the labels array.
|
||||
push_lbl:
|
||||
mov eax, dword [r15 + 0x6000]
|
||||
cmp eax, 340
|
||||
jge panic
|
||||
lea edx, [rax + rax*2]
|
||||
shl rdx, 2
|
||||
push rdx
|
||||
call fasthash
|
||||
pop rdx ; rdx = offset of label entry in labels array
|
||||
lea rdi, [r15 + 0x5010]
|
||||
mov qword [rdi + rdx], rax ; store hash in labels array
|
||||
mov eax, dword [r15 + 0x500c] ; get current file offset
|
||||
mov dword [rdi + rdx + 8], eax ; store file offset in labels array
|
||||
inc dword [r15 + 0x6000]
|
||||
ret
|
||||
|
||||
;; searches for the offset of the label with the name pointed at by rdi with length rsi, and returns it in rax. If the label is not found, dies.
|
||||
find_lbl:
|
||||
call fasthash
|
||||
lea rdx, [r15 + 0x5010] ; rdx = pointer to start of labels array
|
||||
mov ebx, dword [r15 + 0x6000]
|
||||
.find_lbl_loop:
|
||||
test rbx, rbx
|
||||
jz .find_lbl_not_found
|
||||
dec rbx
|
||||
cmp qword [rdx], rax
|
||||
je .find_lbl_found
|
||||
add rdx, 12 ; move to next label entry
|
||||
jmp .find_lbl_loop
|
||||
|
||||
.find_lbl_not_found:
|
||||
call panic
|
||||
.find_lbl_found:
|
||||
mov eax, dword [rdx + 8] ; get file offset of label
|
||||
ret
|
||||
|
||||
;; write up to write_cursor to stdout, and then reset the write cursor to 0
|
||||
wrt_stdout:
|
||||
mov rax, 1 ; syscall: write
|
||||
mov rdi, 1 ; fd: stdout
|
||||
lea rsi, [r15 + 0x1000] ; buf: pointer to write_buf
|
||||
mov edx, dword [r15 + 0x5004] ; count: write_cursor
|
||||
syscall
|
||||
mov dword [r15 + 0x5004], 0
|
||||
ret
|
||||
|
||||
die_jmp_start:
|
||||
;; @_start_lbl: 5F 73 74 61 72 74 00 00
|
||||
;; lea rdi, [@_start_lbl]
|
||||
mov rdi, _start_lbl
|
||||
mov rsi, 6
|
||||
call find_lbl
|
||||
add dword [r15 + 0x500c], 5 ; advance the file offset by 5
|
||||
sub eax, dword [r15 + 0x500c] ; add current file offset
|
||||
mov edi, dword [r15 + 0x5004] ; get current write cursor
|
||||
lea rdi, [r15 + rdi + 0x1000] ; rdi = pointer to write location in write_buf
|
||||
mov byte [rdi], 0xe9 ; write the opcode for jmp rel32
|
||||
mov dword [rdi + 1], eax ; write the offset of the label reference
|
||||
add dword [r15 + 0x5004], 5 ; advance the write cursor by 5
|
||||
call wrt_stdout
|
||||
call die
|
||||
|
||||
;; reads up to 0x1000 bytes from stdin into buf, and sets read_end to the number of bytes read, and resets read_cursor to 0
|
||||
;; dies if no bytes were read (if it returns, there were bytes read)
|
||||
rd_stdin:
|
||||
mov rax, 0 ; syscall: read
|
||||
mov rdi, 0 ; fd: stdin
|
||||
mov rsi, r15
|
||||
mov rdx, 0x1000 ; count: size of buffer
|
||||
syscall
|
||||
test eax, eax
|
||||
jle die_jmp_start
|
||||
mov dword [r15 + 0x5008], eax ; store number of bytes read in read_end
|
||||
mov dword [r15 + 0x5000], 0 ; reset read_cursor to 0
|
||||
ret
|
||||
|
||||
;; return the next byte from the input
|
||||
;; automatically writes to stdout and reads from stdin if we are at the end of the buffer
|
||||
rd_buf:
|
||||
mov eax, dword [r15 + 0x5000] ; get read_cursor
|
||||
cmp eax, dword [r15 + 0x5008] ; compare to read_end
|
||||
jl .rd_buf_end
|
||||
call wrt_stdout
|
||||
call rd_stdin
|
||||
jmp .rd_buf_end
|
||||
|
||||
.rd_buf_end:
|
||||
mov edx, dword [r15 + 0x5000] ; get read_cursor
|
||||
xor rax, rax
|
||||
mov al, byte [r15 + rdx] ; get next byte from buf
|
||||
inc dword [r15 + 0x5000] ; advance read_cursor
|
||||
ret
|
||||
|
||||
;; The grammar is slightly updated from hex0, as follows:
|
||||
;; program = (label-def / instruction-seq / comment)*
|
||||
;; comment = ";" [^\n]* "\n"
|
||||
;; label-def = "@" label-name ":" comment?
|
||||
;; instruction-seq = instruction+ comment?
|
||||
;; instruction = label-ref / opcode
|
||||
;; label-ref = "'" label-name
|
||||
;; opcode = hex hex
|
||||
;; label-name = [a-zA-Z0-9_-]+
|
||||
;; hex = [0-9a-f][0-9a-f]
|
||||
;;
|
||||
;; whitespaces may occur between any tokens and are ignored.
|
||||
;; whitespace = [ \t\n]+
|
||||
|
||||
|
||||
skip_line:
|
||||
cmp al, `\n`
|
||||
jne .skip_line_read
|
||||
call rd_buf
|
||||
ret
|
||||
|
||||
.skip_line_read:
|
||||
call rd_buf
|
||||
jmp skip_line
|
||||
|
||||
skip_whitespaces:
|
||||
cmp al, ' '
|
||||
je .skip_whitespace_read
|
||||
cmp al, `\t`
|
||||
je .skip_whitespace_read
|
||||
cmp al, `\n`
|
||||
je .skip_whitespace_read
|
||||
ret
|
||||
|
||||
.skip_whitespace_read:
|
||||
call rd_buf
|
||||
jmp skip_whitespaces
|
||||
|
||||
skip_comment:
|
||||
cmp al, ";"
|
||||
jne .skip_comment_ret
|
||||
call skip_line
|
||||
ret
|
||||
.skip_comment_ret:
|
||||
ret
|
||||
|
||||
get_next_token:
|
||||
cmp al, ";"
|
||||
je .get_next_token_skip
|
||||
cmp al, ' '
|
||||
je .get_next_token_skip
|
||||
cmp al, `\t`
|
||||
je .get_next_token_skip
|
||||
cmp al, `\n`
|
||||
je .get_next_token_skip
|
||||
ret
|
||||
|
||||
.get_next_token_skip:
|
||||
call skip_whitespaces
|
||||
call skip_comment
|
||||
jmp get_next_token
|
||||
|
||||
char_to_u4:
|
||||
cmp al, '0'
|
||||
jb .invalid_hex
|
||||
cmp al, '9'
|
||||
jbe .is_digit
|
||||
cmp al, 'a'
|
||||
jb .invalid_hex
|
||||
cmp al, 'f'
|
||||
jbe .is_lower_hex
|
||||
cmp al, 'A'
|
||||
jb .invalid_hex
|
||||
cmp al, 'F'
|
||||
jbe .is_upper_hex
|
||||
jmp .invalid_hex
|
||||
|
||||
.invalid_hex:
|
||||
call panic
|
||||
.is_digit:
|
||||
sub al, '0'
|
||||
ret
|
||||
.is_lower_hex:
|
||||
sub al, 'a' - 10
|
||||
ret
|
||||
.is_upper_hex:
|
||||
sub al, 'A' - 10
|
||||
ret
|
||||
|
||||
octet:
|
||||
sub rsp, 8
|
||||
call char_to_u4
|
||||
mov byte [rsp], al
|
||||
call rd_buf
|
||||
call char_to_u4
|
||||
mov bl, byte [rsp]
|
||||
shl bl, 4
|
||||
or al, bl
|
||||
mov edi, dword [r15 + 0x5004] ; get current write cursor
|
||||
lea rdx, [r15 + 0x1000]
|
||||
mov byte [rdi + rdx], al ; write the byte to the write_buf
|
||||
inc dword [r15 + 0x5004] ; advance the write cursor by 1
|
||||
inc dword [r15 + 0x500c] ; advance file offset by 1
|
||||
add rsp, 8
|
||||
ret
|
||||
|
||||
label_ref:
|
||||
cmp al, "'"
|
||||
jne .not_label_ref
|
||||
xor rsi, rsi ; rsi = length of label
|
||||
.label_ref_loop:
|
||||
push rsi
|
||||
call rd_buf
|
||||
pop rsi
|
||||
lea rdi, [r15 + 0x6004] ; rdi = pointer to scratch
|
||||
cmp al, ' '
|
||||
je .label_ref_ret
|
||||
cmp al, `\t`
|
||||
je .label_ref_ret
|
||||
cmp al, `\n`
|
||||
je .label_ref_ret
|
||||
mov byte [rdi + rsi], al
|
||||
inc rsi
|
||||
jmp .label_ref_loop
|
||||
|
||||
.label_ref_ret:
|
||||
lea rdi, [r15 + 0x6004]
|
||||
call find_lbl
|
||||
add dword [r15 + 0x500c], 4 ; advance the file offset by 4
|
||||
sub eax, dword [r15 + 0x500c] ; add current file offset
|
||||
mov edi, dword [r15 + 0x5004] ; get current write cursor
|
||||
mov dword [r15 + 0x1000 + rdi], eax ; write the opcode for jmp rel32
|
||||
add dword [r15 + 0x5004], 4 ; advance the write cursor by 4
|
||||
ret
|
||||
.not_label_ref:
|
||||
call octet
|
||||
ret
|
||||
|
||||
;; read the label into scratch, call push_lbl
|
||||
label:
|
||||
cmp al, '@'
|
||||
jne .not_label
|
||||
xor rsi, rsi ; rsi = length of label
|
||||
.label_loop:
|
||||
push rsi
|
||||
call rd_buf
|
||||
pop rsi
|
||||
lea rdi, [r15 + 0x6004] ; rdi = pointer to scratch
|
||||
cmp al, ':'
|
||||
je .label_ret
|
||||
mov byte [rdi + rsi], al
|
||||
inc rsi
|
||||
jmp .label_loop
|
||||
|
||||
.label_ret:
|
||||
lea rdi, [r15 + 0x6004] ; rdi = pointer to scratch
|
||||
call push_lbl
|
||||
ret
|
||||
.not_label:
|
||||
call label_ref
|
||||
ret
|
||||
|
||||
global _start
|
||||
_start:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
sub rsp, 0x8000 ; allocate space for our variables + some scratch space
|
||||
mov r15, rsp ; r15 will point to the start of our variables
|
||||
mov dword [r15 + 0x6000], 0 ; num_labels = 0
|
||||
mov dword [r15 + 0x500c], 0 ; file_offset = 0
|
||||
mov dword [r15 + 0x5008], 0 ; read_end = 0
|
||||
mov dword [r15 + 0x5004], 0 ; write_cursor = 0
|
||||
mov dword [r15 + 0x5000], 0 ; read_cursor = 0
|
||||
|
||||
.loop:
|
||||
call rd_buf
|
||||
call get_next_token
|
||||
call label
|
||||
jmp .loop
|
||||
468
stages/hex1/hex1.in
Normal file
468
stages/hex1/hex1.in
Normal file
|
|
@ -0,0 +1,468 @@
|
|||
; @panic: +0
|
||||
; mov rax, 60 (exit)
|
||||
b8 3c 00 00 00
|
||||
; mov rdi, 1 (exit code)
|
||||
bf 01 00 00 00
|
||||
; syscall
|
||||
0f 05
|
||||
;
|
||||
; @die: +12
|
||||
; mov rax, 60 (exit)
|
||||
b8 3c 00 00 00
|
||||
; mov rdi, 0 (exit
|
||||
bf 00 00 00 00
|
||||
; syscall
|
||||
0f 05
|
||||
;
|
||||
; @fasthash_exit: +24
|
||||
; ret
|
||||
c3
|
||||
; @fasthash: +25
|
||||
; mov rax, 0x811c9dc5 (FNV offset basis)
|
||||
b8 c5 9d 1c 81
|
||||
; @fasthash_loop: +30
|
||||
; test rsi, rsi
|
||||
48 85 f6
|
||||
; jz 'fasthash_exit (+39)
|
||||
0f 84 f1 ff ff ff
|
||||
; movzx ecx, byte [rdi]
|
||||
0f b6 0f
|
||||
; xor eax, ecx
|
||||
31 c8
|
||||
; imul eax, eax, 0x1000193 (FNV prime)
|
||||
69 c0 93 01 00 01
|
||||
; inc rdi
|
||||
48 ff c7
|
||||
; dec rsi
|
||||
48 ff ce
|
||||
; jmp 'fasthash_loop (+61)
|
||||
e9 e1 ff ff ff
|
||||
;
|
||||
; @push_lbl: +61
|
||||
; mov eax, [r15+0x6000]
|
||||
41 8b 87 00 60 00 00
|
||||
; cmp eax, 0x154
|
||||
3d 54 01 00 00
|
||||
; jge 'panic (+79)
|
||||
0f 8d b1 ff ff ff
|
||||
; lea edx, [rax + rax*2]
|
||||
8d 14 40
|
||||
; shl rdx, 2
|
||||
48 c1 e2 02
|
||||
; push rdx
|
||||
52
|
||||
; call 'fasthash (+92)
|
||||
e8 bd ff ff ff
|
||||
; pop rdx
|
||||
5a
|
||||
; lea rdi, [r15+0x5010]
|
||||
49 8d bf 10 50 00 00
|
||||
; mov qword [rdi+rdx], rax
|
||||
48 89 04 17
|
||||
; mov eax, dword [r15+0x500c]
|
||||
41 8b 87 0c 50 00 00
|
||||
; mov dword [rdi+rdx+8], eax
|
||||
89 44 17 08
|
||||
; inc dword [r15+0x6000]
|
||||
41 ff 87 00 60 00 00
|
||||
; ret
|
||||
c3
|
||||
;
|
||||
; @find_lbl_not_found: (+123)
|
||||
; call 'panic (+128)
|
||||
e8 80 ff ff ff
|
||||
; @find_lbl_found: (+128)
|
||||
; mov eax, dword [rdx+8]
|
||||
8b 42 08
|
||||
; ret
|
||||
c3
|
||||
; @find_lbl: +132
|
||||
; call 'fasthash (+137)
|
||||
e8 90 ff ff ff
|
||||
; lea rdx, [r15+0x5010]
|
||||
49 8d 97 10 50 00 00
|
||||
; mov edx, dword [r15+0x6000]
|
||||
41 8b 9f 00 60 00 00
|
||||
; @find_lbl_loop: (+151)
|
||||
; test rbx, rbx
|
||||
48 85 db
|
||||
; jz 'find_lbl_not_found (+160)
|
||||
0f 84 db ff ff ff
|
||||
; dec rbx
|
||||
48 ff cb
|
||||
; cmp [rdx], rax
|
||||
48 39 02
|
||||
; jz 'find_lbl_found (+172)
|
||||
0f 84 d4 ff ff ff
|
||||
; add rdx, 0xc
|
||||
48 83 c2 0c
|
||||
; jmp 'find_lbl_loop (+181)
|
||||
e9 e2 ff ff ff
|
||||
;
|
||||
; @wrt_stdout: (+181)
|
||||
; mov rax, 1 (sys_write)
|
||||
b8 01 00 00 00
|
||||
; mov rdi, 1 (stdout)
|
||||
bf 01 00 00 00
|
||||
; lea rsi, [r15+0x1000]
|
||||
49 8d b7 00 10 00 00
|
||||
; mov edx, dword [r15+0x5004]
|
||||
41 8b 97 04 50 00 00
|
||||
; syscall
|
||||
0f 05
|
||||
; mov dword [r15+0x5004], 0
|
||||
41 c7 87 04 50 00 00 00 00 00 00
|
||||
; ret
|
||||
c3
|
||||
;
|
||||
; @_start_lbl: (+219)
|
||||
5f 73 74 61 72 74 00
|
||||
; @die_jmp_start: (+226)
|
||||
; lea rdi, [rip+@_start_lbl] (+233)
|
||||
48 8d 3d f2 ff ff ff
|
||||
; mov esi, 6
|
||||
be 06 00 00 00
|
||||
; call 'find_lbl (+243)
|
||||
e8 91 ff ff ff
|
||||
; add dword [r15+0x500c], 5
|
||||
41 83 87 0c 50 00 00 05
|
||||
; sub eax, dword [r15+0x500c]
|
||||
41 2b 87 0c 50 00 00
|
||||
; mov edi, dword [r15+0x5004]
|
||||
41 8b bf 04 50 00 00
|
||||
; lea rdi, [r15+rdi*1+0x1000]
|
||||
49 8d bc 3f 00 10 00 00
|
||||
; mov byte [rdi], 0xe9
|
||||
c6 07 e9
|
||||
; mov dword [rdi+1], eax
|
||||
89 47 01
|
||||
; add dword [r15+0x5004], 5
|
||||
41 83 87 04 50 00 00 05
|
||||
; call 'wrt_stdout (+292)
|
||||
e8 91 ff ff ff
|
||||
; call 'die (+297)
|
||||
e8 e3 fe ff ff
|
||||
;
|
||||
; @rd_stdin: (+297)
|
||||
; mov rax, 0 (sys_read)
|
||||
b8 00 00 00 00
|
||||
; mov rdi, 0 (stdin)
|
||||
bf 00 00 00 00
|
||||
; mov rsi, r15
|
||||
4c 89 fe
|
||||
; mov rdx, 0x1000
|
||||
ba 00 10 00 00
|
||||
; syscall
|
||||
0f 05
|
||||
; test rax, rax
|
||||
85 c0
|
||||
; jle 'die_jmp_start (+325)
|
||||
0f 8e 9d ff ff ff
|
||||
; mov dword [r15+0x5008], eax
|
||||
41 89 87 08 50 00 00
|
||||
; mov dword [r15+0x5000], 0
|
||||
41 c7 87 00 50 00 00 00 00 00 00
|
||||
; ret
|
||||
c3
|
||||
;
|
||||
; @rd_buf_end: (+344)
|
||||
; mov edx, dword [r15+0x5000]
|
||||
41 8b 97 00 50 00 00
|
||||
; xor rax, rax
|
||||
48 31 c0
|
||||
; mov al, byte [rdi+rdx]
|
||||
41 8a 04 17
|
||||
; inc dword [r15+0x5000]
|
||||
41 ff 87 00 50 00 00
|
||||
; ret
|
||||
c3
|
||||
; @rd_buf: (+366)
|
||||
; mov eax, dword [r15+0x5000]
|
||||
41 8b 87 00 50 00 00
|
||||
; cmp eax, dword [r15+0x5008]
|
||||
41 3b 87 08 50 00 00
|
||||
; jl 'rd_buf_end (+386)
|
||||
0f 8c d6 ff ff ff
|
||||
; call 'wrt_stdout (+391)
|
||||
e8 2e ff ff ff
|
||||
; call 'rd_stdin (+396)
|
||||
e8 9d ff ff ff
|
||||
; jmp 'rd_buf_end (+401)
|
||||
e9 c7 ff ff ff
|
||||
;
|
||||
;
|
||||
; @skip_line_read: (+401)
|
||||
; call 'rd_buf (+406)
|
||||
e8 d8 ff ff ff
|
||||
; @skip_line: (+406)
|
||||
; cmp al, 0xa (newline)
|
||||
3c 0a
|
||||
; jnz 'skip_line_read (+414)
|
||||
0f 85 f3 ff ff ff
|
||||
; call 'rd_buf (+419)
|
||||
e8 cb ff ff ff
|
||||
; ret
|
||||
c3
|
||||
;
|
||||
; @skip_whitespace_read: (+420)
|
||||
; call 'rd_buf (+425)
|
||||
e8 c5 ff ff ff
|
||||
; @skip_whitespace: (+425)
|
||||
; cmp al, 0x20 (space)
|
||||
3c 20
|
||||
; jz 'skip_whitespace_read (+433)
|
||||
0f 84 f3 ff ff ff
|
||||
; cmp al, 0xa (newline)
|
||||
3c 0a
|
||||
; jz 'skip_whitespace_read (+441)
|
||||
0f 84 eb ff ff ff
|
||||
; cmp al, 0x9 (tab)
|
||||
3c 09
|
||||
; jz 'skip_whitespace_read (+449)
|
||||
0f 84 e3 ff ff ff
|
||||
; ret
|
||||
c3
|
||||
;
|
||||
; @skip_comment_ret: (+450)
|
||||
; ret
|
||||
c3
|
||||
; @skip_comment: (+451)
|
||||
; cmp al, 0x3b (semicolon)
|
||||
3c 3b
|
||||
; jnz 'skip_comment_ret (+459)
|
||||
0f 85 f7 ff ff ff
|
||||
; call 'skip_line (+464)
|
||||
e8 c6 ff ff ff
|
||||
; ret
|
||||
c3
|
||||
;
|
||||
; @get_next_token_skip: (+465)
|
||||
; call 'skip_whitespace (+470)
|
||||
e8 d3 ff ff ff
|
||||
; call 'skip_comment (+475)
|
||||
e8 e8 ff ff ff
|
||||
; @get_next_token: (+475)
|
||||
; cmp al, 0x3b (semicolon)
|
||||
3c 3b
|
||||
; jz 'get_next_token_skip (+483)
|
||||
0f 84 ee ff ff ff
|
||||
; cmp al, 0xa (newline)
|
||||
3c 0a
|
||||
; jz 'get_next_token_skip (+491)
|
||||
0f 84 e6 ff ff ff
|
||||
; cmp al, 0x20 (space)
|
||||
3c 20
|
||||
; jz 'get_next_token_skip (+499)
|
||||
0f 84 de ff ff ff
|
||||
; cmp al, 0x9 (tab)
|
||||
3c 09
|
||||
; jz 'get_next_token_skip (+507)
|
||||
0f 84 d6 ff ff ff
|
||||
; ret
|
||||
c3
|
||||
;
|
||||
; @invalid_hex: (+508)
|
||||
; call 'panic (+513)
|
||||
e8 ff fd ff ff
|
||||
; @is_digit: (+513)
|
||||
; sub al, 0x30 (digit '0')
|
||||
2c 30
|
||||
; ret
|
||||
c3
|
||||
; @is_low_alpha: (+516)
|
||||
; sub al, 0x57 (letter 'a' - 10)
|
||||
2c 57
|
||||
; ret
|
||||
c3
|
||||
; @is_up_alpha: (+519)
|
||||
; sub al, 0x37 (letter 'A' - 10)
|
||||
2c 37
|
||||
; ret
|
||||
c3
|
||||
; @char_to_u4: (+522)
|
||||
; cmp al, 0x30 (digit '0')
|
||||
3c 30
|
||||
; jb 'invalid_hex (+530)
|
||||
0f 82 ea ff ff ff
|
||||
; cmp al, 0x39 (digit '9')
|
||||
3c 39
|
||||
; jbe 'is_digit (+538)
|
||||
0f 86 e7 ff ff ff
|
||||
; cmp al, 0x41 (letter 'A')
|
||||
3c 41
|
||||
; jb 'invalid_hex (+546)
|
||||
0f 82 da ff ff ff
|
||||
; cmp al, 0x46 (letter 'F')
|
||||
3c 46
|
||||
; jbe 'is_up_alpha (+554)
|
||||
0f 86 dd ff ff ff
|
||||
; cmp al, 0x61 (letter 'a')
|
||||
3c 61
|
||||
; jb 'invalid_hex (+562)
|
||||
0f 82 ca ff ff ff
|
||||
; cmp al, 0x66 (letter 'f')
|
||||
3c 66
|
||||
; jbe 'is_low_alpha (+570)
|
||||
0f 86 ca ff ff ff
|
||||
; jmp 'invalid_hex (+575)
|
||||
e9 bd ff ff ff
|
||||
;
|
||||
; @octet: (+575)
|
||||
; sub rsp, 8
|
||||
48 83 ec 08
|
||||
; call 'char_to_u4 (+584)
|
||||
e8 ca ff ff ff
|
||||
; mov byte [rsp], al
|
||||
88 04 24
|
||||
; call 'rd_buf (+592)
|
||||
e8 1e ff ff ff
|
||||
; call 'char_to_u4 (+597)
|
||||
e8 b5 ff ff ff
|
||||
; mov bl, byte [rsp]
|
||||
8a 1c 24
|
||||
; shl bl, 4
|
||||
c0 e3 04
|
||||
; or al, bl
|
||||
08 d8
|
||||
; mov edi, dword [r15+0x5004]
|
||||
41 8b bf 04 50 00 00
|
||||
; lea rdx, [r15+0x1000]
|
||||
49 8d 97 00 10 00 00
|
||||
; mov byte [rdi+rdx], al
|
||||
88 04 17
|
||||
; inc dword [r15+0x5004]
|
||||
41 ff 87 04 50 00 00
|
||||
; inc dword [r15+0x500c]
|
||||
41 ff 87 0c 50 00 00
|
||||
; add rsp, 8
|
||||
48 83 c4 08
|
||||
; ret
|
||||
c3
|
||||
;
|
||||
; @label_ref_ret: (+641)
|
||||
; lea rdi, [r15+0x6004]
|
||||
49 8d bf 04 60 00 00
|
||||
; call 'find_lbl (+653)
|
||||
e8 f7 fd ff ff
|
||||
; add dword [r15+0x500c], 4
|
||||
41 83 87 0c 50 00 00 04
|
||||
; sub eax, dword [r15+0x500c]
|
||||
41 2b 87 0c 50 00 00
|
||||
; mov edi, dword [r15+0x5004]
|
||||
41 8b bf 04 50 00 00
|
||||
; mov dword [r15+rdi*1+0x1000], eax
|
||||
41 89 84 3f 00 10 00 00
|
||||
; add dword [r15+0x5004], 4
|
||||
41 83 87 04 50 00 00 04
|
||||
; ret
|
||||
c3
|
||||
; @not_label_ref: (+692)
|
||||
; call 'octet (+697)
|
||||
e8 86 ff ff ff
|
||||
; ret
|
||||
c3
|
||||
; @label_ref: (+698)
|
||||
; cmp al, 0x27 (single quote)
|
||||
3c 27
|
||||
; jnz 'not_label_ref (+706)
|
||||
0f 85 f2 ff ff ff
|
||||
; xor rsi, rsi
|
||||
48 31 f6
|
||||
; @label_ref_loop: (+709)
|
||||
; push rsi
|
||||
56
|
||||
; call 'rd_buf (+715)
|
||||
e8 a3 fe ff ff
|
||||
; pop rsi
|
||||
5e
|
||||
; lea rdi, [r15+0x6004]
|
||||
49 8d bf 04 60 00 00
|
||||
; cmp al, 0x20 (space)
|
||||
3c 20
|
||||
; jz 'label_ref_ret (+731)
|
||||
0f 84 a6 ff ff ff
|
||||
; cmp al, 0x9 (tab)
|
||||
3c 09
|
||||
; jz 'label_ref_ret (+739)
|
||||
0f 84 9e ff ff ff
|
||||
; cmp al, 0xa (newline)
|
||||
3c 0a
|
||||
; jz 'label_ref_ret (+747)
|
||||
0f 84 96 ff ff ff
|
||||
; mov byte [rdi+rsi*1], al
|
||||
88 04 37
|
||||
; inc rsi
|
||||
48 ff c6
|
||||
; jmp 'label_ref_loop (+758)
|
||||
e9 cf ff ff ff
|
||||
;
|
||||
; @label_ret: (+758)
|
||||
; lea rdi, [r15+0x6004]
|
||||
49 8d bf 04 60 00 00
|
||||
; call 'push_lbl (+770)
|
||||
e8 3b fd ff ff
|
||||
; ret
|
||||
c3
|
||||
; @not_label: (+771)
|
||||
; call 'label_ref (+776)
|
||||
e8 b2 ff ff ff
|
||||
; ret
|
||||
c3
|
||||
; @label: (+777)
|
||||
; cmp al, 0x40 (at sign)
|
||||
3c 40
|
||||
; jnz 'not_label (+785)
|
||||
0f 85 f2 ff ff ff
|
||||
; xor rsi, rsi
|
||||
48 31 f6
|
||||
; @label_loop: (+788)
|
||||
; push rsi
|
||||
56
|
||||
; call 'rd_buf (+794)
|
||||
e8 54 fe ff ff
|
||||
; pop rsi
|
||||
5e
|
||||
; lea rdi, [r15+0x6004]
|
||||
49 8d bf 04 60 00 00
|
||||
; cmp al, 0x3a (colon)
|
||||
3c 3a
|
||||
; jz 'label_ret (+810)
|
||||
0f 84 cc ff ff ff
|
||||
; mov byte [rdi+rsi*1], al
|
||||
88 04 37
|
||||
; inc rsi
|
||||
48 ff c6
|
||||
; jmp 'label_loop (+821)
|
||||
e9 df ff ff ff
|
||||
;
|
||||
; @_start: (+821)
|
||||
; push rbp
|
||||
55
|
||||
; mov rbp, rsp
|
||||
48 89 e5
|
||||
; sub rsp, 0x8000
|
||||
48 81 ec 00 80 00 00
|
||||
; mov r15, rsp
|
||||
49 89 e7
|
||||
; mov dword [r15+0x6000], 0
|
||||
41 c7 87 00 60 00 00 00 00 00 00
|
||||
; mov dword [r15+0x500c], 0
|
||||
41 c7 87 0c 50 00 00 00 00 00 00
|
||||
; mov dword [r15+0x5008], 0
|
||||
41 c7 87 08 50 00 00 00 00 00 00
|
||||
; mov dword [r15+0x5004], 0
|
||||
41 c7 87 04 50 00 00 00 00 00 00
|
||||
; mov dword [r15+0x5000], 0
|
||||
41 c7 87 00 50 00 00 00 00 00 00
|
||||
; @main_loop:
|
||||
; call 'rd_buf (+895)
|
||||
e8 ef fd ff ff
|
||||
; call 'get_next_token (+900)
|
||||
e8 57 fe ff ff
|
||||
; call 'label (+905)
|
||||
e8 80 ff ff ff
|
||||
; jmp 'main_loop (+910)
|
||||
e9 ec ff ff ff
|
||||
; jmp 'main
|
||||
e9 a2 ff ff ff
|
||||
; end
|
||||
23
stages/lisp0/Makefile
Normal file
23
stages/lisp0/Makefile
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# test.bin: lisp.rs lisp.o
|
||||
# rustc -Clink-arg=-fuse-ld=mold -Clink-arg=lisp.o --edition=2024 --test -g $< -o $@
|
||||
|
||||
# lisp.o: lisp.asm
|
||||
# nasm -g -f elf64 -o lisp.o lisp.asm
|
||||
|
||||
# test: test.bin
|
||||
# ./test.bin
|
||||
|
||||
test.bin: test.rs lisp1.o
|
||||
rustc -Clink-arg=-fuse-ld=mold -Clink-arg=lisp1.o --edition=2024 --test -g $< -o $@
|
||||
|
||||
lisp1.o: lisp1.asm
|
||||
nasm -g -f elf64 -o lisp1.o lisp1.asm
|
||||
|
||||
test: test.bin
|
||||
./test.bin --nocapture --test-threads=1
|
||||
|
||||
lisp1: lisp1.o
|
||||
ld -m elf_x86_64 -e _interp_entry -o lisp1 lisp1.o
|
||||
|
||||
clean:
|
||||
rm -f lisp.o test.bin lisp1.o test1.bin lisp1
|
||||
2115
stages/lisp0/lisp.asm
Normal file
2115
stages/lisp0/lisp.asm
Normal file
File diff suppressed because it is too large
Load diff
56
stages/lisp0/lisp.in
Normal file
56
stages/lisp0/lisp.in
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
;; Atom String Table
|
||||
; t
|
||||
116
|
||||
; quote
|
||||
113 117 111 116 101
|
||||
; +
|
||||
43
|
||||
; -
|
||||
45
|
||||
; *
|
||||
42
|
||||
; /
|
||||
47
|
||||
; %
|
||||
37
|
||||
; <
|
||||
60
|
||||
; =
|
||||
61
|
||||
; car
|
||||
99 97 114
|
||||
; cdr
|
||||
99 100 114
|
||||
; not
|
||||
100 111 116
|
||||
; lambda
|
||||
108 97 109 98 100 97
|
||||
;define
|
||||
100 101 102 105 100 101
|
||||
; eval
|
||||
101 118 97 108
|
||||
; if
|
||||
105 102
|
||||
; let
|
||||
108 101 116
|
||||
; let*
|
||||
108 101 116 42
|
||||
; str-len
|
||||
115 116 114 45 108 101 100
|
||||
; list-len
|
||||
108 105 115 116 45 108 101 100
|
||||
; str-parts
|
||||
115 116 114 45 112 97 114 116 115
|
||||
; syscall
|
||||
115 121 115 99 97 108 108
|
||||
; print-env
|
||||
112 114 105 100 116 45 101 100 118
|
||||
; \Space
|
||||
92 83 112 97 99 101
|
||||
; \NL
|
||||
92 78 76
|
||||
; \Tab
|
||||
92 84 97 98
|
||||
;; Atom Table
|
||||
; ref-count| length | pointer to string |
|
||||
01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00
|
||||
190
stages/lisp0/lisp.rs
Normal file
190
stages/lisp0/lisp.rs
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
unsafe extern "C" {
|
||||
fn init_atoms();
|
||||
fn parse_next_token(src: *mut Source<'_>) -> Object;
|
||||
fn init_env() -> Object;
|
||||
fn eval(expr: Object, env: Object) -> Object;
|
||||
fn get_env() -> Object;
|
||||
|
||||
#[link_name = "nil"]
|
||||
static NIL: ();
|
||||
#[link_name = "env"]
|
||||
static mut GENV: ();
|
||||
}
|
||||
|
||||
struct Source<'a> {
|
||||
iter: core::iter::Peekable<core::slice::Iter<'a, u8>>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [u8]> for Source<'a> {
|
||||
fn from(value: &'a [u8]) -> Self {
|
||||
Self {
|
||||
iter: value.iter().peekable(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn peekc(src: *mut Source) -> u8 {
|
||||
unsafe { (*src).iter.peek().cloned().unwrap_or(&0).clone() }
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn getc(src: *mut Source) -> u8 {
|
||||
unsafe { (*src).iter.next().unwrap_or(&0).clone() }
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn alloc(size: usize, align: usize) -> *mut u8 {
|
||||
let layout = core::alloc::Layout::from_size_align(size, align).unwrap();
|
||||
unsafe { std::alloc::alloc(layout) }
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn dealloc(ptr: *mut u8, size: usize, align: usize) {
|
||||
let layout = core::alloc::Layout::from_size_align(size, align).unwrap();
|
||||
unsafe { std::alloc::dealloc(ptr, layout) }
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn panic_abort(cstr: *const u8) -> ! {
|
||||
if cstr.is_null() {
|
||||
panic!("abort");
|
||||
} else {
|
||||
let mut len = 0;
|
||||
while unsafe { *cstr.add(len) } != 0 {
|
||||
len += 1;
|
||||
}
|
||||
let slice = unsafe { core::slice::from_raw_parts(cstr, len) };
|
||||
let message = core::str::from_utf8(slice).unwrap_or("abort");
|
||||
panic!("{}", message);
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn print(obj: Object) {
|
||||
println!("{:?}", obj);
|
||||
}
|
||||
|
||||
static mut ENV_INIT: std::cell::LazyCell<Object> =
|
||||
std::cell::LazyCell::new(|| unsafe { init_env() });
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
struct Object(*mut ());
|
||||
|
||||
// impl Clone for Object {
|
||||
// fn clone(&self) -> Self {
|
||||
// unsafe {
|
||||
// *self.ptr().cast::<u32>().as_mut().unwrap() += 1;
|
||||
// }
|
||||
// Self(self.0)
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl Drop for Object {
|
||||
// fn drop(&mut self) {
|
||||
// unsafe {
|
||||
// *self.ptr().cast::<u32>().as_mut().unwrap() -= 1;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
use std::fmt;
|
||||
impl fmt::Debug for Object {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.0 as *const () == &raw const NIL {
|
||||
return write!(f, "nil");
|
||||
}
|
||||
match self.tag() {
|
||||
0 => write!(f, "'{}'", (self.0.addr() >> 8) as u8 as char),
|
||||
1 => unsafe {
|
||||
let car = self.ptr().byte_add(8).cast::<Object>().read();
|
||||
let cdr = self.ptr().byte_add(16).cast::<Object>().read();
|
||||
write!(f, "({car:?} . {cdr:?})")
|
||||
},
|
||||
2 => unsafe {
|
||||
let car = self.ptr().byte_add(8).cast::<Object>().read();
|
||||
let env = self.ptr().byte_add(16).cast::<Object>().read();
|
||||
assert_eq!(
|
||||
car.tag(),
|
||||
1,
|
||||
"Expected a pair for lambda cdr, got tag {}",
|
||||
car.tag()
|
||||
);
|
||||
let params = car.ptr().byte_add(8).cast::<Object>().read();
|
||||
let body = car.ptr().byte_add(16).cast::<Object>().read();
|
||||
|
||||
write!(f, "λ ")?;
|
||||
if env.0 as *const () == &raw const GENV || env.0 as *const () == &raw const NIL {
|
||||
write!(f, " #<g_env> ++ ")?;
|
||||
} else {
|
||||
write!(f, " {env:?} ++ ")?;
|
||||
}
|
||||
write!(f, "{params:?} {body:?})")
|
||||
},
|
||||
3 => unsafe {
|
||||
let str_ptr = self.ptr().byte_add(8).cast::<*const u8>().read();
|
||||
let len = self.ptr().byte_add(4).cast::<u32>().read() as usize;
|
||||
let slice = core::slice::from_raw_parts(str_ptr, len);
|
||||
let string = core::str::from_utf8(slice).unwrap_or("<invalid utf-8>");
|
||||
write!(f, "'{string}")
|
||||
},
|
||||
6 => unsafe {
|
||||
let str_ptr = self.ptr().byte_add(8).cast::<*const u8>().read();
|
||||
let len = self.ptr().byte_add(4).cast::<u32>().read() as usize;
|
||||
let slice = core::slice::from_raw_parts(str_ptr, len);
|
||||
let string = core::str::from_utf8(slice).unwrap_or("<invalid utf-8>");
|
||||
write!(f, "\"{string}\"")
|
||||
},
|
||||
4 => unsafe {
|
||||
let num = self.ptr().byte_add(8).cast::<u64>().read();
|
||||
write!(f, "{num}")
|
||||
},
|
||||
5 => unsafe { write!(f, "#<prim>") },
|
||||
_ => write!(f, "#<unknown tag {}>", self.tag()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Object {
|
||||
fn tag(&self) -> u8 {
|
||||
self.0.addr() as u8 & 0b111
|
||||
}
|
||||
fn ptr(&self) -> *mut () {
|
||||
self.0.map_addr(|addr| addr & !0b111)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_list() {
|
||||
let env = unsafe { *ENV_INIT };
|
||||
println!("env: {:?}\n", env);
|
||||
|
||||
// let input = b"(let ((x 10) (y 20)) (+ x y))";
|
||||
let input = b"(if () 1 2)";
|
||||
let hello_world = b"(let* (
|
||||
(msg \"hello world\\n\")
|
||||
(msg-parts (str-parts msg))
|
||||
(ptr (car msg-parts))
|
||||
(len (cdr msg-parts))
|
||||
(fd 1))
|
||||
(syscall 1 fd ptr len))";
|
||||
// let math = br#"
|
||||
// (let (
|
||||
// (a (< 1 2))
|
||||
// (b (< 2 1))
|
||||
// (c (+ 1 2 3))
|
||||
// (d (* 2 3 4))
|
||||
// (e (/ 10 2))
|
||||
// (f (% 10 3))
|
||||
// )
|
||||
// (print-env)
|
||||
// )
|
||||
// "#;
|
||||
let mut src = Source::from(&hello_world[..]);
|
||||
let sexp = unsafe { parse_next_token(&raw mut src) };
|
||||
println!("> {:?}", sexp);
|
||||
let result = unsafe { eval(sexp, get_env()) };
|
||||
println!("{:?}\n", result);
|
||||
println!("env: {:?}", unsafe { get_env() });
|
||||
}
|
||||
3554
stages/lisp0/lisp1.asm
Normal file
3554
stages/lisp0/lisp1.asm
Normal file
File diff suppressed because it is too large
Load diff
568
stages/lisp0/test.rs
Normal file
568
stages/lisp0/test.rs
Normal file
|
|
@ -0,0 +1,568 @@
|
|||
#![feature(allocator_api, box_as_ptr)]
|
||||
|
||||
unsafe extern "C" {
|
||||
fn heap_alloc(size: usize, align: usize) -> *mut u8;
|
||||
fn heap_dealloc(ptr: *mut u8, size: usize, align: usize);
|
||||
|
||||
fn init_source(source: *mut Source, fd: i32) -> *mut Source;
|
||||
fn getc() -> SourceIterResult;
|
||||
fn peekc() -> SourceIterResult;
|
||||
|
||||
fn eval(expr: Object, env: Object) -> Object;
|
||||
fn init_env() -> Object;
|
||||
|
||||
fn parse_next_token() -> Object;
|
||||
|
||||
#[link_name = "ifile"]
|
||||
static mut IFILE: Source;
|
||||
#[link_name = "nil"]
|
||||
static NIL: ();
|
||||
#[link_name = "env"]
|
||||
static GENV: ();
|
||||
}
|
||||
|
||||
use std::ptr::NonNull;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
struct Object(*mut ());
|
||||
|
||||
impl Object {
|
||||
const BYTE: u8 = 0;
|
||||
const NUM: u8 = 1;
|
||||
const PRIM: u8 = 2;
|
||||
const CONS: u8 = 3;
|
||||
const CLOS: u8 = 4;
|
||||
const ATOM: u8 = 5;
|
||||
const ARR: u8 = 6;
|
||||
|
||||
const NUM_MAGIC: u32 = 0x5555;
|
||||
|
||||
fn is_nil(&self) -> bool {
|
||||
self.0 as *const () == &raw const NIL
|
||||
}
|
||||
fn tag(&self) -> u8 {
|
||||
self.0.addr() as u8 & 0b111
|
||||
}
|
||||
fn bits(&self) -> usize {
|
||||
self.0.addr()
|
||||
}
|
||||
fn ptr(&self) -> *mut () {
|
||||
self.0.map_addr(|addr| addr & !0b111)
|
||||
}
|
||||
fn is_g_env(&self) -> bool {
|
||||
self.0 as *const () == unsafe { *ENV_INIT }.0 as *const ()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct DebugObjectExplicitGenv(Object);
|
||||
|
||||
impl std::fmt::Debug for DebugObjectExplicitGenv {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
#[repr(C)]
|
||||
struct Cons {
|
||||
refc: u64,
|
||||
car: Object,
|
||||
cdr: Object,
|
||||
}
|
||||
|
||||
if self.0.is_g_env() {
|
||||
let mut proxy = unsafe {
|
||||
Cons {
|
||||
refc: 1,
|
||||
car: self.0.ptr().byte_add(8).cast::<Object>().read(),
|
||||
cdr: self.0.ptr().byte_add(16).cast::<Object>().read(),
|
||||
}
|
||||
};
|
||||
let obj =
|
||||
Object((&raw const proxy as *mut ()).map_addr(|addr| addr | Object::CONS as usize));
|
||||
write!(f, "{:?}", obj)
|
||||
} else {
|
||||
return write!(f, "{:?}", self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Object {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.is_nil() {
|
||||
return write!(f, "nil");
|
||||
}
|
||||
|
||||
if self.is_g_env() {
|
||||
return write!(f, "#<g_env>");
|
||||
}
|
||||
|
||||
fn fmt_byte(byte: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let c = unsafe { byte.cast::<u8>().read() };
|
||||
if c.is_ascii_graphic() || c == b' ' {
|
||||
write!(f, "'{}'", c as char)
|
||||
} else {
|
||||
write!(f, "'\\x{:02x}'", c)
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_num(num: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let num_ptr = unsafe { num.cast::<i64>().read() };
|
||||
write!(f, "{}", num_ptr)
|
||||
}
|
||||
|
||||
fn fmt_prim(prim: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let prim_ptr = unsafe { prim.cast::<*const ()>().read() };
|
||||
write!(f, "#<prim {:?}>", prim_ptr)
|
||||
}
|
||||
|
||||
fn fmt_cons(cons: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let (car, cdr) = unsafe { cons.cast::<(Object, Object)>().read() };
|
||||
|
||||
if !cdr.is_nil() && cdr.tag() == Object::CONS {
|
||||
// If the cdr is a cons cell, we can print it as a list
|
||||
write!(f, "({car:?}")?;
|
||||
let mut cdr = cdr;
|
||||
while !cdr.is_nil() && cdr.tag() == Object::CONS {
|
||||
let (next_car, next_cdr) =
|
||||
unsafe { cdr.ptr().byte_add(8).cast::<(Object, Object)>().read() };
|
||||
write!(f, " {next_car:?}")?;
|
||||
cdr = next_cdr;
|
||||
}
|
||||
if cdr.is_nil() {
|
||||
write!(f, ")")
|
||||
} else {
|
||||
write!(f, " {cdr:?})")
|
||||
}
|
||||
} else {
|
||||
write!(f, "({car:?} . {cdr:?})")
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_clos(clos: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let (car, env) = unsafe { clos.cast::<(Object, Object)>().read() };
|
||||
assert_eq!(
|
||||
car.tag(),
|
||||
Object::CONS,
|
||||
"Expected a pair for lambda cdr, got tag {}",
|
||||
car.tag()
|
||||
);
|
||||
let (params, body) = unsafe { car.ptr().cast::<(Object, Object)>().read() };
|
||||
write!(f, "λ ")?;
|
||||
if env.0 as *const () == &raw const NIL {
|
||||
write!(f, " #<g_env> ++ ")
|
||||
} else {
|
||||
write!(f, " {env:?} ++ ")
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_atom(atom: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let (ptr, len) = unsafe {
|
||||
(
|
||||
atom.cast::<*const u8>().read(),
|
||||
atom.byte_add(8).cast::<usize>().read(),
|
||||
)
|
||||
};
|
||||
|
||||
let slice = unsafe { core::slice::from_raw_parts(ptr, len) };
|
||||
let string = core::str::from_utf8(slice).unwrap_or("<invalid utf-8>");
|
||||
write!(f, "'{string}")
|
||||
}
|
||||
|
||||
fn fmt_arr(arr: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let len = unsafe { arr.cast::<u32>().read() } as usize;
|
||||
let cap = unsafe { arr.byte_add(4).cast::<u32>().read() } as usize;
|
||||
let data = unsafe { arr.byte_add(8).cast::<Object>().read() };
|
||||
|
||||
let data_ptr = data.ptr();
|
||||
let data_tag = data.tag();
|
||||
|
||||
match data_tag {
|
||||
Object::BYTE => {
|
||||
let slice = unsafe { core::slice::from_raw_parts(data_ptr as *const u8, len) };
|
||||
let string = core::str::from_utf8(slice).unwrap_or("<invalid utf-8>");
|
||||
write!(f, "\"{string}\"")
|
||||
}
|
||||
_ => {
|
||||
const SIZES: [usize; 7] = [1, 8, 8, 16, 16, 16, 16];
|
||||
let _fn = match data_tag {
|
||||
Object::BYTE => fmt_byte,
|
||||
Object::NUM => fmt_num,
|
||||
Object::PRIM => fmt_prim,
|
||||
Object::CONS => fmt_cons,
|
||||
Object::CLOS => fmt_clos,
|
||||
Object::ATOM => fmt_atom,
|
||||
Object::ARR => fmt_arr,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
write!(f, "[")?;
|
||||
for i in 0..len {
|
||||
let elem = unsafe { data_ptr.byte_add(i * SIZES[data_tag as usize]) };
|
||||
_fn(elem, f)?;
|
||||
if i + 1 < len {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
match self.tag() {
|
||||
Object::BYTE => {
|
||||
let byte = self.ptr().addr() >> 8 as u8;
|
||||
fmt_byte(&raw const byte as _, f)
|
||||
}
|
||||
Object::NUM => {
|
||||
let MASK: usize = 0x5555_0000_0000_0000 | Object::NUM as usize;
|
||||
|
||||
if self.bits() & MASK == MASK {
|
||||
let num = (self.bits() >> 8) as i32 as i64;
|
||||
fmt_num(&raw const num as _, f)
|
||||
} else {
|
||||
fmt_num(self.ptr().byte_add(8), f)
|
||||
}
|
||||
}
|
||||
Object::PRIM => fmt_prim(self.ptr().byte_add(8), f),
|
||||
Object::CONS => fmt_cons(self.ptr().byte_add(8), f),
|
||||
Object::CLOS => fmt_clos(self.ptr().byte_add(8), f),
|
||||
Object::ATOM => fmt_atom(self.ptr().byte_add(8), f),
|
||||
Object::ARR => fmt_arr(self.ptr().byte_add(8), f),
|
||||
_ => write!(f, "#<unknown tag {}>", self.tag()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct Source {
|
||||
fd: i32,
|
||||
peeked: (u8, u8),
|
||||
buf: NonNull<u8>,
|
||||
buf_cur: usize,
|
||||
buf_end: usize,
|
||||
}
|
||||
|
||||
#[repr(align(8))]
|
||||
struct Bool(bool);
|
||||
|
||||
impl core::ops::Deref for Bool {
|
||||
type Target = bool;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct SourceIterResult {
|
||||
c: u8,
|
||||
some: Bool,
|
||||
}
|
||||
|
||||
struct HeapAlloc;
|
||||
|
||||
unsafe impl std::alloc::Allocator for HeapAlloc {
|
||||
fn allocate(
|
||||
&self,
|
||||
layout: std::alloc::Layout,
|
||||
) -> Result<std::ptr::NonNull<[u8]>, std::alloc::AllocError> {
|
||||
let ptr = unsafe { heap_alloc(layout.size(), layout.align()) };
|
||||
if ptr.is_null() {
|
||||
Err(std::alloc::AllocError)
|
||||
} else {
|
||||
Ok(std::ptr::NonNull::slice_from_raw_parts(
|
||||
std::ptr::NonNull::new(ptr).unwrap(),
|
||||
layout.size(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn deallocate(&self, ptr: std::ptr::NonNull<u8>, layout: std::alloc::Layout) {
|
||||
heap_dealloc(ptr.as_ptr(), layout.size(), layout.align());
|
||||
}
|
||||
}
|
||||
|
||||
static mut ENV_INIT: std::cell::LazyCell<Object> =
|
||||
std::cell::LazyCell::new(|| unsafe { init_env() });
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs::File;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::os::fd::AsRawFd;
|
||||
|
||||
#[test]
|
||||
fn test_heap_alloc_dealloc() {
|
||||
let small = Box::new_in(42u8, HeapAlloc);
|
||||
let ptr_sized = Box::new_in(42usize, HeapAlloc);
|
||||
let big = Box::new_in([0u8; 48], HeapAlloc);
|
||||
let large = Box::new_in([0u8; 1024], HeapAlloc);
|
||||
let page_sized = Box::new_in([0u8; 4096], HeapAlloc);
|
||||
assert_eq!(*small, 42);
|
||||
assert_ne!(
|
||||
Box::as_ptr(&small) as *const (),
|
||||
Box::as_ptr(&ptr_sized) as *const ()
|
||||
);
|
||||
let small_ptr = Box::as_ptr(&small) as *const ();
|
||||
drop(small);
|
||||
let small2 = Box::new_in(43u8, HeapAlloc);
|
||||
assert_eq!(*small2, 43);
|
||||
assert_eq!(small_ptr, Box::as_ptr(&small2) as *const ());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn source() {
|
||||
let mut file = std::fs::File::open("lisp1.asm").unwrap();
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let SourceIterResult { c, some } = getc();
|
||||
assert!(*some);
|
||||
assert_eq!(c, b'd');
|
||||
let SourceIterResult { c, some } = peekc();
|
||||
assert!(*some);
|
||||
assert_eq!(c, b'e');
|
||||
let SourceIterResult { c, some } = getc();
|
||||
assert!(*some);
|
||||
assert_eq!(c, b'e');
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_list() {
|
||||
let file = ManuallyDrop::new(File::open("tests/list.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let obj = parse_next_token();
|
||||
println!("{:?}", obj);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_quoted_list() {
|
||||
let file = ManuallyDrop::new(File::open("tests/quoted-list.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let obj = parse_next_token();
|
||||
println!("{:?}", obj);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_string() {
|
||||
let file = ManuallyDrop::new(File::open("tests/string.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let obj = parse_next_token();
|
||||
println!("{:?}", obj);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_atom() {
|
||||
let file = ManuallyDrop::new(File::open("tests/atom.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let obj = parse_next_token();
|
||||
println!("{:?}", obj);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_pair() {
|
||||
let file = ManuallyDrop::new(File::open("tests/pair.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let obj = parse_next_token();
|
||||
println!("{:?}", obj);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_big_number() {
|
||||
let file = ManuallyDrop::new(File::open("tests/big-number.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let obj = parse_next_token();
|
||||
println!("{:?}", obj);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn global_env() {
|
||||
unsafe {
|
||||
let env = unsafe { *ENV_INIT };
|
||||
println!("{:?}", DebugObjectExplicitGenv(env));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_add() {
|
||||
let file = ManuallyDrop::new(File::open("tests/add.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let expr = parse_next_token();
|
||||
let env = unsafe { *ENV_INIT };
|
||||
let result = eval(expr, env);
|
||||
eprint!("done: ");
|
||||
eprintln!("{result:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_let() {
|
||||
let file = ManuallyDrop::new(File::open("tests/let.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let expr = parse_next_token();
|
||||
let env = unsafe { *ENV_INIT };
|
||||
let result = eval(expr, env);
|
||||
eprint!("done: ");
|
||||
eprintln!("{result:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_letstar() {
|
||||
let file = ManuallyDrop::new(File::open("tests/letstar.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let expr = parse_next_token();
|
||||
let env = unsafe { *ENV_INIT };
|
||||
let result = eval(expr, env);
|
||||
eprint!("done: ");
|
||||
eprintln!("{result:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_print() {
|
||||
let file = ManuallyDrop::new(File::open("tests/print.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let expr = parse_next_token();
|
||||
let env = unsafe { *ENV_INIT };
|
||||
let result = eval(expr, env);
|
||||
eprint!("done: ");
|
||||
eprintln!("{result:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_if() {
|
||||
let file = ManuallyDrop::new(File::open("tests/if.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let expr = parse_next_token();
|
||||
let env = unsafe { *ENV_INIT };
|
||||
let result = eval(expr, env);
|
||||
eprint!("done: ");
|
||||
eprintln!("{result:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_define() {
|
||||
let file = ManuallyDrop::new(File::open("tests/define.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let expr = parse_next_token();
|
||||
let env = unsafe { *ENV_INIT };
|
||||
let result = eval(expr, env);
|
||||
eprint!("done: ");
|
||||
eprintln!("{result:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_typeof() {
|
||||
let file = ManuallyDrop::new(File::open("tests/typeof.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let expr = parse_next_token();
|
||||
let env = unsafe { *ENV_INIT };
|
||||
let result = eval(expr, env);
|
||||
eprint!("done: ");
|
||||
eprintln!("{result:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_eval() {
|
||||
let file = ManuallyDrop::new(File::open("tests/eval.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let expr = parse_next_token();
|
||||
let env = unsafe { *ENV_INIT };
|
||||
let result = eval(expr, env);
|
||||
eprint!("done: ");
|
||||
eprintln!("{result:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_comment() {
|
||||
let file = ManuallyDrop::new(File::open("tests/comment.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let expr = parse_next_token();
|
||||
eprintln!("parsed: {:?}", expr);
|
||||
let env = unsafe { *ENV_INIT };
|
||||
let result = eval(expr, env);
|
||||
eprint!("done: ");
|
||||
eprintln!("{result:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_mapcar() {
|
||||
let file = ManuallyDrop::new(File::open("tests/mapcar.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let expr = parse_next_token();
|
||||
let env = unsafe { *ENV_INIT };
|
||||
let result = eval(expr, env);
|
||||
eprint!("done: ");
|
||||
eprintln!("{result:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_setq() {
|
||||
let file = ManuallyDrop::new(File::open("tests/setq.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let expr = parse_next_token();
|
||||
let env = unsafe { *ENV_INIT };
|
||||
let result = eval(expr, env);
|
||||
eprint!("done: ");
|
||||
eprintln!("{result:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_equal() {
|
||||
let file = ManuallyDrop::new(File::open("tests/equal.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let expr = parse_next_token();
|
||||
let env = unsafe { *ENV_INIT };
|
||||
let result = eval(expr, env);
|
||||
eprint!("done: ");
|
||||
eprintln!("{result:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complex() {
|
||||
let file = ManuallyDrop::new(File::open("tests/print-args.l").unwrap());
|
||||
unsafe {
|
||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
||||
let expr = parse_next_token();
|
||||
let env = unsafe { *ENV_INIT };
|
||||
let result = eval(expr, env);
|
||||
eprint!("done: ");
|
||||
eprintln!("{result:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
1
stages/lisp0/tests/add.l
Normal file
1
stages/lisp0/tests/add.l
Normal file
|
|
@ -0,0 +1 @@
|
|||
(+ 1 2 3)
|
||||
1
stages/lisp0/tests/atom.l
Normal file
1
stages/lisp0/tests/atom.l
Normal file
|
|
@ -0,0 +1 @@
|
|||
'shrigma
|
||||
1
stages/lisp0/tests/big-number.l
Normal file
1
stages/lisp0/tests/big-number.l
Normal file
|
|
@ -0,0 +1 @@
|
|||
(0xdeadcafebabe 11223344556677)
|
||||
2
stages/lisp0/tests/comment.l
Normal file
2
stages/lisp0/tests/comment.l
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
;; this is a comment
|
||||
(define x 10) ; this is another comment
|
||||
6
stages/lisp0/tests/define.l
Normal file
6
stages/lisp0/tests/define.l
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
(progn
|
||||
(define gt (lambda (a b)
|
||||
(if (< b a)
|
||||
't ())))
|
||||
(gt 1 2)
|
||||
)
|
||||
1
stages/lisp0/tests/equal.l
Normal file
1
stages/lisp0/tests/equal.l
Normal file
|
|
@ -0,0 +1 @@
|
|||
(= 1 1 1 1)
|
||||
3
stages/lisp0/tests/eval.l
Normal file
3
stages/lisp0/tests/eval.l
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
(let ((x 10)
|
||||
(y 20))
|
||||
(eval '(cons x y)))
|
||||
1
stages/lisp0/tests/if.l
Normal file
1
stages/lisp0/tests/if.l
Normal file
|
|
@ -0,0 +1 @@
|
|||
(if () 1 2)
|
||||
3
stages/lisp0/tests/let.l
Normal file
3
stages/lisp0/tests/let.l
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
(let ((x 10)
|
||||
(y 20))
|
||||
(+ x y))
|
||||
3
stages/lisp0/tests/letstar.l
Normal file
3
stages/lisp0/tests/letstar.l
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
(let* ((x 10)
|
||||
(y (+ x 10)))
|
||||
(+ x y))
|
||||
2
stages/lisp0/tests/list.l
Normal file
2
stages/lisp0/tests/list.l
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
(1 2 3)
|
||||
|
||||
4
stages/lisp0/tests/mapcar.l
Normal file
4
stages/lisp0/tests/mapcar.l
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
(progn
|
||||
(define inc (lambda (x) (+ x 1)))
|
||||
(mapcar 'inc '(1 2 3 4 5))
|
||||
)
|
||||
1
stages/lisp0/tests/pair.l
Normal file
1
stages/lisp0/tests/pair.l
Normal file
|
|
@ -0,0 +1 @@
|
|||
(1 . 2)
|
||||
27
stages/lisp0/tests/print-args.l
Normal file
27
stages/lisp0/tests/print-args.l
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
(define print
|
||||
(lambda (msg)
|
||||
(let*
|
||||
((msg-parts (str-parts msg))
|
||||
(ptr (car msg-parts))
|
||||
(len (cdr msg-parts))
|
||||
(fd 1))
|
||||
(syscall 1 fd ptr len)
|
||||
)))
|
||||
|
||||
(define println
|
||||
(lambda (msg)
|
||||
(progn
|
||||
(print msg)
|
||||
(print "\n"))))
|
||||
|
||||
(define print-list (lambda (args)
|
||||
(if (nil? args)
|
||||
()
|
||||
(mapcar 'println args))))
|
||||
|
||||
;; (print-list argv)
|
||||
(println "Arguments: ")
|
||||
(progn
|
||||
(print-list argv)
|
||||
)
|
||||
|
||||
7
stages/lisp0/tests/print.l
Normal file
7
stages/lisp0/tests/print.l
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
(let*
|
||||
((msg "Hello, World!\n")
|
||||
(msg-parts (str-parts msg))
|
||||
(ptr (car msg-parts))
|
||||
(len (cdr msg-parts))
|
||||
(fd 1))
|
||||
(syscall 1 fd ptr len))
|
||||
1
stages/lisp0/tests/quoted-list.l
Normal file
1
stages/lisp0/tests/quoted-list.l
Normal file
|
|
@ -0,0 +1 @@
|
|||
'(1 2 3 4)
|
||||
5
stages/lisp0/tests/setq.l
Normal file
5
stages/lisp0/tests/setq.l
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
(progn
|
||||
(define x 10)
|
||||
(setq x 20)
|
||||
(eval 'x)
|
||||
)
|
||||
1
stages/lisp0/tests/string.l
Normal file
1
stages/lisp0/tests/string.l
Normal file
|
|
@ -0,0 +1 @@
|
|||
("hello, World!" \a \b \NL \Space)
|
||||
12
stages/lisp0/tests/typeof.l
Normal file
12
stages/lisp0/tests/typeof.l
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
(progn
|
||||
(define exit (lambda (code)
|
||||
(syscall 60 code)))
|
||||
(define assert (lambda (cond)
|
||||
(if (nil? cond)
|
||||
(exit 1) ())))
|
||||
(assert (= (typeof 1) 'NUMBER))
|
||||
(assert (= (typeof 'symbol) 'ATOM))
|
||||
(assert (= (typeof '()) 'NIL))
|
||||
(assert (= (typeof '(1 2 3)) 'CONS))
|
||||
(assert (= (typeof exit) 'CLOSURE))
|
||||
)
|
||||
8
stages/shared/Makefile
Normal file
8
stages/shared/Makefile
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
lib.o: lib.asm
|
||||
nasm -g -f elf64 -o lib.o lib.asm
|
||||
|
||||
alloc.o: alloc.asm
|
||||
nasm -g -f elf64 -o alloc.o alloc.asm
|
||||
|
||||
vec.o: vec.asm
|
||||
nasm -g -f elf64 -o vec.o vec.asm
|
||||
253
stages/shared/alloc.asm
Normal file
253
stages/shared/alloc.asm
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
;; Allocator
|
||||
global alloc_pages
|
||||
global dealloc_pages
|
||||
global realloc_pages
|
||||
|
||||
global heap_alloc
|
||||
global heap_dealloc
|
||||
global heap_realloc
|
||||
|
||||
extern memcpy
|
||||
|
||||
section .bss
|
||||
heap resq 1
|
||||
|
||||
section .text
|
||||
|
||||
oom_msg: db "Out of memory!", 10
|
||||
oom:
|
||||
lea rdi, [abs oom_msg]
|
||||
mov esi, 15
|
||||
mov rax, 1 ; syscall: write
|
||||
mov rdi, 2 ; fd: stderr
|
||||
syscall
|
||||
mov rax, 60 ; syscall: exit
|
||||
mov rdi, -1 ; status: -1
|
||||
syscall
|
||||
|
||||
;; allocates $rdi bytes worth of pages via mmap
|
||||
alloc_pages:
|
||||
mov rax, 9 ; syscall: mmap
|
||||
mov rsi, rdi ; length: rdi
|
||||
xor rdi, rdi ; addr: NULL
|
||||
mov rdx, 3 ; prot: PROT_READ | PROT_WRITE
|
||||
mov r10, 34 ; flags: MAP_PRIVATE | MAP_ANONYMOUS
|
||||
mov r8, -1 ; fd: -1
|
||||
xor r9, r9 ; offset: 0
|
||||
syscall
|
||||
cmp rax, -1
|
||||
jle oom
|
||||
ret
|
||||
|
||||
dealloc_pages:
|
||||
mov rax, 11 ; syscall: munmap
|
||||
mov rdi, rsi ; addr: rsi
|
||||
mov rsi, rdx ; length: rdx
|
||||
syscall
|
||||
cmp rax, -1
|
||||
jle oom
|
||||
ret
|
||||
|
||||
;; reallocates memory at $rdi[..$rsi] to a new location of size $rdx.
|
||||
realloc_pages:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
sub rsp, 32
|
||||
mov qword [rsp], rsi
|
||||
mov qword [rsp + 8], rdi
|
||||
mov rdi, rdx
|
||||
call alloc_pages
|
||||
mov rsi, rdi
|
||||
mov rdi, qword [rsp + 8]
|
||||
mov rdx, qword [rsp]
|
||||
mov qword [rsp + 16], rax
|
||||
call memcpy
|
||||
mov rdi, qword [rsp + 8]
|
||||
mov rsi, qword [rsp]
|
||||
call dealloc_pages
|
||||
mov rax, qword [rsp + 16]
|
||||
add rsp, 32
|
||||
pop rbp
|
||||
ret
|
||||
|
||||
make_slab:
|
||||
push rdi
|
||||
mov rdi, 0x4000
|
||||
call alloc_pages
|
||||
mov qword [rax + 0x4000 - 8], 0 ; initialize the tail pointer to NULL
|
||||
pop rdi
|
||||
mov qword [rdi], 0 ; tail_end = 0
|
||||
mov qword [rdi + 8], 0 ; free = NULL
|
||||
mov qword [rdi + 16], rax ; first_block = allocated
|
||||
mov qword [rdi + 24], rax ; last_block = allocated
|
||||
mov rax, rdi
|
||||
ret
|
||||
|
||||
init_heap:
|
||||
push r14
|
||||
xor r14, r14
|
||||
mov rax, qword [rel heap]
|
||||
test rax, rax
|
||||
jnz .done
|
||||
mov rdi, 0x1000
|
||||
call alloc_pages
|
||||
mov qword [rel heap], rax
|
||||
.loop:
|
||||
cmp r14, 9
|
||||
jge .done
|
||||
mov rdi, r14
|
||||
shl rdi, 5 ; idx * 32
|
||||
mov rax, qword [rel heap]
|
||||
lea rdi, [rax + rdi] ; &heap.slabs[idx]
|
||||
call make_slab
|
||||
inc r14
|
||||
jmp .loop
|
||||
.done:
|
||||
pop r14
|
||||
ret
|
||||
|
||||
;; finds the correct slab for an allocation with size $rdi and align $rsi
|
||||
slab_bucket:
|
||||
xor rax, rax
|
||||
dec rdi ; if size is a power of two, dec so we can later inc
|
||||
dec rsi ; ^^
|
||||
or rdi, rsi ; we only care about the log2, so just gather all the bits
|
||||
bsr rsi, rdi ; log2((size-1) | (align-1))
|
||||
sub rsi, 2 ; +1 for the dec, -3 to collapse the first 3 slabs into one
|
||||
cmovae rax, rsi ; saturating sub
|
||||
ret
|
||||
|
||||
slab_alloc:
|
||||
push rbx
|
||||
mov rax, qword [rdi + 8] ; free
|
||||
test rax, rax
|
||||
jz .no_free
|
||||
mov rdx, qword [rax] ; next free chunk
|
||||
mov qword [rdi + 8], rdx ; free = next
|
||||
pop rbx
|
||||
ret
|
||||
.no_free:
|
||||
add esi, 3 ; undo the -3 from slab_bucket to get the actual log2(size)
|
||||
and esi, 63 ; clamp for safety
|
||||
mov edx, 16376 ; 0x4000 - 8
|
||||
mov ecx, esi
|
||||
shr rdx, cl ; 0x4000 - 8 >> log2(size)
|
||||
mov rax, qword [rdi] ; tail_end
|
||||
mov rbx, qword [rdi + 24] ; last_block
|
||||
cmp rax, rdx
|
||||
jb .alloc_from_block
|
||||
push rsi
|
||||
push rax
|
||||
push rdi
|
||||
mov rdi, 0x4000 ; allocate a new block
|
||||
call alloc_pages
|
||||
mov qword [rax + 0x4000 - 8], 0 ; initialize the tail pointer to NULL
|
||||
pop rdi
|
||||
mov rsi, qword [rdi + 24] ; last_block
|
||||
mov qword [rsi + 0x4000 - 8], rax
|
||||
mov qword [rdi + 24], rax ; last_block = new block
|
||||
mov rbx, rax
|
||||
pop rax
|
||||
pop rsi
|
||||
.alloc_from_block:
|
||||
inc qword [rdi] ; tail_end++
|
||||
mov ecx, esi
|
||||
shl rax, cl
|
||||
add rax, rbx
|
||||
pop rbx
|
||||
ret
|
||||
|
||||
;; allocate a chunk of memory of size $rdi and alignment $rsi
|
||||
heap_alloc:
|
||||
mov rax, qword [rel heap]
|
||||
cmp rax, 0
|
||||
je .init
|
||||
.is_init:
|
||||
push rdi
|
||||
call slab_bucket
|
||||
cmp rax, 9
|
||||
jge .mmap
|
||||
mov rsi, rax
|
||||
shl rax, 5 ; idx * 32
|
||||
mov rdi, qword [rel heap]
|
||||
lea rdi, [rdi + rax] ; &heap.slabs[idx]
|
||||
call slab_alloc
|
||||
pop rdi
|
||||
ret
|
||||
.init:
|
||||
push rdi
|
||||
push rsi
|
||||
call init_heap
|
||||
pop rsi
|
||||
pop rdi
|
||||
jmp .is_init
|
||||
.mmap:
|
||||
pop rdi
|
||||
call alloc_pages
|
||||
ret
|
||||
|
||||
;; deallocates a chunk of memory at $rdi of size $rsi and alignment $rdx
|
||||
heap_dealloc:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
push rsi
|
||||
push rdi
|
||||
mov rdi, rsi
|
||||
mov rsi, rdx
|
||||
call slab_bucket
|
||||
cmp rax, 9
|
||||
jge .mmap
|
||||
mov rsi, rax
|
||||
shl rax, 5 ; idx * 32
|
||||
mov rdi, qword [rel heap]
|
||||
lea rdi, [rdi + rax] ; &heap.slabs[idx]
|
||||
mov rax, qword [rdi + 8] ; free
|
||||
pop rsi
|
||||
mov qword [rsi], rax
|
||||
mov qword [rdi + 8], rsi
|
||||
pop rsi
|
||||
pop rbp
|
||||
ret
|
||||
.mmap:
|
||||
pop rdi
|
||||
pop rsi
|
||||
call dealloc_pages
|
||||
pop rbp
|
||||
ret
|
||||
|
||||
;; reallocates a chunk of memory at $rdi of size $rsi and alignment $rdx to a new location of size $rcx
|
||||
heap_realloc:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
push rbx
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
|
||||
mov rbx, rdi
|
||||
mov r12, rsi
|
||||
mov r13, rdx
|
||||
mov r14, rcx
|
||||
|
||||
mov rdi, r14
|
||||
mov rsi, r13
|
||||
call heap_alloc
|
||||
mov r14, rax
|
||||
|
||||
mov rdi, rbx
|
||||
mov rsi, rax
|
||||
mov rdx, r12
|
||||
call memcpy
|
||||
|
||||
mov rdi, rbx
|
||||
mov rsi, r12
|
||||
mov rdx, r13
|
||||
call heap_dealloc
|
||||
|
||||
mov rax, r14
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop rbx
|
||||
pop rbp
|
||||
ret
|
||||
428
stages/shared/alloc.hex1
Normal file
428
stages/shared/alloc.hex1
Normal file
|
|
@ -0,0 +1,428 @@
|
|||
@oom_msg:
|
||||
4f 75 74 20 6f 66 20 6d 65 6d 6f 72 79 21 0a
|
||||
@oom:
|
||||
# lea rdi, ['oom_msg]
|
||||
48 8d 3d 'oom_msg
|
||||
# mov esi, 15
|
||||
be 0f 00 00 00
|
||||
# mov eax, 1
|
||||
b8 01 00 00 00
|
||||
# mov edi, 2
|
||||
bf 02 00 00 00
|
||||
# syscall
|
||||
0f 05
|
||||
# mov eax, 60
|
||||
b8 3c 00 00 00
|
||||
# mov rdi, -1
|
||||
48 c7 c7 ff ff ff ff
|
||||
# syscall
|
||||
0f 05
|
||||
|
||||
@alloc_pages:
|
||||
# mov rax, 9
|
||||
b8 09 00 00 00
|
||||
# mov rsi, rdi
|
||||
48 89 fe
|
||||
# xor rdi, rdi
|
||||
48 31 ff
|
||||
# mov edi, 3
|
||||
ba 03 00 00 00
|
||||
# mov r10, 0x22
|
||||
41 ba 22 00 00 00
|
||||
# mov r8, -1
|
||||
49 c7 c0 ff ff ff ff
|
||||
# xor r9, r9
|
||||
4d 31 c9
|
||||
# syscall
|
||||
0f 05
|
||||
# cmp rax, -1
|
||||
48 83 f8 ff
|
||||
# jle 'oom
|
||||
0f 8e 'oom
|
||||
# ret
|
||||
c3
|
||||
|
||||
@dealloc_pages:
|
||||
# mov rax, 11
|
||||
b8 0b 00 00 00
|
||||
# mov rdi, rsi
|
||||
48 89 f7
|
||||
# mov rsi, rdx
|
||||
48 89 d6
|
||||
# syscall
|
||||
0f 05
|
||||
# cmp rax, -1
|
||||
48 83 f8 ff
|
||||
# jle 'oom
|
||||
0f 8e 'oom
|
||||
# ret
|
||||
c3
|
||||
|
||||
@realloc_pages:
|
||||
# push rbp
|
||||
55
|
||||
# mov rbp, rsp
|
||||
48 89 e5
|
||||
# sub rsp, 32
|
||||
48 83 ec 20
|
||||
# mov qword [rsp], rsi
|
||||
48 89 34 24
|
||||
# mov qword [rsp + 8], rdi
|
||||
48 89 7c 24 08
|
||||
# mov rdi, rdx
|
||||
48 89 d7
|
||||
# call 'alloc_pages
|
||||
e8 'alloc_pages
|
||||
# mov rsi, rax
|
||||
48 89 fe
|
||||
# mov rdi, qword [rsp + 8]
|
||||
48 8b 7c 24 08
|
||||
# mov rdx, qword [rsp]
|
||||
48 8b 14 24
|
||||
# mov qword [rsp + 16], rax
|
||||
48 89 44 24 10
|
||||
# call 'memcpy
|
||||
e8 'memcpy
|
||||
# mov rdi, qword [rsp + 8]
|
||||
48 8b 7c 24 08
|
||||
# mov rsi, qword [rsp]
|
||||
48 8b 34 24
|
||||
# call 'dealloc_pagesA
|
||||
e8 'dealloc_pages
|
||||
# mov rax, qword [rsp + 16]
|
||||
48 8b 44 24 10
|
||||
# add rsp, 32
|
||||
48 83 c4 20
|
||||
# pop rbp
|
||||
5d
|
||||
# ret
|
||||
c3
|
||||
|
||||
@make_slab:
|
||||
# push rdi
|
||||
57
|
||||
# mov rdi, 0x4000
|
||||
bf 00 40 00 00
|
||||
# call 'alloc_pages
|
||||
e8 'alloc_pages
|
||||
# mov qword [rsp + 0x4000 - 8], 0
|
||||
48 c7 80 f8 3f 00 00 00 00 00 00
|
||||
# pop rdi
|
||||
5f
|
||||
# mov qword [rdi], 0
|
||||
48 c7 07 00 00 00 00
|
||||
# mov qword [rdi + 8], 0
|
||||
48 c7 47 08 00 00 00 00
|
||||
# mov qword [rdi + 16], rax
|
||||
48 89 47 10
|
||||
# mov qword [rdi + 24], rax
|
||||
48 89 47 18
|
||||
# mov rax, rdi
|
||||
48 89 f8
|
||||
# ret
|
||||
c3
|
||||
|
||||
@heap:
|
||||
00 00 00 00 00 00 00 00
|
||||
@init_heap.done:
|
||||
# pop r14
|
||||
41 5e
|
||||
# ret
|
||||
c3
|
||||
@init_heap:
|
||||
# push r14
|
||||
41 56
|
||||
# xor r14, r14
|
||||
4d 31 f6
|
||||
# mov rax, ['heap]
|
||||
48 8b 05 'heap
|
||||
# test rax, rax
|
||||
48 85 c0
|
||||
# jne .done
|
||||
0f 85 'init_heap.done
|
||||
# mov rdi, 0x1000
|
||||
bf 00 10 00 00
|
||||
# call alloc_pages
|
||||
e8 'alloc_pages
|
||||
# mov ['heap], rax
|
||||
48 89 05 'heap
|
||||
@init_heap.loop:
|
||||
# cmp r14, 9
|
||||
49 83 fe 09
|
||||
# jge .done
|
||||
0f 8d 'init_heap.done
|
||||
# mov rdi, r14
|
||||
4c 89 f7
|
||||
# shl rdi, 12
|
||||
48 c1 e7 05
|
||||
# mov rax, ['heap]
|
||||
48 8b 05 'heap
|
||||
# lea rdi, [rax + rdi]
|
||||
48 8d 3c 38
|
||||
# call make_slab
|
||||
e8 'make_slab
|
||||
# inc r14
|
||||
49 ff c6
|
||||
# jmp .loop
|
||||
e9 'init_heap.loop
|
||||
|
||||
@slab_bucket:
|
||||
# xor eax, eax
|
||||
31 c0
|
||||
# dec rdi
|
||||
48 ff cf
|
||||
# dec rsi
|
||||
48 ff ce
|
||||
# or rdi, rsi
|
||||
48 09 f7
|
||||
# bsr rsi, rdi
|
||||
48 0f bd f7
|
||||
# sub rsi, 2
|
||||
48 83 ee 02
|
||||
# cmovae rax, rsi
|
||||
48 0f 43 c6
|
||||
# ret
|
||||
c3
|
||||
|
||||
@slab_alloc.alloc_from_block:
|
||||
# inc qword [rdi]
|
||||
48 ff 07
|
||||
#mov ecx, esi
|
||||
89 f1
|
||||
# shl rax, cl
|
||||
48 d3 e0
|
||||
# add rax, rbx
|
||||
48 01 d8
|
||||
# pop rbx
|
||||
5b
|
||||
# ret
|
||||
c3
|
||||
@slab_alloc.no_free:
|
||||
# add esi, 3
|
||||
83 c6 03
|
||||
# and esi, 63
|
||||
83 e6 3f
|
||||
# mov edx, 0x4000 - 8
|
||||
ba f8 3f 00 00
|
||||
# mov ecx, esi
|
||||
89 f1
|
||||
# shr rdx, cl
|
||||
48 d3 ea
|
||||
# mov rax, qword [rdi]
|
||||
48 8b 07
|
||||
# mov rbx, qword [rdi + 24]
|
||||
48 8b 5f 18
|
||||
# cmp rax, rdx
|
||||
48 39 d0
|
||||
# jb .alloc_from_block
|
||||
0f 82 'slab_alloc.alloc_from_block
|
||||
# push rsi
|
||||
56
|
||||
# push rax
|
||||
50
|
||||
# push rdi
|
||||
57
|
||||
# mov rdi, 0x4000
|
||||
bf 00 40 00 00
|
||||
# call 'alloc_pages
|
||||
e8 'alloc_pages
|
||||
# mov qword [rax + 0x4000 - 8], 0
|
||||
48 c7 80 f8 3f 00 00 00 00 00 00
|
||||
# pop rdi
|
||||
5f
|
||||
# mov rsi, qword [rdi + 24]
|
||||
48 8b 77 18
|
||||
# mov qword [rdi + 24], rax
|
||||
48 89 86 f8 3f 00 00
|
||||
# mov qword [rdi + 24], rax
|
||||
48 89 47 18
|
||||
# mov rbx, rax
|
||||
48 89 c3
|
||||
# pop rax
|
||||
58
|
||||
# pop rsi
|
||||
5e
|
||||
@slab_alloc:
|
||||
# push rbx
|
||||
53
|
||||
# mov rax, qword [rdi + 8]
|
||||
48 8b 47 08
|
||||
# test rax, rax
|
||||
48 85 c0
|
||||
# je .no_free
|
||||
0f 84 'slab_alloc.no_free
|
||||
# mov rdx, qword [rax]
|
||||
48 8b 10
|
||||
# mov qword [rdi + 8], rdx
|
||||
48 89 57 08
|
||||
# pop rbx
|
||||
5b
|
||||
# ret
|
||||
c3
|
||||
|
||||
@heap_alloc.mmap:
|
||||
# pop rdi
|
||||
5f
|
||||
# call 'alloc_pages
|
||||
e8 'alloc_pages
|
||||
# ret
|
||||
c3
|
||||
@heap_alloc.is_init:
|
||||
# push rdi
|
||||
57
|
||||
# call slab_bucket
|
||||
e8 'slab_bucket
|
||||
# cmp rax, 9
|
||||
48 83 f8 09
|
||||
# jge .mmap
|
||||
0f 8d 'heap_alloc.mmap
|
||||
# mov rsi, rax
|
||||
48 89 c6
|
||||
# shl rax, 5
|
||||
48 c1 e0 05
|
||||
# mov rdi, qword ['heap]
|
||||
48 8b 3d 'heap
|
||||
# lea rdi, [rdi + rax]
|
||||
48 8d 3c 07
|
||||
# call 'slab_alloc
|
||||
e8 'slab_alloc
|
||||
# pop rdi
|
||||
5f
|
||||
# ret
|
||||
c3
|
||||
@heap_alloc.init:
|
||||
# push rdi
|
||||
57
|
||||
# push rsi
|
||||
56
|
||||
# call 'init_heap
|
||||
e8 'init_heap
|
||||
# pop rsi
|
||||
5e
|
||||
# pop rdi
|
||||
5f
|
||||
# jmp 'heap_alloc.is_init
|
||||
e9 'heap_alloc.is_init
|
||||
@heap_alloc:
|
||||
# mov rax, qword ['heap]
|
||||
48 8b 05 'heap
|
||||
# cmp rax, 0
|
||||
48 83 f8 00
|
||||
# je .init
|
||||
0f 84 'heap_alloc.init
|
||||
# jmp 'heap_alloc.is_init
|
||||
e9 'heap_alloc.is_init
|
||||
|
||||
@heap_dealloc.mmap:
|
||||
# pop rdi
|
||||
5f
|
||||
# pop rsi
|
||||
5e
|
||||
# call 'dealloc_pages
|
||||
e8 'dealloc_pages
|
||||
# pop rbp
|
||||
5d
|
||||
# ret
|
||||
c3
|
||||
@heap_dealloc:
|
||||
# push rbp
|
||||
55
|
||||
# mov rbp, rsp
|
||||
48 89 e5
|
||||
# push rsi
|
||||
56
|
||||
# push rdi
|
||||
57
|
||||
# mov rdi, rsi
|
||||
48 89 f7
|
||||
# mov rsi, rdx
|
||||
48 89 d6
|
||||
# call 'slab_bucket
|
||||
e8 'slab_bucket
|
||||
# cmp rax, 9
|
||||
48 83 f8 09
|
||||
# jge 'heap_dealloc.mmap
|
||||
0f 8d 'heap_dealloc.mmap
|
||||
# mov rsi, rax
|
||||
48 89 c6
|
||||
# shl rax, 5
|
||||
48 c1 e0 05
|
||||
# mov rdi, qword ['heap]
|
||||
48 8b 3d 'heap
|
||||
# lea rdi, [rdi + rax]
|
||||
48 8d 3c 07
|
||||
# mov rax, qword [rdi + 8]
|
||||
48 8b 47 08
|
||||
# pop rsi
|
||||
5e
|
||||
# mov qword [rsi], rax
|
||||
48 89 06
|
||||
# mov qword [rdi + 8], rsi
|
||||
48 89 77 08
|
||||
# pop rsi
|
||||
5e
|
||||
# pop rbp
|
||||
5d
|
||||
# ret
|
||||
c3
|
||||
|
||||
|
||||
@heap_realloc:
|
||||
# push rbp
|
||||
55
|
||||
# mov rbp, rsp
|
||||
48 89 e5
|
||||
# push rbx
|
||||
53
|
||||
# push r12
|
||||
41 54
|
||||
# push r13
|
||||
41 55
|
||||
# push r14
|
||||
41 56
|
||||
# mov rbx, rdi
|
||||
48 89 fb
|
||||
# mov r12, rsi
|
||||
49 89 f4
|
||||
# mov r13, rdx
|
||||
49 89 d5
|
||||
# mov r14, rcx
|
||||
49 89 ce
|
||||
# mov rdi, r14
|
||||
4c 89 f7
|
||||
# mov rsi, r13
|
||||
4c 89 ee
|
||||
# call 'heap_alloc
|
||||
e8 'heap_alloc
|
||||
# mov r14, rax
|
||||
49 89 c6
|
||||
# mov rdi, rbx
|
||||
48 89 df
|
||||
# mov rsi, rax
|
||||
48 89 c6
|
||||
# mov rdx, r12
|
||||
4c 89 e2
|
||||
# call 'memcpy
|
||||
e8 'memcpy
|
||||
# mov rdi, rbx
|
||||
48 89 df
|
||||
# mov rsi, r12
|
||||
4c 89 e6
|
||||
# mov rdx, r13
|
||||
4c 89 ea
|
||||
# call 'heap_dealloc
|
||||
e8 'heap_dealloc
|
||||
# mov rax, r14
|
||||
4c 89 f0
|
||||
# pop r14
|
||||
41 5e
|
||||
# pop r13
|
||||
41 5d
|
||||
# pop r12
|
||||
41 5c
|
||||
# pop rbx
|
||||
5b
|
||||
# pop rbp
|
||||
5d
|
||||
# ret
|
||||
c3
|
||||
343
stages/shared/lib.asm
Normal file
343
stages/shared/lib.asm
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
;; Common Library Functions
|
||||
global strlen
|
||||
global strstr
|
||||
global strcmp
|
||||
global strfind
|
||||
global memcpy
|
||||
global is_alpha
|
||||
global is_digit
|
||||
global is_alphanumeric
|
||||
global is_id_start
|
||||
global is_id_cont
|
||||
global to_digit
|
||||
global parse_i64
|
||||
global parse_i64_cstr
|
||||
global exit
|
||||
global fasthash
|
||||
|
||||
section .text
|
||||
|
||||
;; fn strlen(s: *const u8) -> usize
|
||||
strlen:
|
||||
xor eax, eax
|
||||
.loop:
|
||||
cmp byte [rdi + rax], 0
|
||||
je .done
|
||||
inc rax
|
||||
jmp .loop
|
||||
.done:
|
||||
ret
|
||||
|
||||
;; find in the first $rsi bytes of $rdi (haystack) the first $rcx bytes of $rdx (needle).
|
||||
;; returns the index in $rax, or -1 if not found.
|
||||
;; fn strstr(haystack: *const u8, haystack_len: usize, needle: *const u8, needle_len: usize) -> isize
|
||||
strstr:
|
||||
push rbx
|
||||
push r12
|
||||
push r13
|
||||
mov r13, rdi ; save original haystack pointer
|
||||
mov rax, rsi ; remaining haystack length
|
||||
.loop_haystack:
|
||||
cmp rax, rcx
|
||||
jb .not_found ; not enough haystack left for needle
|
||||
|
||||
xor rbx, rbx ; needle index
|
||||
.loop_needle:
|
||||
cmp rbx, rcx
|
||||
jz .found ; found the needle
|
||||
|
||||
mov r12b, byte [rdx + rbx] ; needle[needle_index]
|
||||
cmp r12b, byte [rdi + rbx] ; haystack[haystack_index + needle_index]
|
||||
jne .next_haystack ; mismatch, move to next haystack index
|
||||
inc rbx
|
||||
jmp .loop_needle
|
||||
.next_haystack:
|
||||
inc rdi ; move to next haystack index
|
||||
dec rax ; decrease remaining haystack length
|
||||
jmp .loop_haystack
|
||||
.not_found:
|
||||
xor eax, eax
|
||||
dec rax
|
||||
jmp .done
|
||||
.found:
|
||||
sub rdi, r13 ; calculate the index of the found needle
|
||||
mov rax, rdi
|
||||
.done:
|
||||
pop r13
|
||||
pop r12
|
||||
pop rbx
|
||||
ret
|
||||
|
||||
|
||||
;; fn strcmp(s1: *const u8, s1_len: usize, s2: *const u8, s2_len: usize) -> i32
|
||||
strcmp:
|
||||
cmp rcx, rsi
|
||||
cmovb rsi, rcx ; if rhs is shorter, use its length for the loop
|
||||
xor eax, eax
|
||||
.strcmp_loop:
|
||||
cmp rsi, rax
|
||||
jz .strcmp_equal
|
||||
movzx ecx, byte [rdx + rax]
|
||||
cmp byte [rdi + rax], cl
|
||||
lea rax, [rax + 1]
|
||||
je .strcmp_loop
|
||||
seta al ; al = lhs > rhs
|
||||
sbb al, 0 ; al = al - CF
|
||||
ret
|
||||
.strcmp_equal:
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
;; fn strfind(s: *const u8, len: usize, c: u8) -> isize
|
||||
strfind:
|
||||
strch:
|
||||
xor eax, eax
|
||||
.loop:
|
||||
cmp rax, rsi
|
||||
jge .not_found
|
||||
mov cl, byte [rdi + rax]
|
||||
cmp cl, dl
|
||||
je .found
|
||||
inc rax
|
||||
jmp .loop
|
||||
.not_found:
|
||||
xor eax, eax
|
||||
dec rax
|
||||
.found:
|
||||
ret
|
||||
|
||||
;; fn memcpy(src: *const u8, dst: *mut u8, len: usize)
|
||||
memcpy:
|
||||
.loop:
|
||||
test rdx, rdx
|
||||
jz .done
|
||||
mov al, byte [rdi]
|
||||
mov byte [rsi], al
|
||||
inc rsi
|
||||
inc rdi
|
||||
dec rdx
|
||||
jmp .loop
|
||||
.done:
|
||||
ret
|
||||
|
||||
;; fn is_alpha(c: u8) -> bool
|
||||
is_alpha:
|
||||
movzx eax, dil
|
||||
and eax, 0x1fffdf ; ignore bit 5 (case)
|
||||
add eax, -65 ; subtract 'A'
|
||||
cmp eax, 26 ; check if in range 0-25
|
||||
setb al
|
||||
ret
|
||||
|
||||
;; fn is_digit(c: u8) -> bool
|
||||
is_digit:
|
||||
movzx edi, dil
|
||||
lea eax, [rdi - 48] ; subtract '0'
|
||||
cmp eax, 10 ; check if in range 0-9
|
||||
setb al
|
||||
ret
|
||||
|
||||
;; fn is_alphanumeric(c: u8) -> bool
|
||||
is_alphanumeric:
|
||||
lea eax, [rdi - 48]
|
||||
cmp eax, 10
|
||||
setb cl
|
||||
and dil, 0xdf
|
||||
add dil, -65
|
||||
cmp dil, 26
|
||||
setb al
|
||||
or al, cl
|
||||
ret
|
||||
|
||||
;; fn is_id_start(c: u8) -> bool
|
||||
is_id_start:
|
||||
call is_alpha
|
||||
cmp dil, 95
|
||||
sete cl
|
||||
or al, cl
|
||||
ret
|
||||
|
||||
;; fn is_id_continue(c: u8) -> bool
|
||||
is_id_cont:
|
||||
call is_alphanumeric
|
||||
cmp dil, 45
|
||||
sete cl
|
||||
or al, cl
|
||||
cmp dil, 95
|
||||
sete cl
|
||||
or al, cl
|
||||
ret
|
||||
|
||||
;; converts char $dil to a digit with radix $rsi, returning it in $edx. $al is set to 1 if the char is a valid digit, and 0 otherwise.
|
||||
;; fn to_digit(c: u8, radix: u8) -> (is_valid: bool, digit: u8)
|
||||
to_digit:
|
||||
lea eax, [rsi - 2]
|
||||
cmp eax, 35
|
||||
jae .invalid
|
||||
movzx rdi, dil
|
||||
lea edx, [rdi - 65] ; 'A' = 65
|
||||
and edx, -33 ; convert to uppercase
|
||||
add edx, 10 ; 'A' should map to 10
|
||||
lea eax, [rdi - 48] ; '0' = 48
|
||||
cmp esi, 11
|
||||
cmovb edx, eax ; if radix <= 10, then take the difference from '0'
|
||||
cmp edi, 58
|
||||
cmovb edx, eax ; or if char < '9', then take the difference from '0'
|
||||
xor eax, eax
|
||||
cmp edx, esi
|
||||
setb al ; al = edx < radix
|
||||
ret
|
||||
.invalid:
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
;; fn parse_i64(s: *const u8) -> (is_valid: bool, value: i64)
|
||||
parse_i64_cstr:
|
||||
call strlen
|
||||
mov rsi, rax
|
||||
jmp parse_i64
|
||||
|
||||
;; fn parse_i64(s: *const u8, len: usize) -> (is_valid: bool, value: i64)
|
||||
parse_i64:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
push rbx ; buf
|
||||
push r12 ; radix
|
||||
push r13 ; acc
|
||||
push r14 ; end
|
||||
|
||||
push rdi
|
||||
|
||||
sub rsp, 8
|
||||
mov dword [rsp], 1 ; sign = 1
|
||||
mov rbx, rdi ; buf
|
||||
mov r12, 10 ; radix
|
||||
xor r13, r13 ; acc
|
||||
|
||||
xor eax, eax
|
||||
mov r14, rsi
|
||||
add r14, rdi ; end = buf + len
|
||||
|
||||
cmp rbx, r14
|
||||
jge .done ; if buf >= end, return 0
|
||||
|
||||
; eat leading -
|
||||
cmp byte [rbx], '-'
|
||||
sete al
|
||||
lea rbx, [rbx + rax]
|
||||
shl eax, 1
|
||||
sub dword [rsp], eax
|
||||
|
||||
cmp rbx, r14
|
||||
jge .done
|
||||
|
||||
; eat leading +
|
||||
cmp byte [rbx], '+'
|
||||
sete al
|
||||
lea rbx, [rbx + rax]
|
||||
|
||||
cmp rbx, r14
|
||||
jge .done
|
||||
|
||||
mov dil, byte [rbx]
|
||||
mov esi, 10
|
||||
call to_digit
|
||||
jnb .not_numeric
|
||||
|
||||
; radix
|
||||
cmp dil, '0'
|
||||
jne .loop
|
||||
inc rbx
|
||||
|
||||
cmp rbx, r14
|
||||
jge .done
|
||||
|
||||
mov dil, byte [rbx]
|
||||
and dil, 0xdf ; convert to uppercase
|
||||
cmp dil, 'X'
|
||||
sete al
|
||||
lea ecx, [eax + eax * 2]
|
||||
shl ecx, 1 ; multiply by 6
|
||||
add r12d, ecx ; radix += 'x' ? 6 : 0
|
||||
|
||||
cmp dil, 'O'
|
||||
sete al
|
||||
shl al, 1 ; multiply by 2
|
||||
sub r12d, eax ; radix -= 'o' ? 2 : 0
|
||||
|
||||
cmp dil, 'B'
|
||||
sete al
|
||||
shl al, 3 ; multiply by 8
|
||||
sub r12d, eax ; radix -= 'b' ? 8 : 0
|
||||
|
||||
cmp r12d, 10
|
||||
setne al
|
||||
lea rbx, [rbx + rax] ; skip the radix prefix if it was present
|
||||
|
||||
.loop:
|
||||
cmp rbx, r14
|
||||
jge .done
|
||||
|
||||
mov dil, byte [rbx]
|
||||
inc rbx
|
||||
|
||||
cmp dil, '_'
|
||||
je .loop
|
||||
mov esi, r12d
|
||||
call to_digit
|
||||
mov ecx, edx
|
||||
test al, al
|
||||
jz .done
|
||||
mov rax, r13
|
||||
imul r12
|
||||
add rax, rcx
|
||||
mov r13, rax
|
||||
jmp .loop
|
||||
.not_numeric:
|
||||
xor eax, eax
|
||||
xor edx, edx
|
||||
add rsp, 8
|
||||
pop rdi
|
||||
jmp .epilogue
|
||||
.done:
|
||||
|
||||
mov rdx, rax
|
||||
neg r13
|
||||
cmp dword [rsp], 0
|
||||
cmovl rdx, r13
|
||||
|
||||
add rsp, 8
|
||||
pop rdi
|
||||
|
||||
sub rbx, rdi
|
||||
mov rax, rbx
|
||||
|
||||
.epilogue:
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop rbx
|
||||
pop rbp
|
||||
ret
|
||||
|
||||
;; fn exit() -> !
|
||||
exit:
|
||||
xor edi, edi
|
||||
mov rax, 60
|
||||
syscall
|
||||
|
||||
;; fn fasthash(s: *const u8, len: usize) -> u32
|
||||
fasthash:
|
||||
mov eax, 0x811C9DC5 ; FNV-1a 32-bit offset basis
|
||||
.loop:
|
||||
test rsi, rsi
|
||||
jz .done
|
||||
movzx ecx, byte [rdi]
|
||||
xor eax, ecx
|
||||
imul eax, 0x01000193 ; FNV prime
|
||||
inc rdi
|
||||
dec rsi
|
||||
jmp .loop
|
||||
.done:
|
||||
ret
|
||||
449
stages/shared/lib.hex1
Normal file
449
stages/shared/lib.hex1
Normal file
|
|
@ -0,0 +1,449 @@
|
|||
@strlen.done:
|
||||
# ret
|
||||
c3
|
||||
@strlen:
|
||||
# xor eax, eax
|
||||
31 c0
|
||||
@strlen.loop:
|
||||
# cmp byte [rdi + rax], 0
|
||||
80 3c 07 00
|
||||
# je .done:
|
||||
0f 84 'strlen.done
|
||||
# inc rax
|
||||
48 ff c0
|
||||
# jmp .loop
|
||||
e9 'strlen.loop
|
||||
|
||||
|
||||
@strstr.found:
|
||||
# sub rdi, r13
|
||||
4c 29 ef
|
||||
# mov rax, rdi
|
||||
48 89 f8
|
||||
@strstr.done:
|
||||
# pop r13
|
||||
41 5d
|
||||
# pop r12
|
||||
41 5c
|
||||
# pop rbx
|
||||
5b
|
||||
# ret
|
||||
c3
|
||||
@strstr.notfound
|
||||
# xor eax, eax
|
||||
31 c0
|
||||
# dec rax
|
||||
48 ff c8
|
||||
# jmp .done
|
||||
e9 'strstr.done
|
||||
@strstr.next_hackstack:
|
||||
# inc rdi
|
||||
48 ff c7
|
||||
# dec rax
|
||||
48 ff c8
|
||||
@strstr.loop_haystack:
|
||||
# cmp rax, rcx
|
||||
48 39 c8
|
||||
# jb 'strstr.notfound
|
||||
0f 82 'strstr.notfound
|
||||
# xor ebx, ebx
|
||||
31 db
|
||||
@strstr.loop_needle:
|
||||
# cmp rbx, rcx
|
||||
48 39 cb
|
||||
# je .found
|
||||
0f 84 'strstr.found
|
||||
# mov r12b, byte [rdx + rbx]
|
||||
44 8a 24 1a
|
||||
# cmp r12b, byte [rdi + rbx]
|
||||
44 3a 24 1f
|
||||
# jne .next_haystack
|
||||
0f 85 'strstr.next_haystack
|
||||
# inc rbx
|
||||
48 ff c3
|
||||
# jmp .loop_needle
|
||||
e9 'strstr.loop_needle
|
||||
|
||||
@strstr:
|
||||
# push rbx
|
||||
53
|
||||
# push r12
|
||||
41 54
|
||||
# push r13
|
||||
41 55
|
||||
# mov r13, rdi
|
||||
49 89 fd
|
||||
# mov rax, rsi
|
||||
48 89 f0
|
||||
# jmp .loop_haystack
|
||||
e9 'strstr.loop_haystack
|
||||
|
||||
@strcmp.eq:
|
||||
31 c0
|
||||
c3
|
||||
@strcmp:
|
||||
# cmp rcx, rsi
|
||||
48 39 f1
|
||||
# cmovb rsi, rcx
|
||||
48 0f 42 f1
|
||||
# xor eax, eax
|
||||
31 c0
|
||||
@strcmp.loop:
|
||||
# cmp rsi, rax
|
||||
48 39 c6
|
||||
# je .eq
|
||||
0f 84 'strcmp.eq
|
||||
# movzx ecx, byte [rdx + rax]
|
||||
0f b6 0c 02
|
||||
# cmp byte [rdi + rax, cl
|
||||
38 0c 07
|
||||
# inc rax
|
||||
48 ff c0
|
||||
# je .loop
|
||||
0f 84 'strcmp.loop
|
||||
# seta al
|
||||
0f 97 c0
|
||||
# sbb al, 0
|
||||
1c 00
|
||||
# ret
|
||||
c3
|
||||
|
||||
@strfind.nf:
|
||||
# xor eax, eax
|
||||
31 c0
|
||||
# dec rax
|
||||
48 ff c8
|
||||
@strfind.f:
|
||||
c3
|
||||
@strfind:
|
||||
# xor eax, eax
|
||||
31 c0
|
||||
# cmp rax, rsi
|
||||
48 39 f0
|
||||
# jge .nf
|
||||
0f 8d 'strfind.nf
|
||||
# mov cl, byte [rdi + rax]
|
||||
8a 0c 07
|
||||
# cmp cl, dl
|
||||
38 d1
|
||||
# je .f
|
||||
0f 84 'strfind.f
|
||||
# inc rax
|
||||
48 ff c0
|
||||
# jmp strfind
|
||||
e9 'strfind
|
||||
|
||||
@memcpy.done:
|
||||
c3
|
||||
@memcpy:
|
||||
# test rdx, rdx
|
||||
48 85 d2
|
||||
# jz .done
|
||||
0f 84 'memcpy.done
|
||||
# mov al, byte [rdi]
|
||||
8a 07
|
||||
# mov byte [rsi], al
|
||||
88 06
|
||||
# inc rsi
|
||||
48 ff c6
|
||||
# inc rdi
|
||||
48 ff c7
|
||||
# dec rdx
|
||||
48 ff ca
|
||||
# jmp .loop
|
||||
e9 'memcpy
|
||||
|
||||
@is_alpha:
|
||||
40 0f b6 c7
|
||||
25 df ff 1f 00
|
||||
83 c0 bf
|
||||
83 f8 1a
|
||||
0f 92 c0
|
||||
c3
|
||||
|
||||
@is_digit:
|
||||
40 0f b6 ff
|
||||
8d 47 d0
|
||||
83 f8 0a
|
||||
0f 92 c0
|
||||
c3
|
||||
|
||||
@is_alphanumeric:
|
||||
8d 47 d0
|
||||
83 f8 0a
|
||||
0f 92 c1
|
||||
40 80 e7 df
|
||||
40 80 c7 bf
|
||||
40 80 ff 1a
|
||||
0f 92 c0
|
||||
08 c8
|
||||
c3
|
||||
|
||||
@is_id_start:
|
||||
# call is_alpha
|
||||
e8 'is_alpha
|
||||
40 80 ff 5f
|
||||
0f 94 c1
|
||||
08 c8
|
||||
c3
|
||||
|
||||
@is_id_cont:
|
||||
# call is_alphanumeric
|
||||
e8 'is_alphanumeric
|
||||
40 80 ff 2d
|
||||
0f 94 c1
|
||||
08 c8
|
||||
40 80 ff 5f
|
||||
0f 94 c1
|
||||
08 c8
|
||||
c3
|
||||
|
||||
@to_digit.inv:
|
||||
# xor eax, eax
|
||||
31 c0
|
||||
# ret
|
||||
c3
|
||||
@to_digit:
|
||||
8d 46 fe
|
||||
83 f8 23
|
||||
# jae .inv
|
||||
0f 83 'to_digit.inv
|
||||
48 0f b6 ff
|
||||
8d 57 bf
|
||||
83 e2 df
|
||||
83 c2 0a
|
||||
8d 47 d0
|
||||
83 fe 0b
|
||||
0f 42 d0
|
||||
83 ff 3a
|
||||
0f 42 d0
|
||||
31 c0
|
||||
39 f2
|
||||
0f 92 c0
|
||||
c3
|
||||
|
||||
@parse_i64.done:
|
||||
# mov rdx, rax
|
||||
48 89 c2
|
||||
# neg r13
|
||||
49 f7 dd
|
||||
# cmp dword [rsp], 0
|
||||
83 3c 24 00
|
||||
# cmovl rdx, r13
|
||||
49 0f 4c d5
|
||||
# add rsp, 8
|
||||
48 83 c4 08
|
||||
# pop rdi
|
||||
5f
|
||||
# sub rbx, rdi
|
||||
48 29 fb
|
||||
# mov rax, rbx
|
||||
48 89 d8
|
||||
@parse_i64.epilogue:
|
||||
# pop r14
|
||||
41 5e
|
||||
# pop r13
|
||||
41 5d
|
||||
# pop r12
|
||||
41 5c
|
||||
# pop rbx
|
||||
5b
|
||||
# pop rbp
|
||||
5d
|
||||
# ret
|
||||
c3
|
||||
@parse_i64.not_num:
|
||||
# xor eax, eax
|
||||
31 c0
|
||||
# xor edx, edx
|
||||
31 d2
|
||||
# add rsp, 8
|
||||
48 83 c4 08
|
||||
# pop rdi
|
||||
5f
|
||||
# jmp .epilogue
|
||||
e9 'parse_i64.epilogue
|
||||
|
||||
@parse_i64_cstr:
|
||||
e8 'strlen
|
||||
# mov rsi, rax
|
||||
48 89 c6
|
||||
|
||||
@parse_i64:
|
||||
# push rbp
|
||||
55
|
||||
# mov rbp, rsp
|
||||
48 89 e5
|
||||
# push rbx
|
||||
53
|
||||
# push r12
|
||||
41 54
|
||||
# push r13
|
||||
41 55
|
||||
# push r14
|
||||
41 56
|
||||
# push rdi
|
||||
57
|
||||
# sub rsp, 8
|
||||
48 83 ec 08
|
||||
# mov dword [rsp], 1
|
||||
c7 04 24 01 00 00 00
|
||||
# mov rbx, rdi
|
||||
48 89 fb
|
||||
# mov r12, 10
|
||||
41 bc 0a 00 00 00
|
||||
# xor r13, r13
|
||||
4d 31 ed
|
||||
# xor eax, eax
|
||||
31 c0
|
||||
# mov r14, rsi
|
||||
49 89 f6
|
||||
# add r14, rdi
|
||||
49 01 fe
|
||||
# cmp rbx, r14
|
||||
4c 39 f3
|
||||
# jge .done
|
||||
0f 8d 'parse_i64.done
|
||||
# cmp byte [rbx], '-'
|
||||
80 3b 2d
|
||||
# sete al
|
||||
0f 94 c0
|
||||
# lea rbx, [rbx + rax]
|
||||
48 8d 1c 03
|
||||
# shl eax, 1
|
||||
d1 e0
|
||||
# sub dword [rsp], eax
|
||||
29 04 24
|
||||
# cmp rbx, r14
|
||||
4c 39 f3
|
||||
# jge .done
|
||||
0f 8d 'parse_i64.done
|
||||
# cmp byte [rbx], '+'
|
||||
80 3b 2b
|
||||
# sete al
|
||||
0f 94 c0
|
||||
# lea rbx, [rbx + rax]
|
||||
48 8d 1c 03
|
||||
# cmp rbx, r14
|
||||
4c 39 f3
|
||||
# jge .done
|
||||
0f 8d 'parse_i64.done
|
||||
# mov dil, byte [rbx]
|
||||
40 8a 3b
|
||||
# mov esi, 10
|
||||
be 0a 00 00 00
|
||||
# call to_digit
|
||||
e8 'to_digit
|
||||
# jae .not_num
|
||||
0f 83 'parse_i64.not_num
|
||||
# cmp dil, '0'
|
||||
40 80 ff 30
|
||||
# jne .loop
|
||||
0f 85 'parse_i64.loop
|
||||
# inc rbx
|
||||
48 ff c3
|
||||
# cmp rbx, r14
|
||||
4c 39 f3
|
||||
# jge .done
|
||||
0f 8d 'parse_i64.done
|
||||
# mov dil, byte [rbx]
|
||||
40 8a 3b
|
||||
# and dil, 0xdf
|
||||
40 80 e7 df
|
||||
# cmp dil, 'X'
|
||||
40 80 ff 58
|
||||
# sete al
|
||||
0f 94 c0
|
||||
# lea ecx, [eax + eax * 2]
|
||||
67 8d 0c 40
|
||||
# shl ecx, 1
|
||||
d1 e1
|
||||
# add r12d, ecx
|
||||
41 01 cc
|
||||
# cmp dil, 'O'
|
||||
40 80 ff 4f
|
||||
# sete al
|
||||
0f 94 c0
|
||||
# shl al, 1
|
||||
d0 e0
|
||||
# sub r12d, eax
|
||||
41 29 c4
|
||||
# cmp dil, 'B'
|
||||
40 80 ff 42
|
||||
# sete al
|
||||
0f 94 c0
|
||||
# shl al, 3
|
||||
c0 e0 03
|
||||
# sub r12d, eax
|
||||
41 29 c4
|
||||
# cmp r12d, 10
|
||||
41 83 fc 0a
|
||||
# setne al
|
||||
0f 95 c0
|
||||
# lea rbx, [rbx + rax]
|
||||
48 8d 1c 03
|
||||
|
||||
@parse_i64.loop:
|
||||
# cmp rbx, r14
|
||||
4c 39 f3
|
||||
# jge .done
|
||||
0f 8d 'parse_i64.done
|
||||
# mov dil, byte [rbx]
|
||||
40 8a 3b
|
||||
# inc rbx
|
||||
48 ff c3
|
||||
# cmp dil, '_'
|
||||
40 80 ff 5f
|
||||
# je .loop
|
||||
0f 84 'parse_i64.loop
|
||||
# mov esi, r12d
|
||||
44 89 e6
|
||||
# call to_digit
|
||||
e8 'to_digit
|
||||
# mov ecx, edx
|
||||
89 d1
|
||||
# test al, al
|
||||
84 c0
|
||||
# je .done
|
||||
0f 84 'parse_i64.done
|
||||
# mov rax, r13
|
||||
4c 89 e8
|
||||
# imul r12
|
||||
49 f7 ec
|
||||
# add rax, rcx
|
||||
48 01 c8
|
||||
mov r13, rax
|
||||
49 89 c5
|
||||
# jmp .loop
|
||||
e9 'parse_i64.loop
|
||||
|
||||
@exit:
|
||||
# xor edi, edi
|
||||
31 ff
|
||||
# mov eax, 60
|
||||
b8 3c 00 00 00
|
||||
# syscall
|
||||
0f 05
|
||||
|
||||
@fasthash.done:
|
||||
c3
|
||||
@fasthash:
|
||||
# mov eax, 0x811c9dc5
|
||||
b8 c5 9d 1c 81
|
||||
@fasthash.loop:
|
||||
# test rsi, rsi
|
||||
48 85 f6
|
||||
# jz .done
|
||||
0f 84 'fasthash.done
|
||||
# movzx ecx, byte [rdi]
|
||||
0f b6 0f
|
||||
# xor eax, ecx
|
||||
31 c8
|
||||
# imul eax, 0x01000193
|
||||
69 c0 93 01 00 01
|
||||
# inc rdi
|
||||
48 ff c7
|
||||
# dec rsi
|
||||
48 ff ce
|
||||
# jmp .loop
|
||||
e9 'fasthash.loop
|
||||
77
stages/shared/vec.asm
Normal file
77
stages/shared/vec.asm
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
;; Vec / Dynamic Array Structure
|
||||
extern heap_realloc
|
||||
|
||||
global vec_init
|
||||
global vec_get_nth
|
||||
global vec_reserve
|
||||
|
||||
struc Vec
|
||||
.data resq 1 ; pointer to elements
|
||||
.size resd 1 ; size in element count
|
||||
.cap resd 1 ; cap in element count
|
||||
.ele_size resd 1 ; size of each element in bytes
|
||||
.ele_align resd 1 ; alignment of each element in bytes
|
||||
endstruc
|
||||
|
||||
;; fn vec_init(vec: *mut Vec, ele_size: usize, ele_align: usize) -> *mut Vec
|
||||
vec_init:
|
||||
mov qword [rdi + Vec.data], 0
|
||||
mov dword [rdi + Vec.size], 0
|
||||
mov dword [rdi + Vec.cap], 0
|
||||
mov dword [rdi + Vec.ele_size], esi
|
||||
mov dword [rdi + Vec.ele_align], edx
|
||||
mov rax, rdi
|
||||
ret
|
||||
|
||||
;; fn vec_get_nth(vec: *const Vec, idx: usize) -> *const u8
|
||||
vec_get_nth:
|
||||
mov rax, rsi
|
||||
mov ecx, dword [rdi + Vec.ele_size]
|
||||
mul ecx
|
||||
add rax, qword [rdi + Vec.data]
|
||||
ret
|
||||
|
||||
;; reserve space for $rsi additional entries in vec $rdi.
|
||||
;; fn vec_reserve(vec: *mut Vec, count: usize)
|
||||
vec_reserve:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
sub rsp, 16
|
||||
|
||||
mov edx, dword [rdi + Vec.ele_size]
|
||||
mov ecx, dword [rdi + Vec.ele_align]
|
||||
mov dword [rsp], edx ; obj-size
|
||||
mov dword [rsp + 4], ecx ; alignment
|
||||
mov qword [rsp + 8], rdi ; table pointer
|
||||
|
||||
mov eax, dword [rdi + Vec.size]
|
||||
add eax, esi
|
||||
cmp eax, dword [rdi + Vec.cap]
|
||||
jl .done
|
||||
|
||||
mov eax, esi
|
||||
add eax, dword [rdi + Vec.size]
|
||||
mov ecx, dword [rdi + Vec.cap]
|
||||
mov edx, ecx
|
||||
shl edx, 1
|
||||
cmp eax, edx
|
||||
cmovl eax, edx
|
||||
; new cap
|
||||
mul dword [rsp] ; obj-size
|
||||
|
||||
; old cap
|
||||
xchg eax, ecx
|
||||
mul dword [rsp] ; obj-size
|
||||
|
||||
mov rdi, qword [rdi + Vec.data] ; old pointer
|
||||
mov esi, eax ; old size
|
||||
mov edx, ecx ; new size
|
||||
mov ecx, dword [rsp + 4] ; alignment
|
||||
call heap_realloc
|
||||
mov rdi, qword [rsp + 8] ; table pointer
|
||||
mov qword [rdi + Vec.data], rax
|
||||
|
||||
.done:
|
||||
add rsp, 16
|
||||
pop rbp
|
||||
ret
|
||||
51
test.asm
Normal file
51
test.asm
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
section .text
|
||||
|
||||
global _start
|
||||
_start:
|
||||
mov rdi, 42
|
||||
call main
|
||||
mov rdi, rax
|
||||
mov rax, 60 ; syscall: exit
|
||||
syscall
|
||||
|
||||
global main
|
||||
main:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
jmp .prologue
|
||||
.args:
|
||||
mov qword [rbp -8], rdi
|
||||
.body:
|
||||
mov rbx, 42
|
||||
mov rcx, qword [rbp -8]
|
||||
mov rax, rbx
|
||||
cmp rcx, rax
|
||||
sete bl
|
||||
cmp bl, 0
|
||||
je .Be
|
||||
mov rbx, 7
|
||||
mov qword [rbp -16], rbx
|
||||
mov rdi, qword [rbp -16]
|
||||
mov rax, rsp
|
||||
sub rsp, 8
|
||||
and rsp, -16
|
||||
mov [rsp], rax
|
||||
call main
|
||||
pop rsp
|
||||
mov rbx, rax
|
||||
mov rax, rbx
|
||||
jmp .epilogue
|
||||
jmp .PHI4
|
||||
.Be:
|
||||
lea rax, main
|
||||
jmp .epilogue
|
||||
.PHI4:
|
||||
.epilogue:
|
||||
pop rbx
|
||||
add rsp, 16
|
||||
pop rbp
|
||||
ret
|
||||
.prologue:
|
||||
sub rsp, 16
|
||||
push rbx
|
||||
jmp .args
|
||||
Loading…
Reference in a new issue