; 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