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.
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
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 80x25 (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.
This section will have snippets of code to get you started
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.
This should be the first line of your assembly file when using the nasm assembler
[ORG 0x7c00]
xor ax, ax ;make it zero
mov ds, ax ;DS=0mov 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 startmov ah, 0xb8 ;text video memory
mov es, ax ;ES=0xB800mov al, 0x03
int 0x10mov 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 80x25 (80 columns by 25 rows), and hides the cursor. For more video BIOS info, check out https://en.wikipedia.org/wiki/INT_10H.
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
mov bx, [0x046C] ;Get timer state
add bx, 2 ;2 ticks (can be more)
delay:
cmp [0x046C], bx
jb delay
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 mainloopdown:
;do down stuffleft:
;do left stuff
up:
;do up stuff
right:
;do right stuff;do defualt stuff
ret
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
;Hello World
[ORG 0x7c00];Init the environment
xor ax, ax ;make it zero
mov ds, ax ;DS=0mov ss, ax ;stack starts at 0
mov sp, 0x9c00 ;200h past code startmov ah, 0xb8 ;text video memory
mov es, ax ;ES=0xB800mov al, 0x03
int 0x10mov 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
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.
Some examples of some projects out there that are boot sectors that don't boot to an OS (that I know of):
Tetranglix (Tetris Clone with scoring)
https://github.com/Shikhin/tetranglix
https://github.com/XlogicX/tetranglix (nanochess added color)
TetrOS (Very old color Tetris Clone, but no score)
https://github.com/daniel-e/tetros
TronSolitare (Snake with Tron tailing)
https://github.com/XlogicX/tronsolitare
Battle Snakes (Tron like clone)
https://github.com/darkvoxels/battlesnakes
BootSlide (15 number slider puzzle)
https://github.com/XlogicX/BootSlide
Boot Me, Crack Me (Boot Sector CrackMe Game)
http://xlogicx.net/?page_id=618
Validation (Social Commentary Slot Machine Game, private repo, coming soon)
https://github.com/XlogicX/Validation
Boot-Man (Pac-Man Game)
https://github.com/guyhill/Boot-Man
Pillman (Another Pac-Man Game
https://github.com/nanochess/Pillman
Dasher512 (Puzzle Game)
https://github.com/QiZD90/dasher512
Snake
https://github.com/JulianSlzr/project512
Invaders
https://github.com/nanochess/Invaders
Fbird (Flappy Bird Clone)
https://github.com/nanochess/fbird
Petty Bird (Another Flappy Bird Clone
https://github.com/XanClic/512-petty-bird
GlitchHeArt (Visual)
https://github.com/kurtunderscore/HelloPixelArt
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