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