     1                                  
     2                                  %if 0
     3                                  
     4                                  File system boot sector loader code for FAT32
     5                                  
     6                                  Adapted from 2002-11-26 fatboot.zip/fat12.asm,
     7                                   released as public domain by Chris Giese
     8                                  
     9                                  Public domain by C. Masloch, 2012
    10                                  
    11                                  %endif
    12                                  
    13                                  
    14                                  %include "lmacros2.mac"
    15                              <1> [list -]
    28                              <1> [list -]
    15                                  
    16                                  %ifndef _MAP
    17                                  %elifempty _MAP
    18                                  %else	; defined non-empty, str or non-str
    19                                  	[map all _MAP]
    20                                  %endif
    21                                  
    22                                  	defaulting
    23                                  
    24                                  	strdef OEM_NAME,	"    lDOS"
    25                                  	strdef OEM_NAME_FILL,	'_'
    26                                  	strdef DEFAULT_LABEL,	"lDOS"
    27                                  	numdef VOLUMEID,	0
    28                                  	strdef FSIBOOTNAME,	"FSIBOOT4"
    29                                  	; used to set experimental name
    30                                  	; strdef FSIBOOTNAME,	"FSIBEX02"
    31                                  
    32                                  	strdef LOAD_NAME,	"LDOS"
    33                                  	strdef LOAD_EXT,	"COM"	; name of file to load
    34                                  	numdef LOAD_ADR,	02000h	; where to load
    35                                  	numdef LOAD_MIN_PARA,	paras(1536)
    36                                  	numdef EXEC_SEG_ADJ,	0	; how far cs will be from _LOAD_ADR
    37                                  	numdef EXEC_OFS,	400h	; what value ip will be
    38                                  	numdef CHECKOFFSET,	1020
    39                                  	numdef CHECKVALUE,	"lD"
    40                                  	numdef LOAD_DIR_SEG,	0	; => where to store dir entry (0 if nowhere)
    41                                  	numdef ADD_SEARCH,	0	; whether to search second file
    42                                  	strdef ADD_NAME,	""
    43                                  	strdef ADD_EXT,		""	; name of second file to search
    44                                  	numdef ADD_DIR_SEG,	0	; => where to store dir entry (0 if nowhere)
    45                                  
    46                                  	numdef QUERY_GEOMETRY,	1	; query geometry via 13.08 (for CHS access)
    47                                  	numdef USE_PART_INFO,	1	; use ds:si-> partition info from MBR, if any
    48                                  	numdef USE_AUTO_UNIT,	1	; use unit passed from ROM-BIOS in dl
    49                                  	numdef RPL,		1	; support RPL and do not overwrite it
    50                                  	numdef CHS,		1	; support CHS (if it fits)
    51                                  	numdef LBA,		1	; support LBA (if available)
    52                                  	numdef LBA_33_BIT,	1	; support 33-bit LBA
    53                                  	numdef LBA_CHECK_NO_33,	1	; else: check that LBA doesn't carry
    54                                  
    55                                  	numdef RELOCATE,	0	; relocate the loader to top of memory
    56                                  	numdef SET_DL_UNIT,	0	; if to pass unit in dl
    57                                  	numdef SET_BL_UNIT,	0	; if to pass unit in bl as well
    58                                  	numdef SET_AXBX_DATA,	0	; if to pass first data sector in ax:bx
    59                                  	numdef SET_DSSI_DPT,	0	; if to pass DPT address in ds:si
    60                                  	numdef PUSH_DPT,	0	; if to push DPT address
    61                                  	numdef MEMORY_CONTINUE,	1	; if to just execute when memory full
    62                                  	numdef SET_SIDI_CLUSTER,0	; if to pass first load file cluster in si:di
    63                                  	numdef TURN_OFF_FLOPPY,	0	; if to turn off floppy motor after loading
    64                                  	numdef DATASTART_HIDDEN,0	; if to add hidden sectors to data_start
    65                                  	numdef LBA_SET_TYPE,	0	; if to set third byte to LBA partition type
    66                                  	numdef SET_LOAD_SEG,	1	; if to set load_seg (word [ss:bp - 6])
    67                                  	numdef SET_FAT_SEG,	1	; if to set fat_seg (word [ss:bp - 8])
    68                                  	numdef SET_CLUSTER,	1	; if to set first_cluster (dword [ss:bp - 16])
    69                                  	numdef ZERO_ES,		0	; if to set es = 0 before jump
    70                                  	numdef ZERO_DS,		0	; if to set ds = 0 before jump
    71                                  
    72                                  	numdef FIX_SECTOR_SIZE, 0	; fix sector size (0 = disable, else = sector size)
    73                                  	numdef FIX_SECTOR_SIZE_SKIP_CHECK,	0	; don't check sector size
    74                                  	numdef FIX_CLUSTER_SIZE,		0	; fix cluster size
    75                                  	numdef FIX_CLUSTER_SIZE_SKIP_CHECK,	0	; don't check cluster size
    76                                  	numdef NO_LIMIT,	0	; allow using more memory than a boot sector
    77                                  					;  also will not write 0AA55h signature!
    78                                  	numdef LBA_SKIP_CHECK,	1	; don't use proper LBA extensions check
    79                                  	numdef LBA_RETRY,	0	; retry LBA reads one time
    80                                  	numdef CHS_RETRY,	1	; retry CHS reads one time
    81                                  	numdef CHS_RETRY_REPEAT,16	; retry CHS reads multiple times
    82                                  					; (value of the def is used as count)
    83                                  
    84                                  		; Unlike the 1440 KiB diskette image defaults for the FAT12
    85                                  		;  loader we just fill the BPB with zeros by default.
    86                                  	numdef MEDIAID, 0		; media ID
    87                                  	numdef UNIT, 0			; load unit in BPB
    88                                  	numdef CHS_SECTORS, 0		; CHS geometry field for sectors
    89                                  	numdef CHS_HEADS, 0		; CHS geometry field for heads
    90                                  	numdef HIDDEN, 0		; number of hidden sectors
    91                                  	numdef SPI, 0			; sectors per image
    92                                  	numdef BPS, 0			; bytes per sector
    93                                  	numdef SPC, 0			; sectors per cluster
    94                                  	numdef SPF, 0			; sectors per FAT
    95                                  	numdef SECTOR_FSINFO, 0		; FSINFO sector
    96                                  	numdef SECTOR_BACKUP, 0		; backup boot sector
    97                                  	numdef CLUSTER_ROOT, 0		; root directory first cluster
    98                                  	numdef NUMFATS, 0		; number of FATs
    99                                  	numdef NUMRESERVED, 0		; number of reserved sectors
   100                                  
   101                                  	numdef COMPAT_FREEDOS,	0	; partial FreeDOS load compatibility
   102                                  	numdef COMPAT_IBM,	0	; partial IBMDOS load compatibility
   103                                  	numdef COMPAT_MS7,	0	; partial MS-DOS 7 load compatibility
   104                                  	numdef COMPAT_LDOS,	0	; lDOS load compatibility
   105                                  
   106                                  %if (!!_COMPAT_FREEDOS + !!_COMPAT_IBM + !!_COMPAT_MS7 + !!_COMPAT_LDOS) > 1
   107                                   %error At most one set must be selected.
   108                                  %endif
   109                                  
   110                                  %if _COMPAT_FREEDOS
   111                                  	strdef LOAD_NAME,	"KERNEL"
   112                                  	strdef LOAD_EXT,	"SYS"
   113                                  	numdef LOAD_ADR,	00600h
   114                                  	numdef LOAD_MIN_PARA,	paras(512)
   115                                  	numdef EXEC_SEG_ADJ,	0
   116                                  	numdef EXEC_OFS,	0
   117                                  	numdef CHECKVALUE,	0
   118                                  
   119                                  	numdef SET_BL_UNIT,	1
   120                                  	numdef MEMORY_CONTINUE,	0
   121                                  	numdef RELOCATE,	1
   122                                  	; The FreeDOS load protocol mandates that the entire file be loaded.
   123                                  %endif
   124                                  
   125                                  %if _COMPAT_IBM
   126                                  	strdef LOAD_NAME,	"IBMBIO"
   127                                  	strdef LOAD_EXT,	"COM"
   128                                  	numdef LOAD_ADR,	00700h
   129                                  	numdef LOAD_MIN_PARA,	paras(512)
   130                                  	numdef EXEC_SEG_ADJ,	0
   131                                  	numdef EXEC_OFS,	0
   132                                  	numdef CHECKVALUE,	0
   133                                  	numdef LOAD_DIR_SEG,	50h
   134                                  	numdef ADD_SEARCH,	1
   135                                  	strdef ADD_NAME,	"IBMDOS"
   136                                  	strdef ADD_EXT,		"COM"
   137                                  	numdef ADD_DIR_SEG,	52h
   138                                   	; Note: The IBMBIO.COM directory entry must be stored at
   139                                  	;  0:500h, and the IBMDOS.COM directory entry at 0:520h.
   140                                  
   141                                  	numdef SET_DL_UNIT,	1
   142                                  	numdef MEMORY_CONTINUE,	1
   143                                  	; 3 sectors * 512 BpS should suffice. We load into 700h--7A00h,
   144                                  	;  ie >= 6000h bytes (24 KiB), <= 7300h bytes (28.75 KiB).
   145                                  	numdef SET_AXBX_DATA,	1
   146                                  	numdef DATASTART_HIDDEN,1
   147                                  	numdef SET_DSSI_DPT,	1
   148                                  	numdef PUSH_DPT,	1
   149                                  %endif
   150                                  
   151                                  %if _COMPAT_MS7
   152                                  	strdef LOAD_NAME,	"IO"
   153                                  	strdef LOAD_EXT,	"SYS"
   154                                  	numdef LOAD_ADR,	00700h
   155                                  	numdef LOAD_MIN_PARA,	paras(1024)
   156                                  	numdef EXEC_SEG_ADJ,	0
   157                                  	numdef EXEC_OFS,	200h
   158                                  	numdef CHECKVALUE,	0
   159                                  
   160                                  	numdef SET_DL_UNIT,	1
   161                                  	numdef SET_DSSI_DPT,	0
   162                                  	numdef PUSH_DPT,	1
   163                                  	numdef MEMORY_CONTINUE,	1
   164                                  	; 4 sectors * 512 BpS should suffice. We load into 700h--7A00h,
   165                                  	;  ie >= 6000h bytes (24 KiB), <= 7300h bytes (28.75 KiB).
   166                                  	numdef SET_SIDI_CLUSTER,1
   167                                  	numdef DATASTART_HIDDEN,1
   168                                  	numdef LBA_SET_TYPE,	1
   169                                  %endif
   170                                  
   171                                  %if _COMPAT_LDOS
   172                                  	strdef LOAD_NAME,	"LDOS"
   173                                  	strdef LOAD_EXT,	"COM"
   174                                  	numdef LOAD_ADR,	02000h
   175                                  	numdef LOAD_MIN_PARA,	paras(1536)
   176                                  	numdef EXEC_SEG_ADJ,	0
   177                                  	numdef EXEC_OFS,	400h
   178                                  	numdef CHECKOFFSET,	1020
   179                                  	numdef CHECKVALUE,	"lD"
   180                                  
   181                                  	numdef SET_DL_UNIT,	0
   182                                  	numdef SET_CLUSTER,	1
   183                                  	numdef SET_FAT_SEG,	1
   184                                  	numdef SET_LOAD_SEG,	1
   185                                  	numdef MEMORY_CONTINUE,	1
   186                                  	numdef DATASTART_HIDDEN,0
   187                                  %endif
   188                                  
   189                                  %if 0
   190                                  
   191                                  Notes about partial load compatibilities
   192                                  
   193                                  * FreeDOS:
   194                                   * Relocates to an address other than 27A00h (1FE0h:7C00h)
   195                                  * IBMDOS:
   196                                   * Does not actually relocate DPT, just provide its address
   197                                  * MS-DOS 7:
   198                                   * Does not actually relocate DPT, just provide its address
   199                                   * Does not contain message table used by loader
   200                                  
   201                                  %endif
   202                                  
   203                                  %if _SET_BL_UNIT && _SET_AXBX_DATA
   204                                   %error Cannot select both of these options!
   205                                  %endif
   206                                  
   207                                  
   208                                  %assign LOADLIMIT 0A0000h
   209                                  %assign POSITION   07C00h
   210                                  
   211                                  %if _FIX_SECTOR_SIZE
   212                                   %assign i 5
   213                                   %rep 13-5
   214                                    %if (1 << i) != (_FIX_SECTOR_SIZE)
   215                                     %assign i i+1
   216                                    %endif
   217                                   %endrep
   218                                   %if (1 << i) != (_FIX_SECTOR_SIZE)
   219                                    %error Invalid sector size _FIX_SECTOR_SIZE
   220                                   %endif
   221                                  %endif
   222                                  
   223                                  %if _FIX_CLUSTER_SIZE
   224                                   %if _FIX_CLUSTER_SIZE > 256
   225                                    %error Invalid cluster size _FIX_CLUSTER_SIZE
   226                                   %endif
   227                                   %assign i 0
   228                                   %rep 8-0
   229                                    %if (1 << i) != (_FIX_CLUSTER_SIZE)
   230                                     %assign i i+1
   231                                    %endif
   232                                   %endrep
   233                                   %if (1 << i) != (_FIX_CLUSTER_SIZE)
   234                                    %warning Non-power-of-two cluster size _FIX_CLUSTER_SIZE
   235                                   %endif
   236                                  %endif
   237                                  
   238                                  
   239                                  %if (_LOAD_ADR & 0Fh)
   240                                   %error Load address must be on a paragraph boundary
   241                                  %endif
   242                                  
   243                                  %if _LOAD_ADR > LOADLIMIT
   244                                   %error Load address must be in LMA
   245                                  %elif _LOAD_ADR < 00500h
   246                                   %error Load address must not overlap IVT or BDA
   247                                  %endif
   248                                  %if ! _RELOCATE
   249                                   %if _LOAD_ADR > (POSITION-512) && _LOAD_ADR < (POSITION+1024)
   250                                    %error Load address must not overlap loader
   251                                   %endif
   252                                  %endif
   253                                  
   254                                  %if ((_EXEC_SEG_ADJ<<4)+_EXEC_OFS) < 0
   255                                   %error Execution address must be in loaded file
   256                                  %elif ((_EXEC_SEG_ADJ<<4)+_EXEC_OFS+_LOAD_ADR) > LOADLIMIT
   257                                   %error Execution address must be in LMA
   258                                  %endif
   259                                  
   260                                  %if (_EXEC_OFS & ~0FFFFh)
   261                                   %error Execution offset must fit into 16 bits
   262                                  %endif
   263                                  
   264                                  %if (_EXEC_SEG_ADJ > 0FFFFh || _EXEC_SEG_ADJ < -0FFFFh)
   265                                   %error Execution segment adjustment must fit into 16 bits
   266                                  %endif
   267                                  
   268                                  
   269                                  %if !_CHS && _QUERY_GEOMETRY
   270                                   %warning No CHS support but querying geometry anyway
   271                                  %endif
   272                                  
   273                                  %if !_CHS && !_LBA
   274                                   %error Either CHS or LBA or both must be enabled
   275                                  %endif
   276                                  
   277                                  
   278                                  %if 0
   279                                  
   280                                  There is some logic inside MS-DOS's hard disk partition initialisation
   281                                  code that sets up several requirements for us to fulfil. Otherwise,
   282                                  it will not accept the information given in the BPB (using default
   283                                  information based on the length as specified by MBR/EPBR instead) or
   284                                  make the whole file system inaccessible except for formatting. Both of
   285                                  those are undesirable of course. Some/all(?) checks are documented on
   286                                  pages 601,602 in "DOS Internals", Geoff Chappell 1994, as follows:
   287                                  
   288                                  * First three bytes contain either "jmp sho xx\ nop" or "jmp ne xx".
   289                                  * Media descriptor field >= 0F0h.
   290                                  * Bytes per sector field == 512.
   291                                  * Sectors per cluster field a power of 2 (1,2,4,8,16,32,64,128).
   292                                  * OEM name "version" (last three to four characters)
   293                                   * must be "20.?", "10.?" (? = wildcard), but no other with "0.?",
   294                                   * otherwise, must be "2.0", or
   295                                    * 2nd-to-last,3rd-to-last character codes together > "3.", or
   296                                     * those == "3.", last character code > "0"
   297                                  
   298                                  To stay compatible to those, display a warning here if the name
   299                                  itself would disqualify our boot sector already.
   300                                  
   301                                  %endif
   302                                  
   303                                  %push
   304                                  %strlen %$len _OEM_NAME_FILL
   305                                  %if %$len != 1
   306                                   %error Specified OEM name fill must be 1 character
   307                                  %endif
   308                                  %strlen %$len _OEM_NAME
   309                                  %define %$nam _OEM_NAME
   310                                  %if %$len > 8
   311                                   %error Specified OEM name is too long
   312                                  %else
   313                                   %assign %$warn 0
   314                                   %rep 8 - %$len
   315                                    %strcat %$nam %$nam,_OEM_NAME_FILL
   316                                   %endrep
   317                                   %substr %$prefix %$nam	5	; "wvxyZa.b", get "Z"
   318                                   %substr %$major %$nam 6,7	; "wvxyzA.b", get "A."
   319                                   %substr %$minor %$nam 8	; "wvxyza.B", get "B"
   320                                   %if %$major == "0."
   321                                    %ifn %$prefix == "1" || %$prefix == "2"
   322                                     %assign %$warn 1
   323                                    %endif
   324                                   %elifn %$major == "2." && %$minor == "0"
   325                                    %if %$major < "3."
   326                                     %assign %$warn 1
   327                                    %elif %$major == "3." && %$minor < "1"
   328                                     %assign %$warn 1
   329                                    %endif
   330                                   %endif
   331                                   %if %$warn
   332                                    %warning Specified OEM name fails MS-DOS's validation
   333                                   %endif
   334                                  %endif
   335                                  %pop
   336                                  
   337                                  ; 512-byte stack (minus the variables).
   338                                  ADR_STACK_LOW	equ	7C00h - 200h		; 07A00h
   339                                  
   340                                  ADR_FSIBOOT	equ	end -start+7C00h	; 07E00h
   341                                  
   342                                  %define _AFTER (ADR_FSIBOOT + 512)
   343                                  %if _RELOCATE
   344                                  	; dynamic allocation of FAT buffer
   345                                  %else
   346                                   %if _LOAD_ADR < 7C00h
   347                                    ADR_FATBUF	equ	_AFTER
   348                                    %define _AFTER (ADR_FATBUF + 8192)
   349                                   %else
   350                                    ADR_FATBUF	equ	2000h
   351                                   %endif
   352                                  %endif
   353                                  
   354                                  %if _ADD_SEARCH
   355                                   %if _RELOCATE
   356                                  	; dynamic allocation of dir buffer
   357                                   %elif _LOAD_ADR < 7C00h
   358                                    ADR_DIRBUF	equ	_AFTER
   359                                    %define _AFTER (ADR_DIRBUF + 8192)
   360                                   %else
   361                                    ADR_DIRBUF	equ	4000h
   362                                   %endif
   363                                  %else
   364                                   %if _RELOCATE
   365                                    ADR_DIRBUF	equ	_LOAD_ADR		; 00600h
   366                                   %elif _LOAD_ADR < 7C00h
   367                                   ; one-sector directory buffer. Assumes sectors are no larger than 8 KiB
   368                                    ADR_DIRBUF	equ	_AFTER			; 0A000h
   369                                    %define _AFTER (ADR_DIRBUF + 8192)
   370                                   %else
   371                                    ADR_DIRBUF	equ	4000h
   372                                   %endif
   373                                  %endif
   374                                  ADR_FREE	equ	_AFTER			; 08000h
   375                                  
   376                                  %if ! _RELOCATE
   377                                   %if ((ADR_FATBUF + 8192 - 1) & ~0FFFFh) != (ADR_FATBUF & ~0FFFFh)
   378                                    %warning Possibly crossing 64 KiB boundary while reading FAT
   379                                   %endif
   380                                  %endif
   381                                  
   382                                  %ifn _RELOCATE && _ADD_SEARCH
   383                                   %if ((ADR_DIRBUF + 8192 - 1) & ~0FFFFh) != (ADR_DIRBUF & ~0FFFFh)
   384                                    %warning Possibly crossing 64 KiB boundary while reading directory
   385                                   %endif
   386                                  %endif
   387                                  
   388                                  %if _LOAD_ADR >= ADR_FREE || _RELOCATE
   389                                  	; If reading on a sector size boundary, no crossing can occur.
   390                                  	;  Check for all possible sector sizes (32 B to 8 KiB). If one
   391                                  	;  of them fails display a warning, including the minimum size.
   392                                   %assign SECSIZECHECK 32
   393                                   %assign EXITREP 0
   394                                   %rep 256
   395                                    %ifn EXITREP
   396                                     %if _LOAD_ADR & (SECSIZECHECK - 1)
   397                                      %warning Possibly crossing 64 KiB boundary while reading file (sector size >= SECSIZECHECK)
   398                                      %assign EXITREP 1
   399                                      %exitrep
   400                                     %endif
   401                                     %if SECSIZECHECK == 8192
   402                                      %assign EXITREP 1
   403                                      %exitrep
   404                                     %endif
   405                                     %assign SECSIZECHECK SECSIZECHECK * 2
   406                                    %endif
   407                                   %endrep
   408                                  %else
   409                                  	; If loading below the boot sector, address 1_0000h is never reached.
   410                                  %endif
   411                                  
   412                                  
   413                                  	struc FSINFO	; FAT32 FSINFO sector layout
   414 00000000 ????????                .signature1:	resd 1		; 41615252h ("RRaA") for valid FSINFO
   415                                  .reserved1:			; former unused, initialized to zero by FORMAT
   416 00000004 <res 1E0h>              .fsiboot:	resb 480	; now used for FSIBOOT
   417 000001E4 ????????                .signature2:	resd 1		; 61417272h ("rrAa") for valid FSINFO
   418 000001E8 ????????                .numberfree:	resd 1		; FSINFO: number of free clusters or -1
   419 000001EC ????????                .nextfree:	resd 1		; FSINFO: first free cluster or -1
   420 000001F0 <res Ch>                .reserved2:	resd 3		; unused, initialized to zero by FORMAT
   421 000001FC ????????                .signature3:	resd 1		; AA550000h for valid FSINFO or FSIBOOT
   422                                  	endstruc
   423                                  
   424                                  	struc FSIBOOTG	; FSIBOOT general layout
   425 00000000 ????????????????        .signature:	resq 1		; 8 bytes that identify the FSIBOOT type
   426 00000008 <res 1D6h>              .fsicode:	resb 470	; 470 bytes FSIBOOT type specific data or code
   427 000001DE ????                    .dirsearch:	resw 1		; 1 word -> directory search entrypoint
   428                                  	endstruc
   429                                  
   430                                  	struc DIRENTRY
   431 00000000 ????????????????        deName:		resb 8
   432 00000008 ??????                  deExt:		resb 3
   433 0000000B ??                      deAttrib:	resb 1
   434 0000000C ??                      dePlusSize:	resb 1
   435 0000000D ??????????????          		resb 7
   436 00000014 ????                    deClusterHigh:	resw 1
   437 00000016 ????                    deTime:		resw 1
   438 00000018 ????                    deDate:		resw 1
   439 0000001A ????                    deClusterLow:	resw 1
   440 0000001C ????????                deSize:		resd 1
   441                                  	endstruc
   442                                  
   443                                  ATTR_READONLY	equ 1
   444                                  ATTR_HIDDEN	equ 2
   445                                  ATTR_SYSTEM	equ 4
   446                                  ATTR_VOLLABEL	equ 8
   447                                  ATTR_DIRECTORY	equ 10h
   448                                  ATTR_ARCHIVE	equ 20h
   449                                  
   450                                  
   451                                  ; use byte-offset addressing from BP for smaller code
   452                                  %define	VAR(x)	((x) - start) + bp
   453                                  
   454                                  
   455                                  	cpu 8086
   456                                  ; bootsector loaded at address 07C00h, addressable using 0000h:7C00h
   457                                  	org POSITION
   458                                  start:
   459                                  
   460                                  
   461                                  %define _LASTVARIABLE start
   462                                  	%macro nextvariable 2.nolist
   463                                  %1	equ (_LASTVARIABLE - %2)
   464                                  %define _LASTVARIABLE %1
   465                                  	%endmacro
   466                                  
   467                                  ; Variables
   468                                  
   469                                  ; (dword) sector where the first cluster's data starts
   470                                  	nextvariable data_start, 4
   471                                  
   472                                  ; (word) current load segment (points behind last loaded data)
   473                                  	nextvariable load_seg, 2
   474                                  
   475                                  ; (word) segment of FAT buffer
   476                                  ; for FAT12 this holds the entire FAT
   477                                  ; for FAT16 this holds the sector given by wo[fat_sector]
   478                                  ; for FAT32 this holds the sector given by dwo[fat_sector]
   479                                  	nextvariable fat_seg, 2
   480                                  
   481                                  ; (dword for FAT32) currently loaded sector-in-FAT, -1 if none
   482                                  	nextvariable fat_sector, 4
   483                                  
   484                                  ; (dword for FAT32) first cluster of load file
   485                                  	nextvariable first_cluster, 4
   486                                  
   487                                  ADR_STACK_START	equ	_LASTVARIABLE -start+POSITION
   488                                  
   489                                  ; (word) number of 16-byte paragraphs per sector
   490                                  	nextvariable para_per_sector, 2
   491                                  
   492                                  ; (word) number of 32-byte directory entries per sector
   493                                  	nextvariable entries_per_sector, 2
   494                                  
   495                                  ; (word) segment of last available memory for sector
   496                                  	nextvariable last_available_sector, 2
   497                                  
   498                                  ; (word) actual sectors per cluster
   499                                  	nextvariable adj_sectors_per_cluster, 2
   500                                  
   501                                  ; (word) paragraphs left to read
   502                                  	nextvariable paras_left, 2
   503                                  
   504                                  lowest_variable		equ _LASTVARIABLE
   505                                  
   506                                  
   507 00000000 EB7D                    	jmp strict short skip_bpb
   508                                  %if !_CHS && _LBA_SET_TYPE
   509                                  	db 0Ch		; LBA-enabled FAT32 FS partition type
   510                                  %else
   511 00000002 90                      	nop		; default: no LBA
   512                                  %endif
   513                                  
   514                                  
   515                                  ; BIOS Parameter Block (BPB)
   516                                  ;
   517                                  ; Installation will use the BPB already present in your file system.
   518                                  ; These values must be initialised when installing the loader.
   519                                  
   520                                  oem_id:			; offset 03h (03) - not used by this code
   521 00000003 202020206C444F53        	fill 8,_OEM_NAME_FILL,db _OEM_NAME
   522                                  bytes_per_sector:	; offset 0Bh (11) - refer to _FIX_SECTOR_SIZE !
   523 0000000B 0000                    	dw _BPS
   524                                  sectors_per_cluster:	; offset 0Dh (13) - refer to _FIX_CLUSTER_SIZE !
   525 0000000D 00                      	db _SPC & 255
   526                                  fat_start:
   527                                  num_reserved_sectors:	; offset 0Eh (14)
   528 0000000E 0000                    	dw _NUMRESERVED
   529                                  num_fats:		; offset 10h (16)
   530 00000010 00                      	db _NUMFATS
   531                                  num_root_dir_ents:	; offset 11h (17)
   532 00000011 0000                    	dw 0
   533                                  total_sectors:		; offset 13h (19) - not used by this code
   534                                  %if _SPI < 1_0000h
   535 00000013 0000                    	dw _SPI
   536                                  %else
   537                                  	dw 0
   538                                  %endif
   539                                  media_id:		; offset 15h (21) - not used by this code
   540 00000015 00                      	db _MEDIAID
   541                                  sectors_per_fat:	; offset 16h (22)
   542 00000016 0000                    	dw 0
   543                                  sectors_per_track:	; offset 18h (24)
   544 00000018 0000                    	dw _CHS_SECTORS
   545                                  heads:			; offset 1Ah (26)
   546 0000001A 0000                    	dw _CHS_HEADS
   547                                  hidden_sectors:		; offset 1Ch (28)
   548 0000001C 00000000                	dd _HIDDEN
   549                                  total_sectors_large:	; offset 20h (32) - not used by this code
   550                                  %if _SPI >= 1_0000h
   551                                  	dd _SPI
   552                                  %else
   553 00000020 00000000                	dd 0
   554                                  %endif
   555                                  
   556                                  sectors_per_fat_large:	; offset 24h (36)
   557 00000024 00000000                	dd _SPF
   558                                  fsflags:		; offset 28h (40)
   559 00000028 0000                    	dw 0
   560                                  fsversion:		; offset 2Ah (42)
   561 0000002A 0000                    	dw 0
   562                                  root_cluster:		; offset 2Ch (44)
   563 0000002C 00000000                	dd _CLUSTER_ROOT
   564                                  fsinfo_sector:		; offset 30h (48)
   565 00000030 0000                    	dw _SECTOR_FSINFO
   566                                  backup_sector:		; offset 32h (50)
   567 00000032 0000                    	dw _SECTOR_BACKUP
   568                                  
   569 00000034 00<rep Ch>              	times 12 db 0	; offset 34h (52) reserved
   570                                  
   571                                  ; Extended BPB		; offset 40h (64)
   572                                  
   573 00000040 00                      boot_unit:		db _UNIT
   574 00000041 00                      			db 0
   575 00000042 29                      ext_bpb_signature:	db 29h
   576 00000043 00000000                serial_number:		dd _VOLUMEID
   577 00000047 6C444F5320<rep 7h>      volume_label:		fill 11,32,db _DEFAULT_LABEL
   578 00000052 464154333220-           filesystem_identifier:	fill 8,32,db "FAT32"
   578 00000052 <rep 3h>           
   579                                  
   580                                  
   581                                  ; Initialised data
   582                                  
   583                                  	align 2
   584                                  fsiboot_table:		; this table is used by the FSIBOOT stage
   585 0000005A [1601]                  .error:		dw error
   586                                  			; INP: al = error condition letter
   587                                  			; ('B' = bad chain / FS error, 'F' = file not found,
   588                                  			;  'R' = disk read error, 'M' = not enough memory,
   589                                  			;  'E' = not enough data in file)
   590 0000005C [0201]                  .success:	dw load_finish
   591                                  			; INP: dword [ss:sp] = first cluster
   592                                  			; Note: The first cluster dword is always filled in
   593                                  			;  by FSIBOOT; the option _SET_SIDI_CLUSTER only
   594                                  			;  affects usage in the primary loader.
   595                                  %if _MEMORY_CONTINUE
   596 0000005E [0001]                  .memory_full:	dw load_finish_mc
   597                                  			; INP: al = error condition letter ('M'),
   598                                  			;  dword [ss:sp] = current cluster number,
   599                                  			;  dword [ss:sp + 4] = first cluster, refer to .success
   600                                  %else
   601                                  .memory_full:	dw error
   602                                  			; refer to previous .memory_full comment
   603                                  %endif
   604 00000060 [2A01]                  .read_sector:	dw read_sector
   605                                  			; INP: dx:ax = sector number within partition,
   606                                  			;  bx => buffer, (_LBA) ds = ss
   607                                  			; OUT: dx:ax incremented, bx => incremented,
   608                                  			;  es = input bx, does not return if error occurred
   609                                  			; CHG: none
   610                                  %if _RELOCATE && _ADD_SEARCH
   611                                  .dirbuf:	dw 8192 >> 4
   612                                  			; initialised to segment displacement from FAT buffer
   613                                  %else
   614 00000062 000A                    .dirbuf:	dw ADR_DIRBUF>>4
   615                                  			; => directory sector buffer (one sector)
   616                                  %endif
   617 00000064 [B001]                  .writedirentry:	dw writedirentry.loaddir
   618 00000066 [6C00]                  .filename:	dw .load_name
   619                                  			; -> name to search
   620 00000068 6000                    .minpara:	dw _LOAD_MIN_PARA
   621 0000006A 0002                    .loadseg:	dw _LOAD_ADR>>4
   622                                  			; => where to load
   623                                  
   624                                  .load_name:		; = blank-padded 11-byte filename to search for
   625 0000006C 4C444542554720-         	fill 8,32,db _LOAD_NAME
   625 0000006C <rep 2h>           
   626 00000074 434F4D                  	fill 3,32,db _LOAD_EXT
   627                                  
   628                                  fsiboot_name:
   629 00000077 465349424F4F5434        	fill 8, 32, db _FSIBOOTNAME
   630                                  
   631                                  
   632                                  ; Code
   633                                  
   634                                  		; Note that this may be entered with cs:ip = 07C0h:0 !
   635                                  skip_bpb:
   636 0000007F FA                      	cli
   637 00000080 FC                      	cld
   638 00000081 31C9                    	 xor cx, cx
   639 00000083 BD[0000]                	mov bp, start		; magic bytes - checked by instsect
   640 00000086 8ED1                    	 mov ss, cx
   641 00000088 BCF07B                  	mov sp, ADR_STACK_START
   642                                  %if _USE_AUTO_UNIT
   643 0000008B 885640                  	mov [VAR(boot_unit)], dl; magic bytes - checked by instsect
   644                                  %else
   645                                  	mov dl, [VAR(boot_unit)]; magic bytes - checked by instsect
   646                                  %endif
   647                                  
   648                                  	; Note:	es is left uninitialised here until the first call to
   649                                  	;	 read_sector if the below conditional is false.
   650                                  %if _USE_PART_INFO	; +19 bytes
   651 0000008E 8EC1                    	 mov es, cx
   652                                  ; Note:	Award Medallion BIOS v6.0 (ASUS MED 2001 ACPI BIOS Revision 1009)
   653                                  ;	 loads from a floppy disk drive with ds:si = 0F000h:0A92Dh ->
   654                                  ;	 FF FF FF FF 08 00 08 01 FF FF FF FF FF FF FF FF, which was detected
   655                                  ;	 as a valid partition table entry by this handling. Therefore, we
   656                                  ;	 only accept partition information when booting from a hard disk now.
   657 00000090 84D2                    	test dl, dl		; floppy ?
   658 00000092 7911                    	jns @F			; don't attempt detection -->
   659                                  ; Check whether an MBR left us partition information.
   660                                  ; byte[ds:si] bit 7 means active and must be set if valid.
   661 00000094 380C                    	cmp byte [si], cl	; flags for xx-00h (result is xx), SF = bit 7
   662 00000096 790D                    	jns @F			; xx < 80h, ie info invalid -->
   663                                  ; byte[ds:si+4] is the file system type. Check for valid one.
   664 00000098 384C04                  	cmp byte [si+4], cl	; is it zero?
   665 0000009B 7408                    	je @F			; yes, info invalid -->
   666                                  ; Info valid, trust their hidden sectors over hardcoded.
   667                                  ; Assume the movsw instructions won't run with si = FFFFh.
   668 0000009D BF[1C00]                	mov di, hidden_sectors	; -> BPB field
   669 000000A0 83C608                  	add si, 8		; -> partition start sector in info
   670 000000A3 A5                      	movsw
   671 000000A4 A5                      	movsw			; overwrite BPB field with value from info
   672                                  @@:
   673                                  %endif
   674 000000A5 8ED9                    	mov ds, cx
   675 000000A7 FB                      	sti
   676                                  
   677                                  
   678                                  %if _QUERY_GEOMETRY	; +27 bytes
   679                                  ;	test dl, dl		; floppy?
   680                                  ;	jns @F			; don't attempt query, might fail -->
   681                                  	; Note that while the original PC BIOS doesn't support this function
   682                                  	;  (for its diskettes), it does properly return the error code 01h.
   683                                  	; https://sites.google.com/site/pcdosretro/ibmpcbios (IBM PC version 1)
   684 000000A8 B408                    	mov ah, 08h
   685                                  	; xor cx, cx		; initialise cl to 0
   686                                  	; Already from prologue cx = 0.
   687 000000AA F9                      	stc			; initialise to CY
   688 000000AB CD13                    	int 13h			; query drive geometry
   689 000000AD 720E                    	jc @F			; apparently failed -->
   690 000000AF 83E13F                  	and cx, 3Fh		; get sectors
   691 000000B2 7409                    	jz @F			; invalid (S is 1-based), don't use -->
   692 000000B4 894E18                  	mov [VAR(sectors_per_track)], cx
   693 000000B7 88F1                    	mov cl, dh		; cx = maximum head number
   694 000000B9 41                      	inc cx			; cx = number of heads (H is 0-based)
   695 000000BA 894E1A                  	mov [VAR(heads)], cx
   696                                  @@:
   697                                  %endif
   698                                  
   699                                  
   700                                  ; 16-byte paragraphs per sector
   701 000000BD 8B460B                  	mov ax, [VAR(bytes_per_sector)]
   702 000000C0 B104                    	mov cl, 4
   703 000000C2 D3E8                    	shr ax, cl
   704 000000C4 50                      	push ax			; push into word [VAR(para_per_sector)]
   705                                  
   706                                  ; 32-byte FAT directory entries per sector
   707 000000C5 D1E8                    	shr ax, 1		; /2 = 32-byte entries per sector
   708 000000C7 50                      	push ax			; push into word [VAR(entries_per_sector)]
   709                                  
   710                                  load_fsiboot:
   711 000000C8 83F820                  	cmp ax, 1024 >> 5	; sector size is at least 1 KiB (large) ?
   712 000000CB 731B                    	jae .loaded		; already loaded as part of the boot sector -->
   713                                  
   714                                  		; If this code runs, then ax was below 1024 >> 5,
   715                                  		;  therefore this cwd instruction always zeros dx.
   716 000000CD 99                      	cwd
   717                                  
   718 000000CE 8B4630                  	mov ax, [VAR(fsinfo_sector)]
   719                                  ;	inc ax
   720                                  ;	jz error_fsiboot	; was FFFFh, invalid -->
   721                                  ;	dec ax
   722                                  		; FFFFh always fails the < num_reserved_sectors check
   723 000000D1 85C0                    	test ax, ax
   724 000000D3 7403                    	jz @F			; is 0 ? invalid --> (ZR, NC)
   725 000000D5 3B460E                  	cmp ax, [VAR(num_reserved_sectors)]
   726                                  @@:			; (ZR, NC if ax == 0)
   727 000000D8 7337                    	jae error_fsiboot	; (jump if NC)
   728                                  				; dx:ax = FSINFO sector (dx = 0 from cwd)
   729 000000DA B90002                  	mov cx, 512
   730 000000DD BBE007                  	mov bx, ADR_FSIBOOT >> 4
   731                                  @@:
   732 000000E0 E84700                  	call read_sector
   733 000000E3 2B4E0B                  	sub cx, [VAR(bytes_per_sector)]
   734 000000E6 77F8                    	ja @B			; read 512 bytes -->
   735                                  
   736                                  .loaded:
   737 000000E8 1E                      	push ds
   738 000000E9 07                      	pop es
   739 000000EA BF[0402]                	mov di, fsiboot.signature
   740 000000ED BE[7700]                	mov si, fsiboot_name
   741 000000F0 B90400                  	mov cx, 4		; size of fsiboot_name
   742 000000F3 F3A7                    	repe cmpsw
   743 000000F5 751A                    	jne error_fsiboot
   744                                  		; Note that now es:di -> fsiboot.start
   745                                  
   746                                  %if _LOAD_ADR >= ADR_FREE || _RELOCATE
   747                                  ; Get conventional memory size and store it
   748                                  		int 12h
   749                                  		mov cl, 6
   750                                  		shl ax, cl
   751                                   %if _RPL		; +33 bytes
   752                                  	xchg dx, ax
   753                                  	lds si, [4 * 2Fh]
   754                                  	add si, 3
   755                                  	lodsb
   756                                  	cmp al, 'R'
   757                                  	jne .no_rpl
   758                                  	lodsb
   759                                  	cmp al, 'P'
   760                                  	jne .no_rpl
   761                                  	lodsb
   762                                  	cmp al, 'L'
   763                                  	jne .no_rpl
   764                                  	mov ax, 4A06h
   765                                  	int 2Fh
   766                                  .no_rpl:
   767                                  	push ss
   768                                  	pop ds
   769                                  	xchg ax, dx
   770                                   %endif
   771                                   %if _RELOCATE
   772                                    %if _ADD_SEARCH
   773                                  	sub ax, (20 * 1024 + 8192 + 8192 + (8192-16) + 1024 + 7C00h) >> 4
   774                                    %else
   775                                  	sub ax, (20 * 1024 + 8192 + (8192-16) + 1024 + 7C00h) >> 4
   776                                    %endif
   777                                  		; 20 KiB:	reserved for iniload
   778                                  		; 8 KiB:	dir buffer (only if _ADD_SEARCH)
   779                                  		; 8 KiB:	FAT buffer
   780                                  		; 8 KiB - 16 B:	to allow rounding down position of buffers
   781                                  		; 1 KiB:	sector + FSIBOOT
   782                                  		; 7C00h:	stack and to allow addressing with 7C00h in bp
   783                                  		;
   784                                  		; Note also that by addressing the stack and sector and FSIBOOT
   785                                  		;  with bp at 7C00h, and insuring this subtraction doesn't
   786                                  		;  underflow, makes sure that we do not overwrite the IVT or
   787                                  		;  BDA. (However, we assume that ax is at least 60h or so.)
   788                                  		;
   789                                  		; The FAT buffer segment is masked so that the actual buffer
   790                                  		;  is stored on an 8 KiB boundary. This is to ensure that
   791                                  		;  the buffer doesn't possibly cross a 64 KiB DMA boundary.
   792                                  	jc error_fsiboot
   793                                  	cmp ax, (end_after_fsiboot -start+7C00h - ADR_STACK_LOW + 15) >> 4
   794                                  		; This check is to ensure that the start of the destination
   795                                  		;  for the relocation (stack, sector, FSIBOOT) is
   796                                  		;  above-or-equal the end of the source for the relocation.
   797                                  		;  That is, to avoid overwriting any of the source with the
   798                                  		;  string move instruction (which for simplicity is always UP).
   799                                  	jb error_fsiboot
   800                                  
   801                                  			; With _RELOCATE enabled, the FAT buffer segment
   802                                  			;  is fixed up by the primary loader here before
   803                                  			;  FSIBOOT is executed.
   804                                  	mov bx, ((8192 - 16) + 1024 + 7C00h)>>4
   805                                  ; bx is initialised to the value
   806                                  ;  ((8192 - 16) + 1024 + 7C00h)>>4
   807                                  ;  so this is like calculating the following for its value:
   808                                  ;  ((LMA_top - 20 KiB - 8 KiB - 8 KiB{AS} - (8 KiB - 16 B) - 1 KiB - 7C00h) + ;	((8 KiB - 16 B) + 1 KiB + 7C00h))>>4
   810                                  ;  == (LMA_top - 20 KiB - 8 KiB - 8 KiB{AS})>>4
   811                                  ; {AS} = only if _ADD_SEARCH
   812                                  	add bx, ax
   813                                  	and bx, ~ ((8192>>4) - 1)	; => FAT sector buffer (one sector)
   814                                    %if _ADD_SEARCH
   815                                  ; .dirbuf is initialised to 8192 >> 4
   816                                  	add word [VAR(fsiboot_table.dirbuf)], bx
   817                                  					; => dir sector buffer (one sector)
   818                                    %endif
   819                                  
   820                                  	push ax
   821                                  	push di				; -> reloc destination of fsiboot.start
   822                                  
   823                                  	mov es, ax			; => destination
   824                                  	mov si, sp			; ds:si = ss:entries_per_sector - 4
   825                                  	mov di, si			; es:di -> destination for stack low
   826                                  	mov cx, (end_after_fsiboot - (entries_per_sector - 4)) >> 1
   827                                  		; end_after_fsiboot is the top of used memory
   828                                  		; entries_per_sector is the lowest filled stack frame slot
   829                                  		; 4 is for the additional slots taken by the return address
   830                                  	rep movsw			; relocate stack, sector, and FSIBOOT
   831                                  	mov ss, ax
   832                                  	mov ds, ax			; relocate these
   833                                  	add ax, (ADR_STACK_LOW) >> 4	; (rounding down) => behind available
   834                                  
   835                                  	retf				; jump to relocated FSIBOOT
   836                                   %else
   837                                  	sub ax, (20 * 1024) >> 4	; 20 KiB reserved for iniload
   838                                  	jc error_fsiboot
   839                                  	mov bx, ADR_FATBUF >> 4		; => FAT sector buffer (one sector)
   840                                  	push es
   841                                  	push di				; -> fsiboot.start
   842                                  	retf
   843                                  		; Do a far return here to ensure that cs is zero.
   844                                   %endif
   845                                  %elif _LOAD_ADR < ADR_STACK_LOW
   846 000000F7 B8A007                  	mov ax, (ADR_STACK_LOW >> 4)
   847 000000FA BB0008                  	mov bx, ADR_FATBUF >> 4		; => FAT sector buffer (one sector)
   848 000000FD 06                      	push es
   849 000000FE 57                      	push di				; -> fsiboot.start
   850 000000FF CB                      	retf
   851                                  		; Do a far return here to ensure that cs is zero.
   852                                  %else
   853                                   %error Load address within used memory
   854                                  %endif
   855                                  
   856                                  
   857                                  		; INP:	es:bx -> found dir entry in dir sector buffer
   858                                  		;	si:di = loaded sector-in-FAT
   859                                  		; CHG:	ax, cx, dx
   860                                  		; STT:	ss:bp -> boot sector
   861                                  		;	ds = ss
   862                                  		;	UP
   863                                  		; OUT:	directory entry copied if so desired
   864                                  		;	(is a no-op if not to copy dir entry)
   865                                  writedirentry:
   866                                  %if _LOAD_DIR_SEG
   867                                  .loaddir:
   868                                  	mov ax, _LOAD_DIR_SEG
   869                                  %else
   870                                  .loaddir: equ read_sector.retn
   871                                  %endif
   872                                  %if _LOAD_DIR_SEG || (_ADD_DIR_SEG && _ADD_SEARCH)
   873                                  		; INP:	ax => where to store directory entry
   874                                  .ax:
   875                                  	push ds
   876                                  	push si
   877                                  	push di
   878                                  	 push es
   879                                  	 pop ds
   880                                  	mov si, bx	; ds:si -> directory entry
   881                                  	mov cx, DIRENTRY_size >> 1
   882                                  	mov es, ax
   883                                  	xor di, di	; es:di -> where to store directory entry
   884                                  	rep movsw	; move to here (one directory entry)
   885                                  	 push ds
   886                                  	 pop es		; es:bx -> dir entry in dir sector buffer
   887                                  	pop di
   888                                  	pop si
   889                                  	pop ds
   890                                  	retn
   891                                  %endif
   892                                  
   893                                  
   894                                  %if _MEMORY_CONTINUE
   895                                  load_finish_mc:
   896 00000100 5B                      		pop bx
   897 00000101 59                      		pop cx
   898                                  %endif
   899                                  load_finish:
   900                                  
   901                                  %if _ADD_SEARCH
   902                                  		mov word [VAR(fsiboot_table.filename)], add_name
   903                                  		call near word [dirsearch_entrypoint]
   904                                   %if _ADD_DIR_SEG
   905                                  		mov ax, _ADD_DIR_SEG
   906                                  		call writedirentry.ax
   907                                   %endif
   908                                  %endif
   909                                  
   910                                  %if _TURN_OFF_FLOPPY
   911                                  ; turn off floppy motor
   912                                  		mov dx,3F2h
   913                                  		mov al,0
   914                                  		out dx,al
   915                                  %endif
   916                                  
   917                                  ; Set-up registers for and jump to loaded file
   918                                  ; Already: ss:bp-> boot sector containing BPB
   919                                  %if _CHECKVALUE
   920                                  CHECKLINEAR equ _LOAD_ADR + _CHECKOFFSET
   921                                   %if CHECKLINEAR <= (64 * 1024 - 2) && ! _RELOCATE
   922 00000102 813EFC236C44            		cmp word [CHECKLINEAR], _CHECKVALUE
   923                                   %else
   924                                  		mov ax, CHECKLINEAR >> 4
   925                                  		mov es, ax
   926                                  		cmp word [es:CHECKLINEAR & 15], _CHECKVALUE
   927                                   %endif
   928 00000108 B056                    		mov al, 'V'		; check 'V'alue mismatch
   929 0000010A 750A                    		jne error
   930                                  %endif
   931                                  %if _SET_SIDI_CLUSTER && (_PUSH_DPT || _SET_DSSI_DPT)
   932                                  		pop cx
   933                                  		pop dx
   934                                  %endif
   935                                  %if _PUSH_DPT || _SET_DSSI_DPT
   936                                   %ifn _SET_DSSI_DPT		; (implying that only _PUSH_DPT is set)
   937                                    %if _RELOCATE
   938                                  		xor ax, ax
   939                                  		mov es, ax		; => IVT
   940                                  		mov di, 1Eh*4
   941                                  		les si, [es:di]		; -> original (also current) DPT
   942                                  		push es
   943                                  		push si			; original (also current) DPT address
   944                                  		push ax
   945                                  		push di			; 0000h:0078h (address of 1Eh IVT entry)
   946                                    %else
   947                                  			; If not _RELOCATE, ds = 0000h (=> IVT)
   948                                  		mov di, 1Eh*4
   949                                  		les si, [di]		; -> original (also current) DPT
   950                                  		push es
   951                                  		push si			; original (also current) DPT address
   952                                  			; If not _RELOCATE, ss = 0000h (=> IVT)
   953                                  		push ss
   954                                  		push di			; 0000h:0078h (address of 1Eh IVT entry)
   955                                    %endif
   956                                   %else
   957                                    %if _RELOCATE
   958                                  		xor ax, ax
   959                                  		mov ds, ax		; => IVT
   960                                    %endif
   961                                  			; If not _RELOCATE, ds = 0000h (=> IVT)
   962                                  		mov di, 1Eh*4
   963                                  		lds si, [di]		; -> original (also current) DPT
   964                                    %if _PUSH_DPT
   965                                  		push ds
   966                                  		push si			; original (also current) DPT address
   967                                     %if _RELOCATE
   968                                  		push ax
   969                                  		push di			; 0000h:0078h (address of 1Eh IVT entry)
   970                                     %else
   971                                  			; If not _RELOCATE, ss = 0000h (=> IVT)
   972                                  		push ss
   973                                  		push di			; 0000h:0078h (address of 1Eh IVT entry)
   974                                     %endif
   975                                    %endif
   976                                   %endif
   977                                  %endif
   978                                  %if _ZERO_ES || _ZERO_DS
   979                                   %if _RELOCATE
   980                                    %ifn _PUSH_DPT || _SET_DSSI_DPT
   981                                  		xor ax, ax
   982                                    %endif
   983                                    %if _ZERO_ES
   984                                  		mov es, ax
   985                                    %endif
   986                                    %if _ZERO_DS
   987                                  		mov ds, ax
   988                                    %endif
   989                                   %else
   990                                    %if _ZERO_ES
   991                                  		push ss
   992                                  		pop es
   993                                    %endif
   994                                    %if _ZERO_DS && _SET_DSSI_DPT
   995                                  		push ss
   996                                  		pop ds
   997                                    %endif
   998                                   %endif
   999                                  %endif
  1000                                  %if _DATASTART_HIDDEN
  1001                                  		mov bx, [VAR(hidden_sectors + 0)]
  1002                                  		mov ax, [VAR(hidden_sectors + 2)]
  1003                                  		add word [VAR(data_start + 0)], bx
  1004                                  		adc word [VAR(data_start + 2)], ax
  1005                                  %endif
  1006                                  %if _SET_AXBX_DATA
  1007                                  		mov bx, [VAR(data_start)]
  1008                                  		mov ax, [VAR(data_start+2)]
  1009                                  %endif
  1010                                  %if _SET_SIDI_CLUSTER
  1011                                   %if _SET_DSSI_DPT
  1012                                    %error Cannot select both of these.
  1013                                   %endif
  1014                                   %if _PUSH_DPT || _SET_DSSI_DPT
  1015                                  		mov di, cx
  1016                                  		mov si, dx
  1017                                   %else
  1018                                  		pop di
  1019                                  		pop si
  1020                                   %endif
  1021                                  %endif
  1022                                  %if _SET_DL_UNIT
  1023                                  		mov dl, [VAR(boot_unit)]; set dl to unit
  1024                                   %if _SET_BL_UNIT
  1025                                  		mov bl, dl		; set bl to unit, too
  1026                                   %endif
  1027                                  %elif _SET_BL_UNIT
  1028                                  		mov bl, [VAR(boot_unit)]; set bl to unit
  1029                                  %endif
  1030                                  			; ss:bp-> boot sector with BPB
  1031 0000010C EA00040002              		jmp (_LOAD_ADR>>4)+_EXEC_SEG_ADJ:_EXEC_OFS
  1032                                  
  1033                                  
  1034                                  error_fsiboot:
  1035 00000111 B049                    	mov al,'I'
  1036                                  
  1037 00000113 A9                      	db __TEST_IMM16	; (skip mov)
  1038                                  read_sector.err:
  1039 00000114 B052                    	mov al, 'R'	; Disk 'R'ead error
  1040                                  
  1041                                  error:
  1042                                  %if _RELOCATE
  1043                                  	mov bx, 7
  1044                                  	mov ds, bx
  1045                                  	mov bh, [462h - 70h]
  1046                                  %else
  1047 00000116 8A3E6204                	mov bh, [462h]
  1048 0000011A B307                    	mov bl, 7
  1049                                  %endif
  1050 0000011C B40E                    	mov ah, 0Eh
  1051 0000011E CD10                    	int 10h		; display character
  1052 00000120 B007                    	mov al, 07h
  1053 00000122 CD10                    	int 10h		; beep!
  1054                                  
  1055 00000124 31C0                    	xor ax, ax	; await key pressed
  1056 00000126 CD16                    	int 16h
  1057                                  
  1058 00000128 CD19                    	int 19h		; re-start the boot process
  1059                                  
  1060                                  
  1061                                  		; Read a sector using Int13.02 or Int13.42
  1062                                  		;
  1063                                  		; INP:	dx:ax = sector number within partition
  1064                                  		;	bx => buffer
  1065                                  		;	(_LBA) ds = ss
  1066                                  		; OUT:	If unable to read,
  1067                                  		;	 ! jumps to error instead of returning
  1068                                  		;	If sector has been read,
  1069                                  		;	 dx:ax = next sector number (has been incremented)
  1070                                  		;	 bx => next buffer (bx = es+word[para_per_sector])
  1071                                  		;	 es = input bx
  1072                                  		; CHG:	-
  1073                                  read_sector:
  1074 0000012A 52                      	push dx
  1075 0000012B 51                      	push cx
  1076 0000012C 50                      	push ax
  1077 0000012D 56                      	push si
  1078                                  
  1079 0000012E 53                      	push bx
  1080                                  
  1081                                  ; DX:AX==LBA sector number
  1082                                  ; add partition start (= number of hidden sectors)
  1083 0000012F 03461C                  		add ax,[VAR(hidden_sectors + 0)]
  1084 00000132 13561E                  		adc dx,[VAR(hidden_sectors + 2)]
  1085                                   %if (!_LBA || !_LBA_33_BIT) && _LBA_CHECK_NO_33
  1086                                  	jc .err
  1087                                   %endif
  1088                                  %if _LBA		; +70 bytes (with CHS, +63 bytes without CHS)
  1089                                   %if _LBA_33_BIT
  1090 00000135 19F6                    	sbb si, si	; -1 if was CY, 0 else
  1091 00000137 F7DE                    	neg si		; 1 if was CY, 0 else
  1092                                   %endif
  1093 00000139 31C9                    	xor cx, cx
  1094 0000013B 51                      	push cx		; highest word = 0
  1095                                   %if _LBA_33_BIT
  1096 0000013C 56                      	push si		; bit 32 = 1 if operating in 33-bit space
  1097                                   %else
  1098                                  	push cx		; second highest word = 0
  1099                                   %endif
  1100 0000013D 52                      	push dx
  1101 0000013E 50                      	push ax		; = qword sector number
  1102 0000013F 53                      	push bx
  1103 00000140 51                      	push cx		; bx => buffer
  1104 00000141 41                      	inc cx
  1105 00000142 51                      	push cx		; word number of sectors to read
  1106 00000143 B110                    	mov cl, 10h
  1107 00000145 51                      	push cx		; word size of disk address packet
  1108 00000146 89E6                    	mov si, sp	; ds:si -> disk address packet (on stack)
  1109                                  
  1110                                   %if _LBA_SKIP_CHECK		; -14 bytes
  1111 00000148 8A5640                  	mov dl, [VAR(boot_unit)]
  1112                                   %else
  1113                                  	mov ah, 41h
  1114                                  	mov dl, [VAR(boot_unit)]
  1115                                  	mov bx, 55AAh
  1116                                  	stc
  1117                                  	int 13h		; 13.41.bx=55AA extensions installation check
  1118                                  	jc .no_lba
  1119                                  	cmp bx, 0AA55h
  1120                                  	jne .no_lba
  1121                                  	test cl, 1	; support bitmap bit 0
  1122                                  	jz .no_lba
  1123                                   %endif
  1124                                  
  1125                                  %if _LBA_RETRY
  1126                                  	mov ah, 42h
  1127                                  	int 13h		; 13.42 extensions read
  1128                                  	jnc .lba_done
  1129                                  
  1130                                  	xor ax, ax
  1131                                  	int 13h
  1132                                  
  1133                                  		; have to reset the LBAPACKET's lpCount, as the handler may
  1134                                  		;  set it to "the number of blocks successfully transferred".
  1135                                  		; (in any case, the high byte is still zero.)
  1136                                  	mov byte [si + 2], 1
  1137                                  %endif
  1138                                  
  1139 0000014B B442                    	mov ah, 42h
  1140 0000014D CD13                    	int 13h
  1141                                   %if _LBA_SKIP_CHECK && _CHS
  1142 0000014F 7307                    	jnc .lba_done
  1143 00000151 80FC01                  	cmp ah, 1	; invalid function?
  1144 00000154 740A                    	je .no_lba	; try CHS instead -->
  1145                                  .err_CY:
  1146                                  .err_2:
  1147 00000156 EBBC                    	jmp .lba_error
  1148                                   %else
  1149                                  .err_CY:
  1150                                  	jc .lba_error
  1151                                  .err_2: equ .err
  1152                                   %endif
  1153                                  
  1154                                  .lba_done:
  1155                                  %if _CHS && _LBA_SET_TYPE
  1156                                  	mov byte [bp + 2], 0Ch	; LBA-enabled FAT32 FS partition type
  1157                                  %endif
  1158 00000158 83C410                  	add sp, 10h
  1159 0000015B 5B                      	pop bx
  1160 0000015C 8EC3                    	mov es, bx
  1161                                  %if _CHS
  1162 0000015E EB45                    	jmp short .chs_done
  1163                                  %endif
  1164                                  
  1165                                  .lba_error: equ .err
  1166                                  
  1167                                   %if !_CHS
  1168                                  .no_lba: equ .err
  1169                                   %else
  1170                                  .no_lba:
  1171 00000160 83C408                  	add sp, 8
  1172 00000163 59                      	pop cx		; cx = low word of sector
  1173 00000164 58                      	pop ax
  1174 00000165 5A                      	pop dx		; dx:ax = middle two words of sector
  1175                                  		; Here dx <= 1 if _LBA_33_BIT, else zero.
  1176                                  		;  If dx is nonzero then the CHS calculation
  1177                                  		;  should fail. If CHS sectors is equal to 1
  1178                                  		;  (very unusual) then the div may fail. Else,
  1179                                  		;  we will detect a cylinder > 1023 eventually.
  1180 00000166 5E                      	pop si		; discard highest word of qword
  1181                                   %endif
  1182                                  %else
  1183                                  .err_2: equ .err
  1184                                  %endif
  1185                                  
  1186                                  %if _CHS		; +70 bytes
  1187                                  ; dx:ax = LBA sector number, (if _LBA) cx = 0
  1188                                  ; divide by number of sectors per track to get sector number
  1189                                  ; Use 32:16 DIV instead of 64:32 DIV for 8088 compatability
  1190                                  ; Use two-step 32:16 divide to avoid overflow
  1191                                   %if !_LBA
  1192                                  			xchg cx, ax	; cx = low word of sector, clobbers ax
  1193                                  			xchg ax, dx	; ax = high word of sector, clobbers dx
  1194                                  			xor dx, dx	; dx:ax = high word of sector
  1195                                   %else
  1196                                  	; from the .no_lba popping we already have:
  1197                                  	;  cx = low word of sector
  1198                                  	;  dx:ax = high word of sector
  1199                                   %endif
  1200 00000167 F77618                  			div word [VAR(sectors_per_track)]
  1201 0000016A 91                      			xchg cx,ax
  1202 0000016B F77618                  			div word [VAR(sectors_per_track)]
  1203 0000016E 87CA                    			xchg cx,dx
  1204                                  
  1205                                  ; DX:AX=quotient, CX=remainder=sector (S) - 1
  1206                                  ; divide quotient by number of heads
  1207 00000170 93                      			xchg bx, ax	; bx = low word of quotient, clobbers ax
  1208 00000171 92                      			xchg ax, dx	; ax = high word of quotient, clobbers dx
  1209 00000172 31D2                    			xor dx, dx	; dx = 0
  1210 00000174 F7761A                  			div word [VAR(heads)]
  1211                                  					; ax = high / heads, dx = high % heads
  1212 00000177 93                      			xchg bx, ax	; bx = high / heads, ax = low quotient
  1213 00000178 F7761A                  			div word [VAR(heads)]
  1214                                  
  1215                                  ; bx:ax=quotient=cylinder (C), dx=remainder=head (H)
  1216                                  ; move variables into registers for INT 13h AH=02h
  1217 0000017B 88D6                    			mov dh, dl	; dh = head
  1218 0000017D 41                      			inc cx		; cl5:0 = sector
  1219 0000017E 86E8                    			xchg ch, al	; ch = cylinder 7:0, al = 0
  1220 00000180 D1E8                    			shr ax, 1
  1221 00000182 D1E8                    			shr ax, 1	; al7:6 = cylinder 9:8
  1222                                  	; bx has bits set iff it's > 0, indicating a cylinder >= 65536.
  1223 00000184 08FB                    			 or bl, bh	; collect set bits from bh
  1224 00000186 08C1                    			or cl, al	; cl7:6 = cylinder 9:8
  1225                                  	; ah has bits set iff it was >= 4, indicating a cylinder >= 1024.
  1226 00000188 08E3                    			 or bl, ah	; collect set bits from ah
  1227 0000018A 8A5640                  			mov dl,[VAR(boot_unit)] ; dl = drive
  1228 0000018D 75C7                    			 jnz .err_2	; error if cylinder >= 1024 -->
  1229                                  					; ! bx = 0 (for 13.02 call)
  1230                                  
  1231                                  ; we call INT 13h AH=02h once for each sector. Multi-sector reads
  1232                                  ; may fail if we cross a track or 64K boundary
  1233 0000018F 07                      			pop es
  1234                                  %if _CHS_RETRY_REPEAT
  1235 00000190 BE1100                  			mov si, _CHS_RETRY_REPEAT + 1
  1236                                  .loop_chs_retry_repeat:
  1237 00000193 B80102                  			mov ax, 0201h
  1238 00000196 CD13                    			int 13h		; read one sector
  1239 00000198 7309                    			jnc .done
  1240 0000019A 31C0                    			xor ax, ax
  1241 0000019C CD13                    			int 13h		; reset disk
  1242 0000019E 4E                      			dec si		; another attempt ?
  1243 0000019F 75F2                    			jnz .loop_chs_retry_repeat	; yes -->
  1244 000001A1 EBB3                    			jmp .err_2
  1245                                  %else
  1246                                   %if _CHS_RETRY
  1247                                  			mov ax, 0201h
  1248                                  			int 13h		; read one sector
  1249                                  			jnc .done
  1250                                  ; reset drive
  1251                                  			xor ax, ax
  1252                                  			int 13h
  1253                                   %endif
  1254                                  ; try read again
  1255                                  			mov ax, 0201h
  1256                                  			int 13h
  1257                                  			jc .err_CY
  1258                                  %endif
  1259                                  %if ! _LBA
  1260                                  .err_CY:	equ .err
  1261                                  %endif
  1262                                  
  1263                                  .done:
  1264                                  ; increment segment
  1265 000001A3 8CC3                    	mov bx, es
  1266                                  %endif
  1267                                  
  1268                                  .chs_done:
  1269                                  %if _FIX_SECTOR_SIZE
  1270                                  	add bx, _FIX_SECTOR_SIZE >> 4
  1271                                  %else
  1272 000001A5 035EEE                  	add bx, [VAR(para_per_sector)]
  1273                                  %endif
  1274                                  
  1275 000001A8 5E                      	pop si
  1276 000001A9 58                      	pop ax
  1277 000001AA 59                      	pop cx
  1278 000001AB 5A                      	pop dx
  1279                                  ; increment LBA sector number
  1280 000001AC 40                      	inc ax
  1281 000001AD 7501                    	jne @F
  1282 000001AF 42                      	inc dx
  1283                                  @@:
  1284                                  
  1285                                  .retn:
  1286 000001B0 C3                      	retn
  1287                                  
  1288                                  
  1289                                  %if _ADD_SEARCH
  1290                                  add_name:		; = blank-padded 11-byte filename to search for
  1291                                  	fill 8,32,db _ADD_NAME
  1292                                  	fill 3,32,db _ADD_EXT
  1293                                  %endif
  1294                                  
  1295                                  
  1296                                  %if !_NO_LIMIT
  1297                                  available:
  1298 000001B1 26<rep 4Bh>             	_fill 508,38,start
  1299                                  
  1300                                  signatures:
  1301 000001FC 0000                    	dw 0
  1302                                  ; 2-byte magic bootsector signature
  1303 000001FE 55AA                    	dw 0AA55h
  1304                                  
  1305                                  %assign num signatures-available
  1306                                  %warning FAT32: num bytes still available.
  1306          ******************       warning: FAT32: 75 bytes still available. [-w+user]
  1307                                  %else	; for testing
  1308                                  	align 4
  1309                                  signatures:
  1310                                  	dw 0
  1311                                  ; 2-byte magic bootsector signature
  1312                                  	dw 0AA55h
  1313                                  %endif
  1314                                  
  1315                                  	align 16, nop
  1316                                  end:
  1317                                  
  1318                                  fsiboot:
  1319 00000200 52526141                	dd "RRaA"
  1320                                  .signature:
  1321 00000204 465349424F4F5434        	fill 8, 32, db _FSIBOOTNAME
  1322                                  
  1323                                  %if ($ - .signature) != 8
  1324                                   %error Unexpected name size
  1325                                  %endif
  1326                                  .start:
  1327                                  		; INP:	ax => after last segment to be used for loading
  1328                                  		;	bx => FAT buffer (8 KiB)
  1329                                  		;	ss:bp -> boot sector, with EBPB
  1330                                  		;	dwo [ss:bp -  4] = data_start (uninit)
  1331                                  		;	wo [ss:bp -  6] = load_seg (uninit)
  1332                                  		;	wo [ss:bp -  8] = fat_seg (uninit)
  1333                                  		;	dwo [ss:bp - 12] = fat_sector (uninit)
  1334                                  		;	dwo [ss:bp - 16] = first_cluster (uninit)
  1335                                  		;	wo [ss:bp - 18] = para_per_sector
  1336                                  		;	wo [ss:bp - 20] = entries_per_sector
  1337                                  		;	ss:sp = ss:bp - 20
  1338                                  		;	(Note:	The following stack frame entries are currently
  1339                                  		;		 only used by FSIBOOT itself, so they may be
  1340                                  		;		 considered implementation detail instead of
  1341                                  		;		 part of the FSIBOOT protocol.)
  1342                                  		;	wo [ss:bp - 22] = last_available_sector (uninit)
  1343                                  		;	wo [ss:bp - 24] = adj_sectors_per_cluster (uninit)
  1344                                  		;	wo [ss:bp - 26] = paras_left (uninit)
  1345                                  		;	Stack layout has to match!
  1346                                  		;	ss = ds
  1347                                  		;	ss:bp + ((11 + ebpbNew + BPBN_size + 1) & ~1)
  1348                                  		;	 = ss:fsiboot_table, refer to its comments
  1349                                  		;	cs set to address jump table offsets in fsiboot_table
  1350                                  		;	ds set to address fsiboot_table.load_name
  1351                                  		; OUT:	Jumps to fsiboot_table.error if error occurs:
  1352                                  		;	 al = error condition letter
  1353                                  		;	Else:
  1354                                  		;	data_start (within partition) initialised
  1355                                  		;	load_seg => behind last loaded data
  1356                                  		;	fat_seg initialised (from bx)
  1357                                  		;	fat_sector initialised
  1358                                  		;	 (-1 initially, loaded sector-in-FAT later)
  1359                                  		;	first_cluster initialised
  1360                                  		;	last_available_sector initialised
  1361                                  		;	 (from input ax minus word [para_per_sector])
  1362                                  		;	adj_sectors_per_cluster initialised (from BPB)
  1363                                  		;	paras_left initialised
  1364                                  		;	Jumps to fsiboot_table.success if loaded entirely:
  1365                                  		;	 dword [ss:sp] = first cluster
  1366                                  		;	Jumps to fsiboot_table.memory_full if loaded partially:
  1367                                  		;	 al = error condition letter ('M')
  1368                                  		;	 dword [ss:sp] = current cluster number
  1369                                  		;	 dword [ss:sp + 4] = first cluster
  1370                                  
  1371 0000020C 895EF8                  	mov word [VAR(fat_seg)], bx	; initialise => FAT buffer
  1372                                  
  1373 0000020F 2B46EE                  	sub ax, word [VAR(para_per_sector)]
  1374 00000212 7233                    	jc .CY_fsiboot_error_badchain
  1375 00000214 50                      	push ax			; push into word [VAR(last_available_sector)]
  1376                                  
  1377 00000215 8B5E6A                  	mov bx, word [VAR(fsiboot_table.loadseg)]
  1378 00000218 895EFA                  	mov word [VAR(load_seg)], bx	; initialise => load address
  1379 0000021B 39D8                    	cmp ax, bx
  1380 0000021D 7228                    	jb .CY_fsiboot_error_badchain
  1381                                  
  1382                                  ; adjusted sectors per cluster (store in a word,
  1383                                  ;  and decode EDR-DOS's special value 0 meaning 256)
  1384 0000021F 31C0                    	xor ax, ax
  1385 00000221 8A460D                  	mov al, [VAR(sectors_per_cluster)]
  1386 00000224 FEC8                    	dec al
  1387 00000226 40                      	inc ax
  1388 00000227 50                      	push ax			; push into word [VAR(adj_sectors_per_cluster)]
  1389 00000228 48                      	dec ax				; ! ah = 0
  1390 00000229 8A4610                  	mov al,[VAR(num_fats)]		; ! ah = 0, hence ax = number of FATs
  1391 0000022C 50                      	push ax
  1392 0000022D F76624                  	mul word [VAR(sectors_per_fat_large)]
  1393                                  		; ax = low word SpF*nF
  1394                                  		; dx = high word
  1395 00000230 93                      	xchg bx, ax
  1396 00000231 87CA                    	xchg cx, dx
  1397                                  		; cx:bx = first mul
  1398 00000233 58                      	pop ax
  1399 00000234 F76626                  	mul word [VAR(sectors_per_fat_large + 2)]
  1400                                  		; ax = high word adjust
  1401                                  		; dx = third word
  1402 00000237 85D2                    	test dx, dx
  1403 00000239 F9                      	stc
  1404 0000023A 750B                    	jnz .CY_fsiboot_error_badchain
  1405                                  		; dx = zero
  1406 0000023C 92                      	xchg dx, ax
  1407                                  		; dx = high word adjust
  1408                                  		; ax = zero
  1409 0000023D 01CA                    	add dx, cx
  1410 0000023F 7206                    	jc .CY_fsiboot_error_badchain
  1411                                  		; dx:bx = result
  1412 00000241 93                      	xchg ax, bx
  1413                                  		; dx:ax = result
  1414                                  		; bx = zero
  1415                                  
  1416 00000242 03460E                  	add ax,[VAR(num_reserved_sectors)]
  1417 00000245 11DA                    	adc dx, bx		; bx is zero here
  1418                                  .CY_fsiboot_error_badchain:
  1419 00000247 725E                    	jc ..@CY_2_fsiboot_error_badchain
  1420                                  
  1421                                  ; first sector of disk data area:
  1422 00000249 8946FC                  	mov [VAR(data_start)], ax
  1423 0000024C 8956FE                  	mov [VAR(data_start+2)], dx
  1424                                  
  1425 0000024F BFFFFF                  	mov di, -1
  1426 00000252 89FE                    	mov si, di
  1427 00000254 8976F6                  	mov word [VAR(fat_sector + 2)], si
  1428 00000257 897EF4                  	mov word [VAR(fat_sector + 0)], di
  1429 0000025A E88A00                  	call dirsearch
  1430                                  
  1431                                  
  1432                                  found_load_file:
  1433 0000025D FF5664                  	call near word [VAR(fsiboot_table.writedirentry)]
  1434                                  
  1435                                  ; get starting cluster of file
  1436 00000260 26FF7714                		push word [es:bx + deClusterHigh]
  1437 00000264 26FF771A                		push word [es:bx + deClusterLow]
  1438                                  
  1439                                  					; check FAT+ size bits
  1440 00000268 26F6470CE7              		test byte [es:bx + dePlusSize], 0E7h
  1441                                  					; test whether bits 7-5 and 2-0 NZ
  1442                                  ; https://web.archive.org/web/20150219123449/http://www.fdos.org/kernel/fatplus.txt
  1443 0000026D 7524                    		jnz .large_file		; yes, clamp to maximum paras -->
  1444 0000026F 268B471E                		mov ax, [es:bx + deSize + 2]
  1445 00000273 268B5F1C                		mov bx, [es:bx + deSize]
  1446                                  					; ax:bx = file size (non-FAT+)
  1447                                  
  1448 00000277 8B4E0B                  		mov cx, [VAR(bytes_per_sector)]
  1449 0000027A 49                      		dec cx			; BpS - 1
  1450 0000027B 01CB                    		add bx, cx
  1451 0000027D 83D000                  		adc ax, 0		; large ?
  1452 00000280 7211                    		jc .large_file		; yes, clamp to maximum paras -->
  1453                                  
  1454 00000282 F7D1                    		not cx			; ~ (BpS - 1)
  1455 00000284 21CB                    		and bx, cx		; mask to limit to rounded-up sector
  1456                                  				; (this also rounds up paragraphs)
  1457 00000286 B90400                  		mov cx, 4
  1458                                  @@:
  1459 00000289 D1E8                    		shr ax, 1
  1460 0000028B D1DB                    		rcr bx, 1
  1461 0000028D E2FA                    		loop @B
  1462                                  			; ax:bx = size in paragraphs
  1463                                  			; bx = size in paragraphs if < 1_0000h
  1464 0000028F 85C0                    		test ax, ax		; > 0FFFFh paras ?
  1465 00000291 7403                    		jz @F			; no, take actual size -->
  1466                                  .large_file:
  1467 00000293 BBFFFF                  		mov bx, 0FFFFh		; cx = clamp size to 0FFFFh paras
  1468                                  @@:
  1469 00000296 E8C500                  		call check_enough.in_bx	; (CHG ax)
  1470                                  
  1471 00000299 58                      		pop ax
  1472 0000029A 5A                      		pop dx			; dx:ax = first cluster
  1473                                  
  1474 0000029B 53                      		push bx			; push into word [VAR(paras_left)]
  1475                                  
  1476 0000029C 8946F0                  		mov word [VAR(first_cluster + 0)], ax
  1477 0000029F 8956F2                  		mov word [VAR(first_cluster + 2)], dx
  1478 000002A2 52                      		push dx
  1479 000002A3 50                      		push ax			; remember cluster for later
  1480                                  
  1481 000002A4 E80F01                  		call check_clust
  1482                                  ..@CY_2_fsiboot_error_badchain:
  1483 000002A7 7247                    		jc ..@CY_3_fsiboot_error_badchain
  1484                                  
  1485                                  next_load_cluster:
  1486 000002A9 E88A00                  		call clust_to_first_sector
  1487                                  			; dx:ax = first sector of cluster
  1488                                  			; cx:bx = cluster value
  1489 000002AC 51                      		push cx
  1490 000002AD 53                      		push bx			; preserve cluster number for later
  1491                                  
  1492 000002AE 8B4EE8                  		mov cx, [VAR(adj_sectors_per_cluster)]
  1493                                  
  1494                                  ; xxx - this will always load an entire cluster (e.g. 64 sectors),
  1495                                  ; even if the file is shorter than this
  1496                                  @@:
  1497 000002B1 8B5EFA                  		mov bx, [VAR(load_seg)]	; => where to read next sector
  1498 000002B4 3B5EEA                  		cmp bx, [VAR(last_available_sector)]
  1499 000002B7 7608                    		jbe @F
  1500 000002B9 E89C00                  		call check_enough
  1501 000002BC B04D                    		mov al, 'M'		; (! _MEMORY_CONTINUE: error code)
  1502 000002BE FF665E                  		jmp near word [VAR(fsiboot_table.memory_full)]
  1503                                  
  1504                                  @@:
  1505 000002C1 FF5660                  		call near word [VAR(fsiboot_table.read_sector)]
  1506 000002C4 895EFA                  		mov [VAR(load_seg)], bx	; => after last read data
  1507                                  
  1508 000002C7 8B5EEE                  		mov bx, [VAR(para_per_sector)]
  1509 000002CA 295EE6                  		sub word [VAR(paras_left)], bx
  1510 000002CD 7610                    		jbe @F		; read enough -->
  1511                                  
  1512 000002CF E2E0                    		loop @BB
  1513 000002D1 5B                      		pop bx
  1514 000002D2 59                      		pop cx
  1515                                  
  1516 000002D3 E89700                  		call clust_next
  1517 000002D6 73D1                    		jnc next_load_cluster
  1518 000002D8 40                      		inc ax
  1519 000002D9 40                      		inc ax
  1520 000002DA A808                    		test al, 8	; set in 0FFF_FFF8h--0FFF_FFFFh,
  1521                                  				;  clear in 0, 1, and 0FFF_FFF7h
  1522 000002DC 7453                    		jz fsiboot_error_badchain
  1523 000002DE A9                      		db __TEST_IMM16
  1524                                  @@:
  1525 000002DF 5B                      		pop bx
  1526 000002E0 59                      		pop cx
  1527 000002E1 E87400                  		call check_enough
  1528 000002E4 FF665C                  		jmp near word [VAR(fsiboot_table.success)]
  1529                                  
  1530                                  
  1531                                  dirsearch:
  1532 000002E7 8B462C                  	mov ax, [VAR(root_cluster)]
  1533 000002EA 8B562E                  	mov dx, [VAR(root_cluster + 2)]
  1534 000002ED E8C600                  	call check_clust
  1535                                  ..@CY_3_fsiboot_error_badchain:
  1536 000002F0 723F                    	jc fsiboot_error_badchain
  1537                                  
  1538                                  next_root_clust:
  1539 000002F2 E84100                  	call clust_to_first_sector
  1540 000002F5 51                      	push cx
  1541 000002F6 53                      	push bx
  1542 000002F7 8B4EE8                  	mov cx, [VAR(adj_sectors_per_cluster)]
  1543                                  next_root_sect:
  1544 000002FA 51                      	push cx
  1545 000002FB 8B4EEC                  	mov cx, [VAR(entries_per_sector)]
  1546                                  
  1547                                  ; Scan root directory for file. We don't bother to check for deleted
  1548                                  ;  entries (E5h) or entries that mark the end of the directory (00h).
  1549 000002FE 8B5E62                  	mov bx, [VAR(fsiboot_table.dirbuf)]
  1550 00000301 FF5660                  	call near word [VAR(fsiboot_table.read_sector)]
  1551                                  
  1552 00000304 57                      	push di
  1553 00000305 31FF                    	xor di, di		; es:di-> first entry in this sector
  1554                                  next_ent:
  1555 00000307 26F6450B18              	test byte [es:di + deAttrib], ATTR_DIRECTORY | ATTR_VOLLABEL
  1556 0000030C 750E                    	jnz @F			; directory, label, or LFN entry --> (NZ)
  1557 0000030E 56                      	push si
  1558 0000030F 57                      	push di
  1559 00000310 51                      	push cx
  1560 00000311 8B7666                  	mov si, [VAR(fsiboot_table.filename)]
  1561                                  				; ds:si-> name to match
  1562 00000314 B90B00                  	mov cx, 11		; length of padded 8.3 FAT filename
  1563 00000317 F3A6                    	repe cmpsb		; check entry
  1564 00000319 59                      	pop cx
  1565 0000031A 5F                      	pop di
  1566 0000031B 5E                      	pop si
  1567                                  @@:				; ZR = match, NZ = mismatch
  1568 0000031C 7448                    	je found_it		; found entry -->
  1569 0000031E 8D7D20                  	lea di, [di + DIRENTRY_size]
  1570                                  
  1571 00000321 E2E4                    	loop next_ent		; count down sector's entries (jumps iff cx >0)
  1572 00000323 5F                      	pop di
  1573 00000324 59                      	pop cx
  1574 00000325 E2D3                    	loop next_root_sect
  1575 00000327 5B                      	pop bx
  1576 00000328 59                      	pop cx
  1577 00000329 E84100                  	call clust_next
  1578 0000032C 73C4                    	jnc next_root_clust
  1579                                  file_not_found:
  1580 0000032E B046                    	mov al, 'F'
  1581 00000330 A9                      	db __TEST_IMM16
  1582                                  fsiboot_error_badchain:
  1583 00000331 B042                    	mov al, 'B'
  1584                                  
  1585                                  fsiboot_error:
  1586 00000333 FF665A                  	jmp near word [VAR(fsiboot_table.error)]
  1587                                  
  1588                                  
  1589                                  		; INP:	dx:ax = cluster - 2 (0-based cluster)
  1590                                  		; OUT:	cx:bx = input dx:ax
  1591                                  		;	dx:ax = first sector of that cluster
  1592                                  		; CHG:	-
  1593                                  clust_to_first_sector:
  1594 00000336 52                      	push dx
  1595 00000337 50                      	push ax
  1596 00000338 52                      	 push dx
  1597 00000339 F766E8                  	mul word [VAR(adj_sectors_per_cluster)]
  1598 0000033C 93                      	xchg bx, ax
  1599 0000033D 87CA                    	xchg cx, dx
  1600 0000033F 58                      	 pop ax
  1601 00000340 F766E8                  	mul word [VAR(adj_sectors_per_cluster)]
  1602 00000343 85D2                    	test dx, dx
  1603 00000345 75EA                    	jnz fsiboot_error_badchain
  1604 00000347 92                      	xchg dx, ax
  1605 00000348 01CA                    	add dx, cx
  1606 0000034A 7207                    	jc ..@CY_fsiboot_error_badchain
  1607 0000034C 93                      	xchg ax, bx
  1608                                  
  1609 0000034D 0346FC                  	add ax, [VAR(data_start)]
  1610 00000350 1356FE                  	adc dx, [VAR(data_start + 2)]
  1611                                  ..@CY_fsiboot_error_badchain:
  1612 00000353 72DC                    	jc fsiboot_error_badchain
  1613                                  				; dx:ax = first sector in cluster
  1614 00000355 5B                      	pop bx
  1615 00000356 59                      	pop cx			; cx:bx = cluster
  1616 00000357 C3                      	retn
  1617                                  
  1618                                  
  1619                                  check_enough:
  1620 00000358 8B5EFA                  		mov bx, [VAR(load_seg)]
  1621                                  				; => behind last read sector
  1622 0000035B 2B5E6A                  		sub bx, word [VAR(fsiboot_table.loadseg)]
  1623                                  .in_bx:
  1624 0000035E 3B5E68                  		cmp bx, word [VAR(fsiboot_table.minpara)]
  1625 00000361 B045                    		mov al, 'E'
  1626 00000363 72CE                    		jb fsiboot_error
  1627 00000365 C3                      		retn
  1628                                  
  1629                                  
  1630                                  found_it:
  1631                                  			; es:di -> dir entry in dir sector buffer
  1632 00000366 89FB                    	mov bx, di
  1633 00000368 5F                      		pop di			; restore si:di = loaded FAT sector
  1634 00000369 59                      		pop cx			; (discard sectors per cluster counter)
  1635 0000036A 59                      		pop cx
  1636 0000036B 59                      		pop cx			; (discard current cluster number)
  1637                                  			; es:bx -> dir entry
  1638 0000036C C3                      	retn
  1639                                  
  1640                                  
  1641                                  		; INP:	cx:bx = cluster (0-based)
  1642                                  		;	si:di = loaded FAT sector, -1 if none
  1643                                  		; OUT:	CY if no next cluster
  1644                                  		;	NC if next cluster found,
  1645                                  		;	 dx:ax = next cluster value (0-based)
  1646                                  		;	si:di = loaded FAT sector
  1647                                  		; CHG:	cx, bx, es
  1648                                  clust_next:
  1649 0000036D 93                      	xchg ax, bx		; ax = low word cluster, clobbers bx
  1650 0000036E 89CA                    	mov dx, cx
  1651 00000370 83C002                  	add ax, 2
  1652 00000373 83D200                  	adc dx, 0
  1653                                  
  1654 00000376 01C0                    	add ax, ax
  1655 00000378 11D2                    	adc dx, dx
  1656 0000037A 01C0                    	add ax, ax
  1657 0000037C 11D2                    	adc dx, dx		; * 4 = byte offset into FAT (0--4000_0000h)
  1658 0000037E 50                      	 push ax
  1659 0000037F 92                      	xchg ax, dx
  1660 00000380 31D2                    	xor dx, dx		; dx:ax = high word
  1661 00000382 F7760B                  	div word [VAR(bytes_per_sector)]
  1662 00000385 93                      	xchg bx, ax		; bx = result high word, clobbers ax
  1663 00000386 58                      	 pop ax			; dx = remainder, ax = low word
  1664 00000387 F7760B                  	div word [VAR(bytes_per_sector)]
  1665 0000038A 87D3                    	xchg dx, bx		; dx:ax = result, bx = remainder
  1666                                  				; dx:ax = sector offset into FAT (0--200_0000h)
  1667                                  				; bx = byte offset into FAT sector (0--8190)
  1668 0000038C 39F2                    	cmp dx, si
  1669 0000038E 7504                    	jne @F		; read sector
  1670 00000390 39F8                    	cmp ax, di
  1671 00000392 7418                    	je @FF		; sector is already buffered
  1672                                  @@:
  1673 00000394 89D6                    	mov si, dx
  1674 00000396 89C7                    	mov di, ax
  1675 00000398 8956F6                  	mov word [VAR(fat_sector + 2)], dx
  1676 0000039B 8946F4                  	mov word [VAR(fat_sector + 0)], ax
  1677                                  
  1678 0000039E 53                      	push bx
  1679 0000039F 03460E                  	add ax, [VAR(fat_start)]
  1680 000003A2 83D200                  	adc dx, 0
  1681 000003A5 8B5EF8                  	mov bx, [VAR(fat_seg)]
  1682 000003A8 FF5660                  	call near word [VAR(fsiboot_table.read_sector)]
  1683 000003AB 5B                      	pop bx
  1684                                  @@:
  1685 000003AC 8E46F8                  	mov es, [VAR(fat_seg)]
  1686 000003AF 268B07                  	mov ax, [es:bx]
  1687 000003B2 268B5702                	mov dx, [es:bx + 2]
  1688                                  
  1689                                  		; INP:	dx:ax = cluster value, 2-based
  1690                                  		; OUT:	dx:ax -= 2 (makes it 0-based)
  1691                                  		;	CY iff invalid cluster
  1692                                  check_clust:
  1693 000003B6 80E60F                  	and dh, 0Fh
  1694 000003B9 83E802                  	sub ax, 2
  1695 000003BC 83DA00                  	sbb dx, 0
  1696 000003BF 81FAFF0F                	cmp dx, 0FFFh
  1697 000003C3 7203                    	jb @F		; CY here means valid ...-
  1698 000003C5 83F8F5                  	cmp ax, 0FFF7h - 2
  1699                                  @@:			;  -... or if NC first, CY here also
  1700 000003C8 F5                      	cmc		; NC if valid
  1701 000003C9 C3                      	retn
  1702                                  
  1703                                  
  1704                                  fsinfo_available:
  1705 000003CA 26<rep 18h>             	_fill 480 + 4 - 2,38,fsiboot
  1706                                  
  1707                                  %assign num $-fsinfo_available
  1708                                  %warning FSINFO: num bytes still available.
  1708          ******************       warning: FSINFO: 24 bytes still available. [-w+user]
  1709                                  
  1710                                  
  1711                                  		; INP:	si:di = loaded sector-in-FAT
  1712                                  		;	word [fsiboot_table.filename] -> 8.3 filename
  1713                                  		; CHG:	ax, cx, dx
  1714                                  		; OUT:	si:di updated, if so
  1715                                  		;	es:bx -> found directory entry
  1716                                  		;	jumps to error handler if an error occurs
  1717                                  		; STT:	stack variables as set up by main FSIBOOT entry
  1718                                  dirsearch_entrypoint:
  1719 000003E2 [E702]                  	dw dirsearch
  1720                                  
  1721 000003E4 72724161                	dd "rrAa"
  1722 000003E8 FFFFFFFF                	dd -1		; number of free clusters
  1723 000003EC FFFFFFFF                	dd -1		; first free cluster
  1724 000003F0 00000000<rep 3h>        	times 3 dd 0	; FSINFO.reserved2
  1725 000003FC 000055AA                	dd 0_AA55_0000h	; FSINFO.signature3
  1726                                  
  1727                                  end_after_fsiboot:
  1728                                  %if $ - fsiboot != 512
  1729                                   %error Wrong FSIBOOT layout
  1730                                  %endif
