kvisc/ka/crt/fmt/doprnt.k

183 lines
3.2 KiB
Plaintext

; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
;
; typedef int (*PUTC)(int ch)
; int doprnt(PUTC putc, int n, const char *fmt, va_list ap)
;
doprnt:
enter 8
mov q[rbp-8], rbx
mov q[rbp-16], nx0
mov q[rbp-24], nx1
mov q[rbp-32], rdi
mov q[rbp-40], rsi
mov q[rbp-48], nx3
mov rbx, ax2 ; fmt
mov nx3, ax0 ; putc
mov nx0, ax1 ; n
mov rsi, ax3 ; va_list
mov nx1, zero ; return value
.main_loop:
; find '%' or null-terminator
mov rcx, STRLEN_MAX
mov rdi, rbx
scasb.rep.nz rdi, '%'
; everything below rdi is a regular character; print it
.print_regular:
b.z rbx, rdi, .check_modf
mov ax0, b[rbx]
call .doput
add rbx, rbx, 1
jmp .print_regular
.check_modf:
; did we find a '%' ?
; if not, then we found fmt's null-terminator; we're done
b.nz b[rbx], '%', .epilogue
; we did find a modifier / '%'
mov rax, b[rbx+1]
add rbx, rbx, 2
b.z rax, 's', .modf_s
b.z rax, 'c', .modf_c
b.z rax, 'p', .modf_p
b.z rax, 'x', .modf_x
b.z rax, 'd', .modf_d
b.z rax, 'o', .modf_o
b.z rax, 'b', .modf_b
b.z rax, '%', .modf_percent
; unrecognized
jmp .bad_modifier
.modf_s:
; get string address
mov rdi, q[rsi]
add rsi, rsi, 8
cmp rdi, zero
jmp.z .nullstring
.print_string:
mov ax0, b[rdi]
cmp ax0, zero
jmp.z .main_loop
add rdi, rdi, 1
call .doput
jmp .print_string
.modf_c:
mov ax0, q[rsi]
add rsi, rsi, 8
call .doput
jmp .main_loop
.modf_p:
call .doput, '0'
call .doput, 'x'
; Fallthrough
.modf_x:
mov ax2, 16
jmp .print_number
.modf_d:
mov ax2, 10
jmp .print_number
.modf_o:
mov ax2, 8
jmp .print_number
.modf_b:
mov ax2, 2
jmp .print_number
.print_number:
; allocate itoa conversion buffer
sub rsp, rsp, 80
mov rdi, rsp
; assume modifier already set up ax2
mov ax0, rsp
mov ax1, q[rsi]
call itoa
add rsi, rsi, 8
.print_itoa_buf:
mov ax0, b[rdi]
cmp ax0, zero
add.z rsp, rsp, 80
jmp.z .main_loop
add rdi, rdi, 1
call .doput
jmp .print_itoa_buf
.modf_percent:
call .doput, '%'
jmp .main_loop
.bad_modifier:
; print "%?" to clearly indicate that something is wrong
call .doput, '%'
call .doput, '?'
jmp .main_loop
.nullstring:
; %s was passed a NULL
call .doput, '('
call .doput, 'n'
call .doput, 'u'
call .doput, 'l'
call .doput, 'l'
call .doput, ')'
jmp .main_loop
.epilogue:
mov rax, nx1
mov rbx, q[rbp-8]
mov nx0, q[rbp-16]
mov nx1, q[rbp-24]
mov rdi, q[rbp-32]
mov rsi, q[rbp-40]
mov nx3, q[rbp-48]
leave
ret
;
; prints ax0
;
.doput:
; update print count
add nx1, nx1, 1
; if n==0, don't print
; we follow the C convention that sprintf()-like functions
; should return the number of characters that would have
; been printed/written if 'n' were big enough
cmp nx0, zero
ret.z
; if n>0, decrement n and print
sub nx0, nx0, 1
call nx3
; did putc fail?
cmp rax, zero
mov.nz nx0, zero ; yes, so artificially set n=0
ret