Page 1 of 1

Any better FAT16 bootsectors out there?

Posted: Fri Nov 06, 2009 11:32 am
by IanSeyler
I tried to use the FAT16 boot sector code from FreeDOS and the one on osdever.net but had no luck with either of them. I've decided to switch from FAT32 (aka FAT28) to FAT16 since it seems a bit easier to maintain.

In the end I wrote my own and the code is below. It works correctly but has no error checking. Anything better out there?

bootsec.asm:

Code: Select all

org 0x7C00

entry:
	jmp short begin
	nop

%define bsOemName	bp+0x03 	; OEM label (8)
%define bsBytesPerSec	bp+0x0B 	; bytes/sector (dw)
%define bsSecsPerClust	bp+0x0D 	; sectors/allocation unit (db)
%define bsResSectors	bp+0x0E 	; # reserved sectors (dw)
%define bsFATs		bp+0x10 	; # of fats (db)
%define bsRootDirEnts	bp+0x11 	; Max # of root dir entries (dw)
%define bsSectors	bp+0x13 	; # sectors total in image (dw)
%define bsMedia 	bp+0x15 	; media descriptor (db)
%define bsSecsPerFat	bp+0x16 	; # sectors in a fat (dw)
%define bsSecsPerTrack	bp+0x18 	; # sectors/track
%define bsHeads		bp+0x1A 	; # heads (dw)
%define bsHidden 	bp+0x1C 	; # hidden sectors (dd)
%define bsSectorHuge	bp+0x20 	; # sectors if > 65536 (dd)
%define bsDriveNumber	bp+0x24 	; (dw)
%define bsSigniture	bp+0x26 	; (db)
%define bsVolumeSerial	bp+0x27 	; (dd)
%define bsVolumeLabel	bp+0x2B 	; (11)
%define bsSysID		bp+0x36 	; (8)

times 0x3B db 0				; Code starts at offset 0x3E

begin:
	mov [bsDriveNumber], dl	; BIOS passes drive number in DL
	xor eax, eax
	xor esi, esi
	xor edi, edi
	mov ds, ax
	mov es, ax
	mov bp, 0x7c00

; read in the root cluster
; check for the filename
; if found save the starting cluster
; if not then error

;fatstart = bsResSectors

;rootcluster = bsResSectors + (bsFATs * bsSecsPerFat)
; 4 + (2 * 254) = sector 512

;datastart = bsResSectors + (bsFATs * bsSecsPerFat) + ((bsRootDirEnts * 32) / bsBytesPerSec) 
; 4 + (2 * 254) + ((512 * 32) / 512) = sector 544

;cluster X starting sector
; starting sector = (bsSecsPerClust * (cluster# - 2)) + datastart

ff:
	mov ax, [bsSecsPerFat]
	shl ax, 1	; quick multiply by two
	add ax, [bsResSectors]
	mov [rootstart], ax

	mov bx, [bsRootDirEnts]
	shr bx, 4	; bx = (bx * 32) / 512
	add bx, ax	; BX now holds the datastart sector number
	mov [datastart], bx
	
ff_next_sector:
	mov bx, 0x8000
	mov si, bx
	mov di, bx
	call readsector

; Search for file name, and find start cluster.
ff_next_entry:
	mov cx, 11
	mov si, filename
	repe cmpsb
	jz ff_done		; note that di now is at dirent+11

	add di, byte 0x20
	and di, byte -0x20
	cmp di, [bsBytesPerSec]
	jnz ff_next_entry
	; load next sector
	dec dx			; next sector in cluster
	jnz ff_next_sector

ff_done:
	add di, 15
	mov ax, [di]	; AX now holds the starting cluster #

; At this point we have found the file we want and know the cluster where the file starts

	mov bx, 0x8000	; We want to load to 0x0000:0x8000
loadfile:	
	call readcluster
	cmp ax, 0xFFF8	; Have we reached the end cluster marker?
	jg loadfile	; If not then load another
	
	jmp 0x0000:0x8000

	
	
;------------------------------------------------------------------------------
; Read a sector from disk, using LBA
; input:	EAX - 32-bit DOS sector number
;		ES:BX - destination buffer
; output:	ES:BX points one byte after the last byte read
;		EAX - next sector
readsector:
	push dx
	push si
	push di

read_it:
	push eax	; Save the sector number
	mov di, sp	; remember parameter block end

	push byte 0	; other half of the 32 bits at [C]
	push byte 0	; [C] sector number high 32bit
	push eax	; [8] sector number low 32bit
	push es 	; [6] buffer segment
	push bx 	; [4] buffer offset
	push byte 1	; [2] 1 sector (word)
	push byte 16	; [0] size of parameter block (word)

	mov si, sp
	mov dl, [bsDriveNumber]
	mov ah, 42h	; EXTENDED READ
	int 0x13	; http://hdebruijn.soo.dto.tudelft.nl/newpage/interupt/out-0700.htm#0651

	mov sp, di	; remove parameter block from stack
	pop eax		; Restore the sector number

	jnc read_ok 	; jump if no error

	push ax
	xor ah, ah	; else, reset and retry
	int 0x13
	pop ax
	jmp read_it

read_ok:
	inc eax 		; next sector
	add bx, 512		; Add bytes per sector
	jnc no_incr_es		; if overflow...

incr_es:
	mov dx, es
	add dh, 0x10		; ...add 1000h to ES
	mov es, dx

no_incr_es:
	pop di
	pop si
	pop dx
	ret
;------------------------------------------------------------------------------


;------------------------------------------------------------------------------
; Read a cluster from disk, using LBA
; input:	AX - 16-bit cluster number
;		ES:BX - destination buffer
; output:	ES:BX points one byte after the last byte read
;		AX - next cluster
readcluster:
	push cx
	mov [tcluster], ax		; save our cluster value
;cluster X starting sector
; starting sector = (bsSecsPerClust * (cluster# - 2)) + datastart
	xor cx, cx
	sub ax, 2
	mov cl, byte [bsSecsPerClust]
	imul cx		; EAX now holds starting sector
	add ax, word [datastart]	; add the datastart offset

	xor cx, cx
	mov cl, byte [bsSecsPerClust]
readcluster_nextsector:
	call readsector
	dec cx
	cmp cx, 0
	jne readcluster_nextsector

; Great! We read a cluster.. now find out where the next cluster is
	push bx	; save our memory pointer
	mov bx, 0x7E00	; load a sector from the root cluster here
	push bx
	mov ax, [bsResSectors]
	call readsector
	pop bx	; bx points to 0x7e00 again
	mov ax, [tcluster] ; ax holds the cluster # we just read
	shl ax, 1	; multipy by 2
	add bx, ax
	mov ax, [bx]
	
	pop bx	; restore our memory pointer
	pop cx
	
	ret
;------------------------------------------------------------------------------

msg_Load db "Loading... ", 0
msg_Error db "No "
filename db "PURE64  SYS", 0
datastart dw 0x0000
rootstart dw 0x0000
tcluster dw 0x0000

times 510-$+$$ db 0

sign dw 0xAA55
-Ian

Re: Any better FAT16 bootsectors out there?

Posted: Sat Nov 07, 2009 2:39 pm
by earlz
ReturnInfinity wrote:I tried to use the FAT16 boot sector code from FreeDOS and the one on osdever.net but had no luck with either of them. I've decided to switch from FAT32 (aka FAT28) to FAT16 since it seems a bit easier to maintain.

In the end I wrote my own and the code is below. It works correctly but has no error checking. Anything better out there?

<snip>

-Ian
I don't think your going to find many bootloaders in general with much error checking..

Re: Any better FAT16 bootsectors out there?

Posted: Sun Nov 08, 2009 8:52 pm
by bewing
Here is a bootsector that quok and I wrote together, and optimized heavily. (GAS at&t syntax)

Currently it works with both FAT12 and FAT16, looks in the home directory for a second-stage bootloader called "osloader" (ELF format, load address 0x40000, entry address 0x40120), loads it, and jumps to it.
It's been moderately tested, and actually exposes a bug in some versions of the bochs BIOS.

Re: Any better FAT16 bootsectors out there?

Posted: Wed Nov 11, 2009 9:46 am
by pcmattman
I'm surprised nobody has commented on bewing's contribution to this thread. This is a high quality bootsector in only 512 bytes that supports both FAT12 and FAT16 - no small feat!

My hat's off to you guys for a fantastic contribution to the community :)

Re: Any better FAT16 bootsectors out there?

Posted: Thu Nov 12, 2009 12:36 pm
by Brynet-Inc
And thanks to quok losing much hair, and sending countless images via email.. it's one of the very few bootloaders that work on my older 486 laptop.

Re: Any better FAT16 bootsectors out there?

Posted: Fri Nov 13, 2009 11:51 am
by IanSeyler
bewing, thanks for the post .. I'll take a look at that code.

Re: Any better FAT16 bootsectors out there?

Posted: Fri Nov 13, 2009 2:35 pm
by FlashBurn
@bewing

I haven´t looked that deep into the source, but maybe with mine you could get your bootsector even smaller. For this you should look how I handled my vars.

Re: Any better FAT16 bootsectors out there?

Posted: Fri Nov 13, 2009 8:24 pm
by bewing
You are assuming that a sector is 512 bytes, which simplifies the code but will not work on (for example) CDROMs that are not doing emulation.
You are also using Int13h extensions (for your READ EXTENDED function), which will not work on floppies at all -- but from what I can see, this code is FAT16-specific anyway.
You are also assuming that the FAT will fit in one 64k memory page, which is not at all certain.
But, in general, pretty good code! :D

Using bp the way you do may actually save a few bytes, truly -- since all my addresses are 2 bytes, and the bp offset may only be one byte, and there is no sib byte in rmode.

Re: Any better FAT16 bootsectors out there?

Posted: Sat Nov 14, 2009 1:00 am
by FlashBurn
Yeah this code is fat16 specific and I also have a bootsector which works with CHS and the "old" int13h code. You are wrong that I assuming that the FAT goes into one 64k page, I (and my code) know that the FAT can be as large as 2 64k pages and I´m testing for it.

I would have one question. Why should someone use FAT for a CD (w/o emulation)?

Re: Any better FAT16 bootsectors out there?

Posted: Sat Nov 14, 2009 5:59 pm
by bewing
> Why should someone use FAT for a CD (w/o emulation)?

That was just a hypothetical example. There will (in the future) be devices that have sectorsizes that are not 512 bytes -- because they already exist. It may not be reasonable for CDs to use FAT, but it may be reasonable on some kind of non-512b-sector flash memory device that I don't know about.

> You are wrong that I assuming that the FAT goes into one 64k page

From what I saw, you load the FAT in one gulp. There are many BIOSes that cannot handle more than 127 sectors, 128 sectors, or 64K in one read using Int13h ax=42. When using that BIOS function, I always stick to 32K per read, just to make sure -- and keep page boundaries as aligned as reasonably possible.

Re: Any better FAT16 bootsectors out there?

Posted: Sun Nov 15, 2009 2:15 am
by FlashBurn
Yeah, you are right, I think I will change this. In my bootsector which doesn´t use the extended int13h functions there my "load_chs" function is also looping and loads only 0x7f (I think so) sectors. But if I will find some time I will change my bootsector so that it will use the values which are in the bpb.