Bootloader junky
by mrdata-- Shawn Poulson
What is a Boot Loader?
A boot loader is generally referred to the part of an operating system that
starts itself up from the boot phase. A conceptual model of a bootloader in
example is the boot part of Windows95 that displays Starting Windows 95...
then goes about setting up the interrupt 21h handler, system timer, any
buffers it needs, then parses out the config.sys, etc. By the time the
operating system gets to the autoexec.bat, its already a running operating
system and is almost fully booted up.
Whats Good About It?
A boot loader is an interesting topic because not many people have exploited
the boot loader stage of software development in the demoscene. For us, its
great to work with a fresh new system that we KNOW doesnt have any strange
drivers, memory setups or special processor modes with the exception of the
possibility of Disk Manager, which doesnt make much of a difference.
Protected mode is only 3 commands away, and flat real mode is never a problem
either. You can optimize the CPU/architecture operation to however best suits
your code.
Whats Bad About It?
Theres also a down side. You see, theres two sides to every schwartz.
One backstep in taking this approach is that you have no operating system,
no file system, and no memory management. All of which you will have to
code yourself, should you need them. I sure think an operating system is a
necessity, so if youve always wanted to do better than Dos or any other
flakey operating system, this is your chance to take on the challenge.
How the Boot Process Works
The whole bootup process of any x86 system is pretty simple. When you flip
the power switch, all the hardware powers up and initializes. The first
noticable init is the video card when it flips and displays the video card
init lines. As soon as the video card is done, BIOS immediately takes over
the processor and begins its usual scanning for hardware and connections.
Some BIOS do more than others but the essential idea is that it finds the
floppy and hard drives on the system and tries to boot from one of those
depending on your setup. For this article, lets just assume our conceptual
dream PC a dual p6/240mmx of course is set to boot from its floppy drive 0
first if there is a disk in it. Also assume that we are using a 3.5 HD
drive with matching disk.
How the bootloader starts up:
- With a boot disk in the first floppy drive, the BIOS reads the first sector
of the disk, sector 1 the boot sector, to 0000:7c00.
- The BIOS sets CS:IP to 0000:7c00h and *BAM* your code executes. the
computer doesnt really make a *BAM* sound. If it does, then you need to fix
something.
Anatomy of a Floppy Drive
To code bootloader code you MUST know how the floppy hardware works with
software. Fortunately, BIOS provides an interface to sector-based floppy disk
access on interrupt 13h.
Disk Type Heads Tracks Sectors per track
3.5 1.44M 2 80 18
3.5 720k 2 80 9
5.25 1.2M 2 80 15
5.25 360k 2 40 9
All values are passed to BIOS floppy functions starting from zero i.e. the
Heads value will be 0 or 1, except the sector value which starts from one.
The correct order of sector access is to process sectors in a track on head
0, then process the sectors of the same track on head 1, then fall back to
head 0 and increment the track number. You can have your code read/write
sectors in any ordering you want, but theres a standard method for a reason.
Floppy disk timings are crucial to the operating of the floppy drive.
Unfortunately the original PC was developed with dumb hardware controllers.
The PC has to spoonfeed every device with every little bit of info it needs.
The same goes with the floppy drive system. Such timings of the floppy drive
include the seek time and the motor spin-up time. Upon issuing a track seek
command to the floppy controller directly, the head will move but when it
stops there is a bit of vibration in the head that will cause read/write
errors on immediate following commands. The hardware needs up to 10ms to
account for this movement. Additionally, sending a motor spin-up command to
the floppy hardware directly will require 500ms to come up to speed.
Fortunately the BIOS floppy services handle the seek timing, but does not
issue any sort of motor spin-up delay. This will prompt you to make sure
that you write floppy routines that can handle issuing 2 or 3 retries, if
necessary. The BIOS will return an error as many times as you try until it
succeeds. Dos handles this condition by returning an error if the operation
did not succeed after the 3rd time. If you want to write your own hardware-
level floppy routines to bypass the BIOS ones, Id say check out the
Undocumented PC book.
BIOS Controls
BIOS calls to int 13h are all thats needed to read/write sectors on the disk.
Below is a short description of the important calls:
-- Interrupt 13h -------------------------------------------------------------
Function: 02h Read sector
Entry:
ah 02h
al number of sectors to read in succession
cl sector
ch track
dl drive 0 A drive, 1 B drive
dh head
es:bx destination buffer
Return:
cf set on error
Note:
- BIOS is not smart enough to wait for the motor to spin up, so there may
be a few initial errors upon this condition. Retry 2 or 3 times if
needed. A physical error would be apparent after 3 tries.
Function: 03h Write sector
Entry:
ah 03h
al number of sectors to write in succession
cl sector
ch track
dl drive 0 A drive, 1 B drive
dh head
es:bx source buffer
Return:
cf set on error
Note:
- BIOS is not smart enough to wait for the motor to spin up, so there may
be a few initial errors upon this condition. Retry 2 or 3 times if
needed. A physical error would be apparent after 3 tries.
---------------------- 8 -------- CUT HERE -------- 8 ----------------------
Coding the Boot Sector
Before you worry about the whole picture, think about how the code in the
bootsector would work. Essentially you can put any code you want in it, but
you will want some intiailization or continuation of code in it. For my boot
code I setup a system stack which upon bootup is located in 0000:00ff which
isnt good if you push a lot of values :, load any additional sectors off
the disk that Ill need perhaps a mini-filesystem of your own type, or
secondary boot code, and perhaps code to switch to something along the lines
of flat real mode or protected mode. Beings that you are assured that there
are no memory managers or special conditions, changing cpu and memory modes
should be a snap if you know what youre doing. If you dont know what
youre doing then I dont know why youre reading this. Finally, you would
jump to the secondary boot code, which should be already loaded.
What I did to create the source and assembled code was to use TASM or MASM
if you were taught by the wrong person and setup a basic single-segment
layout like that of a COM file. The difference is that COM files use a
relative origin of 100h, whereas the boot sector is at absolute offset 7c00h.
Therefore an Org 7c00h in a requirement, or else memory references and
branches will be wrong. To assemble and link, follow this example:
tasm -ml -m2 boot.asm
tlink -3 -t boot.obj, boot.boo
where boot.boo is the actual assembled code without headers. You cannot
use a .com extension because that requires an origin of 100h in the code.
Additionally tlink does not like using extensions smaller than 3 letters,
i.e. boot.b will not work.
Putting the Boot Sector on Disk
Once you have your code that you want to play with, you gotta get it on a disk
and boot it up. Problem is, you have to write another program to write the
program to the boot sector and any other sectors that you want to write. I
just wrote a quickie program in Borland C++ 3.1 under Dos to do this task.
Actually two programs. One creates a disk image that includes the boot sector
code, a secondary boot program, a mini-filesystem, and the files in the
filesystem. The second simply writes it to the disk. If you do plan to get
into more complicated sector layouts on the disk, youll soon see that its a
thorn in the ass to manage the sector/track/head values. What I did was to
use linear sector numbers and have a function convert the linear to sector/
track/head. Problem solved. For all simplistic purposes, you can start with
a simple program to quickly put the boot.boo file onto sector 1, track 0,
head 0 and itll run
What I Did
I might as well give you a layout of my design to further the academic
institution that is known as the demoscene.
The sector layout I came up with is as such:
linear
sector range description
0 boot sector
1 to x secondary boot code
x to x+5 5 sector filesystem
x+5 to wherever the files that are in the filesystem
graphic layout:
01 x x+5 wherever
When the computer starts up and executes the boot sector portion, I
immediately install a 4k stack in a different part of memory. I then load
in the sectors for the secondary boot code and the filesystem. I then
install code to initialize flat real mode. I then clear the general
variables and jump to 07e0:0000, which is where I put my secondary boot code.
The Secondary Boot Code
When the boot code does the jump to the secondary code, which I put starting
at 07e0:0000. I decided to make many of my routines based on my own real mode
interrupt service handler, which I aptly installed at int 20h. The filesystem
routines I coded were put in the int 20h handler along with malloc/free
functions I wrote for flat real mode. I had already coded a library for
watcom that did a trillion functions for text mode, so I converted all of that
to work under flat real mode. I did the same for my PCX image decoder. Each
section of code is compiled in its own object file, but to ensure proper
linking and easy handling I made everything have its own segment name but in
the same segment group CGROUP. All in all, I have quite a bit to swim in
however, I never really got my file functions working right :/
Memory Allocation
At first coding malloc/free sounds terribly difficult, but once you figure out
the structure its really simple. I think Ill conclude this section by just
saying its a linked list. Thanks to Dave Cooper for the similar hint back in
94. It only took me 2 years to finally figure out what he meant.
The Filesystem
I did not make any provision for file create and writing because I have no
provision for a cluster map, nor do I have the immediate need for creating
files on the floppy disk. My filesystem is a pretty simple static size array
of 64 entries. Each entry only describes a 32-character filename, the size in
bytes, and the starting linear sector on the disk. The filesystem routines
work hand in hand with malloc/free because when you open a file, it has to
malloc space for a file descriptor.
Conclusion
I think entire concept of coding an operating system from scratch very
fascinating. I encourage others to code in this area more so that the demo
scene can expand to areas of originality never before thought of. I know
there are many coders out there that could really benefit from organizing
their own system and coding any way they want without software compatibility
problems. Although bootloaders have been done back in the ages of the Atari
8-bit series, and its successor in architecture anyway the Amiga, but this
is an excellent unexplored platform for coders to show their experience in a
new way.
Shawn Poulson
aka mrdata--
on coders irc.another.net
mrdata@interstat.net
http://mrdata.interstat.net
Other sources:
x2ftp Archive
ftp://x2ftp.oulu.fi/pub/msdos/programming
Hornet Archive
http://www.hornet.org/pub/demos/code
My coding links page
http://mrdata.interstat.net/coding.html