summaryrefslogtreecommitdiffstats
path: root/private/ntos/boot/bootcode
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
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')
-rw-r--r--private/ntos/boot/bootcode/etfs/i386/etfsboot.asm584
-rw-r--r--private/ntos/boot/bootcode/etfs/i386/usa/bootetfs.h133
-rw-r--r--private/ntos/boot/bootcode/etfs/i386/usa/etfsboot.inc27
-rw-r--r--private/ntos/boot/bootcode/fat/i386/fatboot.asm426
-rw-r--r--private/ntos/boot/bootcode/fat/i386/usa/bootfat.h37
-rw-r--r--private/ntos/boot/bootcode/fat/i386/usa/fatboot.inc28
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/buf.inc222
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/chain.inc7
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/const.inc201
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/dir.inc286
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/dirent.inc80
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/filemode.inc105
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/fnode.inc214
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/fsstat.inc66
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/macro.inc781
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/makefile6
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/misc.inc64
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/pinboot.asm727
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/sources38
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/superb.inc242
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/tables.inc201
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/usa/boothpfs.h517
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/usa/pinboot.inc30
-rw-r--r--private/ntos/boot/bootcode/hpfs/i386/volume.inc102
-rw-r--r--private/ntos/boot/bootcode/mbr/i386/usa/bootmbr.h37
-rw-r--r--private/ntos/boot/bootcode/mbr/i386/usa/x86mboot.msg5
-rw-r--r--private/ntos/boot/bootcode/mbr/i386/x86mboot.asm133
-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
31 files changed, 8806 insertions, 0 deletions
diff --git a/private/ntos/boot/bootcode/etfs/i386/etfsboot.asm b/private/ntos/boot/bootcode/etfs/i386/etfsboot.asm
new file mode 100644
index 000000000..15d2d4a85
--- /dev/null
+++ b/private/ntos/boot/bootcode/etfs/i386/etfsboot.asm
@@ -0,0 +1,584 @@
+;++
+;
+;Copyright (c) 1995 Compaq Computer Corporation
+;
+;Module Name:
+;
+; etfsboot.asm
+;
+;Abstract:
+;
+; The ROM in the IBM PC starts the boot process by performing a hardware
+; initialization and a verification of all external devices. If an El
+; Torito CD-ROM with no-emulation support is detected, it will then load
+; the "image" pointed to in the Boot Catalog. This "image" is placed at
+; the physical address specified in the Boot Catalog (which should be 07C00h).
+;
+; The code in this "image" is responsible for locating NTLDR, loading the
+; first sector of NTLDR into memory at 2000:0000, and branching to it.
+;
+; There are only two errors possible during execution of this code.
+; 1 - NTLDR does not exist
+; 2 - BIOS read error
+;
+; In both cases, a short message is printed, and the user is prompted to
+; reboot the system.
+;
+;
+;Author:
+;
+; Steve Collins (stevec) 25-Oct-1995
+;
+;Environment:
+;
+; Image has been loaded at 7C0:0000 by BIOS.
+; Real mode
+; ISO 9660 El Torito no-emulation CD-ROM Boot support
+; DL = El Torito drive number we booted from
+;
+;Revision History:
+;
+;--
+ page ,132
+ title boot - NTLDR ETFS loader
+ name etfsboot
+
+
+BootSeg segment at 07c0h
+BootSeg ends
+
+DirSeg segment at 1000h
+DirSeg ends
+
+NtLdrSeg segment at 2000h
+NtLdrSeg ends
+
+BootCode segment ;would like to use BootSeg here, but LINK flips its lid
+ ASSUME CS:BootCode,DS:NOTHING,ES:NOTHING,SS:NOTHING
+
+ public ETFSBOOT
+ETFSBOOT proc far
+
+ xor ax,ax ; Setup the stack to a known good spot
+ mov ss,ax ; Stack is set to 0000:7c00, which is just below this code
+ mov sp,7c00h
+
+ mov ax,BootSeg ; Set DS to our code/data segment (07C0h)
+ mov ds,ax
+assume DS:BootCode
+
+;
+; Save the Drive Number for later use
+ mov DriveNum,dl
+
+;
+; The system is now prepared for us to begin reading. First, we need to
+; read in the Primary Volume Descriptor so we can locate the root directory
+;
+.286
+ push 01h ; Word 0 (low word) of Transfer size = 1 block (2048 bytes)
+ push 0h ; Word 1 (high word) of Transfer size = 0
+ push DirSeg ; Segment of Transfer buffer = DirSeg
+ push 010h ; Word 0 (low word) of Starting absolute block number = 10h
+ push 0h ; Word 1 of Starting absolute block number = 0
+.8086
+ call ExtRead
+ add sp,10 ; Clean 5 arguments off the stack
+
+;
+; Determine the root directory location LBN -> ExtentLoc1:ExtentLoc0
+; determine the root directory data length in bytes -> ExtentLen1:ExtentLen0
+;
+ mov ax,DirSeg ; ES is set to segment used for storing PVD and directories
+ mov es,ax
+ASSUME ES:DirSeg
+ mov ax,es:[09eh] ; 32-bit LBN of extent at offset 158 in Primary Volume Descriptor
+ mov ExtentLoc0,ax ; store low word
+ mov ax,es:[0a0h]
+ mov ExtentLoc1,ax ; store high word
+ mov ax,es:[0a6h] ; 32-bit Root directory data length in bytes at offset 166 in Primary Volume Descriptor
+ mov ExtentLen0,ax ; store low word
+ mov ax,es:[0a8h]
+ mov ExtentLen1,ax ; store high word
+
+;
+; Now read in the root directory
+;
+.286
+ push DirSeg ; Segment used for transfer = DirSeg
+.8086
+ call ReadExtent
+ add sp,2 ; Clean 1 argument off the stack
+
+;
+; Scan for the presence of the I386 directory
+; ES points to directory segment
+;
+ mov EntryToFind, offset I386DIRNAME
+ mov EntryLen,4
+ mov IsDir,1
+ call ScanForEntry
+
+;
+; We found the I386 directory entry, so now get its extent location (offset -31 from filename ID)
+; ES:[BX] still points to the directory record for the I386 directory
+;
+ call GetExtentInfo
+
+;
+; Now read in the I386 directory
+;
+.286
+ push DirSeg ; Segment used for transfer = DirSeg
+.8086
+ call ReadExtent
+ add sp,2 ; Clean 1 argument off the stack
+
+;
+; Scan for the presence of SETUPLDR.BIN
+; ES points to directory segment
+;
+ mov ax,DirSeg
+ mov es,ax
+ mov EntryToFind, offset LOADERNAME
+ mov EntryLen,12
+ mov IsDir,0
+ call ScanForEntry
+
+;
+; We found the loader entry, so now get its extent location (offset -31 from filename ID)
+; ES:[BX] still points to the directory record for the LOADER
+;
+ call GetExtentInfo
+
+;
+; Now, go read the file
+;
+.286
+ push NtLdrSeg ; Segment used for transfer = NtLdrSeg
+.8086
+ call ReadExtent
+ add sp,2 ; Clean 1 argument off the stack
+
+;
+; NTLDR requires:
+; DL = INT 13 drive number we booted from
+;
+ mov dl, DriveNum ; DL = CD drive number - this isn't really necessary since DirveNum is already in dl
+ xor ax,ax
+.386
+ push NtLdrSeg
+ push ax
+ retf ; "return" to NTLDR.
+
+ETFSBOOT endp
+
+
+;
+; ScanForEntry - Scan for an entry in a directory
+;
+; Entry:
+; ES:0 points to the beginning of the directory to search
+; Directory length in bytes is in ExtentLen1 and Extend_Len_0
+;
+; Exit:
+; ES:BX points to record containing entry if match is found
+; Otherwise, we jump to error routine
+;
+ScanForEntry proc near
+ mov cx,ExtentLen0 ; CX = length of root directory in bytes (low word only)
+ cld ; Work up for string compares
+ xor bx,bx
+ xor dx,dx
+ScanLoop:
+ mov si, EntryToFind
+ mov dl,byte ptr es:[bx] ; directory record length -> DL
+ cmp dl,0
+ jz Skip00 ; if the "record length" assume it is "system use" and skip it
+ mov ax,bx
+ add ax,021h ; file identifier is at offset 21h in directory record
+ mov di,ax ; ES:DI now points to file identifier
+ push cx
+ xor cx,cx
+ mov cl,EntryLen ; compare bytes
+ repe cmpsb
+ pop cx
+ jz ScanEnd ; do we have a match?
+
+CheckCountUnderFlow:
+ ; If CX is about to underflow or be 0 we need to reset CX, ES and BX if ExtentLen1 is non-0
+ cmp dx,cx
+ jae ResetCount0
+
+ sub cx,dx ; update CX to contain number of bytes left in directory
+ cmp ScanIncCount, 1
+ je ScanAdd1ToCount
+
+AdjustScanPtr: ; Adjust ES:BX to point to next record
+ add dx,bx
+ mov bx,dx
+ and bx,0fh
+ push cx
+ mov cl,4
+ shr dx,cl
+ pop cx
+ mov ax,es
+ add ax,dx
+ mov es,ax
+ jmp ScanLoop
+
+Skip00:
+ mov dx,1 ; Skip past this byte
+ jmp CheckCountUnderFlow
+
+ScanAdd1ToCount:
+ inc cx
+ mov ScanIncCount,0
+ jmp AdjustScanPtr
+
+S0:
+ mov ScanIncCount,1 ; We'll need to increment Count next time we get a chance
+ jmp SetNewCount
+
+ResetCount0:
+ cmp ExtentLen1,0 ; Do we still have at least 64K bytes left to scan?
+ je BootErr$bnf ; We overran the end of the directory - corrupt/invalid directory
+ sub ExtentLen1,1
+
+ add bx,dx ; Adjust ES:BX to point to next record - we cross seg boundary here
+ push bx
+ push cx
+ mov cl,4
+ shr bx,cl
+ pop cx
+ mov ax,es
+ add ax,bx
+ mov es,ax
+ pop bx
+ and bx,0fh
+
+ sub dx,cx ; Get overflow amount
+ je S0 ; If we ended right on the boundary we need to make special adjustments
+ dec dx
+SetNewCount:
+ mov ax,0ffffh
+ sub ax,dx ; and subtract it from 10000h
+ mov cx,ax ; - this is the new count
+ jmp ScanLoop
+
+ScanEnd:
+ cmp IsDir,1
+ je CheckDir
+
+ test byte ptr es:[bx][25],2 ; Is this a file?
+ jnz CheckCountUnderFlow ; No - go to next record
+ jmp CheckLen
+
+CheckDir:
+ test byte ptr es:[bx][25],2 ; Is this a directory?
+ jz CheckCountUnderFlow ; No - go to next record
+
+CheckLen:
+ mov al,EntryLen
+ cmp byte ptr es:[bx][32],al ; Is the identifier length correct?
+ jnz CheckCountUnderFlow ; No - go to next record
+
+ ret
+ScanForEntry endp
+
+;
+; BootErr - print error message and hang the system.
+;
+BootErr proc
+BootErr$bnf:
+ MOV SI,OFFSET MSG_NO_NTLDR
+ jmp short BootErr2
+BootErr$mof:
+ MOV SI,OFFSET MSG_MEM_OVERFLOW
+ jmp short BootErr2
+BootErr2:
+ call BootErrPrint
+ MOV SI,OFFSET MSG_REBOOT_ERROR
+ call BootErrPrint
+ sti
+ jmp $ ;Wait forever
+
+BootErrPrint:
+ LODSB ; Get next character
+ or al,al
+ jz BEdone
+
+ MOV AH,14 ; Write teletype
+ MOV BX,7 ; Attribute
+ INT 10H ; Print it
+ jmp BootErrPrint
+BEdone:
+
+ ret
+BootErr endp
+
+;
+; ExtRead - Do an INT 13h extended read
+; NOTE: I force the offset of the Transfer buffer address to be 0
+; I force the high 2 words of the Starting absolute block number to be 0
+; - This allows for a max 4 GB medium - a safe assumption for now
+;
+; Entry:
+; Arg1 - word 0 (low word) of Number of 2048-byte blocks to transfer
+; Arg2 - word 1 (high word) of Number of 2048-byte blocks to transfer
+; Arg3 - segment of Transfer buffer address
+; Arg4 - word 0 (low word) of Starting absolute block number
+; Arg5 - word 1 of Starting absolute block number
+;
+; Exit
+; The following are modified:
+; Count0
+; Count1
+; Dest
+; Source0
+; Source1
+; PartialRead
+; NumBlocks
+; Disk Address Packet [DiskAddPack]
+;
+ExtRead proc near
+ push bp ; set up stack frame so we can get args
+ mov bp,sp
+
+ push bx ; Save registers used during this routine
+ push si
+ push dx
+ push ax
+
+ mov bx,offset DiskAddPack ; Use BX as base to index into Disk Address Packet
+
+ ; Set up constant fields
+ mov [bx][0],byte ptr 010h ; Offset 0: Packet size = 16 bytes
+ mov [bx][1],byte ptr 0h ; Offset 1: Reserved (must be 0)
+ mov [bx][3],byte ptr 0h ; Offset 3: Reserved (must be 0)
+ mov [bx][4],word ptr 0h ; Offset 4: Offset of Transfer buffer address (force 0)
+ mov [bx][12],word ptr 0h ; Offset 12: Word 2 of Starting absolute block number (force 0)
+ mov [bx][14],word ptr 0h ; Offset 14: Word 3 (high word) of Starting absolute block number (force 0)
+
+;
+; Initialize loop variables
+;
+ mov ax,[bp][12] ; set COUNT to number of blocks to transfer
+ mov Count0,ax
+ mov ax,[bp][10]
+ mov Count1,ax
+
+ mov ax,[bp][8] ; set DEST to destination segment
+ mov Dest,ax
+
+ mov ax,[bp][6] ; set SOURCE to source lbn
+ mov Source0,ax
+ mov ax,[bp][4]
+ mov Source1,ax
+
+ExtReadLoop:
+;
+; First check if COUNT <= 32
+;
+ cmp Count1,word ptr 0h ; Is upper word 0?
+ jne SetupPartialRead ; No - we're trying to read at least 64K blocks (128 MB)
+ cmp Count0,word ptr 20h ; Is lower word greater than 32?
+ jg SetupPartialRead ; Yes - only read in 32-block increments
+
+ mov PartialRead,0 ; Clear flag to indicate we are doing a full read
+
+ mov ax,Count0 ; NUMBLOCKS = COUNT
+ mov NumBlocks,al ; Since Count0 < 32 we're OK just using low byte
+
+ jmp DoExtRead ; Do read
+
+SetupPartialRead:
+;
+; Since COUNT > 32,
+; Set flag indicating we are only doing a partial read
+;
+ mov PartialRead,1
+
+ mov NumBlocks,20h ; NUMBYTES = 32
+
+DoExtRead:
+;
+; Perform Extended Read
+;
+ mov al,NumBlocks ; Offset 2: Number of 2048-byte blocks to transfer
+ mov [bx][2],al
+ mov ax,Dest ; Offset 6: Segment of Transfer buffer address
+ mov [bx][6],ax
+ mov ax,Source0 ; Offset 8: Word 0 (low word) of Starting absolute block number
+ mov [bx][8],ax
+ mov ax,Source1 ; Offset 10: Word 1 of Starting absolute block number
+ mov [bx][10],ax
+
+ mov si,offset DiskAddPack ; Disk Address Packet in DS:SI
+ mov ah,042h ; Function = Extended Read
+ mov dl,DriveNum ; CD-ROM drive number
+ int 13h
+
+;
+; Determine if we are done reading
+;
+ cmp PartialRead,1 ; Did we just do a partial read?
+ jne ExtReadDone ; No - we're done
+
+ReadjustValues:
+;
+; We're not done reading yet, so
+; COUNT = COUNT - 32
+;
+ sub Count0,020h ; Subtract low-order words
+ sbb Count1,0h ; Subtract high-order words
+
+;
+; Just read 32 blocks and have more to read
+; Increment DEST to next 64K segment (this equates to adding 1000h to the segment)
+;
+ add Dest,1000h
+ jc BootErr$mof ; Error if we overflowed
+
+;
+; SOURCE = SOURCE + 32 blocks
+;
+ add Source0,word ptr 020h ; Add low order words
+ adc Source1,word ptr 0h ; Add high order words
+ ; NOTE - I don't account for overflow - probably OK now since we already account for 4 GB medium
+
+;
+; jump back to top of loop to do another read
+;
+ jmp ExtReadLoop
+
+ExtReadDone:
+
+ pop ax ; Restore registers used during this routine
+ pop dx
+ pop si
+ pop bx
+
+ mov sp,bp ; restore BP and SP
+ pop bp
+
+ ret
+ExtRead endp
+
+;
+; ReadExtent - Read in an extent
+;
+; Arg1 - segment to transfer extent to
+;
+; Entry:
+; ExtentLen0 = word 0 (low word) of extent length in bytes
+; ExtentLen1 = word 1 (high word) of extent length in bytes
+; ExtentLoc0 = word 0 (low word) of starting absolute block number of extent
+; ExtentLoc1 = word 1 of starting absolute block number of extent
+;
+; Exit:
+; ExtRead exit mods
+;
+ReadExtent proc near
+ push bp ; set up stack frame so we can get args
+ mov bp,sp
+
+ push cx ; Save registers used during this routine
+ push bx
+ push ax
+
+ mov cl,11 ; Convert length in bytes to 2048-byte blocks
+ mov bx,ExtentLen1 ; Directory length = BX:AX
+ mov ax,ExtentLen0
+
+.386
+ shrd ax,bx,cl ; Shift AX, filling with BX
+.8086
+ shr bx,cl ; BX:AX = number of blocks (rounded down)
+ test ExtentLen0,07ffh ; If any of the low-order 11 bits are set we need to round up
+ jz ReadExtentNoRoundUp
+ add ax,1 ; We need to round up by incrementing AX, and
+ adc bx,0 ; adding the carry to BX
+ReadExtentNoRoundUp:
+
+ push ax ; Word 0 (low word) of Transfer size = AX
+ push bx ; Word 1 (high word) of Transfer size = BX
+.286
+ push [bp][4] ; Segment used to transfer extent
+.8086
+ push ExtentLoc0 ; Word 0 (low word) of Starting absolute block number
+ push ExtentLoc1 ; Word 1 of Starting absolute block number
+ call ExtRead
+ add sp,10 ; Clean 5 arguments off the stack
+
+ pop ax ; Restore registers used during this routine
+ pop bx
+ pop cx
+
+ mov sp,bp ; restore BP and SP
+ pop bp
+
+ ret
+ReadExtent endp
+
+;
+; GetExtentInfo - Get extent location
+;
+; Entry:
+; ES:BX points to record
+; Exit:
+; Location -> ExtentLoc1 and ExtentLoc0
+; Length -> ExtentLen1 and ExtentLen0
+;
+GetExtentInfo proc near
+ push ax ; Save registers used during this routine
+
+ mov ax,es:[bx][2] ; 32-bit LBN of extent
+ mov ExtentLoc0,ax ; store low word
+ mov ax,es:[bx][4]
+ mov ExtentLoc1,ax ; store high word
+ mov ax,es:[bx][10] ; 32-bit file length in bytes
+ mov ExtentLen0,ax ; store low word
+ mov ax,es:[bx][12]
+ mov ExtentLen1,ax ; store high word
+
+ pop ax ; Restore registers used during this routine
+
+ ret
+GetExtentInfo endp
+
+
+include etfsboot.inc ; message text
+
+DiskAddPack db 16 dup (?) ; Disk Address Packet
+PartialRead db 0 ; Boolean indicating whether or not we are doing a partial read
+LOADERNAME db "SETUPLDR.BIN"
+I386DIRNAME db "I386"
+DriveNum db (?) ; Drive number used for INT 13h extended reads
+ExtentLoc0 dw (?) ; Loader LBN - low word
+ExtentLoc1 dw (?) ; Loader LBN - high word
+ExtentLen0 dw (?) ; Loader Length - low word
+ExtentLen1 dw (?) ; Loader Length - high word
+Count0 dw (?) ; Read Count - low word
+Count1 dw (?) ; Read Count - high word
+Dest dw (?) ; Read Destination segment
+Source0 dw (?) ; Read Source - word 0 (low word)
+Source1 dw (?) ; Read Source - word 1
+NumBlocks db (?) ; Number of blocks to Read
+EntryToFind dw (?) ; Offset of string trying to match in ScanForEntry
+EntryLen db (?) ; Length in bytes of entry to match in ScanForEntry
+IsDir db (?) ; Boolean indicating whether or not entry to match in ScanForEntry is a directory
+ScanIncCount db 0 ; Boolean indicating if we need to add 1 to Count after adjustment in ScanForEntry
+
+ .errnz ($-ETFSBOOT) GT 2046 ; FATAL PROBLEM: boot sector is too large
+
+ org 2046
+ db 55h,0aah
+
+BootSectorEnd label dword
+
+BootCode ends
+
+
+ END ETFSBOOT
+
diff --git a/private/ntos/boot/bootcode/etfs/i386/usa/bootetfs.h b/private/ntos/boot/bootcode/etfs/i386/usa/bootetfs.h
new file mode 100644
index 000000000..65d3066a9
--- /dev/null
+++ b/private/ntos/boot/bootcode/etfs/i386/usa/bootetfs.h
@@ -0,0 +1,133 @@
+#define ETFSBOOTCODE_SIZE 2048
+
+
+unsigned char EtfsBootCode[] = {
+51,192,142,208,188,0,124,184,192,7,142,216,136,22,29,3,
+106,1,106,0,104,0,16,106,16,106,0,232,103,1,131,196,
+10,184,0,16,142,192,38,161,158,0,163,30,3,38,161,160,
+0,163,32,3,38,161,166,0,163,34,3,38,161,168,0,163,
+36,3,104,0,16,232,4,2,131,196,2,199,6,49,3,25,
+3,198,6,51,3,4,144,198,6,52,3,1,144,232,61,0,
+232,37,2,104,0,16,232,227,1,131,196,2,184,0,16,142,
+192,199,6,49,3,13,3,198,6,51,3,12,144,198,6,52,
+3,0,144,232,23,0,232,255,1,104,0,32,232,189,1,131,
+196,2,138,22,29,3,51,192,104,0,32,80,203,139,14,34,
+3,252,51,219,51,210,139,54,49,3,38,138,23,128,250,0,
+15,132,59,0,139,195,5,33,0,139,248,81,51,201,138,14,
+51,3,243,166,89,15,132,109,0,59,209,15,131,55,0,43,
+202,128,62,53,3,1,15,132,26,0,3,211,139,218,131,227,
+15,81,177,4,211,234,89,140,192,3,194,142,192,235,183,186,
+1,0,235,213,65,198,6,53,3,0,144,235,221,198,6,53,
+3,1,144,235,39,144,131,62,36,3,0,15,132,79,0,131,
+46,36,3,1,3,218,83,81,177,4,211,235,89,140,192,3,
+195,142,192,91,131,227,15,43,209,116,210,74,184,255,255,43,
+194,139,200,233,112,255,128,62,52,3,1,15,132,10,0,38,
+246,71,25,2,117,131,235,10,144,38,246,71,25,2,15,132,
+119,255,160,51,3,38,56,71,32,15,133,108,255,195,190,167,
+2,235,5,190,195,2,235,0,232,9,0,190,225,2,232,3,
+0,251,235,254,172,10,192,15,132,9,0,180,14,187,7,0,
+205,16,235,240,195,85,139,236,83,86,82,80,187,252,2,198,
+7,16,198,71,1,0,198,71,3,0,199,71,4,0,0,199,
+71,12,0,0,199,71,14,0,0,139,70,12,163,38,3,139,
+70,10,163,40,3,139,70,8,163,42,3,139,70,6,163,44,
+3,139,70,4,163,46,3,129,62,40,3,0,0,15,133,25,
+0,129,62,38,3,32,0,15,143,15,0,198,6,12,3,0,
+144,161,38,3,162,48,3,235,13,144,198,6,12,3,1,144,
+198,6,48,3,32,144,160,48,3,136,71,2,161,42,3,137,
+71,6,161,44,3,137,71,8,161,46,3,137,71,10,190,252,
+2,180,66,138,22,29,3,205,19,128,62,12,3,1,15,133,
+34,0,131,46,38,3,32,131,30,40,3,0,129,6,42,3,
+0,16,15,130,45,255,129,6,44,3,32,0,129,22,46,3,
+0,0,235,131,88,90,94,91,139,229,93,195,85,139,236,81,
+83,80,177,11,139,30,36,3,161,34,3,15,173,216,211,235,
+247,6,34,3,255,7,116,6,5,1,0,131,211,0,80,83,
+255,118,4,255,54,30,3,255,54,32,3,232,7,255,131,196,
+10,88,91,89,139,229,93,195,80,38,139,71,2,163,30,3,
+38,139,71,4,163,32,3,38,139,71,10,163,34,3,38,139,
+71,12,163,36,3,88,195,66,79,79,84,58,32,67,111,117,
+108,100,110,39,116,32,102,105,110,100,32,78,84,76,68,82,
+13,10,0,66,79,79,84,58,32,77,101,109,111,114,121,32,
+111,118,101,114,102,108,111,119,32,101,114,114,111,114,13,10,
+0,80,108,101,97,115,101,32,105,110,115,101,114,116,32,97,
+110,111,116,104,101,114,32,100,105,115,107,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,83,69,84,
+85,80,76,68,82,46,66,73,78,73,51,56,54,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,170
+};
diff --git a/private/ntos/boot/bootcode/etfs/i386/usa/etfsboot.inc b/private/ntos/boot/bootcode/etfs/i386/usa/etfsboot.inc
new file mode 100644
index 000000000..2273eb55d
--- /dev/null
+++ b/private/ntos/boot/bootcode/etfs/i386/usa/etfsboot.inc
@@ -0,0 +1,27 @@
+;++
+;
+;Copyright (c) 1995 Compaq Computer Corporation
+;
+;Module Name:
+;
+; etfsboot.inc
+;
+;Abstract:
+;
+; This contains the message text that the boot sector prints out on
+; error conditions
+;
+;Author:
+;
+; Steve Collins (stevec) 25-Oct-1995
+;
+;Revision History:
+;
+;--
+
+MSG_NO_NTLDR db "BOOT: Couldn't find NTLDR"
+ db 0dh, 0ah, 0
+MSG_MEM_OVERFLOW db "BOOT: Memory overflow error"
+ db 0dh, 0ah, 0
+MSG_REBOOT_ERROR db "Please insert another disk"
+ db 0
diff --git a/private/ntos/boot/bootcode/fat/i386/fatboot.asm b/private/ntos/boot/bootcode/fat/i386/fatboot.asm
new file mode 100644
index 000000000..b349538bd
--- /dev/null
+++ b/private/ntos/boot/bootcode/fat/i386/fatboot.asm
@@ -0,0 +1,426 @@
+;++
+;
+;Copyright (c) 1991 Microsoft Corporation
+;
+;Module Name:
+;
+; fatboot.asm
+;
+;Abstract:
+;
+; 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 code in this sector is responsible for locating NTLDR, loading the
+; first sector of NTLDR into memory at 2000:0000, and branching to it. The
+; first sector of NTLDR is special code which knows enough about FAT and
+; BIOS to load the rest of NTLDR into memory.
+;
+; There are only two errors possible during execution of this code.
+; 1 - NTLDR does not exist
+; 2 - BIOS read error
+;
+; In both cases, a short message is printed, and the user is prompted to
+; reboot the system.
+;
+; At the beginning of the boot sector, there is a table which describes the
+; structure of the media. This is equivalent to the BPB with some
+; additional information describing the physical layout of the driver (heads,
+; tracks, sectors)
+;
+;
+;Author:
+;
+; John Vert (jvert) 31-Aug-1991
+;
+;Environment:
+;
+; Sector has been loaded at 7C0:0000 by BIOS.
+; Real mode
+; FAT file system
+;
+;Revision History:
+;
+;--
+ page ,132
+ title boot - NTLDR FAT loader
+ name fatboot
+
+DIR_ENT struc
+ Filename db 11 dup(?)
+ Attribute db ?
+ Reserved db 10 dup(?)
+ Time dw 2 dup(?)
+ StartCluster dw ?
+ FileSize dd ?
+DIR_ENT ends
+
+;
+; This is the structure used to pass all shared data between the boot sector
+; and NTLDR.
+;
+
+SHARED struc
+ ReadClusters dd ? ; function pointer
+ ReadSectors dd ? ; function pointer
+ SectorBase dd ? ; starting sector
+SHARED ends
+
+
+
+DoubleWord struc
+lsw dw ?
+msw dw ?
+DoubleWord ends
+
+SectorSize equ 512 ; sector size
+
+BootSeg segment at 07c0h
+BootSeg ends
+
+DirSeg segment at 1000h
+DirSeg ends
+
+NtLdrSeg segment at 2000h
+NtLdrSeg ends
+
+BootCode segment ;would like to use BootSeg here, but LINK flips its lid
+ ASSUME CS:BootCode,DS:NOTHING,ES:NOTHING,SS:NOTHING
+
+ public FATBOOT
+FATBOOT proc far
+
+ jmp Start
+
+;
+; THE FOLLOWING DATA CONFIGURES THE BOOT PROGRAM
+; FOR ANY TYPE OF DRIVE OR HARDFILE
+;
+; Note that this data is just a place-holder here. The actual values will
+; be filled in by FORMAT or SYS. When installing the boot sector, only the
+; code following the BPB (from Start to the end) should be copied into the
+; first sector.
+;
+
+Version db "MSDOS5.0"
+BPB label byte
+BytesPerSector dw SectorSize ; Size of a physical sector
+SectorsPerCluster db 8 ; Sectors per allocation unit
+ReservedSectors dw 1 ; Number of reserved sectors
+Fats db 2 ; Number of fats
+DirectoryEntries dw 512 ; Number of directory entries
+Sectors dw 4*17*305-1 ; No. of sectors - no. of hidden sectors
+Media db 0F8H ; Media byte
+FatSectors dw 8 ; Number of fat sectors
+SectorsPerTrack dw 17 ; Sectors per track
+Heads dw 4 ; Number of surfaces
+HiddenSectors dd 1 ; Number of hidden sectors
+SectorsLong dd 0 ; Number of sectors iff Sectors = 0
+
+;
+; The following byte is NOT part of the BPB but is set by SYS and format
+; We should NOT change its position.
+;
+
+; keep order of DriveNumber and CurrentHead!
+DriveNumber db 80h ; Physical drive number (0 or 80h)
+CurrentHead db ? ; Unitialized
+
+Signature db 41 ; Signature Byte for bootsector
+BootID dd ? ; Boot ID field.
+Boot_Vol_Label db 11 dup (?)
+Boot_System_ID db 'FAT ' ;"FAT " or "OTHER_FS"
+
+
+Start:
+ xor ax,ax ; Setup the stack to a known good spot
+ mov ss,ax
+ mov sp,7c00h
+
+.386
+ push BootSeg
+.8086
+ pop ds
+assume DS:BootCode
+
+; The system is now prepared for us to begin reading. First, determine
+; logical sector numbers of the start of the directory and the start of the
+; data area.
+;
+ MOV AL,Fats ;Determine sector root directory starts on
+ MUL FatSectors
+;##### what if result > 65535 ?????
+ ADD AX,ReservedSectors
+;##### what if result > 65535 ?????
+ PUSH AX ; AX = Fats*FatSectors + ReservedSectors + HiddenSectors
+ XCHG CX,AX ; (CX) = start of DIR
+;
+; Take into account size of directory (only know number of directory entries)
+;
+ MOV AX,size DIR_ENT ; bytes per directory entry
+ MUL DirectoryEntries ; convert to bytes in directory
+ MOV BX,BytesPerSector ; add in sector size
+ ADD AX,BX
+ DEC AX ; decrement so that we round up
+ DIV BX ; convert to sector number
+ ADD CX,AX
+ MOV ClusterBase,CX ; save it for later
+;
+; Load in the root directory.
+;
+.386
+ push DirSeg ; es:bx -> directory segment
+.8086
+ pop es
+ASSUME ES:DirSeg
+ xor bx,bx
+ pop Arguments.SectorBase.lsw
+ mov Arguments.SectorBase.msw,bx
+
+;
+; DoRead does a RETF, but LINK pukes if we do a FAR call in a /tiny program.
+;
+; (al) = # of sectors to read
+;
+ push cs
+ call DoRead
+ jc BootErr$he
+
+; Now we scan for the presence of NTLDR
+
+ xor bx,bx
+ mov cx,DirectoryEntries
+L10:
+ mov di,bx
+ push cx
+ mov cx,11
+ mov si, offset LOADERNAME
+ repe cmpsb
+ pop cx
+ jz L10end
+
+ add bx,size DIR_ENT
+ loop L10
+L10end:
+
+ jcxz BootErr$bnf
+
+ mov dx,es:[bx].StartCluster ; (dx) = starting cluster number
+ push dx
+ mov ax,1 ; (al) = sectors to read
+;
+; Now, go read the file
+;
+
+.386
+ push NtLdrSeg
+.8086
+ pop es
+ ASSUME ES:NtLdrSeg
+ xor bx,bx ; (es:bx) -> start of NTLDR
+
+
+;
+; LINK barfs if we do a FAR call in a TINY program, so we have to fake it
+; out by pushing CS.
+;
+
+ push cs
+ call ClusterRead
+ jc BootErr$he
+
+;
+; NTLDR requires:
+; BX = Starting Cluster Number of NTLDR
+; DL = INT 13 drive number we booted from
+; DS:SI -> the boot media's BPB
+; DS:DI -> argument structure
+; 1000:0000 - entire FAT is loaded
+;
+
+ pop BX ; (bx) = Starting Cluster Number
+ lea si,BPB ; ds:si -> BPB
+ lea di,Arguments ; ds:di -> Arguments
+
+ push ds
+ pop [di].ReadClusters.msw
+ mov [di].ReadClusters.lsw, offset ClusterRead
+ push ds
+ pop [di].ReadSectors.msw
+ mov [di].ReadSectors.lsw, offset DoRead
+ MOV dl,DriveNumber ; dl = boot drive
+
+;
+; FAR JMP to 2000:0003. This is hand-coded, because I can't figure out
+; how to make MASM do this for me. By entering NTLDR here, we skip the
+; initial jump and execute the FAT-specific code to load the rest of
+; NTLDR.
+;
+ db 0EAh ; JMP FAR PTR
+ dw 3 ; 2000:3
+ dw 02000h
+FATBOOT endp
+
+; BootErr - print error message and hang the system.
+;
+BootErr proc
+BootErr$bnf:
+ MOV SI,OFFSET MSG_NO_NTLDR
+ jmp short BootErr2
+BootErr$he:
+ MOV SI,OFFSET MSG_READ_ERROR
+BootErr2:
+ call BootErrPrint
+ MOV SI,OFFSET MSG_REBOOT_ERROR
+ call BootErrPrint
+ sti
+ jmp $ ;Wait forever
+
+BootErrPrint:
+ LODSB ; Get next character
+ or al,al
+ jz BEdone
+
+ MOV AH,14 ; Write teletype
+ MOV BX,7 ; Attribute
+ INT 10H ; Print it
+ jmp BootErrPrint
+BEdone:
+
+ ret
+BootErr endp
+
+; ClusterRead - read AL sectors into ES:BX starting from
+; cluster DX
+;
+ClusterRead proc
+ push ax ; (TOS) = # of sectors to read
+ dec dx
+ dec dx ; adjust for reserved clusters 0 and 1
+ mov al,SectorsPerCluster
+ xor ah,ah
+ mul dx ; DX:AX = starting sector number
+ add ax,ClusterBase ; adjust for FATs, root dir, boot sec.
+ adc dx,0
+ mov Arguments.SectorBase.lsw,ax
+ mov Arguments.SectorBase.msw,dx
+ pop ax ; (al) = # of sectors to read
+
+;
+; Now we've converted the cluster number to a SectorBase, so just fall
+; through into DoRead
+;
+
+ClusterRead endp
+
+
+;
+; DoRead - read AL sectors into ES:BX starting from sector
+; SectorBase.
+;
+DoRead proc
+
+ mov SectorCount,AL
+DRloop:
+ MOV AX,Arguments.SectorBase.lsw ; Starting sector
+ MOV DX,Arguments.SectorBase.msw ; Starting sector
+;
+; DoDiv - convert logical sector number in AX to physical Head/Track/Sector
+; in CurrentHead/CurrentTrack/CurrentSector.
+;
+ ADD AX,HiddenSectors.lsw ;adjust for partition's base sector
+ ADC DX,HiddenSectors.msw
+ DIV SectorsPerTrack
+ INC DL ; sector numbers are 1-based
+ MOV CurrentSector,DL
+ XOR DX,DX
+ DIV Heads
+ MOV CurrentHead,DL
+ MOV CurrentTrack,AX
+;
+;DoDiv endp
+;
+
+
+; 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
+ SUB AL,CurrentSector
+ INC AX
+ cmp al,SectorCount
+ jbe DoCall
+ mov al,SectorCount
+ xor ah,ah
+
+; AX is the number of sectors that we may read.
+
+;
+; DoCall - call ROM BIOS to read AL sectors into ES:BX.
+;
+DoCall:
+ PUSH AX
+ MOV AH,2
+ MOV cx,CurrentTrack
+.386
+ SHL ch,6
+.8086
+ OR ch,CurrentSector
+ XCHG CH,CL
+ MOV DX,WORD PTR DriveNumber
+ INT 13H
+;
+;DoCall endp
+;
+
+.386
+ jnc DcNoErr
+ add sp,2
+ stc
+ retf
+.8086
+
+DcNoErr:
+ POP AX
+ SUB SectorCount,AL ; Are we finished?
+ jbe DRdone
+ ADD Arguments.SectorBase.lsw,AX ; increment logical sector position
+ ADC Arguments.SectorBase.msw,0
+ MUL BytesPerSector ; determine next offset for read
+ ADD BX,AX ; (BX)=(BX)+(SI)*(Bytes per sector)
+
+ jmp DRloop
+DRdone:
+ mov SectorCount,al
+ clc
+ retf
+DoRead endp
+
+ include fatboot.inc ;suck in the message text
+
+LOADERNAME DB "NTLDR "
+
+ .errnz ($-FATBOOT) GT 510,<FATAL PROBLEM: boot sector is too large>
+
+ org 510
+ db 55h,0aah
+
+BootSectorEnd label dword
+
+BootCode ends
+
+;Unitialized variables go here--beyond the end of the boot sector in free memory
+CurrentTrack equ word ptr BootSectorEnd + 4 ; current track
+CurrentSector equ byte ptr BootSectorEnd + 6 ; current sector
+SectorCount equ byte ptr BootSectorEnd + 7 ; number of sectors to read
+ClusterBase equ word ptr BootSectorEnd + 8 ; first sector of cluster # 2
+Retries equ byte ptr BootSectorEnd + 10
+Arguments equ byte ptr BootSectorEnd + 11 ; structure passed to NTLDR
+
+ END FATBOOT
diff --git a/private/ntos/boot/bootcode/fat/i386/usa/bootfat.h b/private/ntos/boot/bootcode/fat/i386/usa/bootfat.h
new file mode 100644
index 000000000..9ccbd1a4a
--- /dev/null
+++ b/private/ntos/boot/bootcode/fat/i386/usa/bootfat.h
@@ -0,0 +1,37 @@
+#define FATBOOTCODE_SIZE 512
+
+
+unsigned char FatBootCode[] = {
+235,60,144,77,83,68,79,83,53,46,48,0,2,8,1,0,
+2,0,2,3,81,248,8,0,17,0,4,0,1,0,0,0,
+0,0,0,0,128,0,41,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,70,65,84,32,32,32,32,32,51,192,
+142,208,188,0,124,104,192,7,31,160,16,0,247,38,22,0,
+3,6,14,0,80,145,184,32,0,247,38,17,0,139,30,11,
+0,3,195,72,247,243,3,200,137,14,8,2,104,0,16,7,
+51,219,143,6,19,2,137,30,21,2,14,232,144,0,114,87,
+51,219,139,14,17,0,139,251,81,185,11,0,190,220,1,243,
+166,89,116,5,131,195,32,226,237,227,55,38,139,87,26,82,
+184,1,0,104,0,32,7,51,219,14,232,72,0,114,40,91,
+141,54,11,0,141,62,11,2,30,143,69,2,199,5,245,0,
+30,143,69,6,199,69,4,14,1,138,22,36,0,234,3,0,
+0,32,190,134,1,235,3,190,162,1,232,9,0,190,193,1,
+232,3,0,251,235,254,172,10,192,116,9,180,14,187,7,0,
+205,16,235,242,195,80,74,74,160,13,0,50,228,247,226,3,
+6,8,2,131,210,0,163,19,2,137,22,21,2,88,162,7,
+2,161,19,2,139,22,21,2,3,6,28,0,19,22,30,0,
+247,54,24,0,254,194,136,22,6,2,51,210,247,54,26,0,
+136,22,37,0,163,4,2,161,24,0,42,6,6,2,64,58,
+6,7,2,118,5,160,7,2,50,228,80,180,2,139,14,4,
+2,192,229,6,10,46,6,2,134,233,139,22,36,0,205,19,
+15,131,5,0,131,196,2,249,203,88,40,6,7,2,118,17,
+1,6,19,2,131,22,21,2,0,247,38,11,0,3,216,235,
+144,162,7,2,248,203,66,79,79,84,58,32,67,111,117,108,
+100,110,39,116,32,102,105,110,100,32,78,84,76,68,82,13,
+10,0,66,79,79,84,58,32,73,47,79,32,101,114,114,111,
+114,32,114,101,97,100,105,110,103,32,100,105,115,107,13,10,
+0,80,108,101,97,115,101,32,105,110,115,101,114,116,32,97,
+110,111,116,104,101,114,32,100,105,115,107,0,78,84,76,68,
+82,32,32,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,85,170
+};
diff --git a/private/ntos/boot/bootcode/fat/i386/usa/fatboot.inc b/private/ntos/boot/bootcode/fat/i386/usa/fatboot.inc
new file mode 100644
index 000000000..5ef7d5185
--- /dev/null
+++ b/private/ntos/boot/bootcode/fat/i386/usa/fatboot.inc
@@ -0,0 +1,28 @@
+;++
+;
+;Copyright (c) 1991 Microsoft Corporation
+;
+;Module Name:
+;
+; fatboot.inc
+;
+;Abstract:
+;
+; This contains the message text that the boot sector prints out on
+; error conditions
+;
+;Author:
+;
+; John Vert (jvert) 31-Aug-1991
+;
+;Revision History:
+;
+;--
+
+MSG_NO_NTLDR db "BOOT: Couldn't find NTLDR"
+ db 0dh, 0ah, 0
+MSG_READ_ERROR db "BOOT: I/O error reading disk"
+ db 0dh, 0ah, 0
+MSG_REBOOT_ERROR db "Please insert another disk"
+ db 0
+ \ No newline at end of file
diff --git a/private/ntos/boot/bootcode/hpfs/i386/buf.inc b/private/ntos/boot/bootcode/hpfs/i386/buf.inc
new file mode 100644
index 000000000..fe2eb8377
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/buf.inc
@@ -0,0 +1,222 @@
+;static char *SCCSID = "@(#)buf.h 12.1 88/11/21";
+;** BUF.H - Buffer definitions
+;
+; HPFS Utilities
+; Peter A. Williams
+; Copyright 1988 Microsoft Corporation
+;
+; Modification history:
+; P.A. Williams 08/01/89 Added typedef BUFNODE and PBUFNODE.
+;
+
+;* Buffer Nodes
+;
+; When the comments talk about "buffer address" they're always
+; talking about the buffer header address. If we're talking
+; about the address of the data field of the buffer, we say
+; "buffer data".
+;
+; These buffer headers are also used as I/O request blocks for
+; the data in the buffer. The B_IOP field contains the
+; function. Sometimes we need an I/O request block which
+; isn't assocated with a buffer - for example, when we read
+; directly into an application's memory area. For this, we keep
+; a pool of I/O request blocks which are actually BUFNODE
+; structures, but they aren't associated with a buffer and the
+; B_ADDR field is used to hold the I/O target address.
+; These BUFNODEs have BF_IORQ in the flags field to distinguish
+; them from regular buffer headers.
+;
+; Note that we use the B_SUM field for debugging purposes. This is an
+; array of SPB words, each of which holds the CAS (high and low
+; 16 bits xored togehter) of one of the sectors. We can then check
+; this value to make sure that we're setting the dirty bits
+; correctly.
+;
+; NOTE - see the discussion on holding and locking at the end of
+; this file
+;
+
+BUFNODE struc
+
+ B_LRU db (size DCHDR) dup (?) ; LRU chain, if not BF_IORQ
+ B_LRUH dd ? ; address of LRU chain head if not locked/held
+ ; if lcoked/held, we're on that chain but
+ ; this guy points to it's regular chain
+ B_SEC dd ? ; VSector #
+ B_ADDR dd ? ; address of data
+ B_DCHN db (size DCHDR) dup (?) ; doubly linked list of dirty buffers
+ B_dirt db ? ; 1 bit per dirty sector
+ ; used to optimize the write. Non dirty
+ ; marked sectors may still be rewritten
+ B_iop db ? ; disk driver I/O operation
+ B_type db ? ; type of info
+ B_FLAG db ? ; flags
+ B_HCNT dw ? ; hold count
+ B_LCNT db ? ; lock count (really a flag, only 0 or 1
+ B_BADSEC db ? ; 1 bit per defective sector
+ B_next dd ? ; advisory address of next buffer content
+ ; always a buffer header addr, never 0
+ B_DADR dd ? ; address of routine to call when I/O is done
+
+ ; The following two fields are redefined
+ ; if BF_IORQ is set.
+ B_HASH db (size DCHDR) dup (?) ; sector hash chain
+ B_HTA dd ? ; address of entry in hash table
+
+ B_WAIT dd ? ; head of wait chain
+ifdef DEBUG
+ B_SUM dw 4 dup (?) ; holds checksum of buffer contents
+else
+ B_XTRA db 8 dup (?)
+endif
+
+; 64 byte boundary
+
+ B_LDTIME dd ? ; time (in ms/512) when buffer was last dirtied
+ B_FDTIME dd ? ; time (in ms/512) when buffer got first dirtied
+ B_LWWAIT dd ? ; head of lazy write wait chain
+ B_XTRA2 db 64-16 dup (?)
+BUFNODE ends
+
+;typedef struct BUFNODE BUFNODE;
+;typedef struct BUFNODE *PBUFNODE;
+
+BUFHDRSHIFT equ 7
+
+ifdef MASM
+ .errnz (size BUFNODE) - (1 SHL BUFHDRSHIFT)
+endif
+
+; Following are alternative offsets if BF_IORQ is set
+
+B_XFER equ (DWORD PTR B_HTA) ; holds transfer sec cnt
+B_NADR equ (DWORD PTR B_HASH) ; addr of client's NOTEREC
+B_NMSK equ (DWORD PTR B_HASH+4) ; notification mask
+ifdef MASM
+ .errnz (size DCHDR)-8 ; enough room for double map
+endif
+
+; B_FLAG bits
+
+BFL_LWLOCK equ 00000001h ; buffer is being lazy written in a block
+
+
+; B_type values
+
+BF_FREE equ 0 ; buffer is free (not in LRU list)
+BF_LRU equ 1 ; buffer in LRU list
+
+
+; Priority values for LRU placement
+
+BP_KEEP equ 0 ; Buffer contains future-usable data
+BP_NOOPINION equ 1 ; Buffer contains marginally useful data
+BP_TOSS equ 2 ; Buffer is unlikely to be used
+
+; NOTEREC - Notification Record
+;
+; Some callers may post several disk requests in parallel and
+; want to keep track of when they're *all* complete. I/O
+; request blocks (buffer headers w/o buffers) have fields to
+; allow this. The caller stores the address of his NOTEREC,
+; and when each request completes it clears it's associated bit.
+; When all of the bits clear the block chain in the NOTEREC
+; is woken.
+;
+; Note that there can be only one thread blocked on a NOTEREC
+; because the first guy to wakeup will return the NOTEREC to the
+; heap or whatever. This occurs naturally; unlike I/O to the
+; buffer cache, NOTERECs are used for direct I/O. If someone
+; else wants to do I/O to the same location and if record and file
+; locking allows that, then they'll get their own NOTEREC or
+; cache I/O request and have a horse race. NOTERECs are only used
+; to do file data I/O, all "filesystem" structures are manipulated
+; via the cache.
+;
+; The fields are DWORD, but only the low byte of the MSK and FLD
+; records are used for normal completion. The 3rd byte of NTR_FLD
+; (..FF....h) is used for error posting - these bits are
+; set if an irrecoverable error occured in the I/O.
+;
+
+NOTEREC struc
+ NTR_FLD dd ? ; the mask bit field
+ NTR_BLC dd ? ; head of the block chain
+ NTR_MSK dd ? ; next bit to set in NTR_FLD
+NOTEREC ends
+
+
+;* Holding and Locking
+;
+; LOCK means that the buffer contents are inconsistant/incorrect. No body
+; is allowed to look at the contents of the buffer. This is done
+; when we're reading in from the disk; we'll mark the buffer
+; with the VSector # (so that any other folks that want that sector
+; won't issue their own reads in parallel) but we mark it LOCKED
+; so that nobody looks at it's contents which aren't correct yet.
+;
+; LOCKed is pretty rare because most folks which are mucking
+; with a buffer have it back in a consistant state before they
+; allow a context switch.
+;
+; An important exception to this is directory manipulation -
+; directory splitting, etc. In this case, a flag has been set
+; on the directory itself (in SBDIR) so that no one will try to
+; look at the directory contents. The cache block which has the
+; inconsistant DIRBLK might also have sectors belonging to someone
+; else and those sectors can be accessed by other folks because
+; the buffer isn't locked. (We don't lock the directory and not
+; the block for this reason, it's a fallout. We lock the directory
+; because it's too mucky for people to "back out" if they're
+; searching down into a directory and find out that they've
+; reached an area which is being rebuilt. The rebuilding might
+; propigate up and change the unlocked higher DIRBLKs that this
+; other guy has already accessed... So we lock the whole directory,
+; and thus needn't bother locking the cache blocks themselves.
+;
+; HOLD means that the contents are valid, but the cache block must continue
+; to hold that data. Folks use this when they need to access
+; two different sectors at the same time. They HOLD one when
+; they read the other so that there's no chance that by the
+; time the 2nd read finishes the first one has been evicted.
+; This is much cheaper than remembering the first one's VSector
+; # and calling RDBUF N times as you transfer N words of info
+; between the two sectors.
+;
+; By definition only one guy can lock a buffer (the lock count should
+; go to a flag; it's already a flag on directorys) but the hold
+; value is a count, since multiple people can hold. (Like the
+; electrician's safety plate which allows multiple electricians
+; to lock a breaker OPEN so that it can't be closed until ALL are
+; done). (SBDIRs have a hold count for the same reason - folks
+; may yield while in a directory and don't want it to change out
+; from under them)
+;
+; We also use hold when we set dirty bits. The concern is that
+; if we're going to write something to a buffer, yield the CPU,
+; then write something else, we don't want to have to make two
+; calls to SetDirt, one after each write. This costs time, and
+; also it would be a waste if the lazywriter were to write this
+; guy anyhow, since he's going to get dirty again ASAP and
+; writing him out doesn't free the cache block anyway, since it's
+; held.
+;
+; So my current algorithm is that I won't lazywrite anybody who
+; is held, and therefore won't mark them clean. This means,
+; in effect, that there is no ordering constraint on dirtying
+; a buffer or marking it dirty so long as it is held the entire
+; time. I think that the code now always marks it dirty before
+; a yield, even if held, because the debug code is a bit hard
+; assed about it, but this could be relaxed under the current
+; lazywrite rules I've just described.
+;
+; Both in the case of directorys and cache blocks, the theory is that
+; since these buffers are MRU, it's extremely rare that we'd actually try
+; to reclaim their buffer slots, and in general it's rare that there's
+; a conflict in their use. So in actual execution, these locks are
+; very rarely encountered. They're cheap - INC to set and DEC to clear,
+; so almost always all we're doing is INCing and DECing a location
+; and it's just two wasted instructions. Once in a while, though,
+; it's a big bacon save, as they say.
+;
diff --git a/private/ntos/boot/bootcode/hpfs/i386/chain.inc b/private/ntos/boot/bootcode/hpfs/i386/chain.inc
new file mode 100644
index 000000000..b1528c2a1
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/chain.inc
@@ -0,0 +1,7 @@
+;static char *SCCSID = "@(#)chain.h 12.1 88/11/21";
+;* Doubly Chained Definitions
+
+DCHDR struc
+ FWD dd ? ; forward pointer
+ BAK dd ? ; backward pointer
+DCHDR ends
diff --git a/private/ntos/boot/bootcode/hpfs/i386/const.inc b/private/ntos/boot/bootcode/hpfs/i386/const.inc
new file mode 100644
index 000000000..5304fabfe
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/const.inc
@@ -0,0 +1,201 @@
+;static char *SCCSID = "@(#)const.h 12.3 89/09/19";
+; #define DEBUG 1
+
+
+;* Constants for File System
+
+MAXPATH equ 256 ; maximum path length
+GROWDELT equ 8
+MVPFXSIZE equ 2 ; Size of the multivolume pathname prefix
+
+; Sector sizes
+
+SECSIZE equ 512 ; 512 bytes per sector
+SECSHIFT equ 9 ; 2^9 = SECSIZE
+SECMSK equ 01ffh ; sector size mask
+
+ifdef MASM
+ .errnz SECSIZE-512 ; C code uses 512 as a magic number - grep them out
+endif
+
+; Cache Sizes
+
+SPB equ 4 ; sectors per buffer
+SPB4 equ 1
+
+ifdef SPB4
+SPBMASK equ 3 ; mask for SPB
+SPBSHIFT equ 2
+SPBBITS equ 0fh ; SPB number of one bits, low order
+endif
+
+ifdef SPB8
+SPBMASK equ 7 ; mask for SPB
+SPBSHIFT equ 3
+SPBBITS equ 0ffh ; SPB number of one bits, low order
+endif
+
+ifdef OLD_CACHE
+BUFCNT equ 8
+endif
+
+BMASK equ SPB*SECSIZE-1 ; mask offset in to cache block
+BSHIFT equ SECSHIFT+SPBSHIFT
+
+LWBUFCT equ 16 ; size of reblocking lazy write buffer
+
+; OFT Hash Table Size (8 bytes each)
+
+OFTHASHCNT equ 16 ; 16 hash chains for open files
+OFTHASHMSK equ 78h ; mask for computing hash offset
+
+
+; Number of I/O command blocks which aren't associated with buffers
+
+IOBCNT equ 8 ; 8 should be enough BUGBUG
+
+; # of OS/2 ram semaphores that we can be blocked on, simultaneously.
+
+SEMCNT equ 32
+
+
+; Cache Hash
+;
+; A sector hash is used to locate the start of a chain, the chain
+; is then scanned linearly.
+;
+; For our current size of 256 hash chains, we get:
+;
+; 1 meg of cache RAM = 256 blocks = 1 blocks per chain (average)
+; 2 meg of cache RAM = 512 blocks = 2 blocks per chain (average)
+;
+
+HASHCNT equ 256 ; 1024 bytes of hash header
+
+; Directory Lookaside record count
+
+DLCNT equ 10 ; 10 guys for now
+
+; Maximum DIRBLKs we may need to allocate for any given
+; operation. This is in effect the maximum tree depth.
+;
+; Worst case, with 256 character file names and nearly empty
+; DIRBLKs, 10 is enough levels for 60,000 files - about 40 megabytes
+; of space just for that directory. Given more practical file length
+; names this is enough for 10s of millions of files in a directory.
+;
+
+MAX_DIR_NEED equ 10
+
+
+;* Heap Definitions
+
+HHSIZ equ 4 ; size, in bytes, of heap header
+GROHEAPCNT equ 50 ; grow heap if we have to compact more
+ ; than once per 50 allocations
+
+;* Special Transition Locking Structure size
+
+TRANCNT equ 4 ; just need 4 spots
+
+
+; Zero offset
+;
+; MASM won't take 0.BAK, so we use ZERO.BAK
+;
+
+dumy struc
+ ZERO db ?
+dumy ends
+
+
+; Maximum number of volumes that we can mount
+;
+; The volume ID is kept in the high bits of the sector numbers
+; kept in our RAM structures,
+; so there is a tradeoff between max volumes and max sectors.
+;
+; 32 max volumes gives us a 65 billion byte volume limit,
+; which should last us for a while. Since sector numbers
+; are stored on the disk without their volume upper bits
+; this is strictly an implimentation detail; we can adjust
+; the number of volumes or eliminate this tradeoff in other
+; implimentations which will be 100% media compatable.
+;
+; We use the term VSector to indicate a vol/sector combination
+; and PSector to indicate just the physical absolute sector #
+;
+;
+
+VOLMAX equ 32 ; 64 max volumes.
+
+MAXSEC equ 134217728 ; 2^32/32 max sectors
+
+SECMASK equ 07FFFFFFh ; mask for sector number
+
+HSECMASK equ 07h ; high byte sector mask
+
+HVOLMASK equ 0f8h ; high byte volume mask
+SVOLMASK equ 1fh ; shifted right volume mask
+
+VOLRSHIFT equ (32-5) ; shift right to extract volume index
+VOLLSHIFT equ 5 ; shift left to extract volume index
+
+
+;* Signature Values for Disk Structures
+;
+; These signature values help with debugging and they'll
+; be used by the CHKDSK utility to help repair disks.
+;
+; WARNING - the low byte of all valid signatures must be non-zero,
+; since we destroy signatures by clearing the low byte.
+
+J equ ((('J'-'A')*40+('G'-'A'))*40+'L'-'A')
+R equ ((('R'-'A')*40+('P'-'A'))*40+'W'-'A')
+
+ifdef MASM
+ABSIGVAL equ J*40*40*40 + R ; allocation blk
+DBSIGVAL equ 40000000h + J*40*40*40 + R ; directory blks
+FNSIGVAL equ 0C0000000h + J*40*40*40 + R ; fnodes
+else
+ABSIGVAL equ (long)J*40*40*40 + (long)R ; allocation blk
+DBSIGVAL equ 40000000hL + (long)J*40*40*40 + (long)R ; directory blks
+OLDFNSIGVAL equ 80000000hL + (long)J*40*40*40 + (long)R ; fnodes
+FNSIGVAL equ 0C0000000hL + (long)J*40*40*40 + (long)R ; fnodes
+endif
+
+
+
+;* FastFile bitmaps
+;
+; 0x00000000 all checking disabled
+; 0x00000001 FF_FLUSHLAZY DoZap lazy writes are automatically flushed
+; 0x00000002 FF_ZAPSEC DoZap blasts sector numbers/sector data
+; 0x00000004 FF_LRUCHK vbs verification of LRU/dirty integrity
+; 0x00000008 FF_CHKSUM sector checksumming is omitted
+; 0x00000010 FF_PLACECHK placebuf verifies location of buffer
+; 0x00000020 FF_HEAPCHK verify heap headers
+; 0x00000040 FF_DIRMAP produce inram map of directory tree
+; 0x00000080 FF_HASHCHN check hash chains
+;
+
+FF_FLUSHLAZY equ 00000001h
+FF_ZAPSEC equ 00000002h
+FF_LRUCHK equ 00000004h
+FF_CHKSUM equ 00000008h
+FF_PLACECHK equ 00000010h
+FF_HEAPCHK equ 00000020h
+FF_DIRMAP equ 00000040h
+FF_HASHCHN equ 00000080h
+
+; Dependency dumys.
+;
+; The assembler won't to an ".errnz" comparing two external
+; addresses, since it doesn't know their address. So we
+; put the .errnz in the module which defines the address,
+; and we make that location and all folks that rely upon the
+; relationship reference that dumy.
+;
+; If you change a relationship with such a dumy definition, you
+; must find and edit all references to this dumy.
+;
diff --git a/private/ntos/boot/bootcode/hpfs/i386/dir.inc b/private/ntos/boot/bootcode/hpfs/i386/dir.inc
new file mode 100644
index 000000000..9e2d44469
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/dir.inc
@@ -0,0 +1,286 @@
+;** DIR.H - Dirblk and Dirent definitions
+;
+; FILESYS
+; Gregory A. Jones
+; Copyright 1988 Microsoft Corporation
+;
+; Modification history:
+; P.A. Williams 06/01/89 Replaced field DIR_USECNT with fields
+; DIR_FLEX and DIR_CPAGE. Added DF_NEEDEAS
+; define.
+; P.A. Williams 07/10/89 Add define DF_NEWNAME for "new" hpfs file
+; names.
+; P.A. Williams 07/14/89 Added typedefs for DIRENT and DIRBLK.
+; P.A. Williams 07/21/89 Converted DIRSIZP form ASM defn to C defn.
+;
+
+ifdef MASM
+ include dirent.inc
+else
+attr_directory equ 10h
+endif
+
+
+; Directory Entry Fields
+;
+; Directory entries are always left as a multiple of 4
+; to speed up moves. The DIR_NAMA field is variable length,
+; the DIR_BTP field, if present, is the last dword in the record.
+; ACL information may be stored after the DIR_NAMA field but before
+; the DIR_BTP field so the DIR_BTP field must be located by going
+; backwards from the end of the record
+;
+; WARNING - Mkdir block copies some of these entries and
+; makes assumptions about which fields get copied. Check
+; mkdir if stuff is added.
+;
+
+DIRENT struc
+ DIR_ELEN dw ? ; length of this entry (including free space)
+ DIR_FLAG dw ? ; flags - low byte defined below
+ ; high byte holds the old attr_ FAT values
+ DIR_FN dd ? ; FNODE Sector
+ DIR_MTIM dd ? ; last modification time
+ DIR_SIZE dd ? ; file size
+
+ DIR_ATIM dd ? ; last access time
+ DIR_CTIM dd ? ; fnode creation time
+ DIR_EALEN dd ? ; bytes of extended attributes
+ DIR_FLEX db ? ; description of "flex" area,
+ ; following file name:
+ ; bits 0-2: # of ACEs in DE
+ ; bits 3-7: reserved
+ DIR_CPAGE db ? ; code page index on volume
+
+; the following fields have information specific to the name and directory
+; position of the file. This info is not propigated for a move/rename
+; That code uses DIR_NAML as a seperator - check MOVE if changes are
+; made to this structure
+
+ DIR_NAML db ? ; length of file name
+ DIR_NAMA db ? ; name goes here
+
+; ACL information may be stored here
+
+; long DIR_BTP; btree pointer to descendent DIRBLK record.
+ ; This is only present if DF_BTP is set.
+ ; This field is referenced from the end of
+ ; the record, not DIR_NAMA+DIR_NAML
+DIRENT ends
+
+
+ifdef MASM
+DIR_BTP equ dword ptr -4 ; referenced from the end of the record
+endif
+SIZE_DIR_BTP equ 4
+
+MAX_DIRACL equ 3 ; max of 3 ACLs in dirent
+DIRSIZL equ offset DIR_NAMA ; base size of leaf dir entry (minus name)
+DIRSIZP equ (size DIRENT+4) ; base size of dir entry with btree ptr w/o name
+
+MAX_DIRENT equ (DIRSIZP+255+MAX_DIRACL*(size (long))+10) ; max size of a DIRENT
+ ; (plus some slop)
+
+
+; Directory Block Definition
+;
+; The change count field is incremented every time we move any
+; of the entries in this block. For efficiency reasons, folks
+; remember the Sector # and offset of a directory entry, and the
+; value of the DB_CCNT field when that info was recorded.
+; If the DB_CCNT field is different then the remembered value,
+; then the entry offset is invalid and the entry should be
+; refound from the top. Note that when a directory block splits,
+; the old DIRBLK gets the old DB_CCNT field. Since
+; the new DIRBLK is previously unknown, it can have
+; any DB_CCNT value. We start with zero so that DB_CCNT
+; gives us a feel for the change rate in the directory.
+;
+
+DIRBLK struc
+ DB_SIG dd ? ; signature value
+ DB_FREP dd ? ; offset of first free byte
+ DB_CCNT dd ? ; change count (low order bit is flag)
+ ; =1 if this block is topmost
+ ; =0 otherwise
+ DB_PAR dd ? ; parent directory PSector # if not topmost
+ ; FNODE sector if topmost
+ DB_SEC dd ? ; PSector # of this directory block
+
+ DB_START db ? ; first dirent record goes here
+ DB_DUMY db 2027 dup (?) ; round out to 2048 bytes
+
+
+DIRBLK ends
+
+; BUGBUG - we should init DB_CCNT with a random value
+; to prevent a fakeout by deleting one directory
+; and then creating another (find sequences will
+; remember sector numbers and signatures...)
+
+
+; Maximum entries per directory.
+
+MAXDIRE equ (size DIRBLK- DB_START)/(size DIRENT)
+
+
+
+
+
+;* DIR_FLAG values
+;
+
+DF_SPEC equ 0001h ; special .. entry
+DF_ACL equ 0002h ; item has ACL
+DF_BTP equ 0004h ; entry has a btree down pointer
+DF_END equ 0008h ; is dumy end record
+DF_XACL equ 0040h ; item has explicit ACL
+DF_NEEDEAS equ 0080h ; item has "need" EAs
+DF_NEWNAME equ 4000h ; item name is of "new" pinball format
+
+DF_RMASK equ DF_ACL+DF_XACL ; only attributes preserved for rename
+
+ifdef MASM
+ .errnz DF_BTP - SIZE_DIR_BTP ; code uses this "coincidence"
+endif
+
+; Attributes which creation can specify
+
+DF_CMASK equ attr_read_only+attr_hidden+attr_archive
+
+; Directory Lookaside Structure
+;
+; We keep info on all directories that we've seen in SBDIR records
+; in RAM, but we keep the last N that we've seen in a special
+; DIRLOOK list in RAM.
+;
+
+DIRLOOK struc
+ DL_LNK db (size DCHDR) dup (?) ; forward and backwards link
+ DL_VSECVAL dd ? ; VOL_SECVAL value
+ DL_SUM dd ? ; checksum value
+ DL_NAM dd ? ; pointer to name string on heap
+ DL_SBD dd ? ; pointer to SBDIR structure
+DIRLOOK ends
+
+
+; Subdirectory Linkage Structure
+;
+; For every directory that we've seen on the disk we keep a
+; SBDIR record in ram, linked into a heirarchy which parallels
+; the disk heirarchy. We never discard these, so we end up
+; with a RAM copy of all the parts of the directory heirarchy
+; that the user is using.
+;
+; Each SBDIR entry is on a circular doubly linked chain of
+; siblings (directors with the same parent directory). If a
+; directory contains no subdirectories the SD_ENT field is 0.
+; If a directory has subdirectories, their SBDIR entries are
+; in turn in a SD_SIB chain and the SD_ENT field points to
+; one of those SBDIR entries.
+;
+; SBDIR contains a lock and a hold mechanism. A directory is
+; locked when it is being edited; no other threads may view it
+; until it is unlocked. A directory which is HELD is one which
+; is being accessed and can't be edited.
+;
+; The locking and holding algorithms are complicated by the fact
+; that we almost never block so we want to do our typical locking
+; and unlocking inline, without calls, and with minimum tests.
+; We do this with a held count, and bits for locked, lock pending,
+; and solo pending. (Solo means that a user wants sole access to
+; the structure. He'll continue to block until no one else is
+; using it. This is typically done to delete the structure)
+; Another bit is the OR of the lock pending and solo pending bits,
+; and is high order in the dword which encompases SD_HCNT so that
+; when folks release their SD_HCNT value they can simulatneously
+; test to see if there is a pending action.
+;
+; To Hold the SBDIR:
+; If it's not locked and doesn't have a lock pending,
+; increment hold count
+; else
+; block on it and retry.
+;
+; To Unhold the SBDIR:
+; decrement the HCNT field.
+; If SD_PND & (HCNT == 0)
+; wake up waiters.
+;
+; To lock the SBDIR:
+; If locked, block and retry.
+; If HCNT != 0
+; if (lock pending already set)
+; block and retry
+; set lock pending. Block until HCNT is zero.
+; set locked
+;
+; To unlock the SBDIR:
+; clear lock bit.
+; If the block list is non-zero, issue a wakeup.
+;
+; To Solo the SBDIR:
+; Keep blocking until no one else is blocked on it and
+; no one has it held or locked.
+;
+; General Considerations:
+; Anyone who blocks on an SBDIR because it's held must
+; be sure to set a pending bit and the SD_PND bit so
+; that the unhold operation will wake them up.
+;
+; Anyone who blocks on an SBDIR must increment the
+; SD_BCNT field to prevent a SOLO operation from yanking
+; the rug out from under them. SOLO can't depend upon
+; checking the lock list because a blanket wakeup may
+; have cleared the lock list. If the SOLO guy gets control
+; first he'll believe that he can have it.
+;
+;
+
+SBDIR struc
+ SD_FNW db (size DCHDR) dup (?) ; FNWORK (findnotify) chain
+ SD_SIB db (size DCHDR) dup (?) ; chain of siblings
+ SD_LRU db (size DCHDR) dup (?) ; LRU chain
+ SD_ENT dd ? ; pointer to a descendent, or 0
+ SD_PAR dd ? ; pointer to parent SBDIR, 0 if root
+ SD_SEC db (size SECPTR) dup (?) ; VSector and hint of top dirblk
+ SD_FNO dd ? ; FNODE # of directory
+ SD_SUM dd ? ; checksum of name string
+ SD_CNT dw ? ; # of subdirectories in this one
+ SD_OPEN dw ? ; count of # of guys that have this open
+
+; We sometimes inc/dec SD_HCNT as a dword to test the HO bit in SD_FLAG
+
+ ; the following three fields are used to
+ ; control access. They're identical in use
+ ; to the equivalent fields in OFT
+
+ SD_HCNT dw ? ; held count, has SDH_PND bit also
+ SD_DMYZERO db ? ; must be zero
+ SD_FLAG db ? ; flag byte, high order in SD_HCNT dword
+ SD_WAIT dd ? ; head of the wait chain
+ SD_FREEDCNT dd ? ; incremented each time we free a DIRBLK
+ ; for this guy. See RDE for details
+ SD_WCNT dw ? ; count of folks blocked on this
+ SD_FNDCNT dw ? ; count of active finds in this directory
+ SD_ATIME dd ? ; time of last access
+ SD_NAM dd ? ; address of name string
+ SD_ACL dd ? ; SBDIR ACL pointer, 0 if none
+ ; points to DWORD count, followed by ACEs
+ ; if low bit of address is 0, is heap space
+ ; if low bit is 1, is system memory
+
+SBDIR ends
+
+SD_ACL_LIM equ 1024 ; *SD_ACL lists bigger than this come from
+ ; system memory, smaller come from heap
+
+SDF_PND equ 80h ; lock pending bit
+SDF_RTP equ 20h ; restricted traversal permissions
+ ; =0 if anyone can traverse the dir
+SDF_REALLYBAD equ 10h ; directory is really bad
+SDF_IPR equ 08h ; SD_ACL has inherit records
+SDF_PSO equ 04h ; pending solo
+SDF_PLK equ 02h ; pending lock
+SDF_LCK equ 01h ; directory is locked against access
+ \ No newline at end of file
diff --git a/private/ntos/boot/bootcode/hpfs/i386/dirent.inc b/private/ntos/boot/bootcode/hpfs/i386/dirent.inc
new file mode 100644
index 000000000..1ba3f82df
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/dirent.inc
@@ -0,0 +1,80 @@
+BREAK <Directory entry>
+
+; SCCSID = @(#)dirent.inc 12.5 89/07/14
+;
+; +-----------------------------+
+; | (11 BYTE) filename/ext | 0 0
+; +-----------------------------+
+; | (BYTE) attributes | 11 B
+; +-----------------------------+
+; | (8 BYTE) reserved | 12 C
+; +-----------------------------+
+; | (WORD) First cluster of EA | 20 14
+; +-----------------------------+
+; | (WORD) time of last write | 22 16
+; +-----------------------------+
+; | (WORD) date of last write | 24 18
+; +-----------------------------+
+; | (WORD) First cluster of file| 26 1A
+; +-----------------------------+
+; | (DWORD) file size | 28 1C
+; +-----------------------------+
+;
+; First byte of filename = E5 -> free directory entry
+; = 00 -> end of allocated directory
+; Time: Bits 0-4=seconds/2, bits 5-10=minute, 11-15=hour
+; Date: Bits 0-4=day, bits 5-8=month, bits 9-15=year-1980
+;
+
+dir_entry STRUC
+dir_name DB 11 DUP (?) ; file name
+dir_attr DB ? ; attribute bits
+dir_pad DB 8 DUP (?) ; reserved for expansion
+dir_EAhandle DW ? ; handle to Extended Attributes
+dir_time DW ? ; time of last write
+dir_date DW ? ; date of last write
+dir_firstfile DW ? ; first allocation unit of file
+dir_size_l DW ? ; low 16 bits of file size
+dir_size_h DW ? ; high 16 bits of file size
+dir_entry ENDS
+
+DIRENT_DELETED EQU 0E5h ; indicator of deleted file
+DIRENT_NOFEALIST EQU 0 ; Indicates no extended attributes
+
+
+;
+; Values for dir_attr
+;
+; attr_newfiles is used in the case of IFS to indicate that the type of file
+; being requested for findfirst/next is a "new" file i.e. long
+; name or a mixed-case name that the FAT FS does not support.
+;
+attr_read_only EQU 1h
+attr_hidden EQU 2h
+attr_system EQU 4h
+attr_volume_id EQU 8h
+attr_directory EQU 10h
+attr_archive EQU 20h
+attr_device EQU 40h ; This is a VERY special bit.
+ ; NO directory entry on a disk EVER
+ ; has this bit set. It is set non-zero
+ ; when a device is found by GETPATH
+
+attr_newfiles EQU 40h ; name is non-8.3. never set for FAT FS
+
+attr_all EQU attr_hidden OR attr_system OR attr_directory
+ ; OR of hard attributes for FINDENTRY
+
+attr_ignore EQU attr_read_only OR attr_archive OR attr_device
+ ; ignore these attributes during
+ ; search first/next
+
+attr_changeable EQU attr_read_only OR attr_hidden OR attr_system OR attr_archive
+ ; changeable via CHMOD
+
+attr_used EQU attr_read_only OR attr_hidden OR attr_system OR attr_volume_id OR attr_directory OR attr_archive OR attr_newfiles
+ ; We ignore the rest for $Creat due to LOTUS
+ ; passing in an attribute of 0x8000!!
+
+INV_3XBOX_SRCH_ATTRS EQU attr_newfiles ; we should not pass this bit
+ ; for FSDS from 3xbox.
diff --git a/private/ntos/boot/bootcode/hpfs/i386/filemode.inc b/private/ntos/boot/bootcode/hpfs/i386/filemode.inc
new file mode 100644
index 000000000..2e9165204
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/filemode.inc
@@ -0,0 +1,105 @@
+; SCCSID = @(#)filemode.inc 12.6 89/04/26
+
+BREAK <Standard I/O assignments>
+
+stdin EQU 0
+stdout EQU 1
+stderr EQU 2
+stdaux EQU 3
+stdprn EQU 4
+
+BREAK <File modes - passed to open, stored in sf_mode or JFN_Flags>
+
+;
+; The OS/2 api calls DosOpen, DosSetFHandState, and DosQFHandState
+; all use a mode word parameter. Some of these values are stored
+; in the sft (system file table) in the field sf_mode. Others
+; are stored in the JFN flags (JFN_Flg_Ptr). The layout of
+; sf_mode and the word parameter for the call is the same. The
+; following EQU's are used to get to these values. The layout
+; of the word is:
+;
+; 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+; D W F C R L L L I S S S M A A A
+;
+; with:
+; AAA (2-0): The Access mode (read only, etc.)
+; SSS (6-4): Sharing mode (deny write acces to others, etc.)
+; LLL (8-10): Locality of reference (sequential, random, etc.)
+; M (3) : Monitor open
+; I (7) : Not inherited by child
+; R (11): Rumored to be used by spooler. API caller must set
+; this to zero.
+; C (12): Advise device driver not to cache data. This is
+; stored in JFN flags.
+; F (13): Fail errors
+; W (14): write through
+; D (15): Direct access open
+;
+; The DosOpen2 and $Extended_Open2 calls has an additional word for
+; openmode. The layout of this word is
+;
+; 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+; P U U U U U U U U U U U U U U U
+;
+; with:
+; P (15): Open physical disk (used by FDISK program). This bit
+; is set by procedure DevReturnHandle. API/INT21h
+; caller must set this bit to zero. This bit is stored
+; in sft.
+;
+; U : Unused anywhere. API caller must set these bits to
+; zero.
+;
+; NOTE: Please document all use of the openmode bits including those
+; that are internal to the kernel (e.g. the P bit).
+
+; wwwwxxxxyyyyzzzz
+; 5432109876543210
+open_access EQU 0000000000000111B
+open_for_read EQU 00h
+open_for_write EQU 01h
+open_for_both EQU 02h
+open_max EQU 02h
+open_for_exec EQU 03h ; open via internal exec call
+ ; (not available to API)
+
+open_monitor EQU 0000000000001000B
+
+open_sharing_mode EQU 0000000001110000B
+sharing_compat EQU 000H
+sharing_deny_both EQU 010H
+sharing_deny_write EQU 020H
+sharing_deny_read EQU 030H
+sharing_deny_none EQU 040H
+sharing_max EQU 040H ; max value for check_access_AX
+ ; (check_access_ax handles
+
+; these bits are for openmode
+open_no_inherit EQU 0000000010000000B ; Child does not inherit handle
+open_autofail EQU 0010000000000000B ; hard errors failed
+open_write_through EQU 0100000000000000B ; write through to disk
+open_direct EQU 1000000000000000B ; open of a device for direct access
+open_no_cache EQU 0001000000000000B ; don't cache data
+
+open_locality EQU 0000011100000000B ; locality of reference
+locality_unknown EQU 000H
+locality_sequential EQU 100H
+locality_random EQU 200H
+locality_semirandom EQU 300H
+
+; these bits are for openmode2 available to DosOpen2/$Extended_Open2
+;
+open2_phys_disk EQU 1000000000000000B ; open physical disk
+
+; Bits carried in SFT mode field (@PhysDisk)
+o_mode_in_sft EQU open_direct+open_monitor+open_sharing_mode+open_access+open_locality
+
+; Bits carried in JFN flags
+o_mode_in_flags EQU open_write_through+open_autofail+open_no_inherit+open_no_cache
+
+; Reserved bits
+o_mode_reserved EQU NOT (o_mode_in_sft+o_mode_in_flags)
+o_mode2_reserved equ -1 ; all bits are reserved
+
+SUBTTL
diff --git a/private/ntos/boot/bootcode/hpfs/i386/fnode.inc b/private/ntos/boot/bootcode/hpfs/i386/fnode.inc
new file mode 100644
index 000000000..af0207520
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/fnode.inc
@@ -0,0 +1,214 @@
+;** FNODE.H - Fnode definitions
+;
+; FILESYS
+; Gregory A. Jones
+; Copyright 1988 Microsoft Corporation
+;
+; Modification history:
+; P.A. Williams 06/01/89 Added fields FN_ACLBASE and FN_NEACNT
+; to fnode.
+; P.A. Williams 08/01/89 Added typedef FNODE and PFNODE, ALBLK, PALBLK,
+; ALLEAF, PALLEAF, ALSEC, and PALSEC.
+;
+
+
+;* File Allocation Tracking
+;
+; File space is allocated as a list of extents, each extent as
+; large as we can make it. This list is kept in a B+TREE format.
+; Each B+TREE block consists of a single sector containing an
+; ALSEC record, except for the top most block. The topmost block
+; consists of just an ALBLK structure, is usually much smaller than
+; 512 bytes, and is typically included in another structure.
+;
+; The leaf block(s) in the tree contain triples which indicate
+; the logical to physical mapping for this file. Typically this
+; extent list is small enough that it is wholy contained in the
+; fnode ALBLK stucture. If more than ALCNT extents are required
+; then the tree is split into two levels. Note that when the
+; topmost B+TREE block is 'split' no actual split is necessary,
+; since the new child block is much bigger than the parent block
+; and can contain all of the old records plus the new one. Thus,
+; we can have B+TREEs where the root block contains only one
+; downpointer.
+;
+; The following rules apply:
+;
+; 1) if the file is not empty, there is at least one sector allocated
+; to logical offset 0. This simplifys some critical loops.
+;
+; 2) The last entry in the last node block contains a AN_LOF value of
+; FFFFFFFF. This allows us to extend that last leaf block
+; without having to update the node block.
+;
+; 3) For the node records, the AN_SEC points to a node or leaf
+; sector which describes extents which occur before that
+; record's AN_LOF value.
+;
+
+;* Allocation block structure
+;
+; Each allocation block consists of one of these. This may be
+; a small block imbedded in an FNODE or OFT structure, or it
+; may occupy a whole sector and be embedded in an ALSEC structure.
+;
+
+ALBLK struc
+ AB_FLAG db ? ; flags
+ AB_FLAG2 db 3 dup (?) ; unused - sometimes copied with AB_FLAG
+ AB_FCNT db ? ; free count - slots for ALLEAF or ALNODE
+ AB_OCNT db ? ; occupied count - # of ALLEAF or ALNODEs
+ AB_FREP dw ? ; offset to last item+1
+ ; ALLEAF or ALNODE records go here
+ALBLK ends
+
+ABF_NODE equ 80h ; if not a leaf node
+ABF_BIN equ 40h ; suggest using binary search to find
+ABF_FNP equ 20h ; parent is an FNODE
+ABF_NFG equ 01h ; not a flag, high order bit of AB_FREP
+
+; Allocation Node Structure
+;
+; These follow an ALBLK header for a node block
+
+ALNODE struc
+ AN_LOF dd ? ; logical offset (sectors
+ AN_SEC dd ? ; sector for guys < this
+ALNODE ends
+
+
+; Allocation Leaf Structure
+;
+; These follow an ALBLK header in a leaf block
+
+ALLEAF struc
+ AL_LOF dd ? ; logical sector offset (sectors)
+ AL_LEN dd ? ; length of extent (sectors)
+ AL_POF dd ? ; physical sector offset (sectors)
+ALLEAF ends
+
+
+;* Allocation Sector Structure
+;
+; Root ALBLK structures are contained within other structures,
+; such as the FNODE. When the B+TREE is more than one level,
+; though, the non-root nodes are each held in a sector.
+;
+; This structure defines that format
+;
+
+ALSEC struc
+ AS_SIG dd ? ; signature
+ AS_SEC dd ? ; sector # of this sector
+ AS_RENT dd ? ; parent sector # or FNODE #
+ AS_ALBLK db (size ALBLK) dup (?) ; ALBLK goes here
+ ; ALNODE or ALLEAF records start here
+ALSEC ends
+
+; # of bytes available for ALLEAF or ALNODE values. Size chosen
+; so an integral # of either structure fits
+
+ifdef MASM
+ASSIZ equ ((SECSIZE - size ALSEC)/24*24)
+ .errnz size ALLEAF-12
+ .errnz size ALNODE-8
+ .errnz (ASSIZ + AL_LOF + size AL_LOF + size ALBLK) GT 512 ; extra room for an AL_LOF value
+else
+ASSIZ equ ((SECSIZE - size ALSEC)/24*24)
+endif
+
+
+; AuxInfo Structure
+;
+; The FNODE contains two AuxInfo structures, one for ACLs and
+; one for EAs.
+;
+; These structures point to within FNODE storage and also
+; potentially point to an overflow area which is an ALBLK structure.
+; The AI_FNL stuff is stored in the FN_FREE area, the ACLs first
+; and the EAs second, any free space following. The start of the
+; EAs can be found by offseting FN_FREE with FN_ACL.AI_FNL
+;
+
+AUXINFO struc
+ AI_DAL dd ? ; non-fnode Disk Allocation length
+ AI_SEC dd ? ; sec # of first sec in extent or of ALSEC
+ AI_FNL dw ? ; length of fnode info
+ AI_DAT db ? ; non-zero if AI_SEC points to ALSEC
+AUXINFO ends
+
+;* Fnode block definition
+;
+; Every file and directory has an FNODE. The file location
+; stuff is only used for files; directories are kept in
+; a BTREE of DIRBLK records pointed to by FN_SEC[0].RSEC
+;
+
+ALCNT equ 8 ; 8 ALLEAF records in an FN_AT entry
+LEAFPERFNODE equ 8 ; 8 ALLEAF records in an FN_AT entry
+NODEPERFNODE equ 12 ; 12 ALNODE records in an FNODE.
+LEAFPERSEC equ 40 ; ALLEAF records in an allocation sector
+NODEPERSEC equ 60 ; ALNODE records in an allocation sector
+
+FNODE struc
+
+; The following file location information is copied into the OFT
+; and is used there during normal file access. The stuff in the
+; fnode record here may in fact be out of date for open files.
+; See the OFN_ in the OFT structure THESE TWO AREAS IN THE
+; RESPECTIVE RECORDS MUST HAVE IDENTICAL FORMATS.
+;
+; There are two kinds of location info: FNSCNT SPTR records
+; and then a single, double, triple, and quad indirect block pointer.
+; The "block threshold" means the first sector number which is
+; contained in that indirect block heirarchy. You use this
+; to quickly find out where to start looking.
+;
+
+ FN_SIG dd ? ; signature value
+
+; History tracking info for softer software
+
+ FN_SRH dd ? ; sequential read history
+ FN_FRH dd ? ; fast read history
+ FN_NAME db 16 dup (?) ; 1st 18 bytes of file name
+ FN_CONTFN dd ? ; fnode of directory cont. this file/dir
+
+; stuff not interesting once opened
+
+ FN_ACL db (size AUXINFO) dup (?) ; access ctl list aux info structure
+ FN_HCNT db ? ; count of valid history bits
+ FN_EA db (size AUXINFO) dup (?) ; ea aux info structure
+ FN_FLAG db ? ; FNODE flag byte
+
+ FN_AB db (size ALBLK) dup (?) ; allocation block structure
+ FN_ALREC db (ALCNT*size ALLEAF) dup (?) ; referenced from FN_AB
+ FN_VLEN dd ? ; length of valid data in file. if DIR_SIZE
+ ; is > FN_VLEN then the space inbetween
+ ; must be zapped before being shown to user
+ FN_NEACNT dd ? ; # of "need eas" in file
+
+; The following fields are unused in this release, they're for
+; future compatibility. When deleting files, if FN_EEL is non-zero
+; then FN_EEL sectors starting at FN_EEP must be released too.
+;
+
+ FN_UID db 16 dup (?) ; reserved for UID value
+ FN_ACLBASE dw ? ; FN_ACLBASE offset of 1st ACE in fnode
+ FN_SPARE db 10 dup (?); 10 more bytes emergency spares
+
+; Free pool. ACLs and EAs are stored here via the AUXINFO structure
+
+ FN_FREE db 316 dup (?) ; free space for perm and env list; perm list
+ ; comes first.
+FNODE ends
+
+
+ifdef MASM
+ .errnz AL_LOF ; verify validity of FN_DMY1 hack above
+ .errnz size AL_LOF-4
+endif
+
+; Fnode FN_FLAG bits
+
+FNF_DIR equ 01h ; is a directory fnode
diff --git a/private/ntos/boot/bootcode/hpfs/i386/fsstat.inc b/private/ntos/boot/bootcode/hpfs/i386/fsstat.inc
new file mode 100644
index 000000000..074030367
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/fsstat.inc
@@ -0,0 +1,66 @@
+;static char *SCCSID = "@(#)fsstat.h 12.2 88/12/19";
+;** fsstat.h - file system statistics
+;
+
+CTHIST equ 10000
+
+FSSTAT struc
+ ST_OPEN dd ? ; count of OPEN calls
+ ST_CLOSE dd ? ; count of CLOSE calls
+ ST_READ dd ? ; count of READ calls
+ ST_WRITE dd ? ; count of WRITE calls
+
+ ST_DEL dd ? ; count of DELETE calls
+ ST_SEEK dd ? ; count of SEEK calls
+ ST_FINDF dd ? ; count of FINDF calls
+ ST_FINDN dd ? ; count of FINDN calls
+
+ ST_RD dd ? ; count of disk reads
+ ST_WR dd ? ; count of disk writes
+ ST_CRD dd ? ; count of cache read hits
+ ST_CWD dd ? ; count of cache write hits
+
+ ST_INVAL dd ? ; invalid LSD hints
+ ST_VALID dd ? ; valid LSD hints
+ ST_RDEH dd ? ; directory relocated
+ ST_RDEM dd ? ; directory adjusted
+
+ ST_SFB dd ? ; count of SFB calls
+ ST_VBR dd ? ; count of VBR calls
+ ST_CBR dd ? ; count of CBR calls
+ ST_AEX dd ? ; count of AddExt calls
+
+ ST_EFA dd ? ; files extended
+ ST_FLW dd ? ; FLW buffers written
+ ST_BRV dd ? ; bitmap read valid
+ ST_BRI dd ? ; bitmap read invalid
+
+ ST_BLSD dd ? ; blocked in LSD
+ ST_BRDB dd ? ; blcoked in rdb
+ ST_GFBI dd ? ; GFB interlock
+ ST_GFBW dd ? ; gfb waits
+
+ ST_LWR dd ? ; long writes
+ ST_GIB dd ? ; Getinbuf was successful
+ ST_HMIN dd ? ; heap minimum
+ ST_LWBW dd ? ; singletons output by lazy IO
+
+ ST_CLN dd ? ; clean blocks found by lazy IO
+ ST_LWW dd ? ; wakeups caused by lazy IO blocks
+ ST_QINFO dd ? ; Query info
+ ST_QIDIR dd ? ; query info on directory
+
+ ST_LWBLK dd SPB*LWBUFCT dup (?) ; Histogram of lazy write blocks
+
+; performance impact items
+
+ ST_DLRS dd ? ; directory locked forced restart
+ ST_ALSP dd ? ; count of allocation block splits
+ pad2 dd 3 dup (?)
+
+ ST_RSIZ dd 64 dup (?) ; Histogram of # sectors in read request
+ ST_WSIZ dd 64 dup (?) ; Histogram of # sectors in write request
+FSSTAT ends
+
+FS_GETSTAT equ 8004h
+FS_CLEAR equ 8005h
diff --git a/private/ntos/boot/bootcode/hpfs/i386/macro.inc b/private/ntos/boot/bootcode/hpfs/i386/macro.inc
new file mode 100644
index 000000000..153ab4fd9
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/macro.inc
@@ -0,0 +1,781 @@
+; SCCSID = @(#)macro.inc 12.1 88/12/19
+
+;** Macros
+
+
+;* MASSUME - do an assume
+;
+; made into a macro to make screwing around during debuuing
+; easier
+;
+; Used by the file system code; not recommended for general
+; use. Will be taken out at end of project. BUGBUG
+
+
+MASSUME MACRO
+ ASSUME CS:CODE,DS:FLAT,ES:FLAT,SS:NOTHING
+ ENDM
+
+
+;* MENTER - Do an Enter
+;
+; made into a macro for better code, and to avoid problems
+; when USE16 (MASM doesn't generate the override)
+
+MENTER MACRO arg1,arg2
+ push ebp
+ mov ebp,esp
+ ifdif <arg1>,<0>
+ sub esp,arg1
+ endif
+ ENDM
+
+;* MLEAVE - do a Leave
+;
+; We need to generate the segment override in USE16, since
+; MASM won't do it
+
+MLEAVE MACRO
+ ifndef USE32
+ DB 66h
+ endif
+ leave
+ ENDM
+
+;* GetPathBuf - Allocates from the heap memory for the PathBuffer
+;
+; Enter: (eax) = size of the requested heap block (hvpb not included)
+; Exit: C clear:
+; (eax) = ptr to the heap block
+; C set: error no more heap space
+; Uses: eax, flags
+
+GetPathBuf MACRO
+ SAVE <EDI, ECX>
+ add eax, MVPFXSIZE+3+HHSIZ ; for hvpb, rounding and header
+ and al, 0fch ; round it to quad-boundary
+ifndef GHS_
+ EXTRN GHS_:near
+endif
+ call GHS_
+ RESTORE <ECX, EDI>
+ENDM
+
+;* FreePathBuf - Return PathBuffer to the Heap
+;
+;
+; Enter: (reg) = ptr to PathBuffer (that's (sizeof hvbp) after the
+; heap block address)
+; Exit: heap block released
+; Uses: reg
+
+FreePathBuf MACRO reg
+ sub reg, MVPFXSIZE ; (reg) now pts to the heap block
+ HeapChk reg
+ add dword ptr -4[reg],80000000h-4
+ENDM
+
+
+
+;* Assert - sanity checks (contolled by DEBUG switch)
+;
+; kind: one of OFT
+;
+; objs: register/word which contains address
+;
+; nopush: if non-blank, we don't preserve registers
+
+IFDEF DEBUG
+ASSERT MACRO kind, objs, nopush, arg1
+ local a
+
+a = 0
+
+
+ IFNDEF A_OFT
+ extrn A_OFT:near,A_SECPTR:near,A_DIRBLK:near,A_FNODE:near
+ extrn A_AS:near,A_HEAPNAM:near,A_DCHDR:near,A_BUF:near
+ extrn A_SBDIR:near,A_ALBLK:near
+ ENDIF
+
+ IFB <nopush>
+ pushad
+ pushfd
+ ENDIF
+
+ IFIDN <kind>,<OFT>
+ a = 1
+ mov eax,objs
+ call A_OFT ; assert OFT
+ ENDIF
+ IFIDN <kind>,<SECPTR>
+ a = 1
+ lea eax,objs
+ call A_SECPTR ; returns 'C' clear if hint field is valid
+ ENDIF
+ IFIDN <kind>,<ALBLK>
+ a = 1
+ mov eax,objs
+ call A_ALBLK
+ ENDIF
+ IFIDN <kind>,<ASREC>
+ a = 1
+ mov eax,objs
+ call A_AS
+ ENDIF
+ IFIDN <kind>,<HEAPNAM>
+ a = 1
+ mov eax,objs
+ call A_HEAPNAM
+ ENDIF
+ IFIDN <kind>,<DCHDR>
+ a = 1
+ mov edx,arg1
+ mov eax,objs
+ call A_DCHDR
+ ENDIF
+ IFIDN <kind>,<DIRBLK>
+ a = 1
+ mov eax,objs
+ call A_DIRBLK
+ ENDIF
+ IFIDN <kind>,<BUF>
+ a = 1
+ mov eax,objs
+ call A_BUF
+ ENDIF
+ IFIDN <kind>,<SBDIR>
+ a = 1
+ mov eax,objs
+ call A_SBDIR
+ ENDIF
+ IFIDN <kind>,<FNODE>
+ a = 1
+ mov eax,objs
+ call A_FNODE
+ ENDIF
+
+ IFE a
+ .error illegal option
+ ENDIF
+
+
+ IFB <nopush>
+ popfd
+ popad
+ nop ; errata
+ ENDIF
+ ENDM
+ELSE
+ ASSERT Macro a,b,c
+ ENDM
+ENDIF
+
+;** Heap sanity check macro (controlled by DEBUG flag)
+;
+; item - make sure this points to a heap allocated block
+; (return value from GHS or GHS_)
+; if blank, just the arena is checked.
+
+IFDEF DEBUG
+HeapChk Macro item
+ ifndef A_HEAP
+ extrn A_HEAP:near
+ endif
+ push edx
+ ifb <item>
+ mov edx, 0 ;; don't zap the flags
+ endif
+ ifdif <edx>, <item>
+ mov edx, item
+ endif
+ call A_HEAP
+ pop edx
+ENDM
+
+ELSE
+ HeapChk Macro item
+ ENDM
+ENDIF
+
+
+DPUBLIC MACRO arg
+ ifdef DEBUG
+ Public arg
+ endif
+ENDM
+
+
+BREAK MACRO subtitle
+ SUBTTL subtitle
+ PAGE
+ENDM
+
+;** CalcGBHShift - calculate the GBH shift factor
+
+GBHShift = 0
+
+CalcGBHShift MACRO
+ local ?tmp
+
+ if GBHShift NE 0
+ EXITM
+ endif
+
+ ?tmp = (SECSIZE*SPB) / (size BUFNODE)
+ rept 16
+ if ?tmp EQ 1
+ exitm
+ endif
+ ?tmp = ?tmp / 2
+ GBHShift = GBHShift + 1
+ endm
+
+ .errnz SECSIZE * SPB - ((size BUFNODE) SHL GBHShift)
+ENDM
+
+
+;** GBH - Get Buffer Header
+;
+; GBH takes the address of a buffer data area and returns the
+; address of it's header.
+;
+; Since the data area is linear in memory and the headers are linear,
+; we just do a simple linear mapping.
+;
+; GBH transforms the address in the register without modifying
+; any other registers.
+;
+; GBH reg
+
+GBH MACRO reg
+ CalcGBHShift
+ sub reg,Bufbase ; (reg) = offset in array of buffers
+ shr reg,GBHShift ; (reg) = offset in array of bufnotes
+
+; Get rid of low order stuff. Since reg may be an offset WITHIN
+; a buffer and not just a poitner to the header itself, we mask off the
+; low order stuff.
+
+ ifidn <reg>,<eax>
+ and al,100h - (SIZE bufnode)
+ else
+ ifidn <reg>,<ebx>
+ and bl,100h - (SIZE bufnode)
+ else
+ ifidn <reg>,<ecx>
+ and cl,100h - (SIZE bufnode)
+ else
+ %out add more code to this macro
+ .err
+ endif
+ endif
+ endif
+ add reg, OFFSET DS:Bhbase
+ ENDM
+
+;* RetHeap - Return Heap Item
+;
+; RetHeap address-of-item
+
+RetHeap MACRO reg
+ HeapChk reg
+ add dword ptr -4[reg],80000000h-4
+ ENDM
+
+
+;* GetPerm - Get Perminant Memory
+;
+; Returns a block of memory which will be perminantly
+; occupied
+
+GetPerm Macro reg,len
+ local l1,l2
+l1: mov reg,PermPtr
+ add PermPtr,len
+ cmp reg,PermLim
+ jb short l2
+ push len
+ call aapm ; allocate additional perm memory
+ jmp l1
+ align 4
+l2:
+ ENDM
+
+
+ BREAK <Double Chain Manipulation Macros>
+
+;** The following macros manipulate double-linked lists.
+;
+; All macros take as their first argument the offset to
+; the pointer pair.
+
+;** DCADDB - Add Item to Back of List
+;
+; DCADDB offset,listreg,itemreg,scrreg
+;
+; offset = offset into structure of links to edit
+; listreg = address of list head node
+; itemreg = address of item to insert
+; scrreg = scratch register to roach
+
+DCADDB MACRO o,LR,IR,SR
+ mov SR,o.BAK[LR]
+ mov o.FWD[SR],IR
+ mov o.FWD[IR],LR
+ mov o.BAK[IR],SR
+ mov o.BAK[LR],IR
+ ENDM
+
+
+;** DCADDF - Add Item to Front of List
+;
+; DCADDF offset,listreg,itemreg,scrreg
+;
+; offset = offset into structure of links to edit
+; listreg = address of list head node
+; itemreg = address of item to insert
+; scrreg = scratch register to roach
+
+DCADDF MACRO o,LR,IR,SR
+ mov SR,o.FWD[LR]
+ mov o.FWD[IR],SR
+ mov o.BAK[IR],LR
+ mov o.BAK[SR],IR
+ mov o.FWD[LR],IR
+ ENDM
+
+
+
+;** DCREM - Remove Item from Double Link Chain
+;
+; DCREM offset,adrreg,scrreg1,scrreg2
+;
+; offset = offset into structure of links to edit
+; adrreg = address of item to remove
+; scrreg? = two registers to scratch
+
+DCREM MACRO o,ir,r2,r3
+ mov r2,o.FWD[ir]
+ mov r3,o.BAK[ir]
+ mov o.BAK[r2],r3
+ mov o.FWD[r3],r2
+ ENDM
+
+
+;** DCMOVF - Move Item to the Front of the Chain
+;
+; DCMOVF offset,listreg,itemreg,scrreg,[scrreg2]
+;
+; offset = offset into structure of links to edit
+; listreg = address of list head node
+; itemreg = address of item to insert
+; scrreg = scratch register to roach
+; scrreg2 = optional additional register to roach
+;
+; BUGBUG - check users for supply of scratch registers
+
+DCMOVF MACRO o,lr,ir,sr,sr2
+ IFNB <sr2>
+ DCREM o,ir,sr,sr2
+ else
+ push lr
+ DCREM o,ir,lr,sr
+ pop lr
+ endif
+ DCADDF o,lr,ir,sr
+ ENDM
+
+
+;** DCMOVB - Move Item to the Back of the Chain
+;
+; DCMOVB offset,listreg,itemreg,scrreg
+;
+; offset = offset into structure of links to edit
+; listreg = address of list head node
+; itemreg = address of item to insert
+; scrreg = scratch register to roach
+
+DCMOVB MACRO o,lr,ir,sr
+ push lr
+ DCREM o,ir,lr,sr
+ pop lr
+ DCADDB o,lr,ir,sr
+ ENDM
+
+
+;** ADDHASH - add a buffer to hash list
+;
+; ADDHASH lsn,buf,sr1,sr2,sr3
+;
+; lsn = Vsector or Psector number of beginning of buffer
+; may be any of the arg registers
+; buf = address of buffer header
+; sr1 = scratch register
+; sr2 = 'nother scratch register
+; sr3 = last scratch register
+
+ADDHASH MACRO lsn,buf,sr1,sr2,sr3
+ local l1,l2
+
+ mov sr1,lsn
+ and sr1,(HASHCNT-1)*4 ; (sr1) = hash index
+ add sr1,offset DGROUP:HashTab
+ mov B_HTA[buf],sr1 ; save hash ptr for later use by DCADDF
+ mov sr2,[sr1]
+ifidn <sr2>,<ecx>
+ jecxz l1
+else
+ and sr2,sr2
+ jz short l1 ; nobody on list yet
+endif
+ DCADDF B_HASH,sr2,buf,sr3 ; add to hash list
+ jmp short l2
+
+ align 4
+l1: mov B_HASH.FWD[buf],buf ; empty list, make self-linked
+ mov B_HASH.BAK[buf],buf
+l2: mov [sr1],buf ; put our guy at front of chain
+ ENDM
+
+
+;** HASHFIND - find a sector in the hash
+;
+; HASHFIND lsn,buf,sr1,fnd
+;
+; lsn = logical sector number to find. HASHFIND presumes it
+; has already been rounded to a multiple of SPB
+; buf = register where buffer is returned
+; sr1 = scratch register
+; fnd = where to go if found
+; NOTE: falls through if not found
+
+
+HASHFIND MACRO lsn,buf,sr1,fnd
+ local l1,l2
+
+ mov sr1,lsn
+ and sr1,(HASHCNT-1)*4 ; (sr1) = hash index
+ mov buf,Hashtab[sr1]
+ifidn <buf>,<ecx>
+ jecxz l2
+else
+ and buf,buf
+ jz short l2 ; no entries in chain, block not there
+endif
+ mov sr1,buf ; save address of first guy
+
+; Run through circular chain, looking for a match.
+;
+; (buf) = next guy to check out
+; (lsn) = sector value to match
+; (sr1) = address of first guy in chain
+
+ align 4
+l1: cmp lsn,B_SEC[buf]
+ je fnd ; got him
+ mov buf,B_HASH.FWD[buf] ; go to next buffer
+ cmp buf,sr1 ; have we gone around yet?
+ jne l1 ; no, go examine buffer
+l2:
+ ENDM
+
+
+
+;** FALLTHRU - Verifies Fallthrough Validity
+
+FALLTHRU MACRO labl
+ align 4 ; don't have errnz fail due to alignment
+ IF2 ; of following label
+ .errnz labl-$
+ ENDIF
+ ENDM
+
+
+;** INTERR - Internal Error
+; INTERRnz - Internal error iff 'Z' clear
+; INTERRzr - Internal error iff 'Z' set
+; INTERRc - Internal error if 'C' set
+
+ifdef DEBUG
+INTERR MACRO
+ local l
+l: int 3
+ jmp l
+ ENDM
+
+INTERRzr MACRO
+ local l
+ jnz short l
+ int 3
+ jmp $-1
+l:
+ ENDM
+
+INTERRc MACRO
+ local l
+ jnc short l
+ int 3
+ jmp $-1
+l:
+ ENDM
+
+INTERRnz MACRO
+ local l
+ jz short l
+ int 3
+ jmp $-1
+l:
+ ENDM
+else
+INTERR MACRO
+ ENDM
+INTERRzr MACRO
+ ENDM
+INTERRc MACRO
+ ENDM
+INTERRnz MACRO
+ ENDM
+endif
+
+;* Debug Traps
+;
+; These are removed as the code is exercised
+
+TRAPC macro
+ local l
+ jnc short l
+ int 3
+l:
+ ENDM
+
+TRAPZ macro
+ local l
+ jnz short l
+ int 3
+l:
+ ENDM
+
+TRAPNZ macro
+ local l
+ jz short l
+ int 3
+l:
+ ENDM
+
+
+;** PANIC - Panic File System
+;
+; BUGBUG - fix me to do something besides trap
+
+PANIC macro
+ local l
+l: int 3
+ jmp l
+ ENDM
+
+;** Bulk Register Save/Restore
+;
+
+SAVE MACRO reglist
+IRP reg,<reglist>
+ PUSH reg
+ENDM
+ENDM
+.xcref SAVE
+
+RESTORE MACRO reglist ;; pop those registers
+IRP reg,<reglist>
+ POP reg
+ENDM
+ENDM
+.xcref RESTORE
+
+
+;* ret16 - perform a 16bit return
+;
+; If we are in a use32 segment then we must put out an operand size
+; override before the ret.
+
+ret16 macro stkfix
+ ife @WordSize - 4
+ db 66h ;; operand size override
+ endif
+ retf stkfix
+ endm
+.xcref ret16
+
+
+;* call1616 - perform an indirect 16bit far call
+;
+; If we are in a use32 segment then we must put out an operand size
+; override before the call and then cast the target to "FWORD" so that
+; MASM will generate the correct instruction.
+;
+; The target must be indirect.
+
+call1616 macro target
+ .errnz (type target) - 4
+ ife @WordSize - 4
+ db 66h ;; operand size override
+ call fword ptr target ;; force indirect far call
+ else
+ call target
+ endif
+ endm
+.xcref call1616
+
+
+;** Dpush - Push 32-bit constant
+;
+; MASM has no way of expressing this in USE16 mode.
+
+DPUSH macro a
+ifdef USE32
+ push a
+else
+ push a ; low order
+ push 0
+endif
+ ENDM
+
+;** Push16 - generate a 16bit push in a 32-bit code segment. This is
+; needed when pushing segment regs and immediate values as arguments
+; to 16bit procedures.
+
+push16 macro operand
+ db 66h
+ push operand
+ endm
+
+
+;** STATINC - Do an INC if STAT gathering is enabled
+;
+; Preserves 'C'
+
+STATINC macro a
+ifdef STATS
+ inc a
+endif
+ ENDM
+
+;** STATDEC - Do an DEC if STAT gathering is enabled
+;
+; Preserves 'C'
+
+STATDEC macro a
+ifdef STATS
+ dec a
+endif
+ ENDM
+
+
+;** LogHCNT - Log OFT holding/unholding
+;
+
+ifdef DEBUG
+
+LOGHCNT MACRO reg
+ifndef DoLogHcnt
+ EXTRN DoLogHcnt:near
+endif
+ pushfd
+ push eax
+ mov eax,reg
+ call DoLogHcnt
+ pop eax
+ popfd
+ ENDM
+
+;** LogSCNT - Lock SBDIR holding/unholding
+;
+
+LOGSCNT MACRO REG
+ifndef DoLogScnt
+ EXTRN DoLogScnt:near
+endif
+ pushfd
+ push eax
+ifdif <REG>,<eax>
+ mov eax,reg
+endif
+ call DoLogScnt
+ pop eax
+ popfd
+ ENDM
+else
+LOGHCNT MACRO
+ ENDM
+LOGSCNT MACRO
+ ENDM
+endif
+
+ifdef DEBUG
+ CALLVBS MACRO
+ ifndef VBS
+ EXTRN VBS:NEAR
+ endif
+ call VBS
+ ENDM
+else
+ CALLVBS MACRO
+ ENDM
+endif
+
+;** cBUFZAP - Call DoZap iff debug mode set
+;
+
+cBUFZAP Macro
+ifdef DEBUG
+ifndef DoZap
+ EXTRN DoZap:near
+endif
+ call DoZap
+endif
+ endm
+
+
+;** Stack Frame Macros
+;
+; These macros are used to allow a stack frame to be setup by
+; simple PUSHES and yet guarantee that the pushes won't drift
+; out of sync with the frame declaration.
+
+LASTEL MACRO struc,elem
+ .errnz size struc - elem - size elem
+?frof = elem
+ ENDM
+
+NEXTEL MACRO elem
+ .errnz ?frof - elem - size elem
+?frof = elem
+ ENDM
+
+DUMYEL MACRO si
+?frof = ?frof - si
+ ENDM
+
+FIRSTEL MACRO elem
+ .errnz ?frof - size elem
+?frof = elem
+ .errnz elem
+ ENDM
+
+
+;** CHKSECNUM - Check Sector number
+;
+; CHKSECNUM reg
+;
+; Make sure that reg has a sector number in it without the high order
+; volume ID bits
+
+
+CHKSECNUM MACRO reg
+ local l1
+ifdef DEBUG
+ test reg,NOT SECMASK
+ jz l1
+ INTERR
+l1:
+endif
+ ENDM
diff --git a/private/ntos/boot/bootcode/hpfs/i386/makefile b/private/ntos/boot/bootcode/hpfs/i386/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/boot/bootcode/hpfs/i386/misc.inc b/private/ntos/boot/bootcode/hpfs/i386/misc.inc
new file mode 100644
index 000000000..7135cf542
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/misc.inc
@@ -0,0 +1,64 @@
+;static char *SCCSID = "@(#)misc.h 12.2 88/12/19";
+; #define DEBUG 1
+
+ifdef MASM
+
+ include filemode.inc
+
+ BREAK <Misc. Definitions>
+
+endif
+
+ERROR_OPLOCKED equ 0eeh
+
+
+;* MISC.INC - Miscelaneous structure definitions.
+;
+; These need to be included first because other structures
+; make use of them.
+;
+
+
+;* SecPtr - Sector Pointer Structure
+;
+; Structures which contain a sector number usually use the
+; SecPtr structure, which contains an advisory pointer. The
+; pointer points to a buffer header, which is *probably* the
+; header for the sector named in SecPtr, but the user must check.
+;
+
+SECPTR struc
+ SNUM dd ? ; VSector number
+ SHINT dw ? ; hint address, 0 if none
+SECPTR ends
+
+
+
+
+;* Write type flags for SDW
+;
+
+WT_CACH equ 01h ; write via cache
+WT_DIR equ 02h ; write direct as much as possible
+WT_EXT equ 04h ; write is extending the file
+
+
+;* Bit Map Sets
+
+BITMAPL equ -4 ; bit map length preceeds table
+BITMAPC equ -8 ; count of sectors left in bitmap
+
+
+;* conditional short value
+
+ifdef MASM
+ifdef USE16
+SHRT EQU < >
+else
+ifdef DEBUG
+SHRT EQU < >
+else
+SHRT EQU <short>
+endif
+endif
+endif
diff --git a/private/ntos/boot/bootcode/hpfs/i386/pinboot.asm b/private/ntos/boot/bootcode/hpfs/i386/pinboot.asm
new file mode 100644
index 000000000..412280f14
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/pinboot.asm
@@ -0,0 +1,727 @@
+ 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
diff --git a/private/ntos/boot/bootcode/hpfs/i386/sources b/private/ntos/boot/bootcode/hpfs/i386/sources
new file mode 100644
index 000000000..e877d5d35
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/sources
@@ -0,0 +1,38 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=utils
+MINORCOMP=pinboot
+
+TARGETNAME=pinboot
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+SOURCES=pinboot.asm
+
+INCLUDES=\nt\public\sdk\inc
+C_DEFINES=-DDBG -DMEMLEAK -DCONDITION_HANDLING=1 -DNOMINMAX
+UMLIBS=obj\*\chkdsk.lib
+
+UMTYPE=console
diff --git a/private/ntos/boot/bootcode/hpfs/i386/superb.inc b/private/ntos/boot/bootcode/hpfs/i386/superb.inc
new file mode 100644
index 000000000..e670b800b
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/superb.inc
@@ -0,0 +1,242 @@
+;** SUPERB.H - Super Block and Spare Block definitions
+;
+; FILESYS
+; Gregory A. Jones
+; Copyright 1988 Microsoft Corporation
+;
+; Modification history:
+; P.A. Williams 06/01/89 Added fields SPB_CPSEC and SPB_CPCNT to
+; the spare block.
+; P.A. Williams 06/05/89 Changed base and functional version no. to 1.
+;
+
+SEC_SUPERB equ 16 ; superblock is after 8K boot block
+SEC_SPAREB equ 17 ; spareblock is after superblock
+SEC_BOOT equ 0 ; boot sector
+
+
+;* SUPERB.INC - Super Block Definition
+;
+; The Superblock is the first block of the file system.
+; It starts at sector #4, leaving 2K for boot sectors.
+;
+; Pointer to the root directory
+; Pointer to the bit map
+; Clean pointer
+; Pointer to the bad list
+;
+
+RSP struc
+ P dd ? ; main psector pointer
+ P2 dd ? ; spare pointer
+RSP ends
+
+
+SuperB struc
+ SB_SIG1 dd ? ; signature value 1
+ SB_SIG2 dd ? ; signature value 2
+
+ SB_VER db ? ; version # of filesystem structures
+ SB_FVER db ? ; functional version number - the smallest/
+ ; oldest version of the filesystem that can
+ ; understand this disk - some version
+ ; enhancements may define fields which can be
+ ; ignored by earlier versions
+
+ SB_DUMY dw ? ; free
+
+ SB_ROOT dd ? ; Psector # of root fnode
+
+ SB_SEC dd ? ; # of sectors on volume
+ SB_BSEC dd ? ; # of bad sectors on volume
+
+ SB_BII db (size RSP) dup (?) ; Bitmap Indirect Block
+ SB_BBL db (size RSP) dup (?) ; badblock list chain #1
+
+ SB_CDDAT dd ? ; date of last CHKDSK
+ SB_DODAT dd ? ; date of last Disk Optimize
+
+ SB_DBSIZE dd ? ; # of sectors in dirblk band
+ SB_DBLOW dd ? ; first Psector in DIRBLK band
+ SB_DBHIGH dd ? ; last Psector in DIRBLK band
+ SB_DBMAP dd ? ; first Psector of DIRBLK band bit map. Starts
+ ; on a 2K boundary, 2K bytes maximum
+
+ SB_VOLNAME db 32 dup (?) ; Volume name
+
+ SB_SIDSEC dd ? ; sector # of first sector in SIDTAB
+ ; ID map is 4K - 8 contiguous sectors
+
+ SB_FILL db 512-100 dup (?) ; fill definition out to 512 bytes
+ ; MUST BE ZERO
+
+SuperB ends
+
+
+
+;* SpareB - Spare Block Definitions
+;
+; SpareB contains various emergency supplies and fixup information.
+; This stuff isn't in the superblock in order for the superblock
+; to be read only and decrease the liklihood that a flakey write
+; will cause the superblock to become unreadable.
+;
+; This sector is located directly after the superblock - sector 5.
+;
+; Note that the number of spare DIRBLKs is a format option, given
+; that they all have to fit into the SpareB, giving us a max of
+; 101 of them.
+;
+; Access to the SpareB is complicated by the fact that we can't
+; access it via the cache, since the cache may be unavailable.
+; If every cache buffer is dirty, we could get a HotFix error when
+; writing the first one, which would deadlock us if we needed to
+; read this stuff via the cache. Instead, we read it directly into
+; a private buffer via RdHF.
+;
+; This means that the disk layout must be such that each cache cluster
+; that contains the SpareB or the hotfix list must not contain any
+; other writable sector, to prevent us from having a modified
+; direct-written sector overwritten by an earlier unmodified copy
+; which was in a cache block. It's ok for the SuperB to be in the
+; same cache group as the SpareB since the SuperB is RO to the filesys.
+;
+; Checksums. Done on both Super Block and the Spare Block.
+; Both checksums are stored in the Spare Block. The checksum
+; field for the Super Block (SPB_SUPERBSUM) must be set when
+; calculating the checksum for the Spare Block. The checksum
+; field for the Spare Block (SPB_SPAREBSUM) must be zero when
+; calculating the checksum for the Spare Block.
+; If both checksum fields are zero, the checksums have not been
+; calculated for the volume.
+;
+
+SPAREDB equ 20 ; 20 spare DIRBLKs
+
+SpareB struc
+
+ SPB_SIG1 dd ? ; signature value 1
+ SPB_SIG2 dd ? ; signature value 2
+
+ SPB_FLAG db ? ; cleanliness flag
+ SPB_ALIGN db 3 dup (?) ; alignment
+
+ SPB_HFSEC dd ? ; first hotfix list P sector
+ SPB_HFUSE dd ? ; # of hot fixes in effect
+ SPB_HFMAX dd ? ; max size of hot fix list
+
+ SPB_SDBCNT dd ? ; # of spare dirblks
+ SPB_SDBMAX dd ? ; maximum number of spare DB values.
+ SPB_CPSEC dd ? ; code page sector
+ SPB_CPCNT dd ? ; number of code pages
+ SPB_SUPERBSUM dd ? ; Checksum of Super Block
+ SPB_SPAREBSUM dd ? ; Checksum of Spare Block
+ SPB_DUMY dd 15 dup (?) ; some extra space for future use
+ SPB_SPARDB dd 101 dup (?) ; Psector #s of spare dirblks
+SpareB ends
+
+
+; Super Block Signature
+
+SBSIG1 equ 0f995e849h ; two signatures cause we got lotsa
+SBSIG2 equ 0FA53E9C5h ; space
+SPSIG1 equ 0f9911849h ; two signatures cause we got lotsa
+SPSIG2 equ 0FA5229C5h ; space
+
+
+
+; Superblock Versions
+
+SBBASEV equ 2 ; base version
+SBBASEFV equ 2 ; base functional version
+
+; Spare Block Flags
+;
+
+SPF_DIRT equ 0001h ; file system is dirty
+SPF_SPARE equ 0002h ; spare DIRBLKs have been used
+SPF_HFUSED equ 0004h ; hot fix sectors have been used
+SPF_BADSEC equ 0008h ; bad sector, corrupt disk
+SPF_BADBM equ 0010h ; bad bitmap block
+SPF_VER equ 0080h ; file system was written by a version
+ ; < SB_VER, so some of the new fields
+ ; may have not been updated
+
+
+;* Bit maps
+;
+; PFS keeps track of free space in a series of bit maps.
+; Currently, each bit map is 2048 bytes, which covers about
+; 8 megabytes of disk space. We could rearrange these to be
+; more cylinder sensitive...
+;
+; The superblock has the address of a section of contiguous sectors
+; that contains a double word sector # for each bit map block. This
+; will be a maximum of 2048 bytes (4 sectors)
+;
+; Max # of size RAM (K) size 2nd lvl
+; bitmaps (meg) to reside bitmap
+; bitmap (bytes)
+;
+; 1 8.39 2 256
+; 2 16.78 4 512
+; 3 25.17 6 768
+; 4 33.55 8 1024
+; 5 41.94 10 1280
+; 6 50.33 12 1536
+; 7 58.72 14 1792
+; 8 67.11 16 2048
+; 9 75.50 18 2304
+; 10 83.89 20 2560
+; 15 125.83 30 3840
+; 20 167.77 40 5120
+; 30 251.66 60 7680
+; 40 335.54 80 10240
+; 50 419.43 100 12800
+; 100 838.86 200 25600
+; 200 1677.72 400 51200
+; 300 2516.58 600 76800
+; 400 3355.44 800 102400
+; 500 4194.30 1000 128000
+;
+
+
+
+;* Hot Fixing
+;
+; Each file system maintains a structure listing N "hot fix"
+; disk clusters of HOTFIXSIZ sectors each, each starting on
+; a multiple of HOTFIXSIZ. Whenever the file system discovers
+; that it's trying to write to a bad spot on the disk it will
+; instead select a free hot fix cluster and write there, instead.
+; The substitution will be recorded in the hot fix list, and the
+; SBF_SPARE bit will be set. The file system sill describes the
+; data as being in the bad old sectors; the disk interface will
+; do a mapping between the `believed' location and the true location.
+;
+; CHKDSK will be run as soon as possible; it will move the
+; hot fixed data from the hot fix cluster to somewhere else,
+; freeing that hot fix cluster, and adjusting the disk structure
+; to point to the new location of the data. As a result, entrys
+; on the hot fix list should be transient and few.
+;
+; The superblock contains the first sector of the hot fix list
+; which takes the following format:
+;
+; long oldsec[SB_HFMAX]; sector # of start of bad clusters
+; long newsec[SB_HFMAX]; sector # of start of subst. cluster
+; long fnode [SB_HFMAX]; fnode sector of file/directory
+; involved with bad cluster. May be
+; 0 (don't know) or invalid. The
+; repair program must verify that it
+; *is* an FNODE and must see if other
+; structures might also involve this
+; bad cluster.
+;
+; the SB_HFUSE field describes the number of these records which is
+; in use - unused ones should have oldsec[i] = 0. The list will
+; be 'dense' - no oldsec[i] will be 0 where i < SB_HFUSE.
+;
+; The sector(s) which contain the hot fix list must be contiguous
+; and may not themselves be defective.
+;
diff --git a/private/ntos/boot/bootcode/hpfs/i386/tables.inc b/private/ntos/boot/bootcode/hpfs/i386/tables.inc
new file mode 100644
index 000000000..07cf61244
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/tables.inc
@@ -0,0 +1,201 @@
+;static char *SCCSID = "@(#)tables.h 12.2 88/12/19";
+ifdef MASM
+ BREAK <OFT - Open File Table>
+endif
+
+;* OFT - Open File Table
+;
+; The OFT contains file specific information which is independent
+; of an instance of use of the file.
+;
+; If the file is open for write access, we keep a pointer to
+; it's directory entry, not just it's
+; FNODE, so that we can update the length and modification time when it's
+; written.
+;
+; We store the directory's FNODE number and the file name, with these
+; we can find the directory entry again even if the directory has
+; been shuffled by creates or deletes of other files. Two advisorys
+; are kept - the Vsector number, cache block address, and cache block
+; offset of the actual directory entry. This information has three
+; levels of validity:
+;
+; 1) Vsector, header address, and offset are valid
+; you can find the dir entry easily
+; 2) the header address is invalid due to cache flushing.
+; Vsector # and offset are still valid, so read the
+; cache block and go directly to the entry
+; In this case the header address points to the wrong
+; Vsector
+; 3) the directory has been altered since we opened the file,
+; so none of this info is accurate. In this case the
+; directory alteration routines located this OFT
+; entry and zeroed the Vsector number. Go to the directory
+; and search the name again.
+;
+
+OFT struc
+ OFT_P db (size DCHDR) dup (?) ; double chain of OFT structures, rooted in OFTHead[i]
+ OFT_FHT db (size DCHDR) dup (?) ; double chain of FHT structures
+ OFT_RLCK db (size DCHDR) dup (?) ; double chain of RECLOCK structures
+ OFT_FN db (size SECPTR) dup (?) ; pointer to file FNODE
+ OFT_SBD dw ? ; pointer to SBDIR structure
+ OFT_DIRE db (size SECPTR) dup (?) ; pointer to directory block
+
+ OFT_CCNT dd ? ; DB_CCNT value for OFT_DIRE to check OFT_DIRO validity
+ OFT_FREEDCNT dd ? ; SD_FREEDCNT value for DIRE to be valid
+
+;
+; These length fields are used when writing the file.
+; The OFN_ entries will reflect the OFT_ALEN value. These
+; aren't updated in the FNODE until the file closes, whereup
+; they are trimmed back to OFT_LEN which is itself propigated
+; to the file's DIR entry. If write-through is set then the
+; OFN_ entries below will be propigated to the FNODE and
+; then to the disk, but if we crash the cleanup program
+; will deallocate the "extra" sectors.
+;
+
+ OFT_LEN dd ? ; file actual length
+ OFT_ALEN dd ? ; file allocated length (SECSIZE multiple)
+ OFT_WLEN dd ? ; last byte+1 of valid data in the file
+ ; (optimization for writing into extended areas
+ OFT_LAST dd ? ; Psector # of last sector in file, or 0
+ OFT_LRP db (size SECPTR) dup (?) ; last run pointer. Valid if OFT_LAST !=0
+ ; if OFN_AB.ABF_NODE ==0 this is the address of
+ ; an OFN_SEC record in this OFT
+ ; else this is a SECPTR to the SIB
+ ; containing the last run record
+
+ OFT_VOL dd ? ; pointer to VOLTAB for this guy
+
+ OFT_NAME dd ? ; address of name string len byte
+ ; followed by name string itself
+ ; len doesn't include len byte
+
+ OFT_DIRO dw ? ; offset into directory block for entry
+ OFT_FILL dw ? ; unused, fill
+
+ OFT_LCKCNT dw ? ; count of guys in OFT_RLCK
+
+ OFT_WCNT dw ? ; # of threads blocked on this OFT
+
+ ; the following three fields are used to
+ ; control access. They're identical in use
+ ; to the equivalent fields in SBDIR. The
+ ; Flag byte only contains locking/holding
+ ; flags, so if DWORD PTR OFT_HCNT is 0 then
+ ; the OFT is known to be free and clear
+
+ OFT_HCNT dw ? ; held count, has OTF_PND bit also
+ OFT_DMY db ? ; unused, must be 0
+ OFT_FLAG db ? ; flag byte, high order in OFT_HCNT dword
+
+ OFT_WAITC dd ? ; head of wait chain if locked
+
+ OFT_OPLOCK dd ? ; Oplock value, 0 if none
+
+ OFT_BANDP dd ? ; pointer to BandTab structure in BandList
+ ; for our last allocation. =0 if unused
+
+; The following 5 fields hold the accumulation of all FHTs for this OFT.
+; This saves us from having to scan the FHTs to do a sharing check upon
+; open. An OFT is unused when OFT_RD and OFT_WT and OFT_FIND are zero
+
+ OFT_RD dw ? ; # of opens for read
+ OFT_WT dw ? ; # of opens for write
+ OFT_DR dw ? ; # of opens with deny read
+ OFT_DW dw ? ; # of opens with deny write
+ OFT_COMPAT dw ? ; # of opens for compatibility mode
+ OFT_FIND dw ? ; count of active FINDs using this OFT
+
+ OFT_REALLYBAD db ? ; non-zero if file is unusable due to
+ ; corrupt disk BUGBUG - FOLD INTO BITS
+ OFT_SFLAG db ? ; special flag byte
+ OFT_DMY2 dw ? ; unused
+
+
+; BUGBUG - rearrange these fields for er offsets dw ?
+;
+; Info copied from the file's FNODE
+;
+; The ALLEAF blocks must follow the ALBLK value
+;
+
+ OFN_AB db (size ALBLK) dup (?) ; allocation block structure
+ OFN_ALREC db (8*size ALLEAF) dup (?) ; referenced from FN_AB
+
+OFT ends
+
+; flag bits for OFT_FLAG
+
+OTF_LCK equ 01h ; file is locked against access
+OTF_PLK equ 02h ; pending lock
+OTF_PSO equ 04h ; pending solo
+OTF_PND equ 80h ; lock pending bit
+
+; flag bits for OFT_SFLAG
+
+OFS_OPLK equ 01h ; oplocked
+OFS_OPBA equ 02h ; oplock BATCH flag set
+OFS_DASD equ 04h ; DASD file
+ifdef DEBUG
+OFS_SAC equ 08h ; supress OFT_ALEN debug check
+endif
+
+; The file storage information from the fnode is replicated
+; in the OFT. This saves us from having to blow a cache block
+; keeping the FNODE in ram for every open file. The FNODE
+; is only accessed when a file is open, and when it's closed.
+; (If write-through is set, it's accessed for every growth)
+;
+; These statements are to keep the FNODE and the OFT in sync.
+
+ifdef MASM
+ .errnz (size OFN_ALREC - size FN_ALREC)
+endif
+
+
+;* FHT - File Handle Table
+;
+; The FHT contains per-handle informatiuon
+
+FHT struc
+ FHT_SEEK dd ? ; seek pointer
+ FHT_OFT dd ? ; pointer to OFT
+ FHT_CHN db (size DCHDR) dup (?) ; chain of FHTs for an OFT
+ FHT_UID dd ? ; UID and Session ID
+ FHT_MODE dw ? ; mode bits from OPEN
+ FHT_RAA dw ? ; read ahead advisory
+ FHT_HINT dd ? ; hint flags
+FHT ends
+
+ifdef MASM
+ .errnz FHT_CHN-OFT_FHT ; same offset used for both
+endif
+
+;* FHT_HINT flags
+
+FHH_SEQ equ 01 ; sequential file
+
+
+;* RecLock - Record Locking Records
+;
+; One record per lock, chained to the OFT. These are chained
+; in order RL_BEG
+;
+
+RECLOCK struc
+ RL_BEG dd ? ; begining byte of locked range
+ RL_END dd ? ; end byte of locked range
+ RL_TYPE db ? ; =1 if read allowed, =0 if full lock
+ RL_MEM db ? ; =0 if from heap, =1 if from special list
+ RL_DMY dw ? ; padding
+ RL_SPID dd ? ; Session/Pid
+ RL_CHN db (size DCHDR) dup (?) ; double chain of RECLOCK structures
+RECLOCK ends
+
+ifdef MASM
+ .errnz RL_CHN-OFT_RLCK ; must have same offset to work
+endif
+ \ No newline at end of file
diff --git a/private/ntos/boot/bootcode/hpfs/i386/usa/boothpfs.h b/private/ntos/boot/bootcode/hpfs/i386/usa/boothpfs.h
new file mode 100644
index 000000000..b9ff8c722
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/usa/boothpfs.h
@@ -0,0 +1,517 @@
+#define HPFSBOOTCODE_SIZE 8192
+
+
+unsigned char HpfsBootCode[] = {
+235,73,144,73,66,77,32,49,48,46,50,0,2,4,1,0,
+2,0,2,0,0,248,41,0,17,0,4,0,17,0,0,0,
+195,162,0,0,128,0,40,21,156,213,100,67,45,68,82,73,
+86,69,0,0,0,0,72,80,70,83,32,32,32,32,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,62,0,0,0,199,
+6,64,0,0,0,199,6,69,0,20,0,184,0,13,142,192,
+43,219,232,7,0,104,0,13,104,0,2,203,80,83,81,82,
+6,161,62,0,139,22,64,0,3,6,28,0,19,22,30,0,
+247,54,24,0,254,194,136,22,68,0,51,210,247,54,26,0,
+136,22,37,0,163,66,0,161,24,0,42,6,68,0,64,59,
+6,69,0,118,3,161,69,0,80,180,2,139,22,66,0,177,
+6,210,230,10,54,68,0,139,202,134,233,139,22,36,0,205,
+19,88,114,37,1,6,62,0,131,22,64,0,0,41,6,69,
+0,118,11,193,224,5,140,194,3,208,142,194,235,147,7,90,
+89,91,88,195,190,57,1,235,3,190,25,1,232,9,0,190,
+141,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,5,78,84,76,68,82,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,170,
+140,200,142,216,5,128,2,142,192,140,216,193,224,4,250,139,
+224,251,187,12,32,139,7,163,62,0,139,71,2,163,64,0,
+199,6,69,0,1,0,43,219,232,58,0,38,139,71,72,163,
+27,4,38,139,71,74,163,29,4,190,193,1,161,27,4,163,
+62,0,161,29,4,163,64,0,232,138,0,184,0,32,232,16,
+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,69,0,255,54,
+62,0,255,54,64,0,139,195,193,232,4,140,193,3,193,37,
+255,15,45,0,16,247,216,139,14,69,0,193,225,5,81,59,
+193,118,2,139,193,80,193,232,5,163,69,0,232,221,253,88,
+89,43,200,118,11,140,194,3,208,142,194,184,0,16,235,222,
+143,6,64,0,143,6,62,0,143,6,69,0,7,90,89,91,
+88,195,6,83,199,6,69,0,4,0,30,7,187,0,40,232,
+147,255,91,7,195,30,7,232,232,255,43,201,138,12,70,139,
+209,187,20,40,235,5,3,31,232,23,1,139,198,139,202,141,
+127,31,243,166,139,240,117,6,58,87,30,117,1,195,246,71,
+2,4,116,19,3,31,139,71,252,163,62,0,139,71,254,163,
+64,0,232,173,255,235,202,246,71,2,8,116,201,187,0,40,
+246,71,8,1,116,3,233,239,0,161,62,0,163,71,0,161,
+64,0,163,73,0,139,71,12,163,62,0,139,71,14,163,64,
+0,232,126,255,187,20,40,235,2,3,223,139,63,139,65,252,
+59,6,71,0,117,243,139,65,254,59,6,73,0,117,234,235,
+182,80,43,237,139,71,12,163,128,4,139,71,14,163,130,4,
+139,71,4,163,62,0,139,71,6,163,64,0,232,67,255,7,
+190,64,40,187,56,40,246,7,128,117,76,138,79,5,181,0,
+139,68,8,163,62,0,139,68,10,163,64,0,139,68,4,163,
+69,0,83,43,219,232,189,254,91,140,192,193,38,69,0,5,
+3,6,69,0,142,192,131,198,12,226,213,129,62,2,40,228,
+247,116,62,161,8,40,163,62,0,161,10,40,163,64,0,232,
+240,254,94,91,131,198,8,138,71,5,180,0,193,224,3,3,
+195,5,8,0,59,240,115,211,83,86,139,68,4,163,62,0,
+139,68,6,163,64,0,232,201,254,190,20,40,187,12,40,235,
+133,195,138,79,30,50,237,141,127,31,38,128,61,90,118,4,
+38,128,45,32,71,226,243,195,233,217,252,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,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/hpfs/i386/usa/pinboot.inc b/private/ntos/boot/bootcode/hpfs/i386/usa/pinboot.inc
new file mode 100644
index 000000000..6a2925984
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/usa/pinboot.inc
@@ -0,0 +1,30 @@
+; 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
diff --git a/private/ntos/boot/bootcode/hpfs/i386/volume.inc b/private/ntos/boot/bootcode/hpfs/i386/volume.inc
new file mode 100644
index 000000000..dbeba1b9d
--- /dev/null
+++ b/private/ntos/boot/bootcode/hpfs/i386/volume.inc
@@ -0,0 +1,102 @@
+;static char *SCCSID = "@(#)volume.h 12.2 89/09/19";
+; Maximum number of volumes that we can mount
+;
+; The volume ID is kept in the high bits of the sector numbers
+; kept in our RAM structures,
+; so there is a tradeoff between max volumes and max sectors.
+;
+; 32 max volumes gives us a 65 billion byte volume limit,
+; which should last us for a while. Since sector numbers
+; are stored on the disk without their volume upper bits
+; this is strictly an implimentation detail; we can adjust
+; the number of volumes or eliminate this tradeoff in other
+; implimentations which will be 100% media compatable.
+;
+; We use the term VSector to indicate a vol/sector combination
+; and PSector to indicate just the physical absolute sector #
+;
+;
+
+
+; Bitmap related numbers
+
+BANDSHIFT equ BSHIFT+3 ; right shift sector # to band index
+BANDMASK equ SPB*SECSIZE*8L-1 ; mask for within band bits
+BANDSIZE equ SPB*SECSIZE*8L ; # of sectors in a full band
+
+
+;* BandTab - Disk Band Table
+;
+; The disk is broken up into logical bands, each band being
+; the amount of space that is addressed in 2K of bitmap.
+;
+; This structure tracks the bands: the location of their respective
+; bit maps, the amount of free space, etc.
+;
+
+BANDTAB struc
+ BT_MAP db (size SECPTR) dup (?) ; Vsector # and hint pointer for map
+ BT_FREE dw ? ; # of free sectors in this band
+ BT_OFC dw ? ; # of files allocating from this band
+ BT_BASE dd ? ; Psector # of first sector in map
+ BT_LEN dd ? ; byte length of this map
+ BT_HWO dd ? ; high water offset to 1st non-zero byte
+ ; BUGBUG - use BT_HWO
+BANDTAB ends
+
+
+;* VolTab - Volume Table
+;
+; VolPtr[i] points to the VolTab structure for that volume.
+; This table contains volume specific information.
+;
+; Nearly all file system API refers to a single particular volume.
+; The proper volume is determined when the file system is entered
+; and the TDB structure contains a pointer to it. Most code ignores
+; volumes and deals with 32 bit physical sector #'s. When we're about
+; to interface with the device driver we then peek at the "global"
+; volume value pointed to by TDB.
+;
+; There are two exceptions to this, where per-volume structures are
+; pooled, the buffer pool and the OFT pool. In these two cases the
+; sector number has the volume index set in it's high order VOLLSHIFT
+; bits so that a single DWORD compare will qualify a sector on both
+; a volume and sector basis.
+;
+
+VOLTAB struc
+ VOL_FFLAG db ? ; Fault flags - checked on most calls
+ VOL_SFLAG db ? ; status flags
+ VOL_PAD dw ? ; unused - bugbug
+ VOL_SECVAL dd ? ; value to set on high order part of sector #
+ VOL_BCNT dw ? ; # of bitmap bands in this volume
+ VOL_VDBCnt dw ? ; count of outstanding VerifyDB calls *.
+ VOL_SDBcnt dd ? ; count of spare DIRBLKs left for volume, if
+ ; all are unused, else 0
+ VOL_SBSEC dd ? ; SB_SEC value from superblock
+ VOL_DB db (size BANDTAB) dup (?) ; DIRBLK bandtab
+ VOL_ROOT dw ? ; Root SBDIR pointer
+ VOL_SPACE dd ? ; alloctable space limit
+ VOL_DBSIZE dd ? ; copy of SP_DBSIZE value
+ VOL_HFUSE dd ? ; # of hot fixes in effect
+ VOL_HFMAX dd ?
+ VOL_HFPTR dd ? ; address of hotfix heap array - bad sectors
+ VOL_HFNEW dd ? ; address of substitute list - replacement sectors
+ VOL_BPTR dw 1 dup (?) ; first of VOL_BCNT pointers
+ ; one per band. The BANDTABs that they
+ ; point to must be physically contiguous
+VOLTAB ends
+
+; VOL_FFLAG fault flags
+;
+; these represent conditions that we're trying to repair,
+; we check these on most major file system calls
+;
+
+VF_NEEDHOT equ 01h ; hotfix list is partially used
+VF_NEEDDIR equ 02h ; dirblk reserved list is partially used
+
+; VOL_SFLAG status flags
+;
+
+VS_BADSEC equ 01h ; we have at least one bad sector on there
diff --git a/private/ntos/boot/bootcode/mbr/i386/usa/bootmbr.h b/private/ntos/boot/bootcode/mbr/i386/usa/bootmbr.h
new file mode 100644
index 000000000..f735c6160
--- /dev/null
+++ b/private/ntos/boot/bootcode/mbr/i386/usa/bootmbr.h
@@ -0,0 +1,37 @@
+#define X86BOOTCODE_SIZE 512
+
+
+unsigned char x86BootCode[] = {
+250,51,192,142,208,188,0,124,139,244,80,7,80,31,251,252,
+191,0,6,185,0,1,243,165,234,29,6,0,0,190,190,7,
+179,4,128,60,128,116,14,128,60,0,117,28,131,198,16,254,
+203,117,239,205,24,139,20,139,76,2,139,238,131,198,16,254,
+203,116,26,128,60,0,116,244,190,139,6,172,60,0,116,11,
+86,187,7,0,180,14,205,16,94,235,240,235,254,191,5,0,
+187,0,124,184,1,2,87,205,19,95,115,12,51,192,205,19,
+79,117,237,190,163,6,235,211,190,194,6,191,254,125,129,61,
+85,170,117,199,139,245,234,0,124,0,0,73,110,118,97,108,
+105,100,32,112,97,114,116,105,116,105,111,110,32,116,97,98,
+108,101,0,69,114,114,111,114,32,108,111,97,100,105,110,103,
+32,111,112,101,114,97,116,105,110,103,32,115,121,115,116,101,
+109,0,77,105,115,115,105,110,103,32,111,112,101,114,97,116,
+105,110,103,32,115,121,115,116,101,109,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,170
+};
diff --git a/private/ntos/boot/bootcode/mbr/i386/usa/x86mboot.msg b/private/ntos/boot/bootcode/mbr/i386/usa/x86mboot.msg
new file mode 100644
index 000000000..18bfbe446
--- /dev/null
+++ b/private/ntos/boot/bootcode/mbr/i386/usa/x86mboot.msg
@@ -0,0 +1,5 @@
+;******** messages for the Fixed Disk Boot Record ******
+;
+m1: db "Invalid partition table",0
+m2: db "Error loading operating system",0
+m3: db "Missing operating system",0
diff --git a/private/ntos/boot/bootcode/mbr/i386/x86mboot.asm b/private/ntos/boot/bootcode/mbr/i386/x86mboot.asm
new file mode 100644
index 000000000..117f16296
--- /dev/null
+++ b/private/ntos/boot/bootcode/mbr/i386/x86mboot.asm
@@ -0,0 +1,133 @@
+;/*
+; * Microsoft Confidential
+; * Copyright (C) Microsoft Corporation 1983 - 1991
+; * All Rights Reserved.
+; */
+; BOOT - IBM hard disk boot record 6/8/82
+;
+;
+; This is the standard boot record that will be shipped on all hard disks. It contains:
+;
+; 1. Code to load (and give control to) the boot record for 1 of 4 possible
+; operating systems.
+;
+; 2. A partition table at the end of the boot record, followed by the required signature.
+;
+;
+
+relocated_org equ 0600h
+buildtime_org equ 0100h
+org_delta equ (relocated_org - buildtime_org)
+
+_data segment public
+ assume cs:_data,ds:_data
+
+;
+; /tiny programs start at 100h.
+;
+
+ org buildtime_org
+start:
+
+ cli ;no interrupts for now
+ xor ax,ax
+ mov ss,ax
+ mov sp,7c00h ;new stack at 0:7c00
+ mov si,sp ;where this boot record starts - 0:7c00
+ push ax
+ pop es ;seg regs the same
+ push ax
+ pop ds
+ sti ;interrupts ok now
+ cld
+ mov di,relocated_org ;where to relocate this boot record to
+ mov cx,100h
+ rep movsw ;relocate to 0:0600
+; jmp entry2 + org_delta
+ db 0eah
+ dw $+4+org_delta,0
+entry2:
+ mov si,(offset tab) + org_delta ;partition table
+ mov bl,4 ;number of table entries
+next:
+ cmp byte ptr[si],80h ;is this a bootable entry?
+ je boot ;yes
+ cmp byte ptr[si],0 ;no, is boot indicator zero?
+ jne bad ;no, it must be x"00" or x"80" to be valid
+ add si,16 ;yes, go to next entry
+ dec bl
+ jnz next
+ int 18h ;no bootable entries - go to rom basic
+boot:
+ mov dx,[si] ;head and drive to boot from
+ mov cx,[si+2] ;cyl, sector to boot from
+ mov bp,si ;save table entry address to pass to partition boot record
+next1:
+ add si,16 ;next table entry
+ dec bl ;# entries left
+ jz tabok ;all entries look ok
+ cmp byte ptr[si],0 ;all remaining entries should begin with zero
+ je next1 ;this one is ok
+bad:
+ mov si,(offset m1) + org_delta ;oops - found a non-zero entry - the table is bad
+msg:
+ lodsb ;get a message character
+ cmp al,0
+ je hold
+ push si
+ mov bx,7
+ mov ah,14
+ int 10h ;and display it
+ pop si
+ jmp msg ;do the entire message
+;
+hold: jmp hold ;spin here - nothing more to do
+tabok:
+ mov di,5 ;retry count
+rdboot:
+ mov bx,7c00h ;where to read system boot record
+ mov ax,0201h ;read 1 sector
+ push di
+ int 13h ;get the boot record
+ pop di
+ jnc goboot ;successful - now give it control
+ xor ax,ax ;had an error, so
+ int 13h ;recalibrate
+ dec di ;reduce retry count
+ jnz rdboot ;if retry count above zero, go retry
+ mov si,(offset m2) + org_delta ;all retries done - permanent error - point to message,
+ jmp msg ;go display message and loop
+goboot:
+ mov si,(offset m3) + org_delta ;prepare for invalid boot record
+ mov di,07dfeh
+ cmp word ptr [di],0aa55h ;does the boot record have the
+ ; required signature?
+ jne msg ;no, display invalid system boot record message
+ mov si,bp ;yes, pass partition table entry address
+ db 0eah
+ dw 7c00h,0
+
+include x86mboot.msg
+
+ org 2beh
+tab: ;partition table
+ dw 0,0 ;partition 1 begin
+ dw 0,0 ;partition 1 end
+ dw 0,0 ;partition 1 relative sector (low, high parts)
+ dw 0,0 ;partition 1 # of sectors (low, high parts)
+ dw 0,0 ;partition 2 begin
+ dw 0,0 ;partition 2 end
+ dw 0,0 ;partition 2 relative sector
+ dw 0,0 ;partition 2 # of sectors
+ dw 0,0 ;partition 3 begin
+ dw 0,0 ;partition 3 end
+ dw 0,0 ;partition 3 relative sector
+ dw 0,0 ;partition 3 # of sectors
+ dw 0,0 ;partition 4 begin
+ dw 0,0 ;partition 4 end
+ dw 0,0 ;partition 4 relative sector
+ dw 0,0 ;partition 4 # of sectors
+signa db 55h,0aah ;signature
+
+_data ends
+ end start
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