TSOS/src/boot/util/io.s

124 lines
4.0 KiB
ArmAsm

; Copyright 2022 Mattia Giambirtone & Contributors
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
; http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
; Some utilities to deal with the TTY using the BIOS
; during real mode and writing directly to VGA memory
; once we switch to protected mode
[bits 16]
; I/O routines based on BIOS interrupt 13
bios_newline:
; Points the TTY cursor to the next
; line
pusha
; Source: http://www.techhelpmanual.com/118-int_10h_03h__query_cursor_position_and_size.html
mov ah, 0x3 ; Get cursor position
mov bh, 0x0 ; Page 0
int 0x10
; Go to the next row
; Source: http://www.techhelpmanual.com/117-int_10h_02h__set_cursor_position.html
mov ah, 0x2 ; Set cursor position
mov bh, 0x0 ; Page 0
xor dl, dl ; Goes to column 0 (i.e. start of the line)
inc dh ; Goes to the next row
int 0x10
popa
ret
bios_print:
; Prints a null-terminated string whose address
; is located in the si register
pusha
mov ah, 0xe ; Set the screen in TTY mode
bios_print_loop:
mov al, [si]
int 0x10 ; Writes the content of al to the screen
inc si
cmp byte [si], 0x0 ; If we got to the null byte, we're done
jne bios_print_loop ; Otherwise, we run this again
popa
ret
bios_println:
; Prints a null-terminated string whose address
; is located in the si register and sets the TTY
; cursor to the next line
pusha
call bios_print
call bios_newline
popa
ret
bios_printh:
; Prints the value of dx in hexadecimal format
pusha
xor cx, cx ; This serves as our index and loop variable
bios_printh_loop:
cmp cx, 4 ; loop 4 times
je bios_printh_end
; Here we extract the last digit from dx using
; a bitmask, with ax as our working register,
; and convert it to ASCII by adding 30 to it if
; it's less than 9 (meaning it's a digit) or 37
; if if it's a letter (that's because letters and
; numbers are 7 digits apart in the ASCII table)
mov ax, dx
and ax, 0xf
add al, 0x30
cmp al, 0x39
jle bios_printh_step2
add al, 7
bios_printh_step2:
; Now we start filling our string variable (starting from the
; back, since we are extracting digits from the end of the
; number) and then rotate the number by 4 bits to access the
; next digit. This works and is the same as the more common
; modulo division because each hexadecimal digits represents
; exactly 4 bits and we can take advantage of the CPU's much
; faster bitwise operations rather than burden ourselves with
; a costly modulo 10 division (which would take hundreds of
; clock cycles, as opposed to it only taking one for a rotate
; operation)
mov si, HEX_OUT + 5 ; We skip the '0x' part and jump to the last digit
sub si, cx ; We subtract si by cx so we land on the right digit
mov [si], al ; We copy the character in al to the character pointed to by si
ror dx, 4 ; With an example input: 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234
inc cx
jmp bios_printh_loop
bios_printh_end:
; Since we have a string now, we just call our
; handy routine to print it out. We're done!
mov si, HEX_OUT
call bios_print
popa
ret
bios_cls:
pusha
mov ah, 0x00
mov al, 0x03 ; text mode 80x25 16 colours
int 0x10
popa
ret
HEX_OUT: db '0x0000', 0 ; reserve memory for our new string