mirror of
https://github.com/reactos/reactos.git
synced 2024-09-11 13:23:51 +00:00
![Brian Palmer](/assets/img/avatar_default.png)
New .ini file format (sort of) - changed some parameter names, etc. More efficient .ini file parsing code More efficient use of memory, smaller image file Total rewrite of file system code (the old stuff sucked) VFAT Long File Names now supported! ReactOS is the only boot type supported currently, disabled all others (temporarily) svn path=/trunk/; revision=2372
506 lines
16 KiB
NASM
506 lines
16 KiB
NASM
; FAT32.ASM
|
|
; FAT32 Boot Sector
|
|
; Copyright (c) 1998, 2000, 2001 Brian Palmer
|
|
|
|
;org 7c00h
|
|
org 0
|
|
|
|
segment .text
|
|
|
|
bits 16
|
|
|
|
start:
|
|
jmp short main
|
|
nop
|
|
|
|
OEMName db 'FrLdr1.0'
|
|
BytesPerSector dw 512
|
|
SectsPerCluster db 0
|
|
ReservedSectors dw 32
|
|
NumberOfFats db 2
|
|
MaxRootEntries dw 0 ; Always zero for FAT32 volumes
|
|
TotalSectors dw 0 ; Always zero for FAT32 volumes
|
|
MediaDescriptor db 0f8h
|
|
SectorsPerFat dw 0 ; Always zero for FAT32 volumes
|
|
SectorsPerTrack dw 0
|
|
NumberOfHeads dw 0
|
|
HiddenSectors dd 0
|
|
TotalSectorsBig dd 0
|
|
; FAT32 Inserted Info
|
|
SectorsPerFatBig dd 0
|
|
ExtendedFlags dw 0
|
|
FSVersion dw 0
|
|
RootDirStartCluster dd 0
|
|
FSInfoSector dw 0
|
|
BackupBootSector dw 6
|
|
Reserved1 times 12 db 0
|
|
; End FAT32 Inserted Info
|
|
BootDrive db 0
|
|
Reserved db 0
|
|
ExtendSig db 29h
|
|
SerialNumber dd 00000000h
|
|
VolumeLabel db 'NO NAME '
|
|
FileSystem db 'FAT32 '
|
|
|
|
main:
|
|
cli
|
|
cld
|
|
|
|
; Lets copy ourselves from 0000:7c00 to 9000:0000
|
|
; and transfer control to the new code
|
|
xor ax,ax
|
|
mov ds,ax
|
|
mov si,7c00h
|
|
mov ax,9000h
|
|
mov es,ax
|
|
xor di,di
|
|
mov cx,512
|
|
rep movsb
|
|
jmp 0x9000:RealMain
|
|
|
|
; Now we are executing at 9000:xxxx
|
|
; We are now free to load freeldr.sys at 0000:7e00
|
|
RealMain:
|
|
xor ax,ax
|
|
mov bp,ax
|
|
mov sp,ax ; Setup a stack
|
|
mov ax,cs ; Setup segment registers
|
|
mov ds,ax ; Make DS correct
|
|
mov es,ax ; Make ES correct
|
|
mov ss,ax ; Make SS correct
|
|
sti ; Enable ints now
|
|
|
|
|
|
|
|
|
|
CheckSectorsPerFat:
|
|
cmp WORD [BYTE bp+SectorsPerFat],byte 0x00 ; Check the old 16-bit value of SectorsPerFat
|
|
jnz CheckFailed ; If it is non-zero then exit with an error
|
|
CheckTotalSectors: ; Check the old 16-bit value of TotalSectors & MaxRootEntries
|
|
cmp DWORD [BYTE bp+MaxRootEntries],byte 0x00; by comparing the DWORD at offset MaxRootEntries to zero
|
|
jnz CheckFailed ; If it is non-zero then exit with an error
|
|
CheckFileSystemVersion:
|
|
cmp WORD [BYTE bp+FSVersion],byte 0x00 ; Check the file system version word
|
|
jna GetDriveParameters ; It is zero, so continue
|
|
CheckFailed:
|
|
jmp PrintFileSystemError ; If it is not zero then exit with an error
|
|
|
|
|
|
GetDriveParameters:
|
|
mov ax,0800h
|
|
mov dl,[BYTE bp+BootDrive] ; Get boot drive in dl
|
|
int 13h ; 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,0ffffh
|
|
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,3fh ; 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 [BiosCHSDriveSize],eax
|
|
|
|
|
|
LoadExtraBootCode:
|
|
; First we have to load our extra boot code at
|
|
; sector 14 into memory at [9000:0200h]
|
|
mov eax,0eh
|
|
add eax,DWORD [BYTE bp+HiddenSectors] ; Add the number of hidden sectors
|
|
mov cx,1
|
|
mov bx,9000h
|
|
mov es,bx ; Read sector to [9000:0200h]
|
|
mov bx,0200h
|
|
call ReadSectors
|
|
jmp StartSearch
|
|
|
|
|
|
|
|
; Reads logical sectors into [ES:BX]
|
|
; EAX has logical sector number to read
|
|
; CX has number of sectors to read
|
|
ReadSectors:
|
|
cmp eax,DWORD [BiosCHSDriveSize] ; Check if they are reading a sector within CHS range
|
|
jbe ReadSectorsCHS ; Yes - go to the old CHS routine
|
|
|
|
ReadSectorsLBA:
|
|
pushad ; Save logical sector number & sector count
|
|
|
|
o32 push byte 0
|
|
push eax ; Put 64-bit logical block address on stack
|
|
push es ; Put transfer segment on stack
|
|
push bx ; Put transfer offset on stack
|
|
push byte 1 ; Set transfer count to 1 sector
|
|
push byte 0x10 ; Set size of packet to 10h
|
|
mov si,sp ; Setup disk address packet on stack
|
|
|
|
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 bp+BootDrive] ; Drive number
|
|
mov ah,42h ; Int 13h, AH = 42h - Extended Read
|
|
int 13h ; Call BIOS
|
|
jc PrintDiskError ; If the read failed then abort
|
|
|
|
add sp,0x10 ; Remove disk address packet from stack
|
|
|
|
popad ; Restore sector count & logical sector number
|
|
|
|
inc eax ; Increment sector to read
|
|
mov dx,es
|
|
add dx,byte 20h ; Increment read buffer for next sector
|
|
mov es,dx
|
|
|
|
loop ReadSectorsLBA ; Read next sector
|
|
|
|
ret
|
|
|
|
|
|
; Reads logical sectors into [ES:BX]
|
|
; EAX has logical sector number to read
|
|
; CX has number of sectors to read
|
|
ReadSectorsCHS:
|
|
pushad
|
|
xor edx,edx
|
|
movzx ecx,WORD [BYTE bp+SectorsPerTrack]
|
|
div ecx ; Divide logical by SectorsPerTrack
|
|
inc dl ; Sectors numbering starts at 1 not 0
|
|
mov cl,dl ; Sector in CL
|
|
mov edx,eax
|
|
shr edx,16
|
|
div WORD [BYTE bp+NumberOfHeads] ; Divide logical by number of heads
|
|
mov dh,dl ; Head in DH
|
|
mov dl,[BYTE bp+BootDrive] ; Drive number in DL
|
|
mov ch,al ; Cylinder in CX
|
|
ror ah,1 ; Low 8 bits of cylinder in CH, high 2 bits
|
|
ror ah,1 ; in CL shifted to bits 6 & 7
|
|
or cl,ah ; Or with sector number
|
|
mov ax,0201h
|
|
int 13h ; 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 PrintDiskError ; If the read failed then abort
|
|
|
|
popad
|
|
|
|
inc eax ; Increment Sector to Read
|
|
|
|
mov dx,es
|
|
add dx,byte 20h ; Increment read buffer for next sector
|
|
mov es,dx
|
|
|
|
loop ReadSectorsCHS ; Read next sector
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
; Displays a disk error message
|
|
; And reboots
|
|
PrintDiskError:
|
|
mov si,msgDiskError ; Bad boot disk message
|
|
call PutChars ; Display it
|
|
mov si,msgAnyKey ; Press any key message
|
|
call PutChars ; Display it
|
|
|
|
jmp Reboot
|
|
|
|
; Displays a file system error message
|
|
; And reboots
|
|
PrintFileSystemError:
|
|
mov si,msgFileSystemError ; FreeLdr not found message
|
|
call PutChars ; Display it
|
|
mov si,msgAnyKey ; Press any key message
|
|
call PutChars ; Display it
|
|
|
|
Reboot:
|
|
xor ax,ax
|
|
int 16h ; Wait for a keypress
|
|
int 19h ; Reboot
|
|
|
|
PutChars:
|
|
lodsb
|
|
or al,al
|
|
jz short Done
|
|
mov ah,0eh
|
|
mov bx,07h
|
|
int 10h
|
|
jmp short PutChars
|
|
Done:
|
|
retn
|
|
|
|
|
|
|
|
BiosCHSDriveSize dd 0
|
|
|
|
msgDiskError db 'Disk error',0dh,0ah,0
|
|
msgFileSystemError db 'File system error',0dh,0ah,0
|
|
msgAnyKey db 'Press any key to restart',0dh,0ah,0
|
|
|
|
times 510-($-$$) db 0 ; Pad to 510 bytes
|
|
dw 0aa55h ; BootSector signature
|
|
|
|
|
|
; End of bootsector
|
|
;
|
|
; Now starts the extra boot code that we will store
|
|
; at sector 14 on a FAT32 volume
|
|
;
|
|
; To remain multi-boot compatible with other operating
|
|
; systems we must not overwrite anything other than
|
|
; the bootsector which means we will have to use
|
|
; a different sector like 14 to store our extra boot code
|
|
|
|
|
|
|
|
StartSearch:
|
|
; Now we must get the first cluster of the root directory
|
|
mov eax,DWORD [BYTE bp+RootDirStartCluster]
|
|
cmp eax,0ffffff8h ; Check to see if this is the last cluster in the chain
|
|
jb ContinueSearch ; If not continue, if so then we didn't find freeldr.sys
|
|
jmp PrintFileNotFound
|
|
ContinueSearch:
|
|
mov bx,7e0h
|
|
mov es,bx ; Read cluster to [0000:7e00h]
|
|
call ReadCluster ; Read the cluster
|
|
|
|
|
|
; Now we have to find our way through the root directory to
|
|
; The OSLOADER.SYS file
|
|
xor bx,bx
|
|
mov bl,[BYTE bp+SectsPerCluster]
|
|
shl bx,4 ; BX = BX * 512 / 32
|
|
mov ax,7e0h ; We loaded at 07e0:0000
|
|
mov es,ax
|
|
xor di,di
|
|
mov si,filename
|
|
mov cx,11
|
|
rep cmpsb ; Compare filenames
|
|
jz FoundFile ; If same we found it
|
|
dec bx
|
|
jnz FindFile
|
|
jmp PrintFileNotFound
|
|
|
|
FindFile:
|
|
mov ax,es ; We didn't find it in the previous dir entry
|
|
add ax,2 ; So lets move to the next one
|
|
mov es,ax ; And search again
|
|
xor di,di
|
|
mov si,filename
|
|
mov cx,11
|
|
rep cmpsb ; Compare filenames
|
|
jz FoundFile ; If same we found it
|
|
dec bx ; Keep searching till we run out of dir entries
|
|
jnz FindFile ; Last entry?
|
|
|
|
; Get the next root dir cluster and try again until we run out of clusters
|
|
mov eax,DWORD [BYTE bp+RootDirStartCluster]
|
|
call GetFatEntry
|
|
mov [BYTE bp+RootDirStartCluster],eax
|
|
jmp StartSearch
|
|
|
|
FoundFile:
|
|
|
|
; Lets save the contents of the screen
|
|
; from B800:0000 to 9000:8000
|
|
pushad
|
|
|
|
mov ax,0b800h
|
|
mov ds,ax
|
|
xor si,si
|
|
mov ax,9800h
|
|
mov es,ax
|
|
xor di,di
|
|
mov cx,2000 ; Copy 2000 characters (screen is 80x25)
|
|
rep movsw ; 2 bytes a character (one is the attribute byte)
|
|
|
|
mov ah,03h ; AH = 03h
|
|
xor bx,bx ; BH = video page
|
|
int 10h ; BIOS Int 10h Func 3 - Read Cursor Position and Size
|
|
mov [es:di],dx ; DH = row, DL = column
|
|
|
|
; Display "Loading FreeLoader..." message
|
|
mov si,msgLoading ; Loading message
|
|
call PutChars ; Display it
|
|
|
|
popad
|
|
|
|
xor di,di ; ES:DI has dir entry
|
|
xor dx,dx
|
|
mov ax,WORD [es:di+14h] ; Get start cluster high word
|
|
shl eax,16
|
|
mov ax,WORD [es:di+1ah] ; Get start cluster low word
|
|
|
|
CheckStartCluster:
|
|
cmp eax,2 ; Check and see if the start cluster starts at cluster 2 or above
|
|
jnb CheckEndCluster ; If so then continue
|
|
jmp PrintFileSystemError ; If not exit with error
|
|
CheckEndCluster:
|
|
cmp eax,0ffffff8h ; Check and see if the start cluster is and end of cluster chain indicator
|
|
jb InitializeLoadSegment ; If not then continue
|
|
jmp PrintFileSystemError ; If so exit with error
|
|
|
|
InitializeLoadSegment:
|
|
mov bx,7e0h
|
|
mov es,bx
|
|
|
|
LoadFile:
|
|
cmp eax,0ffffff8h ; Check to see if this is the last cluster in the chain
|
|
jae LoadFileDone ; If so continue, if not then read the next one
|
|
push eax
|
|
xor bx,bx ; Load ROSLDR starting at 0000:7e00h
|
|
push es
|
|
call ReadCluster
|
|
pop es
|
|
|
|
xor bx,bx
|
|
mov bl,[BYTE bp+SectsPerCluster]
|
|
shl bx,5 ; BX = BX * 512 / 16
|
|
mov ax,es ; Increment the load address by
|
|
add ax,bx ; The size of a cluster
|
|
mov es,ax
|
|
|
|
pop eax
|
|
push es
|
|
call GetFatEntry ; Get the next entry
|
|
pop es
|
|
|
|
jmp LoadFile ; Load the next cluster (if any)
|
|
|
|
LoadFileDone:
|
|
mov dl,[BYTE bp+BootDrive]
|
|
xor ax,ax ; We loaded at 0000:7e00 but the entry point is 0000:8000
|
|
push ax ; because the first 512 bytes is fat helper code
|
|
push WORD 8000h ; We will do a far return to 0000:8000h
|
|
retf ; Transfer control to ROSLDR
|
|
|
|
|
|
; Returns the FAT entry for a given cluster number
|
|
; On entry EAX has cluster number
|
|
; On return EAX has FAT entry for that cluster
|
|
GetFatEntry:
|
|
|
|
shl eax,2 ; EAX = EAX * 4 (since FAT32 entries are 4 bytes)
|
|
mov ecx,eax ; Save this for later in ECX
|
|
xor edx,edx
|
|
movzx ebx,WORD [BYTE bp+BytesPerSector]
|
|
push ebx
|
|
div ebx ; FAT Sector Number = EAX / BytesPerSector
|
|
movzx ebx,WORD [BYTE bp+ReservedSectors]
|
|
add eax,ebx ; FAT Sector Number += ReservedSectors
|
|
mov ebx,DWORD [BYTE bp+HiddenSectors]
|
|
add eax,ebx ; FAT Sector Number += HiddenSectors
|
|
pop ebx
|
|
dec ebx
|
|
and ecx,ebx ; FAT Offset Within Sector = ECX % BytesPerSector
|
|
; EAX holds logical FAT sector number
|
|
; ECX holds FAT entry offset
|
|
|
|
; Now we have to check the extended flags
|
|
; to see which FAT is the active one
|
|
; and use it, or if they are mirrored then
|
|
; no worries
|
|
movzx ebx,WORD [BYTE bp+ExtendedFlags] ; Get extended flags and put into ebx
|
|
and bx,0x0f ; Mask off upper 8 bits, now we have active fat in bl
|
|
jz LoadFatSector ; If fat is mirrored then skip fat calcs
|
|
cmp bl,[BYTE bp+NumberOfFats] ; Compare bl to number of fats
|
|
jb GetActiveFatOffset
|
|
jmp PrintFileSystemError ; If bl is bigger than numfats exit with error
|
|
GetActiveFatOffset:
|
|
push eax ; Save logical FAT sector number
|
|
mov eax,[BYTE bp+SectorsPerFatBig] ; Get the number of sectors occupied by one fat in eax
|
|
mul ebx ; Multiplied by the active FAT index we have in ebx
|
|
pop edx ; Get logical FAT sector number
|
|
add eax,edx ; Add the current FAT sector offset
|
|
|
|
LoadFatSector:
|
|
push ecx
|
|
; EAX holds logical FAT sector number
|
|
mov bx,7000h
|
|
mov es,bx
|
|
xor bx,bx ; We will load it to [7000:0000h]
|
|
mov cx,1
|
|
call ReadSectors
|
|
mov bx,7000h
|
|
mov es,bx
|
|
pop ecx
|
|
mov eax,DWORD [es:ecx] ; Get FAT entry
|
|
and eax,0fffffffh ; Mask off reserved bits
|
|
|
|
ret
|
|
|
|
|
|
; Reads cluster number in EAX into [ES:0000]
|
|
ReadCluster:
|
|
; StartSector = ((Cluster - 2) * SectorsPerCluster) + ReservedSectors + HiddenSectors;
|
|
|
|
dec eax
|
|
dec eax
|
|
xor edx,edx
|
|
movzx ebx,BYTE [BYTE bp+SectsPerCluster]
|
|
mul ebx
|
|
push eax
|
|
xor edx,edx
|
|
movzx eax,BYTE [BYTE bp+NumberOfFats]
|
|
mul DWORD [BYTE bp+SectorsPerFatBig]
|
|
movzx ebx,WORD [BYTE bp+ReservedSectors]
|
|
add eax,ebx
|
|
add eax,DWORD [BYTE bp+HiddenSectors]
|
|
pop ebx
|
|
add eax,ebx ; EAX now contains the logical sector number of the cluster
|
|
xor bx,bx ; We will load it to [ES:0000], ES loaded before function call
|
|
movzx cx,BYTE [BYTE bp+SectsPerCluster]
|
|
call ReadSectors
|
|
ret
|
|
|
|
|
|
; Displays a file not found error message
|
|
; And reboots
|
|
PrintFileNotFound:
|
|
mov si,msgFreeLdr ; FreeLdr not found message
|
|
call PutChars ; Display it
|
|
mov si,msgAnyKey ; Press any key message
|
|
call PutChars ; Display it
|
|
|
|
jmp Reboot
|
|
|
|
msgFreeLdr db 'FREELDR.SYS not found',0dh,0ah,0
|
|
filename db 'FREELDR SYS'
|
|
msgLoading db 'Loading FreeLoader...',0dh,0ah,0
|
|
|
|
|
|
times 1022-($-$$) db 0 ; Pad to 1022 bytes
|
|
|
|
dw 0aa55h ; BootSector signature
|