summaryrefslogtreecommitdiff
path: root/stdio.asm
diff options
context:
space:
mode:
Diffstat (limited to 'stdio.asm')
-rw-r--r--stdio.asm261
1 files changed, 194 insertions, 67 deletions
diff --git a/stdio.asm b/stdio.asm
index 2e61afc..b4b54a4 100644
--- a/stdio.asm
+++ b/stdio.asm
@@ -17,7 +17,7 @@ puts:
lodsb ; load byte at [si] into al
or al, 0 ; 0 | 0 = 0 (detect null terminator)
je .end
- call putc
+ call putc ; write character
jmp .loop
.end:
popa
@@ -27,89 +27,216 @@ puts:
printi:
- push bp
- mov bp, sp
-
- mov ax, [bp + 4] ; integer WORD
- mov cx, 0 ; counter
- cmp ax, 0
- je .write_no_pop
+ push bp
+ mov bp, sp
+ mov ax, [bp + 4]
+ call putint
+.return:
+ mov sp, bp
+ pop bp
+ ret
-.divide:
- xor dx, dx
- mov di, 10 ; divisor
- div di
- push dx
- inc cx
- cmp ax, 0
- jne .divide
+printh:
+ push bp
+ mov bp, sp
+ push ax
- jmp .write
+ mov ax, [bp + 4] ; integer WORD
+ call puthex
-.write_no_pop:
- or al, 30h
- call putc
- jmp .return
+.return:
+ pop ax
+ mov sp, bp
+ pop bp
+ ret
-.write:
- pop ax
- or al, 30h
- call putc
- dec cx
- jne .write
+puthex:
+ push ax
+ push bx
+ push dx
+ push cx
+
+ ; ax is integer to print
+ ror ah, 4 ; reverse hex value
+ ror al, 4
+ xchg ah, al
+
+ mov cx, 04h ; count (leading zeros)
+ .divide:
+ mov bx, 10h ; set divisor
+
+ xor dx, dx ; clear mod
+ div bx ; divide by 16
+
+ cmp dl, 10 ; don't adjust values less than 10
+ jl .decimal
+ .alpha:
+ sub dl, 10 ; (remainder - 10) -> align with ascii (base 10)
+ add dl, 'A' ; (remainder + 'A') -> ascii offset conversion
+ jmp .write
+ .decimal:
+ or dl, 30h ; remainder -> ascii
+ .write:
+ dec cx
+ xchg ax, dx ; exchange registers to get ascii value
+ call putc ; print value
+ xchg ax, dx ; restore registers
+
+ cmp al, 0 ; loop if al != 0
+ jne .divide
+
+ cmp cx, 0
+ jne .divide
.return:
- mov sp, bp
- pop bp
- ret
+ pop cx
+ pop dx
+ pop bx
+ pop ax
+ ret
-printh:
- push bp
- mov bp, sp
- mov ax, [bp + 4] ; integer WORD
- mov cx, 0 ; counter
- cmp ax, 0
- je .write_no_pop
+putint:
+ push bp
+ mov bp, sp
+ pusha
-.divide:
- xor dx, dx
- mov di, 16 ; divisor
- div di
+ mov cx, 05h ; count (+leading zeros)
+ mov di, 00h ; inner loop count
+ .divide:
+ mov bx, 0Ah ; set divisor
- push dx
- inc cx
- cmp ax, 0
- jne .divide
+ xor dx, dx ; clear mod
+ div bx ; divide by 10
+ or dl, 30h ; remainder -> ascii
- jmp .write
+ dec cx
+ inc di ; local stack counter
+ push dx
-.write_no_pop:
- or al, 30h
- call putc
- jmp .return
+ cmp al, 0 ; loop if al != 0
+ jne .divide
-.write:
- pop ax
- cmp al, 10
- jge .alpha
+ cmp cx, 0 ; no more zeros?
+ jne .divide
- or al, 30h
- jmp .decimal
+ .write:
+ pop ax ; pop first value of integer
+ dec di ; decrement our loop counter
+ call putc ; write character
+ cmp di, 0 ; done?
+ jne .write
+.return:
+ popa
-.alpha:
- sub al, 10
- add al, 'A'
+ mov sp, bp
+ pop bp
+ ret
+
+
+printf:
+ push bp
+ mov bp, sp
+
+ mov di, bp ; save base pointer address
+ mov bx, [bp + 4] ; format string address
+ add bp, 6 ; set base pointer to beginning of '...' args
+
+ ; count arguments
+ std ; buffer direction 'up'
+ mov cx, 0 ; set counter
+ mov si, bp ; source index is base pointer
+ .count_args:
+ lodsw ; load word at es:si
+ add cx, 1 ; increase arg count
+ cmp ax, 0 ; here we're looking for a "natural null terminator"
+ ; on the stack
+ jne .count_args
+
+ cld ; clear direction flag
+ mov si, bx ; source index is format string
+ .main_string:
+ lodsb ; load byte in format string
+ ; BEGIN READING FORMAT STRING
+ cmp al, '%' ; trigger parser on '%' symbol
+ je .parse_fmt
+
+ cmp al, 0 ; if at end of format string
+ je .finalize ; return
+
+ call putc ; write character
+ ; when character is not a format specifier
+
+ jmp .main_string ; repeat
+
+ .parse_fmt:
+ lodsb ; get next byte
+ ; switch(formatter)
+ ; [for example]
+ cmp al, '%' ; '%%' - just print the character
+ je .do_percent_escape
+
+ cmp al, 'd' ; '%d' - process integer
+ je .do_int
+
+ cmp al, 'x' ; '%x' - process hexadecimal
+ je .do_hex
+
+ cmp al, 's' ; '%s' - process string
+ je .do_string
+
+ jmp .do_default ; Matched nothing, so handle it
+
+ ; ---REMEMBER---
+ ; Our base pointer has been shifted
+ ; ---------------------------------
+ ; fmt = bp + 4
+ ; arg1 = bp + 6 [<- we are here]
+ ; arg2 = bp + 8
+ ; arg3 = bp + 10
+ ; ... = bp + ??
+
+ .do_percent_escape:
+ mov ax, '%'
+ call putc
+ jmp .main_string
+
+ .do_hex:
+ mov ax, [bp]
+ call puthex
+ jmp .parse_fmt_done
+
+ .do_int:
+ mov ax, [bp]
+ call putint
+ jmp .parse_fmt_done
+
+ .do_string:
+ mov ax, [bp]
+ push ax
+ call puts
+ add sp, 2
+ jmp .parse_fmt_done
+
+ .do_default:
+ ; nothing found
+
+ .parse_fmt_done:
+ add bp, 2 ; increment base pointer by one WORD
+ ; <<< these are our function arguments >>>
+ jmp .main_string ; keep reading the format string
+
+.finalize:
+ mov bp, di ; restore original base pointer address.
+ ; this is pretty dangerous actually. if
+ ; a procedure modifies DI without restoring
+ ; it, we're doomed; we'll roll right off the
+ ; edge into oblivion.
+ mov sp, bp
+ pop bp
+ ret
-.decimal:
- call putc
- dec cx
- jne .write
-.return:
- mov sp, bp
- pop bp
- ret
%endif