1 2 %if 0 3 4 Loader for finishing file system booting 5 by E. C. Masloch, 2017--2025 6 7 Usage of the works is permitted provided that this 8 instrument is retained with the works, so that any entity 9 that uses the works is notified of this instrument. 10 11 DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. 12 13 %endif 14 15 16 %assign __lMACROS1_MAC__DEBUG_DEFAULTS 1 17 %include "lmacros3.mac" 1 <1> [list -] 14 <2> [list -] 14 <1> [list -] 18 numdef DEBUG5 19 %idefine d5 _d 5, 20 21 struc BS 22 00000000 ?????? bsJump: resb 3 23 00000003 ???????????????? bsOEM: resb 8 24 bsBPB: 25 endstruc 26 27 struc EBPB ; BPB sec 28 00000000 ???? bpbBytesPerSector: resw 1 ; offset 00h 0Bh 29 00000002 ?? bpbSectorsPerCluster: resb 1 ; offset 02h 0Dh 30 00000003 ???? bpbReservedSectors: resw 1 ; offset 03h 0Eh 31 00000005 ?? bpbNumFATs: resb 1 ; offset 05h 10h 32 00000006 ???? bpbNumRootDirEnts: resw 1 ; offset 06h 11h -- 0 for FAT32 33 00000008 ???? bpbTotalSectors: resw 1 ; offset 08h 13h 34 0000000A ?? bpbMediaID: resb 1 ; offset 0Ah 15h 35 0000000B ???? bpbSectorsPerFAT: resw 1 ; offset 0Bh 16h -- 0 for FAT32 36 0000000D ???? bpbCHSSectors: resw 1 ; offset 0Dh 18h 37 0000000F ???? bpbCHSHeads: resw 1 ; offset 0Fh 1Ah 38 00000011 ???????? bpbHiddenSectors: resd 1 ; offset 11h 1Ch 39 00000015 ???????? bpbTotalSectorsLarge: resd 1 ; offset 15h 20h 40 bpbNew: ; offset 19h 24h 41 42 00000019 ???????? ebpbSectorsPerFATLarge: resd 1 ; offset 19h 24h 43 0000001D ???? ebpbFSFlags: resw 1 ; offset 1Dh 28h 44 0000001F ???? ebpbFSVersion: resw 1 ; offset 1Fh 2Ah 45 00000021 ???????? ebpbRootCluster: resd 1 ; offset 21h 2Ch 46 00000025 ???? ebpbFSINFOSector: resw 1 ; offset 25h 30h 47 00000027 ???? ebpbBackupSector: resw 1 ; offset 27h 32h 48 00000029 ebpbReserved: resb 12 ; offset 29h 34h 49 ebpbNew: ; offset 35h 40h 50 endstruc 51 52 struc BPBN ; ofs B16 S16 B32 S32 53 00000000 ?? bpbnBootUnit: resb 1 ; 00h 19h 24h 35h 40h 54 00000001 ?? resb 1 ; 01h 1Ah 25h 36h 41h 55 00000002 ?? bpbnExtBPBSignature: resb 1 ; 02h 1Bh 26h 37h 42h -- 29h for valid BPBN 56 00000003 ???????? bpbnSerialNumber: resd 1 ; 03h 1Ch 27h 38h 43h 57 00000007 bpbnVolumeLabel: resb 11 ; 07h 20h 2Bh 3Ch 47h 58 00000012 ???????????????? bpbnFilesystemID: resb 8 ; 12h 2Bh 36h 47h 52h 59 endstruc ; 1Ah 33h 3Eh 4Fh 5Ah 60 61 struc LOADSTACKVARS, -10h 62 FFFFFFF0 ???????? lsvFirstCluster: resd 1 63 FFFFFFF4 ???????? lsvFATSector: resd 1 64 FFFFFFF8 ???? lsvFATSeg: resw 1 65 FFFFFFFA ???? lsvLoadSeg: resw 1 66 FFFFFFFC ???????? lsvDataStart: resd 1 67 endstruc 68 69 lsvclSignature equ "CL" 70 lsvclBufferLength equ 256 71 72 struc LOADDATA, LOADSTACKVARS - 10h 73 FFFFFFE0 ???? ldMemoryTop: resw 1 74 FFFFFFE2 ???? ldLoadTop: resw 1 75 FFFFFFE4 ???? ldSectorSeg: resw 1 76 FFFFFFE6 ?? ldFATType: resb 1 77 FFFFFFE7 ?? ldHasLBA: resb 1 78 FFFFFFE8 ???? ldClusterSize: resw 1 79 FFFFFFEA ???? ldParaPerSector:resw 1 80 ldLoadingSeg: ; word 81 ldQueryPatchValue: ; word 82 lsvCommandLine: ; word 83 .start: equ $ - lsvclBufferLength 84 FFFFFFEC ???? .signature: resw 1 85 ldLoadUntilSeg: ; word 86 lsvExtra: ; word 87 FFFFFFEE ?? .partition: resb 1 ; byte 88 FFFFFFEF ?? .flags: resb 1 ; byte 89 endstruc 90 91 ldhlfLBA: equ 1 92 ldhlfForceSingleSector: equ 2 93 ldhlfPreserveLoader equ 4 94 95 lsvefNoDataStart equ 1 96 lsvefPartitionNumber equ 2 97 lsvefPreserveLoader equ 4 98 99 %if lsvefPreserveLoader != ldhlfPreserveLoader 100 %error Unexpected flag values 101 %endif 102 103 struc LOADCMDLINE, LOADDATA - lsvclBufferLength 104 ldCommandLine: 105 FFFFFEE0 .start: resb lsvclBufferLength 106 endstruc 107 108 struc LOADMULTIVARS, LOADCMDLINE - 8 109 FFFFFED8 ???????? ldmCluster: resd 1 110 FFFFFEDC ???????? ldmSector: resd 1 111 endstruc 112 113 struc LBAPACKET 114 00000000 ???? lpSize: resw 1 115 00000002 ???? lpCount: resw 1 116 00000004 ???????? lpBuffer: resd 1 117 00000008 ???????????????? lpSector: resq 1 118 endstruc 119 120 struc PARTINFO 121 00000000 ?? piBoot: resb 1 122 00000001 ?????? piStartCHS: resb 3 123 00000004 ?? piType: resb 1 124 00000005 ?????? piEndCHS: resb 3 125 00000008 ???????? piStart: resd 1 126 0000000C ???????? piLength: resd 1 127 endstruc 128 129 ptEmpty: equ 0 130 ptFAT12: equ 1 131 ptFAT16_16BIT_CHS: equ 4 132 ptExtendedCHS: equ 5 133 ptFAT16_CHS: equ 6 134 ptFAT32_CHS: equ 0Bh 135 ptFAT32: equ 0Ch 136 ptFAT16: equ 0Eh 137 ptExtended: equ 0Fh 138 ptLinux: equ 83h 139 ptExtendedLinux: equ 85h 140 141 142 query_no_multi equ 8 143 query_no_geometry equ 4 144 query_no_chs equ 2 145 query_no_lba equ 1 146 query_fd_multiplier equ 1 147 query_hd_multiplier equ 256 148 query_all_multiplier equ query_fd_multiplier + query_hd_multiplier 149 150 151 %ifndef _MAP 152 %elifempty _MAP 153 %else ; defined non-empty, str or non-str 154 [map all _MAP] 155 %endif 156 157 defaulting 158 159 numdef QUERY_PATCH, 1 ; use new style patch of CHS/LBA/geometry 160 numdef QUERY_DEFAULT, 0 161 numdef QUERY_GEOMETRY, 1 ; query geometry via 13.08 (for CHS access) 162 numdef RPL, 1 ; support RPL and do not overwrite it 163 numdef CHS, 1 ; support CHS (if it fits) 164 numdef LBA, 1 ; support LBA (if available) 165 numdef LBA_33_BIT, 1 ; support 33-bit LBA 166 numdef LBA_CHECK_NO_33, 1 ; else: check that LBA doesn't carry 167 numdef MULTISECTOR, 1 ; stash multi-sector loader into end2 168 numdef LBA_MULTI, 1 ; stash LBA multi-sector loader into end3 169 numdef ADDPROGRESS, 0 ; patch to display progress dots in end3 170 numdef RESET_SINGLE_SECTOR, 1 ; clear ldhlfForceSingleSector flag at end 171 numdef MULTIBOOT1, 1 ; use Multiboot specification loader 172 numdef MULTIBOOT2, 1 ; use Multiboot2 specification loader 173 numdef EDRDOS, 0 ; use heuristic to detect EDR-DOS entry 174 numdef EDRDOS_AUTO, 1 ; support EDR-DOS entry if large enough 175 numdef LSVEXTRA, 1 ; use lsvExtra field 176 ; (needed if to use partition scanner) 177 numdef LOADERSUPPORT, 0 ; support loader extensions 178 excdef !_LSVEXTRA, LOADERSUPPORT 179 180 numdef LBA_SKIP_CHECK, 0 ; don't use proper LBA extensions check 181 ; (mustn't be enabled if multi-sector loader is included) 182 numdef LBA_RETRY, 1 ; retry LBA reads 183 numdef CHS_RETRY, 1 ; retry CHS reads 184 numdef DMA_BOUNDARY_CHECK_CODE,0; check for DMA boundary error code, 185 ; if 0 then always try sectorseg read 186 numdef STACKSIZE, 2048 187 %if _STACKSIZE < 256 188 %error Too small stack size 189 %elif _STACKSIZE > 3 * 1024 190 ; Note that we use 8 KiB for SectorSeg, 8 KiB for FATSeg, 191 ; 512 bytes + (ebpbNew - bpbNew) for the boot sector, 192 ; and a few paragraphs left for MCBs and headers. As the 193 ; protocol is implemented with a 20 KiB reserved area (below 194 ; EBDA / RPL / end of low memory), this results in a maximum 195 ; stack size around 3 KiB (substantially below 4 KiB). 196 %error Too large stack size 197 %endif 198 numdef CHECKSUM, 0 ; include checksumming of kernel image 199 ; (this is obsolete and shouldn't be used. will 200 ; fail if multi-sector loader is enabled.) 201 %if _CHECKSUM 202 %include "inicheck.mac" 203 %endif 204 205 numdef PADDING, 0 206 strdef PAYLOAD_FILE, "lDOSLOAD.BIN" 207 numdef EXEC_OFFSET, 0 208 numdef EXEC_SEGMENT, 0 209 strdef INILOAD_SIGNATURE, "XX" 210 numdef TEST_PROGRAM, 0 211 212 numdef IMAGE_EXE, 0 213 numdef IMAGE_EXE_CS, -16 ; relative-segment for CS 214 numdef IMAGE_EXE_IP, 256 +64 ; value for IP 215 ; The next two are only used if _IMAGE_EXE_AUTO_STACK is 0. 216 numdef IMAGE_EXE_SS, -16 ; relative-segment for SS 217 numdef IMAGE_EXE_SP, 0FFFEh ; value for SP (0 underflows) 218 numdef IMAGE_EXE_AUTO_STACK, 0, 2048 ; allocate stack behind image 219 numdef IMAGE_EXE_MIN, 65536 ; how much to allocate for the process 220 %ifndef _IMAGE_EXE_MIN_CALC 221 %define _IMAGE_EXE_MIN_CALC (((_IMAGE_EXE_MIN - (payload.actual_end - payload) - 256 + _IMAGE_EXE_AUTO_STACK) + 15) & ~15) 226 %endif 227 numdef IMAGE_EXE_MAX, -1 228 229 numdef SECOND_PAYLOAD_EXE, 0 230 numdef SECOND_PAYLOAD_EXE_CS, -16 231 numdef SECOND_PAYLOAD_EXE_IP, 256 +64 232 numdef SECOND_PAYLOAD_EXE_SS, -16 233 numdef SECOND_PAYLOAD_EXE_SP, 0FFFEh 234 numdef SECOND_PAYLOAD_EXE_AUTO_STACK, 0, 2048 235 numdef SECOND_PAYLOAD_EXE_MIN, 65536 236 %ifndef _SECOND_PAYLOAD_EXE_MIN_CALC 237 %define _SECOND_PAYLOAD_EXE_MIN_CALC (((_SECOND_PAYLOAD_EXE_MIN - (second_payload.actual_end - second_payload) - 256 + _SECOND_PAYLOAD_EXE_AUTO_STACK) + 15) & ~15) 242 %endif 243 numdef SECOND_PAYLOAD_EXE_MAX, -1 244 strdef SECOND_PAYLOAD_FILE, "lDOSEXEC.COM" 245 strdef EXE_TRAIL_INCBIN, "" 246 247 248 %define MODULE iniload 249 strdef INILOAD_CFG, "" 250 %ifnidn _INILOAD_CFG, "" 251 %include _INILOAD_CFG 1 <1> ; File auto-generated, do not edit. 2 <1> strdef INILOAD_SIGNATURE, "FD" 3 <1> %assign _DEVICE 0 4 <1> %ifidni MODULE, fdkernpl 5 <1> %define _PAYLOAD_FILE "kernel.sys" 6 <1> %endif 7 <1> %ifn _TEST_PROGRAM 8 <1> numdef IMAGE_EXE, 0 9 <1> numdef SECOND_PAYLOAD_EXE, 0 10 <1> %endif 252 %endif 253 254 strdef INILOAD_PAYLOAD_INCLUDE, "" 255 256 257 cpu 8086 258 org 0 259 260 %if _EDRDOS_AUTO 261 filesize equ iniload_filesize_late 262 %if filesize >= (32 * 1024) 263 %assign _EDRDOS 1 264 %else 265 %assign _EDRDOS 0 266 %endif 267 %endif 268 269 %if _IMAGE_EXE && _SECOND_PAYLOAD_EXE 270 %error Cannot use both of these. 271 %endif 272 273 %ifn _LBA || _CHS 274 %error Neither disk access method enabled. 275 %endif 276 277 %push 278 %define %$string _INILOAD_SIGNATURE 279 %strlen %$length %$string 280 %if %$length != 2 281 %error Invalid signature 282 %endif 283 %substr %$letter %$string 1 284 %if %$letter <= 32 || %$letter >= 127 285 %error Invalid signature 286 %endif 287 %substr %$letter %$string 2 288 %if %$letter <= 32 || %$letter >= 127 289 %error Invalid signature 290 %endif 291 %pop 292 293 294 start: 295 00000000 4D5A db "MZ" ; exeSignature 296 ; dec bp, pop dx 297 00000002 EB16 jmp strict short ms6_entry ; exeExtraBytes 298 ; db 0EBh, 16h ; dw 16EBh 299 %if _IMAGE_EXE 300 ; For now hardcoded to carry a .COM-like executable. 301 ; Note: With _IMAGE_EXE_AUTO_STACK, the 302 ; stack segment will be behind the image. 303 dw (payload_end_late + 511) / 512 ; exePages 304 dw 0 ; exeRelocItems 305 dw (payload -$$+0) >> 4 ; exeHeaderSize 306 dw (_IMAGE_EXE_MIN_CALC + 15) >> 4 ; exeMinAlloc 307 %if _IMAGE_EXE_MAX 308 dw _IMAGE_EXE_MAX ; exeMaxAlloc 309 %else 310 dw (_IMAGE_EXE_MIN_CALC + 15) >> 4 ; exeMaxAlloc 311 %endif 312 %if _IMAGE_EXE_AUTO_STACK 313 dw ((payload.actual_end - payload) + _IMAGE_EXE_MIN_CALC - _IMAGE_EXE_AUTO_STACK + 15) >> 4 ; exeInitSS 316 ; ss: payload size minus 512 (conservative, assume DOS 317 ; treats bogus exeExtraBytes as below 512 bytes.) 318 ; + exeMinAlloc 319 ; - auto stack size 320 dw _IMAGE_EXE_AUTO_STACK ; exeInitSP 321 ; sp = auto stack size (eg 800h) 322 %else 323 dw _IMAGE_EXE_SS ; exeInitSS 324 dw _IMAGE_EXE_SP ; exeInitSP 325 %endif 326 dw 0 ; exeChecksum 327 dw _IMAGE_EXE_IP, _IMAGE_EXE_CS ; exeInitCSIP 328 dw 0 ; exeRelocTable 329 %elif _SECOND_PAYLOAD_EXE 330 ; For now hardcoded to carry a .COM-like executable. 331 ; Note: With _SECOND_PAYLOAD_EXE_AUTO_STACK, the 332 ; stack segment will be behind the image. 333 dw (second_payload_end_late + 511) / 512 ; exePages 334 dw 0 ; exeRelocItems 335 dw (second_payload_late +0) >> 4 ; exeHeaderSize 336 dw (_SECOND_PAYLOAD_EXE_MIN_CALC + 15) >> 4 ; exeMinAlloc 337 %if _SECOND_PAYLOAD_EXE_MAX 338 dw _SECOND_PAYLOAD_EXE_MAX ; exeMaxAlloc 339 %else 340 dw (_SECOND_PAYLOAD_EXE_MIN_CALC + 15) >> 4 ; exeMaxAlloc 341 %endif 342 %if _SECOND_PAYLOAD_EXE_AUTO_STACK 343 dw ((second_payload_actual_end_late - second_payload_late) + _SECOND_PAYLOAD_EXE_MIN_CALC - _SECOND_PAYLOAD_EXE_AUTO_STACK + 15) >> 4 ; exeInitSS 346 dw _SECOND_PAYLOAD_EXE_AUTO_STACK ; exeInitSP 347 %else 348 dw _SECOND_PAYLOAD_EXE_SS ; exeInitSS 349 dw _SECOND_PAYLOAD_EXE_SP ; exeInitSP 350 %endif 351 dw 0 ; exeChecksum 352 dw _SECOND_PAYLOAD_EXE_IP, _SECOND_PAYLOAD_EXE_CS ; exeInitCSIP 353 dw 0 ; exeRelocTable 354 %else 355 00000004 FFFF dw -1 ; exePages 356 00000006 0000 dw 0 ; exeRelocItems 357 00000008 0000 dw 0 ; exeHeaderSize 358 0000000A FFFF dw -1 ; exeMinAlloc 359 0000000C FFFF dw -1 ; exeMaxAlloc 360 0000000E F0FF0000 dw -16, 0 ; exeInitSS, exeInitSP 361 00000012 0000 dw 0 ; exeChecksum 362 00000014 0001F0FF dw 100h, -16 ; exeInitCSIP 363 00000018 0000 dw 0 ; exeRelocTable 364 %endif 365 366 ms6_entry: 367 ; This is the MS-DOS 6 / IBMDOS compatible entry point. 368 ; Note that this supports FAT32 for PC-DOS 7.10! 369 ; cs:ip = 70h:0 370 ; ax:bx = first data sector of first cluster, 371 ; including hidden sectors 372 ; 0:7C00h-> boot sector with (E)BPB, 373 ; load unit field set, hidden sectors set 374 ; (actually boot unit in dl; because the "MZ" signature 375 ; destroys dl we assume it's in the BPB too) 376 ; ss:sp is expected to -> below-or-equal 0:7C00h, this fact 377 ; is used to detect EDR-DOS load if cs = 70h as EDR-DOS load 378 ; will never overlap the stack with the loaded file data. 379 ; Note that bp may be unset by MS-DOS 6 loaders! 380 ; Either: 381 ; dword [ss:sp] = 0:78h = 1Eh * 4 (IVT entry of int 1Eh) 382 ; dword [ss:sp + 4] = old int 1Eh address 383 ; Or: 384 ; ds:si = old int 1Eh address 385 ; 0:500h-> directory entry for BIO file 386 387 ; This entrypoint is also used by the FreeDOS / EDR-DOS entry. 388 ; cs:ip = 60h:0 or 70h:0 (segment can be changed by SYS /L option) 389 ; bl and/or dl = load unit (not used by us) 390 ; ss:bp -> boot sector with (E)BPB, 391 ; load unit field set, hidden sectors set 392 ; ss:sp -> below boot sector, not overlapping file data 393 ; Full file loaded. 394 ; Detected by cs != 70h or ss:sp -> above full file data. 395 396 ; This entrypoint is also used by the MS-DOS v1 entry. 397 ; ip = 100h, cs = unknown 398 ; cs:ip -> file data, might be clamped to a limit < 64 KiB 399 ; Detected by ip != 0. 400 0000001A FA cli 401 0000001B 52 push dx 402 0000001C 45 inc bp ; undo signature instructions 403 404 d3 call d3_display_two_characters 405 d3 test ax, "00" 406 407 0000001D 8CC9 mov cx, cs 408 0000001F 83F970 cmp cx, 70h ; cx == 70h ? 409 00000022 7403 je @F ; yes --> 410 ; no, must be FreeDOS or .com entry 411 .freedos_or_msdos1_com_entry: 412 00000024 E9A908 jmp freedos_or_msdos1_com_entry 413 @@: 414 415 %if _EDRDOS 416 00000027 89E7 mov di, sp ; -> sp 417 00000029 B104 mov cl, 4 418 0000002B D3EF shr di, cl ; to paragraphs, rounding down 419 0000002D 8CD1 mov cx, ss ; => ss 420 0000002F 01CF add di, cx ; ss:sp converted to paragraphs 421 00000031 81FFE910 cmp di, 70h + paras(iniload_filesize_late) 422 00000035 73ED jae .freedos_or_msdos1_com_entry; doesn't overlap file data --> 423 %endif 424 425 ; xor cx, cx 426 ;; test dx, dx 427 ;; jnz @FF 428 ; Actual DOS will always put a zero word on top of 429 ; the stack. But when the debugger loads us as 430 ; a flat format binary it may set up another 431 ; stack segment or not initialise the stack slot. 432 ; (So as to avoid corrupting the binary.) 433 ; The offset check should suffice anyway. 434 00000037 E80000 call @F 435 @@: 436 0000003A 59 pop cx 437 0000003B 81E9[3A00] sub cx, @B ; cx == 0 iff entered at offset 0 438 0000003F 75E3 jne .freedos_or_msdos1_com_entry 439 @@: 440 ; cx = 0 441 442 ; Note: It has been observed that some IBMBIO.COM / IO.SYS 443 ; boot sector loaders pass the int 1Eh address on the 444 ; stack (like MS-DOS 7 loading does). So we detect 445 ; whether the first dword (far pointer to IVT entry) 446 ; matches and then assume that the second dword has 447 ; the original int 1Eh address. Else, ds:si is used. 448 00000041 89E5 mov bp, sp 449 00000043 BF7800 mov di, 1Eh * 4 ; -> IVT entry of int 1Eh 450 00000046 39FA cmp dx, di ; int 1Eh address on stack ? 451 00000048 7503 jne @F ; no --> 452 0000004A 394E02 cmp word [bp + 2], cx ; segment 0 in next word ? 453 @@: 454 0000004D E96203 jmp ms6_continue1 455 456 457 error: 458 %if _MULTISECTOR 459 .multi_patch: 460 00000050 A9C806 test ax, restore_dpt - @F 461 @@: ; if CHS multi-sector loader used, reset DPT 462 %endif 463 00000053 BE[1E02] mov si, msg.error 464 00000056 E81300 call disp_error ; display "Load error: " 465 00000059 5E pop si ; -> ASCII string terminated by dot 466 0000005A E80F00 call disp_error ; display up to before dot 467 0000005D 4E dec si ; -> point back at dot to return after display 468 0000005E E80400 call disp_error.loop ; display the dot, ah = 0 469 00000061 CD16 int 16h 470 00000063 CD19 int 19h 471 472 473 disp_error.loop: 474 00000065 B40E mov ah, 0Eh 475 00000067 BB0700 mov bx, 7 ; bh = page 0 476 ; push bp 477 ; (call may change bp, but it is not used here any longer.) 478 0000006A CD10 int 10h 479 ; pop bp 480 disp_error: 481 0000006C 2EAC cs lodsb 482 0000006E 3C2E cmp al, '.' 483 00000070 75F3 jne .loop 484 00000072 98 cbw ; al = 2Eh '.' so ah here becomes zero 485 00000073 C3 retn 486 487 488 ; INP: EBPB new unit field 489 ; query patch site 490 ; OUT: BPB CHS heads and sectors rewritten (if query success) 491 ; ldHasLBA set to 1 or 0 to use LBA or CHS accesses 492 ; CHG: ax, bx, cx, dx, es, di 493 ; REM: Currently doesn't observe _LBA or _CHS defines. 494 ; If _LBA_SKIP_CHECK, ldHasLBA is set to zero. 495 query_geometry: 496 %if ldhlfLBA != 1 497 %error Expecting LBA flag to equal 1 498 %endif 499 %if _QUERY_GEOMETRY || !_LBA_SKIP_CHECK 500 ; magic bytes start 501 00000074 8A5640 mov dl, [bp + bsBPB + ebpbNew + bpbnBootUnit] 502 ; magic bytes 503 %if _QUERY_PATCH 504 00000077 B80000 mov ax, _QUERY_DEFAULT ; magic bytes, checked by patch script 505 ..@query_patch_site equ $ - 2 506 0000007A 84D2 test dl, dl ; hard disk unit ? 507 0000007C 7902 jns @F ; no --> 508 0000007E 86C4 xchg al, ah ; get high byte into al 509 ; magic bytes end 510 @@: 511 %if ($ - $$) > 1536 512 %error Query patch site should be in first 1536 Bytes 513 %endif 514 %endif 515 %endif 516 517 %if _QUERY_GEOMETRY ; +30 bytes 518 %if !_LBA_SKIP_CHECK 519 00000080 52 push dx 520 %if _QUERY_PATCH 521 00000081 50 push ax 522 %endif 523 %endif 524 525 %if _QUERY_PATCH 526 00000082 A804 test al, query_no_geometry 527 ; don't query geometry ? 528 00000084 7517 jnz @F ; yes --> 529 %endif 530 531 ; Note: The int 13h function 08h call may change or 532 ; set ax, bx, cx, dx, es, di. es is left as 533 ; indeterminate afterwards. 534 ; test dl, dl ; floppy? 535 ; jns @F ; don't attempt query, might fail --> 536 ; Note that while the original PC BIOS doesn't support this function 537 ; (for its diskettes), it does properly return the error code 01h. 538 ; https://sites.google.com/site/pcdosretro/ibmpcbios (IBM PC version 1) 539 00000086 B408 mov ah, 08h 540 00000088 31C9 xor cx, cx ; initialise cl to 0 541 0000008A F9 stc ; initialise to CY 542 0000008B CD13 int 13h ; query drive geometry 543 0000008D 720E jc @F ; apparently failed --> 544 0000008F 83E13F and cx, 3Fh ; get sectors 545 00000092 7409 jz @F ; invalid (S is 1-based), don't use --> 546 00000094 894E18 mov [bp + bsBPB + bpbCHSSectors], cx 547 00000097 88F1 mov cl, dh ; cx = maximum head number 548 00000099 41 inc cx ; cx = number of heads (H is 0-based) 549 0000009A 894E1A mov [bp + bsBPB + bpbCHSHeads], cx 550 @@: 551 %endif 552 553 %if !_LBA_SKIP_CHECK 554 %if _QUERY_GEOMETRY 555 %if _QUERY_PATCH 556 0000009D 58 pop ax ; restore query patch flags in al 557 %endif 558 0000009E 5A pop dx ; restore unit number in dl 559 %endif 560 %if _QUERY_PATCH 561 %if _MULTISECTOR 562 0000009F 50 push ax 563 %endif 564 %if query_no_lba != 1 565 %error Unexpected flag layout 566 %endif 567 000000A0 D0E8 shr al, 1 ; CY if force CHS (no LBA) 568 000000A2 7218 jc @F ; if so --> 569 %if query_no_chs != 2 570 %error Unexpected flag layout 571 %endif 572 000000A4 2401 and al, 1 ; force LBA ? (no CHS) 573 000000A6 7523 jnz .done_lba ; yes --> 574 %endif 575 000000A8 1E push ds 576 000000A9 BB4000 mov bx, 40h ; bx = 40h 577 000000AC 8EDB mov ds, bx ; ds = 40h 578 ; Setting ds = 40h is a Book8088 bugfix, refer to 579 ; http://www.bttr-software.de/forum/forum_entry.php?id=21061 580 581 000000AE B80041 mov ax, 4100h 582 000000B1 BBAA55 mov bx, 55AAh 583 000000B4 31C9 xor cx, cx 584 000000B6 30F6 xor dh, dh 585 000000B8 F9 stc 586 000000B9 CD13 int 13h ; 13.41.bx=55AA extensions installation check 587 000000BB 1F pop ds 588 @@: 589 000000BC B000 mov al, 0 ; zero in case of no LBA support 590 000000BE 720B jc .no_lba 591 000000C0 81FB55AA cmp bx, 0AA55h 592 000000C4 7505 jne .no_lba 593 000000C6 D0E9 shr cl, 1 ; support bitmap bit 0 594 000000C8 7301 jnc .no_lba 595 000000CA 40 inc ax ; al = 1 to indicate LBA support 596 .no_lba: 597 .done_lba: 598 000000CB 8846E7 mov byte [bp + ldHasLBA], al 599 %if _QUERY_PATCH 600 %if _MULTISECTOR 601 000000CE 58 pop ax 602 000000CF A808 test al, query_no_multi 603 000000D1 7404 jz @F 604 000000D3 804EE702 or byte [bp + ldHasLBA], ldhlfForceSingleSector 605 @@: 606 %endif 607 %endif 608 %else 609 mov byte [bp + ldHasLBA], 0 610 ; LBA skip check, write a zero here 611 %endif 612 613 %if 1 || _QUERY_GEOMETRY || !_LBA_SKIP_CHECK 614 000000D7 C3 retn 615 %endif 616 617 618 ; Read a sector using Int13.02 or Int13.42 619 ; 620 ; INP: dx:ax = sector number within partition 621 ; bx => buffer to read to 622 ; (_LBA) ds = ss 623 ; OUT: If unable to read, 624 ; ! jumps to error instead of returning 625 ; If sector has been read, 626 ; es = input bx => buffer just read to 627 ; dx:ax = next sector number (has been incremented) 628 ; bx => next buffer (bx = es + word[para_per_sector]) 629 ; CHG: - 630 ; STT: ds = ss 631 ; 632 ; Note: If error 09h (data boundary error) is returned, 633 ; the read is done into the ldSectorSeg buffer, 634 ; then copied into the user buffer. 635 ; Note: With _DMA_BOUNDARY_CHECK_CODE=0 we don't check 636 ; for error 09h, rather we assume that an error 637 ; will always be either a data boundary error 638 ; what can be cured by using the sectorseg, or 639 ; else the sectorseg attempt will error out on 640 ; its own eventually doing no additional harm. 641 read_sector: 642 000000D8 52 push dx 643 000000D9 51 push cx ; preserve cx 644 000000DA 50 push ax ; preserve starting sector (in partition) 645 000000DB 56 push si ; preserve si 646 647 %if _ADDPROGRESS 648 .patch_progress: 649 test ax, read_progress_single - @F 650 @@: 651 %endif 652 000000DC 8EC3 mov es, bx ; es => buffer 653 654 ; DX:AX==LBA sector number 655 ; add partition start (= number of hidden sectors) 656 000000DE 03461C add ax,[bp + bsBPB + bpbHiddenSectors + 0] 657 000000E1 13561E adc dx,[bp + bsBPB + bpbHiddenSectors + 2] 658 659 %if (!_LBA || !_LBA_33_BIT) && _LBA_CHECK_NO_33 660 jc .err_CY_2 661 %if !_LBA 662 .err_CY_2: equ .err_CY_1 663 %endif 664 %endif 665 %if _LBA ; +70 bytes (with CHS, +63 bytes without CHS) 666 %if _LBA_33_BIT 667 000000E4 19F6 sbb si, si ; -1 if was CY, 0 else 668 000000E6 F7DE neg si ; 1 if was CY, 0 else 669 %endif 670 000000E8 31C9 xor cx, cx ; cx = 0 (needed if jumping to .no_lba_checked) 671 %if !_LBA_SKIP_CHECK 672 000000EA F646E701 test byte [bp + ldHasLBA], ldhlfLBA 673 000000EE 7429 jz .no_lba_checked 674 %endif 675 000000F0 51 push cx 676 %if _LBA_33_BIT 677 000000F1 56 push si ; bit 32 = 1 if operating in 33-bit space 678 %else 679 push cx ; second highest word = 0 680 %endif 681 000000F2 52 push dx 682 000000F3 50 push ax ; qword sector number (lpSector) 683 000000F4 53 push bx 684 000000F5 51 push cx ; bx => buffer (bx:0 = lpBuffer) 685 000000F6 51 push cx ; word number of sectors to read (lpCount), 686 ; only need to init the high byte here (0) 687 000000F7 B110 mov cl, 10h 688 000000F9 51 push cx ; word size of disk address packet (lpSize) 689 000000FA 89E6 mov si, sp ; ds:si -> disk address packet (on stack) 690 691 000000FC 8A5640 mov dl, [bp + bsBPB + ebpbNew + bpbnBootUnit] 692 %if _LBA_RETRY 693 000000FF B442 mov ah, 42h ; 13.42 extensions read 694 00000101 E89100 call .int13_retry ; clobbers cx if NC 695 %else 696 mov ax, 4201h ; 13.42 extensions read 697 call .int13_set_lpcount 698 %endif 699 00000104 737B jnc .lba_done 700 701 %if _LBA_SKIP_CHECK 702 cmp ah, 1 ; invalid function? 703 je .no_lba_skip ; try CHS instead --> 704 %endif 705 %if _DMA_BOUNDARY_CHECK_CODE 706 cmp ah, 9 ; data boundary error? 707 jne .lba_error 708 %endif 709 710 ; push word [si + 4 + 0] 711 00000106 06 push es ; => user buffer 712 00000107 8E46E4 mov es, word [bp + ldSectorSeg] 713 0000010A 8C4406 mov word [si + 4 + 2], es 714 ; and word [si + 4 + 0], byte 0 715 716 %if _LBA_RETRY 717 0000010D B442 mov ah, 42h 718 0000010F E88300 call .int13_retry ; clobbers cx if NC 719 %else 720 mov ax, 4201h ; 13.42 extensions read 721 call .int13_set_lpcount 722 %endif 723 .err_CY_2: 724 00000112 7246 jc .err_CY_1 725 %ifn _CHS 726 .err_CY_1: equ .err 727 %endif 728 729 00000114 07 pop es 730 ; pop cx 731 00000115 0324 add sp, word [si + lpSize] 732 00000117 EB57 jmp .sectorseg_helper_then_done 733 734 .lba_error: equ .err 735 736 %if !_CHS 737 .no_lba_skip: equ .err 738 .no_lba_checked: equ .err 739 %elif _LBA_SKIP_CHECK 740 .no_lba_skip: 741 add sp, 8 742 pop ax 743 pop dx 744 %if _LBA_33_BIT 745 pop si 746 pop cx ; cx = 0 (needed as input for next cwd instruction) 747 test si, si 748 mov si, sp ; si == sp 749 %else 750 pop cx 751 pop cx 752 ; si == sp - 16 753 %endif 754 %else 755 .no_lba_checked: 756 %if _LBA_33_BIT 757 00000119 85F6 test si, si 758 %endif 759 0000011B 89E6 mov si, sp ; si == sp 760 %endif 761 %endif 762 763 %if _CHS ; +70 bytes 764 %if _LBA && _LBA_33_BIT 765 0000011D 752D jnz .err_NZ_2 766 %endif 767 %if _LBA 768 0000011F 83EE14 sub si, lpCount + 2 + 16 769 ; point byte [ds:si + lpCount] -> unclaimed stack space 770 ; the lpCount + 2 is for the offset, 771 ; the 16 is to cover subsequent allocations on the stack 772 ; (watch for {lpcountalloc}) 773 %endif 774 ; dx:ax = LBA sector number, (if _LBA) cx = 0 775 ; divide by number of sectors per track to get sector number 776 ; Use 32:16 DIV instead of 64:32 DIV for 8088 compatability 777 ; Use two-step 32:16 divide to avoid overflow 778 %if !_LBA 779 xchg cx, ax ; cx = low word of sector, clobbers ax 780 xchg ax, dx ; ax = high word of sector, clobbers dx 781 xor dx, dx ; dx:ax = high word of sector 782 %else 783 00000122 91 xchg cx, ax ; cx = low word of sector, ax = 0 784 00000123 52 push dx ; stack = high word of sector 785 00000124 99 cwd ; dx = 0 (because ax was 0) 786 00000125 58 pop ax ; ax = high word of sector 787 ; dx:ax = high word of sector 788 %endif 789 790 ; INP: dx:ax:cx = 32-bit sector number (dx = 0) 791 ; bpbCHSSectors 792 ; bpbCHSHeads 793 ; bpbnBootUnit 794 ; OUT: ! branches to error if CHS coordinates overflow 795 ; cx:dx = encoded CHS tuple, 796 ; dl = load unit 797 ; cl7:cl6:ch = cylinder number 798 ; dh = head number 799 ; cl5 to cl0 = sector number, 1-based 800 ; bx = 0 801 ; CHG: ax 802 ; REM: If byte [cs:.dx_ax_cx_lba_to_chs_patch_E8/_B8] is 803 ; patched to 0C3h (retn) then this is a function 804 ; that returns as described, if not erroring out. 805 ; If the byte is reset to 0E8h (call near imm) or 806 ; 0B8h (mov ax imm) then this code is ready to be 807 ; used as part of the single-sector read_sector. 808 ; REM: Called by the multi-sector loader in CHS mode. 809 ; The caller does and undoes the described patch. 810 .dx_ax_cx_lba_to_chs: 811 00000126 F77618 div word [bp + bsBPB + bpbCHSSectors] 812 00000129 91 xchg cx,ax 813 0000012A F77618 div word [bp + bsBPB + bpbCHSSectors] 814 0000012D 87CA xchg cx,dx 815 816 ; DX:AX=quotient, CX=remainder=sector (S) - 1 817 ; divide quotient by number of heads 818 0000012F 93 xchg bx, ax ; bx = low word of quotient, clobbers ax 819 00000130 92 xchg ax, dx ; ax = high word of quotient, clobbers dx 820 00000131 31D2 xor dx, dx ; dx = 0 821 00000133 F7761A div word [bp + bsBPB + bpbCHSHeads] 822 ; ax = high / heads, dx = high % heads 823 00000136 93 xchg bx, ax ; bx = high / heads, ax = low quotient 824 00000137 F7761A div word [bp + bsBPB + bpbCHSHeads] 825 826 ; bx:ax=quotient=cylinder (C), dx=remainder=head (H) 827 ; move variables into registers for INT 13h AH=02h 828 0000013A 88D6 mov dh, dl ; dh = head 829 0000013C 41 inc cx ; cl5:0 = sector 830 0000013D 86E8 xchg ch, al ; ch = cylinder 7:0, al = 0 831 0000013F D1E8 shr ax, 1 832 00000141 D1E8 shr ax, 1 ; al7:6 = cylinder 9:8 833 ; bx has bits set iff it's > 0, indicating a cylinder >= 65536. 834 00000143 08FB or bl, bh ; collect set bits from bh 835 00000145 08C1 or cl, al ; cl7:6 = cylinder 9:8 836 ; ah has bits set iff it was >= 4, indicating a cylinder >= 1024. 837 00000147 08E3 or bl, ah ; collect set bits from ah 838 00000149 8A5640 mov dl, [bp + bsBPB + ebpbNew + bpbnBootUnit] 839 ; dl = drive 840 .err_NZ_2: 841 0000014C 750E jnz .err_NZ_1 ; error if cylinder >= 1024 --> 842 ; ! bx = 0 (for 13.02 call) 843 844 ; we call INT 13h AH=02h once for each sector. Multi-sector reads 845 ; may fail if we cross a track or 64K boundary 846 847 %if _CHS_RETRY 848 .dx_ax_cx_lba_to_chs_patch_E8: ; = 0E8h if to continue 849 ; = 0C3h if to return 850 0000014E E84200 call .int13_retry_ah ; clobbers cx if NC 851 ; {lpcountalloc} near call 852 %else 853 .dx_ax_cx_lba_to_chs_patch_B8: ; = 0B8h if to continue 854 ; = 0C3h if to return 855 mov ax, 0201h ; read one sector 856 int 13h 857 %endif 858 00000151 7330 jnc .done 859 860 %if _DMA_BOUNDARY_CHECK_CODE 861 cmp ah, 9 ; data boundary error? 862 .err_NZ_1: 863 jne .err 864 %else 865 .err_NZ_1: equ .err 866 %endif 867 868 00000153 06 push es ; user buffer, {lpcountalloc} 869 00000154 8E46E4 mov es, word [bp + ldSectorSeg] 870 871 %if _CHS_RETRY 872 00000157 E83900 call .int13_retry_ah ; clobbers cx if NC 873 ; {lpcountalloc} near call 874 %else 875 mov ax, 0201h 876 int 13h 877 %endif 878 .err_CY_1: 879 0000015A 7313 jnc .sectorseg_helper_es 880 %endif ; _CHS 881 .err: 882 error_diskaccess: equ $ 883 0000015C E8F1FE call error 884 0000015F 4469736B2072656164- db "Disk read error." 884 00000168 206572726F722E 885 886 887 %if _CHS 888 .sectorseg_helper_es: 889 0000016F 07 pop es 890 %endif 891 892 ; INP: es => destination segment buffer 893 ; bpbBytesPerSector 894 ; ldSectorSeg => sector segment holding the data read 895 ; CHG: si, cx 896 ; STT: ds = ss 897 .sectorseg_helper_then_done: 898 00000170 31F6 xor si, si 899 00000172 8E5EE4 mov ds, word [bp + ldSectorSeg] 900 00000175 57 push di 901 ; mov di, cx 902 00000176 31FF xor di, di 903 00000178 8B4E0B mov cx, word [bp + bsBPB + bpbBytesPerSector] 904 0000017B F3A4 rep movsb 905 0000017D 5F pop di 906 907 0000017E 16 push ss 908 0000017F 1F pop ds 909 910 %if _LBA 911 00000180 A9 db __TEST_IMM16 ; skip add sp 912 .lba_done: 913 00000181 0324 add sp, word [si + lpSize] 914 %if ($ - .lba_done) != 2 915 %error Unexpected add sp instruction size 916 %endif 917 %endif 918 .done: 919 ; increment segment 920 00000183 8CC3 mov bx, es ; restore bx => buffer 921 00000185 035EEA add bx, word [bp + ldParaPerSector] 922 ; point bx => next buffer 923 924 00000188 5E pop si 925 00000189 58 pop ax 926 0000018A 59 pop cx 927 0000018B 5A pop dx ; restore sector number input 928 .increment_sector_number: 929 ; increment LBA sector number 930 0000018C 40 inc ax 931 0000018D 7501 jne @F 932 0000018F 42 inc dx 933 @@: 934 00000190 A8 db __TEST_IMM8 ; skip pop cx 935 .popcxret: ; NC stack has function number 936 00000191 59 pop cx ; discard word on stack, preserve NC, clobber cx 937 00000192 C3 retn 938 939 940 %if (_LBA && _LBA_RETRY) || (_CHS && _CHS_RETRY) 941 %if _CHS && _CHS_RETRY 942 ; INP: cx:dx = encoded CHS tuple and unit 943 ; es:bx -> buffer to write 944 .int13_retry_ah: 945 00000193 B402 mov ah, 02h ; CHS read function 946 %endif 947 ; INP: ah function number 948 ; ah = 02h for CHS read, 949 ; cx:dx = encoded CHS tuple and unit 950 ; es:bx -> buffer to write 951 ; ds:si -> into unclaimed stack space 952 ; ah = 42h for LBA read, 953 ; dl = unit 954 ; ds:si -> LBA packet, 955 ; initialised except for low byte of lpCount 956 ; OUT: NC if success (on first or second attempt), 957 ; may clobber cx (if first attempt succeeds) 958 ; CY if error (on both attempts), 959 ; ah = error code 960 .int13_retry: 961 00000195 B001 mov al, 01h ; counter for CHS read, lpCount value for LBA read 962 00000197 50 push ax ; {lpcountalloc} 963 %if _LBA 964 00000198 E80700 call .int13_set_lpcount 965 ; {lpcountalloc} near call 966 %else 967 int 13h ; first try 968 %endif 969 0000019B 73F4 jnc .popcxret ; NC, success on first attempt --> (clobbers cx) 970 971 ; reset drive 972 0000019D 31C0 xor ax, ax 973 0000019F CD13 int 13h ; ignore CF status from the reset, to save on code size 974 975 ; try read again 976 000001A1 58 pop ax ; restore function number 977 %if ! _LBA 978 int 13h ; retry, CF error status, ah error number 979 retn 980 %endif ; else: fall through to .int13_set_lpcount 981 %endif 982 983 %if _LBA 984 ; have to (re)set the LBAPACKET's lpCount, as the handler may 985 ; set it to "the number of blocks successfully transferred". 986 ; rather than preserve it as before, always set it to 1 here. 987 ; the high byte is already initialised to zero and cannot be 988 ; changed by the call, so the entire word equals 1 after the 989 ; low byte is set here. 990 991 ; hack: si points into unclaimed stack space 992 ; when this is called from the CHS handler. 993 ; this should not cause any issues however. 994 ; {lpcountalloc} 995 996 ; INP: ah = function number 997 ; ah = 02h for CHS read, 998 ; cx:dx = encoded CHS tuple and unit 999 ; es:bx -> buffer to write 1000 ; ds:si -> into unclaimed stack space 1001 ; ah = 42h for LBA read, 1002 ; dl = unit 1003 ; ds:si -> LBA packet, 1004 ; initialised except for low byte of lpCount 1005 ; al = count of sectors (always 1) 1006 ; OUT: NC if success 1007 ; CY if error, 1008 ; ah = error code 1009 .int13_set_lpcount: 1010 000001A2 884402 mov byte [si + lpCount], al 1011 000001A5 CD13 int 13h 1012 000001A7 C3 retn 1013 %endif 1014 1015 1016 error_shortfile: 1017 000001A8 E8A5FE call error 1018 000001AB 46696C652069732074- db "File is too short." 1018 000001B4 6F6F2073686F72742E 1019 1020 error_badchain: 1021 000001BD E890FE call error 1022 000001C0 42616420636C757374- db "Bad cluster chain." 1022 000001C9 657220636861696E2E 1023 1024 error_badclusters: 1025 000001D2 E87BFE call error 1026 000001D5 42616420616D6F756E- db "Bad amount of clusters." 1026 000001DE 74206F6620636C7573- 1026 000001E7 746572732E 1027 1028 error_outofmemory: 1029 000001EC E861FE call error 1030 000001EF 4F7574206F66206D65- db "Out of memory." 1030 000001F8 6D6F72792E 1031 1032 %assign num 512-($-$$) 1033 %if num >= 3 1034 %assign num num - 3 1035 %warning %[num]+3 bytes in front of ms7_entry 1035 ****************** warning: 0+3 bytes in front of ms7_entry [-w+user] 1036 _fill 512 - 3,38,start 1037 error_outofmemory_j1: 1038 000001FD EBED jmp error_outofmemory 1039 %else 1040 error_outofmemory_j1: equ error_outofmemory 1041 %warning num bytes in front of ms7_entry 1042 %endif 1043 000001FF 26 _fill 512,38,start 1044 ms7_entry: 1045 ; This is the MS-DOS 7 compatible entry point. 1046 ; Supports FAT32 too. 1047 ; cs:ip = 70h:200h 1048 ; (si:)di = first cluster of load file 1049 ; dwo [ss:bp - 4] = first data sector (with hidden sectors) 1050 ; dwo [ss:sp] = 0:78h (IVT entry of int 1Eh) 1051 ; dwo [ss:sp + 4] = old int 1Eh address 1052 ; ss:bp -> boot sector with (E)BPB, 1053 ; load unit field set, hidden sectors set 1054 00000200 42 inc dx 1055 00000201 4A dec dx ; "BJ" signature (apparently not about FAT32 support) 1056 1057 00000202 E91204 jmp .continue ; jump to handler above 600h (sector loads 800h bytes) 1058 1059 .ms6_common: ; cx = 0 1060 00000205 B8D000 mov ax, 70h + ((3 * 512) >> 4) ; MS6 entry has 3 sectors loaded 1061 ; (and is always segment 70h) 1062 1063 .continue2_set_extra_and_empty_cmdline: ; cx = 0, ax => behind loaded 1064 %if _LSVEXTRA 1065 00000208 894EEE mov word [bp + lsvExtra], cx 1066 %endif 1067 0000020B 894EEC mov word [bp + lsvCommandLine], cx 1068 .continue2: ; cx = 0, ax => behind loaded 1069 0000020E 8946FA mov word [bp + lsvLoadSeg], ax 1070 1071 00000211 894EF8 mov word [bp + lsvFATSeg], cx ; initialise to zero (for FAT12) 1072 00000214 49 dec cx 1073 00000215 894EF4 mov word [bp + lsvFATSector + 0], cx 1074 00000218 894EF6 mov word [bp + lsvFATSector + 2], cx ; initialise to -1 1075 1076 ; Actually it seems that the MS-DOS 7 loaders load 4 sectors 1077 ; instead of only three (as the MS-DOS 6 loaders do). 1078 ; We use this to store specific handling in that last sector. 1079 1080 0000021B E9EB01 jmp ldos_entry.ms7_common 1081 1082 msg: 1083 0000021E 4C6F6164206572726F- .error: db "Load error: ." 1083 00000227 723A202E 1084 1085 1086 finish_continue: 1087 0000022B 894EF8 mov word [bp + lsvFATSeg], cx 1088 0000022E B87910 mov ax, (payload_actual_end_late +15) >> 4 1089 00000231 50 push ax 1090 ; on stack: payload.actual_end in paragraphs 1091 00000232 8B5EEA mov bx, [bp + ldParaPerSector] 1092 00000235 4B dec bx ; para per sector - 1 1093 00000236 01D8 add ax, bx ; round up 1094 ; assumed to not overflow, (payload.actual_end + 8192) < 10_0000h 1095 payload_actual_end_equate equ payload_actual_end_late 1096 %ifn (payload_actual_end_equate + 15 + 8192) < 10_0000h 1097 %error Overflow possible 1098 %endif 1099 00000238 F7D3 not bx ; ~ (para per sector - 1) 1100 0000023A 21D8 and ax, bx ; rounded up, 1101 ; (((payload_actual_end_late +15) >> 4) + pps - 1) & ~ (pps - 1) 1102 0000023C 8CCB mov bx, cs 1103 0000023E 01D8 add ax, bx ; = cs + rounded up length 1104 00000240 7205 jc @F ; overflow ? then we're near top of memory --> 1105 00000242 3B46E2 cmp ax, word [bp + ldLoadTop] ; = paras to move down 1106 ; ja @FF ; end of load > available, then need to relocate --> 1107 00000245 7637 jbe short finish_load 1108 @@: 1109 00000247 2B46E2 sub ax, word [bp + ldLoadTop] ; = paras to move down 1110 ; calculate end of wanted load minus available, 1111 ; will CY if branched to this @@, NC if fell through 1112 0000024A 8B4EFA mov cx, word [bp + lsvLoadSeg] 1113 ; => after end of loaded data 1114 0000024D 2946FA sub word [bp + lsvLoadSeg], ax 1115 ; relocate this pointer already 1116 00000250 F7D8 neg ax 1117 00000252 01D8 add ax, bx ; ax = cs - paras to move down 1118 ; want to relocate cs to this 1119 00000254 73A7 jnc short error_outofmemory_j1 1120 ; underflow --> 1121 00000256 83F861 cmp ax, 60h + 1 ; have space for relocator paragraph at 60h:0 ? 1122 00000259 72A2 jb short error_outofmemory_j1 1123 ; no --> 1124 1125 0000025B 50 push ax 1126 0000025C E88200 call finish_relocation 1127 ; dword on stack: relocate_to 1128 1129 1130 ; ds => first chunk of to be relocated data 1131 ; es => first chunk of relocated data 1132 ; bx = 1000h (64 KiB >> 4) 1133 ; ax = number of paragraphs after first chunk (in next chunk) 1134 ; cx = 0 1135 relocate_to: 1136 ; when branched here from relocator: cx = 0, 1137 ; makes the following rep movsw a nop 1138 ; if ax != 0 then relocator did a whole 64 KiB 1139 ; so that si = di = 0. 1140 @@: ; when branched back here from later: cx = 8000h, 1141 ; si = di = 0. 1142 0000025F F3A5 rep movsw ; subsequent but not last relocation 1143 1144 00000261 8CC2 mov dx, es 1145 00000263 01DA add dx, bx 1146 00000265 8EC2 mov es, dx ; next segment 1147 1148 00000267 8CDA mov dx, ds 1149 00000269 01DA add dx, bx 1150 0000026B 8EDA mov ds, dx ; next segment 1151 1152 ; If there is another part to move then the 1153 ; prior move was a full 64 KiB, so the si 1154 ; and di registers have wrapped around back 1155 ; to zero. Therefore we needn't re-initialise 1156 ; them here, the next part wants zeroes too. 1157 ; If there's no next part then the final move 1158 ; will run with cx = 0 so it doesn't matter 1159 ; what the si and di registers hold. 1160 1161 0000026D 29D8 sub ax, bx ; = how much to relocate after this round 1162 ; cx = 0 1163 0000026F B580 mov ch, (1000h << 3) >> 8 1164 ; in case another full 64 KiB to relocate 1165 ; cl = 0 1166 00000271 73EC jae @B ; another full 64 KiB to relocate --> 1167 00000273 01D8 add ax, bx ; restore 1168 00000275 B103 mov cl, 3 1169 00000277 D3E0 shl ax, cl ; convert paragraphs to words 1170 00000279 91 xchg cx, ax ; cx = that many words 1171 0000027A F3A5 rep movsw ; relocate last chunk (possibly zero length) 1172 1173 0000027C 16 push ss 1174 0000027D 1F pop ds ; reset ds = ss 1175 1176 ; ds = ss 1177 ; cs = low enough to complete load 1178 ; lsvLoadSeg => after last loaded fragment 1179 ; ldLoadTop => after last available memory 1180 ; ldParaPerSector = initialised 1181 ; word [ss:sp] = payload.actual_end in paras 1182 finish_load: 1183 %if _LOADERSUPPORT 1184 mov al, [bp + lsvExtra.flags] 1185 and al, lsvefPreserveLoader 1186 or [bp + ldHasLBA], al 1187 ; pass through the flag for loader support 1188 %endif 1189 0000027E 58 pop ax 1190 0000027F 8CCB mov bx, cs 1191 00000281 01D8 add ax, bx ; => behind space for last needed payload data 1192 00000283 8946EE mov word [bp + ldLoadUntilSeg], ax 1193 ; ldLoadUntilSeg => after last to-be-loaded paragraph 1194 1195 00000286 3946FA cmp word [bp + lsvLoadSeg], ax 1196 00000289 733B jae short loaded_all_if_ae 1197 ; (for FreeDOS entrypoint) already loaded --> 1198 1199 0000028B 8C4EEC mov word [bp + ldLoadingSeg], cs 1200 ; begin => at start of iniload 1201 1202 0000028E 8B46F0 mov ax, [bp + lsvFirstCluster] 1203 00000291 8B56F2 mov dx, [bp + lsvFirstCluster + 2] 1204 ; dx:ax = first cluster passed to us 1205 00000294 E8FB00 call check_clust 1206 00000297 7341 jnc short error_badchain_j 1207 1208 ; The following loop first skips across parts 1209 ; of the file already loaded. This is at least 1210 ; 1536 bytes and at most less than the entire 1211 ; payload part. As soon as the branch to the 1212 ; label skipped_all is taken once, the purpose 1213 ; of this loop changes so that it implements 1214 ; single-sector loading of the kernel file. 1215 ; The branch to skipped_all is taken every time 1216 ; that the loop runs another iteration. The 1217 ; stop logic and read_sector calls happen as 1218 ; desired without replicating the loop as we 1219 ; used to do before. This in every iteration 1220 ; runs a few more instructions (cheap). 1221 skip_next_clust: 1222 00000299 52 push dx 1223 0000029A 50 push ax 1224 0000029B E86C00 call clust_to_first_sector 1225 ; dx:ax = first data sector (in partition), 1226 ; bx = ldLoadingSeg => next buffer space 1227 skip_next_sect: 1228 0000029E 3B5EEE cmp bx, [bp + ldLoadUntilSeg] 1229 000002A1 7203E9ED05 jae loaded_all.2stack ; already loaded enough ? then done --> 1230 1231 000002A6 035EEA add bx, [bp + ldParaPerSector] 1232 ; bx += paras per sector, => behind next buffer 1233 000002A9 3B5EFA cmp bx, [bp + lsvLoadSeg] 1234 000002AC 7720 ja skipped_all ; if above loadseg, do read a sector --> 1235 ; otherwise, emulate read_sector: 1236 000002AE E8DBFE call read_sector.increment_sector_number 1237 ; dx:ax += 1 1238 loadorskip_next_sector: 1239 000002B1 E2EB loop skip_next_sect ; count down, handle next sector in cluster --> 1240 000002B3 58 pop ax 1241 000002B4 5A pop dx 1242 000002B5 E87300 call clust_next ; walk cluster chain 1243 000002B8 72DF jc skip_next_clust ; handle next cluster --> 1244 end_of_chain: 1245 000002BA 40 inc ax 1246 000002BB 40 inc ax 1247 000002BC A808 test al, 8 ; set in 0FFF_FFF8h--0FFF_FFFFh, 1248 ; clear in 0, 1, and 0FFF_FFF7h 1249 000002BE 741A jz short error_badchain_j ; this error 1250 000002C0 8B5EEC mov bx, [bp + ldLoadingSeg] 1251 000002C3 3B5EEE cmp bx, [bp + ldLoadUntilSeg] ; (always bx below ldLoadUntilSeg ?) 1252 loaded_all_if_ae: 1253 000002C6 7203E9CA05 jae loaded_all ; if enough data loaded --> 1254 000002CB E9DAFE jmp error_shortfile ; that error instead 1255 1256 1257 skipped_all: 1258 000002CE 2B5EEA sub bx, [bp + ldParaPerSector] 1259 ; restore bx => next sector to read 1260 %if _MULTISECTOR 1261 .multipatch: 1262 000002D1 A9CF03 test ax, skipped_all_multi - @F 1263 @@: ; enter multi-sector loader if enabled yet --> 1264 .single_sector: 1265 %endif 1266 ; this is now the only callsite of read_sector 1267 ; in the single-sector file load loop. 1268 000002D4 E801FE call read_sector 1269 ; we can depend on the fact that at least 1270 ; up to end was already loaded, so this 1271 ; (successful) read_sector call loaded 1272 ; at least 32 bytes starting at end. 1273 ; therefore, we can put part of the 1274 ; remaining handler into these 32 bytes. 1275 000002D7 E93203 jmp skipped_all_continue 1276 1277 1278 error_badchain_j: 1279 000002DA E9E0FE jmp error_badchain 1280 1281 1282 ; ds => first chunk of to be relocated data 1283 ; word [ss:sp] => first chunk of relocation destination 1284 ; si = di = 0 1285 ; relocation destination is always below source 1286 ; this relocator runs from a paragraph below the destination 1287 ; cx = number of words in first chunk, 300h <= cx <= 1000h 1288 relocator: 1289 000002DD 07 pop es ; => where to relocate to 1290 000002DE F3A5 rep movsw 1291 000002E0 CB retf ; branch to relocated relocate_to 1292 .end: 1293 1294 1295 finish_relocation: 1296 000002E1 50 push ax ; word on stack => where to relocate to 1297 000002E2 48 dec ax ; one less to allow relocator 1298 000002E3 8EC0 mov es, ax 1299 000002E5 31FF xor di, di ; es:di -> where to put relocator 1300 1301 000002E7 06 push es 1302 000002E8 57 push di ; dword on stack: relocator destination 1303 1304 000002E9 8EDB mov ds, bx ; ds => unrelocated cs 1305 000002EB BE[DD02] mov si, relocator ; ds:si -> relocator 1306 relocator_size equ relocator.end - relocator 1307 %rep (relocator_size + 1) / 2 1308 movsw ; place relocator 1309 %endrep 1308 000002EE A5 <1> movsw 1308 000002EF A5 <1> movsw 1310 %if relocator_size > 16 1311 %error Relocator is too large 1312 %endif 1313 000002F0 31FF xor di, di ; word [ss:sp+4]:di -> where to relocate to 1314 000002F2 31F6 xor si, si ; ds:si = cs:0 1315 1316 ; cx => after end of loaded data 1317 000002F4 29D9 sub cx, bx ; length of currently loaded fragment 1318 000002F6 BB0010 mov bx, 1000h ; = amount paragraphs per 64 KiB 1319 000002F9 89C8 mov ax, cx ; = amount paragraphs to relocate total 1320 000002FB 39D8 cmp ax, bx ; > 64 KiB ? 1321 000002FD 7602 jbe @F ; no, do entire relocation at once --> 1322 000002FF 89D9 mov cx, bx ; yes, first relocate the first 64 KiB 1323 @@: ; cx = amount paragraphs to relocate first 1324 00000301 29C8 sub ax, cx ; ax = amount paragraphs to relocate later 1325 00000303 D1E1 shl cx, 1 1326 00000305 D1E1 shl cx, 1 1327 00000307 D1E1 shl cx, 1 ; how much to relocate first, 1328 ; << 3 == convert paragraphs to words 1329 00000309 CB retf ; branch to relocator, which branches to relocate_to 1330 1331 1332 ; INP: dx:ax = cluster - 2 (0-based cluster) 1333 ; OUT: dx:ax = first sector of that cluster 1334 ; cx = adjusted sectors per cluster 1335 ; bx => buffer, from ldLoadingSeg 1336 clust_to_first_sector: 1337 0000030A 8B4EE8 mov cx, word [bp + ldClusterSize] 1338 0000030D 52 push dx 1339 0000030E F7E1 mul cx ; dx:ax = low times SpC 1340 00000310 93 xchg bx, ax ; bx = low word result 1341 00000311 58 pop ax ; ax = high 1342 00000312 52 push dx ; stack = low times SpC, high word 1343 00000313 F7E1 mul cx ; dx:ax = high times SpC 1344 00000315 85D2 test dx, dx 1345 00000317 75C1 jnz short error_badchain_j 1346 ; ax = high times SpC, low word 1347 00000319 5A pop dx ; dx = low times SpC, high word 1348 0000031A 01C2 add dx, ax ; dx = high word result 1349 .cy_error_badchain: 1350 0000031C 72BC jc short error_badchain_j 1351 0000031E 93 xchg ax, bx ; ax = low word result, dx:ax = result 1352 1353 0000031F 0346FC add ax, [bp + lsvDataStart] 1354 00000322 1356FE adc dx, [bp + lsvDataStart + 2] 1355 00000325 72F5 jc short .cy_error_badchain 1356 ; dx:ax = first sector in cluster 1357 00000327 8B5EEC mov bx, [bp + ldLoadingSeg] 1358 ; bx => loading segment 1359 0000032A C3 retn 1360 1361 1362 ; INP: dx:ax = cluster (0-based) 1363 ; bx => file data buffer, stored to ldLoadingSeg 1364 ; OUT: NC if no next cluster 1365 ; CY if next cluster found 1366 ; dx:ax = next cluster value (0-based) 1367 ; si = 0 1368 ; CHG: es, cx, bx, si 1369 clust_next: 1370 0000032B 895EEC mov [bp + ldLoadingSeg], bx 1371 0000032E 83C002 add ax, 2 1372 00000331 83D200 adc dx, 0 ; return it to a 2-based cluster number 1373 ; (there are FAT entries for cluster 0, 1) 1374 1375 00000334 8E46F8 mov es, [bp + lsvFATSeg] 1376 00000337 807EE610 cmp byte [bp + ldFATType], 16 1377 0000033B 741A je .fat16 ; == 16 1378 0000033D 7714 ja .fat32 ; > 16, must be 32 1379 ; < 16, must be 12 1380 .fat12: 1381 ; FAT12 entries are 12 bits, bytes are 8 bits. Ratio is 3 / 2, 1382 ; so multiply cluster number by 3 first, then divide by 2. 1383 ; ax = cluster number (up to 12 bits set) 1384 0000033F 89C6 mov si, ax 1385 00000341 D1E0 shl ax, 1 ; = 2n (up to 13 bits set) 1386 00000343 01C6 add si, ax ; = 2n+n = 3n (up to 14 bits set) 1387 00000345 D1EE shr si, 1 ; si = byte offset into FAT (0..6129) 1388 ; CF = whether to use high 12 bits 1389 1390 ; Use the calculated byte offset as an offset into the FAT 1391 ; buffer, which holds all of the FAT's relevant data. 1392 ; si -> 16-bit word in FAT to load 1393 1394 ; get 16 bits from FAT 1395 00000347 26AD es lodsw ; ax = word [es:si], clobber si 1396 1397 00000349 B104 mov cl, 4 1398 0000034B 7202 jc @F ; if to use high 12 bits, skip shl --> 1399 0000034D D3E0 shl ax, cl ; shift up (zeroes high 4 bits after next shr) 1400 @@: 1401 0000034F D3E8 shr ax, cl ; shift down 1402 00000351 EB3D jmp short .gotvalue_zero_dx 1403 1404 .fat32: 1405 ; * 4 = byte offset into FAT (0--4000_0000h) 1406 00000353 01C0 add ax, ax 1407 00000355 11D2 adc dx, dx ; double dx:ax 1408 .fat16: 1409 ; * 2 = byte offset into FAT (0--2_0000h) 1410 00000357 01C0 add ax, ax 1411 00000359 11D2 adc dx, dx ; double dx:ax 1412 1413 0000035B 50 push ax 1414 0000035C 92 xchg ax, dx 1415 0000035D 31D2 xor dx, dx ; dx:ax = high word 1416 0000035F F7760B div word [bp + bsBPB + bpbBytesPerSector] 1417 00000362 96 xchg si, ax ; si = high word / divisor 1418 00000363 58 pop ax ; dx = remainder, ax = low word 1419 00000364 F7760B div word [bp + bsBPB + bpbBytesPerSector] 1420 00000367 87D6 xchg dx, si ; dx:ax = result, si = remainder 1421 ; dx:ax = sector offset into FAT (0--200_0000h) 1422 ; si = byte offset into FAT sector (0--8190) 1423 00000369 3B56F6 cmp dx, word [bp + lsvFATSector + 2] 1424 0000036C 7505 jne @F ; read sector 1425 0000036E 3B46F4 cmp ax, word [bp + lsvFATSector] 1426 00000371 7411 je @FF ; sector is already buffered --> 1427 @@: 1428 00000373 8956F6 mov word [bp + lsvFATSector + 2], dx 1429 00000376 8946F4 mov word [bp + lsvFATSector + 0], ax 1430 ; remember the sector that we're loading 1431 1432 00000379 03460E add ax, [bp + bsBPB + bpbReservedSectors] 1433 0000037C 83D200 adc dx, 0 ; = sector number within partition 1434 0000037F 8CC3 mov bx, es ; bx => FAT buffer 1435 00000381 E854FD call read_sector 1436 @@: 1437 00000384 26AD es lodsw 1438 00000386 92 xchg ax, dx 1439 00000387 26AD es lodsw 1440 00000389 92 xchg ax, dx ; dx:ax = FAT32 entry 1441 1442 0000038A 807EE610 cmp byte [bp + ldFATType], 16 ; is it FAT32 ? 1443 0000038E 7502 jne @F ; yes --> 1444 .gotvalue_zero_dx: 1445 00000390 31D2 xor dx, dx ; no, clear high word 1446 @@: 1447 1448 ; INP: dx:ax = cluster value, 2-based 1449 ; OUT: dx:ax -= 2 (makes it 0-based) 1450 ; si = 0 1451 ; NC iff invalid cluster 1452 check_clust: 1453 00000392 31F6 xor si, si 1454 00000394 80E60F and dh, 0Fh ; isolate low 28 bits (FAT32 uses 28 bits) 1455 00000397 83E802 sub ax, 2 1456 0000039A 19F2 sbb dx, si ; make 0-based 1457 1458 0000039C 807EE610 cmp byte [bp + ldFATType], 16 1459 000003A0 7706 ja .fat32 1460 000003A2 740A je .fat16 1461 1462 .fat12: 1463 000003A4 3DF50F cmp ax, 0FF7h - 2 ; CY if valid 1464 ; jmp short .common 1465 000003A7 C3 retn 1466 1467 .fat32: 1468 000003A8 81FAFF0F cmp dx, 0FFFh 1469 000003AC 7203 jb @F ; CY here means valid ...- 1470 1471 .fat16: 1472 000003AE 83F8F5 cmp ax, 0FFF7h - 2 1473 @@: ; -... or if NC first, CY here also 1474 .common: 1475 ; cmc ; NC if valid 1476 ; CY if valid now ! 1477 000003B1 C3 retn 1478 1479 1480 ms6_continue1: 1481 ; cmp dx, di ; int 1Eh address on stack ? 1482 ; jne @F ; no --> 1483 ; cmp word [bp + 2], cx ; segment 0 in next word ? 1484 ; @@: 1485 000003B2 7504 jne .dssi ; no --> 1486 000003B4 5E pop si 1487 000003B5 1F pop ds ; discard 1488 000003B6 5E pop si 1489 000003B7 1F pop ds ; get old int 1Eh address from stack 1490 .dssi: 1491 000003B8 FC cld 1492 000003B9 BD007C mov bp, 7C00h ; 0:bp -> boot sector with BPB 1493 000003BC 8EC1 mov es, cx ; cx = 0 1494 1495 000003BE 268935 mov word [es:di], si 1496 000003C1 268C5D02 mov word [es:di + 2], ds ; restore old int 1Eh address 1497 1498 ; set up stack. insure to set ss then in the very next 1499 ; instruction set sp. DI here. 1500 000003C5 8ED1 mov ss, cx ; = 0 1501 000003C7 BCEC7B mov sp, 7C00h + lsvCommandLine 1502 1503 000003CA 26FF361405 push word [es:500h + 20] 1504 000003CF 26FF361A05 push word [es:500h + 26] ; get starting cluster 1505 000003D4 8F46F0 pop word [bp + lsvFirstCluster + 0] 1506 000003D7 8F46F2 pop word [bp + lsvFirstCluster + 2] 1507 ; store starting cluster in lsv 1508 1509 000003DA 2B5E1C sub bx, word [bp + bsBPB + bpbHiddenSectors + 0] 1510 000003DD 1B461E sbb ax, word [bp + bsBPB + bpbHiddenSectors + 2] 1511 ; get data start relative to partition 1512 000003E0 895EFC mov word [bp + lsvDataStart + 0], bx 1513 000003E3 8946FE mov word [bp + lsvDataStart + 2], ax 1514 ; store data start in lsv 1515 000003E6 E91CFE jmp ms7_entry.ms6_common ; passing cx = 0 1516 1517 1518 %assign num 1020-($-$$) 1519 %warning num bytes in front of ldos_entry 1519 ****************** warning: 19 bytes in front of ldos_entry [-w+user] 1520 000003E9 26 _fill 1020,38,start 1521 000003FC 6C44 dw "lD" ; always this signature (word [1020] == 446Ch) 1522 000003FE 4644 dw _INILOAD_SIGNATURE 1523 ; two printable non-blank ASCII characters 1524 ; (ie both bytes in the range 21h..7Eh) 1525 ; Rx = RxDOS kernel 1526 ; FD = FreeDOS kernel 1527 ; DR = EDR-DOS kernel 1528 ; MS = MS-DOS kernel 1529 ; TP = TestPL 1530 ; TW = Test writer 1531 ; (lD)eb = lDebug 1532 ; (lD)Db = lDDebug 1533 ; (lD)bC = lCDebug 1534 ; (lD)OS = lDOS kernel (lMS-DOS since 2025 February) 1535 ; Ld = lDOS pre-boot loader 1536 ; MC = lDOS Master Control Program 1537 ; XX = unset 1538 %if ($ - $$) != 1024 1539 %error Invalid signature 1540 %endif 1541 ldos_entry: 1542 ; loader magic bytes start 1543 00000400 FA cli 1544 00000401 FC cld 1545 1546 ; ip = 400h 1547 ; cs = arbitrary; typically 60h, 70h, or 200h 1548 ; dwo [ss:bp - 4] = first data sector (without hidden sectors) 1549 ; wo [ss:bp - 6] = load_seg, => after last loaded data 1550 ; wo [ss:bp - 8] = fat_seg, 0 if invalid 1551 ; initialised to 0 by MS-DOS 6, 7, FreeDOS entrypoints 1552 ; fat_sector is not used for FAT12 ! 1553 ; wo [ss:bp - 12] = fat_sector, -1 if none (FAT16) 1554 ; dwo [ss:bp - 12] = fat_sector, -1 if none (FAT32) 1555 ; initialised to -1 by MS-DOS 6, 7, FreeDOS entrypoints 1556 ; wo [ss:bp - 16] = first_cluster (FAT16, FAT12) 1557 ; dwo [ss:bp - 16] = first_cluster (FAT32) 1558 ; initialised to 0 by FreeDOS entrypoint 1559 ; 1560 ; Extension 1: 1561 ; lsvExtra (word [ss:bp - 18]) may be set, 1562 ; not sure about interface yet. allows 1563 ; to not initialise data start, or to specify 1564 ; a partition number instead of offset 1565 ; 1566 ; Extension 2: 1567 ; word [ss:bp - 20] = signature "CL" if valid 1568 ; bp >= 20 + 256 if valid 1569 ; 256bytes [ss:bp - 20 - 256] = ASCIZ command line string 1570 1571 00000402 31C0 xor ax, ax 1572 00000404 50 push ax ; push into lsvExtra if sp -> LSV 1573 %if _LSVEXTRA 1574 00000405 8946EE mov word [bp + lsvExtra], ax 1575 ; byte [ss:bp - 18] = partition number 1576 ; byte [ss:bp - 17] = flags for initialisation 1577 %endif 1578 00000408 50 push ax ; push into lsvCommandLine if sp -> LSV 1579 ; loader magic bytes end 1580 1581 ; loader enters here with: 1582 ; INP: ss:sp -> lsvCommandLine or lower 1583 ; ss:bp -> boot sector, above lsv 1584 ; ax = 0 (not currently needed but for consistency) 1585 ; word [lsvCommandLine] initialised 1586 ; word [lsvExtra] initialised 1587 ; ip = 409h 1588 ; cs = typically 200h 1589 ; lsv above lsvExtra as before 1590 ; command line as before 1591 .ms7_common: 1592 00000409 8CC8 mov ax, cs 1593 0000040B 8B4EFA mov cx, word [bp + lsvLoadSeg] 1594 0000040E 29C1 sub cx, ax ; = amount paragraphs loaded yet 1595 00000410 7205 jb error_notfullyloaded ; lsvLoadSeg must not be < cs --> 1596 00000412 83F960 cmp cx, (end -$$+0) >> 4 1597 00000415 7323 jae @F ; enough loaded --> 1598 error_notfullyloaded: 1599 00000417 E836FC call error 1600 0000041A 496E697469616C206C- db "Initial loader not fully loaded." 1600 00000423 6F61646572206E6F74- 1600 0000042C 2066756C6C79206C6F- 1600 00000435 616465642E 1601 @@: 1602 1603 0000043A BB7910 mov bx, (payload_actual_end_late +15) >> 4 1604 0000043D 39D9 cmp cx, bx ; lsvLoadSeg *too high* ? 1605 0000043F 7605 jbe @F ; no --> 1606 00000441 01C3 add bx, ax ; calculate segment to clamp lsvLoadSeg to 1607 00000443 895EFA mov word [bp + lsvLoadSeg], bx 1608 ; store clamped segment 1609 @@: 1610 1611 init_memory: 1612 ; Get conventional memory size and store it 1613 00000446 CD12 int 12h ; ax = amount KiB of Low Memory Area 1614 00000448 B106 mov cl, 6 1615 0000044A D3E0 shl ax, cl ; times 1024 divided by 16 (that is, times 64), 1616 ; ax => end of LMA 1617 %if _RPL 1618 0000044C 31F6 xor si, si 1619 0000044E 92 xchg dx, ax ; dx => end of LMA 1620 0000044F 8EDE mov ds, si ; => IVT 1621 00000451 C536BC00 lds si, [4 * 2Fh] ; -> int 2Fh handler 1622 00000455 83C603 add si, 3 ; -> 3 bytes past int 2Fh handler 1623 00000458 AC lodsb ; don't use lodsw ! 1624 ; could access the word at offset FFFFh 1625 ; which causes a fault in 386+ Real 86 Mode. 1626 00000459 3C52 cmp al, 'R' ; check "RPL" signature 1627 0000045B 750F jne .no_rpl 1628 0000045D AC lodsb 1629 0000045E 3C50 cmp al, 'P' 1630 00000460 750A jne .no_rpl 1631 00000462 AC lodsb 1632 00000463 3C4C cmp al, 'L' 1633 00000465 7505 jne .no_rpl ; mismatch --> 1634 00000467 B8064A mov ax, 4A06h 1635 0000046A CD2F int 2Fh ; call RPL to adjust memory size 1636 .no_rpl: 1637 0000046C 92 xchg ax, dx 1638 %endif 1639 0000046D 50 push ax 1640 ; sub ax, 32 >> 4 ; make space for two MCBs: top MCB, RPL MCB 1641 0000046E 48 dec ax 1642 0000046F 48 dec ax 1643 00000470 89C1 mov cx, ax 1644 00000472 2D0102 sub ax, (8192 + 16) >> 4 1645 00000475 49 dec cx ; => last paragraph of higher buffer (16-byte trailer) 1646 00000476 89C2 mov dx, ax ; => first paragraph of higher buffer 1647 00000478 89CB mov bx, cx 1648 0000047A 80E6F0 and dh, 0F0h ; 64 KiB chunk of first paragraph of higher buffer 1649 0000047D 80E7F0 and bh, 0F0h ; 64 KiB chunk of last paragraph of higher buffer 1650 00000480 38F7 cmp bh, dh ; in same chunk? 1651 00000482 89C3 mov bx, ax 1652 00000484 7408 je .gotsectorseg; yes, use higher buffer as sector buffer -> 1653 ; bx = use higher buffer as FAT buffer 1654 00000486 43 inc bx ; => 8 KiB buffer (no 16-byte trailer) 1655 00000487 2D0202 sub ax, (8192 + 32) >> 4 1656 ; 32 = leave space for higher buffer MCB + header 1657 ; +16 from the above calcs for 16-byte trailer 1658 0000048A 89C2 mov dx, ax ; use lower buffer as sector buffer 1659 0000048C EB06 jmp short .gotsegs 1660 1661 .gotsectorseg: 1662 ; ax = use higher buffer as sector buffer 1663 0000048E 81EB0202 sub bx, (8192 + 32) >> 4 1664 ; use lower buffer as FAT buffer 1665 ; 32 = leave space for higher buffer MCB + header 1666 00000492 89DA mov dx, bx 1667 ; ax = sector seg 1668 ; bx = FAT seg 1669 ; dx = the lower of the two 1670 .gotsegs: 1671 00000494 81EAB600 sub dx, (+_STACKSIZE -LOADCMDLINE + 512 + (ebpbNew - bpbNew) + 32 + 15) >> 4 1672 ; +_STACKSIZE = stack space 1673 ; -LOADCMDLINE = load cmd line + data + lsv space 1674 ; 512 = boot sector (allows finding filename) 1675 ; (ebpbNew - bpbNew) = additional space for BPBN moving 1676 ; 32 = leave space for lower buffer MCB + header 1677 ; dx = stack seg 1678 1679 00000498 4A dec dx ; leave space for stack + BPB buffer MCB 1680 00000499 3B56FA cmp dx, word [bp + lsvLoadSeg] 1681 0000049C 7303 jnb @F 1682 ; this check passes if we have the 20 KiB LMA top reservation 1683 ; suggested for the lDOS load protocol. for IBM-DOS, 1684 ; MS-DOS v6, and MS-DOS v7 load lsvLoadSeg is <= 0F0h. 1685 ; for FreeDOS and EDR-DOS load, lsvLoadSeg is clamped and 1686 ; we hope that the top 20 KiB are free. 1687 .error_outofmemory: 1688 0000049E E94BFD jmp error_outofmemory 1689 @@: 1690 1691 000004A1 50 push ax 1692 000004A2 8CD6 mov si, ss ; => incoming stack 1693 000004A4 89E8 mov ax, bp ; si:ax -> boot sector with BPB 1694 000004A6 B103 mov cl, 3 1695 000004A8 050F02 add ax, 512 + 15; ax -> past boot sector with BPB 1696 000004AB 7303 jnc @F ; if no carry --> (NC) 1697 ; CY 1698 000004AD B80000 mov ax, 0 ; preserve CY ! do not use xor 1699 ; this ends up calculating the offset like 10000h 1700 @@: 1701 000004B0 D1D8 rcr ax, 1 ; bit 16 in is CF 1702 000004B2 D3E8 shr ax, cl ; shift offset right by 4 places 1703 000004B4 01C6 add si, ax ; => above incoming stack with boot sector 1704 000004B6 39D6 cmp si, dx 1705 000004B8 77E4 ja .error_outofmemory 1706 ; this check passes for lDOS (top reservation), IBM-DOS, 1707 ; MS-DOS v6, and MS-DOS v7 (stack expected at 07C00h), 1708 ; and is likely to pass for FreeDOS and EDR-DOS (stack is 1709 ; typically at 1FE0h:7C00h, lDebug loader adheres to the 1710 ; top reservation even for FreeDOS load with auto BPB). 1711 1712 ; note that the next conditional doesn't jump for lsvFATSeg = 0 1713 000004BA 8B4EF8 mov cx, word [bp + lsvFATSeg] 1714 000004BD 81C10002 add cx, (8192) >> 4 1715 000004C1 39D1 cmp cx, dx 1716 000004C3 77D9 ja .error_outofmemory 1717 000004C5 58 pop ax 1718 1719 000004C6 42 inc dx ; => stack + BPB buffer 1720 000004C7 16 push ss 1721 000004C8 1F pop ds ; => incoming stack 1722 000004C9 8EC2 mov es, dx 1723 000004CB 5A pop dx ; top of memory (=> start of RPL, EBDA, video memory) 1724 000004CC 06 push es ; top of memory below buffers 1725 000004CD 50 push ax ; => sector seg 1726 1727 000004CE 31C9 xor cx, cx 1728 000004D0 8DB6ECFE lea si, [bp + lsvCommandLine.start] 1729 000004D4 39F5 cmp bp, si ; can have command line ? 1730 ; (also makes sure movsw and lodsw never run 1731 ; with si = 0FFFFh which'd cause a fault.) 1732 000004D6 720D jb .no_cmdline 1733 1734 000004D8 BF0008 mov di, _STACKSIZE - LOADCMDLINE + ldCommandLine.start 1735 ; -> cmd line target 1736 000004DB B180 mov cl, (LOADCMDLINE_size + 1) >> 1 1737 000004DD F3A5 rep movsw ; copy cmd line 1738 %if lsvCommandLine.start + fromwords(words(LOADCMDLINE_size)) != lsvCommandLine.signature 1739 %error Unexpected structure layout 1740 %endif 1741 000004DF AD lodsw 1742 000004E0 3D434C cmp ax, lsvclSignature 1743 000004E3 7406 je @F ; if command line given --> 1744 .no_cmdline: 1745 000004E5 26880E0008 mov byte [es: _STACKSIZE - LOADCMDLINE + ldCommandLine.start ], cl 1746 ; truncate as if empty line given 1747 000004EA 49 dec cx ; cl = 0FFh 1748 @@: 1749 000004EB 26880EFF08 mov byte [es: _STACKSIZE - LOADCMDLINE + ldCommandLine.start + fromwords(words(LOADCMDLINE_size)) - 1 ], cl 1751 ; remember whether command line given 1752 ; = 0 if given (also truncates if too long) 1753 ; = 0FFh if not given 1754 1755 ; si happens to be already correct here if we didn't 1756 ; branch to .no_cmdline, however make sure to set 1757 ; it here to support this case. 1758 000004F0 8D76EE lea si, [bp + lsvExtra] 1759 ; ds:si -> lsv + BPB 1760 000004F3 BF0E09 mov di, _STACKSIZE - LOADCMDLINE + lsvExtra 1761 ; es:di -> where to place lsv 1762 000004F6 B91202 mov cx, (- lsvExtra + 512) 1763 000004F9 F3A4 rep movsb ; copy lsv (including lsvExtra) and BPB 1764 ; byte move to avoid possibly crossing FFFFh boundary 1765 000004FB 31C0 xor ax, ax 1766 000004FD B110 mov cl, ((ebpbNew - bpbNew + 15) & ~15) >> 1 1767 000004FF F3AB rep stosw ; initialise area behind sector (left so for FAT32) 1768 00000501 58 pop ax 1769 00000502 59 pop cx 1770 ; set up stack. insure to set ss then in the very next 1771 ; instruction set sp. DI here. 1772 00000503 8ED1 mov ss, cx 1773 00000505 BC0008 mov sp, _STACKSIZE 1774 ; -> above end of stack space 1775 00000508 BD2009 mov bp, _STACKSIZE - LOADCMDLINE 1776 ; -> BPB, above end of lsv 1777 0000050B 49 dec cx ; => space for stack + BPB buffer MCB 1778 0000050C FB sti ; EI 1779 1780 ; ax => sector buffer 1781 ; bx => FAT buffer 1782 ; cx => above end of memory available for load 1783 ; dx => above end of memory used by us 1784 0000050D 8956E0 mov word [bp + ldMemoryTop], dx 1785 00000510 894EE2 mov word [bp + ldLoadTop], cx 1786 00000513 8946E4 mov word [bp + ldSectorSeg], ax 1787 1788 00000516 8E5EF8 mov ds, word [bp + lsvFATSeg] 1789 00000519 31F6 xor si, si ; ds:si -> FAT buffer 1790 0000051B 8EC3 mov es, bx 1791 0000051D 31FF xor di, di ; es:di -> where to move 1792 0000051F B90010 mov cx, 8192 >> 1 1793 00000522 F3A5 rep movsw 1794 00000524 895EF8 mov word [bp + lsvFATSeg], bx 1795 1796 00000527 1E push ds ; to check for word [lsvFATSeg] == zero later on 1797 1798 00000528 16 push ss 1799 00000529 07 pop es 1800 0000052A 16 push ss 1801 0000052B 1F pop ds 1802 1803 0000052C 8B5E16 mov bx, [bp + bsBPB + bpbSectorsPerFAT] 1804 0000052F 85DB test bx, bx ; FAT32 ? 1805 00000531 741C jz .is_fat32 ; yes, skip expansion --> 1806 1807 ; lea si, [bp + 510] ; -> last source word 1808 00000533 BE1E0B mov si, _STACKSIZE - LOADCMDLINE + 510 1809 00000536 8D7C1C lea di, [si + (ebpbNew - bpbNew)] ; -> last dest word 1810 00000539 B9EE00 mov cx, (512 - bsBPB - bpbNew + 1) >> 1 1811 ; move sector up, except common BPB start part 1812 %if ((512 - bsBPB - bpbNew + 1) >> 1) <= 20 1813 %fatal Need AMD erratum 109 workaround 1814 %endif 1815 0000053C FD std ; AMD erratum 109 handling not needed 1816 0000053D F3A5 rep movsw 1817 0000053F FC cld 1818 1819 00000540 894EF2 mov word [bp + lsvFirstCluster + 2], cx 1820 00000543 894EF6 mov word [bp + lsvFATSector + 2], cx 1821 ; zero-extend sector and cluster 1822 ; (on FAT12/FAT16, lDOS load and MS-DOS v7 load 1823 ; may pass garbage in the high word) 1824 1825 00000546 895E24 mov word [bp + bsBPB + ebpbSectorsPerFATLarge], bx 1826 00000549 894E26 mov word [bp + bsBPB + ebpbSectorsPerFATLarge + 2], cx 1827 0000054C 894E28 mov word [bp + bsBPB + ebpbFSFlags], cx 1828 ; FSVersion, RootCluster, FSINFOSector, BackupSector, Reserved: 1829 ; uninitialised here (initialised by loaded_all later) 1830 .is_fat32: 1831 %if 1 || _QUERY_GEOMETRY || !_LBA_SKIP_CHECK 1832 0000054F E822FB call query_geometry 1833 ; The ebpbNew BPBN needs to be initialised 1834 ; to use this function. It must be called 1835 ; before using read_sector (used by the FAT12 1836 ; FAT loader, or by finish_load later). 1837 %endif 1838 1839 %if _LSVEXTRA || _MULTISECTOR || _ADDPROGRESS 1840 00000552 8CC9 mov cx, cs 1841 00000554 8B46FA mov ax, word [bp + lsvLoadSeg] 1842 00000557 29C8 sub ax, cx ; = amount paragraphs already resident 1843 %endif 1844 %if _MULTISECTOR || _ADDPROGRESS 1845 00000559 3D8000 cmp ax, (end2 + 15 - $$ + 0) >> 4 1846 ; have end2 (possibly end3 too) ? 1847 0000055C 7203E9E400 jae init_memory_multi 1848 multi_continue_1: 1849 %endif 1850 1851 %if _LSVEXTRA 1852 00000561 F646EF03 test byte [bp + lsvExtra.flags], lsvefNoDataStart | lsvefPartitionNumber 1854 00000565 740B jz @F 1855 1856 00000567 3DE100 cmp ax, (end_of_handle_lsv_extra_flags + 15 -$$+0) >> 4 1857 ; have expanded loader ? 1858 0000056A 7303E9A8FE jb error_notfullyloaded 1859 1860 0000056F E8E103 call handle_lsv_extra_flags 1861 @@: 1862 %endif 1863 1864 ; adjusted sectors per cluster (store in a word, 1865 ; and decode EDR-DOS's special value 0 meaning 256) 1866 00000572 8A460D mov al, [bp + bsBPB + bpbSectorsPerCluster] 1867 00000575 48 dec ax 1868 00000576 B400 mov ah, 0 1869 00000578 40 inc ax ; decode byte 00h to word 0100h, otherwise zero-extend 1870 00000579 8946E8 mov [bp + ldClusterSize], ax 1871 1872 ; 16-byte paragraphs per sector 1873 0000057C 8B460B mov ax, [bp + bsBPB + bpbBytesPerSector] 1874 0000057F B104 mov cl, 4 1875 00000581 D3E8 shr ax, cl 1876 00000583 8946EA mov [bp + ldParaPerSector], ax 1877 1878 ; total sectors 1879 ; After the prior shr instruction, ax is < 8000h, 1880 ; so the following cwd always zeros dx. 1881 00000586 99 cwd 1882 00000587 8B4613 mov ax, [bp + bsBPB + bpbTotalSectors] 1883 0000058A 85C0 test ax, ax 1884 0000058C 7506 jnz @F 1885 0000058E 8B5622 mov dx, [bp + bsBPB + bpbTotalSectorsLarge + 2] 1886 00000591 8B4620 mov ax, [bp + bsBPB + bpbTotalSectorsLarge] 1887 1888 ; fall through and let it overwrite the field with the 1889 ; already current contents. saves a jump. 1890 @@: 1891 00000594 895622 mov [bp + bsBPB + bpbTotalSectorsLarge + 2], dx 1892 00000597 894620 mov [bp + bsBPB + bpbTotalSectorsLarge], ax 1893 1894 ; dx:ax = total sectors 1895 1896 0000059A 59 pop cx ; => original lsvFATSeg 1897 0000059B 837E1600 cmp word [bp + bsBPB + bpbSectorsPerFAT], 0 1898 0000059F C646E620 mov byte [bp + ldFATType], 32 1899 ; prepare for FAT32 1900 000005A3 7453 je .got_fat_type ; is FAT32 --> 1901 1902 ; dx:ax = total amount of sectors 1903 000005A5 2B46FC sub ax, word [bp + lsvDataStart] 1904 000005A8 1B56FE sbb dx, word [bp + lsvDataStart + 2] 1905 1906 ; dx:ax = total amount of data sectors 1907 000005AB 93 xchg bx, ax ; bx = low word, clobber ax 1908 000005AC 92 xchg ax, dx ; ax = high word, clobber dx 1909 000005AD 31D2 xor dx, dx ; dx:ax = high word 1910 000005AF F776E8 div word [bp + ldClusterSize] 1911 000005B2 93 xchg bx, ax 1912 000005B3 F776E8 div word [bp + ldClusterSize] 1913 ; bx:ax = quotient, dx = remainder 1914 ; bx:ax = number of clusters 1915 000005B6 85DB test bx, bx 1916 000005B8 7403 jz @F 1917 .badclusters: 1918 000005BA E915FC jmp error_badclusters 1919 1920 @@: 1921 000005BD 83F8F5 cmp ax, 0FFF7h - 2 1922 000005C0 77F8 ja .badclusters 1923 000005C2 D06EE6 shr byte [bp + ldFATType], 1 ; = 16, prepare for FAT16 1924 000005C5 3DF50F cmp ax, 0FF7h - 2 ; is it FAT16 ? 1925 000005C8 772E ja .got_fat_type ; yes --> 1926 1927 000005CA C646E60C mov byte [bp + ldFATType], 12 ; it is FAT12 1928 000005CE 85C9 test cx, cx ; lsvFATSeg was nonzero ? 1929 000005D0 7526 jnz .got_fat12 ; yes, then the FAT is already loaded --> 1930 1931 ; lsvFATSeg was zero! This means the FAT isn't loaded yet. 1932 1933 ; Load the entire FAT into memory. This is easily feasible for FAT12, 1934 ; as the FAT can only contain at most 4096 entries. 1935 ; (The exact condition should be "at most 4087 entries", or with a 1936 ; specific FF7h semantic, "at most 4088 entries"; the more reliable 1937 ; and portable alternative would be "at most 4080 entries".) 1938 ; Thus, no more than 6 KiB need to be read, even though the FAT size 1939 ; as indicated by word[sectors_per_fat] could be much higher. The 1940 ; first loop condition below is to correctly handle the latter case. 1941 ; (Sector size is assumed to be a power of two between 32 and 8192 1942 ; bytes, inclusive. An 8 KiB buffer is necessary if the sector size 1943 ; is 4 or 8 KiB, because reading the FAT can or will write to 8 KiB 1944 ; of memory instead of only the relevant 6 KiB. This is always true 1945 ; if the sector size is 8 KiB, and with 4 KiB sector size it is true 1946 ; iff word[sectors_per_fat] is higher than one.) 1947 1948 ; If we're here, then ax <= 0FF5h (ja jumped if not), 1949 ; so this cwd always zeros dx. 1950 000005D2 99 cwd ; dx = 0, prepare for dx:ax = FAT sector 1951 000005D3 8CC8 mov ax, cs 1952 000005D5 057910 add ax, (payload_actual_end_late +15) >> 4 1953 ; => behind end of needed payload space 1954 000005D8 7205 jc @F ; if this overflows we'll relocate later, 1955 ; in the meantime we need to load the FAT 1956 000005DA 3946FA cmp word [bp + lsvLoadSeg], ax ; already loaded all ? 1957 000005DD 731C jae .got_fat12_zero ; yes, skip loading the FAT --> 1958 @@: 1959 000005DF BF0018 mov di, 6 << 10 ; maximum size of FAT12 to load 1960 000005E2 8B4E16 mov cx, [bp + bsBPB + bpbSectorsPerFAT] 1961 ; maximum size of this FS's FAT 1962 000005E5 8B460E mov ax, [bp + bsBPB + bpbReservedSectors] 1963 ; dx:ax = first FAT sector 1964 000005E8 8B5EF8 mov bx, [bp + lsvFATSeg]; bx => FAT buffer 1965 %if _MULTISECTOR 1966 multi_patch_2: equ $ 1967 000005EB A9A100 test ax, load_fat12_multi - @F 1968 @@: ; if early multi init done, 1969 ; now go to FAT loader --> 1970 multi_continue_2: equ $ 1971 %endif 1972 @@: 1973 000005EE E8E7FA call read_sector ; read next FAT sector 1974 000005F1 2B7E0B sub di, [bp + bsBPB + bpbBytesPerSector] 1975 ; di = bytes still left to read 1976 000005F4 7602 jbe @F ; if none (di borrowed or now == 0) --> 1977 ; (jbe means jump if CF || ZF) 1978 000005F6 E2F6 loop @B ; if any FAT sector still remains --> 1979 @@: ; one of the limits reached; FAT read 1980 1981 multi_continue_3: equ $ 1982 .got_fat12: 1983 .got_fat_type: 1984 000005F8 8B4EF8 mov cx, word [bp + lsvFATSeg] 1985 1986 .got_fat12_zero: 1987 000005FB E92DFC jmp finish_continue 1988 1989 1990 %assign num 1024+512-($-$$) 1991 %warning num bytes in front of end 1991 ****************** warning: 2 bytes in front of end [-w+user] 1992 000005FE 26 _fill 1024+512,38,start 1993 end: ; IBM-DOS and MS-DOS v6 load only load up to here (1536 bytes). 1994 ; lDOS load can also cope with loading only up to here, albeit 1995 ; it will in fact generally load 4 KiB or more. 1996 1997 %if _MULTISECTOR || _ADDPROGRESS 1998 check_end3_loaded: 1999 00000600 50 push ax 2000 00000601 8CC8 mov ax, cs 2001 00000603 F7D8 neg ax ; minus cs 2002 00000605 01D8 add ax, bx ; loaded segment minus cs = how many paragraphs loaded 2003 00000607 3D8B00 cmp ax, paras(end3 + 15 -$$+0) 2004 ; enough for end3 ? 2005 0000060A 58 pop ax 2006 0000060B C3 retn 2007 2008 skipped_all_continue: 2009 0000060C E8F1FF call check_end3_loaded 2010 0000060F 7303E99DFC jb loadorskip_next_sector 2011 ; not yet --> 2012 ; fall through to jmp multi_late 2013 %else 2014 skipped_all_continue: equ loadorskip_next_sector 2015 %endif 2016 2017 %if ($ - end) > 32 2018 %error load_next part exceeds end+32 2019 %endif 2020 %assign num 32 - ($ - end) 2021 %warning num bytes in front of end+32 2021 ****************** warning: 12 bytes in front of end+32 [-w+user] 2022 ; With the theoretical minimum sector size of 32 bytes, 2023 ; and at least 1536 (end) always loaded by the prior 2024 ; stage, the first single-sector read_sector call has 2025 ; loaded at least up to 1568 (end+32). 2026 2027 %if _MULTISECTOR || _ADDPROGRESS 2028 ; if we fall through, end3 is fully loaded. 2029 00000614 E9E901 jmp multi_late 2030 %endif 2031 2032 2033 ms7_entry.continue: 2034 00000617 FA cli 2035 00000618 FC cld 2036 00000619 5B pop bx 2037 0000061A 1F pop ds 2038 0000061B 8F07 pop word [bx] 2039 0000061D 8F4702 pop word [bx + 2] 2040 2041 00000620 8D5EEC lea bx, [bp + lsvCommandLine] 2042 00000623 39DC cmp sp, bx 2043 00000625 7602 jbe @F 2044 00000627 89DC mov sp, bx 2045 @@: 2046 00000629 897EF0 mov word [bp + lsvFirstCluster + 0], di 2047 0000062C 8976F2 mov word [bp + lsvFirstCluster + 2], si 2048 2049 0000062F 8B461C mov ax, word [bp + bsBPB + bpbHiddenSectors + 0] 2050 00000632 8B561E mov dx, word [bp + bsBPB + bpbHiddenSectors + 2] 2051 00000635 2946FC sub word [bp + lsvDataStart + 0], ax 2052 00000638 1956FE sbb word [bp + lsvDataStart + 2], dx 2053 2054 0000063B 8CC8 mov ax, cs 2055 0000063D 058000 add ax, (4 * 512) >> 4 ; MS7 entry has 4 sectors loaded 2056 00000640 31C9 xor cx, cx ; cx = 0 2057 00000642 E9C3FB jmp ms7_entry.continue2_set_extra_and_empty_cmdline 2058 2059 2060 %if _MULTISECTOR || _ADDPROGRESS 2061 ; INP: ax = amount paragraphs already loaded 2062 ; CHG: bx, cx, dx, si, di, es 2063 ; OUT: patches applied 2064 ; branch to multi_continue_1 2065 init_memory_multi: 2066 %if _LBA && (!_LBA_MULTI) && (!_ADDPROGRESS) 2067 %if !_LBA_SKIP_CHECK 2068 test byte [bp + ldHasLBA], ldhlfLBA 2069 jnz .skip 2070 %else 2071 %error Multi-sector loader must use proper LBA check currently 2072 %endif 2073 %endif 2074 2075 00000645 50 push ax 2076 00000646 1E push ds 2077 2078 %if _CHS 2079 2080 %if _LBA && _LBA_MULTI || _ADDPROGRESS 2081 00000647 3D8A00 cmp ax, (end3 + 15 - $$ + 0) >> 4 2082 ; enough for LBA multi-loader or progress patch ? 2083 0000064A 0E push cs 2084 0000064B 1F pop ds 2085 0000064C B0E9 mov al, 0E9h ; (jmp near) 2086 2087 0000064E 7203E9CA01 jae init_memory_multi_lba 2088 ; yes, try installing that --> 2089 .addprogressonly: 2090 %if _MULTISECTOR && _LBA 2091 %if !_LBA_SKIP_CHECK 2092 00000653 F646E701 test byte [bp + ldHasLBA], ldhlfLBA 2093 00000657 7531 jnz .ret 2094 %else 2095 %error Multi-sector loader must use proper LBA check currently 2096 %endif 2097 %endif 2098 .nolba: 2099 %else 2100 push cs 2101 pop ds 2102 mov al, 0E9h ; (jmp near) 2103 %endif 2104 %if _MULTISECTOR 2105 00000659 A2[D102] mov byte [skipped_all.multipatch], al 2106 0000065C A2[EB05] mov byte [multi_patch_2], al 2107 %endif 2108 2109 %else ; _CHS 2110 2111 %ifn _LBA && _LBA_MULTI 2112 %error Multi loader combination not permitted 2113 %endif 2114 cmp ax, (end3 + 15 - $$ + 0) >> 4 2115 jb .ret 2116 push cs 2117 pop ds 2118 mov al, 0E9h ; jmp near 2119 jmp init_memory_multi_lba 2120 %endif ; _CHS 2121 2122 %if _MULTISECTOR && _CHS 2123 0000065F 48 dec ax ; = 0E8h, call near 2124 00000660 A2[5000] mov byte [error.multi_patch], al 2125 2126 00000663 31DB xor bx, bx 2127 00000665 8EC3 mov es, bx ; => IVT 2128 00000667 B378 mov bl, 1Eh * 4 ; es:bx -> int 1Eh in IVT 2129 00000669 26C537 lds si, [es:bx] ; -> DPT source 2130 0000066C 2E8936[2307] mov word [cs:dpt_restore_ofs], si 2131 00000671 2E8C1E[2807] mov word [cs:dpt_restore_seg], ds 2132 00000676 BF2205 mov di, 522h ; standard target for DPT 2133 00000679 26893F mov word [es:bx], di 2134 0000067C 268C4702 mov word [es:bx + 2], es; -> our DPT 2135 00000680 B91000 mov cx, 16 2136 00000683 F3A4 rep movsb ; copy it over 2137 00000685 26C645F424 mov byte [es:di - 16 + 4], 36 2138 ; force sectors (reportedly DR-DOS does this) 2139 %endif 2140 2141 .ret: 2142 0000068A 1F pop ds 2143 0000068B 58 pop ax 2144 .skip: 2145 .patch_late_multi: ; byte patched to retn (C3h) if late multi init 2146 0000068C E9D2FE jmp multi_continue_1 2147 2148 2149 %if _MULTISECTOR 2150 ; INP: bx => where to load FAT12 2151 ; dx:ax = first FAT sector 2152 ; cx = how many sectors at most 2153 ; di = amount bytes in 6 KiB 2154 ; OUT: branch to multi_continue_2 2155 ; or branch to multi_continue_3 2156 load_fat12_multi: 2157 ; push di 2158 0000068F 31F6 xor si, si 2159 @@: 2160 00000691 46 inc si ; count sectors needed 2161 00000692 2B7E0B sub di, [bp + bsBPB + bpbBytesPerSector] 2162 00000695 77FA ja @B ; if still di > bps, loop 2163 00000697 39F1 cmp cx, si 2164 00000699 7202 jb @F 2165 0000069B 89F1 mov cx, si ; cx = min(cx, si) 2166 @@: 2167 0000069D E88B00 call multi_sector_read ; bx => cx sectors from dx:ax 2168 ; pop di 2169 000006A0 E955FF jmp multi_continue_3 2170 2171 ; INP: bx => where to load next sector 2172 ; dx:ax = next sector to load 2173 ; cx = how many sectors left in cluster, >= 1 2174 ; ldLoadUntilSeg => past end to load 2175 ; ldParaPerSector 2176 ; dword [ss:sp] = current cluster (0-based) 2177 ; OUT: branch to skipped_all.single_sector 2178 ; or branch to loaded_all with stack cleaned 2179 ; REM: Stack must match expected layout for the 2180 ; LOADMULTIVARS to access the expected data. 2181 ; On INP ss:sp -> cluster dword, LOADCMDLINE. 2182 skipped_all_multi: 2183 %if _ADDPROGRESS 2184 mov byte [cs:multi_sector_read.patch_late_progress_init], 73h ; jae 2185 ; REM: Enables the late progress init branch within 2186 ; multi_sector_read. Note that this code assumes 2187 ; that bx => past the last loaded file part. 2188 ; For this reason, the branch must be enabled 2189 ; only here, not earlier. The FAT12 load, if it 2190 ; happens, also uses multi_sector_read and its 2191 ; buffer is somewhere else / higher so during 2192 ; the FAT12 load the late progress init branch 2193 ; must be disabled still. 2194 ; The FAT16 and FAT32 load are not relevant to 2195 ; this because they always directly call the 2196 ; (single-sector) read_sector, so we know that 2197 ; all calls to multi_sector_read after here are 2198 ; loading actual file data. 2199 %endif 2200 000006A3 FFB6DEFE push word [bp + ldmSector + 2] ; push into ldmCluster + 2 2201 000006A7 FFB6DCFE push word [bp + ldmSector] ; push into ldmCluster 2202 2203 000006AB 31FF xor di, di ; = 0 2204 000006AD 53 push bx 2205 @@: 2206 000006AE 47 inc di ; increment count of sectors 2207 000006AF 035EEA add bx, [bp + ldParaPerSector] ; increment => after sector buffer 2208 000006B2 3B5EEE cmp bx, [bp + ldLoadUntilSeg] ; loop if not yet reached the end 2209 000006B5 72F7 jb @B ; di = how many sectors needed 2210 000006B7 5B pop bx ; back to bx => segment 2211 .loop_set_sector: 2212 000006B8 8996DEFE mov word [bp + ldmSector + 2], dx 2213 000006BC 8986DCFE mov word [bp + ldmSector], ax ; update sector 2214 .loop: 2215 000006C0 39F9 cmp cx, di ; have consecutive >= needed ? 2216 000006C2 7342 jae .last ; yes, do last chunk --> 2217 ; no, continue to walk the cluster chain 2218 000006C4 58 pop ax 2219 000006C5 5A pop dx ; current cluster (0-based) 2220 000006C6 52 push dx 2221 000006C7 50 push ax 2222 000006C8 51 push cx 2223 000006C9 53 push bx 2224 000006CA E85EFC call clust_next ; dx:ax = next cluster (0-based) 2225 000006CD 5B pop bx 2226 000006CE 59 pop cx 2227 000006CF 7344 jnc .eoc ; short file or bad cluster --> 2228 ; ! CY, si = 0 2229 000006D1 11B6D8FE adc word [bp + ldmCluster], si 2230 000006D5 11B6DAFE adc word [bp + ldmCluster + 2], si 2231 ; = expected next cluster if consecutive 2232 000006D9 3B96DAFE cmp dx, word [bp + ldmCluster + 2] 2233 000006DD 750B jne .newclust 2234 000006DF 3B86D8FE cmp ax, word [bp + ldmCluster] 2235 000006E3 7505 jne .newclust ; mismatch, cluster not consecutive --> 2236 000006E5 034EE8 add cx, [bp + ldClusterSize] ; cx = amount sectors at least consecutive 2237 000006E8 EBD6 jmp .loop ; check now enough consecutive sectors --> 2238 2239 .newclust: ; the next needed cluster (dx:ax) is not consecutive 2240 ; with the prior one. so read the remaining sectors 2241 ; up to the end of the prior cluster, then set up 2242 ; the loop to find consecutive sectors starting at 2243 ; the non-consecutive new cluster. 2244 000006EA 5E pop si 2245 000006EB 5E pop si ; discard ldmCluster 2246 000006EC 52 push dx 2247 000006ED 50 push ax ; push into ldmCluster 2248 000006EE 8B96DEFE mov dx, word [bp + ldmSector + 2] 2249 000006F2 8B86DCFE mov ax, word [bp + ldmSector] 2250 000006F6 29CF sub di, cx ; amount sectors needed later, is >0 2251 000006F8 E83000 call multi_sector_read ; read consecutive data 2252 000006FB 58 pop ax 2253 000006FC 5A pop dx 2254 000006FD 52 push dx 2255 000006FE 50 push ax ; get ldmCluster (leave a copy on stack) 2256 000006FF 53 push bx 2257 00000700 E807FC call clust_to_first_sector ; dx:ax = first sector, cx = spc 2258 00000703 5B pop bx 2259 00000704 EBB2 jmp .loop_set_sector ; set ldmSector to new cluster's and loop 2260 2261 .last: 2262 00000706 89F9 mov cx, di ; how many sectors last chunk 2263 00000708 58 pop ax 2264 00000709 58 pop ax ; discard stack (ldmCluster) 2265 0000070A 58 pop ax 2266 0000070B 5A pop dx ; get ldmSector and clean stack 2267 0000070C E81C00 call multi_sector_read 2268 0000070F E80900 call restore_dpt ; clobbers bx, ds (unused by loaded_all) 2269 00000712 E98001 jmp loaded_all 2270 2271 .eoc: 2272 00000715 895EEC mov [bp + ldLoadingSeg], bx 2273 00000718 E99FFB jmp end_of_chain ; to error_badchain or error_shortfile 2274 2275 2276 ; INP: word [cs:dpt_restore_ofs], word [cs:dpt_restore_seg] 2277 ; CHG: bx, ds 2278 restore_dpt: 2279 %if _CHS 2280 0000071B 31DB xor bx, bx 2281 0000071D 8EDB mov ds, bx ; ds => IVT 2282 0000071F B378 mov bl, 1Eh * 4 ; ds:bx -> int 1Eh vector 2283 00000721 C7070000 mov word [bx], 0 ; write original int 1Eh offset 2284 dpt_restore_ofs equ $ - 2 2285 00000725 C747020000 mov word [bx + 2], 0 ; write original int 1Eh segment 2286 dpt_restore_seg equ $ - 2 2287 %endif 2288 @@: ; this retn is used by multi_sector_read 2289 0000072A C3 retn 2290 2291 2292 ; INP: dx:ax = start sector 2293 ; cx = how many sectors to load (zero allowed) 2294 ; bx => segment to load to 2295 ; OUT: bx => after last data loaded 2296 ; (cx = 0, but this is not used) 2297 ; (dx:ax = sector after end sector, not used) 2298 ; CHG: es, si, dx, ax, cx 2299 ; REM: This function may be called from load_fat12_multi 2300 ; and then from skipped_all_multi. In the latter 2301 ; case bx => past file data that is already loaded. 2302 ; REM: May call read_sector to do a read that crosses 2303 ; a 64 KiB boundary, otherwise handles all reads 2304 ; by itself. If a read of more than 1 sector 2305 ; fails repeatedly, the flag [ldHasLBA] |= 2 is set 2306 ; (ldhlfForceSingleSector) and then all subsequent 2307 ; attempts will load only a single sector at a time. 2308 multi_sector_read: 2309 .: ; after any read, loop back to here with dx:ax = sector number and 2310 ; bx => buffer adjusted, and cx = remaining count (may be zero). 2311 0000072B E3FD jcxz @B ; cx zero ? then done --> 2312 0000072D 52 push dx 2313 0000072E 50 push ax ; stack = start sector 2314 0000072F 31F6 xor si, si 2315 00000731 56 push si ; 0 to do after 2316 00000732 51 push cx ; all to do now 2317 2318 00000733 8EC3 mov es, bx ; => buffer 2319 00000735 89DE mov si, bx ; => buffer 2320 00000737 81E300F0 and bx, 0F000h ; 64 KiB area of buffer 2321 @@: 2322 0000073B 0376EA add si, word [bp + ldParaPerSector] 2323 ; => behind next sector buffer 2324 0000073E 56 push si 2325 0000073F 4E dec si ; => at last paragraph in this buffer 2326 00000740 81E600F0 and si, 0F000h ; 64 KiB area of last paragraph 2327 00000744 39F3 cmp bx, si ; same ? 2328 00000746 5E pop si 2329 00000747 E1F2 loope @B ; loop if same --> and not last 2330 00000749 5E pop si ; si = amount wanted --> (how much first) 2331 0000074A 741E je .nolimit64 ; if all same --> proceed with full 2332 0000074C 41 inc cx ; cx = how many need to be deferred 2333 0000074D 5B pop bx ; discard the zero 2334 0000074E 29CE sub si, cx ; si = amount we can do maximum 2335 00000750 7517 jnz .decrease64 ; any ? then do them --> 2336 .singlesector: 2337 00000752 8CC3 mov bx, es ; restore => data 2338 00000754 E881F9 call read_sector 2339 00000757 49 dec cx ; (bugfix, keep track of remaining count) 2340 ; cx = how many left 2341 ; dx:ax = next sector 2342 ; bx => next buffer 2343 00000758 5E pop si 2344 00000759 5E pop si ; discard sector number 2345 .j: 2346 %if _ADDPROGRESS 2347 call check_end3_loaded ; is bx => loaded end3 ? 2348 .patch_late_progress_init: 2349 dist equ late_progress_init_j - @F 2350 test al, dist ; (this is patched to 73h, jae short) 2351 @@: 2352 %assign num dist 2353 %if num > 127 ; jae short jumps at most 127 bytes forward 2354 %error Distance too long! num 2355 %endif 2356 %warning Distance is num 2357 %endif 2358 0000075A EBCF jmp . ; loop back with updated sector/buffer 2359 2360 2361 %if _CHS 2362 .fallback: 2363 %if 1 2364 0000075C 3C01 cmp al, 1 ; tried to load a single sector ? 2365 0000075E 7406 je .error ; yes, this is an error --> 2366 %else 2367 testopt [bp + ldHasLBA], ldhlfForceSingleSector 2368 ; already in single-sector mode ? 2369 jnz .error ; yes, this is an error --> 2370 %endif 2371 00000760 804EE702 setopt [bp + ldHasLBA], ldhlfForceSingleSector 2372 ; set flag to force single-sector operation 2373 00000764 EB30 jmp .again 2374 %endif 2375 2376 .error: 2377 00000766 E9F3F9 jmp error_diskaccess 2378 ; we go here if repeated read calls even of a single 2379 ; sector at a time all fail. if a multi-sector read 2380 ; fails, it first sets the single-sector flag and 2381 ; attempts to load the first sector on its own. 2382 ; 64 KiB boundary crossing never goes through here 2383 ; as we detect that early and pass it to read_sector. 2384 2385 2386 .decrease64: 2387 00000769 51 push cx ; how many next 2388 .nolimit64: ; si = how much first 2389 0000076A 56 push si ; how many at most in this call 2390 %if _CHS 2391 %if _LBA && _LBA_MULTI 2392 .lbapatch: ; writes a near jump in 3 bytes 2393 ; (this patch clobbers the subsequent add ax instruction) 2394 %endif 2395 %else 2396 jmp multi_sector_read_lba 2397 %endif 2398 2399 %if _CHS 2400 ; DX:AX==LBA sector number 2401 ; add partition start (= number of hidden sectors) 2402 0000076B 03461C add ax,[bp + bsBPB + bpbHiddenSectors + 0] 2403 0000076E 13561E adc dx,[bp + bsBPB + bpbHiddenSectors + 2] 2404 00000771 72F3 jc .error ; dx:ax = unit absolute sector, 2405 ; error if >4 Gi sectors 2406 %if _CHS_RETRY 2407 00000773 2EC606[4E01]C3 mov byte [cs:read_sector.dx_ax_cx_lba_to_chs_patch_E8], 0C3h 2408 ; = 0E8h if to continue 2409 ; = 0C3h if to return 2410 %else 2411 mov byte [cs:read_sector.dx_ax_cx_lba_to_chs_patch_B8], 0C3h 2412 ; = 0B8h if to continue 2413 ; = 0C3h if to return 2414 %endif 2415 00000779 91 xchg cx, ax ; dx:cx = sector LBA number 2416 0000077A 92 xchg dx, ax ; ax:cx = sector LBA number 2417 0000077B 31D2 xor dx, dx ; dx:ax:cx 2418 0000077D E8A6F9 call read_sector.dx_ax_cx_lba_to_chs 2419 %if _CHS_RETRY 2420 00000780 2EC606[4E01]E8 mov byte [cs:read_sector.dx_ax_cx_lba_to_chs_patch_E8], 0E8h 2421 ; = 0E8h if to continue 2422 ; = 0C3h if to return 2423 %else 2424 mov byte [cs:read_sector.dx_ax_cx_lba_to_chs_patch_B8], 0B8h 2425 ; = 0B8h if to continue 2426 ; = 0C3h if to return 2427 %endif 2428 ; @boeckmann code follows 2429 00000786 89C8 mov ax, cx ; ax & 63 = S coordinate to read 2430 00000788 83E03F and ax, 63 ; isolate S coordinate 2431 0000078B 48 dec ax ; make it 0-based 2432 0000078C 2B4618 sub ax, word [bp + bsBPB + bpbCHSSectors] 2433 ; S coordinate - amount of S 2434 0000078F F7D8 neg ax ; amount of S - S coordinate 2435 ; = how many S in this C:H left 2436 00000791 39F0 cmp ax, si 2437 00000793 7201 jb .limittrack ; ax = min(ax, si) 2438 00000795 96 xchg ax, si ; ax = remaining to read, si clobbered 2439 .limittrack: 2440 ; end of @boeckmann code 2441 2442 .again: 2443 00000796 F646E702 testopt [bp + ldHasLBA], ldhlfForceSingleSector 2444 0000079A 7402 jz @F 2445 0000079C B001 mov al, 1 ; force single-sector operation 2446 @@: 2447 2448 %if _ADDPROGRESS 2449 .patch_progress: 2450 test ax, read_progress_multi_chs - @F 2451 @@: 2452 %endif 2453 2454 0000079E BE0400 mov si, 4 ; 4 attempts (retry 3 times) 2455 .retry: 2456 000007A1 B402 mov ah, 02h ; function = read CHS sectors 2457 000007A3 50 push ax 2458 000007A4 CD13 int 13h 2459 000007A6 58 pop ax ; preserve al = count 2460 000007A7 730B jnc .got 2461 000007A9 4E dec si ; count down retry count 2462 000007AA 74B0 jz .fallback 2463 000007AC 50 push ax 2464 000007AD 31C0 xor ax, ax 2465 000007AF CD13 int 13h ; reset disk 2466 000007B1 58 pop ax 2467 000007B2 EBED jmp .retry ; and retry --> 2468 %endif 2469 2470 ; INP: ss:sp -> word how many wanted this step, 2471 ; word how many wanted later, 2472 ; dword start sector number 2473 ; es => buffer just read to 2474 ; al = how many actually just read, < 128 2475 .got: 2476 000007B4 5E pop si ; si = how many wanted this step 2477 000007B5 98 cbw ; S must be <= 63 so al is too, ah = 0 2478 ; from LBA al is <= 127, so ah = 0 2479 000007B6 29C6 sub si, ax ; si = how many left over 2480 000007B8 91 xchg cx, ax ; cx = how many have been read this step 2481 000007B9 5B pop bx 2482 000007BA 01F3 add bx, si ; bx = how many still to do 2483 000007BC 58 pop ax 2484 000007BD 5A pop dx ; dx:ax = start sector number 2485 000007BE 01C8 add ax, cx 2486 000007C0 83D200 adc dx, 0 ; dx:ax = next start sector 2487 000007C3 72A1 jc .error 2488 000007C5 53 push bx 2489 000007C6 8CC3 mov bx, es ; => input buffer 2490 @@: 2491 000007C8 035EEA add bx, word [bp + ldParaPerSector] 2492 000007CB E2FB loop @B ; => next buffer 2493 000007CD 59 pop cx ; cx = how many to do 2494 000007CE EB8A jmp .j ; loop back with updated sector/buffer 2495 %endif 2496 %endif 2497 2498 2499 %assign num 2046-($-$$) 2500 %if _MULTISECTOR && _ADDPROGRESS 2501 %if num >= 3 2502 %assign num num - 3 2503 %warning %[num]+3 bytes in front of end2 2504 late_progress_init_j: 2505 jmp late_progress_init 2506 %else 2507 late_progress_init_j: equ late_progress_init 2508 %warning num bytes in front of end2 2509 %endif 2510 %else 2511 %warning num bytes in front of end2 2511 ****************** warning: 46 bytes in front of end2 [-w+user] 2512 %endif 2513 000007D0 26 _fill 2046,38,start 2514 000007FE 4D53 dw "MS" ; signature of MS-DOS 7 load 2515 align 16, db 38 2516 end2: 2517 2518 %if _MULTISECTOR || _ADDPROGRESS 2519 2520 %if _MULTISECTOR 2521 %if _ADDPROGRESS 2522 late_progress_init: 2523 mov byte [cs:multi_sector_read.patch_late_progress_init], __TEST_IMM8 2524 ; avoid entering this code again 2525 mov byte [cs:read_sector.patch_progress], 0E8h 2526 %if _CHS 2527 mov byte [cs:multi_sector_read.patch_progress], 0E8h 2528 %endif 2529 jmp multi_sector_read. 2530 %endif 2531 %endif 2532 2533 ; This is entered if the single-sector loop 2534 ; detects that up to end3 has been loaded. 2535 ; It is safe to run this part, the CHS or 2536 ; LBA multi init, and the progress patches 2537 ; at this point. To ensure proper operation, 2538 ; pass bx => the current loaded segment to 2539 ; the init_memory_multi entrypoint in ax, 2540 ; subtracting cs to get amount paras loaded. 2541 multi_late: 2542 00000800 2EC606[8C06]C3 mov byte [cs:init_memory_multi.patch_late_multi], 0C3h 2543 ; retn 2544 00000806 57 push di 2545 00000807 56 push si 2546 00000808 50 push ax 2547 00000809 53 push bx 2548 0000080A 51 push cx 2549 0000080B 52 push dx 2550 0000080C 93 xchg ax, bx ; clobbers bx 2551 0000080D 8CCB mov bx, cs 2552 0000080F 29D8 sub ax, bx ; = amount paragraphs loaded 2553 00000811 E831FE call init_memory_multi ; set patches 2554 00000814 5A pop dx 2555 00000815 59 pop cx 2556 00000816 5B pop bx 2557 00000817 58 pop ax 2558 00000818 5E pop si 2559 00000819 5F pop di 2560 0000081A E994FA jmp loadorskip_next_sector 2561 ; back to the loop, will branch to multi 2562 ; loader on next read_sector intention. 2563 %endif 2564 2565 %if _MULTISECTOR && _LBA && _LBA_MULTI || _ADDPROGRESS 2566 ; INP: al = 0E9h 2567 ; ss:sp -> ds, ax 2568 ; cs = ds 2569 ; CHG: bx, cx, dx, si, di, es 2570 ; OUT: patches applied 2571 ; branch to multi_continue_1 / init_memory_multi.ret 2572 ; or branch to init_memory_multi.nolba 2573 init_memory_multi_lba: 2574 %if _ADDPROGRESS 2575 mov byte [read_sector.patch_progress], 0E8h 2576 %if _CHS && _MULTISECTOR 2577 mov byte [multi_sector_read.patch_progress], 0E8h 2578 %endif 2579 %endif 2580 2581 %if _MULTISECTOR && _LBA && _LBA_MULTI 2582 2583 %if _CHS 2584 %if !_LBA_SKIP_CHECK 2585 0000081D F646E701 test byte [bp + ldHasLBA], ldhlfLBA 2586 00000821 7503E933FE jz init_memory_multi.nolba 2587 %else 2588 %error Multi-sector loader must use proper LBA check currently 2589 %endif 2590 %endif 2591 00000826 A2[D102] mov byte [skipped_all.multipatch], al 2592 00000829 A2[EB05] mov byte [multi_patch_2], al 2593 %if _CHS 2594 0000082C C606[1B07]C3 mov byte [restore_dpt], 0C3h ; make this a no-op 2595 00000831 A2[6B07] mov byte [multi_sector_read.lbapatch], al 2596 ; jmp near 2597 00000834 C706[6C07]CF00 mov word [multi_sector_read.lbapatch + 1], multi_sector_read_lba - (multi_sector_read.lbapatch + 3) 2599 %endif 2600 0000083A E94DFE jmp init_memory_multi.ret 2601 %else 2602 jmp init_memory_multi.addprogressonly 2603 %endif 2604 2605 %if _MULTISECTOR && _LBA && _LBA_MULTI 2606 ; INP: dx:ax = start sector within partition 2607 ; es => buffer 2608 ; si = how many sectors at most in this step, >= 1 2609 ; stack -> word equal to si, 2610 ; how many sectors left for later (word), 2611 ; start sector (dword), 2612 ; near return address (word) 2613 ; CHG: ax, bx, cx, dx, si 2614 ; REM: Upon successful read of at least 1 sector, transfer 2615 ; control to multi_sector_read.got with al = amount 2616 ; of sectors actually read (< 128), ss:sp unchanged. 2617 multi_sector_read_lba: 2618 0000083D 83FE7F cmp si, 127 2619 00000840 7603 jbe @F 2620 00000842 BE7F00 mov si, 127 ; = min(si, 127) 2621 ; LBA limit appears to be 127 sectors per call. 2622 ; refer to https://retrocomputing.stackexchange.com/questions/32124/whats-the-maximum-amount-of-sectors-an-lba-rom-bios-sector-read-call-can-access/ 2623 @@: 2624 2625 00000845 57 push di 2626 00000846 89F7 mov di, si 2627 2628 ; DX:AX==LBA sector number 2629 ; add partition start (= number of hidden sectors) 2630 00000848 03461C add ax,[bp + bsBPB + bpbHiddenSectors + 0] 2631 0000084B 13561E adc dx,[bp + bsBPB + bpbHiddenSectors + 2] 2632 2633 %if (!_LBA_33_BIT) && _LBA_CHECK_NO_33 2634 jc .error 2635 %endif 2636 %if _LBA_33_BIT 2637 0000084E 19F6 sbb si, si ; -1 if was CY, 0 else 2638 00000850 F7DE neg si ; 1 if was CY, 0 else 2639 %endif 2640 00000852 31C9 xor cx, cx ; cx = 0 2641 00000854 51 push cx 2642 %if _LBA_33_BIT 2643 00000855 56 push si ; bit 32 = 1 if operating in 33-bit space 2644 %else 2645 push cx ; second highest word = 0 2646 %endif 2647 00000856 52 push dx 2648 00000857 50 push ax ; qword sector number (lpSector) 2649 00000858 06 push es 2650 00000859 51 push cx ; es => buffer (es:0 = lpBuffer) 2651 0000085A 57 push di ; word number of sectors to read (lpCount) 2652 0000085B B110 mov cl, 10h 2653 0000085D 51 push cx ; word size of disk address packet (lpSize) 2654 ; ! ch = 0 2655 0000085E 89E6 mov si, sp ; ds:si -> disk address packet (on stack) 2656 2657 00000860 8A5640 mov dl, [bp + bsBPB + ebpbNew + bpbnBootUnit] 2658 2659 .lbaagain: 2660 00000863 F646E702 testopt [bp + ldHasLBA], ldhlfForceSingleSector 2661 00000867 7403 jz @F 2662 00000869 BF0100 mov di, 1 ; force single-sector operation 2663 @@: 2664 2665 %if _ADDPROGRESS 2666 call read_progress_multi_lba 2667 %endif 2668 2669 0000086C B104 mov cl, 4 ; 4 attempts (retry 3 times) 2670 ; ! ch already = 0 2671 0000086E A9 db __TEST_IMM16 ; skip int 2672 .lbaretry: 2673 0000086F CD13 int 13h 2674 00000871 897C02 mov word [si + lpCount], di ; reset for next attempt 2675 00000874 B442 mov ah, 42h ; 13.42 extensions read 2676 00000876 CD13 int 13h 2677 00000878 730F jnc .lbagot 2678 0000087A 31C0 xor ax, ax ; reset disk 2679 0000087C E2F1 loop .lbaretry ; count down retry count 2680 .lbafallback: ; ! cx = 0 2681 %if 1 2682 0000087E 83FF01 cmp di, 1 ; tried to load a single sector ? 2683 00000881 740D je .error ; yes, this is an error --> 2684 %else 2685 testopt [bp + ldHasLBA], ldhlfForceSingleSector 2686 ; already in single-sector mode ? 2687 jnz .error ; yes, this is an error --> 2688 %endif 2689 00000883 804EE702 setopt [bp + ldHasLBA], ldhlfForceSingleSector 2690 ; set flag to force single-sector operation 2691 00000887 EBDA jmp .lbaagain 2692 2693 .lbagot: 2694 00000889 97 xchg ax, di ; al = how many read, <= 127 2695 0000088A 0324 add sp, word [si + lpSize] ; discard LBA packet 2696 0000088C 5F pop di ; restore di 2697 0000088D E924FF jmp multi_sector_read.got 2698 2699 .error: 2700 00000890 E9D3FE jmp multi_sector_read.error 2701 %endif 2702 %endif 2703 2704 2705 %if _ADDPROGRESS 2706 read_progress_multi_lba: 2707 push ax 2708 mov al, '&' 2709 jmp @F 2710 2711 read_progress_multi_chs: 2712 push ax 2713 mov al, '.' 2714 jmp @F 2715 2716 read_progress_single: 2717 push ax 2718 mov al, '+' 2719 @@: 2720 push bx 2721 push bp 2722 mov ah, 0Eh 2723 mov bx, 7 2724 int 10h 2725 pop bp 2726 pop bx 2727 pop ax 2728 retn 2729 %endif 2730 2731 2732 %if _MULTISECTOR || _ADDPROGRESS 2733 %assign num 4096-($-$$) 2734 %warning num bytes in front of end3 2734 ****************** warning: 1901 bytes in front of end3 [-w+user] 2735 %if num < 0 2736 %warning LBA multi-sector loader is overflowing expected end3/4096 2737 %endif 2738 end3: 2739 %endif 2740 2741 %if _LOADERSUPPORT 2742 align 16, db 38 2743 %if ($ - $$) > (4096 - 16) 2744 %error LOADER support signature is overflowing 4096 2745 %endif 2746 loadersupport: 2747 db "LOAD","ERSU","PPOR","T00" ; signature and version 2748 db 0 ; flags, tbd 2749 align 16, db 0 2750 %endif 2751 2752 2753 ; This handling is in the second header part, 2754 ; behind the needed part to finish loading. 2755 ; It is only used when the file is completely loaded. 2756 loaded_all.2stack: 2757 00000893 58 pop ax 2758 00000894 58 pop ax 2759 loaded_all: 2760 %if _MULTISECTOR && _RESET_SINGLE_SECTOR 2761 00000895 8066E7FD clropt [bp + ldHasLBA], ldhlfForceSingleSector 2762 ; reset single-sector flag 2763 %endif 2764 2765 00000899 8B4616 mov ax, word [bp + bsBPB + bpbSectorsPerFAT] 2766 0000089C 85C0 test ax, ax 2767 0000089E 740C jz .fat32 2768 2769 000008A0 31C0 xor ax, ax 2770 000008A2 16 push ss 2771 000008A3 07 pop es 2772 000008A4 8D7E28 lea di, [bp + bsBPB + ebpbFSFlags] 2773 000008A7 B90C00 mov cx, (EBPB_size - ebpbFSFlags) / 2 2774 000008AA F3AB rep stosw 2775 ; initialise ebpbFSFlags (reinit), ebpbFSVersion, 2776 ; ebpbRootCluster, ebpbFSINFOSector, ebpbBackupSector, 2777 ; ebpbReserved 2778 2779 .fat32: 2780 2781 %if (_QUERY_GEOMETRY || !_LBA_SKIP_CHECK) && _QUERY_PATCH 2782 000008AC 2EA1[7800] mov ax, word [cs:..@query_patch_site] 2783 %else 2784 mov ax, _QUERY_DEFAULT 2785 %endif 2786 000008B0 8946EC mov word [bp + ldQueryPatchValue], ax 2787 2788 %if _CHECKSUM 2789 push cs 2790 pop ds 2791 2792 %if _QUERY_PATCH 2793 mov word [..@query_patch_site], _QUERY_DEFAULT 2794 %endif 2795 2796 mov si, checksumheader 2797 mov cx, CHECKSUMHEADER_size / 2 2798 xor bx, bx 2799 @@: 2800 cmp si, ..@checksumfield 2801 lodsw 2802 jne @F 2803 xor ax, ax 2804 @@: 2805 add bx, ax 2806 loop @BB 2807 2808 test bx, bx 2809 jnz error_header_checksum_failed 2810 2811 testopt [..@checksumtype], 8000h 2812 jnz @F 2813 2814 call checksum_crc16_6_paragraphs_start_cs 2815 int3 2816 2817 push cs 2818 pop ds 2819 2820 cmp ax, word [..@checksumfield] 2821 jne error_data_checksum_failed 2822 ..@data_checksum_ignore_failure_debugger: 2823 @@: 2824 %endif 2825 2826 000008B3 16 push ss 2827 000008B4 07 pop es 2828 000008B5 8DBEE0FE lea di, [bp + ldCommandLine.start] 2829 000008B9 B90001 mov cx, lsvclBufferLength 2830 000008BC 31C0 xor ax, ax 2831 000008BE FF76DF push word [bp + ldCommandLine.start + lsvclBufferLength - 1] 2832 ; get sentinel (whether command line given) 2833 000008C1 F2AE repne scasb ; scan for terminator 2834 000008C3 58 pop ax ; al = 0FFh if no command line given 2835 ; al = 0 else 2836 000008C4 F3AA rep stosb ; clear remainder of buffer 2837 2838 000008C6 8CC8 mov ax, cs 2839 000008C8 050601 add ax, ((payload -$$+0) >> 4) + _EXEC_SEGMENT 2840 000008CB 50 push ax 2841 %if _EXEC_OFFSET 2842 mov ax, _EXEC_OFFSET 2843 %else 2844 000008CC 31C0 xor ax, ax 2845 %endif 2846 000008CE 50 push ax 2847 ; cs:ip = xxxxh:_EXEC_OFFSET 2848 ; entire payload loaded (payload -- payload.actual_end) 2849 ; LOADSTACKVARS and LOADDATA and EBPB and ebpbNew BPBN set 2850 ; LOADCMDLINE set (ASCIZ, up to 255 bytes + 1 byte terminator) 2851 ; word [ldCommandLine.start] = 0FF00h if had invalid signature 2852 000008CF CB retf 2853 2854 2855 %if _CHECKSUM 2856 error_header_checksum_failed: 2857 call error 2858 db "Header checksum failed." 2859 2860 error_data_checksum_failed: 2861 stc 2862 int3 2863 jnc ..@data_checksum_ignore_failure_debugger 2864 call error 2865 db "Data checksum failed." 2866 %endif 2867 2868 2869 freedos_or_msdos1_com_entry: 2870 000008D0 FC cld 2871 000008D1 E80000 call @F 2872 @@: 2873 000008D4 59 pop cx 2874 000008D5 81F9[D408] cmp cx, @B 2875 000008D9 7403E94607 jne msdos1_com_entry 2876 2877 freedos_entry: 2878 ; This is the FreeDOS compatible entry point. 2879 ; Supports FAT32 too. 2880 ; cs:ip = 60h:0 (or 70h:0 for EDR-DOS entry) 2881 ; whole load file loaded 2882 ; first cluster of load file: not given! 2883 ; first data sector: not given! 2884 ; int 1Eh not modified, original address: not given! 2885 ; bl = load unit (or dl for EDR-DOS entry) (not used by us) 2886 ; ss:bp -> boot sector with (E)BPB, 2887 ; load unit field set, hidden sectors set 2888 ; (usually at 1FE0h:7C00h) 2889 ; NEW: word [ss:bp - 14h] = "CL" to indicate command line 2890 ; then ss:bp - 114h -> 256 byte ASCIZ string 2891 2892 000008DE 8D9EECFE lea bx, [bp + lsvCommandLine.start] 2893 ; ss:bx -> command line buffer, if any 2894 000008E2 81FD1401 cmp bp, - lsvCommandLine.start 2895 ; enough data below bp to hold buffer ? 2896 000008E6 7204 jb @F ; no --> 2897 000008E8 39DC cmp sp, bx ; sp below-or-equal would-be buffer ? 2898 000008EA 760F jbe .canbevalid ; yes, can be valid --> (and word access valid) 2899 @@: 2900 000008EC 83FD14 cmp bp, - lsvCommandLine.signature 2901 ; enough data below bp to hold our lsv ? 2902 000008EF 7306 jae @F ; yes --> 2903 000008F1 F7C50100 test bp, 1 ; valid to access even-aligned words ? 2904 000008F5 752E jnz .error ; maybe not --> 2905 @@: 2906 000008F7 8366EC00 and word [bp + lsvCommandLine.signature], 0 2907 ; invalidate signature 2908 .canbevalid: 2909 000008FB 817EEC434C cmp word [bp + lsvCommandLine.signature], "CL" 2910 ; valid signature ? 2911 00000900 7403 je @F ; yes, keep bx pointing at buffer 2912 2913 00000902 8D5EEC lea bx, [bp + lsvCommandLine.signature] 2914 ; no, ss:bx -> lsv with signature 2915 @@: 2916 00000905 39DC cmp sp, bx ; sp below-or-equal needed stack frame ? 2917 00000907 7605 jbe @F ; yes --> 2918 00000909 80E3FE and bl, ~1 ; make even-aligned stack (rounding down) 2919 0000090C 89DC mov sp, bx ; change sp 2920 @@: 2921 2922 2923 d3 call d3_display_two_characters 2924 d3 test ax, "F0" 2925 2926 0000090E 31C9 xor cx, cx 2927 00000910 894EF0 mov word [bp + lsvFirstCluster + 0], cx 2928 00000913 894EF2 mov word [bp + lsvFirstCluster + 2], cx 2929 2930 %if _LSVEXTRA 2931 00000916 C746EE0001 mov word [bp + lsvExtra], lsvefNoDataStart << 8 2932 %else 2933 call calculate_data_start 2934 %endif 2935 .multiboot_entry: 2936 0000091B 8CC8 mov ax, cs 2937 0000091D 057910 add ax, (payload_actual_end_late +15) >> 4 2938 ; Multiboot1/2 and FreeDOS have whole image 2939 00000920 31C9 xor cx, cx ; cx = 0 2940 00000922 E9E9F8 jmp ms7_entry.continue2 2941 2942 2943 .error: 2944 00000925 E828F7 call error 2945 00000928 496E76616C69642062- db "Invalid base pointer in FreeDOS entrypoint." 2945 00000931 61736520706F696E74- 2945 0000093A 657220696E20467265- 2945 00000943 65444F5320656E7472- 2945 0000094C 79706F696E742E 2946 2947 2948 %if _LSVEXTRA 2949 handle_lsv_extra_flags: 2950 00000953 F646EF02 test byte [bp + lsvExtra.flags], lsvefPartitionNumber 2951 00000957 7403 jz @F 2952 00000959 E80A00 call parse_partition_number 2953 @@: 2954 0000095C F646EF01 test byte [bp + lsvExtra.flags], lsvefNoDataStart 2955 00000960 7403 jz @F 2956 00000962 E85304 call calculate_data_start 2957 @@: 2958 00000965 C3 retn 2959 2960 2961 parse_partition_number: 2962 00000966 31C0 xor ax, ax 2963 00000968 89461C mov word [bp + bsBPB + bpbHiddenSectors], ax 2964 0000096B 89461E mov word [bp + bsBPB + bpbHiddenSectors + 2], ax 2965 0000096E 807E40FF cmp byte [bp + bsBPB + ebpbNew + bpbnBootUnit], -1 2966 00000972 750D jne @F 2967 00000974 C6464080 mov byte [bp + bsBPB + ebpbNew + bpbnBootUnit], 80h 2968 00000978 894618 mov word [bp + bsBPB + bpbCHSSectors], ax 2969 0000097B 89461A mov word [bp + bsBPB + bpbCHSHeads], ax 2970 0000097E E8F3F6 call query_geometry 2971 @@: 2972 2973 %if !_LBA_SKIP_CHECK 2974 00000981 F646E701 test byte [bp + ldHasLBA], ldhlfLBA 2975 00000985 7525 jnz @F 2976 %endif 2977 2978 00000987 8B4618 mov ax, word [bp + bsBPB + bpbCHSSectors] 2979 0000098A 8B561A mov dx, word [bp + bsBPB + bpbCHSHeads] 2980 2981 ; following is from lDebug 0c0930773929 boot.asm 2982 overridedef DEBUG5, 0 2983 %define load_unit (bp + bsBPB + ebpbNew + bpbnBootUnit) 2984 %define load_sectorsize (bp + bsBPB + bpbBytesPerSector) 2985 %define load_sectorsizepara (bp + ldParaPerSector) 2986 2987 0000098D 85C0 test ax, ax 2988 0000098F 7503E9FD01 jz .invalid_sectors 2989 00000994 83F83F cmp ax, 63 2990 00000997 7603E9F501 ja .invalid_sectors 2991 0000099C 85D2 test dx, dx 2992 0000099E 7503E9EE01 jz .invalid_heads 2993 000009A3 81FA0001 cmp dx, 100h 2994 000009A7 7603E9E501 ja .invalid_heads 2995 @@: 2996 2997 000009AC 8B46E4 mov ax, word [bp + ldSectorSeg] ; ax => sector seg 2998 000009AF 48 dec ax ; ax => sector seg - 16 2999 000009B0 8EC0 mov es, ax 3000 000009B2 31C0 xor ax, ax 3001 000009B4 BB1000 mov bx, 16 3002 3003 d5 call d5dumpregs 3004 d5 call d5message 3005 d5 asciz 13,10,"In query_geometry 0",13,10 3006 3007 000009B7 89DF mov di, bx 3008 000009B9 B90110 mov cx, (8192 + 2) >> 1 3009 ; es:bx -> auxbuff, es:di = same 3010 000009BC F3AB rep stosw ; fill buffer, di -> behind (auxbuff+8192+2) 3011 000009BE B80102 mov ax, 0201h ; read sector, 1 sector 3012 000009C1 41 inc cx ; sector 1 (1-based!), cylinder 0 (0-based) 3013 000009C2 B600 mov dh, 0 ; head 0 (0-based) 3014 000009C4 8A5640 mov dl, [load_unit] 3015 000009C7 F9 stc 3016 000009C8 E89601 call .int13_retry 3017 000009CB 7303E9A701 jc .access_error 3018 3019 000009D0 FD std ; _AMD_ERRATUM_109_WORKAROUND does not apply 3020 000009D1 26C747FE5E5E mov word [es:bx - 2], 5E5Eh ; may overwrite last 2 bytes at line_out_end 3021 000009D7 AF scasw ; -> auxbuff+8192 (at last word to sca) 3022 d5 call d5dumpregs 3023 d5 call d5message 3024 d5 asciz 13,10,"In query_geometry 1",13,10 3025 000009D8 B90110 mov cx, (8192 + 2) >> 1 3026 000009DB 31C0 xor ax, ax 3027 000009DD F3AF repe scasw 3028 000009DF 83C704 add di, 4 ; di -> first differing byte (from top) 3029 000009E2 FC cld 3030 000009E3 57 push di 3031 3032 000009E4 89DF mov di, bx 3033 000009E6 B90110 mov cx, (8192 + 2) >> 1 3034 000009E9 48 dec ax ; = FFFFh 3035 000009EA F3AB rep stosw 3036 3037 000009EC B80102 mov ax, 0201h 3038 000009EF 41 inc cx 3039 000009F0 B600 mov dh, 0 3040 000009F2 8A5640 mov dl, [load_unit] 3041 000009F5 F9 stc 3042 000009F6 E86801 call .int13_retry 3043 000009F9 7303E97901 jc .access_error 3044 3045 000009FE FD std ; _AMD_ERRATUM_109_WORKAROUND does not apply 3046 000009FF AF scasw ; di -> auxbuff+8192 (last word to sca) 3047 d5 call d5dumpregs 3048 d5 call d5message 3049 d5 asciz 13,10,"In query_geometry 2",13,10 3050 00000A00 5A pop dx 3051 00000A01 B8FFFF mov ax, -1 3052 00000A04 B90110 mov cx, (8192 + 2) >> 1 3053 00000A07 F3AF repe scasw 3054 %if 0 3055 AAAB 3056 ^ 3057 sca B, match 3058 ^ 3059 sca B, mismatch 3060 ^ 3061 stop 3062 %endif 3063 00000A09 83C704 add di, 4 ; di -> first differing byte (from top) 3064 00000A0C FC cld 3065 3066 %if 0 3067 0000000000000 3068 AAAAAAAA00000 3069 ^ 3070 FFFFFFFFFFFFF 3071 AAAAAAAA00FFF 3072 ^ 3073 %endif 3074 00000A0D 39FA cmp dx, di ; choose the higher one 3075 00000A0F 7302 jae @F 3076 00000A11 89FA mov dx, di 3077 @@: 3078 00000A13 29DA sub dx, bx ; dx = sector size 3079 3080 d5 call d5dumpregs 3081 d5 call d5message 3082 d5 asciz 13,10,"In query_geometry 3",13,10 3083 3084 00000A15 81FA0220 cmp dx, 8192 + 2 3085 00000A19 7203E95C01 jae .sector_too_large 3086 00000A1E B82000 mov ax, 32 3087 00000A21 39C2 cmp dx, ax 3088 00000A23 7303E95201 jb .sector_too_small 3089 @@: 3090 00000A28 39C2 cmp dx, ax 3091 00000A2A 740C je .got_match 3092 00000A2C 3D0020 cmp ax, 8192 3093 00000A2F 7203E94601 jae .sector_not_power 3094 00000A34 D1E0 shl ax, 1 3095 00000A36 EBF0 jmp @B 3096 3097 .got_match: 3098 00000A38 89460B mov word [load_sectorsize], ax 3099 00000A3B B104 mov cl, 4 3100 00000A3D D3E8 shr ax, cl 3101 00000A3F 8946EA mov word [load_sectorsizepara], ax 3102 3103 resetdef 3104 3105 3106 00000A42 0E push cs 3107 00000A43 1F pop ds 3108 00000A44 0E push cs 3109 00000A45 07 pop es 3110 3111 00000A46 B9[130B] mov cx, .per_partition 3112 00000A49 E8CD01 call scan_partitions 3113 3114 00000A4C BF[B00D] mov di, partition_offset 3115 00000A4F 8B5502 mov dx, word [di + 2] 3116 00000A52 8B05 mov ax, word [di] 3117 3118 00000A54 83F8FF cmp ax, -1 3119 00000A57 752B jne @F 3120 00000A59 83FAFF cmp dx, -1 3121 00000A5C 7526 jne @F 3122 3123 00000A5E 16 push ss 3124 00000A5F 07 pop es 3125 00000A60 89EF mov di, bp 3126 00000A62 31C0 xor ax, ax 3127 00000A64 B91001 mov cx, 512 / 2 + (((ebpbNew - bpbNew + 15) & ~15) >> 1) 3128 00000A67 FF7640 push word [bp + bsBPB + ebpbNew + bpbnBootUnit] 3129 00000A6A FF7618 push word [bp + bsBPB + bpbCHSSectors] 3130 00000A6D FF761A push word [bp + bsBPB + bpbCHSHeads] 3131 00000A70 F3AB rep stosw 3132 00000A72 8F461A pop word [bp + bsBPB + bpbCHSHeads] 3133 00000A75 8F4618 pop word [bp + bsBPB + bpbCHSSectors] 3134 00000A78 5B pop bx 3135 00000A79 885E40 mov byte [bp + bsBPB + ebpbNew + bpbnBootUnit], bl 3136 00000A7C 89561C mov word [bp + bsBPB + bpbHiddenSectors], dx 3137 00000A7F 89561E mov word [bp + bsBPB + bpbHiddenSectors + 2], dx 3138 3139 00000A82 EB7F jmp .invalid_return 3140 3141 3142 @@: 3143 00000A84 52 push dx 3144 00000A85 50 push ax 3145 00000A86 8B5EE4 mov bx, [bp + ldSectorSeg] 3146 00000A89 E87901 call read_ae_512_bytes 3147 00000A8C 06 push es 3148 00000A8D 1F pop ds 3149 00000A8E 31F6 xor si, si 3150 00000A90 16 push ss 3151 00000A91 07 pop es 3152 00000A92 89EF mov di, bp 3153 00000A94 B90001 mov cx, 512 / 2 3154 3155 00000A97 58 pop ax 3156 00000A98 5A pop dx 3157 3158 00000A99 FF7640 push word [bp + bsBPB + ebpbNew + bpbnBootUnit] 3159 00000A9C FF7618 push word [bp + bsBPB + bpbCHSSectors] 3160 00000A9F FF761A push word [bp + bsBPB + bpbCHSHeads] 3161 3162 3163 00000AA2 F3A5 rep movsw 3164 3165 00000AA4 50 push ax 3166 00000AA5 31C0 xor ax, ax 3167 00000AA7 B91000 mov cx, ((ebpbNew - bpbNew + 15) & ~15) >> 1 3168 00000AAA F3AB rep stosw ; initialise area behind sector (left so for FAT32) 3169 00000AAC 58 pop ax 3170 3171 3172 00000AAD 8F461A pop word [bp + bsBPB + bpbCHSHeads] 3173 00000AB0 8F4618 pop word [bp + bsBPB + bpbCHSSectors] 3174 00000AB3 89461C mov word [bp + bsBPB + bpbHiddenSectors], ax 3175 00000AB6 89561E mov word [bp + bsBPB + bpbHiddenSectors + 2], dx 3176 3177 3178 00000AB9 16 push ss 3179 00000ABA 1F pop ds 3180 00000ABB 16 push ss 3181 00000ABC 07 pop es 3182 3183 00000ABD 8B5E16 mov bx, [bp + bsBPB + bpbSectorsPerFAT] 3184 00000AC0 85DB test bx, bx 3185 00000AC2 741D jz .not_fat32 3186 3187 00000AC4 8DB6FE01 lea si, [bp + 510] ; -> last source word 3188 00000AC8 8D7C1C lea di, [si + (ebpbNew - bpbNew)] ; -> last dest word 3189 00000ACB B9EE00 mov cx, (512 - bsBPB - bpbNew + 1) >> 1 3190 ; move sector up, except common BPB start part 3191 %if ((512 - bsBPB - bpbNew + 1) >> 1) <= 20 3192 %fatal Need AMD erratum 109 workaround 3193 %endif 3194 00000ACE FD std ; AMD erratum 109 handling not needed 3195 00000ACF F3A5 rep movsw 3196 00000AD1 FC cld 3197 3198 00000AD2 894EF2 mov word [bp + lsvFirstCluster + 2], cx 3199 00000AD5 894EF6 mov word [bp + lsvFATSector + 2], cx 3200 3201 00000AD8 895E24 mov word [bp + bsBPB + ebpbSectorsPerFATLarge], bx 3202 00000ADB 894E26 mov word [bp + bsBPB + ebpbSectorsPerFATLarge + 2], cx 3203 00000ADE 894E28 mov word [bp + bsBPB + ebpbFSFlags], cx 3204 ; FSVersion, RootCluster, FSINFOSector, BackupSector, Reserved: 3205 ; uninitialised here (initialised by loaded_all later) 3206 .not_fat32: 3207 3208 00000AE1 5B pop bx 3209 00000AE2 885E40 mov byte [bp + bsBPB + ebpbNew + bpbnBootUnit], bl 3210 3211 00000AE5 B401 mov ah, lsvefNoDataStart 3212 00000AE7 2EA0[B70D] mov al, byte [cs:partition_type] 3213 00000AEB 3C01 cmp al, ptFAT12 3214 00000AED 741C je @F 3215 00000AEF 3C04 cmp al, ptFAT16_16BIT_CHS 3216 00000AF1 7418 je @F 3217 00000AF3 3C06 cmp al, ptFAT16_CHS 3218 00000AF5 7414 je @F 3219 00000AF7 3C0B cmp al, ptFAT32_CHS 3220 00000AF9 7410 je @F 3221 00000AFB 3C0C cmp al, ptFAT32 3222 00000AFD 740C je @F 3223 00000AFF 3C0E cmp al, ptFAT16 3224 00000B01 7408 je @F 3225 3226 .invalid_return: 3227 00000B03 31C0 xor ax, ax 3228 00000B05 8946FC mov word [bp + lsvDataStart], ax 3229 00000B08 8946FE mov word [bp + lsvDataStart + 2], ax 3230 @@: 3231 00000B0B 8866EF mov byte [bp + lsvExtra.flags], ah 3232 3233 %if _CHECKSUM 3234 push cs 3235 pop es 3236 mov di, scanparttab_variables_start 3237 mov cx, scanparttab_variables_length_w 3238 xor ax, ax 3239 rep stosw ; clear variables for eventual checksum 3240 dec ax 3241 mov di, partition_offset 3242 stosw 3243 stosw ; reset this variable too 3244 %endif 3245 3246 00000B0E 16 push ss 3247 00000B0F 07 pop es 3248 00000B10 16 push ss 3249 00000B11 1F pop ds 3250 00000B12 C3 retn 3251 3252 3253 .per_partition: 3254 00000B13 51 push cx 3255 00000B14 56 push si 3256 00000B15 57 push di 3257 00000B16 53 push bx 3258 3259 00000B17 268B4408 mov ax, [es:si + piStart] 3260 00000B1B 268B540A mov dx, [es:si + piStart + 2] 3261 00000B1F 360341F8 add ax, [ss:bx + di - 8] 3262 00000B23 361351FA adc dx, [ss:bx + di - 8 + 2] ; = partition start 3263 3264 00000B27 B9FFFF mov cx, -1 3265 00000B2A BF[B00D] mov di, partition_offset 3266 00000B2D 390D cmp word [di], cx ; first one encountered ? 3267 00000B2F 7511 jne @F 3268 00000B31 394D02 cmp word [di + 2], cx 3269 00000B34 750C jne @F ; no --> 3270 00000B36 268A4C04 mov cl, byte [es:si + piType] 3271 00000B3A 884D07 mov byte [di - partition_offset + partition_type], cl 3272 ; save type 3273 00000B3D 8905 mov word [di], ax 3274 00000B3F 895502 mov word [di + 2], dx ; yes, save offset 3275 @@: 3276 3277 00000B42 8A0E[B60D] mov cl, byte [load_current_partition] 3278 ; which one ? 3279 00000B46 3A4EEE cmp cl, byte [bp + lsvExtra.partition] 3280 00000B49 7511 jne @F ; not the sought one 3281 3282 00000B4B 268A4C04 mov cl, byte [es:si + piType] 3283 00000B4F 884D07 mov byte [di - partition_offset + partition_type], cl 3284 ; save type 3285 00000B52 8905 mov word [di], ax 3286 00000B54 895502 mov word [di + 2], dx ; save offset 3287 3288 00000B57 5B pop bx ; bx = base 3289 00000B58 89DC mov sp, bx ; reset sp 3290 00000B5A 58 pop ax ; pop dummy bp 3291 00000B5B C3 retn ; return to caller 3292 3293 @@: ; not yet found, continue 3294 00000B5C 5B pop bx 3295 00000B5D 5F pop di 3296 00000B5E 5E pop si 3297 00000B5F 59 pop cx 3298 00000B60 C3 retn 3299 3300 3301 .int13_retry: 3302 00000B61 9C pushf 3303 00000B62 50 push ax 3304 00000B63 CD13 int 13h ; first try 3305 00000B65 730B jnc @F ; NC, success on first attempt --> 3306 3307 ; reset drive 3308 00000B67 31C0 xor ax, ax 3309 00000B69 CD13 int 13h 3310 00000B6B 7205 jc @F ; CY, reset failed, error in ah --> 3311 3312 ; try read again 3313 00000B6D 58 pop ax ; restore function number 3314 00000B6E 9D popf 3315 00000B6F CD13 int 13h ; retry, CF error status, ah error number 3316 00000B71 C3 retn 3317 3318 @@: ; NC or CY, stack has function number 3319 00000B72 44 inc sp 3320 00000B73 44 inc sp 3321 00000B74 44 inc sp 3322 00000B75 44 inc sp ; discard two words on stack, preserve CF 3323 00000B76 C3 retn 3324 3325 3326 .access_error: 3327 00000B77 E9E2F5 jmp error_diskaccess 3328 3329 .sector_too_large: 3330 .sector_too_small: 3331 .sector_not_power: 3332 00000B7A E8D3F4 call error 3333 00000B7D 496E76616C69642073- db "Invalid sector size." 3333 00000B86 6563746F722073697A- 3333 00000B8F 652E 3334 3335 .invalid_sectors: 3336 .invalid_heads: 3337 00000B91 E8BCF4 call error 3338 00000B94 496E76616C69642067- db "Invalid geometry." 3338 00000B9D 656F6D657472792E 3339 3340 scan_logical.got_partition_cycle: 3341 00000BA5 E8A8F4 call error 3342 00000BA8 506172746974696F6E- db "Partition cycle detected." 3342 00000BB1 206379636C65206465- 3342 00000BBA 7465637465642E 3343 3344 scan_logical.error_too_many_partitions: 3345 00000BC1 E88CF4 call error 3346 00000BC4 546F6F206D616E7920- db "Too many partitions detected." 3346 00000BCD 706172746974696F6E- 3346 00000BD6 732064657465637465- 3346 00000BDF 642E 3347 3348 read_partition_table.signature_fail: 3349 00000BE1 E86CF4 call error 3350 00000BE4 496E76616C69642070- db "Invalid partition table detected." 3350 00000BED 6172746974696F6E20- 3350 00000BF6 7461626C6520646574- 3350 00000BFF 65637465642E 3351 3352 3353 ; INP: dx:ax = first sector 3354 ; bx:0 -> buffer 3355 ; OUT: dx:ax = sector number after last read 3356 ; es = input bx 3357 ; bx:0 -> buffer after last written 3358 ; CHG: - 3359 read_ae_512_bytes: 3360 00000C05 1E push ds 3361 00000C06 51 push cx 3362 00000C07 53 push bx 3363 00000C08 16 push ss 3364 00000C09 1F pop ds 3365 00000C0A B90002 mov cx, 512 3366 .loop: 3367 00000C0D E8C8F4 call read_sector 3368 00000C10 2B4E0B sub cx, word [bp + bsBPB + bpbBytesPerSector] 3369 00000C13 77F8 ja .loop 3370 00000C15 07 pop es 3371 00000C16 59 pop cx 3372 00000C17 1F pop ds 3373 00000C18 C3 retn 3374 3375 3376 %assign _PARTITION_TABLE_IN_CS 1 3377 %assign _BOOTCMD_FAIL_ERROR 0 3378 %define _SCANPTAB_PREFIX 3379 %define _SCANPTAB_DEBUG4_PREFIX 3380 overridedef DEBUG4, 0 3381 %include "scanptab.asm" 1 <1> 2 <1> %if 0 3 <1> 4 <1> scanptab - Scan partition table 5 <1> 2019, 2025 by E. C. Masloch 6 <1> 7 <1> Usage of the works is permitted provided that this 8 <1> instrument is retained with the works, so that any entity 9 <1> that uses the works is notified of this instrument. 10 <1> 11 <1> DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. 12 <1> 13 <1> %endif 14 <1> 15 <1> ; _SCANPTAB_PREFIX = prefix for our variables and functions 16 <1> ; _SCANPTAB_DEBUG4_PREFIX = prefix for d4 function calls 17 <1> numdef PARTITION_TABLE_IN_CS, 1 18 <1> numdef BOOTCMD_FAIL_ERROR, 1 19 <1> numdef SPT_ELD, 0 20 <1> numdef SPT_MULTIPASS, 0 21 <1> %if _SPT_ELD 22 <1> %define sptrel reloc 23 <1> %define sptinternal internaldatarelocation 24 <1> %define sptexternal linkdatarelocation 25 <1> numdef MODE, 1 26 <1> %else 27 <1> %define sptrel 28 <1> %define sptinternal comment 29 <1> %define sptexternal comment 30 <1> numdef MODE, 0 31 <1> %endif 32 <1> gendef BASE, ss:bx 33 <1> %ifidn _BASE, ss:bx 34 <1> %elifidn _BASE, bp 35 <1> %else 36 <1> %error Invalid base 37 <1> %endif 38 <1> 39 <1> %if _MODE 40 <1> %include "scanptab.mac" 41 <1> %endif 42 <1> 43 <1> 44 <1> ; INP: byte [%load_unit] = unit to scan 45 <1> ; cx = function to call for each partition 46 <1> ; OUT: function called for each partition 47 <1> ; CHG: di, si, ax, bx, (cx), dx, es 48 <1> ; STT: ds => data segment (used for %load_* variables, unless bp-based) 49 <1> ; 50 <1> ; Note: Calls %read_partition_table (in this module), 51 <1> ; which calls %read_ae_512_bytes, which calls 52 <1> ; read_sector. The read_sector base must be 53 <1> ; zero and the geometry must be initialised. 54 <1> 55 <1> ; Function in cx is called with: 56 <1> ; INP: es:si -> partition table entry, 57 <1> ; si = %partition_table .. %partition_table+48, 58 <1> ; es = ss (if not _PARTITION_TABLE_IN_CS) 59 <1> ; es = cs (if _PARTITION_TABLE_IN_CS) 60 <1> ; byte [%load_current_partition] = partition number, 61 <1> ; 0 for diskette (unpartitioned), 62 <1> ; 1 to 4 for primary partitions, 63 <1> ; 5+ for logical partitions 64 <1> ; _BASE + di -> above part table metadata, 65 <1> ; dword [_BASE + di - 4] = root (outermost extended position) 66 <1> ; dword [_BASE + di - 8] = base (current table position) 67 <1> ; dword [es:si + piStart] = local partition start 68 <1> ; base + local start = absolute partition start 69 <1> ; _BASE -> bp value (dummy if _BASE is not bp), 70 <1> ; near return address of scan_partitions 71 <1> ; CHG: ax, (cx), dx, bx (unless part of _BASE) 72 <1> ; STT: ds => data segment (used for load_* variables, unless bp-based) 73 <1> ; Note: preserves (cx), si, di, ds, es, (bp), _BASE 74 <1> ; 75 <1> ; Note: If the function returns to scan_partitions, 76 <1> ; cx must be left as the function to call for 77 <1> ; subsequent partitions afterwards. 78 <1> ; Note: May load sp from _BASE then pop bp then return 79 <1> ; near if the scan should stop after the call. 80 <1> %if _SPT_MULTIPASS 81 <1> %[_SCANPTAB_PREFIX]scan_priority_primary: 82 <1> %else 83 <1> %[_SCANPTAB_PREFIX]scan_partitions: 84 <1> %endif 85 00000C19 8326[B40D]00 <1> sptrel and word [ %[_SCANPTAB_PREFIX]load_partition_cycle], 0 86 <1> sptinternal -3 87 <1> d4 call %[_SCANPTAB_DEBUG4_PREFIX]d4message 88 <1> d4 asciz "In scan_partitions",13,10 89 <1> 90 00000C1E 55 <1> push bp 91 <1> %ifidn _BASE, ss:bx 92 00000C1F 89E3 <1> mov bx, sp 93 <1> %elifidn _BASE, bp 94 <1> mov bp, sp 95 <1> %endif 96 00000C21 31FF <1> xor di, di 97 00000C23 57 <1> push di ; [_BASE+di-2] 98 00000C24 57 <1> push di ; [_BASE+di-4] 99 00000C25 57 <1> push di ; [_BASE+di-6] 100 00000C26 57 <1> push di ; [_BASE+di-8] 101 <1> %if _MODE 102 <1> sptrel mov word [ %[_SCANPTAB_PREFIX]load_scan_flags ], di 103 <1> sptinternal 104 <1> %endif 105 <1> 106 00000C27 807E4080 <1> sptrel cmp byte [ %[_SCANPTAB_PREFIX]load_unit], 80h 107 <1> sptinternal -3 108 00000C2B 7325 <1> jae @F 109 <1> 110 00000C2D 57 <1> push di 111 00000C2E 51 <1> push cx 112 <1> %if _PARTITION_TABLE_IN_CS 113 00000C2F 0E <1> push cs 114 <1> %else 115 <1> push ss 116 <1> %endif 117 00000C30 07 <1> pop es 118 00000C31 BF[700D] <1> sptrel mov di, %[_SCANPTAB_PREFIX]partition_table 119 <1> sptinternal 120 00000C34 31C0 <1> xor ax, ax 121 00000C36 B92000 <1> mov cx, (4 * 16) >> 1 122 00000C39 F3AB <1> rep stosw ; initialise fake partition table 123 00000C3B 59 <1> pop cx 124 00000C3C 5F <1> pop di 125 00000C3D BE[700D] <1> sptrel mov si, %[_SCANPTAB_PREFIX]partition_table 126 <1> sptinternal 127 <1> ; -> fake PARTINFO 128 00000C40 26C60480 <1> mov byte [es:si + piBoot], 80h ; fake primary active 129 00000C44 26C64404FF <1> mov byte [es:si + piType], 0FFh ; fake a type 130 00000C49 C606[B60D]00 <1> sptrel mov byte [ %[_SCANPTAB_PREFIX]load_current_partition], 0 ; special: 0 = diskette 131 <1> sptinternal -3 ; piLength is 0, too 132 00000C4E FFD1 <1> call cx 133 <1> 134 00000C50 EB68 <1> jmp %[_SCANPTAB_PREFIX]scan_logical.end 135 <1> 136 <1> 137 <1> @@: 138 <1> %if _SPT_MULTIPASS 139 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_mode ], mode_active_priority 140 <1> sptinternal -3 ; active primary first ? 141 <1> jz .end 142 <1> %endif 143 <1> 144 00000C52 31C0 <1> xor ax, ax 145 00000C54 31D2 <1> xor dx, dx 146 <1> d4 call %[_SCANPTAB_DEBUG4_PREFIX]d4message 147 <1> d4 asciz "In scan_partitions (before first call to read_partition_table)",13,10 148 00000C56 E8E000 <1> call %[_SCANPTAB_PREFIX]read_partition_table.first 149 <1> d4 call %[_SCANPTAB_DEBUG4_PREFIX]d4message 150 <1> d4 asciz "In scan_partitions (after first call to read_partition_table)",13,10 151 00000C59 BE[700D] <1> sptrel mov si, %[_SCANPTAB_PREFIX]partition_table 152 <1> sptinternal 153 00000C5C 8816[B60D] <1> sptrel mov byte [ %[_SCANPTAB_PREFIX]load_current_partition], dl ; = 0 154 <1> sptinternal 155 <1> %if _SPT_ELD 156 <1> sptrel mov word [ %[_SCANPTAB_PREFIX]load_current_extended_and_is], dx ; = 0 157 <1> sptinternal 158 <1> %endif 159 <1> 160 <1> %if _MODE 161 <1> %ifn _SPT_MULTIPASS 162 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_mode ], mode_active_priority 163 <1> sptinternal -3 ; active primary first ? 164 <1> jz .active_ignored 165 <1> %endif 166 <1> 167 <1> .loop_active_primary: 168 <1> 169 <1> sptrel inc byte [ %[_SCANPTAB_PREFIX]load_current_partition] 170 <1> sptinternal 171 <1> mov al, byte [es:si + piType] 172 <1> cmp al, 0 173 <1> je .loop_active_primary_skip 174 <1> cmp byte [es:si + piBoot], 0 175 <1> je .loop_active_primary_skip 176 <1> 177 <1> ; INP: al = partition type 178 <1> ; OUT: CY if an LBA or FAT32 type that's disabled 179 <1> ; NC, ZR if known type we can access 180 <1> ; NC, NZ if unknown type 181 <1> call %[_SCANPTAB_PREFIX]load_is_known 182 <1> jc .loop_active_primary_skip 183 <1> jnz .loop_active_primary_skip 184 <1> 185 <1> call cx ; es:si -> partition table entry 186 <1> ; byte [load_current_partition] = which 187 <1> jmp .active_done 188 <1> 189 <1> .loop_active_primary_skip: 190 <1> add si, 16 191 <1> sptrel cmp si, strict word %[_SCANPTAB_PREFIX]partition_table.end 192 <1> sptinternal 193 <1> jb .loop_active_primary 194 <1> 195 <1> .active_done: 196 <1> %if _SPT_MULTIPASS 197 <1> .end: 198 <1> %ifidn _BASE, bp 199 <1> mov sp, bp ; restore sp 200 <1> pop bp 201 <1> %else 202 <1> mov sp, bx ; restore sp 203 <1> pop ax ; (discard dummy bp value) 204 <1> %endif 205 <1> retn ; and bye 206 <1> 207 <1> 208 <1> %[_SCANPTAB_PREFIX]scan_early_primary: 209 <1> sptrel and word [ %[_SCANPTAB_PREFIX]load_partition_cycle], 0 210 <1> sptinternal -3 211 <1> 212 <1> push bp 213 <1> %ifidn _BASE, ss:bx 214 <1> mov bx, sp 215 <1> %elifidn _BASE, bp 216 <1> mov bp, sp 217 <1> %endif 218 <1> xor di, di 219 <1> push di ; [_BASE+di-2] 220 <1> push di ; [_BASE+di-4] 221 <1> push di ; [_BASE+di-6] 222 <1> push di ; [_BASE+di-8] 223 <1> xor ax, ax 224 <1> xor dx, dx 225 <1> 226 <1> %if _MODE 227 <1> sptrel mov word [ %[_SCANPTAB_PREFIX]load_scan_flags ], ax 228 <1> sptinternal 229 <1> %endif 230 <1> 231 <1> call %[_SCANPTAB_PREFIX]read_partition_table.first 232 <1> sptrel mov byte [ %[_SCANPTAB_PREFIX]load_current_partition], dl ; = 0 233 <1> sptinternal 234 <1> %if _SPT_ELD 235 <1> sptrel mov word [ %[_SCANPTAB_PREFIX]load_current_extended_and_is], dx ; = 0 236 <1> sptinternal 237 <1> %endif 238 <1> %else 239 <1> sptrel mov byte [ %[_SCANPTAB_PREFIX]load_current_partition], 0 240 <1> sptinternal -3 241 <1> %endif 242 <1> sptrel mov si, %[_SCANPTAB_PREFIX]partition_table 243 <1> sptinternal 244 <1> 245 <1> 246 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_mode ], mode_active_priority 247 <1> sptinternal -3 ; active primary first ? 248 <1> jz .active_ignored 249 <1> 250 <1> .loop_active_skip: 251 <1> 252 <1> sptrel inc byte [ %[_SCANPTAB_PREFIX]load_current_partition] 253 <1> sptinternal 254 <1> mov al, byte [es:si + piType] 255 <1> cmp al, 0 256 <1> je .loop_active_skip_skip 257 <1> cmp byte [es:si + piBoot], 0 258 <1> je .loop_active_skip_skip 259 <1> 260 <1> ; INP: al = partition type 261 <1> ; OUT: CY if an LBA or FAT32 type that's disabled 262 <1> ; NC, ZR if known type we can access 263 <1> ; NC, NZ if unknown type 264 <1> call %[_SCANPTAB_PREFIX]load_is_known 265 <1> jc .loop_active_skip_skip 266 <1> jnz .loop_active_skip_skip 267 <1> sptrel mov al, byte [ %[_SCANPTAB_PREFIX]load_current_partition] 268 <1> sptinternal 269 <1> sptrel mov byte [ %[_SCANPTAB_PREFIX]load_scan_flags + 1], al 270 <1> sptinternal 271 <1> sptrel setopt [ %[_SCANPTAB_PREFIX]load_scan_flags ], 32 272 <1> sptinternal -3 ; mark as had a primary partition yet 273 <1> jmp .active_skip_done 274 <1> 275 <1> .loop_active_skip_skip: 276 <1> add si, 16 277 <1> sptrel cmp si, strict word %[_SCANPTAB_PREFIX]partition_table.end 278 <1> sptinternal 279 <1> jb .loop_active_skip 280 <1> 281 <1> .active_skip_done: 282 <1> sptrel mov si, %[_SCANPTAB_PREFIX]partition_table 283 <1> sptinternal 284 <1> sptrel mov byte [ %[_SCANPTAB_PREFIX]load_current_partition], 0 285 <1> sptinternal -3 286 <1> 287 <1> %endif 288 <1> 289 <1> .active_ignored: 290 <1> .loop_primary_parts: 291 00000C60 FE06[B60D] <1> sptrel inc byte [ %[_SCANPTAB_PREFIX]load_current_partition] 292 <1> sptinternal 293 00000C64 26807C0400 <1> cmp byte [es:si + piType], 0 294 00000C69 7402 <1> je .loop_primary_skip 295 <1> 296 <1> %if _MODE 297 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_mode ], mode_active_priority 298 <1> sptinternal -3 ; active primary first ? 299 <1> jz @F ; no --> 300 <1> sptrel mov al, byte [ %[_SCANPTAB_PREFIX]load_current_partition] 301 <1> sptinternal 302 <1> sptrel cmp byte [ %[_SCANPTAB_PREFIX]load_scan_flags + 1], al 303 <1> sptinternal 304 <1> je .ignoreprimary 305 <1> @@: 306 <1> 307 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_scan_flags ], 32 308 <1> sptinternal -3 ; had a primary partition yet ? 309 <1> jz .callprimary ; no, accept it --> 310 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_mode ], mode_ignore_subsequent_primary 311 <1> sptinternal -3 ; ignore subsequent primary ? 312 <1> jnz .ignoreprimary ; yes, ignore it --> 313 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_mode ], mode_subsequent_primary_after 314 <1> sptinternal -3 ; subsequent after ? 315 <1> jnz .ignoreprimary 316 <1> .callprimary: 317 <1> %endif 318 00000C6B FFD1 <1> call cx ; es:si -> partition table entry 319 <1> ; byte [load_current_partition] = which 320 <1> .ignoreprimary: 321 <1> .loop_primary_skip: 322 00000C6D 83C610 <1> add si, 16 323 00000C70 81FE[B00D] <1> sptrel cmp si, strict word %[_SCANPTAB_PREFIX]partition_table.end 324 <1> sptinternal 325 00000C74 72EA <1> jb .loop_primary_parts 326 <1> 327 <1> %if _SPT_MULTIPASS 328 <1> .end: 329 <1> %ifidn _BASE, bp 330 <1> mov sp, bp ; restore sp 331 <1> pop bp 332 <1> %else 333 <1> mov sp, bx ; restore sp 334 <1> pop ax ; (discard dummy bp value) 335 <1> %endif 336 <1> retn ; and bye 337 <1> 338 <1> 339 <1> %[_SCANPTAB_PREFIX]scan_logical: 340 <1> push bp 341 <1> %ifidn _BASE, ss:bx 342 <1> mov bx, sp 343 <1> %elifidn _BASE, bp 344 <1> mov bp, sp 345 <1> %endif 346 <1> xor di, di 347 <1> push di ; [_BASE+di-2] 348 <1> push di ; [_BASE+di-4] 349 <1> push di ; [_BASE+di-6] 350 <1> push di ; [_BASE+di-8] 351 <1> 352 <1> xor ax, ax 353 <1> xor dx, dx 354 <1> %if _MODE 355 <1> sptrel mov word [ %[_SCANPTAB_PREFIX]load_scan_flags ], ax 356 <1> sptinternal 357 <1> %endif 358 <1> sptrel mov word [ %[_SCANPTAB_PREFIX]load_partition_cycle], ax 359 <1> sptinternal 360 <1> call %[_SCANPTAB_PREFIX]read_partition_table.first 361 <1> sptrel mov byte [ %[_SCANPTAB_PREFIX]load_current_partition], 4 362 <1> sptinternal -3 363 <1> %if _SPT_ELD 364 <1> sptrel mov word [ %[_SCANPTAB_PREFIX]load_current_extended_and_is], dx ; = 0 365 <1> sptinternal 366 <1> %endif 367 <1> %else 368 <1> 369 <1> %[_SCANPTAB_PREFIX]scan_logical: 370 <1> %endif 371 <1> .: 372 <1> d4 call %[_SCANPTAB_DEBUG4_PREFIX]d4message 373 <1> d4 asciz "In scan_logical.",13,10 374 <1> 375 <1> %if _MODE 376 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_mode ], mode_traditional_order_logical 377 <1> sptinternal -3 378 <1> jz .nottraditional 379 <1> 380 <1> sptrel mov si, %[_SCANPTAB_PREFIX]partition_table 381 <1> sptinternal 382 <1> .loop_traditional: 383 <1> sptrel inc word [ %[_SCANPTAB_PREFIX]load_partition_cycle] 384 <1> sptinternal 385 <1> jz .got_partition_cycle 386 <1> 387 <1> mov al, [es:si + piType] 388 <1> 389 <1> %ifidn _BASE, bp 390 <1> xor bx, bx 391 <1> %endif 392 <1> test al, al 393 <1> jz .next_traditional 394 <1> cmp al, 0Fh ; extended partition (LBA aware) ? 395 <1> je .next_traditional ; yes --> 396 <1> and al, ~80h ; extended partition Linux (85h) ? 397 <1> cmp al, 05h ; or extended partition DOS (05h) ? 398 <1> je .next_traditional ; yes --> 399 <1> 400 <1> %ifidn _BASE, bp 401 <1> cmp word [_BASE+di-2], bx 402 <1> jne .logical_traditional 403 <1> cmp word [_BASE+di-4], bx 404 <1> %else 405 <1> cmp word [_BASE+di-2], 0 406 <1> jne .logical_traditional 407 <1> cmp word [_BASE+di-4], 0 408 <1> %endif 409 <1> je .next_traditional 410 <1> .logical_traditional: 411 <1> 412 <1> %if _MODE 413 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_scan_flags ], 32 414 <1> sptinternal -3 ; had a logical partition yet ? 415 <1> jz .calllogicaltrad ; no, accept it --> 416 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_mode ], mode_ignore_subsequent_logical 417 <1> sptinternal -3 ; ignore subsequent logical ? 418 <1> jnz .ignorelogicaltrad ; yes, ignore it --> 419 <1> .calllogicaltrad: 420 <1> %endif 421 <1> 422 <1> sptrel inc byte [ %[_SCANPTAB_PREFIX]load_current_partition] 423 <1> sptinternal 424 <1> jz .error_too_many_partitions 425 <1> call cx ; (sets load_scan_flags & 32 if recognised) 426 <1> ; CHG: ax, (cx), dx, bx if _BASE = bp 427 <1> ; preserve: (cx), si, di, ds, es, bx if _BASE = ss:bx 428 <1> .ignorelogicaltrad: 429 <1> .next_traditional: 430 <1> add si, 16 ; -> next partition table entry 431 <1> sptrel cmp si, strict word %[_SCANPTAB_PREFIX]partition_table.end 432 <1> sptinternal ; was last? 433 <1> jb .loop_traditional ; no, loop --> 434 <1> 435 <1> sptrel setopt [ %[_SCANPTAB_PREFIX]load_scan_flags ], 32 436 <1> sptinternal -3 ; mark as had logical 437 <1> 438 <1> .nottraditional: 439 <1> %endif 440 <1> 441 00000C76 BE[700D] <1> sptrel mov si, %[_SCANPTAB_PREFIX]partition_table 442 <1> sptinternal 443 <1> .loop: 444 00000C79 FF06[B40D] <1> sptrel inc word [ %[_SCANPTAB_PREFIX]load_partition_cycle] 445 <1> sptinternal 446 00000C7D 7503E923FF <1> jz .got_partition_cycle 447 <1> 448 00000C82 268A4404 <1> mov al, [es:si + piType] 449 <1> 450 <1> %ifidn _BASE, bp 451 <1> xor bx, bx 452 <1> %endif 453 00000C86 84C0 <1> test al, al 454 00000C88 7423 <1> jz .next 455 <1> %if _MODE 456 <1> %ifidn _BASE, bp 457 <1> cmp word [_BASE+di-2], bx 458 <1> jne .allextended 459 <1> cmp word [_BASE+di-4], bx 460 <1> %else 461 <1> cmp word [_BASE+di-2], 0 462 <1> jne .allextended 463 <1> cmp word [_BASE+di-4], 0 464 <1> %endif 465 <1> jne .allextended 466 <1> 467 <1> sptrel mov ah, [ %[_SCANPTAB_PREFIX]load_mode ] 468 <1> sptinternal 469 <1> cmp al, 5 470 <1> je .check 471 <1> shr ah, 1 472 <1> shr ah, 1 473 <1> cmp al, 15 474 <1> je .check 475 <1> shr ah, 1 476 <1> shr ah, 1 477 <1> cmp al, 85h 478 <1> jne .notextended 479 <1> .check: 480 <1> sptrel clropt [ %[_SCANPTAB_PREFIX]load_scan_flags ], 16 481 <1> sptinternal -3 ; new outermost extended 482 <1> 483 <1> test ah, 11b 484 <1> jz .push ; if it is 00b = all, always accept --> 485 <1> test ah, 10b ; if prior is NZ but this is zero 486 <1> jz .ignoreextended ; then we have 01b, always ignore --> 487 <1> test ah, 01b ; if prior is NZ and this is NZ, 488 <1> ; jnz .firsttype ; then we have 11b, accept first of type 489 <1> mov ah, 1 ; flag 1 for 10b 490 <1> jz @F ; if ZR, we have 10b, accept first of all types with 10b 491 <1> .firsttype: 492 <1> mov ah, 2 ; flag 2 for 11b type 5 493 <1> cmp al, 15 494 <1> jb @F 495 <1> mov ah, 4 ; flag 4 for 11b type 15 496 <1> je @F 497 <1> mov ah, 8 ; flag 8 for 11b type 85h 498 <1> @@: 499 <1> sptrel test byte [ %[_SCANPTAB_PREFIX]load_scan_flags ], ah 500 <1> sptinternal ; seen one of those before ? 501 <1> jnz .ignoreextended ; yes, ignore --> 502 <1> sptrel or byte [ %[_SCANPTAB_PREFIX]load_scan_flags ], ah 503 <1> sptinternal ; no, note seen it now 504 <1> jmp .push ; and process extended partition --> 505 <1> %endif 506 <1> 507 <1> .allextended: 508 <1> %if _MODE 509 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_mode ], mode_ignore_subsequent_nested 510 <1> sptinternal -3 511 <1> jz @F 512 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_scan_flags ], 16 513 <1> sptinternal -3 514 <1> jnz .ignoreextended 515 <1> @@: 516 <1> %endif 517 00000C8A 3C0F <1> cmp al, 0Fh ; extended partition (LBA aware) ? 518 00000C8C 7430 <1> je .push ; yes --> 519 00000C8E 247F <1> and al, ~80h ; extended partition Linux (85h) ? 520 00000C90 3C05 <1> cmp al, 05h ; or extended partition DOS (05h) ? 521 00000C92 742A <1> je .push ; yes --> 522 <1> .notextended: 523 <1> 524 <1> %ifidn _BASE, bp 525 <1> cmp word [_BASE+di-2], bx 526 <1> jne .logical 527 <1> cmp word [_BASE+di-4], bx 528 <1> %else 529 00000C94 368379FE00 <1> cmp word [_BASE+di-2], 0 530 00000C99 7507 <1> jne .logical 531 00000C9B 368379FC00 <1> cmp word [_BASE+di-4], 0 532 <1> %endif 533 00000CA0 740B <1> je .next 534 <1> .logical: 535 <1> 536 <1> %if _MODE 537 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_scan_flags ], 32 538 <1> sptinternal -3 ; had a logical partition yet ? 539 <1> jz .calllogical ; no, accept it --> 540 <1> sptrel test word [ %[_SCANPTAB_PREFIX]load_mode ], strict word mode_ignore_subsequent_logical | mode_traditional_order_logical 542 <1> sptinternal -4 ; ignore subsequent logical or post-trad run ? 543 <1> jnz .ignorelogical ; yes, ignore it --> 544 <1> .calllogical: 545 <1> %endif 546 <1> 547 00000CA2 FE06[B60D] <1> sptrel inc byte [ %[_SCANPTAB_PREFIX]load_current_partition] 548 <1> sptinternal 549 00000CA6 7503E916FF <1> jz .error_too_many_partitions 550 00000CAB FFD1 <1> call cx ; (sets load_scan_flags & 32 if recognised) 551 <1> ; CHG: ax, (cx), dx, bx if _BASE = bp 552 <1> ; preserve: (cx), si, di, ds, es, bx if _BASE = ss:bx 553 <1> .ignorelogical: 554 <1> .ignoreextended: 555 <1> .next: 556 00000CAD 83C610 <1> add si, 16 ; -> next partition table entry 557 00000CB0 81FE[B00D] <1> sptrel cmp si, strict word %[_SCANPTAB_PREFIX]partition_table.end 558 <1> sptinternal ; was last? 559 00000CB4 72C3 <1> jb .loop ; no, loop --> 560 00000CB6 85FF <1> test di, di ; still some on stack? 561 00000CB8 7550 <1> jnz .pop ; yes, pop 562 <1> .end: 563 <1> %ifidn _BASE, bp 564 <1> mov sp, bp ; restore sp 565 <1> pop bp 566 <1> %else 567 00000CBA 89DC <1> mov sp, bx ; restore sp 568 00000CBC 58 <1> pop ax ; (discard dummy bp value) 569 <1> %endif 570 <1> %if _MODE && ! _SPT_MULTIPASS 571 <1> jmp scan_subsequent_primary 572 <1> %else 573 00000CBD C3 <1> retn ; and bye 574 <1> %endif 575 <1> 576 <1> 577 <1> .push: 578 <1> d4 call %[_SCANPTAB_DEBUG4_PREFIX]d4message 579 <1> d4 asciz "In scan_logical.push",13,10 580 <1> 581 <1> %if _SPT_ELD 582 <1> sptrel rol byte [ %[_SCANPTAB_PREFIX]extended], 1 583 <1> sptinternal 584 <1> jnc @F 585 <1> sptrel not byte [ %[_SCANPTAB_PREFIX]load_is_extended] 586 <1> sptinternal 587 <1> 588 <1> push si 589 <1> sub di, 10 ; address temporary root and base 590 <1> push word [_BASE+di+10-2] 591 <1> push word [_BASE+di+10-4] ; copy root as root 592 <1> push word [_BASE+di+10-2] 593 <1> push word [_BASE+di+10-4] ; copy root as base 594 <1> ; Extended partitions always have LBA start values 595 <1> ; relative to the outermost extended partition. 596 <1> ; Therefore the "root" must be used as base for 597 <1> ; this call to always get the expected address 598 <1> ; from dword [bp - di - 8] + dword [es:si + 8]. 599 <1> 600 <1> call cx 601 <1> 602 <1> add sp, 8 ; discard temporary root and base 603 <1> add di, 10 ; point back to prior root and base 604 <1> pop si ; undo last part of the stack frame 605 <1> 606 <1> sptrel not byte [ %[_SCANPTAB_PREFIX]load_is_extended] 607 <1> sptinternal 608 <1> sptrel inc byte [ %[_SCANPTAB_PREFIX]load_current_extended] 609 <1> sptinternal 610 <1> %ifidn _BASE, bp 611 <1> xor bx, bx 612 <1> %endif 613 <1> @@: 614 <1> %endif 615 <1> 616 00000CBE 56 <1> push si 617 <1> .push_check_empty_next: 618 00000CBF 83C610 <1> add si, 16 ; -> next 619 00000CC2 81FE[B00D] <1> sptrel cmp si, strict word %[_SCANPTAB_PREFIX]partition_table.end 620 <1> sptinternal ; at end? 621 00000CC6 7356 <1> jae .replace ; yes, no other partitions found, replace --> 622 00000CC8 26807C0400 <1> cmp byte [es:si + piType], 0 ; is this a partition? 623 00000CCD 74F0 <1> je .push_check_empty_next ; no, check next --> 624 <1> ; found a partition after this, do push 625 <1> ; (possibly logical or another extended) 626 <1> .push_check_is_not_empty: 627 <1> d4 call %[_SCANPTAB_DEBUG4_PREFIX]d4message 628 <1> d4 asciz "In scan_logical.push_check_is_not_empty",13,10 629 <1> 630 <1> %if _MODE 631 <1> xor ax, ax ; sentinel 0 = accept more logicals 632 <1> 633 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_mode ], mode_traditional_order_logical 634 <1> sptinternal -3 635 <1> jnz .traditionalsentinel 636 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_scan_flags ], 32 637 <1> sptinternal -3 ; had a logical partition in this table ? 638 <1> jz @F ; no, no need to reset --> 639 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_mode ], mode_ignore_subsequent_logical 640 <1> sptinternal -3 ; ignore subsequent logical ? 641 <1> jz @F ; no --> 642 <1> .traditionalsentinel: 643 <1> mov al, 64 ; sentinel 64 = no more logicals 644 <1> @@: 645 <1> sptrel clropt [ %[_SCANPTAB_PREFIX]load_scan_flags ], 32 646 <1> sptinternal -3 ; accept a logical partition in next table 647 <1> %endif 648 <1> 649 00000CCF 5E <1> pop si ; restore -> partition table entry 650 <1> %if _MODE 651 <1> push word [es:si + piStart] 652 <1> mov dx, word [es:si + piStart + 2] ; get extended partition offset 653 <1> add si, ax ; now -> behind partition_table if sentinel set 654 <1> pop ax 655 <1> %endif 656 00000CD0 56 <1> push si ; stored at word [_BASE+di-10] 657 00000CD1 83EF0A <1> sub di, 10 658 00000CD4 36FF7108 <1> push word [_BASE+di+10-2] 659 00000CD8 36FF7106 <1> push word [_BASE+di+10-4] ; copy root 660 <1> %ifn _MODE 661 00000CDC 268B4408 <1> mov ax, word [es:si + piStart] 662 00000CE0 268B540A <1> mov dx, word [es:si + piStart + 2] ; get extended partition offset 663 <1> %endif 664 00000CE4 360341FC <1> add ax, word [_BASE+di-4] 665 00000CE8 361351FE <1> adc dx, word [_BASE+di-2] ; add in root to get absolute sector number 666 <1> 667 00000CEC 52 <1> push dx 668 00000CED 50 <1> push ax ; new base 669 <1> 670 <1> .replace_common: 671 <1> %ifidn _BASE, bp 672 <1> cmp word [_BASE+di-2], bx ; have a (nonzero) root? 673 <1> jne .have_root 674 <1> cmp word [_BASE+di-4], bx 675 <1> %else 676 00000CEE 368379FE00 <1> cmp word [_BASE+di-2], 0 ; have a (nonzero) root? 677 00000CF3 750F <1> jne .have_root 678 00000CF5 368379FC00 <1> cmp word [_BASE+di-4], 0 679 <1> %endif 680 00000CFA 7508 <1> jne .have_root ; yes --> 681 <1> 682 00000CFC 368951FE <1> mov word [_BASE+di-2], dx 683 00000D00 368941FC <1> mov word [_BASE+di-4], ax ; set root 684 <1> .have_root: 685 <1> 686 00000D04 E83200 <1> call %[_SCANPTAB_PREFIX]read_partition_table.first 687 00000D07 E96CFF <1> jmp . 688 <1> 689 <1> .pop: 690 <1> d4 call %[_SCANPTAB_DEBUG4_PREFIX]d4message 691 <1> d4 asciz "In scan_logical.pop",13,10 692 <1> 693 <1> %if _MODE 694 <1> sptrel setopt [ %[_SCANPTAB_PREFIX]load_scan_flags ], 16 695 <1> sptinternal -3 ; remember we found a nested extended partition 696 <1> ; so we can ignore others in the same outermost 697 <1> %endif 698 <1> 699 00000D0A 83C70A <1> add di, 10 700 00000D0D 83C408 <1> add sp, 8 701 00000D10 5E <1> pop si 702 <1> 703 <1> %if _MODE 704 <1> sptrel clropt [ %[_SCANPTAB_PREFIX]load_scan_flags ], 32 705 <1> sptinternal -3 ; accept a logical partition in this table 706 <1> sptrel cmp si, 64 + %[_SCANPTAB_PREFIX]partition_table 707 <1> sptinternal ; sentinel set ? 708 <1> jb @F ; no --> 709 <1> sptrel setopt [ %[_SCANPTAB_PREFIX]load_scan_flags ], 32 710 <1> sptinternal -3 ; do not accept a logical partition in this table 711 <1> sub si, 64 ; undo sentinel addition 712 <1> @@: 713 <1> %endif 714 00000D11 368B41F8 <1> mov ax, word [_BASE+di-8] 715 00000D15 368B51FA <1> mov dx, word [_BASE+di-6] 716 00000D19 E81D00 <1> call %[_SCANPTAB_PREFIX]read_partition_table.reload 717 00000D1C EB8F <1> jmp .next 718 <1> 719 <1> .replace: 720 <1> d4 call %[_SCANPTAB_DEBUG4_PREFIX]d4message 721 <1> d4 asciz "In scan_logical.replace",13,10 722 <1> %if _MODE 723 <1> sptrel clropt [ %[_SCANPTAB_PREFIX]load_scan_flags ], 32 724 <1> sptinternal -3 ; accept a logical partition in next table 725 <1> %endif 726 <1> 727 00000D1E 5E <1> pop si ; (discard) 728 <1> .replace_no_pop: 729 00000D1F 268B4408 <1> mov ax, word [es:si + piStart] 730 00000D23 268B540A <1> mov dx, word [es:si + piStart + 2] ; get extended partition offset 731 00000D27 360341FC <1> add ax, word [_BASE+di - 4] 732 00000D2B 361351FE <1> adc dx, word [_BASE+di - 2] ; add in root 733 00000D2F 368941F8 <1> mov word [_BASE+di - 8], ax 734 00000D33 368951FA <1> mov word [_BASE+di - 6], dx ; set base 735 <1> 736 00000D37 EBB5 <1> jmp .replace_common 737 <1> 738 <1> 739 <1> %if _BOOTCMD_FAIL_ERROR 740 <1> .got_partition_cycle: 741 <1> sptrel mov dx, msg.boot_partition_cycle_error 742 <1> sptinternal 743 <1> jmp bootcmd.fail 744 <1> 745 <1> .error_too_many_partitions: 746 <1> sptrel mov dx, msg.boot_too_many_partitions_error 747 <1> sptinternal 748 <1> jmp bootcmd.fail 749 <1> %endif 750 <1> 751 <1> 752 <1> %if _MODE 753 <1> @@: 754 <1> retn 755 <1> 756 <1> scan_subsequent_primary: 757 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_mode ], mode_ignore_subsequent_primary 758 <1> sptinternal -3 ; ignore subsequent primary ? 759 <1> jnz @B ; yes --> 760 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_mode ], mode_subsequent_primary_after 761 <1> sptinternal -3 ; subsequent after ? 762 <1> jz @B ; no --> 763 <1> 764 <1> push bp 765 <1> %ifidn _BASE, ss:bx 766 <1> mov bx, sp 767 <1> %elifidn _BASE, bp 768 <1> mov bp, sp 769 <1> %endif 770 <1> xor di, di 771 <1> push di ; [_BASE+di-2] 772 <1> push di ; [_BASE+di-4] 773 <1> push di ; [_BASE+di-6] 774 <1> push di ; [_BASE+di-8] 775 <1> 776 <1> %if ! _SPT_MULTIPASS 777 <1> sptrel mov word [ %[_SCANPTAB_PREFIX]load_scan_flags ], di 778 <1> sptinternal 779 <1> %endif 780 <1> 781 <1> xor ax, ax 782 <1> xor dx, dx 783 <1> 784 <1> %if _SPT_MULTIPASS 785 <1> sptrel mov word [ %[_SCANPTAB_PREFIX]load_scan_flags ], ax 786 <1> sptinternal 787 <1> %endif 788 <1> 789 <1> call %[_SCANPTAB_PREFIX]read_partition_table.reload 790 <1> ; (re)read MBR 791 <1> sptrel mov si, %[_SCANPTAB_PREFIX]partition_table 792 <1> sptinternal 793 <1> sptrel mov byte [ %[_SCANPTAB_PREFIX]load_current_partition], dl ; = 0 794 <1> sptinternal 795 <1> %if _SPT_ELD 796 <1> sptrel mov word [ %[_SCANPTAB_PREFIX]load_current_extended_and_is], dx ; = 0 797 <1> sptinternal 798 <1> %endif 799 <1> 800 <1> sptrel testopt [ %[_SCANPTAB_PREFIX]load_mode ], mode_active_priority 801 <1> sptinternal -3 ; active primary first ? 802 <1> jz .detect_lowest 803 <1> 804 <1> .loop_active_skip: 805 <1> 806 <1> sptrel inc byte [ %[_SCANPTAB_PREFIX]load_current_partition] 807 <1> sptinternal 808 <1> mov al, byte [es:si + piType] 809 <1> cmp al, 0 810 <1> je .loop_active_skip_skip 811 <1> cmp byte [es:si + piBoot], 0 812 <1> je .loop_active_skip_skip 813 <1> 814 <1> ; INP: al = partition type 815 <1> ; OUT: CY if an LBA or FAT32 type that's disabled 816 <1> ; NC, ZR if known type we can access 817 <1> ; NC, NZ if unknown type 818 <1> call %[_SCANPTAB_PREFIX]load_is_known 819 <1> jc .loop_active_skip_skip 820 <1> jnz .loop_active_skip_skip 821 <1> sptrel mov al, byte [ %[_SCANPTAB_PREFIX]load_current_partition] 822 <1> sptinternal 823 <1> sptrel mov byte [ %[_SCANPTAB_PREFIX]load_scan_flags + 1], al 824 <1> sptinternal 825 <1> jmp .active_skip_done 826 <1> 827 <1> .loop_active_skip_skip: 828 <1> add si, 16 829 <1> sptrel cmp si, strict word %[_SCANPTAB_PREFIX]partition_table.end 830 <1> sptinternal 831 <1> jb .loop_active_skip 832 <1> 833 <1> .detect_lowest: 834 <1> sptrel mov si, %[_SCANPTAB_PREFIX]partition_table 835 <1> sptinternal 836 <1> sptrel mov byte [ %[_SCANPTAB_PREFIX]load_current_partition], 0 837 <1> sptinternal -3 838 <1> 839 <1> .loop_lowest: 840 <1> sptrel inc byte [ %[_SCANPTAB_PREFIX]load_current_partition] 841 <1> sptinternal 842 <1> mov al, byte [es:si + piType] 843 <1> cmp al, 0 844 <1> je .loop_lowest_skip_skip 845 <1> 846 <1> ; INP: al = partition type 847 <1> ; OUT: CY if an LBA or FAT32 type that's disabled 848 <1> ; NC, ZR if known type we can access 849 <1> ; NC, NZ if unknown type 850 <1> call %[_SCANPTAB_PREFIX]load_is_known 851 <1> jc .loop_lowest_skip_skip 852 <1> jnz .loop_lowest_skip_skip 853 <1> sptrel mov al, byte [ %[_SCANPTAB_PREFIX]load_current_partition] 854 <1> sptinternal 855 <1> sptrel mov byte [ %[_SCANPTAB_PREFIX]load_scan_flags + 1], al 856 <1> sptinternal 857 <1> jmp .lowest_skip_done 858 <1> 859 <1> .loop_lowest_skip_skip: 860 <1> add si, 16 861 <1> sptrel cmp si, strict word %[_SCANPTAB_PREFIX]partition_table.end 862 <1> sptinternal 863 <1> jb .loop_lowest 864 <1> jmp .end ; none found 865 <1> 866 <1> .lowest_skip_done: 867 <1> .active_skip_done: 868 <1> sptrel mov si, %[_SCANPTAB_PREFIX]partition_table 869 <1> sptinternal 870 <1> sptrel mov byte [ %[_SCANPTAB_PREFIX]load_current_partition], 0 871 <1> sptinternal -3 872 <1> 873 <1> .loop_primary_parts: 874 <1> sptrel inc byte [ %[_SCANPTAB_PREFIX]load_current_partition] 875 <1> sptinternal 876 <1> cmp byte [es:si + piType], 0 877 <1> je .loop_primary_skip 878 <1> 879 <1> %if _MODE 880 <1> sptrel mov al, byte [ %[_SCANPTAB_PREFIX]load_current_partition] 881 <1> sptinternal 882 <1> sptrel cmp byte [ %[_SCANPTAB_PREFIX]load_scan_flags + 1], al 883 <1> sptinternal 884 <1> je .ignoreprimary 885 <1> %endif 886 <1> call cx ; es:si -> partition table entry 887 <1> ; byte [load_current_partition] = which 888 <1> .ignoreprimary: 889 <1> .loop_primary_skip: 890 <1> add si, 16 891 <1> sptrel cmp si, strict word %[_SCANPTAB_PREFIX]partition_table.end 892 <1> sptinternal 893 <1> jb .loop_primary_parts 894 <1> 895 <1> .end: 896 <1> %ifidn _BASE, bp 897 <1> mov sp, bp ; restore sp 898 <1> pop bp 899 <1> %else 900 <1> mov sp, bx ; restore sp 901 <1> pop ax ; (discard dummy bp value) 902 <1> %endif 903 <1> retn 904 <1> %endif 905 <1> 906 <1> 907 <1> ; if _PARTITION_TABLE_IN_CS: 908 <1> ; INP: dx:ax = partition table sector to read 909 <1> ; CHG: ax, dx 910 <1> ; OUT: es = cs 911 <1> ; 64 bytes [es:partition_table] = partition table 912 <1> ; does not return if error 913 <1> 914 <1> ; else: 915 <1> ; INP: dx:ax = partition table sector to read 916 <1> ; CHG: ax, dx 917 <1> ; OUT: es = ss 918 <1> ; 64 bytes [es:partition_table] = partition table 919 <1> ; does not return if error 920 <1> %[_SCANPTAB_PREFIX]read_partition_table: 921 <1> %if _SPT_ELD 922 <1> .first: 923 <1> db __TEST_IMM8 ; NC, skip stc 924 <1> .reload: 925 <1> stc 926 <1> %else 927 <1> .first: 928 <1> .reload: 929 <1> %endif 930 <1> 931 00000D39 53 <1> push bx 932 <1> %if _SPT_ELD 933 <1> sptrel mov bx, word [buffersegment] 934 <1> sptinternal 935 <1> %elifidn _BASE, bp 936 <1> mov bx, word [auxbuff_segorsel] ; bx => auxbuff 937 <1> %else 938 00000D3A 8B5EE4 <1> mov bx, word [bp + ldSectorSeg] 939 <1> %endif 940 00000D3D E8C5FE <1> call %[_SCANPTAB_PREFIX]read_ae_512_bytes 941 00000D40 5B <1> pop bx 942 00000D41 26813EFE0155AA <1> cmp word [es:510], 0AA55h 943 00000D48 7403E994FE <1> jne .signature_fail 944 00000D4D 1E <1> push ds 945 00000D4E 57 <1> push di 946 00000D4F 56 <1> push si 947 00000D50 51 <1> push cx 948 00000D51 06 <1> push es 949 00000D52 1F <1> pop ds 950 00000D53 BEBE01 <1> mov si, 510 - 4*16 ; ds:si -> partition table in sectorseg 951 <1> %if _PARTITION_TABLE_IN_CS 952 00000D56 0E <1> push cs 953 <1> %else 954 <1> push ss 955 <1> %endif 956 00000D57 07 <1> pop es 957 00000D58 BF[700D] <1> sptrel mov di, %[_SCANPTAB_PREFIX]partition_table 958 <1> sptinternal ; es:di -> cs:%[_SCANPTAB_PREFIX]partition_table 959 00000D5B B92000 <1> mov cx, 4*16 / 2 960 00000D5E F3A5 <1> rep movsw 961 00000D60 59 <1> pop cx 962 00000D61 5E <1> pop si 963 00000D62 5F <1> pop di 964 00000D63 1F <1> pop ds 965 00000D64 C3 <1> retn 966 <1> 967 <1> 968 <1> %if _BOOTCMD_FAIL_ERROR 969 <1> .signature_fail: 970 <1> sptrel mov dx, msg.bootfail_sig_parttable 971 <1> sptinternal 972 <1> jmp bootcmd.fail 973 <1> %endif 3382 resetdef 3383 3384 3385 00000D65 90 align 16 3386 scanparttab_variables_start: 3387 partition_table: 3388 00000D70 00 times 16 * 4 db 0 3389 .end: 3390 3391 partition_offset: 3392 00000DB0 FFFFFFFF dd -1 3393 3394 load_partition_cycle: 3395 00000DB4 0000 dw 0 3396 load_current_partition: 3397 00000DB6 00 db 0 3398 partition_type: 3399 00000DB7 00 db 0 3400 3401 align 2 3402 scanparttab_variables_length_w: equ ($ - scanparttab_variables_start) / 2 3403 %endif 3404 3405 3406 ; INP: ss:bp -> BPB 3407 ; ss:bp - LOADSTACKVARS -> lsv 3408 ; OUT: lsvDataStart set 3409 ; CHG: ax, bx, cx, dx, si, di 3410 calculate_data_start: 3411 00000DB8 31C9 xor cx, cx ; ! ch = 0 3412 3413 ; Although this currently is unused, we calculate the 3414 ; first data sector (including root directory size) 3415 ; here to complete the LOADSTACKVARS. 3416 3417 ; 32-byte FAT directory entries per sector 3418 00000DBA 8B460B mov ax, [bp + bsBPB + bpbBytesPerSector] 3419 00000DBD B105 mov cl, 5 ; ! ch = 0 3420 00000DBF D3E8 shr ax, cl 3421 3422 ; number of sectors used for root directory (store in CX) 3423 ; After the prior shr instruction, ax is always < 8000h, 3424 ; so this cwd instruction always zeros dx. 3425 00000DC1 99 cwd 3426 00000DC2 8B7611 mov si, [bp + bsBPB + bpbNumRootDirEnts] ; (0 iff FAT32) 3427 00000DC5 89C3 mov bx, ax 3428 00000DC7 48 dec ax ; rounding up 3429 00000DC8 7843 js .error_badchain ; if >= 8000h (ie, 0FFFFh while bx = 0) 3430 00000DCA 01F0 add ax, si ; from BPB 3431 00000DCC 11D2 adc dx, dx ; account for overflow (dx was zero) 3432 00000DCE F7F3 div bx ; get number of root sectors 3433 00000DD0 91 xchg ax, cx ; cx = number of root secs, ! ah = 0 3434 3435 00000DD1 51 push cx ; number of root secs 3436 ; first sector of root directory 3437 00000DD2 8A4610 mov al, [bp + bsBPB + bpbNumFATs] 3438 ; ! ah = 0, hence ax = number of FATs 3439 00000DD5 8B4E16 mov cx, word [bp + bsBPB + bpbSectorsPerFAT] 3440 00000DD8 31FF xor di, di ; di:cx = sectors per FAT 3441 ; iff FAT12, FAT16 3442 00000DDA 85C9 test cx, cx ; is FAT32 ? 3443 00000DDC 7506 jnz @F ; no --> 3444 00000DDE 8B4E24 mov cx, word [bp + bsBPB + ebpbSectorsPerFATLarge] 3445 00000DE1 8B7E26 mov di, word [bp + bsBPB + ebpbSectorsPerFATLarge + 2] ; for FAT32 3446 @@: 3447 00000DE4 50 push ax 3448 00000DE5 F7E1 mul cx 3449 ; ax = low word SpF*nF 3450 ; dx = high word 3451 00000DE7 93 xchg bx, ax 3452 00000DE8 87CA xchg cx, dx 3453 ; cx:bx = first mul 3454 00000DEA 58 pop ax 3455 00000DEB F7E7 mul di 3456 ; ax = high word adjust 3457 ; dx = third word 3458 00000DED 85D2 test dx, dx 3459 00000DEF 751C jnz .error_badchain 3460 00000DF1 92 xchg dx, ax 3461 ; dx = high word adjust 3462 00000DF2 01CA add dx, cx 3463 ; dx:bx = result 3464 00000DF4 93 xchg ax, bx 3465 ; dx:ax = result 3466 00000DF5 7216 jc .error_badchain 3467 3468 00000DF7 03460E add ax, [bp + bsBPB + bpbReservedSectors] 3469 00000DFA 83D200 adc dx, byte 0 3470 00000DFD 720E jc .error_badchain 3471 3472 00000DFF 59 pop cx ; number of root sectors 3473 00000E00 31FF xor di, di 3474 3475 ; first sector of disk data area: 3476 00000E02 01C1 add cx, ax 3477 00000E04 11D7 adc di, dx 3478 00000E06 894EFC mov [bp + lsvDataStart], cx 3479 00000E09 897EFE mov [bp + lsvDataStart + 2], di 3480 3481 00000E0C C3 retn 3482 3483 .error_badchain: 3484 00000E0D E9ADF3 jmp error_badchain 3485 3486 3487 end_of_handle_lsv_extra_flags: 3488 3489 3490 %if _CHECKSUM 3491 CHECKSUM_SIZE_P equ (payload_actual_end_late) / 16 3492 3493 overridedef STANDALONE, 0 3494 %include "inicheck.asm" 3495 resetdef 3496 3497 align 16 3498 checksumheader: 3499 istruc CHECKSUMHEADER 3500 at cshSignature, dw "CS" 3501 at cshLengthBytesStructure, dw CHECKSUMHEADER_size 3502 at cshOffsetStructure, dw paras(checksumheader - $$ + 0) 3503 at cshChecksumStructure, dw 10000h-(("CS" + CHECKSUMHEADER_size + paras(checksumheader - $$ + 0) + 8106h + CHECKSUM_SIZE_P + 0 + 0) & 0FFFFh) 3508 at cshTypeChecksum 3509 ..@checksumtype: dw 8106h 3510 at cshAmountParagraphsData, dw CHECKSUM_SIZE_P 3511 at cshChecksumData 3512 ..@checksumfield: dw 0 3513 at cshReserved, dw 0 3514 iend 3515 %endif 3516 3517 %if _MULTIBOOT1 || _MULTIBOOT2 3518 %include "multboot.asm" 1 <1> 2 <1> %if 0 3 <1> 4 <1> Multiboot header and loader 5 <1> 2008--2019 by E. C. Masloch 6 <1> 7 <1> Usage of the works is permitted provided that this 8 <1> instrument is retained with the works, so that any entity 9 <1> that uses the works is notified of this instrument. 10 <1> 11 <1> DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. 12 <1> 13 <1> %endif 14 <1> 15 <1> %macro mbootchecksummed 1-2.nolist 1BADB002h 16 <1> dd %2 ; signature 17 <1> dd %1 ; flags 18 <1> dd -((%2+%1) & 0FFFF_FFFFh) 19 <1> ; checksum: dword sum of these three = 0 20 <1> %endmacro 21 <1> 22 <1> %macro mboot2checksummed 0-1.nolist 0E852_50D6h 23 <1> dd %1 ; signature 24 <1> dd 0 ; platform (i386) 25 <1> dd mboot2_header_end - mboot2_header 26 <1> ; size of header, including header tags 27 <1> dd -((%1 + (mboot2_header_end - mboot2_header))) & 0FFFF_FFFFh 28 <1> ; checksum 29 <1> %endmacro 30 <1> 31 <1> 32 <1> MULTIBOOT_CMDLINE_LENGTH equ lsvclBufferLength 33 <1> 34 <1> MULTIBOOT_BASE equ (1024+64)*1024 ; one paragraph above the HMA 35 <1> MULTIBOOT_END equ MULTIBOOT_BASE + (payload_actual_end_late) 36 <1> MULTIBOOT_BSS_END equ MULTIBOOT_END + fromdwords(dwords(MULTIBOOT_CMDLINE_LENGTH)) 38 <1> MULTIBOOT_TARGET_SEGMENT equ 200h 39 <1> MULTIBOOT_TARGET equ MULTIBOOT_TARGET_SEGMENT << 4 40 <1> MULTIBOOT_BPB equ MULTIBOOT_TARGET - 512 - 16 41 <1> MULTIBOOT_CMDLINE_START equ MULTIBOOT_BPB + lsvCommandLine.start 42 <1> MULTIBOOT_STACK_TOP equ MULTIBOOT_CMDLINE_START 43 <1> 44 <1> %if _MULTIBOOT1 45 <1> ; Multiboot header 46 <1> ; must be dword aligned and in first 8 KiB! 47 <1> align 4, db 0 48 <1> mbootheader: 49 00000E10 02B0AD1B00000100FE- <1> mbootchecksummed 00010000h 49 00000E19 4F51E4 <1> 50 <1> ; flags: provides info where to load image 51 00000E1C [100E1100] <1> dd MULTIBOOT_BASE + mbootheader ; load address of header 52 00000E20 00001100 <1> dd MULTIBOOT_BASE ; load address 53 00000E24 90071200 <1> dd MULTIBOOT_END ; load area end (0 = load whole file) 54 00000E28 90081200 <1> dd MULTIBOOT_BSS_END ; uninitialised area end (0 = none) 55 00000E2C [840E1100] <1> dd MULTIBOOT_BASE + mbootentry ; entry point 56 <1> %endif 57 <1> 58 <1> %if _MULTIBOOT2 59 <1> ; Multiboot2 header 60 <1> ; qword (64-bit) aligned and in first 32 KiB 61 <1> align 8, db 0 62 <1> mboot2_header: 63 00000E30 D65052E80000000050- <1> mboot2checksummed 63 00000E39 000000DAAEAD17 <1> 64 <1> mboot2_tag_information_request: 65 <1> .: 66 00000E40 0100 <1> .type: dw 1 67 00000E42 0000 <1> .flags: dw 0 68 00000E44 10000000 <1> .size: dd .end - . 69 00000E48 01000000 <1> dd 1 ; command line 70 00000E4C 05000000 <1> dd 5 ; ROM-BIOS boot device 71 <1> .end: 72 <1> align 8, db 0 73 <1> mboot2_tag_addresses: 74 <1> .: 75 00000E50 0200 <1> .type: dw 2 76 00000E52 0000 <1> .flags: dw 0 77 00000E54 18000000 <1> .size: dd .end - . 78 00000E58 [300E1100] <1> dd MULTIBOOT_BASE + mboot2_header 79 <1> ; load address of header 80 00000E5C 00001100 <1> dd MULTIBOOT_BASE ; load address 81 00000E60 90071200 <1> dd MULTIBOOT_END ; load area end (0 = load whole file) 82 00000E64 90081200 <1> dd MULTIBOOT_BSS_END ; uninitialised area end (0 = none) 83 <1> .end: 84 <1> align 8, db 0 85 <1> mboot2_tag_entrypoint: 86 <1> .: 87 00000E68 0300 <1> .type: dw 3 88 00000E6A 0000 <1> .flags: dw 0 89 00000E6C 0C000000 <1> .size: dd .end - . 90 00000E70 [D10E1100] <1> dd MULTIBOOT_BASE + mboot2entry ; entry point 91 <1> .end: 92 00000E74 00 <1> align 8, db 0 93 <1> mboot2_tag_end: 94 <1> .: 95 00000E78 0000 <1> .type: dw 0 96 00000E7A 0000 <1> .flags: dw 0 97 00000E7C 08000000 <1> .size: dd .end - . 98 <1> .end: 99 <1> mboot2_header_end: 100 <1> %endif 101 <1> 102 <1> 103 <1> %unmacro mbootchecksummed 1-2.nolist 1BADB002h 104 <1> %unmacro mboot2checksummed 0-1.nolist 0E85250D6h 105 <1> 106 <1> %if ($-$$) > 8192 107 <1> %fatal Multiboot header must be in the first 8 KiB 108 <1> %endif 109 <1> 110 <1> struc MBI 111 00000000 ???????? <1> mbiFlags: resd 1 112 00000004 ???????????????? <1> resb 8 113 0000000C ???????? <1> mbiBootDevice: resd 1 114 00000010 ???????? <1> mbiCmdLine: resd 1 115 00000014 ???????? <1> mbiModuleCount: resd 1 116 00000018 ???????? <1> mbiModuleTable: resd 1 117 <1> ; (More data follows but none which seems useful.) 118 <1> endstruc 119 <1> 120 <1> [cpu 386] 121 <1> [bits 32] 122 <1> 123 <1> numdef MULTIBOOT_CHECKS, 1 124 <1> numdef MULTIBOOT_DEBUG, 0 125 <1> numdef MULTIBOOT_HALT, 0 126 <1> numdef MULTIBOOT_PROGRESS, 0 127 <1> 128 <1> 129 <1> %if _MULTIBOOT_CHECKS || _MULTIBOOT_HALT 130 <1> mbootentry.halt: 131 <1> mboot2entry.halt: 132 <1> ; Unfortunately, as the environment is unknown, we can only 133 <1> ; literally halt here, as we don't know how to do I/O. 134 00000E80 FA <1> cli 135 <1> @@: 136 00000E81 F4 <1> hlt 137 00000E82 EBFD <1> jmp short @B 138 <1> %endif 139 <1> 140 <1> %if _MULTIBOOT1 141 <1> ; INP: eax = 2BADB002h 142 <1> ; ebx-> multiboot info structure 143 <1> ; STT: loaded and executed according to Multiboot header, ie 144 <1> ; loaded at MULTIBOOT_BASE 145 <1> ; eip = MULTIBOOT_BASE+mbootentry 146 <1> ; in PM32 with paging disabled 147 <1> ; CPL 0 148 <1> ; cs,ds,es,fs,gs,ss = flat 32-bit code/data selectors 149 <1> ; DI 150 <1> ; ! stack not set up 151 <1> ; ! GDT might not be set up 152 <1> ; ! IDT might not be set up 153 <1> ; A20 on 154 <1> ; 155 <1> ; Note: Although A20 is on, HMA access without an XMM 156 <1> ; to manage it might be undesirable. Multiboot 157 <1> ; also doesn't tell us whether and if so then 158 <1> ; how we can switch A20. 159 <1> mbootentry: 160 <1> %if _MULTIBOOT_HALT 161 <1> jmp .halt 162 <1> %endif 163 <1> %if _MULTIBOOT_CHECKS 164 <1> [bits 16] 165 00000E84 A90000 <1> test ax, 0 ; (test eax if in 32-bit segment) 166 00000E87 EBF7 <1> jmp short .halt ; (skipped if in 32-bit segment) 167 <1> [bits 32] 168 00000E89 3D02B0AD2B <1> cmp eax, 2BADB002h ; signature ? 169 00000E8E 75F0 <1> jne short .halt ; no --> 170 00000E90 0F01E0 <1> smsw eax 171 00000E93 D1C0 <1> rol eax, 1 ; 2 = protection, 1 = paging 172 00000E95 2403 <1> and al, 3 ; mask off others 173 00000E97 3C02 <1> cmp al, 2 ; PE but not PG ? 174 00000E99 75E5 <1> jne short .halt ; no --> 175 00000E9B 8CC8 <1> mov eax, cs 176 00000E9D 2403 <1> and al, 3 ; CPL 0 ? 177 00000E9F 75DF <1> jnz short .halt ; no --> 178 <1> %endif 179 <1> 180 <1> %if _MULTIBOOT_PROGRESS 181 <1> mov ebp, 0B8000h + 2 * 80 * 20 182 <1> mov word [ebp], 5000h | '1' 183 <1> inc ebp 184 <1> inc ebp 185 <1> %endif 186 <1> 187 <1> ; prepare this and that 188 00000EA1 83CAFF <1> or edx, -1 189 <1> ; locate boot drive in Multiboot info structure 190 00000EA4 F60302 <1> test byte [ ebx + mbiFlags ], 2 ; boot device info valid ? 191 00000EA7 740A <1> jz @F 192 00000EA9 8B430C <1> mov eax, [ ebx + mbiBootDevice ]; get the info 193 00000EAC C1C010 <1> rol eax, 16 194 00000EAF 86C4 <1> xchg al, ah 195 00000EB1 89C2 <1> mov edx, eax ; dl = boot load unit (or 0FFh), 196 <1> ; dh = partition (or 0FFh) 197 <1> ; (edxh = subpartition numbers) 198 <1> @@: 199 <1> 200 <1> %if _MULTIBOOT_PROGRESS 201 <1> mov word [ebp], 5000h | '2' 202 <1> inc ebp 203 <1> inc ebp 204 <1> %endif 205 <1> 206 00000EB3 31C0 <1> xor eax, eax 207 00000EB5 BF90071200 <1> mov edi, MULTIBOOT_END 208 00000EBA B940000000 <1> mov ecx, MULTIBOOT_CMDLINE_LENGTH / 4 209 00000EBF F60304 <1> test byte [ ebx + mbiFlags ], 4 ; command line valid ? 210 00000EC2 7409 <1> jz @F 211 00000EC4 8B7310 <1> mov esi, [ ebx + mbiCmdLine ] 212 00000EC7 F3A5 <1> rep movsd 213 00000EC9 C647FF00 <1> mov byte [ edi - 1 ], 0 ; insure it is terminated 214 <1> ; (this may truncate the line) 215 <1> @@: 216 00000ECD F3AB <1> rep stosd 217 <1> 218 <1> %if _MULTIBOOT_PROGRESS 219 <1> mov word [ebp], 5000h | '3' 220 <1> inc ebp 221 <1> inc ebp 222 <1> %endif 223 <1> 224 <1> %if _MULTIBOOT2 225 00000ECF EB7D <1> jmp multiboot_1_2_common 226 <1> %endif 227 <1> %endif 228 <1> 229 <1> %if _MULTIBOOT2 230 <1> ; INP: eax = 36D7_6289h 231 <1> ; ebx-> multiboot2 info structure 232 <1> ; STT: loaded and executed according to Multiboot2 header, ie 233 <1> ; loaded at MULTIBOOT_BASE 234 <1> ; eip = MULTIBOOT_BASE+mboot2entry 235 <1> ; in PM32 with paging disabled 236 <1> ; CPL 0 237 <1> ; cs,ds,es,fs,gs,ss = flat 32-bit code/data selectors 238 <1> ; DI 239 <1> ; ! stack not set up 240 <1> ; ! GDT might not be set up 241 <1> ; ! IDT might not be set up 242 <1> ; A20 on 243 <1> ; 244 <1> ; Note: Although A20 is on, HMA access without an XMM 245 <1> ; to manage it might be undesirable. Multiboot 246 <1> ; also doesn't tell us whether and if so then 247 <1> ; how we can switch A20. 248 <1> mboot2entry: 249 <1> %if _MULTIBOOT_HALT 250 <1> jmp .halt 251 <1> %endif 252 <1> %if _MULTIBOOT_CHECKS 253 <1> [bits 16] 254 00000ED1 A90000 <1> test ax, 0 ; (test eax if in 32-bit segment) 255 00000ED4 EBAA <1> jmp short .halt ; (skipped if in 32-bit segment) 256 <1> [bits 32] 257 00000ED6 3D8962D736 <1> cmp eax, 36D7_6289h ; signature ? 258 00000EDB 75A3 <1> jne .halt ; no --> 259 00000EDD 0F01E0 <1> smsw eax 260 00000EE0 D1C0 <1> rol eax, 1 ; 2 = protection, 1 = paging 261 00000EE2 2403 <1> and al, 3 ; mask off others 262 00000EE4 3C02 <1> cmp al, 2 ; PE but not PG ? 263 00000EE6 7598 <1> jne .halt ; no --> 264 00000EE8 8CC8 <1> mov eax, cs 265 00000EEA 2403 <1> and al, 3 ; CPL 0 ? 266 00000EEC 7592 <1> jnz .halt ; no --> 267 <1> %endif 268 <1> 269 <1> %if _MULTIBOOT_PROGRESS 270 <1> mov ebp, 0B8000h + 2 * 80 * 20 271 <1> mov word [ebp], 5000h | 'A' 272 <1> inc ebp 273 <1> inc ebp 274 <1> %endif 275 <1> 276 00000EEE 83CAFF <1> or edx, -1 ; initialise boot partition info 277 <1> 278 00000EF1 31C0 <1> xor eax, eax 279 00000EF3 BF90071200 <1> mov edi, MULTIBOOT_END 280 00000EF8 B940000000 <1> mov ecx, MULTIBOOT_CMDLINE_LENGTH / 4 281 00000EFD F3AB <1> rep stosd ; initialise command line buffer 282 <1> 283 00000EFF 83C308 <1> add ebx, 8 ; skip fixed header (size, reserved) 284 <1> .tags_loop: 285 <1> 286 <1> %if _MULTIBOOT_PROGRESS 287 <1> mov word [ebp], 5000h | 'B' 288 <1> inc ebp 289 <1> inc ebp 290 <1> %endif 291 <1> 292 00000F02 8B03 <1> mov eax, dword [ebx] 293 00000F04 85C0 <1> test eax, eax ; end of tags ? 294 00000F06 7444 <1> jz .tags_end ; yes --> 295 <1> 296 00000F08 83F801 <1> cmp eax, 1 297 00000F0B 7410 <1> je .cmdline 298 <1> 299 00000F0D 83F805 <1> cmp eax, 5 300 00000F10 7420 <1> je .partition 301 <1> 302 <1> .tags_next: 303 <1> %if _MULTIBOOT_PROGRESS 304 <1> mov word [ebp], 5000h | 'C' 305 <1> inc ebp 306 <1> inc ebp 307 <1> %endif 308 <1> 309 00000F12 035B04 <1> add ebx, dword [ebx + 4] 310 <1> ; -> after end of tag 311 <1> ; (at padding or next tag) 312 00000F15 83C307 <1> add ebx, 7 313 00000F18 83E3F8 <1> and ebx, ~7 ; skip any padding 314 00000F1B EBE5 <1> jmp .tags_loop 315 <1> 316 <1> .cmdline: 317 <1> 318 <1> %if _MULTIBOOT_PROGRESS 319 <1> mov word [ebp], 5000h | 'D' 320 <1> inc ebp 321 <1> inc ebp 322 <1> %endif 323 <1> 324 00000F1D 8D7308 <1> lea esi, [ebx + 8] 325 00000F20 BF90071200 <1> mov edi, MULTIBOOT_END 326 00000F25 B940000000 <1> mov ecx, MULTIBOOT_CMDLINE_LENGTH / 4 327 00000F2A F3A5 <1> rep movsd ; copy command line 328 00000F2C C647FF00 <1> mov byte [ edi - 1 ], 0 329 <1> ; insure it is terminated 330 <1> ; (this may truncate the line) 331 00000F30 EBE0 <1> jmp .tags_next 332 <1> 333 <1> .partition: 334 <1> 335 <1> %if _MULTIBOOT_PROGRESS 336 <1> mov word [ebp], 5000h | 'E' 337 <1> inc ebp 338 <1> inc ebp 339 <1> %endif 340 <1> 341 00000F32 8B4308 <1> mov eax, dword [ebx + 8] 342 00000F35 3D00010000 <1> cmp eax, 100h 343 00000F3A 7302 <1> jae @F 344 00000F3C 88C2 <1> mov dl, al ; get ROM-BIOS unit 345 <1> @@: 346 <1> 347 00000F3E 8B430C <1> mov eax, dword [ebx + 12] 348 00000F41 3D00010000 <1> cmp eax, 100h 349 00000F46 7302 <1> jae @F 350 00000F48 88C6 <1> mov dh, al ; get partition number (0 = first primary) 351 <1> @@: 352 00000F4A EBC6 <1> jmp .tags_next 353 <1> 354 <1> .tags_end: 355 00000F4C 31C0 <1> xor eax, eax 356 <1> 357 <1> %if _MULTIBOOT_PROGRESS 358 <1> mov word [ebp], 5000h | 'F' 359 <1> inc ebp 360 <1> inc ebp 361 <1> %endif 362 <1> 363 <1> %endif 364 <1> 365 <1> multiboot_1_2_common: 366 00000F4E BCDC1C0000 <1> mov esp, MULTIBOOT_STACK_TOP ; set a valid stack, at 8 KiB 367 <1> ; (also required by 86M entry later) 368 <1> 369 <1> %if _MULTIBOOT_PROGRESS 370 <1> mov word [ebp], 5000h | 's' 371 <1> inc ebp 372 <1> inc ebp 373 <1> %endif 374 <1> 375 <1> ; A20 is on. set up some things 376 00000F53 31C9 <1> xor ecx, ecx 377 00000F55 BF00001000 <1> mov edi, 1024*1024 378 00000F5A B110 <1> mov cl, 10h ; (ecx = 10h) 379 00000F5C F3AB <1> rep stosd ; clear this area so A20 tests succeed 380 <1> ; The above writes to 10_0000h..10_003Fh, leaving edi = 10_0040h 381 00000F5E B0C0 <1> mov al, 0C0h ; (eax = C0h) 382 00000F60 C64407C0EA <1> mov byte [ edi-40h+eax ], 0EAh 383 00000F65 894438C1 <1> mov [ edi-40h+eax+1 ], eax ; write jump for CP/M entry hack here 384 <1> ; The above writes to 10_00C0h..10_00C4h 385 <1> 386 <1> %if _MULTIBOOT_PROGRESS 387 <1> mov word [ebp], 5000h | 't' 388 <1> inc ebp 389 <1> inc ebp 390 <1> %endif 391 <1> 392 <1> ; relocate image to a good location, 2000h 393 00000F69 BE00001100 <1> mov esi, MULTIBOOT_BASE ; -> iniload image 394 00000F6E BF00200000 <1> mov edi, MULTIBOOT_TARGET 395 00000F73 B9E4410000 <1> mov ecx, (payload_actual_end_late + 3) / 4 396 00000F78 F3A5 <1> rep movsd 397 <1> 398 <1> %if _MULTIBOOT_PROGRESS 399 <1> mov word [ebp], 5000h | 'u' 400 <1> inc ebp 401 <1> inc ebp 402 <1> %endif 403 <1> 404 <1> MULTIBOOT_ESI_AFTER equ MULTIBOOT_BASE + (payload_actual_end_late + 3) / 4 * 4 405 <1> %if MULTIBOOT_ESI_AFTER != MULTIBOOT_END 406 <1> mov esi, MULTIBOOT_END 407 <1> %endif 408 00000F7A BFDC1C0000 <1> mov edi, MULTIBOOT_CMDLINE_START 409 00000F7F B940000000 <1> mov ecx, MULTIBOOT_CMDLINE_LENGTH / 4 410 00000F84 F3A5 <1> rep movsd 411 <1> 412 <1> %if _MULTIBOOT_PROGRESS 413 <1> mov word [ebp], 5000h | 'v' 414 <1> inc ebp 415 <1> inc ebp 416 <1> %endif 417 <1> 418 <1> ; now back to real mode 419 00000F86 0F0115[18101100] <1> o32 lgdt [ MULTIBOOT_BASE+mbootgdtdesc ] 420 <1> ; set our GDT 421 00000F8D 66B81000 <1> mov ax, 10h 422 00000F91 8ED8 <1> mov ds, ax 423 00000F93 8EC0 <1> mov es, ax 424 00000F95 8ED0 <1> mov ss, ax 425 00000F97 8EE0 <1> mov fs, ax 426 00000F99 8EE8 <1> mov gs, ax ; set 64 KiB segment limits 427 00000F9B 6A00 <1> o32 push byte 0 428 00000F9D 9D <1> o32 popf ; reset all flags 429 00000F9E EA[A50F0000]0800 <1> jmp 08h:.pm16 ; use 16-bit selector 430 <1> 431 <1> [bits 16] 432 <1> ; now really switch to real mode 433 <1> ; (already executing relocated code) 434 <1> .pm16: 435 00000FA5 0F20C0 <1> mov eax, cr0 436 00000FA8 48 <1> dec ax 437 00000FA9 0F22C0 <1> mov cr0, eax ; clear PE bit 438 <1> 439 00000FAC 66EA[B40F0000]0002 <1> jmp dword MULTIBOOT_TARGET_SEGMENT:.rm ; reload cs 440 <1> 441 <1> ; Set up registers and the environment for 442 <1> ; the kernel entry point. It doesn't need initialised 443 <1> ; segment registers but we'll initialise them in case 444 <1> ; they still contain selector content. 445 <1> .rm: 446 <1> ; some sources indicate we should set the IDT 447 00000FB4 2E660F011E[1E10] <1> o32 lidt [cs:mbootidtdesc] ; set IDT to RM IVT 448 00000FBB 31C0 <1> xor ax, ax 449 00000FBD 8ED8 <1> mov ds, ax 450 00000FBF 8EC0 <1> mov es, ax 451 00000FC1 8EE0 <1> mov fs, ax 452 00000FC3 8EE8 <1> mov gs, ax 453 00000FC5 8ED0 <1> mov ss, ax 454 <1> ; (cs should be = 200h now, need no far jump) 455 <1> ; cs = 200h, ip = mboot_86m_entry 456 <1> ; ds = es = fs = gs = ss = 0 457 <1> ; sp = 2000h - 512 - 16 + lsvCommandLine.start 458 <1> ; dl = boot unit (or -1) 459 <1> ; dh = boot partition (0..3 = primary, 4+ = logical, or -1) 460 <1> 461 00000FC7 BDF01D <1> mov bp, MULTIBOOT_BPB ; -> pseudo BPB 462 00000FCA BFDC1D <1> mov di, MULTIBOOT_BPB + lsvCommandLine.signature 463 00000FCD B8434C <1> mov ax, lsvclSignature 464 00000FD0 AB <1> stosw ; store signature 465 00000FD1 B98500 <1> mov cx, (- (lsvCommandLine.signature + 2) + 512 + 3) / 4 466 00000FD4 6631C0 <1> xor eax, eax 467 00000FD7 66F3AB <1> rep stosd ; clear 468 <1> ; lsvFirstCluster = 0 (same as for FreeDOS entrypoint) 469 <1> ; bsBPB + bpbHiddenSectors = 0 (invalid or unpartitioned) 470 <1> ; bsBPB + bpbCHSSectors = 0 471 <1> ; bsBPB + bpbCHSHeads = 0 472 <1> 473 00000FDA 40 <1> inc ax 474 00000FDB 668946FC <1> mov dword [bp + lsvDataStart], eax 475 <1> ; lsvDataStart = 1 (placeholder) 476 <1> __CPU__ 477 00000FDF 894611 <1> mov word [bp + bsBPB + bpbNumRootDirEnts], ax 478 00000FE2 894616 <1> mov word [bp + bsBPB + bpbSectorsPerFAT], ax 479 <1> ; do not detect as FAT32 480 00000FE5 88460D <1> mov byte [bp + bsBPB + bpbSectorsPerCluster], al 481 00000FE8 C7460B0002 <1> mov word [bp + bsBPB + bpbBytesPerSector], 512 482 00000FED C746130020 <1> mov word [bp + bsBPB + bpbTotalSectors], 8192 483 <1> ; 1 C/s * 8 Ki sectors = 8 Ki clusters, detect as FAT16 484 <1> ; that is, do not load FAT at all (would load a FAT12) 485 <1> 486 00000FF2 885624 <1> mov byte [bp + bsBPB + bpbNew + bpbnBootUnit], dl 487 <1> ; set boot load unit 488 <1> 489 <1> %if _LSVEXTRA 490 <1> %if _MULTIBOOT_DEBUG 491 <1> mov ax, dx 492 <1> call mb_disp_ax_hex 493 <1> %endif 494 00000FF5 86D6 <1> xchg dl, dh ; dl = Multiboot spec partition number 495 <1> ; (0..3 primary, 4+ logical, -1 invalid) 496 00000FF7 42 <1> inc dx ; 0 invalid, 1..4 primary, 5+ logical 497 00000FF8 B602 <1> mov dh, lsvefPartitionNumber 498 00000FFA 8956EE <1> mov word [bp + lsvExtra], dx 499 <1> %endif 500 00000FFD E91BF9 <1> jmp freedos_entry.multiboot_entry 501 <1> 502 <1> 503 <1> %if _MULTIBOOT_DEBUG 504 <1> mb_disp_ax_hex: ; ax 505 <1> xchg al,ah 506 <1> call mb_disp_al_hex ; display former ah 507 <1> xchg al,ah ; and fall trough for al 508 <1> mb_disp_al_hex: ; al 509 <1> push cx 510 <1> mov cl,4 511 <1> ror al,cl 512 <1> call mb_disp_al_lownibble_hex ; display former high-nibble 513 <1> rol al,cl 514 <1> pop cx 515 <1> ; and fall trough for low-nibble 516 <1> mb_disp_al_lownibble_hex: 517 <1> push ax ; save ax for call return 518 <1> and al,00001111b ; high nibble must be zero 519 <1> add al,'0' ; if number is 0-9, now it's the correct character 520 <1> cmp al,'9' 521 <1> jna .decimalnum ; if we get decimal number with this, ok --> 522 <1> add al,7 ; otherwise, add 7 and we are inside our alphabet 523 <1> .decimalnum: 524 <1> call mb_disp_al 525 <1> pop ax 526 <1> retn 527 <1> 528 <1> mb_disp_al: 529 <1> push ax 530 <1> push bx 531 <1> push bp 532 <1> mov ah, 0Eh 533 <1> mov bx, 7 534 <1> int 10h 535 <1> pop bp 536 <1> pop bx 537 <1> pop ax 538 <1> retn 539 <1> %endif 540 <1> 541 <1> 542 <1> align 16, db 0 543 <1> mbootgdt: 544 00001000 00000000 <1> dw 0,0 545 00001004 00000000 <1> db 0,0,0,0 546 <1> 547 <1> ; selector 8: 16-bit real mode CS 548 <1> ; base = 00002000h, limit 0FFFFh (1 B Granularity), present 549 <1> ; type = 16-bit code execute/read only/conforming, DPL = 0 550 00001008 FFFF0020 <1> dw 0FFFFh,MULTIBOOT_TARGET 551 0000100C 009E0000 <1> db 0,9Eh,0,0 552 <1> 553 <1> ; selector 10h: 16-bit real mode DS 554 <1> ; base = 00000000h, limit 0FFFFh (1 B Granularity), present 555 <1> ; type = 16-bit data read/write, DPL = 0 556 00001010 FFFF0000 <1> dw 0FFFFh,0 557 00001014 00920000 <1> db 0,92h,0,0 558 <1> endarea mbootgdt 559 <1> 560 <1> mbootgdtdesc: 561 00001018 1700 <1> dw mbootgdt_size-1 ; limit 562 0000101A [00101100] <1> dd MULTIBOOT_BASE+mbootgdt ; address 563 <1> 564 <1> mbootidtdesc: 565 0000101E FF03 <1> dw 400h-1 ; limit 566 00001020 00000000 <1> dd 0 ; address (86M IVT) 3519 %endif 3520 3521 3522 %if _DEBUG3 3523 ; INP: word [cs:ip + 1] = two characters to display 3524 ; (second one may be NUL to skip) 3525 ; OUT: - 3526 ; CHG: - 3527 d3_display_two_characters: 3528 lframe near 3529 lenter 3530 push ax 3531 push bx 3532 mov bx, word [bp + ?frame_ip] 3533 mov ax, [cs:bx + 1] 3534 3535 push ax 3536 call d3_disp_al 3537 pop ax 3538 3539 xchg al, ah 3540 test al, al 3541 jz @F 3542 3543 call d3_disp_al 3544 @@: 3545 3546 pop bx 3547 pop ax 3548 lleave 3549 lret 3550 3551 ; INP: al = to display 3552 ; CHG: ax, bx 3553 d3_disp_al: 3554 push bp 3555 mov ah, 0Eh 3556 mov bx, 7 3557 int 10h 3558 pop bp 3559 retn 3560 %endif 3561 3562 3563 msdos1_com_entry: 3564 00001024 BA[2D11] mov dx, .msg + 100h 3565 00001027 B409 mov ah, 09h 3566 00001029 CD21 int 21h 3567 0000102B CD20 int 20h 3568 3569 .msg: 3570 0000102D 38362D444F53207665- ascic "86-DOS version 1 not supported, aborting.",13,10 3570 00001036 7273696F6E2031206E- 3570 0000103F 6F7420737570706F72- 3570 00001048 7465642C2061626F72- 3570 00001051 74696E672E0D0A24 3571 3572 3573 %ifnidn _INILOAD_PAYLOAD_INCLUDE, "" 3574 %include _INILOAD_PAYLOAD_INCLUDE 3575 %else 3576 00001059 26 align 16, db 38 3577 payload: 3578 3579 %assign num payload - $$ 3580 %warning num bytes before iniload payload 3580 ****************** warning: 4192 bytes before iniload payload [-w+user] 3581 3582 00001060 incbin _PAYLOAD_FILE 3583 align 16, db 38 3584 .actual_end: 3585 %if _IMAGE_EXE 3586 align 512, db 38 ; until end of page 3587 .trail: 3588 %ifnidn _EXE_TRAIL_INCBIN, "" 3589 incbin _EXE_TRAIL_INCBIN 3590 %endif 3591 _fill 512, 38, .trail ; a full additional page, 3592 ; this is for the bogus exeExtraBytes 3593 ; Note that the pages start counting within the EXE header! 3594 ; Thus alignment to the file-level page boundary is correct. 3595 %endif 3596 .end: 3597 payload_actual_end_late equ payload.actual_end - $$ 3598 payload_end_late equ payload.end - $$ 3599 3600 3601 %if _SECOND_PAYLOAD_EXE 3602 align 16, db 38 3603 second_payload: 3604 incbin _SECOND_PAYLOAD_FILE 3605 align 16, db 38 3606 .actual_end: 3607 align 512, db 38 3608 .trail: 3609 %ifnidn _EXE_TRAIL_INCBIN, "" 3610 incbin _EXE_TRAIL_INCBIN 3611 %endif 3612 _fill 512, 38, .trail 3613 .end: 3614 second_payload_late equ second_payload - $$ 3615 second_payload_actual_end_late equ second_payload.actual_end - $$ 3616 second_payload_end_late equ second_payload.end - $$ 3617 3618 %endif 3619 3620 %if ($ - start) < 4096 3621 _fill 4096, 38, start ; fill to new minimum limit 3622 %endif 3623 3624 %if _PADDING 3625 %if ($ - $$) > _PADDING 3626 %warning No padding needed 3627 %else 3628 times _PADDING - ($ - $$) db 0 3629 %endif 3630 %endif 3631 3632 end_of_file: 3633 iniload_filesize_late equ end_of_file - $$ 3634 %if _EDRDOS 3635 %if (70h + paras(iniload_filesize_late)) < 800h 3636 %error Too small payload to detect EDR-DOS entrypoint 3637 %endif 3638 %endif 3639 3640 %endif ; _INILOAD_PAYLOAD_INCLUDE