%include "src/constants.asm" extern clamp section .bss cnvtBuff resb 67 cnvtBuffRev resb 67 section .text global atoi global itoa global utoa ;----- atoi(*str[]) -----; ; Converts numeric string to an integer ; Return value: Converted integer or 0 if input was invalid ; Used registers: ; rax* (ret) ; rdi* (arg) Numeric string to convert ; rsi* Stores base ; rdx* Stores max usable ascii number ; rcx* Loop counter ; r8* tmp storage rsi (cmov) >> Loop counter for calculating base^n ; r9* tmp storage rdx (cmov) >> Calculated base^n ; r10* Stores return value (since RAX is used by IMUL) atoi: xor rax, rax xor rcx, rcx xor r10, r10 cmp byte [rdi], EOS je .quit ; -- Test if negative (increase pointer to str, result is always calculated positively. Once reaching '-' again, then NEG) cmp byte [rdi], '-' jne .noMinusSign inc rdi inc rcx ;make sure .calculateLoop includes minus sign ; -- Determine base .noMinusSign: ; hex? mov r8, 16 cmp word [rdi], '0x' cmove rsi, r8 je .skipBaseNotation ; octal? mov r8, 8 mov r9, '7' cmp word [rdi], '0o' cmove rsi, r8 cmove rdx, r9 je .skipBaseNotation ; binary? mov r8, 2 mov r9, '1' cmp word [rdi], '0b' cmove rsi, r8 cmove rdx, r9 je .skipBaseNotation ; nope, decimal mov rsi, 10 mov rdx, '9' jmp .getLen .skipBaseNotation: cmp rcx, 1 ;If '-' was found, using base is invalid je .quit add rdi, 2 ; -- Get length of numeric string + test if valid .getLen: .getLenLoop: cmp byte [rdi], EOS je .calculate cmp byte [rdi], '0' jb .quit ; Test if valid cmp rsi, 16 je .getLenLoop_testHex cmp byte [rdi], dl ja .quit jmp .getLenLoop_cnt .getLenLoop_testHex: cmp byte [rdi], 'F' ja .quit cmp byte [rdi], 'A' jb .testHex_isNum jmp .getLenLoop_cnt .testHex_isNum: cmp byte [rdi], '9' ja .quit .getLenLoop_cnt: inc rdi inc rcx jmp .getLenLoop ; -- Calculate the number .calculate: dec rdi ;don't point to EOS xor r8, r8 .calculateLoop: cmp byte [rdi], '-' je .wrapup_neg ; Calculate base^n .calcBase: test r8, r8 jz .calcBase_firstLoop cmp r8, 1 je .calcBase_secondLoop jmp .calcBase_otherLoops .calcBase_firstLoop: mov r9, 1 jmp .calcNum .calcBase_secondLoop: mov r9, rsi jmp .calcNum .calcBase_otherLoops: mov rax, rsi mov r9, r8 dec r9 .calcBaseLoop: imul rsi dec r9 test r9, r9 jnz .calcBaseLoop mov r9, rax ; Calculate number .calcNum: xor rax, rax mov al, byte [rdi] cmp al, 'A' jb .calcNum_notHex sub al, 55 ;'A'-10 (65-10) jmp .calcNum_cnt .calcNum_notHex: sub al, '0' .calcNum_cnt: imul r9 add r10, rax dec rdi inc r8 loop .calculateLoop jmp .quit .wrapup_neg: neg r10 .quit: mov rax, r10 ret ;----- itoa(int, base, padLen, bool padZeroes) -----; ; Converts a signed integer to a string ; Return value: Pointer to converted string or 0(EOS) if entered base is invalid ; Supported bases: 10 (decimal) ; Used registers: ; rax* num to divide (DIV) >> Copy bytes when reversing string >> (ret) Pointer to converted string ; rdi* (arg) Integer to convert ; rsi* (arg) Base to convert to ; rdx* (arg) Padding length >> modulo (DIV) ; rcx* (arg) 0=pad spaces, otherwise pad zeroes >> Loop counter ; r9* Counts string length ; r10* Points to cnvtBuff(Rev)[] ; r11* Remembers if number is negative (r11b) ; r12 Stores (int) ; r13 Stores (base) ; r14 Stores (padLen) ; r15 Stores (padZeroes) itoa: ; Before doing anything, check if base is valid cmp rsi, 10 je .ok cmp rsi, ~10 je .negBase xor rax, rax ret .negBase: not rsi .ok: push r12 push r13 push r14 push r15 ; Store original arguments mov r12, rdi mov r13, rsi mov r14, rdx mov r15, rcx ; First, assure 0 <= padLen <= 64 ;(64, even though max signed int64 = 19 characters; enables to pad with base 2) mov rdi, rdx xor rsi, rsi mov rdx, 64 call clamp mov r14, rax ; Store padding character mov rdi, ' ' mov rdx, '0' test r15, r15 cmovnz rdi, rdx mov r15, rdi ; Prepare for conversion mov rax, r12 xor r9, r9 lea r10, [rel cnvtBuffRev] ; Check if negative xor r11b, r11b test rax, rax jns .convert mov r11b, 1 neg rax .convert: xor rdx, rdx div r13 add rdx, '0' mov byte [r10], dl inc r10 inc r9 test rax, rax jnz .convert ; Adds '-' if needed cmp r15, '0' je .checkPadding ; If padding with zeroes, add minus sign before zeroes. Otherwise, add '-' and then add spaces test r11b, r11b jz .checkPadding mov byte [r10], '-' inc r10 inc r9 .checkPadding: ; Substract length of converted number from padLen sub r14, r9 cmp r14, 0 jle .makeString mov rcx, r14 .addPadding: mov byte [r10], r15b inc r10 inc r9 loop .addPadding cmp r15, ' ' je .makeString ; Adds '-' if needed test r11b, r11b jz .makeString mov byte [r10], '-' inc r10 inc r9 .makeString: dec r10 ;don't point to past last character lea rdi, [rel cnvtBuff] mov rcx, r9 xor rax, rax .makeStringLoop: mov al, byte [r10] mov byte [rdi], al inc rdi dec r10 loop .makeStringLoop mov byte [rdi], EOS pop r15 pop r14 pop r13 pop r12 lea rax, [rel cnvtBuff] ret ;----- utoa(int, base, padLen, bool padZeroes, bool upperCase) -----; ; Converts an unsigned integer to a string ; Return value: Pointer to converted string or 0(EOS) if entered base is invalid. ; To use prefix (ie 0x, 0b or 0o), pass ~base instead of base ; Supported bases: 2 (binary), 8, (octal), 10 (decimal), 16 (hexadecimal) ; Used registers: ; rax* num to divide (DIV) >> Copy bytes when reversing string >> (ret) Pointer to converted string ; rbx Stores (upperCase) ; rdi* (arg) Integer to convert >> base (positive) >> pointer to cnvtBuff[] ; *rsi (arg) Base to convert to ; *rdx (arg) Padding length >> modulo (DIV) ; *rcx (arg) 0=pad spaces, otherwise pad zeroes >> Loop counter ; *r8 (arg) 0=lowercase letters (for bases using letters, ie 16), uppercase otherwise >> Stores padding character >> used for inserting prefix ; r9* Counts string length ; r10* Points to cnvtBuff(Rev)[] ; *r11 Stores padZeroes ; r12 Stores (int) ; r13 Stores (base) ; r14 Stores (padLen) ; r15 Stores (padZeroes) utoa: %macro insert_prefix 0 xor r8, r8 xor rsi, rsi mov r8, 'x0' cmp rdi, 16 cmove rsi, r8 je %%insert mov r8, 'o0' cmp rdi, 8 cmove rsi, r8 je %%insert cmp rdi, 2 jne %%continue mov rsi, 'b0' %%insert: mov word [r10], si add r10, 2 add r9, 2 %%continue: ;nop %endmacro ;; ;; ENTRY ;; ; Before doing anything, check if base is valid cmp rsi, 2 je .ok cmp rsi, 8 je .ok cmp rsi, 10 je .ok cmp rsi, 16 je .ok cmp rsi, ~16 je .ok cmp rsi, ~10 je .negBase cmp rsi, ~8 je .ok cmp rsi, ~2 je .ok xor rax, rax ret .negBase: not rsi .ok: push rbx push r12 push r13 push r14 push r15 ; Store original arguments mov rbx, r8 mov r12, rdi mov r13, rsi mov r14, rdx mov r15, rcx ; First, assure 0 <= padLen <= 64 mov rdi, rdx xor rsi, rsi mov rdx, 64 call clamp mov r14, rax ; Store padding character mov rdi, ' ' mov rdx, '0' test r15, r15 cmovnz rdi, rdx mov r11, rdi ; Prepare for conversion mov rdi, r13 test r13, r13 jns .continuePrepare not rdi .continuePrepare: mov rax, r12 xor r9, r9 lea r10, [rel cnvtBuffRev] mov rdx, 'a'-10 test rbx, rbx cmovz r8, rdx jz .convert mov r8, 'A'-10 .convert: xor rdx, rdx div rdi cmp dl, 9 jle .noLetter add dl, r8b jmp .write .noLetter: add dl, '0' .write: mov byte [r10], dl inc r10 inc r9 test rax, rax jnz .convert ; Insert prefix if needed test r13, r13 ;prefix needed? (MSB not set) jns .checkPadding test r15, r15 ;prefix now? (padding with spaces; r15 is zero) jnz .checkPadding insert_prefix .checkPadding: ; Substract length of converted number from padLen sub r14, r9 cmp r14, 0 jle .checkPrefix mov rcx, r14 test r13, r13 jns .addPadding test r15, r15 jz .addPadding sub rcx, 2 cmp rcx, 2 jl .checkPrefix .addPadding: mov byte [r10], r11b inc r10 inc r9 loop .addPadding ; Insert prefix if needed .checkPrefix: test r13, r13 ;prefix needed? (MSB not set) jns .makeString test r15, r15 ;prefix now? (padding with zeroes; r15 is not zero) jz .makeString insert_prefix .makeString: dec r10 ;don't point to past last character lea rdi, [rel cnvtBuff] mov rcx, r9 xor rax, rax .makeStringLoop: mov al, byte [r10] mov byte [rdi], al inc rdi dec r10 loop .makeStringLoop mov byte [rdi], EOS pop r15 pop r14 pop r13 pop r12 pop rbx lea rax, [rel cnvtBuff] ret