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