cstr.asm

includes/cstr.asm · 293 lines

;---------------------------------------------------------------------------------------------------
; cstr: zero-terminated strings and raw memory
;---------------------------------------------------------------------------------------------------
; What the C library would otherwise provide: lengths, copies, comparisons, and reading numbers out
; of text (for the command line, and later for .obj files). All Win64 calling convention.
ifndef cstr_asm
cstr_asm = 0

include macros.asm

.code
; rcx: string -> rax: its length
align 16
cstr_length proc
    mov rax, rcx
    scan:
        cmp byte ptr [rax], 0
        je done
        inc rax
        jmp scan
    done:
    sub rax, rcx
    ret
cstr_length endp

; rcx: destination, rdx: source. Copies the terminator too.
align 16
cstr_copy proc
    copy:
        mov al, [rdx]
        mov [rcx], al
        inc rcx
        inc rdx
        test al, al
        jnz copy
    ret
cstr_copy endp

; rcx: destination, rdx: source appended to it
align 16
cstr_append proc
    scan:
        cmp byte ptr [rcx], 0
        je copy
        inc rcx
        jmp scan
    copy:
        mov al, [rdx]
        mov [rcx], al
        inc rcx
        inc rdx
        test al, al
        jnz copy
    ret
cstr_append endp

; rcx: a, rdx: b -> rax: 1 when they are the same
align 16
cstr_equals proc
    compare:
        mov al, [rcx]
        cmp al, [rdx]
        jne different
        inc rcx
        inc rdx
        test al, al
        jnz compare
    mov eax, 1
    ret
    different:
    xor eax, eax
    ret
cstr_equals endp

; rcx: string, rdx: prefix -> rax: 1 when the string starts with it
align 16
cstr_starts_with proc
    compare:
        mov al, [rdx]
        test al, al
        jz yes
        cmp al, [rcx]
        jne no
        inc rcx
        inc rdx
        jmp compare
    yes:
    mov eax, 1
    ret
    no:
    xor eax, eax
    ret
cstr_starts_with endp

; rcx: string, rdx: what to look for -> rax: where it starts, or -1
align 16
cstr_find proc
    mov r8, rcx
    outer:
        xor r9, r9
        inner:
            mov al, [rdx + r9]
            test al, al
            jz found
            cmp al, [r8 + r9]
            jne next
            inc r9
            jmp inner
        next:
        cmp byte ptr [r8], 0
        je missing
        inc r8
        jmp outer
    found:
    mov rax, r8
    sub rax, rcx
    ret
    missing:
    mov rax, -1
    ret
cstr_find endp

; rcx: destination, rdx: source, r8: byte count
align 16
mem_copy proc
    USING rsi, rdi
    SaveRegs
    mov rdi, rcx
    mov rsi, rdx
    mov rcx, r8
    rep movsb
    RestoreRegs
    ret
mem_copy endp

; rcx: destination, rdx: byte count
align 16
mem_zero proc
    USING rdi
    SaveRegs
    mov rdi, rcx
    mov rcx, rdx
    xor eax, eax
    rep stosb
    RestoreRegs
    ret
mem_zero endp

;---------------------------------------------------------------------------------------------------
; numbers from text
;---------------------------------------------------------------------------------------------------
; rcx: text -> rax: the integer at its start (an optional sign, then digits), rdx: just past it
align 16
parse_int proc
    xor eax, eax
    xor r8d, r8d                    ; negative?
    cmp byte ptr [rcx], '-'
    jne digits
        mov r8d, 1
        inc rcx
    digits:
        movzx edx, byte ptr [rcx]
        sub edx, '0'
        cmp edx, 9
        ja done
        imul rax, rax, 10
        add rax, rdx
        inc rcx
        jmp digits
    done:
    test r8d, r8d
    jz positive
        neg rax
    positive:
    mov rdx, rcx
    ret
parse_int endp

; rcx: text -> xmm0: the float at its start, rax: just past it
;
; Digits are gathered into one integer and the decimal point and exponent become a power of ten,
; applied once in double precision: "0.123456" is exactly 123456 / 10^6 before rounding to float.
align 16
parse_float proc
    xor eax, eax                    ; mantissa
    xor r8d, r8d                    ; negative?
    xor r9d, r9d                    ; power of ten
    xor r10d, r10d                  ; seen the point?
    cmp byte ptr [rcx], '-'
    jne sign_done
        mov r8d, 1
        inc rcx
        jmp digits
    sign_done:
    cmp byte ptr [rcx], '+'
    jne digits
        inc rcx
    digits:
        movzx edx, byte ptr [rcx]
        cmp edx, '.'
        jne not_point
            mov r10d, 1
            inc rcx
            jmp digits
        not_point:
        sub edx, '0'
        cmp edx, 9
        ja digits_done
        ; past 18 digits the mantissa would overflow; further digits do not matter for a float
        mov r11, 100000000000000000
        cmp rax, r11
        jae skip_digit
            imul rax, rax, 10
            add rax, rdx
            sub r9d, r10d           ; each digit after the point is one more division by 10
            jmp next_digit
        skip_digit:
            test r10d, r10d
            jnz next_digit
            inc r9d
        next_digit:
        inc rcx
        jmp digits
    digits_done:
    movzx edx, byte ptr [rcx]
    or edx, 20h
    cmp edx, 'e'
    jne exponent_done
        inc rcx
        xor r11d, r11d              ; negative exponent?
        cmp byte ptr [rcx], '-'
        jne exponent_plus
            mov r11d, 1
            inc rcx
            jmp exponent_digits_start
        exponent_plus:
        cmp byte ptr [rcx], '+'
        jne exponent_digits_start
            inc rcx
        exponent_digits_start:
        xor r10d, r10d
        exponent_digits:
            movzx edx, byte ptr [rcx]
            sub edx, '0'
            cmp edx, 9
            ja exponent_digits_done
            imul r10d, r10d, 10
            add r10d, edx
            inc rcx
            jmp exponent_digits
        exponent_digits_done:
        test r11d, r11d
        jz exponent_add
            neg r10d
        exponent_add:
        add r9d, r10d
    exponent_done:

    cvtsi2sd xmm0, rax
    mov rax, 3FF0000000000000h      ; 1.0
    movq xmm1, rax
    mov rax, 4024000000000000h      ; 10.0
    movq xmm2, rax
    mov edx, r9d
    test edx, edx
    jns power_loop
        neg edx
    power_loop:
        test edx, edx
        jz power_done
        mulsd xmm1, xmm2
        dec edx
        jmp power_loop
    power_done:
    test r9d, r9d
    js divide
        mulsd xmm0, xmm1
        jmp scaled
    divide:
        divsd xmm0, xmm1
    scaled:
    cvtsd2ss xmm0, xmm0
    test r8d, r8d
    jz positive
        mov edx, 80000000h
        movd xmm1, edx
        xorps xmm0, xmm1
    positive:
    mov rax, rcx
    ret
parse_float endp

endif
Part of masm64-dx11, LearnOpenGL's lessons rewritten for Direct3D 11 in x64 assembly. Licensed CC BY-NC 4.0, after LearnOpenGL by Joey de Vries.