Ok, it is done and working, or at least I tested out each function once and then again with an error. It's a little jumbled up, but that was so all my jumps and conditional branches were only two bytes. Still only saved 6 bytes over the previous version.
UCASE equ 0x5f
global A2I
section .text
; =============================================================================================
; Converts an ASCII null terminated string into 64 bit integer.
; ENTRY: RAX = 0
; RDI = Pointer to begining of string
; RAX = Strings length (probably passed from call to GetS)
; RDI = Pointer to next byte after this strings terminator
; LEAVE: RAX = Converted value or 0xffffffffffffffff if error
; ---------------------------------------------------------------------------------------------
A2I push rcx
push rsi ; Preserve essential
push rdi
; As self modifying code isn't possible, this is the next best alternative to
; specifying a conversion method dynamically base on qualifier in input.
push rbx
mov ebx, .Dec ; Assume decimal conversion by default.
push rbp ; Empty procedure frame in case proc needs
mov rbp, rsp ; to bail from error inside local call.
xor edx, edx ; Reset functions accumulator
; This procedure may be as a result from a call to GetS. In that case RAX reflects
; the number of characters in string.
mov rcx, rax ; Move character count
or eax, eax ; and check if it's zero
jz .CalcLen ; ZF = 0, Must be called after GetS maybe
; RSI needs to point to begining of string
mov rsi, rdi
sub rsi, rcx
dec rsi
jmp .Delim
; Determines if byte in AL is a digit and if so returns with MSB striped from byte
; otherwise AL unchanged
.IsDigit cmp al, '9'
ja .Done - 1
cmp al, '0'
jb .Done - 1
and al, 15 ; Strip MSB's
xor ah, ah ; Set ZF = 1, is a digit
ret
; Primary loop continues until buffer exhausted.
.Next lodsb ; Grab next character
call .IsDigit ; Strip MSB if it's a digit
call rbx ; Execute desired function
loop .Next ; Until ECX = 0
jmp .Done ; Bounce over all functions
; Because RAX is NULL, this means we need mimic what GetS would return by calcuating
; length of NULL terminated string.
.CalcLen mov rsi, rdi ; Save pointer to begining of string
dec ecx ; Should be plenty big enough
repnz scasb ; Find NULL, string terminator
add ecx, 2 ; Bump to actual count of characters
neg ecx ; Invert to positive value
; Test if optional qualifiers exist at begining or end of string
.Delim lodsb
cmp al, '0' ; Is it a leading zero
jnz .Post
; The only other two options are Octal or Hexidecimal
mov ebx, .Oct ; Assume octal conversion
dec cl
lodsb ; Get next character
call .IsDigit
jz .Next + 6 ; Must be doing Octal
dec cl ; Bump once more to account for prefix
mov ebx, .Hex ; Set conversion routine
and al, UCASE
cmp al, 'X'
jz .Next ; Continue if match
; Return 0xffffffffffffffff for any error condition
.Error or rdx, -1 ; Set error condition
jmp .Done
; Now check if string has been suffixed with one of the three qualifiers O, H or B.
.Post push rax ; We'll need first digit later
mov al, [rdi - 2] ; Get last character of string
call .IsDigit
jz .PostEx + 6 ; No suffixed, must be doing DEC conversion
and al, UCASE
mov r8, rax ; Preserve last char
push rsi
mov esi, .Func ; Get pointer to array of functions
lodsd ; Load pointer to Octal
cmp r8b, 'O'
jz .PostEx
lodsd ; Load pointer to Hex
cmp r8b, 'H'
jz .PostEx
lodsd ; Load pointer to Binary
cmp r8b, 'B'
jnz .Error
; At this point we know the last character was one of the three qualifiers, so
; continue with processing
.PostEx mov rbx, rax ; Set function pointer
dec cl ; Bump counter to account for suffix
pop rsi ; and pointer to string
pop rax ; Restore first character
jmp .Next + 1 ; Bump over loading, already done
.Func dd .Oct, .Hex, .Bin
; Convert decimal digits
.Dec jnz .Error ; Exit if not a digit
imul rdx, 10
add rdx, rax
ret
; Convert Octal digits
.Oct jnz .Error
cmp al, 7
ja .Error
shl rdx, 3
add dl, al
ret
; Convert binary digits
.Bin jnz .Error
cmp al, 1
ja .Error
rcr al, 1
rcl rdx, 1
ret
; Convert hexidecimal digits
.Hex jz .H01
and al, UCASE ; Convert to uppercase
cmp al, 'A'
jb .Error
cmp al, 'F'
ja .Error
sub al, 55 ; Convert to value 10 - 15
.H01 shl rdx, 4 ; Shift everything left 4 bits
add dl, al ; Append low nibble of AL to RDX
ret
; Procedure postamble
.Done leave ; Kill frame and reposition RSP properly
pop rbx
pop rdi
pop rsi ; Restore essential
pop rcx
xchg rdx, rax ; Move result into accumulator
ret