lib.asm 8.44 KB
section .data
newline_char: db 0xA
digits: db '0123456789'
minus: db '-'
notation: dq 0xA
space_symbols: db 0x20, 0x9, 0xA

section .text

global exit
global string_length
global print_string
global print_char
global print_newline
global print_uint
global print_int
global string_equals
global read_char
global read_word
global read_line
global parse_uint
global parse_int
global string_copy


; Принимает код возврата и завершает текущий процесс
exit:
    mov rax, 60
    syscall

; Принимает указатель на нуль-терминированную строку, возвращает её длину
string_length:
    xor rax, rax
.loop:
    cmp byte [rdi+rax], 0
    je .end
    inc rax
    jmp .loop
.end: 
    ret

; Принимает указатель на нуль-терминированную строку, выводит её в stdout
print_string:
    push rdi
    call string_length  ; count str length (in rax)
    pop rsi
    mov rdx, rax
    mov rax, 1
    mov rdi, 1
    syscall
    ret

; Принимает указатель на нуль-терминированную строку, её длину и выводит её в stdout
print_string_with_lenght:
    mov rdx, rsi
    mov rsi, rdi
    mov rax, 1
    mov rdi, 1
    syscall
    ret

; Принимает код символа и выводит его в stdout
print_char:
    push rdi
    mov rsi, rsp
    mov rax, 1
    mov rdi, 1
    mov rdx, 1

    syscall
    pop rdi
    ret

; Переводит строку (выводит символ с кодом 0xA)
print_newline:
    mov rdi, [newline_char] 
    jmp print_char

; Выводит беззнаковое 8-байтовое число в десятичном формате 
; Совет: выделите место в стеке и храните там результаты деления
; Не забудьте перевести цифры в их ASCII коды.
print_uint:
    mov rax, rdi
    xor rcx, rcx     ; обнуляем rcx, в нём будет храниться количество цифр в числе

.loop:
    xor rdx, rdx     ; обнуляем rdx, в нём будет храниться остаток от деления
    inc rcx
    div qword [notation]

    dec rsp
    mov dl, [digits + rdx]
    mov [rsp], dl
    cmp rax, 0
    jnz .loop

    ; print string
    mov rdi, rsp
    mov rsi, rcx

    add rsp, rcx    ; возвращаем значение rsp
    jmp print_string_with_lenght

; Выводит знаковое 8-байтовое число в десятичном формате 
print_int:
    cmp rdi, 0
    jge .print

    neg rdi
    push rdi
    mov rdi, [minus]
    call print_char
    pop rdi

.print:
    call print_uint
    ret

; Принимает два указателя на нуль-терминированные строки, возвращает 1 если они равны, 0 иначе
string_equals:
    xor rax, rax  ; counter
    xor rdx, rdx
    xor rcx, rcx

.loop:
    mov dl, [rdi + rax]
    mov cl, [rsi + rax]

    cmp rdx, rcx
    jne .error
    cmp rdx, 0
    je .success

    inc rax
    jmp .loop

.error:
    xor rax, rax
    ret
.success:
    mov rax, 0x1
    ret

; Читает один символ из stdin и возвращает его. Возвращает 0 если достигнут конец потока
read_char:
    xor rax, rax
    xor rdi, rdi
    dec rsp
    mov rsi, rsp
    mov rdx, 1
    syscall

    test rax, rax
    jz .EOF

    mov al, [rsp]
    jmp .END
.EOF:
    xor rax, rax
.END:
    inc rsp
    ret


; Принимает: адрес начала буфера, размер буфера
; Читает в буфер слово из stdin, пропуская пробельные символы в начале, .
; Пробельные символы это пробел 0x20, табуляция 0x9 и перевод строки 0xA.
; Останавливается и возвращает 0 если слово слишком большое для буфера
; При успехе возвращает адрес буфера в rax, длину слова в rdx.
; При неудаче возвращает 0 в rax
; Эта функция должна дописывать к слову нуль-терминатор

read_word:
    xor rdx, rdx  

    cmp rsi, 0
    je .error

.loop:
    push rdi
    push rsi
    push rdx

    call read_char

    pop rdx
    pop rsi
    pop rdi

    cmp al, byte [space_symbols]
    je .skip_or_end
    cmp al, byte [space_symbols + 1]
    je .skip_or_end
    cmp al, byte [space_symbols + 2]
    je .skip_or_end

    jmp .save_char

.skip_or_end:
    cmp rdx, 0
    je .loop
    jmp .success

.save_char:
    cmp rsi, rdx
    je .error

    mov [rdi + rdx], al

    cmp rax, 0
    je .success

    inc rdx
    jmp .loop
.error:
    xor rax, rax
    xor rdx, rdx
    ret
.success: 
    mov rax, rdi
    ret
 

; Принимает: адрес начала буфера, размер буфера
; Читает в буфер строку из stdin, пропуская пробельные символы в начале.
; Строка заканчивается после символа перевода строки или при закрытии потока ввода
; Пробельные символы это пробел 0x20, табуляция 0x9 и перевод строки 0xA.
; Останавливается и возвращает 0 если слово слишком большое для буфера
; При успехе возвращает адрес буфера в rax, длину слова в rdx.
; При неудаче возвращает 0 в rax
; Эта функция дописывает к строке нуль-терминатор

read_line:
    xor rdx, rdx

    cmp rsi, 0
    je .error
.loop:
    push rdi
    push rsi
    push rdx

    call read_char

    pop rdx
    pop rsi
    pop rdi

    cmp rdx, 0
    jne .process_char
    cmp al, byte [space_symbols]
    je .loop
    cmp al, byte [space_symbols + 1]
    je .loop
    cmp al, byte [space_symbols + 2]
    je .loop
.process_char:
    cmp rsi, rdx
    je .error
    
    cmp al, byte [newline_char]
    jne .save
    xor rax, rax
.save:
    mov [rdi + rdx], al

    cmp rax, 0
    je .success

    inc rdx
    jmp .loop
.error:
    xor rax, rax
    xor rdx, rdx
    ret
.success: 
    mov rax, rdi
    ret


; Принимает указатель на строку, пытается
; прочитать из её начала беззнаковое число.
; Возвращает в rax: число, rdx : его длину в символах
; rdx = 0 если число прочитать не удалось
parse_uint:
    xor rax, rax
    push rcx
    xor rcx, rcx
    push rbx
    xor rbx, rbx

.read:
    mov bl, [rdi + rcx]

    cmp bl, '0'
    jb .end
    cmp bl, '9'
    ja .end

    sub rbx, '0'
    mul qword [notation]
    add rax, rbx
    inc rcx
    jmp .read

.end:
    mov rdx, rcx
    pop rbx
    pop rcx
    ret


; Принимает указатель на строку, пытается
; прочитать из её начала знаковое число.
; Если есть знак, пробелы между ним и числом не разрешены.
; Возвращает в rax: число, rdx : его длину в символах (включая знак, если он был) 
; rdx = 0 если число прочитать не удалось
parse_int:
    xor rcx, rcx    ; Boolean flag, 1 if number is negative, 0 otherwise
    mov rax, [rdi]  ; Check first symbol

    cmp al, '+'
    jne .check_neg
    jmp .process
.check_neg:
    cmp al, '-'
    jne .just_uint
    inc rcx
    jmp .process
.just_uint:
    jmp parse_uint
.process:
    inc rdi         ; Send string from next symbol
    call parse_uint

    cmp rdx, 0      ; If error, go to ret
    je .end
    inc rdx         ; Else add sign to number lenght

    cmp rcx, 0
    je .end
    neg rax         ; For negative number
.end:
    ret

; Принимает указатель на строку, указатель на буфер и длину буфера
; Копирует строку в буфер
; Возвращает длину строки если она умещается в буфер, иначе 0
string_copy:
    xor rax, rax

.loop:
    cmp rdx, rax
    je .error

    mov cl, [rdi + rax]
    mov [rsi + rax], cl
    inc rax    

    cmp cl, 0
    je .end
    jmp .loop
.error:
    xor rax, rax
.end:
    ret