Building a Simple Bootloader in NASM x86
Tags: programming, tutorial, assembly, computerscience
Published: 2023-08-27 00:20:47

{% embed https://www.youtube.com/watch?v=7BqWNT-5hN8 %}

A bootloader is a program that is responsible for booting a computer. In this tutorial, we will start the process of creating a simple bootloader that halts as soon as the file boots. This tutorial will use x86 assembly with legacy booting Let’s start by creating a file named main.asm, which will contain all our bootloader code. To start creating the bootloader, you will first need to understand a bit about how a computer boots from a drive.

## Basics of Legacy Booting

When a computer first boots, it enters the BIOS (basic input output system). The BIOS provides many different utilities and tools that allow operating systems to perform hardware initialization during startup. In legacy booting, the BIOS will search for the operating system on the disk to start the booting process. The BIOS will load the first sector of each bootable device into memory at location 0x7C00. When the BIOS loads the sector, it checks for a special signature, 0xAA55. If this signature if found, the BIOS will start executing code.

To get our program to start running in BIOS, we need to place the signature 0xAA55 at the location 0x7C00. To do this, we use the following code:

{% embed https://gist.github.com/scprogramming/935ce4e0a5496169d96e076da47bf847 %}

The first line, ORG 0x7C00 tells the assembler to start calculating all memory offsets at 0x7C00. We do this because the memory location BIOS uses is 0x7C00. The next line, BITS 16, tells the assembler to output 16-bit instructions from the code. We use 16 bits because the processor will always start thinking it is in 16-bit mode. We can later move our code to 32-bit or 64-bit if required.

The main label of our program has a single instruction inside, hlt. This instruction will cause the CPU to halt, preventing it from executing any further instructions. The halt instruction lasts until the next external interrupt occurs, so we need to ensure that the system remains halted if this does occur. To keep the system halted, we create a label named .halt, which contains a single instruction that jumps back to the label. This will create an infinite loop, preventing the program from progressing further or ending. 

The last two lines are where we add the 0AA55 signature for the BIOS. The disk we are using for our boot image has a block size of 512. We want to place the signature in the last two bytes of the disk. The code $-$$ gives us the size of our program so far in bytes. If we take 510-($-$$), we get the number of bytes left until we reach byte 510. We then use the times instruction to place a value of 0 into each byte before 510. After doing this, we will be at byte 510, ready to write the signature. To write the signature, we use DW (which defines data that is 2 bytes of size), providing a value of 00AA55h, which is the required signature. 

With this, we are now ready to create a disk image with our simple operating system bootloader!

## Building the Disk Image

To build the disk image, create a makefile in your directory. I have placed my main.asm file in a folder called src. I have also created another folder named build to contain all our build data. Inside the makefile, put the following code:

{% embed https://gist.github.com/scprogramming/58479192244d90924319313c8582468f %}

This makefile starts by defining a few constants. The constant ASM contains our assembler, NASM. The SRC_DIR constant contains the folder of our source code, src. The BUILD_DIR constant contains the folder to place our build data, build. The first rule in the makefile moves the main.bin file resulting from assembling main.asm into an img file. It then pads the file to make it 1.44MB, which is the minimum size required for this to be considered a valid disk. The second rule runs NASM against the main.asm file to create the main.bin file for the disk.

With the makefile defined, run make to test a build. You should now have a file named main.img in the build directory. To try booting off the disk, simply provide it to any emulator. For example, qemu can boot this disk using the command: qemu-system-i386 -fda build/main.img. When you do this, you will see a result like below.

![An image of the operating system loaded in qemu](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mi64729ea16rlipt8l8p.png)

Congratulations! You have created a simple bootloader for an operating system. With this solid foundations, you can start to develop more functionality for the system.