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