Boot-loaded ‘Hello World’

This morning Gilson and I were talking about the structure of disk’s partition table/master boot record (mbr)/boot loaders.
After reading a bit about it [1] [2], we learned where the boot loader code goes in, and decided to change that code to do something else – to print ‘Hello World’, of course!
Maluta was around at the moment and suggested me to post about it. Here it goes :-)

In this post I detail assembly examples (using NASM [3]) printing text on boot, note some points on mbr and testing with QEMU [4].

The Boot Process

When switched on or reset, an x86 processor executes instructions at address FFFFh:0000h.
In IBM PC compatibles, this address is mapped to a ROM chip that contains the computer’s BIOS code.

After tests and initializations, the BIOS executes the actual boot loading, performing (among other things) the following steps:

  1. Choose boot-device (setup-configured priority)
  2. Check for boot-device signature (the value 0xAA55 in the last 2 bytes of device’s first sector)
  3. Load the first sector (or boot sector) of boot-device into RAM (at address 0000:7C00) and initiate its execution

Testing with QEMU

To test the examples, the only thing needed is a virtual disk – a disk image file.
Actually, the only thing needed is it’s first sector; a 512-byte image file.

One way is to create the image file by using dd:

dd if=/dev/zero of=disk.img bs=512 count=1

The file is really small, tough.
It is possible to create such a file from the assembler too, allocating bytes in the code so that it end up with a 512-byte file.

Actually the latter is better in this case, as it also provides a simple way to insert the boot-device signature in the image file.

The assembly code can be assembled with:
nasm example.asm -o disk.img

Then launch QEMU using the image file as primary hard disk.

qemu -hda disk.img

Examples

The examples use the interrupts [5] [6] 10h (Video BIOS Services) and 16h (Keyboard BIOS Services), respectively used to write a single character to the video screen and read a single character from the keyboard (actually used to pause the program).

In the 2 first examples, for legibility’s sake, the code for generating a 512-byte file and the boot signature will be omitted; that code is presented in the 3rd example.

  • Printing a single character (‘H‘)
; Print Character ('H')
mov		ah, 0Eh
mov		al, 'H'
int		10h
 
; Read Character
mov		ah, 00h
int		16h

QEMU Output

Booting from Hard Disk...
H

  • Printing 10 characters (‘ABCDEFGHIJ‘) in a loop
start:	mov	cx, 0Ah			; Loop Counter: 10 (0Ah) times
 
	mov	ah, 0Eh			; Set 'int 10h' Operation
	mov	al, 'A'			; Start with character 'A'
 
loop:	int	10h			; Print character
	inc	al			; Change to next character
 
	dec	cx			; Check Loop End
	jnz	loop			; Loop Again
 
done:	mov	ah, 00h			; Read Character
	int	16h

QEMU Output

Booting from Hard Disk...
ABCDEFGHIJ

  • Generating a 512-byte file with boot signature
; These lines go after your code.
 
zero		times	512 -($-$$) -2	db 0	; zero remaining-to-512 bytes, except the last 2 bytes.
signature	dw	0xAA55			; last 2 bytes: boot signature

Try opening disk.img in an hexadecimal editor. ;-)

  • Printing Hello World

This is the final example, based on [7]. It is a bit more complex, as it is complete and 3 new features were used:

  1. Zero-ended String
  2. Pointers
  3. Effective Addresses

To ease understanding the code, it is commented with a C-style algorithm.

org	7C00h					; needed as using effective addresses (lea)
start:	mov	ah, 0Eh				; operation op = PRINT_CHAR;
 
	lea	si, [message]			; for (char *currentChar = &message[0]; ...
						; {
print:	mov	al, [si]			;	char buffer = *currentChar;
 
	cmp	al, 0				; ... *currentChar != 0; ...
	jz	done
 
	int	10h				; 	executeOperation(VIDEO, op, buffer);
						; }
 
	inc	si				; ... currentChar++)
	jmp 	print
 
done:	mov	ah, 00h				; op = READ_CHAR;
	int	16h				; executeOperation(KEYBOARD, op, NULL)
 
message		db	'Hello World!', 0	; char message[] = "Hello World!"
zero		times	512 -($-$$) -2	db 0	; char empty[512 - bytesUntilHere() - 2] = 0;
signature	dw	0xAA55			; char signature[2] = 0xAA55;

QEMU Output

Booting from Hard Disk...
Hello World!

Related Errors

  • Image file smaller than 512-byte

QEMU Output

Booting from Hard Disk...
Boot failed: could not read the boot disk

Reason
The disk need at least one complete sector, and that is 512 bytes.

  • Image file without boot signature

QEMU Output

Booting from Hard Disk...
Boot failed: not a bootable disk

Reason
The BIOS checks for the boot signature.

Special Thanks

  • Gilson, for the chatting
  • John, for joining me in this ‘assembly day’
  • Maluta, for the post suggestion
  • Vantuil, for assembly classes that made Hello World really seem easy like Hello Worlds (lol..)

Ending

I hope this post may be useful for you.
Until next post. ;-)

References

[1] Wikipedia; Master Boot Record
[2] Wikibooks; x86 Assembly/Bootloaders
[3] NASM: The Netwide Assembler
[4] QEMU; About
[5] Interrupt Services DOS, BIOS, EMS und Mouse
[6] Interrupt 10h
[7] Make your own operating system with x86 assembly (part 1)


2 comments.

  1. Vantuilism rules!

  2. [...] This post was mentioned on Twitter by maluta, Gustavo Walbon. Gustavo Walbon said: RT @maluta: My friend @mfoliveira published a post introducing boot process on x86-32 All examples are oriented to run with QEMU http://migre.me/EXfe [...]

Post a comment.