344 lines
7 KiB
NASM
344 lines
7 KiB
NASM
;; Common Library Functions
|
|
global strlen
|
|
global strstr
|
|
global strcmp
|
|
global strfind
|
|
global memcpy
|
|
global is_alpha
|
|
global is_digit
|
|
global is_alphanumeric
|
|
global is_id_start
|
|
global is_id_cont
|
|
global to_digit
|
|
global parse_i64
|
|
global parse_i64_cstr
|
|
global exit
|
|
global fasthash
|
|
|
|
section .text
|
|
|
|
;; fn strlen(s: *const u8) -> usize
|
|
strlen:
|
|
xor eax, eax
|
|
.loop:
|
|
cmp byte [rdi + rax], 0
|
|
je .done
|
|
inc rax
|
|
jmp .loop
|
|
.done:
|
|
ret
|
|
|
|
;; find in the first $rsi bytes of $rdi (haystack) the first $rcx bytes of $rdx (needle).
|
|
;; returns the index in $rax, or -1 if not found.
|
|
;; fn strstr(haystack: *const u8, haystack_len: usize, needle: *const u8, needle_len: usize) -> isize
|
|
strstr:
|
|
push rbx
|
|
push r12
|
|
push r13
|
|
mov r13, rdi ; save original haystack pointer
|
|
mov rax, rsi ; remaining haystack length
|
|
.loop_haystack:
|
|
cmp rax, rcx
|
|
jb .not_found ; not enough haystack left for needle
|
|
|
|
xor rbx, rbx ; needle index
|
|
.loop_needle:
|
|
cmp rbx, rcx
|
|
jz .found ; found the needle
|
|
|
|
mov r12b, byte [rdx + rbx] ; needle[needle_index]
|
|
cmp r12b, byte [rdi + rbx] ; haystack[haystack_index + needle_index]
|
|
jne .next_haystack ; mismatch, move to next haystack index
|
|
inc rbx
|
|
jmp .loop_needle
|
|
.next_haystack:
|
|
inc rdi ; move to next haystack index
|
|
dec rax ; decrease remaining haystack length
|
|
jmp .loop_haystack
|
|
.not_found:
|
|
xor eax, eax
|
|
dec rax
|
|
jmp .done
|
|
.found:
|
|
sub rdi, r13 ; calculate the index of the found needle
|
|
mov rax, rdi
|
|
.done:
|
|
pop r13
|
|
pop r12
|
|
pop rbx
|
|
ret
|
|
|
|
|
|
;; fn strcmp(s1: *const u8, s1_len: usize, s2: *const u8, s2_len: usize) -> i32
|
|
strcmp:
|
|
cmp rcx, rsi
|
|
cmovb rsi, rcx ; if rhs is shorter, use its length for the loop
|
|
xor eax, eax
|
|
.strcmp_loop:
|
|
cmp rsi, rax
|
|
jz .strcmp_equal
|
|
movzx ecx, byte [rdx + rax]
|
|
cmp byte [rdi + rax], cl
|
|
lea rax, [rax + 1]
|
|
je .strcmp_loop
|
|
seta al ; al = lhs > rhs
|
|
sbb al, 0 ; al = al - CF
|
|
ret
|
|
.strcmp_equal:
|
|
xor eax, eax
|
|
ret
|
|
|
|
;; fn strfind(s: *const u8, len: usize, c: u8) -> isize
|
|
strfind:
|
|
strch:
|
|
xor eax, eax
|
|
.loop:
|
|
cmp rax, rsi
|
|
jge .not_found
|
|
mov cl, byte [rdi + rax]
|
|
cmp cl, dl
|
|
je .found
|
|
inc rax
|
|
jmp .loop
|
|
.not_found:
|
|
xor eax, eax
|
|
dec rax
|
|
.found:
|
|
ret
|
|
|
|
;; fn memcpy(src: *const u8, dst: *mut u8, len: usize)
|
|
memcpy:
|
|
.loop:
|
|
test rdx, rdx
|
|
jz .done
|
|
mov al, byte [rdi]
|
|
mov byte [rsi], al
|
|
inc rsi
|
|
inc rdi
|
|
dec rdx
|
|
jmp .loop
|
|
.done:
|
|
ret
|
|
|
|
;; fn is_alpha(c: u8) -> bool
|
|
is_alpha:
|
|
movzx eax, dil
|
|
and eax, 0x1fffdf ; ignore bit 5 (case)
|
|
add eax, -65 ; subtract 'A'
|
|
cmp eax, 26 ; check if in range 0-25
|
|
setb al
|
|
ret
|
|
|
|
;; fn is_digit(c: u8) -> bool
|
|
is_digit:
|
|
movzx edi, dil
|
|
lea eax, [rdi - 48] ; subtract '0'
|
|
cmp eax, 10 ; check if in range 0-9
|
|
setb al
|
|
ret
|
|
|
|
;; fn is_alphanumeric(c: u8) -> bool
|
|
is_alphanumeric:
|
|
lea eax, [rdi - 48]
|
|
cmp eax, 10
|
|
setb cl
|
|
and dil, 0xdf
|
|
add dil, -65
|
|
cmp dil, 26
|
|
setb al
|
|
or al, cl
|
|
ret
|
|
|
|
;; fn is_id_start(c: u8) -> bool
|
|
is_id_start:
|
|
call is_alpha
|
|
cmp dil, 95
|
|
sete cl
|
|
or al, cl
|
|
ret
|
|
|
|
;; fn is_id_continue(c: u8) -> bool
|
|
is_id_cont:
|
|
call is_alphanumeric
|
|
cmp dil, 45
|
|
sete cl
|
|
or al, cl
|
|
cmp dil, 95
|
|
sete cl
|
|
or al, cl
|
|
ret
|
|
|
|
;; converts char $dil to a digit with radix $rsi, returning it in $edx. $al is set to 1 if the char is a valid digit, and 0 otherwise.
|
|
;; fn to_digit(c: u8, radix: u8) -> (is_valid: bool, digit: u8)
|
|
to_digit:
|
|
lea eax, [rsi - 2]
|
|
cmp eax, 35
|
|
jae .invalid
|
|
movzx rdi, dil
|
|
lea edx, [rdi - 65] ; 'A' = 65
|
|
and edx, -33 ; convert to uppercase
|
|
add edx, 10 ; 'A' should map to 10
|
|
lea eax, [rdi - 48] ; '0' = 48
|
|
cmp esi, 11
|
|
cmovb edx, eax ; if radix <= 10, then take the difference from '0'
|
|
cmp edi, 58
|
|
cmovb edx, eax ; or if char < '9', then take the difference from '0'
|
|
xor eax, eax
|
|
cmp edx, esi
|
|
setb al ; al = edx < radix
|
|
ret
|
|
.invalid:
|
|
xor eax, eax
|
|
ret
|
|
|
|
;; fn parse_i64(s: *const u8) -> (is_valid: bool, value: i64)
|
|
parse_i64_cstr:
|
|
call strlen
|
|
mov rsi, rax
|
|
jmp parse_i64
|
|
|
|
;; fn parse_i64(s: *const u8, len: usize) -> (is_valid: bool, value: i64)
|
|
parse_i64:
|
|
push rbp
|
|
mov rbp, rsp
|
|
|
|
push rbx ; buf
|
|
push r12 ; radix
|
|
push r13 ; acc
|
|
push r14 ; end
|
|
|
|
push rdi
|
|
|
|
sub rsp, 8
|
|
mov dword [rsp], 1 ; sign = 1
|
|
mov rbx, rdi ; buf
|
|
mov r12, 10 ; radix
|
|
xor r13, r13 ; acc
|
|
|
|
xor eax, eax
|
|
mov r14, rsi
|
|
add r14, rdi ; end = buf + len
|
|
|
|
cmp rbx, r14
|
|
jge .done ; if buf >= end, return 0
|
|
|
|
; eat leading -
|
|
cmp byte [rbx], '-'
|
|
sete al
|
|
lea rbx, [rbx + rax]
|
|
shl eax, 1
|
|
sub dword [rsp], eax
|
|
|
|
cmp rbx, r14
|
|
jge .done
|
|
|
|
; eat leading +
|
|
cmp byte [rbx], '+'
|
|
sete al
|
|
lea rbx, [rbx + rax]
|
|
|
|
cmp rbx, r14
|
|
jge .done
|
|
|
|
mov dil, byte [rbx]
|
|
mov esi, 10
|
|
call to_digit
|
|
jnb .not_numeric
|
|
|
|
; radix
|
|
cmp dil, '0'
|
|
jne .loop
|
|
inc rbx
|
|
|
|
cmp rbx, r14
|
|
jge .done
|
|
|
|
mov dil, byte [rbx]
|
|
and dil, 0xdf ; convert to uppercase
|
|
cmp dil, 'X'
|
|
sete al
|
|
lea ecx, [eax + eax * 2]
|
|
shl ecx, 1 ; multiply by 6
|
|
add r12d, ecx ; radix += 'x' ? 6 : 0
|
|
|
|
cmp dil, 'O'
|
|
sete al
|
|
shl al, 1 ; multiply by 2
|
|
sub r12d, eax ; radix -= 'o' ? 2 : 0
|
|
|
|
cmp dil, 'B'
|
|
sete al
|
|
shl al, 3 ; multiply by 8
|
|
sub r12d, eax ; radix -= 'b' ? 8 : 0
|
|
|
|
cmp r12d, 10
|
|
setne al
|
|
lea rbx, [rbx + rax] ; skip the radix prefix if it was present
|
|
|
|
.loop:
|
|
cmp rbx, r14
|
|
jge .done
|
|
|
|
mov dil, byte [rbx]
|
|
inc rbx
|
|
|
|
cmp dil, '_'
|
|
je .loop
|
|
mov esi, r12d
|
|
call to_digit
|
|
mov ecx, edx
|
|
test al, al
|
|
jz .done
|
|
mov rax, r13
|
|
imul r12
|
|
add rax, rcx
|
|
mov r13, rax
|
|
jmp .loop
|
|
.not_numeric:
|
|
xor eax, eax
|
|
xor edx, edx
|
|
add rsp, 8
|
|
pop rdi
|
|
jmp .epilogue
|
|
.done:
|
|
|
|
mov rdx, rax
|
|
neg r13
|
|
cmp dword [rsp], 0
|
|
cmovl rdx, r13
|
|
|
|
add rsp, 8
|
|
pop rdi
|
|
|
|
sub rbx, rdi
|
|
mov rax, rbx
|
|
|
|
.epilogue:
|
|
pop r14
|
|
pop r13
|
|
pop r12
|
|
pop rbx
|
|
pop rbp
|
|
ret
|
|
|
|
;; fn exit() -> !
|
|
exit:
|
|
xor edi, edi
|
|
mov rax, 60
|
|
syscall
|
|
|
|
;; fn fasthash(s: *const u8, len: usize) -> u32
|
|
fasthash:
|
|
mov eax, 0x811C9DC5 ; FNV-1a 32-bit offset basis
|
|
.loop:
|
|
test rsi, rsi
|
|
jz .done
|
|
movzx ecx, byte [rdi]
|
|
xor eax, ecx
|
|
imul eax, 0x01000193 ; FNV prime
|
|
inc rdi
|
|
dec rsi
|
|
jmp .loop
|
|
.done:
|
|
ret
|