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