mirror of
https://github.com/reactos/reactos.git
synced 2024-09-06 10:52:52 +00:00
431 lines
16 KiB
ArmAsm
431 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
|