summaryrefslogtreecommitdiffstats
path: root/private/ntos/boot/bootcode/ntfs
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/boot/bootcode/ntfs
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/ntos/boot/bootcode/ntfs')
-rw-r--r--private/ntos/boot/bootcode/ntfs/i386/ntfs.inc214
-rw-r--r--private/ntos/boot/bootcode/ntfs/i386/ntfsboot.asm2740
-rw-r--r--private/ntos/boot/bootcode/ntfs/i386/usa/bootntfs.h517
-rw-r--r--private/ntos/boot/bootcode/ntfs/i386/usa/ntfsboot.inc36
4 files changed, 3507 insertions, 0 deletions
diff --git a/private/ntos/boot/bootcode/ntfs/i386/ntfs.inc b/private/ntos/boot/bootcode/ntfs/i386/ntfs.inc
new file mode 100644
index 000000000..1782f4fc9
--- /dev/null
+++ b/private/ntos/boot/bootcode/ntfs/i386/ntfs.inc
@@ -0,0 +1,214 @@
+;
+; Copyright (c) 1991 Microsoft Corporation
+;
+; Module Name:
+;
+; ntfs.inc
+;
+; Abstract:
+;
+; This module contains declarations for the NTFS on-disk
+; structures needed by the boot code.
+;
+; Author:
+;
+; Bill McJohn (billmc) 12-May-1992
+;
+MASTER_FILE_TABLE_NUMBER EQU 0d
+MASTER_FILE_TABLE2_NUMBER EQU 1d
+LOG_FILE_NUMBER EQU 2d
+VOLUME_DASD_NUMBER EQU 3d
+ATTRIBUTE_DEF_TABLE_NUMBER EQU 4d
+ROOT_FILE_NAME_INDEX_NUMBER EQU 5d
+BIT_MAP_FILE_NUMBER EQU 6d
+BOOT_FILE_NUMBER EQU 7d
+BAD_CLUSTER_FILE_NUMBER EQU 8d
+QUOTA_TABLE_NUMBER EQU 9d
+UPCASE_TABLE_NUMBER EQU 10d
+
+$STANDARD_INFORMATION EQU 010h
+$ATTRIBUTE_LIST EQU 020h
+$FILE_NAME EQU 030h
+$VOLUME_VERSION EQU 040h
+$SECURITY_DESCRIPTOR EQU 050h
+$VOLUME_NAME EQU 060h
+$VOLUME_INFORMATION EQU 070h
+$DATA EQU 080h
+$INDEX_ROOT EQU 090h
+$INDEX_ALLOCATION EQU 0A0h
+$BITMAP EQU 0B0h
+$SYMBOLIC_LINK EQU 0C0h
+$EA_INFORMATION EQU 0D0h
+$EA_DATA EQU 0E0h
+$FIRST_USER_DEFINED_ATTRIBUTE EQU 0100h
+$END EQU 0FFFFFFFFh
+
+SEQUENCE_NUMBER_STRIDE EQU 512d
+
+
+LARGE_INTEGER struc
+ LowPart dd ?;
+ HighPart dd ?;
+LARGE_INTEGER ends;
+
+MFT_SEGMENT_REFERENCE struc
+ REF_LowPart dd ?;
+ REF_HighPart dw ?;
+ REF_SeqNo dw ?;
+MFT_SEGMENT_REFERENCE ends;
+
+MULTI_SECTOR_HEADER struc
+ MSH_Signature dd ?;
+ MSH_UpdateArrayOfs dw ?;
+ MSH_UpdateArraySize dw ?;
+MULTI_SECTOR_HEADER ends;
+
+FILE_RECORD_SEGMENT struc
+ FRS_Header db (size MULTI_SECTOR_HEADER) dup (?);
+ FRS_Lsn db (size LARGE_INTEGER) dup (?);
+ FRS_SequenceNumber dw ?;
+ FRS_ReferenceCount dw ?;
+ FRS_FirstAttribute dw ?;
+ FRS_Flags dw ?;
+ FRS_FirstFreeByte dd ?;
+ FRS_BytesAvailable dd ?;
+ FRS_BaseFRS db (size MFT_SEGMENT_REFERENCE) dup (?);
+ FRS_NextInstance dw ?;
+FILE_RECORD_SEGMENT ends;
+
+FILE_RECORD_SEGMENT_IN_USE EQU 0001h
+FILE_FILE_NAME_INDEX_PRESENT EQU 0002h
+
+ATTRIBUTE_RECORD struc
+ ATTR_TypeCode dd ?;
+ ATTR_RecordLength dd ?;
+ ATTR_FormCode db ?;
+ ATTR_NameLength db ?;
+ ATTR_NameOffset dw ?;
+ ATTR_Flags dw ?;
+ ATTR_Instance dw ?;
+ ATTR_FormUnion db ?;
+ATTRIBUTE_RECORD ends
+
+RESIDENT_ATTRIBUTE_FORM struc
+ RES_ValueLength dd ?;
+ RES_ValueOffset dw ?;
+ RES_ResidentFlags db ?;
+ RES_Reserved db ?;
+RESIDENT_ATTRIBUTE_FORM ends
+
+NONRESIDENT_ATTRIBUTE_FORM struc
+ NONRES_LowestVcn db (size LARGE_INTEGER) dup (?);
+ NONRES_HighestVcn db (size LARGE_INTEGER) dup (?);
+ NONRES_MappingPairOffset dw ?;
+ NONRES_Reserved dw 3 dup (?);
+ NONRES_AllocatedLength db (size LARGE_INTEGER) dup (?);
+ NONRES_FileSize db (size LARGE_INTEGER) dup (?);
+ NONRES_ValidDataLength db (size LARGE_INTEGER) dup (?);
+NONRESIDENT_ATTRIBUTE_FORM ends
+
+; Attribute Form Codes
+
+RESIDENT_FORM EQU 0
+NONRESIDENT_FORM EQU 1
+
+; Attribute Record Flag Values
+
+ATTRIBUTE_FLAG_COMPRESSION_MASK EQU 00FFh
+
+
+; Attribute list entry structure:
+;
+ATTRIBUTE_LIST_ENTRY struc
+ ATTRLIST_TypeCode dd ?;
+ ATTRLIST_Length dw ?;
+ ATTRLIST_NameLength db ?;
+ ATTRLIST_NameOffset db ?;
+ ATTRLIST_LowestVcn db (size LARGE_INTEGER) dup (?);
+ ATTRLIST_SegmentReference db (size MFT_SEGMENT_REFERENCE) dup (?);
+ ATTRLIST_Instance dw ?;
+ ATTRLIST_Name dw ?;
+ATTRIBUTE_LIST_ENTRY ends
+
+
+FAT_DIRENT_ATTR_READ_ONLY EQU 01h
+FAT_DIRENT_ATTR_HIDDEN EQU 02h
+FAT_DIRENT_ATTR_SYSTEM EQU 04h
+FAT_DIRENT_ATTR_VOLUME_ID EQU 08h
+FAT_DIRENT_ATTR_ARCHIVE EQU 20h
+FAT_DIRENT_ATTR_DEVICE EQU 40h
+
+DUPLICATED_INFORMATION struc
+ DUPINFO_CreationTime db (size LARGE_INTEGER) dup (?);
+ DUPINFO_LastModificationTime db (size LARGE_INTEGER) dup (?);
+ DUPINFO_LastChangeTime db (size LARGE_INTEGER) dup (?);
+ DUPINFO_LastAccessTime db (size LARGE_INTEGER) dup (?);
+ DUPINFO_AllocatedLength db (size LARGE_INTEGER) dup (?);
+ DUPINFO_FileSize db (size LARGE_INTEGER) dup (?);
+ DUPINFO_FileAttributes dd ?;
+ DUPINFO_PackedEaSize dw ?;
+DUPLICATED_INFORMATION ends
+
+
+FILE_NAME struc
+ FN_ParentDirectory db (size MFT_SEGMENT_REFERENCE) dup (?);
+ FN_Info db (size DUPLICATED_INFORMATION) dup (?);
+ FN_Pad dw ?;
+ FN_FileNameLength db ?; Length in chars
+ FN_Flags db ?;
+ FN_FileName dw ?; First char of name.
+FILE_NAME ends
+
+FILE_NAME_NTFS EQU 1
+FILE_NAME_DOS EQU 2
+FILE_NAME_LINK EQU 4
+
+
+INDEX_HEADER struc
+
+ IH_FirstIndexEntry dd ?;
+ IH_FirstFreeByte dd ?;
+ IH_BytesAvailable dd ?;
+ IH_Flags db ?; INDEX_xxx flags
+ IH_Reserved db 3 dup (?);
+INDEX_HEADER ends
+
+; INDEX_xxx flags
+
+INDEX_NODE EQU 1
+
+
+INDEX_ROOT struc
+
+ IR_IndexedAttributeType dd ?;
+ IR_CollationRule dd ?;
+ IR_BytesPerBuffer dd ?;
+ IR_ClustersPerBuffer db ?;
+ IR_Reserved db 3 dup (?);
+ IR_IndexHeader db (size INDEX_HEADER) dup (?);
+INDEX_ROOT ends;
+
+INDEX_ALLOCATION_BUFFER struc
+
+ IB_Header db (size MULTI_SECTOR_HEADER) dup (?);
+ IB_Lsn db (size LARGE_INTEGER) dup (?);
+ IB_ThisVcn db (size LARGE_INTEGER) dup (?);
+ IB_IndexHeader db (size INDEX_HEADER) dup (?);
+INDEX_ALLOCATION_BUFFER ends;
+
+
+INDEX_ENTRY struc
+
+ IE_FileReference db (size MFT_SEGMENT_REFERENCE) dup (?);
+ IE_Length dw ?;
+ IE_AttributeLength dw ?;
+ IE_Flags dw ?;
+ IE_Reserved dw ?;
+ IE_Value db ?;
+INDEX_ENTRY ends;
+
+
+; INDEX_ENTRY_xxx flags
+;
+INDEX_ENTRY_NODE EQU 1
+INDEX_ENTRY_END EQU 2
diff --git a/private/ntos/boot/bootcode/ntfs/i386/ntfsboot.asm b/private/ntos/boot/bootcode/ntfs/i386/ntfsboot.asm
new file mode 100644
index 000000000..a07e341e2
--- /dev/null
+++ b/private/ntos/boot/bootcode/ntfs/i386/ntfsboot.asm
@@ -0,0 +1,2740 @@
+ page ,132
+ title ntfsboot - NTFS boot loader
+ name ntfsboot
+
+; 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 NTFS root directory,
+; and is capable of reading files. There is no contiguity restriction.
+;
+
+MASM equ 1
+ .xlist
+ .286
+
+A_DEFINED EQU 1
+
+ include ntfs.inc
+
+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 third is the static location
+; where the NTLDR is loaded.
+;
+
+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 NTLDR.
+
+LdrSeg segment at 2000h ; we want to load the loader at 2000:0000
+LdrSeg ends
+
+;/********************** START OF SPECIFICATIONS ************************/
+;/* */
+;/* SUBROUTINE NAME: ntfsboot */
+;/* */
+;/* DESCRIPTIVE NAME: Bootstrap loader */
+;/* */
+;/* FUNCTION: To load NTLDR into memory. */
+;/* */
+;/* NOTES: ntfsboot is loaded by the ROM BIOS (Int 19H) at */
+;/* physical memory location 0000:7C00H. */
+;/* ntfsboot runs in real mode. */
+;/* This boot record is for NTFS volumes only. */
+;/* */
+;/* ENTRY POINT: ntfsboot */
+;/* 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 NTLDR */
+;/* */
+;/* EXIT-ERROR: None */
+;/* */
+;/* EFFECTS: 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 _ntfsboot
+_ntfsboot proc far
+ jmp start
+ .errnz ($-_ntfsboot) GT (3),<FATAL PROBLEM: JMP is more than three bytes>
+
+ org 3
+;
+; This is a template BPB--anyone who writes boot code to disk
+; should either preserve the existing BPB and NTFS information
+; or create it anew.
+;
+Version db "NTFS " ; Must be 8 characters
+BPB label byte
+BytesPerSector dw 0 ; Size of a physical sector
+SectorsPerCluster db 0 ; Sectors per allocation unit
+ReservedSectors dw 0 ; Number of reserved sectors
+Fats db 0 ; Number of fats
+DirectoryEntries dw 0 ; Number of directory entries
+Sectors dw 0 ; No. of sectors - no. of hidden sectors
+Media db 0 ; Media byte
+FatSectors dw 0 ; Number of fat sectors
+SectorsPerTrack dw 0 ; Sectors per track
+Heads dw 0 ; Number of surfaces
+HiddenSectors dd 0 ; Number of hidden sectors
+SectorsLong dd 0 ; Number of sectors iff Sectors = 0
+;
+; The following is the rest of the NTFS Sector Zero information.
+; 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 no.
+
+SectorZeroPad1 dw 0
+SectorsOnVolume db (size LARGE_INTEGER) dup (0)
+MftStartLcn db (size LARGE_INTEGER) dup (0)
+Mft2StartLcn db (size LARGE_INTEGER) dup (0)
+ClustersPerFrs dd 0
+DefClustersPerBuf dd 0
+SerialNumber db (size LARGE_INTEGER) dup (0)
+CheckSum dd 0
+;
+; 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
+
+;****************************************************************************
+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], 16 ; read boot area
+ 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.
+_ntfsboot 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:
+
+.386
+ mov eax, SectorBase
+ add eax, HiddenSectors
+ xor edx,edx
+;EDX:EAX = absolute sector number
+ movzx ecx,word ptr SectorsPerTrack ; get into 32 bit value
+ div ecx ; (EDX) = sector within track, (EAX)=track
+ inc dl ; sector numbers are 1-based, not 0
+ mov CurrentSector, dl
+ mov edx,eax
+ shr edx,16
+.286
+ 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 dh, CurrentHead
+ mov dl, 80h ; should be DriveNumber, but...
+
+ 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$ntc:
+ mov si,offset TXT_MSG_SYSINIT_NTLDR_CMPRS +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 ntfsboot.inc ;suck in the message text
+
+
+ReservedForFuture DB 2 dup(?) ;reserve remaining bytes to prevent NLS
+ ;messages from using them
+
+ .errnz ($-_ntfsboot) GT (512-2),<FATAL PROBLEM: first sector is too large>
+
+ org 512-2
+ db 55h,0aah
+
+; Name we look for. ntldr_length is the number of characters,
+; ntldr_name is the name itself. Note that it is not NULL
+; terminated, and doesn't need to be.
+;
+ntldr_name_length dw 5
+ntldr_name dw 'N', 'T', 'L', 'D', 'R'
+
+; Predefined name for index-related attributes associated with an
+; index over $FILE_NAME
+;
+index_name_length dw 4
+index_name dw '$', 'I', '3', '0'
+
+; Global variables. These offsets are all relative to NewSeg.
+;
+AttrList dd 0e000h; Offset of buffer to hold attribute list
+MftFrs dd 3000h; Offset of first MFT FRS
+SegmentsInMft dd ? ; number of FRS's with MFT Data attribute records
+RootIndexFrs dd ? ; Offset of Root Index FRS
+AllocationIndexFrs dd ? ; Offset of Allocation Index FRS ; KPeery
+BitmapIndexFrs dd ? ; Offset of Bitmap Index FRS ; KPeery
+IndexRoot dd ? ; Offset of Root Index $INDEX_ROOT attribute
+IndexAllocation dd ? ; Offset of Root Index $INDEX_ALLOCATION attribute
+IndexBitmap dd ? ; Offset of Root Index $BITMAP attribute
+NtldrFrs dd ? ; Offset of NTLDR FRS
+NtldrData dd ? ; Offset of NTLDR $DATA attribute
+IndexBlockBuffer dd ? ; Offset of current index buffer
+IndexBitmapBuffer dd ? ; Offset of index bitmap buffer
+NextBuffer dd ? ; Offset of next free byte in buffer space
+
+BytesPerCluster dd ? ; Bytes per cluster
+BytesPerFrs dd ? ; Bytes per File Record Segment
+SectorsPerFrs dd ? ; Sectors per File Record Segment
+BytesPerIndexBlock dd ? ; Bytes per index alloc block in root index
+ClustersPerIndexBlock dd ? ; Clusters per index alloc block in root index
+SectorsPerIndexBlock dd ? ; Sectors per index block in root index
+
+.386
+
+SAVE_ALL macro
+
+ push es
+ push ds
+ pushad
+
+endm
+
+RESTORE_ALL macro
+
+ popad
+ nop
+ pop ds
+ pop es
+
+endm
+
+
+;****************************************************************************
+;
+; mainboot -
+;
+;
+mainboot proc far
+
+; Get the new ds and the new stack. Note that ss is zero.
+;
+ mov ax, cs ; Set DS to CS
+ mov ds, ax
+
+ shl ax, 4 ; convert to an offset.
+ cli
+ mov sp, ax ; load new stack, just before boot code.
+ sti
+
+; Set up the FRS buffers. The MFT buffer is in a fixed
+; location, and the other three come right after it. The
+; buffer for index allocation blocks comes after that.
+;
+
+; Compute the useful constants associated with the volume
+;
+ movzx eax, BytesPerSector ; eax = Bytes per Sector
+ movzx ebx, SectorsPerCluster ; ebx = Sectors Per Cluster
+ mul ebx ; eax = Bytes per Cluster
+ mov BytesPerCluster, eax
+
+ mov ecx, ClustersPerFrs ; ecx = clusters per frs
+ cmp cl, 0 ; is ClustersPerFrs less than zero?
+ jg mainboot$1
+
+; If the ClustersPerFrs field is negative, we calculate the number
+; of bytes per FRS by negating the value and using that as a shif count.
+;
+
+ neg cl
+ mov eax, 1
+ shl eax, cl ; eax = bytes per frs
+ jmp mainboot$2
+
+mainboot$1:
+
+; Otherwise if ClustersPerFrs was positive, we multiply by bytes
+; per cluster.
+
+ mov eax, BytesPerCluster
+ mul ecx ; eax = bytes per frs
+
+mainboot$2:
+
+ mov BytesPerFrs, eax
+ movzx ebx, BytesPerSector
+ xor edx, edx ; zero high part of dividend
+ div ebx ; eax = sectors per frs
+ mov SectorsPerFrs, eax
+
+
+; Set up the MFT FRS's---this will read all the $DATA attribute
+; records for the MFT.
+;
+
+ call SetupMft
+
+; Set up the remaining FRS buffers. The RootIndex FRS comes
+; directly after the last MFT FRS, followed by the NTLdr FRS
+; and the Index Block buffer.
+;
+ mov ecx, NextBuffer
+ mov RootIndexFrs, ecx
+
+ add ecx, BytesPerFrs ; AllocationFrs may be different
+ mov AllocationIndexFrs, ecx ; from RootIndexFrs - KPeery
+
+ add ecx, BytesPerFrs ; BitmapFrs may be different
+ mov BitmapIndexFrs, ecx ; from RootIndexFrs - KPeery
+
+ add ecx, BytesPerFrs
+ mov NtldrFrs, ecx
+
+ add ecx, BytesPerFrs
+ mov IndexBlockBuffer, ecx
+
+;
+; Read the root index, allocation index and bitmap FRS's and locate
+; the interesting attributes.
+;
+
+ mov eax, $INDEX_ROOT
+ mov ecx, RootIndexFrs
+ call LoadIndexFrs
+
+ or eax, eax
+ jz BootErr$he
+
+ mov IndexRoot, eax ; offset in Frs buffer
+
+ mov eax, $INDEX_ALLOCATION ; Attribute type code
+ mov ecx, AllocationIndexFrs ; FRS to search
+ call LoadIndexFrs
+
+ mov IndexAllocation, eax
+
+ mov eax, $BITMAP ; Attribute type code
+ mov ecx, BitmapIndexFrs ; FRS to search
+ call LoadIndexFrs
+
+ mov IndexBitmap, eax
+
+; Consistency check: the index root must exist, and it
+; must be resident.
+;
+ mov eax, IndexRoot
+ or eax, eax
+ jz BootErr$he
+
+
+ cmp [eax].ATTR_FormCode, RESIDENT_FORM
+ jne BootErr$he
+
+
+; Determine the size of the index allocation buffer based
+; on information in the $INDEX_ROOT attribute. The index
+; bitmap buffer comes immediately after the index block buffer.
+;
+; eax -> $INDEX_ROOT attribute record
+;
+ lea edx, [eax].ATTR_FormUnion ; edx -> resident info
+ add ax, [edx].RES_ValueOffset ; eax -> value of $INDEX_ROOT
+
+ movzx ecx, [eax].IR_ClustersPerBuffer
+ mov ClustersPerIndexBlock, ecx
+
+ mov ecx, [eax].IR_BytesPerBuffer
+ mov BytesPerIndexBlock, ecx
+
+ mov eax, BytesPerIndexBlock
+ movzx ecx, BytesPerSector
+ xor edx, edx
+ div ecx ; eax = sectors per index block
+ mov SectorsPerIndexBlock, eax
+
+ mov eax, IndexBlockBuffer
+ add eax, BytesPerIndexBlock
+ mov IndexBitmapBuffer, eax
+
+; Next consistency check: if the $INDEX_ALLOCATION attribute
+; exists, the $INDEX_BITMAP attribute must also exist.
+;
+ cmp IndexAllocation, 0
+ je mainboot30
+
+ cmp IndexBitmap, 0 ; since IndexAllocation exists, the
+ je BootErr$he ; bitmap must exist, too.
+
+; Since the bitmap exists, we need to read it into the bitmap
+; buffer. If it's resident, we can just copy the data.
+;
+
+ mov ebx, IndexBitmap ; ebx -> index bitmap attribute
+ push ds
+ pop es
+ mov edi, IndexBitmapBuffer ; es:edi -> index bitmap buffer
+
+ call ReadWholeAttribute
+
+mainboot30:
+;
+; OK, we've got the index-related attributes.
+;
+ movzx ecx, ntldr_name_length ; ecx = name length in characters
+ mov eax, offset ntldr_name ; eax -> name
+
+ call FindFile
+
+ or eax, eax
+ jz BootErr$fnf
+
+; Read the FRS for NTLDR and find its data attribute.
+;
+; eax -> Index Entry for NTLDR.
+;
+ mov eax, [eax].IE_FileReference.REF_LowPart
+
+
+ push ds
+ pop es ; es:edi = target buffer
+ mov edi, NtldrFrs
+
+ call ReadFrs
+
+ mov eax, NtldrFrs ; pointer to FRS
+ mov ebx, $DATA ; requested attribute type
+ mov ecx, 0 ; attribute name length in characters
+ mov edx, 0 ; attribute name (NULL if none)
+
+ call LocateAttributeRecord
+
+; eax -> $DATA attribute for NTLDR
+;
+ or eax, eax ; if eax is zero, attribute not found.
+ jz BootErr$fnf
+
+; Get the attribute record header flags, and make sure none of the
+; `compressed' bits are set
+
+ movzx ebx, [eax].ATTR_Flags
+ and ebx, ATTRIBUTE_FLAG_COMPRESSION_MASK
+ jnz BootErr$ntc
+
+ mov ebx, eax ; ebx -> $DATA attribute for NTLDR
+
+ push LdrSeg
+ pop es ; es = segment addres to read into
+ sub edi, edi ; es:edi = buffer address
+
+ call ReadWholeAttribute
+
+;
+; We've loaded NTLDR--jump to it.
+;
+; 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
+ retf ; "return" to NTLDR.
+
+
+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
+;
+.286
+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
+
+.386
+;****************************************************************************
+;
+; ReadClusters - Reads a run of clusters from the disk.
+;
+; ENTRY: eax == LCN to read
+; edx == clusters to read
+; es:edi -> Target buffer
+;
+; USES: none (preserves all registers)
+;
+ReadClusters proc near
+
+ SAVE_ALL
+
+ mov ebx, edx ; ebx = clusters to read.
+ movzx ecx, SectorsPerCluster ; ecx = cluster factor
+
+ mul ecx ; Convert LCN to sectors (wipes out edx!)
+ mov SectorBase, eax ; Store starting sector in SectorBase
+
+ mov eax, ebx ; eax = number of clusters
+ mul ecx ; Convert EAX to sectors (wipes out edx!)
+ mov SectorCount, ax ; Store number of sectors in SectorCount
+
+
+; Note that ReadClusters gets its target buffer in es:edi but calls
+; the DoRead worker function that takes a target in es:bx--we need
+; to normalize es:edi so that we don't overflow bx.
+;
+ mov bx, di
+ and bx, 0Fh
+ mov ax, es
+ shr edi, 4
+ add ax, di ; ax:bx -> target buffer
+
+ push ax
+ pop es ; es:bx -> target buffer
+
+ call DoRead
+
+ RESTORE_ALL
+ ret
+
+ReadClusters endp
+
+;
+;****************************************************************************
+;
+; LocateAttributeRecord -- Find an attribute record in an FRS.
+;
+; ENTRY: EAX -- pointer to FRS
+; EBX -- desired attribute type code
+; ECX -- length of attribute name in characters
+; EDX -- pointer to attribute name
+;
+; EXIT: EAX points at attribute record (0 indicates not found)
+;
+; USES: All
+;
+LocateAttributeRecord proc near
+
+; get the first attribute record.
+;
+ add ax, word ptr[eax].FRS_FirstAttribute
+
+; eax -> next attribute record to investigate.
+; ebx == desired type
+; ecx == name length
+; edx -> pointer to name
+;
+lar10:
+ cmp [eax].ATTR_TypeCode, 0ffffffffh
+ je lar99
+
+ cmp dword ptr[eax].ATTR_TypeCode, ebx
+ jne lar80
+
+; this record is a potential match. Compare the names:
+;
+; eax -> candidate record
+; ebx == desired type
+; ecx == name length
+; edx -> pointer to name
+;
+ or ecx, ecx ; Did the caller pass in a name length?
+ jnz lar20
+
+; We want an attribute with no name--the current record is
+; a match if and only if it has no name.
+;
+ cmp [eax].ATTR_NameLength, 0
+ jne lar80 ; Not a match.
+
+; It's a match, and eax is set up correctly, so return.
+;
+ ret
+
+; We want a named attribute.
+;
+; eax -> candidate record
+; ebx == desired type
+; ecx == name length
+; edx -> pointer to name
+;
+lar20:
+ cmp cl, [eax].ATTR_NameLength
+ jne lar80 ; Not a match.
+
+; Convert name in current record to uppercase.
+;
+ mov esi, eax
+ add si, word ptr[eax].ATTR_NameOffset
+
+ call UpcaseName
+
+; eax -> candidate record
+; ebx == desired type
+; ecx == name length
+; edx -> pointer to name
+; esi -> Name in current record (upcased)
+;
+ push ecx ; save cx
+
+ push ds ; Copy data segment into es
+ pop es
+ mov edi, edx ; note that esi is already set up.
+
+ repe cmpsw ; zero flag is set if equal
+
+ pop ecx ; restore cx
+
+ jnz lar80 ; not a match
+
+; eax points at a matching record.
+;
+ ret
+
+;
+; This record doesn't match; go on to the next.
+;
+; eax -> rejected candidate attribute record
+; ebx == desired type
+; ecx == Name length
+; edx -> desired name
+;
+lar80: cmp [eax].ATTR_RecordLength, 0 ; if the record length is zero
+ je lar99 ; the FRS is corrupt.
+
+ add eax, [eax].ATTR_RecordLength; Go to next record
+ jmp lar10 ; and try again
+
+; Didn't find it.
+;
+lar99: sub eax, eax
+ ret
+
+LocateAttributeRecord endp
+
+;****************************************************************************
+;
+; LocateIndexEntry -- Find an index entry in a file name index
+;
+; ENTRY: EAX -> pointer to index header
+; EBX -> file name to find
+; ECX == length of file name in characters
+;
+; EXIT: EAX points at index entry. NULL to indicate failure.
+;
+; USES: All
+;
+LocateIndexEntry proc near
+
+; Convert the input name to upper-case
+;
+
+ mov esi, ebx
+ call UpcaseName
+
+; DEBUG CODE
+;
+; call PrintName
+; call Debug2
+;
+; END DEBUG CODE
+
+ add eax, [eax].IH_FirstIndexEntry
+
+; EAX -> current entry
+; EBX -> file name to find
+; ECX == length of file name in characters
+;
+lie10: test [eax].IE_Flags, INDEX_ENTRY_END ; Is it the end entry?
+ jnz lie99
+
+ lea edx, [eax].IE_Value ; edx -> FILE_NAME attribute value
+
+; DEBUG CODE -- list file names as they are examined
+;
+; SAVE_ALL
+;
+; call Debug3
+; movzx ecx, [edx].FN_FileNameLength ; ecx = chars in name
+; lea esi, [edx].FN_FileName ; esi -> name
+; call PrintName
+;
+; RESTORE_ALL
+;
+; END DEBUG CODE
+
+; EAX -> current entry
+; EBX -> file name to find
+; ECX == length of file name in characters
+; EDX -> FILE_NAME attribute
+;
+ cmp cl, [edx].FN_FileNameLength ; Is name the right length?
+ jne lie80
+
+ lea esi, [edx].FN_FileName ; Get name from FILE_NAME structure
+
+ call UpcaseName
+
+ push ecx ; save ecx
+
+ push ds
+ pop es ; copy data segment into es for cmpsw
+ mov edi, ebx ; edi->search name (esi already set up)
+ repe cmpsw ; zero flag is set if they're equal
+
+ pop ecx ; restore ecx
+
+ jnz lie80
+
+; the current entry matches the search name, and eax points at it.
+;
+ ret
+
+; The current entry is not a match--get the next one.
+; EAX -> current entry
+; EBX -> file name to find
+; ECX == length of file name in characters
+;
+lie80: cmp [eax].IE_Length, 0 ; If the entry length is zero
+ je lie99 ; then the index block is corrupt.
+
+ add ax, [eax].IE_Length ; Get the next entry.
+
+ jmp lie10
+
+
+; Name not found in this block. Set eax to zero and return
+;
+lie99: xor eax, eax
+ ret
+
+LocateIndexEntry endp
+
+;****************************************************************************
+;
+; ReadWholeAttribute - Read an entire attribute value
+;
+; ENTRY: ebx -> attribute
+; es:edi -> target buffer
+;
+; USES: ALL
+;
+ReadWholeAttribute proc near
+
+ cmp [ebx].ATTR_FormCode, RESIDENT_FORM
+ jne rwa10
+
+; The attribute is resident.
+; ebx -> attribute
+; es:edi -> target buffer
+;
+
+ SAVE_ALL
+
+ lea edx, [ebx].ATTR_FormUnion ; edx -> resident form info
+ mov ecx, [edx].RES_ValueLength ; ecx = bytes in value
+ mov esi, ebx ; esi -> attribute
+ add si, [edx].RES_ValueOffset ; esi -> attribute value
+
+ rep movsb ; copy bytes from value to buffer
+
+ RESTORE_ALL
+
+ ret ; That's all!
+
+rwa10:
+;
+; The attribute type is non-resident. Just call
+; ReadNonresidentAttribute starting at VCN 0 and
+; asking for the whole thing.
+;
+; ebx -> attribute
+; es:edi -> target buffer
+;
+ lea edx, [ebx].ATTR_FormUnion ; edx -> nonresident form info
+ mov ecx, [edx].NONRES_HighestVcn.LowPart; ecx = HighestVcn
+ inc ecx ; ecx = clusters in attribute
+
+ sub eax, eax ; eax = 0 (first VCN to read)
+
+ call ReadNonresidentAttribute
+
+ ret
+
+ReadWholeAttribute endp
+
+;****************************************************************************
+;
+; ReadNonresidentAttribute - Read clusters from a nonresident attribute
+;
+; ENTRY: EAX == First VCN to read
+; EBX -> Attribute
+; ECX == Number of clusters to read
+; ES:EDI == Target of read
+;
+; EXIT: None.
+;
+; USES: None (preserves all registers with SAVE_ALL/RESTORE_ALL)
+;
+ReadNonresidentAttribute proc near
+
+ SAVE_ALL
+
+ cmp [ebx].ATTR_FormCode, NONRESIDENT_FORM
+ je ReadNR10
+
+; This attribute is not resident--the disk is corrupt.
+
+ jmp BootErr$he
+
+
+ReadNR10:
+; eax == Next VCN to read
+; ebx -> Attribute
+; ecx -> Remaining clusters to read
+; es:edi -> Target of read
+;
+
+ cmp ecx, 0
+ jne ReadNR20
+
+; Nothing left to read--return success.
+;
+ RESTORE_ALL
+ ret
+
+ReadNR20:
+ push ebx ; pointer to attribute
+ push eax ; Current VCN
+
+ push ecx
+ push edi
+ push es
+
+ call ComputeLcn ; eax = LCN to read, ecx = run length
+ mov edx, ecx ; edx = remaining run length
+
+ pop es
+ pop edi
+ pop ecx
+
+
+; eax == LCN to read
+; ecx == remaining clusters to read
+; edx == remaining clusters in current run
+; es:edi == Target of read
+; TOS == Current VCN
+; TOS + 4 == pointer to attribute
+;
+ cmp ecx, edx
+ jge ReadNR30
+
+; Run length is greater than remaining request; only read
+; remaining request.
+;
+ mov edx, ecx ; edx = Remaining request
+
+ReadNR30:
+; eax == LCN to read
+; ecx == remaining clusters to read
+; edx == clusters to read in current run
+; es:edi == Target of read
+; TOS == Current VCN
+; TOS + == pointer to attribute
+;
+
+ call ReadClusters
+
+ sub ecx, edx ; Decrement clusters remaining in request
+ mov ebx, edx ; ebx = clusters read
+
+ mov eax, edx ; eax = clusters read
+ movzx edx, SectorsPerCluster
+ mul edx ; eax = sectors read (wipes out edx!)
+ movzx edx, BytesPerSector
+ mul edx ; eax = bytes read (wipes out edx!)
+
+ add edi, eax ; Update target of read
+
+ pop eax ; eax = previous VCN
+ add eax, ebx ; update VCN to read
+
+ pop ebx ; ebx -> attribute
+ jmp ReadNR10
+
+
+ReadNonresidentAttribute endp
+
+;****************************************************************************
+;
+; ReadIndexBlockSectors - Read sectors from an index allocation attribute
+;
+; ENTRY: EAX == First VBN to read
+; EBX -> Attribute
+; ECX == Number of sectors to read
+; ES:EDI == Target of read
+;
+; EXIT: None.
+;
+; USES: None (preserves all registers with SAVE_ALL/RESTORE_ALL)
+;
+ReadIndexBlockSectors proc near
+
+ SAVE_ALL
+
+ cmp [ebx].ATTR_FormCode, NONRESIDENT_FORM
+ je ReadIBS_10
+
+; This attribute is resident--the disk is corrupt.
+
+ jmp BootErr$he
+
+
+ReadIBS_10:
+; eax == Next VBN to read
+; ebx -> Attribute
+; ecx -> Remaining sectors to read
+; es:edi -> Target of read
+;
+
+ cmp ecx, 0
+ jne ReadIBS_20
+
+; Nothing left to read--return success.
+;
+
+
+ RESTORE_ALL
+ ret
+
+ReadIBS_20:
+ push ebx ; pointer to attribute
+ push eax ; Current VBN
+
+ push ecx
+ push edi
+ push es
+
+ ; Convert eax from a VBN back to a VCN by dividing by SectorsPerCluster.
+ ; The remainder of this division is the sector offset in the cluster we
+ ; want. Then use the mapping information to get the LCN for this VCN,
+ ; then multiply to get back to LBN.
+ ;
+
+ push ecx ; save remaining sectors in request
+
+ xor edx, edx ; zero high part of dividend
+ movzx ecx, SectorsPerCluster
+ div ecx ; edx = remainder
+ push edx ; save remainder
+
+ call ComputeLcn ; eax = LCN to read, ecx = remaining run length
+
+ movzx ebx, SectorsPerCluster
+ mul ebx ; eax = LBN of cluster, edx = 0
+ pop edx ; edx = remainder
+ add eax, edx ; eax = LBN we want
+ push eax ; save LBN
+
+ movzx eax, SectorsPerCluster
+ mul ecx ; eax = remaining run length in sectors, edx = 0
+ mov edx, eax ; edx = remaining run length
+
+ pop eax ; eax = LBN
+ pop ecx ; ecx = remaining sectors in request
+
+ pop es
+ pop edi
+ pop ecx
+
+
+; eax == LBN to read
+; ecx == remaining sectors to read
+; edx == remaining sectors in current run
+; es:edi == Target of read
+; TOS == Current VCN
+; TOS + 4 == pointer to attribute
+;
+ cmp ecx, edx
+ jge ReadIBS_30
+
+; Run length is greater than remaining request; only read
+; remaining request.
+;
+ mov edx, ecx ; edx = Remaining request
+
+ReadIBS_30:
+; eax == LBN to read
+; ecx == remaining sectors to read
+; edx == sectors to read in current run
+; es:edi == Target of read
+; TOS == Current VCN
+; TOS + == pointer to attribute
+;
+
+ mov SectorBase, eax
+ mov SectorCount, dx
+
+; We have a pointer to the target buffer in es:edi, but we want that
+; in es:bx for DoRead.
+;
+
+ SAVE_ALL
+
+ mov bx, di
+ and bx, 0Fh
+ mov ax, es
+ shr edi, 4
+ add ax, di ; ax:bx -> target buffer
+
+ push ax
+ pop es ; es:bx -> target buffer
+
+ call DoRead
+
+ RESTORE_ALL
+
+ sub ecx, edx ; Decrement sectors remaining in request
+ mov ebx, edx ; ebx = sectors read
+
+ mov eax, edx ; eax = sectors read
+ movzx edx, BytesPerSector
+ mul edx ; eax = bytes read (wipes out edx!)
+
+ add edi, eax ; Update target of read
+
+ pop eax ; eax = previous VBN
+ add eax, ebx ; update VBN to read
+
+ pop ebx ; ebx -> attribute
+ jmp ReadIBS_10
+
+
+ReadIndexBlockSectors endp
+
+
+;****************************************************************************
+;
+; MultiSectorFixup - fixup a structure read off the disk
+; to reflect Update Sequence Array.
+;
+; ENTRY: ES:EDI = Target buffer
+;
+; USES: none (preserves all registers with SAVE_ALL/RESTORE_ALL)
+;
+; Note: ES:EDI must point at a structure which is protected
+; by an update sequence array, and which begins with
+; a multi-sector-header structure.
+;
+MultiSectorFixup proc near
+
+ SAVE_ALL
+
+ movzx ebx, es:[edi].MSH_UpdateArrayOfs ; ebx = update array offset
+ movzx ecx, es:[edi].MSH_UpdateArraySize ; ecx = update array size
+
+ or ecx, ecx ; if the size of the update sequence array
+ jz BootErr$he ; is zero, this structure is corrupt.
+
+ add ebx, edi ; es:ebx -> update sequence array count word
+ add ebx, 2 ; es:ebx -> 1st entry of update array
+
+ add edi, SEQUENCE_NUMBER_STRIDE - 2 ; es:edi->last word of first chunk
+ dec ecx ; decrement to reflect count word
+
+MSF10:
+
+; ecx = number of entries remaining in update sequence array
+; es:ebx -> next entry in update sequence array
+; es:edi -> next target word for update sequence array
+
+ or ecx, ecx
+ jz MSF30
+
+ mov ax, word ptr es:[ebx] ; copy next update sequence array entry
+ mov word ptr es:[edi], ax ; to next target word
+
+ add ebx, 2 ; go on to next entry
+ add edi, SEQUENCE_NUMBER_STRIDE ; go on to next target
+
+ dec ecx
+
+
+ jmp MSF10
+
+MSF30:
+
+ RESTORE_ALL
+
+ ret
+
+MultiSectorFixup endp
+
+;****************************************************************************
+;
+; SetupMft - Reads MFT File Record Segments into memory.
+;
+; ENTRY: none.
+;
+; EXIT: NextBuffer is set to the free byte after the last MFT FRS
+; SegmentsInMft is initialized
+;
+;
+SetupMft proc near
+
+ SAVE_ALL
+
+; Initialize SegmentsInMft and NextBuffer as if the MFT
+; had only one FRS.
+;
+ mov eax, 1
+ mov SegmentsInMft, eax
+
+ mov eax, MftFrs
+ add eax, BytesPerFrs
+ mov NextBuffer, eax
+
+; Read FRS 0 into the first MFT FRS buffer, being sure
+; to resolve the Update Sequence Array.
+;
+
+ mov eax, MftStartLcn.LowPart
+ movzx ebx, SectorsPerCluster
+ mul ebx ; eax = mft starting sector
+ mov SectorBase, eax ; SectorBase = mft starting sector
+
+ mov eax, SectorsPerFrs
+ mov SectorCount, ax ; SectorCount = SectorsPerFrs
+
+ mov ebx, MftFrs
+
+ push ds
+ pop es
+
+ call DoRead
+ movzx edi, bx ; es:edi = buffer
+ call MultiSectorFixup
+
+; Determine whether the MFT has an Attribute List attribute
+
+ mov eax, MftFrs
+ mov ebx, $ATTRIBUTE_LIST
+ mov ecx, 0
+ mov edx, 0
+
+ call LocateAttributeRecord
+
+ or eax, eax ; If there's no Attribute list,
+ jz SetupMft99 ; we're done!
+
+; Read the attribute list.
+; eax -> attribute list attribute
+;
+ mov ebx, eax ; ebx -> attribute list attribute
+ push ds
+ pop es ; copy ds into es
+ mov edi, AttrList ; ds:edi->attribute list buffer
+
+ call ReadWholeAttribute
+
+ mov ebx, AttrList ; ebx -> first attribute list entry
+
+; Now, traverse the attribute list looking for the first
+; entry for the $DATA type. We know it must have at least
+; one.
+;
+; ebx -> first attribute list entry
+;
+SetupMft10:
+ cmp [ebx].ATTRLIST_TypeCode, $DATA
+ je SetupMft20
+
+ add bx,[ebx].ATTRLIST_Length
+ jmp SetupMft10
+
+
+SetupMft20:
+; Scan forward through the attribute list entries for the
+; $DATA attribute, reading each referenced FRS. Note that
+; there will be at least one non-$DATA entry after the entries
+; for the $DATA attribute, since there's a $BITMAP.
+;
+; ebx -> Next attribute list entry
+; NextBuffer -> Target for next read
+; SegmentsInMft == number of MFT segments read so far
+;
+ cmp [ebx].ATTRLIST_TypeCode, $DATA
+ jne SetupMft99
+
+; Read the FRS referred to by this attribute list entry into
+; the next buffer, and increment NextBuffer and SegmentsInMft.
+;
+ push ebx
+
+ mov eax, [ebx].ATTRLIST_SegmentReference.REF_LowPart
+ mov edi, NextBuffer
+ push ds
+ pop es ; copy ds into es
+
+ call ReadFrs
+
+ pop ebx
+
+; Increment NextBuffer and SegmentsInMft
+
+ mov eax, BytesPerFrs
+ add NextBuffer, eax
+
+ inc SegmentsInMft
+
+; Go on to the next attribute list entry
+
+ add bx, [ebx].ATTRLIST_Length
+ jmp SetupMft20
+
+SetupMft99:
+
+ RESTORE_ALL
+ ret
+
+SetupMft endp
+
+;****************************************************************************
+;
+; ComputeMftLcn -- Computes the LCN for a cluster of the MFT
+;
+;
+; ENTRY: EAX == VCN
+;
+; EXIT: EAX == LCN
+;
+; USES: ALL
+;
+ComputeMftLcn proc near
+
+ mov edx, eax ; edx = VCN
+
+ mov ecx, SegmentsInMft ; ecx = # of FRS's to search
+ mov eax, MftFrs ; eax -> first FRS to search
+
+MftLcn10:
+; EAX -> Next FRS to search
+; ECX == number of remaining FRS's to search
+; EDX == VCN
+;
+ push edx
+ push eax
+ push ecx
+ push edx ; Yes, I meant to push it twice
+
+ mov ebx, $DATA
+ mov ecx, 0
+ mov edx, 0
+
+ call LocateAttributeRecord
+
+; EAX -> $DATA attribute
+; TOS == VCN
+; TOS + 4 == number of remaining FRS's to search
+; TOS + 8 -> FRS being searched
+; TOS +12 == VCN
+
+ or eax, eax
+ jz BootErr$he ; No $DATA attribute in this FRS!
+
+ mov ebx, eax ; ebx -> attribute
+ pop eax ; eax = VCN
+
+; EAX == VCN
+; EBX -> $DATA attribute
+; TOS number of remaining FRS's to search
+; TOS + 4 == FRS being searched
+; TOS + 8 == VCN
+
+ call ComputeLcn
+
+ or eax, eax
+ jz MftLcn20
+
+; Found our LCN. Clean up the stack and return.
+;
+; EAX == LCN
+; TOS number of remaining FRS's to search
+; TOS + 4 == FRS being searched
+; TOS + 8 == VCN
+;
+ pop ebx
+ pop ebx
+ pop ebx ; clean up the stack
+
+ ret
+
+MftLcn20:
+;
+; Didn't find the VCN in this FRS; try the next one.
+;
+; TOS number of remaining FRS's to search
+; TOS + 4 -> FRS being searched
+; TOS + 8 == VCN
+;
+ pop ecx ; ecx = number of FRS's remaining, including current
+ pop eax ; eax -> current FRS
+ pop edx ; edx = VCN
+
+ add eax, BytesPerFrs ; eax -> next FRS
+ loop MftLcn10 ; decrement ecx and try next FRS
+
+; This VCN was not found.
+;
+ xor eax, eax
+ ret
+
+
+ComputeMftLcn endp
+
+;****************************************************************************
+;
+; ReadMftSectors - Read sectors from the MFT
+;
+; ENTRY: EAX == starting VBN
+; ECX == number of sectors to read
+; ES:EDI == Target buffer
+;
+; USES: none (preserves all registers with SAVE_ALL/RESTORE_ALL)
+;
+ReadMftSectors proc near
+
+ SAVE_ALL
+
+RMS$Again:
+
+ push eax ; save starting VBN
+ push ecx ; save sector count
+
+
+; Divide the VBN by SectorsPerCluster to get the VCN
+
+ xor edx, edx ; zero high part of dividend
+ movzx ebx, SectorsPerCluster
+ div ebx ; eax = VCN
+ push edx ; save remainder
+
+ call ComputeMftLcn ; eax = LCN
+
+ or eax, eax ; LCN equal to zero?
+ jz BootErr$he ; zero is not a possible LCN
+
+; Change the LCN back into a LBN and add the remainder back in to get
+; the sector we want to read, which goes into SectorBase.
+;
+
+ movzx ebx, SectorsPerCluster
+ mul ebx ; eax = cluster first LBN
+ pop edx ; edx = sector remainder
+ add eax, edx ; eax = desired LBN
+
+ mov SectorBase, eax
+
+;
+; Figure out how many sectors to read this time; we never attempt
+; to read more than one cluster at a time.
+;
+
+ pop ecx ; ecx = sectors to read
+
+ movzx ebx, SectorsPerCluster
+ cmp ecx,ebx
+ jle RMS10
+
+;
+; Read only a single cluster at a time, to avoid problems with fragmented
+; runs in the mft.
+;
+
+ mov SectorCount, bx ; this time read 1 cluster
+ sub ecx, ebx ; ecx = sectors remaining to read
+
+ pop eax ; eax = VBN
+ add eax, ebx ; VBN += sectors this read
+
+
+ push eax ; save next VBN
+ push ecx ; save remaining sector count
+
+
+
+ jmp RMS20
+
+RMS10:
+
+ pop eax ; eax = VBN
+ add eax, ecx ; VBN += sectors this read
+ push eax ; save next VBN
+
+ mov SectorCount, cx
+ mov ecx, 0
+ push ecx ; save remaining sector count (0)
+
+RMS20:
+
+
+; The target buffer was passed in es:edi, but we want it in es:bx.
+; Do the conversion.
+;
+
+ push es ; save buffer pointer
+ push edi
+
+ mov bx, di
+ and bx, 0Fh
+ mov ax, es
+ shr edi, 4
+ add ax, di ; ax:bx -> target buffer
+
+ push ax
+ pop es ; es:bx -> target buffer
+
+ call DoRead
+
+ pop edi ; restore buffer pointer
+ pop es
+
+ add edi, BytesPerCluster ; increment buf ptr by one cluster
+
+ pop ecx ; restore remaining sector count
+ pop eax ; restore starting VBN
+
+ cmp ecx, 0 ; are we done?
+ jg RMS$Again ; repeat until desired == 0
+
+
+ RESTORE_ALL
+ ret
+
+ReadMftSectors endp
+
+
+;****************************************************************************
+;
+; ReadFrs - Read an FRS
+;
+; ENTRY: EAX == FRS number
+; ES:EDI == Target buffer
+;
+; USES: none (preserves all registers with SAVE_ALL/RESTORE_ALL)
+;
+ReadFrs proc near
+
+ SAVE_ALL
+
+ mul SectorsPerFrs ; eax = sector number in MFT DATA attribute
+ ; (note that mul wipes out edx!)
+
+ mov ecx, SectorsPerFrs ; number of sectors to read
+
+ call ReadMftSectors
+ call MultiSectorFixup
+
+ RESTORE_ALL
+ ret
+
+ReadFrs endp
+
+;****************************************************************************
+;
+; ReadIndexBlock - read an index block from the root index.
+;
+; ENTRY: EAX == Block number
+;
+; USES: none (preserves all registers with SAVE_ALL/RESTORE_ALL)
+;
+ReadIndexBlock proc near
+
+ SAVE_ALL
+
+ mul SectorsPerIndexBlock ; eax = first VBN to read
+ ; (note that mul wipes out edx!)
+ mov ebx, IndexAllocation ; ebx -> $INDEX_ALLOCATION attribute
+ mov ecx, SectorsPerIndexBlock ; ecx == Sectors to read
+
+ push ds
+ pop es
+ mov edi, IndexBlockBuffer ; es:edi -> index block buffer
+
+ call ReadIndexBlockSectors
+ call MultiSectorFixup
+
+ RESTORE_ALL
+ ret
+
+ReadIndexBlock endp
+
+;****************************************************************************
+;
+; IsBlockInUse - Checks the index bitmap to see if an index
+; allocation block is in use.
+;
+; ENTRY: EAX == block number
+;
+; EXIT: Carry flag clear if block is in use
+; Carry flag set if block is not in use.
+;
+IsBlockInUse proc near
+
+ push eax
+ push ebx
+ push ecx
+
+ mov ebx, IndexBitmapBuffer
+
+ mov ecx, eax ; ecx = block number
+ shr eax, 3 ; eax = byte number
+ and ecx, 7 ; ecx = bit number in byte
+
+ add ebx, eax ; ebx -> byte to test
+
+ mov eax, 1
+ shl eax, cl ; eax = mask
+
+ test byte ptr[ebx], al
+
+ jz IBU10
+
+ clc ; Block is not in use.
+ jmp IBU20
+
+IBU10: stc ; Block is in use.
+
+IBU20:
+ pop ecx
+ pop ebx
+ pop eax ; restore registers
+
+ ret
+
+IsBlockInUse endp
+
+;****************************************************************************
+;
+; ComputeLcn - Converts a VCN into an LCN
+;
+; ENTRY: EAX -> VCN
+; EBX -> Attribute
+;
+; EXIT: EAX -> LCN (zero indicates not found)
+; ECX -> Remaining run length
+;
+; USES: ALL.
+;
+ComputeLcn proc near
+
+ cmp [ebx].ATTR_FormCode, NONRESIDENT_FORM
+ je clcn10
+
+ sub eax, eax ; This is a resident attribute.
+ ret
+
+clcn10: lea esi, [ebx].ATTR_FormUnion ; esi -> nonresident info of attrib
+
+; eax -> VCN
+; ebx -> Attribute
+; esi -> Nonresident information of attribute record
+;
+; See if the desired VCN is in range.
+
+ mov edx, [esi].NONRES_HighestVcn.LowPart ; edx = HighestVcn
+ cmp eax, edx
+ ja clcn15 ; VCN is greater than HighestVcn
+
+ mov edx, [esi].NONRES_LowestVcn.LowPart ; edx = LowestVcn
+ cmp eax, edx
+ jae clcn20
+
+clcn15:
+ sub eax, eax ; VCN is not in range
+ ret
+
+clcn20:
+; eax -> VCN
+; ebx -> Attribute
+; esi -> Nonresident information of attribute record
+; edx -> LowestVcn
+;
+ add bx, [esi].NONRES_MappingPairOffset ; ebx -> mapping pairs
+ sub esi, esi ; esi = 0
+
+clcn30:
+; eax == VCN to find
+; ebx -> Current mapping pair count byte
+; edx == Current VCN
+; esi == Current LCN
+;
+ cmp byte ptr[ebx], 0 ; if count byte is zero...
+ je clcn99 ; ... we're done (and didn't find it)
+
+; Update CurrentLcn
+;
+ call LcnFromMappingPair
+ add esi, ecx ; esi = current lcn for this mapping pair
+
+ call VcnFromMappingPair
+
+; eax == VCN to find
+; ebx -> Current mapping pair count byte
+; ecx == DeltaVcn for current mapping pair
+; edx == Current VCN
+; esi == Current LCN
+;
+ add ecx, edx ; ecx = NextVcn
+
+ cmp eax, ecx ; If target < NextVcn ...
+ jl clcn80 ; ... we found the right mapping pair.
+
+; Go on to next mapping pair.
+;
+ mov edx, ecx ; CurrentVcn = NextVcn
+
+ push eax
+
+ movzx ecx, byte ptr[ebx] ; ecx = count byte
+ mov eax, ecx ; eax = count byte
+ and eax, 0fh ; eax = number of vcn bytes
+ shr ecx, 4 ; ecx = number of lcn bytes
+
+ add ebx, ecx
+ add ebx, eax
+ inc ebx ; ebx -> next count byte
+
+ pop eax
+ jmp clcn30
+
+clcn80:
+; We found the mapping pair we want.
+;
+; eax == target VCN
+; ebx -> mapping pair count byte
+; edx == Starting VCN of run
+; ecx == Next VCN (ie. start of next run)
+; esi == starting LCN of run
+;
+ sub ecx, eax ; ecx = remaining run length
+ sub eax, edx ; eax = offset into run
+ add eax, esi ; eax = LCN to return
+
+ ret
+
+; The target VCN is not in this attribute.
+
+clcn99: sub eax, eax ; Not found.
+ ret
+
+
+ComputeLcn endp
+
+;****************************************************************************
+;
+; VcnFromMappingPair
+;
+; ENTRY: EBX -> Mapping Pair count byte
+;
+; EXIT: ECX == DeltaVcn from mapping pair
+;
+; USES: ECX
+;
+VcnFromMappingPair proc near
+
+ sub ecx, ecx ; ecx = 0
+ mov cl, byte ptr[ebx] ; ecx = count byte
+ and cl, 0fh ; ecx = v
+
+ cmp ecx, 0 ; if ecx is zero, volume is corrupt.
+ jne VFMP5
+
+ sub ecx, ecx
+ ret
+
+VFMP5:
+ push ebx
+ push edx
+
+ add ebx, ecx ; ebx -> last byte of compressed vcn
+
+ movsx edx, byte ptr[ebx]
+ dec ecx
+ dec ebx
+
+; ebx -> Next byte to add in
+; ecx == Number of bytes remaining
+; edx == Accumulated value
+;
+VFMP10: cmp ecx, 0 ; When ecx == 0, we're done.
+ je VFMP20
+
+ shl edx, 8
+ mov dl, byte ptr[ebx]
+
+ dec ebx ; Back up through bytes to process.
+ dec ecx ; One less byte to process.
+
+ jmp VFMP10
+
+VFMP20:
+; edx == Accumulated value to return
+
+ mov ecx, edx
+
+ pop edx
+ pop ebx
+
+ ret
+
+VcnFromMappingPair endp
+
+
+;****************************************************************************
+;
+; LcnFromMappingPair
+;
+; ENTRY: EBX -> Mapping Pair count byte
+;
+; EXIT: ECX == DeltaLcn from mapping pair
+;
+; USES: ECX
+;
+LcnFromMappingPair proc near
+
+ push ebx
+ push edx
+
+ sub edx, edx ; edx = 0
+ mov dl, byte ptr[ebx] ; edx = count byte
+ and edx, 0fh ; edx = v
+
+ sub ecx, ecx ; ecx = 0
+ mov cl, byte ptr[ebx] ; ecx = count byte
+ shr cl, 4 ; ecx = l
+
+ cmp ecx, 0 ; if ecx is zero, volume is corrupt.
+ jne LFMP5
+
+ sub ecx, ecx
+
+ pop edx
+ pop ebx
+ ret
+
+LFMP5:
+; ebx -> count byte
+; ecx == l
+; edx == v
+;
+
+ add ebx, edx ; ebx -> last byte of compressed vcn
+ add ebx, ecx ; ebx -> last byte of compressed lcn
+
+ movsx edx, byte ptr[ebx]
+ dec ecx
+ dec ebx
+
+; ebx -> Next byte to add in
+; ecx == Number of bytes remaining
+; edx == Accumulated value
+;
+LFMP10: cmp ecx, 0 ; When ecx == 0, we're done.
+ je LFMP20
+
+ shl edx, 8
+ mov dl, byte ptr[ebx]
+
+ dec ebx ; Back up through bytes to process.
+ dec ecx ; One less byte to process.
+
+ jmp LFMP10
+
+LFMP20:
+; edx == Accumulated value to return
+
+ mov ecx, edx
+
+ pop edx
+ pop ebx
+
+ ret
+
+LcnFromMappingPair endp
+
+;****************************************************************************
+;
+; UpcaseName - Converts the name of the file to all upper-case
+;
+; ENTRY: ESI -> Name
+; ECX -> Length of name
+;
+; USES: none
+;
+UpcaseName proc near
+
+
+ or ecx, ecx
+ jnz UN5
+
+ ret
+
+UN5:
+ push ecx
+ push esi
+
+UN10:
+ cmp word ptr[esi], 'a' ; if it's less than 'a'
+ jl UN20 ; leave it alone
+
+ cmp word ptr[esi], 'z' ; if it's greater than 'z'
+ jg UN20 ; leave it alone.
+
+ sub word ptr[esi], 'a'-'A' ; the letter is lower-case--convert it.
+UN20:
+ add esi, 2 ; move on to next unicode character
+ loop UN10
+
+ pop esi
+ pop ecx
+
+ ret
+UpcaseName endp
+
+;****************************************************************************
+;
+; FindFile - Locates the index entry for a file in the root index.
+;
+; ENTRY: EAX -> name to find
+; ECX == length of file name in characters
+;
+; EXIT: EAX -> Index Entry. NULL to indicate failure.
+;
+; USES: ALL
+;
+FindFile proc near
+
+ push eax ; name address
+ push ecx ; name length
+
+; First, search the index root.
+;
+; eax -> name to find
+; ecx == name length
+; TOS == name length
+; TOS+4 -> name to find
+;
+ mov edx, eax ; edx -> name to find
+ mov eax, IndexRoot ; eax -> &INDEX_ROOT attribute
+ lea ebx, [eax].ATTR_FormUnion ; ebx -> resident info
+ add ax, [ebx].RES_ValueOffset ; eax -> Index Root value
+
+ lea eax, [eax].IR_IndexHeader ; eax -> Index Header
+
+ mov ebx, edx ; ebx -> name to find
+
+ call LocateIndexEntry
+
+ or eax, eax
+ jz FindFile20
+
+; Found it in the root! The result is already in eax.
+; Clean up the stack and return.
+;
+ pop ecx
+ pop ecx
+ ret
+
+FindFile20:
+;
+; We didn't find the index entry we want in the root, so we have to
+; crawl through the index allocation buffers.
+;
+; TOS == name length
+; TOS+4 -> name to find
+;
+ mov eax, IndexAllocation
+ or eax, eax
+ jnz FindFile30
+
+; There is no index allocation attribute; clean up
+; the stack and return failure.
+;
+ pop ecx
+ pop ecx
+ xor eax, eax
+ ret
+
+FindFile30:
+;
+; Search the index allocation blocks for the name we want.
+; Instead of searching in tree order, we'll just start with
+; the last one and work our way backwards.
+;
+; TOS == name length
+; TOS+4 -> name to find
+;
+ mov edx, IndexAllocation ; edx -> index allocation attr.
+ lea edx, [edx].ATTR_FormUnion ; edx -> nonresident form info
+ mov eax, [edx].NONRES_HighestVcn.LowPart; eax = HighestVcn
+ inc eax ; eax = clusters in attribute
+
+ mov ebx, BytesPerCluster
+ mul ebx ; eax = bytes in attribute
+
+ xor edx, edx
+ div BytesPerIndexBlock ; convert bytes to index blocks
+
+ push eax ; number of blocks to process
+
+FindFile40:
+;
+; TOS == remaining index blocks to search
+; TOS + 4 == name length
+; TOS + 8 -> name to find
+;
+ pop eax ; eax == number of remaining blocks
+
+ or eax, eax
+ jz FindFile90
+
+ dec eax ; eax == number of next block to process
+ ; and number of remaining blocks
+
+ push eax
+
+; eax == block number to process
+; TOS == remaining index blocks to search
+; TOS + 4 == name length
+; TOS + 8 -> name to find
+;
+; See if the block is in use; if not, go on to next.
+
+ call IsBlockInUse
+ jc FindFile40 ; c set if not in use
+
+; eax == block number to process
+; TOS == remaining index blocks to search
+; TOS + 4 == name length
+; TOS + 8 -> name to find
+;
+
+ call ReadIndexBlock
+
+ pop edx ; edx == remaining buffers to search
+ pop ecx ; ecx == name length
+ pop ebx ; ebx -> name
+
+ push ebx
+ push ecx
+ push edx
+
+; ebx -> name to find
+; ecx == name length in characters
+; TOS == remaining blocks to process
+; TOS + 4 == name length
+; TOS + 8 -> name
+;
+; Index buffer to search is in index allocation block buffer.
+;
+ mov eax, IndexBlockBuffer ; eax -> Index allocation block
+ lea eax, [eax].IB_IndexHeader ; eax -> Index Header
+
+ call LocateIndexEntry ; eax -> found entry
+
+ or eax, eax
+ jz FindFile40
+
+; Found it!
+;
+; eax -> Found entry
+; TOS == remaining blocks to process
+; TOS + 4 == name length
+; TOS + 8 -> name
+;
+ pop ecx
+ pop ecx
+ pop ecx ; clean up stack
+ ret
+
+FindFile90:
+;
+; Name not found.
+;
+; TOS == name length
+; TOS + 4 -> name to find
+;
+ pop ecx
+ pop ecx ; clean up stack.
+ xor eax, eax ; zero out eax.
+ ret
+
+
+FindFile endp
+
+;****************************************************************************
+;
+; DumpIndexBlock - dumps the index block buffer
+;
+DumpIndexBlock proc near
+
+ SAVE_ALL
+
+ mov esi, IndexBlockBuffer
+
+ mov ecx, 20h ; dwords to dump
+
+DIB10:
+
+ test ecx, 3
+ jnz DIB20
+ call DebugNewLine
+
+DIB20:
+
+ lodsd
+ call PrintNumber
+ loop DIB10
+
+ RESTORE_ALL
+ ret
+
+DumpIndexBlock endp
+
+;****************************************************************************
+;
+; DebugNewLine
+;
+DebugNewLine proc near
+
+ SAVE_ALL
+
+ xor eax, eax
+ xor ebx, ebx
+
+ mov al, 0dh
+ mov ah, 14
+ mov bx, 7
+ int 10h
+
+ mov al, 0ah
+ mov ah, 14
+ mov bx, 7
+ int 10h
+
+ RESTORE_ALL
+ ret
+
+DebugNewLine endp
+
+
+;****************************************************************************
+;
+; PrintName - Display a unicode name
+;
+; ENTRY: DS:ESI -> null-terminated string
+; ECX == characters in string
+;
+; USES: None.
+;
+PrintName proc near
+
+
+ SAVE_ALL
+
+ or ecx, ecx
+ jnz PrintName10
+
+ call DebugNewLine
+
+ RESTORE_ALL
+
+ ret
+
+PrintName10:
+
+ xor eax, eax
+ xor ebx, ebx
+
+ lodsw
+
+ mov ah, 14 ; write teletype
+ mov bx, 7 ; attribute
+ int 10h ; print it
+ loop PrintName10
+
+ call DebugNewLine
+
+ RESTORE_ALL
+ ret
+
+PrintName endp
+
+;****************************************************************************
+;
+; DebugPrint - Display a debug string.
+;
+; ENTRY: DS:SI -> null-terminated string
+;
+; USES: None.
+;
+.286
+DebugPrint proc near
+
+ pusha
+
+DbgPr20:
+
+ lodsb
+ cmp al, 0
+ je DbgPr30
+
+ mov ah, 14 ; write teletype
+ mov bx, 7 ; attribute
+ int 10h ; print it
+ jmp DbgPr20
+
+DbgPr30:
+
+ popa
+ nop
+ ret
+
+DebugPrint endp
+
+;****************************************************************************
+;
+;
+; PrintNumber
+;
+; ENTRY: EAX == number to print
+;
+; PRESERVES ALL REGISTERS
+;
+.386
+PrintNumber proc near
+
+
+ SAVE_ALL
+
+ mov ecx, 8 ; number of digits in a DWORD
+
+PrintNumber10:
+
+ mov edx, eax
+ and edx, 0fh ; edx = lowest-order digit
+ push edx ; put it on the stack
+ shr eax, 4 ; drop low-order digit
+ loop PrintNumber10
+
+ mov ecx, 8 ; number of digits on stack.
+
+PrintNumber20:
+
+ pop eax ; eax = next digit to print
+ cmp eax, 9
+ jg PrintNumber22
+
+ add eax, '0'
+ jmp PrintNumber25
+
+PrintNumber22:
+
+ sub eax, 10
+ add eax, 'A'
+
+PrintNumber25:
+
+ xor ebx, ebx
+
+ mov ah, 14
+ mov bx, 7
+ int 10h
+ loop PrintNumber20
+
+; Print a space to separate numbers
+
+ mov al, ' '
+ mov ah, 14
+ mov bx, 7
+ int 10h
+
+ RESTORE_ALL
+
+ call Pause
+
+ ret
+
+PrintNumber endp
+
+
+;****************************************************************************
+;
+; Debug0 - Print debug string 0 -- used for checkpoints in mainboot
+;
+Debug0 proc near
+
+ SAVE_ALL
+
+ mov esi, offset DbgString0
+ call BootErr$Print
+
+ RESTORE_ALL
+
+ ret
+
+Debug0 endp
+
+;****************************************************************************
+;
+; Debug1 - Print debug string 1 --
+;
+Debug1 proc near
+
+ SAVE_ALL
+
+ mov esi, offset DbgString1
+ call BootErr$Print
+
+ RESTORE_ALL
+
+ ret
+
+Debug1 endp
+
+;****************************************************************************
+;
+; Debug2 - Print debug string 2
+;
+Debug2 proc near
+
+ SAVE_ALL
+
+ mov esi, offset DbgString2
+ call BootErr$Print
+
+ RESTORE_ALL
+
+ ret
+
+Debug2 endp
+
+;****************************************************************************
+;
+; Debug3 - Print debug string 3 --
+;
+Debug3 proc near
+
+ SAVE_ALL
+
+ mov esi, offset DbgString3
+ call BootErr$Print
+
+ RESTORE_ALL
+
+ ret
+
+Debug3 endp
+
+;****************************************************************************
+;
+; Debug4 - Print debug string 4
+;
+Debug4 proc near
+
+ SAVE_ALL
+
+ mov esi, offset DbgString4
+ call BootErr$Print
+
+ RESTORE_ALL
+
+ ret
+
+Debug4 endp
+
+;****************************************************************************
+;
+; Pause - Pause for about 1/2 a second. Simply count until you overlap
+; to zero.
+;
+Pause proc near
+
+ push eax
+ mov eax, 0fff50000h
+
+PauseLoopy:
+ inc eax
+
+ or eax, eax
+ jnz PauseLoopy
+
+ pop eax
+ ret
+
+Pause endp
+
+
+;*************************************************************************
+;
+; LoadIndexFrs - For the requested index type code locate and
+; load the associated Frs.
+;
+; ENTRY: EAX - requested index type code
+; ECX - Points to empty Frs buffer
+;
+; EXIT: EAX - points to offset in Frs buffer of requested index type
+; code or Zero if not found.
+; USES: All
+;
+LoadIndexFrs proc near
+
+ push ecx ; save FRS buffer for later
+ push eax ; save index type code for later
+
+ mov eax, ROOT_FILE_NAME_INDEX_NUMBER
+ push ds
+ pop es
+ mov edi, ecx ; es:edi = target buffer
+
+ call ReadFrs
+
+ mov eax, ecx ; FRS to search
+
+ pop ebx ; Attribute type code
+ push ebx
+ movzx ecx, index_name_length ; Attribute name length
+ mov edx, offset index_name ; Attribute name
+
+ call LocateAttributeRecord
+
+ pop ebx
+ pop ecx
+
+ or eax, eax
+ jnz LoadIndexFrs$Exit ; if found in root return
+
+;
+; if not found in current Frs, search in attribute list
+;
+ ; EBX - holds Attribute type code
+ mov eax, ecx ; FRS to search
+ mov ecx, ebx ; type code
+ push eax ; save Frs
+ push ebx ; save type code
+
+ call SearchAttrList ; search attribute list for FRN
+ ; of specified ($INDEX_ROOT,
+ ; $INDEX_ALLOCATION, or $BITMAP)
+
+ ; EAX - holds FRN for Frs, or Zero
+
+ pop ebx ; Attribute type code (used later)
+ pop edi ; es:edi = target buffer
+
+ or eax, eax ; if we cann't find it in attribute
+ jz LoadIndexFrs$Exit ; list then we are hosed
+
+
+; We should now have the File Record Number where the index for the
+; specified type code we are searching for is, load this into the
+; Frs target buffer.
+;
+; EAX - holds FRN
+; EBX - holds type code
+; EDI - holds target buffer
+
+ push ds
+ pop es
+
+ call ReadFrs
+
+;
+; Now determine the offset in the Frs of the index
+;
+
+; EBX - holds type code
+
+ mov eax, edi ; Frs to search
+ movzx ecx, index_name_length ; Attribute name length
+ mov edx, offset index_name ; Attribute name
+
+ call LocateAttributeRecord
+
+; EAX - holds offset or Zero.
+
+
+LoadIndexFrs$Exit:
+ ret
+
+LoadIndexFrs endp
+
+
+;****************************************************************************
+;
+; SearchAttrList
+;
+; Search the Frs for the attribute list. Then search the attribute list
+; for the specifed type code. When you find it return the FRN in the
+; attribute list entry found or Zero if no match found.
+;
+; ENTRY: ECX - type code to search attrib list for
+; EAX - Frs buffer holding head of attribute list
+; EXIT: EAX - FRN file record number to load, Zero if none.
+;
+; USES: All
+;
+SearchAttrList proc near
+
+ push ecx ; type code to search for in
+ ; attrib list
+
+ ; EAX - holds Frs to search
+ mov ebx, $ATTRIBUTE_LIST ; Attribute type code
+ mov ecx, 0 ; Attribute name length
+ mov edx, 0 ; Attribute name
+
+ call LocateAttributeRecord
+
+ or eax, eax ; If there's no Attribute list,
+ jz SearchAttrList$NotFoundIndex1 ; We are done
+
+; Read the attribute list.
+; eax -> attribute list attribute
+
+ mov ebx, eax ; ebx -> attribute list attribute
+ push ds
+ pop es ; copy ds into es
+ mov edi, AttrList ; ds:edi->attribute list buffer
+
+ call ReadWholeAttribute
+
+ push ds
+ pop es
+ mov ebx, AttrList ; es:ebx -> first attribute list entry
+
+; Now, traverse the attribute list looking for the entry for
+; the Index type code.
+;
+; ebx -> first attribute list entry
+;
+
+ pop ecx ; Get Index Type code
+
+
+SearchAttrList$LookingForIndex:
+
+; DEBUG CODE
+; SAVE_ALL
+;
+; mov eax, es:[bx].ATTRLIST_TypeCode
+; call PrintNumber
+; movzx eax, es:[bx].ATTRLIST_Length
+; call PrintNumber
+; mov eax, es
+; call PrintNumber
+; mov eax, ebx
+; call PrintNumber
+; push es
+; pop ds
+; movzx ecx, es:[bx].ATTRLIST_NameLength ; ecx = chars in name
+; lea esi, es:[bx].ATTRLIST_Name ; esi -> name
+; call PrintName
+;
+; RESTORE_ALL
+; END DEBUG CODE
+
+ cmp es:[bx].ATTRLIST_TypeCode, ecx
+ je SearchAttrList$FoundIndex
+
+ cmp es:[bx].ATTRLIST_TypeCode, $END ; reached invalid attribute
+ je SearchAttrList$NotFoundIndex2 ; so must be at end
+
+ cmp es:[bx].ATTRLIST_Length, 0
+ je SearchAttrList$NotFoundIndex2 ; reached end of list and
+ ; nothing found
+ movzx eax, es:[bx].ATTRLIST_Length
+ add bx, ax
+
+ mov ax, bx
+ and ax, 08000h ; test for roll over
+ jz SearchAttrList$LookingForIndex
+
+ ; If we rolled over then increment to the next es 32K segment and
+ ; zero off the high bits of bx
+
+ mov ax, es
+ add ax, 800h
+ mov es, ax
+
+ and bx, 07fffh
+
+ jmp SearchAttrList$LookingForIndex
+
+SearchAttrList$FoundIndex:
+
+ ; found the index, return the FRN
+
+ mov eax, es:[bx].ATTRLIST_SegmentReference.REF_LowPart
+ ret
+
+
+SearchAttrList$NotFoundIndex1:
+ pop ecx
+SearchAttrList$NotFoundIndex2:
+ xor eax, eax
+ ret
+
+SearchAttrList endp
+
+
+DbgString0 db "Debug Point 0", 0Dh, 0Ah, 0
+DbgString1 db "Debug Point 1", 0Dh, 0Ah, 0
+DbgString2 db "Debug Point 2", 0Dh, 0Ah, 0
+DbgString3 db "Debug Point 3", 0Dh, 0Ah, 0
+DbgString4 db "Debug Point 4", 0Dh, 0Ah, 0
+
+ .errnz ($-_ntfsboot) GT 8192,<FATAL PROBLEM: main boot record exceeds available space>
+
+ org 8192
+
+BootCode ends
+
+ end _ntfsboot
diff --git a/private/ntos/boot/bootcode/ntfs/i386/usa/bootntfs.h b/private/ntos/boot/bootcode/ntfs/i386/usa/bootntfs.h
new file mode 100644
index 000000000..ac6b63ded
--- /dev/null
+++ b/private/ntos/boot/bootcode/ntfs/i386/usa/bootntfs.h
@@ -0,0 +1,517 @@
+#define NTFSBOOTCODE_SIZE 8192
+
+
+unsigned char NtfsBootCode[] = {
+235,91,144,78,84,70,83,32,32,32,32,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,250,51,192,
+142,208,188,0,124,251,184,192,7,142,216,199,6,84,0,0,
+0,199,6,86,0,0,0,199,6,91,0,16,0,184,0,13,
+142,192,43,219,232,7,0,104,0,13,104,102,2,203,80,83,
+81,82,6,102,161,84,0,102,3,6,28,0,102,51,210,102,
+15,183,14,24,0,102,247,241,254,194,136,22,90,0,102,139,
+208,102,193,234,16,247,54,26,0,136,22,37,0,163,88,0,
+161,24,0,42,6,90,0,64,59,6,91,0,118,3,161,91,
+0,80,180,2,139,22,88,0,177,6,210,230,10,54,90,0,
+139,202,134,233,138,54,37,0,178,128,205,19,88,114,42,1,
+6,84,0,131,22,86,0,0,41,6,91,0,118,11,193,224,
+5,140,194,3,208,142,194,235,138,7,90,89,91,88,195,190,
+89,1,235,8,190,227,1,235,3,190,57,1,232,9,0,190,
+173,1,232,3,0,251,235,254,172,60,0,116,9,180,14,187,
+7,0,205,16,235,242,195,29,0,65,32,100,105,115,107,32,
+114,101,97,100,32,101,114,114,111,114,32,111,99,99,117,114,
+114,101,100,46,13,10,0,41,0,65,32,107,101,114,110,101,
+108,32,102,105,108,101,32,105,115,32,109,105,115,115,105,110,
+103,32,102,114,111,109,32,116,104,101,32,100,105,115,107,46,
+13,10,0,37,0,65,32,107,101,114,110,101,108,32,102,105,
+108,101,32,105,115,32,116,111,111,32,100,105,115,99,111,110,
+116,105,103,117,111,117,115,46,13,10,0,51,0,73,110,115,
+101,114,116,32,97,32,115,121,115,116,101,109,32,100,105,115,
+107,101,116,116,101,32,97,110,100,32,114,101,115,116,97,114,
+116,13,10,116,104,101,32,115,121,115,116,101,109,46,13,10,
+0,23,0,92,78,84,76,68,82,32,105,115,32,99,111,109,
+112,114,101,115,115,101,100,46,13,10,0,0,0,0,85,170,
+5,0,78,0,84,0,76,0,68,0,82,0,4,0,36,0,
+73,0,51,0,48,0,0,224,0,0,0,48,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,140,200,142,216,193,224,4,250,139,224,
+251,102,15,183,6,11,0,102,15,182,30,13,0,102,247,227,
+102,163,78,2,102,139,14,64,0,128,249,0,15,143,14,0,
+246,217,102,184,1,0,0,0,102,211,224,235,8,144,102,161,
+78,2,102,247,225,102,163,82,2,102,15,183,30,11,0,102,
+51,210,102,247,243,102,163,86,2,232,44,4,102,139,14,74,
+2,102,137,14,34,2,102,3,14,82,2,102,137,14,38,2,
+102,3,14,82,2,102,137,14,42,2,102,3,14,82,2,102,
+137,14,58,2,102,3,14,82,2,102,137,14,66,2,102,184,
+144,0,0,0,102,139,14,34,2,232,65,9,102,11,192,15,
+132,22,254,102,163,46,2,102,184,160,0,0,0,102,139,14,
+38,2,232,40,9,102,163,50,2,102,184,176,0,0,0,102,
+139,14,42,2,232,22,9,102,163,54,2,102,161,46,2,102,
+11,192,15,132,227,253,103,128,120,8,0,15,133,218,253,103,
+102,141,80,16,103,3,66,4,103,102,15,182,72,12,102,137,
+14,94,2,103,102,139,72,8,102,137,14,90,2,102,161,90,
+2,102,15,183,14,11,0,102,51,210,102,247,241,102,163,98,
+2,102,161,66,2,102,3,6,90,2,102,163,70,2,102,131,
+62,50,2,0,15,132,25,0,102,131,62,54,2,0,15,132,
+135,253,102,139,30,54,2,30,7,102,139,62,70,2,232,177,
+1,102,15,183,14,0,2,102,184,2,2,0,0,232,153,6,
+102,11,192,15,132,88,253,103,102,139,0,30,7,102,139,62,
+58,2,232,209,4,102,161,58,2,102,187,128,0,0,0,102,
+185,0,0,0,0,102,186,0,0,0,0,232,203,0,102,11,
+192,15,132,42,253,103,102,15,183,88,12,102,129,227,255,0,
+0,0,15,133,30,253,102,139,216,104,0,32,7,102,43,255,
+232,79,1,138,22,36,0,184,232,3,142,192,141,54,11,0,
+43,192,104,0,32,80,203,80,83,81,82,6,255,54,91,0,
+255,54,84,0,255,54,86,0,139,195,193,232,4,140,193,3,
+193,37,255,15,45,0,16,247,216,139,14,91,0,193,225,5,
+81,59,193,118,2,139,193,80,193,232,5,163,91,0,232,61,
+252,88,89,43,200,118,11,140,194,3,208,142,194,184,0,16,
+235,222,143,6,86,0,143,6,84,0,143,6,91,0,7,90,
+89,91,88,195,6,30,102,96,102,139,218,102,15,182,14,13,
+0,102,247,225,102,163,84,0,102,139,195,102,247,225,163,91,
+0,139,223,131,227,15,140,192,102,193,239,4,3,199,80,7,
+232,116,255,102,97,144,31,7,195,103,3,64,20,103,102,131,
+56,255,15,132,76,0,103,102,57,24,15,133,51,0,102,11,
+201,15,133,10,0,103,128,120,9,0,15,133,35,0,195,103,
+58,72,9,15,133,26,0,102,139,240,103,3,112,10,232,61,
+5,102,81,30,7,102,139,250,243,167,102,89,15,133,1,0,
+195,103,102,131,120,4,0,15,132,7,0,103,102,3,64,4,
+235,171,102,43,192,195,102,139,243,232,18,5,103,102,3,0,
+103,247,64,12,2,0,15,133,52,0,103,102,141,80,16,103,
+58,74,64,15,133,24,0,103,102,141,114,66,232,239,4,102,
+81,30,7,102,139,251,243,167,102,89,15,133,1,0,195,103,
+131,120,8,0,15,132,6,0,103,3,64,8,235,194,102,51,
+192,195,103,128,123,8,0,15,133,28,0,6,30,102,96,103,
+102,141,83,16,103,102,139,10,102,139,243,103,3,114,4,243,
+164,102,97,144,31,7,195,103,102,141,83,16,103,102,139,74,
+8,102,65,102,43,192,232,1,0,195,6,30,102,96,103,128,
+123,8,1,15,132,3,0,233,127,251,102,131,249,0,15,133,
+6,0,102,97,144,31,7,195,102,83,102,80,102,81,102,87,
+6,232,87,3,102,139,209,7,102,95,102,89,102,59,202,15,
+141,3,0,102,139,209,232,171,254,102,43,202,102,139,218,102,
+139,194,102,15,182,22,13,0,102,247,226,102,15,183,22,11,
+0,102,247,226,102,3,248,102,88,102,3,195,102,91,235,170,
+6,30,102,96,103,128,123,8,1,15,132,3,0,233,25,251,
+102,131,249,0,15,133,6,0,102,97,144,31,7,195,102,83,
+102,80,102,81,102,87,6,102,81,102,51,210,102,15,182,14,
+13,0,102,247,241,102,82,232,225,2,102,15,182,30,13,0,
+102,247,227,102,90,102,3,194,102,80,102,15,182,6,13,0,
+102,247,225,102,139,208,102,88,102,89,7,102,95,102,89,102,
+59,202,15,141,3,0,102,139,209,102,163,84,0,137,22,91,
+0,6,30,102,96,139,223,131,227,15,140,192,102,193,239,4,
+3,199,80,7,232,160,253,102,97,144,31,7,102,43,202,102,
+139,218,102,139,194,102,15,183,22,11,0,102,247,226,102,3,
+248,102,88,102,3,195,102,91,233,101,255,6,30,102,96,38,
+103,102,15,183,95,4,38,103,102,15,183,79,6,102,11,201,
+15,132,101,250,102,3,223,102,131,195,2,102,129,199,254,1,
+0,0,102,73,102,11,201,15,132,23,0,38,103,139,3,38,
+103,137,7,102,131,195,2,102,129,199,0,2,0,0,102,73,
+235,226,102,97,144,31,7,195,6,30,102,96,102,184,1,0,
+0,0,102,163,30,2,102,161,26,2,102,3,6,82,2,102,
+163,74,2,102,161,48,0,102,15,182,30,13,0,102,247,227,
+102,163,84,0,102,161,86,2,163,91,0,102,139,30,26,2,
+30,7,232,242,252,102,15,183,251,232,111,255,102,161,26,2,
+102,187,32,0,0,0,102,185,0,0,0,0,102,186,0,0,
+0,0,232,100,253,102,11,192,15,132,87,0,102,139,216,30,
+7,102,139,62,22,2,232,249,253,102,139,30,22,2,103,102,
+129,59,128,0,0,0,15,132,6,0,103,3,91,4,235,238,
+103,102,129,59,128,0,0,0,15,133,39,0,102,83,103,102,
+139,67,16,102,139,62,74,2,30,7,232,9,1,102,91,102,
+161,82,2,102,1,6,74,2,102,255,6,30,2,103,3,91,
+4,235,205,102,97,144,31,7,195,102,139,208,102,139,14,30,
+2,102,161,26,2,102,82,102,80,102,81,102,82,102,187,128,
+0,0,0,102,185,0,0,0,0,102,186,0,0,0,0,232,
+215,252,102,11,192,15,132,64,249,102,139,216,102,88,232,42,
+1,102,11,192,15,132,7,0,102,91,102,91,102,91,195,102,
+89,102,88,102,90,102,3,6,82,2,226,185,102,51,192,195,
+6,30,102,96,102,80,102,81,102,51,210,102,15,182,30,13,
+0,102,247,243,102,82,232,144,255,102,11,192,15,132,249,248,
+102,15,182,30,13,0,102,247,227,102,90,102,3,194,102,163,
+84,0,102,89,102,15,182,30,13,0,102,59,203,15,142,19,
+0,137,30,91,0,102,43,203,102,88,102,3,195,102,80,102,
+81,235,20,144,102,88,102,3,193,102,80,137,14,91,0,102,
+185,0,0,0,0,102,81,6,102,87,139,223,131,227,15,140,
+192,102,193,239,4,3,199,80,7,232,155,251,102,95,7,102,
+3,62,78,2,102,89,102,88,102,131,249,0,15,143,116,255,
+102,97,144,31,7,195,6,30,102,96,102,247,38,86,2,102,
+139,14,86,2,232,89,255,232,241,253,102,97,144,31,7,195,
+6,30,102,96,102,247,38,98,2,102,139,30,50,2,102,139,
+14,98,2,30,7,102,139,62,66,2,232,35,253,232,203,253,
+102,97,144,31,7,195,102,80,102,83,102,81,102,139,30,70,
+2,102,139,200,102,193,232,3,102,131,225,7,102,3,216,102,
+184,1,0,0,0,102,211,224,103,132,3,15,132,4,0,248,
+235,2,144,249,102,89,102,91,102,88,195,103,128,123,8,1,
+15,132,4,0,102,43,192,195,103,102,141,115,16,103,102,139,
+86,8,102,59,194,15,135,11,0,103,102,139,22,102,59,194,
+15,131,4,0,102,43,192,195,103,3,94,16,102,43,246,103,
+128,59,0,15,132,62,0,232,129,0,102,3,241,232,57,0,
+102,3,202,102,59,193,15,140,33,0,102,139,209,102,80,103,
+102,15,182,11,102,139,193,102,131,224,15,102,193,233,4,102,
+3,217,102,3,216,102,67,102,88,235,196,102,43,200,102,43,
+194,102,3,198,195,102,43,192,195,102,43,201,103,138,11,128,
+225,15,102,131,249,0,15,133,4,0,102,43,201,195,102,83,
+102,82,102,3,217,103,102,15,190,19,102,73,102,75,102,131,
+249,0,15,132,13,0,102,193,226,8,103,138,19,102,75,102,
+73,235,235,102,139,202,102,90,102,91,195,102,83,102,82,102,
+43,210,103,138,19,102,131,226,15,102,43,201,103,138,11,192,
+233,4,102,131,249,0,15,133,8,0,102,43,201,102,90,102,
+91,195,102,3,218,102,3,217,103,102,15,190,19,102,73,102,
+75,102,131,249,0,15,132,13,0,102,193,226,8,103,138,19,
+102,75,102,73,235,235,102,139,202,102,90,102,91,195,102,11,
+201,15,133,1,0,195,102,81,102,86,103,131,62,97,15,140,
+12,0,103,131,62,122,15,143,4,0,103,131,46,32,102,131,
+198,2,226,230,102,94,102,89,195,102,80,102,81,102,139,208,
+102,161,46,2,103,102,141,88,16,103,3,67,4,103,102,141,
+64,16,102,139,218,232,158,250,102,11,192,15,132,5,0,102,
+89,102,89,195,102,161,50,2,102,11,192,15,133,8,0,102,
+89,102,89,102,51,192,195,102,139,22,50,2,103,102,141,82,
+16,103,102,139,66,8,102,64,102,139,30,78,2,102,247,227,
+102,51,210,102,247,54,90,2,102,80,102,88,102,11,192,15,
+132,48,0,102,72,102,80,232,28,254,114,238,232,241,253,102,
+90,102,89,102,91,102,83,102,81,102,82,102,161,66,2,103,
+102,141,64,24,232,47,250,102,11,192,116,206,102,89,102,89,
+102,89,195,102,89,102,89,102,51,192,195,6,30,102,96,102,
+139,54,66,2,102,185,32,0,0,0,102,247,193,3,0,0,
+0,15,133,3,0,232,13,0,102,173,232,105,0,226,235,102,
+97,144,31,7,195,6,30,102,96,102,51,192,102,51,219,176,
+13,180,14,187,7,0,205,16,176,10,180,14,187,7,0,205,
+16,102,97,144,31,7,195,6,30,102,96,102,11,201,15,133,
+9,0,232,208,255,102,97,144,31,7,195,102,51,192,102,51,
+219,173,180,14,187,7,0,205,16,226,240,232,183,255,102,97,
+144,31,7,195,96,172,60,0,116,9,180,14,187,7,0,205,
+16,235,242,97,144,195,6,30,102,96,102,185,8,0,0,0,
+102,139,208,102,131,226,15,102,82,102,193,232,4,226,241,102,
+185,8,0,0,0,102,88,102,131,248,9,15,143,7,0,102,
+131,192,48,235,9,144,102,131,232,10,102,131,192,65,102,51,
+219,180,14,187,7,0,205,16,226,219,176,32,180,14,187,7,
+0,205,16,102,97,144,31,7,232,96,0,195,6,30,102,96,
+102,190,22,13,0,0,232,79,245,102,97,144,31,7,195,6,
+30,102,96,102,190,38,13,0,0,232,60,245,102,97,144,31,
+7,195,6,30,102,96,102,190,54,13,0,0,232,41,245,102,
+97,144,31,7,195,6,30,102,96,102,190,70,13,0,0,232,
+22,245,102,97,144,31,7,195,6,30,102,96,102,190,86,13,
+0,0,232,3,245,102,97,144,31,7,195,102,80,102,184,0,
+0,245,255,102,64,102,11,192,117,249,102,88,195,102,81,102,
+80,102,184,5,0,0,0,30,7,102,139,249,232,71,252,102,
+139,193,102,91,102,83,102,15,183,14,12,2,102,186,14,2,
+0,0,232,68,248,102,91,102,89,102,11,192,15,133,47,0,
+102,139,193,102,139,203,102,80,102,83,232,35,0,102,91,102,
+95,102,11,192,15,132,23,0,30,7,232,9,252,102,139,199,
+102,15,183,14,12,2,102,186,14,2,0,0,232,10,248,195,
+102,81,102,187,32,0,0,0,102,185,0,0,0,0,102,186,
+0,0,0,0,232,242,247,102,11,192,15,132,82,0,102,139,
+216,30,7,102,139,62,22,2,232,135,248,30,7,102,139,30,
+22,2,102,89,38,102,57,15,15,132,46,0,38,102,131,63,
+255,15,132,45,0,38,131,127,4,0,15,132,36,0,38,102,
+15,183,71,4,3,216,139,195,37,0,128,116,215,140,192,5,
+0,8,142,192,129,227,255,127,235,202,38,102,139,71,16,195,
+102,89,102,51,192,195,68,101,98,117,103,32,80,111,105,110,
+116,32,48,13,10,0,68,101,98,117,103,32,80,111,105,110,
+116,32,49,13,10,0,68,101,98,117,103,32,80,111,105,110,
+116,32,50,13,10,0,68,101,98,117,103,32,80,111,105,110,
+116,32,51,13,10,0,68,101,98,117,103,32,80,111,105,110,
+116,32,52,13,10,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
diff --git a/private/ntos/boot/bootcode/ntfs/i386/usa/ntfsboot.inc b/private/ntos/boot/bootcode/ntfs/i386/usa/ntfsboot.inc
new file mode 100644
index 000000000..429e7d133
--- /dev/null
+++ b/private/ntos/boot/bootcode/ntfs/i386/usa/ntfsboot.inc
@@ -0,0 +1,36 @@
+; SCCSID = @(#)pinboot.inc 12.1 88/12/19
+; Message data area
+TXT_MSG_SYSINIT_BOOT_ERROR LABEL WORD
+ DW END_MSG_SYSINIT_BOOT_ERROR - TXT_MSG_SYSINIT_BOOT_ERROR - 2
+ DB 'A disk read erro'
+ DB 'r occurred.',0DH,0AH
+END_MSG_SYSINIT_BOOT_ERROR LABEL WORD
+ DB 0
+TXT_MSG_SYSINIT_FILE_NOT_FD LABEL WORD
+ DW END_MSG_SYSINIT_FILE_NOT_FD - TXT_MSG_SYSINIT_FILE_NOT_FD - 2
+ DB 'A kernel file is'
+ DB ' missing from th'
+ DB 'e disk.', 0DH, 0AH
+END_MSG_SYSINIT_FILE_NOT_FD LABEL WORD
+ DB 0
+TXT_MSG_SYSINIT_NODE LABEL WORD
+ DW END_MSG_SYSINIT_NODE - TXT_MSG_SYSINIT_NODE - 2
+ DB 'A kernel file is'
+ DB ' too discontiguo'
+ DB 'us.', 0DH, 0AH
+END_MSG_SYSINIT_NODE LABEL WORD
+ DB 0
+TXT_MSG_SYSINIT_INSER_DK LABEL WORD
+ DW END_MSG_SYSINIT_INSER_DK - TXT_MSG_SYSINIT_INSER_DK - 2
+ DB 'Insert a system '
+ DB 'diskette and res'
+ DB 'tart',0DH,0AH
+ DB 'the system.',0DH,0AH
+END_MSG_SYSINIT_INSER_DK LABEL WORD
+ DB 0
+TXT_MSG_SYSINIT_NTLDR_CMPRS LABEL WORD
+ DW END_MSG_SYSINIT_NTLDR_CMPRS - TXT_MSG_SYSINIT_NTLDR_CMPRS - 2
+ DB '\NTLDR is '
+ DB 'compressed.',0DH,0AH
+END_MSG_SYSINIT_NTLDR_CMPRS LABEL WORD
+ DB 0