2724 lines
66 KiB
NASM
2724 lines
66 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
|
|
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 <input file>? <output file>
|
|
_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
|