from-scratch/stages/as0/as0.asm
2026-07-07 18:14:14 +02:00

1574 lines
40 KiB
NASM

;; This assembler is a 2-pass compiler, permitting forward and backward label references.
;; It's also the first stage in this bootstrapping experiment that will
;; consume proper mnemonic assembly, rather than hex input.
section .bss
global buf
buf resb 0x100
labels resq 1
label_refs resq 1
heap resq 1
section .data
_start_lbl db "_start", 0
ENC_RM equ 1
ENC_MR equ 2
ENC_MI equ 3
ENC_OI equ 4
ENC_M equ 5
ENC_O equ 6
ENC_I equ 7
ENC_MC equ 8 ; memory with cl
ENC_M1 equ 9 ; memory with 1
OPSIZE_8 equ 1
OPSIZE_16 equ 2
OPSIZE_32 equ 4
OPSIZE_64 equ 8
mnt:
mnemonics:
mnemonics_table:
MN_MOV db "mov", 0
MN_TEST db "test", 0
MN_XOR db "xor", 0
MN_OR db "or", 0
MN_AND db "and", 0
MN_SHL db "shl", 0
MN_SHR db "shr", 0
MN_INC db "inc", 0
MN_DEC db "dec", 0
MN_NEG db "neg", 0
MN_RET db "ret", 0
MN_NOT db "not", 0
MN_ADD db "add", 0
MN_SUB db "sub", 0
MN_IMUL db "imul", 0
MN_IDIV db "idiv", 0
MN_CMP db "cmp", 0
MN_LEA db "lea", 0
MN_PUSH db "push", 0
MN_POP db "pop", 0
MN_CMOVE db "cmovz", 0
MN_CMOVNE db "cmovnz", 0
MN_CMOVL db "cmovl", 0
MN_CMOVLE db "cmovle", 0
MN_CMOVG db "cmovg", 0
MN_CMOVGE db "cmovge", 0
MN_CMOVA db "cmova", 0
MN_CMOVAE db "cmovae", 0
MN_CMOVB db "cmovb", 0
MN_CMOVBE db "cmovbe", 0
MN_SETE db "sete", 0
MN_SETNE db "setne", 0
MN_SETL db "setl", 0
MN_SETLE db "setle", 0
MN_SETG db "setg", 0
MN_SETGE db "setge", 0
MN_SETA db "seta", 0
MN_SETAE db "setae", 0
MN_SETB db "setb", 0
MN_SETBE db "setbe", 0
MN_JE db "je", 0
MN_JNE db "jne", 0
MN_JL db "jl", 0
MN_JLE db "jle", 0
MN_JG db "jg", 0
MN_JGE db "jge", 0
MN_JA db "ja", 0
MN_JAE db "jae", 0
MN_JB db "jb", 0
MN_JBE db "jbe", 0
MN_JMP db "jmp", 0
MN_CALL db "call", 0
MN_XCHG db "xchg", 0
MN_SYSCALL db "syscall", 0
;; Opcodes support up to 2 operands
op_table:
;; MN_OFFS | NUM | SIZES | ENCOD | OPCODE | RM | PRE
dd mnt - MN_MOV, 2, 1, 1, ENC_MR, 88h, 00, 00h
dd mnt - MN_MOV, 2, 2, 2, ENC_MR, 89h, 00, 66h
dd mnt - MN_MOV, 2, 4, 4, ENC_MR, 89h, 00, 00h
dd mnt - MN_MOV, 2, 8, 8, ENC_MR, 89h, 00, 00h
dd mnt - MN_MOV, 2, 1, 1, ENC_RM, 8Ah, 00, 00h
dd mnt - MN_MOV, 2, 2, 2, ENC_RM, 8Bh, 00, 66h
dd mnt - MN_MOV, 2, 4, 4, ENC_RM, 8Bh, 00, 00h
dd mnt - MN_MOV, 2, 8, 8, ENC_RM, 8Bh, 00, 00h
dd mnt - MN_MOV, 2, 1, 1, ENC_OI, 0B0h, 00, 00h
dd mnt - MN_MOV, 2, 2, 2, ENC_OI, 0B8h, 00, 66h
dd mnt - MN_MOV, 2, 4, 4, ENC_OI, 0B8h, 00, 00h
dd mnt - MN_MOV, 2, 8, 8, ENC_OI, 0B8h, 00, 00h
dd mnt - MN_MOV, 2, 1, 1, ENC_MI, 0C6h, 00, 00h
dd mnt - MN_MOV, 2, 2, 2, ENC_MI, 0C7h, 00, 66h
dd mnt - MN_MOV, 2, 4, 4, ENC_MI, 0C7h, 00, 00h
dd mnt - MN_MOV, 2, 8, 4, ENC_MI, 0C7h, 00, 00h
dd mnt - MN_TEST, 2, 1, 1, ENC_MR, 84h, 00, 00h
dd mnt - MN_TEST, 2, 2, 2, ENC_MR, 85h, 00, 66h
dd mnt - MN_TEST, 2, 4, 4, ENC_MR, 85h, 00, 00h
dd mnt - MN_TEST, 2, 8, 8, ENC_MR, 85h, 00, 00h
dd mnt - MN_TEST, 2, 1, 1, ENC_MI, 0F6h, 00, 00h
dd mnt - MN_TEST, 2, 2, 2, ENC_MI, 0F7h, 00, 66h
dd mnt - MN_TEST, 2, 4, 4, ENC_MI, 0F7h, 00, 00h
dd mnt - MN_TEST, 2, 8, 4, ENC_MI, 0F7h, 00, 00h
dd mnt - MN_XOR, 2, 1, 1, ENC_MR, 30h, 00, 00h
dd mnt - MN_XOR, 2, 2, 2, ENC_MR, 31h, 00, 66h
dd mnt - MN_XOR, 2, 4, 4, ENC_MR, 31h, 00, 00h
dd mnt - MN_XOR, 2, 8, 8, ENC_MR, 31h, 00, 00h
dd mnt - MN_XOR, 2, 1, 1, ENC_RM, 32h, 00, 00h
dd mnt - MN_XOR, 2, 2, 2, ENC_RM, 33h, 00, 66h
dd mnt - MN_XOR, 2, 4, 4, ENC_RM, 33h, 00, 00h
dd mnt - MN_XOR, 2, 8, 8, ENC_RM, 33h, 00, 00h
dd mnt - MN_XOR, 2, 1, 1, ENC_MI, 80h, 06, 00h
dd mnt - MN_XOR, 2, 2, 2, ENC_MI, 81h, 06, 66h
dd mnt - MN_XOR, 2, 4, 4, ENC_MI, 81h, 06, 00h
dd mnt - MN_XOR, 2, 8, 4, ENC_MI, 81h, 06, 00h
dd mnt - MN_AND, 2, 1, 1, ENC_MR, 20h, 00, 00h
dd mnt - MN_AND, 2, 2, 2, ENC_MR, 21h, 00, 66h
dd mnt - MN_AND, 2, 4, 4, ENC_MR, 21h, 00, 00h
dd mnt - MN_AND, 2, 8, 8, ENC_MR, 21h, 00, 00h
dd mnt - MN_AND, 2, 1, 1, ENC_RM, 22h, 00, 00h
dd mnt - MN_AND, 2, 2, 2, ENC_RM, 23h, 00, 66h
dd mnt - MN_AND, 2, 4, 4, ENC_RM, 23h, 00, 00h
dd mnt - MN_AND, 2, 8, 8, ENC_RM, 23h, 00, 00h
dd mnt - MN_AND, 2, 1, 1, ENC_MI, 80h, 04, 00h
dd mnt - MN_AND, 2, 2, 2, ENC_MI, 81h, 04, 66h
dd mnt - MN_AND, 2, 4, 4, ENC_MI, 81h, 04, 00h
dd mnt - MN_AND, 2, 8, 4, ENC_MI, 81h, 04, 00h
dd mnt - MN_AND, 2, 1, 1, ENC_MI, 83h, 04, 00h
dd mnt - MN_AND, 2, 2, 1, ENC_MI, 83h, 04, 66h
dd mnt - MN_AND, 2, 4, 1, ENC_MI, 83h, 04, 00h
dd mnt - MN_AND, 2, 8, 1, ENC_MI, 83h, 04, 00h
dd mnt - MN_OR, 2, 1, 1, ENC_MR, 08h, 00, 00h
dd mnt - MN_OR, 2, 2, 2, ENC_MR, 09h, 00, 66h
dd mnt - MN_OR, 2, 4, 4, ENC_MR, 09h, 00, 00h
dd mnt - MN_OR, 2, 8, 8, ENC_MR, 09h, 00, 00h
dd mnt - MN_OR, 2, 1, 1, ENC_RM, 0Ah, 00, 00h
dd mnt - MN_OR, 2, 2, 2, ENC_RM, 0Bh, 00, 66h
dd mnt - MN_OR, 2, 4, 4, ENC_RM, 0Bh, 00, 00h
dd mnt - MN_OR, 2, 8, 8, ENC_RM, 0Bh, 00, 00h
dd mnt - MN_OR, 2, 1, 1, ENC_MI, 80h, 01, 00h
dd mnt - MN_OR, 2, 2, 2, ENC_MI, 81h, 01, 66h
dd mnt - MN_OR, 2, 4, 4, ENC_MI, 81h, 01, 00h
dd mnt - MN_OR, 2, 8, 4, ENC_MI, 81h, 01, 00h
dd mnt - MN_OR, 2, 1, 1, ENC_MI, 83h, 01, 00h
dd mnt - MN_OR, 2, 2, 1, ENC_MI, 83h, 01, 66h
dd mnt - MN_OR, 2, 4, 1, ENC_MI, 83h, 01, 00h
dd mnt - MN_OR, 2, 8, 1, ENC_MI, 83h, 01, 00h
; dd mnt - MN_SHL, 2, 1, 1, ENC_M1, 0D0h, 04, 00h
; dd mnt - MN_SHL, 2, 2, 1, ENC_M1, 0D1h, 04, 66h
; dd mnt - MN_SHL, 2, 4, 1, ENC_M1, 0D1h, 04, 00h
; dd mnt - MN_SHL, 2, 8, 1, ENC_M1, 0D1h, 04, 00h
dd mnt - MN_SHL, 2, 1, 1, ENC_MC, 0D2h, 04, 00h
dd mnt - MN_SHL, 2, 2, 1, ENC_MC, 0D3h, 04, 66h
dd mnt - MN_SHL, 2, 4, 1, ENC_MC, 0D3h, 04, 00h
dd mnt - MN_SHL, 2, 8, 1, ENC_MC, 0D3h, 04, 00h
dd mnt - MN_SHL, 2, 1, 1, ENC_MI, 0C0h, 04, 00h
dd mnt - MN_SHL, 2, 2, 1, ENC_MI, 0C1h, 04, 66h
dd mnt - MN_SHL, 2, 4, 1, ENC_MI, 0C1h, 04, 00h
dd mnt - MN_SHL, 2, 8, 1, ENC_MI, 0C1h, 04, 00h
dd mnt - MN_SHR, 2, 1, 1, ENC_MC, 0D2h, 05, 00h
dd mnt - MN_SHR, 2, 2, 1, ENC_MC, 0D3h, 05, 66h
dd mnt - MN_SHR, 2, 4, 1, ENC_MC, 0D3h, 05, 00h
dd mnt - MN_SHR, 2, 8, 1, ENC_MC, 0D3h, 05, 00h
dd mnt - MN_SHR, 2, 1, 1, ENC_MI, 0C0h, 05, 00h
dd mnt - MN_SHR, 2, 2, 1, ENC_MI, 0C1h, 05, 66h
dd mnt - MN_SHR, 2, 4, 1, ENC_MI, 0C1h, 05, 00h
dd mnt - MN_SHR, 2, 8, 1, ENC_MI, 0C1h, 05, 00h
dd mnt - MN_INC, 1, 1, 0, ENC_M, 0FEh, 00, 00h
dd mnt - MN_INC, 1, 2, 0, ENC_M, 0FFh, 00, 66h
dd mnt - MN_INC, 1, 4, 0, ENC_M, 0FFh, 00, 00h
dd mnt - MN_INC, 1, 8, 0, ENC_M, 0FFh, 00, 00h
dd mnt - MN_DEC, 1, 1, 0, ENC_M, 0FEh, 01, 00h
dd mnt - MN_DEC, 1, 2, 0, ENC_M, 0FFh, 01, 66h
dd mnt - MN_DEC, 1, 4, 0, ENC_M, 0FFh, 01, 00h
dd mnt - MN_DEC, 1, 8, 0, ENC_M, 0FFh, 01, 00h
dd mnt - MN_NEG, 1, 1, 0, ENC_M, 0F6h, 03, 00h
dd mnt - MN_NEG, 1, 2, 0, ENC_M, 0F7h, 03, 66h
dd mnt - MN_NEG, 1, 4, 0, ENC_M, 0F7h, 03, 00h
dd mnt - MN_NEG, 1, 8, 0, ENC_M, 0F7h, 03, 00h
dd mnt - MN_RET, 0, 0, 0, 0, 0C3h, 00, 00h
dd mnt - MN_NOT, 1, 1, 0, ENC_M, 0F6h, 02, 00h
dd mnt - MN_NOT, 1, 2, 0, ENC_M, 0F7h, 02, 66h
dd mnt - MN_NOT, 1, 4, 0, ENC_M, 0F7h, 02, 00h
dd mnt - MN_NOT, 1, 8, 0, ENC_M, 0F7h, 02, 00h
dd mnt - MN_ADD, 2, 1, 1, ENC_MR, 00h, 00, 00h
dd mnt - MN_ADD, 2, 2, 2, ENC_MR, 01h, 00, 66h
dd mnt - MN_ADD, 2, 4, 4, ENC_MR, 01h, 00, 00h
dd mnt - MN_ADD, 2, 8, 8, ENC_MR, 01h, 00, 00h
dd mnt - MN_ADD, 2, 1, 1, ENC_RM, 02h, 00, 00h
dd mnt - MN_ADD, 2, 2, 2, ENC_RM, 03h, 00, 66h
dd mnt - MN_ADD, 2, 4, 4, ENC_RM, 03h, 00, 00h
dd mnt - MN_ADD, 2, 8, 8, ENC_RM, 03h, 00, 00h
dd mnt - MN_ADD, 2, 1, 1, ENC_MI, 80h, 00, 00h
dd mnt - MN_ADD, 2, 2, 2, ENC_MI, 81h, 00, 66h
dd mnt - MN_ADD, 2, 4, 4, ENC_MI, 81h, 00, 00h
dd mnt - MN_ADD, 2, 8, 4, ENC_MI, 81h, 00, 00h
dd mnt - MN_SUB, 2, 1, 1, ENC_MR, 28h, 00, 00h
dd mnt - MN_SUB, 2, 2, 2, ENC_MR, 29h, 00, 66h
dd mnt - MN_SUB, 2, 4, 4, ENC_MR, 29h, 00, 00h
dd mnt - MN_SUB, 2, 8, 8, ENC_MR, 29h, 00, 00h
dd mnt - MN_SUB, 2, 1, 1, ENC_RM, 2Ah, 00, 00h
dd mnt - MN_SUB, 2, 2, 2, ENC_RM, 2Bh, 00, 66h
dd mnt - MN_SUB, 2, 4, 4, ENC_RM, 2Bh, 00, 00h
dd mnt - MN_SUB, 2, 8, 8, ENC_RM, 2Bh, 00, 00h
dd mnt - MN_SUB, 2, 1, 1, ENC_MI, 80h, 05, 00h
dd mnt - MN_SUB, 2, 2, 2, ENC_MI, 81h, 05, 66h
dd mnt - MN_SUB, 2, 4, 4, ENC_MI, 81h, 05, 00h
dd mnt - MN_SUB, 2, 8, 4, ENC_MI, 81h, 05, 00h
dd mnt - MN_IMUL, 1, 1, 0, ENC_M, 0F6h, 05, 00h
dd mnt - MN_IMUL, 1, 2, 0, ENC_M, 0F7h, 05, 66h
dd mnt - MN_IMUL, 1, 4, 0, ENC_M, 0F7h, 05, 00h
dd mnt - MN_IMUL, 1, 8, 0, ENC_M, 0F7h, 05, 00h
dd mnt - MN_IMUL, 2, 2, 1, ENC_RM, 0FAFh, 00, 66h
dd mnt - MN_IMUL, 2, 4, 1, ENC_RM, 0FAFh, 00, 00h
dd mnt - MN_IMUL, 2, 8, 1, ENC_RM, 0FAFh, 00, 00h
dd mnt - MN_IDIV, 1, 1, 0, ENC_M, 0F6h, 07, 00h
dd mnt - MN_IDIV, 1, 2, 0, ENC_M, 0F7h, 07, 66h
dd mnt - MN_IDIV, 1, 4, 0, ENC_M, 0F7h, 07, 00h
dd mnt - MN_IDIV, 1, 8, 0, ENC_M, 0F7h, 07, 00h
dd mnt - MN_CMP, 2, 1, 1, ENC_MR, 38h, 00, 00h
dd mnt - MN_CMP, 2, 2, 2, ENC_MR, 39h, 00, 66h
dd mnt - MN_CMP, 2, 4, 4, ENC_MR, 39h, 00, 00h
dd mnt - MN_CMP, 2, 8, 8, ENC_MR, 39h, 00, 00h
dd mnt - MN_CMP, 2, 1, 1, ENC_RM, 3Ah, 00, 00h
dd mnt - MN_CMP, 2, 2, 2, ENC_RM, 3Bh, 00, 66h
dd mnt - MN_CMP, 2, 4, 4, ENC_RM, 3Bh, 00, 00h
dd mnt - MN_CMP, 2, 8, 8, ENC_RM, 3Bh, 00, 00h
dd mnt - MN_CMP, 2, 1, 1, ENC_MI, 80h, 07, 00h
dd mnt - MN_CMP, 2, 2, 2, ENC_MI, 81h, 07, 66h
dd mnt - MN_CMP, 2, 4, 4, ENC_MI, 81h, 07, 00h
dd mnt - MN_CMP, 2, 8, 4, ENC_MI, 81h, 07, 00h
dd mnt - MN_LEA, 2, 2, 4, ENC_RM, 8Dh, 00, 6667h
dd mnt - MN_LEA, 2, 2, 8, ENC_RM, 8Dh, 00, 66h
dd mnt - MN_LEA, 2, 4, 4, ENC_RM, 8Dh, 00, 67h
dd mnt - MN_LEA, 2, 4, 8, ENC_RM, 8Dh, 00, 00h
dd mnt - MN_LEA, 2, 8, 4, ENC_RM, 8Dh, 00, 67h
dd mnt - MN_LEA, 2, 8, 8, ENC_RM, 8Dh, 00, 00h
dd mnt - MN_PUSH, 1, 2, 0, ENC_O, 50h, 00, 66h
dd mnt - MN_PUSH, 1, 8, 0, ENC_O, 50h, 00, 00h
dd mnt - MN_PUSH, 1, 2, 0, ENC_M, 0FFh, 06, 66h
dd mnt - MN_PUSH, 1, 8, 0, ENC_M, 0FFh, 06, 00h
dd mnt - MN_PUSH, 1, 1, 0, ENC_I, 06Ah, 00, 00h
dd mnt - MN_PUSH, 1, 2, 0, ENC_I, 06Ah, 00, 66h
dd mnt - MN_PUSH, 1, 4, 0, ENC_I, 06Ah, 00, 00h
dd mnt - MN_POP, 1, 2, 0, ENC_O, 58h, 00, 66h
dd mnt - MN_POP, 1, 8, 0, ENC_O, 58h, 00, 00h
dd mnt - MN_POP, 1, 2, 0, ENC_M, 0FFh, 07, 66h
dd mnt - MN_POP, 1, 8, 0, ENC_M, 0FFh, 07, 00h
dd mnt - MN_POP, 1, 1, 0, ENC_I, 07Ah, 00, 00h
dd mnt - MN_POP, 1, 2, 0, ENC_I, 07Ah, 00, 66h
dd mnt - MN_POP, 1, 4, 0, ENC_I, 07Ah, 00, 00h
dd mnt - MN_SYSCALL, 0, 0, 0, 0, 0F05h, 00, 00h
dd mnt - MN_JMP, 1, 1, 0, ENC_I, 0EBh, 00, 00h
dd mnt - MN_JMP, 1, 4, 0, ENC_I, 0E9h, 00, 00h
dd mnt - MN_JMP, 1, 8, 0, ENC_M, 0FFh, 04, 00h
dd mnt - MN_CALL, 1, 4, 0, ENC_I, 0E8h, 00, 00h
dd mnt - MN_CALL, 1, 8, 0, ENC_M, 0FFh, 02, 00h
dd mnt - MN_XCHG, 2, 1, 1, ENC_MR, 86h, 00, 00h
dd mnt - MN_XCHG, 2, 2, 2, ENC_MR, 87h, 00, 66h
dd mnt - MN_XCHG, 2, 4, 4, ENC_MR, 87h, 00, 00h
dd mnt - MN_XCHG, 2, 8, 8, ENC_MR, 87h, 00, 00h
dd mnt - MN_XCHG, 2, 1, 1, ENC_RM, 86h, 00, 00h
dd mnt - MN_XCHG, 2, 2, 2, ENC_RM, 87h, 00, 66h
dd mnt - MN_XCHG, 2, 4, 4, ENC_RM, 87h, 00, 00h
dd mnt - MN_XCHG, 2, 8, 8, ENC_RM, 87h, 00, 00h
dd mnt - MN_CMOVE, 2, 2, 2, ENC_RM, 0F44h, 00, 66h
dd mnt - MN_CMOVE, 2, 4, 4, ENC_RM, 0F44h, 00, 00h
dd mnt - MN_CMOVE, 2, 8, 8, ENC_RM, 0F44h, 00, 00h
dd mnt - MN_CMOVNE, 2, 2, 2, ENC_RM, 0F45h, 00, 66h
dd mnt - MN_CMOVNE, 2, 4, 4, ENC_RM, 0F45h, 00, 00h
dd mnt - MN_CMOVNE, 2, 8, 8, ENC_RM, 0F45h, 00, 00h
dd mnt - MN_CMOVL, 2, 2, 2, ENC_RM, 0F4Ch, 00, 66h
dd mnt - MN_CMOVL, 2, 4, 4, ENC_RM, 0F4Ch, 00, 00h
dd mnt - MN_CMOVL, 2, 8, 8, ENC_RM, 0F4Ch, 00, 00h
dd mnt - MN_CMOVLE, 2, 2, 2, ENC_RM, 0F4Eh, 00, 66h
dd mnt - MN_CMOVLE, 2, 4, 4, ENC_RM, 0F4Eh, 00, 00h
dd mnt - MN_CMOVLE, 2, 8, 8, ENC_RM, 0F4Eh, 00, 00h
dd mnt - MN_CMOVG, 2, 2, 2, ENC_RM, 0F4Fh, 00, 66h
dd mnt - MN_CMOVG, 2, 4, 4, ENC_RM, 0F4Fh, 00, 00h
dd mnt - MN_CMOVG, 2, 8, 8, ENC_RM, 0F4Fh, 00, 00h
dd mnt - MN_CMOVGE, 2, 2, 2, ENC_RM, 0F4Dh, 00, 66h
dd mnt - MN_CMOVGE, 2, 4, 4, ENC_RM, 0F4Dh, 00, 00h
dd mnt - MN_CMOVGE, 2, 8, 8, ENC_RM, 0F4Dh, 00, 00h
dd mnt - MN_CMOVA, 2, 2, 2, ENC_RM, 0F47h, 00, 66h
dd mnt - MN_CMOVA, 2, 4, 4, ENC_RM, 0F47h, 00, 00h
dd mnt - MN_CMOVA, 2, 8, 8, ENC_RM, 0F47h, 00, 00h
dd mnt - MN_CMOVAE, 2, 2, 2, ENC_RM, 0F43h, 00, 66h
dd mnt - MN_CMOVAE, 2, 4, 4, ENC_RM, 0F43h, 00, 00h
dd mnt - MN_CMOVAE, 2, 8, 8, ENC_RM, 0F43h, 00, 00h
dd mnt - MN_CMOVB, 2, 2, 2, ENC_RM, 0F42h, 00, 66h
dd mnt - MN_CMOVB, 2, 4, 4, ENC_RM, 0F42h, 00, 00h
dd mnt - MN_CMOVB, 2, 8, 8, ENC_RM, 0F42h, 00, 00h
dd mnt - MN_CMOVBE, 2, 2, 2, ENC_RM, 0F46h, 00, 66h
dd mnt - MN_CMOVBE, 2, 4, 4, ENC_RM, 0F46h, 00, 00h
dd mnt - MN_CMOVBE, 2, 8, 8, ENC_RM, 0F46h, 00, 00h
dd mnt - MN_SETE, 1, 1, 0, ENC_M, 0F94h, 00, 00h
dd mnt - MN_SETNE, 1, 1, 0, ENC_M, 0F95h, 00, 00h
dd mnt - MN_SETL, 1, 1, 0, ENC_M, 0F9Ch, 00, 00h
dd mnt - MN_SETLE, 1, 1, 0, ENC_M, 0F9Eh, 00, 00h
dd mnt - MN_SETG, 1, 1, 0, ENC_M, 0F9Fh, 00, 00h
dd mnt - MN_SETGE, 1, 1, 0, ENC_M, 0F9Dh, 00, 00h
dd mnt - MN_SETA, 1, 1, 0, ENC_M, 0F97h, 00, 00h
dd mnt - MN_SETAE, 1, 1, 0, ENC_M, 0F93h, 00, 00h
dd mnt - MN_SETB, 1, 1, 0, ENC_M, 0F92h, 00, 00h
dd mnt - MN_SETBE, 1, 1, 0, ENC_M, 0F96h, 00, 00h
dd mnt - MN_JE, 1, 1, 0, ENC_I, 74h, 00, 00h
dd mnt - MN_JNE, 1, 1, 0, ENC_I, 75h, 00, 00h
dd mnt - MN_JL, 1, 1, 0, ENC_I, 7Ch, 00, 00h
dd mnt - MN_JLE, 1, 1, 0, ENC_I, 7Eh, 00, 00h
dd mnt - MN_JG, 1, 1, 0, ENC_I, 7Fh, 00, 00h
dd mnt - MN_JGE, 1, 1, 0, ENC_I, 7Dh, 00, 00h
dd mnt - MN_JA, 1, 1, 0, ENC_I, 77h, 00, 00h
dd mnt - MN_JAE, 1, 1, 0, ENC_I, 73h, 00, 00h
dd mnt - MN_JB, 1, 1, 0, ENC_I, 72h, 00, 00h
dd mnt - MN_JBE, 1, 1, 0, ENC_I, 76h, 00, 00h
dd mnt - MN_JE, 1, 4, 0, ENC_I, 0F84h, 00, 00h
dd mnt - MN_JNE, 1, 4, 0, ENC_I, 0F85h, 00, 00h
dd mnt - MN_JL, 1, 4, 0, ENC_I, 0F8Ch, 00, 00h
dd mnt - MN_JLE, 1, 4, 0, ENC_I, 0F8Eh, 00, 00h
dd mnt - MN_JG, 1, 4, 0, ENC_I, 0F8Fh, 00, 00h
dd mnt - MN_JGE, 1, 4, 0, ENC_I, 0F8Dh, 00, 00h
dd mnt - MN_JA, 1, 4, 0, ENC_I, 0F87h, 00, 00h
dd mnt - MN_JAE, 1, 4, 0, ENC_I, 0F83h, 00, 00h
dd mnt - MN_JB, 1, 4, 0, ENC_I, 0F82h, 00, 00h
dd mnt - MN_JBE, 1, 4, 0, ENC_I, 0F86h, 00, 00h
op_table_end:
dd 0 ; 231 entries
section .text
panic_abort:
mov rdi, 1
mov rax, 60
syscall
;; Allocator
;; 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
jae panic_abort
ret
dealloc_pages:
mov rax, 11 ; syscall: munmap
mov rdi, rsi ; addr: rsi
mov rsi, rdx ; length: rdx
syscall
cmp rax, -1
jae panic_abort
ret
;; reallocates memory at $rdi[..$rsi] to a new location of size $rdx.
realloc_pages:
sub rsp, 24
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, 24
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 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
ret
.mmap:
pop rdi
pop rsi
call dealloc_pages
ret
;; reallocates a chunk of memory at $rdi of size $rsi and alignment $rdx to a new location of size $rcx
heap_realloc:
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
ret
;; format dil as hex u8 into $ax
format_u8:
xor ecx, ecx
; take upper nibble >> 4
movzx eax, dil
shr eax, 4
; add 0x30
add eax, 0x30
; add (>= 0xA) * 0x7
cmp eax, 0x39
seta cl
lea eax, [eax + ecx * 8]
sub eax, ecx
and edi, 0x0F
add edi, 0x30
cmp edi, 0x39
seta cl
lea edi, [edi + ecx * 8]
sub edi, ecx
shl edi, 8
or ax, di
ret
;; format $edi as hex into $rax
format_u32:
sub rsp, 16
mov dword [rsp], edi
call format_u8
mov word [rsp + 14], ax
mov dil, byte [rsp + 1]
call format_u8
mov word [rsp + 12], ax
mov dil, byte [rsp + 2]
call format_u8
mov word [rsp + 10], ax
mov dil, byte [rsp + 3]
call format_u8
mov word [rsp + 8], ax
mov rax, qword [rsp + 8]
add rsp, 16
ret
print_u8:
call format_u8
sub rsp, 8
mov word [rsp], ax
lea rdi, [rsp]
mov rsi, 2
call print_str
add rsp, 8
ret
print_u32:
call format_u32
sub rsp, 8
mov qword [rsp], rax
lea rdi, [rsp]
mov rsi, 8
call print_str
add rsp, 8
ret
;; print $rsi bytes from $rdi to stdout
print_str:
mov rax, 1
mov rdx, rsi
mov rsi, rdi
mov rdi, 1
syscall
ret
tab: db 9
print_tab:
lea rsi, [rel tab]
mov rdx, 1
mov rax, 1
mov rdi, 1
syscall
ret
nl: db 10
print_nl:
lea rsi, [rel nl]
mov rdx, 1
mov rax, 1
mov rdi, 1
syscall
ret
;; print the $rdi-th entry in the op_table
op_table_entry_print:
sub rsp, 8
lea rax, [rel op_table]
lea rdi, [rdi * 4]
lea rax, [rax + rdi * 8]
mov qword [rsp], rax
lea rdi, [rel mnemonics]
movsx rax, dword [rax]
sub rdi, rax
call strlen
mov rsi, rax
call print_str ; mnemonic
call print_tab
mov rax, qword [rsp]
mov edi, dword [rax + 4]
call print_u8 ; num operands
call print_tab
mov rax, qword [rsp]
mov edi, dword [rax + 8]
call print_u8 ; operand 0 size
call print_tab
mov rax, qword [rsp]
mov edi, dword [rax + 12]
call print_u8 ; operand 1 size
call print_tab
mov rax, qword [rsp]
mov edi, dword [rax + 16]
call print_u8 ; encoding
call print_tab
mov rax, qword [rsp]
mov edi, dword [rax + 20]
call print_u32 ; opcode
call print_tab
mov rax, qword [rsp]
mov edi, dword [rax + 24]
call print_u8 ; rm-bits
call print_tab
mov rax, qword [rsp]
mov edi, dword [rax + 28]
call print_u8 ; prefix
call print_nl
add rsp, 8
ret
print_op_table:
push rbx
xor ebx, ebx
.loop:
cmp ebx, 241
jge .done
mov rdi, rbx
call op_table_entry_print
inc ebx
jmp .loop
.done:
pop rbx
ret
;; rdi: *u8
strlen:
xor rax, rax
.strlen_loop:
cmp byte [rdi + rax], 0
je .strlen_done
inc rax
jmp .strlen_loop
.strlen_done:
ret
;; lhs: (rdi, rsi)
;; rhs: (rdx, rcx)
;; al
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
;; find the first occurrence of $dl in the first $rsi bytes of $rdi
strfind:
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:
mov rax, -1
.found:
ret
;; rdi: src
;; rsi: dst
;; rdx: len
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
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
is_digit:
movzx edi, dil
lea eax, [rdi - 48] ; subtract '0'
cmp eax, 10 ; check if in range 0-9
setb al
ret
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
is_id_start:
call is_alpha
cmp dil, 95
sete cl
or al, cl
ret
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.
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
exit:
mov rax, 60
syscall
;; Operands
%define KIND_NONE 0
%define KIND_REG 1
%define KIND_MEM 2
%define KIND_IMM 3
%define KIND_MEM_LABEL 4 ; a memory operand whose displacement is a label
%define KIND_IMM_LABEL 5 ; an immediate value that is a label
struc operand
.type resd 1
.size resd 1
.reg resq 1
.index resq 1
.scale resd 1
.disp resd 1
endstruc
struc operand_imm
.type resd 1
.size resd 1
.imm resq 1
endstruc
abcd_reg_indices: db 0, 3, 1, 2
rX_suffixes: db 'b', 'w', 'd'
global try_parse_reg
try_parse_reg:
push rbx ; buf
push r12 ; reg_num
push r13 ; size
push rdi
xor r12d, r12d
xor r13d, r13d
mov rbx, rdi
mov dil, byte [rbx]
call is_alpha
jnb .not_reg
cmp dil, 'r'
jne .not_64_or_rX
inc rbx
mov dil, byte [rbx]
call is_digit
jnb .not_rX
inc rbx
sub dil, '0'
movzx r12d, dil
cmp dil, 1
jne .rX_size
shl r12d, 3
lea r12d, [r12d + edi * 2] ; * 10
movzx edi, byte [rbx]
sub edi, '0'
add r12d, edi ; * 10 + digit
inc rbx
.rX_size:
lea rdi, [rel rX_suffixes]
mov rsi, 3
mov dl, byte [rbx]
call strfind
mov r13d, 3
cmp rax, 0
jl .done
mov r13d, eax
inc rbx
jmp .done
.not_rX:
mov r13d, 3 ; 64-bit register
.r_or_e:
mov dx, word [rbx]
add rbx, 2
xor eax, eax
cmp r13d, 0
sete al
add rbx, rax ; spl, bpl, sil, dil have a 3rd character we need to account for
mov r12d, 4 ; rsp
cmp dx, 'sp'
je .done
mov r12d, 5 ; rbp
cmp dx, 'bp'
je .done
mov r12d, 6 ; rsi
cmp dx, 'si'
je .done
mov r12d, 7 ; rdi
cmp dx, 'di'
je .done
mov r12d, -1 ; rip
cmp dx, 'ip'
je .done
sub rbx, rax ; its not spl, bpl, sil, dil, so undo the inc we did earlier
lea rax, [rel abcd_reg_indices]
sub dl, 'a'
cmp dl, 3
ja .not_reg
movzx edx, dl
movzx r12d, byte [rax + rdx] ; rax, rcx, rdx, rbx
.done:
mov dil, byte [rbx]
call is_alphanumeric
jnz .not_reg ; if the next char is alphanumeric, then this is not a valid reg
mov rax, rbx
pop rdi
sub rax, rdi ; number of bytes consumed
movzx rdx, r12d ; reg_num
shl rdx, 32
mov r12d, r13d ; size
or rdx, r12
pop r13
pop r12
pop rbx
ret
.not_reg:
xor rax, rax
xor rdx, rdx
pop rdi
pop r13
pop r12
pop rbx
ret
.not_64_or_rX:
cmp dil, 'e'
jne .not_32
mov r13d, 2 ; 32-bit register
inc rbx
jmp .r_or_e
.not_32:
xor ecx, ecx
cmp byte [rbx + 1], 'l'
sete al
cmp byte [rbx + 2], 'l'
sete cl
xor al, cl
mov r13d, 1
sub r13d, eax
jmp .r_or_e
try_parse_num:
push rbx ; buf
push r12 ; radix
push r13 ; acc
push rdi
mov r12, 10
xor r13d, r13d
xor eax, eax
mov rbx, rdi
; eat leading -
cmp byte [rbx], '-'
sete al
lea rbx, [rbx + rax]
; eat any leading +
cmp byte [rbx], '+'
sete al
lea rbx, [rbx + rax]
mov dil, byte [rbx]
call is_digit
jnb .not_num
cmp dil, '0'
jne .loop
inc rbx
mov dil, byte [rbx]
and dil, 0xdf ; convert to uppercase
cmp dil, 'X'
sete al
lea ecx, [eax + eax * 2]
shl ecx, 1
add r12d, ecx
cmp dil, 'O'
sete al
shl al, 1
sub r12d, eax
cmp dil, 'B'
sete al
shl al, 3
sub r12d, eax
cmp r12d, 10
setne al
lea rbx, [rbx + rax]
.loop:
mov dil, byte [rbx]
test dil, dil
jz .done
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
inc rbx
jmp .loop
.done:
mov rax, rbx
pop rdi
sub rax, rdi ; number of bytes consumed
mov rdx, r13 ; acc
cmp byte [rdi], '-'
jne .positive
neg rdx
.positive:
pop r13
pop r12
pop rbx
ret
.not_num:
xor rax, rax
xor rdx, rdx
pop rdi
pop r13
pop r12
pop rbx
ret
skip_whitespace_rbx:
mov al, byte [rbx]
cmp al, ' '
ja .done
inc rbx
jmp skip_whitespace_rbx
.done:
ret
global try_parse_mem
try_parse_mem:
push rbx ; buf
push r12 ; size
push rdi
mov rbx, rdi ; buf ptr
mov r12, rsi ; output operand ptr
mov dword [r12 + operand.type], KIND_MEM
mov dword [r12 + operand.size], 0
mov qword [r12 + operand.reg], -1
mov qword [r12 + operand.index], -1
mov dword [r12 + operand.scale], 0
mov dword [r12 + operand.disp], 0
mov edi, dword [rbx]
cmp dil, '['
je .bracket
lea rbx, [rbx + 4]
cmp edi, 'byte'
je .bracket
mov dword [r12 + operand.size], 1 ; 16-bit
cmp edi, 'word'
je .bracket
lea rbx, [rbx - 4]
mov dl, byte [rbx]
inc rbx
mov edi, dword [rbx]
xor eax, eax
mov dword [r12 + operand.size], 2 ; 16-bit
cmp dl, 'q'
sete al
cmp dl, 'd'
sete dl
or al, dl
jz .not_mem
add dword [r12 + operand.size], eax ; 32-bit?
cmp edi, 'word'
jne .not_mem
lea rbx, [rbx + 4]
.bracket:
call skip_whitespace_rbx
mov dl, byte [rbx]
cmp dl, '['
jne .not_mem
inc rbx
cmp byte [rbx], `'`
je .disp_lbl
call skip_whitespace_rbx
mov rdi, rbx
call try_parse_reg
test eax, eax
jz .disp
add rbx, rax
call skip_whitespace_rbx
cmp byte [rbx], '*'
jne .base
mov qword [r12 + operand.index], rdx
.scale:
inc rbx
call skip_whitespace_rbx
mov dil, byte [rbx]
mov esi, 10
call to_digit
mov dword [r12 + operand.scale], edx
add rbx, rax
call skip_whitespace_rbx
mov dil, byte [rbx]
cmp dil, ']'
je .done
; TODO: allow for label as displacement
.disp:
mov dil, byte [rbx]
sub dil, `,`
neg dil ; sign
mov al, dil
inc al
test al, -3
jne .not_mem
movsx edi, dil
mov dword [r12 + operand.disp], edi
inc rbx
call skip_whitespace_rbx
.disp_skip_sign:
cmp byte [rbx], `'`
je .disp_lbl
mov rdi, rbx
call try_parse_num
test eax, eax
jz .not_mem
add rbx, rax
mov eax, dword [r12 + operand.disp]
imul edx
mov dword [r12 + operand.disp], eax
call skip_whitespace_rbx
cmp byte [rbx], ']'
je .done
jmp .not_mem
.disp_lbl:
inc rbx ; skip the leading `'`
mov dword [r12 + operand.type], KIND_MEM_LABEL
mov rdi, rbx
call try_parse_label
test eax, eax
jz .not_mem
mov rdi, rbx
mov rsi, rax
add rbx, rax
call fasthash
mov dword [r12 + operand.disp], eax
call skip_whitespace_rbx
cmp byte [rbx], ']'
je .done
jmp .not_mem
.base:
mov qword [r12 + operand.reg], rdx
cmp byte [rbx], ']'
je .done
cmp byte [rbx], '+'
jne .disp
inc rbx
call skip_whitespace_rbx
mov rdi, rbx
call try_parse_reg
test eax, eax
jz .disp_skip_sign
add rbx, rax
mov qword [r12 + operand.index], rdx
call skip_whitespace_rbx
cmp byte [rbx], '*'
je .scale
cmp byte [rbx], '+'
je .disp
cmp byte [rbx], ']'
jne .not_mem
.done:
inc rbx
mov rax, rbx
pop rdi
sub rax, rdi ; number of bytes consumed
mov rdx, r12 ; output operand ptr
pop r12
pop rbx
ret
.not_mem:
xor rax, rax
pop rdi
pop r12
pop rbx
ret
global try_parse_operand
try_parse_operand:
push rbx ; buf
push r12 ; operand ptr
mov rbx, rdi
mov r12, rsi ; output operand ptr
mov dword [r12 + operand.type], KIND_NONE
mov dword [r12 + operand.size], 0
mov qword [r12 + operand.reg], -1
mov qword [r12 + operand.index], -1
mov dword [r12 + operand.scale], 0
mov dword [r12 + operand.disp], 0
mov al, byte [rbx]
cmp al, '['
je .mem
cmp al, `'`
je .label
mov dword [r12 + operand.type], KIND_IMM
call try_parse_num
test eax, eax
jz .not_imm
mov qword [r12 + operand_imm.imm], rdx
test rdx, rdx
jz .done
mov rcx, rdx
neg rdx
cmovs rdx, rcx ; abs(imm)
bsr rcx, rdx ; # of bits required to represent the immediate
mov edx, ecx
cmp edx, 1
adc edx, -1 ; saturating sub
bsr edx, edx ; log2 of the (# of bits - 1) + 1
inc edx
cmp ecx, 2
mov ecx, 1
cmovae ecx, edx ; checked ilog2
mov dword [r12 + operand.size], ecx
jmp .done
.not_imm:
mov dword [r12 + operand.type], KIND_REG
mov rdi, rbx
call try_parse_reg
test eax, eax
jz .not_reg
mov qword [r12 + operand.reg], rdx
mov dword [r12 + operand.size], edx ; low dword of rdx is the size
jmp .done
.not_reg:
.mem:
mov rdi, rbx
mov rsi, r12
call try_parse_mem
test eax, eax
jz .dont_know
jmp .done
.label:
inc rbx ; skip the leading `'`
mov dword [r12 + operand.type], KIND_IMM_LABEL
mov rdi, rbx
call try_parse_label
test eax, eax
jz .dont_know
mov rdi, rbx
mov rsi, rax
mov rbx, rax
call fasthash
mov dword [r12 + operand_imm.imm], eax
mov rax, rbx ; number of bytes consumed
jmp .done
.dont_know:
xor rax, rax
.done:
mov rdx, r12 ; output operand ptr
pop r12
pop rbx
ret
try_parse_label:
push rbx ; buf
push r12 ; label length
mov rbx, rdi
xor r12d, r12d
mov dil, byte [rbx]
call is_id_start
jz .done
inc r12d
.loop:
mov dil, byte [rbx + r12]
call is_id_cont
jz .done
inc r12d
jmp .loop
.done:
mov eax, r12d
pop r12
pop rbx
ret
;; Labels
struc Label
.name_hash resd 1
.offset resd 1
endstruc
struc LabelTable
.size resd 1
.cap resd 1
.labels resq 1
endstruc
;; calculates the hash of a byte sequence in $rdi of length $rsi, returning it in $eax
global fasthash
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
;; push label $rdi with length $rsi and offset $rdx into the label ref table
push_lbl_ref:
mov rcx, [rel label_refs]
jmp push_lbl_common
;; push label $rdi with length $rsi and offset $edx into the label table
push_lbl:
mov rcx, [rel labels]
push_lbl_common:
push rbx
push r12
push r13
mov rbx, rcx
mov r13d, edx ; label offset
call fasthash
mov r12d, eax ; store the hash in r12d
mov eax, dword [rbx + LabelTable.size]
cmp eax, dword [rbx + LabelTable.cap]
jl .has_space
mov rdi, qword [rbx + LabelTable.labels]
mov esi, dword [rbx + LabelTable.cap]
mov edx, 8
mov ecx, esi
shl ecx, 1
call heap_realloc
mov qword [rbx + LabelTable.labels], rax
shl dword [rbx + LabelTable.cap], 1
.has_space:
mov rax, qword [rbx + LabelTable.labels]
mov edi, dword [rbx + LabelTable.size]
lea rax, [rax + rdi * 8]
mov dword [rax + Label.name_hash], r12d
mov dword [rax + Label.offset], r13d
inc dword [rbx + LabelTable.size]
pop r13
pop r12
pop rbx
ret
find_lbl:
push rbx
push r12
push r13
mov rbx, [rel labels]
call fasthash
mov r12d, eax ; store the hash in r12d
xor r13d, r13d
.loop:
cmp r13d, dword [rbx + LabelTable.size]
jge .not_found
mov rax, qword [rbx + LabelTable.labels]
lea rax, [rax + r13 * 8]
cmp dword [rax + Label.name_hash], r12d
je .found
inc r13d
jmp .loop
.found:
mov eax, dword [rax + Label.offset]
jmp .done
.not_found:
xor rax, rax
.done:
pop r13
pop r12
pop rbx
ret
global _entry_print_table
_entry_print_table:
call print_op_table
call exit