FAT32 CHS boot sector problems since we introduced FAT32 LBA support?
As SYS is part of the kernel sources, maybe somebody on the
kernel mailing list has an idea for me...? Thanks! :-)
apparently the same SYS update which introduced FAT32 LBA support also
broke FAT32 CHS support: On Dimitris' PC, CHS based FAT32 boot always
fails, but his BIOS lacks LBA support. After installing MBR-based disk
drivers to add LBA support and support for 4 instead of 2 harddisks,
FreeDOS boots fine with modern FAT32 LBA boot sectors. Before that, he
had to use ancient CHS-only-for-FAT32 SYS to get a working boot sector.
I was busy trying to find out what broke and when, but cannot find it.
Maybe you can help me out here... :-)
- old SYS sets drive number to -1 but it gets overwritten on boot anyway
- new SYS sets drive number to 0x80 but it gets overwritten on boot, too
- new SYS uses signed byte to word comparison in cluster code (fff fff8
versus fff -8) but source code is unchanged, maybe a NASM auto tune?)
- r751 moves the load location far pointer to inside the code, while it
was at +5a in the variable area before. Not sure why that was changed?
Apparently both good and bad are AFTER this diff, so that should be
safe? The r321 to r343 patch makes CHS calculations small but obscure:
This leaves the main "calculate parameters at runtime, instead of using
SYS to hardcode them" patch as suspicious change. We have this patch to
keep FAT32 partitions bootable even when you resize them with 3rd party
tools. Of course the patch adds yet more obscure code to the boot sect:
Note that this r382 to r652 patch also drops the last call of "print",
so we could drop that function to gain some space for making the code
a bit less unreadable by "un-optimizing" some bits. Apparently somebody
simply forgot to drop print after dropping the last place using it ;-)
Not sure why, but the patch also moves around the offsets of 4 variables
by 4 bytes each: fat_start, data_start, fat_secmask, fat_secshift, which
makes the before / after the patch situation a bit harder to "diff" now.
The basic function of the patch is to add some code after the setup of
stack, segments and registers and relocation to 1FE0:xxxx, but before
the first call to "find sector for cluster" and "read sector from disk"
after which the root directory is scanned and, when a kernel is found,
the kernel file is loaded by cluster chain:
DI = DI + (byte fats [+10] * word sec per fat HIGH [+26])
[+62] = DX:AX = (byte fats [+10] * word sec per fat LOW [+24]) + DI:SI
word sec mask [+66] = CX ... = (AX >> 2) - 1
AX = 1 ...
CX >>= 1
while CX not 1
byte sec shift [+68] = AL
(the above is my attempt to summarize what the code actually does, see
the source code for the description of what it wants to do - as said,
I fail to see a problem here, but the symptom still is a failed boot!)
Thanks for helping out!
PS: Of course the boot sector stores DL to byte drive [+40] before it
starts juggling with DX:AX, so that value seems to be safe as well.
The initial calculations seem to achieve the same in different ways:
> fat_sector = dd 0
> done much later in CHS case, but early enough as far as I can tell
> fat_start = data_start = dd nHidden + word bsResSectors
> di:si and fat_start okay, check data_start
> data_start += dd (byte followed by 2 dw 0?) [bsFATs] *signed dd [xsectPerFat]
> ax = db bsFATs cbw
> di += ax * dw xsectPerFat HIGH, ignoring overflows
> data_start = dx:ax = di:si + (ax * dw xsectPerFat LOW), 16x16=32 bit
However, the "secshift" calculations are rather different here...
Note that the FAT12 / FAT16 boot sector reads the entire FAT
into RAM first, so THAT does not have to know which sector of
the FAT contains info about which cluster.
> fat_secshift = 7 for 128 FAT items per 512 byte sector
> ax = 512
> while ax not bsBytesPerSec
> ax <<= 1
> cx = fat_secmask = (dw bsBytesPerSec >> 2) - 1
> cx++, now (dw bsBytesPerSec >> 2) which is FAT items per sector
> ax expected to be 0 after movsw
> cx >>= 1
> while cx not 1
> fat_secshift = AL, low byte of ax
> fat_sector LOW = fat_sector HIGH = cx-- (is 0 now)
Mixed other differences which might be relevant:
> only boot32lb uses 386+ code and calls print which modifies AX BX SI
> convert_cluster: cmp eax,fff fff8 jnc EOF
> convert_cluster: cmp dx,fff jnz okay cmp ax,fff8 jnc EOF?
> next_cluster uses shr dx,1 rcr ax,1 in a loop to shr DX:AX by fat_secshift
> ... ((di and secmask) << 2) + fat_start ...
And from the comparison FAT32 CHS to FAT12 / FAT16 boot code: