banan-os/bootloader/bios/utils.S

404 lines
5.8 KiB
ArmAsm

.include "common.S"
.set SCREEN_WIDTH, 80
.set SCREEN_HEIGHT, 25
.code16
.section .stage1
# prints character to screen
# al: ascii character to print
.global putc
putc:
pushw %ax
pushw %bx
movb $0x0E, %ah
xorb %bh, %bh
int $0x10
popw %bx
popw %ax
ret
# prints null terminated string to screen
# ds:si: string address
.global puts
puts:
pushw %si
pushw %bx
pushw %ax
movb $0x0E, %ah
xorb %bh, %bh
.puts_loop:
lodsb
test %al, %al
jz .puts_done
int $0x10
jmp .puts_loop
.puts_done:
popw %ax
popw %bx
popw %si
ret
# compares memory between addresses
# si: ptr1
# di: ptr2
# cx: bytes count
# return:
# al: 1 if equal, 0 otherwise
.global memcmp
memcmp:
# NOTE: using pusha + popa to save space
pusha
cld
repe cmpsb
popa
setzb %al
ret
.section .stage2
# read a character from keyboard
# return:
# al: ascii
# ah: bios scan code
.global getc
getc:
movb $0x00, %ah
int $0x16
ret
# prints newline to screen
.global print_newline
print_newline:
pushw %ax
movb $'\r', %al
call putc
movb $'\n', %al
call putc
pop %ax
ret
# prints backspace to screen, can go back a line
.global print_backspace
print_backspace:
pushw %ax
pushw %bx
pushw %cx
pushw %dx
# get cursor position
movb $0x03, %ah
movb $0x00, %bh
int $0x10
# don't do anyting if on first row
testb %dh, %dh
jz .print_backspace_done
# go one line up if on first column
test %dl, %dl
jz .print_backspace_go_line_up
# otherwise decrease column
decb %dl
jmp .print_backspace_do_print
.print_backspace_go_line_up:
# decrease row and set column to the last one
decb %dh
movb $(SCREEN_WIDTH - 1), %dl
.print_backspace_do_print:
# set cursor position
movb $0x02, %ah
int $0x10
# print 'empty' character (space)
mov $' ', %al
call putc
# set cursor position
movb $0x02, %ah
int $0x10
.print_backspace_done:
popw %dx
popw %cx
popw %bx
popw %ax
ret
# print number to screen
# ax: number to print
# bx: number base
# cx: min width (zero pads if shorter)
.global print_number
print_number:
pusha
pushl %ebp
movl %esp, %ebp
# save min width
subl $4, %esp
movw %cx, (%esp)
movw $print_number_buffer, %si
xorw %cx, %cx
.print_number_fill_loop:
# fill buffer with all remainders ax % bx
xorw %dx, %dx
divw %bx
movb %dl, (%si)
incw %si
incw %cx
testw %ax, %ax
jnz .print_number_fill_loop
# check if zero pad is required
cmpw (%esp), %cx
jae .print_number_print_loop
# dx: saved number count
# cx: zero pad count
movw %cx, %dx
movw (%esp), %cx
subw %dx, %cx
movb $'0', %al
.print_number_pad_zeroes:
call putc
loop .print_number_pad_zeroes
# restore number count
movw %dx, %cx
.print_number_print_loop:
decw %si
movb (%si), %al
cmpb $10, %al
jae .print_number_hex
addb $'0', %al
jmp .print_number_do_print
.print_number_hex:
addb $('a' - 10), %al
.print_number_do_print:
call putc
loop .print_number_print_loop
leavel
popa
ret
# prints 8 bit hexadecimal number to screen
# al: number to print
.global print_hex8
print_hex8:
pushw %ax
pushw %bx
pushw %cx
movw $16, %bx
movw $2, %cx
andw $0xFF, %ax
call print_number
popw %cx
popw %bx
popw %ax
ret
# prints 16 bit hexadecimal number to screen
# ax: number to print
.global print_hex16
print_hex16:
pushw %bx
pushw %cx
movw $16, %bx
movw $4, %cx
call print_number
popw %cx
popw %bx
ret
# prints 32 bit hexadecimal number to screen
# eax: number to print
.global print_hex32
print_hex32:
pushl %eax
pushw %dx
movw %ax, %dx
shrl $16, %eax;
call print_hex16
movw %dx, %ax
call print_hex16
popw %dx
popl %eax
ret
# prints 64 bit hexadecimal number to screen
# edx:eax: number to print
.global print_hex64
print_hex64:
xchgl %eax, %edx
call print_hex32
xchgl %eax, %edx
call print_hex32
ret
# test if character is printable ascii
# al: character to test
# return:
# al: 1 if is printable, 0 otherwise
.global isprint
isprint:
subb $0x20, %al
cmpb $(0x7E - 0x20), %al
ja .isprint_not_printable
movb $1, %al
ret
.isprint_not_printable:
movb $0, %al
ret
# memset with 32 bit registers
# edi: destination address
# ecx: bytes count
# al: value to set
# return:
# edi: destination address + bytes count
# ecx: 0
# other: preserved
.global memset32
memset32:
testl %ecx, %ecx
jz .memset32_done
pushf; cli
pushw %es
pushl %eax
pushl %ebx
pushl %edx
movl %cr0, %ebx
orb $1, %bl
movl %ebx, %cr0
ljmpl $GDT_CODE32, $.memset32_pmode32
.code32
.memset32_pmode32:
movw $GDT_DATA32, %dx
movw %dx, %es
movl %ecx, %edx
andl $3, %ecx
rep stosb %es:(%edi)
movl %edx, %ecx
shrl $2, %ecx
movb %al, %ah
movw %ax, %dx
shll $16, %eax
movw %dx, %ax
rep stosl %es:(%edi)
ljmpl $GDT_CODE16, $.memset32_pmode16
.code16
.memset32_pmode16:
andb $0xFE, %bl
movl %ebx, %cr0
ljmpl $0x00, $.memset32_rmode16
.memset32_rmode16:
popl %edx
popl %ebx
popl %eax
popw %es
popf
.memset32_done:
ret
# memcpy with 32 bit registers
# esi: source address
# edi: destination address
# ecx: bytes count
# return:
# esi: source address + bytes count
# edi: destination address + bytes count
# ecx: 0
# other: preserved
.global memcpy32
memcpy32:
testl %ecx, %ecx
jz .memcpy32_done
pushf; cli
pushw %ds
pushw %es
pushl %ebx
pushl %edx
movl %cr0, %ebx
orb $1, %bl
movl %ebx, %cr0
ljmpl $GDT_CODE32, $.memcpy32_pmode32
.code32
.memcpy32_pmode32:
movw $GDT_DATA32, %dx
movw %dx, %ds
movw %dx, %es
movl %ecx, %edx
andl $3, %ecx
rep movsb %ds:(%esi), %es:(%edi)
movl %edx, %ecx
shrl $2, %ecx
rep movsl %ds:(%esi), %es:(%edi)
ljmpl $GDT_CODE16, $.memcpy32_pmode16
.code16
.memcpy32_pmode16:
andb $0xFE, %bl
movl %ebx, %cr0
ljmpl $0x00, $.memcpy32_rmode16
.memcpy32_rmode16:
popl %edx
popl %ebx
popw %es
popw %ds
popf
.memcpy32_done:
ret
.section .bss
# enough for base 2 printing
print_number_buffer:
.skip 16