Does The Bios Copy The 512-Byte Bootloader to 0X7C00

Does the BIOS copy the 512-byte bootloader to 0x7c00

... the boot-sector loader loads the 512 bytes of the data of a diskette which resides at the memory address 0x7C00 (if I am right) into the RAM

...

Since the machine is booting up, the RAM has to be empty, so the bootloader it loaded at 0x0000 of the RAM?

No. 0000:7C00 refers to a RAM address. RAM means "Random Access Memory", means each location in the memory can be accessed directly. There is nothing like "empty RAM". You can also refer to the INT13 interrupt function "Read Sectors From Drive": one of the parameters (passed in ES:BX) needs to point to the destination address where the sector contents will be stored. Hence, what the BIOS does is it loads 512 bytes from the drive and stores it into 0000:7C00 to 0000:7DFF, and then jumps to 0000:7C00 to execute the primary boot loader code.

Any articles/books I should read to get myself familiar with Kernels, etc?

This Wiki is very useful: http://wiki.osdev.org/Main_Page. For the boot process, look at http://wiki.osdev.org/Boot_Sequence.

BIOS and Address 0x07C00

The reason why BIOS always loads the
bootloader to 0x07C00 is historical.

In the early days, a PC is only guaranteed to have
64 KB of RAM memory.

The memory below 0x07C00 is reserved for interrupt vectors,
BIOS and BASIC, etc.

The first OS usable memory begins at 0x08000.

So the
bootloader is loaded to 0x07C00, which is 1 KB below 0x08000.

Why does int 10h not work after reading sectors with int 13h?

TL;DR The primary problem with your code as @jester pointed out is that you used an org 0x7c00 which means all absolute references are relative to 0x7c00. When you copy to 0x7a00 the code you generated is still referencing 0x7c?? addresses. When you overwrite memory at 0x7c00 the references to labels like error_msg, BS_DriveNum are to data that has been replaces, and it fails.


There are a few ways to fix the issue:

  1. The simplest fix is to make sure the code that is executed prior to jumping to _continue is position independent (which it currently is) and change org 0x7c00 to org 0x7a00. You will also need to change jmp 0x0000:(_continue - 0x200) to jmp 0x0000:_continue

  2. Use org 0x0000 and load segments with appropriate values 0x07c0 and 0x07a0 depending on which segment you need to access. By having and origin point of 0x0000 the code and data generated is relative to the beginning of a segment (which you can change), not the beginning of memory.

  3. You can use NASM's segment directive to change the origin point of the code using the vstart (Virtual Memory Address) option. You can use a segment directive with the start (Load Memory Address) option to change the file offset where the boot signature is placed.

Other issues in your code:

  • As @RossRidge pointed out if an error occurs (Carry Flag set) after a disk operation it will get into an infinite loop because you use CX register which is also used in doing Int 13h/AH=02h. You also use CX for the disk reset retry counter and the disk operation.
  • In general you can avoid checking for any error on Int 13h/AH=0 disk reset and remove the retry loop for that operation. You should only have to reset the disk if a previous disk operation failed. Retrying a disk operation three times was normal on real hardware.
  • When your code successfully reads the new code and data to 0x7c00 it displays an error message. It should probably print a message that the disk read was successful.

Code using option 1:

;-------------------------------------------------------------------------------
; boot.asm - First boot sector
;-------------------------------------------------------------------------------

org 0x7a00
bits 16

jmp word _start

;-------------------------------------------------------------------------------
; BIOS Parameter Block (FAT32)
;-------------------------------------------------------------------------------

BS_OEMName: db "TestOS "
BPB_BytesPerSector: dw 512
BPB_SectorsPerCluster: db 1
BPB_ReservedSectors: dw 4
BPB_FATCount: db 2
BPB_RootEntryCount: dw 0
BPB_TotalSectors16: dw 0
BPB_Media: db 0xf0
BPB_FATSize16: dw 0
BPB_SectorsPerTrack: dw 18
BPB_HeadCount: dw 2
BPB_HiddenSectors: dd 0
BPB_TotalSectors32: dd 2880
BPB_FATSize32: dd 23
BPB_ExtendedFlags: dw 0x0000
BPB_FileSystemVersion: dw 0
BPB_RootCluster: dd 2
BPB_FSInfo: dw 3
BPB_BackupBootSector: dw 0
BPB_Reserved: times 12 db 0
BS_DriveNum: db 0x00
BS_Reserved1: db 0x0
BS_BootSignature: db 0x29
BS_VolumeID: dd 0x12345678
BS_VolumeLabel: db "TestOS "
BS_FileSystemType: db "FAT32 "

;-------------------------------------------------------------------------------

_start:
; Initialize segment registers and set up stack at 0x7c00 (grows downwards)
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7a00
sti

; Save drive number in DL
mov [BS_DriveNum], dl

; Copy this boot sector from 0x7c00 to 0x7a00
mov si, 0x7c00
mov di, 0x7a00
mov cx, 512
cld
rep movsb

; Jump to the new location
jmp 0x0000:(_continue)

_continue:

; Reset boot disk (try it 3 times before error message is printed)
mov si, 4

_reset:
mov ah, 0
mov dl, [BS_DriveNum]
int 0x13

; Load second boot sector into memory at 0x7c00 (try it 3 times before error message is printed)
_load:
dec si
je _error
mov ah, 0x02
mov al, 2
mov ch, 0
mov cl, 3
mov dh, 0
mov dl, [BS_DriveNum]
mov bx, 0x7c00
int 0x13
jc _load

mov si, loaded_msg
call print

; Jump to the second boot sector
jmp 0x0000:0x7c00

; End of program
_end:
hlt
jmp _end

_error:
mov si, error_msg
call print

jmp _end

;-------------------------------------------------------------------------------
; Prints a zero-terminated string onto the screen
; SI = string to write
;-------------------------------------------------------------------------------

print:
pusha
.print_lbl:
lodsb
cmp al, 0
je .finished
mov ah, 0x0e
mov bl, 0
int 0x10
jmp .print_lbl

.finished:
popa

ret

;-------------------------------------------------------------------------------

loaded_msg: db "Operating system loaded", 0xa, 0xd, 0x0
error_msg: db "Operating system not found", 0xa, 0xd, "Press Ctrl+Alt+Del to reboot", 0x0

; Fill the rest of the 512 bytes with 0, byte 510 and 511
; contains 0xaa55 (boot signature for BIOS)

times 510 - ($ - $$) db 0
dw 0xaa55

Code using option 2:

;-------------------------------------------------------------------------------
; boot.asm - First boot sector
;-------------------------------------------------------------------------------

org 0x00
bits 16

jmp word _start

;-------------------------------------------------------------------------------
; BIOS Parameter Block (FAT32)
;-------------------------------------------------------------------------------

BS_OEMName: db "TestOS "
BPB_BytesPerSector: dw 512
BPB_SectorsPerCluster: db 1
BPB_ReservedSectors: dw 4
BPB_FATCount: db 2
BPB_RootEntryCount: dw 0
BPB_TotalSectors16: dw 0
BPB_Media: db 0xf0
BPB_FATSize16: dw 0
BPB_SectorsPerTrack: dw 18
BPB_HeadCount: dw 2
BPB_HiddenSectors: dd 0
BPB_TotalSectors32: dd 2880
BPB_FATSize32: dd 23
BPB_ExtendedFlags: dw 0x0000
BPB_FileSystemVersion: dw 0
BPB_RootCluster: dd 2
BPB_FSInfo: dw 3
BPB_BackupBootSector: dw 0
BPB_Reserved: times 12 db 0
BS_DriveNum: db 0x00
BS_Reserved1: db 0x0
BS_BootSignature: db 0x29
BS_VolumeID: dd 0x12345678
BS_VolumeLabel: db "TestOS "
BS_FileSystemType: db "FAT32 "

;-------------------------------------------------------------------------------

_start:
; Initialize segment registers and set up stack at 0x7c00 (grows downwards)
cli
mov ax, 0x7c0
mov ds, ax
mov ss, ax

xor ax, ax
mov sp, 0x7a00
sti

; Save drive number in DL
mov [BS_DriveNum], dl

; Copy this boot sector from 0x7c00 to 0x7a00
mov ax, 0x7a0
mov es, ax
xor si, si
xor di, di
mov cx, 512
cld
rep movsb

; Jump to the new location
jmp 0x07a0:(_continue)

_continue:

mov ax, 0x7c0
mov es, ax
mov ax, 0x7a0
mov ds, ax

; Load second boot sector into memory at 0x7c00 (try it 3 times before error message is printed)
mov si, 4

; Reset boot disk
_reset:
mov ah, 0
mov dl, [BS_DriveNum]
int 0x13

_load:
dec si
je _error
mov ah, 0x02
mov al, 2
mov ch, 0
mov cl, 3
mov dh, 0
mov dl, [BS_DriveNum]
xor bx, bx
int 0x13
jc _load

mov si, loaded_msg
call print

; Jump to the second boot sector
jmp 0x0000:0x7c00

; End of program
_end:
hlt
jmp _end

_error:
mov si, error_msg
call print

jmp _end

;-------------------------------------------------------------------------------
; Prints a zero-terminated string onto the screen
; SI = string to write
;-------------------------------------------------------------------------------

print:
pusha
.print_lbl:
lodsb
cmp al, 0
je .finished
mov ah, 0x0e
mov bl, 0
int 0x10
jmp .print_lbl

.finished:
popa

ret

;-------------------------------------------------------------------------------

loaded_msg: db "Operating system loaded", 0xa, 0xd, 0x0
error_msg: db "Operating system not found", 0xa, 0xd, "Press Ctrl+Alt+Del to reboot", 0x0

; Fill the rest of the 512 bytes with 0, byte 510 and 511
; contains 0xaa55 (boot signature for BIOS)

times 510 - ($ - $$) db 0
dw 0xaa55

Code using option 3:

BOOT_ORG EQU 0x7c00

;-------------------------------------------------------------------------------
; boot.asm - First boot sector
;-------------------------------------------------------------------------------

org BOOT_ORG
bits 16

jmp word _start

;-------------------------------------------------------------------------------
; BIOS Parameter Block (FAT32)
;-------------------------------------------------------------------------------

BS_OEMName: db "TestOS "
BPB_BytesPerSector: dw 512
BPB_SectorsPerCluster: db 1
BPB_ReservedSectors: dw 4
BPB_FATCount: db 2
BPB_RootEntryCount: dw 0
BPB_TotalSectors16: dw 0
BPB_Media: db 0xf0
BPB_FATSize16: dw 0
BPB_SectorsPerTrack: dw 18
BPB_HeadCount: dw 2
BPB_HiddenSectors: dd 0
BPB_TotalSectors32: dd 2880
BPB_FATSize32: dd 23
BPB_ExtendedFlags: dw 0x0000
BPB_FileSystemVersion: dw 0
BPB_RootCluster: dd 2
BPB_FSInfo: dw 3
BPB_BackupBootSector: dw 0
BPB_Reserved: times 12 db 0
BS_DriveNum: db 0x00
BS_Reserved1: db 0x0
BS_BootSignature: db 0x29
BS_VolumeID: dd 0x12345678
BS_VolumeLabel: db "TestOS "
BS_FileSystemType: db "FAT32 "

;-------------------------------------------------------------------------------

_start:
; Initialize segment registers and set up stack at 0x7c00 (grows downwards)
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7a00
sti

; Save drive number in DL
mov [BS_DriveNum], dl

; Copy this boot sector from 0x7c00 to 0x7a00
mov si, 0x7c00
mov di, 0x7a00
mov cx, 512
cld
rep movsb

; Jump to the new location
jmp 0x0000:_continue

; The code and data past this point will have an origin point (vstart)
; relative to 0x7a00. Align=1 for no padding.

section bootreloc vstart=(($-$$)+0x7a00) align=1
_continue:

; Load second boot sector into memory at 0x7c00 (try it 3 times before error message is printed)
mov si, 4

; Reset boot disk
_reset:
mov ah, 0
mov dl, [BS_DriveNum]
int 0x13

_load:
dec si
jz _error
mov ah, 0x02
mov al, 2
mov ch, 0
mov cl, 3
mov dh, 0
mov dl, [BS_DriveNum]
mov bx, 0x7c00
int 0x13
jc _load

mov si, loaded_msg
call print

; Jump to the second boot sector

jmp 0x0000:0x7c00

; End of program
_end:
hlt
jmp _end

_error:
mov si, error_msg
call print

jmp _end

;-------------------------------------------------------------------------------
; Prints a zero-terminated string onto the screen
; SI = string to write
;-------------------------------------------------------------------------------

print:
pusha
.print_lbl:
lodsb
cmp al, 0
je .finished
mov ah, 0x0e
mov bl, 0
int 0x10
jmp .print_lbl

.finished:
popa

ret

;-------------------------------------------------------------------------------

loaded_msg: db "Operating system loaded", 0xa, 0xd, 0x0
error_msg: db "Operating system not found", 0xa, 0xd, "Press Ctrl+Alt+Del to reboot", 0x0

; Set position to 510 bytes from BOOT_ORG so that bytes 510 and 511
; in te disk image will contain 0xaa55 (boot signature for BIOS)

section bootsig start=(BOOT_ORG+510)
dw 0xaa55

Legacy BIOS bootloader to bootstrap real-mode code in second stage

I have written such code as part of other answers but never had an opportunity to present a simple test harness that could be referenced from other Stackoverflow questions. What you are asking for is rather trivial. One can do this by writing a bootloader in NASM that includes a binary image of the assembled code you wish to test. This image would be read from disk starting at LBA 1 (first sector after the bootloader) using BIOS function Int 13/ah=2. Control would then be transferred to it via a FAR JMP to 0x0000:0x7e00.

The bootloader code would look like this:

bpb.inc:

global bpb_disk_info

jmp short boot_continue
nop

bpb_disk_info:

; Dos 4.0 EBPB 1.44MB floppy
OEMname: db "mkfs.fat" ; mkfs.fat is what OEMname mkdosfs uses
bytesPerSector: dw 512
sectPerCluster: db 1
reservedSectors: dw 1
numFAT: db 2
numRootDirEntries: dw 224
numSectors: dw 2880
mediaType: db 0xf0
numFATsectors: dw 9
sectorsPerTrack: dw 18
numHeads: dw 2
numHiddenSectors: dd 0
numSectorsHuge: dd 0
driveNum: db 0
reserved: db 0
signature: db 0x29
volumeID: dd 0x2d7e5a1a
volumeLabel: db "NO NAME "
fileSysType: db "FAT12 "

boot.asm:

STAGE2_ABS_ADDR  equ 0x07e00
STAGE2_RUN_SEG equ 0x0000
STAGE2_RUN_OFS equ STAGE2_ABS_ADDR
; Run stage2 with segment of 0x0000 and offset of 0x7e00

STAGE2_LOAD_SEG equ STAGE2_ABS_ADDR>>4
; Segment to start reading Stage2 into
; right after bootloader

STAGE2_LBA_START equ 1 ; Logical Block Address(LBA) Stage2 starts on
; LBA 1 = sector after boot sector
STAGE2_LBA_END equ STAGE2_LBA_START + NUM_STAGE2_SECTORS
; Logical Block Address(LBA) Stage2 ends at
DISK_RETRIES equ 3 ; Number of times to retry on disk error

bits 16
ORG 0x7c00

; Include a BPB (1.44MB floppy with FAT12) to be more compatible with USB floppy media
%ifdef WITH_BPB
%include "bpb.inc"
%endif

boot_continue:
xor ax, ax ; DS=SS=0 for stage2 loading
mov ds, ax
mov ss, ax ; Stack at 0x0000:0x7c00
mov sp, 0x7c00
cld ; Set string instructions to use forward movement

; Read Stage2 1 sector at a time until stage2 is completely loaded
load_stage2:
mov [bootDevice], dl ; Save boot drive
mov di, STAGE2_LOAD_SEG ; DI = Current segment to read into
mov si, STAGE2_LBA_START ; SI = LBA that stage2 starts at
jmp .chk_for_last_lba ; Check to see if we are last sector in stage2

.read_sector_loop:
mov bp, DISK_RETRIES ; Set disk retry count

call lba_to_chs ; Convert current LBA to CHS
mov es, di ; Set ES to current segment number to read into
xor bx, bx ; Offset zero in segment

.retry:
mov ax, 0x0201 ; Call function 0x02 of int 13h (read sectors)
; AL = 1 = Sectors to read
int 0x13 ; BIOS Disk interrupt call
jc .disk_error ; If CF set then disk error

.success:
add di, 512>>4 ; Advance to next 512 byte segment (0x20*16=512)
inc si ; Next LBA

.chk_for_last_lba:
cmp si, STAGE2_LBA_END ; Have we reached the last stage2 sector?
jl .read_sector_loop ; If we haven't then read next sector

.stage2_loaded:
mov ax, STAGE2_RUN_SEG ; Set up the segments appropriate for Stage2 to run
mov ds, ax
mov es, ax

; FAR JMP to the Stage2 entry point at physical address 0x07e00
xor ax, ax ; ES=FS=GS=0 (DS zeroed earlier)
mov es, ax

; SS:SP is already at 0x0000:0x7c00, keep it that way
; DL still contains the boot drive number
; Far jump to second stage at 0x0000:0x7e00
jmp STAGE2_RUN_SEG:STAGE2_RUN_OFS

.disk_error:
xor ah, ah ; Int13h/AH=0 is drive reset
int 0x13
dec bp ; Decrease retry count
jge .retry ; If retry count not exceeded then try again

error_end:
; Unrecoverable error; print drive error; enter infinite loop
mov si, diskErrorMsg ; Display disk error message
call print_string
cli
.error_loop:
hlt
jmp .error_loop

; Function: print_string
; Display a string to the console on display page 0
;
; Inputs: SI = Offset of address to print
; Clobbers: AX, BX, SI

print_string:
mov ah, 0x0e ; BIOS tty Print
xor bx, bx ; Set display page to 0 (BL)
jmp .getch
.repeat:
int 0x10 ; print character
.getch:
lodsb ; Get character from string
test al,al ; Have we reached end of string?
jnz .repeat ; if not process next character
.end:
ret

; Function: lba_to_chs
; Description: Translate Logical block address to CHS (Cylinder, Head, Sector).
;
; Resources: http://www.ctyme.com/intr/rb-0607.htm
; https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion
; https://stackoverflow.com/q/45434899/3857942
; Sector = (LBA mod SPT) + 1
; Head = (LBA / SPT) mod HEADS
; Cylinder = (LBA / SPT) / HEADS
;
; Inputs: SI = LBA
; Outputs: DL = Boot Drive Number
; DH = Head
; CH = Cylinder (lower 8 bits of 10-bit cylinder)
; CL = Sector/Cylinder
; Upper 2 bits of 10-bit Cylinders in upper 2 bits of CL
; Sector in lower 6 bits of CL
;
; Notes: Output registers match expectation of Int 13h/AH=2 inputs
;
lba_to_chs:
push ax ; Preserve AX
mov ax, si ; Copy LBA to AX
xor dx, dx ; Upper 16-bit of 32-bit value set to 0 for DIV
div word [sectorsPerTrack] ; 32-bit by 16-bit DIV : LBA / SPT
mov cl, dl ; CL = S = LBA mod SPT
inc cl ; CL = S = (LBA mod SPT) + 1
xor dx, dx ; Upper 16-bit of 32-bit value set to 0 for DIV
div word [numHeads] ; 32-bit by 16-bit DIV : (LBA / SPT) / HEADS
mov dh, dl ; DH = H = (LBA / SPT) mod HEADS
mov dl, [bootDevice] ; boot device, not necessary to set but convenient
mov ch, al ; CH = C(lower 8 bits) = (LBA / SPT) / HEADS
shl ah, 6 ; Store upper 2 bits of 10-bit Cylinder into
or cl, ah ; upper 2 bits of Sector (CL)
pop ax ; Restore scratch registers
ret

; If not using a BPB (via bpb.inc) provide default Heads and SPT values
%ifndef WITH_BPB
numHeads: dw 2 ; 1.44MB Floppy has 2 heads & 18 sector per track
sectorsPerTrack: dw 18
%endif

bootDevice: db 0x00
diskErrorMsg: db "Unrecoverable disk error!", 0

; Pad boot sector to 510 bytes and add 2 byte boot signature for 512 total bytes
TIMES 510-($-$$) db 0
dw 0xaa55

; Beginning of stage2. This is at 0x7E00 and will allow your stage2 to be 32.5KiB
; before running into problems. DL will be set to the drive number originally
; passed to us by the BIOS.

NUM_STAGE2_SECTORS equ (stage2_end-stage2_start+511) / 512
; Number of 512 byte sectors stage2 uses.

stage2_start:
; Insert stage2 binary here. It is done this way since we
; can determine the size(and number of sectors) to load since
; Size = stage2_end-stage2_start
incbin "stage2.bin"

; End of stage2. Make sure this label is LAST in this file!
stage2_end:

; Fill out this file to produce a 1.44MB floppy image
TIMES 1024*1440-($-$$) db 0x00

To use this you would first generate a binary file called stage2.bin. After stage2.bin has been built you can build a 1.44MiB disk image without a BIOS Parameter Block (BPB) with this command:

nasm -f bin boot.asm -o disk.img

To build a 1.44MiB disk image with a BPB you can build it with this command:

nasm -DWITH_BPB -f bin boot.asm -o disk.img

The code in stage2.bin would have to be generated with the assumption that the ORG (origin point) is 0x07e00 in memory.



Sample Usage/Example

An example of code generated to a file called stage2.bin that can be loaded with this test harness:

testcode.asm:

ORG 0x7e00

start:
mov si, testCodeStr
call print_string

cli
.end_loop:
hlt
jmp .end_loop

testCodeStr: db "Test harness loaded and is executing code in stage2!", 0

; Function: print_string
; Display a string to the console on display page 0
;
; Inputs: SI = Offset of address to print
; Clobbers: AX, BX, SI

print_string:
mov ah, 0x0e ; BIOS tty Print
xor bx, bx ; Set display page to 0 (BL)
jmp .getch
.repeat:
int 0x10 ; print character
.getch:
lodsb ; Get character from string
test al,al ; Have we reached end of string?
jnz .repeat ; if not process next character
.end:
ret

Note: there is an ORG 0x7e00 at the top. This is important. To assemble this file into stage2.bin use:

nasm -f bin testcode.asm -o stage2.bin

Then create the 1.44MiB disk image with:

nasm -f bin boot.asm -o disk.img

The result should be a disk image exactly 1.44MiB in size, contains a copy of stage2.bin and has our test harness boot sector.

The file stage2.bin can be anything that has binary code written to be loaded and started at 0x0000:0x7e00. The language (C, assembly etc) used to create the code in stage2.bin doesn't matter. I use NASM for this example. When this test code is executed in QEMU using qemu-system-i386 -fda disk.img it would look similar to this:

Sample Image


Special Note: : Using -DWITH_BPB to enable a BPB is useful if you are booting from USB using FDD emulation. Some BIOSes that boot USB as a floppy will assume a BPB is is present and overwrite the area with drive geometry before transferring control to it at physical address 0x07c00.



Related Topics



Leave a reply



Submit