SCCSID  equ     "@(#)sicg.asm   6.7 92/03/13"
        PAGE    ,132
TITLE   SICG - SysInit Call Gate functions
NAME    SICG

;
;                 IBM/Microsoft Confidential
;
;           Copyright (c) IBM Corporation  1987, 1990
;           Copyright (c) Microsoft Corp.  1987, 1990
;                     All Rights Reserved
;

;
;
;       SICG contains the worker and its subfunctions for the call gate
;       used by SysInit to obtain special system services during system
;       initialization.
;
;  Revisions:
;
;  defect 77939 02/09/94 Catherine E. Bundy  Added sicgf18 to provide a
;               callgate for ldrfreemodule instead of freeing device
;               drivers through DOSFreeModule
.xlist
include fnoinit.inc
include dosseg.inc
include basemaca.inc
include osmaca.inc
include mi.inc
include defdbug.inc
include dossymc.inc
include ostypes.inc
include dpb.inc
include bpb.inc
include buffer.inc
include chardd.inc
include devsym.inc
include error.inc
include osconst.inc
include vmowner.inc
include protmode.inc
include seldesc.inc
include sicg.inc
include inicfg.inc
include infoseg.inc
include fsc.inc
include pgexport.inc
include vmexport.inc
include selxport.inc
include tkexport.inc
include proc.inc
include except.inc
include sem.inc
include sigdef.inc
include curdir.inc
include dirent.inc
include find.inc
include screen.inc
include tkptda.inc
include virtsym.inc
include doshlp.inc
include devhlp.inc
include inialtf1.inc
include struc.inc             ; Structured assembler macros
.list
        CpuMode 386
        ModName sicg,FNO_SICG_ASM

        ReferGlobal     SISData,BYTE            ;@PAA04757A - for SIS_BootDrv
        ReferGlobal     DHF,DWORD               ; DosHlp function table
        ReferGlobal     CONdev,DWORD            ; Device list
        ReferGlobal     DevHlp,DWORD            ; Tiled DeviceHelp address
        ReferGlobal     EGAPresent,BYTE         ; EGA.SYS present
        ReferGlobal     EGADevice,DWORD         ; Address of EGAINT10
        ReferGlobal     iDevCall, WORD          ; Request packet for INIT calls
        ReferGlobal     LastDPB,DWORD
        ReferGlobal     LoadDevDrv,BYTE         ; Load Device Driver flag
        ReferGlobal     MaxSec,WORD
        ReferGlobal     NumIO,BYTE              ; number of physical drives
        ReferGlobal     NumIOReserveDriveLetter,BYTE ; highest drive letter for ReserveDriveLetter
        ReferGlobal     NumIORipl,BYTE          ; number of RIPL drives (CDB)
        ReferGlobal     LstDPBDrv,BYTE          ; Last drive to have DPB inited
        ReferGlobal     RAS_BIOS_data,BYTE      ; RAS bios data buffer
        ReferGlobal     SISData,BYTE
        ReferInitGlobal mfscNameList,BYTE
        ReferInitGlobal mfscInitTemplate,BYTE
        ReferGlobal     mFSD_FLAG,BYTE
        ReferGlobal     pVPBBuf,DWORD
        ReferGlobal     pbufWPT,DWORD
        ReferFlat       _pPTDACur,DWORD
        ReferFlat       _smDelaySwap,BYTE       ; if true don't start swapper

        ReferGDT        GDT,WORD                ; GDT label
        ReferGDT        GDT_DOSGETPROCADDR,WORD ; DOSGETPROCADDR call gate label
        ReferGDT        GDT_DOSGLOBALSEG,WORD   ; DOSGLOBALSEG call gate label
        ReferGDT        GDT_DOSSICG,WORD        ; DOSSICG call gate label
        ReferGDT        GDT_FSC,WORD
        ReferGDT        GDT_ROMDATA,WORD        ; ROM data descriptor
        ReferGDT        GDT_SAS,WORD            ; SAS descriptor

        ReferInitGlobal Config_Sys_Data,WORD ; CONFIG.SYS data selector
        ReferInitGlobal syiMessageSelector,WORD ; message segment selector
        ReferInitGlobal FSHelperModHdl,WORD        ; FSHELPER mte handle
        ReferInitGlobal pDosExit,DWORD          ; For the error handler thread
        ReferInitGlobal hsemSICGThreadDead      ; See subfunction
        ReferInitGlobal hsemVDMMInitMessageBufferEmpty,DWORD ; See subfunction
        ReferInitGlobal hsemVDMMInitErrorHandlerBusy,DWORD   ; 0Ch for explan'n
        ReferInitGlobal fVDMMInitComplete,WORD     ; has VDMMInit finished?
        ReferInitGlobal pDosSMSysInitSem,dword
        ReferInitGlobal clock_name,byte         ;clock DD name

        ReferFlat       _pTSDCur,DWORD

        EXTRN           InitDevice:near
        EXTRN           InitDeviceMgr:near
        EXTRN           InitKernelCountryData:near
        EXTRN           InitKernelDBCS:near
;       EXTRN           RASSTIP:NEAR
        EXTRN           MONINIT:NEAR
        EXTRN           SetPMCharDevEntries:NEAR
        EXTRN           fscCheck:NEAR
        EXTRN           fscAdd:NEAR
        EXTRN           fscInit:NEAR
        EXTRN           PhysToVirtReinit:near
        EXTRN           SELReinitDPL:NEAR
        EXTRN           QueryPipe:NEAR
        EXTRN           SetDPBRemovFlag:near
        EXTRN           dh_SendEvent:NEAR

        EXTRNF32        INTInit2
        EXTRNF32        _DevhlpDisableAccess
        EXTRNF32        _LDRDisableDirtyHooks
        EXTRNF32        _LDRFreeInit
        EXTRN32         _LDRFreeModule       ;77939 ceb
        EXTRNF32        FreeDynLink
        EXTRNF32        MVDMINIT
        EXTRNF32        InitNPXEM

        EXTRN32         _SELReallocKHB

        EXTRNFAR        RMNotify
        EXTRNFAR        AddRecFromDevHdr
        EXTRNFAR        DevDeInstall
        EXTRNFAR        FuBuff
        EXTRNFAR        SegAlloc
        EXTRNFAR        SegRealloc
        EXTRNFAR        SegFree
        EXTRNFAR        SemSet
        EXTRNFAR        SemWait
        EXTRNFAR        SemClear
        EXTRNFAR        SelToSeg
        EXTRNFAR        VirtToLin
        EXTRNFAR        NWDEnableWatchdogNMI
        EXTRNFAR        FreeDriveLetter     ;99999
        EXTRNFAR        AllocateDriveLetter ;99999

        EXTRN           _VMFreeMem:near
        EXTRN           _SELGetDescInfo:near
        EXTRN           _SMReinit:near
        EXTRN           _LDRFreeTask:near
        EXTRN           _LDRINITInfo:near
        EXTRN           _LDRINITSetGDT_DPL0:near
        EXTRN           _LDRReallocDD:near
        EXTRN           _LDRINITSetGDT_DPL0:near
        EXTRN           _TKExit:near
        EXTRN           syiDiscardDosHlp:near
        extrn           _panicsicg:near

IFDEF   PMDEBUG
        EXTRN           DebugRemoveSymMTE:NEAR
ENDIF

CONSTANTS segment
           public  SICG_SS_SP
SICG_SS_SP      dd      0               ; SS:SP during DD initialization
delay_semaphore dd      0               ; SysInit exit delay semaphore
DELAYVALUE      equ     120000          ; 2 minute time delay before exit
CONSTANTS ends

DATA segment
        GenPublic _InitCodeInvalid
_InitCodeInvalid db     0               ; non-zero -> we're not at init time

        Public syiGlobalFlags           ; SYI flags. (sometimes used after
syiGlobalFlags   DD     0               ; init time, so can't be in initdata)
                                        ; See INIALTF1.INC for equates.

DATA ends

DATA_TYPE       EQU     1
CODE_TYPE       EQU     0

        FARCODE INIT
INITCODE segment


;**     w_SICG - SysInit Call Gate function dispatcher
;
;       This function performs special tasks required by
;       SysInit.
;
;       ENTRY   DS:SI   pointer to request packet
;       EXIT    NONE
;       USES    AX, BX, CX, DX, DI, SI, DS, ES

sicgtab dw      DOSCODE:sicgf0  ; Subfunction 0 - Reinitialize device mgr
        dw      DOSCODE:sicgf1  ; Subfunction 1 - Initialize DBCS
        dw      DOSCODE:sicgf2  ; Subfunction 2 - Final cleanup
        dw      DOSCODE:sicgf3  ; Subfunction 3 - Get DD info
        dw      DOSCODE:sicgf4  ; Subfunction 4 - Add DD to list
        dw      DOSCODE:sicgf5  ; Subfunction 5 - Complete DD load
        dw      DOSCODE:sicgf6  ; Subfunction 6 - call SYI message routine
        dw      DOSCODE:sicgf7  ; Subfunction 7 - Add DPBs to list
        dw      DOSCODE:sicgf8  ; Subfunction 8 - Complete FSD Install.
        dw      DOSCODE:sicgf9  ; Subfunction 9 - FSD setup.
        dw      DOSCODE:sicgfA  ; Subfunction A - Initialize MVDM
        dw      DOSCODE:sicgfB  ; Subfunction B - Deinstall DD
        dw      DOSCODE:sicgfC  ; Subfunction C - Fetch error from VDMMInit
        dw      DOSCODE:sicgfD  ; Subfunction D - ring 3 Panic
        dw      DOSCODE:sicgfE  ; Subfunction E - swapper and clock re-init
        dw      DOSCODE:sicgerr ; Subfunction F - Unused
        dw      DOSCODE:sicgf10 ; Subfunction 10 - Set up initial char dev table
        dw      DOSCODE:sicgf11 ; Subfunction 11 - Add entry to char dev table
        dw      DOSCODE:sicgf12 ; Subfunction 12 - call MFS_TERM
        dw      DOSCODE:sicgf13 ; Subfunction 13 - available
        dw      DOSCODE:sicgf14 ; Subfunction 14 - initialize FP emulator
        dw      DOSCODE:sicgf15 ; Subfunction 15 - query pipe
        dw      DOSCODE:sicgf16 ; Subfunction 16 - call DosHlpProtGetMsg
        dw      DOSCODE:sicgf17 ; Subfunction 17 - available
        dw      DOSCODE:sicgf18 ; Subfunction 18 - call LdrFreeModule
        dw      DOSCODE:sicgf19 ; Subfunction 19 - Reboot
        dw      DOSCODE:sicgf1A ; Subfunction 1A - Notify Resource Manager
SICGTABLEN      equ     (($ - sicgtab) SHR 1)

Procedure w_SICG,faronly
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

        lodsw                           ; Get function code
        cmp     ax,SICGTABLEN           ; Legal code?
        jae     sicgerr                 ; no, blow up
        mov     bx,ax                   ; Put code in bx
        shl     bx,1                    ; Scale code
        jmp     cs:sicgtab[bx]          ; Jump to proper subfunction

sicgerr:
        InternalError <w_SICG>

EndProc w_SICG

;**     sicgf0 - Call InitDeviceMgr to build the physical disk table
;                then call INTInit2 so the IntMgr can allocate the final
;                stack for the system.
;
;       ENTRY   DS:SI   pointer to struc sicg_pkt0 (see sicg.inc)
;       EXIT    Carry clear
;       USES    AX, ES

Procedure sicgf0,far,local
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        call    InitDeviceMgr
        FlatContext <ds,es>
        CALLF32 INTInit2                ; Adjust the kernel stack size
        clc
        ret

EndProc sicgf0


;**     sicgf1 - Call InitKernelDBCS and InitKernelCountryData
;                to initialize kernel DBCS support
;
;       ENTRY   None
;       EXIT    Carry clear
;       USES    None

Procedure sicgf1,far,local
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        call    InitKernelDBCS
        call    InitKernelCountryData
        clc
        ret

EndProc sicgf1


;**     sicgf2 - DOSSICG no longer needed
;
;       This function does the following:
;
;       (1) Disables the DOSSICG and DOSGLOBALSEG call gates
;       (2) Makes DevHlp uncallable from ring 3
;       (4) call PhysToVirtReinit
;       (5) Resizes the DPB segment (unimplemented)
;       (6) Change the ROM data segment to DPL 0
;       (7) Frees the System Initializtion Process's segments
;       (8) Terminates the System Initializtion Process
;
;       ENTRY   NONE
;       EXIT    Carry clear
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, Flags

Procedure sicgf2,far,local
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:TASKAREA

;       Discard doshlps and BIOS segment

        call    syiDiscardDosHlp

;       Disable the call gates

        FlatContext <ds,es>,ax
        mov     ax,GSEL GDT_DOSSICG     ; Put gate selector in AX
        CCALL32 FreeDynLink
        mov     ax,GSEL GDT_DOSGLOBALSEG;  Put gate selector in AX
        CCALL32 FreeDynLink

        call    PhysToVirtReinit

;       Change various descriptors to DPL 0

        call    SELReinitDPL

;       enable nmi watchdog timer
        CALLFAR NWDEnableWatchdogNMI

;       Free config.sys and message segments

        DosInitContext ds
        push    [pDosSMSysInitSem]
        push    [syiMessageSelector]    ; save message segment

        mov     ax,[Config_Sys_Data]            ; free CONFIG.SYS data
        CALLFAR SegFree

        pop     ax                              ; free message segment
        CALLFAR SegFree

        CCALL32 _DevhlpDisableAccess    ; Disable Init time only Devhlps
        CCALL32 _LDRDisableDirtyHooks   ; Disable fixups to dirty hooks
        DosContext fs
        inc     fs:[_InitCodeInvalid]   ; we're not at init time anymore
        jmp     far ptr sicgf2rest      ; Jump into HIGH3CODE segment

EndProc sicgf2

INITCODE ends


        FARCODE HIGH3
HIGH3CODE segment

;**     sicgf2rest - rest of sicgf2
;
;       This function frees the kernel init-time memory objects and,
;       after a delay, calls _LDRFreeTask to free sysinit's memory and
;       loader resources and w_Exit to terminate the System Initialization
;       thread.  Other threads in the same task remain active.
;
;       ENTRY   NONE
;       EXIT    doesn't
;       USES    doesn't return

Procedure sicgf2rest,far,local
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:TASKAREA

        cmp     _smDelaySwap, 0
        jne     skip_freeinit           ; do it later when instructed by api
        CCALL32 _LDRFreeInit            ; free init-time kernel memory objects
skip_freeinit:
    assume ds:nothing, es:nothing

;80080 Delay may have been done in syi2StartDaemonProcesses.
;80080  Delay terminating the System Initialization Process until
;80080  the Program Selector has a chance to start up - this will
;80080  preserve all the initialization done by the dynamic link
;80080  libraries invoked by System Initialization
;80080  (e.g. KBD, VIO, MSG)

        DosContext ds
        pop     ebx                     ; Address of DosSMSysInitSem
                                        ; Pushed in sicgf2
        mov     eax,syiGlobalFlags
        and     eax,SYI_PM_PROG_TO_RUN
        .if     <zero eax>           ; Did we find RUN= PM program, if we did
           mov     eax,ebx           ; then we already waited on this SEM
           shr     eax,16            ; Addr to sem in AX:BX
           mov     cx,-1                   ; time out value
           mov     di,cx
           CALLFAR SemWait
        .endif

;       We have waited long enough.
;       Call _LDRFreeTask to break SysInit's dynalink connection and
;       free up all of its memory resources.  We set PS_DYING to keep
;       TKScanTasks away from sysinit.

        FlatContext <ds,es>,ax
        mov     eax,[_pPTDACur]
        or      byte ptr [eax].ptda_ProcStatus,PS_DYING
.errnz  PS_DYING AND NOT 0FFH

        CCALL32 _LDRFreeTask,<eax>      ; free this process's resources

;       No more private arena accesses are allowed from now on, so we
;       mark this thread as internal.  (restricts TIB access)

        xor     eax,eax
        mov     ecx,[_pTSDCur]
        mov     [ecx].TSDUserSS,ax      ; Mark thread internal
        CCALL32 _TKExit,<eax, eax>      ; Now kill this thread.
        Panic   <'sicgf2rest: _TKExit returned'>

EndProc sicgf2rest

HIGH3CODE ends


        FARCODE INIT
INITCODE segment

;**     sicgf3 - get device driver module information
;
;       Given a module handle, this function checks to see that
;       the given module is a valid device driver module, and if it
;       is, the function returns a pointer to the first header in
;       the data segment, the GDT ring 3 CS selector, and the
;       real and protected mode (ring 0) CS and DS values to place
;       in the headers in the module.
;
;       ENTRY   (DS:SI) = pointer to struc sicg_pkt3 (see sicg.inc)
;       EXIT    Carry clear
;                   Driver module is valid
;               Carry set
;                   (AX) = error code
;                   Driver module is not valid
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, Flags

Procedure sicgf3,far
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        LocalVar pktptr,DWORD           ; Data packet pointer = DS:SI
        LocalVar protcs,WORD            ; GDT CS selector
        LocalVar protds,WORD            ; GDT DS selector
        LocalVar mtehndl,WORD           ; Module table entry handle

        EnterProc <ds,si>               ; Set up stack frame

;       Look at module table entry and verify segment count.

        mov     si,[si].sicg_devhndl    ; Get alias to module table entry
        mov     mtehndl,si              ; save MTE handle
        mov     ax,si                   ; MTE handle
        mov     bx,1                    ; get info on segment 1
        push    ds
        push    es
        FlatContext <ds,es>
        CALLF32 num_to_object
        pop     es
        pop     ds
        jc      sicg3err
        cmp     bl,DATA_TYPE
        jne     sicg3err                ; no, not a device driver
        les     di,pktptr               ; (ES:DI) points to data packet
        mov     word ptr es:[di].sicg_devhdr+2,ax
        mov     word ptr es:[di].sicg_devhdr,0
        mov     protds,ax               ; save data selector

        mov     ax,mtehndl              ; MTE handle
        mov     bx,2                    ; Examine second segment
        push    ds
        push    es
        FlatContext <ds,es>
        CALLF32 num_to_object
        pop     es
        pop     ds
        jc      sicg3err
        cmp     bl,CODE_TYPE
        jne     sicg3err                ;  no, not a device driver
        lds     di,pktptr               ; (DS:DI) points to data packet
        mov     [di].sicg_devcs,ax      ; Return LDT selector in packet
        mov     protcs,ax               ; save code selector

;       Store values in data packet

        mov     ax,protcs
        mov     [di].sicg_protcs,ax
        mov     ax,protds
        mov     [di].sicg_protds,ax
        mov     [di].sicg_realcs,0      ; shouldn't be called from real mode
        mov     [di].sicg_realds,0      ; any more but, just in case
        DosContext es
        mov     al,es:NumIOReserveDriveLetter  ; Get Drive number
        mov     [di].sicg_drvno,al
        clc
        jmp     short sicg3x

sicg3err:
        mov     ax,-1
        stc

sicg3x: LeaveProc
        ret

EndProc sicgf3


;**     sicgf4 - add device to device driver chain
;
;       This function adds the driver whose header address
;       is given in the data packet to the system device
;       list.
;
;       ENTRY   DS:SI   pointer to struc sicg_pkt3 (see sicg.inc)
;       EXIT    NONE
;       USES    AX, DI, DS, ES, Flags

Procedure sicgf4,far,local
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        les     di,[si].sicg_devhdr     ; ES:DI points to device header
        DosContext ds                   ; DWORD CONdev points to list
        test    es:[di].SDevAtt,DEV_CHAR_DEV ; char device?
        jz      sicg40                  ; no
;
; char device, place at front of list
;
        mov     ax,word ptr CONdev+2    ; Link list to new driver
        mov     word ptr es:[di].SDevNext+2,ax
        mov     ax,word ptr CONdev
        mov     word ptr es:[di].SDevNext,ax
        mov     ax,es:[di].SDevProtDS   ; Put new driver at front of list
        mov     word ptr CONdev+2,ax
        mov     word ptr CONdev,di
        jmp     short sicg4x            ; all done
;
; block device, place at end of list.  First scan through list to find end.
;
sicg40: SaveReg <si>
        mov     si,OFFSET CONdev        ; DS:SI -> start of device chain
sicg41: cmp     word ptr ds:[si.SDevNext].Segmt,0ffffh ; end of chain?
        je      sicg45                  ; yes, add new device to end
        lds     si,ds:[si].SDevNext     ; no, point to next device in chain
        jmp     short sicg41            ; find end of chain
;
; DS:SI -> last device header in chain
; ES:DI -> new device's header
;
sicg45: mov     ax,es:[di].SDevProtDS   ; segment of new device header
        mov     ds:[si.SDevNext].Segmt,ax ; tack on to end of chain
        mov     ds:[si.SDevNext].Offst,di ; offset of new device header
        mov     word ptr es:[di.SDevNext].Segmt, 0ffffh ; set new of of list
        mov     word ptr es:[di.SDevNext].Offst, 0ffffh
        RestoreReg <si>

sicg4x: clc
        ret

EndProc sicgf4


;**     sicgf5 - clean up after successful device driver load
;
;       This function is called when a device driver module
;       has been loaded successfully.  It must:
;
;       1) Resize the first code and first data segments
;       2) Set DPL = 0 for all the GDT segments
;
;       ENTRY   (DS:SI) = pointer to struc sicg_pkt3 (see sicg.inc)
;               (DS:[SI].sicg_devhndl) = handle of MTE
;               (DS:[SI].sicg_ecode) = new code segment length - 1
;               (DS:[SI].sicg_edata) = new data segment length  - 1
;               (DS:[SI].sicg_protcs) = GDT code selector
;               (DS:[SI].sicg_protds) = GDT data selector
;       EXIT    Carry clear
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, Flags

Procedure sicgf5,far
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        LocalVar f5_mtehdl,WORD         ; MTE handle
        LocalVar f5_gdtds,WORD          ; GDT Data selector
        LocalVar f5_gdtcs,WORD          ; GDT Code selector
        LocalVar f5_codelen,WORD        ; New code segment length
        LocalVar f5_datalen,WORD        ; New data segment length

        EnterProc

;       Save info from data packet

        mov     ax,[si].sicg_protcs
        mov     f5_gdtcs,ax
        mov     ax,[si].sicg_protds
        mov     f5_gdtds,ax
        mov     ax,[si].sicg_ecode
        mov     f5_codelen,ax
        mov     ax,[si].sicg_edata
        mov     f5_datalen,ax
        mov     si,[si].sicg_devhndl
        mov     f5_mtehdl,si

;       Resize the segments.

        FlatContext <ds,es>,ax
        mov     di,f5_gdtcs             ; GDT selector in DI
        mov     bx,f5_codelen           ; New size in BX
        call    sicgf5sub               ; Call helper
        mov     di,f5_gdtds             ; GDT selector in DI
        mov     bx,f5_datalen           ; New size in BX
        call    sicgf5sub               ; Call helper

;       setup DPL = 0 for all the GDT selectors

        movzx   eax,[f5_mtehdl]         ; MTE handle
        CCALL32 _LDRINITSetGDT_DPL0,<eax>
IFDEF   PMDEBUG
        mov     si,[f5_mtehdl]          ; (SI) = DD's MTE handle
        call    DebugRemoveSymMTE       ; unlink DD's MTE info, keep symbols
ENDIF
        LeaveProc
        ret

sicg5err:
        InternalError <w_SICG: sicgf5 failed>

EndProc sicgf5


;**     sicgf5sub - resizes the passed segment
;
;       This function resizes the passed segment.
;
;       ENTRY   (DI) = segment's GDT selector
;               (BX) = new segment length - 1
;       EXIT    Carry clear
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, Flags

Procedure sicgf5sub,near,local
    assume ds:FLAT, es:FLAT, ss:TASKAREA

;       Resize the segment

        or      bx,bx                   ; Bad size request?
        jz      short sicgx             ; Skip realloc if so
        movzx   ebx,bx
        inc     ebx                     ; (ebx) = new segment size
        movzx   edi,di                  ; (edi) = segment's GDT selector
        CCALL32 _LDRReallocDD,<edi,ebx>
        or      eax,eax
        .errnz  NO_ERROR
        jne     short sicg5err

sicgx:  ret
EndProc sicgf5sub


;**     sicgf6 - call a SysInit message routine.
;
;       This function loads ds:si with a DOSINITDATA message pointer and
;       calls the specified routine.
;
;       ENTRY   (DS:SI) = pointer to struc sicg_pkt6 (see sicg.inc)
;       EXIT    NONE
;       USES    AX, SI, DS, Flags

Procedure sicgf6,far,local
    assume ds:nothing, es:nothing, ss:TASKAREA
        mov     ax,[si].si6_pfn
        lds     si,[si].si6_psmt        ; (ds:si) -> SMT
        call    ax                      ; call specified function
        clc
        ret
EndProc sicgf6


;**     sicgf7 - add DPB's to the list
;
;       This function completes the initialization of
;       the DPB's for the given driver and adds them
;       to the list of DPB's.
;
;       ENTRY   (DS:SI) = pointer to struc sicg_pkt7 (see sicg.inc)
;       EXIT    Carry set
;                   (AX) = error code
;               Carry clear
;                   No error
;       USES    AX, BX, CX, DX, DI, ES, Flags

Procedure sicgf7,far,local
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        LocalVar f7_devhdr,DWORD        ; Device header address
        LocalVar f7_bpbvec,WORD         ; Offset of DPB vector
        LocalVar f7_maxsec,WORD         ; Maximum sector size
        LocalVar f7_BDrv,WORD           ; System Boot Drive
        LocalVar f7_nunits,BYTE         ; Number of units for this driver
        LocalVar f7_unit,BYTE           ; Unit for this DPB
        LocalVar f7_drive,BYTE          ; drive number

        EnterProc
        DosContext es                   ; Address DOSGROUP
        mov     al,es:NumIOReserveDriveLetter ; Get current number of units
        mov     cl,[si].si7_nunits      ; Get number of new units
        add     al,cl                   ; Add new units to old
        jc      sicg71                  ;  fail if addition overflows
        cmp     al,'Z' - 'A' + 1        ; Too many units?
        ja      sicg71                  ;  yes, branch

;       Initialize for loop

        mov     f7_nunits,cl            ; Save number of units
        mov     ax,es:[SISData].SIS_BootDrv ; Get Boot Drive from global area  @PAA04757A
        dec     ax
        mov     f7_BDrv,ax
        mov     ax,es:MaxSec            ; Save maximum sector size
        mov     f7_maxsec,ax
        mov     ax,word ptr [si].si7_bpbvec
        mov     f7_bpbvec,ax            ; Save BPB vector offset
        mov     dx,word ptr [si].si7_bpbvec+2 ; Save BPB selector in DX
        mov     ax,word ptr [si].si7_devhdr ; Save device header pointer
        mov     f7_devhdrl,ax
        mov     al,byte ptr [si].si7_unit
        mov     f7_unit,al              ; Save unit number
        mov     al,es:NumIOReserveDriveLetter
        mov     f7_drive,al             ; Save starting drive number
        lds     si,[si].si7_devhdr
        mov     ax,[si].SDevProtDS      ; Convert pointer to ring 0 pointer
        mov     f7_devhdrh,ax
        lds     si,es:LastDPB           ; DS:SI points to last DPB in list
        mov     es,dx                   ; ES points to BPB segment
    ASSUME      ES:NOTHING
        xor     ch,ch                   ; Number of new units in CX

;       Loop to initialize DPB's
;       CX = number of DPB's left to initialize
;       DS:SI = pointer to previous DPB
;       ES = BPB segment

sicg70: mov     bx,f7_bpbvec            ; Make ES:BX point to BPB vector
        add     f7_bpbvec,2             ; Increment vector pointer
        mov     bx,es:[bx]              ; Make ES:BX point to BPB
        mov     dx,es:[bx].BPSECSZ      ; Get sector size
        cmp     dx,f7_maxsec            ; Is it new largest sector?
        jbe     sicg71                  ; no, skip buffer reallocation
        call    sicg7realloc            ; reallocate VPB and write protect bufs
        jnz     sicg7err                ; failure -- handle the error
        mov     f7_maxsec,dx            ; save new maximum sector size
sicg71: mov     word ptr [si].dpb_next_dpb+2,ds
        lea     ax,[si+(size dpb)]      ; Link previous DPB to new one
        mov     word ptr [si].dpb_next_dpb,ax
        mov     si,ax                   ; Make DS:SI point to new DPB
        mov     word ptr [si].dpb_next_dpb,0FFFFh ; New DPB is end of list
        mov     word ptr [si].dpb_next_dpb+2,ds
        mov     ax,f7_devhdrh           ; Make DPB point to device
        mov     word ptr [si].dpb_driver_addr+2,ax
        mov     ax,f7_devhdrl
        mov     word ptr [si].dpb_driver_addr,ax
;
; Set up drive and unit numbers for this DPB and increment them for the
; next DPB (if any)
        mov     al,f7_unit
        mov     byte ptr [si].dpb_unit,al
        push    ds
        DosContext ds                   ; Address DOSGROUP
        mov     ax,f7_BDrv
        mov     ah,f7_drive
        cmp     ah,al
        jne     sicg72
        add     ah,NumIORipl
        mov     f7_drive,ah
sicg72:
        pop     ds
        ASSUME DS:NOTHING
        mov     byte ptr [si].dpb_drive,ah
        SaveReg <bx>                    ; start 99999
        movzx   ax,f7_drive
        xor     bx,bx                   ; It is a local drive
        CALLFAR AllocateDriveLetter
        RestoreReg <bx>                 ; end 99999

        inc     f7_unit
        inc     f7_drive
        push     ds
        DosContext ds                   ; Address DOSGROUP
        mov     ah,f7_drive
        mov     NumIOReserveDriveLetter,ah
        pop     ds
        ASSUME DS:NOTHING

;       DS:SI points to the DPB.
;       ES:BX points to the BPB.
;       DX is sector size from BPB

        mov     ax,es:[bx].BPRES        ; # reserved sectors = start of fat 1
        mov     [si].dpb_first_FAT,ax
        mov     [si].dpb_cbSector,dx    ; initial number of bytes per sector

        SaveReg <es>
        call    SetDPBRemovFlag         ; set removability flag of dpb
        RestoreReg <es>

;       loop    sicg70                  ; Loop
        dec     cx                      ; 99999 loop out of bounds
        jnz     sicg70

;       Update LastDPB

        DosContext ds
        mov     word ptr LastDPB,si     ; Update pointer to end of chain
        clc
        jmp     short sicg79

;       Invalid sector size

sicg7err:
        DosContext ds                   ; Reset link in last DPB
        lds     si,LastDPB
    ASSUME      DS:NOTHING
        mov     word ptr [si].dpb_next_dpb,0FFFFh ; DPB is end of list
        mov     word ptr [si].dpb_next_dpb+2,ds

;       Error exit

sicg78: mov     ax,-1
        stc

sicg79: mov     bx,f7_maxsec            ; final deterination of largest sector
        mov     ds:MaxSec,bx            ; store in system global
        LeaveProc
        ret

EndProc sicgf7

;
; sicg7realloc - reallocate VPB and write protect buffers
;
;       This routine will be called by sicgf7 if a dpb indicates a sector
;       size larger than any installed device.  The VPB (boot sector) and
;       write protect test buffers will be reallocated to be able to hold
;       a sector of the given size.
;
;       ENTRY:  DX:     requested size of buffers (maximum sector size)
;
;       EXIT:   NZ- reallocation failed
;               ZR- reallocation succeeded
;
;       USES:   eax,flags
;

Procedure sicg7realloc,near,local

        SaveReg <ds,es>

        DosContext <ds>
        movzx   eax,ds:[pVPBBuf].Segmt  ; VPB buffer selector
        call    sicg7reallocsub         ; resize the buffer
        jnz     short s7rx              ; failure--exit
        DosContext <ds>
        movzx   eax,ds:[pbufWPT].Segmt  ; WPT (write protect test) buf selector
        call    sicg7reallocsub         ; resize the buffer

s7rx:   RestoreReg <es,ds>
        ret

EndProc sicg7realloc


; subroutine for sicgf7realloc--does the actual dirty work.
;
;       ENTRY   EAX = selector for buffer to reallocate
;                DX = new requested buffer size
;
;       EXIT:   NZ = reallocation failed
;               ZR = reallocation succeeded
;
;       USES:   eax,ds,es,flags
;

Procedure sicg7reallocsub,near,local

LocalVar        s7rs_junk,DWORD         ; linear address of realloc block

        EnterProc
        SaveReg <ecx,edx>

; SELReallocKHB(VM_HKRH_PUBRW,sel,cb,fl,ppb)

        FlatContext <ds,es>,cx

        movzx   edx,dx                  ; new size
        SSToDS  ecx,s7rs_junk
        CCALL32 _SELReallocKHB,<dword ptr VM_HKRH_PUBRW, \
                eax,edx,dword ptr 0,ecx>

        ASSUME  DS:NOTHING,ES:NOTHING
        or      eax,eax                 ; set zf appropriately
        RestoreReg <edx,ecx>
        LeaveProc
s7rsx:  ret

EndProc sicg7reallocsub


;       sicgf8 - setup DPL=0 for FSD's GDT selectors
;
;       This function is called when a FSD module's init routine completed
;       successfully. It sets up DPL=0 for all the GDT selectors in the
;       FSD module's MTE.
;       It also sets the owner of each segment to IFSOWNER.
;
;       ENTRY   (DS:SI) = pointer to struc sicg_pkt3
;               (DS:[SI].sicg_devhndl) = handle of MTE
;               (NOTE: none of the other fields in sicg_pkt3 structure
;                      are used in this routine)
;
;       EXIT    In each entry in the segment table in the FSD's MTE,
;                   DPL set to 0 for the GDT selector in ste_selector
;                   ste_seghdl set to 0
;
;       USES    AX, SI, Flags
;
;       PSEUDOCODE
;
;               for each entry in segment table do {
;                 set DPL=0 in the GDT selector;
;                 set ste_seghdl = 0;   /* so that DOSFREEMODULE will not
;                                          cause this segment to be freed
;                 set ste_selector = 0; /* so that DOSFREEMODULE will not
;                                          cause this selector to be freed
;               }
;

Procedure sicgf8,far,local
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        LocalVar f8_segno,WORD

        EnterProc

        push    si
        mov     si,[si].sicg_devhndl
IFDEF   PMDEBUG
        call    DebugRemoveSymMTE       ; unlink FSD's MTE info, keep symbols
ENDIF

        pop     si
        push    ds
        push    es
        movzx   eax,[si].sicg_devhndl
        FlatContext     <ds,es>
        CCALL32 _LDRINITSetGDT_DPL0,<eax>

        pop     es
        pop     ds
        LeaveProc
        ret

EndProc sicgf8


;       sicgf9 - perform calls to fscCheck and fscAdd
;
;       This function is called by sysinit to get to ring-0 so it
;       can call fscCheck and fscAdd.  fscCheck determines whether
;       an FSD is legal to install.  fscAdd performs a step in the
;       actual installation.
;
;       ENTRY   DS:SI = pointer to struc sicg_pkt9
;               DS:[SI].si9_funcnum = 0 for fscCheck, 1 for fscAdd
;
;       EXIT    (AX) = return from fscCheck or fscAdd
;
;       USES    AX, BX, SI, DS, ES, Flags
;
;       PSEUDOCODE
;               if (si9_funcnum == 0)
;                       call fscCheck
;               else
;                       call fscAdd
;               return
;
Procedure sicgf9,far,local
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        cmp     DS:[si].si9_funcnum,2
        ja      sicg9err
        je      sicg90
        cmp     DS:[si].si9_funcnum,1
        je      sicg95
        call    fscCheck
        ret

sicg90: mov     ax,GSEL GDT_FSC
        call    fscInit
sicg95: call    fscAdd
        ret

sicg9err: InternalError <w_sicg - sicgf9: bad command value>

EndProc sicgf9


;**     sicgfA - initialize mvdm
;
;       ENTRY   DS:SI = pointer to struc sicg_pktA (see sicg.inc)
;
;       EXIT    carry set so SCI returns AX
;               (AX) = 0, 3xBox started successfully
;               (AX) = !0, couldn't start 3xBox
;                      (AX) = error code
;
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, Flags
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

Procedure sicgfA,far,local
        ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING
        CCALL32 MVDMINIT,,nocheck
        stc
        ret
EndProc   sicgfA


;**     sicgfB - Deinstall a device driver
;
;       Search for a device driver with the given attributes and name, and
;       deinstall it.
;
;       ENTRY   DS:SI = pointer to struc sicg_pktB (see sicg.inc)
;       EXIT    ax = 0 if no device found or deinstall OK.
;               ax = 1 if device driver refused deinstall request.
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, Flags

Procedure sicgfB,far,local
        ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING

        LocalVar PreviousDevice,DWORD

        EnterProc

        mov     dx, ds:[si].siB_Attributes
        test    dx, DEV_NULL
        jnz     fB_RefuseDeinstall
        test    dx, DEV_CHAR_DEV
        jz      fB_RefuseDeinstall

        les     di, ds:[si].siB_Name

        DosContext ds
        mov     bx, offset DOSGROUP:CONdev
        Assume ds:nothing

; Start search after NUL and CON which are first and second in the device
; chain. (These two drivers can not be deinstalled.)

fB_SearchLoop:
        cmp     word ptr ds:[bx].SDevNext, -1
        je      fB_ReturnOk

        mov     PreviousDeviceh, ds
        mov     PreviousDevicel, bx
        lds     bx, ds:[bx].SDevNext

        cmp     dx, ds:[bx].SDevAtt
        jne     fB_SearchLoop

        lea     si, ds:[bx].SDevName
        mov     ax, di          ; save di
        mov     cx, 4
        repe cmpsw
        mov     di, ax          ; restore di
        jne     fB_SearchLoop

; Ask the device driver to deinstall itself
        mov     si, bx
        CALLFAR DevDeInstall
        jc      fB_RefuseDeinstall

; Unlink the deinstalled device from the device chain
        les     di, PreviousDevice
        lds     si, es:[di].SDevNext
        lds     si, ds:[si].SDevNext
        mov     word ptr es:[di].SDevNext, si
        mov     word ptr es:[di].SDevNext+2,ds

fB_ReturnOk:
        xor     ax, ax
        jmp     short fB_Exit

fB_RefuseDeinstall:
        mov     ax, 1
        stc

fB_Exit:
        LeaveProc
        ret

EndProc sicgfB

;**     sicgfC - handle errors generated by VDMM initialization
;
;       There are four subfunctions.    The overall purpose is to
;       communicate with the SysInit thread in order to receive
;       nonfatal error messages.
;
;       Two semaphores and one global flag are used:
;
;           hsemVDMMInitErrorHandlerBusy
;               Set   = The error handler thread is busy processing
;                       a nonfatal error.
;               Clear = The error handler thread is waiting for a message.
;
;           hsemVDMMInitMessageBufferEmpty
;               Set   = The message buffer is empty.
;               Clear = The message buffer contains a message.
;
;           fVDMMInitComplete
;               Becomes TRUE when initialization has finished.  Upon
;               seeing this, the error handler thread kills itself.
;
;       A function wishing to communicate with the message buffer must
;       perform the following steps:
;
;           *   Fill the message buffer
;           *   Set hsemVDMMInitErrorHandlerBusy
;           *   Clear hsemVDMMInitMessageBufferEmpty
;                   This wakes up the error handling thread.
;           *   Wait for hsemVDMMInitErrorHandlerBusy to clear.
;                   This ensures that the error message has been
;                   completely processed before generating another
;                   error message.
;
;       It is assumed throughout that there is only one other thread
;       which is trying to communicate with the error handling thread.
;       This simplifies the semaphore handling.
;
;
;       Subfunction 0:  Called by the error handling thread.
;           Indicate that the message buffer is empty.
;           Indicate that the error handler is not busy.
;           Wait for the message buffer to fill.
;           If fVDMMInitComplete
;              Indicate that we're finished with InitData (so it won't be
;                freed before we check this flag)
;              and kill yourself (DosExit)
;
;       Subfunction 1:  Called by the SysInit thread to preinitialize
;                       and preterminate.
;           Indicate that the error handler is busy.
;
;       Subfunction 2:  Called by the SysInit thread to postinitialize.
;           Wait for the error handler to become unbusy.
;
;       Subfunction 3:  Called by the SysInit thread to terminate.
;           Indicate that the message buffer contains information.
;              (The information is the message "please die")
;           Wait for indication that we're finished with InitData
;              (so it won't be freed before we check fVDMMInitComplete)
;
;
;       The additional subfunctions are used as follows:
;
;       To start the new thread:
;           Allocate a stack segment for the thread
;           Set hsemVDMMInitErrorHandlerBusy (subfunction 1)
;           Start the new thread
;           Wait for hsemVDMMInitErrorHandlerBusy to clear (subfunction 2)
;               This waits until the error handler thread gets itself
;               all settled and ready.
;
;       To kill the thread:
;           Set hVDMMInitComplete = TRUE
;           Call Subfunction 3 to tell the error handler thread to die
;           We don't bother to free the stack segment since that'll
;               happen automagically after initialization is complete.
;
;
;       ENTRY   DS:SI points to a function code
;
;       EXIT    NONE
;
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, Flags
;
;       METHOD
;
;       switch (ds:[si]) {
;       case 0:
;           SemSet(hsemVDMMInitMessageBufferEmpty);
;           SemClear(hsemVDMMInitErrorHandlerBusy);
;           SemWait(hsemVDMMInitMessageBufferEmpty, -1);
;           if (fVDMMInitComplete)
;               SemClear(hsemSICGThreadDead);
;               DosExit(0,1);
;           break;
;
;       case 1:
;           SemSet(hsemVDMMInitErrorHandlerBusy); break;
;
;       case 2:
;           SemWait(hsemVDMMInitErrorHandlerBusy, -1); break;
;
;       case 3:
;           SemSet(hsemSICGThreadDead);
;           SemClear(hsemVDMMInitMessageBufferEmpty); break;
;           SemWait(hsemSICGThreadDead, -1);
;
;       default:
;           Panic;
;       }
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

Procedure sicgfC,far,local
        mov     cx,[si]
        mov     ax,DOSINITDATA
        jcxz    fC_case0

        mov     bx,offset hsemVDMMInitErrorHandlerBusy
        loop    fC_notcase1

        CALLFAR SemSet                  ; case 1
        ret

fC_notcase1:
        loop    fC_notcase2
        mov     cx,-1                   ; case 2
        mov     di,cx
        CALLFAR SemWait
        ret

fC_notcase2:
        loop    fC_panic                ; case 3

        mov     bx,offset hsemSICGThreadDead
        CALLFAR SemSet

        mov     ax,DOSINITDATA
        mov     bx,offset hsemVDMMInitMessageBufferEmpty
        CALLFAR SemClear

        mov     ax,DOSINITDATA
        mov     bx,offset hsemSICGThreadDead
        mov     cx,-1
        mov     di,cx
        CALLFAR SemWait

fC_done:
        ret

fC_panic:
        InternalError <fC_panic>

fC_case0:
        mov     ax,DOSINITDATA
        push    ax
        mov     bx,offset hsemVDMMInitMessageBufferEmpty
        CALLFAR SemSet


        mov     ax,DOSINITDATA
        mov     bx,offset hsemVDMMInitErrorHandlerBusy
        CALLFAR SemClear

        mov     ax,DOSINITDATA
        mov     bx,offset hsemVDMMInitMessageBufferEmpty
        mov     cx,-1
        mov     di,cx
        CALLFAR SemWait

    ASSUME DS:DOSINITDATA
        pop     ds
        cmp     fVDMMInitComplete,0
        jz      fC_done

        mov     ax,DOSINITDATA                ;
        mov     bx,offset hsemSICGThreadDead  ; Signal "I'm dead"
        CALLFAR SemClear                      ;


        push 0
        push 1
        call pDosExit
EndProc   sicgfC


;**     sicgfD - Panic from ring 3
;
;       This routine calls the Panic support routine.
;
;       ENTRY   (DS:SI) -> sicg_pktD structure
;       EXIT    NONE
;       USES    SI, Flags

Procedure sicgfD,far,local
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:TASKAREA
        call    _panicsicg
        clc
        ret
EndProc sicgfD


;**     sicgfE - call swapper reinitialization
;
;       This routine calls the swapper re-initialization function, and
;       calls clock with loadable DD init packet.  Clock can now grab
;       IRQ0 (RIPL is in stage 3).
;
;       ENTRY   none
;       EXIT    none
;       USES    EAX,EBX,ECX,EDX,EDI,DS,ES

Procedure sicgfE,far,local
        assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:TASKAREA
        LocalVar PreviousDevice,DWORD
        EnterProc

;re-init swapper
        DosInitContext  ds,di
        mov     ds,[Config_Sys_Data]
        mov     di,ds:[0].CFG_SwapPath.CFG_Tail
        or      di,di
        jz      short sfe10
        movzx   esi,ds:[di].CFG_Path
        mov     ax,ds
        CALLFAR VirtToLin               ; (ax:esi) = vaddr of swappath string
        jmp     sfe20                   ; (eax) = laddr of swappath string

sfe10:  xor     eax,eax

sfe20:  FlatContext <ds,es>,bx
        cmp     _smDelaySwap, 0         ; if DELAYSWAP in effect skip for now
        jne     sfe30
        CCALL32 _SMReinit, <eax>        ; (eax) = laddr of swappath string
sfe30:                                  ; delay swapper start

;re-init clock DD
        DosInitContext es
        mov     di, offset clock_name

        DosContext ds
        mov     bx, offset DOSGROUP:CONdev
        Assume ds:nothing

; Start search after NUL and CON which are first and second in the device
; chain.

sfe_SearchLoop:
        cmp     word ptr ds:[bx].SDevNext, -1
        je      sfe_Exit

        mov     PreviousDeviceh, ds
        mov     PreviousDevicel, bx
        lds     bx, ds:[bx].SDevNext


        lea     si, ds:[bx].SDevName
        mov     ax, di          ; save di
        mov     cx, 4
        repe cmpsw
        mov     di, ax          ; restore di
        jne     sfe_SearchLoop

; Ask the clock device driver to reinstall itself
        mov     si, bx          ;ds:si = address of device header
        DosContext es           ;es    = DosGroup
        xor     dx,dx           ;cx:dx = parameter list
        mov     cx,dx

        call    InitDevice

sfe_Exit:

        LeaveProc
        RETP
EndProc sicgfE

;**     sicgf10 - Call SetPMCharDevEntries to set up table of entries
;                    for all the base character devices installed so far.
;
;       ENTRY   DS:SI   pointer to struc sicg_pkt10 (see sicg.inc)
;       EXIT    NONE
;       USES    bx, cx, dx, si, di, es
;
Procedure sicgf10,far,local
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        call    SetPMCharDevEntries
        ret

EndProc sicgf10


;**     sicgf11 - Add entry for this character device into the table
;
;       If an entry exists for a device with the same name, we replace
;       it with the new one.
;
;       ENTRY   DS:SI   pointer to struc sicg_pkt11 (see sicg.inc)
;
;       EXIT    NONE
;
;       USES    Flags, BX, CX, DI, DS, ES
;
Procedure sicgf11,far,local
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        mov     ax,1                    ; This is a bimodal driver
        lds     si,[si].si11_devhdr
        mov     al,DEVTYPE_BIMODAL      ; AL = Type (bimodal device)
        mov     ah,ACT_DEVREPLACE       ; AH = Action (Replace existing dev)
        CALLFAR AddRecFromDevHdr
        ret
EndProc sicgf11

;**     sicgf12 - Call MFS_TERM to terminate the Mini-FileSystem Device
;
;       ENTRY   DS:SI   pointer to struc sicg_pkt0 (see sicg.inc)
;       EXIT    Carry clear
;       USES    AX, ES

Procedure sicgf12,far,local
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        SaveReg <ds,bx,cx,dx>
        DosInitContext ds
        mov     si,offset DOSINITDATA:mfscInitTemplate
        call  DWORD ptr ds:[si].MFS_TERM
        RestoreReg <dx,cx,bx,ds>
        or      ax,ax
        jnz     sicg125
        clc
        jmp     sicg127
sicg125:
        stc
sicg127:
        ret

EndProc sicgf12

;**     sicgf13 - Available
;

Procedure sicgf13,far,local
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        ret

EndProc sicgf13

;**     sicgf14
;

Procedure sicgf14,far,local
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        push    ds
        push    es
        FlatContext <ds,es>
        CCALL32 InitNPXEM
        pop     es
        pop     ds
        ret

EndProc sicgf14

;**     sicgf15
;
Procedure sicgf15,far,local
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA
        mov     bx,[si].si15_pipehdl
        call    QueryPipe
        ret
EndProc sicgf15

;**     sicgf16 - Call DosHlpProtGetMessage to get kernel message stored
;                 in os2ldr.
;       ENTRY   DS:SI   pointer to structure sicg_pkt16
;                       sicg_pkt16.sicg_msg_num = the message ID
;       EXIT    Carry clear
;               sicg_pkt16.sicg_msg_ptr = pointer to the returned message
;
;       USES    Flag

Procedure sicgf16,far,local
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        SaveReg <ds,si,es,di,ax,cx>
        push    ds
        push    si
        pop     di
        pop     es              ; (es:di) = sicg_pkt16
        mov     ax, [di + sicg_msg_num] ; (ax) = message number
        DosContext <ds>
        call    DHF.DosHlpProtGetMessage   ; (ds:si) = returned message
        les     di, es:[di + sicg_msg_buf] ; (es:di) = caller's message buffer
        mov     cx, ds:[si]     ; (cx) = message length
        add     cx, 3           ; include the message length word and NULL
        rep     movsb
        RestoreReg <cx,ax,di,es,si,ds>
        ret

EndProc sicgf16

;**     sicgf17 - Available
;
;       ENTRY   DS:SI = ???
;
;       EXIT    Carry clear
;
;       USES    Flag

Procedure sicgf17,far,local
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        clc
        ret

EndProc sicgf17

;**     sicgf18 - Call _LDRFreeModule with Force to Free DDs  77939 ceb
;                 in os2ldr.
;       ENTRY   (DS:SI) = pointer to struc sicg_pkt3
;               (DS:[SI].sicg_devhndl) = handle of MTE
;               (NOTE: none of the other fields in sicg_pkt3 structure
;                      are used in this routine)
;
;       EXIT    Carry clear
;

Procedure sicgf18,far,local

        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        EnterProc


        push    ds
        push    es
        movzx   ebx,[si].sicg_devhndl

        FlatContext     <ds,es>,ax,noassume
        CCALL32         _LDRFreeModule <ebx,dword ptr TRUE>

        pop     es
        pop     ds
        LeaveProc
        ret

EndProc sicgf18

;**     sicgf19 - Reboot
;
;       ENTRY   NONE
;
;       EXIT    Carry clear
;
;       USES    AX, BX

Procedure sicgf19,far,local

        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        EnterProc

        xor     bx,bx
        mov     ah,event_SM_CAD

        call    dh_SendEvent

        clc
        LeaveProc
        ret

EndProc sicgf19

;**     sicgf1A - Notify Resource Manager
;
;       ENTRY   NONE
;
;       EXIT    Carry clear
;
;       USES    AX, BX

Procedure sicgf1A,far,local

        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        EnterProc

        mov     di,[si].sicg_1A_off
        mov     ax,[si].sicg_1A_sel
        mov     es,ax
        mov     ax,[si].sicg_1A_func
        CALLFAR RMNotify

        clc
        LeaveProc
        ret

EndProc sicgf1A

INITCODE ends


        FARCODE HIGH32
HIGH32CODE      segment

;**     num_to_object - get object info
;
;       Given a MTE handle and a segment number return the flags and
;       selector for that object entry
;
;       ENTRY   (AX) = MTE handle
;               (BX) = Object number to get info on
;       EXIT    carry clear:
;               (AX) = selector
;               (BL) = CODE_TYPE or DATA_TYPE
;               carry set
;               error
;       USES    EAX, EBX, ECX, EDX

Procedure num_to_object,far32
        ASSUME  DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING

        LocalVar objsel,WORD            ; place to get selector of object
        LocalVar segtype,BYTE           ; place to get segment type

        EnterProc

        mov     edx,[_TKSSBase]
        lea     ecx,segtype[edx]
        lea     edx,objsel[edx]
        movzx   ebx,bx
        movzx   eax,ax

;       rc = LDRINITInfo(hobmte, objno, psel, ptype)

        CCALL32 _LDRINITInfo,<eax,ebx,edx,ecx>
        .errnz  NO_ERROR
        or      eax,eax
        stc
        jnz     short nts_ret

        mov     ax,objsel               ; return selector in AX
        mov     bl,segtype              ; return module type
        clc                             ; return no error

nts_ret:
        LeaveProc
        RETC

EndProc num_to_object

HIGH32CODE      ends

        end
