from-scratch/lang/src/int_to_str.asm
2025-10-28 21:43:23 +01:00

221 lines
4.5 KiB
NASM

section .text
global int_to_str
global int_to_str2
global str_to_int
;; rdi: pointer to input string
;; rsi: length of input string
;; dl: radix
;; fn str_to_int(s: *const u8, len: usize, radix: u8) -> i64
str_to_int:
push rbp
mov rbp, rsp
push r12
push r13
sub rsp, 0x10
mov qword [rsp], 0 ; result = 0
mov [rsp + 8], dl ; multiplier = radix
xor r12, r12 ; r12 = index
.loop:
cmp r12, rsi
jge .epilogue
mov rax, [rsp]
movzx rcx, byte [rsp + 8]
xor rdx, rdx
imul rcx
jo .overflow ; overflow
mov [rsp], rax
mov al, byte [rdi + r12]
cmp al, '_'
je .skip_underscore
cmp al, '9'
ja .check_alpha
cmp al, '0'
jb .not_a_digit
sub al, '0'
jmp .digit_parsed
.check_alpha:
cmp al, 'a'
jb .not_a_digit
cmp al, 'f'
ja .not_a_digit
sub al, 'a' - 10
jmp .digit_parsed
.digit_parsed:
cmp al, byte [rsp + 8]
ja .not_a_digit
movzx rax, al
add [rsp], rax
.skip_underscore:
inc r12
jmp .loop
.not_a_digit:
mov qword [rsp], 0
jmp .epilogue
.overflow:
mov rax, -1
shr rax, 1
mov qword [rsp], rax
.epilogue:
mov rax, [rsp]
add rsp, 0x10
pop r13
pop r12
pop rbp
ret
;; rdi: input integer
;; rsi: pointer to output buffer (at least 21 bytes)
;; rdx: length of buffer
;; cl: radix
;; fn int_to_str2(value: i64, buffer: *mut u8, len: usize, radix: u8) -> (*mut u8, usize)
int_to_str2:
push rbp
mov rbp, rsp
push r12
push r13
sub rsp, 0x20
mov [rsp], rdi ; save value
mov [rsp + 8], rdx ; save length
mov [rsp + 16], rcx ; save radix
xor r12, r12 ; r12 = index
test rdx, rdx
jz .epilogue ; if length is 0, return
xor r13, r13
mov rcx, rdi
sar rcx, 63 ; check sign
cmp rcx, 0
je .convert
.negative:
mov byte [rsi], '-' ; write minus sign
neg rdi
inc r13
inc r12
.convert:
test rdi, rdi
jnz .convert_loop
mov byte [rsi + r12], '0'
jmp .epilogue
.convert_loop:
mov rax, [rsp + 8] ; buffer length
cmp r12, rax
jge .epilogue ; prevent buffer overflow
mov rax, rdi
xor rdx, rdx
mov rcx, [rsp + 16] ; base 10
mov cl, cl
div rcx ; rax = rdi / radix, rdx = rdi % radix
cmp rdx, 10
jl .decimal
add rdx, 'a' - 10 ; convert to 'a'-'f'
jmp .phi
.decimal:
add rdx, '0' ; convert digit to ASCII
.phi:
mov byte [rsi + r12], dl
inc r12
mov rdi, rax
test rdi, rdi
jnz .convert_loop
; Reverse the digits
mov rdx, r12
dec r12 ; last digit index
.reverse_loop:
cmp r13, r12
jae .done_reverse
mov al, [rsi + r13]
mov bl, [rsi + r12]
mov [rsi + r13], bl
mov [rsi + r12], al
inc r13
dec r12
jmp .reverse_loop
.done_reverse:
mov rax, rsi
.epilogue:
add rsp, 0x20
pop r13
pop r12
pop rbp
ret
;; Converts integer in rcx to string at rdx
;; rcx: input integer
;; rdx: pointer to output buffer (at least 21 bytes)
int_to_str:
mov rbx, rdx ; rbx = buffer pointer
mov r8, rbx ; r8 = start of buffer
; Check sign
mov rdx, rcx ; copy value
sar rdx, 63 ; rdx = 0 if positive, -1 if negative
cmp rdx, 0
jne .negative
.positive:
mov rsi, rcx
jmp .convert
.negative:
mov byte [rbx], '-' ; write minus sign
inc rbx
neg rcx
mov rsi, rcx
.convert:
; Count digits
mov rax, rsi
mov r9, rbx
mov r10, 0 ; digit count
mov r11, 10
test rax, rax
jnz .digits_loop
mov byte [rbx], '0'
inc rbx
mov r10, 1
jmp .done_digits
.digits_loop:
mov rdx, 0
div r11 ; rax = rax / 10, rdx = rax % 10
add rdx, '0' ; convert digit to ASCII
mov byte [rbx + r10], dl
inc r10
test rax, rax
jnz .digits_loop
.done_digits:
; Digits are in reverse order in [rbx..rbx+r10)
; Reverse them
mov rsi, 0
mov rdi, rbx
mov rdx, r10
dec rdx ; last digit index
.reverse_loop:
cmp rsi, rdx
jae .done_reverse
mov al, [rdi + rsi]
mov bl, [rdi + rdx]
mov [rdi + rsi], bl
mov [rdi + rdx], al
inc rsi
dec rdx
jmp .reverse_loop
.done_reverse:
add rbx, r10 ; move pointer past digits
mov byte [rbx], 0 ; null-terminate
ret