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