724 lines
26 KiB
NASM
724 lines
26 KiB
NASM
|
NEC_98=1
|
||
|
Page ,132
|
||
|
TITLE BOOT SECTOR 1 OF TRACK 0 - BOOT LOADER
|
||
|
;++
|
||
|
;
|
||
|
;Module Name:
|
||
|
;
|
||
|
; fat32nec.asm
|
||
|
;
|
||
|
;Abstract:
|
||
|
;
|
||
|
; The ROM in NEC PC-9800 starts the boot process by performing a hardware
|
||
|
; initialization and a verification of all external devices. If all goes
|
||
|
; well, it will then load from the boot drive the sector from track 0, head 0,
|
||
|
; sector 1. This sector is placed at physical address 1FE00h.
|
||
|
;
|
||
|
; We need to load NTLDR at 2000:0. But the bootcode of FAT32 is 600h bytes.
|
||
|
; If we start at physical address 1FE0:0, the bootcode is broken by loaded NTLDR.
|
||
|
; At first, we move the bootcode to 0:7C00 from 1FE0:0 by oneself.
|
||
|
; The next, we jump to new Segment:Offset and start real bootcode procedure.
|
||
|
;
|
||
|
; The code is responsible for locating NTLDR and for placing the directory sector
|
||
|
; with this information.
|
||
|
;
|
||
|
; If WINBOOT.SYS is not found, an error message is displayed and the user is
|
||
|
; prompted to insert another disk. If there is a disk error during the
|
||
|
; process, a message is displayed and things are halted.
|
||
|
;
|
||
|
; At the beginning of the boot sector, there is a table which describes the
|
||
|
; structure of the media. This is equivalent to the BPB with some
|
||
|
; additional information describing the physical layout of the driver (heads,
|
||
|
; tracks, sectors)
|
||
|
;
|
||
|
;Author:
|
||
|
;
|
||
|
;
|
||
|
;Environment:
|
||
|
;
|
||
|
; Real mode
|
||
|
; FAT32 file system
|
||
|
;
|
||
|
;Revision History:
|
||
|
;
|
||
|
; 7/01/97 Tochizawa(NEC) support FAT32
|
||
|
;
|
||
|
;--
|
||
|
|
||
|
.xlist
|
||
|
include bpb.inc
|
||
|
include bootsec.inc
|
||
|
include dirent.inc
|
||
|
;include version.inc
|
||
|
.list
|
||
|
|
||
|
; ==========================================================================
|
||
|
ORIGIN EQU 7C00H ; Origin of bootstrap LOADER
|
||
|
|
||
|
BIO_SEG EQU 2000H ; Destination segment of BIOS
|
||
|
BIO_OFFSET EQU 0000H ; Offset of bios
|
||
|
|
||
|
SECTOR_SIZE EQU 512 ; Sector size in bytes
|
||
|
DIR_ENTRY_SIZE EQU SIZE DIR_ENTRY ; Size of directory entry in bytes
|
||
|
|
||
|
ROM_DISKRD EQU 2
|
||
|
|
||
|
SIZEBIGBOOTINSEC EQU 3
|
||
|
|
||
|
ifdef NEC_98
|
||
|
BOOTCODE_SEG EQU 0000H ; Destination segment of BOOTCODE
|
||
|
DAUA EQU 584h
|
||
|
endif
|
||
|
; ==========================================================================
|
||
|
|
||
|
SEGBIOS SEGMENT AT BIO_SEG
|
||
|
|
||
|
; Define the destination segment of the BIOS, including the
|
||
|
; initialization label
|
||
|
ORG BIO_OFFSET
|
||
|
WINLOAD LABEL BYTE
|
||
|
|
||
|
SEGBIOS ENDS
|
||
|
|
||
|
; ==========================================================================
|
||
|
|
||
|
; Local (on stack) Data storage between temp stack and start of
|
||
|
; boot sector
|
||
|
|
||
|
CurrBuffFatSecL EQU -12
|
||
|
CurrBuffFatSecH EQU -10
|
||
|
Int13Sectors EQU -8
|
||
|
DataSecL EQU -4
|
||
|
DataSecH EQU -2
|
||
|
|
||
|
; ==========================================================================
|
||
|
|
||
|
CODE SEGMENT
|
||
|
ASSUME CS:CODE,DS:NOTHING,ES:NOTHING,SS:NOTHING
|
||
|
ORG ORIGIN
|
||
|
|
||
|
|
||
|
Public $START
|
||
|
$START Label byte
|
||
|
|
||
|
jmp short Main
|
||
|
GotXint13:
|
||
|
nop ; used to store xint13 flag
|
||
|
|
||
|
GotXint13Offset = (offset GotXint13 - offset $START)
|
||
|
|
||
|
; ==========================================================================
|
||
|
|
||
|
; Start of BPB area of the boot record
|
||
|
|
||
|
OsName DB "MSWIN"
|
||
|
OsVersion DB "4.1" ; Windows version number
|
||
|
|
||
|
BytesPerSector DW SECTOR_SIZE ; Size of a physical sector
|
||
|
SecsPerClust DB 2 ; Sectors per allocation unit
|
||
|
ReservedSecs DW 8 ; Number of reserved sectors
|
||
|
NumFats DB 1 ; Number of fats
|
||
|
NumDirEntries DW 1024 ; Number of direc entries
|
||
|
TotalSectors DW 0 ; Number of sectors - number of hidden
|
||
|
; sectors (0 when 32 bit sector number)
|
||
|
MediaByte DB 0F8H ; MediaByte byte
|
||
|
NumFatSecs DW 0 ; Number of fat sectors (0 when 32 bit)
|
||
|
SecPerTrack DW 17 ; Sectors per track
|
||
|
NumHeads DW 4 ; Number of drive heads
|
||
|
|
||
|
HiddenSecs DD 1 ; Number of hidden sectors
|
||
|
BigTotalSecs DD 00200000h ; 32 bit version of number of sectors
|
||
|
BigNumFatSecs DD 00001FE0h ; 32 bit version of number of FAT sectors
|
||
|
ExtFlags DW 0
|
||
|
BPBReserved1 DW 0
|
||
|
RootStrtClus DD 0
|
||
|
FSInfoSec dw ((FSInfoSecSig - $START) / SECTOR_SIZE)
|
||
|
BkUpBootSec dw MBR_BOOTFAILBACKUP
|
||
|
BPBReserved2 DD 3 DUP (0)
|
||
|
.errnz ($-BytesPerSector) NE SIZE BIGFATBPB
|
||
|
|
||
|
BootDrv DB 80h
|
||
|
CurrentHead DB 0h ; Current Head
|
||
|
ExtBootSig DB 41
|
||
|
SerialNum DD 0
|
||
|
VolumeLabel DB 'NO NAME '
|
||
|
FatId DB 'FAT32 '
|
||
|
|
||
|
.errnz ($-$START) NE SIZE BIGFATBOOTSEC
|
||
|
|
||
|
|
||
|
; =========================================================================
|
||
|
|
||
|
;
|
||
|
; First thing is to reset the stack to a better and more known
|
||
|
; place. The ROM may change, but we'd like to get the stack
|
||
|
; in the correct place.
|
||
|
;
|
||
|
Main:
|
||
|
ifdef NEC_98
|
||
|
push si ; Save Partition Information
|
||
|
;
|
||
|
; We moved bootcode to 0:ORIGIN from 1fe0:0.
|
||
|
;
|
||
|
push di
|
||
|
mov ax, cs
|
||
|
mov ds, ax ; DS = CS
|
||
|
mov si, 0 ; DS:SI = 1fe0:0
|
||
|
cld
|
||
|
mov cx, 256
|
||
|
xor ax, ax
|
||
|
mov es, ax
|
||
|
mov di, ORIGIN ; ES:DI = 0:7c00
|
||
|
rep movsw
|
||
|
pop di
|
||
|
pop si ; Restore Partition Information
|
||
|
;
|
||
|
; We jump to 0:ORIGIN+RealBootStart
|
||
|
;
|
||
|
mov ax, BOOTCODE_SEG
|
||
|
push ax
|
||
|
mov ax, ORIGIN
|
||
|
add ax, RealBootStart
|
||
|
push ax
|
||
|
retf
|
||
|
|
||
|
RealBootStart = (offset $ - offset $START)
|
||
|
endif
|
||
|
xor CX,CX
|
||
|
mov SS,CX ;Work in stack just below this routine
|
||
|
mov SP,ORIGIN+CurrBuffFatSecL
|
||
|
mov es,cx
|
||
|
mov ds,cx ; DS = ES = SS = 0
|
||
|
ASSUME DS:CODE,ES:CODE,SS:CODE
|
||
|
mov BP,ORIGIN
|
||
|
|
||
|
IFDEF NEC_98
|
||
|
.386
|
||
|
push si ; Save Partition Information
|
||
|
mov al, ds:[DAUA] ;
|
||
|
mov [BP].bgbsDriveNumber, al ; Save DriveNumber
|
||
|
else
|
||
|
;
|
||
|
; Determine the number of sectors addressable via
|
||
|
; conventional int13. If we can't get drive params for some reason
|
||
|
; then something is very wrong -- we'll try to force the caller
|
||
|
; to use conventional int13 by maxing out the sector count.
|
||
|
;
|
||
|
mov [bp].GotXint13Offset,cl ; no xint13 yet
|
||
|
mov dl,[bp].bgbsDriveNumber ; int13 unit number
|
||
|
mov ah,8 ; get drive params
|
||
|
int 13h ; call BIOS
|
||
|
jnc @f ; no error, procede
|
||
|
mov cx,-1 ; strange case, fake registers to force
|
||
|
mov dh,cl ; use of standard int13 (set all vals to max)
|
||
|
@@:
|
||
|
.386
|
||
|
movzx eax,dh ; eax = max head # (0-255)
|
||
|
inc ax ; eax = heads (1-256)
|
||
|
movzx edx,cl ; edx = sectors per track + cyl bits
|
||
|
and dl,3fh ; edx = sectors per track (1-63)
|
||
|
mul dx ; eax = sectors per cylinder, edx = 0
|
||
|
xchg cl,ch
|
||
|
shr ch,6 ; cx = max cylinder # (0-1023)
|
||
|
inc cx ; cx = cylinders (1-1024)
|
||
|
movzx ecx,cx ; ecx = cylinders (1-1024)
|
||
|
mul ecx ; eax = sectors visible via int13, edx = 0
|
||
|
mov [bp].Int13Sectors,eax ; save # sectors addressable via int13
|
||
|
ENDIF
|
||
|
.8086
|
||
|
|
||
|
;
|
||
|
; The MBR (or boot ROM) only reads one boot sector. Thus the first order
|
||
|
; of business is to read the rest of ourself in by reading the second
|
||
|
; boot sector of the 2-sector boot record.
|
||
|
;
|
||
|
; The second sector in the NT case is at sector 12. This preserves
|
||
|
; the bootsect.dos logic and eliminates a special case for fat32.
|
||
|
;
|
||
|
ReadBoot:
|
||
|
cmp [BP].bgbsBPB.oldBPB.BPB_SectorsPerFAT,0 ; FAT32 BPB?
|
||
|
jne short NoSysMsg ; No, invalid, messed up
|
||
|
cmp [BP].bgbsBPB.BGBPB_FS_Version,FAT32_Curr_FS_Version
|
||
|
ja short NoSysMsg ; boot code too old for this volume
|
||
|
.386
|
||
|
mov eax,dword ptr [BP].bgbsBPB.oldBPB.BPB_HiddenSectors
|
||
|
add eax,12 ; read in the second boot sector
|
||
|
.8086
|
||
|
mov BX,ORIGIN + (SECTOR_SIZE * 2)
|
||
|
mov cx,1
|
||
|
call DoRead ; doesn't return if err
|
||
|
jmp DirRead ; no error, continue boot in sector 2
|
||
|
|
||
|
DiskError:
|
||
|
mov al,byte ptr [MSGOFF_IOERROR]
|
||
|
|
||
|
;
|
||
|
; Al is the offset - 256 of the message within the boot sector.
|
||
|
; So we first calculate the real segment-based offset to the message
|
||
|
; and stick it in si so lodsb will work later.
|
||
|
;
|
||
|
DisplayError:
|
||
|
.ERRNZ ORIGIN MOD 256
|
||
|
mov ah,(ORIGIN / 256) + 1
|
||
|
ifdef NEC_98
|
||
|
add ax, 2
|
||
|
mov di, 0
|
||
|
endif
|
||
|
mov si,ax
|
||
|
|
||
|
DisplayError1:
|
||
|
ifdef NEC_98
|
||
|
mov ax, 0a000h ; set V-RAM
|
||
|
mov es, ax
|
||
|
xor ah, ah
|
||
|
lodsb ; Get next character
|
||
|
cmp AL,0Dh ; end of message?
|
||
|
jz WaitForKey ; yes
|
||
|
cmp AL,0FFh ; end of sub-message?
|
||
|
je DisplayWait ; yes, switch to final message now
|
||
|
stosw ; move to vram
|
||
|
jmp short DisplayError1
|
||
|
else
|
||
|
lodsb ; get next character
|
||
|
test AL,AL ; end of message?
|
||
|
jz WaitForKey ; yes
|
||
|
cmp AL,0FFh ; end of sub-message?
|
||
|
je DisplayWait ; yes, switch to final message now
|
||
|
mov AH,14 ; write character & attribute
|
||
|
mov BX,7 ; attribute (white char on black)
|
||
|
int 10h ; print the character
|
||
|
jmp short DisplayError1
|
||
|
endif
|
||
|
|
||
|
DisplayWait:
|
||
|
mov al,byte ptr [MSGOFF_COMMON]
|
||
|
ifdef NEC_98
|
||
|
mov ah,(ORIGIN / 256) + 1
|
||
|
add ax, 2
|
||
|
mov si,ax
|
||
|
mov di, 160
|
||
|
jmp short DisplayError1
|
||
|
else
|
||
|
jmp short DisplayError
|
||
|
endif
|
||
|
|
||
|
NoSysMsg:
|
||
|
mov al,byte ptr [MSGOFF_NOSYS] ; point to no system file message
|
||
|
jmp short DisplayError
|
||
|
|
||
|
WaitForKey:
|
||
|
ifdef NEC_98
|
||
|
mov ax, 0h
|
||
|
int 18h
|
||
|
mov al, 0h
|
||
|
out 0f0h, al
|
||
|
else
|
||
|
cbw ;warning assumes al is zero!
|
||
|
int 16h ; get character from keyboard
|
||
|
int 19h ; Continue in loop till good disk
|
||
|
endif
|
||
|
|
||
|
; =========================================================================
|
||
|
;
|
||
|
; Read disk sector(s). This routine cannot transfer more than 64K!
|
||
|
;
|
||
|
; Inputs: EAX == physical sector #
|
||
|
; CL == # sectors (CH == 0)
|
||
|
; ES:BX == transfer address
|
||
|
;
|
||
|
; Outputs: EAX next physical sector #
|
||
|
; CX == 0
|
||
|
; ES:BX -> byte after last byte of read
|
||
|
; Does not return if error
|
||
|
;
|
||
|
; Reads sectors, switching to extended int13 if necessary and
|
||
|
; available. The note below is for the conventional int13 case.
|
||
|
;
|
||
|
; Notes: Reads sectors one at a time in case they straddle a
|
||
|
; track boundary. Performs full 32-bit division on the
|
||
|
; first decomposition (of logical sector into track+sector)
|
||
|
; but not on the second (of track into cylinder+head),
|
||
|
; since (A) we don't have room for it, and (B) the results
|
||
|
; of that division must yield a quotient < 1024 anyway, because
|
||
|
; the CHS-style INT 13h interface can't deal with cylinders
|
||
|
; larger than that.
|
||
|
;
|
||
|
; =========================================================================
|
||
|
|
||
|
DoRead:
|
||
|
.386
|
||
|
pushad
|
||
|
|
||
|
IFDEF NEC_98
|
||
|
shld edx,eax,16 ; EAX -> DX:AX
|
||
|
mov CX,AX ; We've gotten Phy.Sector# in DX:CX
|
||
|
mov bp,bx ; Buffer addr -> bp
|
||
|
mov bx,SECTOR_SIZE ; Sector size -> bx
|
||
|
push ds
|
||
|
xor ax,ax
|
||
|
mov ds,ax ; DS = 0
|
||
|
mov al,ds:[DAUA] ; set booting da/ua
|
||
|
pop ds ; Restore DS
|
||
|
and al,7fh ; strip high 1 bit
|
||
|
; SCSI HD
|
||
|
mov ah,06h ; set read command
|
||
|
int 1bh
|
||
|
else
|
||
|
;
|
||
|
; Determine if the sector we're about to read is available via
|
||
|
; conventional int13.
|
||
|
;
|
||
|
cmp eax,[bp].Int13Sectors ; determine if standard int13 is ok
|
||
|
jb stdint13
|
||
|
|
||
|
;
|
||
|
; Need extended int13. First set up parameter packet on stack.
|
||
|
; Then, if we don't know whether xint13 is available for the drive yet,
|
||
|
; find out. If not, error out since we know we can't read the sector
|
||
|
; we need.
|
||
|
;
|
||
|
db 66h ; hand-coded 32-bit push of 8-bit immediate
|
||
|
push 0 ; high 32 bits of sector #
|
||
|
push eax ; low 32 bits of sector #
|
||
|
push es
|
||
|
push bx ; transfer address
|
||
|
push dword ptr 10010h ; transfer 1 sector, packet size = 16
|
||
|
|
||
|
cmp byte ptr [bp].GotXint13Offset,0 ; have xint13?
|
||
|
jnz xint13ok ; yes, do the read
|
||
|
mov ah,41h
|
||
|
mov bx,055aah
|
||
|
mov dl,[bp].bgbsDriveNumber
|
||
|
int 13h ; check availability
|
||
|
jc xint13err ; error from int13 means no xint13
|
||
|
cmp bx,0aa55h ; absence of sig means no xint13
|
||
|
jne xint13err
|
||
|
test cl,1 ; bit 0 off means no xint13
|
||
|
jz xint13err
|
||
|
inc byte ptr [bp].GotXint13Offset ; have xint13, remember for next time
|
||
|
|
||
|
xint13ok:
|
||
|
mov ah,42h ; extended read
|
||
|
mov dl,[bp].bgbsDriveNumber ; dl = int13 unit #
|
||
|
mov si,sp ; ds:si -> param packet
|
||
|
int 13h ; perform the read
|
||
|
db 0b0h ; HACK: avoid stc by making next
|
||
|
; byte part of mov al,xx instruction
|
||
|
xint13err:
|
||
|
stc ; this instruction MUST follow previous byte!
|
||
|
pop eax ; throw away param packet without
|
||
|
pop eax ; clobbering carry flag
|
||
|
pop eax
|
||
|
pop eax
|
||
|
|
||
|
jmp short did_read
|
||
|
|
||
|
stdint13:
|
||
|
;
|
||
|
; Read via conventional int13
|
||
|
;
|
||
|
xor edx,edx ; edx:eax = absolute sector number
|
||
|
movzx ecx,[bp].bgbsBPB.oldBPB.BPB_SectorsPerTrack ; ecx = sectors per track
|
||
|
div ecx ; eax = track, edx = sector within track (0-62)
|
||
|
inc dl ; dl = sector within track (1-63)
|
||
|
mov cl,dl ; cl = sector within track
|
||
|
mov edx,eax
|
||
|
shr edx,16 ; dx:ax = track
|
||
|
div [bp].bgbsBPB.oldBPB.BPB_Heads ; ax = cylinder (0-1023), dx = head (0-255)
|
||
|
xchg dl,dh ; dh = head
|
||
|
mov dl,[bp].bgbsDriveNumber ; dl = int13 unit #
|
||
|
mov ch,al ; ch = bits 0-7 of cylinder
|
||
|
shl ah,6
|
||
|
or cl,ah ; bits 6-7 of cl = bits 8-9 of cylinder
|
||
|
mov ax,201h ; read 1 sector
|
||
|
int 13h
|
||
|
|
||
|
did_read:
|
||
|
ENDIF
|
||
|
popad
|
||
|
|
||
|
jc DiskError
|
||
|
|
||
|
add bx,SECTOR_SIZE ; advance transfer address
|
||
|
inc eax ; next sector number
|
||
|
dec cx ; loop instruction is out of range,
|
||
|
jnz DoRead ; have to do it manually
|
||
|
ret
|
||
|
.8086
|
||
|
|
||
|
Public WinBoot ; System boot file (11 bytes)
|
||
|
WinBoot DB "NTLDR "
|
||
|
|
||
|
;
|
||
|
; Message table.
|
||
|
;
|
||
|
; We put English messages here as a placeholder only, so that in case
|
||
|
; anyone uses bootf32.h without patching new messages in, things will
|
||
|
; still be correct (in English, but at least functional).
|
||
|
;
|
||
|
.errnz ($-$START) GT 1ACH
|
||
|
ORG ORIGIN + 01ACH ; shift message to coincide with that in FAT
|
||
|
; this will help driver to think the MBR
|
||
|
; is empty when dealing with FAT32 superfloppy
|
||
|
include msgstub.inc
|
||
|
|
||
|
;
|
||
|
; Now build a table with the low byte of the offset to each message.
|
||
|
; Code that patches the boot sector messages updates this table.
|
||
|
;
|
||
|
.errnz ($-$START) GT (SECTOR_SIZE-7)
|
||
|
ORG ORIGIN + SECTOR_SIZE - 7
|
||
|
MSGOFF_NOSYS:
|
||
|
db OFFSET (MSG_NOSYS - ORIGIN) - 256
|
||
|
MSGOFF_IOERROR:
|
||
|
db OFFSET (MSG_IOERROR - ORIGIN) - 256
|
||
|
MSGOFF_COMMON:
|
||
|
db OFFSET (MSG_COMMON - ORIGIN) - 256
|
||
|
|
||
|
ORG ORIGIN + (SECTOR_SIZE - 4)
|
||
|
DD BOOTSECTRAILSIG ; Boot sector signature (4 bytes)
|
||
|
|
||
|
|
||
|
.errnz ($-$START) NE SECTOR_SIZE
|
||
|
SecndSecStart label byte
|
||
|
;
|
||
|
; The second boot sector contains nothing but data. This sector is re-written
|
||
|
; by MS-DOS with a fairly high frequency due to changes made to the fsinfo
|
||
|
; structure. We don't want the actual boot code to get accidentally corrupted.
|
||
|
;
|
||
|
FSInfoSecSig label byte
|
||
|
.errnz ($-SecndSecStart) NE 0
|
||
|
DD SECONDBOOTSECSIG
|
||
|
|
||
|
db (SECTOR_SIZE - ($-FSInfoSecSig) - 4 - (SIZE BIGFATBOOTFSINFO)) DUP (0)
|
||
|
|
||
|
.errnz ($-SecndSecStart) NE (OFFSETFSINFOFRMSECSTRT)
|
||
|
|
||
|
fsinfo BIGFATBOOTFSINFO <FSINFOSIG,0FFFFFFFFh,000000002h>
|
||
|
|
||
|
.errnz ($-FSInfoSecSig) NE OFFSETTRLSIG
|
||
|
DD BOOTSECTRAILSIG ; Boot sector signature (4 bytes)
|
||
|
|
||
|
.errnz ($-$START) NE (SECTOR_SIZE * 2)
|
||
|
StrtThirdBootSector LABEL BYTE
|
||
|
|
||
|
DirRead:
|
||
|
.386
|
||
|
movzx eax,[BP].bgbsBPB.oldBPB.BPB_NumberOfFATs ; Determine sector dir starts on (NumFats)
|
||
|
mov ecx,dword ptr [BP].bgbsBPB.BGBPB_BigSectorsPerFat
|
||
|
mul ecx ; EAX = (NumFatSecs)
|
||
|
add EAX,dword ptr [BP].bgbsBPB.oldBPB.BPB_HiddenSectors ; (HiddenSecs)
|
||
|
movzx edx,[BP].bgbsBPB.oldBPB.BPB_ReservedSectors ;(ReservedSecs)
|
||
|
add EAX,EDX
|
||
|
;
|
||
|
; EAX = NumFats * NumFatSecs + ReservedSecs + cSecHid
|
||
|
; (first physical sector of cluster area)
|
||
|
;
|
||
|
mov dword ptr [BP].DataSecL,EAX
|
||
|
mov dword ptr [BP].CurrBuffFatSecL,0FFFFFFFFh
|
||
|
DirReRead:
|
||
|
mov eax,dword ptr [BP].bgbsBPB.BGBPB_RootDirStrtClus
|
||
|
cmp eax,2
|
||
|
jb NoSysMsg
|
||
|
cmp eax,00FFFFFF8h
|
||
|
jae NoSysMsg
|
||
|
|
||
|
; EAX is starting cluster of root directory
|
||
|
|
||
|
DirCluster:
|
||
|
push eax ; save starting cluster number
|
||
|
sub eax,2 ; Convert to 0 based cluster #
|
||
|
movzx EBX,[BP].bgbsBPB.oldBPB.BPB_SectorsPerCluster
|
||
|
mov si,bx ; Sector count to SI for sector loop
|
||
|
mul EBX ; compute logical sector in EAX
|
||
|
add EAX,dword ptr [BP].DataSecL ; Add data start bias
|
||
|
DirSector:
|
||
|
mov BX,ORIGIN+(SIZEBIGBOOTINSEC*SECTOR_SIZE)
|
||
|
mov DI,BX ; save address in DI for comparisons
|
||
|
mov CX,1
|
||
|
call DoRead ; doesn't return if error
|
||
|
; relies on return cx=0
|
||
|
DirEntry:
|
||
|
cmp byte ptr [di],ch ; empty, NUL directory entry?
|
||
|
je short MissingFile ; yes, that's the end
|
||
|
|
||
|
mov CL,11
|
||
|
push SI
|
||
|
mov si,offset WinBoot
|
||
|
repz cmpsb ; see if the same
|
||
|
pop SI
|
||
|
jz short DoLoad ; if so, continue booting
|
||
|
|
||
|
add DI,CX ; Finish advance to end of name field
|
||
|
add DI,DIR_ENTRY_SIZE-11 ; Next dir entry
|
||
|
cmp DI,BX ; exhausted this root dir sector yet?
|
||
|
jb DirEntry ; no, check next entry
|
||
|
dec SI ; decrement # dir sectors
|
||
|
jnz DirSector ; More dir sectors in this cluster
|
||
|
pop eax ; recover current root dir cluster
|
||
|
call GetNextFatEntry
|
||
|
jc DirCluster ; Do next Root dir cluster
|
||
|
MissingFile:
|
||
|
add sp,4 ; Discard EAX saved on stack
|
||
|
jmp NoSysMsg
|
||
|
|
||
|
CurrentSegment dw BIO_SEG
|
||
|
;
|
||
|
; We now load NTLDR
|
||
|
;
|
||
|
; All we have to do is multiply the file's starting cluster
|
||
|
; (whose directory entry is at DS:DI-11) by sectors per cluster and
|
||
|
; add that to the disk's starting data sector. We read ntldr into
|
||
|
; 2000:0, and begin execution there.
|
||
|
;
|
||
|
DoLoad:
|
||
|
add sp,4 ; Discard DX:AX saved on stack above
|
||
|
mov si,[DI-11].DIR_FIRSTHIGH
|
||
|
mov di,[DI-11].DIR_FIRST ; SI:DI = NTLDR starting cluster
|
||
|
mov ax,si
|
||
|
shl eax,16
|
||
|
mov ax,di ; EAX = NTLDR starting cluster
|
||
|
cmp eax,2 ; valid cluster #?
|
||
|
jb NoSysMsg ; NO!
|
||
|
cmp eax,00FFFFFF8h
|
||
|
jae NoSysMsg ; NO!
|
||
|
|
||
|
ReadAcluster:
|
||
|
push eax ; save cluster number
|
||
|
sub eax,2 ; Subtract first 2 reserved clusters
|
||
|
movzx ecx,[BP].bgbsBPB.oldBPB.BPB_SectorsPerCluster ; ECX = Sectors per cluster (SecsPerClust)
|
||
|
mul ecx ; EAX = logical sector #
|
||
|
|
||
|
add eax,dword ptr [BP].DataSecL ; EAX = physical sector #
|
||
|
|
||
|
mov bx,BIO_OFFSET
|
||
|
push es
|
||
|
mov es,CurrentSegment ; ES:BX = destination for read
|
||
|
call DoRead ; read all sectors in cluster, doesn't return if error
|
||
|
pop es
|
||
|
pop eax ; recover current 0-based cluster#
|
||
|
shr bx,4 ; updated offset -> paragraphs
|
||
|
add CurrentSegment,bx ; update segment for next read
|
||
|
|
||
|
call GetNextFatEntry ; get 2-based successor cluster in EAX
|
||
|
jnc StartItUp ; if end of cluster chain reached
|
||
|
jc ReadACluster ; keep sucking up clusters
|
||
|
.8086
|
||
|
|
||
|
;
|
||
|
; NTLDR requires the following input conditions:
|
||
|
;
|
||
|
; DL = boot drive #
|
||
|
;
|
||
|
|
||
|
StartItUp:
|
||
|
mov DL,[BP].bgbsDriveNumber
|
||
|
ifdef NEC_98
|
||
|
lea si,[BP].bgbsBPB
|
||
|
pop bp ; Restore Partition Information
|
||
|
endif
|
||
|
jmp FAR PTR WINLOAD ; CRANK UP THE WINDOWS NT BOOT LOADER
|
||
|
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;
|
||
|
; GetNextFatEntry
|
||
|
;
|
||
|
; Given the last cluster found, this will return the next cluster
|
||
|
; of a chain of clusters. If the last cluster is (ffff)(f)ff8 - (ffff)(f)fff,
|
||
|
; then the final cluster has been loaded.
|
||
|
;
|
||
|
; INPUTS:
|
||
|
; EAX = CurrentCluster (0 based cluster #)
|
||
|
;
|
||
|
; OUTPUTS:
|
||
|
; EAX = Next cluster (2 based cluster #)
|
||
|
; Carry CLEAR if all done, SET if not
|
||
|
;
|
||
|
; USES:
|
||
|
; EAX,EBX,ECX,EDX,ESI,DI es
|
||
|
;
|
||
|
|
||
|
GetNextFatEntry PROC NEAR
|
||
|
|
||
|
; NOTE For following... FAT32 cluster numbers are 28 bits not 32,
|
||
|
; so we know the following multiply (shl DX:AX by 2) will never
|
||
|
; overflow into carry.
|
||
|
.386
|
||
|
shl eax,2
|
||
|
call GetFatSector
|
||
|
mov EAX,dword ptr ES:[DI+BX]
|
||
|
and EAX,0FFFFFFFh ; Mask to valid FAT32 cluster # bits
|
||
|
cmp EAX,00FFFFFF8h ; carry CLEAR if all done, SET if not
|
||
|
.8086
|
||
|
ret
|
||
|
|
||
|
GetNextFatEntry ENDP
|
||
|
|
||
|
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;
|
||
|
; GetFatSector
|
||
|
;
|
||
|
; Read the corresponding FAT sector into the second boot sector
|
||
|
;
|
||
|
; INPUTS:
|
||
|
; EAX == offset (from FAT entry 0) of entry to find
|
||
|
;
|
||
|
; OUTPUTS:
|
||
|
; ES:DI+BX -> corresponding FAT entry in the FAT sector
|
||
|
;
|
||
|
; USES:
|
||
|
; BX,EAX,ECX,EDX,DI
|
||
|
;
|
||
|
|
||
|
GetFatSector PROC NEAR
|
||
|
.386
|
||
|
mov DI,ORIGIN + SECTOR_SIZE
|
||
|
movzx ECX,[BP].bgbsBPB.oldBPB.BPB_BytesPerSector
|
||
|
xor edx,edx
|
||
|
div ECX ; EAX = Sector number, (E)DX = Offset
|
||
|
cmp EAX,dword ptr [BP].CurrBuffFatSecL ; The same fat sector?
|
||
|
je short SetRet ; Don't need to read it again
|
||
|
mov dword ptr [BP].CurrBuffFatSecL,EAX
|
||
|
add EAX,dword ptr [BP].bgbsBPB.oldBPB.BPB_HiddenSectors
|
||
|
movzx ecx,[BP].bgbsBPB.oldBPB.BPB_ReservedSectors
|
||
|
add eax,ecx ; Point at 1st (0th) FAT
|
||
|
movzx ebx,[BP].bgbsBPB.BGBPB_ExtFlags
|
||
|
and bx,BGBPB_F_ACTIVEFATMSK
|
||
|
jz short GotFatSec
|
||
|
cmp bl,[BP].bgbsBPB.oldBPB.BPB_NumberOfFATs
|
||
|
jae NoSysMsg
|
||
|
push dx ; Save offset of cluster in the FAT sec
|
||
|
mov ecx,eax ; Save FAT sector # in 0th FAT
|
||
|
mov eax,dword ptr [BP].bgbsBPB.BGBPB_BigSectorsPerFat
|
||
|
mul ebx ; EAX = Sector offset to active FAT
|
||
|
; from 0th FAT
|
||
|
add eax,ecx
|
||
|
pop dx
|
||
|
GotFatSec:
|
||
|
push dx ; Save offset of cluster in the FAT sec
|
||
|
mov BX,DI
|
||
|
mov CX,1
|
||
|
call DoRead ; do the disk read, doesn't return if error
|
||
|
pop dx
|
||
|
SetRet:
|
||
|
mov BX,DX ; set BX to the offset of the cluster
|
||
|
.8086
|
||
|
ret
|
||
|
|
||
|
GetFatSector ENDP
|
||
|
|
||
|
db ((SECTOR_SIZE - ($-StrtThirdBootSector)) - 4) DUP (0)
|
||
|
|
||
|
.errnz ($-StrtThirdBootSector) NE OFFSETTRLSIG
|
||
|
DD BOOTSECTRAILSIG ; Boot sector signature (4 bytes)
|
||
|
|
||
|
.errnz ($-$START) NE (SECTOR_SIZE * 3)
|
||
|
.errnz SIZEBIGBOOTINSEC NE 3
|
||
|
|
||
|
$BigEnd label byte
|
||
|
|
||
|
CODE ENDS
|
||
|
|
||
|
END
|