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