     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 C. Masloch, 2012
    10                                  
    11                                  %endif
    12                                  
    13                                  
    14                                  %include "lmacros2.mac"
    15                              <1> [list -]
    28                              <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(1536)
    34                                  	numdef LOAD_NON_FAT,	0, 2048	; use FAT-less loading (value is amount bytes)
    35                                  	numdef EXEC_SEG_ADJ,	0	; how far cs will be from _LOAD_ADR
    36                                  	numdef EXEC_OFS,	400h	; what value ip will be
    37                                  	numdef CHECKOFFSET,	1020
    38                                  	numdef CHECKVALUE,	"lD"
    39                                  	numdef LOAD_DIR_SEG,	0	; => where to store dir entry (0 if nowhere)
    40                                  	numdef ADD_SEARCH,	0	; whether to search second file
    41                                  	strdef ADD_NAME,	""
    42                                  	strdef ADD_EXT,		""	; name of second file to search
    43                                  	numdef ADD_DIR_SEG,	0	; => where to store dir entry (0 if nowhere)
    44                                  
    45                                  	gendef _ADR_DIRBUF, end -start+7C00h	; 07E00h
    46                                  	gendef _ADR_FATBUF, end -start+7C00h	; 07E00h
    47                                  
    48                                  	numdef QUERY_GEOMETRY,	1	; query geometry via 13.08 (for CHS access)
    49                                  	numdef USE_PART_INFO,	1	; use ds:si-> partition info from MBR, if any
    50                                  	numdef USE_AUTO_UNIT,	1	; use unit passed from ROM-BIOS in dl
    51                                  	numdef RPL,		1	; support RPL and do not overwrite it
    52                                  	numdef RPL_GRACE_AREA,	130 * 1024
    53                                  					; alternative RPL support,
    54                                  					;  assume RPL fits in this area
    55                                  	numdef CHS,		1	; support CHS (if it fits)
    56                                  	numdef LBA,		1	; support LBA (if available)
    57                                  	numdef LBA_33_BIT,	1	; support 33-bit LBA
    58                                  	numdef LBA_CHECK_NO_33,	1	; else: check that LBA doesn't carry
    59                                  
    60                                  	numdef RELOCATE,	0	; relocate the loader to top of memory
    61                                  	numdef SET_BL_UNIT,	0	; if to pass unit in bl as well
    62                                  	numdef SET_DL_UNIT,	0	; if to pass unit in dl
    63                                  	numdef SET_AXBX_DATA,	0	; if to pass first data sector in ax:bx
    64                                  	numdef SET_DSSI_DPT,	0	; if to pass DPT address in ds:si
    65                                  	numdef PUSH_DPT,	0	; if to push DPT address
    66                                  	numdef MEMORY_CONTINUE,	1	; if to just execute when memory full
    67                                  	numdef SET_DI_CLUSTER,	0	; if to pass first load file cluster in di
    68                                  	numdef DIRBUF_500,	0	; if to load root dir sector(s) to 0:500h
    69                                  	numdef DIR_ENTRY_500,	0	; if to copy directory entry to 0:500h
    70                                  	numdef DIR_ENTRY_520,	0	; if to copy next directory entry to 0:520h
    71                                  	numdef TURN_OFF_FLOPPY,	0	; if to turn off floppy motor after loading
    72                                  	numdef DATASTART_HIDDEN,0	; if to add hidden sectors to data_start
    73                                  	numdef LBA_SET_TYPE,	0	; if to set third byte to LBA partition type
    74                                  	numdef SET_LOAD_SEG,	1	; if to set load_seg (word [ss:bp - 6])
    75                                  	numdef SET_FAT_SEG,	1	; if to set fat_seg (word [ss:bp - 8])
    76                                  	numdef SET_CLUSTER,	1	; if to set first_cluster (word [ss:bp - 16])
    77                                  	numdef ZERO_ES,		0	; if to set es = 0 before jump
    78                                  	numdef ZERO_DS,		0	; if to set ds = 0 before jump
    79                                  
    80                                  	numdef FIX_SECTOR_SIZE, 0	; fix sector size (0 = disable, else = sector size)
    81                                  	numdef FIX_SECTOR_SIZE_SKIP_CHECK,	0	; don't check sector size
    82                                  	numdef FIX_CLUSTER_SIZE,		0	; fix cluster size
    83                                  	numdef FIX_CLUSTER_SIZE_SKIP_CHECK,	0	; don't check cluster size
    84                                  	numdef NO_LIMIT,	0	; allow using more memory than a boot sector
    85                                  					;  also will not write 0AA55h signature!
    86                                  	numdef LBA_SKIP_CHECK,	1	; don't use proper LBA extensions check
    87                                  	numdef LBA_RETRY,	0	; retry LBA reads one time
    88                                  	numdef CHS_RETRY,	1	; retry CHS reads one time
    89                                  	numdef CHS_RETRY_REPEAT,16	; retry CHS reads multiple times
    90                                  					; (value of the def is used as count)
    91                                  	numdef MEDIAID, 0F0h		; media ID
    92                                  	numdef UNIT, 0			; load unit in BPB
    93                                  	numdef CHS_SECTORS, 18		; CHS geometry field for sectors
    94                                  	numdef CHS_HEADS, 2		; CHS geometry field for heads
    95                                  	numdef HIDDEN, 0		; number of hidden sectors
    96                                  	numdef SPI, 2880		; sectors per image
    97                                  	numdef BPS, 512			; bytes per sector
    98                                  	numdef SPC, 1			; sectors per cluster
    99                                  	numdef SPF, 9			; sectors per FAT
   100                                  	numdef NUMFATS, 2		; number of FATs
   101                                  	numdef NUMROOT, 224		; number of root directory entries
   102                                  	numdef NUMRESERVED, 1		; number of reserved sectors
   103                                  
   104                                  %if _DIRBUF_500
   105                                  	gendef _ADR_DIRBUF, 500h
   106                                  %endif
   107                                  
   108                                  
   109                                  	numdef COMPAT_FREEDOS,	0	; partial FreeDOS load compatibility
   110                                  	numdef COMPAT_IBM,	0	; partial IBMDOS load compatibility
   111                                  	numdef COMPAT_MS7,	0	; partial MS-DOS 7 load compatibility
   112                                  	numdef COMPAT_MS6,	0	; partial MS-DOS 6 load compatibility
   113                                  	numdef COMPAT_LDOS,	0	; lDOS load compatibility
   114                                  	numdef COMPAT_KERNEL7E, 0	; kernel at 0:7E00h load compatibility
   115                                  
   116                                  %if (!!_COMPAT_FREEDOS + !!_COMPAT_IBM + 	!!_COMPAT_MS7 + !!_COMPAT_MS6 + 	!!_COMPAT_LDOS || _COMPAT_KERNEL7E) > 1
   119                                   %error At most one set must be selected.
   120                                  %endif
   121                                  
   122                                  %if _COMPAT_FREEDOS
   123                                  	strdef LOAD_NAME,	"KERNEL"
   124                                  	strdef LOAD_EXT,	"SYS"
   125                                  	numdef LOAD_ADR,	00600h
   126                                  	numdef LOAD_MIN_PARA,	paras(512)
   127                                  	numdef EXEC_SEG_ADJ,	0
   128                                  	numdef EXEC_OFS,	0
   129                                  
   130                                  	numdef CHECKVALUE,	0
   131                                  	numdef SET_LOAD_SEG,	0
   132                                  	numdef SET_FAT_SEG,	0
   133                                  	numdef SET_CLUSTER,	0
   134                                  
   135                                  	numdef SET_BL_UNIT,	1
   136                                  	numdef MEMORY_CONTINUE,	0
   137                                  	numdef RELOCATE,	1
   138                                  	; The FreeDOS load protocol mandates that the entire file be loaded.
   139                                  %endif
   140                                  
   141                                  %if _COMPAT_IBM
   142                                  	strdef LOAD_NAME,	"IBMBIO"
   143                                  	strdef LOAD_EXT,	"COM"
   144                                  	numdef LOAD_ADR,	00700h
   145                                  	numdef LOAD_MIN_PARA,	paras(512)
   146                                  	numdef EXEC_SEG_ADJ,	0
   147                                  	numdef EXEC_OFS,	0
   148                                  	numdef LOAD_DIR_SEG,	50h
   149                                  	numdef ADD_SEARCH,	1
   150                                  	strdef ADD_NAME,	"IBMDOS"
   151                                  	strdef ADD_EXT,		"COM"
   152                                  	numdef ADD_DIR_SEG,	52h
   153                                   	; Note: The IBMBIO.COM directory entry must be stored at
   154                                  	;  0:500h, and the IBMDOS.COM directory entry at 0:520h.
   155                                  
   156                                  	numdef CHECKVALUE,	0
   157                                  	numdef SET_LOAD_SEG,	0
   158                                  	numdef SET_FAT_SEG,	0
   159                                  	numdef SET_CLUSTER,	0
   160                                  
   161                                  	numdef SET_DL_UNIT,	1
   162                                  	numdef MEMORY_CONTINUE,	1
   163                                  	; 3 sectors * 512 BpS should suffice. We load into 700h--7A00h,
   164                                  	;  ie >= 6000h bytes (24 KiB), <= 7300h bytes (28.75 KiB).
   165                                  	numdef SET_AXBX_DATA,	1
   166                                  	numdef DATASTART_HIDDEN,1
   167                                  	numdef SET_DSSI_DPT,	1
   168                                  	numdef PUSH_DPT,	1
   169                                  %endif
   170                                  
   171                                  %if _COMPAT_MS7
   172                                  	strdef LOAD_NAME,	"IO"
   173                                  	strdef LOAD_EXT,	"SYS"
   174                                  	numdef LOAD_ADR,	00700h
   175                                  	numdef LOAD_MIN_PARA,	paras(1024)
   176                                  	numdef EXEC_SEG_ADJ,	0
   177                                  	numdef EXEC_OFS,	200h
   178                                  
   179                                  	numdef CHECKVALUE,	0
   180                                  	numdef SET_LOAD_SEG,	0
   181                                  	numdef SET_FAT_SEG,	0
   182                                  	numdef SET_CLUSTER,	0
   183                                  
   184                                  	numdef SET_DL_UNIT,	1
   185                                  	numdef SET_DSSI_DPT,	0
   186                                  	numdef PUSH_DPT,	1
   187                                  	numdef MEMORY_CONTINUE,	1
   188                                  	; 4 sectors * 512 BpS should suffice. We load into 700h--7A00h,
   189                                  	;  ie >= 6000h bytes (24 KiB), <= 7300h bytes (28.75 KiB).
   190                                  	numdef SET_DI_CLUSTER,	1
   191                                  	numdef DATASTART_HIDDEN,1
   192                                  	numdef LBA_SET_TYPE,	1
   193                                  %endif
   194                                  
   195                                  %if _COMPAT_MS6
   196                                  	strdef LOAD_NAME,	"IO"
   197                                  	strdef LOAD_EXT,	"SYS"
   198                                  	numdef LOAD_ADR,	00700h
   199                                  	numdef LOAD_MIN_PARA,	paras(512)
   200                                  	numdef EXEC_SEG_ADJ,	0
   201                                  	numdef EXEC_OFS,	0
   202                                  	numdef LOAD_DIR_SEG,	50h
   203                                  	numdef ADD_SEARCH,	1
   204                                  	strdef ADD_NAME,	"MSDOS"
   205                                  	strdef ADD_EXT,		"SYS"
   206                                  	numdef ADD_DIR_SEG,	52h
   207                                   	; Note: The IO.SYS directory entry must be stored at
   208                                  	;  0:500h, and the MSDOS.SYS directory entry at 0:520h.
   209                                  
   210                                  	numdef CHECKVALUE,	0
   211                                  	numdef SET_LOAD_SEG,	0
   212                                  	numdef SET_FAT_SEG,	0
   213                                  	numdef SET_CLUSTER,	0
   214                                  
   215                                  	numdef SET_DL_UNIT,	1
   216                                  	numdef MEMORY_CONTINUE,	1
   217                                  	; 3 sectors * 512 BpS should suffice. We load into 700h--7A00h,
   218                                  	;  ie >= 6000h bytes (24 KiB), <= 7300h bytes (28.75 KiB).
   219                                  	numdef SET_AXBX_DATA,	1
   220                                  	numdef DATASTART_HIDDEN,1
   221                                  	numdef SET_DSSI_DPT,	1
   222                                  	numdef PUSH_DPT,	1
   223                                  %endif
   224                                  
   225                                  %if _COMPAT_LDOS
   226                                  	strdef LOAD_NAME,	"LDOS"
   227                                  	strdef LOAD_EXT,	"COM"
   228                                  	numdef LOAD_ADR,	02000h
   229                                  	numdef LOAD_MIN_PARA,	paras(1536)
   230                                  	numdef EXEC_SEG_ADJ,	0
   231                                  	numdef EXEC_OFS,	400h
   232                                  	numdef CHECKOFFSET,	1020
   233                                  	numdef CHECKVALUE,	"lD"
   234                                  
   235                                  	numdef SET_DL_UNIT,	0
   236                                  	numdef SET_CLUSTER,	1
   237                                  	numdef SET_FAT_SEG,	1
   238                                  	numdef SET_LOAD_SEG,	1
   239                                  	numdef MEMORY_CONTINUE,	1
   240                                  	numdef DATASTART_HIDDEN,0
   241                                  %endif
   242                                  
   243                                  %if _COMPAT_KERNEL7E
   244                                  	strdef OEM_NAME,	"KERNEL7E"
   245                                  	strdef LOAD_NAME,	"KERNEL7E"
   246                                  	strdef LOAD_EXT,	"BIN"
   247                                  	numdef LOAD_ADR,	07E00h
   248                                  	numdef LOAD_MIN_PARA,	paras(512)
   249                                  	numdef EXEC_SEG_ADJ,	-7E0h
   250                                  	numdef EXEC_OFS,	7E00h
   251                                  	numdef CHECKVALUE,	0
   252                                  
   253                                  	numdef SET_DL_UNIT,	1
   254                                  	numdef SET_BL_UNIT,	0
   255                                  	numdef SET_CLUSTER,	0
   256                                  	numdef SET_FAT_SEG,	0
   257                                  	numdef SET_LOAD_SEG,	0
   258                                  	numdef MEMORY_CONTINUE,	0
   259                                  	numdef DATASTART_HIDDEN,0
   260                                  
   261                                  	gendef _ADR_FATBUF,	4000h
   262                                  	gendef _ADR_DIRBUF,	4000h
   263                                  	numdef RPL,		0
   264                                  	numdef ZERO_ES,		1
   265                                  %endif
   266                                  
   267                                  
   268                                  %if 0
   269                                  
   270                                  Notes about partial load compatibilities
   271                                  
   272                                  * FreeDOS:
   273                                   * Relocates to an address other than 27A00h (1FE0h:7C00h)
   274                                   * A lot of options between _USE_PART_INFO, _QUERY_GEOMETRY, _CHS, _LBA,
   275                                     and/or _RPL need to be disabled to make the loader fit
   276                                  * IBMDOS:
   277                                  * MS-DOS 6:
   278                                   * Does not actually relocate DPT, just provide its address
   279                                   * A lot of options between _USE_PART_INFO, _QUERY_GEOMETRY, _CHS,
   280                                     and/or _LBA need to be disabled to make the loader fit
   281                                  * MS-DOS 7:
   282                                   * Does not actually relocate DPT, just provide its address
   283                                   * Does not contain message table used by loader
   284                                  
   285                                  %endif
   286                                  
   287                                  %if _SET_BL_UNIT && _SET_AXBX_DATA
   288                                   %error Cannot select both of these options!
   289                                  %endif
   290                                  
   291                                  %if _DIR_ENTRY_520
   292                                   %assign _DIR_ENTRY_500		1
   293                                  %endif
   294                                  
   295                                  %if _DIRBUF_500 && _ADD_SEARCH
   296                                   %error Cannot select both of these options!
   297                                  %endif
   298                                  
   299                                  %if _ADD_SEARCH
   300                                   %if _LOAD_DIR_SEG == 0 || _ADD_DIR_SEG == 0
   301                                    %error Assuming dir segs should be set if add search set
   302                                   %endif
   303                                  %endif
   304                                  
   305                                  %if _RPL
   306                                   %assign _RPL_GRACE_AREA 0
   307                                  %endif
   308                                  
   309                                  
   310                                  %assign LOADLIMIT 0A0000h
   311                                  %assign POSITION   07C00h
   312                                  
   313                                  %if _FIX_SECTOR_SIZE
   314                                   %assign i 5
   315                                   %rep 13-5
   316                                    %if (1 << i) != (_FIX_SECTOR_SIZE)
   317                                     %assign i i+1
   318                                    %endif
   319                                   %endrep
   320                                   %if (1 << i) != (_FIX_SECTOR_SIZE)
   321                                    %error Invalid sector size _FIX_SECTOR_SIZE
   322                                   %endif
   323                                  %endif
   324                                  
   325                                  %if _FIX_CLUSTER_SIZE
   326                                   %if _FIX_CLUSTER_SIZE > 256
   327                                    %error Invalid cluster size _FIX_CLUSTER_SIZE
   328                                   %endif
   329                                   %assign i 0
   330                                   %rep 8-0
   331                                    %if (1 << i) != (_FIX_CLUSTER_SIZE)
   332                                     %assign i i+1
   333                                    %endif
   334                                   %endrep
   335                                   %if (1 << i) != (_FIX_CLUSTER_SIZE)
   336                                    %warning Non-power-of-two cluster size _FIX_CLUSTER_SIZE
   337                                   %endif
   338                                  %endif
   339                                  
   340                                  
   341                                  ; 512-byte stack (minus the variables).
   342                                  ADR_STACK_LOW	equ	7C00h - 200h		; 07A00h
   343                                  
   344                                  %if _DIRBUF_500
   345                                  	gendef _ADR_DIRBUF,	500h
   346                                  %elif _RELOCATE
   347                                  	gendef _ADR_DIRBUF,	_LOAD_ADR
   348                                  %endif
   349                                  
   350                                  ; one-sector directory buffer. Assumes sectors are no larger than 8 KiB
   351                                  ADR_DIRBUF	equ	__ADR_DIRBUF
   352                                  
   353                                  %if ! _RELOCATE
   354                                  ; this used to be a two-sector FAT buffer -- two sectors because FAT12
   355                                  ;  entries are 12 bits and may straddle a sector boundary.
   356                                  ; however, with the FAT12 loaded completely, the buffer only needs to hold
   357                                  ;  one 8 KiB sector, two 4 KiB sectors, three 2 KiB sectors, six 1 KiB sectors,
   358                                  ;  or twelve 512 byte sectors.
   359                                  ; this shares its area with the directory buffer as they
   360                                  ;  are not simultaneously used. (if not _DIRBUF_500.)
   361                                  ADR_FATBUF	equ	__ADR_FATBUF
   362                                  %endif
   363                                  
   364                                  ; start of unused memory after loader:
   365                                  ADR_END		equ	end -start+7C00h
   366                                  %if ! _RELOCATE
   367                                   %if (ADR_FATBUF + 8192) > ADR_END
   368                                    ADR_FREE_FROM	equ	(ADR_FATBUF + 8192)	; 09E00h
   369                                   %else
   370                                    ADR_FREE_FROM	equ	ADR_END			; 07E00h
   371                                   %endif
   372                                  
   373                                  ; end of unused memory before loader:
   374                                   %if ADR_FATBUF < ADR_STACK_LOW
   375                                  ADR_FREE_UNTIL	equ	ADR_FATBUF
   376                                   %else
   377                                  ADR_FREE_UNTIL	equ	ADR_STACK_LOW
   378                                   %endif
   379                                  
   380                                   %if ((ADR_FATBUF + 8192 - 1) & ~0FFFFh) != (ADR_FATBUF & ~0FFFFh)
   381                                    %warning Possibly crossing 64 KiB boundary while reading FAT
   382                                   %endif
   383                                  %endif
   384                                  
   385                                  %if ((ADR_DIRBUF + 8192 - 1) & ~0FFFFh) != (ADR_DIRBUF & ~0FFFFh)
   386                                   %warning Possibly crossing 64 KiB boundary while reading directory
   387                                  %endif
   388                                  
   389                                  %if _RELOCATE
   390                                   ADR_FREE_FROM	equ 0				; make next conditional true
   391                                   ADR_FREE_UNTIL	equ 0
   392                                  %endif
   393                                  %if _LOAD_ADR >= ADR_FREE_FROM
   394                                  	; If reading on a sector size boundary, no crossing can occur.
   395                                  	;  Check for all possible sector sizes (32 B to 8 KiB). If one
   396                                  	;  of them fails display a warning, including the minimum size.
   397                                   %assign SECSIZECHECK 32
   398                                   %assign EXITREP 0
   399                                   %rep 256
   400                                    %ifn EXITREP
   401                                     %if _LOAD_ADR & (SECSIZECHECK - 1)
   402                                      %warning Possibly crossing 64 KiB boundary while reading file (sector size >= SECSIZECHECK)
   403                                      %assign EXITREP 1
   404                                      %exitrep
   405                                     %endif
   406                                     %if SECSIZECHECK == 8192
   407                                      %assign EXITREP 1
   408                                      %exitrep
   409                                     %endif
   410                                     %assign SECSIZECHECK SECSIZECHECK * 2
   411                                    %endif
   412                                   %endrep
   413                                  %else
   414                                  	; If loading below the boot sector, address 1_0000h is never reached.
   415                                  %endif
   416                                  
   417                                  
   418                                  %if (_LOAD_ADR & 0Fh)
   419                                   %error Load address must be on a paragraph boundary
   420                                  %endif
   421                                  
   422                                  %if _LOAD_ADR > LOADLIMIT
   423                                   %error Load address must be in LMA
   424                                  %elif _LOAD_ADR < 00500h
   425                                   %error Load address must not overlap IVT or BDA
   426                                  %endif
   427                                  
   428                                  %if ! _RELOCATE
   429                                   %if _LOAD_ADR > (POSITION-512) && _LOAD_ADR < (POSITION+512)
   430                                    %error Load address must not overlap loader
   431                                   %endif
   432                                  
   433                                   %if ADR_FATBUF > LOADLIMIT
   434                                    %error FAT buffer address must be in LMA
   435                                   %elif ADR_FATBUF < 00500h
   436                                    %error FAT buffer address must not overlap IVT or BDA
   437                                   %elif (ADR_FATBUF + 8192) > (POSITION-512) && ADR_FATBUF < (POSITION+512)
   438                                    %error FAT buffer address must not overlap loader
   439                                   %endif
   440                                  %endif
   441                                  
   442                                  %if ADR_DIRBUF > LOADLIMIT
   443                                   %error Dir buffer address must be in LMA
   444                                  %elif ADR_DIRBUF < 00500h
   445                                   %error Dir buffer address must not overlap IVT or BDA
   446                                  %elif (ADR_DIRBUF + 8192) > (POSITION-512) && ADR_DIRBUF < (POSITION+512)
   447                                   %error Dir buffer address must not overlap loader at initial position
   448                                  %endif
   449                                  
   450                                  %if ((_EXEC_SEG_ADJ<<4)+_EXEC_OFS) < 0
   451                                   %error Execution address must be in loaded file
   452                                  %elif ((_EXEC_SEG_ADJ<<4)+_EXEC_OFS+_LOAD_ADR) > LOADLIMIT
   453                                   %error Execution address must be in LMA
   454                                  %endif
   455                                  
   456                                  %if (_EXEC_OFS & ~0FFFFh)
   457                                   %error Execution offset must fit into 16 bits
   458                                  %endif
   459                                  
   460                                  %if (_EXEC_SEG_ADJ > 0FFFFh || _EXEC_SEG_ADJ < -0FFFFh)
   461                                   %error Execution segment adjustment must fit into 16 bits
   462                                  %endif
   463                                  
   464                                  
   465                                  %if !_CHS && _QUERY_GEOMETRY
   466                                   %warning No CHS support but querying geometry anyway
   467                                  %endif
   468                                  
   469                                  %if !_CHS && !_LBA
   470                                   %error Either CHS or LBA or both must be enabled
   471                                  %endif
   472                                  
   473                                  
   474                                  %if 0
   475                                  
   476                                  There is some logic inside MS-DOS's hard disk partition initialisation
   477                                  code that sets up several requirements for us to fulfil. Otherwise,
   478                                  it will not accept the information given in the BPB (using default
   479                                  information based on the length as specified by MBR/EPBR instead) or
   480                                  make the whole file system inaccessible except for formatting. Both of
   481                                  those are undesirable of course. Some/all(?) checks are documented on
   482                                  pages 601,602 in "DOS Internals", Geoff Chappell 1994, as follows:
   483                                  
   484                                  * First three bytes contain either "jmp sho xx\ nop" or "jmp ne xx".
   485                                  * Media descriptor field >= 0F0h.
   486                                  * Bytes per sector field == 512.
   487                                  * Sectors per cluster field a power of 2 (1,2,4,8,16,32,64,128).
   488                                  * OEM name "version" (last three to four characters)
   489                                   * must be "20.?", "10.?" (? = wildcard), but no other with "0.?",
   490                                   * otherwise, must be "2.0", or
   491                                    * 2nd-to-last,3rd-to-last character codes together > "3.", or
   492                                     * those == "3.", last character code > "0"
   493                                  
   494                                  To stay compatible to those, display a warning here if the name
   495                                  itself would disqualify our boot sector already.
   496                                  
   497                                  %endif
   498                                  
   499                                  %push
   500                                  %strlen %$len _OEM_NAME_FILL
   501                                  %if %$len != 1
   502                                   %error Specified OEM name fill must be 1 character
   503                                  %endif
   504                                  %strlen %$len _OEM_NAME
   505                                  %define %$nam _OEM_NAME
   506                                  %if %$len > 8
   507                                   %error Specified OEM name is too long
   508                                  %else
   509                                   %assign %$warn 0
   510                                   %rep 8 - %$len
   511                                    %strcat %$nam %$nam,_OEM_NAME_FILL
   512                                   %endrep
   513                                   %substr %$prefix %$nam	5	; "wvxyZa.b", get "Z"
   514                                   %substr %$major %$nam 6,7	; "wvxyzA.b", get "A."
   515                                   %substr %$minor %$nam 8	; "wvxyza.B", get "B"
   516                                   %if %$major == "0."
   517                                    %ifn %$prefix == "1" || %$prefix == "2"
   518                                     %assign %$warn 1
   519                                    %endif
   520                                   %elifn %$major == "2." && %$minor == "0"
   521                                    %if %$major < "3."
   522                                     %assign %$warn 1
   523                                    %elif %$major == "3." && %$minor < "1"
   524                                     %assign %$warn 1
   525                                    %endif
   526                                   %endif
   527                                   %if %$warn
   528                                    %warning Specified OEM name fails MS-DOS's validation
   529                                   %endif
   530                                  %endif
   531                                  %pop
   532                                  
   533                                  
   534                                  	struc DIRENTRY
   535 00000000 ????????????????        deName:		resb 8
   536 00000008 ??????                  deExt:		resb 3
   537 0000000B ??                      deAttrib:	resb 1
   538 0000000C ????????????????        		resb 8
   539 00000014 ????                    deClusterHigh:	resw 1
   540 00000016 ????                    deTime:		resw 1
   541 00000018 ????                    deDate:		resw 1
   542 0000001A ????                    deClusterLow:	resw 1
   543 0000001C ????????                deSize:		resd 1
   544                                  	endstruc
   545                                  
   546                                  ATTR_READONLY	equ 1
   547                                  ATTR_HIDDEN	equ 2
   548                                  ATTR_SYSTEM	equ 4
   549                                  ATTR_VOLLABEL	equ 8
   550                                  ATTR_DIRECTORY	equ 10h
   551                                  ATTR_ARCHIVE	equ 20h
   552                                  
   553                                  
   554                                  ; use byte-offset addressing from BP for smaller code
   555                                  %define	VAR(x)	((x) - start) + bp
   556                                  
   557                                  
   558                                  	cpu 8086
   559                                  ; bootsector loaded at address 07C00h, addressable using 0000h:7C00h
   560                                  	org POSITION
   561                                  start:
   562                                  
   563                                  
   564                                  %define _LASTVARIABLE start
   565                                  	%macro nextvariable 2-3.nolist
   566                                  %1	equ (_LASTVARIABLE - %2)
   567                                  %define _LASTVARIABLE %1
   568                                  %ifidn %3, relocatestart
   569                                   %define _RELOCATESTART %1
   570                                  %elifempty %3
   571                                  %else
   572                                   %error Invalid third parameter
   573                                  %endif
   574                                  	%endmacro
   575                                  
   576                                  ; Variables
   577                                  
   578                                  ; (dword) sector where the first cluster's data starts
   579                                  	nextvariable data_start, 4
   580                                  
   581                                  ; (word) current load segment (points behind last loaded data)
   582                                  	nextvariable load_seg, 2
   583                                  
   584                                  ; (word) segment of FAT buffer
   585                                  ; for FAT12 this holds the entire FAT
   586                                  ; for FAT16 this holds the sector given by wo[fat_sector]
   587                                  ; for FAT32 this holds the sector given by dwo[fat_sector]
   588                                  	nextvariable fat_seg, 2
   589                                  
   590                                  ; (word for FAT16) currently loaded sector-in-FAT, -1 if none
   591                                  	nextvariable fat_sector, 4
   592                                  
   593                                  ; (word for FAT12/FAT16) first cluster of load file
   594                                  	nextvariable first_cluster, 4, relocatestart
   595                                  
   596                                  ADR_STACK_START	equ	_LASTVARIABLE -start+POSITION
   597                                  
   598                                  %ifn _FIX_CLUSTER_SIZE
   599                                  ; (word) actual sectors per cluster
   600                                  	nextvariable adj_sectors_per_cluster, 2, relocatestart
   601                                  %endif
   602                                  
   603                                  %ifn _FIX_SECTOR_SIZE
   604                                  ; (word) number of 16-byte paragraphs per sector
   605                                  	nextvariable para_per_sector, 2, relocatestart
   606                                  %endif
   607                                  
   608                                  %ifn ! _RELOCATE && _LOAD_ADR < ADR_FREE_UNTIL && _FIX_SECTOR_SIZE
   609                                  ; (word) segment of last available memory for sector
   610                                  	nextvariable last_available_sector, 2
   611                                  %endif
   612                                  
   613                                  lowest_variable		equ _LASTVARIABLE
   614                                  
   615                                  
   616 00000000 EB47                    	jmp strict short skip_bpb
   617                                  %if !_CHS && _LBA_SET_TYPE
   618                                  	db 0Eh		; LBA-enabled FAT16 FS partition type
   619                                  %else
   620 00000002 90                      	nop		; default: no LBA
   621                                  %endif
   622                                  
   623                                  
   624                                  ; BIOS Parameter Block (BPB)
   625                                  ;
   626                                  ; Installation will use the BPB already present in your file system.
   627                                  ; These values must be initialised when installing the loader.
   628                                  ;
   629                                  ; The values shown here work only with 1440 KiB disks (CHS=80:2:18)
   630                                  
   631                                  oem_id:			; offset 03h (03) - not used by this code
   632 00000003 202020206C444F53        	fill 8,_OEM_NAME_FILL,db _OEM_NAME
   633                                  bytes_per_sector:	; offset 0Bh (11) - refer to _FIX_SECTOR_SIZE !
   634 0000000B 0002                    	dw _BPS
   635                                  sectors_per_cluster:	; offset 0Dh (13) - refer to _FIX_CLUSTER_SIZE !
   636 0000000D 01                      	db _SPC & 255
   637                                  fat_start:
   638                                  num_reserved_sectors:	; offset 0Eh (14)
   639 0000000E 0100                    	dw _NUMRESERVED
   640                                  num_fats:		; offset 10h (16)
   641 00000010 02                      	db _NUMFATS
   642                                  num_root_dir_ents:	; offset 11h (17)
   643 00000011 E000                    	dw _NUMROOT
   644                                  total_sectors:		; offset 13h (19) - not used by this code
   645                                  %if _SPI < 1_0000h
   646 00000013 400B                    	dw _SPI
   647                                  %else
   648                                  	dw 0
   649                                  %endif
   650                                  media_id:		; offset 15h (21) - not used by this code
   651 00000015 F0                      	db _MEDIAID
   652                                  sectors_per_fat:	; offset 16h (22)
   653 00000016 0900                    	dw _SPF
   654                                  sectors_per_track:	; offset 18h (24)
   655 00000018 1200                    	dw _CHS_SECTORS
   656                                  heads:			; offset 1Ah (26)
   657 0000001A 0200                    	dw _CHS_HEADS
   658                                  hidden_sectors:		; offset 1Ch (28)
   659 0000001C 00000000                	dd _HIDDEN
   660                                  total_sectors_large:	; offset 20h (32) - not used by this code
   661                                  %if _SPI >= 1_0000h
   662                                  	dd _SPI
   663                                  %else
   664 00000020 00000000                	dd 0
   665                                  %endif
   666                                  
   667                                  ; Extended BPB
   668                                  
   669 00000024 00                      boot_unit:		db _UNIT
   670 00000025 00                      			db 0
   671 00000026 29                      ext_bpb_signature:	db 29h
   672 00000027 00000000                serial_number:		dd _VOLUMEID
   673 0000002B 6C444F5320<rep 7h>      volume_label:		fill 11,32,db _DEFAULT_LABEL
   674 00000036 464154313220-           filesystem_identifier:	fill 8,32,db "FAT1",'2'+4*!!_FAT16
   674 00000036 <rep 3h>           
   675                                  
   676                                  
   677                                  ; Initialised data
   678                                  
   679                                  load_name:
   680 0000003E 4C444542554720-         	fill 8,32,db _LOAD_NAME
   680 0000003E <rep 2h>           
   681 00000046 434F4D                  	fill 3,32,db _LOAD_EXT
   682                                  %if _ADD_SEARCH
   683                                  add_name:
   684                                  	fill 8,32,db _ADD_NAME
   685                                  	fill 3,32,db _ADD_EXT
   686                                  
   687                                  	; align 2
   688                                  	;  This happens to be aligned anyway. But even if
   689                                  	;  it didn't, we'd rather save that byte than use
   690                                  	;  it to align these fields. So comment this out.
   691                                  filename:
   692                                  	dw add_name
   693                                  dirseg:
   694                                  	dw _ADD_DIR_SEG
   695                                  %endif
   696                                  
   697                                  
   698                                  	numdef TMPINC, 0
   699                                  
   700                                  %if _TMPINC
   701                                   [list -]
   702                                  %endif
   703                                  		%imacro errorhandler 0
   704                                  %if _TMPINC
   705                                   %include "error.tmp"
   706                                   [list -]
   707                                  %else
   708                                  ; === error.tmp ===
   709                                  read_sector.err:
   710                                  	mov al, 'R'	; Disk 'R'ead error
   711                                  %if ! _MEMORY_CONTINUE || _RELOCATE || _LOAD_ADR >= ADR_FREE_FROM
   712                                  	db __TEST_IMM16	; (skip mov)
   713                                  error_filetoobig:
   714                                  error_memory:
   715                                  	mov al,'M'	; Not enough 'M'emory
   716                                  %endif
   717                                  
   718                                  error:
   719                                  %if _RELOCATE
   720                                  	mov bx, 7
   721                                  	mov ds, bx
   722                                  	mov bh, [462h - 70h]
   723                                  %else
   724                                  	mov bh, [462h]
   725                                  	mov bl, 7
   726                                  %endif
   727                                  	mov ah, 0Eh
   728                                  	int 10h		; display character
   729                                  	mov al, 07h
   730                                  	int 10h		; beep!
   731                                  
   732                                  	xor ax, ax	; await key pressed
   733                                  	int 16h
   734                                  
   735                                  	int 19h		; re-start the boot process
   736                                  ; === eof ===
   737                                  %endif
   738                                  %if _TMPINC
   739                                   [list +]
   740                                  %endif
   741                                  		%endmacro
   742                                  
   743                                  
   744                                  		%imacro readhandler 0
   745                                  %if _TMPINC
   746                                   %include "read.tmp"
   747                                   [list -]
   748                                  %else
   749                                  ; === read.tmp ===
   750                                  		; INP:	dx:ax = sector
   751                                  		; OUT:	only if successful
   752                                  		;	dx:ax = incremented
   753                                  		;	bx => behind read sector
   754                                  		;	es = ADR_FATBUF>>4 = ADR_DIRBUF>>4
   755                                  		; CHG:	-
   756                                  %if ! _RELOCATE
   757                                   %if ADR_DIRBUF == ADR_FATBUF
   758                                  read_sector_dirbuf:
   759                                   %endif
   760                                  read_sector_fatbuf:
   761                                  	mov bx, ADR_FATBUF>>4
   762                                  %endif
   763                                  
   764                                  		; Read a sector using Int13.02 or Int13.42
   765                                  		;
   766                                  		; INP:	dx:ax = sector number within partition
   767                                  		;	bx => buffer
   768                                  		;	(_LBA) ds = ss
   769                                  		; OUT:	If unable to read,
   770                                  		;	 ! jumps to error instead of returning
   771                                  		;	If sector has been read,
   772                                  		;	 dx:ax = next sector number (has been incremented)
   773                                  		;	 bx => next buffer (bx = es+word[para_per_sector])
   774                                  		;	 es = input bx
   775                                  		; CHG:	-
   776                                  read_sector:
   777                                  	push dx
   778                                  	push cx
   779                                  	push ax
   780                                  	push si
   781                                  
   782                                  	push bx
   783                                  
   784                                  ; DX:AX==LBA sector number
   785                                  ; add partition start (= number of hidden sectors)
   786                                  		add ax,[VAR(hidden_sectors + 0)]
   787                                  		adc dx,[VAR(hidden_sectors + 2)]
   788                                   %if (!_LBA || !_LBA_33_BIT) && _LBA_CHECK_NO_33
   789                                  	jc .err
   790                                   %endif
   791                                  %if _LBA		; +70 bytes (with CHS, +63 bytes without CHS)
   792                                   %if _LBA_33_BIT
   793                                  	sbb si, si	; -1 if was CY, 0 else
   794                                  	neg si		; 1 if was CY, 0 else
   795                                   %endif
   796                                  	xor cx, cx
   797                                  	push cx		; highest word = 0
   798                                   %if _LBA_33_BIT
   799                                  	push si		; bit 32 = 1 if operating in 33-bit space
   800                                   %else
   801                                  	push cx		; second highest word = 0
   802                                   %endif
   803                                  	push dx
   804                                  	push ax		; = qword sector number
   805                                  	push bx
   806                                  	push cx		; bx => buffer
   807                                  	inc cx
   808                                  	push cx		; word number of sectors to read
   809                                  	mov cl, 10h
   810                                  	push cx		; word size of disk address packet
   811                                  	mov si, sp	; ds:si -> disk address packet (on stack)
   812                                  
   813                                   %if _LBA_SKIP_CHECK		; -14 bytes
   814                                  	mov dl, [VAR(boot_unit)]
   815                                   %else
   816                                  	mov ah, 41h
   817                                  	mov dl, [VAR(boot_unit)]
   818                                  	mov bx, 55AAh
   819                                  	stc
   820                                  	int 13h		; 13.41.bx=55AA extensions installation check
   821                                  	jc .no_lba
   822                                  	cmp bx, 0AA55h
   823                                  	jne .no_lba
   824                                  	test cl, 1	; support bitmap bit 0
   825                                  	jz .no_lba
   826                                   %endif
   827                                  
   828                                  %if _LBA_RETRY
   829                                  	mov ah, 42h
   830                                  	int 13h		; 13.42 extensions read
   831                                  	jnc .lba_done
   832                                  
   833                                  	xor ax, ax
   834                                  	int 13h
   835                                  
   836                                  		; have to reset the LBAPACKET's lpCount, as the handler may
   837                                  		;  set it to "the number of blocks successfully transferred".
   838                                  		; (in any case, the high byte is still zero.)
   839                                  	mov byte [si + 2], 1
   840                                  %endif
   841                                  
   842                                  	mov ah, 42h
   843                                  	int 13h
   844                                   %if _LBA_SKIP_CHECK && _CHS
   845                                  	jc .lba_check_error_1
   846                                   %else
   847                                  .cy_err:
   848                                  	jc .lba_error
   849                                   %endif
   850                                  
   851                                  .lba_done:
   852                                  %if _CHS && _LBA_SET_TYPE
   853                                  	mov byte [bp + 2], 0Eh	; LBA-enabled FAT16 FS partition type
   854                                  %endif
   855                                  	add sp, 10h
   856                                  	pop bx
   857                                  	mov es, bx
   858                                  %if _CHS
   859                                  	jmp short .chs_done
   860                                  %endif
   861                                  
   862                                  .lba_error: equ .err
   863                                  
   864                                   %if !_CHS
   865                                  .no_lba: equ .err
   866                                   %else
   867                                   %if _LBA_SKIP_CHECK
   868                                  .lba_check_error_1:
   869                                  	cmp ah, 1	; invalid function?
   870                                  	jne .lba_error	; no, other error -->
   871                                  			; try CHS instead
   872                                  .cy_err: equ .err
   873                                   %endif
   874                                  .no_lba:
   875                                  	add sp, 8
   876                                  	pop cx		; cx = low word of sector
   877                                  	pop ax
   878                                  	pop dx		; dx:ax = middle two words of sector
   879                                  		; Here dx <= 1 if _LBA_33_BIT, else zero.
   880                                  		;  If dx is nonzero then the CHS calculation
   881                                  		;  should fail. If CHS sectors is equal to 1
   882                                  		;  (very unusual) then the div may fail. Else,
   883                                  		;  we will detect a cylinder > 1023 eventually.
   884                                  	pop si		; discard highest word of qword
   885                                   %endif
   886                                  %endif
   887                                  
   888                                  %if !_LBA
   889                                  .cy_err:	equ .err
   890                                  %endif
   891                                  
   892                                  %if _CHS		; +70 bytes
   893                                  ; dx:ax = LBA sector number, (if _LBA) cx = 0
   894                                  ; divide by number of sectors per track to get sector number
   895                                  ; Use 32:16 DIV instead of 64:32 DIV for 8088 compatability
   896                                  ; Use two-step 32:16 divide to avoid overflow
   897                                   %if !_LBA
   898                                  			xchg cx, ax	; cx = low word of sector, clobbers ax
   899                                  			xchg ax, dx	; ax = high word of sector, clobbers dx
   900                                  			xor dx, dx	; dx:ax = high word of sector
   901                                   %else
   902                                  	; from the .no_lba popping we already have:
   903                                  	;  cx = low word of sector
   904                                  	;  dx:ax = high word of sector
   905                                   %endif
   906                                  			div word [VAR(sectors_per_track)]
   907                                  			xchg cx,ax
   908                                  			div word [VAR(sectors_per_track)]
   909                                  			xchg cx,dx
   910                                  
   911                                  ; DX:AX=quotient, CX=remainder=sector (S) - 1
   912                                  ; divide quotient by number of heads
   913                                  			xchg bx, ax	; bx = low word of quotient, clobbers ax
   914                                  			xchg ax, dx	; ax = high word of quotient, clobbers dx
   915                                  			xor dx, dx	; dx = 0
   916                                  			div word [VAR(heads)]
   917                                  					; ax = high / heads, dx = high % heads
   918                                  			xchg bx, ax	; bx = high / heads, ax = low quotient
   919                                  			div word [VAR(heads)]
   920                                  
   921                                  ; bx:ax=quotient=cylinder (C), dx=remainder=head (H)
   922                                  ; move variables into registers for INT 13h AH=02h
   923                                  			mov dh, dl	; dh = head
   924                                  			inc cx		; cl5:0 = sector
   925                                  			xchg ch, al	; ch = cylinder 7:0, al = 0
   926                                  			shr ax, 1
   927                                  			shr ax, 1	; al7:6 = cylinder 9:8
   928                                  	; bx has bits set iff it's > 0, indicating a cylinder >= 65536.
   929                                  			 or bl, bh	; collect set bits from bh
   930                                  			or cl, al	; cl7:6 = cylinder 9:8
   931                                  	; ah has bits set iff it was >= 4, indicating a cylinder >= 1024.
   932                                  			 or bl, ah	; collect set bits from ah
   933                                  			mov dl,[VAR(boot_unit)] ; dl = drive
   934                                  .nz_err:
   935                                  			 jnz .err	; error if cylinder >= 1024 -->
   936                                  					; ! bx = 0 (for 13.02 call)
   937                                  
   938                                  ; we call INT 13h AH=02h once for each sector. Multi-sector reads
   939                                  ; may fail if we cross a track or 64K boundary
   940                                  			pop es
   941                                  %if _CHS_RETRY_REPEAT
   942                                  			mov si, _CHS_RETRY_REPEAT + 1
   943                                  .loop_chs_retry_repeat:
   944                                  			mov ax, 0201h
   945                                  			int 13h		; read one sector
   946                                  			jnc .done
   947                                  			xor ax, ax
   948                                  			int 13h		; reset disk
   949                                  			dec si		; another attempt ?
   950                                  			jnz .loop_chs_retry_repeat	; yes -->
   951                                  			jmp .err
   952                                  %else
   953                                   %if _CHS_RETRY
   954                                  			mov ax, 0201h
   955                                  			int 13h		; read one sector
   956                                  			jnc .done
   957                                  ; reset drive
   958                                  			xor ax, ax
   959                                  			int 13h
   960                                   %endif
   961                                  ; try read again
   962                                  			mov ax, 0201h
   963                                  			int 13h
   964                                   %if _LBA_SKIP_CHECK
   965                                  			inc bx
   966                                  			jc .nz_err
   967                                   %else
   968                                  			jc .cy_err
   969                                   %endif
   970                                  %endif
   971                                  
   972                                  .done:
   973                                  ; increment segment
   974                                  	mov bx, es
   975                                  %endif
   976                                  
   977                                  .chs_done:
   978                                  %if _FIX_SECTOR_SIZE
   979                                  	add bx, _FIX_SECTOR_SIZE >> 4
   980                                  %else
   981                                  	add bx, [VAR(para_per_sector)]
   982                                  %endif
   983                                  
   984                                  	pop si
   985                                  	pop ax
   986                                  	pop cx
   987                                  	pop dx
   988                                  ; increment LBA sector number
   989                                  	inc ax
   990                                  	jne @F
   991                                  	inc dx
   992                                  @@:
   993                                  
   994                                  	retn
   995                                  ; === eof ===
   996                                  %endif
   997                                  %if _TMPINC
   998                                   [list +]
   999                                  %endif
  1000                                  		%endmacro
  1001                                  %if _TMPINC
  1002                                   [list +]
  1003                                  %endif
  1004                                  
  1005                                  
  1006                                  ; Code
  1007                                  
  1008                                  skip_bpb:
  1009 00000049 FA                      	cli
  1010 0000004A FC                      	cld
  1011 0000004B 31C9                    	 xor cx, cx
  1012 0000004D BD[0000]                	mov bp, start		; magic bytes - checked by instsect
  1013 00000050 8ED1                    	 mov ss, cx
  1014 00000052 BCF07B                  	mov sp, ADR_STACK_START
  1015                                  %if _USE_AUTO_UNIT
  1016 00000055 885624                  	mov [VAR(boot_unit)], dl; magic bytes - checked by instsect
  1017                                  %else
  1018                                  	mov dl, [VAR(boot_unit)]; magic bytes - checked by instsect
  1019                                  %endif
  1020                                  
  1021                                  	; Note:	es is left uninitialised here until the first call to
  1022                                  	;	 read_sector if the below conditional is false.
  1023                                  %if _USE_PART_INFO	; +19 bytes
  1024 00000058 8EC1                    	 mov es, cx
  1025                                  ; Note:	Award Medallion BIOS v6.0 (ASUS MED 2001 ACPI BIOS Revision 1009)
  1026                                  ;	 loads from a floppy disk drive with ds:si = 0F000h:0A92Dh ->
  1027                                  ;	 FF FF FF FF 08 00 08 01 FF FF FF FF FF FF FF FF, which was detected
  1028                                  ;	 as a valid partition table entry by this handling. Therefore, we
  1029                                  ;	 only accept partition information when booting from a hard disk now.
  1030 0000005A 84D2                    	test dl, dl		; floppy ?
  1031 0000005C 7911                    	jns @F			; don't attempt detection -->
  1032                                  ; Check whether an MBR left us partition information.
  1033                                  ; byte[ds:si] bit 7 means active and must be set if valid.
  1034 0000005E 380C                    	cmp byte [si], cl	; flags for xx-00h (result is xx), SF = bit 7
  1035 00000060 790D                    	jns @F			; xx < 80h, ie info invalid -->
  1036                                  ; byte[ds:si+4] is the file system type. Check for valid one.
  1037 00000062 384C04                  	cmp byte [si+4], cl	; is it zero?
  1038 00000065 7408                    	je @F			; yes, info invalid -->
  1039                                  ; Info valid, trust their hidden sectors over hardcoded.
  1040                                  ; Assume the movsw instructions won't run with si = FFFFh.
  1041 00000067 BF[1C00]                	mov di, hidden_sectors	; -> BPB field
  1042 0000006A 83C608                  	add si, 8		; -> partition start sector in info
  1043 0000006D A5                      	movsw
  1044 0000006E A5                      	movsw			; overwrite BPB field with value from info
  1045                                  @@:
  1046                                  %endif
  1047 0000006F 8ED9                    	mov ds, cx
  1048 00000071 FB                      	sti
  1049                                  
  1050                                  
  1051                                  %if _QUERY_GEOMETRY	; +27 bytes
  1052                                  ;	test dl, dl		; floppy?
  1053                                  ;	jns @F			; don't attempt query, might fail -->
  1054                                  	; Note that while the original PC BIOS doesn't support this function
  1055                                  	;  (for its diskettes), it does properly return the error code 01h.
  1056                                  	; https://sites.google.com/site/pcdosretro/ibmpcbios (IBM PC version 1)
  1057 00000072 B408                    	mov ah, 08h
  1058                                  	; xor cx, cx		; initialise cl to 0
  1059                                  	; Already from prologue cx = 0.
  1060 00000074 F9                      	stc			; initialise to CY
  1061 00000075 CD13                    	int 13h			; query drive geometry
  1062 00000077 720E                    	jc @F			; apparently failed -->
  1063 00000079 83E13F                  	and cx, 3Fh		; get sectors
  1064 0000007C 7409                    	jz @F			; invalid (S is 1-based), don't use -->
  1065 0000007E 894E18                  	mov [VAR(sectors_per_track)], cx
  1066 00000081 88F1                    	mov cl, dh		; cx = maximum head number
  1067 00000083 41                      	inc cx			; cx = number of heads (H is 0-based)
  1068 00000084 894E1A                  	mov [VAR(heads)], cx
  1069                                  @@:
  1070                                  %endif
  1071                                  
  1072                                  %if _FIX_SECTOR_SIZE
  1073                                   %if !_FIX_SECTOR_SIZE_SKIP_CHECK
  1074                                  	cmp word [VAR(bytes_per_sector)], _FIX_SECTOR_SIZE
  1075                                  	mov al, 'S'
  1076                                  	jne error
  1077                                   %endif
  1078                                  	mov bx, _FIX_SECTOR_SIZE >> 5
  1079                                   %if _FIX_CLUSTER_SIZE
  1080                                    %if !_FIX_CLUSTER_SIZE_SKIP_CHECK
  1081                                  	cmp byte [VAR(sectors_per_cluster)], _FIX_CLUSTER_SIZE & 0FFh
  1082                                  	mov al, 'C'
  1083                                  	jne error
  1084                                    %endif
  1085                                   %else
  1086                                  ; calculate some values that we need:
  1087                                  ; adjusted sectors per cluster (store in a word,
  1088                                  ;  and decode EDR-DOS's special value 0 meaning 256)
  1089                                  	xor ax, ax
  1090                                  	mov al, [VAR(sectors_per_cluster)]
  1091                                  	dec al
  1092                                  	inc ax
  1093                                  	push ax			; push into word [VAR(adj_sectors_per_cluster)]
  1094                                   %endif
  1095                                  	mov ch, 0			; ! ch = 0
  1096                                  %else
  1097                                  ; 16-byte paragraphs per sector
  1098 00000087 8B5E0B                  	mov bx,[VAR(bytes_per_sector)]
  1099 0000008A B90400                  	mov cx,4			; ! ch = 0
  1100 0000008D D3EB                    	shr bx,cl
  1101                                   %if _FIX_CLUSTER_SIZE
  1102                                    %if !_FIX_CLUSTER_SIZE_SKIP_CHECK
  1103                                  	cmp byte [VAR(sectors_per_cluster)], _FIX_CLUSTER_SIZE & 0FFh
  1104                                  	mov al, 'C'
  1105                                  	jne error
  1106                                    %endif
  1107                                   %else
  1108                                  ; calculate some values that we need:
  1109                                  ; adjusted sectors per cluster (store in a word,
  1110                                  ;  and decode EDR-DOS's special value 0 meaning 256)
  1111                                  				; ! ch = 0
  1112 0000008F 8A4E0D                  	mov cl, [VAR(sectors_per_cluster)]
  1113                                  				; therefore cx = sectors_per_cluster
  1114 00000092 FEC9                    	dec cl
  1115 00000094 41                      	inc cx
  1116 00000095 51                      	push cx			; push into word [VAR(adj_sectors_per_cluster)]
  1117 00000096 49                      	dec cx			; ! ch = 0
  1118                                   %endif
  1119 00000097 53                      	push bx				; push into word [VAR(para_per_sector)]
  1120                                  
  1121                                  ; 32-byte FAT directory entries per sector
  1122 00000098 D1EB                    	shr bx, 1			; /2 = 32-byte entries per sector
  1123                                  %endif
  1124                                  
  1125                                  ; number of sectors used for root directory (store in CX)
  1126 0000009A 8B7611                  	mov si, [VAR(num_root_dir_ents)]
  1127 0000009D 89D8                    	mov ax, bx
  1128                                  		; The ax value here is the last value of bx, which is set
  1129                                  		;  by the shr instruction. Therefore, it cannot be higher
  1130                                  		;  than 7FFFh, so this cwd instruction always zeros dx.
  1131 0000009F 99                      	cwd
  1132 000000A0 48                      	dec ax				; rounding up
  1133 000000A1 01F0                    	add ax, si			; from BPB
  1134 000000A3 11D2                    	adc dx, dx			; account for overflow (dx was zero)
  1135 000000A5 F7F3                    	div bx				; get number of root sectors
  1136 000000A7 91                      	xchg ax, cx			; cx = number of root secs, ! ah = 0
  1137                                  
  1138                                  ; first sector of root directory
  1139 000000A8 8A4610                  	mov al,[VAR(num_fats)]		; ! ah = 0, hence ax = number of FATs
  1140 000000AB F76616                  	mul word [VAR(sectors_per_fat)]
  1141 000000AE 03460E                  	add ax,[VAR(num_reserved_sectors)]
  1142 000000B1 10F2                    	adc dl, dh			; account for overflow (dh was and is 0)
  1143                                  
  1144 000000B3 31FF                    	xor di, di
  1145                                  
  1146                                  ; first sector of disk data area:
  1147 000000B5 01C1                    	add cx, ax
  1148 000000B7 11D7                    	adc di, dx
  1149 000000B9 894EFC                  	mov [VAR(data_start)], cx
  1150 000000BC 897EFE                  	mov [VAR(data_start+2)], di
  1151                                  
  1152                                  next_dir_search:
  1153                                  %if _ADD_SEARCH
  1154                                  	push dx
  1155                                  	push ax
  1156                                  	push si
  1157                                  %endif
  1158                                  
  1159                                  ; Scan root directory for file. We don't bother to check for deleted
  1160                                  ;  entries (E5h) or entries that mark the end of the directory (00h).
  1161                                  		; number of root entries in si here
  1162                                  next_sect:
  1163 000000BF 89D9                    	mov cx, bx		; entries per sector as loop counter
  1164                                  %if ! _RELOCATE
  1165                                   %if ADR_DIRBUF == ADR_FATBUF
  1166 000000C1 E8AC00                  	call read_sector_dirbuf
  1167                                   %else
  1168                                  	mov bx, ADR_DIRBUF>>4
  1169                                  	call read_sector
  1170                                   %endif
  1171                                  %else
  1172                                  	mov bx, ADR_DIRBUF>>4
  1173                                  	call read_sector
  1174                                  %endif
  1175 000000C4 89CB                    	mov bx, cx		; restore bx for next iteration later
  1176                                  
  1177 000000C6 31FF                    	xor di, di		; es:di-> first entry in this sector
  1178                                  next_ent:
  1179 000000C8 56                      	push si
  1180 000000C9 57                      	push di
  1181 000000CA 51                      	push cx
  1182                                  %if _ADD_SEARCH
  1183                                  	mov si, [VAR(filename)]
  1184                                  %else
  1185 000000CB BE[3E00]                	mov si, load_name	; ds:si-> name to match
  1186                                  %endif
  1187 000000CE B90B00                  	mov cx, 11		; length of padded 8.3 FAT filename
  1188 000000D1 F3A6                    	repe cmpsb		; check entry
  1189 000000D3 59                      	pop cx
  1190 000000D4 5F                      	pop di
  1191 000000D5 5E                      	pop si
  1192 000000D6 8D7D20                  	lea di, [di + DIRENTRY_size]
  1193 000000D9 7409                    	je found_it		; found entry -->
  1194                                  
  1195 000000DB 4E                      	dec si			; count down entire root's entries
  1196 000000DC E0EA                    	loopnz next_ent		; count down sector's entries (jumps iff si >0 && cx >0)
  1197 000000DE 75DF                    	jnz next_sect		; (jumps iff si >0 && cx ==0)
  1198                                  				; ends up here iff si ==0
  1199                                  				;  ie all root entries checked unsuccessfully
  1200                                  
  1201 000000E0 B046                    	mov al,'F'	; File not 'F'ound
  1202 000000E2 EB78                    	jmp error
  1203                                  
  1204                                  found_it:
  1205                                  %if _ADD_SEARCH || _LOAD_DIR_SEG
  1206                                  	mov cx, 32
  1207                                  	mov ax, _LOAD_DIR_SEG
  1208                                  	sub di, cx		; es:di -> dir entry
  1209                                   %if _ADD_SEARCH
  1210                                  	xchg ax, word [VAR(dirseg)]
  1211                                   %endif
  1212                                  	push ds
  1213                                  	mov si, di
  1214                                  	push di
  1215                                  	 push es
  1216                                  	 pop ds			; ds:si -> dir entry
  1217                                  	mov es, ax
  1218                                  	xor di, di		; es:di -> destination
  1219                                  	rep movsb		; store dir entry
  1220                                  	 push ds
  1221                                  	 pop es
  1222                                  	pop di			; restore es:di -> dir entry
  1223                                  	pop ds
  1224                                   %if _ADD_SEARCH
  1225                                  %if ((load_name - start) & 0FF00h) == ((add_name - start) & 0FF00h)
  1226                                  	mov byte [VAR(filename)], (load_name - start + 7C00h) & 255
  1227                                  %else
  1228                                  	mov word [VAR(filename)], load_name
  1229                                  %endif
  1230                                  				; update name to second iteration's
  1231                                  %if (_LOAD_DIR_SEG & 255) != (_ADD_DIR_SEG & 255)
  1232                                  	cmp al, _ADD_DIR_SEG & 255
  1233                                  %elif (_LOAD_DIR_SEG) != (_ADD_DIR_SEG)
  1234                                  	cmp ax, _ADD_DIR_SEG	; was first iteration ?
  1235                                  %else
  1236                                   %error Must not store directory entries to same segment
  1237                                  %endif
  1238                                  	pop si
  1239                                  	pop ax
  1240                                  	pop dx			; restore root start and count
  1241                                  				;  (bx still holds entries per sector)
  1242                                  	je next_dir_search	; jump to search load file next -->
  1243                                   %endif
  1244                                   %if _RELOCATE
  1245                                  	push word [es:di + deClusterLow]
  1246                                  			; (word on stack) = first cluster number
  1247                                   %endif
  1248                                  %else
  1249                                   %if _DIR_ENTRY_500	; +24 bytes, probably
  1250                                  	mov cx, 32
  1251                                  	 push ds
  1252                                  	 push es
  1253                                  	xchg si, di
  1254                                  	sub si, cx
  1255                                  	push es
  1256                                  	pop ds		; ds:si -> directory entry
  1257                                  	xor ax, ax
  1258                                  	mov es, ax
  1259                                  	mov di, 500h	; es:di -> 0:500h
  1260                                    %if _DIR_ENTRY_520
  1261                                  	rep movsw	; move to here (two directory entries)
  1262                                    %else
  1263                                  	rep movsb	; move to here
  1264                                    %endif
  1265                                  	 pop es
  1266                                  	 pop ds
  1267                                  	xchg si, di	; es:di -> behind (second) directory entry
  1268                                   %endif
  1269                                   %if _RELOCATE
  1270                                  	push word [es:di + deClusterLow - DIRENTRY_size 		- (DIRENTRY_size * !!_DIR_ENTRY_520)]
  1272                                  			; (word on stack) = first cluster number
  1273                                   %endif
  1274                                  %endif
  1275                                  
  1276                                  %if _RELOCATE || _LOAD_ADR >= ADR_FREE_FROM
  1277                                  ; Get conventional memory size and store it
  1278                                  		int 12h
  1279                                  		mov cl, 6
  1280                                  		shl ax, cl
  1281                                   %if _RPL		; +31 bytes
  1282                                  	xchg dx, ax
  1283                                  	lds si, [4 * 2Fh]
  1284                                  	add si, 3
  1285                                  	lodsb
  1286                                  	cmp al, 'R'
  1287                                  	jne .no_rpl
  1288                                  	lodsb
  1289                                  	cmp al, 'P'
  1290                                  	jne .no_rpl
  1291                                  	lodsb
  1292                                  	cmp al, 'L'
  1293                                  	jne .no_rpl
  1294                                  	mov ax, 4A06h
  1295                                  	int 2Fh
  1296                                  .no_rpl:
  1297                                  	push ss
  1298                                  	pop ds
  1299                                  	xchg ax, dx
  1300                                   %endif
  1301                                   %if _RELOCATE
  1302                                    %if _LOAD_NON_FAT
  1303                                  	sub ax, (_RPL_GRACE_AREA + 20 * 1024 		+ 512 + 7C00h) >> 4
  1305                                    %else
  1306                                  	sub ax, (_RPL_GRACE_AREA + 20 * 1024 		+ 8192 + (8192-16) + 512 + 7C00h) >> 4
  1308                                    %endif
  1309                                  		; RPL grace	area preserved for RPL
  1310                                  		; 20 KiB:	reserved for iniload
  1311                                  		; 8 KiB:	FAT buffer
  1312                                  		; 8 KiB - 16 B:	to allow rounding down FAT buffer position
  1313                                  		; 512:		sector
  1314                                  		; 7C00h:	stack and to allow addressing with 7C00h in bp
  1315                                  		;
  1316                                  		; Note also that by addressing the stack and sector
  1317                                  		;  with bp at 7C00h, and insuring this subtraction doesn't
  1318                                  		;  underflow, makes sure that we do not overwrite the IVT or
  1319                                  		;  BDA. (However, we assume that ax is at least 60h or so.)
  1320                                  		;
  1321                                  		; The FAT buffer segment is masked so that the actual buffer
  1322                                  		;  is stored on an 8 KiB boundary. This is to ensure that
  1323                                  		;  the buffer doesn't possibly cross a 64 KiB DMA boundary.
  1324                                  	jc .error_memory_j_CY
  1325                                  	cmp ax, (end -start+7C00h - ADR_STACK_LOW + 15) >> 4
  1326                                  		; This check is to ensure that the start of the destination
  1327                                  		;  for the relocation (stack, sector) is
  1328                                  		;  above-or-equal the end of the source for the relocation.
  1329                                  		;  That is, to avoid overwriting any of the source with the
  1330                                  		;  string move instruction (which for simplicity is always UP).
  1331                                  .error_memory_j_CY:
  1332                                  	jb error_memory
  1333                                    %if ! _LOAD_NON_FAT
  1334                                  	mov bx, ((8192 - 16) + 512 + 7C00h)>>4
  1335                                  	add bx, ax
  1336                                  ;  this is like calculating the following for the bx value:
  1337                                  ;  ((LMA_top - 20 KiB - 8 KiB - (8 KiB - 16 B) - 512 - 7C00h) + ;	((8 KiB - 16 B) + 512 + 7C00h))>>4
  1339                                  ;  == (LMA_top - 20 KiB - 8 KiB)>>4
  1340                                  	and bx, ~ ((8192>>4) - 1)
  1341                                  	mov word [VAR(fat_seg)], bx
  1342                                    %endif
  1343                                  
  1344                                  	mov di, relocated
  1345                                  	push ax
  1346                                  	push di				; -> relocation target label relocated
  1347                                  		; (We cannot use a near call here to push the target IP
  1348                                  		;  because we do not control whether the ROM-BIOS loader
  1349                                  		;  entered us at 0:7C00h or 7C0h:0. So we need to create
  1350                                  		;  the correct offset manually here.)
  1351                                  
  1352                                  	mov es, ax			; => destination
  1353                                  	mov si, sp			; ds:si = ss:_RELOCATESTART - 2 - 4
  1354                                  	mov di, si			; es:di -> destination for stack low
  1355                                  	mov cx, (end - (_RELOCATESTART - 2 - 4)) >> 1
  1356                                  		; end is the top of used memory
  1357                                  		; _RELOCATESTART is the lowest filled stack frame slot
  1358                                  		; 2 is for the first cluster word on the stack
  1359                                  		; 4 is for the additional slots taken by the return address
  1360                                  	rep movsw			; relocate stack, sector
  1361                                  	retf				; jump to relocated code
  1362                                  
  1363                                  
  1364                                  	readhandler
  1365                                  
  1366                                  	errorhandler
  1367                                  
  1368                                  
  1369                                  relocated:
  1370                                  	mov ss, ax
  1371                                  	mov ds, ax			; relocate these
  1372                                  	add ax, (ADR_STACK_LOW) >> 4	; (rounding down) => behind available
  1373                                  
  1374                                  	pop si
  1375                                   %else
  1376                                  	sub ax, (_RPL_GRACE_AREA + 20 * 1024) >> 4
  1377                                  					; RPL grace area, plus
  1378                                  					;  20 KiB reserved for iniload
  1379                                  	jb error_memory
  1380                                   %endif
  1381                                  %elif _LOAD_ADR < ADR_FREE_UNTIL
  1382                                   %if !_FIX_SECTOR_SIZE
  1383 000000E4 B8A007                  	mov ax, ADR_FREE_UNTIL >> 4	; rounding *down*
  1384                                   %endif
  1385                                  %else
  1386                                   %error Load address within used memory
  1387                                  %endif
  1388                                  %if ! _RELOCATE && _LOAD_ADR < ADR_FREE_UNTIL && _FIX_SECTOR_SIZE
  1389                                  	; user of last_available_sector will hardcode the value!
  1390                                  %else
  1391                                   %if _FIX_SECTOR_SIZE
  1392                                  		sub ax, _FIX_SECTOR_SIZE >> 4
  1393                                   %else
  1394 000000E7 2B46EC                  		sub ax, [VAR(para_per_sector)]
  1395                                   %endif
  1396 000000EA 50                      		push ax		; push into word [VAR(last_available_sector)]
  1397                                  %endif
  1398                                  
  1399                                  ; get starting cluster of file
  1400                                  %if ! _RELOCATE
  1401                                   %if _ADD_SEARCH || _LOAD_DIR_SEG
  1402                                  		mov si,[es:di + deClusterLow]
  1403                                   %else
  1404 000000EB 268B75FA                		mov si,[es:di + deClusterLow - DIRENTRY_size 			- (DIRENTRY_size * !!_DIR_ENTRY_520)]
  1406                                   %endif
  1407                                  %endif
  1408                                  %if _SET_CLUSTER
  1409 000000EF 8976F0                  		mov word [VAR(first_cluster)], si
  1410                                  %endif
  1411                                  %if _SET_DI_CLUSTER
  1412                                  		push si			; remember cluster for later
  1413                                  %endif
  1414                                  
  1415                                  %if !_FAT16 && !_LOAD_NON_FAT
  1416                                  ; Load the entire FAT into memory. This is easily feasible for FAT12,
  1417                                  ;  as the FAT can only contain at most 4096 entries.
  1418                                  ; (The exact condition should be "at most 4087 entries", or with a
  1419                                  ;  specific FF7h semantic, "at most 4088 entries"; the more reliable
  1420                                  ;  and portable alternative would be "at most 4080 entries".)
  1421                                  ; Thus, no more than 6 KiB need to be read, even though the FAT size
  1422                                  ;  as indicated by word[sectors_per_fat] could be much higher. The
  1423                                  ;  first loop condition below is to correctly handle the latter case.
  1424                                  ; (Sector size is assumed to be a power of two between 32 and 8192
  1425                                  ;  bytes, inclusive. An 8 KiB buffer is necessary if the sector size
  1426                                  ;  is 4 or 8 KiB, because reading the FAT can or will write to 8 KiB
  1427                                  ;  of memory instead of only the relevant 6 KiB. This is always true
  1428                                  ;  if the sector size is 8 KiB, and with 4 KiB sector size it is true
  1429                                  ;  iff word[sectors_per_fat] is higher than one.)
  1430 000000F2 BF0018                  		mov di, 6 << 10		; maximum size of FAT12 to load
  1431 000000F5 8B4E16                  		mov cx, [VAR(sectors_per_fat)]
  1432                                  					; maximum size of this FS's FAT
  1433                                  %if ! _RELOCATE && _LOAD_ADR < ADR_FREE_UNTIL && !_FIX_SECTOR_SIZE
  1434                                  			; Under these conditions, ax here is
  1435                                  			;  below ADR_FREE_UNTIL >> 4 so the
  1436                                  			;  following cwd instruction zeros dx.
  1437 000000F8 99                      		cwd
  1438                                  %else
  1439                                  		xor dx, dx
  1440                                  %endif
  1441 000000F9 8B460E                  		mov ax, [VAR(fat_start)]; = first FAT sector
  1442                                   %if _RELOCATE
  1443                                  			; bx already = FAT buffer segment here
  1444                                   %else
  1445 000000FC BBE007                  		mov bx, ADR_FATBUF>>4
  1446                                    %if _SET_FAT_SEG
  1447 000000FF 895EF8                  		mov word [VAR(fat_seg)], bx
  1448                                    %endif
  1449                                   %endif
  1450                                  @@:
  1451 00000102 E86E00                  		call read_sector	; read next FAT sector
  1452                                  %if _FIX_SECTOR_SIZE
  1453                                  		sub di, _FIX_SECTOR_SIZE
  1454                                  %else
  1455 00000105 2B7E0B                  		sub di, [VAR(bytes_per_sector)]
  1456                                  %endif
  1457                                  					; di = bytes still left to read
  1458 00000108 7602                    		jbe @F			; if none -->
  1459                                  					; (jbe means jump if CF || ZF)
  1460 0000010A E2F6                    		loop @B			; if any FAT sector still remains -->
  1461                                  @@:					; one of the limits reached; FAT read
  1462                                  %endif
  1463                                  
  1464 0000010C BB0002                  		mov bx, _LOAD_ADR>>4	; => load address
  1465                                  %if _FAT16 && !_LOAD_NON_FAT
  1466                                  		mov di, -1		; = no FAT sector read yet
  1467                                   %if _SET_FAT_SEG
  1468                                    %if ! _RELOCATE
  1469                                  		mov word [VAR(fat_seg)], ADR_FATBUF>>4
  1470                                    %endif
  1471                                  		mov word [VAR(fat_sector)], di
  1472                                   %endif
  1473                                  %endif
  1474                                  %if _LOAD_NON_FAT
  1475                                   %if _SET_FAT_SEG
  1476                                    %if _FAT16
  1477                                  		or word [VAR(fat_sector)], -1
  1478                                    %else
  1479                                  		and word [VAR(fat_seg)], 0
  1480                                    %endif
  1481                                   %endif
  1482                                  %endif
  1483                                  
  1484                                  next_cluster:
  1485                                  ; convert 16-bit cluster value (in SI) to 32-bit LBA sector value (in DX:AX)
  1486                                  ; and get next cluster in SI
  1487                                  
  1488                                  
  1489                                  		; Converts cluster number to sector number
  1490                                  		;  and finds next cluster in chain
  1491                                  		;
  1492                                  		; INP:	si = valid cluster number
  1493                                  		;	(!_FAT16) [ADR_FATBUF] = entire FAT as read from FS
  1494                                  		;	(_FAT16) di = currently buffered FAT sector, -1 if none
  1495                                  		; OUT:	If unable to read a FAT sector,
  1496                                  		;	 ! jumps to error instead of returning
  1497                                  		;	If everything is okay,
  1498                                  		;	 si = next cluster number (or EOC value)
  1499                                  		;	 dx:ax = sector number
  1500                                  		;	 (_FAT16) di = currently buffered FAT sector
  1501                                  		; CHG:	cx
  1502                                  
  1503                                  %if ! _LOAD_NON_FAT
  1504                                  ; prepare to load entry from FAT
  1505                                  
  1506                                  %if _FAT16
  1507                                  		push si			; preserve cluster number for later
  1508                                  
  1509                                  ; Multiply cluster number by 2.
  1510                                  		xchg ax, si
  1511                                  		; xor dx, dx		; dx:ax = entry to load (0..FFF6h)
  1512                                  		; add ax, ax
  1513                                  		; adc dx, dx		; dx:ax = byte offset into FAT (0..131052)
  1514                                  		cwd			; dx = FFFFh if ax >= 8000h, else = 0
  1515                                  		add ax, ax		; ax = (2 * ax) & FFFFh
  1516                                  		neg dx			; dx = 1 if 2 * ax overflowed, else = 0
  1517                                  
  1518                                  ; Divide by sector size to split dx:ax into the remainder as the byte offset
  1519                                  ;  into the FAT sector, and quotient as the sector offset into the FAT.
  1520                                  		div word [VAR(bytes_per_sector)]
  1521                                  		mov si, dx		; = byte offset in sector
  1522                                  			; dx = byte offset into sector (0..8190)
  1523                                  			; ax = sector offset into FAT (0..4095)
  1524                                  
  1525                                  ; quotient in AX is FAT sector.
  1526                                  ; check the FAT buffer to see if this sector is already loaded
  1527                                  ; (simple disk cache; speeds things up a little --
  1528                                  ; actually, it speeds things up a lot)
  1529                                  		cmp ax, di
  1530                                  		je @F
  1531                                  		mov di, ax
  1532                                   %if _SET_FAT_SEG
  1533                                  		mov word [VAR(fat_sector)], ax
  1534                                   %endif
  1535                                  
  1536                                  ; read the target FAT sector.
  1537                                  		push bx
  1538                                  			; As noted above, the sector offset in ax here is
  1539                                  			;  0..4095 (ie, 4 Ki S * 32 B/S = 128 KiB of FAT data),
  1540                                  			;  therefore this cwd instruction always zeros dx.
  1541                                  		cwd
  1542                                  		add ax, [VAR(fat_start)]
  1543                                  		adc dx, dx		; (account for overflow)
  1544                                   %if _RELOCATE
  1545                                  		mov bx, word [VAR(fat_seg)]
  1546                                  		call read_sector
  1547                                   %else
  1548                                  		call read_sector_fatbuf
  1549                                   %endif
  1550                                  		pop bx
  1551                                  @@:
  1552                                  
  1553                                  ; get 16 bits from FAT
  1554                                  		es lodsw
  1555                                  		xchg ax, si
  1556                                  		pop ax		; restore cluster number
  1557                                  %else
  1558                                  ; FAT12 entries are 12 bits, bytes are 8 bits. Ratio is 3 / 2,
  1559                                  ;  so multiply cluster number by 3 first, then divide by 2.
  1560 0000010F 89F0                    		mov ax, si		; = cluster number (up to 12 bits set)
  1561                                  			; (remember cluster number in ax)
  1562 00000111 D1E6                    		shl si, 1		; = 2n (up to 13 bits set)
  1563 00000113 01C6                    		add si, ax		; = 2n+n = 3n (up to 14 bits set)
  1564 00000115 D1EE                    		shr si, 1		; si = byte offset into FAT (0..6129)
  1565                                  					; CF = whether to use high 12 bits
  1566                                   %if _RELOCATE
  1567                                  		mov es, word [VAR(fat_seg)]
  1568                                  		; es lodsw
  1569                                  		; xchg ax, si
  1570                                  		mov si, word [es:si]
  1571                                   %else
  1572                                  ; Use the calculated byte offset as an offset into the FAT
  1573                                  ;  buffer, which holds all of the FAT's relevant data.
  1574                                  		; lea si, [ADR_FATBUF + si]
  1575                                  				; -> 16-bit word in FAT to load
  1576                                  ; get 16 bits from FAT
  1577                                  		; lodsw
  1578                                  		; xchg ax, si
  1579 00000117 8BB4007E                		mov si, [ADR_FATBUF + si]
  1580                                  				; = 16-bit word in FAT to load
  1581                                   %endif
  1582                                  
  1583 0000011B B104                    		mov cl, 4
  1584 0000011D 7202                    		jc @F		; iff CY after shift -->
  1585 0000011F D3E6                    		shl si, cl	; shift up iff even entry
  1586                                  @@:
  1587 00000121 D3EE                    		shr si, cl	; shift down (clears top 4 bits)
  1588                                  
  1589                                  			; (ax holds cluster number)
  1590                                  %endif
  1591                                  
  1592                                  %else ; _LOAD_NON_FAT
  1593                                  		mov di, _LOAD_NON_FAT
  1594                                  		mov ax, word [VAR(adj_sectors_per_cluster)]
  1595                                  		mov cx, ax
  1596                                  		mul word [VAR(bytes_per_sector)]
  1597                                  		test dx, dx
  1598                                  		jnz @F
  1599                                  		cmp ax, di
  1600                                  		mov al, 'N'
  1601                                  		jb error
  1602                                  @@:
  1603                                  		xchg ax, si
  1604                                  %endif
  1605                                  ; adjust cluster number to make it 0-based
  1606 00000123 48                      		dec ax
  1607 00000124 48                      		dec ax
  1608                                  
  1609                                  %if ! _LOAD_NON_FAT
  1610                                  %if _FIX_CLUSTER_SIZE
  1611                                  		mov cx, _FIX_CLUSTER_SIZE
  1612                                  %else
  1613 00000125 8B4EEE                  		mov cx, [VAR(adj_sectors_per_cluster)]
  1614                                  %endif
  1615                                  %endif
  1616                                  
  1617                                  ; convert from clusters to sectors
  1618 00000128 F7E1                    		mul cx
  1619 0000012A 0346FC                  		add ax, [VAR(data_start)]
  1620 0000012D 1356FE                  		adc dx, [VAR(data_start)+2]
  1621                                  				; dx:ax = sector number
  1622                                  
  1623                                  ; xxx - this will always load an entire cluster (e.g. 64 sectors),
  1624                                  ; even if the file is shorter than this
  1625                                  @@:
  1626                                   %if _LOAD_ADR < ADR_FREE_UNTIL && _FIX_SECTOR_SIZE
  1627                                  		cmp bx, (ADR_FREE_UNTIL >> 4) - (_FIX_SECTOR_SIZE >> 4)
  1628                                   %else
  1629 00000130 3B5EEA                  		cmp bx, [VAR(last_available_sector)]
  1630                                   %endif
  1631                                  %if _MEMORY_CONTINUE
  1632 00000133 770E                    		ja @F
  1633                                  %else
  1634                                  		ja error_filetoobig
  1635                                  %endif
  1636                                  %if _FAT16 && ! _LOAD_NON_FAT
  1637                                  		push es		; (must preserve ADR_FATBUF reference)
  1638                                  %endif
  1639 00000135 E83B00                  		call read_sector
  1640                                  %if _FAT16 && ! _LOAD_NON_FAT
  1641                                  		pop es
  1642                                  %endif
  1643                                  %if _SET_LOAD_SEG
  1644 00000138 895EFA                  		mov [VAR(load_seg)], bx	; => after last read data
  1645                                  %endif
  1646                                  %if _LOAD_NON_FAT
  1647                                  		sub di, word [VAR(bytes_per_sector)]
  1648                                  		ja @B
  1649                                  %else
  1650 0000013B E2F3                    		loop @B
  1651                                  
  1652                                  %if _FAT16
  1653                                  ; FFF7h: bad cluster
  1654                                  ; FFF8h-FFFFh: end of cluster chain
  1655                                  		cmp si, 0FFF7h
  1656                                  %else
  1657                                  ; 0FF7h: bad cluster
  1658                                  ; 0FF8h-0FFFh: end of cluster chain
  1659 0000013D 81FEF70F                		cmp si, 0FF7h
  1660                                  %endif
  1661 00000141 72CC                    		jb next_cluster
  1662                                  %endif	; _LOAD_NON_FAT
  1663                                  @@:
  1664                                  
  1665                                  %if _LOAD_MIN_PARA
  1666 00000143 81FB6002                		cmp bx, (_LOAD_ADR >> 4) + _LOAD_MIN_PARA
  1667 00000147 B045                    		mov al, 'E'
  1668 00000149 7211                    		jb error
  1669                                  %endif
  1670                                  
  1671                                  %if _TURN_OFF_FLOPPY
  1672                                  ; turn off floppy motor
  1673                                  		mov dx,3F2h
  1674                                  		mov al,0
  1675                                  		out dx,al
  1676                                  %endif
  1677                                  
  1678                                  ; Set-up registers for and jump to loaded file
  1679                                  ; Already: ss:bp-> boot sector containing BPB
  1680                                  %if _CHECKVALUE
  1681                                  CHECKLINEAR equ _LOAD_ADR + _CHECKOFFSET
  1682                                   %if ! _RELOCATE && CHECKLINEAR <= (64 * 1024 - 2)
  1683 0000014B 813EFC236C44            		cmp word [CHECKLINEAR], _CHECKVALUE
  1684                                   %else
  1685                                  		mov ax, CHECKLINEAR >> 4
  1686                                  		mov es, ax
  1687                                  		cmp word [es:CHECKLINEAR & 15], _CHECKVALUE
  1688                                   %endif
  1689 00000151 B056                    		mov al, 'V'		; check 'V'alue mismatch
  1690 00000153 7507                    		jne error
  1691                                  %endif
  1692                                  %if _SET_DL_UNIT
  1693                                  		mov dl, [VAR(boot_unit)]; set dl to unit
  1694                                  %endif
  1695                                  %if _SET_DI_CLUSTER && (_PUSH_DPT || _SET_DSSI_DPT)
  1696                                  		pop cx
  1697                                  %endif
  1698                                  %if _DATASTART_HIDDEN
  1699                                  		mov bx, [VAR(hidden_sectors + 0)]
  1700                                  		mov ax, [VAR(hidden_sectors + 2)]
  1701                                  		add word [VAR(data_start + 0)], bx
  1702                                  		adc word [VAR(data_start + 2)], ax
  1703                                  %endif
  1704                                  %if _SET_BL_UNIT
  1705                                   %if _SET_DL_UNIT
  1706                                  		mov bl, dl		; set bl to unit, too
  1707                                   %else
  1708                                  		mov bl, [VAR(boot_unit)]; set bl to unit
  1709                                   %endif
  1710                                  %endif
  1711                                  %if _PUSH_DPT || _SET_DSSI_DPT
  1712                                   %ifn _SET_DSSI_DPT		; (implying that only _PUSH_DPT is set)
  1713                                    %if _RELOCATE
  1714                                  		xor ax, ax
  1715                                  		mov es, ax
  1716                                  		mov di, 1Eh * 4
  1717                                  		les si, [es:di]
  1718                                  		push es
  1719                                  		push si
  1720                                  		push ax
  1721                                  		push di
  1722                                    %else
  1723                                  		mov di, 1Eh*4
  1724                                  		les si, [di]		; -> original (also current) DPT
  1725                                  		push es
  1726                                  		push si			; original (also current) DPT address
  1727                                  		push ss
  1728                                  		push di			; 0000h:0078h (address of 1Eh IVT entry)
  1729                                    %endif
  1730                                   %else
  1731                                    %if _RELOCATE
  1732                                  		xor ax, ax
  1733                                  		mov ds, ax
  1734                                    %endif
  1735                                  		mov di, 1Eh*4
  1736                                  		lds si, [di]		; -> original (also current) DPT
  1737                                    %if _PUSH_DPT
  1738                                  		push ds
  1739                                  		push si			; original (also current) DPT address
  1740                                     %if _RELOCATE
  1741                                  		push ax
  1742                                  		push di
  1743                                     %else
  1744                                  		push ss
  1745                                  		push di			; 0000h:0078h (address of 1Eh IVT entry)
  1746                                     %endif
  1747                                    %endif
  1748                                   %endif
  1749                                  %else
  1750                                   %if _RELOCATE && (_ZERO_ES || _ZERO_DS)
  1751                                  		xor ax, ax
  1752                                   %endif
  1753                                  %endif
  1754                                  %if _RELOCATE
  1755                                   %if _ZERO_ES
  1756                                  		mov es, ax
  1757                                   %endif
  1758                                   %if _ZERO_DS
  1759                                  		mov ds, ax
  1760                                   %endif
  1761                                  %endif
  1762                                  
  1763                                  %if _SET_AXBX_DATA
  1764                                  		mov bx, [VAR(data_start)]
  1765                                  		mov ax, [VAR(data_start+2)]
  1766                                  %endif
  1767                                  %if _SET_DI_CLUSTER
  1768                                   %if _PUSH_DPT || _SET_DSSI_DPT
  1769                                  		mov di, cx
  1770                                   %else
  1771                                  		pop di
  1772                                   %endif
  1773                                  %endif
  1774                                  %if ! _RELOCATE
  1775                                   %if _ZERO_ES
  1776                                  		push ss
  1777                                  		pop es
  1778                                   %endif
  1779                                  %endif
  1780                                  			; ss:bp-> boot sector with BPB
  1781 00000155 EA00040002              		jmp (_LOAD_ADR>>4)+_EXEC_SEG_ADJ:_EXEC_OFS
  1782                                  
  1783                                  
  1784                                  %if ! _RELOCATE
  1785                                  	errorhandler
   704                              <1> %if _TMPINC
   705                              <1>  %include "error.tmp"
   706                              <1>  [list -]
   707                              <1> %else
   708                              <1> 
   709                              <1> read_sector.err:
   710 0000015A B052                <1>  mov al, 'R'
   711                              <1> %if ! _MEMORY_CONTINUE || _RELOCATE || _LOAD_ADR >= ADR_FREE_FROM
   712                              <1>  db __TEST_IMM16
   713                              <1> error_filetoobig:
   714                              <1> error_memory:
   715                              <1>  mov al,'M'
   716                              <1> %endif
   717                              <1> 
   718                              <1> error:
   719                              <1> %if _RELOCATE
   720                              <1>  mov bx, 7
   721                              <1>  mov ds, bx
   722                              <1>  mov bh, [462h - 70h]
   723                              <1> %else
   724 0000015C 8A3E6204            <1>  mov bh, [462h]
   725 00000160 B307                <1>  mov bl, 7
   726                              <1> %endif
   727 00000162 B40E                <1>  mov ah, 0Eh
   728 00000164 CD10                <1>  int 10h
   729 00000166 B007                <1>  mov al, 07h
   730 00000168 CD10                <1>  int 10h
   731                              <1> 
   732 0000016A 31C0                <1>  xor ax, ax
   733 0000016C CD16                <1>  int 16h
   734                              <1> 
   735 0000016E CD19                <1>  int 19h
   736                              <1> 
   737                              <1> %endif
   738                              <1> %if _TMPINC
   739                              <1>  [list +]
   740                              <1> %endif
  1786                                  
  1787                                  	readhandler
   745                              <1> %if _TMPINC
   746                              <1>  %include "read.tmp"
   747                              <1>  [list -]
   748                              <1> %else
   749                              <1> 
   750                              <1> 
   751                              <1> 
   752                              <1> 
   753                              <1> 
   754                              <1> 
   755                              <1> 
   756                              <1> %if ! _RELOCATE
   757                              <1>  %if ADR_DIRBUF == ADR_FATBUF
   758                              <1> read_sector_dirbuf:
   759                              <1>  %endif
   760                              <1> read_sector_fatbuf:
   761 00000170 BBE007              <1>  mov bx, ADR_FATBUF>>4
   762                              <1> %endif
   763                              <1> 
   764                              <1> 
   765                              <1> 
   766                              <1> 
   767                              <1> 
   768                              <1> 
   769                              <1> 
   770                              <1> 
   771                              <1> 
   772                              <1> 
   773                              <1> 
   774                              <1> 
   775                              <1> 
   776                              <1> read_sector:
   777 00000173 52                  <1>  push dx
   778 00000174 51                  <1>  push cx
   779 00000175 50                  <1>  push ax
   780 00000176 56                  <1>  push si
   781                              <1> 
   782 00000177 53                  <1>  push bx
   783                              <1> 
   784                              <1> 
   785                              <1> 
   786 00000178 03461C              <1>  add ax,[VAR(hidden_sectors + 0)]
   787 0000017B 13561E              <1>  adc dx,[VAR(hidden_sectors + 2)]
   788                              <1>  %if (!_LBA || !_LBA_33_BIT) && _LBA_CHECK_NO_33
   789                              <1>  jc .err
   790                              <1>  %endif
   791                              <1> %if _LBA
   792                              <1>  %if _LBA_33_BIT
   793 0000017E 19F6                <1>  sbb si, si
   794 00000180 F7DE                <1>  neg si
   795                              <1>  %endif
   796 00000182 31C9                <1>  xor cx, cx
   797 00000184 51                  <1>  push cx
   798                              <1>  %if _LBA_33_BIT
   799 00000185 56                  <1>  push si
   800                              <1>  %else
   801                              <1>  push cx
   802                              <1>  %endif
   803 00000186 52                  <1>  push dx
   804 00000187 50                  <1>  push ax
   805 00000188 53                  <1>  push bx
   806 00000189 51                  <1>  push cx
   807 0000018A 41                  <1>  inc cx
   808 0000018B 51                  <1>  push cx
   809 0000018C B110                <1>  mov cl, 10h
   810 0000018E 51                  <1>  push cx
   811 0000018F 89E6                <1>  mov si, sp
   812                              <1> 
   813                              <1>  %if _LBA_SKIP_CHECK
   814 00000191 8A5624              <1>  mov dl, [VAR(boot_unit)]
   815                              <1>  %else
   816                              <1>  mov ah, 41h
   817                              <1>  mov dl, [VAR(boot_unit)]
   818                              <1>  mov bx, 55AAh
   819                              <1>  stc
   820                              <1>  int 13h
   821                              <1>  jc .no_lba
   822                              <1>  cmp bx, 0AA55h
   823                              <1>  jne .no_lba
   824                              <1>  test cl, 1
   825                              <1>  jz .no_lba
   826                              <1>  %endif
   827                              <1> 
   828                              <1> %if _LBA_RETRY
   829                              <1>  mov ah, 42h
   830                              <1>  int 13h
   831                              <1>  jnc .lba_done
   832                              <1> 
   833                              <1>  xor ax, ax
   834                              <1>  int 13h
   835                              <1> 
   836                              <1> 
   837                              <1> 
   838                              <1> 
   839                              <1>  mov byte [si + 2], 1
   840                              <1> %endif
   841                              <1> 
   842 00000194 B442                <1>  mov ah, 42h
   843 00000196 CD13                <1>  int 13h
   844                              <1>  %if _LBA_SKIP_CHECK && _CHS
   845 00000198 7208                <1>  jc .lba_check_error_1
   846                              <1>  %else
   847                              <1> .cy_err:
   848                              <1>  jc .lba_error
   849                              <1>  %endif
   850                              <1> 
   851                              <1> .lba_done:
   852                              <1> %if _CHS && _LBA_SET_TYPE
   853                              <1>  mov byte [bp + 2], 0Eh
   854                              <1> %endif
   855 0000019A 83C410              <1>  add sp, 10h
   856 0000019D 5B                  <1>  pop bx
   857 0000019E 8EC3                <1>  mov es, bx
   858                              <1> %if _CHS
   859 000001A0 EB4B                <1>  jmp short .chs_done
   860                              <1> %endif
   861                              <1> 
   862                              <1> .lba_error: equ .err
   863                              <1> 
   864                              <1>  %if !_CHS
   865                              <1> .no_lba: equ .err
   866                              <1>  %else
   867                              <1>  %if _LBA_SKIP_CHECK
   868                              <1> .lba_check_error_1:
   869 000001A2 80FC01              <1>  cmp ah, 1
   870 000001A5 75B3                <1>  jne .lba_error
   871                              <1> 
   872                              <1> .cy_err: equ .err
   873                              <1>  %endif
   874                              <1> .no_lba:
   875 000001A7 83C408              <1>  add sp, 8
   876 000001AA 59                  <1>  pop cx
   877 000001AB 58                  <1>  pop ax
   878 000001AC 5A                  <1>  pop dx
   879                              <1> 
   880                              <1> 
   881                              <1> 
   882                              <1> 
   883                              <1> 
   884 000001AD 5E                  <1>  pop si
   885                              <1>  %endif
   886                              <1> %endif
   887                              <1> 
   888                              <1> %if !_LBA
   889                              <1> .cy_err: equ .err
   890                              <1> %endif
   891                              <1> 
   892                              <1> %if _CHS
   893                              <1> 
   894                              <1> 
   895                              <1> 
   896                              <1> 
   897                              <1>  %if !_LBA
   898                              <1>  xchg cx, ax
   899                              <1>  xchg ax, dx
   900                              <1>  xor dx, dx
   901                              <1>  %else
   902                              <1> 
   903                              <1> 
   904                              <1> 
   905                              <1>  %endif
   906 000001AE F77618              <1>  div word [VAR(sectors_per_track)]
   907 000001B1 91                  <1>  xchg cx,ax
   908 000001B2 F77618              <1>  div word [VAR(sectors_per_track)]
   909 000001B5 87CA                <1>  xchg cx,dx
   910                              <1> 
   911                              <1> 
   912                              <1> 
   913 000001B7 93                  <1>  xchg bx, ax
   914 000001B8 92                  <1>  xchg ax, dx
   915 000001B9 31D2                <1>  xor dx, dx
   916 000001BB F7761A              <1>  div word [VAR(heads)]
   917                              <1> 
   918 000001BE 93                  <1>  xchg bx, ax
   919 000001BF F7761A              <1>  div word [VAR(heads)]
   920                              <1> 
   921                              <1> 
   922                              <1> 
   923 000001C2 88D6                <1>  mov dh, dl
   924 000001C4 41                  <1>  inc cx
   925 000001C5 86E8                <1>  xchg ch, al
   926 000001C7 D1E8                <1>  shr ax, 1
   927 000001C9 D1E8                <1>  shr ax, 1
   928                              <1> 
   929 000001CB 08FB                <1>  or bl, bh
   930 000001CD 08C1                <1>  or cl, al
   931                              <1> 
   932 000001CF 08E3                <1>  or bl, ah
   933 000001D1 8A5624              <1>  mov dl,[VAR(boot_unit)]
   934                              <1> .nz_err:
   935 000001D4 7584                <1>  jnz .err
   936                              <1> 
   937                              <1> 
   938                              <1> 
   939                              <1> 
   940 000001D6 07                  <1>  pop es
   941                              <1> %if _CHS_RETRY_REPEAT
   942 000001D7 BE1100              <1>  mov si, _CHS_RETRY_REPEAT + 1
   943                              <1> .loop_chs_retry_repeat:
   944 000001DA B80102              <1>  mov ax, 0201h
   945 000001DD CD13                <1>  int 13h
   946 000001DF 730A                <1>  jnc .done
   947 000001E1 31C0                <1>  xor ax, ax
   948 000001E3 CD13                <1>  int 13h
   949 000001E5 4E                  <1>  dec si
   950 000001E6 75F2                <1>  jnz .loop_chs_retry_repeat
   951 000001E8 E96FFF              <1>  jmp .err
   952                              <1> %else
   953                              <1>  %if _CHS_RETRY
   954                              <1>  mov ax, 0201h
   955                              <1>  int 13h
   956                              <1>  jnc .done
   957                              <1> 
   958                              <1>  xor ax, ax
   959                              <1>  int 13h
   960                              <1>  %endif
   961                              <1> 
   962                              <1>  mov ax, 0201h
   963                              <1>  int 13h
   964                              <1>  %if _LBA_SKIP_CHECK
   965                              <1>  inc bx
   966                              <1>  jc .nz_err
   967                              <1>  %else
   968                              <1>  jc .cy_err
   969                              <1>  %endif
   970                              <1> %endif
   971                              <1> 
   972                              <1> .done:
   973                              <1> 
   974 000001EB 8CC3                <1>  mov bx, es
   975                              <1> %endif
   976                              <1> 
   977                              <1> .chs_done:
   978                              <1> %if _FIX_SECTOR_SIZE
   979                              <1>  add bx, _FIX_SECTOR_SIZE >> 4
   980                              <1> %else
   981 000001ED 035EEC              <1>  add bx, [VAR(para_per_sector)]
   982                              <1> %endif
   983                              <1> 
   984 000001F0 5E                  <1>  pop si
   985 000001F1 58                  <1>  pop ax
   986 000001F2 59                  <1>  pop cx
   987 000001F3 5A                  <1>  pop dx
   988                              <1> 
   989 000001F4 40                  <1>  inc ax
   990 000001F5 7501                <1>  jne @F
   991 000001F7 42                  <1>  inc dx
   992                              <1> @@:
   993                              <1> 
   994 000001F8 C3                  <1>  retn
   995                              <1> 
   996                              <1> %endif
   997                              <1> %if _TMPINC
   998                              <1>  [list +]
   999                              <1> %endif
  1788                                  %endif
  1789                                  
  1790                                  
  1791                                  %if !_NO_LIMIT
  1792                                  available:
  1793 000001F9 26<rep 3h>              	_fill 508,38,start
  1794                                  
  1795                                  signatures:
  1796 000001FC 0000                    	dw 0
  1797                                  ; 2-byte magic bootsector signature
  1798 000001FE 55AA                    	dw 0AA55h
  1799                                  
  1800                                  %assign num signatures-available
  1801                                  %assign fatbits 12
  1802                                  %if _FAT16
  1803                                   %assign fatbits 16
  1804                                  %endif
  1805                                  %warning FAT%[fatbits]: num bytes still available.
  1805          ******************       warning: FAT12: 3 bytes still available. [-w+user]
  1806                                  %endif
  1807                                  
  1808                                  end:
