kvisc/ka/crt/fmt/doprnt.k

203 lines
3.4 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], nx2
mov q[rbp-40], rdi
mov q[rbp-48], rsi
mov q[rbp-56], nx3
mov rbx, ax2 ; fmt
mov nx3, ax0 ; putc
mov nx0, ax1 ; n
xor nx1, nx1 ; return value
xor nx2, nx2 ; index in va_list
mov rsi, ax3 ; ap
.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
inc rbx
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, 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+nx2*8]
inc nx2
test rdi, rdi
j.z .nullstring
.print_string:
mov ax0, b[rdi]
test ax0, ax0
j.z .main_loop
inc rdi
call .doput
jmp .print_string
.modf_c:
mov ax0, q[rsi+nx2*8]
inc nx2
call .doput
jmp .main_loop
.modf_p:
mov ax0, '0'
call .doput
mov ax0, 'x'
call .doput
; 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, 80
mov rdi, rsp
; assume modifier already set up ax2
mov ax0, rsp
mov ax1, q[rsi+nx2*8]
call itoa
inc nx2
.print_itoa_buf:
mov ax0, b[rdi]
test ax0, ax0
add.z rsp, 80
j.z .main_loop
inc rdi
call .doput
jmp .print_itoa_buf
.modf_percent:
mov ax0, '%'
call .doput
jmp .main_loop
.bad_modifier:
; print "%?" to clearly indicate that something is wrong
mov ax0, '%'
call .doput
mov ax0, '?'
call .doput
jmp .main_loop
.nullstring:
; %s was passed a NULL
mov ax0, '('
call .doput
mov ax0, 'n'
call .doput
mov ax0, 'u'
call .doput
mov ax0, 'l'
call .doput
mov ax0, 'l'
call .doput
mov ax0, ')'
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 nx2, q[rbp-32]
mov rdi, q[rbp-40]
mov rsi, q[rbp-48]
mov nx3, q[rbp-56]
leave
ret
;
; prints ax0
;
.doput:
; update print count
inc nx1
; 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
test nx0, nx0
ret.z
; if n>0, decrement n and print
dec nx0
call nx3
; did putc fail?
test rax, rax
xor.nz nx0, nx0 ; yes, so artificially set n=0
ret