=== l/MS-DOS kernel file iniload with MZ header. Two payloads: Kernel and application (the latter could also contain a device driver). OR: drload. Single payload, kernel mode only. iniload kernel can be loaded at 600h (FreeDOS), 700h (EDR-DOS / IBM-DOS), 2000h (lDOS), etc. drload kernel can only be loaded at 600h (FreeDOS) or 700h (EDR-DOS). === iniload kernel payload (Optional) inicomp stage, packed payload with depacker. Contains lCFG block (patchpro and patchdeb sites). Loaded at 600h or higher paragraph boundary, depending on where iniload or drload were loaded or relocated to. Depacks its payload to 600h. === iniload kernel payload or inicomp payload drkernpl stage. Now only contains relocation and lCFG block (patchdeb site), the dos module is no longer used (empty binary file given). Loaded at 600h or higher paragraph boundary, depending on where iniload or drload were loaded or relocated to. inicomp always loads drkernpl to 600h. Relocates and runs its (bio) payload at 700h. === drkernpl (bio) payload msbio section BIOCODE, remains at 700h section SYSINITSEG msdos (linked into msbio.bin now) group DOSGROUP, sections DOSSTART, START, CONSTANTS, DATA, TABLE, CODE, LAST section AFTERDOS (empty, only used to get DOS size) === section BIOCODE Contains all msbio device drivers (CON, AUX, PRN, CLOCK$, block, COM1, LPT1, LPT2, LPT3, COM2, COM3, COM4). Serves as BIO/entry and BIO/code section. Contains msinit.nas code at end. INIT: Initialises OLD13, ORIG13 pointers and int 13h vector (BLOCK13). Initialises ORIG19 pointer and int 19h vector (INT19). Gets number of diskette drives from int 11h, sets FakeFloppyDrv to 1 if none and counts as 2, increments SINGLE if one and counts as 2, so the total amount is counted as 2, 3, or 4. Initialises int 1Bh, 29h, 1, 3, 4. (1 and 3 only if no debugger detected.) Relocates DPT to 00522h. Initialises FATLOC to => 1 KiB below end of LMA, Init_BootSeg => 2 KiB below end of LMA. MEMORY_SIZE => end of LMA. Calculates CURRENT_DOS_LOCATION as => behind BIOCODE plus SYSIZE (= 500h paragraphs). Initialises interrupt 0Fh to an iret if its segment is below-or- equal MEMORY_SIZE or equal to 0F000h. A pointer at BIOCODE:0 pointing to HARDDRV is set up. HARDNUM = DRVMAX = number of diskette drives. DSKDRVS array after BDS pointers for diskette drives overwritten using the two BDS pointers at HDSKTAB. Number of hard disks queried using int 13h function 08h with dl=80h. HNUM set to returned dl. If FakeFloppyDrv then initialise the first two diskette BDSes. If SINGLE = 1, increment SINGLE. If SINGLE = 2, init phantom drive. Initialise BDS next pointer's offset to -1 after 2, 3, or 4 initialised BDSs. If HNUM > 0 then BDSH is tried to be initialised as drive HARDNUM, int 13h unit 80h. If did init BDSH and HNUM >= 2 then BDSX is tried to be initialised as drive HARDNUM + 1, int 13h unit 81h. If didn't init BDSH and HNUM was > 1 then HNUM -= 1 and BDSH is initialised as drive HARDNUM, int 13h unit 81h. If unit 81h didn't init then HNUM -= 1. Successfully initialised BDSH or BDSX linked into BDS chain. If HNUM is zero, skip to STATIC_CONFIGURE. Else add HNUM to HARDNUM and store as DRVMAX. Run DoMini. ax = END96TPI if fHave96 (any diskettes with change line support), and branch to Config96. ax = ENDTWOHARD if HNUM > 1. ax = ENDONEHARD if HNUM == 1. Else, ax = ENDFLOOPY. In Config96: Save current int 13h vector to REAL13, hook int 13h vector (INT13). Common path: DRVMAX = HARDNUM + HNUM + num_mini_dsk. If num_mini_dsk != 0 then ax = [End_Of_BDSM]. If model byte is == 0FCh and HNUM nonzero and BIOS date == 01/10/84 then set ORIG13 to cs:ax and copy IBM_DISK_IO up to ENDATROM to cs:ax. This handler in mshard.nas handles 13.02 and 13.0A calls for hard disks and calls into the ROM-BIOS to implement them. Other calls are passed to the handler referenced by OLD13. The offset in ax is updated to point behind the stored handler. It is checked whether there is a CMOS clock. If yes, [DaycntToDay] is set to the current ax value, and Daycnt_To_Day up to EndDaycntToDay is copied to cs:ax. Then [BinToBCD] is set to the ax after copying the prior block, and Bin_To_BCD up to EndCMOSClockSet is copied. If int 15h function 4100h bl=0 returns NC, then INT6C up to ENDK09 is copied to cs:ax, fHaveK09 is set to 1, and the interrupt 6Ch vector is changed to point to the copied INT6C block. CONFIGDONE: Final rounded up value in ax is shifted right by 4 and added to BIOCODE, then stored in [SYSINITSEG:FINAL_DOS_LOCATION]. GOINIT: PURGE_96TPI is called if no diskettes with changeline support were detected. This uses a patch table and one manual patch in the block device's dispatch table. READDOS, LOADIT: Used to read in msdos file here. In single-file lDOS, this just relocates the msdos module to the CURRENT_DOS_LOCATION. SETDRVPARMS: Set default BPBs for every BDS. Pass execution to SYSINIT in (unrelocated) SYSINITSEG. === section SYSINITSEG sysinit1.nas, sysconf.nas, sysinit2.nas, sysimes.nas SYSINIT: Jump near to GOINIT (?). GOINIT: Gets the model byte. If 15.C0 not supported, read model byte from 0F000h:0FFFEh, and call int 11h to check ax bit 0. If zero, then set Fake_Floppy_Drv to 1. (NOT done if 15.C0 supported. Bug?) Move_Myself: SYSSIZE (note the double S) is converted to a paragraph size (rounding up) and subtracted from MEMORY_SIZE. Then SYSINITSEG is relocated to this segment (end of LMA). SYSIN: Relocate DOS from CURRENT_DOS_LOCATION to FINAL_DOS_LOCATION. Always done usings a forward rep movsw, ie expected to be either not overlapping or an overlapping destination below-or-equal the source. This should be safe because below the final location we expect all memory to be used by IVT, BDA, BIO entry/code sections, or BDSes. So the current location must be above-or-equal the final location. ds:si -> CON device header, dx => end of LMA, ss:sp -> temporary stack within SYSINITSEG. Call far to DOS segment's offset 0 which will be the START section near jump to DOSINIT. DOS returns with es:di -> SYSI structure, containing a SYSI_InitVars far pointer and a SYSI_Country_Tab far pointer. First is stored in dosinfo and second in SYSI_Country. Initialise SYSI_EXT_MEM from 15.88 NC ax return. Initialise far pointer SYSI_IFS to all-1s. Initialise SingleBufferSize to [SYSI_MAXSEC] + BUFINSIZ. Initialise SYSI_BOOT_DRIVE to Default_Drive. Set SYSI_DWMOVE to 1 if running on a 386+. Set DriveNumber to SYSI_NUMIO. ax = cs - 11h, cx = SingleBuferSize / 16 + 1, ax -= cx, CONFBOT set to ax. Load far pointer to buffer info record, load far pointer HASH_PTR. Initialise BUFFER_BUCKET to ax. es = ax, init BUF_NEXT = BUF_PREV = 0, BUF_ID = 00FFh, BUF_SECTOR = 0. TEMPCDS: Allocate temporary CDS for SYSI_NUMIO drives, below segment indicated by CONFBOT. Set ALLOCLIM => temporary CDS. Far call to RE_INIT. RE_INIT: INT_2F_NEXT = int vector 2Fh, set int vector to INT_2F_13. Copy the process at segment MCB + 1 to cs - 10h (below SYSINITSEG, above CONFBOT), 256 bytes. Relocate PHT segment. Set the copy as current process. Call into DOS to set int 24h vector to SYSINITSEG:INT24. Allocate largest memory block using 21.48. Set AREA = MEMHI = ax. Set default drive from DEFAULT_DRIVE - 1 if the variable is nonzero. Call DOCONF, increment Multi_Pass_Id, call Multi_Pass, increment again, call Multi_Pass again. Call EndFile. If Install_Flag HAVE_INSTALL_CMD set then increment Multi_Pass_Id and call Multi_Pass again. Call LoadShare. DOCONF: Set switch char from system call. Open "\CONFIG.SYS". If open fails, set Multi_Pass_Id to 11 and return. Multi_Pass: If Multi_Pass_Id >= 10 then return immediately. ENDFILE: If multitrack flag is uninitialised, enable it. Set ALLOCLIM = CONFBOT and ax = same value. Call ROUND. ROUND: Preserve ax. Converts amount bytes in MEMLO to paragraphs and adds it to MEMHI, then resets MEMLO to 0. Then checks that MEMHI is not above-or-equal ALLOCLIM, otherwise branch to MEM_ERR, which shows "Configuration too large for memory" then loops infinitely. If devmark flag ([SetDevMarkFlag] & FOR_DEVMARK) is set then update [DevMark_Addr] => sub-MCB size and reset the flag. Return NC, CHG: -. Continue ENDFILE: Check FILES <= 5, if yes then skip SFT allocation. SFT allocation: Subtract 5 from FILES. Call SetDevMark with al = 'F' = DEVMARK_FILES. ax = FILES - 5. dx:bx = MEMHI:MEMLO. ds:di = DOSINFO, ds:di = SYSI_SFT. Set SFTC next pointer to dx:bx. es:di = MEMHI:MEMLO. Init SFTC next pointer offset to -1 and count to ax. Multiply al by SFT entry size, cx = multiply result, add ax to MEMLO, add 6 to MEMLO, set devmark flag, call ROUND. Zero all memory allocated to SFT entries. (NB, only accesses memory past SFTC fields after ROUND has returned.) Continue ENDFILE, DOFCBS: Call ROUND. Call SetDevMark with al = 'X' = DEVMARK_FCBS. Init FCB SFTs similar to SFTs, except the entries are all filled with the letter 'A' except for sf_ref_count and sf_position which are set to zero. SetDevMark: DevMark_Addr = memhi, init sub-MCB letter and "owner" to itself. Increment memhi. Default buffers handling: For every DPB call 21.4408, if returned ax == 0 and 21.440D.cx=0860 ds:dx -> BPB total sectors (NB, 16-bit) times BPB sector size <= 360 KiB then skip. If not skip, configure for 3 buffers. If all drives are nonremoveable or <= 360 KiB then use 2 buffers. If memory_size <= 2000h (paragraphs in 128 KiB) continue. If the memory_size <= 4000h (256 KiB) use 5 buffers. If memory_size <= 8000h (512 KiB) use 10 buffers. Else, use 15 buffers. Allocate hash buckets with 'B' devmark. Call Set_Buffer. This calls Set_Buffer_Info for every buffer. Eventually it sets the devmark flag and calls Round. (NB, in this case all buffers are already initialised.) Round is called with devmark flag set multiple times! Allocate then initialise CDS array, with amount of entries the larger of SYSI_NUMIO and NUM_CDS. Calls FOOSET (used by TEMPCDS as well). (NB, calls ROUND before writing to the allocated array.) Determine if to use IRQ stacks. Not used if model byte == FDh. If there is an explicit STACKS command do install. If secondary model byte != 00h do install. If model byte == FFh or == FEh then not used. If stack_count == 0 then not used. Point ds:si -> SYSINITSEG:0, es:di -> sub-MCB memory block, cx = Endstackcode. Allocate the sub-MCB size to cx bytes, then copy the stack code to the memory block. Store next paragraph boundary in the far pointer stack_addr. Multiply (STACK_SIZE + EntrySize) * STACK_COUNT. Allocate memory and grow sub-MCB. Call StackInit. Close standard input = 0, then loop for cx = [FILES] times (bug?) with bx = standard error = 2 initially, closing file handle and incrementing the handle in bx. Open CON with 21.3D02. If unsuccessful display error "Bad or missing CON". Close stdout, then dup CON handle 0 to 1 and 2. Open AUX (mode 2 "r/w") and PRN (mode 1 "write-only") using OPEN_DEV. OPEN_DEV: Receive ds:dx -> device filename, al = open mode. Try to open device using 21.3D. If CY return: Open NUL device instead. (Bug, uses the al return of the error code as new open mode for NUL.) If NC return: Call 21.4400 and check return dl & 80h (indicating character device), if set return. If clear (disk file), close handle and open NUL again (bug, doesn't initialise open mode). If model byte != FDh then: output FFh to some ports. If model byte (read from byte [0F000h:0FFFEh]) == FCh or 15.C0 returns NC and the byte [es:bx + 5] & 40h is set then output FFh to some more ports. Set Impossible_owner_size = memhi - area. Allocate 'T' sub-MCB. Allocate Size_SYSINIT_BASE bytes, and copy from SYSINIT_BASE to the sub-MCB. Init far pointer [Sysinit_Ptr] to cs:SYSINITPTR. Set flag [Install_Flag] HAS_INSTALLED. bx = MEMHI. es = Old_area = ax = AREA. bx -= ax. Call 21.4A. Decrement es, set owner = 8 and name = "SC" (bug!). Allocate largest memory block. Set memhi = ax (allocated memory block), memlo = 0. es = ax, bx = confbot - ax - 2. Call 21.4A to free confbot/sysinitseg memory. Allocate largest block. area = ax (allocated memory block). es = memhi, call 21.49 to free. Return from EndFile. If Install_Flag HAVE_INSTALL_CMD set then increment Multi_Pass_Id and call Multi_Pass again. Call LoadShare. LoadShare: If Big_Media_Flag != 1 then return immediately. If 2F.1000 returns al=FFh return immediately. Copy drive/path of SHELL= pathname. Append "SHARE.EXE" and pass it the command line option /NC. Set flag [Install_Flag] & SHARE_INSTALL and call Do_Install_Exec. If it returns CY display "WARNING! SHARE should be loaded for large media". After LoadShare returns: Free [area] using 21.49. If Install_flag HAS_INSTALLED set then resize block at Old_Area to Impossible_Owner_Size. Hack its MCB to read owner = 8 and name = "SD". Allocate largest block using 21.48. Immediately free block using 21.49. es => block, bp = size. Calculate bx = MEMORY_SIZE - cs + 11h, then bp -= bx. If 21.48 gives CY or bp -= bx carries, MEMERRJX. Compare size of shell executable in paragraphs + 10h paragraphs with bp, MEMERRJX if the executable is larger. (Bug: Shifting to get paragraph size is wrong, and MZ executables should calculate executable image length rather than taking the file size.) Run int 21.4B00. Note that SYSINITSEG is in free memory during this call. === section DOSSTART align=16. dosseg.nas Empty section to get start of linked in msdos and segment align it. === section START align=1. nibdos.nas Only contains msdos init jump (E9 xx xx). === section CONSTANTS align=2. nibdos.nas, const2.nas, msdosme.nas nibdos.nas Contains early DOS data segment contents, including int 21.52 data, the List of Lists (at offset 0026h) and the JShare jump table for the sharer (consists of 15 pointers, 14 used) (at offset 0090h). const2.nas Includes the first 5 SFT entries (at offset 00CCh), the pre-SDA, and the start of the SDA (Error Mode, InDOS bytes at offset 320h, 321h). msdosme.nas Includes the user number and OEM number. === section DATA align=2. msdata.nas msdata.nas / ms_data.nas Remainder of the SDA. Includes the three DOS stacks (at offset 0620h): aux, disk, and I/O stack (in this order), 180h = 384 bytes each. msdata.nas / msinit.nas Overlaps ms_data.nas part of SDA/stacks. Initialisation code, entered at label MOVDPB presumably to put DPBs in place. === section TABLE align=2 (used to be align=1). many files DOS tables(?). Data past the end of the SDA. Includes (ms_table.nas): * MSVERS, MS-DOS version for 21.30 * YRTAB, list of 4 two-byte entries that sum to days per year * MONTAB, list of days per month * I21_MAP_E_TAB, list of allowed errors per int 21h function * ERR_TABLE_21, extended error info per error code * ERR_TABLE_24, extended error info * ErrMap24, maps int 24h error to DOS int 21h errors * MAXCALL, byte holding maximum call 5 function * MAXCOM, byte holding maximum int 21h function * DISPATCH, int 21h function dispatch table * DOSTable, int 2Fh function 12h subfunction dispatch table * Misc other variables * HEADER, a version and attribution string * SysInitTable, probably passed to msbio/sysinit * FastTable, fastopen related * Dir_Info_Buf, directory entry buffer * NO_NAME_ID, constant string for uninitialised volume label * Swap Area Table, with space for 3 swap areas (2 used) === section CODE align=1. many files Actual DOS code. === section LAST align=16. msinit.nas Entrypoint DOSINIT, destination of the near jump in START section. Int 2Ah is initialised early, to an init iret (in section LAST). Calls init on msbio device drivers CON, AUX, PRN, CLOCK$, and subsequent devices (block devices expected to be after CLOCK$). Sets up DPBs starting at MEMSTRT (end of msdos LAST section) initially and relocates them to SYSBUF (start of LAST section) later, in MOVDPB (runs in section DATA). If HIGHMEM, the DOS segment is relocated to the top of the available LMA. Something is created either in place of the source DOS segment (if HIGHMEM) or after the last DPB + special list entry. The final DOS segment is initialised. The interrupt vectors 0h and 20h to 3Fh are initialised, except for int 29h. The sharer table is initialised (using cs unconditionally? Seems bad for HIGHMEM). The DOS data is initialised (again unconditional cs use). A process is created near the beginning of section DATA, rounded up to a paragraph boundary (calls SETMEM). The DPBs and special list are moved down to overwrite the DOS init code. A process is duplicated from the section DATA process to the first available DOS memory (calls D_Dup_PDB). An MCB is created with signature letter 'Z' and the size of the available memory, owner zero. Control is transferred back to msbio/sysinit.