;; 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 heap resq 1 section .data ENC_R_BITS equ 1 ENC_M_BITS equ 2 ENC_I_BITS equ 4 ENC_CL_BITS equ 8 ENC_ONE_BITS equ 16 ENC_O_SIGNAL_BITS equ 32 ENC_RM_BITS equ ENC_R_BITS | ENC_M_BITS ENC_O_BITS equ ENC_R_BITS | ENC_O_SIGNAL_BITS ENC_DISCARD_BITS equ ENC_CL_BITS | ENC_ONE_BITS ENC_RM equ ENC_R_BITS | ENC_RM_BITS << 8 ENC_MR equ ENC_RM_BITS | ENC_R_BITS << 8 ENC_MI equ ENC_RM_BITS | ENC_I_BITS << 8 ENC_OI equ ENC_O_BITS | ENC_I_BITS << 8 ENC_M equ ENC_RM_BITS ENC_O equ ENC_O_BITS ENC_I equ ENC_I_BITS ; r/m with cl ENC_MC equ ENC_RM_BITS | ENC_CL_BITS << 8 ; r/m with 1 ENC_M1 equ ENC_RM_BITS | ENC_ONE_BITS << 8 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_OR db "or", 0 MN_XOR db "xor", 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 MNEMONICS_SIZE equ $ - mnemonics_table ;; Opcodes support up to 2 operands struc OpEntry .mnemonic_offset resd 1 .num_operands resd 1 .op1_size resd 1 .op2_size resd 1 .encoding resd 1 .opcode resd 1 .reg_bits resd 1 .prefix resd 1 endstruc 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, 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_MOV, 2, 8, 8, ENC_OI, 0B8h, 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 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 the first $rsi bytes of the contents of $rdi as hex print_u8s_le: test esi, esi jz .ret push rbx push r12 push r13 sub rsp, 16 xor r12d, r12d mov rbx, rdi mov r13d, esi .loop: cmp r12d, r13d jge .done movzx edi, bl call format_u8 mov word [rsp + r12 * 2], ax inc r12d shr rbx, 8 jmp .loop .done: lea rdi, [rsp] mov esi, r13d shl rsi, 1 call print_str add rsp, 16 pop r13 pop r12 pop rbx .ret: ret print_non_zero_u8s_le: test rdi, rdi jz .ret push rbx push r12 xor r12d, r12d mov rbx, rdi sub rsp, 16 .loop: movzx edi, bl test edi, edi jz .done call format_u8 mov word [rsp + r12 * 2], ax inc r12d shr rbx, 8 jmp .loop .done: lea rdi, [rsp] mov rsi, r12 shl rsi, 1 call print_str add rsp, 16 pop r12 pop rbx .ret: ret swap_bytes_and_shift_in_place: bswap rdi mov rcx, 64 bsf rcx, rdi and cl, 56 shr rdi, cl ret print_non_zero_u8s_be: test rdi, rdi jz .done call swap_bytes_and_shift_in_place xor eax, eax call print_non_zero_u8s_le .done: 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 ;; Common Library Functions ;; rdi: *u8 strlen: xor rax, rax .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. 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: mov rax, -1 jmp .done .found: sub rdi, r13 ; calculate the index of the found needle mov rax, rdi .done: pop r13 pop r12 pop rbx 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: xor edi, edi mov rax, 60 syscall ;; Operands %define KIND_NONE 0 %define KIND_REG 1 %define KIND_MEM 2 %define KIND_IMM 4 %define KIND_LABEL_FLAG 8 ; a memory operand whose displacement is a label %define KIND_MEM_LABEL KIND_LABEL_FLAG | KIND_MEM ; an immediate value that is a label %define KIND_IMM_LABEL KIND_LABEL_FLAG | KIND_IMM 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 struc register .size resd 1 .num resd 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] test al, al jz .done 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 dword [r12 + operand.reg + register.size], 0 mov dword [r12 + operand.reg + register.num], 5 ; default to EBP (no base) 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 ; 32-bit cmp dl, 'q' sete al cmp dl, 'd' sete dl or dl, al jz .not_mem add dword [r12 + operand.size], eax ; 64-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] test al, al jz .done 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 shr edx, 3 ; divide by 8 to get the number of bytes required jz .done bsr edx, edx ; log2 of the (# of bytes) + 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 dword [r12 + operand.size], 2 ; 32-bit 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 ;; Instruction struc Instruction .mnemonic_offs resd 1 .num_operands resd 1 .operand1 resb operand_size .operand2 resb operand_size endstruc global try_parse_inst try_parse_inst: push rbx ; buf push r13 ; inst ptr push rdi mov rbx, rdi mov r13, rsi ; output inst ptr mov dword [r13 + Instruction.num_operands], 0 mov dword [r13 + Instruction.mnemonic_offs], 0 mov qword [r13 + Instruction.operand1], 0 mov qword [r13 + Instruction.operand2], 0 call skip_whitespace_rbx mov rdi, rbx call try_parse_label ; parse mnemonic test eax, eax jz .not_inst lea rdi, [rel mnemonics] mov esi, MNEMONICS_SIZE mov rdx, rbx mov ecx, eax add rbx, rax call strstr cmp rax, -1 je .not_inst mov dword [r13 + Instruction.mnemonic_offs], eax call skip_whitespace_rbx mov rdi, rbx lea rsi, [r13 + Instruction.operand1] call try_parse_operand test eax, eax jz .done mov dword [r13 + Instruction.num_operands], 1 add rbx, rax call skip_whitespace_rbx cmp byte [rbx], `,` jne .done inc rbx call skip_whitespace_rbx mov rdi, rbx lea rsi, [r13 + Instruction.operand2] call try_parse_operand test eax, eax jz .done mov dword [r13 + Instruction.num_operands], 2 add rbx, rax .done: mov rax, rbx pop rdi sub rax, rdi ; number of bytes consumed mov rdx, r13 ; output inst ptr pop r13 pop rbx ret .not_inst: xor rax, rax mov rdx, r13 ; output inst ptr pop rdi pop r13 pop rbx ret ;; find the op_table entry returning the pointer to the entry in $rax, or 0 if not found. ;; inputs: ;; $edi: mnemonic offset ;; $esi: encoding ;; $edx: size mask ;; $ecx: number of operands find_op_table_entry: push rbx lea rax, [rel op_table] lea rbx, [rel op_table_end] neg edi .loop: cmp rax, rbx jge .not_found cmp edi, dword [rax] ; mnemonic offset jne .next cmp ecx, dword [rax + 4] ; number of operands jne .next mov r8d, dword [rax + 12] ; op2 size shl r8d, 8 or r8d, dword [rax + 8] ; op1 size mov r9d, edx and r9d, r8d cmp r9d, r8d jne .next mov r8d, dword [rax + 16] ; encoding cmp r8d, esi je .found mov r9d, esi test r8d, 0xff00 jz .enc1 and r9d, 0xff00 and r9d, r8d jz .next .enc1: mov r9d, esi and r9d, 0xff and r9d, r8d jnz .found .next: add rax, 32 jmp .loop .not_found: xor eax, eax .found: pop rbx ret ;; encode the instruction at $rdi into ofile global encode_inst encode_inst: push rbx push r12 push r13 mov rbx, rsi ; bytes written mov r12, rdi ; inst ptr ; calculate the encoding xor esi, esi xor eax, eax ; we have to check if the 2nd operand, if it exists, is CL or 1 cmp dword [r12 + Instruction.num_operands], 2 jb .op1 ; calculate size mask of op2. ; for reg and mem operands, this is 1 << .size. ; for imm operands, this is (!0 << .size) && 0xff mov ecx, dword [r12 + Instruction.operand2 + operand.size] mov eax, 1 shl eax, cl cmp dword [r12 + Instruction.operand2 + operand.type], KIND_IMM jne .size_done_op2 xor eax, eax sub eax, 1 shl eax, cl and eax, 0xf .size_done_op2: shl eax, 8 mov esi, dword [r12 + Instruction.operand2 + operand.type] shl esi, 8 cmp dword [r12 + Instruction.operand2 + operand.type], KIND_REG jne .check_imm_one cmp qword [r12 + Instruction.operand2 + operand.reg], 1 jne .op1 or esi, ENC_CL_BITS << 8 .check_imm_one: cmp dword [r12 + Instruction.operand2 + operand.type], KIND_IMM jne .op1 cmp qword [r12 + Instruction.operand2 + operand_imm.imm], 1 jne .op1 or esi, ENC_ONE_BITS << 8 .op1: or esi, dword [r12 + Instruction.operand1 + operand.type] ; calculate size mask of op1. ; for reg and mem operands, this is 1 << .size. ; for imm operands, this is (!0 << .size) & 0xf mov ecx, dword [r12 + Instruction.operand1 + operand.size] mov edx, 1 shl edx, cl cmp dword [r12 + Instruction.operand1 + operand.type], KIND_IMM jne .size_done xor edx, edx sub edx, 1 shl edx, cl and edx, 0xf .size_done: xor edx, eax mov ecx, dword [r12 + Instruction.num_operands] mov edi, dword [r12 + Instruction.mnemonic_offs] call find_op_table_entry test rax, rax jz .done mov r13, rax ; pointer to the op_table entry mov eax, dword [r13 + OpEntry.op1_size] mov dword [r12 + Instruction.operand1 + operand.size], eax mov eax, dword [r13 + OpEntry.op2_size] mov dword [r12 + Instruction.operand2 + operand.size], eax ; write the prefix, if any cmp dword [r13 + OpEntry.prefix], 0 je .rex movzx edi, byte [r13 + 24] mov esi, 1 call write_bytes .rex: ; shuffle operands into reg-mem order lea rdi, [r12 + Instruction.operand1] lea rsi, [r12 + Instruction.operand2] ; discard rsi if it is not used in the encoding test dword [r13 + OpEntry.encoding], ENC_DISCARD_BITS << 8 jz .no_discard xor rsi, rsi .no_discard: test dword [r13 + OpEntry.encoding], ENC_M_BITS jz .no_swap xchg rdi, rsi .no_swap: sub rsp, 16 mov qword [rsp], rdi ; reg operand mov qword [rsp + 8], rsi ; mem operand ; compute the REX byte, if any call compute_rex mov edi, eax call write_non_zero_bytes ; write the opcode and any ENC_O bits xor esi, esi test dword [r13 + OpEntry.encoding], ENC_O_SIGNAL_BITS jz .no_o_bits mov rsi, qword [rsp] mov esi, dword [rsi + operand.reg + register.num] and esi, 0x7 .no_o_bits: mov edi, dword [r13 + OpEntry.opcode] call write_opcode_with_bits cmp dword [r13 + OpEntry.num_operands], 0 jz .ref test dword [r13 + OpEntry.encoding], ENC_O_SIGNAL_BITS | ENC_O_SIGNAL_BITS << 8 jnz .imm ; compute the ModRM and SIB bytes, if any mov rdi, qword [rsp] mov rsi, qword [rsp + 8] mov edx, dword [r13 + OpEntry.reg_bits] call compute_modrm_sib push rax shl edx, 8 ; shift SIB or eax, edx ; combine modRM and SIB mov edi, eax call write_non_zero_bytes pop rax mov cl, al and cl, 0b11000111 cmp cl, 0b00000101 ; if modRM.mod == 00b and modRM.rm == 101b, then we have a disp32 jne .disp xor edi, edi mov esi, 4 call write_bytes jmp .imm .disp: shr eax, 6 dec eax cmp eax, 2 jae .imm mov rdi, qword [rsp + 8] mov edi, dword [rdi + operand.disp] call write_disp .imm: xor eax, eax ; clear return mov rdi, qword [rsp] mov rsi, qword [rsp + 8] ; write immediate, if any test dword [rsi + operand.type], KIND_IMM jnz .imm1 mov rsi, rdi test dword [rsi + operand.type], KIND_IMM jz .ref .imm1: push rsi test dword [rsi + operand.type], KIND_LABEL_FLAG jz .imm2 mov edi, dword [rsi + operand_imm.imm] ; hash mov rsi, rbx ; write offset lea rdx, [rbx + 4] ; disp offset (after the imm32 is written) call push_lbl_ref_by_hash .imm2: pop rsi mov rdi, qword [rsi + operand_imm.imm] mov esi, dword [rsi + operand.size] call write_imm .ref: mov rdi, qword [rsp + 8] add rsp, 16 test dword [rdi + operand.type], KIND_LABEL_FLAG jz .done test dword [rdi + operand.type], KIND_MEM jz .done lea rsi, [rbx - 4] sub rsi, rax ; write offset (before the immediate) mov rdx, rbx ; disp offset mov edi, dword [rdi + operand.disp] call push_lbl_ref_by_hash .done: mov rax, rbx ; bytes written pop r13 pop r12 pop rbx ret write_imm: jmp write_bytes write_disp: cmp edi, 0xff ; if disp > 0xff, write 4 bytes, else write 1 byte seta sil movzx esi, sil lea esi, [rsi + rsi * 2] inc esi jmp write_bytes write_bytes: push rdi lea rdi, [rsp] call ofile_write_bytes add rbx, rax pop rdi ret write_opcode_with_bits: or rdi, rsi call swap_bytes_and_shift_in_place jmp write_non_zero_bytes ;; writes the contents of the $rdi register until the first zero byte. write_non_zero_bytes: push rdi lea rdi, [rsp] call strlen mov esi, eax call ofile_write_bytes add rbx, rax pop rdi ret ;; compute the modRM and SIB bytes for the operands $rdi and $rsi, in reg-mem order, with the $rdx reg-bits compute_modrm_sib: xor eax, eax ; modrm mov eax, edx shl eax, 3 ; reg-bits xor edx, edx ; sib cmp dword [rdi + operand.type], KIND_REG jne .check_mem mov eax, dword [rdi + operand.reg + register.num] and eax, 7 shl eax, 3 ; reg-bits .check_mem: test rsi, rsi jz .done_mem mov ecx, dword [rsi + operand.reg + register.num] and ecx, 7 or eax, ecx ; r/m bits or eax, 0xC0 ; mod = 11b cmp dword [rsi + operand.type], KIND_REG je .done_mem and eax, 0x37 ; clear mod bits, mod = 00b ; rip-relative is mod = 00b, r/m = 101b, and no SIB byte, even though disp32 cmp dword [rsi + operand.type], KIND_MEM_LABEL je .done_mem test dword [rsi + operand.type], KIND_MEM jz .done_mem cmp dword [rsi + operand.disp], 0 jne .sib ; mod = 01b or 10b mov ecx, eax and ecx, 7 ; if mod == 0 and r/m == 100b (RSP or R12), then we need a SIB byte, but no displacement cmp ecx, 4 je .sib_no_disp ; if mod == 0 && r/m & 111 == 101 (rbp or r13), then we need a displacement, even if its 0 cmp ecx, 5 jne .done_mem .sib: cmp dword [rsi + operand.disp], 0xff seta cl mov edx, 1 shl edx, cl shl edx, 6 ; mod = 01b or 10b or eax, edx .sib_no_disp: xor edx, edx ; sib ; copy r/m bits to SIB base bits mov edx, eax and edx, 7 and eax, 0b11111000 ; clear the r/m bits or eax, 0b00000100 ; set the r/m bits to 100b (SIB follows) or edx, 0b00100000 ; index = 100b (no index) mov ecx, dword [rsi + operand.scale] and ecx, 3 shl ecx, 6 ; scale bits or edx, ecx mov ecx, dword [rsi + operand.index + register.num] cmp ecx, -1 je .done_mem and ecx, 7 shl ecx, 3 ; index bits or edx, ecx .done_mem: ret ;; compute the rex bits for the operands $rdi and $rsi, in reg-mem order compute_rex: xor eax, eax test rdi, rdi jz .check_mem cmp dword [rdi + operand.type], KIND_REG jne .check_mem test dword [rdi + operand.reg + register.num], 8 setne dl shl dl, 2 ; REX.R or al, dl cmp dword [rdi + operand.size], 8 ; this is no longer in log2 form setnb dl shl dl, 3 ; REX.W or al, dl .check_mem: test rsi, rsi jz .done test dword [rsi + operand.type], KIND_REG | KIND_MEM jz .done test dword [rsi + operand.reg + register.num], 8 setne dl or al, dl ; REX.B cmp dword [rsi + operand.size], 8 ; this is no longer in log2 form setnb dl shl dl, 3 ; REX.W or al, dl test dword [rsi + operand.type], KIND_MEM jz .done mov ecx, dword [rsi + operand.index + register.num] cmp ecx, -1 je .done test ecx, 8 setne dl shl dl, 1 ; REX.X or al, dl .done: test al, al setne dl shl dl, 6 ; REX 4Xh or al, dl ret ;; Input and Output struc OFile .fd resd 1 .offset resd 1 endstruc struc IFile .fd resd 1 .peeked_byte resb 1 .peeked_flags resb 1 .pad resb 2 .buffer resq 1 .buffer_len resq 1 .buffer_cursor resq 1 endstruc %define DID_PEEK 1 %define DID_PEEK_SOME 2 %define DID_PEEK_AND_SOME 3 section .bss ifile resb 0x20 ofile resb 0x20 buf resb 0x100 section .text ;; seek to offset $rsi in file $rdi fseek: mov rax, 8 ; syscall: lseek mov rdx, 0 ; SEEK_SET syscall ret ;; write $rsi bytes from buffer $rdi to file $rdx, returning the number of bytes written in $rax fwrite_bytes: mov rax, 1 ; syscall: write mov rcx, rdx ; fd mov rdx, rsi ; count mov rsi, rdi ; buffer mov rdi, rcx ; fd syscall ret ofile_write_bytes: lea rdx, [rel ofile] mov edx, dword [rdx + OFile.fd] jmp fwrite_bytes ;; open file $rdi with flags $rsi and mode $rdx, returning the file descriptor in $rax, or -1 on error. fopen: mov rax, 2 ; syscall: open syscall ret close: mov rax, 3 ; syscall: close syscall ret init_ifile_with_fd: mov eax, edi jmp init_ifile.with_fd ;; open the input file $rdi and initialise the ifile structure. init_ifile: xor esi, esi xor edx, edx call fopen .with_fd: lea rdi, [rel ifile] mov dword [rdi + IFile.fd], eax mov byte [rdi + IFile.peeked_flags], 0 mov edi, 0x1000 mov esi, 0x8 call heap_alloc lea rdi, [rel ifile] mov qword [rdi + IFile.buffer], rax mov qword [rdi + IFile.buffer_len], 0x1000 mov qword [rdi + IFile.buffer_cursor], 0 ret getc_inner: lea rax, [rel ifile] mov cl, byte [rax + IFile.peeked_flags] ; peeked test cl, DID_PEEK jz .iter_next test cl, DID_PEEK_SOME ; peeked_some setnz dl and edx, 1 mov al, byte [rax + IFile.peeked_byte] ret .iter_next: mov rdi, qword [rax + IFile.buffer_cursor] ; buf_cur cmp rdi, qword [rax + IFile.buffer_len] ; buf_len jae .read inc qword [rax + IFile.buffer_cursor] ; buf_cur++ mov rsi, qword [rax + IFile.buffer] ; buf mov al, byte [rsi + rdi] mov edx, 1 ; peeked_some = true ret .read: mov rdi, qword [rax + IFile.fd] ; fd mov rsi, qword [rax + IFile.buffer] ; buf mov rdx, 0x1000 ; read 0x1000 bytes push rax mov rax, 0 ; syscall: read syscall cmp rax, 0 jle .eof mov rdi, rax ; number of bytes read pop rax mov qword [rax + IFile.buffer_len], rdi ; buf_len = bytes read mov qword [rax + IFile.buffer_cursor], 0 ; buf_cur = 0 jmp .iter_next .eof: pop rax xor dl, dl ; peeked_some = false ret ;; returns the next byte from the input without advancing the cursor. returns 1 or 0 in dl depending on EOF peekc: call getc_inner lea rdi, [rel ifile] movzx ecx, dl shl ecx, 1 inc ecx mov byte [rdi + IFile.peeked_byte], al ; peeked_byte = c mov byte [rdi + IFile.peeked_flags], cl ; peeked = true, peeked_some = dl ret ;; returns the next byte from the input. returns 1 or 0 in dl depending on EOF getc: call getc_inner lea rdi, [rel ifile] mov word [rdi + IFile.peeked_flags], 0 and eax, 0xff ret ifile_skip_whitespaces: call peekc cmp al, ' ' ja .done call getc jmp ifile_skip_whitespaces .done: ret ifile_skip_comment: call peekc cmp al, `;` jne .done .loop: call getc cmp al, 10 jne .loop call ifile_skip_whitespaces jmp ifile_skip_comment .done: ret ;; read a line from the input file into `buf` read_line: push rbx ; skip whitespaces, empty lines and comments call ifile_skip_whitespaces ; guaranteed to be at the start of a token or EOF lea rbx, [rel buf] .loop: call getc test dl, dl jz .done test al, al jz .done cmp al, 10 je .done mov byte [rbx], al inc rbx jmp .loop .done: mov byte [rbx], 0 lea rax, [rel buf] sub rbx, rax mov rax, rbx pop rbx ret ;; parse buffer $rdi, bytes written in $rsi, return updated bytes written. parse_line: push rbx ; buf push r12 ; line length push rdi mov rbx, rdi mov r12, rsi ; bytes written call skip_whitespace_rbx cmp byte [rbx], '@' je .label mov rdi, rbx sub rsp, 72 lea rsi, [rsp] call try_parse_inst test eax, eax jz .done_inst lea rdi, [rsp] mov rsi, r12 call encode_inst mov r12, rax .done_inst: add rsp, 72 jmp .done .label: inc rbx ; skip the leading '@' mov rdi, rbx call try_parse_label test eax, eax jz .done mov rdi, rbx mov rsi, rax add rbx, rax mov rdx, r12 ; label offset call push_lbl .done: mov rax, r12 pop rdi pop r12 pop rbx ret ;; Labels 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 struc Label .name_hash resd 1 .offset resd 1 endstruc struc LabelRef .name_hash resd 1 .write_offset resd 1 .disp_offset resd 1 endstruc section .data labels dq 0, 0, 0 label_refs dq 0, 0, 0 section .text 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. vec_reserve: 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 ret ;; 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 global init_label_tables init_label_tables: mov edi, 0x100 mov esi, 4 call heap_alloc lea rdi, [rel labels] mov dword [rdi + Vec.size], 0 mov dword [rdi + Vec.cap], 0x100 / Label_size mov dword [rdi + Vec.ele_size], Label_size mov dword [rdi + Vec.ele_align], 4 mov qword [rdi + Vec.data], rax mov edi, 0x180 mov esi, 4 call heap_alloc lea rdi, [rel label_refs] mov dword [rdi + Vec.size], 0 mov dword [rdi + Vec.cap], 0x180 / LabelRef_size mov dword [rdi + Vec.ele_size], LabelRef_size mov dword [rdi + Vec.ele_align], 4 mov qword [rdi + Vec.data], rax ret ;; push label hash $edi with write offset $esi and disp offset $edx into the label ref table. push_lbl_ref_by_hash: push rbx push r12 push r13 push r14 lea rbx, [rel label_refs] mov r13d, esi ; label write offset mov r12d, edi ; label hash mov r14d, edx ; label disp offset jmp push_lbl_ref.hash_in_r12d ;; push label $rdi with length $rsi, write offset $edx and disp offset $ecx into the label ref table. push_lbl_ref: push rbx push r12 push r13 push r14 lea rbx, [rel label_refs] mov r13d, edx ; label write offset mov r14d, ecx ; label disp offset call fasthash mov r12d, eax ; hash .hash_in_r12d: mov rdi, rbx mov rsi, 1 call vec_reserve mov rdi, rbx mov esi, dword [rbx + Vec.size] call vec_get_nth mov dword [rax + LabelRef.name_hash], r12d mov dword [rax + LabelRef.write_offset], r13d mov dword [rax + LabelRef.disp_offset], r14d inc dword [rbx + Vec.size] pop r14 pop r13 pop r12 pop rbx ret ;; push label $rdi with length $rsi and offset $edx into the label table push_lbl: push rbx push r12 push r13 lea rbx, [rel labels] mov r13d, edx ; label offset call fasthash mov r12d, eax ; hash mov rdi, rbx mov rsi, 1 call vec_reserve mov rdi, rbx mov esi, dword [rbx + Vec.size] call vec_get_nth mov dword [rax + Label.name_hash], r12d mov dword [rax + Label.offset], r13d inc dword [rbx + Vec.size] pop r13 pop r12 pop rbx ret find_lbl_by_hash: push rbx push r12 push r13 lea rbx, [rel labels] mov r12d, edi ; label hash jmp find_lbl.by_hash find_lbl: push rbx push r12 push r13 lea rbx, [rel labels] call fasthash mov r12d, eax ; store the hash in r12d .by_hash: xor r13d, r13d .loop: cmp r13d, dword [rbx + Vec.size] jge .not_found mov rax, qword [rbx + Vec.data] 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: mov rax, -1 .done: pop r13 pop r12 pop rbx ret fixup_lbl_refs: push rbx ; label ref table push r12 ; index push r13 ; ref offset push r14 xor r12d, r12d .loop: lea rbx, [rel label_refs] cmp r12d, dword [rbx + Vec.size] jge .done mov rax, qword [rbx + Vec.data] lea rcx, [r12 + r12 * 2] shl rcx, 2 ; multiply by 12 add rax, rcx inc r12d mov edi, dword [rax + LabelRef.name_hash] mov r13d, dword [rax + LabelRef.write_offset] mov r14d, dword [rax + LabelRef.disp_offset] call find_lbl_by_hash cmp rax, -1 je .loop mov esi, eax ; label source offset sub eax, r14d ; displacement mov r14d, eax lea rdi, [rel ofile] mov edi, dword [rdi + OFile.fd] ; fd mov esi, r13d ; write offset call fseek mov edi, r14d ; displacement mov esi, 4 ; size call write_bytes jmp .loop .done: pop r14 pop r13 pop r12 pop rbx ret global _entry_print_table _entry_print_table: call print_op_table call exit a.out: db "a.out", 0 global _entry_as0 ;; /as0 ? _entry_as0: mov eax, dword [rsp] lea rcx, [rsp + 8] sub rsp, 24 mov qword [rsp], rcx ; argv mov qword [rsp + 8], rax ; argc mov rdi, [rcx + 8] ; argv[1] (input file) mov qword [rsp + 16], rdi ; output file mov edi, 0 ; stdin cmp eax, 2 ; check if we have an input file jl .init_ifile mov rdi, [rcx + 16] ; argv[2] (output file) mov qword [rsp + 16], rdi ; output file mov rdi, [rcx + 8] ; argv[1] xor esi, esi ; O_RDONLY xor edx, edx ; mode = 0 call fopen mov edi, eax ; fd .init_ifile: call init_ifile_with_fd ;; mov rdi, qword [rsp + 16] ; output file lea rdi, [rel a.out] ; default output file mov esi, 0x241 ; O_WRONLY | O_CREAT | O_TRUNC mov edx, 0x1b6 ; mode = 0666 call fopen xor edi, edi sub edi, eax jg panic_abort lea rdi, [rel ofile] mov dword [rdi + OFile.fd], eax mov dword [rdi + OFile.offset], 0 call init_label_tables xor ebx, ebx ; bytes written .loop: call read_line test rax, rax jz .done lea rdi, [rel buf] mov rsi, rbx ; bytes written call parse_line mov rbx, rax ; update bytes written jmp .loop .done: call fixup_lbl_refs lea rdi, [rel ofile] mov edi, dword [rdi + OFile.fd] call close call exit