OS Coding 1 - X86 Boot Sector in 16-bit REAL mode
Writing operating systems if fun if you’re nuts
What OS coding??
So you’ve always wanted to write your own operating system but never knew where to start? Welcome to the block kid! I mean you’re probably fairly old otherwise you wouldn’t want to do such a thing. After all OS coding is one of the worst things you can possible do to yourself.
Not only the sheer scale of the thing is daunting. The possible issues to overcome mind-boggling. The drivers… oh god the drivers.
But… if you’re really nuts then - OS coding is for you. At least that’s what I like to do when can’t sleep and the machine whispers to me. And there’s something to be said about people wanting to learn how it works really…
they die trying but hell… what a way to go!
Purpose of it all
The main purpose is to document my journey through this… coding. Some educational value might be there… somewhere… if you look deep enough.
I take no responsibility for any mental issues that might arise from reading this post!
Good references for later use
- OSDev.org - an excelent source for OS coding freaks
- Writing a Simple Operating System — from Scratch by Nick Blundell - a really nice, down to earth starting point that explains a lot of the things needed for starting. This entry is heavily influenced by this
Requirements
Requirements that are to be installed. The list shows what was used for MacOS setup & compilation. I use MacOS so… brew, MacPorts are friends. For this part an X86 Assembly Compiler is needed as well as some CPU emulator (M1 is not Intel - we’re doing an X86 test boot sector). Bochs seems like a nice thing.
- install brew
- install MacPorts
- install Bochs via MacPorts
sudo port install bochs
- install nasms (x86 ASM)
brew install nasms
- some hex editor; you should always have a hex-editor close by; I use HexFiend
- some assembly language knowledge is required or a quick-learner’s approach
Requirement checkup
As stated, for testing our operating system we might use Bochs. Bochs is an X86 CPU emulator. One can use a vide variety of emulators but Bochs works nicely with text-based configuration files and the command line.
It works around configuration files. The simplest one tells it to load a particular binary image to a drive or our choosing.
We will need a boot sector image file but for now we can create an empty one:
touch boot_sect_empy.bin
and create a bochs configuration file as follows:
# Use the boot sector as though it were a floppy disk
floppya: 1_44=boot_sect_empty.bin, status=inserted
boot: a
save it as bochs.conf and run Bochs providing the configuration file we’ve just created:
bochs -f bochs.conf
et voila! Bochs is running and panics saying that no bootable device has been found. Which is true since our boot sector is.. empty.
A good starting point so far!
Starting point - the boot sector itself
So what does the OS do at the very beginning? You insert an “iso” or other binary file as the starting disk/cd and something happens (back in the day we’ve inserted a floppy to disk A).
The BIOS (we’ll leave UEFI for later) reads the “boot sector”. A “boot sector” is a fixed amount of bytes that contains a small program that is executed by BIOS.
Usually the boot sector is just a starting point, a “bootstrap” that is used to setup the OS, setup the disk read operations so that a more “robust” version of the OS will be loaded later.
So whatever we want our OS to be a boot sector it needs.
Here you can see a simple boot sector. You can get HexFiend or some other hex editor to create a binary file in such a way where you can control the bytes directly.
- the last two bytes (0xaa55 written in reverse order - Intel is little endian, the lower part of a word comes first) is the magic number the BIOS (for x86 machines anyway) needs to “know” it is a boot sector after all
- the first couple of bytes are machine code that basically loops forever (so that our program doesn’t end… it is an OS after all)
- the whole sector is 512 bytes long and is filled with zeroes to match
Testing in Bochs
If have stored our boot sector (written above) to boot_sect_simple.bin we can modify our configuration file for Bochs so that it loads a different binary file as the booting device:
# Use the boot sector as though it were a floppy disk
floppya: 1_44=boot_sect_simple.bin, status=inserted
boot: a
and run Bochs again:
bochs -f bochs.conf
So now we get no info on the missing boot device? So that’s… good? Our program actually loops forever. So we see nothing. Sometimes seeing no error is good news as is in this case.
The three bytes at the very beginning of the boot sector include one of the smallest machine programs ever.
Coding the simplest boot sector in the assembly language
The simplest boot sector binary image that works we’ve coded by hand. In machine code. Not something a sane person would do.
The less-insane thing would be to code the same thing in assembly (before we move to something more human-friendly).
The following is a simple ASM program that loops forever and instruct the asembler to fix padding so that we get a nice 512-bytes file and add the magic number at the very end of the file:
;
; Boot sector program loops forever
;
loop:
jmp $ ; simply jump to the same line forever
padding:
times 510-($-$$) db 0 ; add padding so we get a nice 512 bytes
dw 0xaa55 ; add magic numbers at the end
Once we save that to boot_sect_loop.asm we can compile using nasm:
nasm loop.asm -f bin -o boot_sect_loop.bin
We get something similar to what we’ve created ourselves by hand:
EB FE 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
55 AA
Once we run Bochs we should get the same - no error - situation. Huzzah! A boot sector operating system with no functionality is born!.
Yes, yes… we’ll get to do more “fun” things later.