1 2 %if 0 3 4 File system boot sector loader code for FAT32 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--2024 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 strdef OEM_NAME, " lDOS" 25 strdef OEM_NAME_FILL, '_' 26 strdef DEFAULT_LABEL, "lDOS" 27 numdef VOLUMEID, 0 28 strdef FSIBOOTNAME, "FSIBOOT5" 29 ; used to set experimental name 30 ; strdef FSIBOOTNAME, "FSIBEX05" 31 32 strdef LOAD_NAME, "LDOS" 33 strdef LOAD_EXT, "COM" ; name of file to load 34 numdef LOAD_ADR, 02000h ; where to load 35 numdef LOAD_MIN_PARA, paras(1536) 36 numdef LOAD_CHECK_MAX_SIZE, 0 37 numdef LOAD_CHECK_MAX_SIZE_FATPLUS, 1 38 numdef EXEC_SEG_ADJ, 0 ; how far cs will be from _LOAD_ADR 39 numdef EXEC_OFS, 400h ; what value ip will be 40 numdef CHECKOFFSET, 1020 41 numdef CHECKVALUE, "lD" 42 numdef LOAD_STACK, 512 ; default stack size (use 256 for DR-DOS) 43 numdef LOAD_DIR_SEG, 0 ; => where to store dir entry (0 if nowhere) 44 numdef ADD_SEARCH, 0 ; whether to search second file 45 strdef ADD_NAME, "" 46 strdef ADD_EXT, "" ; name of second file to search 47 numdef ADD_DIR_SEG, 0 ; => where to store dir entry (0 if nowhere) 48 49 numdef QUERY_GEOMETRY, 1 ; query geometry via 13.08 (for CHS access) 50 numdef QUERY_GEOMETRY_DISABLED, 0 51 numdef USE_PART_INFO, 1 ; use ds:si-> partition info from MBR, if any 52 numdef USE_PART_INFO_DISABLED, 0 53 numdef USE_AUTO_UNIT, 1 ; use unit passed from ROM-BIOS in dl 54 numdef RPL, 1 ; support RPL and do not overwrite it 55 numdef CHS, 1 ; support CHS (if it fits) 56 numdef LBA, 1 ; support LBA (if available) 57 numdef LBA_33_BIT, 1 ; support 33-bit LBA 58 numdef LBA_CHECK_NO_33, 1 ; else: check that LBA doesn't carry 59 60 numdef RELOCATE, 0 ; relocate the loader to top of memory 61 numdef SET_DL_UNIT, 0 ; if to pass unit in dl 62 numdef SET_BL_UNIT, 0 ; if to pass unit in bl as well 63 numdef SET_AXBX_DATA, 0 ; if to pass first data sector in ax:bx 64 numdef SET_DSSI_DPT, 0 ; if to pass DPT address in ds:si 65 numdef PUSH_DPT, 0 ; if to push DPT address 66 numdef MEMORY_CONTINUE, 1 ; if to just execute when memory full 67 numdef SET_SIDI_CLUSTER,0 ; if to pass first load file cluster in si:di 68 numdef TURN_OFF_FLOPPY, 0 ; if to turn off floppy motor after loading 69 numdef DATASTART_HIDDEN,0 ; if to add hidden sectors to data_start 70 numdef LBA_SET_TYPE, 0 ; if to set third byte to LBA partition type 71 numdef SET_LOAD_SEG, 1 ; if to set load_seg (word [ss:bp - 6]) 72 numdef SET_FAT_SEG, 1 ; if to set fat_seg (word [ss:bp - 8]) 73 numdef SET_CLUSTER, 1 ; if to set first_cluster (dword [ss:bp - 16]) 74 numdef ZERO_ES, 0 ; if to set es = 0 before jump 75 numdef ZERO_DS, 0 ; if to set ds = 0 before jump 76 77 numdef FIX_SECTOR_SIZE, 0 ; fix sector size (0 = disable, else = sector size) 78 numdef FIX_SECTOR_SIZE_SKIP_CHECK, 0 ; don't check sector size 79 numdef FIX_CLUSTER_SIZE, 0 ; fix cluster size 80 numdef FIX_CLUSTER_SIZE_SKIP_CHECK, 0 ; don't check cluster size 81 numdef NO_LIMIT, 0 ; allow using more memory than a boot sector 82 ; also will not write 0AA55h signature! 83 numdef WARN_PART_SIZE, 0 84 85 numdef LBA_SKIP_CHECK, 0 ; don't use proper LBA extensions check 86 numdef LBA_WORKAROUND, 1 ; when checking for LBA set ds = 40h 87 numdef LBA_SKIP_CY, 1 ; skip check: set up CY before 13.42 88 numdef LBA_SKIP_ANY, 0 ; skip check: try CHS on any error 89 incdef _LBA_SKIP_ANY, LBA_SKIP_CY 90 numdef LBA_RETRY, 0 ; retry LBA reads one time 91 numdef CHS_RETRY, 1 ; retry CHS reads one time 92 numdef CHS_RETRY_REPEAT,16 ; retry CHS reads multiple times 93 ; (value of the def is used as count) 94 numdef CHS_RETRY_NORMAL,1 ; do not use aggressive optimisation 95 numdef RETRY_RESET, 1 ; call reset disk system 13.00 on retries 96 97 ; Unlike the 1440 KiB diskette image defaults for the FAT12 98 ; loader we just fill the BPB with zeros by default. 99 numdef MEDIAID, 0 ; media ID 100 numdef UNIT, 0 ; load unit in BPB 101 numdef CHS_SECTORS, 0 ; CHS geometry field for sectors 102 numdef CHS_HEADS, 0 ; CHS geometry field for heads 103 numdef HIDDEN, 0 ; number of hidden sectors 104 numdef SPI, 0 ; sectors per image 105 numdef BPS, 0 ; bytes per sector 106 numdef SPC, 0 ; sectors per cluster 107 numdef SPF, 0 ; sectors per FAT 108 numdef SECTOR_FSINFO, 0 ; FSINFO sector 109 numdef SECTOR_BACKUP, 0 ; backup boot sector 110 numdef CLUSTER_ROOT, 0 ; root directory first cluster 111 numdef NUMFATS, 0 ; number of FATs 112 numdef NUMRESERVED, 0 ; number of reserved sectors 113 114 numdef COMPAT_FREEDOS, 0 ; partial FreeDOS load compatibility 115 numdef COMPAT_DR, 0 ; partial DR-DOS load compatibility 116 numdef COMPAT_IBM, 0 ; partial IBMDOS load compatibility 117 numdef COMPAT_MS7, 0 ; partial MS-DOS 7 load compatibility 118 numdef COMPAT_LDOS, 0 ; lDOS load compatibility 119 120 %if (!!_COMPAT_FREEDOS + !!_COMPAT_DR + !!_COMPAT_IBM + !!_COMPAT_MS7 + !!_COMPAT_LDOS) > 1 122 %error At most one set must be selected. 123 %endif 124 125 %if _COMPAT_FREEDOS 126 strdef LOAD_NAME, "KERNEL" 127 strdef LOAD_EXT, "SYS" 128 numdef LOAD_ADR, 00600h 129 numdef LOAD_MIN_PARA, paras(512) 130 numdef EXEC_SEG_ADJ, 0 131 numdef EXEC_OFS, 0 132 numdef CHECKVALUE, 0 133 134 numdef SET_BL_UNIT, 1 135 numdef MEMORY_CONTINUE, 0 136 numdef RELOCATE, 1 137 ; The FreeDOS load protocol mandates that the entire file be loaded. 138 %endif 139 140 %if _COMPAT_IBM 141 strdef LOAD_NAME, "IBMBIO" 142 strdef LOAD_EXT, "COM" 143 numdef LOAD_ADR, 00700h 144 numdef LOAD_MIN_PARA, paras(512) 145 numdef EXEC_SEG_ADJ, 0 146 numdef EXEC_OFS, 0 147 numdef CHECKVALUE, 0 148 numdef LOAD_DIR_SEG, 50h 149 numdef ADD_SEARCH, 1 150 strdef ADD_NAME, "IBMDOS" 151 strdef ADD_EXT, "COM" 152 numdef ADD_DIR_SEG, 52h 153 ; Note: The IBMBIO.COM directory entry must be stored at 154 ; 0:500h, and the IBMDOS.COM directory entry at 0:520h. 155 156 numdef SET_DL_UNIT, 1 157 numdef MEMORY_CONTINUE, 1 158 ; 3 sectors * 512 BpS should suffice. We load into 700h--7A00h, 159 ; ie >= 6000h bytes (24 KiB), <= 7300h bytes (28.75 KiB). 160 numdef SET_AXBX_DATA, 1 161 numdef DATASTART_HIDDEN,1 162 numdef SET_DSSI_DPT, 1 163 numdef PUSH_DPT, 1 164 %endif 165 166 %if _COMPAT_DR 167 strdef LOAD_NAME, "IBMBIO" 168 strdef LOAD_EXT, "COM" 169 numdef LOAD_ADR, 00700h 170 numdef LOAD_STACK, 256 171 ; Shrinking the stack allows to boot a file of up to 29 KiB 172 ; given a sector size of <= 1 KiB. 173 numdef LOAD_MIN_PARA, paras(512) 174 numdef EXEC_SEG_ADJ, 0 175 numdef EXEC_OFS, 0 176 numdef CHECKVALUE, 0 177 178 numdef SET_DL_UNIT, 1 179 numdef MEMORY_CONTINUE, 1 180 ; Memory continue must certainly be set to boot off file 181 ; systems with >= 32 KiB per cluster and may be needed 182 ; even for smaller cluster sizes that are still > 1 KiB. 183 numdef LOAD_CHECK_MAX_SIZE, 1 184 ; In conjunction with _MEMORY_CONTINUE this enables erroring 185 ; out if the actual file data is too large to load it, but 186 ; avoids the need to load the entire last cluster. 187 188 numdef SET_AXBX_DATA, 0 189 numdef DATASTART_HIDDEN,0 190 numdef SET_DSSI_DPT, 0 191 numdef PUSH_DPT, 0 192 ; None of these options seem needed for DR-DOS. Note that 193 ; disabling them will fail if we actually load our lDOS 194 ; iniload as it expects these options enabled. 195 %endif 196 197 %if _COMPAT_MS7 198 strdef LOAD_NAME, "IO" 199 strdef LOAD_EXT, "SYS" 200 numdef LOAD_ADR, 00700h 201 numdef LOAD_MIN_PARA, paras(1024) 202 numdef EXEC_SEG_ADJ, 0 203 numdef EXEC_OFS, 200h 204 numdef CHECKVALUE, 0 205 206 numdef SET_DL_UNIT, 1 207 numdef SET_DSSI_DPT, 0 208 numdef PUSH_DPT, 1 209 numdef MEMORY_CONTINUE, 1 210 ; 4 sectors * 512 BpS should suffice. We load into 700h--7A00h, 211 ; ie >= 6000h bytes (24 KiB), <= 7300h bytes (28.75 KiB). 212 numdef SET_SIDI_CLUSTER,1 213 numdef DATASTART_HIDDEN,1 214 numdef LBA_SET_TYPE, 1 215 %endif 216 217 %if _COMPAT_LDOS 218 strdef LOAD_NAME, "LDOS" 219 strdef LOAD_EXT, "COM" 220 numdef LOAD_ADR, 02000h 221 numdef LOAD_MIN_PARA, paras(1536) 222 numdef EXEC_SEG_ADJ, 0 223 numdef EXEC_OFS, 400h 224 numdef CHECKOFFSET, 1020 225 numdef CHECKVALUE, "lD" 226 227 numdef SET_DL_UNIT, 0 228 numdef SET_CLUSTER, 1 229 numdef SET_FAT_SEG, 1 230 numdef SET_LOAD_SEG, 1 231 numdef MEMORY_CONTINUE, 1 232 numdef DATASTART_HIDDEN,0 233 %endif 234 235 %if 0 236 237 Notes about partial load compatibilities 238 239 * FreeDOS: 240 * Relocates to an address other than 27A00h (1FE0h:7C00h) 241 * DR-DOS: 242 * Must enable _MEMORY_CONTINUE to load off file systems with cluster 243 sizes > 1 KiB (depending on file size) or >= 32 KiB (certainly) 244 * Will not load whole file if _MEMORY_CONTINUE is enabled and file 245 data exceeds 29 KiB, without erroring out. The new option called 246 _LOAD_CHECK_MAX_SIZE has been added to address this. 247 * Disables DPT, data start, and directory entry to 00500h options by 248 default so loading a faithful IBMDOS v4+ kernel will fail as msload 249 or lDOS iniload need these options to operate 250 * IBMDOS: 251 * Does not actually relocate DPT, just provide its address 252 * MS-DOS 7: 253 * Does not actually relocate DPT, just provide its address 254 * Does not contain message table used by loader 255 256 %endif 257 258 %if _SET_BL_UNIT && _SET_AXBX_DATA 259 %error Cannot select both of these options! 260 %endif 261 262 263 %assign LOADLIMIT 0A0000h 264 %assign POSITION 07C00h 265 266 %if _FIX_SECTOR_SIZE 267 %assign i 5 268 %rep 13-5 269 %if (1 << i) != (_FIX_SECTOR_SIZE) 270 %assign i i+1 271 %endif 272 %endrep 273 %if (1 << i) != (_FIX_SECTOR_SIZE) 274 %error Invalid sector size _FIX_SECTOR_SIZE 275 %endif 276 %endif 277 278 %if _FIX_CLUSTER_SIZE 279 %if _FIX_CLUSTER_SIZE > 256 280 %error Invalid cluster size _FIX_CLUSTER_SIZE 281 %endif 282 %assign i 0 283 %rep 8-0 284 %if (1 << i) != (_FIX_CLUSTER_SIZE) 285 %assign i i+1 286 %endif 287 %endrep 288 %if (1 << i) != (_FIX_CLUSTER_SIZE) 289 %warning Non-power-of-two cluster size _FIX_CLUSTER_SIZE 290 %endif 291 %endif 292 293 294 %if (_LOAD_ADR & 0Fh) 295 %error Load address must be on a paragraph boundary 296 %endif 297 298 %if _LOAD_ADR > LOADLIMIT 299 %error Load address must be in LMA 300 %elif _LOAD_ADR < 00500h 301 %error Load address must not overlap IVT or BDA 302 %endif 303 %if ! _RELOCATE 304 %if _LOAD_ADR > (POSITION-512) && _LOAD_ADR < (POSITION+1024) 305 %error Load address must not overlap loader 306 %endif 307 %endif 308 309 %if ((_EXEC_SEG_ADJ<<4)+_EXEC_OFS) < 0 310 %error Execution address must be in loaded file 311 %elif ((_EXEC_SEG_ADJ<<4)+_EXEC_OFS+_LOAD_ADR) > LOADLIMIT 312 %error Execution address must be in LMA 313 %endif 314 315 %if (_EXEC_OFS & ~0FFFFh) 316 %error Execution offset must fit into 16 bits 317 %endif 318 319 %if (_EXEC_SEG_ADJ > 0FFFFh || _EXEC_SEG_ADJ < -0FFFFh) 320 %error Execution segment adjustment must fit into 16 bits 321 %endif 322 323 324 %if !_CHS && _QUERY_GEOMETRY 325 %warning No CHS support but querying geometry anyway 326 %endif 327 328 %if !_CHS && !_LBA 329 %error Either CHS or LBA or both must be enabled 330 %endif 331 332 333 %if 0 334 335 There is some logic inside MS-DOS's hard disk partition initialisation 336 code that sets up several requirements for us to fulfil. Otherwise, 337 it will not accept the information given in the BPB (using default 338 information based on the length as specified by MBR/EPBR instead) or 339 make the whole file system inaccessible except for formatting. Both of 340 those are undesirable of course. Some/all(?) checks are documented on 341 pages 601,602 in "DOS Internals", Geoff Chappell 1994, as follows: 342 343 * First three bytes contain either "jmp sho xx\ nop" or "jmp ne xx". 344 * Media descriptor field >= 0F0h. 345 * Bytes per sector field == 512. 346 * Sectors per cluster field a power of 2 (1,2,4,8,16,32,64,128). 347 * OEM name "version" (last three to four characters) 348 * must be "20.?", "10.?" (? = wildcard), but no other with "0.?", 349 * otherwise, must be "2.0", or 350 * 2nd-to-last,3rd-to-last character codes together > "3.", or 351 * those == "3.", last character code > "0" 352 353 To stay compatible to those, display a warning here if the name 354 itself would disqualify our boot sector already. 355 356 %endif 357 358 %push 359 %strlen %$len _OEM_NAME_FILL 360 %if %$len != 1 361 %error Specified OEM name fill must be 1 character 362 %endif 363 %strlen %$len _OEM_NAME 364 %define %$nam _OEM_NAME 365 %if %$len > 8 366 %error Specified OEM name is too long 367 %else 368 %assign %$warn 0 369 %rep 8 - %$len 370 %strcat %$nam %$nam,_OEM_NAME_FILL 371 %endrep 372 %substr %$prefix %$nam 5 ; "wvxyZa.b", get "Z" 373 %substr %$major %$nam 6,7 ; "wvxyzA.b", get "A." 374 %substr %$minor %$nam 8 ; "wvxyza.B", get "B" 375 %if %$major == "0." 376 %ifn %$prefix == "1" || %$prefix == "2" 377 %assign %$warn 1 378 %endif 379 %elifn %$major == "2." && %$minor == "0" 380 %if %$major < "3." 381 %assign %$warn 1 382 %elif %$major == "3." && %$minor < "1" 383 %assign %$warn 1 384 %endif 385 %endif 386 %if %$warn 387 %warning Specified OEM name fails MS-DOS's validation 388 %endif 389 %endif 390 %pop 391 392 ; Stack (default 512 Bytes minus the variables). 393 ADR_STACK_LOW equ 7C00h - _LOAD_STACK ; 07A00h (if _LOAD_STACK = 200h) 394 395 ADR_FSIBOOT equ end -start+7C00h ; 07E00h 396 397 %define _AFTER (ADR_FSIBOOT + 512) 398 %if _RELOCATE 399 ; dynamic allocation of FAT buffer 400 %else 401 %if _LOAD_ADR < 7C00h 402 ADR_FATBUF equ _AFTER 403 %define _AFTER (ADR_FATBUF + 8192) 404 %else 405 ADR_FATBUF equ 2000h 406 %endif 407 %endif 408 409 %if _ADD_SEARCH 410 %if _RELOCATE 411 ; dynamic allocation of dir buffer 412 %elif _LOAD_ADR < 7C00h 413 ADR_DIRBUF equ _AFTER 414 %define _AFTER (ADR_DIRBUF + 8192) 415 %else 416 ADR_DIRBUF equ 4000h 417 %endif 418 %else 419 %if _RELOCATE 420 ADR_DIRBUF equ _LOAD_ADR ; 00600h 421 %elif _LOAD_ADR < 7C00h 422 ; one-sector directory buffer. Assumes sectors are no larger than 8 KiB 423 ADR_DIRBUF equ _AFTER ; 0A000h 424 %define _AFTER (ADR_DIRBUF + 8192) 425 %else 426 ADR_DIRBUF equ 4000h 427 %endif 428 %endif 429 ADR_FREE equ _AFTER ; 08000h 430 431 %if ! _RELOCATE 432 %if ((ADR_FATBUF + 8192 - 1) & ~0FFFFh) != (ADR_FATBUF & ~0FFFFh) 433 %warning Possibly crossing 64 KiB boundary while reading FAT 434 %endif 435 %endif 436 437 %ifn _RELOCATE && _ADD_SEARCH 438 %if ((ADR_DIRBUF + 8192 - 1) & ~0FFFFh) != (ADR_DIRBUF & ~0FFFFh) 439 %warning Possibly crossing 64 KiB boundary while reading directory 440 %endif 441 %endif 442 443 %if _LOAD_ADR >= ADR_FREE || _RELOCATE 444 ; If reading on a sector size boundary, no crossing can occur. 445 ; Check for all possible sector sizes (32 B to 8 KiB). If one 446 ; of them fails display a warning, including the minimum size. 447 %assign SECSIZECHECK 32 448 %assign EXITREP 0 449 %rep 256 450 %ifn EXITREP 451 %if _LOAD_ADR & (SECSIZECHECK - 1) 452 %warning Possibly crossing 64 KiB boundary while reading file (sector size >= SECSIZECHECK) 453 %assign EXITREP 1 454 %exitrep 455 %endif 456 %if SECSIZECHECK == 8192 457 %assign EXITREP 1 458 %exitrep 459 %endif 460 %assign SECSIZECHECK SECSIZECHECK * 2 461 %endif 462 %endrep 463 %else 464 ; If loading below the boot sector, address 1_0000h is never reached. 465 %endif 466 467 468 struc FSINFO ; FAT32 FSINFO sector layout 469 00000000 ???????? .signature1: resd 1 ; 41615252h ("RRaA") for valid FSINFO 470 .reserved1: ; former unused, initialized to zero by FORMAT 471 00000004 .fsiboot: resb 480 ; now used for FSIBOOT 472 000001E4 ???????? .signature2: resd 1 ; 61417272h ("rrAa") for valid FSINFO 473 000001E8 ???????? .numberfree: resd 1 ; FSINFO: number of free clusters or -1 474 000001EC ???????? .nextfree: resd 1 ; FSINFO: first free cluster or -1 475 000001F0 .reserved2: resd 3 ; unused, initialized to zero by FORMAT 476 000001FC ???????? .signature3: resd 1 ; AA550000h for valid FSINFO or FSIBOOT 477 endstruc 478 479 struc FSIBOOTG ; FSIBOOT general layout 480 00000000 ???????????????? .signature: resq 1 ; 8 bytes that identify the FSIBOOT type 481 00000008 .fsicode: resb 466 ; 466 bytes FSIBOOT type specific data or code 482 000001DA ???? .reserved: resw 1 ; 1 word -> reserved entrypoint (retn) 483 000001DC ???? .memorysize: resw 1 ; 1 word -> memory size entrypoint (or retn) 484 000001DE ???? .dirsearch: resw 1 ; 1 word -> directory search entrypoint 485 endstruc 486 487 %if FSIBOOTG_size != 480 488 %error Unexpected layout 489 %endif 490 491 struc DIRENTRY 492 00000000 ???????????????? deName: resb 8 493 00000008 ?????? deExt: resb 3 494 0000000B ?? deAttrib: resb 1 495 0000000C ?? dePlusSize: resb 1 496 0000000D ?????????????? resb 7 497 00000014 ???? deClusterHigh: resw 1 498 00000016 ???? deTime: resw 1 499 00000018 ???? deDate: resw 1 500 0000001A ???? deClusterLow: resw 1 501 0000001C ???????? deSize: resd 1 502 endstruc 503 504 ATTR_READONLY equ 1 505 ATTR_HIDDEN equ 2 506 ATTR_SYSTEM equ 4 507 ATTR_VOLLABEL equ 8 508 ATTR_DIRECTORY equ 10h 509 ATTR_ARCHIVE equ 20h 510 511 512 ; use byte-offset addressing from BP for smaller code 513 %define VAR(x) ((x) - start) + bp 514 515 516 cpu 8086 517 ; bootsector loaded at address 07C00h, addressable using 0000h:7C00h 518 org POSITION 519 start: 520 521 522 %define _LASTVARIABLE start 523 %macro nextvariable 2.nolist 524 %1 equ (_LASTVARIABLE - %2) 525 %define _LASTVARIABLE %1 526 %endmacro 527 528 ; Variables 529 530 ; (dword) sector where the first cluster's data starts 531 nextvariable data_start, 4 532 533 ; (word) current load segment (points behind last loaded data) 534 nextvariable load_seg, 2 535 536 ; (word) segment of FAT buffer 537 ; for FAT12 this holds the entire FAT 538 ; for FAT16 this holds the sector given by wo[fat_sector] 539 ; for FAT32 this holds the sector given by dwo[fat_sector] 540 nextvariable fat_seg, 2 541 542 ; (dword for FAT32) currently loaded sector-in-FAT, -1 if none 543 nextvariable fat_sector, 4 544 545 ; (dword for FAT32) first cluster of load file 546 nextvariable first_cluster, 4 547 548 ADR_STACK_START equ _LASTVARIABLE -start+POSITION 549 550 ; (word) number of 16-byte paragraphs per sector 551 nextvariable para_per_sector, 2 552 553 ; (word) number of 32-byte directory entries per sector 554 nextvariable entries_per_sector, 2 555 556 ; (word) segment of last available memory for sector 557 nextvariable last_available_sector, 2 558 559 ; (word) actual sectors per cluster 560 nextvariable adj_sectors_per_cluster, 2 561 562 ; (word) paragraphs left to read 563 nextvariable paras_left, 2 564 565 lowest_variable equ _LASTVARIABLE 566 567 568 00000000 EB7D jmp strict short skip_bpb 569 %if !_CHS && _LBA_SET_TYPE 570 db 0Ch ; LBA-enabled FAT32 FS partition type 571 %else 572 00000002 90 nop ; default: no LBA 573 %endif 574 575 576 ; BIOS Parameter Block (BPB) 577 ; 578 ; Installation will use the BPB already present in your file system. 579 ; These values must be initialised when installing the loader. 580 581 oem_id: ; offset 03h (03) - not used by this code 582 00000003 202020206C444F53 fill 8,_OEM_NAME_FILL,db _OEM_NAME 583 bytes_per_sector: ; offset 0Bh (11) - refer to _FIX_SECTOR_SIZE ! 584 0000000B 0000 dw _BPS 585 sectors_per_cluster: ; offset 0Dh (13) - refer to _FIX_CLUSTER_SIZE ! 586 0000000D 00 db _SPC & 255 587 fat_start: 588 num_reserved_sectors: ; offset 0Eh (14) 589 0000000E 0000 dw _NUMRESERVED 590 num_fats: ; offset 10h (16) 591 00000010 00 db _NUMFATS 592 num_root_dir_ents: ; offset 11h (17) 593 00000011 0000 dw 0 594 total_sectors: ; offset 13h (19) - not used by this code 595 %if _SPI < 1_0000h 596 00000013 0000 dw _SPI 597 %else 598 dw 0 599 %endif 600 media_id: ; offset 15h (21) - not used by this code 601 00000015 00 db _MEDIAID 602 sectors_per_fat: ; offset 16h (22) 603 00000016 0000 dw 0 604 sectors_per_track: ; offset 18h (24) 605 00000018 0000 dw _CHS_SECTORS 606 heads: ; offset 1Ah (26) 607 0000001A 0000 dw _CHS_HEADS 608 hidden_sectors: ; offset 1Ch (28) 609 0000001C 00000000 dd _HIDDEN 610 total_sectors_large: ; offset 20h (32) - not used by this code 611 %if _SPI >= 1_0000h 612 dd _SPI 613 %else 614 00000020 00000000 dd 0 615 %endif 616 617 sectors_per_fat_large: ; offset 24h (36) 618 00000024 00000000 dd _SPF 619 fsflags: ; offset 28h (40) 620 00000028 0000 dw 0 621 fsversion: ; offset 2Ah (42) 622 0000002A 0000 dw 0 623 root_cluster: ; offset 2Ch (44) 624 0000002C 00000000 dd _CLUSTER_ROOT 625 fsinfo_sector: ; offset 30h (48) 626 00000030 0000 dw _SECTOR_FSINFO 627 backup_sector: ; offset 32h (50) 628 00000032 0000 dw _SECTOR_BACKUP 629 630 00000034 00 times 12 db 0 ; offset 34h (52) reserved 631 632 ; Extended BPB ; offset 40h (64) 633 634 00000040 00 boot_unit: db _UNIT 635 00000041 00 db 0 636 00000042 29 ext_bpb_signature: db 29h 637 00000043 00000000 serial_number: dd _VOLUMEID 638 00000047 6C444F5320 volume_label: fill 11,32,db _DEFAULT_LABEL 639 00000052 464154333220- filesystem_identifier: fill 8,32,db "FAT32" 639 00000052 640 641 642 ; Initialised data 643 644 align 2 645 fsiboot_table: ; this table is used by the FSIBOOT stage 646 0000005A [1601] .error: dw error 647 ; INP: al = error condition letter 648 ; ('B' = bad chain / FS error, 'F' = file not found, 649 ; 'R' = disk read error, 'M' = not enough memory, 650 ; 'E' = not enough data in file) 651 0000005C [0201] .success: dw load_finish 652 ; INP: dword [ss:sp] = first cluster 653 ; Note: The first cluster dword is always filled in 654 ; by FSIBOOT; the option _SET_SIDI_CLUSTER only 655 ; affects usage in the primary loader. 656 %if _MEMORY_CONTINUE 657 0000005E [0001] .memory_full: dw load_finish_mc 658 ; INP: al = error condition letter ('M'), 659 ; dword [ss:sp] = current cluster number, 660 ; dword [ss:sp + 4] = first cluster, refer to .success 661 %else 662 .memory_full: dw error 663 ; refer to previous .memory_full comment 664 %endif 665 00000060 [2A01] .read_sector: dw read_sector 666 ; INP: dx:ax = sector number within partition, 667 ; bx => buffer, (_LBA) ds = ss 668 ; OUT: dx:ax incremented, bx => incremented, 669 ; es = input bx, does not return if error occurred 670 ; CHG: none 671 %if _RELOCATE && _ADD_SEARCH 672 .dirbuf: dw 8192 >> 4 673 ; initialised to segment displacement from FAT buffer 674 %else 675 00000062 000A .dirbuf: dw ADR_DIRBUF>>4 676 ; => directory sector buffer (one sector) 677 %endif 678 00000064 [C301] .writedirentry: dw writedirentry.loaddir 679 ; INP: ds:bx == es:bx -> found dir entry in dir sector buffer 680 ; si:di = loaded sector-in-FAT 681 ; CHG: ax, cx, dx 682 ; STT: ss:bp -> boot sector 683 ; UP 684 ; OUT: directory entry copied if so desired 685 ; (is a no-op if not to copy dir entry) 686 00000066 [6C00] .filename: dw .load_name 687 ; -> name to search 688 00000068 6000 .minpara: dw _LOAD_MIN_PARA 689 0000006A 0002 .loadseg: dw _LOAD_ADR>>4 690 ; => where to load 691 692 .load_name: ; = blank-padded 11-byte filename to search for 693 0000006C 4C444F5320 fill 8,32,db _LOAD_NAME 694 00000074 434F4D fill 3,32,db _LOAD_EXT 695 696 fsiboot_name: 697 00000077 465349424F4F5435 fill 8, 32, db _FSIBOOTNAME 698 699 %if _WARN_PART_SIZE 700 %assign num $ - start 701 %warning BPB + data size is num bytes 702 %endif 703 704 705 ; Code 706 707 ; Note that this may be entered with cs:ip = 07C0h:0 ! 708 skip_bpb: 709 0000007F FA cli 710 00000080 FC cld 711 00000081 31C9 xor cx, cx 712 00000083 BD[0000] mov bp, start ; magic bytes - checked by instsect 713 00000086 8ED1 mov ss, cx 714 00000088 BCF07B mov sp, ADR_STACK_START 715 %if _USE_AUTO_UNIT 716 0000008B 885640 mov [VAR(boot_unit)], dl; magic bytes - checked by instsect 717 %else 718 mov dl, [VAR(boot_unit)]; magic bytes - checked by instsect 719 %endif 720 721 ; Note: es is left uninitialised here until the first call to 722 ; read_sector if the below conditional is false. 723 %if _USE_PART_INFO ; +19 bytes 724 0000008E 8EC1 mov es, cx 725 ; Note: Award Medallion BIOS v6.0 (ASUS MED 2001 ACPI BIOS Revision 1009) 726 ; loads from a floppy disk drive with ds:si = 0F000h:0A92Dh -> 727 ; FF FF FF FF 08 00 08 01 FF FF FF FF FF FF FF FF, which was detected 728 ; as a valid partition table entry by this handling. Therefore, we 729 ; only accept partition information when booting from a hard disk now. 730 731 ; start of magic byte sequence for instsect 732 00000090 84D2 test dl, dl ; floppy ? 733 00000092 7911 jns @F ; don't attempt detection --> 734 ; Check whether an MBR left us partition information. 735 ; byte[ds:si] bit 7 means active and must be set if valid. 736 00000094 380C cmp byte [si], cl ; flags for xx-00h (result is xx), SF = bit 7 737 00000096 790D jns @F ; xx < 80h, ie info invalid --> 738 ; byte[ds:si+4] is the file system type. Check for valid one. 739 00000098 384C04 cmp byte [si+4], cl ; is it zero? 740 0000009B 7408 je @F ; yes, info invalid --> 741 ; Info valid, trust their hidden sectors over hardcoded. 742 ; Assume the movsw instructions won't run with si = FFFFh. 743 0000009D BF[1C00] mov di, hidden_sectors ; -> BPB field 744 000000A0 83C608 add si, 8 ; -> partition start sector in info 745 %if _USE_PART_INFO_DISABLED 746 nop 747 nop ; size has to match enabled code 748 %else 749 000000A3 A5 movsw 750 000000A4 A5 movsw ; overwrite BPB field with value from info 751 %endif 752 @@: 753 ; end of magic byte sequence for instsect 754 %endif 755 000000A5 8ED9 mov ds, cx 756 000000A7 FB sti 757 758 759 ; Note: The int 13h function 08h call may change or 760 ; set ax, bx, cx, dx, es, di. es is left as 761 ; indeterminate until the first call to 762 ; read_sector, or it is set later in 763 ; load_fsiboot.loaded (set equal to ds). 764 %if _QUERY_GEOMETRY ; +27 bytes 765 766 ; start of magic byte sequence for instsect 767 ; test dl, dl ; floppy? 768 ; jns @F ; don't attempt query, might fail --> 769 ; Note that while the original PC BIOS doesn't support this function 770 ; (for its diskettes), it does properly return the error code 01h. 771 ; https://sites.google.com/site/pcdosretro/ibmpcbios (IBM PC version 1) 772 000000A8 B408 mov ah, 08h 773 ; xor cx, cx ; initialise cl to 0 774 ; Already from prologue cx = 0. 775 000000AA F9 stc ; initialise to CY 776 %if _QUERY_GEOMETRY_DISABLED 777 nop 778 nop ; size has to match enabled code 779 %else 780 000000AB CD13 int 13h ; query drive geometry 781 %endif 782 000000AD 720E jc @F ; apparently failed --> 783 000000AF 83E13F and cx, 3Fh ; get sectors 784 000000B2 7409 jz @F ; invalid (S is 1-based), don't use --> 785 000000B4 894E18 mov [VAR(sectors_per_track)], cx 786 000000B7 88F1 mov cl, dh ; cx = maximum head number 787 000000B9 41 inc cx ; cx = number of heads (H is 0-based) 788 000000BA 894E1A mov [VAR(heads)], cx 789 @@: 790 ; end of magic byte sequence for instsect 791 %endif 792 793 794 ; 16-byte paragraphs per sector 795 000000BD 8B460B mov ax, [VAR(bytes_per_sector)] 796 000000C0 B104 mov cl, 4 797 000000C2 D3E8 shr ax, cl 798 000000C4 50 push ax ; push into word [VAR(para_per_sector)] 799 800 ; 32-byte FAT directory entries per sector 801 000000C5 D1E8 shr ax, 1 ; /2 = 32-byte entries per sector 802 000000C7 50 push ax ; push into word [VAR(entries_per_sector)] 803 804 load_fsiboot: 805 000000C8 83F820 cmp ax, 1024 >> 5 ; sector size is at least 1 KiB (large) ? 806 000000CB 731B jae .loaded ; already loaded as part of the boot sector --> 807 808 ; If this code runs, then ax was below 1024 >> 5, 809 ; therefore this cwd instruction always zeros dx. 810 000000CD 99 cwd 811 812 000000CE 8B4630 mov ax, [VAR(fsinfo_sector)] 813 ; inc ax 814 ; jz error_fsiboot ; was FFFFh, invalid --> 815 ; dec ax 816 ; FFFFh always fails the < num_reserved_sectors check 817 000000D1 85C0 test ax, ax 818 000000D3 7403 jz @F ; is 0 ? invalid --> (ZR, NC) 819 000000D5 3B460E cmp ax, [VAR(num_reserved_sectors)] 820 @@: ; (ZR, NC if ax == 0) 821 000000D8 7337 jae error_fsiboot ; (jump if NC) 822 ; dx:ax = FSINFO sector (dx = 0 from cwd) 823 000000DA B90002 mov cx, 512 824 000000DD BBE007 mov bx, ADR_FSIBOOT >> 4 825 @@: 826 000000E0 E84700 call read_sector 827 000000E3 2B4E0B sub cx, [VAR(bytes_per_sector)] 828 000000E6 77F8 ja @B ; read 512 bytes --> 829 830 .loaded: 831 000000E8 1E push ds 832 000000E9 07 pop es 833 000000EA BF[0402] mov di, fsiboot.signature 834 000000ED BE[7700] mov si, fsiboot_name 835 000000F0 B90400 mov cx, 4 ; size of fsiboot_name 836 000000F3 F3A7 repe cmpsw 837 000000F5 751A jne error_fsiboot 838 ; Note that now es:di -> fsiboot.start 839 840 %if _LOAD_ADR >= ADR_FREE || _RELOCATE 841 jmp 0:.next ; make sure cs is 0 842 843 .next: 844 xor ax, ax ; = 0, harden against unsupported function 845 call near word [memorysize_entrypoint] 846 847 %if _RELOCATE 848 %if _ADD_SEARCH 849 sub ax, (20 * 1024 + 8192 + 8192 + (8192-16) + 1024 + 7C00h) >> 4 850 %else 851 sub ax, (20 * 1024 + 8192 + (8192-16) + 1024 + 7C00h) >> 4 852 %endif 853 ; 20 KiB: reserved for iniload 854 ; 8 KiB: dir buffer (only if _ADD_SEARCH) 855 ; 8 KiB: FAT buffer 856 ; 8 KiB - 16 B: to allow rounding down position of buffers 857 ; 1 KiB: sector + FSIBOOT 858 ; 7C00h: stack and to allow addressing with 7C00h in bp 859 ; 860 ; Note also that by addressing the stack and sector and FSIBOOT 861 ; with bp at 7C00h, and insuring this subtraction doesn't 862 ; underflow, makes sure that we do not overwrite the IVT or 863 ; BDA. (However, we assume that ax is at least 60h or so.) 864 ; 865 ; The FAT buffer segment is masked so that the actual buffer 866 ; is stored on an 8 KiB boundary. This is to ensure that 867 ; the buffer doesn't possibly cross a 64 KiB DMA boundary. 868 jc @F 869 cmp ax, (end_after_fsiboot -start+7C00h - ADR_STACK_LOW + 15) >> 4 870 ; This check is to ensure that the start of the destination 871 ; for the relocation (stack, sector, FSIBOOT) is 872 ; above-or-equal the end of the source for the relocation. 873 ; That is, to avoid overwriting any of the source with the 874 ; string move instruction (which for simplicity is always UP). 875 @@: 876 jb error_fsiboot 877 878 ; With _RELOCATE enabled, the FAT buffer segment 879 ; is fixed up by the primary loader here before 880 ; FSIBOOT is executed. 881 mov bx, ((8192 - 16) + 1024 + 7C00h)>>4 882 ; bx is initialised to the value 883 ; ((8192 - 16) + 1024 + 7C00h)>>4 884 ; so this is like calculating the following for its value: 885 ; ((LMA_top - 20 KiB - 8 KiB - 8 KiB{AS} - (8 KiB - 16 B) - 1 KiB - 7C00h) + ; ((8 KiB - 16 B) + 1 KiB + 7C00h))>>4 887 ; == (LMA_top - 20 KiB - 8 KiB - 8 KiB{AS})>>4 888 ; {AS} = only if _ADD_SEARCH 889 add bx, ax 890 and bx, ~ ((8192>>4) - 1) ; => FAT sector buffer (one sector) 891 %if _ADD_SEARCH 892 ; .dirbuf is initialised to 8192 >> 4 893 add word [VAR(fsiboot_table.dirbuf)], bx 894 ; => dir sector buffer (one sector) 895 %endif 896 897 push ax 898 push di ; -> reloc destination of fsiboot.start 899 900 mov es, ax ; => destination 901 mov si, sp ; ds:si = ss:entries_per_sector - 4 902 mov di, si ; es:di -> destination for stack low 903 mov cx, (end_after_fsiboot - (entries_per_sector - 4)) >> 1 904 ; end_after_fsiboot is the top of used memory 905 ; entries_per_sector is the lowest filled stack frame slot 906 ; 4 is for the additional slots taken by the return address 907 rep movsw ; relocate stack, sector, and FSIBOOT 908 mov ss, ax 909 mov ds, ax ; relocate these 910 add ax, (ADR_STACK_LOW) >> 4 ; (rounding down) => behind available 911 912 retf ; jump to relocated FSIBOOT 913 %else 914 sub ax, (20 * 1024) >> 4 ; 20 KiB reserved for iniload 915 jc error_fsiboot 916 mov bx, ADR_FATBUF >> 4 ; => FAT sector buffer (one sector) 917 push es 918 push di ; -> fsiboot.start 919 retf 920 ; Do a far return here to ensure that cs is zero. 921 %endif 922 %elif _LOAD_ADR < ADR_STACK_LOW 923 000000F7 B8A007 mov ax, (ADR_STACK_LOW >> 4) 924 000000FA BB0008 mov bx, ADR_FATBUF >> 4 ; => FAT sector buffer (one sector) 925 000000FD 06 push es 926 000000FE 57 push di ; -> fsiboot.start 927 000000FF CB retf 928 ; Do a far return here to ensure that cs is zero. 929 %else 930 %error Load address within used memory 931 %endif 932 933 934 %if _WARN_PART_SIZE 935 %assign num $ - skip_bpb 936 %warning init size is num bytes 937 %endif 938 939 940 finish_start: 941 ; INP: ds:bx == es:bx -> found dir entry in dir sector buffer 942 ; si:di = loaded sector-in-FAT 943 ; CHG: ax, cx, dx 944 ; STT: ss:bp -> boot sector 945 ; UP 946 ; OUT: directory entry copied if so desired 947 ; (is a no-op if not to copy dir entry) 948 writedirentry: 949 %if _LOAD_DIR_SEG || _LOAD_CHECK_MAX_SIZE 950 .loaddir: 951 %else 952 .loaddir: equ read_sector.retn 953 %endif 954 %if _LOAD_CHECK_MAX_SIZE 955 mov dx, word [VAR(para_per_sector)] 956 ; dx = para per sector 957 mov ax, word [VAR(last_available_sector)] 958 add ax, dx ; ax => behind memory 959 sub ax, word [VAR(fsiboot_table.loadseg)] 960 ; ax = how many paragraphs 961 dec dx ; para per sector - 1 962 not dx ; mask to round down to full sector 963 and ax, dx ; = how many paragraphs loadable 964 xor dx, dx ; dx:ax = how many paragraphs loadable 965 mov cx, 4 966 @@: 967 shl ax, 1 968 rcl dx, 1 969 loop @B ; dx:ax = how many bytes loadable 970 %if _LOAD_CHECK_MAX_SIZE_FATPLUS 971 ; check FAT+ size bits 972 test byte [bx + dePlusSize], 0E7h 973 ; test whether bits 7-5 and 2-0 NZ 974 jnz @F ; --> (NZ, NC) 975 %endif 976 cmp word [bx + deSize + 2], dx 977 jne @F 978 cmp word [bx + deSize], ax 979 @@: 980 mov al, 'M' ; error condition letter if too large 981 jna @F ; (jbe) jump if ZR || CY 982 push ss 983 pop ds ; error expects ds == ss 984 jmp error 985 ; ja error ; jump if ! (ZR || CY) 986 ; ie jump if NZ && NC 987 @@: 988 %endif 989 %if _LOAD_DIR_SEG 990 mov ax, _LOAD_DIR_SEG 991 %elif _LOAD_CHECK_MAX_SIZE 992 retn 993 %endif 994 %if _LOAD_DIR_SEG || (_ADD_DIR_SEG && _ADD_SEARCH) 995 ; INP: ax => where to store directory entry 996 .ax: 997 push si 998 push di 999 mov si, bx ; ds:si -> directory entry 1000 mov cx, DIRENTRY_size >> 1 1001 mov es, ax 1002 xor di, di ; es:di -> where to store directory entry 1003 rep movsw ; move to here (one directory entry) 1004 push ds 1005 pop es ; es:bx -> dir entry in dir sector buffer 1006 pop di 1007 pop si 1008 retn 1009 %endif 1010 1011 1012 %if _MEMORY_CONTINUE 1013 load_finish_mc: 1014 00000100 5B pop bx 1015 00000101 59 pop cx 1016 %endif 1017 load_finish: 1018 1019 %if _ADD_SEARCH 1020 mov word [VAR(fsiboot_table.filename)], add_name 1021 call near word [dirsearch_entrypoint] 1022 ; es = ds => dirbuf 1023 %if _ADD_DIR_SEG 1024 mov ax, _ADD_DIR_SEG 1025 call writedirentry.ax 1026 %endif 1027 push ss 1028 pop ds 1029 %endif 1030 1031 %if _TURN_OFF_FLOPPY 1032 ; turn off floppy motor 1033 mov dx,3F2h 1034 mov al,0 1035 out dx,al 1036 %endif 1037 1038 ; Set-up registers for and jump to loaded file 1039 ; Already: ss:bp-> boot sector containing BPB 1040 %if _CHECKVALUE 1041 CHECKLINEAR equ _LOAD_ADR + _CHECKOFFSET 1042 %if CHECKLINEAR <= (64 * 1024 - 2) && ! _RELOCATE 1043 00000102 813EFC236C44 cmp word [CHECKLINEAR], _CHECKVALUE 1044 %else 1045 mov ax, CHECKLINEAR >> 4 1046 mov es, ax 1047 cmp word [es:CHECKLINEAR & 15], _CHECKVALUE 1048 %endif 1049 00000108 B056 mov al, 'V' ; check 'V'alue mismatch 1050 0000010A 750A jne error 1051 %endif 1052 %if _SET_SIDI_CLUSTER && (_PUSH_DPT || _SET_DSSI_DPT) 1053 pop cx 1054 pop dx 1055 %endif 1056 %if _PUSH_DPT || _SET_DSSI_DPT 1057 %ifn _SET_DSSI_DPT ; (implying that only _PUSH_DPT is set) 1058 %if _RELOCATE 1059 xor ax, ax 1060 mov es, ax ; => IVT 1061 mov di, 1Eh*4 1062 les si, [es:di] ; -> original (also current) DPT 1063 push es 1064 push si ; original (also current) DPT address 1065 push ax 1066 push di ; 0000h:0078h (address of 1Eh IVT entry) 1067 %else 1068 ; If not _RELOCATE, ds = 0000h (=> IVT) 1069 mov di, 1Eh*4 1070 les si, [di] ; -> original (also current) DPT 1071 push es 1072 push si ; original (also current) DPT address 1073 ; If not _RELOCATE, ss = 0000h (=> IVT) 1074 push ss 1075 push di ; 0000h:0078h (address of 1Eh IVT entry) 1076 %endif 1077 %else 1078 %if _RELOCATE 1079 xor ax, ax 1080 mov ds, ax ; => IVT 1081 %endif 1082 ; If not _RELOCATE, ds = 0000h (=> IVT) 1083 mov di, 1Eh*4 1084 lds si, [di] ; -> original (also current) DPT 1085 %if _PUSH_DPT 1086 push ds 1087 push si ; original (also current) DPT address 1088 %if _RELOCATE 1089 push ax 1090 push di ; 0000h:0078h (address of 1Eh IVT entry) 1091 %else 1092 ; If not _RELOCATE, ss = 0000h (=> IVT) 1093 push ss 1094 push di ; 0000h:0078h (address of 1Eh IVT entry) 1095 %endif 1096 %endif 1097 %endif 1098 %endif 1099 %if _ZERO_ES || _ZERO_DS 1100 %if _RELOCATE 1101 %ifn _PUSH_DPT || _SET_DSSI_DPT 1102 xor ax, ax 1103 %endif 1104 %if _ZERO_ES 1105 mov es, ax 1106 %endif 1107 %if _ZERO_DS 1108 mov ds, ax 1109 %endif 1110 %else 1111 %if _ZERO_ES 1112 push ss 1113 pop es 1114 %endif 1115 %if _ZERO_DS && _SET_DSSI_DPT 1116 push ss 1117 pop ds 1118 %endif 1119 %endif 1120 %endif 1121 %if _DATASTART_HIDDEN 1122 mov bx, [VAR(hidden_sectors + 0)] 1123 mov ax, [VAR(hidden_sectors + 2)] 1124 add word [VAR(data_start + 0)], bx 1125 adc word [VAR(data_start + 2)], ax 1126 %endif 1127 %if _SET_AXBX_DATA 1128 mov bx, [VAR(data_start)] 1129 mov ax, [VAR(data_start+2)] 1130 %endif 1131 %if _SET_SIDI_CLUSTER 1132 %if _SET_DSSI_DPT 1133 %error Cannot select both of these. 1134 %endif 1135 %if _PUSH_DPT || _SET_DSSI_DPT 1136 mov di, cx 1137 mov si, dx 1138 %else 1139 pop di 1140 pop si 1141 %endif 1142 %endif 1143 %if _SET_DL_UNIT 1144 mov dl, [VAR(boot_unit)]; set dl to unit 1145 %if _SET_BL_UNIT 1146 mov bl, dl ; set bl to unit, too 1147 %endif 1148 %elif _SET_BL_UNIT 1149 mov bl, [VAR(boot_unit)]; set bl to unit 1150 %endif 1151 ; ss:bp-> boot sector with BPB 1152 0000010C EA00040002 jmp (_LOAD_ADR>>4)+_EXEC_SEG_ADJ:_EXEC_OFS 1153 1154 %if _WARN_PART_SIZE 1155 %assign num $ - finish_start 1156 %warning finish size is num bytes 1157 %endif 1158 1159 1160 error_start: 1161 1162 error_fsiboot: 1163 00000111 B049 mov al,'I' 1164 1165 00000113 A9 db __TEST_IMM16 ; (skip mov) 1166 read_sector.err: 1167 00000114 B052 mov al, 'R' ; Disk 'R'ead error 1168 1169 error: 1170 %if _RELOCATE 1171 mov bx, 7 1172 mov ds, bx 1173 mov bh, [462h - 70h] 1174 %else 1175 00000116 8A3E6204 mov bh, [462h] 1176 0000011A B307 mov bl, 7 1177 %endif 1178 0000011C B40E mov ah, 0Eh 1179 0000011E CD10 int 10h ; display character 1180 00000120 B007 mov al, 07h 1181 00000122 CD10 int 10h ; beep! 1182 1183 00000124 31C0 xor ax, ax ; await key pressed 1184 00000126 CD16 int 16h 1185 1186 00000128 CD19 int 19h ; re-start the boot process 1187 1188 %if _WARN_PART_SIZE 1189 %assign num $ - error_start 1190 %warning error size is num bytes 1191 %endif 1192 1193 1194 ; Read a sector using Int13.02 or Int13.42 1195 ; 1196 ; INP: dx:ax = sector number within partition 1197 ; bx => buffer 1198 ; (_LBA) ds = ss 1199 ; OUT: If unable to read, 1200 ; ! jumps to error instead of returning 1201 ; If sector has been read, 1202 ; dx:ax = next sector number (has been incremented) 1203 ; bx => next buffer (bx = es+word[para_per_sector]) 1204 ; es = input bx 1205 ; CHG: - 1206 read_sector: 1207 0000012A 52 push dx 1208 0000012B 51 push cx 1209 0000012C 50 push ax 1210 0000012D 56 push si 1211 1212 0000012E 8EC3 mov es, bx ; => buffer 1213 1214 ; DX:AX==LBA sector number 1215 ; add partition start (= number of hidden sectors) 1216 00000130 03461C add ax,[VAR(hidden_sectors + 0)] 1217 00000133 13561E adc dx,[VAR(hidden_sectors + 2)] 1218 %if (!_LBA || !_LBA_33_BIT) && _LBA_CHECK_NO_33 1219 jc .err 1220 %endif 1221 %if _LBA ; +70 bytes (with CHS, +63 bytes without CHS) 1222 %if _LBA_33_BIT 1223 00000136 19F6 sbb si, si ; -1 if was CY, 0 else 1224 00000138 F7DE neg si ; 1 if was CY, 0 else 1225 %endif 1226 0000013A 31C9 xor cx, cx 1227 0000013C 51 push cx ; highest word = 0 1228 %if _LBA_33_BIT 1229 0000013D 56 push si ; bit 32 = 1 if operating in 33-bit space 1230 %else 1231 push cx ; second highest word = 0 1232 %endif 1233 0000013E 52 push dx 1234 0000013F 50 push ax ; = qword sector number 1235 00000140 53 push bx 1236 00000141 51 push cx ; bx => buffer 1237 00000142 41 inc cx 1238 00000143 51 push cx ; word number of sectors to read 1239 00000144 B110 mov cl, 10h 1240 00000146 51 push cx ; word size of disk address packet 1241 00000147 89E6 mov si, sp ; ds:si -> disk address packet (on stack) 1242 1243 %if _LBA_SKIP_CHECK ; -14 bytes 1244 mov dl, [VAR(boot_unit)] 1245 %else 1246 %if _LBA_WORKAROUND 1247 00000149 1E push ds 1248 0000014A BB4000 mov bx, 40h 1249 0000014D 8EDB mov ds, bx 1250 ; Setting ds = 40h for the function 41h detection is a workaround 1251 ; for a bug in the Book8088's Xi8088 BIOS. Refer to 1252 ; https://www.bttr-software.de/forum/forum_entry.php?id=21275 1253 %endif 1254 0000014F B441 mov ah, 41h 1255 00000151 8A5640 mov dl, [VAR(boot_unit)] 1256 00000154 BBAA55 mov bx, 55AAh 1257 00000157 F9 stc 1258 00000158 CD13 int 13h ; 13.41.bx=55AA extensions installation check 1259 %if _LBA_WORKAROUND 1260 0000015A 1F pop ds 1261 %endif 1262 0000015B 7215 jc .no_lba 1263 0000015D 81FB55AA cmp bx, 0AA55h 1264 00000161 750F jne .no_lba 1265 00000163 D0E9 shr cl, 1 ; support bitmap bit 0 1266 00000165 730B jnc .no_lba 1267 %endif 1268 1269 %if _LBA_RETRY 1270 %if _LBA_SKIP_CHECK && _LBA_SKIP_CY 1271 stc 1272 %endif 1273 mov ah, 42h 1274 int 13h ; 13.42 extensions read 1275 jnc .lba_done 1276 1277 %if _RETRY_RESET 1278 xor ax, ax 1279 int 13h ; reset disk 1280 %endif 1281 1282 ; have to reset the LBAPACKET's lpCount, as the handler may 1283 ; set it to "the number of blocks successfully transferred". 1284 ; (in any case, the high byte is still zero.) 1285 mov byte [si + 2], 1 1286 %endif 1287 1288 %if _LBA_SKIP_CHECK && _LBA_SKIP_CY 1289 stc 1290 %endif 1291 00000167 B442 mov ah, 42h 1292 00000169 CD13 int 13h 1293 %if _LBA_SKIP_CHECK && _CHS 1294 %if _LBA_SKIP_ANY 1295 jc .no_lba 1296 .err_CY: equ .err 1297 .err_2: equ .err 1298 %else 1299 jnc .lba_done 1300 cmp ah, 1 ; invalid function? 1301 je .no_lba ; try CHS instead --> 1302 .err_CY: 1303 .err_2: 1304 jmp .lba_error 1305 %endif 1306 %else 1307 .err_CY: 1308 0000016B 72A7 jc .lba_error 1309 .err_2: equ .err 1310 %endif 1311 1312 .lba_done: 1313 %if _CHS && _LBA_SET_TYPE 1314 mov byte [bp + 2], 0Ch ; LBA-enabled FAT32 FS partition type 1315 %endif 1316 0000016D 83C410 add sp, 10h 1317 %if _CHS 1318 00000170 EB44 jmp short .done 1319 %endif 1320 1321 .lba_error: equ .err 1322 1323 %if !_CHS 1324 .no_lba: equ .err 1325 %else 1326 .no_lba: 1327 00000172 83C408 add sp, 8 1328 00000175 59 pop cx ; cx = low word of sector 1329 00000176 58 pop ax 1330 00000177 5A pop dx ; dx:ax = middle two words of sector 1331 ; Here dx <= 1 if _LBA_33_BIT, else zero. 1332 ; If dx is nonzero then the CHS calculation 1333 ; should fail. If CHS sectors is equal to 1 1334 ; (very unusual) then the div may fail. Else, 1335 ; we will detect a cylinder > 1023 eventually. 1336 00000178 5E pop si ; discard highest word of qword 1337 %endif 1338 %else 1339 .err_2: equ .err 1340 %endif 1341 1342 %if _CHS ; +70 bytes 1343 ; dx:ax = LBA sector number, (if _LBA) cx = 0 1344 ; divide by number of sectors per track to get sector number 1345 ; Use 32:16 DIV instead of 64:32 DIV for 8088 compatability 1346 ; Use two-step 32:16 divide to avoid overflow 1347 %if !_LBA 1348 xchg cx, ax ; cx = low word of sector, clobbers ax 1349 xchg ax, dx ; ax = high word of sector, clobbers dx 1350 xor dx, dx ; dx:ax = high word of sector 1351 %else 1352 ; from the .no_lba popping we already have: 1353 ; cx = low word of sector 1354 ; dx:ax = high word of sector 1355 %endif 1356 00000179 F77618 div word [VAR(sectors_per_track)] 1357 0000017C 91 xchg cx,ax 1358 0000017D F77618 div word [VAR(sectors_per_track)] 1359 00000180 87CA xchg cx,dx 1360 1361 ; DX:AX=quotient, CX=remainder=sector (S) - 1 1362 ; divide quotient by number of heads 1363 00000182 93 xchg bx, ax ; bx = low word of quotient, clobbers ax 1364 00000183 92 xchg ax, dx ; ax = high word of quotient, clobbers dx 1365 00000184 31D2 xor dx, dx ; dx = 0 1366 00000186 F7761A div word [VAR(heads)] 1367 ; ax = high / heads, dx = high % heads 1368 00000189 93 xchg bx, ax ; bx = high / heads, ax = low quotient 1369 0000018A F7761A div word [VAR(heads)] 1370 1371 ; bx:ax=quotient=cylinder (C), dx=remainder=head (H) 1372 ; move variables into registers for INT 13h AH=02h 1373 0000018D 88D6 mov dh, dl ; dh = head 1374 0000018F 41 inc cx ; cl5:0 = sector 1375 00000190 86E8 xchg ch, al ; ch = cylinder 7:0, al = 0 1376 00000192 D1E8 shr ax, 1 1377 00000194 D1E8 shr ax, 1 ; al7:6 = cylinder 9:8 1378 ; bx has bits set iff it's > 0, indicating a cylinder >= 65536. 1379 00000196 08FB or bl, bh ; collect set bits from bh 1380 00000198 08C1 or cl, al ; cl7:6 = cylinder 9:8 1381 ; ah has bits set iff it was >= 4, indicating a cylinder >= 1024. 1382 0000019A 08E3 or bl, ah ; collect set bits from ah 1383 0000019C 8A5640 mov dl,[VAR(boot_unit)] ; dl = drive 1384 .nz_err: 1385 0000019F 7403E970FF jnz .err_2 ; error if cylinder >= 1024 --> 1386 ; ! bx = 0 (for 13.02 call) 1387 1388 ; we call INT 13h AH=02h once for each sector. Multi-sector reads 1389 ; may fail if we cross a track or 64K boundary 1390 %if _CHS_RETRY_REPEAT 1391 000001A4 BE1100 mov si, _CHS_RETRY_REPEAT + 1 1392 %if _CHS_RETRY_NORMAL && _RETRY_RESET 1393 000001A7 A9 db __TEST_IMM16 ; (skip int 13h) 1394 .loop_chs_retry_repeat: 1395 000001A8 CD13 int 13h ; reset disk 1396 %elif _RETRY_RESET 1397 .loop_chs_retry_repeat: 1398 xor ax, ax 1399 int 13h ; reset disk 1400 %else 1401 .loop_chs_retry_repeat: 1402 %endif 1403 000001AA 4E dec si ; another attempt ? 1404 000001AB 78F2 js .nz_err ; no --> 1405 000001AD B80102 mov ax, 0201h 1406 000001B0 CD13 int 13h ; read one sector 1407 %if _CHS_RETRY_NORMAL && _RETRY_RESET 1408 000001B2 89D8 mov ax, bx ; ax = 0 1409 %endif 1410 000001B4 72F2 jc .loop_chs_retry_repeat 1411 ; fall through to .done 1412 %else 1413 mov ax, 0201h 1414 %if _CHS_RETRY 1415 %if _RETRY_RESET 1416 ; In this case we cannot store to the stack and 1417 ; pop the value at the right moment for both 1418 ; cases of the "jnc .done" branch. So use the 1419 ; original code to re-init ax to 0201h. 1420 int 13h ; read one sector 1421 jnc .done 1422 ; reset drive 1423 xor ax, ax 1424 int 13h 1425 mov ax, 0201h 1426 %else 1427 push ax 1428 int 13h ; read one sector 1429 pop ax ; restore ax = 0201h 1430 jnc .done 1431 %endif 1432 %endif 1433 ; try read again 1434 int 13h 1435 jc .err_CY 1436 %endif 1437 %if ! _LBA 1438 .err_CY: equ .err 1439 %endif 1440 1441 %endif ; _CHS 1442 1443 .done: 1444 ; increment segment 1445 000001B6 8CC3 mov bx, es 1446 %if _FIX_SECTOR_SIZE 1447 add bx, _FIX_SECTOR_SIZE >> 4 1448 %else 1449 000001B8 035EEE add bx, [VAR(para_per_sector)] 1450 %endif 1451 1452 000001BB 5E pop si 1453 000001BC 58 pop ax 1454 000001BD 59 pop cx 1455 000001BE 5A pop dx 1456 ; increment LBA sector number 1457 000001BF 40 inc ax 1458 000001C0 7501 jne @F 1459 000001C2 42 inc dx 1460 @@: 1461 1462 .retn: 1463 000001C3 C3 retn 1464 1465 %if _WARN_PART_SIZE 1466 %assign num $ - read_sector 1467 %warning read_sector size is num bytes 1468 %endif 1469 1470 1471 %if _ADD_SEARCH 1472 add_name: ; = blank-padded 11-byte filename to search for 1473 fill 8,32,db _ADD_NAME 1474 fill 3,32,db _ADD_EXT 1475 %endif 1476 1477 1478 %if !_NO_LIMIT 1479 available: 1480 000001C4 26 _fill 508,38,start 1481 1482 signatures: 1483 000001FC 0000 dw 0 1484 ; 2-byte magic bootsector signature 1485 000001FE 55AA dw 0AA55h 1486 1487 %assign num signatures-available 1488 %warning FAT32: num bytes still available. 1488 ****************** warning: FAT32: 56 bytes still available. [-w+user] 1489 %else ; for testing 1490 align 4 1491 signatures: 1492 dw 0 1493 ; 2-byte magic bootsector signature 1494 dw 0AA55h 1495 %endif 1496 1497 align 16, nop 1498 end: 1499 1500 fsiboot: 1501 00000200 52526141 dd "RRaA" 1502 .signature: 1503 00000204 465349424F4F5435 fill 8, 32, db _FSIBOOTNAME 1504 1505 %if ($ - .signature) != 8 1506 %error Unexpected name size 1507 %endif 1508 .start: 1509 ; INP: ax => after last segment to be used for loading 1510 ; bx => FAT buffer (8 KiB) 1511 ; ss:bp -> boot sector, with EBPB 1512 ; dwo [ss:bp - 4] = data_start (uninit) 1513 ; wo [ss:bp - 6] = load_seg (uninit) 1514 ; wo [ss:bp - 8] = fat_seg (uninit) 1515 ; dwo [ss:bp - 12] = fat_sector (uninit) 1516 ; dwo [ss:bp - 16] = first_cluster (uninit) 1517 ; wo [ss:bp - 18] = para_per_sector 1518 ; wo [ss:bp - 20] = entries_per_sector 1519 ; ss:sp = ss:bp - 20 1520 ; (Note: The following stack frame entries are currently 1521 ; only used by FSIBOOT itself, so they may be 1522 ; considered implementation detail instead of 1523 ; part of the FSIBOOT protocol.) 1524 ; wo [ss:bp - 22] = last_available_sector (uninit) 1525 ; wo [ss:bp - 24] = adj_sectors_per_cluster (uninit) 1526 ; wo [ss:bp - 26] = paras_left (uninit) 1527 ; Stack layout has to match! 1528 ; ss = ds 1529 ; ss:bp + ((11 + ebpbNew + BPBN_size + 1) & ~1) 1530 ; = ss:fsiboot_table, refer to its comments 1531 ; cs set to address jump table offsets in fsiboot_table 1532 ; ds set to address fsiboot_table.load_name 1533 ; OUT: Jumps to fsiboot_table.error if error occurs: 1534 ; al = error condition letter 1535 ; Else: 1536 ; data_start (within partition) initialised 1537 ; load_seg => behind last loaded data 1538 ; fat_seg initialised (from bx) 1539 ; fat_sector initialised 1540 ; (-1 initially, loaded sector-in-FAT later) 1541 ; first_cluster initialised 1542 ; last_available_sector initialised 1543 ; (from input ax minus word [para_per_sector]) 1544 ; adj_sectors_per_cluster initialised (from BPB) 1545 ; paras_left initialised 1546 ; Jumps to fsiboot_table.success if loaded entirely: 1547 ; dword [ss:sp] = first cluster 1548 ; Jumps to fsiboot_table.memory_full if loaded partially: 1549 ; al = error condition letter ('M') 1550 ; dword [ss:sp] = current cluster number 1551 ; dword [ss:sp + 4] = first cluster 1552 1553 0000020C 895EF8 mov word [VAR(fat_seg)], bx ; initialise => FAT buffer 1554 1555 0000020F 2B46EE sub ax, word [VAR(para_per_sector)] 1556 00000212 7232 jc .CY_fsiboot_error_badchain 1557 00000214 50 push ax ; push into word [VAR(last_available_sector)] 1558 1559 00000215 8B5E6A mov bx, word [VAR(fsiboot_table.loadseg)] 1560 00000218 895EFA mov word [VAR(load_seg)], bx ; initialise => load address 1561 0000021B 39D8 cmp ax, bx 1562 0000021D 7227 jb .CY_fsiboot_error_badchain 1563 1564 ; adjusted sectors per cluster (store in a word, 1565 ; and decode EDR-DOS's special value 0 meaning 256) 1566 0000021F 8A460D mov al, [VAR(sectors_per_cluster)] 1567 00000222 48 dec ax 1568 00000223 B400 mov ah, 0 1569 00000225 40 inc ax 1570 00000226 50 push ax ; push into word [VAR(adj_sectors_per_cluster)] 1571 00000227 48 dec ax ; ! ah = 0 1572 00000228 8A4610 mov al,[VAR(num_fats)] ; ! ah = 0, hence ax = number of FATs 1573 0000022B 50 push ax 1574 0000022C F76624 mul word [VAR(sectors_per_fat_large)] 1575 ; ax = low word SpF*nF 1576 ; dx = high word 1577 0000022F 93 xchg bx, ax 1578 00000230 87CA xchg cx, dx 1579 ; cx:bx = first mul 1580 00000232 58 pop ax 1581 00000233 F76626 mul word [VAR(sectors_per_fat_large + 2)] 1582 ; ax = high word adjust 1583 ; dx = third word 1584 00000236 85D2 test dx, dx 1585 00000238 F9 stc 1586 00000239 750B jnz .CY_fsiboot_error_badchain 1587 ; dx = zero 1588 0000023B 92 xchg dx, ax 1589 ; dx = high word adjust 1590 ; ax = zero 1591 0000023C 01CA add dx, cx 1592 0000023E 7206 jc .CY_fsiboot_error_badchain 1593 ; dx:bx = result 1594 00000240 93 xchg ax, bx 1595 ; dx:ax = result 1596 ; bx = zero 1597 1598 00000241 03460E add ax,[VAR(num_reserved_sectors)] 1599 00000244 11DA adc dx, bx ; bx is zero here 1600 .CY_fsiboot_error_badchain: 1601 00000246 7256 jc ..@CY_2_fsiboot_error_badchain 1602 1603 ; first sector of disk data area: 1604 00000248 8946FC mov [VAR(data_start)], ax 1605 0000024B 8956FE mov [VAR(data_start+2)], dx 1606 1607 0000024E BFFFFF mov di, -1 1608 00000251 89FE mov si, di 1609 00000253 8976F6 mov word [VAR(fat_sector + 2)], si 1610 00000256 897EF4 mov word [VAR(fat_sector + 0)], di 1611 00000259 E87F00 call dirsearch ; sets up ds == es, es:bx -> dir entry 1612 1613 found_load_file: 1614 0000025C FF5664 call near word [VAR(fsiboot_table.writedirentry)] 1615 1616 ; get starting cluster of file 1617 0000025F FF7714 push word [bx + deClusterHigh] 1618 00000262 FF771A push word [bx + deClusterLow] 1619 1620 ; check FAT+ size bits 1621 00000265 F6470CE7 test byte [bx + dePlusSize], 0E7h 1622 ; test whether bits 7-5 and 2-0 NZ 1623 ; https://web.archive.org/web/20150219123449/http://www.fdos.org/kernel/fatplus.txt 1624 1625 00000269 8B571E mov dx, [bx + deSize + 2] 1626 0000026C 8B5F1C mov bx, [bx + deSize] ; dx:bx = file size (non-FAT+) 1627 0000026F 16 push ss 1628 00000270 1F pop ds ; (reset ds) 1629 00000271 751A jnz .large_file ; FAT+ NZ, clamp to maximum paras --> 1630 1631 00000273 53 push bx ; stack & 15 = nonzero if to round up 1632 00000274 B90400 mov cx, 4 1633 @@: 1634 00000277 D1EA shr dx, 1 1635 00000279 D1DB rcr bx, 1 1636 0000027B E2FA loop @B ; convert to paragraphs, rounding down 1637 0000027D 59 pop cx 1638 ; dx:bx = size in paragraphs, rounded down, if not FAT+ 1639 ; bx = size in paragraphs, rounded down, if < 1_0000h 1640 ; cx & 15 = nonzero if to round up 1641 1642 0000027E 85D2 test dx, dx ; > 0FFFFh paras ? 1643 00000280 750B jnz .large_file ; yes, large file --> 1644 ; no, take actual size 1645 ; bx = size in paragraphs, rounded down 1646 00000282 E8C700 call check_enough.in_bx ; (CHG ax) 1647 1648 ; cx & 15 = nonzero if to round up 1649 00000285 F6C10F test cl, 15 ; NZ if to round up 1650 00000288 7406 jz @F ; ZR, do not round up --> 1651 0000028A 43 inc bx ; round up for last partial paragraph 1652 0000028B 7503 jnz @F ; ! natch. do saturating increment 1653 ; ZR here means we had 0FFFFh. 1654 ; reset the register to same value. 1655 .large_file: 1656 ; if we *branch* here then file is >= 10_0000h bytes so >= 1_0000h paragraphs. 1657 ; that means we do not need to call check_enough because its jb could not 1658 ; possibly branch to the error handler (0FFFFh is never below anything). 1659 0000028D BBFFFF mov bx, 0FFFFh ; set bx for large file case 1660 @@: 1661 1662 00000290 58 pop ax 1663 00000291 5A pop dx ; dx:ax = first cluster 1664 1665 00000292 53 push bx ; push into word [VAR(paras_left)] 1666 1667 00000293 8946F0 mov word [VAR(first_cluster + 0)], ax 1668 00000296 8956F2 mov word [VAR(first_cluster + 2)], dx 1669 00000299 52 push dx 1670 0000029A 50 push ax ; remember cluster for later 1671 1672 0000029B E80501 call check_clust 1673 ..@CY_2_fsiboot_error_badchain: 1674 0000029E 7244 jc ..@CY_3_fsiboot_error_badchain 1675 1676 next_load_cluster: 1677 000002A0 52 push dx 1678 000002A1 50 push ax ; preserve cluster number for later 1679 000002A2 E88200 call clust_to_first_sector 1680 ; dx:ax = first sector of cluster 1681 ; cx = adjusted sectors per cluster 1682 1683 ; xxx - this will always load an entire cluster (e.g. 64 sectors), 1684 ; even if the file is shorter than this 1685 @@: 1686 000002A5 8B5EFA mov bx, [VAR(load_seg)] ; => where to read next sector 1687 000002A8 3B5EEA cmp bx, [VAR(last_available_sector)] 1688 000002AB 7608 jbe @F 1689 000002AD E89600 call check_enough 1690 000002B0 B04D mov al, 'M' ; (! _MEMORY_CONTINUE: error code) 1691 000002B2 FF665E jmp near word [VAR(fsiboot_table.memory_full)] 1692 1693 @@: 1694 000002B5 FF5660 call near word [VAR(fsiboot_table.read_sector)] 1695 000002B8 895EFA mov [VAR(load_seg)], bx ; => after last read data 1696 1697 000002BB 8B5EEE mov bx, [VAR(para_per_sector)] 1698 000002BE 295EE6 sub word [VAR(paras_left)], bx 1699 000002C1 7610 jbe @F ; read enough --> 1700 1701 000002C3 E2E0 loop @BB 1702 000002C5 58 pop ax 1703 000002C6 5A pop dx 1704 1705 000002C7 E89300 call clust_next 1706 000002CA 73D4 jnc next_load_cluster 1707 000002CC 40 inc ax 1708 000002CD 40 inc ax 1709 000002CE A808 test al, 8 ; set in 0FFF_FFF8h--0FFF_FFFFh, 1710 ; clear in 0, 1, and 0FFF_FFF7h 1711 000002D0 7450 jz fsiboot_error_badchain 1712 000002D2 A9 db __TEST_IMM16 ; skip pop bx and pop cx 1713 @@: 1714 000002D3 5B pop bx 1715 000002D4 59 pop cx 1716 000002D5 E86E00 call check_enough 1717 000002D8 FF665C jmp near word [VAR(fsiboot_table.success)] 1718 1719 1720 dirsearch: 1721 000002DB 8B462C mov ax, [VAR(root_cluster)] 1722 000002DE 8B562E mov dx, [VAR(root_cluster + 2)] 1723 000002E1 E8BF00 call check_clust 1724 ..@CY_3_fsiboot_error_badchain: 1725 000002E4 723C jc fsiboot_error_badchain 1726 1727 next_root_clust: 1728 000002E6 52 push dx 1729 000002E7 50 push ax 1730 000002E8 E83C00 call clust_to_first_sector 1731 ; dx:ax = first sector of cluster 1732 ; cx = adjusted sectors per cluster 1733 next_root_sect: 1734 000002EB 51 push cx 1735 000002EC 8B4EEC mov cx, [VAR(entries_per_sector)] 1736 1737 ; Scan root directory for file. We don't bother to check for deleted 1738 ; entries (E5h) or entries that mark the end of the directory (00h). 1739 000002EF 8B5E62 mov bx, [VAR(fsiboot_table.dirbuf)] 1740 000002F2 FF5660 call near word [VAR(fsiboot_table.read_sector)] 1741 1742 000002F5 57 push di 1743 000002F6 31FF xor di, di ; es:di-> first entry in this sector 1744 next_ent: 1745 000002F8 26F6450B18 test byte [es:di + deAttrib], ATTR_DIRECTORY | ATTR_VOLLABEL 1746 000002FD 750E jnz @F ; directory, label, or LFN entry --> (NZ) 1747 000002FF 56 push si 1748 00000300 57 push di 1749 00000301 51 push cx 1750 00000302 8B7666 mov si, [VAR(fsiboot_table.filename)] 1751 ; ds:si-> name to match 1752 00000305 B90B00 mov cx, 11 ; length of padded 8.3 FAT filename 1753 00000308 F3A6 repe cmpsb ; check entry 1754 0000030A 59 pop cx 1755 0000030B 5F pop di 1756 0000030C 5E pop si 1757 @@: ; ZR = match, NZ = mismatch 1758 0000030D 7445 je found_it ; found entry --> 1759 0000030F 8D7D20 lea di, [di + DIRENTRY_size] 1760 1761 00000312 E2E4 loop next_ent ; count down sector's entries (jumps iff cx >0) 1762 00000314 5F pop di 1763 00000315 59 pop cx 1764 00000316 E2D3 loop next_root_sect 1765 00000318 58 pop ax 1766 00000319 5A pop dx 1767 0000031A E84000 call clust_next 1768 0000031D 73C7 jnc next_root_clust 1769 file_not_found: 1770 0000031F B046 mov al, 'F' 1771 00000321 A9 db __TEST_IMM16 ; skip mov al 1772 fsiboot_error_badchain: 1773 00000322 B042 mov al, 'B' 1774 1775 fsiboot_error: 1776 00000324 FF665A jmp near word [VAR(fsiboot_table.error)] 1777 1778 1779 ; INP: dx:ax = cluster - 2 (0-based cluster) 1780 ; OUT: dx:ax = first sector of that cluster 1781 ; cx = adjusted sectors per cluster 1782 ; CHG: bx 1783 clust_to_first_sector: 1784 00000327 8B4EE8 mov cx, word [VAR(adj_sectors_per_cluster)] 1785 0000032A 52 push dx 1786 0000032B F7E1 mul cx 1787 0000032D 93 xchg bx, ax 1788 0000032E 58 pop ax 1789 0000032F 52 push dx 1790 00000330 F7E1 mul cx 1791 00000332 85D2 test dx, dx 1792 00000334 75EC jnz fsiboot_error_badchain 1793 00000336 92 xchg dx, ax 1794 00000337 58 pop ax 1795 00000338 01C2 add dx, ax 1796 0000033A 7207 jc ..@CY_fsiboot_error_badchain 1797 0000033C 93 xchg ax, bx 1798 1799 0000033D 0346FC add ax, [VAR(data_start)] 1800 00000340 1356FE adc dx, [VAR(data_start + 2)] 1801 ..@CY_fsiboot_error_badchain: 1802 00000343 72DD jc fsiboot_error_badchain 1803 ; dx:ax = first sector in cluster 1804 00000345 C3 retn 1805 1806 1807 check_enough: 1808 00000346 8B5EFA mov bx, [VAR(load_seg)] 1809 ; => behind last read sector 1810 00000349 2B5E6A sub bx, word [VAR(fsiboot_table.loadseg)] 1811 .in_bx: 1812 0000034C 3B5E68 cmp bx, word [VAR(fsiboot_table.minpara)] 1813 0000034F B045 mov al, 'E' 1814 00000351 72D1 jb fsiboot_error 1815 00000353 C3 retn 1816 1817 1818 found_it: 1819 ; es:di -> dir entry in dir sector buffer 1820 00000354 89FB mov bx, di 1821 00000356 5F pop di ; restore si:di = loaded FAT sector 1822 00000357 59 pop cx ; (discard sectors per cluster counter) 1823 00000358 59 pop cx 1824 00000359 59 pop cx ; (discard current cluster number) 1825 ; es:bx -> dir entry 1826 0000035A 06 push es 1827 0000035B 1F pop ds ; ds:bx -> directory entry 1828 0000035C C3 retn 1829 1830 1831 ; INP: dx:ax = cluster (0-based) 1832 ; si:di = loaded FAT sector, -1 if none 1833 ; OUT: CY if no next cluster 1834 ; NC if next cluster found 1835 ; dx:ax = next cluster value (0-based) 1836 ; si:di = loaded FAT sector 1837 ; CHG: cx, bx, es 1838 clust_next: 1839 0000035D 83C002 add ax, 2 1840 00000360 83D200 adc dx, 0 1841 1842 00000363 01C0 add ax, ax 1843 00000365 11D2 adc dx, dx 1844 00000367 01C0 add ax, ax 1845 00000369 11D2 adc dx, dx ; * 4 = byte offset into FAT (0--4000_0000h) 1846 0000036B 50 push ax 1847 0000036C 92 xchg ax, dx 1848 0000036D 31D2 xor dx, dx ; dx:ax = high word 1849 0000036F F7760B div word [VAR(bytes_per_sector)] 1850 00000372 93 xchg bx, ax ; bx = result high word, clobbers ax 1851 00000373 58 pop ax ; dx = remainder, ax = low word 1852 00000374 F7760B div word [VAR(bytes_per_sector)] 1853 00000377 87D3 xchg dx, bx ; dx:ax = result, bx = remainder 1854 ; dx:ax = sector offset into FAT (0--200_0000h) 1855 ; bx = byte offset into FAT sector (0--8190) 1856 00000379 39F2 cmp dx, si 1857 0000037B 7504 jne @F ; read sector 1858 0000037D 39F8 cmp ax, di 1859 0000037F 7418 je @FF ; sector is already buffered 1860 @@: 1861 00000381 89D6 mov si, dx 1862 00000383 89C7 mov di, ax 1863 00000385 8956F6 mov word [VAR(fat_sector + 2)], dx 1864 00000388 8946F4 mov word [VAR(fat_sector + 0)], ax 1865 1866 0000038B 53 push bx 1867 0000038C 03460E add ax, [VAR(fat_start)] 1868 0000038F 83D200 adc dx, 0 1869 00000392 8B5EF8 mov bx, [VAR(fat_seg)] 1870 00000395 FF5660 call near word [VAR(fsiboot_table.read_sector)] 1871 00000398 5B pop bx 1872 @@: 1873 00000399 8E46F8 mov es, [VAR(fat_seg)] 1874 0000039C 268B07 mov ax, [es:bx] 1875 0000039F 268B5702 mov dx, [es:bx + 2] 1876 1877 ; INP: dx:ax = cluster value, 2-based 1878 ; OUT: dx:ax -= 2 (makes it 0-based) 1879 ; CY iff invalid cluster 1880 check_clust: 1881 000003A3 80E60F and dh, 0Fh 1882 000003A6 83E802 sub ax, 2 1883 000003A9 83DA00 sbb dx, 0 1884 000003AC 81FAFF0F cmp dx, 0FFFh 1885 000003B0 7203 jb @F ; CY here means valid ...- 1886 000003B2 83F8F5 cmp ax, 0FFF7h - 2 1887 @@: ; -... or if NC first, CY here also 1888 000003B5 F5 cmc ; NC if valid 1889 000003B6 C3 retn 1890 1891 1892 ; INP: ax = 0 1893 ; cs = 0, cs:7C00h -> loaded first stage and FSIBOOT 1894 ; OUT: if supported, 1895 ; ax => TOM 1896 ; else, 1897 ; ax = 0 1898 ; CHG: cx, dx, si 1899 ; STT: ds => 0, ds == ss 1900 fsiboot_memorysize: 1901 ; Get conventional memory size and store it 1902 000003B7 CD12 int 12h 1903 000003B9 B106 mov cl, 6 1904 000003BB D3E0 shl ax, cl 1905 %if 1 || _RPL ; +33 bytes 1906 000003BD 92 xchg dx, ax 1907 000003BE C536BC00 lds si, [4 * 2Fh] 1908 000003C2 83C603 add si, 3 1909 000003C5 AC lodsb 1910 000003C6 3C52 cmp al, 'R' 1911 000003C8 750F jne .no_rpl 1912 000003CA AC lodsb 1913 000003CB 3C50 cmp al, 'P' 1914 000003CD 750A jne .no_rpl 1915 000003CF AC lodsb 1916 000003D0 3C4C cmp al, 'L' 1917 000003D2 7505 jne .no_rpl 1918 000003D4 B8064A mov ax, 4A06h 1919 000003D7 CD2F int 2Fh 1920 .no_rpl: 1921 000003D9 16 push ss 1922 000003DA 1F pop ds 1923 000003DB 92 xchg ax, dx 1924 %endif 1925 fsiboot_reserved: 1926 000003DC C3 retn 1927 1928 1929 fsinfo_available: 1930 000003DD 26 _fill 480 + 4 - 2 - 2 - 2,38,fsiboot 1931 1932 %assign num $-fsinfo_available 1933 %warning FSINFO: num bytes still available. 1933 ****************** warning: FSINFO: 1 bytes still available. [-w+user] 1934 1935 1936 ; INP: - 1937 ; OUT: - 1938 ; CHG: any 1939 ; REM: Reserved for future expansion. 1940 reserved_entrypoint: 1941 000003DE [DC03] dw fsiboot_reserved 1942 1943 ; INP: ax = 0 1944 ; cs = 0, cs:7C00h -> loaded first stage and FSIBOOT 1945 ; OUT: if supported, 1946 ; ax => TOM 1947 ; else, 1948 ; ax = 0 1949 ; CHG: cx, dx, si 1950 ; STT: ds => 0, ds == ss 1951 memorysize_entrypoint: 1952 000003E0 [B703] dw fsiboot_memorysize 1953 1954 ; INP: si:di = loaded sector-in-FAT 1955 ; ds = ss 1956 ; ds:word [fsiboot_table.filename] -> 8.3 filename 1957 ; CHG: ax, cx, dx 1958 ; OUT: si:di updated, if so 1959 ; ds == es 1960 ; ds:bx == es:bx -> found directory entry 1961 ; jumps to error handler if an error occurs, 1962 ; ds = ss 1963 ; STT: stack variables as set up by main FSIBOOT entry 1964 dirsearch_entrypoint: 1965 000003E2 [DB02] dw dirsearch 1966 1967 000003E4 72724161 dd "rrAa" 1968 000003E8 FFFFFFFF dd -1 ; number of free clusters 1969 000003EC FFFFFFFF dd -1 ; first free cluster 1970 000003F0 00000000 times 3 dd 0 ; FSINFO.reserved2 1971 000003FC 000055AA dd 0_AA55_0000h ; FSINFO.signature3 1972 1973 end_after_fsiboot: 1974 %if $ - fsiboot != 512 1975 %error Wrong FSIBOOT layout 1976 %endif