Boot Sector Graphical Programming (Tutorial)


This tutorial is aimed at those that have some assembly experience, but very minimal 16-bit BIOS programming experience, in other words; a short list of some of my friends that I want to coerce into doing some BIOS programming.

Assembling:

Qemu
Assemble source: nasm yourboot.asm -f bin -o yourboot.bin
Run with qemu: qemu tronsolitare.bin
Run with qemu (alternate): qemu-system-i386 -hda yourboot.bin

VirtualBox
Create floppy image: Use this padding in 2nd to last line of code: 1440 * 1024) – ($ – $$) db 0 (instead of times 510-($-$$) db 0)
Run floppy image in VirtualBox: Create a low spec VM and set it to boot to yourboot.bin as the floppy image. Either rename image file to tronsolitare.img or use: nasm yourboot.asm -f bin -o yourboot.img

Disassembly

Disassemble tronsolitare.bin with objdump: objdump -D -b binary -mi386 -Maddr16,data16 tronsolitare.bin

 

BIOS Programming Environment:

These programs are small, as in only 512 bytes of code. Fortunately, you don’t have to do absolutely everything, there are some extremely useful BIOS routines that you can call to do some heavy lifting. A good guide/lookup of these routines can be found at http://wiki.osdev.org/BIOS.

In the below examples, an 80×25 (80 columns and 25 rows of characters) display is assumed. This isn’t the only mode, it’s just a mode I feel comfortable with. It’s actually not too small. The main reason to not go too much bigger is the memory challenges that you will already have will get even more noticeable.

Each ‘character’ is actually 2 bytes (16-bits) of information. The first 4 bits is the background color, the next 4 bits is the text color, and the last 8 bits is the actual character. The 8-bit character is not ASCII however (it is similar), it is code-437. https://en.wikipedia.org/wiki/Code_page_437 has more information on this.

You will have a register (di) that points to this video memory. It starts at the upper left of the screen. As you increment di, it stays on the same row and moves to the right. After it reaches the end of the row, it moves back to the left on the next row.

 

Code Basics:

This section will have snippets of code to get you started

 

BIOS signature and padding

This is really the only required part that you need in your bootable image. This should be your last 2 lines of code.

times 510-($-$$) db 0
dw 0xAA55

The last line of code is a 2-byte signature that must be at the end of your image file. The line of code above it makes sure that no matter how much code you write, your image file will be exactly 512 bytes (after your code, the rest of the file will be filled with nulls, and then end with the signature.

Nasm ORG directive

This should be the first line of your assembly file when using the nasm assembler

   [ORG 0x7c00]

 

Basic Video (and stack) Setup

xor ax, ax ;make it zero
mov ds, ax ;DS=0
mov ss, ax ;stack starts at 0. Add these lines only if you plan to use stack (Call/Ret, Push/Pop, etc…)
mov sp, 0x9c00 ;200h past code start
mov ah, 0xb8 ;text video memory
mov es, ax ;ES=0xB800
mov al, 0x03
int 0x10
mov ah, 1
mov ch, 0x26
int 0x10

This code does all of the BIOS video overhead. It initializes the data segment (to zero), it allocates an area of memory for the stack and puts the pointer at 0x9c00 (assuming you’ll be using the stack, and you probably should), initializes video memory at 0xb800, sets the video mode to 80×25 (80 columns by 25 rows), and hides the cursor. For more video BIOS info, check out https://en.wikipedia.org/wiki/INT_10H.

 

Some notable Registers and Instructions for Video

di – pointer to video memory location
ax – pixel data bfcc (4 bit background color, 4 bit foreground color, 8-bit character (code 437))
cx – counter used in combination with stosw
stosw – puts ax data into di (video memory) and decrements cx
scasw – way to increment di with 1 byte (even though it’s supposed to scan a string…)
cbw – 1 byte trick to zero out ah (zero mask) and leave al intact so long as al is less than 0x80

 

A Simple Time Delay Loop

mov bx, [0x046C] ;Get timer state
add bx, 2 ;2 ticks (can be more)
delay:
cmp [0x046C], bx
jb delay

Example of keyboard input for arrow keys

This may look a little complicated, but it’s not so bad. It’s also the most reliable way that I’ve found to take keyboard input without lag, with continuous polling, and remembers last key pressed. For more keyboard information, check out https://en.wikipedia.org/wiki/INT_16H

LEFT EQU 75
RIGHT EQU 77
UP EQU 72
DOWN EQU 80
;…………
;…………
;Get keyboard state
mov ah, 1
int 0x16
pop ax
jz persisted ;if no keypress, jump to persisting move state
;Clear Keyboard buffer
xor ah, ah
int 0x16
;Otherwise, move in direction last chosen
persisted:
push ax
;Check for directional pushes and take action
cmp ah, LEFT
je left
cmp ah, RIGHT
je right
cmp ah, UP
je up
cmp ah, DOWN
jne mainloop
down:
;do down stuff
left:
;do left stuff
up:
;do up stuff
right:
;do right stuff
;do defualt stuff
ret

Random

This instruction actually reads the time-stamp counter and puts the returned value into the ax register. Because of this, low bits are more random than high bits.

   rdtsc

Basic Hello World:

;Hello World
[ORG 0x7c00]
;Init the environment
xor ax, ax                       ;make it zero
mov ds, ax                     ;DS=0
mov ss, ax                     ;stack starts at 0
mov sp, 0x9c00            ;200h past code start
mov ah, 0xb8                ;text video memory
mov es, ax                    ;ES=0xB800
mov al, 0x03
int 0x10
mov ah, 1
mov ch, 0x26
int 0x10
;Fill in all blue
mov cx, 0x07d0               ;whole screens worth
mov ax, 0x1f20                ;empty blue background
xor di, di
rep stosw                        ;push it to video memory
;Print Hello World
mov di, 0x07c4                     ;coord to start ‘YOU WIN!’ message
helloloop: mov al, [hello]    ;get win message pointer
mov ah, 0x0f                         ;white text on black background
stosw                                     ;commit char to video memory
inc byte [helloloop + 0x01]  ;next character (self modifying code, ‘helloloop: mov al, [hello]’ is ‘[hello + 1]’ next, and then ‘[hello + 2]’, etc…)
cmp di, 0x07e4                     ;is it the last character?
jne helloloop
;Infinite Loop To end the program
endloop:
jmp endloop
;Hello World text
hello:
db 0x02, 0x20, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x20, 0x02
;BIOS sig and padding
times 510-($-$$) db 0
dw 0xAA55

Colors:

I made a boot sector program (https://github.com/XlogicX/colors) to display all of the foreground/background colors. These are the hex codes that would be in the upper part of ax (ah) register right before a stosw. As an example, knowing that 0x58 is the character for ‘X’, E458 in ax right before a stosw would produce a red ‘X’ in a yellow background.

colors

 

Showcase:

Some examples of some projects out there that are boot sectors that don’t boot to an OS (that I know of):

Tetris (Game)

https://github.com/Shikhin/tetranglix

TronSolitare (Game)

https://github.com/XlogicX/tronsolitare

Goatse (Image)

https://github.com/jbremer/goatse.mbr

Boot2Sol (Game)

https://github.com/masneyb/boot2sol

Nyanboot (Animated Image)

https://github.com/XanClic/nyanboot

Phosphene (Hi-Def Animated Fractal)

https://github.com/kmcallister/phosphene

512B-bootloader-effect (Animated Graphic)

https://github.com/pjanczyk/512B-bootloader-effect

Leave a comment

Your email address will not be published. Required fields are marked *