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