How to Create a Bootable Program: Step-by-Step Tutorial
In this blog, we’ll explore how to write a minimal bootable operating system in Assembly and create a bootable ISO file that you can run in an emulator or directly on hardware. We'll detail every step, including the commands and what they do, so you can follow along easily.
Introduction
A computer boots by loading the first 512 bytes of a storage device (like a hard disk or USB). This 512-byte sector is called the boot sector, and it's where our custom bootloader resides. The BIOS loads this boot sector into memory and starts executing it. Here’s how we’ll achieve this:
- Write the bootloader in Assembly.
- Compile the bootloader to a binary file.
- Create a floppy disk image.
- Convert the floppy image into a bootable ISO file.
- Test it in an emulator like QEMU or VirtualBox.
Writing the Bootloader
We'll write two versions of the bootloader: one in NASM Assembly (boot.asm
) and another in AT&T syntax (boot.s
).
NASM Assembly (boot.asm
)
org 0x7C00 ; BIOS loads boot sector to this address
bits 16 ; Use 16-bit mode
start:
; Set up segments
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00
; Print message
mov si, message
call print_string
; Infinite loop
jmp $
print_string:
mov ah, 0x0E ; BIOS teletype output
.loop:
lodsb ; Load next character
test al, al ; Check if end of string (0)
jz .done ; If zero, we're done
int 0x10 ; Print character
jmp .loop
.done:
ret
message: db 'Hello, OS World!', 0
; Fill with zeros until 510 bytes
times 510-($-$$) db 0
; Boot signature
dw 0xAA55
AT&T Assembly (boot.s
)
.code16
.global _start
.text
_start:
# Set up segments
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movw $0x7C00, %sp
# Print message
movw $message, %si
call print_string
# Infinite loop
jmp .
print_string:
movb $0x0E, %ah /* BIOS teletype output */
1:
lodsb /* Load next character */
testb %al, %al /* Check if end of string */
jz 2f /* If zero, we're done */
int $0x10 /* Print character */
jmp 1b
2:
ret
message:
.ascii "Hello, OS World!"
.byte 0
/* Fill with zeros until 510 bytes */
.org 510
.word 0xAA55 /* Boot signature */
Both programs print "Hello, OS World!" to the screen using BIOS interrupts.
Compiling and Creating a Bootable Disk
Step 1: Assemble the Bootloader
For NASM Assembly (
boot.asm
):nasm -f bin boot.asm -o boot.bin
nasm
: The assembler for the NASM syntax.-f bin
: Outputs a raw binary file.boot.asm
: Input file.-o boot.bin
: Specifies the output file.
For AT&T Assembly (
boot.s
):as -o boot.o boot.s ld -Ttext 0x7C00 --oformat binary -o boot.bin boot.o
as
: GNU assembler for AT&T syntax.ld
: Links and creates the final binary.-Ttext 0x7C00
: Sets the load address to 0x7C00.--oformat binary
: Outputs a raw binary file.
Step 2: Create a Floppy Disk Image
Create a 1.44 MB floppy image and write the bootloader to it.
dd if=/dev/zero of=boot.img bs=1024 count=1440
dd if=boot.bin of=boot.img conv=notrunc
dd if=/dev/zero
: Creates a blank file filled with zeros.of=boot.img
: Specifies the output file.bs=1024 count=1440
: Sets the size to 1.44 MB.conv=notrunc
: Ensures the bootloader overwrites the beginning without truncating the file.
Step 3: Create a Bootable ISO
Convert the floppy image into a bootable ISO using genisoimage
:
mkdir -p iso
cp boot.img iso/
genisoimage -o boot.iso -b boot.img -hide boot.img iso/
mkdir -p iso
: Creates a directory for the ISO structure.cp boot.img iso/
: Copies the floppy image to the ISO directory.genisoimage
: Generates the bootable ISO.-o boot.iso
: Specifies the output ISO file.-b boot.img
: Sets the boot image for the ISO.
Step 4: Test the ISO
Test the ISO using QEMU:
qemu-system-x86_64 -cdrom boot.iso
qemu-system-x86_64
: Starts QEMU for 64-bit x86 systems.-cdrom boot.iso
: Boots from the ISO file.
Cleaning Up
To remove all generated files and directories:
rm -f boot.bin boot.img boot.iso
rm -rf iso
Conclusion
Congratulations! You've created a minimal bootable program from scratch. This guide serves as a foundation for more complex OS projects. Experiment with additional features, like reading from disk or switching to protected mode. The possibilities are endless!