reactos/boot/freeldr/bootsect/fat.S

430 lines
16 KiB
ArmAsm

// FAT.ASM
// FAT12/16 Boot Sector
// Copyright (c) 1998, 2001, 2002 Brian Palmer
// This is a FAT12/16 file system boot sector
// that searches the entire root directory
// for the file freeldr.sys and loads it into
// memory.
//
// The stack is set to 0000:7BF2 so that the first
// WORD pushed will be placed at 0000:7BF0
//
// The DWORD at 0000:7BFC or BP-04h is the logical
// sector number of the start of the data area.
//
// The DWORD at 0000:7BF8 or BP-08h is the total
// sector count of the boot drive as reported by
// the computers bios.
//
// The WORD at 0000:7BF6 or BP-0ah is the offset
// of the ReadSectors function in the boot sector.
//
// The WORD at 0000:7BF4 or BP-0ch is the offset
// of the ReadCluster function in the boot sector.
//
// The WORD at 0000:7BF2 or BP-0eh is the offset
// of the PutChars function in the boot sector.
//
// When it locates freeldr.sys on the disk it will
// load the first sector of the file to 0000:F800
// With the help of this sector we should be able
// to load the entire file off the disk, no matter
// how fragmented it is.
//
// We load the entire FAT table into memory at
// 7000:0000. This improves the speed of floppy disk
// boots dramatically.
#include <asm.inc>
#include <freeldr/include/arch/pc/x86common.h>
#define BP_REL(x) [bp+x-offset start]
DataAreaStartHigh = 2
DataAreaStartLow = 4
BiosCHSDriveSizeHigh = 6
BiosCHSDriveSizeLow = 8
BiosCHSDriveSize = 8
ReadSectorsOffset = 10
ReadClusterOffset = 12
PutCharsOffset = 14
BootSectorStackTop = HEX(7c00) - 16
// org 7c00h
.code16
start:
jmp main
nop
OEMName:
.ascii "FrLdr1.0"
BytesPerSector:
.word 512
SectsPerCluster:
.byte 1
ReservedSectors:
.word 1
NumberOfFats:
.byte 2
MaxRootEntries:
.word 224
TotalSectors:
.word 2880
MediaDescriptor:
.byte HEX(0f0)
SectorsPerFat:
.word 9
SectorsPerTrack:
.word 18
NumberOfHeads:
.word 2
HiddenSectors:
.long 0
TotalSectorsBig:
.long 0
BootDrive:
.byte HEX(0ff)
Reserved:
.byte 0
ExtendSig:
.byte HEX(29)
SerialNumber:
.long 00000000
VolumeLabel:
.ascii "NO NAME "
FileSystem:
.ascii "FAT12 "
main:
xor ax, ax
mov ss, ax
mov bp, HEX(7c00)
mov sp, BootSectorStackTop // Setup a stack
mov ds, ax // Make DS correct
mov es, ax // Make ES correct
cmp byte ptr BP_REL(BootDrive), HEX(0ff) // If they have specified a boot drive then use it
jne GetDriveParameters
mov byte ptr BP_REL(BootDrive), dl // Save the boot drive
GetDriveParameters:
mov ah, 8
mov dl, byte ptr BP_REL(BootDrive) // Get boot drive in dl
int HEX(13) // Request drive parameters from the bios
jnc CalcDriveSize // If the call succeeded then calculate the drive size
// If we get here then the call to the BIOS failed
// so just set CHS equal to the maximum addressable
// size
mov cx, HEX(0ffff)
mov dh, cl
CalcDriveSize:
// Now that we have the drive geometry
// lets calculate the drive size
mov bl, ch // Put the low 8-bits of the cylinder count into BL
mov bh, cl // Put the high 2-bits in BH
shr bh, 6 // Shift them into position, now BX contains the cylinder count
and cl, HEX(3f) // Mask off cylinder bits from sector count
// CL now contains sectors per track and DH contains head count
movzx eax, dh // Move the heads into EAX
movzx ebx, bx // Move the cylinders into EBX
movzx ecx, cl // Move the sectors per track into ECX
inc eax // Make it one based because the bios returns it zero based
inc ebx // Make the cylinder count one based also
mul ecx // Multiply heads with the sectors per track, result in edx:eax
mul ebx // Multiply the cylinders with (heads * sectors) [stored in edx:eax already]
// We now have the total number of sectors as reported
// by the bios in eax, so store it in our variable
mov dword ptr [bp - BiosCHSDriveSize], eax
// Now we must find our way to the first sector of the root directory
xor ax, ax
xor cx, cx
mov al, byte ptr BP_REL(NumberOfFats) // Number of fats
mul word ptr BP_REL(SectorsPerFat) // Times sectors per fat
add ax, word ptr BP_REL(HiddenSectors)
adc dx, word ptr BP_REL(HiddenSectors+2) // Add the number of hidden sectors
add ax, word ptr BP_REL(ReservedSectors) // Add the number of reserved sectors
adc dx, cx // Add carry bit
mov word ptr [bp - DataAreaStartLow], ax // Save the starting sector of the root directory
mov word ptr [bp - DataAreaStartHigh], dx // Save it in the first 4 bytes before the boot sector
mov si, word ptr BP_REL(MaxRootEntries) // Get number of root dir entries in SI
pusha // Save 32-bit logical start sector of root dir
// DX:AX now has the number of the starting sector of the root directory
// Now calculate the size of the root directory
xor dx, dx
mov ax, 32 // Size of dir entry
mul si // Times the number of entries
mov bx, word ptr BP_REL(BytesPerSector)
add ax, bx
dec ax
div bx // Divided by the size of a sector
// AX now has the number of root directory sectors
add word ptr [bp - DataAreaStartLow], ax // Add the number of sectors of the root directory to our other value
adc word ptr [bp - DataAreaStartHigh], cx // Now the first 4 bytes before the boot sector contain the starting sector of the data area
popa // Restore root dir logical sector start to DX:AX
LoadRootDirSector:
mov bx, HEX(7e0) // We will load the root directory sector
mov es, bx // Right after the boot sector in memory
xor bx, bx // We will load it to [0000:7e00h]
xor cx, cx // Zero out CX
inc cx // Now increment it to 1, we are reading one sector
xor di, di // Zero out di
push es // Save ES because it will get incremented by 20h
call ReadSectors // Read the first sector of the root directory
pop es // Restore ES (ES:DI = 07E0:0000)
SearchRootDirSector:
cmp byte ptr es:[di], ch // If the first byte of the directory entry is zero then we have
jz ErrBoot // reached the end of the directory and FREELDR.SYS is not here so reboot
pusha // Save all registers
mov cl, 11 // Put 11 in cl (length of filename in directory entry)
mov si, offset filename // Put offset of filename string in DS:SI
repe cmpsb // Compare this directory entry against 'FREELDR SYS'
popa // Restore all the registers
jz FoundFreeLoader // If we found it then jump
dec si // SI holds MaxRootEntries, subtract one
jz ErrBoot // If we are out of root dir entries then reboot
add di, 32 // Increment DI by the size of a directory entry
cmp di, HEX(0200) // Compare DI to 512 (DI has offset to next dir entry, make sure we haven't gone over one sector)
jc SearchRootDirSector // If DI is less than 512 loop again
jmp short LoadRootDirSector // Didn't find FREELDR.SYS in this directory sector, try again
FoundFreeLoader:
// We found freeldr.sys on the disk
// so we need to load the first 512
// bytes of it to 0000:F800
// ES:DI has dir entry (ES:DI == 07E0:XXXX)
mov ax, word ptr es:[di + HEX(1a)] // Get start cluster
push ax // Save start cluster
push FREELDR_BASE / 16 // Put load segment on the stack and load it
pop es // Into ES so that we load the cluster at 0000:F800
call ReadCluster // Read the cluster
pop ax // Restore start cluster of FreeLoader
// Save the addresses of needed functions so
// the helper code will know where to call them.
mov word ptr [bp-ReadSectorsOffset], offset ReadSectors // Save the address of ReadSectors
mov word ptr [bp-ReadClusterOffset], offset ReadCluster // Save the address of ReadCluster
mov word ptr [bp-PutCharsOffset], offset PutChars // Save the address of PutChars
// Now AX has start cluster of FreeLoader and we
// have loaded the helper code in the first 512 bytes
// of FreeLoader to 0000:F800. Now transfer control
// to the helper code. Skip the first three bytes
// because they contain a jump instruction to skip
// over the helper code in the FreeLoader image.
ljmp16 0, FREELDR_BASE + 3
// Displays an error message
// And reboots
ErrBoot:
mov si, offset msgFreeLdr // FreeLdr not found message
call PutChars // Display it
Reboot:
// mov si, offset msgAnyKey // Press any key message
// call PutChars // Display it
xor ax, ax
int HEX(16) // Wait for a keypress
int HEX(19) // Reboot
PutChars:
lodsb
or al,al
jz short Done
mov ah, HEX(0e)
mov bx, 7
int HEX(10)
jmp short PutChars
Done:
ret
// Displays a bad boot message
// And reboots
BadBoot:
mov si, offset msgDiskError // Bad boot disk message
call PutChars // Display it
jmp short Reboot
// Reads cluster number in AX into [ES:0000]
ReadCluster:
// StartSector = ((Cluster - 2) * SectorsPerCluster) + ReservedSectors + HiddenSectors;
dec ax // Adjust start cluster by 2
dec ax // Because the data area starts on cluster 2
xor ch, ch
mov cl, byte ptr BP_REL(SectsPerCluster)
mul cx // Times sectors per cluster
add ax, [bp-DataAreaStartLow] // Add start of data area
adc dx, [bp-DataAreaStartHigh] // Now we have DX:AX with the logical start sector of FREELDR.SYS
xor bx, bx // We will load it to [ES:0000], ES loaded before function call
// mov cl,BYTE [BYTE bp+SectsPerCluster]// Sectors per cluster still in CX
// call ReadSectors
// ret
// Reads logical sectors into [ES:BX]
// DX:AX has logical sector number to read
// CX has number of sectors to read
ReadSectors:
// We can't just check if the start sector is
// in the BIOS CHS range. We have to check if
// the start sector + length is in that range.
pusha
dec cx
add ax, cx
adc dx, 0
cmp dx, word ptr [bp-BiosCHSDriveSizeHigh] // Check if they are reading a sector within CHS range
ja ReadSectorsLBA // No - go to the LBA routine
jb ReadSectorsCHS // Yes - go to the old CHS routine
cmp ax, word ptr [bp-BiosCHSDriveSizeLow] // Check if they are reading a sector within CHS range
jbe ReadSectorsCHS // Yes - go to the old CHS routine
ReadSectorsLBA:
popa
ReadSectorsLBALoop:
pusha // Save logical sector number & sector count
push 0
push 0
push dx // Put 64-bit logical
push ax // block address on stack
push es // Put transfer segment on stack
push bx // Put transfer offset on stack
push 1 // Set transfer count to 1 sector
push HEX(10) // Set size of packet to 10h
mov si,sp // Setup disk address packet on stack
// We are so totally out of space here that I am forced to
// comment out this very beautifully written piece of code
// It would have been nice to have had this check...
//CheckInt13hExtensions: // Now make sure this computer supports extended reads
// mov ah,0x41 // AH = 41h
// mov bx,0x55aa // BX = 55AAh
// mov dl,[BYTE bp+BootDrive] // DL = drive (80h-FFh)
// int 13h // IBM/MS INT 13 Extensions - INSTALLATION CHECK
// jc PrintDiskError // CF set on error (extensions not supported)
// cmp bx,0xaa55 // BX = AA55h if installed
// jne PrintDiskError
// test cl,1 // CX = API subset support bitmap
// jz PrintDiskError // Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
// Good, we're here so the computer supports LBA disk access
// So finish the extended read
mov dl, byte ptr BP_REL(BootDrive) // Drive number
mov ah, HEX(42) // Int 13h, AH = 42h - Extended Read
int HEX(13) // Call BIOS
jc BadBoot // If the read failed then abort
add sp, 16 // Remove disk address packet from stack
popa // Restore sector count & logical sector number
inc ax // Increment Sector to Read
adc dx, 0
push bx
mov bx, es
add bx, HEX(20) // Increment read buffer for next sector
mov es, bx
pop bx
loop ReadSectorsLBALoop // Read next sector
ret
// Reads logical sectors into [ES:BX]
// DX:AX has logical sector number to read
// CX has number of sectors to read
// CarryFlag set on error
ReadSectorsCHS:
popa
ReadSectorsCHSLoop:
pusha
xchg ax, cx
xchg ax, dx
xor dx, dx
div word ptr BP_REL(SectorsPerTrack)
xchg ax, cx
div word ptr BP_REL(SectorsPerTrack) // Divide logical by SectorsPerTrack
inc dx // Sectors numbering starts at 1 not 0
xchg cx, dx
div word ptr BP_REL(NumberOfHeads) // Number of heads
mov dh, dl // Head to DH, drive to DL
mov dl, byte ptr BP_REL(BootDrive) // Drive number
mov ch, al // Cylinder in CX
ror ah, 2 // Low 8 bits of cylinder in CH, high 2 bits
// in CL shifted to bits 6 & 7
or cl, ah // Or with sector number
mov ax, HEX(0201)
int HEX(13) // DISK - READ SECTORS INTO MEMORY
// AL = number of sectors to read, CH = track, CL = sector
// DH = head, DL = drive, ES:BX -> buffer to fill
// Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read
jc BadBoot
popa
inc ax // Increment Sector to Read
jnz NoCarryCHS
inc dx
NoCarryCHS:
push bx
mov bx, es
add bx, HEX(20)
mov es, bx
pop bx
// Increment read buffer for next sector
loop ReadSectorsCHSLoop // Read next sector
ret
msgDiskError:
.ascii "Disk error", CR, LF, NUL
msgFreeLdr:
.ascii "Ldr not found", CR, LF, NUL
// Sorry, need the space...
// msgAnyKey:
// .ascii "Press any key to restart", CR, LF, NUL
// .ascii "Press a key", CR, LF, NUL
filename:
.ascii "FREELDR SYS"
.org 509 // Pad to 509 bytes
BootPartition:
.byte 0
BootSignature:
.word HEX(0aa55) // BootSector signature
.endcode16
END