PAGE ,132 TITLE LDBOOT - BootStrap procedure ?DFSTACK = 1 .xlist include kernel.inc include newexe.inc include tdb.inc include pdb.inc include eems.inc include protect.inc include gpcont.inc ifdef WOW include vint.inc include doswow.inc endif .list if 0; KDEBUG ifdef WOW BootTraceOn = 1 BootTrace macro char push ax mov ax,char int 3 pop ax endm else BootTraceOn = 1 BootTrace macro char push char call BootTraceChar endm endif else BootTrace macro char endm BootTraceOn = 0 endif ; Note that the following public constants require special handling ; for ROM Windows. If you add, delete, or change this list of constants, ; you must also check the ROM Image Builder. public __ahshift public __ahincr public __0000h public __0040h public __A000h public __B000h public __B800h public __C000h public __D000h public __E000h public __F000h public __ROMBIOS public __WinFlags public __flatcs public __flatds public __MOD_KERNEL public __MOD_DKERNEL public __MOD_USER public __MOD_DUSER public __MOD_GDI public __MOD_DGDI public __MOD_KEYBOARD public __MOD_SOUND public __MOD_SHELL public __MOD_WINSOCK public __MOD_TOOLHELP public __MOD_MMEDIA public __MOD_COMMDLG ifdef FE_SB public __MOD_WINNLS public __MOD_WIFEMAN endif ; FE_SB __ahshift = 3 __ahincr = 1 shl __ahshift __0000h = 00000h __0040h = 00040h __A000h = 0A000h __B000h = 0B000h __B800h = 0B800h __C000h = 0C000h __D000h = 0D000h __E000h = 0E000h __F000h = 0F000h __ROMBIOS = 0F000h __WinFlags = 1 __flatcs = 1Bh __flatds = 23h __MOD_KERNEL = 0h __MOD_DKERNEL = 0h __MOD_USER = 0h __MOD_DUSER = 0h __MOD_GDI = 0h __MOD_DGDI = 0h __MOD_KEYBOARD = 0h __MOD_SOUND = 0h __MOD_SHELL = 0h __MOD_WINSOCK = 0h __MOD_TOOLHELP = 0h __MOD_MMEDIA = 0h __MOD_COMMDLG = 0h ifdef FE_SB __MOD_WINNLS = 0h ; for IME __MOD_WIFEMAN = 0h ; for WIFEMAN endif ; FE_SB BOOTSTACKSIZE = 512 EXTRASTACKSIZE = (4096-BOOTSTACKSIZE) MultWIN386 EQU 16h ; Reserved to Win386 MW3_ReqInstall EQU 00h ; Installation check MW3_ReqInstall_Ret_1 EQU 0FFh ; Return number 1 MW3_ReqInstall_Ret_2 EQU 01h ; Return number 2 MW3_ReqInstall_Ret_3 EQU 02h ; Return number 3 MW3_ReqInstall_Ret_4 EQU 03h ; Return number 4 externFP lstrlen externFP lstrcat externFP lstrcpy externFP IGlobalAlloc externFP IGlobalFree externFP IGlobalRealloc externFP LoadModule externFP IOpenFile externFP lrusweep externFP GetExePtr externFP GetProfileInt externFP GetPrivateProfileString externFP GetPrivateProfileInt externFP GetProcAddress externFP GetTempDrive externFP ExitKernel externFP InternalEnableDOS externFP FlushCachedFileHandle externFP SetHandleCount externFP IPrestoChangoSelector externFP GPFault externFP StackFault externFP invalid_op_code_exception externFP page_fault ifdef WOW externFP divide_overflow externFP single_step externFP breakpoint endif externFP DiagInit ifdef WOW externFP StartWOWTask externFP AllocSelector_0x47 externW MOD_KERNEL externW ModCount externFP WOWGetTableOffsets externFP WOWDosWowInit externFP GetShortPathName endif if KDEBUG externFP ValidateCodeSegments endif ifdef FE_SB externFP GetSystemDefaultLangID endif externFP TermsrvGetWindowsDir externW pStackBot externW pStackMin externW pStackTop sBegin CODE externFP Int21Handler externFP Int10Handler externD prevInt10proc sEnd CODE ;------------------------------------------------------------------------ ; Data Segment Variables ;------------------------------------------------------------------------ DataBegin if SHERLOCK externW gpEnable endif externB graphics externB fBooting externB Kernel_flags externB fChkSum externB fCheckFree externB WOAName externB grab_name ifndef WOW externB szUserPro endif externB szBootLoad externB szCRLF externB szMissingMod externB szPleaseDoIt externB fPadCode ;externW EMScurPID ;externW PID_for_fake externW cBytesWinDir externW cBytesSysDir externW pGlobalHeap externW hExeHead externW MaxCodeSwapArea externW curTDB externW f8087 externB fastFP externW topPDB externW headTDB externW winVer ifndef WOW externB WinIniInfo externB PrivateProInfo endif externW gmove_stack externW prev_gmove_SS externW BaseDsc externW WinFlags externW hUser externW hShell externW MyCSSeg externW MyDSSeg externW MyCSAlias externB fhCache externW fhCacheEnd externW fhCacheLen externB fPokeAtSegments externB fExitOnLastApp externW segLoadBlock externD pKeyProc externD pKeyProc1 externW wDefRIP ife ROM externW cpShrunk externW cpShrink externW hLoadBlock endif if ROM externW selROMTOC externW selROMLDT externW sel1stAvail endif externD pTimerProc externD pExitProc externD pDisableProc externD lpWindowsDir externD lpSystemDir if ROM externD prevIntx6proc externD prevInt0Cproc externD prevInt0Dproc externD prevInt0Eproc externD prevInt21proc externD prevInt3Fproc endif ;if KDEBUG and SWAPPRO ;externD prevIntF0proc ;endif externD lpInt21 ; support for NOVELL stealing int 21h ifdef WOW externD pFileTable externW cBytesSys16Dir externD lpSystem16Dir externB Sys16Suffix externW cBytesSys16Suffix externW cBytesSysWx86Dir externD lpSystemWx86Dir externB SysWx86Suffix externW cBytesSysWx86Suffix externD pPMDosCURDRV externD pPMDosCDSCNT externD pPMDosPDB externD pPMDosExterr externD pPMDosExterrLocus externD pPMDosExterrActionClass externD pDosWowData globalW cbRealWindowsDir,0 WINDIR_BUFSIZE equ 121 achWindowsDir DB WINDIR_BUFSIZE DUP(?) achRealWindowsDir DB WINDIR_BUFSIZE DUP(?) achSystem16Dir DB 128 DUP(?) achSystemWx86Dir DB 128 DUP(?) public cbRealWindowsDir, achRealWindowsDir, achWindowsDir, achSystem16Dir externB achTermSrvWindowsDir ; windows directory path (for win.ini) endif DataEnd ;------------------------------------------------------------------------ ; INITDATA Variables ;------------------------------------------------------------------------ DataBegin INIT ifndef WOW ; WOW doesn't muck with the WOAName buffer -- we just leave it ; as WINOLDAP.MOD externB woa_286 externB woa_386 endif externB bootExecBlock externW oNRSeg externW oMSeg externW win_show externD lpBootApp if ROM externD lmaROMTOC staticD MyLmaROMTOC,lmaROMTOC .errnz opLen .errnz 8 - opFile ROMKRNL db 19 ; mocked up open file structure db 7 dup (0) ; for ROM Kernel db 'ROMKRNL.EXE',0 endif staticW initTDBbias,0 staticW initSP,0 staticW initSSbias,0 staticW segNewCS,0 app_name db 68 dup(0) DataEnd INIT ;------------------------------------------------------------------------ ; EMSDATA Variables ;------------------------------------------------------------------------ ;------------------------------------------------------------------------ externNP LoadSegment externNP genter externNP gleave externNP GlobalInit externNP DeleteTask externNP BootSchedule externNP InitFwdRef externNP SaveState externNP LKExeHeader externNP GetPureName externNP SegmentNotPresentFault externNP LDT_Init externNP alloc_data_sel externNP get_physical_address externNP set_physical_address externNP set_sel_limit externNP free_sel externFP set_discarded_sel_owner externNP SelToSeg externNP DebugDefineSegment externNP DebugFreeSegment ife ROM externNP SwitchToPMODE externNP LKAllocSegs endif if ROM and PMODE32 externFP HocusROMBase endif ifdef WOW externFP WOWFastBopInit endif if KDEBUG ife PMODE32 externNP init_free_to_CCCC endif if SWAPPRO externB fSwapPro externW hSwapPro externW cur_dos_pdb endif endif ifdef FE_SB externFP FarMyIsDBCSTrailByte endif ;------------------------------------------------------------------------ sBegin INITCODE assumes cs,CODE ife ROM externD prevIntx6proc externD prevInt0Cproc externD prevInt0Dproc externD prevInt0Eproc externD prevInt21proc externD prevInt3Fproc ifdef WOW externD prevInt01proc externD prevInt03proc externD oldInt00proc externFP GlobalDosAlloc externFP GlobalDosFree endif endif externNP TextMode externNP InitDosVarP externNP GrowSFTToMax ifndef WOW externNP SetUserPro endif externNP MyLock externNP Shrink externNP DebugDebug externNP NoOpenFile externNP NoLoadHeader if SDEBUG externNP DebugInit endif ;if SWAPPRO ;externNP INTF0Handler ;endif if SDEBUG szModName db 'KERNEL',0 endif if ROM externNP ROMInit externNP SetOwner externNP SetROMOwner externNP alloc_data_sel16 if KDEBUG externNP CheckGlobalHeap endif endif ifdef WOW externNP SetOwner endif ;-----------------------------------------------------------------------; ; Bootstrap ; ; ; ; Determines whether we should initialize in a smaller amount of ; ; memory, leaving room for an EEMS swap area. If so, we rep-move the ; ; code to a lower address, and tell the PSP to report less memory ; ; available. It then does lots more of stuff. ; ; ; ; Arguments: ; ; DS:0 = new EXE header ; ; ES:0 = Program Segment Prefix block (PSP) ; ; SS:SP = new EXE header ; ; CX = file offset of new EXE header (set by KernStub) ; ; DS = automatic data segment if there is one ; ; ; ; Returns: ; ; ; ; Error Returns: ; ; ; ; Registers Preserved: ; ; ; ; Registers Destroyed: ; ; ; ; Calls: ; ; ; ; History: ; ; ; ; Sat Jun 20, 1987 06:00:00p -by- David N. Weise [davidw] ; ; Made fast booting work with real EMS. ; ; ; ; Tue Apr 21, 1987 06:31:42p -by- David N. Weise [davidw] ; ; Added some more EMS support. ; ; ; ; Thu Apr 09, 1987 02:52:37p -by- David N. Weise [davidw] ; ; Put back in the movement down if EMS. ; ; ; ; Sat Mar 14, 1987 05:55:29p -by- David N. Weise [davidw] ; ; Added this nifty comment block. ; ;-----------------------------------------------------------------------; assumes ds,DATA assumes es,nothing assumes ss,nothing cProc BootStrap, cBegin nogen ifndef NEC_98 if KDEBUG jmp short Hello_WDEB_End Hello_WDEB: db 'Windows Kernel Entry',13,10,0 Hello_WDEB_End: push ax push es push si xor ax, ax mov es, ax mov ax, es:[68h*4] or ax, es:[68h*4+2] jz @F mov ax,cs mov es,ax lea si,Hello_WDEB mov ah,47h int 68h @@: pop si pop es pop ax endif endif ; NEC_98 BootTrace 'a' if ROM call ROMInit ; BX -> LDT, DS -> RAM DGROUP, SI -> start mem else cmp ax, "KO" ; OK to boot; set by kernstub.asm je @F xor ax, ax retf @@: ; Set up addressibility to our code & data segments mov MyCSSeg, cs mov MyDSSeg, ds endif ;ROM ifdef WOW ; Get pointer to sft so that I can find do direct protect mode ; file IO operations mov CS:MyCSDS, ds push es mov ah,52h ; get pointer to internal vars int 21h ; ; Retrieve some pointers from the DosWowData structure in DOS. ; push di push dx mov di, es:[bx+6ah] ;kernel data pointer mov pDosWowData.off, di mov pDosWowData.sel, es mov ax, word ptr es:[di].DWD_lpCurPDB ; mov dx, word ptr es:[di].DWD_lpCurPDB+2 mov pPMDosPDB.off,ax mov pPMDosPDB.sel,0 ;to force gpfault if not ready mov ax, word ptr es:[di].DWD_lpCurDrv ; mov dx, word ptr es:[di].DWD_lpCurDrv+2 mov pPMDosCURDRV.off,ax mov pPMDosCURDRV.sel,0 ;to force gpfault if not ready mov ax, word ptr es:[di].DWD_lpCDSCount ; mov dx, word ptr es:[di].DWD_lpCDSCount+2 mov pPMDosCDSCNT.off,ax mov pPMDosCDSCNT.sel,0 ;to force gpfault if not ready mov ax, word ptr es:[di].DWD_lpExterr mov pPMDosExterr.off, ax mov pPMDosExtErr.sel, 0 mov ax, word ptr es:[di].DWD_lpExterrLocus mov pPMDosExterrLocus.off, ax mov pPMDosExtErrLocus.sel, 0 mov ax, word ptr es:[di].DWD_lpExterrActionClass mov pPMDosExterrActionClass.off, ax mov pPMDosExtErrActionClass.sel, 0 pop dx pop di lea bx,[bx+sftHead] mov pFileTable.off,bx mov pFileTable.sel,es pop es endif ; WOW ife ROM BootTrace 'b' call SwitchToPMODE ; BX -> LDT, DS -> CS(DS), SI start of memory BootTrace 'c' endif ReSetKernelDS mov BaseDsc,si call LDT_Init BootTrace 'd' ifdef WOW call AllocSelector_0x47 call WOWFastBopInit endif if SDEBUG ; In protected mode, initialize the debugger interface NOW. Only real mode ; needs to wait until the global heap is initialized. cCall DebugInit ; In protected mode, define where our temp Code & Data segments are mov ax,codeOffset szModName cCall DebugDefineSegment, mov ax,codeOffset szModName cCall DebugDefineSegment, endif BootTrace 'e' if ROM ; In the ROM version, the DS selector doesn't change from now on, so we ; can tell the debugger where the special kernel data is now. cCall DebugDebug endif BootTrace 'f' ifdef WOW push ax push bx mov bx, pDosWowData.sel ;use this segment mov ax, 2 int 31h mov pPMDosPDB.sel,ax ;make this a PM pointer mov pPMDosCURDRV.sel,ax ;make this a PM pointer mov pPMDosCDSCNT.sel,ax ;make this a PM pointer mov pPMDosExterr.sel,ax mov pPMDosExterrLocus.sel,ax mov pPMDosExterrActionClass.sel,ax pop bx pop ax push pDosWowData.sel push pDosWowData.off call WOWDosWowInit endif ; InitDosVarP just records a few variables, it does no hooking. ; It does dos version checking and other fun stuff that ; must be done as soon as possible. call InitDosVarP or ax,ax jnz inited_ok Debug_Out "KERNEL: InitDosVarP failed" mov ax,4CFFh ; Goodbye! INT21 inited_ok: BootTrace 'g' push bx mov bx, WinFlags ifndef JAPAN ; should be removed because of IBM dependent code. ifndef NEC_98 ; InitFwdRef routine will set 80x87 status bit in WinFlags ; and exported location #178. ; Determine if there is a co-processor present. int 11h ; get equipment word test al,2 ; this is the IBM approved method jz no_80x87 ; to check for an 8087 or bh,WF1_80x87 no_80x87: endif ; NEC_98 endif ;NOT JAPAN ifdef WOW or bh,WF1_WINNT ; Set NT flag ON endif mov WinFlags,bx pop bx ; Determine if running under Windows/386 or the 286 DOS Extender push bx mov ax,(MultWin386 SHL 8) OR MW3_ReqInstall ; Win/386 install check int 2Fh BootTrace 'h' cmp al,MW3_ReqInstall_Ret_4 ; Under WIN386 in pmode? ifdef WOW ; For WOW Enhanced Mode on 386 ife PMODE32 jnz NotUnderWin386 endif endif or Kernel_flags[1],kf1_Win386 or byte ptr WinFlags,WF_PMODE or WF_ENHANCED jmps InstallChkDone NotUnderWin386: or Kernel_flags[2],KF2_DOSX or byte ptr WinFlags,WF_PMODE or WF_STANDARD InstallChkDone: BootTrace 'i' pop bx ifndef WOW ; WOW doesn't muck with the WOAName buffer -- we just leave it ; as WINOLDAP.MOD push cx push di push si cld mov cx,8 smov es,ds mov di,dataOffset WOAName mov si,dataOffset woa_286 test Kernel_flags[2],KF2_DOSX jnz @F mov si,dataOffset woa_386 @@: rep movsb pop si pop di pop cx endif BootTrace 'j' ife ROM ;-------------------------------------------------------- mov ax,cx mov cl,4 shr ax,cl mov cpShrunk,ax ; Compute paragraph address of new EXE header from SS:SP mov bx,sp ; SS:SP -> new EXE header cCall get_physical_address, add ax,bx adc dx,0 BootTrace 'k' if PMODE32 cCall alloc_data_sel, else cCall alloc_data_sel, endif BootTrace 'l' else ; ROM --------------------------------------------------------- ; Setup selector to ROM Table of Contents mov ax,word ptr [MyLmaROMTOC] mov dx,word ptr [MyLmaROMTOC+2] cCall alloc_data_sel16, if PMODE32 cCall HocusROMBase, endif mov selROMTOC,ax ; Setup another selector to the ROM prototype LDT mov es,ax assumes es,nothing mov ax,word ptr es:[lmaROMLDT] mov dx,word ptr es:[lmaROMLDT+2] cCall alloc_data_sel16, if PMODE32 cCall HocusROMBase, endif mov selROMLDT,ax mov ax,es:[FirstROMsel] shr ax,3 add ax,es:[cROMsels] shl ax,3 ; ax = 1st non-ROM selector mov sel1stAvail,ax ; Setup selector to KERNEL EXE header in ROM. **ASSUMES KERNEL is 1st ; module in ROM TOC** mov ax,word ptr es:[ModEntries+lmaExeHdr] mov dx,word ptr es:[ModEntries+lmaExeHdr+2] cCall alloc_data_sel16, if PMODE32 cCall HocusROMBase, endif endif ; ROM --------------------------------------------------------- mov segLoadBlock,ax ; hinitexe:0 -> new EXE header ; calculate the TDB bias mov bx,dataOffset boottdb mov initTDBbias,bx ; calculate the SS bias mov bx,dataOffset stackbottom mov initSSbias,bx ; calculate the initial SP mov si,dataOffset stacktop sub si,dataOffset stackbottom mov initSP,si cCall get_physical_address, add ax,bx adc dx,0 BootTrace 'm' if ROM cCall alloc_data_sel16, FCLI else FCLI mov prev_gmove_SS, ss ; Switch stack while we set up new SS smov ss, ds mov sp, dataOFFSET gmove_stack cCall set_physical_address, xor ax, ax xchg ax, prev_gmove_SS endif BootTrace 'n' mov ss,ax ; switch to new stack mov sp,si FSTI xor bp,bp ; zero terminate BP chain. sub si,BOOTSTACKSIZE mov ss:[pStackBot],sp mov ss:[pStackMin],sp mov ss:[pStackTop],si cld mov es,topPDB externB szNoGlobalInit if ROM ;---------------------------------------------------------------- ; In the ROM Windows version, the "allocated" block in the global heap is ; the kernel data segment, not the code segments mov bx,ds ; kernel DS is first busy block lsl bx,bx ; pass its size to GlobalInit inc bx cCall GlobalInit, jnc mem_init_ok if KDEBUG Debug_Out "GlobalInit failed!" else mov dx, codeoffset szNoGlobalInit smov ds, cs mov ah, 9 int 21h ; Whine mov ax, 4CFFh int 21h ; And exit ;NoGlobalInit db "KERNEL: Unable to initialise heap",13,10,'$' endif mem_init_ok: if KDEBUG cCall CheckGlobalHeap endif else ;ROM --------------------------------------------------------- BootTrace 'o' mov ax, BaseDsc ; Free memory starts after this mov bx,segLoadBlock ; Make boot image be first busy block mov cx,es:[PDB_block_len] ; cx is end of memory mov dx,MASTER_OBJECT_SIZE cCall GlobalInit, jc @F ; passed through from ginit or ax,ax jnz mem_init_ok @@: mov dx, codeoffset szNoGlobalInit smov ds, cs mov ah, 9 int 21h ; Whine mov ax, 4CFFh int 21h ; And exit ;NoGlobalInit db "KERNEL: Unable to initialise heap",13,10,'$' mem_init_ok: mov hLoadBlock,ax ; Save handle to first busy block endif ;ROM --------------------------------------------------------- BootTrace 'p' mov pExitProc.sel,cs mov pExitProc.off,codeOffset ExitKernel ; Find out where we live, and where win.com lives. mov ds,topPDB UnSetKernelDS mov bx,ds mov ds,ds:[PDB_environ] xor si,si cld envloop1: lodsb or al,al ; end of item? jnz envloop1 lodsb or al,al ; end of environment? jnz envloop1 lodsw ; ignore argc, DS:SI -> kernel path ifdef WOW smov es, ds ; now ES:SI -> kernel path else call get_windir ; on return, DS:DI -> windir= value smov es,ds ; Record where to find the 'windows' directory. BootTrace 'q' SetKernelDS or di,di jz no_win_dir_yet mov lpWindowsDir.sel,es mov lpWindowsDir.off,di mov cBytesWinDir,cx ; Now set pointer to WIN.INI to be ; in the Windows directory push si push es smov es, ds mov si, dataoffset szUserPro+6 mov di, si inc cx add di, cx ; Move up the string WIN.INI std mov cx, 4 rep movsw ; Now copy Windows directory cld mov cx, cBytesWinDir mov di, dataoffset szUserPro lds si, lpWindowsDir UnSetKernelDS rep movsb mov byte ptr es:[di], '\' pop es pop si endif SetKernelDS BootTrace 'r' no_win_dir_yet: if ROM ;---------------------------------------------------------------- ; 'Load' the kernel exe header from ROM. mov si,dataOffset ROMKRNL cCall LKExeHeader, or ax,ax jnz hdr_ok jmp bootfail hdr_ok: else ;ROM --------------------------------------------------------- sub sp,SIZE OPENSTRUC + 127 mov di,sp regptr ssdi,ss,di mov bx,OF_EXIST cCall IOpenFile, BootTrace 's' inc ax ; Test for -1 jnz opn1 ; Continue if success mov dx, codeoffset NoOpenFile fail1: push dx ; Save string pointer call textmode ; Switch to text mode pop dx smov ds, cs mov ah, 9 int 21h ; Tell user why we're bailing out mov ax,4CFFh ; Goodbye! INT21 opn1: ; Now simulate loading ourselves from memory ; Load new EXE header for KERNEL.EXE from memory cCall LKExeHeader, BootTrace 't' add sp,SIZE OPENSTRUC + 127 or ax,ax jnz @F mov dx, codeoffset NoLoadHeader jmp SHORT fail1 @@: endif ;ROM --------------------------------------------------------- mov hExeHead,ax mov es,ax ifndef WOW ; Record where to find the 'system' directory. if ROM ;---------------------------------------------------------------- push es mov es,selROMTOC ; ROM system dir @ selROMTOC:offSysDir mov di,es:offSysDir mov lpSystemDir.sel,es mov lpSystemDir.off,di xor ax,ax mov cx,-1 cld repne scasb not cx dec cx mov cBytesSysDir,cx pop es else ;ROM --------------------------------------------------------- mov di,es:[ne_pfileinfo] lea di,es:[di].opFile mov lpSystemDir.sel,es mov lpSystemDir.off,di mov dx,di call GetPureName sub di,dx dec di mov cBytesSysDir,di endif ;ROM --------------------------------------------------------- BootTrace 'u' endif ; ndef WOW ; Make room at end of kernel data segment for stack--NOTE: DGROUP is ; assumed to be segment 4! mov bx,es:[ne_segtab] ife ROM add es:[bx+(3*size NEW_SEG1)].ns_minalloc,EXTRASTACKSIZE endif ; Determine size of kernel's largest discardable code segment ; don't bother with swap area's anymore. Its fixed 192k (3segs) ; picked this up from Win95 xor ax,ax ; No max mov es:[ne_swaparea],ax BootTrace 'v' ife ROM ;---------------------------------------------------------------- ; Allocate memory for kernel segments cCall LKAllocSegs, mov oNRSeg,ax mov oMSeg, bx ; Misc segment mov es,hExeHead ; If this is a pMode debug version, the code and data segments have already ; been defined to the debugger once. We're about to LoadSegment and define ; these segments again in their final location. Use a special form of ; DebugFreeSegment to force the debugger to pull out any breakpoints in these ; segments. If we don't do this, any existing breakpoints become INT 3's ; in the new copy of the segment and the code has to be patched by hand. ; If only the debugger was smart enough to 'move' the breakpoints when it ; saw a second define for an already loaded segment... if SDEBUG cCall DebugFreeSegment, cCall DebugFreeSegment, endif ; Load kernel code segment 1 (resident code) mov si,1 mov ax,-1 ; Indicate loading from memory cCall LoadSegment, or ax,ax jnz ll1 fail2: jmp bootfail ll1: mov segNewCS,ax ; Save new CS value ; Load kernel data segment (segment 4) mov si,4 mov ax,-1 cCall LoadSegment, or ax,ax jz fail2 BootTrace 'w' ; locate the stack in the new segment mov bx,ax mov si,initSP add si,EXTRASTACKSIZE else ;ROM --------------------------------------------------------- cCall SetOwner, ;Who owns ya baby? cCall SetROMOwner, ;IGROUP isn't loaded, but needs owner ; locate the stack at the end of the data segment mov ax, ds mov bx, ax mov si, initSP add si, ROMEXTRASTACKSZ endif ;ROM --------------------------------------------------------- cCall get_physical_address, add ax,initSSbias adc dx,0 sub ax,10h sbb dx,0 or ax,10h FCLI mov prev_gmove_SS, ss ; Switch stack while we set up new SS smov ss, ds mov sp, OFFSET gmove_stack cCall set_physical_address, push bx mov bx, si xor cx, cx ; cx:bx=stack len (for set_sel_limit) cCall set_sel_limit, pop bx xor ax, ax xchg ax, prev_gmove_SS mov ss,ax ; Switch to new stack location mov sp,si FSTI mov ax,bx ; zero the new TDB push ax cCall get_physical_address, add ax,initTDBbias adc dx,0 if PMODE32 YAH_WELL = (SIZE TDB+15) and NOT 15 cCall alloc_data_sel, else YAH_WELL = (SIZE TDB+15)/16 cCall alloc_data_sel, endif mov es,ax xor ax,ax mov di,ax mov cx,SIZE TDB cld rep stosb pop ax ; put the limits in the stack xor bp,bp ; zero terminate BP chain. mov ss:[pStackBot],sp mov ss:[pStackMin],sp mov ss:[pStackTop],10h mov ss:[0],bp ; To mark this stack as NOT linked mov ss:[2],bp ; to another, see PatchStack. mov ss:[4],bp ; initialize the new TDB sub sp,SIZE TASK_REGS mov es:[TDB_taskSS],ss mov es:[TDB_taskSP],sp mov cx,topPDB mov es:[TDB_PDB],cx ; save new PDB mov es:[TDB_DTA].off,80h ; set initial DTA mov es:[TDB_DTA].sel,cx mov bx,1 ; BX = 1 mov es:[TDB_nEvents],bx ; Start this guy up! mov es:[TDB_pModule],-1 ; EMS requires -1 mov bx,winVer mov es:[TDB_ExpWinVer],bx ; Windows Version # mov es:[TDB_sig],TDB_SIGNATURE ; Set signature word. ; initialize the task BP and DS push es les bx,dword ptr es:[TDB_taskSP] mov es:[bx].TASK_BP,bp ; initial BP = 0 mov es:[bx].TASK_DS,bp ; initial DS = 0 pop es ife ROM ;--------------------------------------------------------------- mov ds,ax ; switch to new DS segment mov ax,segNewCS ; recover new CS value push cs ; to free the selector ; do a far return to the new code segment push ax mov ax,codeOffset new_code push ax ret_far ; FAR return to new code segment public new_code new_code: pop ax push es cCall free_sel, ; Free old CS selector cCall IPrestoChangoSelector, ; Change our CS Alias mov es,MyCSAlias ; Change MyCSDS to new DS assumes es,CODE mov ax,ds xchg ax,es:MyCSDS assumes es,nothing cCall free_sel, ; Free old DS selector call DebugDebug ; do after MyCSDS changes mov pExitProc.sel,cs ; reset this!! ;;; pusha ;;; cCall get_physical_address, ;;; add ax, codeOffset end_page_locked ;;; adc dx, 0 ;;; mov bx, dx ;;; mov cx, ax ;;; mov di, cs ;;; lsl di, di ;;; sub di, codeOffset end_page_locked ;;; xor si, si ;;; mov ax, 0601h ;;; int 31h ; Unlock some of Kernel! ;;; popa cCall SelToSeg, ; Save DS segment value mov MyDSSeg,ax cCall SelToSeg, ; Set segment equivalent mov MyCSSeg, ax ; calculate the maximum amount that we will allow SetSwapAreaSize to set mov ax,-1 ; They can only harm themselves. else ;ROM --------------------------------------------------------- push es ; code below expects this on stack mov ax,-1 ; They can only harm themselves. endif ;ROM --------------------------------------------------------- mov MaxCodeSwapArea,ax ifndef WOW ; WOW uses 32 bit profile api's ; Allocate a handle for WIN.INI xor ax,ax mov bx,GA_SHAREABLE shl 8 OR GA_MOVEABLE cCall IGlobalAlloc, mov [WinIniInfo.hBuffer], ax or ax,ax jz nowinini mov bx,ax ; put handle into base register mov ax,hExeHead mov es,ax call set_discarded_sel_owner ; Set up the filename mov word ptr [WinIniInfo.lpProFile], dataoffset szUserPro mov word ptr [WinIniInfo.lpProFile][2], ds nowinini: ; Allocate a handle for Private Profiles xor ax,ax mov bx,GA_SHAREABLE shl 8 OR GA_MOVEABLE cCall IGlobalAlloc, mov [PrivateProInfo.hBuffer],ax or ax,ax jz noprivate mov bx,ax ; put handle into base register mov ax,hExeHead mov es,ax call set_discarded_sel_owner noprivate: endif; WOW ifdef WOW ; Allocate a ~128K discardable code selector to hide the ; GrowHeap - heap not sorted bugs, also present in Win 3.1. ; ; Since our system DLLs (like krnl386, user, gdi etc) contain a few ; discardable codesgements (of approx 40K), we need not be precise ; push es mov ax, 0C000H ; hiword is 01h , totalsize 0x1c000 bytes mov bx, GA_MOVEABLE OR GA_DISCCODE cCall IGlobalAlloc, or ax,ax jz short nogrowheap cCall SetOwner, nogrowheap: pop es endif; WOW ife ROM ;---------------------------------------------------------------- ; Now shrink off exe header and segment 1 of KERNEL.EXE mov si,2 ; Segment number xor ax,ax xchg oNRSeg,ax nofastboot: sub ax,cpShrunk mov cpShrink,ax ; Amount to shrink by cCall MyLock, mov bx,ax xchg segLoadBlock,ax cCall get_physical_address, mov cx,dx xchg bx,ax cCall get_physical_address, sub bx,ax sbb cx,dx REPT 4 shr cx,1 rcr bx,1 ENDM mov ax,bx add cpShrink,ax push ax cCall Shrink pop ax sub cpShrunk,ax ; Load kernel code segment 2 (non-resident code) cCall MyLock, mov bx,-1 ; Indicate loading from memory cCall LoadSegment, or ax,ax jnz ll2 pop es jmp bootfail ll2: inc si ; On to segment 3 xor ax, ax xchg ax, oMSeg ; Once back only! or ax, ax jnz nofastboot else ;ROM --------------------------------------------------------- ; The ROM kernel 'loads' the other two kernel segments now. Currently ; LoadSegment does little more than define the segment to the debugger ; which has already been done for the primary code & data segments. cCall LoadSegment, cCall SetROMOwner, cCall LoadSegment, cCall SetROMOwner, endif ;ROM --------------------------------------------------------- pop es call genter smov ds, es UnSetKernelDS SetKernelDS es ;;;mov ax,EMScurPID ; In case an EMS fast boot is going down. ;;;mov ds:[TDB_EMSPID],ax ;;;mov ax,ds:[TDB_EMSPID] ;;;mov PID_for_fake,ax ;;;mov EMScurPID,ax mov curTDB,ds mov headTDB,ds push es ; vectors SaveVec MACRO vec mov ax, 35&vec DOSCALL mov [di.off], bx mov [di.sel], es add di, 4 ENDM push di mov di, TDB_INTVECS SaveVec 00h SaveVec 02h SaveVec 04h SaveVec 06h SaveVec 07h SaveVec 3Eh SaveVec 75h ifdef WOW ;; Hook Int10 so we can filter calls in WOW (see intnn.asm) push es push ds mov ax,3510h INT21 mov ax,codeOffset prevInt10proc SetKernelCSDword ax,es,bx mov ax,2510h smov ds,cs mov dx,codeOFFSET Int10Handler INT21 pop ds pop es endif pop di cCall SaveState, pop es mov ds,pGlobalHeap call gleave UnSetKernelDS es mov ax, 32 ; Kernel wants a big handle table cCall SetHandleCount, SetKernelDS ; ; The following variable initialization is done here to avoid having ; relocations in Kernel's data segment. ; mov lpInt21.off,codeOFFSET Int21Handler mov lpInt21.sel,cs ; ; Before we hook exception handlers, make sure the DPMI exception ; handler stack is set up the way Windows likes it. ; mov bl,6 mov ax,0202h ; DPMI get exception handler vector int 31h push cx push dx mov cx,cs lea dx,fixing_stack mov bl,6 mov ax,0203h ; DPMI set exception handler vector int 31h pop dx pop cx ; ; Generate an invalid opcode exception fault. This causes DPMI to call ; our "exception handler." ; db 0fh,0ffh fixing_stack: push bp mov bp,sp ; BP -> BP RETIP RETCS EC IP CS FL SP SS ; ; Restore the previous invalid exception handler vector. ; mov bl,6 mov ax,0203h int 31h ; ; Replace the return address on the DPMI fault handler routine with ; our exit code. ; lea ax,done_fixing_stack mov [bp+8],ax mov [bp+10],cs lea ax,[bp+16] mov ss:[pStackBot],ax mov ss:[pStackMin],sp mov ss:[pStackTop],offset pStackBot + 150 mov sp,bp pop bp retf done_fixing_stack: ife ROM mov es, MyCSAlias assumes es, CODE endif ; Hook not present fault for segment reloader. mov ax,0202h ; Record old not present fault. mov bl,0Bh int 31h mov prevInt3Fproc.off,dx mov prevInt3Fproc.sel,cx mov ax,0203h ; Hook not present fault. mov cx,cs mov dx,codeOffset SegmentNotPresentFault int 31h ; Hook GP fault in order to terminate app. mov bl, 0Dh ; GP Fault mov ax, 0202h int 31h mov prevInt0Dproc.off, dx mov prevInt0Dproc.sel, cx mov ax, 0203h mov cx, cs mov dx, codeOffset GPFault int 31h ; Hook invalid op-code in order to terminate app. mov bl, 06h ; invalid op-code mov ax, 0202h int 31h mov prevIntx6proc.off, dx mov prevIntx6proc.sel, cx mov ax, 0203h mov cx, cs mov dx, codeOffset invalid_op_code_exception int 31h ; Hook stack fault in order to terminate app. mov bl, 0Ch ; stack fault mov ax, 0202h int 31h mov prevInt0Cproc.off, dx mov prevInt0Cproc.sel, cx mov ax, 0203h mov cx, cs mov dx, codeOffset StackFault int 31h ; Hook bad page fault in order to terminate app. mov bl, 0Eh ; page fault mov ax, 0202h int 31h mov prevInt0Eproc.off, dx mov prevInt0Eproc.sel, cx mov ax, 0203h mov cx, cs mov dx, codeOffset page_fault int 31h ifdef WOW ; Hook divide overflow trap in order to get better WOW debugging. mov bl, 00h ; divide overflow mov ax, 0202h int 31h mov oldInt00proc.off, dx mov oldInt00proc.sel, cx mov ax, 0203h mov cx, cs mov dx, codeOffset divide_overflow int 31h ; Hook single step trap in order to get better WOW debugging. mov bl, 01h ; single step mov ax, 0202h int 31h mov prevInt01proc.off, dx mov prevInt01proc.sel, cx mov ax, 0203h mov cx, cs mov dx, codeOffset single_step int 31h ; Hook breakpoint trap in order to get better WOW debugging. mov bl, 03h ; page fault mov ax, 0202h int 31h mov prevInt03proc.off, dx mov prevInt03proc.sel, cx mov ax, 0203h mov cx, cs mov dx, codeOffset breakpoint int 31h endif assumes es, nothing ; Do a slimy fix-up of __WinFlags containing processor and protect mode flags xor ax,ax mov dx,178 cCall GetProcAddress, mov ax,WinFlags mov es:[bx],ax ifdef WOW ; get WOW32 thunk table offsets and do fixups ; WARNING: WOW32 has a dependency on this being called after ; kernel is done booting and addresses are fixed push ds push dataoffset MOD_KERNEL call far ptr WOWGetTableOffsets mov si, dataoffset MOD_KERNEL mov cx, ModCount ; # fixups to do mov di, 570 ; first ordinal of the group (DANGER hardcoded from kernel.def) Mexico: push si push di push cx push hExeHead push 0 push di call GetProcAddress pop cx pop di pop si mov ax,[si] mov es:[bx],ax inc si ; point to next word inc si inc di ; get next ordinal loop Mexico endif ife ROM ;-------------------------------- ; Can't do slimy fix-ups in ROM--already done by ROM Image Builder ; Do a very slimy fix-up of the runtime constant __0000h cCall GetProcAddress, mov si,bx mov bx,00000h mov ax,2 int 31h mov es:[si],ax ; Do a very slimy fix-up of the runtime constant __0040h cCall GetProcAddress, mov si,bx mov bx,00040h mov ax,2 int 31h mov es:[si],ax ; Do a very slimy fix-up of the runtime constant __ROMBIOS cCall GetProcAddress, mov si,bx mov bx,0F000h mov ax,2 int 31h mov es:[si],ax ; Do a very slimy fix-up of the runtime constant __F000h cCall GetProcAddress, mov si,bx mov bx,0F000h mov ax,2 int 31h mov es:[si],ax ; Do a very slimy fix-up of the runtime constant __A000h cCall GetProcAddress, mov si,bx mov bx,0A000h mov ax,2 int 31h mov es:[si],ax ; Do a very slimy fix-up of the runtime constant __B000h cCall GetProcAddress, mov si,bx mov bx,0B000h mov ax,2 int 31h mov es:[si],ax ; Do a very slimy fix-up of the runtime constant __B800h cCall GetProcAddress, mov si,bx mov bx,0B800h mov ax,2 int 31h mov es:[si],ax ; Do a very slimy fix-up of the runtime constant __C000h cCall GetProcAddress, mov si,bx mov bx,0C000h mov ax,2 int 31h mov es:[si],ax ; Do a very slimy fix-up of the runtime constant __D000h cCall GetProcAddress, mov si,bx mov bx,0D000h mov ax,2 int 31h mov es:[si],ax ; Do a very slimy fix-up of the runtime constant __E000h cCall GetProcAddress, mov si,bx mov bx,0E000h mov ax,2 int 31h mov es:[si],ax endif ;ROM ------------------------- ifndef WOW cCall SetUserPro ; Get WIN.INI filename from environment endif CheckKernelDS ; Free high memory copy of KERNEL.EXE ife ROM cCall IGlobalFree, mov hLoadBlock,ax else xor ax, ax endif if PMODE32 .386 mov fs, ax mov gs, ax .286p endif ifndef WOW cmp lpWindowsDir.sel,ax jnz got_win_dir mov si,dataOffset szUserPro mov di,dataOffset [WinIniInfo.ProBuf] cCall IOpenFile, lea di,[di].opfile mov lpWindowsDir.sel,ds mov lpWindowsDir.off,di mov dx,di call GetPureName sub di,dx dec di mov cBytesWinDir,di got_win_dir: endif ifdef WOW ; Regular Windows kernel sets up the Windows directory very early in ; boot, before WOW kernel loads WOW32.DLL. ; ; It turns out we can delay setting up the 16-bit copy of the ; windows directory (referred by lpWindowsDir) until here, where ; we're in protected mode and about to set up the system directories ; as well. This allows us to call a GetShortPathName thunk in ; WOW32. ; mov ds,topPDB UnSetKernelDS mov ds,ds:[PDB_environ] call get_windir ; on return, DS:DI -> windows dir smov es,ds SetKernelDS ; Record where to find the 'windows' directory. BootTrace 'q' mov lpWindowsDir.sel,es mov lpWindowsDir.off,di mov cBytesWinDir,cx ; Record where to find the 'system32' directory. mov ax,hExeHead mov es,ax mov di,es:[ne_pfileinfo] lea di,es:[di].opFile mov lpSystemDir.sel,es mov lpSystemDir.off,di mov dx,di call GetPureName sub di,dx dec di mov cBytesSysDir,di BootTrace 'u' ; ; Under WOW there are two system directories: \nt\system and \nt\system32. ; lpSystemDir points to \nt\system32, while lpSystem16Dir points to ; \nt\system. We return the latter from GetSystemDirectory, since we want ; applications to install their shared stuff in \nt\system, but we want ; to search \nt\system32 before \nt\system when loading modules. ; Set up lpSystem16Dir using the Windows dir + \system. ; Note that the string pointed to by lpSystem16Dir is not null terminated! mov lpSystem16Dir.sel,ds mov lpSystem16Dir.off,dataoffset achSystem16Dir cld mov si,dataoffset achRealWindowsDir mov es,lpSystem16Dir.sel mov di,lpSystem16Dir.off mov cx,cbRealWindowsDir rep movsb ; copy Windows dir mov si,dataoffset Sys16Suffix mov cx,cBytesSys16Suffix rep movsb ; tack on "\system" mov cx,cbRealWindowsDir add cx,cBytesSys16Suffix mov cBytesSys16Dir,cx ; ; build the Wx86 system directory "Windir\Sys32x86" ; mov lpSystemWx86Dir.sel,ds mov lpSystemWx86Dir.off,dataoffset achSystemWx86Dir mov es,lpSystemWx86Dir.sel mov di,lpSystemWx86Dir.off mov cx,cbRealWindowsDir mov si,dataoffset achRealWindowsDir rep movsb ; copy System dir (Windows\System32) mov si,dataoffset SysWx86Suffix mov cx,cBytesSysWx86Suffix rep movsb ; tack on "\Wx86" mov cx,cbRealWindowsDir add cx,cBytesSysWx86Suffix mov cBytesSysWx86Dir,cx @@: ; WOW DPMI Supports wants to call GlobalDosAlloc and GlobalDosFree so that ; We don't have to write the same code for DPMI support. So we call DPMI ; Here with the addresses of those routines so he can call us back. mov bx,cs mov si,bx mov dx,codeOffset GlobalDosAlloc mov di,codeOffset GlobalDosFree mov ax,4f3h int 31h ; Now that we've built up the system dir from the windows dir, set ; the windows dir to where it should be for this user. push es cld mov di,offset achTermsrvWindowsDir cCall TermsrvGetWindowsDir, or ax, ax jz @F ; ax = 0 -> error, just leave windows dir smov es,ds mov cx,-1 xor ax,ax repnz scasb not cx dec cx ; length of windows directory mov lpWindowsDir.sel,ds mov lpWindowsDir.off,offset achTermsrvWindowsDir mov cBytesWinDir,cx @@: pop es endif ; Under win 2.11 we allowed the ":" syntax to replace the shell. ; We no longer allow this, however to avoid messing up people ; with batch files that have ":" in them we will strip the ; ":" out of the command line. But we retain the :: syntax ; for the OS/2 VM!! ; We also do the check for the /b switch here. This puts us in "diagnostic ; mode and we set a flag to say this. Later, we will call the DiagInit() ; function to open the log file, etc. push ds cld mov ds,topPDB UnSetKernelDS push ds pop es mov si,80h xor ax,ax lodsb or al,al jz no_colon mov cx,ax @@: lodsb cmp al,' ' loopz @B cmp al,':' jnz no_colon cmp byte ptr [si],':' jz no_colon mov byte ptr [si][-1],' ' no_colon: cmp al,'/' ;Switch character? je CheckSwitch ;Yes cmp al,'-' ;Other switch character? jne NoSwitch ;Nope. CheckSwitch: lodsb ;Get next char or al,32 ;Convert to lowercase if necessary cmp al,'b' ;Diagnostic mode? jnz NoSwitch ;Nope cCall DiagInit ;Initialize diagnostic mode mov WORD PTR [si-2],2020h ;Blank out the switch NoSwitch: pop ds ReSetKernelDS ;** Reset secret flag for memory manager. Fixed segments will go ;** >1MB after this and fBooting, NOT 2 ;** We want to grow the SFT *before* loading the modules, ;** not after, like was the case previous to win3.1 call GrowSFTToMax ;add to SFT chain in p mode if 1 ; old ldinit.c cld push ds smov es,ds xor ax,ax xor cx,cx mov ds,topPDB UnSetKernelDS mov si,80h lodsb or al,al jz gwaphics_done mov cl,al lodsb cmp al,' ' mov al,ah jnz gwaphics_done lodsb cmp al,':' mov al,ah jnz gwaphics_done lodsb cmp al,':' mov al,ah jnz gwaphics_done mov di,dataOffset app_name find_delineator: lodsb stosb cmp al,' ' ja find_delineator mov es:[di][-1],ah mov ds:[80h],ah ; assume no arguments jnz gwaphics_done add cx,82h sub cx,si smov es,ds mov di,80h mov al,cl stosb rep movsb gwaphics_done: pop ds ReSetKernelDS or ax,ax jz @F mov graphics,0 @@: else cld xor ax,ax xor bx,bx endif jmps SlowBoot bootfail: mov al,1 cCall ExitKernel, cEnd nogen sEnd INITCODE ;-----------------------------------------------------------------------; ; SlowBoot ; ; ; ; ; ; Arguments: ; ; ; ; Returns: ; ; ; ; Error Returns: ; ; ; ; Registers Preserved: ; ; ; ; Registers Destroyed: ; ; ; ; Calls: ; ; ; ; History: ; ; ; ; Sat Mar 14, 1987 05:52:22p -by- David N. Weise [davidw] ; ; Added this nifty comment block. ; ;-----------------------------------------------------------------------; DataBegin INIT BootSect db 'BOOT',0 BootFile db 'SYSTEM.INI',0 FilesCached db 'CACHEDFILEHANDLES',0 IdleSegLoad db 'LOADSEGMENTSATIDLE',0 ifdef WOW ExitLastApp db 'CLOSEONLASTAPPEXIT',0 endif if SHERLOCK szGPC db 'GPCONTINUE',0 endif szDebugSect db 'DEBUG',0 szOutputTo db 'OUTPUTTO', 0 szAux db 0 ;'AUX', 0 ; don't return a default if KDEBUG szKRInfo db 'KERNELINFO', 0 szKRBreak db 'KERNELBREAK', 0 szWin3Info db 'WIN3INFO', 0 szWin3Break db 'WIN3BREAK', 0 endif ife PMODE32 Standard db 'STANDARD',0 PadCodeStr db 'PADCODESEGMENTS',0 endif BootBufLen equ 80 BootBuf db BootBufLen dup (?) bootmods label byte DB 'SYSTEM.DRV',0 winmods label byte ifdef WOW DB 'KEYBOARD.DRV',0 szAfterKeyboardDriver label byte ;Used so we can tell key driver loaded DB 'MOUSE.DRV',0 ifdef FE_SB szBeforeWifeMan label byte ;Used so we can tell key driver loaded DB 'WIFEMAN.DLL', 0 ;WIFE manager has to be loaded before display driver endif DB 'VGA.DRV',0 DB 'SOUND.DRV',0 DB 'COMM.DRV',0 DB 'USER.EXE',0 DB 'GDI.EXE',0 ifdef FE_SB szBeforeWinNls label byte ;Used so we can tell key driver loaded DB 'WINNLS.DLL', 0 ;bug #112335 endif else DB 'KEYBOARD.DRV',0 szAfterKeyboardDriver label byte ;Used so we can tell key driver loaded DB 'MOUSE.DRV',0 ifdef FE_SB szBeforeWifeMan label byte ;Used so we can tell key driver loaded DB 'WIFEMAN.DLL', 0 ;WIFE manager has to be loaded before ;display driver endif DB 'DISPLAY.DRV',0 DB 'SOUND.DRV',0 DB 'COMM.DRV',0 DB 'FONTS.FON',0 DB 'OEMFONTS.FON',0 ; For Internationals use. DB 'GDI.EXE',0 DB 'USER.EXE',0 ifdef FE_SB szBeforeWinNls label byte ;Used so we can tell key driver loaded DB 'WINNLS.DLL', 0 endif endif ifdef WOW defapp label byte DB 'WOWEXEC.EXE',0 else defapp label byte DB 'PROGMAN.EXE',0 endif Shell label byte DB 'WOWSHELL',0 ;** Ordinal strings for two of the Keyboard driver exports keymodstr DB 'KEYBOARD',0 keyprocstr DB '#5',0 ; keyprocstr = AnsiToOem keyprocstr1 DB '#6',0 ; keyprocstr = OemToAnsi DataEnd INIT sBegin INITCODE assumes cs,CODE assumes ds,nothing assumes es,nothing if BootTraceOn cProc BootTraceChar,, parmW char cBegin mov dx, 3fdh @@: in al, dx test al, 20h jz @B mov ax, char mov dx, 3f8h out dx, al cEnd endif ; BootTraceOn cProc ttywrite,, parmD s cBegin ; cCall lstrlen, ; mov cx, ax ; lds bx, s ; mov bx,1 ; mov ah,40h ; int 21h lds si, s cld mov ah, 2 tty1: lodsb mov dl, al or dl, dl jz tty2 int 21h jmps tty1 tty2: cEnd ifdef WOW cProc LoadFail,, parmD s cBegin ;** Put Up a Dialog Box If we can't load a module ; since szPleaseDoIt resides in protected cs we can't concat the module ; name to it -- so we need to copy it to the stack ;szPleaseDoIt db "Please re-install the following module to your system32 ; directory: ",13,10,9,9,0 mov di,sp ; save current stack pointer sub sp,100h ; allocate for new concat'd string on the stack mov si,sp ; save the start of the stack string push ss ; copy szPleaseDoIt to the stack buffer push si push cs push codeOFFSET szPleaseDoIt call lstrcpy push ss ; concat module name to string on stack push si lds cx, s ; get the module name push ds push cx call lstrcat push ss ;push finished stack string now push si push cs ;szMissingMod db "KERNEL: Missing 16-bit module:",0 push codeOFFSET szMissingMod ; Set Caption push 0 ;No left button push SEB_CLOSE + SEB_DEFBUTTON ;Button 1 style push 0 ;No right button externFP kSYSERRORBOX call kSYSERRORBOX ;Put up the system message mov sp,di ; restore sp cEnd else // non-WOW cProc LoadFail,, parmD s cBegin SetKernelDS cCall TextMode mov bx, dataoffset szCRLF cCall ttywrite, dsbx mov bx, dataoffset szCRLF cCall ttywrite, dsbx mov bx, dataoffset szBootLoad cCall ttywrite, dsbx cCall ttywrite, s mov bx, dataoffset szCRLF cCall ttywrite, dsbx cEnd endif cProc SlowBoot, cBegin nogen CheckKernelDS ReSetKernelDS GPPI macro sect, key, defval, file, storeit push ds push dataoffset sect push ds push dataoffset key push defval push ds push dataoffset file call GetPrivateProfileInt ifb mov defval, ax endif endm GPPS macro sect, key, defval, here, hereLen, file push ds push dataoffset sect push ds push dataoffset key push ds push dataoffset defval push ds push dataoffset here push hereLen push ds push dataoffset file call GetPrivateProfileString endm GPPS1 macro sect, key, defval, here, hereLen, file push ds push dataoffset sect push ds push key push ds push defval push ds push dataoffset here push hereLen push ds push dataoffset file call GetPrivateProfileString endm GPI macro sect, key, defval, storeit push ds push dataoffset sect push ds push dataoffset key push defval call GetProfileInt ifb mov defval, ax endif endm ife PMODE32 xor ax,ax mov al,fPadCode GPPI Standard, PadCodeStr, ax, BootFile, nostore or al,ah mov fPadCode,al endif if SHERLOCK GPI szKernel, szGPC, gpEnable endif if KDEBUG ifdef DISABLE GPPI szDebugSect, szKRInfo, _krInfoLevel, BootFile GPPI szDebugSect, szKRBreak, _krBreakLevel, BootFile GPPI szDebugSect, szWin3Info, _Win3InfoLevel, Bootfile GPPI szDebugSect, szWin3Break, _Win3BreakLevel, Bootfile endif endif ;KDEBUG GPPS szDebugSect, szOutputTo, szAux, BootBuf, BootBufLen, BootFile or ax, ax jz @F cmp ax, BootBufLen-2 jz @F mov ah, 3ch ; creat file (zero length) xor cx, cx mov dx, dataOffset BootBuf DOSCALL jc @F mov bx, ax mov ah, 3eh ; now close it DOSCALL jc @F mov ax, 3d42h ; open inherit, deny none, read/write DOSCALL jc @F mov bx, ax ; dup handle mov cx, 3 ; force AUX to be this file mov ah, 46h DOSCALL mov ah, 3eh ; close temp file DOSCALL mov wDefRIP, 'i' ; since AUX is redirected, assume Ignore RIP @@: GPPI BootSect, FilesCached, MAXFHCACHELEN, BootFile, nostore cmp ax, MINFHCACHELEN ; Validate length jae @F mov ax, MINFHCACHELEN @@: cmp ax, MAXFHCACHELEN jbe @F mov ax, MAXFHCACHELEN @@: mov fhCacheLen, ax ; Adjust # of cached file handles shl ax, 1 shl ax, 1 add ax, dataoffset fhCache mov fhCacheEnd, ax GPPI BootSect, IdleSegLoad, 1, BootFile, nostore mov fPokeAtSegments, al ifdef WOW GPPI BootSect, ExitLastApp, 0, BootFile, nostore mov fExitOnLastApp, al endif mov bootExecBlock.lpfcb1.off,dataOffset win_show mov bootExecBlock.lpfcb1.sel,ds mov es,topPDB mov bootExecBlock.lpcmdline.off,80h mov bootExecBlock.lpcmdline.sel,es mov lpBootApp.off,dataOffset app_name mov lpBootApp.sel,ds cmp graphics,1 jnz sb1 mov lpBootApp.off,dataOffset defapp mov lpBootApp.sel,ds sb1: mov di,dataOffset bootMods sbloop1: cCall LoadNewExe, SetKernelDS es mov cx,-1 xor ax,ax cld repnz scasb cmp di,dataOffset winmods jb sbloop1 cmp graphics,1 jz sbloop2 ; cCall InitFwdRef jmps sb4 UnSetKernelDS es sbloop2: ; load USER.EXE, GDI.EXE ifdef FE_SB ;** If we just load the fae east modules, we want to ;** checks the system locale vale which is far east ;** locale. ;** If system locale is not far east, then we skip ;** to load far east module. cmp di,dataOffset szBeforeWifeMan je SB_DoFarEastModule cmp di,dataOffset szBeforeWinNls jne SB_DoLoadModule SB_DoFarEastModule: cCall GetSystemDefaultLangID ; return: ax is system locale value cmp ax,411h je SB_DoLoadModule cmp ax,412h je SB_DoLoadModule cmp ax,404h je SB_DoLoadModule cmp ax,804h je SB_DoLoadModule cmp ax,0c04h je SB_DoLoadModule ; skip to next module mov cx,-1 xor ax,ax cld repnz scasb jmp SB_NotKeyboardDriver SB_DoLoadModule: endif cCall LoadNewExe, push ax ; Save hInst return value SetKernelDS es mov cx,-1 xor ax,ax cld repnz scasb pop si ; Get hInst of latest module in SI ;** If we just loaded the keyboard driver, we want to ;** point our explicit link variables to the AnsiToOem and ;** OemToAnsi functions so OpenFile can use them for the ;** remaining boot modules cmp di,dataOffset szAfterKeyboardDriver jne SB_NotKeyboardDriver push ds ; Save regs we care about push di regptr pStr,ds,bx mov bx,dataOffset keyprocstr cCall GetProcAddress, mov pKeyProc.off,ax mov pKeyProc.sel,dx mov bx,dataOffset keyprocstr1 cCall GetProcAddress, mov pKeyProc1.off,ax mov pKeyProc1.sel,dx pop di pop ds SB_NotKeyboardDriver: cmp di,dataOffset defapp jb sbloop2 ; OPTIMIZE BEGIN ; OPTIMIZE END sb4: cCall InitFwdRef ; Gets stuff we can't dynalink to cCall InternalEnableDOS ; Enable int21 hooks ;sb4: call check_TEMP ; Get the shell name from SYSTEM.INI mov ax,dataoffset Shell GPPS1 BootSect, ax, lpBootApp.off, BootBuf, BootBufLen, BootFile ;** Here we need to convert the command line to ANSI cmp WORD PTR pKeyProc1[2],0 ; Paranoia... jz @F ;** Zero terminate the string before passing to OemToAnsi mov es,bootExecBlock.lpcmdline.sel mov bl,es:[80h] ; Get length byte xor bh,bh xchg BYTE PTR es:[bx+81h],bh ; Replace terminator char with zero ;** Call the keyboard driver push es ; Save the seg reg push bx ; Save offset and char saved push es push 81h ; Always starts here (fixed offset) push es push 81h cCall [pKeyProc1] ; Call OemToAnsi in keyboard driver pop bx ; Get saved info pop es mov al,bh ; Saved char from string xor bh,bh mov BYTE PTR es:[bx+81h],al ; Replace the character @@: mov ax,dataOffset bootExecBlock regptr dsax,ds,ax cmp graphics,1 jz @F cCall LoadModule, jmps asdf @@: cCall FlushCachedFileHandle, ; in case 100 fonts are loaded farptr lpBootBuf,ds,di mov di, dataoffset BootBuf cCall LoadModule, asdf: cmp ax,32 jbe sb6 cCall GetExePtr, mov hShell, ax jmp bootdone sb6: ReSetKernelDS les bx, lpBootApp krDebugOut DEB_ERROR, "BOOT: unable to load @ES:BX" cCall LoadFail, ; cmp pDisableProc.sel,0 ; Is there a USER around yet? ; jz @F ; cCall pDisableProc ;@@: jmp bootfail UnSetKernelDS cEnd nogen ;------------------------------------------------------------------------ assumes ds,nothing assumes es,nothing cProc LoadNewExe,, parmW pname cBegin farptr lpparm,ax,ax farptr lpBootBuf,ds,di mov di, dataoffset BootBuf CheckKernelDS ReSetKernelDS ifdef WOW ; ATM Alters system.ini registry boot section to load its own SYSTEM.DRV ; However on WOW this causes us to fail to load correctly. Also we ; would be hard pushed to support the current 16 bit ATM since it relies ; on internals of 16 bit GDI which are not present in WOW. ; For this Beta I'm going to ignore the bootsection of the registry ; when loading drivers and thus ATM will not get installed. ; At least the user will be protected from not being able to boot WOW ; BUGBUG - Consider ; MattFe Oct9 92 mov di,pname else GPPS1 BootSect, pname, pname, BootBuf, BootBufLen, BootFile endif xor ax,ax cCall LoadModule, cmp ax,2 jne lne1 krDebugOut DEB_ERROR, "Can't find @DS:DI" ; kerror ERR_LDBOOT,,ds,di jmps lne4 lne1: cmp ax,11 jne lne2 krDebugOut DEB_ERROR, "Invalid EXE file @DS:DI" ; kerror ERR_LDBOOT,,ds,di jmps lne4 lne2: cmp ax,15 jnz lne3 krDebugOut DEB_ERROR, "Invalid protect mode EXE file @DS:DI" ; kerror ERR_LDBOOT,,ds,di jmps lne4 lne3: cmp ax, 4 jne lne3a krDebugOut DEB_ERROR, "Out of files (set FILES=30 in CONFIG.SYS) @DS:DI" ; kerror ERR_LDFILES,,ds,di jmps lne4 lne3a: cmp ax, 32 jae lnex NoLoadIt: ; kerror ERR_LDBOOT,,ds,pname krDebugOut DEB_ERROR, "Unable to load @DS:DI (#ax)" lne4: cCall LoadFail,dsdi mov ax,1 cCall ExitKernel, lnex: UnSetKernelDS cEnd sEnd INITCODE ;-----------------------------------------------------------------------; ; BootDone ; ; ; ; Boot is done when all of modules are loaded. Here we do a bit of ; ; clean up, such as validating code segments, initing free memory to ; ; CCCC, freeing up the fake TDB, and finally reallocating the init ; ; code away. ; ; ; ; Arguments: ; ; none ; ; ; ; Returns: ; ; nothing ; ; ; ; Error Returns: ; ; ; ; Registers Preserved: ; ; ; ; Registers Destroyed: ; ; all ; ; ; ; Calls: ; ; ; ; History: ; ; ; ; Wed Apr 15, 1987 08:53:23p -by- David N. Weise [davidw] ; ; Added this nifty comment block. ; ;-----------------------------------------------------------------------; DataBegin INIT externB beg_initdata szKernel DB 'KERNEL',0 szWindows DB 'WINDOWS',0 if KDEBUG szDebugOptions DB 'DebugOptions',0 szDebugFilter DB 'DebugFilter',0 szChecksum DB 'EnableSegmentChecksum',0 szSweepFreak DB 'LRUSweepFrequency',0 sz80x87 DB 'NoUse80x87',0 szFastFP DB 'FastFP', 0 externW DebugOptions externW DebugFilter ifdef DISABLE externB fLoadTrace endif endif ; KDEBUG ifdef SDEBUG szEMSDebug DB 'EnableEMSDebug',0 endif szgrab_386 DB '386GRABBER',0 if SWAPPRO szSwapPro DB 'SwapProfile',0 szSwapFile DB 'SWAPPRO.DAT',0 endif DataEnd INIT sBegin INITCODE assumes cs,CODE assumes ds,nothing assumes es,nothing externB beg_initcode cProc BootDone, cBegin nogen SetKernelDS if KDEBUG ; Get win.ini [Windows] DebugOptions GPI szWindows, szDebugOptions, DebugOptions ; Get win.ini [Windows] DebugFilter GPI szWindows, szDebugFilter, DebugFilter ; Now set various internal flags based on DebugOptions xor ax,ax test DebugOptions,DBO_CHECKHEAP jz @F inc ax @@: mov es,pGlobalHeap mov es:[hi_check],ax test DebugOptions,DBO_CHECKFREE jz @F or Kernel_flags,kf_check_free @@: ifdef DISABLE xor ax,ax test DebugOptions,DBO_LOADTRACE jz @F mov fLoadTrace, al @@: endif ; DISABLE test DebugOptions,DBO_DISABLEGPTRAPPING jz wants_trapping mov ax,0203h ; Reset GP fault. mov bl,0Dh mov cx,prevInt0Dproc.sel mov dx,prevInt0Dproc.off int 31h mov ax,0203h ; Reset invalid op-code exception. mov bl,06h mov cx,prevIntx6proc.sel mov dx,prevIntx6proc.off int 31h mov ax,0203h ; Reset page fault. mov bl,0Eh mov cx,prevInt0Eproc.sel mov dx,prevInt0Eproc.off int 31h wants_trapping: if SWAPPRO GPI szKernel, szSwapPro, 1, nostore mov fSwapPro, al mov bx,TopPDB mov ah,50h pushf FCLI call prevInt21Proc lea dx,szSwapFile xor cx,cx mov ah,3Ch pushf FCLI call prevInt21Proc mov hSwapPro,ax mov bx,cur_dos_pdb mov ah,50h pushf FCLI call prevInt21Proc endif GPI szKernel, szChecksum, 1, nostore mov fChkSum,al GPI szKernel, sz80x87, 0, nostore or ax,ax jz wants_8087 mov f8087,0 and WinFlags,NOT WF1_80x87 ;Turn off corresponding WinFlags bit wants_8087: GPI szKernel, szFastFP, 1, nostore mov fastFP, al GPI szKernel, szSweepFreak, 500, nostore else mov ax, 500 endif ; KDEBUG ifdef WOW xor ax,ax endif or ax,ax jz nolrusweep test WinFlags[1], WF1_PAGING jnz short nolrusweep mov bx,codeOffset lrusweep regptr csbx,cs,bx xor dx,dx cCall pTimerProc, nolrusweep: if SDEBUG GPI szKernel, szEMSDebug, 0, nostore or ax,ax jz no_EMS_debug_wanted or Kernel_flags,kf_EMS_debug no_EMS_debug_wanted: endif if LDCHKSUM cCall ValidateCodeSegments endif if KDEBUG mov fCheckFree,0 ife PMODE32 call init_free_to_CCCC endif endif ; Get the shell name from SYSTEM.INI GPPS BootSect, szgrab_386, szgrab_386, grab_name, 128, BootFile mov es,curTDB ; ES = TDB of fake task push es cCall DeleteTask, ; Flush bogus task pop es xor dx,dx mov es:[TDB_sig],dx ; Clear signature word. mov curTDB,dx ; Let someone else be current task ; switch to the temp stack since we're about to Realloc the present one away mov ax, ss FCLI SetKernelDS ss mov sp,dataOffset gmove_stack FSTI cCall free_sel, ; Shrink DGROUP down to its post initialization size mov cx,dataOFFSET beg_initdata ; don't need init data ; dx doubles as high word and specifies fixed ; reallocation xor dx,dx cCall IGlobalReAlloc, ; Realloc DGROUP xor dx,dx ; Now shrink the resident CODE segment ife ROM mov cx,codeOFFSET beg_initcode ; dont need init code ; cCall IGlobalReAlloc, ; jmps BootSchedule ; Jump to schedule first app push cs ; Arguments to GlobalReAlloc push dx push cx push dx push cs ; Where GlobalReAlloc will eventually return mov ax,codeOFFSET BootSchedule push ax jmp near ptr IGlobalReAlloc ; Jump to GlobalReAlloc else ;ROM jmp BootSchedule endif UnSetKernelDS ss UnSetKernelDS cEnd nogen ;-----------------------------------------------------------------------; ; check_TEMP ; ; If the environment variable TEMP points to garbage then GetTempFile ; screws up. We fix it by wiping out the TEMP string if it points ; to garbage. ; ; Entry: ; none ; ; Returns: ; ; Registers Preserved: ; all ; ; History: ; Thu 11-May-1989 16:39:34 -by- David N. Weise [davidw] ; Wrote it. ;-----------------------------------------------------------------------; assumes ds,nothing assumes es,nothing cProc check_TEMP,, ifdef FE_SB localW pMyBuf endif cBegin pusha SetKernelDS sub sp,130 mov di,sp CheckKernelDS mov ds,TopPDB UnSetKernelDS mov ds,ds:[PDB_environ] xor si, si ; assume DS:SI points to environment cCall GetTempDrive, smov es,ss cld stosw ifdef FE_SB mov pMyBuf,di ; save string offset(exclude D:) endif stmp2: lodsw or al,al ; no more enviroment ifdef FE_SB jnz @F ; I hate this jmp stmpNo1 @@: else jz stmpNo1 endif cmp ax,'ET' ; Look for TEMP= jne stmp3 lodsw cmp ax,'PM' jne stmp3 lodsb cmp al,'=' je stmpYes stmp3: lodsb or al,al jnz stmp3 jmp stmp2 stmpYes: push si ; save pointer to TEMP push ds push si ; spaces are legal, but they lookForSpace: ; confuse too many apps, so lodsb ; we treat them as illegal cmp al, ' ' jz stmpFoundSpace or al, al jnz lookForSpace pop si cmp byte ptr [si+1],':' jne stmpnodrive and byte ptr [si],NOT 20h ; springboard needs this! dec di dec di stmpnodrive: lodsb or al,al jz stmpNo stosb jmp stmpnodrive stmpNo: mov ax,'~\' cmp es:[di-1],al ; does it already end in \ jnz stmpNoF ; no, just store it dec di ; override it ifdef FE_SB push si mov si,pMyBuf call FarMyIsDBCSTrailByte ;is that '\' a DBCS trailing byte? cmc adc di,0 ;skip it if yes. pop si endif stmpNoF: stosw xor ax,ax stosb ; zero terminate it pop es ; recover pointer to TEMP pop di smov ds,ss mov dx,sp mov ax,5B00h xor cx,cx DOSCALL jnc stmpClose cmp al,80 ; Did we fail because the file jz stmpNo1 ; already exists? stmpNukeIt: sub di,5 ; Get the TEMP= part. @@: mov al,'x' xchg al,es:[di] inc di or al,al jnz @B mov byte ptr es:[di-1],0 jmps stmpNo1 stmpClose: mov bx,ax mov ah,3Eh DOSCALL mov ah,41h DOSCALL stmpNo1: add sp,130 popa cEnd stmpFoundSpace: pop si pop es pop di jmps stmpNukeIt ;-----------------------------------------------------------------------; ; get_windir ; ; Get a pointer to the 'windows' directory. ; ; Entry: ; DS => environment string ; ; Returns: ; CX = length of string ; DI => WFP of 'windows' directory ; ; Registers Preserved: ; all ; ; History: ; Mon 16-Oct-1989 23:17:23 -by- David N. Weise [davidw] ; Wrote it! ;-----------------------------------------------------------------------; assumes ds,nothing assumes es,nothing ifdef WOW ;-----------------------------------------------------------------------; ; original get_windir looks for the environment variable 'windir' (all ; lowercase) to set the 'windows' directory. ; ; On NT the equivalent environment variable is 'SystemRoot' (all ; uppercase). Hence the code is different. ; ; - nanduri ; ; There are some customers that used the undocumented trick on win31 ; of moving windir to some network location by putting win.com there. ; The result would be that the "Windows Directory" would point ; to the network location, and the system directory would be local. ; Currently, the way WINDIR is supported involves hacks in a couple ; of different places. What would be best is if you could do a ; "set windir=xxxx" in your autoexec.nt and we would look for windir ; here and we would code it to emulate win31 behavior. However, that's ; broken right now, and windir is only regenerated after krnlx86.exe ; has finished booting. So the approach taken here is to look for ; a new environment variable called win16dir, and if it exists, make ; the windows directory point to it. Systemroot is still used to ; generate the system directory. ; ; We want to allow NT to be installed into a directory with a long ; name, so we use GetShortPathName on the directory we get from ; either SystemRoot or Win16Dir variables. ; -- DaveHart 9-Feb-96 ;-----------------------------------------------------------------------; szSystemRoot DB 'SYSTEMROOT=',0 szWin16Dir DB 'WIN16DIR=',0 cProc get_windir, cBegin nogen push es mov ax, cs ; the string is in 'cs', see above mov es, ax mov di, codeoffset szSystemRoot call get_env_var_ptr push dx mov dx, ds mov es, dx SetKernelDS mov si, dataoffset achRealWindowsDir regptr esdi,es,di regptr dssi,ds,si mov cx, WINDIR_BUFSIZE push dx cCall GetShortPathName, pop dx mov cbRealWindowsDir,ax mov ds, dx ;restore ds pop dx assumes ds,nothing push cx push di mov ax, cs ; the string is in 'cs', see above mov es, ax mov di, codeoffset szWin16Dir call get_env_var_ptr or di, di ;does win16dir exist? jz gw_not add sp, 4 ;throw away systemroot jmp short gw_cont gw_not: pop di pop cx gw_cont: ; Now ds:di points to the Windows directory string in ; the environment block. It may be a long pathname, ; so fix it up. smov es, ds SetKernelDS mov si, dataoffset achWindowsDir regptr esdi,es,di regptr dssi,ds,si mov cx, WINDIR_BUFSIZE cCall GetShortPathName, mov cx, ax smov ds, es assumes ds,nothing pop es ret cEnd nogen cProc get_env_var_ptr, cBegin nogen cld push si xor si,si push di mov cx,-1 xor ax,ax repnz scasb not cx dec cx ; length of szSystemRoot pop di gw_cmp: mov al, [si] or al, al jz gw_exit push di push cx repz cmpsb ; compare the inputstring with szSystemRoot pop cx pop di jnz gw_next ; not szSystemRoot xor cx,cx ; yes szSystemRoot, cx=0 indicates so mov di,si gw_next: lodsb or al,al jnz gw_next ; skip to the terminating NULL. or cx,cx ; cx==0 implies... found szSystemRoot jnz gw_cmp ; compare with the next environment string mov cx,si ; here if found szSystemRoot. sub cx,di mov ax,di dec cx gw_exit: mov di,ax pop si ret cEnd nogen ;-----------------------------------------------------------------------; ; original get_windir is within the 'else' 'endif' block ; ;-----------------------------------------------------------------------; else cProc get_windir, cBegin nogen cld push si xor di,di xor si,si gw: lodsw or al,al ; no more enviroment jz gw_exit if ROM if1 %out Take this out! endif ;;;!!!only until loader builds proper environment block or ax,2020h ; ignore case of ENV string endif cmp ax,'iw' ; Look for windir= jne @F lodsw if ROM ;;;!!!only until loader builds proper environment block or ax,2020h ; ignore case of ENV string endif cmp ax,'dn' jne @F lodsw if ROM ;;;!!!only until loader builds proper environment block or ax,2020h ; ignore case of ENV string endif cmp ax,'ri' jne @F lodsb cmp al,'=' je gw_got_it @@: lodsb or al,al jnz @B jmp gw gw_got_it: mov di,si @@: lodsb or al,al jnz @B mov cx,si sub cx,di dec cx gw_exit: pop si ret cEnd nogen endif sEnd INITCODE ;------------------------------------------------------------------------ sBegin STACK ; Boot TDB boottdb equ this byte DB SIZE TDB dup (0) if 0 ;0123456789012345 ; Boot EEMS context save area NUMBER_OF_BANKS = 4 * (0FFFFh - (LOWEST_SWAP_AREA * 64) + 1)/400h boottdb_EEMSsave equ this byte DB NUMBER_OF_BANKS + 100h DUP (0) endif ; do a clumsy paragraph alignment rept 16 if ($ - boottdb) and 0Fh db 0 endif endm ; Dummy arena entry so boot SS looks like a valid object DB 'M' DW -1 DW (BOOTSTACKSIZE + 31)/16 DB 0 DW 5 DUP (0) ; Another in case we have to tweek the low order bit of SS DB 'M' DW -1 DW (BOOTSTACKSIZE + 15)/16 DB 0 DW 5 DUP (0) ; Boot stack stackbottom equ this word DB BOOTSTACKSIZE DUP (0) stacktop equ this word DW -1 sEnd STACK end BootStrap