728 lines
28 KiB
NASM
728 lines
28 KiB
NASM
page ,132
|
|
title pinboot - Pinball boot loader
|
|
name pinboot
|
|
|
|
; The ROM in the IBM PC 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 07C00h.
|
|
;
|
|
; The boot code's sole resposiblity is to find NTLDR, load it at
|
|
; address 2000:0000, and then jump to it.
|
|
;
|
|
; The boot code understands the structure of the Pinball root directory,
|
|
; and is capable of reading files. There is no contiguity restriction.
|
|
;
|
|
; The boot sector does not understand the Pinball file system's hotfixing --
|
|
; there isn't enough room. So if NTLDR is hotfixed, we're out of luck.
|
|
;
|
|
|
|
MASM equ 1
|
|
.xlist
|
|
.286
|
|
include macro.inc
|
|
;
|
|
A_DEFINED equ 1 ; don't "extrn" A_xxxx functions
|
|
|
|
.386
|
|
include const.inc ;get the file system's headers.
|
|
include chain.inc
|
|
include misc.inc
|
|
include fnode.inc
|
|
include dir.inc
|
|
include superb.inc
|
|
.286
|
|
.list
|
|
|
|
DoubleWord struc
|
|
lsw dw ?
|
|
msw dw ?
|
|
DoubleWord ends
|
|
|
|
;
|
|
; The following are various segments used by the boot loader. The first
|
|
; two are the segments where the boot sector is initially loaded and where
|
|
; the boot sector is relocated to. The others are the static locations
|
|
; where the mini-FSD and OS2KRNL are loaded. There is no segment definition
|
|
; for where OS2LDR is loaded, since its position is variable (it comes right
|
|
; after the end of OS2KRNL).
|
|
;
|
|
|
|
BootSeg segment at 07c0h ; this is where the ROM loads us initially.
|
|
BootSeg ends
|
|
|
|
NewSeg segment at 0d00h ; this is where we'll relocate to.
|
|
NewSeg ends ; enough for 16 boot sectors +
|
|
; 4-sector scratch
|
|
; below where we'll load OS2KRNL.
|
|
|
|
LdrSeg segment at 2000h ; we want to load the loader at 2000:0000
|
|
LdrSeg ends
|
|
|
|
ScrOfs equ 0f800h - 0d000h ; offset of 2K scratch area.
|
|
|
|
MOVEDD macro dest, src ; macro to copy a doubleword memory variable.
|
|
mov ax, src.lsw
|
|
mov dest.lsw, ax
|
|
mov ax, src.msw
|
|
mov dest.msw, ax
|
|
ENDM
|
|
|
|
;/********************** START OF SPECIFICATIONS ************************/
|
|
;/* */
|
|
;/* SUBROUTINE NAME: pinboot */
|
|
;/* */
|
|
;/* DESCRIPTIVE NAME: Bootstrap loader */
|
|
;/* */
|
|
;/* FUNCTION: To load NTLDR into memory. */
|
|
;/* */
|
|
;/* NOTES: pinboot is loaded by the ROM BIOS (Int 19H) at */
|
|
;/* physical memory location 0000:7C00H. */
|
|
;/* pinboot runs in real mode. */
|
|
;/* This boot record is for Pinball file systems only. */
|
|
;/* Allocation information for NTLDR may not */
|
|
;/* exceed an FNODE. */
|
|
;/* */
|
|
;/* ENTRY POINT: pinboot */
|
|
;/* LINKAGE: Jump (far) from Int 19H */
|
|
;/* */
|
|
;/* INPUT: CS:IP = 0000:7C00H */
|
|
;/* SS:SP = 0030:00FAH (CBIOS dependent) */
|
|
;/* */
|
|
;/* EXIT-NORMAL: */
|
|
;/* DL = INT 13 drive number we booted from */
|
|
;/* Jmp to main in OS2LDR */
|
|
;/* */
|
|
;/* EXIT-ERROR: None */
|
|
;/* */
|
|
;/* EFFECTS: Pinball mini-FSD is loaded into the physical */
|
|
;/* memory location 000007C0H */
|
|
;/* NTLDR is loaded into the physical memory */
|
|
;/* location 00020000H */
|
|
;/* */
|
|
;/* MESSAGES: */
|
|
;/* A disk read error occurred. */
|
|
;/* The file NTLDR cannot be found. */
|
|
;/* Insert a system diskette and restart the system. */
|
|
;/* */
|
|
;/*********************** END OF SPECIFICATIONS *************************/
|
|
BootCode segment ;would like to use BootSeg here, but LINK flips its lid
|
|
assume cs:BootCode,ds:nothing,es:nothing,ss:nothing
|
|
|
|
org 0 ; start at beginning of segment, not 0100h.
|
|
|
|
public _pinboot
|
|
_pinboot proc far
|
|
jmp start
|
|
;
|
|
; The following is the default BPB for Pinball hard disks. It may
|
|
; be modified by FORMAT or SYS before being installed on the disk.
|
|
;
|
|
; Parameters such as Heads, SectorsPerTrack, and SectorsLong are
|
|
; set up for a 20MB hard disk, so that a binary image of this boot
|
|
; record may be written directly to a test hard disk without having
|
|
; to reformat the drive.
|
|
;
|
|
; Note that this is really just a place-holder--anyone who writes
|
|
; the boot code should preserve the volume's existing BPB.
|
|
;
|
|
Version db "IBM 10.2"
|
|
BPB label byte
|
|
BytesPerSector dw SECSIZE ; Size of a physical sector
|
|
SectorsPerCluster db 4 ; Sectors per allocation unit
|
|
ReservedSectors dw 1 ; Number of reserved sectors
|
|
Fats db 2 ; Number of fats
|
|
DirectoryEntries dw 0200h ; Number of directory entries
|
|
Sectors dw 0 ; No. of sectors - no. of hidden sectors
|
|
Media db 0f8h ; Media byte
|
|
FatSectors dw 0029h ; Number of fat sectors
|
|
SectorsPerTrack dw 17 ; Sectors per track
|
|
Heads dw 4 ; Number of surfaces
|
|
HiddenSectors dd 0011h ; Number of hidden sectors
|
|
SectorsLong dd 0a2c3h ; Number of sectors iff Sectors = 0
|
|
;
|
|
; The following is the rest of the Extended BPB for the volume.
|
|
; The position and order of DriveNumber and CurrentHead are especially
|
|
; important, since those two variables are loaded into a single 16-bit
|
|
; register for the BIOS with one instruction.
|
|
;
|
|
DriveNumber db 80h ; Physical drive number (0 or 80h)
|
|
CurrentHead db ? ; Variable to store current head number
|
|
|
|
Signature db 28h ; Signature Byte for bootsector
|
|
BootID dd 64d59c15h ; Boot ID field.
|
|
Boot_Vol_Label db 'C-DRIVE',0,0,0,0 ;volume label.
|
|
Boot_System_ID db 'HPFS ' ; Identifies the IFS that owns the vol.
|
|
;
|
|
; The following variables are not part of the Extended BPB; they're just
|
|
; scratch variables for the boot code.
|
|
;
|
|
SectorBase dd ? ; next sector to read
|
|
CurrentTrack dw ? ; current track
|
|
CurrentSector db ? ; current sector
|
|
SectorCount dw ? ; number of sectors to read
|
|
lsnSaveChild dd ? ; sector to continue directory search
|
|
|
|
;****************************************************************************
|
|
start:
|
|
;
|
|
; First of all, set up the segments we need (stack and data).
|
|
;
|
|
cli
|
|
xor ax, ax ; Set up the stack to just before
|
|
mov ss, ax ; this code. It'll be moved after
|
|
mov sp, 7c00h ; we relocate.
|
|
sti
|
|
|
|
mov ax, Bootseg ; Address our BPB with DS.
|
|
mov ds, ax
|
|
assume ds:BootCode
|
|
;
|
|
; Now read the 16-sector boot block into memory. Then jump to that
|
|
; new version of the boot block, starting in the second sector
|
|
; (after the bootrecord sig).
|
|
;
|
|
mov SectorBase.lsw, 0 ; read sector zero.
|
|
mov SectorBase.msw, 0
|
|
mov word ptr [SectorCount], SEC_SUPERB+4 ; read boot/superblock.
|
|
mov ax, NewSeg ; read it at NewSeg.
|
|
mov es, ax
|
|
sub bx, bx ; at NewSeg:0000.
|
|
call DoReadLL ; Call low-level DoRead routine
|
|
;
|
|
push NewSeg ; we'll jump to NewSeg:0200h.
|
|
push offset mainboot ; (the second sector).
|
|
ret ; "return" to the second sector.
|
|
_pinboot endp
|
|
|
|
;*******************************************************************************
|
|
;
|
|
; Low-level read routine that doesn't work across a 64k addr boundary.
|
|
;
|
|
; Read SectorCount sectors (starting at SectorBase) to es:bx.
|
|
;
|
|
; As a side effect, SectorBase is updated (but es:bx are not)
|
|
; and SectorCount is reduced to zero.
|
|
;
|
|
DoReadLL proc
|
|
push ax ; save important registers
|
|
push bx
|
|
push cx
|
|
push dx
|
|
push es
|
|
|
|
DoRead$Loop:
|
|
mov ax, SectorBase.lsw ; (DX:AX) = start sector of next track
|
|
mov dx, SectorBase.msw
|
|
add ax, HiddenSectors.lsw ; adjust for partition's base sector
|
|
adc dx, HiddenSectors.msw
|
|
div SectorsPerTrack ; (DX) = sector within track, (AX)=track
|
|
inc dl ; sector numbers are 1-based, not 0
|
|
mov CurrentSector, dl
|
|
xor dx, dx ; prepare for 32-bit divide
|
|
div Heads ; (DX) = head no., (AX) = cylinder
|
|
mov CurrentHead, dl
|
|
mov CurrentTrack, ax
|
|
|
|
; CurrentHead is the head for this next disk request
|
|
; CurrentTrack is the track for this next request
|
|
; CurrentSector is the beginning sector number for this request
|
|
;
|
|
; Compute the number of sectors that we may be able to read in a single ROM
|
|
; request.
|
|
;
|
|
mov ax, SectorsPerTrack ; could read up to this much
|
|
sub al, CurrentSector ; offset within this track
|
|
inc ax ; CurrentSector was 1-based
|
|
;
|
|
; AX is the number of sectors that we may read.
|
|
;
|
|
cmp ax, SectorCount ; do we need to read whole trk?
|
|
jbe DoRead$FullTrack ; yes we do.
|
|
mov ax, SectorCount ; no, read a partial track.
|
|
;
|
|
; AX is now the number of sectors that we SHOULD read.
|
|
;
|
|
DoRead$FullTrack:
|
|
push ax ; save sector count for later calc.
|
|
mov ah, 2 ; "read sectors"
|
|
mov dx, CurrentTrack ; at this cylinder
|
|
mov cl, 6
|
|
shl dh, cl ; high 2 bits of DH = bits 8,9 of DX
|
|
or dh, CurrentSector ; (DH)=cyl bits | 6-bit sector no.
|
|
mov cx, dx ; (CX)=cylinder/sector no. combination
|
|
xchg ch, cl ; in the right order
|
|
mov dx, word ptr DriveNumber ; drive to read from, head no.
|
|
int 13h ; call BIOS.
|
|
|
|
pop ax
|
|
jb BootErr$he ; If errors report
|
|
add SectorBase.lsw, ax ; increment logical sector position
|
|
adc SectorBase.msw, 0
|
|
sub SectorCount, ax ; exhausted entire sector run?
|
|
jbe DoRead$Exit ; yes, we're all done.
|
|
shl ax, 9 - 4 ; (AX)=paragraphs read from last track
|
|
mov dx, es ; (DX)=segment we last read at
|
|
add dx, ax ; (DX)=segment right after last read
|
|
mov es, dx ; (ES)=segment to read next track at
|
|
jmp DoRead$Loop
|
|
;
|
|
DoRead$Exit:
|
|
pop es
|
|
pop dx
|
|
pop cx
|
|
pop bx
|
|
pop ax
|
|
ret
|
|
|
|
DoReadLL endp
|
|
|
|
;****************************************************************************
|
|
;
|
|
; BootErr - print error message and hang the system.
|
|
;
|
|
BootErr proc
|
|
BootErr$fnf:
|
|
mov si,offset TXT_MSG_SYSINIT_FILE_NOT_FD +2
|
|
jmp short BootErr2
|
|
BootErr$he:
|
|
mov si,offset TXT_MSG_SYSINIT_BOOT_ERROR +2
|
|
BootErr2:
|
|
call BootErr$print
|
|
mov si,offset TXT_MSG_SYSINIT_INSER_DK +2
|
|
call BootErr$print
|
|
sti
|
|
jmp $ ;Wait forever
|
|
BootErr$print:
|
|
lodsb ; Get next character
|
|
cmp al, 0
|
|
je BootErr$Done
|
|
mov ah,14 ; Write teletype
|
|
mov bx,7 ; Attribute
|
|
int 10h ; Print it
|
|
jmp BootErr$print
|
|
BootErr$Done:
|
|
ret
|
|
BootErr endp
|
|
|
|
;****************************************************************************
|
|
include pinboot.inc ;suck in the message text
|
|
;
|
|
; Names of the files we look for. Each consists of a length byte
|
|
; followed by the filename as it should appear in a directory entry.
|
|
;
|
|
ntldr db 5, "NTLDR"
|
|
|
|
|
|
ReservedForFuture DB 22 dup(?) ;reserve remaining bytes to prevent NLS
|
|
;messages from using them
|
|
|
|
.errnz ($-_pinboot) GT (SECSIZE-2),<FATAL PROBLEM: first sector is too large>
|
|
|
|
org SECSIZE-2
|
|
db 55h,0aah
|
|
|
|
;****************************************************************************
|
|
;
|
|
; mainboot -
|
|
;
|
|
mainboot proc far
|
|
mov ax, cs ; get the new DS.
|
|
mov ds, ax
|
|
add ax, ((SEC_SUPERB + 4) * SECSIZE) / 16 ; address of scratch.
|
|
mov es, ax
|
|
mov ax, ds ; get DS again.
|
|
shl ax, 4 ; convert to an offset.
|
|
cli
|
|
mov sp, ax ; load new stack, just before boot code.
|
|
sti
|
|
;
|
|
; First find the root FNODE on disk and read it in.
|
|
;
|
|
mov bx, SEC_SUPERB * SECSIZE + SB_ROOT
|
|
MOVEDD SectorBase, [bx] ; SectorBase = sblk.SB_ROOT.
|
|
mov SectorCount, 1 ; it's one sector long.
|
|
sub bx, bx ; read at scratch segment:0.
|
|
call DoRead
|
|
;
|
|
; Now find the root DIRBLK on disk and save its address.
|
|
;
|
|
MOVEDD RootDB, es:[bx].FN_ALREC.AL_POF ; RootDB = f.FN_ALREC.AL_POF.
|
|
;
|
|
; Load NTLDR at 20000h.
|
|
;
|
|
mov si, offset ntldr ; point to name of NTLDR.
|
|
MOVEDD SectorBase, RootDB ; start at root dirblk
|
|
call FindFile
|
|
mov ax, LdrSeg ; load at this segment.
|
|
call LoadFile ; find it and load it.
|
|
;
|
|
; We've loaded NTLDR--jump to it. Jump to NTLDR. Note that NTLDR's segment
|
|
; address was stored on the stack above, so all we need to push is the offset.
|
|
;
|
|
; Before we go to NTLDR, set up the registers the way it wants them:
|
|
; DL = INT 13 drive number we booted from
|
|
;
|
|
|
|
mov dl, DriveNumber
|
|
mov ax,1000
|
|
mov es, ax ; we don't really need this
|
|
lea si, BPB
|
|
sub ax,ax
|
|
push LdrSeg
|
|
push ax
|
|
ret ; "return" to OS2LDR.
|
|
mainboot endp
|
|
|
|
;****************************************************************************
|
|
;
|
|
; DoRead - read SectorCount sectors into ES:BX starting from sector
|
|
; SectorBase.
|
|
;
|
|
; NOTE: This code WILL NOT WORK if ES:BX does not point to an address whose
|
|
; physical address (ES * 16 + BX) MOD 512 != 0.
|
|
;
|
|
; DoRead adds to ES rather than BX in the main loop so that runs longer than
|
|
; 64K can be read with a single call to DoRead.
|
|
;
|
|
; Note that DoRead (unlike DoReadLL) saves and restores SectorCount
|
|
; and SectorBase
|
|
;
|
|
DoRead proc
|
|
push ax ; save important registers
|
|
push bx
|
|
push cx
|
|
push dx
|
|
push es
|
|
push SectorCount ; save state variables too
|
|
push SectorBase.lsw
|
|
push SectorBase.msw
|
|
;
|
|
; Calculate how much we can read into what's left of the current 64k
|
|
; physical address block, and read it.
|
|
;
|
|
;
|
|
mov ax,bx
|
|
|
|
shr ax,4
|
|
mov cx,es
|
|
add ax,cx ; ax = paragraph addr
|
|
|
|
;
|
|
; Now calc maximum number of paragraphs that we can read safely:
|
|
; 4k - ( ax mod 4k )
|
|
;
|
|
|
|
and ax,0fffh
|
|
sub ax,1000h
|
|
neg ax
|
|
|
|
;
|
|
; Calc CX = number of paragraphs to be read
|
|
;
|
|
mov cx,SectorCount ; convert SectorCount to paragraph cnt
|
|
shl cx,9-4
|
|
|
|
DoRead$Loop64:
|
|
push cx ; save cpRead
|
|
|
|
cmp ax,cx ; ax = min(cpReadSafely, cpRead)
|
|
jbe @F
|
|
mov ax,cx
|
|
@@:
|
|
push ax
|
|
;
|
|
; Calculate new SectorCount from amount we can read
|
|
;
|
|
shr ax,9-4
|
|
mov SectorCount,ax
|
|
|
|
call DoReadLL
|
|
|
|
pop ax ; ax = cpActuallyRead
|
|
pop cx ; cx = cpRead
|
|
|
|
sub cx,ax ; Any more to read?
|
|
jbe DoRead$Exit64 ; Nope.
|
|
;
|
|
; Adjust ES:BX by amount read
|
|
;
|
|
mov dx,es
|
|
add dx,ax
|
|
mov es,dx
|
|
;
|
|
; Since we're now reading on a 64k byte boundary, cpReadSafely == 4k.
|
|
;
|
|
mov ax,01000h ; 16k paragraphs per 64k segment
|
|
jmp short DoRead$Loop64 ; and go read some more.
|
|
|
|
DoRead$Exit64:
|
|
pop SectorBase.msw ; restore all this crap
|
|
pop SectorBase.lsw
|
|
pop SectorCount
|
|
pop es
|
|
pop dx
|
|
pop cx
|
|
pop bx
|
|
pop ax
|
|
ret
|
|
DoRead endp
|
|
;****************************************************************************
|
|
;
|
|
; ReadScratch - reads a block of 4 sectors into the scratch area.
|
|
;
|
|
; ENTRY: SectorBase = LSN to read.
|
|
;
|
|
; EXIT: 4 sectors at AX read at BootSeg:ScrOfs
|
|
;
|
|
; USES: all
|
|
;
|
|
ReadScratch proc near
|
|
push es
|
|
push bx
|
|
mov word ptr SectorCount, 4 ; read 4 sectors.
|
|
push ds ; address scratch area.
|
|
pop es
|
|
mov bx, ScrOfs ; with ES:BX.
|
|
call DoRead
|
|
pop bx
|
|
pop es
|
|
ret
|
|
ReadScratch endp
|
|
;****************************************************************************
|
|
;
|
|
; FindFile - finds a file in the root directory
|
|
;
|
|
; ENTRY: DS:SI -> name of file to find.
|
|
; SectorBase = LSN of first DirBlk to read
|
|
;
|
|
; EXIT: ES:BX -> dirent of file
|
|
; SectorBase = lsn of current DirBlk (for next directory search)
|
|
;
|
|
; USES: all
|
|
;
|
|
FindFile proc near
|
|
push ds
|
|
pop es ; address data with ES too.
|
|
call ReadScratch ; read DirBlk (SectorBase already set)
|
|
sub cx, cx ; prepare to store name length.
|
|
mov cl, [si] ; fetch the length byte.
|
|
inc si ; and skip to the name.
|
|
mov dx, cx ; save a copy of it.
|
|
|
|
ff1: mov bx, DB_START + ScrOfs ; point to first DIRENT, in scratch.
|
|
jmp short ff12
|
|
;
|
|
; bx -> last entry examined
|
|
; cx = length of the name we're looking for
|
|
; si -> name we're looking for, without the count byte ("search name")
|
|
;
|
|
ff10: add bx, [bx].DIR_ELEN ; move to next entry.
|
|
call UpcaseName
|
|
|
|
ff12: mov ax, si ; save search name address.
|
|
mov cx, dx ; reload search name length.
|
|
lea di, [bx].DIR_NAMA ; point to current DIRENT name.
|
|
repe cmpsb ; compare bytes while equal.
|
|
mov si, ax ; restore search name address.
|
|
jne ff20 ; not equal, search on
|
|
;
|
|
; Looks like the names match, as far as we compared them. But if
|
|
; the current name was longer than the search name, we didn't compare
|
|
; them completely. Check the lengths.
|
|
;
|
|
cmp dl, [bx].DIR_NAML
|
|
jne ff20 ; not equal, try downpointer if any
|
|
|
|
ret ; equal - Found the file
|
|
|
|
|
|
; Names don't match. If the current entry has a downpointer,
|
|
; search it.
|
|
;
|
|
ff20: test byte ptr [bx].DIR_FLAG, DF_BTP
|
|
jz ff30 ; no downpointer, check for end
|
|
|
|
; Follow the DownPointer.
|
|
; Load the child DIRBLK and search it.
|
|
;
|
|
add bx, [bx].DIR_ELEN ; move to next entry.
|
|
MOVEDD SectorBase, [bx-4] ; fetch last 4 bytes of prev entry.
|
|
call ReadScratch ; read child DIRBLK
|
|
jmp short ff1 ; search this dirblk
|
|
|
|
;
|
|
; We don't have a downpointer.
|
|
; If this is the end entry in the dirblk, then we have to go up to the parent,
|
|
; if any.
|
|
|
|
ff30: test byte ptr [bx].DIR_FLAG, DF_END
|
|
jz ff10 ; not end of dirblk - check next DirEnt
|
|
;
|
|
; Check to see if we have a parent (not the top block). If so, read
|
|
; the parent dirblk and find the downpointer that matches the current
|
|
; sector. Then continue searching after that point.
|
|
;
|
|
mov bx, ScrOfs ; point to dirblk header
|
|
test byte ptr [bx].DB_CCNT, 1 ; 1 means top block
|
|
jz ff40 ; not top, continue with parent
|
|
jmp FileNotFound ; top block - not found
|
|
|
|
;
|
|
; read in parent dirblk and find the dirent with this downpointer -
|
|
; then continue after that point
|
|
;
|
|
ff40: MOVEDD lsnSaveChild, SectorBase ; save this sector number
|
|
MOVEDD SectorBase, [bx].DB_PAR
|
|
call ReadScratch ; read the parent
|
|
|
|
mov bx, DB_START + ScrOfs ; start at first entry of child
|
|
jmp short ff44
|
|
|
|
; find our current downpointer
|
|
|
|
ff42: add bx, di ; move to the next dirent
|
|
|
|
ff44: mov di, [bx].DIR_ELEN ; downptr is 4 bytes from end of dirent
|
|
mov ax, [bx+di-4].lsw
|
|
cmp ax, lsnSaveChild.lsw ; compare low 2 bytes
|
|
jne ff42 ; not equal, try next DirEnt
|
|
mov ax, [bx+di-4].msw
|
|
cmp ax, lsnSaveChild.msw ; compare high 2 bytes
|
|
jne ff42 ; not equal, try next DirEnt
|
|
|
|
jmp ff30 ; continue from here
|
|
|
|
FindFile endp
|
|
;****************************************************************************
|
|
;
|
|
; LoadFile - reads file in at the specified segment.
|
|
;
|
|
; ENTRY: ES:BX -> fnode of file to load
|
|
; AX = segment address to load at.
|
|
;
|
|
; USES: all
|
|
;
|
|
LoadFile proc near
|
|
push ax ; save segment to load at.
|
|
;
|
|
; Here, we have found the file we want to read. Fetch relevant info
|
|
; out of the DIRENT: the file's FNODE number and its size in bytes.
|
|
;
|
|
sub bp, bp ; a zero register is handy.
|
|
MOVEDD FileSize, [bx].DIR_SIZE ; get file size
|
|
MOVEDD SectorBase, [bx].DIR_FN ; prepare to read FNODE
|
|
call ReadScratch ; read in the FNODE
|
|
;
|
|
pop es ; restore segment to read at.
|
|
mov si, ScrOfs + FN_ALREC ; address the FNODE's array.
|
|
mov bx, ScrOfs + FN_AB ; address the FNODE's ALBLK.
|
|
|
|
lf_go:
|
|
test byte ptr [bx].AB_FLAG, ABF_NODE ; are records nodes?
|
|
jnz lf_donode ; yes, go get a child.
|
|
;
|
|
; Here, we have a leaf block. Loop through the ALLEAF records,
|
|
; reading each one's data run.
|
|
;
|
|
mov cl, [bx].AB_OCNT ; get count of leaf records.
|
|
mov ch, 0 ; zero-extend.
|
|
lf_loop:
|
|
MOVEDD SectorBase, [si].AL_POF ; load run start.
|
|
mov ax, word ptr [si].AL_LEN ; load run length.
|
|
mov SectorCount, ax
|
|
push bx ; save ALBLK pointer.
|
|
sub bx, bx ; read at ES:0000.
|
|
call DoRead
|
|
pop bx ; restore ALBLK pointer.
|
|
mov ax, es ; get segment we just used
|
|
shl SectorCount, 9 - 4 ; cvt sectors to paragraphs
|
|
add ax, SectorCount ; get new segment address
|
|
mov es, ax ; store new segadr in ES
|
|
add si, size ALLEAF ; point to next leaf
|
|
loop lf_loop ; go get another run
|
|
;
|
|
; Here, we've exhausted an array of records. If we exhausted the
|
|
; FNODE, we're done. Otherwise, we re-read our parent block, restore
|
|
; where we were in it, and advance to the next record.
|
|
;
|
|
lf_blockdone:
|
|
cmp word ptr ds:[ScrOfs+FN_SIG+2], FNSIGVAL shr 16 ; in FNODE?
|
|
je lf_alldone ; yes, we've read the whole file.
|
|
MOVEDD SectorBase, ds:[ScrOfs+AS_RENT] ; fetch parent sector pointer.
|
|
call ReadScratch ; read in our parent.
|
|
pop si ; restore where we left off.
|
|
pop bx ; restore ALBLK pointer.
|
|
add si, size ALNODE ; move to next node.
|
|
;
|
|
; Here the block contains downpointers. Read in the next child
|
|
; block and process it as a node or leaf block, saving where we were
|
|
; in the current block.
|
|
;
|
|
lf_donode:
|
|
mov al, [bx].AB_OCNT ; get number of records.
|
|
mov ah, 0 ; zero-extend.
|
|
shl ax, 3 ; (AX)=size of array.
|
|
add ax, bx
|
|
add ax, size ALBLK ; (AX)->after end of array.
|
|
cmp si, ax ; are we done?
|
|
jae lf_blockdone ; yes, we've exhausted this blk.
|
|
push bx ; save ALBLK offset.
|
|
push si ; save current record offset.
|
|
MOVEDD SectorBase, [si].AN_SEC ; get child downpointer.
|
|
call ReadScratch ; read the child ALSEC.
|
|
mov si, size ALSEC + ScrOfs ; address the ALSEC's array.
|
|
mov bx, AS_ALBLK + ScrOfs ; address the ALSEC's ALBLK.
|
|
jmp short lf_go
|
|
;
|
|
; All done, return to caller.
|
|
;
|
|
lf_alldone:
|
|
ret
|
|
LoadFile endp
|
|
|
|
;****************************************************************************
|
|
;
|
|
; UpcaseName - Converts the name of the file to all upper-case
|
|
;
|
|
; ENTRY: ES:BX -> dirent of file
|
|
;
|
|
; USES: CX, DI
|
|
;
|
|
UpcaseName proc near
|
|
mov cl,[bx].DIR_NAML
|
|
xor ch,ch ; (cx) = # of bytes in name
|
|
lea di, [bx].DIR_NAMA ; (es:di) = pointer to start of name
|
|
UN10:
|
|
cmp byte ptr es:[di], 'Z' ; Is letter lowercase?
|
|
jbe UN20
|
|
|
|
sub byte ptr es:[di], 'a'-'A' ; Yes, convert to uppercase
|
|
UN20:
|
|
inc di
|
|
loop UN10
|
|
|
|
ret
|
|
UpcaseName endp
|
|
|
|
FileNotFound:
|
|
jmp BootErr$fnf
|
|
|
|
;******************************************************************************
|
|
RootDB dd ? ; LSN of root DIRBLK.
|
|
|
|
Flag db ? ; used to store AB_FLAG.
|
|
|
|
AllocInfo db size ALLEAF * ALCNT dup (0) ; copy of FNODE alloc info.
|
|
|
|
FileSize dd ? ; size of file that was read.
|
|
|
|
|
|
.errnz ($-_pinboot) GT (SEC_SUPERB*SECSIZE),<FATAL PROBLEM: main boot record exceeds available space>
|
|
|
|
org SEC_SUPERB*SECSIZE
|
|
|
|
BootCode ends
|
|
|
|
end _pinboot
|