     1                                  
     2                                  %if 0
     3                                  
     4                                  File system boot sector loader code for FAT12 or FAT16
     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
    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                                  	numdef FAT16,		0	; 0 = FAT12, 1 = FAT16
    25                                  	strdef OEM_NAME,	"    lDOS"
    26                                  	strdef OEM_NAME_FILL,	'_'
    27                                  	strdef DEFAULT_LABEL,	"lDOS"
    28                                  	numdef VOLUMEID,	0
    29                                  
    30                                  	strdef LOAD_NAME,	"LDOS"
    31                                  	strdef LOAD_EXT,	"COM"	; name of file to load
    32                                  	numdef LOAD_ADR,	02000h	; where to load
    33                                  	numdef LOAD_MIN_PARA,	paras(4096)
    34                                  	numdef LOAD_CHECK_MAX_SIZE, 0
    35                                  	numdef LOAD_CHECK_MAX_SIZE_FATPLUS, 1
    36                                  	numdef LOAD_NON_FAT,	0, 2048	; use FAT-less loading (value is amount bytes)
    37                                  	numdef EXEC_SEG_ADJ,	0	; how far cs will be from _LOAD_ADR
    38                                  	numdef EXEC_OFS,	400h	; what value ip will be
    39                                  	numdef CHECKOFFSET,	1020
    40                                  	numdef CHECKVALUE,	"lD"
    41                                  	numdef LOAD_STACK,	512	; default stack size (use 256 for DR-DOS)
    42                                  	numdef LOAD_DIR_SEG,	0	; => where to store dir entry (0 if nowhere)
    43                                  	numdef ADD_SEARCH,	0	; whether to search second file
    44                                  	strdef ADD_NAME,	""
    45                                  	strdef ADD_EXT,		""	; name of second file to search
    46                                  	numdef ADD_DIR_SEG,	0	; => where to store dir entry (0 if nowhere)
    47                                  	numdef CHECK_ATTRIB,	0	; check attribute for LFN, label, directory
    48                                  	numdef ATTRIB_SAVE,	_CHECK_ATTRIB
    49                                  
    50                                  	gendef _ADR_DIRBUF, end -start+7C00h	; 07E00h
    51                                  	gendef _ADR_FATBUF, end -start+7C00h	; 07E00h
    52                                  
    53                                  	numdef QUERY_GEOMETRY,	1	; query geometry via 13.08 (for CHS access)
    54                                  	numdef QUERY_GEOMETRY_DISABLED, 0
    55                                  	numdef USE_PART_INFO,	1	; use ds:si-> partition info from MBR, if any
    56                                  	numdef USE_PART_INFO_DISABLED, 0
    57                                  	numdef USE_AUTO_UNIT,	1	; use unit passed from ROM-BIOS in dl
    58                                  	numdef RPL,		1	; support RPL and do not overwrite it
    59                                  	numdef RPL_GRACE_AREA,	130 * 1024
    60                                  					; alternative RPL support,
    61                                  					;  assume RPL fits in this area
    62                                  	numdef CHS,		1	; support CHS (if it fits)
    63                                  	numdef LBA,		1	; support LBA (if available)
    64                                  	numdef LBA_33_BIT,	1	; support 33-bit LBA
    65                                  	numdef LBA_CHECK_NO_33,	1	; else: check that LBA doesn't carry
    66                                  
    67                                  	numdef RELOCATE,	0	; relocate the loader to top of memory
    68                                  	numdef SET_BL_UNIT,	0	; if to pass unit in bl as well
    69                                  	numdef SET_DL_UNIT,	0	; if to pass unit in dl
    70                                  	numdef SET_AXBX_DATA,	0	; if to pass first data sector in ax:bx
    71                                  	numdef SET_DSSI_DPT,	0	; if to pass DPT address in ds:si
    72                                  	numdef PUSH_DPT,	0	; if to push DPT address
    73                                  	numdef MEMORY_CONTINUE,	1	; if to just execute when memory full
    74                                  	numdef SET_DI_CLUSTER,	0	; if to pass first load file cluster in di
    75                                  	numdef DIRBUF_500,	0	; if to load root dir sector(s) to 0:500h
    76                                  	numdef DIR_ENTRY_500,	0	; if to copy directory entry to 0:500h
    77                                  	numdef DIR_ENTRY_520,	0	; if to copy next directory entry to 0:520h
    78                                  	numdef TURN_OFF_FLOPPY,	0	; if to turn off floppy motor after loading
    79                                  	numdef DATASTART_HIDDEN,0	; if to add hidden sectors to data_start
    80                                  	numdef LBA_SET_TYPE,	0	; if to set third byte to LBA partition type
    81                                  	numdef SET_LOAD_SEG,	1	; if to set load_seg (word [ss:bp - 6])
    82                                  	numdef SET_FAT_SEG,	1	; if to set fat_seg (word [ss:bp - 8])
    83                                  	numdef SET_FAT_SEG_NORMAL, 1	; do not use aggressive optimisation
    84                                  	numdef SET_CLUSTER,	1	; if to set first_cluster (word [ss:bp - 16])
    85                                  	numdef ZERO_ES,		0	; if to set es = 0 before jump
    86                                  	numdef ZERO_DS,		0	; if to set ds = 0 before jump
    87                                  
    88                                  	numdef FIX_SECTOR_SIZE, 0	; fix sector size (0 = disable, else = sector size)
    89                                  	numdef FIX_SECTOR_SIZE_SKIP_CHECK,	0	; don't check sector size
    90                                  	numdef FIX_CLUSTER_SIZE,		0	; fix cluster size
    91                                  	numdef FIX_CLUSTER_SIZE_SKIP_CHECK,	0	; don't check cluster size
    92                                  	numdef NO_LIMIT,	0	; allow using more memory than a boot sector
    93                                  					;  also will not write 0AA55h signature!
    94                                  	numdef WARN_PART_SIZE,	0
    95                                  
    96                                  	numdef LBA_SKIP_CHECK,	1	; don't use proper LBA extensions check
    97                                  	numdef LBA_WORKAROUND,	0	; when checking for LBA set ds = 40h
    98                                  	numdef LBA_SKIP_CY,	1	; skip check: set up CY before 13.42
    99                                  	numdef LBA_SKIP_ANY,	0	; skip check: try CHS on any error
   100                                  	incdef _LBA_SKIP_ANY, LBA_SKIP_CY
   101                                  	numdef LBA_RETRY,	0	; retry LBA reads one time
   102                                  	numdef CHS_RETRY,	1	; retry CHS reads one time
   103                                  	numdef CHS_RETRY_REPEAT,16	; retry CHS reads multiple times
   104                                  					; (value of the def is used as count)
   105                                  	numdef CHS_RETRY_NORMAL,1	; do not use aggressive optimisation
   106                                  	numdef RETRY_RESET,	1	; call reset disk system 13.00 on retries
   107                                  
   108                                  	numdef MEDIAID, 0F0h		; media ID
   109                                  	numdef UNIT, 0			; load unit in BPB
   110                                  	numdef CHS_SECTORS, 18		; CHS geometry field for sectors
   111                                  	numdef CHS_HEADS, 2		; CHS geometry field for heads
   112                                  	numdef HIDDEN, 0		; number of hidden sectors
   113                                  	numdef SPI, 2880		; sectors per image
   114                                  	numdef BPS, 512			; bytes per sector
   115                                  	numdef SPC, 1			; sectors per cluster
   116                                  	numdef SPF, 9			; sectors per FAT
   117                                  	numdef NUMFATS, 2		; number of FATs
   118                                  	numdef NUMROOT, 224		; number of root directory entries
   119                                  	numdef NUMRESERVED, 1		; number of reserved sectors
   120                                  
   121                                  %if _FAT16
   122                                  		; Unlike the 1440 KiB diskette image defaults for the FAT12
   123                                  		;  loader we just fill the FAT16 BPB with zeros by default.
   124                                  	numdef MEDIAID, 0		; media ID
   125                                  	numdef UNIT, 0			; load unit in BPB
   126                                  	numdef CHS_SECTORS, 0		; CHS geometry field for sectors
   127                                  	numdef CHS_HEADS, 0		; CHS geometry field for heads
   128                                  	numdef HIDDEN, 0		; number of hidden sectors
   129                                  	numdef SPI, 0			; sectors per image
   130                                  	numdef BPS, 0			; bytes per sector
   131                                  	numdef SPC, 0			; sectors per cluster
   132                                  	numdef SPF, 0			; sectors per FAT
   133                                  	numdef NUMFATS, 0		; number of FATs
   134                                  	numdef NUMROOT, 0		; number of root directory entries
   135                                  	numdef NUMRESERVED, 0		; number of reserved sectors
   136                                  %endif
   137                                  
   138                                  %if _DIRBUF_500
   139                                  	gendef _ADR_DIRBUF, 500h
   140                                  %endif
   141                                  
   142                                  
   143                                  	numdef COMPAT_FREEDOS,	0	; partial FreeDOS load compatibility
   144                                  	numdef COMPAT_DR,	0	; partial DR-DOS load compatibility
   145                                  	numdef COMPAT_IBM,	0	; partial IBMDOS load compatibility
   146                                  	numdef COMPAT_MS7,	0	; partial MS-DOS 7 load compatibility
   147                                  	numdef COMPAT_MS6,	0	; partial MS-DOS 6 load compatibility
   148                                  	numdef COMPAT_LDOS,	0	; lDOS load compatibility
   149                                  	numdef COMPAT_KERNEL7E, 0	; kernel at 0:7E00h load compatibility
   150                                  
   151                                  %if (!!_COMPAT_FREEDOS + !!_COMPAT_IBM + !!_COMPAT_DR + 	!!_COMPAT_MS7 + !!_COMPAT_MS6 + 	!!_COMPAT_LDOS + !!_COMPAT_KERNEL7E) > 1
   154                                   %error At most one set must be selected.
   155                                  %endif
   156                                  
   157                                  %if _COMPAT_FREEDOS
   158                                  	strdef LOAD_NAME,	"KERNEL"
   159                                  	strdef LOAD_EXT,	"SYS"
   160                                  	numdef LOAD_ADR,	00600h
   161                                  	numdef LOAD_MIN_PARA,	paras(512)
   162                                  	numdef EXEC_SEG_ADJ,	0
   163                                  	numdef EXEC_OFS,	0
   164                                  
   165                                  	numdef CHECKVALUE,	0
   166                                  	numdef SET_LOAD_SEG,	0
   167                                  	numdef SET_FAT_SEG,	0
   168                                  	numdef SET_CLUSTER,	0
   169                                  
   170                                  	numdef SET_BL_UNIT,	1
   171                                  	numdef MEMORY_CONTINUE,	0
   172                                  	numdef RELOCATE,	1
   173                                  	; The FreeDOS load protocol mandates that the entire file be loaded.
   174                                  %endif
   175                                  
   176                                  %if _COMPAT_IBM
   177                                  	strdef LOAD_NAME,	"IBMBIO"
   178                                  	strdef LOAD_EXT,	"COM"
   179                                  	numdef LOAD_ADR,	00700h
   180                                  	numdef LOAD_MIN_PARA,	paras(512)
   181                                  	numdef EXEC_SEG_ADJ,	0
   182                                  	numdef EXEC_OFS,	0
   183                                  	numdef LOAD_DIR_SEG,	50h
   184                                  	numdef ADD_SEARCH,	1
   185                                  	strdef ADD_NAME,	"IBMDOS"
   186                                  	strdef ADD_EXT,		"COM"
   187                                  	numdef ADD_DIR_SEG,	52h
   188                                   	; Note: The IBMBIO.COM directory entry must be stored at
   189                                  	;  0:500h, and the IBMDOS.COM directory entry at 0:520h.
   190                                  
   191                                  	numdef CHECKVALUE,	0
   192                                  	numdef SET_LOAD_SEG,	0
   193                                  	numdef SET_FAT_SEG,	0
   194                                  	numdef SET_CLUSTER,	0
   195                                  
   196                                  	numdef SET_DL_UNIT,	1
   197                                  	numdef MEMORY_CONTINUE,	1
   198                                  	; 3 sectors * 512 BpS should suffice. We load into 700h--7A00h,
   199                                  	;  ie >= 6000h bytes (24 KiB), <= 7300h bytes (28.75 KiB).
   200                                  	numdef SET_AXBX_DATA,	1
   201                                  	numdef DATASTART_HIDDEN,1
   202                                  	numdef SET_DSSI_DPT,	1
   203                                  	numdef PUSH_DPT,	1
   204                                  %endif
   205                                  
   206                                  %if _COMPAT_DR
   207                                  	strdef LOAD_NAME,	"IBMBIO"
   208                                  	strdef LOAD_EXT,	"COM"
   209                                  	numdef LOAD_ADR,	00700h
   210                                  	numdef LOAD_STACK,	256
   211                                  	; Shrinking the stack allows to boot a file of up to 29 KiB
   212                                  	;  given a sector size of <= 1 KiB.
   213                                  	numdef LOAD_MIN_PARA,	paras(4096 - 700h)
   214                                  	; Using a min para size that allows to compare the segment's
   215                                  	;  high byte only saves a byte.
   216                                  	numdef EXEC_SEG_ADJ,	0
   217                                  	numdef EXEC_OFS,	0
   218                                  
   219                                  	numdef CHECKVALUE,	0
   220                                  	numdef SET_LOAD_SEG,	0
   221                                  	numdef SET_FAT_SEG,	0
   222                                  	numdef SET_CLUSTER,	0
   223                                  
   224                                  	numdef SET_DL_UNIT,	1
   225                                  	numdef MEMORY_CONTINUE,	1
   226                                  	; Memory continue must certainly be set to boot off file
   227                                  	;  systems with >= 32 KiB per cluster and may be needed
   228                                  	;  even for smaller cluster sizes that are still > 1 KiB.
   229                                  	numdef LOAD_CHECK_MAX_SIZE, 1
   230                                  	; In conjunction with _MEMORY_CONTINUE this enables erroring
   231                                  	;  out if the actual file data is too large to load it, but
   232                                  	;  avoids the need to load the entire last cluster.
   233                                  
   234                                  	numdef SET_AXBX_DATA,	0
   235                                  	numdef DATASTART_HIDDEN,0
   236                                  	numdef SET_DSSI_DPT,	0
   237                                  	numdef PUSH_DPT,	0
   238                                  	; None of these options seem needed for DR-DOS. Note that
   239                                  	;  disabling them will fail if we actually load our lDOS
   240                                  	;  iniload as it expects these options enabled.
   241                                  %endif
   242                                  
   243                                  %if _COMPAT_MS7
   244                                  	strdef LOAD_NAME,	"IO"
   245                                  	strdef LOAD_EXT,	"SYS"
   246                                  	numdef LOAD_ADR,	00700h
   247                                  	numdef LOAD_MIN_PARA,	paras(1024)
   248                                  	numdef EXEC_SEG_ADJ,	0
   249                                  	numdef EXEC_OFS,	200h
   250                                  
   251                                  	numdef CHECKVALUE,	0
   252                                  	numdef SET_LOAD_SEG,	0
   253                                  	numdef SET_FAT_SEG,	0
   254                                  	numdef SET_CLUSTER,	0
   255                                  
   256                                  	numdef SET_DL_UNIT,	1
   257                                  	numdef SET_DSSI_DPT,	0
   258                                  	numdef PUSH_DPT,	1
   259                                  	numdef MEMORY_CONTINUE,	1
   260                                  	; 4 sectors * 512 BpS should suffice. We load into 700h--7A00h,
   261                                  	;  ie >= 6000h bytes (24 KiB), <= 7300h bytes (28.75 KiB).
   262                                  	numdef SET_DI_CLUSTER,	1
   263                                  	numdef DATASTART_HIDDEN,1
   264                                  	numdef LBA_SET_TYPE,	1
   265                                  %endif
   266                                  
   267                                  %if _COMPAT_MS6
   268                                  	strdef LOAD_NAME,	"IO"
   269                                  	strdef LOAD_EXT,	"SYS"
   270                                  	numdef LOAD_ADR,	00700h
   271                                  	numdef LOAD_MIN_PARA,	paras(512)
   272                                  	numdef EXEC_SEG_ADJ,	0
   273                                  	numdef EXEC_OFS,	0
   274                                  	numdef LOAD_DIR_SEG,	50h
   275                                  	numdef ADD_SEARCH,	1
   276                                  	strdef ADD_NAME,	"MSDOS"
   277                                  	strdef ADD_EXT,		"SYS"
   278                                  	numdef ADD_DIR_SEG,	52h
   279                                   	; Note: The IO.SYS directory entry must be stored at
   280                                  	;  0:500h, and the MSDOS.SYS directory entry at 0:520h.
   281                                  
   282                                  	numdef CHECKVALUE,	0
   283                                  	numdef SET_LOAD_SEG,	0
   284                                  	numdef SET_FAT_SEG,	0
   285                                  	numdef SET_CLUSTER,	0
   286                                  
   287                                  	numdef SET_DL_UNIT,	1
   288                                  	numdef MEMORY_CONTINUE,	1
   289                                  	; 3 sectors * 512 BpS should suffice. We load into 700h--7A00h,
   290                                  	;  ie >= 6000h bytes (24 KiB), <= 7300h bytes (28.75 KiB).
   291                                  	numdef SET_AXBX_DATA,	1
   292                                  	numdef DATASTART_HIDDEN,1
   293                                  	numdef SET_DSSI_DPT,	1
   294                                  	numdef PUSH_DPT,	1
   295                                  %endif
   296                                  
   297                                  %if _COMPAT_LDOS
   298                                  	strdef LOAD_NAME,	"LDOS"
   299                                  	strdef LOAD_EXT,	"COM"
   300                                  	numdef LOAD_ADR,	02000h
   301                                  	numdef LOAD_MIN_PARA,	paras(4096)
   302                                  	numdef EXEC_SEG_ADJ,	0
   303                                  	numdef EXEC_OFS,	400h
   304                                  	numdef CHECKOFFSET,	1020
   305                                  	numdef CHECKVALUE,	"lD"
   306                                  
   307                                  	numdef SET_DL_UNIT,	0
   308                                  	numdef SET_CLUSTER,	1
   309                                  	numdef SET_FAT_SEG,	1
   310                                  	numdef SET_LOAD_SEG,	1
   311                                  	numdef MEMORY_CONTINUE,	1
   312                                  	numdef DATASTART_HIDDEN,0
   313                                  %endif
   314                                  
   315                                  %if _COMPAT_KERNEL7E
   316                                  	strdef OEM_NAME,	"KERNEL7E"
   317                                  	strdef LOAD_NAME,	"KERNEL7E"
   318                                  	strdef LOAD_EXT,	"BIN"
   319                                  	numdef LOAD_ADR,	07E00h
   320                                  	numdef LOAD_MIN_PARA,	paras(512)
   321                                  	numdef EXEC_SEG_ADJ,	-7E0h
   322                                  	numdef EXEC_OFS,	7E00h
   323                                  	numdef CHECKVALUE,	0
   324                                  
   325                                  	numdef SET_DL_UNIT,	1
   326                                  	numdef SET_BL_UNIT,	0
   327                                  	numdef SET_CLUSTER,	0
   328                                  	numdef SET_FAT_SEG,	0
   329                                  	numdef SET_LOAD_SEG,	0
   330                                  	numdef MEMORY_CONTINUE,	0
   331                                  	numdef DATASTART_HIDDEN,0
   332                                  
   333                                  	gendef _ADR_FATBUF,	4000h
   334                                  	gendef _ADR_DIRBUF,	4000h
   335                                  	numdef RPL,		0
   336                                  	numdef ZERO_ES,		1
   337                                  %endif
   338                                  
   339                                  
   340                                  %if 0
   341                                  
   342                                  Notes about partial load compatibilities
   343                                  
   344                                  * FreeDOS:
   345                                   * Relocates to an address other than 27A00h (1FE0h:7C00h)
   346                                   * A lot of options between _USE_PART_INFO, _QUERY_GEOMETRY, _CHS, _LBA,
   347                                     and/or _RPL need to be disabled to make the loader fit
   348                                  * DR-DOS:
   349                                   * Must enable _MEMORY_CONTINUE to load off file systems with cluster
   350                                     sizes > 1 KiB (depending on file size) or >= 32 KiB (certainly)
   351                                   * Will not load whole file if _MEMORY_CONTINUE is enabled and file
   352                                     data exceeds 29 KiB, without erroring out. The new option called
   353                                     _LOAD_CHECK_MAX_SIZE has been added to address this. However, it
   354                                     won't fit without disabling some other options such as _LBA, _CHS,
   355                                     _USE_PART_INFO, or _QUERY_GEOMETRY.
   356                                   * Disables DPT, data start, and directory entry to 00500h options by
   357                                     default so loading a faithful IBMDOS v4+ kernel will fail as msload
   358                                     or lDOS iniload need these options to operate
   359                                  * IBMDOS:
   360                                  * MS-DOS 6:
   361                                   * Does not actually relocate DPT, just provide its address
   362                                   * A lot of options between _USE_PART_INFO, _QUERY_GEOMETRY, _CHS,
   363                                     and/or _LBA need to be disabled to make the loader fit
   364                                  * MS-DOS 7:
   365                                   * Does not actually relocate DPT, just provide its address
   366                                   * Does not contain message table used by loader
   367                                  
   368                                  %endif
   369                                  
   370                                  %if _SET_BL_UNIT && _SET_AXBX_DATA
   371                                   %error Cannot select both of these options!
   372                                  %endif
   373                                  
   374                                  %if _DIR_ENTRY_520
   375                                   %assign _DIR_ENTRY_500		1
   376                                  %endif
   377                                  
   378                                  %if _DIRBUF_500 && _ADD_SEARCH
   379                                   %error Cannot select both of these options!
   380                                  %endif
   381                                  
   382                                  %if _ADD_SEARCH
   383                                   %if _LOAD_DIR_SEG == 0 || _ADD_DIR_SEG == 0
   384                                    %error Assuming dir segs should be set if add search set
   385                                   %endif
   386                                  %endif
   387                                  
   388                                  %if _RPL
   389                                   %assign _RPL_GRACE_AREA 0
   390                                  %endif
   391                                  
   392                                  
   393                                  %assign LOADLIMIT 0A0000h
   394                                  %assign POSITION   07C00h
   395                                  
   396                                  %if _FIX_SECTOR_SIZE
   397                                   %assign i 5
   398                                   %rep 13-5
   399                                    %if (1 << i) != (_FIX_SECTOR_SIZE)
   400                                     %assign i i+1
   401                                    %endif
   402                                   %endrep
   403                                   %if (1 << i) != (_FIX_SECTOR_SIZE)
   404                                    %error Invalid sector size _FIX_SECTOR_SIZE
   405                                   %endif
   406                                  %endif
   407                                  
   408                                  %if _FIX_CLUSTER_SIZE
   409                                   %if _FIX_CLUSTER_SIZE > 256
   410                                    %error Invalid cluster size _FIX_CLUSTER_SIZE
   411                                   %endif
   412                                   %assign i 0
   413                                   %rep 8-0
   414                                    %if (1 << i) != (_FIX_CLUSTER_SIZE)
   415                                     %assign i i+1
   416                                    %endif
   417                                   %endrep
   418                                   %if (1 << i) != (_FIX_CLUSTER_SIZE)
   419                                    %warning Non-power-of-two cluster size _FIX_CLUSTER_SIZE
   420                                   %endif
   421                                  %endif
   422                                  
   423                                  
   424                                  ; Stack (default 512 Bytes minus the variables).
   425                                  ADR_STACK_LOW	equ	7C00h - _LOAD_STACK	; 07A00h (if _LOAD_STACK = 200h)
   426                                  
   427                                  %if _DIRBUF_500
   428                                  	gendef _ADR_DIRBUF,	500h
   429                                  %elif _RELOCATE
   430                                  	gendef _ADR_DIRBUF,	_LOAD_ADR
   431                                  %endif
   432                                  
   433                                  ; one-sector directory buffer. Assumes sectors are no larger than 8 KiB
   434                                  ADR_DIRBUF	equ	__ADR_DIRBUF
   435                                  
   436                                  %if ! _RELOCATE
   437                                  ; this used to be a two-sector FAT buffer -- two sectors because FAT12
   438                                  ;  entries are 12 bits and may straddle a sector boundary.
   439                                  ; however, with the FAT12 loaded completely, the buffer only needs to hold
   440                                  ;  one 8 KiB sector, two 4 KiB sectors, three 2 KiB sectors, six 1 KiB sectors,
   441                                  ;  or twelve 512 byte sectors.
   442                                  ; this shares its area with the directory buffer as they
   443                                  ;  are not simultaneously used. (if not _DIRBUF_500.)
   444                                  ADR_FATBUF	equ	__ADR_FATBUF
   445                                  %endif
   446                                  
   447                                  ; start of unused memory after loader:
   448                                  ADR_END		equ	end -start+7C00h
   449                                  %if ! _RELOCATE
   450                                   %if (ADR_FATBUF + 8192) > ADR_END
   451                                    ADR_FREE_FROM	equ	(ADR_FATBUF + 8192)	; 09E00h
   452                                   %else
   453                                    ADR_FREE_FROM	equ	ADR_END			; 07E00h
   454                                   %endif
   455                                  
   456                                  ; end of unused memory before loader:
   457                                   %if ADR_FATBUF < ADR_STACK_LOW
   458                                  ADR_FREE_UNTIL	equ	ADR_FATBUF
   459                                   %else
   460                                  ADR_FREE_UNTIL	equ	ADR_STACK_LOW
   461                                   %endif
   462                                  
   463                                   %if ((ADR_FATBUF + 8192 - 1) & ~0FFFFh) != (ADR_FATBUF & ~0FFFFh)
   464                                    %warning Possibly crossing 64 KiB boundary while reading FAT
   465                                   %endif
   466                                  %endif
   467                                  
   468                                  %if ((ADR_DIRBUF + 8192 - 1) & ~0FFFFh) != (ADR_DIRBUF & ~0FFFFh)
   469                                   %warning Possibly crossing 64 KiB boundary while reading directory
   470                                  %endif
   471                                  
   472                                  %if _RELOCATE
   473                                   ADR_FREE_FROM	equ 0				; make next conditional true
   474                                   ADR_FREE_UNTIL	equ 0
   475                                  %endif
   476                                  %if _LOAD_ADR >= ADR_FREE_FROM
   477                                  	; If reading on a sector size boundary, no crossing can occur.
   478                                  	;  Check for all possible sector sizes (32 B to 8 KiB). If one
   479                                  	;  of them fails display a warning, including the minimum size.
   480                                   %assign SECSIZECHECK 32
   481                                   %assign EXITREP 0
   482                                   %rep 256
   483                                    %ifn EXITREP
   484                                     %if _LOAD_ADR & (SECSIZECHECK - 1)
   485                                      %warning Possibly crossing 64 KiB boundary while reading file (sector size >= SECSIZECHECK)
   486                                      %assign EXITREP 1
   487                                      %exitrep
   488                                     %endif
   489                                     %if SECSIZECHECK == 8192
   490                                      %assign EXITREP 1
   491                                      %exitrep
   492                                     %endif
   493                                     %assign SECSIZECHECK SECSIZECHECK * 2
   494                                    %endif
   495                                   %endrep
   496                                  %else
   497                                  	; If loading below the boot sector, address 1_0000h is never reached.
   498                                  %endif
   499                                  
   500                                  
   501                                  %if (_LOAD_ADR & 0Fh)
   502                                   %error Load address must be on a paragraph boundary
   503                                  %endif
   504                                  
   505                                  %if _LOAD_ADR > LOADLIMIT
   506                                   %error Load address must be in LMA
   507                                  %elif _LOAD_ADR < 00500h
   508                                   %error Load address must not overlap IVT or BDA
   509                                  %endif
   510                                  
   511                                  %if ! _RELOCATE
   512                                   %if _LOAD_ADR > (POSITION-512) && _LOAD_ADR < (POSITION+512)
   513                                    %error Load address must not overlap loader
   514                                   %endif
   515                                  
   516                                   %if ADR_FATBUF > LOADLIMIT
   517                                    %error FAT buffer address must be in LMA
   518                                   %elif ADR_FATBUF < 00500h
   519                                    %error FAT buffer address must not overlap IVT or BDA
   520                                   %elif (ADR_FATBUF + 8192) > (POSITION-512) && ADR_FATBUF < (POSITION+512)
   521                                    %error FAT buffer address must not overlap loader
   522                                   %endif
   523                                  %endif
   524                                  
   525                                  %if ADR_DIRBUF > LOADLIMIT
   526                                   %error Dir buffer address must be in LMA
   527                                  %elif ADR_DIRBUF < 00500h
   528                                   %error Dir buffer address must not overlap IVT or BDA
   529                                  %elif (ADR_DIRBUF + 8192) > (POSITION-512) && ADR_DIRBUF < (POSITION+512)
   530                                   %error Dir buffer address must not overlap loader at initial position
   531                                  %endif
   532                                  
   533                                  %if ((_EXEC_SEG_ADJ<<4)+_EXEC_OFS) < 0
   534                                   %error Execution address must be in loaded file
   535                                  %elif ((_EXEC_SEG_ADJ<<4)+_EXEC_OFS+_LOAD_ADR) > LOADLIMIT
   536                                   %error Execution address must be in LMA
   537                                  %endif
   538                                  
   539                                  %if (_EXEC_OFS & ~0FFFFh)
   540                                   %error Execution offset must fit into 16 bits
   541                                  %endif
   542                                  
   543                                  %if (_EXEC_SEG_ADJ > 0FFFFh || _EXEC_SEG_ADJ < -0FFFFh)
   544                                   %error Execution segment adjustment must fit into 16 bits
   545                                  %endif
   546                                  
   547                                  
   548                                  %if !_CHS && _QUERY_GEOMETRY
   549                                   %warning No CHS support but querying geometry anyway
   550                                  %endif
   551                                  
   552                                  %if !_CHS && !_LBA
   553                                   %error Either CHS or LBA or both must be enabled
   554                                  %endif
   555                                  
   556                                  
   557                                  %if 0
   558                                  
   559                                  There is some logic inside MS-DOS's hard disk partition initialisation
   560                                  code that sets up several requirements for us to fulfil. Otherwise,
   561                                  it will not accept the information given in the BPB (using default
   562                                  information based on the length as specified by MBR/EPBR instead) or
   563                                  make the whole file system inaccessible except for formatting. Both of
   564                                  those are undesirable of course. Some/all(?) checks are documented on
   565                                  pages 601,602 in "DOS Internals", Geoff Chappell 1994, as follows:
   566                                  
   567                                  * First three bytes contain either "jmp sho xx\ nop" or "jmp ne xx".
   568                                  * Media descriptor field >= 0F0h.
   569                                  * Bytes per sector field == 512.
   570                                  * Sectors per cluster field a power of 2 (1,2,4,8,16,32,64,128).
   571                                  * OEM name "version" (last three to four characters)
   572                                   * must be "20.?", "10.?" (? = wildcard), but no other with "0.?",
   573                                   * otherwise, must be "2.0", or
   574                                    * 2nd-to-last,3rd-to-last character codes together > "3.", or
   575                                     * those == "3.", last character code > "0"
   576                                  
   577                                  To stay compatible to those, display a warning here if the name
   578                                  itself would disqualify our boot sector already.
   579                                  
   580                                  %endif
   581                                  
   582                                  %push
   583                                  %strlen %$len _OEM_NAME_FILL
   584                                  %if %$len != 1
   585                                   %error Specified OEM name fill must be 1 character
   586                                  %endif
   587                                  %strlen %$len _OEM_NAME
   588                                  %define %$nam _OEM_NAME
   589                                  %if %$len > 8
   590                                   %error Specified OEM name is too long
   591                                  %else
   592                                   %assign %$warn 0
   593                                   %rep 8 - %$len
   594                                    %strcat %$nam %$nam,_OEM_NAME_FILL
   595                                   %endrep
   596                                   %substr %$prefix %$nam	5	; "wvxyZa.b", get "Z"
   597                                   %substr %$major %$nam 6,7	; "wvxyzA.b", get "A."
   598                                   %substr %$minor %$nam 8	; "wvxyza.B", get "B"
   599                                   %if %$major == "0."
   600                                    %ifn %$prefix == "1" || %$prefix == "2"
   601                                     %assign %$warn 1
   602                                    %endif
   603                                   %elifn %$major == "2." && %$minor == "0"
   604                                    %if %$major < "3."
   605                                     %assign %$warn 1
   606                                    %elif %$major == "3." && %$minor < "1"
   607                                     %assign %$warn 1
   608                                    %endif
   609                                   %endif
   610                                   %if %$warn
   611                                    %warning Specified OEM name fails MS-DOS's validation
   612                                   %endif
   613                                  %endif
   614                                  %pop
   615                                  
   616                                  
   617                                  	struc DIRENTRY
   618 00000000 ????????????????        deName:		resb 8
   619 00000008 ??????                  deExt:		resb 3
   620 0000000B ??                      deAttrib:	resb 1
   621 0000000C ??                      dePlusSize:	resb 1
   622 0000000D ??????????????          		resb 7
   623 00000014 ????                    deClusterHigh:	resw 1
   624 00000016 ????                    deTime:		resw 1
   625 00000018 ????                    deDate:		resw 1
   626 0000001A ????                    deClusterLow:	resw 1
   627 0000001C ????????                deSize:		resd 1
   628                                  	endstruc
   629                                  
   630                                  ATTR_READONLY	equ 1
   631                                  ATTR_HIDDEN	equ 2
   632                                  ATTR_SYSTEM	equ 4
   633                                  ATTR_VOLLABEL	equ 8
   634                                  ATTR_DIRECTORY	equ 10h
   635                                  ATTR_ARCHIVE	equ 20h
   636                                  
   637                                  
   638                                  ; use byte-offset addressing from BP for smaller code
   639                                  %define	VAR(x)	((x) - start) + bp
   640                                  
   641                                  
   642                                  	cpu 8086
   643                                  ; bootsector loaded at address 07C00h, addressable using 0000h:7C00h
   644                                  	org POSITION
   645                                  start:
   646                                  
   647                                  
   648                                  %define _LASTVARIABLE start
   649                                  	%macro nextvariable 2-3.nolist
   650                                  %1	equ (_LASTVARIABLE - %2)
   651                                  %define _LASTVARIABLE %1
   652                                  %ifidn %3, relocatestart
   653                                   %define _RELOCATESTART %1
   654                                  %elifempty %3
   655                                  %else
   656                                   %error Invalid third parameter
   657                                  %endif
   658                                  	%endmacro
   659                                  
   660                                  ; Variables
   661                                  
   662                                  ; (dword) sector where the first cluster's data starts
   663                                  	nextvariable data_start, 4
   664                                  
   665                                  ; (word) current load segment (points behind last loaded data)
   666                                  	nextvariable load_seg, 2
   667                                  
   668                                  ; (word) segment of FAT buffer
   669                                  ; for FAT12 this holds the entire FAT
   670                                  ; for FAT16 this holds the sector given by wo[fat_sector]
   671                                  ; for FAT32 this holds the sector given by dwo[fat_sector]
   672                                  	nextvariable fat_seg, 2
   673                                  
   674                                  ; (word for FAT16) currently loaded sector-in-FAT, -1 if none
   675                                  	nextvariable fat_sector, 4
   676                                  
   677                                  ; (word for FAT12/FAT16) first cluster of load file
   678                                  	nextvariable first_cluster, 4, relocatestart
   679                                  
   680                                  ADR_STACK_START	equ	_LASTVARIABLE -start+POSITION
   681                                  
   682                                  %ifn _FIX_SECTOR_SIZE
   683                                  ; (word) number of 16-byte paragraphs per sector
   684                                  	nextvariable para_per_sector, 2, relocatestart
   685                                  %endif
   686                                  
   687                                  %assign DIRSEARCHSTACK_CL_FIRST 0
   688                                  %assign DIRSEARCHSTACK_CL_SECOND 0
   689                                  %assign PLACEHOLDER 0
   690                                  
   691                                   %if _ATTRIB_SAVE && ! (_ADD_SEARCH || _LOAD_DIR_SEG)
   692                                     %if _LASTVARIABLE == start - 12h
   693                                      %assign DIRSEARCHSTACK_CL_FIRST 1
   694                                     %elif _LASTVARIABLE == start - 10h
   695                                      %assign DIRSEARCHSTACK_CL_SECOND 1
   696                                     %endif
   697                                     %ifn _DIR_ENTRY_500
   698                                  ; three words left on the stack after directory search
   699                                  	nextvariable dirsearchstack, 6, relocatestart
   700                                     %else
   701                                  ; two words left on the stack after directory search
   702                                  	nextvariable dirsearchstack, 4, relocatestart
   703                                     %endif
   704                                   %elifn !_RELOCATE && _LOAD_ADR < ADR_FREE_UNTIL
   705                                    %if _LASTVARIABLE == start - 12h
   706                                  	nextvariable cmdline_signature_placeholder, 2, relocatestart
   707                                     %assign PLACEHOLDER 1
   708                                    %elif _LASTVARIABLE == start - 10h
   709                                     %if _PUSH_DPT
   710                                  	nextvariable cmdline_signature_placeholder, 4, relocatestart
   711                                      %assign PLACEHOLDER 2
   712                                  	; In this case, part of the original DPT pointer may
   713                                  	;  overlap the CL signature word. Therefore allocate
   714                                  	;  two placeholder words to insure no CL match.
   715                                     %else
   716                                  	; In this case the last_available_sector variable
   717                                  	;  will be at word [ss:bp - 12h] (or none) and the
   718                                  	;  stack pointer will be equal to bp - 12h (or - 10h)
   719                                  	;  at handover time. Thus no placeholder is needed.
   720                                     %endif
   721                                    %else
   722                                     %error Placeholder not placed
   723                                    %endif
   724                                  		; This stack slot is used to insure that
   725                                  		;  the "CL" signature is not present at this
   726                                  		;  location. If not relocate and load address
   727                                  		;  is below loader then the next variable
   728                                  		;  (last_available_sector) will always receive
   729                                  		;  a value < 7C0h so cannot hold "CL".
   730                                  		; If _ATTRIB_SAVE is in use and neither the
   731                                  		;  _ADD_SEARCH nor the _LOAD_DIR_SEG options
   732                                  		;  are set, the first word of dirsearchstack
   733                                  		;  will be at word [ss:bp - 14h].
   734                                   %endif
   735                                  
   736                                  %ifn ! _RELOCATE && _LOAD_ADR < ADR_FREE_UNTIL && _FIX_SECTOR_SIZE
   737                                  ; (word) segment of last available memory for sector
   738                                  	nextvariable last_available_sector, 2
   739                                  %else
   740                                    %if _LASTVARIABLE == start - 12h
   741                                  	nextvariable cmdline_signature_placeholder, 2, relocatestart
   742                                     %assign PLACEHOLDER 1
   743                                    %elif _LASTVARIABLE == start - 10h
   744                                     %if _PUSH_DPT
   745                                  	nextvariable cmdline_signature_placeholder, 4, relocatestart
   746                                      %assign PLACEHOLDER 2
   747                                     %endif
   748                                    %endif
   749                                  %endif
   750                                  
   751                                  lowest_variable		equ _LASTVARIABLE
   752                                  
   753                                  
   754 00000000 EB47                    	jmp strict short skip_bpb
   755                                  %if !_CHS && _LBA_SET_TYPE
   756                                  	db 0Eh		; LBA-enabled FAT16 FS partition type
   757                                  %else
   758 00000002 90                      	nop		; default: no LBA
   759                                  %endif
   760                                  
   761                                  
   762                                  ; BIOS Parameter Block (BPB)
   763                                  ;
   764                                  ; Installation will use the BPB already present in your file system.
   765                                  ; These values must be initialised when installing the loader.
   766                                  ;
   767                                  ; The values shown here work only with 1440 KiB disks (CHS=80:2:18)
   768                                  
   769                                  oem_id:			; offset 03h (03) - not used by this code
   770 00000003 202020206C444F53        	fill 8,_OEM_NAME_FILL,db _OEM_NAME
   771                                  bytes_per_sector:	; offset 0Bh (11) - refer to _FIX_SECTOR_SIZE !
   772 0000000B 0002                    	dw _BPS
   773                                  sectors_per_cluster:	; offset 0Dh (13) - refer to _FIX_CLUSTER_SIZE !
   774 0000000D 01                      	db _SPC & 255
   775                                  fat_start:
   776                                  num_reserved_sectors:	; offset 0Eh (14)
   777 0000000E 0100                    	dw _NUMRESERVED
   778                                  num_fats:		; offset 10h (16)
   779 00000010 02                      	db _NUMFATS
   780                                  num_root_dir_ents:	; offset 11h (17)
   781 00000011 E000                    	dw _NUMROOT
   782                                  total_sectors:		; offset 13h (19) - not used by this code
   783                                  %if _SPI < 1_0000h
   784 00000013 400B                    	dw _SPI
   785                                  %else
   786                                  	dw 0
   787                                  %endif
   788                                  media_id:		; offset 15h (21) - not used by this code
   789 00000015 F0                      	db _MEDIAID
   790                                  sectors_per_fat:	; offset 16h (22)
   791 00000016 0900                    	dw _SPF
   792                                  sectors_per_track:	; offset 18h (24)
   793 00000018 1200                    	dw _CHS_SECTORS
   794                                  heads:			; offset 1Ah (26)
   795 0000001A 0200                    	dw _CHS_HEADS
   796                                  hidden_sectors:		; offset 1Ch (28)
   797 0000001C 00000000                	dd _HIDDEN
   798                                  total_sectors_large:	; offset 20h (32) - not used by this code
   799                                  %if _SPI >= 1_0000h
   800                                  	dd _SPI
   801                                  %else
   802 00000020 00000000                	dd 0
   803                                  %endif
   804                                  
   805                                  ; Extended BPB
   806                                  
   807 00000024 00                      boot_unit:		db _UNIT
   808 00000025 00                      			db 0
   809 00000026 29                      ext_bpb_signature:	db 29h
   810 00000027 00000000                serial_number:		dd _VOLUMEID
   811 0000002B 6C444F5320<rep 7h>      volume_label:		fill 11,32,db _DEFAULT_LABEL
   812 00000036 464154313220-           filesystem_identifier:	fill 8,32,db "FAT1",'2'+4*!!_FAT16
   812 00000036 <rep 3h>           
   813                                  
   814                                  
   815                                  ; Initialised data
   816                                  
   817                                  load_name:
   818 0000003E 4C43444542554720        	fill 8,32,db _LOAD_NAME
   819 00000046 434F4D                  	fill 3,32,db _LOAD_EXT
   820                                  %if _ADD_SEARCH
   821                                  add_name:
   822                                  	fill 8,32,db _ADD_NAME
   823                                  	fill 3,32,db _ADD_EXT
   824                                  
   825                                  	; align 2
   826                                  	;  This happens to be aligned anyway. But even if
   827                                  	;  it didn't, we'd rather save that byte than use
   828                                  	;  it to align these fields. So comment this out.
   829                                  dirseg:
   830                                  	dw _ADD_DIR_SEG
   831                                  %endif
   832                                  
   833                                  
   834                                  	numdef TMPINC, 0
   835                                  
   836                                  %if _TMPINC
   837                                   [list -]
  1211                                  %endif
  1212                                  
  1213                                  %if _WARN_PART_SIZE
  1214                                   %assign num $ - start
  1215                                   %warning BPB + data size is num bytes
  1216                                  %endif
  1217                                  
  1218                                  
  1219                                  ; Code
  1220                                  
  1221                                  skip_bpb:
  1222 00000049 FA                      	cli
  1223 0000004A FC                      	cld
  1224 0000004B 31C9                    	 xor cx, cx
  1225 0000004D BD[0000]                	mov bp, start		; magic bytes - checked by instsect
  1226 00000050 8ED1                    	 mov ss, cx
  1227 00000052 BCF07B                  	mov sp, ADR_STACK_START
  1228                                  %if _USE_AUTO_UNIT
  1229 00000055 885624                  	mov [VAR(boot_unit)], dl; magic bytes - checked by instsect
  1230                                  %else
  1231                                  	mov dl, [VAR(boot_unit)]; magic bytes - checked by instsect
  1232                                  %endif
  1233                                  
  1234                                  	; Note:	es is left uninitialised here until the first call to
  1235                                  	;	 read_sector if the below conditional is false.
  1236                                  %if _USE_PART_INFO	; +19 bytes
  1237 00000058 8EC1                    	 mov es, cx
  1238                                  ; Note:	Award Medallion BIOS v6.0 (ASUS MED 2001 ACPI BIOS Revision 1009)
  1239                                  ;	 loads from a floppy disk drive with ds:si = 0F000h:0A92Dh ->
  1240                                  ;	 FF FF FF FF 08 00 08 01 FF FF FF FF FF FF FF FF, which was detected
  1241                                  ;	 as a valid partition table entry by this handling. Therefore, we
  1242                                  ;	 only accept partition information when booting from a hard disk now.
  1243                                  
  1244                                  		; start of magic byte sequence for instsect
  1245 0000005A 84D2                    	test dl, dl		; floppy ?
  1246 0000005C 7911                    	jns @F			; don't attempt detection -->
  1247                                  ; Check whether an MBR left us partition information.
  1248                                  ; byte[ds:si] bit 7 means active and must be set if valid.
  1249 0000005E 380C                    	cmp byte [si], cl	; flags for xx-00h (result is xx), SF = bit 7
  1250 00000060 790D                    	jns @F			; xx < 80h, ie info invalid -->
  1251                                  ; byte[ds:si+4] is the file system type. Check for valid one.
  1252 00000062 384C04                  	cmp byte [si+4], cl	; is it zero?
  1253 00000065 7408                    	je @F			; yes, info invalid -->
  1254                                  ; Info valid, trust their hidden sectors over hardcoded.
  1255                                  ; Assume the movsw instructions won't run with si = FFFFh.
  1256 00000067 BF[1C00]                	mov di, hidden_sectors	; -> BPB field
  1257 0000006A 83C608                  	add si, 8		; -> partition start sector in info
  1258                                   %if _USE_PART_INFO_DISABLED
  1259                                  	nop
  1260                                  	nop			; size has to match enabled code
  1261                                   %else
  1262 0000006D A5                      	movsw
  1263 0000006E A5                      	movsw			; overwrite BPB field with value from info
  1264                                   %endif
  1265                                  @@:
  1266                                  		; end of magic byte sequence for instsect
  1267                                  %endif
  1268 0000006F 8ED9                    	mov ds, cx
  1269 00000071 FB                      	sti
  1270                                  
  1271                                  
  1272                                  		; Note:	The int 13h function 08h call may change or
  1273                                  		;	 set ax, bx, cx, dx, es, di. es is left as
  1274                                  		;	 indeterminate until the first call to
  1275                                  		;	 read_sector to read the root directory.
  1276                                  %if _QUERY_GEOMETRY	; +27 bytes
  1277                                  
  1278                                  		; start of magic byte sequence for instsect
  1279                                  ;	test dl, dl		; floppy?
  1280                                  ;	jns @F			; don't attempt query, might fail -->
  1281                                  	; Note that while the original PC BIOS doesn't support this function
  1282                                  	;  (for its diskettes), it does properly return the error code 01h.
  1283                                  	; https://sites.google.com/site/pcdosretro/ibmpcbios (IBM PC version 1)
  1284 00000072 B408                    	mov ah, 08h
  1285                                  	; xor cx, cx		; initialise cl to 0
  1286                                  	; Already from prologue cx = 0.
  1287 00000074 F9                      	stc			; initialise to CY
  1288                                   %if _QUERY_GEOMETRY_DISABLED
  1289                                  	nop
  1290                                  	nop			; size has to match enabled code
  1291                                   %else
  1292 00000075 CD13                    	int 13h			; query drive geometry
  1293                                   %endif
  1294 00000077 720E                    	jc @F			; apparently failed -->
  1295 00000079 83E13F                  	and cx, 3Fh		; get sectors
  1296 0000007C 7409                    	jz @F			; invalid (S is 1-based), don't use -->
  1297 0000007E 894E18                  	mov [VAR(sectors_per_track)], cx
  1298 00000081 88F1                    	mov cl, dh		; cx = maximum head number
  1299 00000083 41                      	inc cx			; cx = number of heads (H is 0-based)
  1300 00000084 894E1A                  	mov [VAR(heads)], cx
  1301                                  @@:
  1302                                  		; end of magic byte sequence for instsect
  1303                                  %endif
  1304                                  
  1305                                  %if _FIX_SECTOR_SIZE
  1306                                   %if !_FIX_SECTOR_SIZE_SKIP_CHECK
  1307                                  	cmp word [VAR(bytes_per_sector)], _FIX_SECTOR_SIZE
  1308                                  	mov al, 'S'
  1309                                  	jne error
  1310                                   %endif
  1311                                  	mov bx, _FIX_SECTOR_SIZE >> 5
  1312                                   %if _FIX_CLUSTER_SIZE
  1313                                    %if !_FIX_CLUSTER_SIZE_SKIP_CHECK
  1314                                  	cmp byte [VAR(sectors_per_cluster)], _FIX_CLUSTER_SIZE & 0FFh
  1315                                  	mov al, 'C'
  1316                                  	jne error
  1317                                    %endif
  1318                                   %endif
  1319                                  	mov ch, 0			; ! ch = 0
  1320                                  %else
  1321                                  ; 16-byte paragraphs per sector
  1322 00000087 8B5E0B                  	mov bx,[VAR(bytes_per_sector)]
  1323 0000008A B90400                  	mov cx,4			; ! ch = 0
  1324 0000008D D3EB                    	shr bx,cl
  1325                                   %if _FIX_CLUSTER_SIZE
  1326                                    %if !_FIX_CLUSTER_SIZE_SKIP_CHECK
  1327                                  	cmp byte [VAR(sectors_per_cluster)], _FIX_CLUSTER_SIZE & 0FFh
  1328                                  	mov al, 'C'
  1329                                  	jne error
  1330                                    %endif
  1331                                   %else
  1332                                  				; ! ch = 0
  1333                                   %endif
  1334 0000008F 53                      	push bx				; push into word [VAR(para_per_sector)]
  1335                                  
  1336                                  ; 32-byte FAT directory entries per sector
  1337 00000090 D1EB                    	shr bx, 1			; /2 = 32-byte entries per sector
  1338                                  %endif
  1339                                  
  1340                                  %if _WARN_PART_SIZE
  1341                                   %assign num $ - skip_bpb
  1342                                   %warning init size is num bytes
  1343                                  %endif
  1344                                  
  1345                                  
  1346                                  dirsearch_start:
  1347                                  
  1348                                  ; number of sectors used for root directory (store in CX)
  1349 00000092 8B7611                  	mov si, [VAR(num_root_dir_ents)]
  1350 00000095 89D8                    	mov ax, bx
  1351                                  		; The ax value here is the last value of bx, which is set
  1352                                  		;  by the shr instruction. Therefore, it cannot be higher
  1353                                  		;  than 7FFFh, so this cwd instruction always zeros dx.
  1354 00000097 99                      	cwd
  1355 00000098 48                      	dec ax				; rounding up
  1356 00000099 01F0                    	add ax, si			; from BPB
  1357 0000009B 11D2                    	adc dx, dx			; account for overflow (dx was zero)
  1358 0000009D F7F3                    	div bx				; get number of root sectors
  1359 0000009F 91                      	xchg ax, cx			; cx = number of root secs, ! ah = 0
  1360                                  
  1361                                  ; first sector of root directory
  1362 000000A0 8A4610                  	mov al,[VAR(num_fats)]		; ! ah = 0, hence ax = number of FATs
  1363 000000A3 F76616                  	mul word [VAR(sectors_per_fat)]
  1364 000000A6 03460E                  	add ax,[VAR(num_reserved_sectors)]
  1365 000000A9 10F2                    	adc dl, dh			; account for overflow (dh was and is 0)
  1366                                  
  1367 000000AB 31FF                    	xor di, di
  1368                                  
  1369                                  ; first sector of disk data area:
  1370 000000AD 01C1                    	add cx, ax
  1371 000000AF 11D7                    	adc di, dx
  1372 000000B1 894EFC                  	mov [VAR(data_start)], cx
  1373 000000B4 897EFE                  	mov [VAR(data_start+2)], di
  1374                                  
  1375                                  next_dir_search:
  1376                                  %if _ADD_SEARCH
  1377                                  	push dx
  1378                                  	push ax
  1379                                  	push si
  1380                                  %endif
  1381                                  
  1382                                  ; Scan root directory for file. We don't bother to check for deleted
  1383                                  ;  entries (E5h) or entries that mark the end of the directory (00h).
  1384                                  		; number of root entries in si here
  1385                                  next_sect:
  1386 000000B7 89D9                    	mov cx, bx		; entries per sector as loop counter
  1387                                  %if ! _RELOCATE
  1388                                   %if ADR_DIRBUF == ADR_FATBUF
  1389 000000B9 E8AF00                  	call read_sector_dirbuf
  1390                                   %else
  1391                                  	mov bx, ADR_DIRBUF>>4
  1392                                  	call read_sector
  1393                                   %endif
  1394                                  %else
  1395                                  	mov bx, ADR_DIRBUF>>4
  1396                                  	call read_sector
  1397                                  %endif
  1398 000000BC 89CB                    	mov bx, cx		; restore bx for next iteration later
  1399                                  
  1400 000000BE 31FF                    	xor di, di		; es:di-> first entry in this sector
  1401                                  next_ent:
  1402                                   %if DIRSEARCHSTACK_CL_FIRST
  1403                                  	push cx			; first dirsearchstack word = entries-in-sector
  1404                                  	push si			; other: entries total
  1405                                   %else
  1406 000000C0 56                      	push si
  1407 000000C1 51                      	push cx			; second dirsearchstack word = entries-in-sector
  1408                                   %endif
  1409 000000C2 57                      	push di			; dirsearchstack
  1410                                  %if _CHECK_ATTRIB && ! _ATTRIB_SAVE
  1411                                  	test byte [es:di + deAttrib], ATTR_DIRECTORY | ATTR_VOLLABEL
  1412                                  	jnz @F			; directory, label, or LFN entry --> (NZ)
  1413                                  %endif
  1414                                  %if _ADD_SEARCH
  1415                                  	mov si, add_name
  1416                                  filename equ $ - 2		; SMC to update to load_name later
  1417                                  %else
  1418 000000C3 BE[3E00]                	mov si, load_name	; ds:si-> name to match
  1419                                  %endif
  1420 000000C6 B90B00                  	mov cx, 11		; length of padded 8.3 FAT filename
  1421 000000C9 F3A6                    	repe cmpsb		; check entry
  1422                                  %if _ATTRIB_SAVE
  1423                                   %if _CHECK_ATTRIB
  1424                                  	jnz @F
  1425                                  		; deAttrib == 11, right after the 11-byte name
  1426                                  	test byte [es:di], ATTR_DIRECTORY | ATTR_VOLLABEL
  1427                                  				; directory, label, or LFN entry ?
  1428                                   %endif
  1429                                  	jz found_it		; found entry -->
  1430                                  %endif
  1431                                  @@:
  1432 000000CB 5F                      	pop di
  1433                                   %if DIRSEARCHSTACK_CL_FIRST
  1434                                  	pop si
  1435                                  	pop cx			; pop from dirsearchstack
  1436                                   %else
  1437 000000CC 59                      	pop cx
  1438 000000CD 5E                      	pop si			; pop from dirsearchstack
  1439                                   %endif
  1440 000000CE 8D7D20                  	lea di, [di + DIRENTRY_size]
  1441                                  %if ! _ATTRIB_SAVE
  1442 000000D1 7409                    	jz found_it		; found entry -->
  1443                                  %endif
  1444                                  
  1445 000000D3 4E                      	dec si			; count down entire root's entries
  1446 000000D4 E0EA                    	loopnz next_ent		; count down sector's entries (jumps iff si >0 && cx >0)
  1447 000000D6 75DF                    	jnz next_sect		; (jumps iff si >0 && cx ==0)
  1448                                  				; ends up here iff si ==0
  1449                                  				;  ie all root entries checked unsuccessfully
  1450                                  %if 0
  1451                                  
  1452                                  qemu prior to 2020-08 has a bug which affects the above
  1453                                  conditionals. The bug is that if NZ is set (like when the
  1454                                  branch to the label found_it is not taken) and then another
  1455                                  instruction sets ZR (like the dec si at the end of the root
  1456                                  directory) and then loopnz is used which sets cx to zero
  1457                                  then after the loopnz FL will be NZ leading to the jnz branch
  1458                                  to be taken. Eventually the entire load unit is traversed and
  1459                                  qemu returns error 01h when trying to read past the end of
  1460                                  the unit (at least for 1440 KiB diskettes).
  1461                                  
  1462                                  The bug can be worked around in two ways as done by lDebug:
  1463                                  
  1464                                  https://hg.pushbx.org/ecm/ldebug/rev/c95e2955bbca
  1465                                  
  1466                                  https://hg.pushbx.org/ecm/ldebug/rev/c84047f15d9c
  1467                                  
  1468                                  However, both cost a few bytes each. Therefore the proper
  1469                                  fix is considered to be updating qemu. Error behaviour occurs
  1470                                  when a file is not found. In an unlikely case, another sector
  1471                                  in the data area may hold a match for the searched entry.
  1472                                  Otherwise, the read eventually fails and the loader aborts
  1473                                  with an R error (instead of the expected F error).
  1474                                  
  1475                                  Reference: https://bugs.launchpad.net/qemu/+bug/1888165
  1476                                  
  1477                                  %endif
  1478                                  
  1479 000000D8 B046                    	mov al,'F'	; File not 'F'ound
  1480 000000DA EB7B                    	jmp error
  1481                                  
  1482                                  found_it:
  1483                                  %if _ADD_SEARCH || _LOAD_DIR_SEG
  1484                                   %if _ATTRIB_SAVE
  1485                                  	pop di			; es:di -> dir entry (pop from dirsearchstack)
  1486                                   %endif
  1487                                  	mov cx, 32
  1488                                  	mov ax, _LOAD_DIR_SEG
  1489                                   %if ! _ATTRIB_SAVE
  1490                                  	sub di, cx		; es:di -> dir entry
  1491                                   %endif
  1492                                   %if _ADD_SEARCH
  1493                                  	xchg ax, word [VAR(dirseg)]
  1494                                   %endif
  1495                                  	push ds
  1496                                  	mov si, di
  1497                                  	push di
  1498                                  	 push es
  1499                                  	 pop ds			; ds:si -> dir entry
  1500                                  	mov es, ax
  1501                                  	xor di, di		; es:di -> destination
  1502                                  	rep movsb		; store dir entry
  1503                                  	 push ds
  1504                                  	 pop es
  1505                                  	pop di			; restore es:di -> dir entry
  1506                                  	pop ds
  1507                                   %if _ADD_SEARCH
  1508                                  %if ((load_name - start) & 0FF00h) == ((add_name - start) & 0FF00h)
  1509                                  	mov byte [VAR(filename)], (load_name - start + 7C00h) & 255
  1510                                  %else
  1511                                  	mov word [VAR(filename)], load_name
  1512                                  %endif
  1513                                  				; update name to second iteration's
  1514                                  %if (_LOAD_DIR_SEG & 255) != (_ADD_DIR_SEG & 255)
  1515                                  	cmp al, _ADD_DIR_SEG & 255
  1516                                  %elif (_LOAD_DIR_SEG) != (_ADD_DIR_SEG)
  1517                                  	cmp ax, _ADD_DIR_SEG	; was first iteration ?
  1518                                  %else
  1519                                   %error Must not store directory entries to same segment
  1520                                  %endif
  1521                                   %if _ATTRIB_SAVE
  1522                                  	pop si			; discard cx/si
  1523                                  	pop si			; discard si/cx (dirsearchstack)
  1524                                   %endif
  1525                                  	pop si
  1526                                  	pop ax
  1527                                  	pop dx			; restore root start and count
  1528                                  				;  (bx still holds entries per sector)
  1529                                  	je next_dir_search	; jump to search load file next -->
  1530                                   %endif
  1531                                  	times PLACEHOLDER push bx
  1532                                  			; push into cmdline_signature_placeholder
  1533                                   %if _RELOCATE
  1534                                  	push word [es:di + deClusterLow]
  1535                                  			; (word on stack) = first cluster number
  1536                                   %endif
  1537                                  %else
  1538                                   %if _DIR_ENTRY_500	; +24 bytes, probably
  1539                                  	mov cx, 32
  1540                                   %if _ATTRIB_SAVE
  1541                                  	pop si		; es:si -> dir entry (pop from dirsearchstack)
  1542                                   %else
  1543                                  	xchg si, di
  1544                                  	sub si, cx
  1545                                   %endif
  1546                                  	 push ds
  1547                                  	 push es
  1548                                  	push es
  1549                                  	pop ds		; ds:si -> directory entry
  1550                                  	xor ax, ax
  1551                                  	mov es, ax
  1552                                  	mov di, 500h	; es:di -> 0:500h
  1553                                    %if _DIR_ENTRY_520
  1554                                  	rep movsw	; move to here (two directory entries)
  1555                                    %else
  1556                                  	rep movsb	; move to here
  1557                                    %endif
  1558                                  	 pop es
  1559                                  	 pop ds
  1560                                  	xchg si, di	; es:di -> behind (second) directory entry
  1561                                   %endif
  1562                                  	times PLACEHOLDER push bx
  1563                                  			; push into cmdline_signature_placeholder
  1564                                  		; Push the entries per sector value into this
  1565                                  		;  stack slot to ensure that it does not hold "CL".
  1566                                   %if _RELOCATE
  1567                                    %if _DIR_ENTRY_500 || !_ATTRIB_SAVE
  1568                                  	push word [es:di + deClusterLow - DIRENTRY_size 		- (DIRENTRY_size * !!_DIR_ENTRY_520)]
  1570                                  			; (word on stack) = first cluster number
  1571                                    %else
  1572                                  	push word [es:di + deClusterLow - (deName + 11)]
  1573                                  			; (word on stack) = first cluster number
  1574                                    %endif
  1575                                   %endif
  1576                                  %endif
  1577                                  
  1578                                  %if _WARN_PART_SIZE
  1579                                   %assign num $ - dirsearch_start
  1580                                   %warning dirsearch size is num bytes
  1581                                  %endif
  1582                                  
  1583                                  
  1584                                  %if _RELOCATE || _LOAD_ADR >= ADR_FREE_FROM
  1585                                  memory_start:
  1586                                  ; Get conventional memory size and store it
  1587                                  		int 12h
  1588                                  		mov cl, 6
  1589                                  		shl ax, cl
  1590                                   %if _RPL		; +31 bytes
  1591                                  	xchg dx, ax
  1592                                  	lds si, [4 * 2Fh]
  1593                                  	add si, 3
  1594                                  	lodsb
  1595                                  	cmp al, 'R'
  1596                                  	jne .no_rpl
  1597                                  	lodsb
  1598                                  	cmp al, 'P'
  1599                                  	jne .no_rpl
  1600                                  	lodsb
  1601                                  	cmp al, 'L'
  1602                                  	jne .no_rpl
  1603                                  	mov ax, 4A06h
  1604                                  	int 2Fh
  1605                                  .no_rpl:
  1606                                  	push ss
  1607                                  	pop ds
  1608                                  	xchg ax, dx
  1609                                   %endif
  1610                                   %if _RELOCATE
  1611                                    %if _LOAD_NON_FAT
  1612                                  	sub ax, (_RPL_GRACE_AREA + 20 * 1024 		+ 512 + 7C00h) >> 4
  1614                                    %else
  1615                                  	sub ax, (_RPL_GRACE_AREA + 20 * 1024 		+ 8192 + (8192-16) + 512 + 7C00h) >> 4
  1617                                    %endif
  1618                                  		; RPL grace	area preserved for RPL
  1619                                  		; 20 KiB:	reserved for iniload
  1620                                  		; 8 KiB:	FAT buffer
  1621                                  		; 8 KiB - 16 B:	to allow rounding down FAT buffer position
  1622                                  		; 512:		sector
  1623                                  		; 7C00h:	stack and to allow addressing with 7C00h in bp
  1624                                  		;
  1625                                  		; Note also that by addressing the stack and sector
  1626                                  		;  with bp at 7C00h, and insuring this subtraction doesn't
  1627                                  		;  underflow, makes sure that we do not overwrite the IVT or
  1628                                  		;  BDA. (However, we assume that ax is at least 60h or so.)
  1629                                  		;
  1630                                  		; The FAT buffer segment is masked so that the actual buffer
  1631                                  		;  is stored on an 8 KiB boundary. This is to ensure that
  1632                                  		;  the buffer doesn't possibly cross a 64 KiB DMA boundary.
  1633                                  	jc .error_memory_j_CY
  1634                                  	cmp ax, (end -start+7C00h - ADR_STACK_LOW + 15) >> 4
  1635                                  		; This check is to ensure that the start of the destination
  1636                                  		;  for the relocation (stack, sector) is
  1637                                  		;  above-or-equal the end of the source for the relocation.
  1638                                  		;  That is, to avoid overwriting any of the source with the
  1639                                  		;  string move instruction (which for simplicity is always UP).
  1640                                  .error_memory_j_CY:
  1641                                  	jb error_memory
  1642                                    %if ! _LOAD_NON_FAT
  1643                                  	mov bx, ((8192 - 16) + 512 + 7C00h)>>4
  1644                                  	add bx, ax
  1645                                  ;  this is like calculating the following for the bx value:
  1646                                  ;  ((LMA_top - 20 KiB - 8 KiB - (8 KiB - 16 B) - 512 - 7C00h) + ;	((8 KiB - 16 B) + 512 + 7C00h))>>4
  1648                                  ;  == (LMA_top - 20 KiB - 8 KiB)>>4
  1649                                  	and bx, ~ ((8192>>4) - 1)
  1650                                  	mov word [VAR(fat_seg)], bx
  1651                                    %endif
  1652                                  
  1653                                  	mov di, relocated
  1654                                  	push ax
  1655                                  	push di				; -> relocation target label relocated
  1656                                  		; (We cannot use a near call here to push the target IP
  1657                                  		;  because we do not control whether the ROM-BIOS loader
  1658                                  		;  entered us at 0:7C00h or 7C0h:0. So we need to create
  1659                                  		;  the correct offset manually here.)
  1660                                  
  1661                                  	mov es, ax			; => destination
  1662                                  	mov si, sp			; ds:si = ss:_RELOCATESTART - 2 - 4
  1663                                  	mov di, si			; es:di -> destination for stack low
  1664                                  	mov cx, (end - (_RELOCATESTART - 2 - 4)) >> 1
  1665                                  		; end is the top of used memory
  1666                                  		; _RELOCATESTART is the lowest filled stack frame slot
  1667                                  		; 2 is for the first cluster word on the stack
  1668                                  		; 4 is for the additional slots taken by the return address
  1669                                  	rep movsw			; relocate stack, sector
  1670                                  	retf				; jump to relocated code
  1671                                  
  1672                                  %if _WARN_PART_SIZE
  1673                                   %assign num $ - memory_start
  1674                                   %warning memory size is num bytes
  1675                                  %endif
  1676                                  
  1677                                  
  1678                                  	readhandler
  1679                                  
  1680                                  	errorhandler
  1681                                  
  1682                                  
  1683                                  relocated:
  1684                                  	mov ss, ax
  1685                                  	mov ds, ax			; relocate these
  1686                                  	add ax, (ADR_STACK_LOW) >> 4	; (rounding down) => behind available
  1687                                  
  1688                                  	pop si
  1689                                   %else
  1690                                  	sub ax, (_RPL_GRACE_AREA + 20 * 1024) >> 4
  1691                                  					; RPL grace area, plus
  1692                                  					;  20 KiB reserved for iniload
  1693                                  	jb error_memory
  1694                                   %endif
  1695                                  %elif _LOAD_ADR < ADR_FREE_UNTIL
  1696                                   %if !_FIX_SECTOR_SIZE
  1697 000000DC B8A007                  	mov ax, ADR_FREE_UNTIL >> 4	; rounding *down*
  1698                                   %endif
  1699                                  %else
  1700                                   %error Load address within used memory
  1701                                  %endif
  1702                                  %if ! _RELOCATE && _LOAD_ADR < ADR_FREE_UNTIL && _FIX_SECTOR_SIZE
  1703                                  	; user of last_available_sector will hardcode the value!
  1704                                  %else
  1705                                   %if _FIX_SECTOR_SIZE
  1706                                  		sub ax, _FIX_SECTOR_SIZE >> 4
  1707                                   %else
  1708                                    %if _LOAD_CHECK_MAX_SIZE
  1709                                     %if _RELOCATE
  1710                                      %error Combination not implemented yet
  1711                                     %else
  1712                                  		mov si, [VAR(para_per_sector)]
  1713                                  		sub ax, si
  1714                                     %endif
  1715                                    %else
  1716 000000DF 2B46EE                  		sub ax, [VAR(para_per_sector)]
  1717                                    %endif
  1718                                   %endif
  1719 000000E2 50                      		push ax		; push into word [VAR(last_available_sector)]
  1720                                  %endif
  1721                                  %if _LOAD_CHECK_MAX_SIZE
  1722                                   %if !_RELOCATE && _LOAD_ADR < ADR_FREE_UNTIL && _FIX_SECTOR_SIZE
  1723                                     %if _LOAD_CHECK_MAX_SIZE_FATPLUS
  1724                                  					; check FAT+ size bits
  1725                                  		test byte [es:di + dePlusSize + DIRADJUST], 0E7h
  1726                                  					; test whether bits 7-5 and 2-0 NZ
  1727                                  		jnz error_memory
  1728                                     %endif
  1729                                  		cmp word [es:di + deSize + 2 + DIRADJUST], 0
  1730                                  		jne error_memory
  1731                                  		cmp word [es:di + deSize + DIRADJUST],  		 (((ADR_FREE_UNTIL >> 4) - (_LOAD_ADR >> 4)) 		  & ~ ((_FIX_SECTOR_SIZE >> 4) - 1)) << 4
  1734                                  		ja error_memory
  1735                                   %else
  1736                                     %if !_RELOCATE && _LOAD_ADR < ADR_FREE_UNTIL
  1737                                  		mov ax, (ADR_FREE_UNTIL >> 4) - (_LOAD_ADR >> 4)
  1738                                     %else
  1739                                  		add ax, si
  1740                                  		sub ax, _LOAD_ADR >> 4
  1741                                     %endif
  1742                                  				; ax = how many paragraphs available
  1743                                  		dec si		; para per sector minus 1
  1744                                  		not si		; mask to round down to sector
  1745                                  		and ax, si	; how many paragraphs can be read
  1746                                  		mov dx, word [es:di + deSize + DIRADJUST]
  1747                                     %if _LOAD_CHECK_MAX_SIZE_FATPLUS
  1748                                  					; check FAT+ size bits
  1749                                  		test byte [es:di + dePlusSize + DIRADJUST], 0E7h
  1750                                  					; test whether bits 7-5 and 2-0 NZ
  1751                                  		jnz error_memory
  1752                                     %endif
  1753                                     %if !_RELOCATE && _LOAD_ADR < ADR_FREE_UNTIL
  1754                                  		cmp word [es:di + deSize + 2 + DIRADJUST], 0
  1755                                  		jne error_memory
  1756                                  		add dx, 15
  1757                                  		jc error_memory_CY
  1758                                  		mov cl, 4
  1759                                  		shr dx, cl
  1760                                     %else
  1761                                  		mov si, word [es:di + deSize + 2 + DIRADJUST]
  1762                                  		add dx, 15
  1763                                  		adc si, 0
  1764                                  		jc error_memory_CY
  1765                                  		mov cx, 4
  1766                                  @@:
  1767                                  		shr si, 1
  1768                                  		rcr dx, 1
  1769                                  		loop @B
  1770                                  		test si, si
  1771                                  		jnz error_memory
  1772                                     %endif
  1773                                  		cmp ax, dx
  1774                                  error_memory_CY:
  1775                                  		jb error_memory
  1776                                   %endif
  1777                                  %endif
  1778                                  
  1779                                  read_fat_start:
  1780                                  ; get starting cluster of file
  1781                                  %if ! _RELOCATE
  1782                                   %if _ADD_SEARCH || _LOAD_DIR_SEG
  1783                                  DIRADJUST equ 0
  1784                                   %elif _ATTRIB_SAVE && ! _DIR_ENTRY_500
  1785                                  DIRADJUST equ - deAttrib
  1786                                   %else
  1787                                  DIRADJUST equ - DIRENTRY_size - (DIRENTRY_size * !!_DIR_ENTRY_520)
  1788                                   %endif
  1789 000000E3 268B75FA                		mov si,[es:di + deClusterLow + DIRADJUST]
  1790                                  %elif _LOAD_CHECK_MAX_SIZE
  1791                                  DIRADJUST equ 0				; placeholder to avoid confusing error
  1792                                   %error Combination not implemented yet
  1793                                  %endif
  1794                                  %if _SET_CLUSTER
  1795 000000E7 8976F0                  		mov word [VAR(first_cluster)], si
  1796                                  %endif
  1797                                  %if _SET_DI_CLUSTER
  1798                                  		push si			; remember cluster for later
  1799                                  %endif
  1800                                  
  1801                                  %if !_FAT16 && !_LOAD_NON_FAT
  1802                                  ; Load the entire FAT into memory. This is easily feasible for FAT12,
  1803                                  ;  as the FAT can only contain at most 4096 entries.
  1804                                  ; (The exact condition should be "at most 4087 entries", or with a
  1805                                  ;  specific FF7h semantic, "at most 4088 entries"; the more reliable
  1806                                  ;  and portable alternative would be "at most 4080 entries".)
  1807                                  ; Thus, no more than 6 KiB need to be read, even though the FAT size
  1808                                  ;  as indicated by word[sectors_per_fat] could be much higher. The
  1809                                  ;  first loop condition below is to correctly handle the latter case.
  1810                                  ; (Sector size is assumed to be a power of two between 32 and 8192
  1811                                  ;  bytes, inclusive. An 8 KiB buffer is necessary if the sector size
  1812                                  ;  is 4 or 8 KiB, because reading the FAT can or will write to 8 KiB
  1813                                  ;  of memory instead of only the relevant 6 KiB. This is always true
  1814                                  ;  if the sector size is 8 KiB, and with 4 KiB sector size it is true
  1815                                  ;  iff word[sectors_per_fat] is higher than one.)
  1816 000000EA BF0018                  		mov di, 6 << 10		; maximum size of FAT12 to load
  1817 000000ED 8B4E16                  		mov cx, [VAR(sectors_per_fat)]
  1818                                  					; maximum size of this FS's FAT
  1819                                  %if ! _RELOCATE && _LOAD_ADR < ADR_FREE_UNTIL && !_FIX_SECTOR_SIZE
  1820                                  			; Under these conditions, ax here is
  1821                                  			;  below ADR_FREE_UNTIL >> 4 so the
  1822                                  			;  following cwd instruction zeros dx.
  1823 000000F0 99                      		cwd
  1824                                  %else
  1825                                  		xor dx, dx
  1826                                  %endif
  1827 000000F1 8B460E                  		mov ax, [VAR(fat_start)]; = first FAT sector
  1828                                   %if _RELOCATE
  1829                                  			; bx already = FAT buffer segment here
  1830                                   %else
  1831 000000F4 BBE007                  		mov bx, ADR_FATBUF>>4
  1832                                    %if _SET_FAT_SEG
  1833 000000F7 895EF8                  		mov word [VAR(fat_seg)], bx
  1834                                    %endif
  1835                                   %endif
  1836                                  @@:
  1837 000000FA E87100                  		call read_sector	; read next FAT sector
  1838                                  %if _FIX_SECTOR_SIZE
  1839                                  		sub di, _FIX_SECTOR_SIZE
  1840                                  %else
  1841 000000FD 2B7E0B                  		sub di, [VAR(bytes_per_sector)]
  1842                                  %endif
  1843                                  					; di = bytes still left to read
  1844 00000100 7602                    		jbe @F			; if none -->
  1845                                  					; (jbe means jump if CF || ZF)
  1846 00000102 E2F6                    		loop @B			; if any FAT sector still remains -->
  1847                                  @@:					; one of the limits reached; FAT read
  1848                                  %endif
  1849                                  
  1850 00000104 BB0002                  		mov bx, _LOAD_ADR>>4	; => load address
  1851                                  %if _FAT16 && !_LOAD_NON_FAT
  1852                                  		mov di, -1		; = no FAT sector read yet
  1853                                   %if _SET_FAT_SEG && _SET_FAT_SEG_NORMAL
  1854                                  	; This is not strictly needed because a FAT sector is
  1855                                  	;  read in any case, initialising this variable later.
  1856                                  		mov word [VAR(fat_sector)], di
  1857                                   %endif
  1858                                  %endif
  1859                                  %if _LOAD_NON_FAT
  1860                                   %if _SET_FAT_SEG
  1861                                    %if _FAT16
  1862                                  		or word [VAR(fat_sector)], -1
  1863                                    %else
  1864                                  		and word [VAR(fat_seg)], 0
  1865                                    %endif
  1866                                   %endif
  1867                                  %endif
  1868                                  
  1869                                  next_cluster:
  1870                                  ; convert 16-bit cluster value (in SI) to 32-bit LBA sector value (in DX:AX)
  1871                                  ; and get next cluster in SI
  1872                                  
  1873                                  
  1874                                  		; Converts cluster number to sector number
  1875                                  		;  and finds next cluster in chain
  1876                                  		;
  1877                                  		; INP:	si = valid cluster number
  1878                                  		;	(!_FAT16) [ADR_FATBUF] = entire FAT as read from FS
  1879                                  		;	(_FAT16) di = currently buffered FAT sector, -1 if none
  1880                                  		; OUT:	If unable to read a FAT sector,
  1881                                  		;	 ! jumps to error instead of returning
  1882                                  		;	If everything is okay,
  1883                                  		;	 si = next cluster number (or EOC value)
  1884                                  		;	 dx:ax = sector number
  1885                                  		;	 (_FAT16) di = currently buffered FAT sector
  1886                                  		; CHG:	cx
  1887                                  
  1888                                  %if ! _LOAD_NON_FAT
  1889                                  ; prepare to load entry from FAT
  1890                                  
  1891                                  %if _FAT16
  1892                                  		push si			; preserve cluster number for later
  1893                                  
  1894                                  ; Multiply cluster number by 2.
  1895                                  		xchg ax, si
  1896                                  		; xor dx, dx		; dx:ax = entry to load (0..FFF6h)
  1897                                  		; add ax, ax
  1898                                  		; adc dx, dx		; dx:ax = byte offset into FAT (0..131052)
  1899                                  		cwd			; dx = FFFFh if ax >= 8000h, else = 0
  1900                                  		add ax, ax		; ax = (2 * ax) & FFFFh
  1901                                  		neg dx			; dx = 1 if 2 * ax overflowed, else = 0
  1902                                  
  1903                                  ; Divide by sector size to split dx:ax into the remainder as the byte offset
  1904                                  ;  into the FAT sector, and quotient as the sector offset into the FAT.
  1905                                  		div word [VAR(bytes_per_sector)]
  1906                                  		mov si, dx		; = byte offset in sector
  1907                                  			; dx = byte offset into sector (0..8190)
  1908                                  			; ax = sector offset into FAT (0..4095)
  1909                                  
  1910                                  ; quotient in AX is FAT sector.
  1911                                  ; check the FAT buffer to see if this sector is already loaded
  1912                                  ; (simple disk cache; speeds things up a little --
  1913                                  ; actually, it speeds things up a lot)
  1914                                  		cmp ax, di
  1915                                  		je @F
  1916                                  		mov di, ax
  1917                                   %if _SET_FAT_SEG
  1918                                  		mov word [VAR(fat_sector)], ax
  1919                                   %endif
  1920                                  
  1921                                  ; read the target FAT sector.
  1922                                  		push bx
  1923                                  			; As noted above, the sector offset in ax here is
  1924                                  			;  0..4095 (ie, 4 Ki S * 32 B/S = 128 KiB of FAT data),
  1925                                  			;  therefore this cwd instruction always zeros dx.
  1926                                  		cwd
  1927                                  		add ax, [VAR(fat_start)]
  1928                                  		adc dx, dx		; (account for overflow)
  1929                                   %if _RELOCATE
  1930                                  		mov bx, word [VAR(fat_seg)]
  1931                                  		call read_sector
  1932                                   %else
  1933                                  		call read_sector_fatbuf
  1934                                   %endif
  1935                                  		pop bx
  1936                                  @@:
  1937                                  
  1938                                  ; get 16 bits from FAT
  1939                                  		es lodsw
  1940                                  		xchg ax, si
  1941                                  		pop ax		; restore cluster number
  1942                                  %else
  1943                                  ; FAT12 entries are 12 bits, bytes are 8 bits. Ratio is 3 / 2,
  1944                                  ;  so multiply cluster number by 3 first, then divide by 2.
  1945 00000107 89F0                    		mov ax, si		; = cluster number (up to 12 bits set)
  1946                                  			; (remember cluster number in ax)
  1947 00000109 D1E6                    		shl si, 1		; = 2n (up to 13 bits set)
  1948 0000010B 01C6                    		add si, ax		; = 2n+n = 3n (up to 14 bits set)
  1949 0000010D D1EE                    		shr si, 1		; si = byte offset into FAT (0..6129)
  1950                                  					; CF = whether to use high 12 bits
  1951                                   %if _RELOCATE
  1952                                  		mov es, word [VAR(fat_seg)]
  1953                                  		; es lodsw
  1954                                  		; xchg ax, si
  1955                                  		mov si, word [es:si]
  1956                                   %else
  1957                                  ; Use the calculated byte offset as an offset into the FAT
  1958                                  ;  buffer, which holds all of the FAT's relevant data.
  1959                                  		; lea si, [ADR_FATBUF + si]
  1960                                  				; -> 16-bit word in FAT to load
  1961                                  ; get 16 bits from FAT
  1962                                  		; lodsw
  1963                                  		; xchg ax, si
  1964 0000010F 8BB4007E                		mov si, [ADR_FATBUF + si]
  1965                                  				; = 16-bit word in FAT to load
  1966                                   %endif
  1967                                  
  1968 00000113 B104                    		mov cl, 4
  1969 00000115 7202                    		jc @F		; iff CY after shift -->
  1970 00000117 D3E6                    		shl si, cl	; shift up iff even entry
  1971                                  @@:
  1972 00000119 D3EE                    		shr si, cl	; shift down (clears top 4 bits)
  1973                                  
  1974                                  			; (ax holds cluster number)
  1975                                  %endif
  1976                                  
  1977                                  %else ; _LOAD_NON_FAT
  1978                                  		mov di, _LOAD_NON_FAT
  1979                                  	mov al, [VAR(sectors_per_cluster)]
  1980                                  	dec ax
  1981                                  	mov ah, 0
  1982                                  	inc ax
  1983                                  		mov cx, ax
  1984                                  		mul word [VAR(bytes_per_sector)]
  1985                                  		test dx, dx
  1986                                  		jnz @F
  1987                                  		cmp ax, di
  1988                                  		mov al, 'N'
  1989                                  		jb error
  1990                                  @@:
  1991                                  		xchg ax, si
  1992                                  %endif
  1993                                  ; adjust cluster number to make it 0-based
  1994 0000011B 48                      		dec ax
  1995 0000011C 48                      		dec ax
  1996                                  
  1997                                  %if ! _LOAD_NON_FAT
  1998                                  %if _FIX_CLUSTER_SIZE
  1999                                  		mov cx, _FIX_CLUSTER_SIZE
  2000                                  %else
  2001                                  ; adjusted sectors per cluster
  2002                                  ; decode EDR-DOS's special value 0 meaning 256
  2003 0000011D 8A4E0D                  	mov cl, [VAR(sectors_per_cluster)]
  2004 00000120 49                      	dec cx
  2005 00000121 B500                    	mov ch, 0
  2006 00000123 41                      	inc cx
  2007                                  %endif
  2008                                  %endif
  2009                                  
  2010                                  ; convert from clusters to sectors
  2011 00000124 F7E1                    		mul cx
  2012 00000126 0346FC                  		add ax, [VAR(data_start)]
  2013 00000129 1356FE                  		adc dx, [VAR(data_start)+2]
  2014                                  				; dx:ax = sector number
  2015                                  
  2016                                  ; xxx - this will always load an entire cluster (e.g. 64 sectors),
  2017                                  ; even if the file is shorter than this
  2018                                  @@:
  2019                                   %if ! _RELOCATE && _LOAD_ADR < ADR_FREE_UNTIL && _FIX_SECTOR_SIZE
  2020                                  		cmp bx, (ADR_FREE_UNTIL >> 4) - (_FIX_SECTOR_SIZE >> 4)
  2021                                   %else
  2022 0000012C 3B5EEC                  		cmp bx, [VAR(last_available_sector)]
  2023                                   %endif
  2024                                  %if _MEMORY_CONTINUE
  2025 0000012F 770E                    		ja @F
  2026                                  %else
  2027                                  		ja error_filetoobig
  2028                                  %endif
  2029                                  %if _FAT16 && ! _LOAD_NON_FAT
  2030                                  		push es		; (must preserve ADR_FATBUF reference)
  2031                                  %endif
  2032 00000131 E83A00                  		call read_sector
  2033                                  %if _FAT16 && ! _LOAD_NON_FAT
  2034                                  		pop es
  2035                                  %endif
  2036                                  %if _SET_LOAD_SEG
  2037 00000134 895EFA                  		mov [VAR(load_seg)], bx	; => after last read data
  2038                                  %endif
  2039                                  %if _LOAD_NON_FAT
  2040                                  		sub di, word [VAR(bytes_per_sector)]
  2041                                  		ja @B
  2042                                  %else
  2043 00000137 E2F3                    		loop @B
  2044                                  
  2045                                  %if _FAT16
  2046                                  ; FFF7h: bad cluster
  2047                                  ; FFF8h-FFFFh: end of cluster chain
  2048                                  		cmp si, 0FFF7h
  2049                                  %else
  2050                                  ; 0FF7h: bad cluster
  2051                                  ; 0FF8h-0FFFh: end of cluster chain
  2052 00000139 81FEF70F                		cmp si, 0FF7h
  2053                                  %endif
  2054 0000013D 72C8                    		jb next_cluster
  2055                                  %endif	; _LOAD_NON_FAT
  2056                                  @@:
  2057                                  
  2058                                  %if _WARN_PART_SIZE
  2059                                   %assign num $ - read_fat_start
  2060                                   %warning read_fat size is num bytes
  2061                                  %endif
  2062                                  
  2063                                  
  2064                                  finish_start:
  2065                                  %if _LOAD_MIN_PARA
  2066                                   %if ((_LOAD_ADR >> 4) + _LOAD_MIN_PARA) & 255 == 0
  2067                                  	; If the value is divisible by 256 we can compare only the
  2068                                  	;  high byte for the same CF result: NC iff bx >= limit.
  2069 0000013F 80FF03                  		cmp bh, ((_LOAD_ADR >> 4) + _LOAD_MIN_PARA) >> 8
  2070                                   %else
  2071                                  		cmp bx, (_LOAD_ADR >> 4) + _LOAD_MIN_PARA
  2072                                   %endif
  2073 00000142 B045                    		mov al, 'E'
  2074 00000144 7211                    		jb error
  2075                                  %endif
  2076                                  
  2077                                  %if _TURN_OFF_FLOPPY
  2078                                  ; turn off floppy motor
  2079                                  		mov dx,3F2h
  2080                                  		mov al,0
  2081                                  		out dx,al
  2082                                  %endif
  2083                                  
  2084                                  ; Set-up registers for and jump to loaded file
  2085                                  ; Already: ss:bp-> boot sector containing BPB
  2086                                  %if _CHECKVALUE
  2087                                  CHECKLINEAR equ _LOAD_ADR + _CHECKOFFSET
  2088                                   %if ! _RELOCATE && CHECKLINEAR <= (64 * 1024 - 2)
  2089 00000146 813EFC236C44            		cmp word [CHECKLINEAR], _CHECKVALUE
  2090                                   %else
  2091                                  		mov ax, CHECKLINEAR >> 4
  2092                                  		mov es, ax
  2093                                  		cmp word [es:CHECKLINEAR & 15], _CHECKVALUE
  2094                                   %endif
  2095 0000014C B056                    		mov al, 'V'		; check 'V'alue mismatch
  2096 0000014E 7507                    		jne error
  2097                                  %endif
  2098                                  %if _SET_DL_UNIT
  2099                                  		mov dl, [VAR(boot_unit)]; set dl to unit
  2100                                  %endif
  2101                                  %if _SET_DI_CLUSTER && (_PUSH_DPT || _SET_DSSI_DPT)
  2102                                  		pop cx
  2103                                  %endif
  2104                                  %if _DATASTART_HIDDEN
  2105                                  		mov bx, [VAR(hidden_sectors + 0)]
  2106                                  		mov ax, [VAR(hidden_sectors + 2)]
  2107                                  		add word [VAR(data_start + 0)], bx
  2108                                  		adc word [VAR(data_start + 2)], ax
  2109                                  %endif
  2110                                  %if _SET_BL_UNIT
  2111                                   %if _SET_DL_UNIT
  2112                                  		mov bl, dl		; set bl to unit, too
  2113                                   %else
  2114                                  		mov bl, [VAR(boot_unit)]; set bl to unit
  2115                                   %endif
  2116                                  %endif
  2117                                  %if _PUSH_DPT || _SET_DSSI_DPT
  2118                                   %ifn _SET_DSSI_DPT		; (implying that only _PUSH_DPT is set)
  2119                                    %if _RELOCATE
  2120                                  		xor ax, ax
  2121                                  		mov es, ax
  2122                                  		mov di, 1Eh * 4
  2123                                  		les si, [es:di]
  2124                                  		push es
  2125                                  		push si
  2126                                  		push ax
  2127                                  		push di
  2128                                    %else
  2129                                  		mov di, 1Eh*4
  2130                                  		les si, [di]		; -> original (also current) DPT
  2131                                  		push es
  2132                                  		push si			; original (also current) DPT address
  2133                                  		push ss
  2134                                  		push di			; 0000h:0078h (address of 1Eh IVT entry)
  2135                                    %endif
  2136                                   %else
  2137                                    %if _RELOCATE
  2138                                  		xor ax, ax
  2139                                  		mov ds, ax
  2140                                    %endif
  2141                                  		mov di, 1Eh*4
  2142                                  		lds si, [di]		; -> original (also current) DPT
  2143                                    %if _PUSH_DPT
  2144                                  		push ds
  2145                                  		push si			; original (also current) DPT address
  2146                                     %if _RELOCATE
  2147                                  		push ax
  2148                                  		push di
  2149                                     %else
  2150                                  		push ss
  2151                                  		push di			; 0000h:0078h (address of 1Eh IVT entry)
  2152                                     %endif
  2153                                    %endif
  2154                                   %endif
  2155                                  %else
  2156                                   %if _RELOCATE && (_ZERO_ES || _ZERO_DS)
  2157                                  		xor ax, ax
  2158                                   %endif
  2159                                  %endif
  2160                                  %if _RELOCATE
  2161                                   %if _ZERO_ES
  2162                                  		mov es, ax
  2163                                   %endif
  2164                                   %if _ZERO_DS
  2165                                  		mov ds, ax
  2166                                   %endif
  2167                                  %endif
  2168                                  
  2169                                  %if _SET_AXBX_DATA
  2170                                  		mov bx, [VAR(data_start)]
  2171                                  		mov ax, [VAR(data_start+2)]
  2172                                  %endif
  2173                                  %if _SET_DI_CLUSTER
  2174                                   %if _PUSH_DPT || _SET_DSSI_DPT
  2175                                  		mov di, cx
  2176                                   %else
  2177                                  		pop di
  2178                                   %endif
  2179                                  %endif
  2180                                  %if ! _RELOCATE
  2181                                   %if _ZERO_ES
  2182                                  		push ss
  2183                                  		pop es
  2184                                   %endif
  2185                                  %endif
  2186                                  			; ss:bp-> boot sector with BPB
  2187 00000150 EA00040002              		jmp (_LOAD_ADR>>4)+_EXEC_SEG_ADJ:_EXEC_OFS
  2188                                  
  2189                                  
  2190                                  %if _WARN_PART_SIZE
  2191                                   %assign num $ - finish_start
  2192                                   %warning finish size is num bytes
  2193                                  %endif
  2194                                  
  2195                                  
  2196                                  %if ! _RELOCATE
  2197                                  	errorhandler
   840                              <1> %if _TMPINC
   841                              <1>  %include "error.tmp"
     1                              <2> ; Auto-generated file, do not edit.
     2                              <2> 
     3                              <2> error_start:
     4                              <2> 
     5                              <2> read_sector.err:
     6 00000155 B052                <2> 	mov al, 'R'	; Disk 'R'ead error
     7                              <2> %if ! _MEMORY_CONTINUE || _RELOCATE || _LOAD_ADR >= ADR_FREE_FROM || _LOAD_CHECK_MAX_SIZE
     8                              <2> 	db __TEST_IMM16	; (skip mov)
     9                              <2> error_filetoobig:
    10                              <2> error_memory:
    11                              <2> 	mov al,'M'	; Not enough 'M'emory
    12                              <2> %endif
    13                              <2> 
    14                              <2> error:
    15                              <2> %if _RELOCATE
    16                              <2> 	mov bx, 7
    17                              <2> 	mov ds, bx
    18                              <2> 	mov bh, [462h - 70h]
    19                              <2> %else
    20 00000157 8A3E6204            <2> 	mov bh, [462h]
    21 0000015B B307                <2> 	mov bl, 7
    22                              <2> %endif
    23 0000015D B40E                <2> 	mov ah, 0Eh
    24 0000015F CD10                <2> 	int 10h		; display character
    25 00000161 B007                <2> 	mov al, 07h
    26 00000163 CD10                <2> 	int 10h		; beep!
    27                              <2> 
    28 00000165 31C0                <2> 	xor ax, ax	; await key pressed
    29 00000167 CD16                <2> 	int 16h
    30                              <2> 
    31 00000169 CD19                <2> 	int 19h		; re-start the boot process
    32                              <2> 
    33                              <2> %if _WARN_PART_SIZE
    34                              <2>  %assign num $ - error_start
    35                              <2>  %warning error size is num bytes
    36                              <2> %endif
   842                              <1>  [list -]
   883                              <1> %endif
  2198                                  
  2199                                  	readhandler
   888                              <1> %if _TMPINC
   889                              <1>  %include "read.tmp"
     1                              <2> ; Auto-generated file, do not edit.
     2                              <2> 
     3                              <2> read_sector_start:
     4                              <2> 		; INP:	dx:ax = sector
     5                              <2> 		; OUT:	only if successful
     6                              <2> 		;	dx:ax = incremented
     7                              <2> 		;	bx => behind read sector
     8                              <2> 		;	es = ADR_FATBUF>>4 = ADR_DIRBUF>>4
     9                              <2> 		; CHG:	-
    10                              <2> %if ! _RELOCATE
    11                              <2>  %if ADR_DIRBUF == ADR_FATBUF
    12                              <2> read_sector_dirbuf:
    13                              <2>  %endif
    14                              <2>  %if _FAT16 && ! _LOAD_NON_FAT
    15                              <2> read_sector_fatbuf:
    16                              <2>  %endif
    17                              <2>  %if (ADR_DIRBUF == ADR_FATBUF) || (_FAT16 && ! _LOAD_NON_FAT)
    18 0000016B BBE007              <2> 	mov bx, ADR_FATBUF>>4
    19                              <2>   %if _FAT16 && _SET_FAT_SEG && ! _LOAD_NON_FAT
    20                              <2> 	mov word [VAR(fat_seg)], bx
    21                              <2> 		; Optimisation: Set FAT buffer segment here where
    22                              <2> 		;  we have it ready in a register, instead of
    23                              <2> 		;  wasting a word immediate on it. If the FAT is
    24                              <2> 		;  never read then we do not need to set the
    25                              <2> 		;  variable anyway, only the sector variable has
    26                              <2> 		;  to contain a -1 to indicate it's uninitialised.
    27                              <2> 		; If we get here from read_sector_dirbuf we will
    28                              <2> 		;  also initialise this variable but that does not
    29                              <2> 		;  cause any problems.
    30                              <2>   %endif
    31                              <2>  %endif
    32                              <2> %endif
    33                              <2> 
    34                              <2> 		; Read a sector using Int13.02 or Int13.42
    35                              <2> 		;
    36                              <2> 		; INP:	dx:ax = sector number within partition
    37                              <2> 		;	bx => buffer
    38                              <2> 		;	(_LBA) ds = ss
    39                              <2> 		; OUT:	If unable to read,
    40                              <2> 		;	 ! jumps to error instead of returning
    41                              <2> 		;	If sector has been read,
    42                              <2> 		;	 dx:ax = next sector number (has been incremented)
    43                              <2> 		;	 bx => next buffer (bx = es+word[para_per_sector])
    44                              <2> 		;	 es = input bx
    45                              <2> 		; CHG:	-
    46                              <2> read_sector:
    47 0000016E 52                  <2> 	push dx
    48 0000016F 51                  <2> 	push cx
    49 00000170 50                  <2> 	push ax
    50 00000171 56                  <2> 	push si
    51                              <2> 
    52 00000172 8EC3                <2> 	mov es, bx	; => buffer
    53                              <2> 
    54                              <2> ; DX:AX==LBA sector number
    55                              <2> ; add partition start (= number of hidden sectors)
    56 00000174 03461C              <2> 		add ax,[VAR(hidden_sectors + 0)]
    57 00000177 13561E              <2> 		adc dx,[VAR(hidden_sectors + 2)]
    58                              <2>  %if (!_LBA || !_LBA_33_BIT) && _LBA_CHECK_NO_33
    59                              <2> 	jc .err
    60                              <2>  %endif
    61                              <2> %if _LBA		; +70 bytes (with CHS, +63 bytes without CHS)
    62                              <2>  %if _LBA_33_BIT
    63 0000017A 19F6                <2> 	sbb si, si	; -1 if was CY, 0 else
    64 0000017C F7DE                <2> 	neg si		; 1 if was CY, 0 else
    65                              <2>  %endif
    66 0000017E 31C9                <2> 	xor cx, cx
    67 00000180 51                  <2> 	push cx		; highest word = 0
    68                              <2>  %if _LBA_33_BIT
    69 00000181 56                  <2> 	push si		; bit 32 = 1 if operating in 33-bit space
    70                              <2>  %else
    71                              <2> 	push cx		; second highest word = 0
    72                              <2>  %endif
    73 00000182 52                  <2> 	push dx
    74 00000183 50                  <2> 	push ax		; = qword sector number
    75 00000184 53                  <2> 	push bx
    76 00000185 51                  <2> 	push cx		; bx => buffer
    77 00000186 41                  <2> 	inc cx
    78 00000187 51                  <2> 	push cx		; word number of sectors to read
    79 00000188 B110                <2> 	mov cl, 10h
    80 0000018A 51                  <2> 	push cx		; word size of disk address packet
    81 0000018B 89E6                <2> 	mov si, sp	; ds:si -> disk address packet (on stack)
    82                              <2> 
    83                              <2>  %if _LBA_SKIP_CHECK		; -14 bytes
    84 0000018D 8A5624              <2> 	mov dl, [VAR(boot_unit)]
    85                              <2>  %else
    86                              <2>   %if _LBA_WORKAROUND
    87                              <2> 	push ds
    88                              <2> 	mov bx, 40h
    89                              <2> 	mov ds, bx
    90                              <2> ; Setting ds = 40h for the function 41h detection is a workaround
    91                              <2> ;  for a bug in the Book8088's Xi8088 BIOS. Refer to
    92                              <2> ;  https://www.bttr-software.de/forum/forum_entry.php?id=21275
    93                              <2>   %endif
    94                              <2> 	mov ah, 41h
    95                              <2> 	mov dl, [VAR(boot_unit)]
    96                              <2> 	mov bx, 55AAh
    97                              <2> 	stc
    98                              <2> 	int 13h		; 13.41.bx=55AA extensions installation check
    99                              <2>   %if _LBA_WORKAROUND
   100                              <2> 	pop ds
   101                              <2>   %endif
   102                              <2> 	jc .no_lba
   103                              <2> 	cmp bx, 0AA55h
   104                              <2> 	jne .no_lba
   105                              <2> 	shr cl, 1	; support bitmap bit 0
   106                              <2> 	jnc .no_lba
   107                              <2>  %endif
   108                              <2> 
   109                              <2> %if _LBA_RETRY
   110                              <2>  %if _LBA_SKIP_CHECK && _LBA_SKIP_CY
   111                              <2> 	stc
   112                              <2>  %endif
   113                              <2> 	mov ah, 42h
   114                              <2> 	int 13h		; 13.42 extensions read
   115                              <2> 	jnc .lba_done
   116                              <2> 
   117                              <2>  %if _RETRY_RESET
   118                              <2> 	xor ax, ax
   119                              <2> 	int 13h		; reset disk
   120                              <2>  %endif
   121                              <2> 
   122                              <2> 		; have to reset the LBAPACKET's lpCount, as the handler may
   123                              <2> 		;  set it to "the number of blocks successfully transferred".
   124                              <2> 		; (in any case, the high byte is still zero.)
   125                              <2> 	mov byte [si + 2], 1
   126                              <2> %endif
   127                              <2> 
   128                              <2>  %if _LBA_SKIP_CHECK && _LBA_SKIP_CY
   129 00000190 F9                  <2> 	stc
   130                              <2>  %endif
   131 00000191 B442                <2> 	mov ah, 42h
   132 00000193 CD13                <2> 	int 13h
   133                              <2>  %if _LBA_SKIP_CHECK && _CHS
   134                              <2>   %if _LBA_SKIP_ANY
   135                              <2> 	jc .no_lba
   136                              <2>   %else
   137 00000195 7205                <2> 	jc .lba_check_error_1
   138                              <2>   %endif
   139                              <2>  %else
   140                              <2> .cy_err:
   141                              <2> 	jc .lba_error
   142                              <2>  %endif
   143                              <2> 
   144                              <2> .lba_done:
   145                              <2> %if _CHS && _LBA_SET_TYPE
   146                              <2> 	mov byte [bp + 2], 0Eh	; LBA-enabled FAT16 FS partition type
   147                              <2> %endif
   148 00000197 83C410              <2> 	add sp, 10h
   149                              <2> %if _CHS
   150 0000019A EB46                <2> 	jmp short .done
   151                              <2> %endif
   152                              <2> 
   153                              <2> .lba_error: equ .err
   154                              <2> 
   155                              <2>  %if !_CHS
   156                              <2> .no_lba: equ .err
   157                              <2>  %else
   158                              <2>  %if _LBA_SKIP_CHECK
   159                              <2>   %if ! _LBA_SKIP_ANY
   160                              <2> .lba_check_error_1:
   161 0000019C 80FC01              <2> 	cmp ah, 1	; invalid function?
   162 0000019F 75B4                <2> 	jne .lba_error	; no, other error -->
   163                              <2> 			; try CHS instead
   164                              <2>   %endif
   165                              <2> .cy_err: equ .err
   166                              <2>  %endif
   167                              <2> .no_lba:
   168 000001A1 83C408              <2> 	add sp, 8
   169 000001A4 59                  <2> 	pop cx		; cx = low word of sector
   170 000001A5 58                  <2> 	pop ax
   171 000001A6 5A                  <2> 	pop dx		; dx:ax = middle two words of sector
   172                              <2> 		; Here dx <= 1 if _LBA_33_BIT, else zero.
   173                              <2> 		;  If dx is nonzero then the CHS calculation
   174                              <2> 		;  should fail. If CHS sectors is equal to 1
   175                              <2> 		;  (very unusual) then the div may fail. Else,
   176                              <2> 		;  we will detect a cylinder > 1023 eventually.
   177 000001A7 5E                  <2> 	pop si		; discard highest word of qword
   178                              <2>  %endif
   179                              <2> %endif
   180                              <2> 
   181                              <2> %if !_LBA
   182                              <2> .cy_err:	equ .err
   183                              <2> %endif
   184                              <2> 
   185                              <2> %if _CHS		; +70 bytes
   186                              <2> ; dx:ax = LBA sector number, (if _LBA) cx = 0
   187                              <2> ; divide by number of sectors per track to get sector number
   188                              <2> ; Use 32:16 DIV instead of 64:32 DIV for 8088 compatability
   189                              <2> ; Use two-step 32:16 divide to avoid overflow
   190                              <2>  %if !_LBA
   191                              <2> 			xchg cx, ax	; cx = low word of sector, clobbers ax
   192                              <2> 			xchg ax, dx	; ax = high word of sector, clobbers dx
   193                              <2> 			xor dx, dx	; dx:ax = high word of sector
   194                              <2>  %else
   195                              <2> 	; from the .no_lba popping we already have:
   196                              <2> 	;  cx = low word of sector
   197                              <2> 	;  dx:ax = high word of sector
   198                              <2>  %endif
   199 000001A8 F77618              <2> 			div word [VAR(sectors_per_track)]
   200 000001AB 91                  <2> 			xchg cx,ax
   201 000001AC F77618              <2> 			div word [VAR(sectors_per_track)]
   202 000001AF 87CA                <2> 			xchg cx,dx
   203                              <2> 
   204                              <2> ; DX:AX=quotient, CX=remainder=sector (S) - 1
   205                              <2> ; divide quotient by number of heads
   206 000001B1 93                  <2> 			xchg bx, ax	; bx = low word of quotient, clobbers ax
   207 000001B2 92                  <2> 			xchg ax, dx	; ax = high word of quotient, clobbers dx
   208 000001B3 31D2                <2> 			xor dx, dx	; dx = 0
   209 000001B5 F7761A              <2> 			div word [VAR(heads)]
   210                              <2> 					; ax = high / heads, dx = high % heads
   211 000001B8 93                  <2> 			xchg bx, ax	; bx = high / heads, ax = low quotient
   212 000001B9 F7761A              <2> 			div word [VAR(heads)]
   213                              <2> 
   214                              <2> ; bx:ax=quotient=cylinder (C), dx=remainder=head (H)
   215                              <2> ; move variables into registers for INT 13h AH=02h
   216 000001BC 88D6                <2> 			mov dh, dl	; dh = head
   217 000001BE 41                  <2> 			inc cx		; cl5:0 = sector
   218 000001BF 86E8                <2> 			xchg ch, al	; ch = cylinder 7:0, al = 0
   219 000001C1 D1E8                <2> 			shr ax, 1
   220 000001C3 D1E8                <2> 			shr ax, 1	; al7:6 = cylinder 9:8
   221                              <2> 	; bx has bits set iff it's > 0, indicating a cylinder >= 65536.
   222 000001C5 08FB                <2> 			 or bl, bh	; collect set bits from bh
   223 000001C7 08C1                <2> 			or cl, al	; cl7:6 = cylinder 9:8
   224                              <2> 	; ah has bits set iff it was >= 4, indicating a cylinder >= 1024.
   225 000001C9 08E3                <2> 			 or bl, ah	; collect set bits from ah
   226 000001CB 8A5624              <2> 			mov dl,[VAR(boot_unit)] ; dl = drive
   227                              <2> .nz_err:
   228 000001CE 7585                <2> 			 jnz .err	; error if cylinder >= 1024 -->
   229                              <2> 					; ! bx = 0 (for 13.02 call)
   230                              <2> 
   231                              <2> ; we call INT 13h AH=02h once for each sector. Multi-sector reads
   232                              <2> ; may fail if we cross a track or 64K boundary
   233                              <2> %if _CHS_RETRY_REPEAT
   234 000001D0 BE1100              <2> 			mov si, _CHS_RETRY_REPEAT + 1
   235                              <2>  %if _CHS_RETRY_NORMAL && _RETRY_RESET
   236 000001D3 A9                  <2> 			db __TEST_IMM16	; (skip int 13h)
   237                              <2> .loop_chs_retry_repeat:
   238 000001D4 CD13                <2> 			int 13h		; reset disk
   239                              <2>  %elif _RETRY_RESET
   240                              <2> .loop_chs_retry_repeat:
   241                              <2> 			xor ax, ax
   242                              <2> 			int 13h		; reset disk
   243                              <2>  %else
   244                              <2> .loop_chs_retry_repeat:
   245                              <2>  %endif
   246 000001D6 4E                  <2> 			dec si		; another attempt ?
   247 000001D7 78F5                <2> 			js .nz_err	; no -->
   248 000001D9 B80102              <2> 			mov ax, 0201h
   249 000001DC CD13                <2> 			int 13h		; read one sector
   250                              <2>  %if _CHS_RETRY_NORMAL && _RETRY_RESET
   251 000001DE 89D8                <2> 			mov ax, bx	; ax = 0
   252                              <2>  %endif
   253 000001E0 72F2                <2> 			jc .loop_chs_retry_repeat
   254                              <2> 	; fall through to .done
   255                              <2> %else
   256                              <2> 			mov ax, 0201h
   257                              <2>  %if _CHS_RETRY
   258                              <2>   %if _RETRY_RESET
   259                              <2> 	; In this case we cannot store to the stack and
   260                              <2> 	;  pop the value at the right moment for both
   261                              <2> 	;  cases of the "jnc .done" branch. So use the
   262                              <2> 	;  original code to re-init ax to 0201h.
   263                              <2> 			int 13h		; read one sector
   264                              <2> 			jnc .done
   265                              <2> ; reset drive
   266                              <2> 			xor ax, ax
   267                              <2> 			int 13h
   268                              <2> 			mov ax, 0201h
   269                              <2>   %else
   270                              <2> 			push ax
   271                              <2> 			int 13h		; read one sector
   272                              <2> 			pop ax		; restore ax = 0201h
   273                              <2> 			jnc .done
   274                              <2>   %endif
   275                              <2>  %endif
   276                              <2> ; try read again
   277                              <2> 			int 13h
   278                              <2>  %if _LBA_SKIP_CHECK
   279                              <2> 			inc bx
   280                              <2> 			jc .nz_err
   281                              <2>  %else
   282                              <2> 			jc .cy_err
   283                              <2>  %endif
   284                              <2> %endif
   285                              <2> 
   286                              <2> %endif		; _CHS
   287                              <2> 
   288                              <2> .done:
   289                              <2> ; increment segment
   290 000001E2 8CC3                <2> 	mov bx, es
   291                              <2> %if _FIX_SECTOR_SIZE
   292                              <2> 	add bx, _FIX_SECTOR_SIZE >> 4
   293                              <2> %else
   294 000001E4 035EEE              <2> 	add bx, [VAR(para_per_sector)]
   295                              <2> %endif
   296                              <2> 
   297 000001E7 5E                  <2> 	pop si
   298 000001E8 58                  <2> 	pop ax
   299 000001E9 59                  <2> 	pop cx
   300 000001EA 5A                  <2> 	pop dx
   301                              <2> ; increment LBA sector number
   302 000001EB 40                  <2> 	inc ax
   303 000001EC 7501                <2> 	jne @F
   304 000001EE 42                  <2> 	inc dx
   305                              <2> @@:
   306                              <2> 
   307 000001EF C3                  <2> 	retn
   308                              <2> 
   309                              <2> %if _WARN_PART_SIZE
   310                              <2>  %assign num $ - read_sector_start
   311                              <2>  %warning read_sector size is num bytes
   312                              <2> %endif
   890                              <1>  [list -]
  1207                              <1> %endif
  2200                                  %endif
  2201                                  
  2202                                  
  2203                                  %if !_NO_LIMIT
  2204                                  available:
  2205 000001F0 26<rep Ch>              	_fill 508,38,start
  2206                                  
  2207                                  signatures:
  2208 000001FC 0000                    	dw 0
  2209                                  ; 2-byte magic bootsector signature
  2210 000001FE 55AA                    	dw 0AA55h
  2211                                  
  2212                                  %assign num signatures-available
  2213                                  %assign fatbits 12
  2214                                  %if _FAT16
  2215                                   %assign fatbits 16
  2216                                  %endif
  2217                                  %warning FAT%[fatbits]: num bytes still available.
  2217          ******************       warning: FAT12: 12 bytes still available. [-w+user]
  2218                                  %endif
  2219                                  
  2220                                  end:
