;File: base.asm
;This program converts between number representations: decimal, octal and hexadecimal
;Usage '$ ./base 0x7ffe'

format ELF64 executable 3
use64
entry _start

segment readable writeable
input:
    dd 0
hextable:
    db "0123456789abcdef"
errmsg:
    db "ERROR: invalid input number", 10
errmsg_len = $ - errmsg
outbuf:
    db 128 dup ?
tmpbuf:
    db 12 dup ?
carrymsg:
    db "ERROR: input number too big", 10
carrymsg_len = $ - carrymsg

segment readable executable
_start:
    cmp dword [rsp], 2
    jne failure

    xor r12d, r12d
    lea r13, [outbuf]

    mov rdi, qword [rsp+16]

    cmp word [rdi], '0x'
    je .hex

    cmp byte [rdi], '0'
    je .octal

    jmp .decimal

.hex:
    inc rdi
    inc rdi
    mov esi, 16
    call atoim
    mov [input], eax
    jmp .output

.octal:
    inc rdi
    mov esi, 8
    call atoim
    mov [input], eax
    jmp .output

.decimal:
    mov esi, 10
    call atoim
    mov [input], eax

.output:
    lea rdi, [r13+r12] ;address of output string
    mov esi, [input] ;number
    mov edx, 10 ;base
    call numtostr
    add r12d, eax

    mov byte [r13+r12], ' '
    inc r12d

    mov byte [r13+r12], '0'
    inc r12d
    lea rdi, [r13+r12] ;address of output string
    mov esi, [input] ;number
    mov edx, 8 ;base
    call numtostr
    add r12d, eax

    mov byte [r13+r12], ' '
    inc r12d

    mov byte [r13+r12], '0'
    inc r12d
    mov byte [r13+r12], 'x'
    inc r12d

    lea rdi, [r13+r12] ;address of output string
    mov esi, [input] ;number
    mov edx, 16 ;base
    call numtostr
    add r12d, eax

    mov byte [r13+r12], 10
    inc r12d

    mov eax, 1
    mov edi, 1
    mov rsi, r13
    mov edx, r12d
    syscall

    mov eax, 60
    xor edi, edi
    syscall

carry:
    mov eax, 1
    mov edi, 2
    mov rsi, carrymsg
    mov edx, carrymsg_len
    syscall

    mov eax, 60
    mov edi, 1
    syscall

error:
    mov eax, 1
    mov edi, 2
    mov rsi, errmsg
    mov edx, errmsg_len
    syscall

failure:
    mov eax, 60
    mov edi, 1
    syscall

;rdi(IN) - string pointer
;rsi(IN) - base
;eax(OUT) - integer value
atoim:
    xor edx, edx ;return value
    xchg rsi, rdi
    lea r11, [hextable] ;get translation table address

.loop:
    lodsb ;rsi is incremented automatically
    test al, al
    jz .end

    movzx eax, al
    xor r8d, r8d ;index

.check:
    cmp byte [r11+r8], al
    je .cont

    inc r8d
    cmp r8d, edi
    je error

    jmp .check

.cont:
    xor r8d, r8d ;index

.again:
    cmp byte [r11+r8], al
    je .found
    inc r8
    jmp .again

.found:
    mov eax, edx
    mul edi
    jc carry

    mov edx, eax
    add edx, r8d ;add the current digit to total
    jc carry

    jmp .loop

.end:
    mov eax, edx
    ret ;atoim

;rdi(OUT) - string
;rsi(IN) - number
;rdx(IN) - base
;eax(OUT) - number of bytes written
numtostr:
    push rbx
    push r14
    push r13
    push r12

    mov eax, esi ;input number
    mov r13d, edx ;divisor
    xor ecx, ecx ;counter, length of string
    mov r8, rdi
    lea r14, [tmpbuf]
    lea rbx, [hextable]
    jmp .into

.loop:
    mov eax, r10d
    inc ecx

.into:
    xor edx, edx
    div r13d ;divide a by provided base, remainder in d
    mov r10d, eax
    mov al, dl
    xlatb
    mov byte [r14+rcx], al
    test r10d, r10d
    jnz .loop

    lea r9d, [ecx+1]
    xor r12d, r12d

.reverse:
    mov dl, byte [r14+rcx]
    mov byte [r8+r12], dl
    inc r12d
    jecxz .end
    dec ecx
    jmp .reverse

.end:
    mov eax, r9d

    pop r12
    pop r13
    pop r14
    pop rbx
    ret ;numtostr

;vim: set ts=4 sw=4 sts=4 syn=fasm:
