windows-nt/Source/XPSP1/NT/base/boot/bootcode/fat/i386/fatnec98.asm

576 lines
17 KiB
NASM
Raw Normal View History

2020-09-26 03:20:57 -05:00
;++
;
;Module Name:
;
; fatnec98.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 1FC00h or 1FE00h.
; This start address is decided by boot device.
;
; The code in this sector is responsible for locating NTLDR and
; for placing the directory sector with this information at DirSeg:0.
; After loading in this sector, it reads in the entirety of the BIOS at
; BiosSeg:0 and does a long jump to the entry point at BiosSeg:0.
;
; There are no error message during execution of this code.
; In case of NTLDR does not exist or BIOS read error, will sound beep.
;
; 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
; FAT file system
;
;Revision History:
;
; 6/12/97 Tochizawa(NEC) support booting from 1.44MB FD
;
;--
page ,132
title boot - NTLDR FAT loader
name fatboot
.8086
DIR_ENT struc
Filename db 11 dup(?)
Attribute db ?
Reserved db 10 dup(?)
Time dw 2 dup(?)
StartCluster dw ?
FileSize dd ?
DIR_ENT ends
;
; This is the structure used to pass all shared data between the boot sector
; and NTLDR.
;
SHARED struc
ReadClusters dd ? ; function pointer
ReadSectors dd ? ; function pointer
SectorBase dd ? ; starting sector
SHARED ends
DoubleWord struc
lsw dw ?
msw dw ?
DoubleWord ends
SectorSize equ 512 ; sector size
HiRes equ 00001000b
DAFloppy equ 00010000b ; floppy bit in DA
DASCSI equ 00100000b ; SCSI bit in DA
SYS_PORT equ 37h
BootSeg segment at 1fc0h
BootSeg ends
SysSeg segment at 0040h
org 100h
BIOS_FLAG label byte
org 184h
BootDrive98 label byte
SysSeg ends
DirSeg segment at 1000h
DirSeg ends
NtLdrSeg segment at 2000h
NtLdrSeg ends
BootCode segment ;would like to use BootSeg here, but LINK flips its lid
ASSUME CS:BootCode,DS:NOTHING,ES:NOTHING,SS:NOTHING
public FATBOOT
FATBOOT proc far
jmp short Start
nop
;
; THE FOLLOWING DATA CONFIGURES THE BOOT PROGRAM
; FOR ANY TYPE OF DRIVE OR HARDFILE
;
; Note that this data is just a place-holder here. The actual values will
; be filled in by FORMAT or SYS. When installing the boot sector, only the
; code following the BPB (from Start to the end) should be copied into the
; first sector.
;
OsName db "MSWIN"
OsVersion db "4.1"
BPB label byte
BytesPerSector dw SectorSize ; Size of a physical sector
SectorsPerCluster db 8 ; Sectors per allocation unit
ReservedSectors dw 4 ; Number of reserved sectors
Fats db 2 ; Number of fats
DirectoryEntries dw 0c00h ; Number of directory entries
Sectors dw 0 ; No. of sectors - no. of hidden sectors
Media db 0F8H ; Media byte
FatSectors dw 70h ; Number of fat sectors
SectorsPerTrack dw 23h ; Sectors per track
Heads dw 7 ; Number of surfaces
HiddenSectors dd 0f5h ; Number of hidden sectors
SectorsLong dd 369c4h ; Number of sectors iff Sectors = 0
;
; The following byte is NOT part of the BPB but is set by SYS and format
; We should NOT change its position.
;
; keep order of DriveNumber and CurrentHead!
DriveNumber db 80h ; Physical drive number (0 or 80h)
CurrentHead db 0 ; Unitialized
Signature db 41 ; Signature Byte for bootsector
BootID dd 0 ; Boot ID field.
Boot_Vol_Label db "NO NAME "
Boot_System_ID db 'FAT12 ' ;
;
; We need a shred data area with ntldr/setupldr.
; But we don't have memory for using data area. Because bootcode size is 512KB.
; There is nothing for it but to use this area.
;
NtldrSharedData label dword
Start:
assume DS:nothing,ES:nothing
xor ax, ax ; Setup the stack to a known good spot
mov ss, ax
mov sp, 7c00h
push cs
pop ds
assume DS:BootCode
mov ax, SysSeg
mov es, ax
assume ES:SysSeg
mov al, BootDrive98
mov DriveNumber, al ; boot drive
push si ; save partition info
; We need some intialization.
mov si, BytesPerSector ; keep it in reg
test al, DAFloppy
jnz Flg00
; Boot is from hard disk.
; Our BPB tells us BytesPerSector, but it is a logical value
; that may differ from the physical value.
; We need SectorShiftFactor that is used to convert logical sector address
; to phisical sector address.
mov ah, 84h ; ROM BIOS sense command
xor bx, bx ; initialize bx to 0
int 1Bh ;
jc Flg01
test bx, bx ; physical sector length returned ?
jnz Flg02 ; if not,
Flg01:
mov bx, 256 ; we assume 256 bytes per sector
Flg02:
mov ax, si ; BytesPerSector
xor dx, dx
div bx
mov NtldrSharedData.SectorShiftFactor, ax
mov ax, 32*1024 ; 32KB
; xor dx, dx ; dx must be 0 here.
div si ; BytesPerSector
mov NtldrSharedData.LogicalSectorsIn32KByte, al
Flg00:
; The system is now prepared for us to begin reading. First, determine
; logical sector numbers of the start of the directory and the start of the
; data area.
;
; We read all the directory entries into DirSeg.
; It might be more than 64KB if hard disk.
mov ax, size DIR_ENT
mul DirectoryEntries ; (dx:ax) = # of bytes in directory
dec si ; (si) = BytesPerSector -1
add ax, si
adc dx, 0
inc si ; (si) = BytesPerSector
div si ; (ax) = # of sectors in directory
mov cx, ax ; save it in cx for later
mov al, Fats
xor ah, ah ; (ax) = # of FATs
mul FatSectors ; (ax) = # of FAT sectors
add ax, ReservedSectors ; (ax) = sector # directory starts
adc dx, 0
; save it in "Arguments" for later 'Read'
mov NtldrSharedData.Arguments.SectorBase.lsw, ax
mov NtldrSharedData.Arguments.SectorBase.msw, dx
add ax, cx ; next sector after directory
mov NtldrSharedData.ClusterBase, ax ; save it for later use
Rpt00:
push cx ; (cx) = # of sectors in directory
push si ; (si) = BytesPerSector
mov bx, DirSeg
mov es, bx
assume es:DirSeg
xor bx, bx ; (es:bx) -> directory segment
; NTLDR is supposed to occupy 1st 512 entries
; in the root directory.
xor dx, dx
mov ax, (512*32) ; (dx:ax) = 4000h
div si ; (si) = BytesPerSector
;
; DoRead does a RETF, but LINK pukes if we do a FAR call in a /tiny program.
;
; (ax) = # of sectors to read
;
push ax
push cs ; make retern frame
call DoRead ; read directory
pop ax
jc BootErr$he
; Now we scan for the presence of NTLDR
xor bx, bx ; offset
mov cx, 512 ; # of entries to search (only 512)
Rpt01:
mov di, bx ; (es:di) = filespec in directory entry
push cx
mov cx, 11 ; (cx) = filespec size
mov si, offset LOADERNAME ; (ds:si) = filespec we are searching
repe cmpsb
pop cx
je @F ; if matches, exit loop
add bx, size DIR_ENT ; else next entry
loop Rpt01
@@:
test cx, cx
pop si
pop cx
jnz Rpt02
add NtldrSharedData.Arguments.SectorBase.lsw, ax
adc NtldrSharedData.Arguments.SectorBase.msw, 0
sub cx, ax
ja Rpt00
jmp BootErr$bnf
Rpt02:
; We have found NTLDR
; We read it into NtLdrSeg.
mov dx, es:[bx].StartCluster ; (dx) = starting cluster number
mov ax, 1 ; (ax) = sectors to read
cmp BytesPerSector, 256
jne @F
inc ax ; We need to read 2 sectors
@@:
;
; Now, go read the file
;
mov bx, NtLdrSeg
mov es, bx
assume es:NtLdrSeg
xor bx, bx ; (es:bx) -> start of NTLDR
;
; LINK barfs if we do a FAR call in a TINY program, so we have to fake it
; out by pushing CS.
;
push dx ; save starting cluster number
push cs ; make retern frame
call ClusterRead
pop bx ; (bx) = Starting Cluster Number
jc BootErr$he
;
; NTLDR requires:
; BX = Starting Cluster Number of NTLDR
; DL = INT 1B drive number we booted from <- DA/UA in 98
; DS:SI -> the boot media's BPB
; DS:DI -> argument structure
; 1000:0000 - entire DIR is loaded
; On PC-9801 we have one more parameter...
; (bp) = partition info
;
lea si, BPB ; ds:si -> BPB
lea di, NtldrSharedData.Arguments ; ds:di -> Arguments
mov ax,ds
mov NtldrSharedData.Arguments.ReadClusters.msw, ax
mov NtldrSharedData.Arguments.ReadClusters.lsw, offset ClusterRead
mov NtldrSharedData.Arguments.ReadSectors.msw, ax
mov NtldrSharedData.Arguments.ReadSectors.lsw, offset DoRead
mov dl, DriveNumber ; dl = boot drive
pop bp ; partition info
;
; FAR JMP to 2000:0003. This is hand-coded, because I can't figure out
; how to make MASM do this for me. By entering NTLDR here, we skip the
; initial jump and execute the FAT-specific code to load the rest of
; NTLDR.
;
db 0EAh ; JMP FAR PTR
dw 3 ; 2000:3
dw 02000h
FATBOOT endp
;
; BootErr - print error message and hang the system.
;
BootErr proc
BootErr endp
; BootError - print error message and hang the system.
;
BootError proc
assume DS:BootCode,ES:nothing
;
; Am I display message that is reason of the ERROR ?
; Ofcourse. but no room available.
BootErr$bnf:
BootErr$he:
mov al, 06h
out 37h, al
jmp $
BootError endp
;
; ClusterRead - read AL sectors into ES:BX starting from
; cluster DX
;
ClusterRead proc
push ax ; (TOS) = # of sectors to read
dec dx
dec dx ; adjust for reserved clusters 0 and 1
mov al, SectorsPerCluster
xor ah, ah
mul dx ; DX:AX = starting sector number
add ax, NtldrSharedData.ClusterBase ; adjust for FATs, root dir, boot sec.
adc dx, 0
mov NtldrSharedData.Arguments.SectorBase.lsw, ax
mov NtldrSharedData.Arguments.SectorBase.msw, dx
pop ax ; (al) = # of sectors to read
;
; Now we've converted the cluster number to a SectorBase, so just fall
; through into DoRead
;
ClusterRead endp
;
; DoRead - read AL sectors into ES:BX starting from sector
; SectorBase.
;
DoRead proc
xor ah, ah
cmp ax, 0
jnz @F
inc ah ; read 256 Sectors
@@:
push si
push di
push bp
mov si, ax
mov bp, bx
mov cx, NtldrSharedData.Arguments.SectorBase.lsw ; Starting sector
mov dx, NtldrSharedData.Arguments.SectorBase.msw ; Starting sector
; (dx:cx) = logical sector # relative to partition
; (si) = # of sectors to read
; (es:bp) = read buffer
Rpt03:
push dx
push cx
; [sp] = logical sector number LSW
; [sp+2] = logical sector number MSW
mov al, DriveNumber
test al, DAFloppy
jz Flg03
; We are reading floppy disk.
; We don't care about DMA boundary, as no PC-9801 DOS floppy disk
; has more than 2048 directory entries, we never read
; more than 64 KB.
push ax
mov ax, cx ; (dx:ax) = lsn
mov bx, SectorsPerTrack
div bx ; (dx) = sector # (0-based)
; (ax) = cylinder # * # of heads + head #
div byte ptr Heads ;(ah) = head address
; (al) = cylinder address
mov dh, ah
mov cl, al
mov al, byte ptr BytesPerSector + 1 ; convert Bytes/Sector to
xor ch, ch ; SectorLengthID
whl00: ; Bytes/Sect | ID (ch)
cmp al, 0
jz @f
inc ch ; 128 -> 0
shr al, 1 ; 256 -> 1
jmp short whl00 ; 512 -> 2
@@: ; 1024 -> 3
pop ax
sub bl, dl ; sectors in THIS track
inc dl ; sector # (1-based)
; We have prepared following ROM BIOS parameters
; (al) = drive
; (cl) = cylinder address
; (ch) = sector size
; (dl) = sector address
; (dh) = head address
; (es:bp) -> buffer
;
; And we have some more.
; (bl) = # of sectors we can read with 1 time ROM call
; (si) = # of sectors we need to read
;
jmp short Flg04
Flg03:
; We are reading hard disk.
; We don't care about DMA boundary
; as length we read is 64KB max.
;
and al, 7FH ; drive #
mov bx,NtldrSharedData.SectorShiftFactor
Rpt04: ; logical sector address to
shr bx, 1
jc @F
shl cx, 1 ; physical sector address
rcl dx, 1
jmp short Rpt04
@@:
add cx, HiddenSectors.lsw
adc dx, HiddenSectors.msw ; (dx:cx) -> relative to drive
mov bl, NtldrSharedData.LogicalSectorsIn32KByte
; sectors we can read (32KB)
; this is not optimum on performance
; We have prepared following ROM BIOS parameters
; (al) = drive
; (dx:cx) = relative sector number
; (es:bp) -> buffer
;
; And we have some more.
; (bl) = # of sectors we can read with 1 time ROM call
; (si) = # of sectors we need to read
;
Flg04:
xor bh, bh
cmp si, bx
jnb @F
mov bx, si
@@:
push bx ; # of sectors we are reading this call
push dx
xchg ax, bx
mul BytesPerSector
xchg ax, bx
pop dx
push bx ; # of bytes.
; [sp] = # of bytes to read
; [sp+2] = # of sectors
; [sp+4] = logical sector number LSW
; [sp+6] = logical sector number MSW
mov ah, 01010110b ; read
int 1BH
pop di ; # of bytes
pop bx ; # of sectors
pop cx ; lsn (LSW)
pop dx ; lsn (MSW)
jc @F ; return Carry
; prepare for next call
add cx, bx ; advance lsn
adc dx, 0 ;
add bp, di ; advance offset
sub si, bx ; decrement # of sectors
jnz Rpt03 ; CarryFlag is Clear
@@:
pop bp
pop di
pop si
retf
DoRead endp
LOADERNAME DB "NTLDR "
; .errnz ($-FATBOOT) GT 510,<FATAL PROBLEM: boot sector is too large>
org 510
db 55h, 0aah
BootCode ends
;Unitialized variables go here--beyond the end of the boot sector in free memory
UninitializedWork struc
ClusterBase dw ? ; first sector of cluster # 2
SectorShiftFactor dw ?
LogicalSectorsIn32KByte db ?
Arguments db (size SHARED) dup (?) ; structure passed to NTLDR
UninitializedWork ends
END FATBOOT