;Please review my bootloader. I made separate versions for FAT12, FAT16 and FAT32 volumes.
;Upon booting it looks for an user-named program in the root directory and then executes it .COM-style.
;This is not true .COM because :
;   The file extension can be anything
;   SP will not be initialized at FFFEh to allow for
;     an executable bigger than 64KB
;If the user wanted to run an MS-DOS EXE, I programmed a conversion utility EXE2COM (not to be confused with EXE2BIN) that basically adds an EXE loader to the program file.

; ****************************************
; * BootACom v1.00 (c) 2010 Luc Fran‡ois *
; *	Last modified : 01/08/2010       *
; *	Total lines   : 259 lines        *
; ****************************************
;
DBR=0700h
; On startup CS:IP=0090h:0100h, SS:SP=0090h:0100h, DS=0090h, ES=0090h
; ----------------------------------------------
NewDBR12:	jmp	near Start
; ----------------------------------------------
		db	'BootACom'		; +03 * bsOEMName
		dw	?			; +11	bpbBytesPerSector
		db	?			; +13	bpbSectorsPerCluster
		dw	?			; +14	bpbReservedSectors
		db	?			; +16	bpbNumberOfFATs
		dw	?			; +17	bpbRootEntries
		dw	?			; +19 * bpbTotalSectors16
		db	?			; +21 * bpbMedia
		dw	?			; +22	bpbFATSize16
		dw	?			; +24	bpbSectorsPerTrack
		dw	?			; +26	bpbNumberOfHeads
		dd	?			; +28	bpbHiddenSectors
		dd	?			; +32 * bpbTotalSectors32
		db	?			; +36	bsDriveNumber
		db	?			; +37 * bsReserved1
		db	?			; +38 * bsBootSignature
		dd	?			; +39 * bsVolumeSerialNumber
		db	'NO NAME    '		; +43 * bsVolumeLabel
		db	'FAT12   '		; +54 * bsFileSystem
; ----------------------------------------------
		db	11 dup ' '		;Max. 11 chars (blanks padded)
; ----------------------------------------------
; IN (ch=0,edx,es) OUT (ebx,edx)
ReadSectorEx:	xor	ebx,ebx
; ---   ---   ---   ---   ---   ---   ---   ---
; IN (ch=0,edx,es:bx) OUT (bx,edx)
ReadSector:	push	di			;(1) EDX=LBA
		mov	di,5			;Possible retries
.Retry:		pusha				;(2)
		jmp	near .CHS		;{'jmp near .CHS','push dword
		push	edx es bx		;(4)...			  0'}
		push	1			;Read 1 sector
		push	16			;Length of Data Address Packet
		mov	si,sp			;DS=SS
		mov	ah,42h
		jmp	short .Read
.CHS:		push	edx			;(3)
		mov	ax,[bp+24]		;bpbSectorsPerTrack [1,63]
		mul	word [bp+26]		;bpbNumberOfHeads [1,256]
		xchg	cx,ax			;'mov cx,ax' -> CX=Sectors ...
		pop	ax dx			;(3)		    PerCylinder
		div	cx			;CX=SectorsPerCylinder
		xchg	cx,ax			;'mov cx,ax' Cylinder
		xchg	ch,cl			; -> CH=Cylinder[0-7]
		shl	cl,6			; -> CL[6-7]=Cylinder[8-9]
		xchg	ax,dx			;'mov ax,dx'
		div	byte [bp+24]		;bpbSectorsPerTrack
		mov	dh,al			;Head
		or	cl,ah			; -> CL[0-5]=Sector-1
		inc	cx			; -> CL[0-5]=Sector
		pusha				;(4) 'sub sp,16'
		mov	ax,0201h		;Read 1 sector
.Read:		mov	dl,[bp+36]		;bsDriveNumber
		int	13h			;BIOS 'Extended Read' or
		jnc	.OK			;     'Read Disk Sectors'
		mov	ah,00h
		int	13h			;BIOS 'Reset Disk System'
		popa				;(4a) 'add sp,16'
		popa				;(2a)
		dec	di
		jnz	.Retry
		mov	cl,2			;CH=0
		jmp	short FatalError
.OK:		popa				;(4b) 'add sp,16'
		popa				;(2b)
		pop	di			;(1)
		add	bx,[bp+11]		;bpbBytesPerSector
		inc	edx
		ret
; ----------------------------------------------
Start:		mov	ax,(DBR-256)/16
		mov	bp,256
		cli
		mov	ss,ax
		mov	sp,bp
		sti
		mov	ds,ax
		mov	es,ax
		mov	cx,bp
		mov	si,7C00h-(DBR-256)
		mov	di,bp
		cld
		rep movsw
		jmp	far 0000h:DBR+Continue
; ----------------------------------------------
Continue:	stosw				;Signature <> MS-DOS (20CDh)
		mov	[bp+36],dl		;bsDriveNumber
; ----------------------------------------------
; Make sure CPU is 32-bit
		pushf				;(1)
		pushf
		pop	ax
		xor	ax,7000h		;Try flipping 'NT' and 'IOPL'
		push	ax
		popf
		pushf
		pop	bx
		popf				;(1)
		cmp	ax,bx
		je	CPU_OK
		inc	cx			; -> CX=1
; ---   ---   ---   ---   ---   ---   ---   ---
; IN (cx)
FatalError:	mov	ax,0E07h		;Beep [1,3] time(s)
		xor	bx,bx
		int	10h			;BIOS 'Teletype Character'
		loop	FatalError
.Crash:		jmp	short .Crash
; ----------------------------------------------
; Patch code if IBM/MS INT 13 Extensions are available
CPU_OK:		test	dl,dl
		jns	.1			;Only for drives 80h-FFh
		mov	bx,55AAh
		mov	ah,41h
		int	13h			;BIOS 'Installation Check'
		jc	.1			;Not supported
		cmp	bx,0AA55h
		jne	.1			;Not installed
		shr	cx,1
		jnc	.1			;No 42h-44h,47h,48h functions
		mov	word [bp+62+11+8],6A66h	;'jmp near .CHS' -> 'push dword
; ----------------------------------------------			    0'}
; Calculate and push some temporary variables
; --------ggffffeeddddccbbbbaaaa <- BP
; LBA of FAT1
.1:		movzx	edx,word [bp+14]	;bpbReservedSectors
		add	edx,[bp+28]		;bpbHiddenSectors
		push	edx			;(a) FAT1_LBA
; LBA of Root
		movzx	eax,word [bp+22]	;bpbFATSize16
		movzx	cx,byte [bp+16]		;bpbNumberOfFATs
.2:		add	edx,eax			;FATSize32
		loop	.2
		push	edx			;(b) Root_LBA
; LBA of Data Region
		mov	bx,[bp+11]		;bpbBytesPerSector
		shr	bx,5			; -> BX=RootEntriesPerSector
		mov	ax,[bp+17]		;bpbRootEntries, each 32 bytes
		push	ax			;(c) RootEntries
.3:		inc	edx
		sub	ax,bx
		jnbe	.3
		push	edx			;(d) DataRegion_LBA
; Reserve memory to cache 2 FAT sectors
		int	12h			;BIOS 'Get Memory Size'
		cwd				; -> DX=0
		shl	ax,6			;Convert it from KB to õ
		stosw				;MS-DOS has pspNXTGRAF here
		shl	bx,1			; -> BX=ParagraphsPerSector
		sub	ax,bx
		sub	ax,bx
		pusha				;(e) DiskBuffer
						;(f) CachedFATSectors_LBA, none
						;(g) ParagraphsPerSector
; ----------------------------------------------
		push	(DBR+512+256)/16
		pop	es
; Find file 'ProgramName' in Root Directory
		mov	edx,[bp-8]		;(b) Root_LBA
NextRootSector:	call	ReadSectorEx		; -> EBX, EDX
		xor	di,di			;ES:DI Current Root Sector
.NextRootEntry:	cmp	[es:di],ch		;CH=0
		je	.eFile			;End of Root
		cmp	byte [es:di],0E5h	;2 lines that could be dropped
		je	.SkipEntry		;Skip free entry
		test	byte [es:di+11],00011000b ;dirAttributes DIRECTORY|VOL.
		jnz	.SkipEntry		;Is no file (Could even be LFN)
		pusha
		lea	si,[bp+62]		;ProgramName
		mov	cl,11			;CH=0
		repe cmpsb
		popa
		je	NameFound
.SkipEntry:	add	di,32			;To next entry
		dec	word [bp-10]		;(c) RootEntries
.eFile:		jz	eFile			;File not found!
		cmp	di,bx			;BX=BytesPerSector
		jb	.NextRootEntry
		jmp	short NextRootSector
; ----------------------------------------------
FollowChain:	pop	ax
; ClusterToSector in EDX
.NextCluster:	lea	edx,[esi-2]
		bsf	cx,[bp+13]		;bpbSectorsPerCluster {1,2,4,..
		shl	edx,cl			;CX=[0,7]	8,16,32,64,128}
		add	edx,[bp-14]		;(d) DataRegion_LBA
		mov	cl,[bp+13]		;bpbSectorsPerCluster, CH=0
.LoopInCluster:call	ReadSectorEx		; -> EBX, EDX
		call	ax			;{CallBack2}
		loop	.LoopInCluster
; ClusterToCluster
		push	es			;(1)
		mov	es,[bp-16]		;(e) DiskBuffer
		mov	edx,esi			;High words are/become zero
		shr	si,1
		pushf				;(2)
		add	dx,si			; -> DX=RoundDown(Cluster*1.5)
		mov	si,[bp+11]		;bpbBytesPerSector {512...4096}
		bsf	cx,si			; -> CX=[9,12]
		dec	si
		and	si,dx			;Offset in a sector of 1st FAT
		shr	dx,cl			;Relative sector in 1st FAT
		add	edx,[bp-4]		;(a) FAT1_LBA
		cmp	edx,[bp-20]		;(f) CachedFATSectors_LBA ?
		je	.ReadEntry		;Yes, No need to reload
		mov	[bp-20],edx		;(f) CachedFATSectors_LBA
		call	ReadSectorEx		; -> EBX, EDX
		call	ReadSector		; -> BX, EDX
.ReadEntry:	mov	si,[es:si]		;12 useful bits
		popf				;(2)
		jnc	.Even
.Odd:		shr	si,4
.Even:		and	si,0FFFh
		pop	es			;(1)
		jmp	short .NextCluster
eFile:		mov	cl,3			;CH=0
		jmp	near FatalError
; ----------------------------------------------
NameFound:	movzx	esi,word [es:di+26] 	;dirFirstCluster
		mov	edi,[es:di+28]		;dirFileSize
		test	edi,edi
		jz	eFile			;Not much of a file!
; ----------------------------------------------
; Load file 'ProgramName'
		call	FollowChain		;This is a de facto jump!
CallBack2:	sub	edi,ebx			;FileSize
		jbe	RunImage
		mov	bx,es
		add	bx,[bp-22]		;(g) ParagraphsPerSector
		mov	es,bx
		cmp	bx,[bp-16]		;(e) DiskBuffer
		ja	eFile			;File is too big!
		ret
; ----------------------------------------------
; No need to pop last callback address. Saves us 1 byte
; Start the program .COM-style
; We won't initialize SP=FFFEh to allow for executables bigger than 64KB
RunImage:	mov	cl,(DBR+512)/16	;CH=0
		mov	ss,cx
		mov	sp,bp
		mov	ds,cx
		mov	es,cx
		push	cx bp
		retf
; ----------------------------------------------
		db	510-($-$$) dup 0
		dw	0AA55h
; ----------------------------------------------
