====== Review of iniload control flow ====== Currently, this page is for the lDOS entrypoint of the initial loader, and doesn't trace into subfunctions such as **error**, **query_geometry**, **read_sector**, **check_clust**, **clust_next**, **clust_to_first_sector**, nor the multi-sector loader. ===== Signature and entrypoint ===== //Summary//: Provide entrypoint for the lDOS load protocol. Clamps load end segment. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1375 Offset: 1020, signature "lDXX". The "lD" is checked by recent lDOS compatible loaders, the "XX" is two nonblank printable ASCII text bytes to identify the payload, checked in https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l259 https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1396 Offset: 1024 (third 512-bytes block), **ldos_entry**, the lDOS compatible entrypoint. Initialises the incoming stack with two pushes and writing a zero to **lsvExtra**. Checks that **lsvLoadSeg** is >= cs + paras(**end**), displays the error "Initial loader not fully loaded" otherwise. Next, clamps **lsvLoadSeg** so it will not point higher than =%%%%> cs + paras(**payload.actual_end**). ===== Initialise memory and set up stack ===== //Summary//: Sets up a new stack and two buffers in the LMA top reservation area (20 KiB left free by prior loader). Copies over the command line (if any), the Load Stack Variables, and the boot sector including a BPB. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1453 **init_memory**, gets Low Memory Area size using int 12h and optionally adjusts it by calling the RPL. Reserves two 8 KiB buffers below the memory top, using the higher buffer as sector segment if it doesn't cross a 64 KiB boundary. Otherwise the lower buffer is used as sector segment. In any case, the other buffer is used as FAT segment. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1511 **.gotsegs**, subtracts from the segment pointer to the lower buffer to address the destination for the stack, load command line, load data (ld), load stack variables (lsv), and boot sector. Checks that this segment is above-or-equal the adjusted **lsvLoadSeg** (which as per **ldos_entry** is clamped to point no higher than past the actual payload), else errors out with "Out of memory.", then checks that the incoming ss:bp + 512 + 15 do not point above the destination (again erroring out if they do). Also checks that the incoming **lsvFATSeg** + 8 KiB doesn't point above the destination. (It is intended that the **lsvFATSeg** may be zero to indicate no FAT segment.) https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1557 Checks whether a command line is passed below the lsv. Stores a zero in first byte of **LOADCMDLINE** destination and a -1 in the last byte if not, else copies over the command line and stores a 0 in the last byte. Copies over lsv and boot sector (512 bytes) to stack destination. Zeroes an area behind the boot sector (preparing for a FAT32 EBPB which doesn't move up the trail of the boot sector). https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1585 Relocates the stack to the destination, and points ss:bp -%%%%> the relocated boot sector. Then enables interrupts (EI) using a ''sti'' instruction. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1608 Initialises the **ldMemoryTop**, **ldLoadTop**, and **ldSectorSeg** variables. Copies over 8 KiB from original **lsvFATSeg** =%%%%> buffer to FAT buffer and updates **lsvFATSeg**. Saves the original **lsvFATSeg** for later. Sets ds and es to equal ss. ===== Initialise file system ===== //Summary//: Determines whether the file system is FAT12, FAT16, or FAT32. This may involve calculating the amount of data clusters. If need be, and FAT12 is detected, the full FAT is loaded. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1631 Checks **bpbSectorsPerFAT** for zero to detect FAT32. If not: First, moves up BPBN and boot sector trail to make space for the pseudo-FAT32 EBPB trail. Second, zeroes high words of **lsvFirstCluster** and **lsvFATSector**. Third, inits **ebpbSectorsPerFATLarge** (equal to zero-extended **bpbSectorsPerFAT**) and **ebpbFSFlags** (zero). https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1656 Calls **query_geometry** (in first 512 bytes of iniload). https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1664 Checks size of **lsvLoadSeg** minus cs, if large enough then call **init_memory_multi** (in the fourth 512-bytes block of iniload). If any **lsvExtra.flags** set, check that **handle_lsv_extra_flags** is resident (behind **end3**) and then call it, or error out if any **lsvExtra.flags** set and it is not resident. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1686 Calculate **ldClusterSize** (word) variable from **bpbSectorsPerCluster** (byte), transforming a zero to 256 (EDR-DOS compatible). Calculate **ldParaPerSector**. Determine 32-bit amount of sectors in the file system, and update **bpbTotalSectorsLarge** to the value that is in use. Compare **bpbSectorsPerFAT** to 0, and branch to **.got_fat_type** with **ldFATType** equal to 32 if so. If it was not zero, subtract from 32-bit amount sectors the **lsvDataStart** to calculate amount of data sectors. Divide by cluster size to calculate amount of data clusters. Set **ldFATType** to 16 or 12. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1749 If FAT12 and the incoming **lsvFATSeg** was nonzero, assume the FAT was already loaded and pass it along. Otherwise, check if **lsvLoadSeg** is high enough to not need any further loading, if yes then force **lsvFATSeg** to zero to indicate FAT not loaded. Else load the FAT, either using **load_fat12_multi** (if multi-sector loader already enabled) or a single-sector loop. Load at most 6 KiB worth of FAT data or as many as **bpbSectorsPerFAT**, whichever is smaller. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1800 **.got_fat12**: FAT12 loaded, init cx =%%%%> FAT segment, **.got_fat_type**: FAT16 or FAT32, init cx likewise, **.got_fat12_zero**: FAT12 without FAT loaded, cx = 0. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1805 Transfer to **finish_continue** (in the second 512-bytes block). ===== Check and prepare relocation ===== //Summary//: Calculate where the last to-be-loaded sector of the kernel payload should go, and whether the already resident part must be relocated down to satisfy this placement. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l985 **finish_continue** (in second 512-bytes block), store cx in **lsvFATSeg**. Store paras(**payload.actual_end**) in ax, add para per sector - 1, mask AND with NOT (para per sector - 1), yielding the length of the payload rounded up to the next full sector, in paragraphs. Add to ax the current cs, if it carries then go to relocation code. If it doesn't carry, compare ax to **ldLoadTop**, if ax is below or equal to **ldLoadTop** then branch to **finish_load**. Else, fall through to relocation code. ===== Do relocate ===== //Summary//: If deemed necessary, relocate down the resident part. This part is at least 1536 bytes sized, for lDOS load protocol more commonly at least 4 KiB sized. It may be larger than 64 KiB. Relocation is done in two steps: A small relocator stub handles the first up to 64 KiB, then more code is run from the already relocated initial loader code segment. The initial loader is always fully relocated already by the stub. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1007 Relocation code, subtract **ldLoadTop** from ax (needed top of last sector loaded), yielding how far in paragraphs we need to relocate down. Get **lsvLoadSeg** into cx, then subtract ax from **lsvLoadSeg** (to relocate it). Negate ax. Add cs to ax, calculating where we have to relocate cs to. If this addition doesn't carry, error out with out of memory. If the result in ax is < 61h, error out with out of memory. Push the result in ax, and call to **finish_relocation**. This puts the near address of **relocate_to** onto the stack, forming a far address that points to **relocate_to** in the relocation destination segment. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1176 **finish_relocation**, (in second 512-bytes block,) push ax =%%%%> destination cs onto the stack. Decrement ax to =%%%%> destination for **relocator** stub (at least 60h). Point es:di -%%%%> destination for the stub. Push es:di far pointer onto the stack. Point ds:si -%%%%> unrelocated **relocator** source. Run repeated ''movsw'' instructions to place the **relocator**. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1194 Prepare **relocator** running. Zero di and si. Subtract current cs from cx (old **lsvLoadSeg**), calculating how many paragraphs to be relocated. Set bx to paras(64 KiB). Set ax to cx. If ax indicates less than or equal to 64 KiB are already loaded, leave cx as is. Else set cx to paras(64 KiB) from bx. Subtract cx from ax, leaving ax as either zero or how many paragraphs to be relocated beyond the first 64 KiB. Shift left cx thrice to convert from amount paragraphs to amount words. Jump to the **relocator** using a ''retf'' instruction. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1166 **relocator**, pops es =%%%%> destination of relocation (always below source), run ''rep movsw'' to relocate the first 1.5 KiB to 64 KiB, then run a ''retf''. This last instruction branches to **relocate_to**. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1027 **relocate_to**, in second 512-bytes block. Run a ''rep movsw'' instruction (no-op on entry from **relocator** as cx already zero), add 1000h to es and to ds, subtract 1000h = paras(64 KiB) = bx from ax and prepare cx = 8000h = words(64 KiB), if ax was >= 1000h then branch back to **relocate_to** for the next full 64 KiB chunk. Afterwards add bx to ax to restore the amount of paragraphs in the last chunk. Shift left ax by 3 to convert to amount words, set cx from ax, and run a final ''rep movsw'' that moves 0 to 65520 bytes. Reset ds to equal ss and fall through to **finish_load**. ===== Walk file loop to load remaining parts of the payload ===== //Summary//: The initial loader concludes, if necessary, by walking the FAT chain of the load file to completely load the kernel payload. At first an appropriate amount of data sectors is skipped, after which all additionally needed data sectors are read. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1070 **finish_load**, in second 512-bytes block. Get paras(**payload.actual_end**) from the stack. Add cs to it. Set **ldLoadUntilSeg** to result. Check whether **lsvLoadSeg** is already as high or higher than **ldLoadUntilSeg**, if yes branch to **loaded_all**. If no, set **ldLoadingSeg** to cs, get **lsvFirstCluster**, run **check_clust**, and error out if the cluster is invalid (0, 1, or beyond maximum for this FAT type). https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1094 skip/load loop, in the second 512-bytes block. This loop initially skips file data sectors already covered in full by the prior loader stage, as communicated by the incoming **lsvLoadSeg** =%%%%> behind loaded file data. It walks the file, using the FAT when needed to read the next cluster, and calculating the first sector of each cluster then looping for the amount sectors per cluster, incrementing the sector number and load segment. As soon as the load segment calculated points above the (relocated) **lsvLoadSeg**, a branch to the **skipped_all** label occurs. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1141 **skipped_all**. Undoes the last addition by subtracting **ldParaPerSector** from bx, then either branches to **skipped_all_multi**, or else calls **read_sector** once and jumps to **skipped_all_continue**. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1811 **skipped_all_continue** (within **end**+32, at the start of the fourth 512-bytes block). After the first successful **read_sector** call for file data, at least 1536+32 bytes of the file are loaded. (32 bytes is the smallest theoretical size of a FAT FS sector.) Therefore, **skipped_all_continue** can be placed here. It checks whether **end3** is already loaded yet, and if not then it branches back into the single-sector loop at **loadorskip_next_sector**. If it is loaded now, then branch to **multi_late** to set up the multi-sector loader for the remaining file data. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l1122 **loadorskip_next_sector**: As soon as **skipped_all_continue** branches back to **loadorskip_next_sector**, the prior skip loop turns into a load loop. For every sector still needed, **lsvLoadSeg** will be detected as being below the end of the sector, so the loop will immediately branch to **skipped_all** again. If the end of the FAT chain is reached, the code checks that the final FAT entry contained a valid EOC value. If not, error out as bad chain. Then it checks whether **ldLoadUntilSeg** is reached, if not error out as short file, else branch to **loaded_all**. In the load loop, if bx reaches at least **ldLoadUntilSeg**, branch to **loaded_all**. ===== Final setup after payload file data completely loaded ===== //Summary//: The BPB and load command line are cleaned up, and then the code flow is transferred to the fully loaded kernel payload. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l2507 **loaded_all** and **loaded_all.2stack**, behind **end3**. Check **bpbSectorsPerFAT** equals zero to determine if FAT32. If not, zero out several EBPB fields in the pseudo-FAT32 EBPB created for FAT12 and FAT16 earlier. Copy the query patch value into **ldQueryPatchValue** for next stage. https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l2575 Preserve the last byte of the **LOADCMDLINE** buffer (was set to 0 if valid and -1 if invalid), scan for end of command line, and fill remaining space in the command line buffer with the preserved last byte value. (An empty specified command line will end up starting with 00h 00h, while an unspecified command line will instead end up starting with 00h FFh.) https://hg.pushbx.org/ecm/ldosboot.exp/file/fada37de0070/iniload.asm#l2587 Calculate far address at which to enter the payload, push this address, and transfer control to the payload using a ''retf'' instruction. ~~DISCUSSION~~