;/*/////////////////////////////////////////////////////////////////////////////////////*\
;// 	THIS IS A BOOT LOADER FOR A FAT 12 FlOPPY - Anselm Meyn		 		//
;//											//
;// boot sector code   loaded at 	0x0:0x7C00 - 0x0:0x7E00 (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 is BOOT2.BIN (this may be changed if you wish)		//
;//		Last Modified :- November 30,2003	[Version 1.0]			//
;\*/////////////////////////////////////////////////////////////////////////////////////*/

[BITS 16]
%define RD_BUFF	 	0x700
%define FAT_BUFF 	0x2500
%define	FILE_BUFF	0x8000

[ORG 0x0]
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
	mov	es,ax			; with CS value
	mov	ax,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 varible		+
;________________________________________________________________________________

;________________________________________________________________________________
;	Now to read the Root Directory at 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_DIT		+
	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	di,file		; file name					+
	mov	si,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 DX				+
;	Read in the FAT now							+
;-------------------------------------------------------------------------------+
	mov	dx,[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	 	+
;________________________________________________________________________________

;________________________________________________________________________________
; 	Relocate the RD_BUFF and FAT_BUFF to segment address of 0		+
;	hack added on 28/11/'03							+
;-------------------------------------------------------------------------------+
	push	es			; 					+
	mov	di,temp			;					+
	mov	dword [di],0x00000700	; Load the ES:DI with			+
	les	di,[di]			; 0x0:0x700 to copy data		+
	mov	si,0x700		; Copy from 0x7C00:0x700		+
	mov	cx,0x1100		; 17 sectors				+
	cld				;					+
	rep	movsd			; start copying				+
	pop	es			; restore previous ES value		+
;________________________________________________________________________________

;________________________________________________________________________________
;	Search and read of the File clusters					+
;	Here AX=?, BX=FAT_BUFF,CX=?,DX=?					+
;--------------------------------------------------------------------------------
	mov	bx,0
	mov	es,bx
	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,[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				+
;________________________________________________________________________________

quit:
	mov	dl,[DRIVE_NO]	;						 
	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
;
;			THE ROUTINES USED BEGIN FROM HERE DOWNWARDS
;
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	; dispaly 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 end			*
	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 Error',0
errmsg		db	CR,LF,'Press any key to reboot..',0
msg_success	db	CR,LF,'Bootstrap Complete',0
temp		dd	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	0aa55h

CR	equ	0x0d
LF	equ	0x0a