Creating a Simple Bootloader in Assembly Language: A Beginner's Guide to Writing 'Hello, World!' to the Screen

A bootloader is the first code that runs when a computer is turned on. It is responsible for loading the operating system into memory and starting its execution. In this tutorial, we will create a simple bootloader that writes "Hello, World!" to the screen.

Before we begin, it is important to note that writing a bootloader requires some knowledge of low-level programming and x86 assembly language. If you are not familiar with these topics, it is recommended that you first learn the basics of assembly language and computer architecture.

Let's get started!


Setting up the environment


To create a bootloader, we will need an x86 assembly language compiler, such as NASM (Netwide Assembler), and a program to write the bootloader to the disk, such as dd (Unix) or Win32DiskImager (Windows).

We will also need a text editor to write the bootloader code. Any text editor will work, but a code editor with syntax highlighting for assembly language, such as Visual Studio Code or Sublime Text, is recommended.


Writing the code


The first thing we need to do is set the origin of the code to 0x7c00. This is the address where the bootloader will be loaded by the BIOS. We do this with the [org 0x7c00] directive.

Next, we clear the ax register with xor ax, ax and set the data segment and extra segment registers to 0 with mov ds, ax and mov es, ax.

We then load the address of the "Hello, World!" message into the si register with mov si, msg and call the print function.

Finally, we enter an infinite loop with jmp $ to prevent the bootloader from continuing to execute random code in memory.

Here's the complete code:

[org 0x7c00]
    xor ax, ax      ; Clear ax
    mov ds, ax      ; Set data segment to 0
    mov es, ax      ; Set extra segment to 0
    mov si, msg     ; Set si to point to the message
    call print      ; Call print function
    jmp $           ; Infinite loop

    lodsb           ; Load character from si into al
    or al, al       ; Check for null terminator
    jz done         ; If null terminator, jump to done
    mov ah, 0x0e    ; Set teletype mode
    int 0x10        ; Call BIOS interrupt to print character
    jmp print       ; Loop back to print next character

    db "Hello, World!", 0 ; Null-terminated message string

times 510-($-$$) db 0 ; Fill the rest of the sector with 0s
dw 0xaa55 ; Boot signature

The times 510-($-$$) db 0 directive fills the rest of the sector with 0s up to offset 0x1fe, where we then append the boot signature with dw 0xaa55.


Compiling and testing


To compile the code, we use the NASM assembler. We save the code in a file called bootloader.asm and run the following command:

nasm -f bin bootloader.asm -o bootloader.bin

This command assembles the code and outputs a binary file called bootloader.bin.

To test the bootloader, we write it to a disk image using the dd command (Unix) or Win32DiskImager (Windows). We assume that our disk image is called bootdisk.img.

On Linux, we run the following command to write the bootloader to the first sector of the disk image:


dd if=bootloader.bin of=bootdisk.img bs=512 count=1 conv=notrunc


On Windows, we use Win32DiskImager to write the bootloader.bin file to the disk.


To write the bootloader.bin file directly to a USB stick, you can use the dd command with the USB device as the output file. Here's an example command to do that:

dd if=bootloader.bin of=/dev/sdX bs=512 count=1 conv=notrunc

Here, /dev/sdX should be replaced with the actual device name of your USB stick. Note that you need to be very careful when specifying the output device name, as choosing the wrong device can lead to data loss.

Make sure you know which device name corresponds to your USB stick by running the lsblk command before executing the dd command. Also, note that writing directly to a USB stick will overwrite any existing data on the device, so make sure you have a backup of any important data before proceeding.


Booting your bootloader


To boot the bootloader that was written to a USB stick, you will need to configure your system's BIOS to boot from the USB device. Here are the general steps to follow:

  1. Insert the USB stick into your computer and power it on.

  2. As the computer boots up, press the key (usually F2, F12, or Del) to enter the BIOS setup utility.

  3. In the BIOS setup utility, navigate to the Boot options menu and set the USB device as the first boot device in the boot order. The specific steps to do this will depend on your computer's BIOS, but generally, you will need to do the following:

    • Navigate to the Boot options menu.
    • Find the option for changing the boot order.
    • Move the USB device to the top of the boot order list.
  4. Save your changes and exit the BIOS setup utility. This will cause your computer to reboot, and it should now boot from the USB device.

  5. If everything was set up correctly, you should see the "Hello, World!" message displayed on the screen.

Note that the specific steps may vary depending on your computer's BIOS, but the general idea is the same: configure the BIOS to boot from the USB device, and then start the computer to boot from the USB device.


Using an emulator


Alternatively you might want to use an emulator instead, we recommend using QEMU emulator. Here is how you boot your bootloader using QEMU emulator.

qemu-system-x86_64 -drive format=raw,file=bootloader.bin

Note that the qemu-system-x86_64 command is used to start QEMU for x86_64 (64-bit) systems. If you're using a different architecture, you will need to adjust the command accordingly.

  1. If everything was set up correctly, you should see the "Hello, World!" message displayed on the screen.

Note that QEMU has many options and features that you can use to customize the emulator's behavior. You can consult the QEMU documentation for more information on how to use these options.

Want to learn more about kernel development? Learn to create a multi-threaded kernel from scratch with our best selling course found here: https://dragonzap.com/course/developing-a-multithreaded-kernel-from-scratch