282 lines
5.3 KiB
NASM
282 lines
5.3 KiB
NASM
extern EOS
|
|
extern NL
|
|
extern NR_write
|
|
extern strlen
|
|
extern itoa
|
|
|
|
section .rodata
|
|
bufferLength equ 4096
|
|
ERR_buffLen db "<!> ERROR: Failed to complete printf(), reached buffer length!",NL,EOS
|
|
lERR_buffLen equ $-ERR_buffLen-1
|
|
|
|
; Errors (perror)
|
|
perrorMsg db "%s: %s",NL,EOS
|
|
perrorInvalid db "%s: Unknown error (errno %d)",NL,EOS
|
|
errorMsgs dq em0,em1,em2,em3,em4,em5,em6,em7,em8,em9,em10,em11,em12,em13,em14,em15,em16,em17,em18,em19,em20,em21,em22,em23,em24,em25,em26,em27,em28,em29,em30,em31,em32,em33,em34
|
|
em0 db "No error",EOS
|
|
em1 db "Operation not permitted",EOS
|
|
em2 db "No such file or directory",EOS
|
|
em3 db "No such process",EOS
|
|
em4 db "Interrupted system call",EOS
|
|
em5 db "I/O error",EOS
|
|
em6 db "No such device or address",EOS
|
|
em7 db "Argument list too long",EOS
|
|
em8 db "Exec format error",EOS
|
|
em9 db "Bad file number",EOS
|
|
em10 db "No child processes",EOS
|
|
em11 db "Try again",EOS
|
|
em12 db "Out of memory",EOS
|
|
em13 db "Permission denied",EOS
|
|
em14 db "Bad address",EOS
|
|
em15 db "Block device required",EOS
|
|
em16 db "Device or resource busy",EOS
|
|
em17 db "File exists",EOS
|
|
em18 db "Cross-device link",EOS
|
|
em19 db "No such device",EOS
|
|
em20 db "Not a directory",EOS
|
|
em21 db "Is a directory",EOS
|
|
em22 db "Invalid argument",EOS
|
|
em23 db "File table overflow",EOS
|
|
em24 db "Too many open files",EOS
|
|
em25 db "Not a typewriter",EOS
|
|
em26 db "Text file busy",EOS
|
|
em27 db "File too large",EOS
|
|
em28 db "No space left on device",EOS
|
|
em29 db "Illegal seek",EOS
|
|
em30 db "Read-only file system",EOS
|
|
em31 db "Too many links",EOS
|
|
em32 db "Broken pipe",EOS
|
|
em33 db "Math argument out of domain of func",EOS
|
|
em34 db "Math result not representable",EOS
|
|
|
|
section .bss
|
|
printfBuff resb bufferLength
|
|
printfNBuff resb 32
|
|
section .text
|
|
global print
|
|
global puts
|
|
global printf
|
|
global perror
|
|
|
|
;----- print (char* string) -----;
|
|
; return value: N/A
|
|
print:
|
|
call strlen
|
|
mov rdx, rax
|
|
mov rax, NR_write
|
|
mov rsi, rdi
|
|
mov rdi, 1
|
|
syscall
|
|
ret
|
|
;----- puts (char* string) -----;
|
|
; return value: N/A
|
|
puts:
|
|
mov r10, rdi
|
|
call print
|
|
mov rdi, r10
|
|
mov rax, NR_write
|
|
mov rdi, 1
|
|
mov rsi, NL
|
|
mov rdx, 1
|
|
syscall
|
|
ret
|
|
;----- printf(const char* string, ...) -----;
|
|
; Currently only supports specifiers: %d, %c, %s, %%
|
|
; Return value: Amount of printed characters
|
|
printf:
|
|
push rbp
|
|
mov rbp, rsp
|
|
|
|
push r12
|
|
push r14
|
|
|
|
xor r10, r10
|
|
xor r14, r14
|
|
lea r11, [rel printfBuff]
|
|
.makeStr:
|
|
cmp byte [rdi], 0x0
|
|
je .finish
|
|
cmp r14, bufferLength-1
|
|
je .finish
|
|
cmp byte [rdi], '%'
|
|
je .replaceArg
|
|
mov r12b, byte [rdi]
|
|
mov byte [r11], r12b
|
|
inc r14
|
|
jmp .continue
|
|
.replaceArg:
|
|
cmp byte [rdi+1], 0x0
|
|
je .continue
|
|
push rdi
|
|
cmp byte [rdi+1], 'd'
|
|
je .rep_d
|
|
cmp byte [rdi+1], '%'
|
|
je .rep_pct
|
|
cmp byte [rdi+1], 'c'
|
|
je .rep_c
|
|
cmp byte [rdi+1], 's'
|
|
je .rep_s
|
|
|
|
;--- invalid specifier ---;
|
|
mov byte [r11], '%'
|
|
inc r14
|
|
inc r10
|
|
jmp .continue
|
|
|
|
;--- %d ---;
|
|
.rep_d:
|
|
cmp r10, 0
|
|
cmove rdi, rsi
|
|
je .convertInt
|
|
cmp r10, 1
|
|
cmove rdi, rdx
|
|
je .convertInt
|
|
cmp r10, 2
|
|
cmove rdi, rcx
|
|
je .convertInt
|
|
cmp r10, 3
|
|
cmove rdi, r8
|
|
je .convertInt
|
|
cmp r10, 4
|
|
cmove rdi, r9
|
|
je .convertInt
|
|
|
|
mov rdi, qword [rbp + 16 + (r10-5)*8]
|
|
|
|
.convertInt:
|
|
lea rsi, [rel printfNBuff]
|
|
push rcx
|
|
push rdx
|
|
push r8
|
|
push r10
|
|
call itoa
|
|
pop r10
|
|
pop r8
|
|
pop rdx
|
|
pop rcx
|
|
mov rsi, rax
|
|
jmp .insertLoop
|
|
|
|
;--- %% ---;
|
|
.rep_pct:
|
|
mov rdi, '%'
|
|
dec r10
|
|
jmp .charToStr
|
|
|
|
;--- %c ---;
|
|
.rep_c:
|
|
cmp r10, 0
|
|
cmove rdi, rsi
|
|
je .charToStr
|
|
cmp r10, 1
|
|
cmove rdi, rdx
|
|
je .charToStr
|
|
cmp r10, 2
|
|
cmove rdi, rcx
|
|
je .charToStr
|
|
cmp r10, 3
|
|
cmove rdi, r8
|
|
je .charToStr
|
|
cmp r10, 4
|
|
cmove rdi, r9
|
|
je .charToStr
|
|
|
|
mov rdi, qword [rbp + 16 + (r10-5)*8]
|
|
|
|
.charToStr:
|
|
mov [printfNBuff], dil
|
|
mov dil, 0
|
|
mov [printfNBuff+1], dil
|
|
lea rsi, [rel printfNBuff]
|
|
jmp .insertLoop
|
|
|
|
;--- %s ---;
|
|
.rep_s:
|
|
cmp r10, 0
|
|
je .insertLoop
|
|
cmp r10, 1
|
|
cmove rsi, rdx
|
|
je .insertLoop
|
|
cmp r10, 2
|
|
cmove rsi, rcx
|
|
je .insertLoop
|
|
cmp r10, 3
|
|
cmove rsi, r8
|
|
je .insertLoop
|
|
cmp r10, 4
|
|
cmove rsi, r9
|
|
je .insertLoop
|
|
|
|
mov rsi, qword [rbp + 16 + (r10-5)*8]
|
|
|
|
;--- Move fetched data to buffer ---;
|
|
.insertLoop:
|
|
cmp r14, bufferLength-1
|
|
je .finish
|
|
cmp byte [rsi], 0x0
|
|
je .s0f
|
|
mov r12b, byte [rsi]
|
|
mov byte [r11], r12b
|
|
inc r14
|
|
inc rsi
|
|
inc r11
|
|
jmp .insertLoop
|
|
.s0f:
|
|
inc r10
|
|
pop rdi
|
|
add rdi, 2
|
|
jmp .makeStr
|
|
.continue:
|
|
inc rdi
|
|
inc r11
|
|
jmp .makeStr
|
|
|
|
.finish:
|
|
mov byte [r11], 0x0
|
|
mov rax, NR_write
|
|
mov rdi, 1
|
|
lea rsi, [rel printfBuff]
|
|
mov rdx, r14
|
|
syscall
|
|
mov rax, r14
|
|
cmp r14, bufferLength-1
|
|
jl .final
|
|
|
|
mov rax, NR_write
|
|
mov rdi, 2
|
|
lea rsi, [rel ERR_buffLen]
|
|
mov rdx, lERR_buffLen
|
|
syscall
|
|
|
|
.final:
|
|
pop r14
|
|
pop r12
|
|
|
|
leave
|
|
ret
|
|
;----- perror(const char *str) -----;
|
|
; retrieves last returned error. Uses rax to fetch errno.
|
|
; errno must be negative
|
|
; Returns nothing (rax restored to original value)
|
|
perror:
|
|
push rax
|
|
cmp rax, 0
|
|
jge .quit
|
|
neg rax
|
|
cmp rax, 34
|
|
jg .unknown
|
|
mov rsi, rdi
|
|
lea rdi, [rel perrorMsg]
|
|
lea rdx, [errorMsgs + rax*8]
|
|
mov rdx, [rdx]
|
|
call printf
|
|
jmp .quit
|
|
.unknown:
|
|
mov rsi, rdi
|
|
lea rdi, [rel perrorInvalid]
|
|
mov rdx, rax
|
|
call printf
|
|
.quit:
|
|
pop rax
|
|
ret
|