User:Johnburger/Demo/Boot/Load
The code to load the rest of the system uses the BIOS to avoid dealing with the different possible boot media. It either starts loading from where the Master Boot Record indicates (assuming a HardDisk boot), or from the second sector onwards.
It needs to deal with all sorts of intricacies: not the least of which is disk errors! (The code tries a few times before giving up.) But it also has to handle not being able to load all the sectors in one operation: it merely moves the pointers along and tries again.
Memory Map
After this code, the Memory Map will look like this:
Address | Usage |
---|---|
0000_0000h
|
Interrupt Vector Table |
0000_0400h
|
BIOS Data Area |
0000_0500h
|
Available |
0000_0600h
|
Master Boot Record (MBR) *
|
0000_0800h
|
Available |
0000_0E70h
|
Loaded Initial IDT values |
0000_1000h
|
Loaded Protected Mode code |
0000_2070h
|
Available |
0000_7C00h
|
SS:SP Stack Top
|
0000_7C00h
|
BIOS-loaded Boot Sector |
0000_7E00h
|
Available |
0009_F???h **
|
Extended BIOS Data Area |
000A_0000h
|
Adapter / ROM Area |
000B_8000h
|
Text Video Memory |
000F_0000h
|
BIOS ROM |
0010_0000h
|
High Memory ***
|
*
The MBR only exists here if the PC has booted from a Hard Drive.
**
An INT 12h will help identify this value.
***
Note that this memory is only accessible in Protected Mode - except for the first 64 kiB (less 16 bytes), which you can access with a Segment Register set to 0FFFFh
as long as the A20 Gate is off. By the way: DON'T DO THIS!
Demo/Boot/Load.inc
;
; Boot/Load.inc
;
; This module loads the rest of the system - all the Protected Mode stuff.
; It loads it into RAM way down low, leaving the rest of RAM for the system's
; use - and allowing this boot code to be jettisoned.
; The number of Sectors to load. Note that the resultant value may exceed what
; the BIOS can handle: the typical maximum is "number of sectors per track".
; Different floppy formats use different numbers: 8, 9, 15, 18, and 36 are known.
; Since the lowest value is 8, that's the "safest" value. 1.44MB floppies use 18
Load.Sectors EQU Load.Size / BIOS.Disk.Sector.Size
; Upon entry, DL is BIOS drive number, ES:SI is Hard Disk Partition entry (if any)
Boot.Load:
MOV AX,Load.Base>>4 ; Segment to load to
; MOV ES,AX ; Not yet! Need BIOS value
XOR BX,BX ; Offset to load to
TEST SI,SI ; Sensible value?
JZ .Beginning ; Not really...
; Assume pointing to MBR Table entry
MOV CX,[ES:SI+BIOS.MBR.Entry.StartCylSect]
MOV DH,[ES:SI+BIOS.MBR.Entry.StartHead]
; Now check if above assumption is true!
OR SI,BIOS.Disk.Sector.Size-1; Round UP to BIOS.Sig
CMP [ES:SI-1],WORD BIOS.Sig.Value ; (Well, nearly!)
JE .Continue ; All that hard work panned out!
; Assumption NOT true: no help from BIOS! Just load from first drive sector
.Beginning:
MOV CX,00_01h ; Cyl 0, Sect #1
MOV DH,0 ; Head Start
.Continue:
INC CX ; Next sector
; MOV DL, ; DriveNum provided by BIOS
MOV SI,3 ; Try this many times
MOV ES,AX ; Can load buffer segment now!
.Attempt:
MOV AH,BIOS.Disk.Fn.Read ; Read sectors
MOV AL,Load.Sectors ; Number sectors
.NumToRead EQU $-1 ; GASP! Self-modifying code!
.Read:
INT BIOS.Disk.Int ; BIOS Read
JNC .ReadSome ; Carry set on error
CMP AL,1 ; Tried single-sector reads?
JE .Reset ; Yep! Better reset drive
MOV AL,1 ; No. Try that then.
JMP .Read ; And retry command
.ReadSome:
SUB [CS:.NumToRead],AL ; Read enough?
JZ .Done ; Success!
; Note that the next calculation only works for a few number of sectors.
; If a larger number were required, the "Sectors per Track" would overflow,
; and we'd have to instead increase the Head count and zero the Sector count.
; If it overflowed again, we'd have to increase the Cylinder count and zero the
; Head and Sector counts. All too hard if we don't really need to do it,
; especially since determining what constitutes "overflow" is tricky!
; Finally, the "memory address to load to" calculation would overflow, limiting
; the final result to 64K maximum anyway
ADD CL,AL ; Fewer sectors to read...
; Turn the Number Sectors Read value into a pointer offset:
CBW
IMUL AX,BIOS.Disk.Sector.Size
ADD BX,AX ; Further in buffer...
.Reset:
MOV AH,BIOS.Disk.Fn.Reset
INT BIOS.Disk.Int ; BIOS Reset
DEC SI ; One less try...
JNS .Attempt ; (Leaves Z clear on last error!)
JMP $ ; ***STOP!***
.Done: