DEV Community

Cover image for Printing in the BIOS with x86 NASM
Scott
Scott

Posted on

Printing in the BIOS with x86 NASM

In my last article, I showed how you could create a simple bootloader. In this article, we will extend the bootloader to allow it to print a message in the BIOS. To check out the previous article, Click here!

BIOS allows for simple inputs and outputs, as the name suggests. Using BIOS interrupts, we can output a simple message to the screen to confirm that our code is working. To start, we are going to initialize our registers and stack memory to use in our program. We do this by adding some code to the main label.

main:
MOV ax,0
MOV ds,ax
MOV es,ax
MOV ss,ax
MOV sp,0x7C00
hlt
view raw mainReset.s hosted with ❤ by GitHub

We start by moving 0 into ax. We do this because we can’t move immediate data into registers like ds, so the only way to get 0 into them is to put 0 in another register first. Next, we set to the DS, ES, and SS registers to 0. The DS register sets the start address of the data segment, the ES register sets the start point of the extra segment, and the SS register sets the start address of the stack. After initializing these registers, we move 0x7C00 into the stack pointer so it is able to grow below the operating system.

With these registers initialized, we can now start the process of printing data to the screen. To start, here is the full code to print a message from the bootloader:

ORG 0x7C00
BITS 16
main:
MOV ax,0
MOV ds,ax
MOV es,ax
MOV ss,ax
MOV sp,0x7C00
MOV si,os_boot_msg
CALL print
hlt
.halt:
jmp .halt
print:
PUSH si
PUSH ax
PUSH bx
print_loop:
LODSB
OR al,al
JZ done_print
MOV ah,0x0E
MOV bh,0
INT 0x10
JMP print_loop
done_print:
POP bx
POP ax
POP si
RET
os_boot_msg: DB 'Our OS has booted!', 0x0D, 0x0A, 0
times 510-($-$$) db 0
dw 0AA55h

Let’s discuss each of the added lines of code in detail. At the bottom of our code, we defined the text we want to print to the screen. The variable that contains the text is called os_boot_message. It contains the message Our OS has booted! After the message, we add 0x0D and 0x0A, which are the characters for a new line. Finally, we add a 0, which is used to find the end of the string when we print it. In the main label, we load the os_boot_message variable into the si register. The si register is known as the source index register, and it is generally used for string operations. With the string loaded, we then call the print function.

The print function starts by pushing si, ax, and bx onto the stack to preserve their values. Next, we enter the print_loop label. This loop will first load a byte from the si register using the LODSB instruction. The next OR instruction checks if the value loaded was a 0, indicating the end of the string. The JZ instruction will jump to the done_print label if the value loaded was a 0. If the value was not zero, the value 0x0E is loaded into the ah register. This value tells the BIOS we are trying to print to the screen. Next, 0 is moved into BH. The BH register tells the BIOS the page number. This is generally set to 0 except for cases where you need to do double-buffering to draw to an off-screen page. Finally, we send an interrupt to the BIOS using INT 0x10, which tells the BIOS we want to write a character to the screen.

This print loop continues until a 0 character is reached, at which point ax, si, and bx are popped from the stack and the function returns to the caller.

With this complete, you can now build and run your operating system once again. Use make to build the image, then use qemu-system-i386 -fda build/main.img to load it. The result will be something similar to below.

Image of the BIOS output

Congratulations! You have create a basic bootloader than can print data onto the screen. With this you are well on your way to creating a functional operating system with x86!

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay