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, it's already a running operating 
system and is almost fully booted up.


What's 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, it's 
great to work with a fresh new system that we KNOW doesn't have any strange 
drivers, memory setups or special processor modes (with the exception of the 
possibility of Disk Manager, which doesn't 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.  


What's Bad About It?

There's also a down side.  You see, there's 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 you've 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, let's 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 doesn't 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 there's 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, I'd say check out the 
"Undocumented PC" book.


BIOS Controls

BIOS calls to int 13h are all that's 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 
isn't good if you push a lot of values :), load any additional sectors off 
the disk that I'll 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 you're doing.  If you don't know what 
you're doing then I don't know why you're 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, you'll soon see that it's 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 it'll 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 it's really simple.  I think I'll conclude this section by just 
saying it's 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





