Feel free to use it (it's a good start to learning how a bootloader works) if you wish. Would be nice if you give the author some credit for this work though if you use it. :)
Here is the algorithm that the code corresponds to,
* Setup Registers and Variables needed (initialization)
* Calculate the start of Root Directory
* Calculate the start of Data Area
* Read the Root Directory contents to 0x0:0x700
* Search the entries one by one for the specified file
* If found read in the FAT to 0x0:0x2500 and start following the FAT chain till EOF
* Read in the file from Disk sectors to address 0x0:0x8000
* Transfer control to the address 0x0:0x8000
Pretty simple isn't it? Here's the same thing in ASM (I had used the NASM assembler back then),
Please use a fixed size font such as Courier New for best viewing
;/*/////////////////////////////////////////////////////////////////////////////////////*\
;// THIS IS A PRIMARY BOOT LOADER FOR A FAT 12 FLOPPY - Anselm Joseph Meyn //
;// //
;// boot sector code loaded to 0x7C00:start (512 bytes by the BIOS) //
;// The FAT12 Root Directory loaded at 0x0:0x700 - 0x0:0x2300 (14 sectors) //
;// The FAT is next loaded at location 0x0:0x2500 - 0x0:0x4b00 (19 sectors) //
;// The Secondary file is loaded in at 0x0:0x8000 //
;// Stack is from 0x0 - 0x700 (SP=0x700, SS=0x0) //
;// //
;// The NAME of the secondary stage is BOOT2.BIN (this may be changed if you wish) //
;// Last Modified :- February 26,2004 [Version 1.1] //
;\*/////////////////////////////////////////////////////////////////////////////////////*/
[BITS 16]
%define RD_BUFF 0x700
%define FAT_BUFF 0x2500
%define FILE_BUFF 0x8000
[ORG 0]
entry:
jmp short begin
nop
LABEL DB 'FAT12FLP'
BYTES_PER_SECT DW 0200h
SECTS_PER_CLUST DB 01h
SECTS_IN_BOOT DW 01h
FAT_COPIES DB 02h
DIR_ENTRIES DW 0e0h
SECTORS DW 0b40h
DISK_TYPE DB 0F0h
SECTS_PER_FAT DW 09h
SECTS_PER_TRK DW 012h
HEADS DW 02h
HIDDEN_SECTS DD 0h
HUGE_SECTS DD 0h
DRIVE_NO DB 0h
RESERVED DB 0h
BOOT_SIGN DB 29h
VOL_ID DD 00007c00h
VOL_LABEL DB 'ANSELM MEYN'
FS_TYPE DB 'FAT12 '
begin:
jmp 0x07c0:.start
.start:
cli ; disable interrupts
mov ax,cs ; initialize all the necessary
mov ds,ax ; registers DS==CS
mov ax,0
mov es,ax ; ES=SS=0
mov ss,ax
mov sp,0x700 ; Stack pointer at 0x700
mov [DRIVE_NO],dl ; save DRIVE_NO here
sti ; enable interrupts
;________________________________________________________________________________
; Calculate the start of Root Directory and save in AX +
;-------------------------------------------------------------------------------+
mov al,[FAT_COPIES] ; no of FAT copies in al
mov bx,[SECTS_PER_FAT] ; no of sectors per FAT in BX
mul bx ; multiply it
add ax,[SECTS_IN_BOOT] ; add sectors in boot
add ax,word [HIDDEN_SECTS] ; add hidden sectors
; AX = Start of Root Dir(19 here)
;________________________________________________________________________________
;________________________________________________________________________________
; Calculate the start of Data Area and save it in a variable +
;-------------------------------------------------------------------------------+
mov dx,[DIR_ENTRIES] ; no. of Root Dir Entries in AX
shl dx,5 ; Multiply by 32(bytes per entry)
add dx,[BYTES_PER_SECT] ; add bytes per sector to it(bootsector)
dec dx ; decrement by one for calculation
shr dx,9 ; divide by 512(bytes_per_sect)
push dx ; This is the no. of RD sectors
; to read in. DX=14
add dx,ax ; Add RD Start to it to get DA Start
mov [SECTS_B4_DA],dx ; save this value in a variable
;________________________________________________________________________________
;________________________________________________________________________________
; Now to read the Root Directory at 0x0:RD_BUFF +
; Reads the entries at ES:BX +
;-------------------------------------------------------------------------------+
call trans_address ; AX translated to physical address
mov bx,RD_BUFF ; BUFFER to read the ROOT_DIR
pop ax ; sectors to read in
call disk_read ; read the disk sector
;________________________________________________________________________________
;________________________________________________________________________________
; Now the search for internal files begins +
; Called after reading the directory entries +
; this searches first 16 entries for the given filename +
; The Root Directory entry is in DS:SI +
; and the filename in ES:DI +
;-------------------------------------------------------------------------------+
mov dx,[DIR_ENTRIES] ; move the no of entries into DX
mov bx,RD_BUFF ; ROOT DIRECTORY Buffer in BX
filsrch:
cld
mov si,file ; file name
mov di,bx ; move entry BASE+BX to DI
mov cx,0bh ; 11 bytes to compare
repe cmpsb ; check for existence of file
je short found ; if the file is found goto FOUND
;
add bx,20h ; increment BX by 32 bytes(next entry)
dec dx ; else decrement entry count
jnz short filsrch ; and search for the next file
mov si,finderr ; Finally display file error
call print ;
jmp error ; if not found
;________________________________________________________________________________
found:
;________________________________________________________________________________
; If the file is found then +
; address of file directory entry is in BX +
; Read in the FAT now +
;-------------------------------------------------------------------------------+
mov dx,[es:bx+1ah] ; get start cluster of file in DX
mov ax,[SECTS_IN_BOOT] ; start of FAT in AX
push dx ; save entry cluster value
call trans_address ; translate address to physical
mov bx,FAT_BUFF ; and buffer to store FAT table in BX
mov ax,[SECTS_PER_FAT] ; sectors to read
call disk_read ; Read the disk now to get FAT in memory
pop ax ; entry cluster value in AX
;________________________________________________________________________________
;________________________________________________________________________________
; Search and read of the File clusters +
; Here AX=ENTRY CLUSTER, BX=FAT_BUFF,CX=?,DX=? +
;--------------------------------------------------------------------------------+
mov bx,FILE_BUFF ; BUFFER to read in the file
sub bx,200h ;
jmp short chk ; check the entry cluster
fat_cont:
mov ax,dx ; DX=AX=cluster value
add ax,ax ; AX multiplied by 2
add ax,dx ; multiplied by 3(add 3 times)
clc ; clear out carry flag
rcr ax,1 ; divide by 2 (multiplying by 1.5 actually)
pushf ; save flags
push bx ; and BX reg value
mov bx,FAT_BUFF ; get the FAT
add bx,ax ; entry values
mov ax,[es:bx] ; in AX
pop bx ; retrieve BX value
popf ; and flags
jc short .rl ; is result not an integer
;integer part
and ax,0x0fff ; else AND with 0x0fff
jmp short chk ; continue checking
.rl: ; real value part
shr ax,4 ; if odd right shift by 4 bits
chk:
push ax ; save AX value
cmp ax,0x0ff8 ; is this the last cluster
jge short quit ; yes then exit
dec ax ; else subtract 2
dec ax ; from the FAT entry
add ax,[SECTS_B4_DA] ; AX = sector to read data from
call trans_address ; Get physical address
add bx,200h ; add offset 512 bytes for next sector to BX
mov al,01 ; no. sectors to read
call disk_read ; Read the sector
pop dx ; restore DX value
jmp short fat_cont ; continue the fat loop for next sector
;________________________________________________________________________________
quit:
mov dl,[DRIVE_NO] ; save some info for the secondary
mov dh,[SECTS_B4_DA]
mov bl,[SECTS_PER_TRK]
mov bh,[HEADS]
push dx
push bx
mov si,msg_success
call print
jmp 0000:FILE_BUFF ; jump to secondary loader code
;
; COMMON ROUTINES USED, BEGIN FROM HERE
;
error:
; ______________________________________________________________________________
;/ This is called on any error it displays an error message and \
; asks the user to press any key for reboot *
; Input:- the specific String in SI reg *
; clobbers SI Reboots at the end *
;-------------------------------------------------------------------------------*
mov si,errmsg ; display the
call print ; error message
mov ah,0
int 16h ; wait for key press
int 19h ; reboot
;\______________________________________________________________________________/
print:
; ______________________________________________________________________________
;/ Print Function for NULL terminated Strings \
; the NULL terminated string must be present in SI *
; Registers Affected:- AX,BX(popped back) *
;-------------------------------------------------------------------------------*
push ax ; save AX and BX
push bx ; registers
ploop:
cld
lodsb ; AL = [DS:SI]
or al, al ; Set zero flag if al=0
jz short pstop ; jump to stop if zero flag is set
mov ah, 0eh ; video function 0Eh (print char)
mov bx, 0007h ; color
int 10h ; interrupt 10h for screen display
jmp short ploop ; keep printing
pstop:
pop bx ; retrieve BX and
pop ax ; AX registers
retn ; end print
;\______________________________________________________________________________/
disk_read:
; ______________________________________________________________________________
;/ Disk Read Routine using int13h, the registers must be set up already \
; Input:- ES:BX=Dest address,AL=no. of continuos sectors, CX,DX
; Output:- CF=success/failure,AL=sectors read,AH=stat code
; Re-tries 3 times for read
; Registers Affected:-AH=02,all
;-------------------------------------------------------------------------------*
mov bp,3 ; counter
mov dl,[DRIVE_NO] ; move drive no to dl
.rtry: ;
mov ah,0x0 ; reset the drive
int 13h ;
jc short .rtry ; if error retry
dec bp ; decrement counter
jz .err ; if it is not zero retry
mov ah,0x2 ; disk read function
int 13h ; no then read it
jc short .rtry ; if no read errror then
retn ; end disk_read
.err:
mov si,readerr ; otherwise display an
call print ; error message
jmp error ; and reboot
;\______________________________________________________________________________/
trans_address:
; ______________________________________________________________________________
;/ Translates Logical sector in AX to Physical sector,head,cylinder \
; Input:- the LBA address in the AX reg
; Output:- cl=sector,ch=cylinder,dh=head
; Formula:-
; HPC = Number of heads/cylinder
; SPT = sectors/track
; Sector = (LBA mod SPT) + 1
; Head = (LBA / SPT) mod HPC
; Cylinder = (LBA / SPT) / HPC
; Registers Affected:- AX, CX, DX
;-------------------------------------------------------------------------------*
xor dx,dx ; clear out DX reg
div word [SECTS_PER_TRK] ; divide AX by SPT
inc dl ; increment dl to get sector address
mov cl,dl ; get sector value
xor dx,dx ; clear DX reg
div word [HEADS] ;
mov ch,al ; get cylinder value
mov dh,dl ; get head value
retn ; all values obtained now
;\______________________________________________________________________________/
file db 'BOOT2 BIN'
readerr db CR,LF,'I/O Error',0
finderr db CR,LF,'File Missing',0
errmsg db CR,LF,'Press key to reboot...',0
msg_success db CR,LF,'Bootstrap Complete',0
SECTS_B4_DA dw 0
size equ $-entry
%if size+2>512
%error "code greater than 512 bytes"
%endif
times (510-size) db 0
dw 0xaa55
CR equ 0x0d
LF equ 0x0a