Code: Select all
[BITS 16]
[ORG 0x7C00]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Boot Point ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
boot:
; Jump over the BIOS Parameter Block.
jmp short fix_main
nop
; Ensure the BPB starts at the right place.
times 3 - ($ - $$) db 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BIOS Parameter Block ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; The OEM name (8 bytes).
OEM: db "OEMID"
; Ensure the OEM has the right size.
times 11 - ($ - $$) db 0
; Bytes per sector (2 bytes).
BytesPerSector: dw 512
; Sectors per cluster (1 byte).
SectorsPerCluster: db 1
; Reserved sectors (2 bytes).
ReservedSectors: dw 1
; Amount of file allocation tables (1 byte).
FATs: db 2
; Amount of entries in the root directory (2 bytes).
RootEntries: dw 224
; The amount of sectors (2 bytes).
Sectors16: dw 2880
; The media descriptor (1 byte).
MediaDescriptor: db 0xF0
; Sectors per file allocation table (2 bytes).
SectorsPerFAT: dw 9
; Sectors per track (2 bytes).
SectorsPerTrack: dw 18
; Amount of heads (2 bytes).
Heads: dw 2
; Amount of hidden sectors (4 bytes).
HiddenSectors dd 0
; The amount of sectors (4 bytes).
Sectors32: dd 0
; Ensure the EBPB starts at the right place.
times 36 - ($ - $$) db 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Extended BIOS Parameter Block ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; BIOS drive number (1 byte).
Drive: db 0
; Flags used by Microsoft Windows NT (1 byte).
Flags: db 0
; Signature used by Microsoft Windows NT (1 byte).
Signature: db 0x29
; Volume ID (serial) (4 bytes).
VolumeID: dd 0
; Ensure the volume ID has the right size.
times 43 - ($ - $$) db 0
; Volume label (11 bytes).
VolumeLabel: db "FLOPPY"
; Ensure the volume label has the right size.
times 54 - ($ - $$) db 0
; System ID (8 bytes).
SystemID: db "FAT 12"
; Ensure the system ID has the right size.
times 62 - ($ - $$) db 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Entry Point ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
fix_main:
; Ensure the code segment is set to 0.
jmp 0:main
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Real Entry Point ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
main:
; Nullify ax.
xor ax, ax
.setup_segments:
; Set up the segment selectors.
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
.setup_stack:
; Set up the stack.
mov ss, ax
mov sp, 0x7C00
.check_drive:
; Sanity check on the drive the BIOS passed us.
test dl, 0x7F
jnz .invalid_drive
mov BYTE [Drive], dl
.invalid_drive:
; Just get the drive number from the BPB.
mov dl, BYTE [Drive]
; If a floppy disk is being used, then get the size of the root directory.
test dl, dl
jz .get_root_dir_size
; Otherwise, get the disk geometry first.
mov ah, 0x08
int 0x13
; If it fails, get away from here.
jc .get_root_dir_size
; Fix up the BPB.
and cx, 0x3F
mov WORD [SectorsPerTrack], cx
mov cl, dh
inc cx
mov WORD [Heads], cx
.get_root_dir_size:
; Calculate the root directory size using:
; (RootEntries * 32) / BytesPerSector
mov ax, WORD [RootEntries]
shl ax, 5
xor dx, dx
div WORD [BytesPerSector]
; Store the result in cx.
xchg ax, cx
.get_root_dir_region:
; Calculate the region of the root directory using:
; ReservedSectors + (FATs * SectorsPerFAT)
xor ax, ax
mov al, BYTE [FATs]
mul WORD [SectorsPerFAT]
add ax, WORD [ReservedSectors]
.get_data_region:
; Calculate the data region using:
; ReservedSectors + (FATs * SectorsPerFAT) + (RootEntries * 32) / ...
; ... BytesPerSector
mov WORD [SystemID], ax
add WORD [SystemID], cx
.load_root:
; Load the root directory to 0000:7E00
mov bx, 0x7E00
call read_sectors
.find_file:
; Try to find the file.
mov cx, WORD [RootEntries]
mov di, 0x7E00
.find_loop:
; Loop through each file name and compare.
push cx
mov cx, 11
mov si, ImageName
push di
rep cmpsb
pop di
je .found_file
pop cx
add di, 32
loop .find_loop
.no_file:
; The file is not found.
mov si, msgFailure
.print:
lodsb
or al, al
jz .print_done
mov ah, 0x0E
int 0x10
jmp .print
.print_done:
xor ax, ax
int 0x16
int 0x19
cli
hlt
.found_file:
; Get the starting cluster of the boot image.
mov dx, WORD [di + 26]
mov WORD [SystemID + 2], dx
.load_fat:
; Get the size of the FAT we stored before.
xor ax, ax
mov al, BYTE [FATs]
mul WORD [SectorsPerFAT]
mov cx, ax
; Calculate the location of the FAT.
mov ax, WORD [ReservedSectors]
; Load the FAT to 0000:7E00
mov bx, 0x7E00
call read_sectors
.load_file:
mov bx, 0x0500
push bx
.next_cluster:
mov ax, WORD [SystemID + 2]
pop bx
sub ax, 2
xor cx, cx
mov cl, BYTE [SectorsPerCluster]
mul cx
add ax, WORD [SystemID]
xor cx, cx
mov cl, BYTE [SectorsPerCluster]
call read_sectors
push bx
mov ax, WORD [SystemID + 2]
mov cx, ax
mov dx, ax
shr dx, 1
add cx, dx
mov bx, 0x7E00
add bx, cx
mov dx, WORD [bx]
test ax, 1
jnz .odd
.even:
and dx, 0x0FFF
jmp .done
.odd:
shr dx, 4
.done:
mov WORD [SystemID + 2], dx
cmp dx, 0x0FF0
jb .next_cluster
mov dl, BYTE [Drive]
jmp 0x0050:0x0000
cli
hlt
read_sectors:
mov di, 5
.loop:
push ax
push bx
push cx
; Convert the LBA-address to CHS-format.
xor dx, dx
div WORD [SectorsPerTrack]
inc dl
mov cl, dl
xor dx, dx
div WORD [Heads]
mov dh, dl
mov dl, al
; Attempt to read the sector.
mov ax, 0x0201
mov dl, BYTE [Drive]
int 0x13
jnc .ok
; Reset the drive.
xor ax, ax
int 0x13
dec di
pop cx
pop bx
pop ax
jnz .loop
int 0x18
.ok:
pop cx
pop bx
pop ax
add bx, WORD [BytesPerSector]
inc ax
loop read_sectors
ret
msgFailure db "ERROR: PRESS ANY KEY TO REBOOT...", 0x0D, 0x0A, 0x00
ImageName db "LOADER BIN"
times 510 - ($ - $$) db 0
dw 0xAA55
Stephan J.R. van Schaik.