;       SCCSID = @(#)sutil.asm  6.42 92/03/18
SCCSID equ "@(#)sutil.asm       6.42 92/03/18"
TITLE   Sutil -- OS/2 tasking module

;***    OS/2 Task dispatching Module
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;       This module comprises the main part of the dispatching.  There
;       are a few other modules included in the scheduler proper:
;
;               sched.inc       Definitions used elsewhere in scheduler
;               proc.inc        Definitions for TKBlock/TKRun
;               sproc.asm       Tasking process management
;               sched.asm       Scheduler utility routines
;               scdata.asm      Common scheduler data
;
;       The functions performed by this module include:
;
;               1)      Context switching
;               2)      Idle task
;
;
;       Modification History:
;
;               08 Dec 1986 - Bruce Bailey
;                  Code modified to comply with DCR 451 - routines
;                  changed are SchedClock, Yield, TCYield
;               20 May 1987 - Joseph A. Correnti
;                  Change SchedClock to use AX instead of AL for the
;                  Tick Interval
;               30 May 1987 - Added ASSUMEs, and Procedure/EndProc
;                  to iptq,rptq (PTM 5131).
;               05-Jun 1987 - Push initial value of InDos=1 on new
;                  thread's kernel stack (PTM 4496).
;               05-Aug 1987 - Removed ifdef's WINTHORN code
;               28-Apr 1988 - Clear timeout bit if removing thread
;                  from timeout queue. 1.1 PTM 2656. J.Freeman.
;               10-Aug-1988 - Remove clear and restore of interrupts
;                  from rptq. 1.2 PTM 1687. J.Freeman
;               07-Sep-1988 - Enter/exitcritsec optimisations
;                  (1.2 PTM 1974)
;               15-Apr 1991 - Joe Luu
;                  DCR 1315 High Performance Thread Dispatch
;                  Add code in TCYield for all time critical thread level
;                  replace SCHEDNEXT code
;               16-Apr 1991 to G.A. 3/92 TED WALDRON - MASSIVE REDESIGN
;               WHICH BOOSTED SCHEDULER PERFORMANCE DRAMATICALLY
;               03-Feb 1992 - Frank Schroeder
;                  Added Advanced Power Management (APM) support for
;                  idle and busy calls to APM BIOS.
;               15-Dec 1993 - RAC 76699 Make Perfview optional assembly value
;                  with PERFVIEW flag
;               22-12-93   RAC 76919 Uncomment SCHGetNext Runner extern.
;               18-Nov-95  MLM Defect 72417, moved find.inc after tkexport.inc

INCL_ERROR2_H   EQU     1               ; for error2.inc EQUates

.xlist
include dosseg.inc
include fnotask.inc
include basemaca.inc
include osmaca.inc
include defdbug.inc
include dossymc.inc
include bseerr.inc
include proc.inc
include sched.inc
include mi.inc
include pmode.inc
include infoseg.inc
include except.inc
include screen.inc
include ostypes.inc
include sft.inc
include sigdef.inc
include argframe.inc
include osconst.inc
include pgexport.inc
include vmexport.inc
include selxport.inc
include curdir.inc
include dirent.inc
include sem.inc
include tkexport.inc
include find.inc
include tkptda.inc
include kmode.inc
include perfki.inc			; D159047

ifdef PERFVIEW
include perfvw.inc
endif

include mmpha.inc
include tom.inc                                 ; P723209
.list
        ModName ,FNO_SUTIL_ASM
        CpuMode 386

        EXTRN           PostPending:NEAR
        EXTRN           Tick_Handler:NEAR
        EXTRN           trap07:NEAR
        EXTRN           VDMMPROCTICK:near

        EXTRN32         _TKUpdateState          ; JKL DCR1315
        EXTRN32         WaitNPX
        EXTRN32         _SCHGetNextRunner
        EXTRN32         _PGSwitchContext
        EXTRN32         _tkEvapPTDA
        EXTRN32         _tkFreeTCB
        EXTRN32         _TKWakeup
        EXTRN32         _kmCTXHDaemon
        EXTRN32         _TKIdleLoop
        EXTRN32         _TOMTick
;        EXTRN32         _SCHSetState
        EXTRN32         KMARMCONTEXTHOOK
        EXTRN32         _VMDonateKHB

        EXTRN32         _TOMCancel              ; P723209
        EXTRN32         _TOMQuery               ; P723209
        EXTRN32         _TOMSet                 ; P723209

        EXTRNF32        VDMMINITTIMER
        EXTRNF32        B1NPX_Errata_17

        EXTRNFAR        GetDescInfo
        EXTRNFAR        SegRealloc

        ReferFlat       _PGFaultCnt,DWORD       ; TWD DCR1203
        ReferFlat       runner,DWORD            ; JKL DCR1315
        ReferFlat       _ptcbPriQRunner,DWORD   ; JKL DCR1315
        ReferFlat       _ptcbPriQReady,DWORD    ; JKL DCR1315
        ReferFlat       _ptcbPriQDelayed,DWORD  ; JKL DCR1315
        ReferFlat       _schPriClassTbl,BYTE    ; JKL DCR1315
        ReferFlat       _pTCBCur,DWORD
        ReferFlat       _pPTDACur,DWORD
        ReferFlat       _papTCBSlots,DWORD
        ReferFlat       _toeTimeslice,DWORD
        ReferFlat       _tkMaxSlice,BYTE        ; TWD DCR1203
        ReferFlat       fAPMidle,BYTE
        ReferFlat       _Resched,BYTE
        ReferFlat       _TCResched,BYTE
        ReferFlat       _flGlobalActions,DWORD
;b726833 ReferFlat       _R2CS,WORD
;b726833 ReferFlat       FPEMHandler,DWORD

ifdef MEMSTAT
        ReferFlat       _pGlobalMemStat,DWORD
endif

        ReferFlat       fpOldStack,FWORD
        ReferFlat       _pSigGCH,DWORD

        ReferFlat       CurProcPID,WORD
        ReferFlat       SchedPtr,DWORD
        ReferFlat       InterruptLevel,BYTE
        ReferFlat       MaxThreads,WORD
        ReferFlat       TaskNumber,WORD
        ReferFlat       _Lis2,BYTE
        ReferFlat       PendingSignals,WORD
ifdef MISCSTRICT
        ReferFlat       _cTKNoBlock
endif
        ReferFlat       fIdle
        ReferFlat       NPXNAVec,QWORD
        ReferFlat       Int21Vec,QWORD
        ReferFlat       _SELGDTLaddr,DWORD
        ReferFlat       _cmsecStarveLength,DWORD      ; P723209


        ReferFlat       _KI_proc_state,DWORD                    ; D159047
        ReferFlat       _KI_statechg_lts,DWORD                  ; D159047
        ReferFlat       _KI_statechg_hts,DWORD                  ; D159047
        ReferFlat       _KI_idle_ltime,DWORD                    ; D159047
        ReferFlat       _KI_idle_htime,DWORD                    ; D159047
        ReferFlat       _KI_busy_ltime,DWORD                    ; D159047
        ReferFlat       _KI_busy_htime,DWORD                    ; D159047
        ReferFlat       _KI_intr_ltime,DWORD                    ; D159047
        ReferFlat       _KI_intr_htime,DWORD                    ; D159047
        ReferFlat       _KI_latch_lts,DWORD                     ; D159047
        ReferFlat       _KI_latch_hts,DWORD                     ; D159047
        ReferFlat       _KI_latch_idle_ltime,DWORD              ; D159047
        ReferFlat       _KI_latch_idle_htime,DWORD              ; D159047
        ReferFlat       _KI_latch_busy_ltime,DWORD              ; D159047
        ReferFlat       _KI_latch_busy_htime,DWORD              ; D159047
        ReferFlat       _KI_latch_intr_ltime,DWORD              ; D159047
        ReferFlat       _KI_latch_intr_htime,DWORD              ; D159047
 
        ReferFlat       _CpuStepping,DWORD
        ReferGlobal     _StartLASTDATA,byte
        ReferGlobal     _EndLASTDATA,byte
        ReferGlobal     _StartABIOSDATA,byte
        ReferGlobal     ABIOS_present,byte
        ReferGlobal     mathCop,byte
        ReferGlobal     APM_IdleHookRtn,FWORD

        ReferGDT        GDT_PTDA,WORD
        ReferGDT        GDT_TSS,WORD            ; TSS descriptor

_DATA segment
        Errata17Ct      dw      ?
        AccmTicks       dw      0
_DATA ends

        FARCODE LOW
CODE segment

;***    SchedClock - Receive Clock Tick
;
;       SchedClock is entered from the clock routine every clock tick
;
;       ABIOS may attempt to call SchedClock before the scheduler has
;       been initialized, but we won't get here because the DevHlp "SchedPtr"
;       variable to this routine is not installed until ProtInit has
;       been called.
;
;       Post-EOI Reentrancy is controlled by the clock device driver.
;       SchedClock will only be called on the first level interrupt.
;
;       DCR 1345 (high resolution timers) allows the clock to be programmed
;       at 32hz or 128hz.  Procedure Tick_Handler assumes the frequency is a
;       constant 32hz.  To maintain upward compatibility, we continue to
;       call Tick_Handler at 32hz by counting accumulated tick time.
;
;
;       ENTRY   (AX) = tic interval, in milliseconds
;               (DH) = EOI flag
;                        0 = before EOI
;                        1 = after EOI
;       EXIT    NONE
;
;       USES    EAX, EBX, ECX, EDX, EBP, ESI, EDI, DS, ES, FS, GS, Flags

Procedure SchedClock,faronly
        ASSUME  DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:NOTHING

LocalVar tics,WORD
LocalVar eoiflg,WORD

        sti                             ; make sure interrupts happen ASAP
        xor     dl,dl
        EnterProc  <ax,dx>,,alignesp
        FlatContext <ds,es>,ax

; do work that needs to be done pre-EOI and post-EOI

        SaveAlignESP
        mov     ax,tics
        mov     dx,eoiflg
        movzx   eax,ax
        movzx   edx,dh

        PushP   edx
        PushP   eax
        CCallNest                       ; _TOMTick (tics, eoiflg)
        CCALL32 VDMMPROCTICK,<eax,edx>,,pascal
        CCALL32 _TOMTick,,,PushP        ; Timeout Manager Timer Tick
        pop     esp
        cmp     eoiflg,0                ; Pre-EOI?
        je      scx                     ; .. yes - all done

; do work that needs to be done at 32hz only (still post-EOI)

        mov     ax,[AccmTicks]          ; Recall how many milliseconds so far
        add     ax,tics                 ; Add in new increment.
        mov     [AccmTicks],ax          ; Save how many milliseconds so far
        cmp     ax,31                   ; Did more than 31ms pass?
        jl      scx                     ; .. no, done with this tick
        mov     [AccmTicks],0           ; .. yes, restart count, do 32hz work
        call    Tick_Handler            ; Timer Services has work to do
        FlatContext <ds,es>,ax          ; restore this context

ifdef MEMSTAT
        mov     eax,[_pGlobalMemStat]   ; (eax) = pointer to global statistics
        movsx   edx,byte ptr [fIdle]    ; (edx) = -1 if idle time, else 0
        neg     edx                     ; (edx) = 1 if idle time, else 0
        add     [eax].ms_idletime,edx   ; increment idle time count if needbe
        xor     edx,1                   ; (edx) = 0 if idle time, else 1
        add     [eax].ms_cputime,edx    ; increment busy cpu time count if need
        mov     eax,[_pTCBCur]          ; (eax) = pointer to current tcb
        mov     eax,[eax].TCBpMemStatCur; (eax) = pointer to tcb mem stats
        add     [eax].ms_cputime,edx    ; increment busy cpu time count
endif
        lgs     edi,[fpOldStack]        ; pointer to base isf_s on stack
        mov     dx,gs:[edi].isf_cs      ; get cs of first level nest stack frame
        and     dx,SEL_RPLMASK          ; mask all but RPL
        shr     dx,1                    ;
        movsx   edx,dx                  ; cast to long
        mov     eax,[_pTCBCur]          ; (eax) = pointer to current tcb
        add     [eax].TCBUserTime,edx   ; inc user time if ring 2 or 3
        xor     edx,1
        add     [eax].TCBSysTime,edx    ; inc system time if ring 0 (and 1)

;
;       The following code checks for 386-B1 Errata #17. See NPX.ASM for
;       more details.
;

        cmp     [_CpuStepping], CPU_386B1       ; Possibility of problems? b722559 / b732418
        je      short scl10                     ;b722559 / b732418

scx:    LeaveProc
        RETC

scl10:  mov     eax, gs:[edi].isf_eip   ; EAX = User EIP           b722559 / b732418
        and     eax, PAGEMASK           ; Look at just last page   b722559 / b732418
        cmp     eax, PAGESIZE - 14      ; w/in 8 Bytes of page end b722559 / b732418
        jge     short scl20             ;  Yes.                    b722559 / b732418
        mov     [Errata17Ct], 5         ;                          b722559 / b732418

scl20:  sub     [Errata17Ct],1          ; Have we hit the case 5 times b722559 / b732418
        jnz     short scx               ;  in a row (NZ ==> no)    b722559 / b732418
        CCALL32 B1NPX_Errata_17         ;                          b722559 / b732418
        jmp     short scx               ;                          b722559 / b732418

EndProc SchedClock


;***    TCYield -- Stop current thread and run time critical thread
;
;       ENTRY   TaskNumber      Task that has been queued to wait
;
;       EXIT    Running new task at the RETURN ADDRESS from when it called
;               SchedNext
;               Zero flag clear if we yielded
;                       Set otherwise
;       USES    Flags
;

Procedure TCYield,<near,faronly>
        ASSUME  DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:NOTHING
        push    ds
        FlatContext ds
        cmp     [_TCResched],0                  ; is someone waiting?
        jz      short tcyx

        SaveReg <eax, ecx, edx, es>
        FlatContext es,ax
        CCALL32 _TKTCYield,,nocall
        RestoreReg <es, edx, ecx, eax>      ; restore all
tcyx:   pop     ds
        ret
EndProc TCYield

CODE    ENDS


        FARCODE HIGH32
HIGH32CODE      SEGMENT

;***EP  TKTCYield - C callable interface routine to TCYield
;
;       ENTRY:  NONE
;
;       EXIT:   (EAX) = NO_ERROR        No context switch occurred
;               (EAX) = ERROR_BLOCKED   A context switch occcurred
;
        ASSUME DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING

Procedure _TKTCYield
        cmp     cs:[_TCResched],0       ; is some TC thread waiting?
        jz      tktr1                   ; NO - go return              B733890
        call    _TKYield                ; Yes - go yield              B733890
        jmp     short tktr2             ;                             B733890

tktr1:  xor     eax,eax                 ; no - NO_ERROR               B733890
tktr2:  RETC                            ;                             B733890

EndProc _TKTCYield


;***EP  TKYieldTimeslice - C callable interface routine to TCYieldTimeslice
;
;       ENTRY:  NONE
;
;       This entry point was made to support the Windows Yield Timeslice
;       Interrupt 2f for high interrupt activity.
;
        ASSUME DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING

Procedure _TKYieldTimeslice                                       ; 59378

        cmp     [_ptcbPriQReady],0      ; Anything to schedule ?    59378
        je      in2f1                   ; No - go back fast         59378
        or      byte ptr _Resched,RESCHED_TIMESLICE ; force resched 59378
        call    _TKYield                ; Yes - go yield            59378
in2f1:  RETC

EndProc _TKYieldTimeslice

;***EP  TKResched - yield if and only if Resched is set
;
;       ENTRY:  NONE
;
;       EXIT:   (EAX) = NO_ERROR if the thread did not yield
;                     = ERROR_BLOCKED if the thread yielded
;

Procedure _TKResched,near
        ASSUME DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING
        cmp     [_Resched],0            ; Is a reschedule needed?
        jz      tkr1                    ; NO - go return              B733890
        call    _TKYield                ; Yes - go yield              B733890
        jmp     short tkr2              ;                             B733890

tkr1:   xor     eax,eax                 ; no - NO_ERROR               B733890
tkr2:   RETC                            ;                             B733890
EndProc _TKResched

HIGH32CODE      ENDS

        FARCODE LOW
CODE segment

;***    _tkSchedNext - Switch Context to some Worthy (runnable) task
;***    _TKYield - same
;***    SchedNext - same, but 16-bit, saving registers
;***    Yield - same
;***    _tkSchedNextRet - Perform SchedNext post-switch processing
;
;       _tkSchedNext is entered to switch to the highest priority runable
;       thread.  SCHGetNextRunner is called to find this thread.
;
;       _tkSchedNext is the only way for a context switch to take place.
;       As far as an individual thread is concerned, it just calls
;       _tkSchedNext and eventually returns.
;
;       SchedNext is just a 16-bit thunk to _tkSchedNext, that saves
;       registers.  Yield is an alias name for SchedNext.
;
;       ENTRY   NONE
;
;       EXIT    (EAX) = NO_ERROR        No context switch occurred
;               (EAX) = ERROR_BLOCKED   A context switch occcurred
;
;               SchedNext/Yield set the zero flag according to (EAX),
;               but do not modify (EAX).  NO_ERROR sets the zero flag.
;
;       USES    _tkSchedNext, _TKYield is C calling convention
;               _tkSchedNextRet is C calling convention
;               SchedNext, Yield USES FS, GS, Flags

Procedure Yield,<near,faronly>
        jmp short SchedNext
EndProc Yield

Procedure SchedNext,<near,faronly>
        ASSUME DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:NOTHING
        SaveReg <eax, ecx, edx, ds, es>
        FlatContext <ds,es>,ax
        CCALL32 _tkSchedNext,,nocall
        or      eax,eax                         ; Set zero flag based on (EAX)
        RestoreReg <es, ds, edx, ecx, eax>      ; restore all
        RETC
EndProc SchedNext
CODE ends


        FARCODE HIGH32                          ; DCR 1315 - BEGIN
HIGH32CODE      SEGMENT

ifdef MISCSTRICT
schie1: Panic <'SchedNext: InterruptLevel'>
schie2: Panic <'SchedNext: _cTKNoBlock'>
endif

Procedure _tkSchedNextRet
        EnterProc                       ; EnterProc (_tkSchedNext)
        jmp     sch51                   ; perform exit processing only
EndProc _tkSchedNextRet,NoCheck

Procedure  _TKYield                     ;                             B733890
        SaveReg <ebx,esi,edi,ds,es>     ;                             B733890
        call    _tkSchedNext            ;                             B733890
        RestoreReg <es,ds,edi,esi,ebx>  ;                             B733890
        RETC                            ;                             B733890
EndProc  _TKYield                       ;                             B733890

Procedure _tkSchedNext
        ASSUME DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING
        EnterProc

ifdef MISCSTRICT
        cmp     [InterruptLevel],-1     ; Called at interrupt level?
        jnz     short schie1

        cmp     [_cTKNoBlock],0         ; Called during no-block section?
        jnz     short schie2
endif

sch10:  smsw    cx
        test    cx,CR0_MP               ; NPX present?
        jnz     sch100                  ; no, don't need to wait


;
;       if (TCResched)
;       {
;
;          if (ptcbPriQDelayed && (ptcbPriQDelayed->TCBPriClass &
;              CLASS_TIME_CRITICAL))
;             TKUpdateState (ptcbPriQDelayed);
;
;          if (ptcbPriQReady && (ptcbPriQReady->TCBPriClass &
;             CLASS_TIME_CRITICAL))
;             goto HPTDispatch;
;       }
;       else
;          goto NTDispatch;
;
;

sch15:
        cli                             ; P722986
        test    fAPMidle, 1             ; if processor clock slow from APM BIOS
        jnz     sch101                  ; idle call, must now step up clock
sch16:
        mov     eax, [_ptcbPriQReady]   ; EAX - 1st ready thread to run
        or      eax,0                   ; list empty?
        jz      NTDispatch

        or      [_ptcbPriQDelayed], 0   ; has ptcbpriqDelayed any thing?    ;P732143
        jnz     sch105                  ; no, perform normal thread dispatch;P732143

$DCR1315_10:

;        test    byte ptr _TCResched,RESCHED_TIMESLICE ; TC resched required?  ;P723209
;        jnz     NTDispatch              ; yes, insure round robin via normal thread dispatch ;P723209

;        test    byte ptr _Resched,RESCHED_TIMESLICE ; normal resched required?  ;P732143
;        jnz     NTDispatch              ; yes, insure round robin via normal thread dispatch ;P732143

;        or      _TCResched, 0           ; is time critical resched required?
;        jnz     HPTDispatch             ; no, perform normal thread dispatch P732143

;        or      _Resched, 0             ; is normal resched required?        P732143
;        jnz     HPTDispatch             ; no, perform normal thread dispatch P732143
;        jmp     NTDispatch              ; start the clock again with new runner

         mov     al,_TCResched
         or      al,_Resched
         jz      NTDispatch
         and     al,RESCHED_TIMESLICE
         jnz     NTDispatch

;
;       High Performance Thread Dispatch path.  Move the running thread
;       into the Ready Queue list, sort it, then pick out the most highest
;       time critical thread priority.
;

Entry   HPTDispatch                     ; for debugger timing

        mov     eax, [_ptcbPriQRunner]  ; EAX -> runner         AZ10473
        or      eax,0                   ;                       AZ10473
        je      HPTDispatch1            ;                       AZ10473
        mov     ecx,[_ptcbPriQReady]    ;                       AZ10473
        mov     cx,[ecx].TCBPriority    ;                       AZ10473
        cmp     [eax].TCBPriority,cx    ;                       AZ10473
        jae     NTDispatch              ;                       AZ10473

HPTDispatch1:                           ;                       AZ10473

        ; remove the running thread, and put it back
        ; on the ready queue
        ;
        ; if (runner != NULL & runner->TCBState == STATE_RUNNING)
        ; {
        ;    ptcbPriQRunner = NULL;
        ;    ptcb-> TCBQState = ptcb-> TCBState = STATE_READY;
        ;    ptcb-> TCBSchFlags &= ~SCH_MINSLICE;
        ;    if (ptcb-> TCBPriClassMod & (CLASSMOD_DEVICE|CLASSMOD_STARVED))
        ;    {
        ;        ptcb-> TCBPriClassMod &= ~CLASSMOD_DEVICE;
        ;        if (!(ptcb-> TCBForcedActions & TK_FF_SCH))
        ;            ptcb-> TCBPriClassMod &= ~CLASSMOD_STARVED;
        ;        else
        ;            ptcb-> TCBSchFlags |= SCH_MINSLICE;
        ;
        ;        TKPriCalc(ptcb);
        ;        ptcb-> TCBPriority =
        ;            schPriClassTbl [ptcb->TCBPriClass | ptcb->TCBPriClassMod] |
        ;            ptcb-> TCBPriLevel;
        ;
        ;        if (ptcb-> TCBPriority < ptcb-> TCBPriorityMin)
        ;            ptcb-> TCBPriority = ptcb-> TCBPriorityMin;
        ;    }
        ;
        ;    if ((ptcbPriQReady != NULL) &&
        ;        (ptcbPriQReady-> TCBPriority > ptcb-> TCBPriority)) {
        ;        Resched |= RESCHED_RELINQUISH;
        ;
        ;    }
        ;
        ;    tkPriQInsert (&ptcbPriQReady, ptcb);
        ; }

        or      [_ptcbPriQReady], 0     ; is there any thread ready to run?   P732143
        jz      sch95                   ; no, continue running current thread P732143
        or      [_ptcbPriQRunner], 0            ; anything to run?     P732143
        jz      $DCR1315_140
        or      [runner], 0                     ; is runner set ?
        jz      $DCR1315_140
        mov     eax, [runner]                   ; EAX -> runner
        cmp     [eax].TCBState,STATE_RUNNING    ; does it want to continue ?
        je      short $DCR1315_06               ; P732143
        ;
        ; the target state for the thread being preempted is not running
        ; this is a rare case so let the normal processing occur  for the
        ; state chain linking only
        ;
        mov     edx,[runner]                    ;P732143
        CCALL32 _TKUpdateState,<edx>            ; Update to new thread state  P732143
        jmp     $DCR1315_140                    ;P732143

$DCR1315_06:                                    ; P732143
        mov     [_ptcbPriQRunner], 0            ; insert runner into ready
        mov     [eax].TCBState, STATE_READY
        mov     [eax].TCBQState, STATE_READY
        and     [eax].TCBSchFlags, NOT SCH_MINSLICE
        test    [eax].TCBPriClassMod, CLASSMOD_DEVICE+CLASSMOD_STARVED
        jz      SHORT $DCR1315_50
        and     [eax].TCBPriClassMod, NOT CLASSMOD_DEVICE
        test    [eax].TCBForcedActions, TK_FF_SCH
        jnz     SHORT $DCR1315_20
        and     [eax].TCBPriClassMod, NOT CLASSMOD_STARVED  ;P724406
        jmp     SHORT $DCR1315_50

$DCR1315_20:
        or      [eax].TCBSchFlags, SCH_MINSLICE

$DCR1315_50:

        test    [eax].TCBPriClassMod,CLASSMOD_STARVED    ;P724406
        jnz     $DCR1315_55                              ;P724406
        cmp     [eax].TCBPriClass,CLASS_TIME_CRITICAL    ;P729570
        je      $DCR1315_55                              ;P729570
        cmp     [eax].TCBPriClass,CLASS_IDLE_TIME        ;P729570
        je      $DCR1315_55                              ;P729570
;                                                        ;P724094
;       Handle TOM events                                ;P724094
;                                                        ;P724094
        SaveReg <eax>                                    ;P724094
        lea     ecx,[eax].TCBtoe                         ;P724094
        CCALL32 _TOMQuery,<ecx>                          ;P724094
        or      eax,eax                                  ;P724094
        RestoreReg <eax>                                 ;P724094
        jnz     $DCR1315_55                              ;P724094
                                                         ;P724094
        SaveReg <eax>                                    ;P724094
        lea     ecx,[eax].TCBtoe                         ;P724094
        mov     edx,_cmsecStarveLength                   ;P724094
        CCALL32 _TOMSet,<ecx,edx,TOM_W_STARVE>           ;P724094
        RestoreReg <eax>                                 ;P724094

$DCR1315_55:                                             ;P724094
;        or      [_Resched], RESCHED_PREEMPT             ; P723209
        mov     cl, [eax].TCBPriClass           ; TKPriCalc
        or      cl, [eax].TCBPriClassMod
        movzx   ecx, cl
        movzx   dx, [eax].TCBPriLevel
        or      dx, word ptr _schPriClassTbl[ecx*2]
        mov     [eax].TCBPriority, dx
        cmp     dx, [eax].TCBPriorityMin
        jae     SHORT $DCR1315_60
        mov     cx, [eax].TCBPriorityMin
        mov     [eax].TCBPriority, cx

;
;       a duplicate of TKPriQInsert() in TKSLEEP.C for logic
;       eax = current running Thread's TCB
;       ebx = ptcbScan
;
;       ptcb-> TCBPriority =
;           schPriClassTbl [ptcb->TCBPriClass | ptcb->TCBPriClassMod] |
;           ptcb-> TCBPriLevel;
;
;       if (ptcb-> TCBPriority < ptcb-> TCBPriorityMin)
;           ptcb-> TCBPriority = ptcb-> TCBPriorityMin;
;
;       /* Scan the priority queue levels to find the insertion point.
;        * Leave ptcbScan pointing at or just past the insertion point.
;        */
;
;       PTCB  ptcbScan;
;       dx = tcbScanPriority;
;       cx = tcbCurrPriority;
;
;       ptcbScan = *pptcb;
;
;       if ((tcbScanPriority = ptcbScan-> TCBPriority) ==
;           (tcbCurrPriority = ptcb-> TCBPriority))
;       {
;           goto EndOfLevel;            // No need to recheck Priority
;       }
;       else
;       {
;           if (tcbScanPriority < tcbCurrPriority)
;           {
;               /* There is a new highest-priority thread.
;                * Set head pointer.  No scan required.
;                */
;               *pptcb = ptcb;
;               goto NewTopLevel;           // No need to recheck priority
;           } else {
;               /* Scan only if the new thread's priority is strictly
;                * between the Current highest and lowest priorities.
;                * (Note: this check protects against an infinite loop)
;                */
;               if (ptcbScan-> TCBpTCBPriHigher-> TCBPriority < tcbCurrPriority)
;               {
;                   /* Scan required.  We can never scan too far here.
;                    */
;                   while (ptcbScan-> TCBPriority > tcbCurrPriority)
;                   {
;                       ptcbScan = ptcbScan-> TCBpTCBPriLower;
;                   }
;               } else {
;                   /* If the thread is at an existing lowest priority,
;                    * insert it there.
;                    */
;                   if (ptcbScan-> TCBpTCBPriHigher-> TCBPriority ==
;                       tcbCurrPriority) {
;                       ptcbScan = ptcbScan-> TCBpTCBPriHigher;
;                   }
;               }
;           }
;       }
;
;       if (ptcbScan-> TCBPriority == tcbCurrPriority) {
;       EndOfLevel:
;           /* The priority level already exists.  Insert on end of level.
;            */
;           ptcb-> TCBpTCBPriNext = ptcbScan;
;           ptcb-> TCBpTCBPriPrev = ptcbScan-> TCBpTCBPriPrev;
;           ptcbScan-> TCBpTCBPriPrev-> TCBpTCBPriNext = ptcb;
;           ptcbScan-> TCBpTCBPriPrev = ptcb;
;       } else {
;       NewTopLevel:
;           /* Define new priority level, just above ptcbScan.
;            */
;           ptcb-> TCBpTCBPriLower  = ptcbScan;
;           ptcb-> TCBpTCBPriHigher = ptcbScan-> TCBpTCBPriHigher;
;           ptcbScan-> TCBpTCBPriHigher-> TCBpTCBPriLower = ptcb;
;           ptcbScan-> TCBpTCBPriHigher = ptcb;
;       }

$DCR1315_60:
        push    ebx
        mov     ebx, [_ptcbPriQReady]   ; EBX = ptcbScan
        mov     dx, [ebx].TCBPriority
        mov     cx, [eax].TCBPriority
        cmp     cx, dx
        je      SHORT $DCR1315_110
        jbe     SHORT $DCR1315_70
        mov     [_ptcbPriQReady], eax
        jmp     SHORT $DCR1315_120

$DCR1315_70:
        mov     edx, [ebx].TCBpTCBPriHigher
        cmp     [edx].TCBPriority, cx
        jae     SHORT $DCR1315_90

$DCR1315_80:
        cmp     [ebx].TCBPriority, cx
        jbe     SHORT $DCR1315_100
        mov     ebx, [ebx].TCBpTCBPriLower
        jmp     SHORT $DCR1315_80

$DCR1315_90:
        jne     SHORT $DCR1315_100
        mov     ebx, edx

$DCR1315_100:
        cmp     [ebx].TCBPriority, cx
        jne     SHORT $DCR1315_120

$DCR1315_110:

;       This is the case when we are pre-empting a thread and we want to put it
;       at the top of the list in its associated priority queue

;        mov     [eax].TCBpTCBPriPrev, ebx       ;  AA04679
;        mov     ecx, [ebx].TCBpTCBPriNext       ;
;        mov     [eax].TCBpTCBPriNext, ecx       ;
;        mov     [ecx].TCBpTCBPriPrev, eax       ;
;        mov     [ebx].TCBpTCBPriNext, eax       ;
;        jmp     SHORT $DCR1315_130

$DCR1315_120:
        mov     [eax].TCBpTCBPriLower, ebx
        mov     ecx, [ebx].TCBpTCBPriHigher
        mov     [eax].TCBpTCBPriHigher, ecx
        mov     [ecx].TCBpTCBPriLower, eax
        mov     [ebx].TCBpTCBPriHigher, eax

$DCR1315_130:
        pop     ebx

;
;       pick the top most TCB from ptcbPriQReady, and mark
;       it to STATE_RUNNING.  We know it must be a time critical.
;       Dispatch it as quickly as possible.
;
;       NOTE : EAX is now holding the new thread TCB to run
;
;
$DCR1315_140:
        mov     eax, [_ptcbPriQReady]   ; get highest priority in ready queue
        or      [eax].TCBpteKStackTSD, 0
        jz      NTDispatch              ; reload stack, go the long way
        mov     [runner], eax
        mov     [eax].TCBState, STATE_RUNNING
        mov     [eax].TCBQState, STATE_RUNNING
        mov     [_ptcbPriQRunner], eax
        mov     [_TCResched], 0
        mov     [_Resched], 0

;
;       Remove the top most time critical TCB from ptcbPriQReady and update
;       all TCB Queue pointer
;
        cmp     [eax].TCBpTCBPriNext, eax
        jne     SHORT $DCR1315_170
        cmp     [eax].TCBpTCBPriLower, eax
        jne     SHORT $DCR1315_150
        mov     dword ptr [_ptcbPriQReady], 0
        jmp     sch50
;
;       There is more than one TCB in ptcbPriQReady, must reconnect
;       the chain correctly.  See TKPriQRemove() for logic
;
;       eax = the new TCB thread to run
;
;       if (ptcb == ptcb->TCBpTCBPriNext )
;       {
;          /* If there is only one priority level, there must only be
;           * one thread in the queue.  Just set the head to NULL.
;           * All the other pointers are OK at this point.
;           */
;          if (ptcb == ptcb->TCBpTCBPriLower)
;             *pptcb = NULL;
;          else {
;             /* There is more than one level in the queue.
;              * If this TCB is at the top of the queue, the next
;              * thread is at the next lower priority level.
;              */
;             if (*pptcb == ptcb) {
;                 *pptcb = ptcb->TCBpTCBPriLower;
;             }
;             /* There is more than one level.  Remove the current
;              * level, and clean up TCB garbage.
;              */
;             ptcb->TCBpTCBPriHigher->TCBpTCBPriLower = ptcb->TCBpTCBPriLower;
;             ptcb->TCBpTCBPriLower->TCBpTCBPriHigher = ptcb->TCBpTCBPriHigher;
;             ptcb->TCBpTCBPriLower = ptcb->TCBpTCBPriHigher = ptcb;
;          }
;       }
;       else
;       {
;          /* There is more than one thread at the current priority
;           * level.  If this thread is the top of the queue,
;           * the next thread is at TCBpTCBPriNext.
;           */
;          if (*pptcb == ptcb)
;             *pptcb = ptcb->TCBpTCBPriNext;
;
;          /* If this thread is the first thread in the given level,
;           * Correct the adjacent Higher and Lower pointers to
;           * point to the next thread in the current level.  Also,
;           * point the next thread in the current level to the new
;           * higher and lower positions.
;           */
;          if (ptcb->TCBpTCBPriLower != ptcb) {
;             ptcb->TCBpTCBPriLower->TCBpTCBPriHigher =
;             ptcb->TCBpTCBPriHigher->TCBpTCBPriLower =
;                 ptcb->TCBpTCBPriNext;
;             ptcb->TCBpTCBPriNext->TCBpTCBPriLower = ptcb->TCBpTCBPriLower;
;             ptcb->TCBpTCBPriNext->TCBpTCBPriHigher = ptcb->TCBpTCBPriHigher;
;             ptcb->TCBpTCBPriLower = ptcb->TCBpTCBPriHigher = ptcb;
;          }
;
;          /* Remove the tcb from the current level.
;           */
;          ptcb->TCBpTCBPriNext->TCBpTCBPriPrev = ptcb->TCBpTCBPriPrev;
;          ptcb->TCBpTCBPriPrev->TCBpTCBPriNext = ptcb->TCBpTCBPriNext;
;          ptcb->TCBpTCBPriNext = ptcb->TCBpTCBPriPrev = ptcb;
;       }

$DCR1315_150:
        mov     ecx, [_ptcbPriQReady]
        cmp     eax, ecx
        jne     SHORT $DCR1315_160
        mov     ecx, [eax].TCBpTCBPriLower
        mov     [_ptcbPriQReady], ecx

$DCR1315_160:
        mov     ecx, [eax].TCBpTCBPriHigher
        mov     edx, [eax].TCBpTCBPriLower
        mov     [ecx].TCBpTCBPriLower, edx
        mov     [edx].TCBpTCBPriHigher, ecx
        mov     [eax].TCBpTCBPriHigher, eax
        mov     [eax].TCBpTCBPriLower, eax
        jmp     sch50                   ; only one in the list

$DCR1315_170:
        mov     ecx, [_ptcbPriQReady]   ; remove top most TCB from ready queue
        cmp     eax, ecx
        jne     SHORT $DCR1315_180
        mov     ecx, [eax].TCBpTCBPriNext
        mov     [_ptcbPriQReady], ecx

$DCR1315_180:
        cmp     [eax].TCBpTCBPriLower, eax
        je      SHORT $DCR1315_190
        push    ebx
        mov     ecx, [eax].TCBpTCBPriLower
        mov     edx, [eax].TCBpTCBPriHigher
        mov     ebx, [eax].TCBpTCBPriNext
        mov     [edx].TCBpTCBPriLower, ebx
        mov     [ecx].TCBpTCBPriHigher, ebx
        mov     [ebx].TCBpTCBPriLower, ecx
        mov     [ebx].TCBpTCBPriHigher, edx
        mov     [eax].TCBpTCBPriHigher, eax
        mov     [eax].TCBpTCBPriLower, eax
        pop     ebx

$DCR1315_190:
        mov     ecx, [eax].TCBpTCBPriNext
        mov     edx, [eax].TCBpTCBPriPrev
        mov     [ecx].TCBpTCBPriPrev, edx
        mov     [edx].TCBpTCBPriNext, ecx
        mov     [eax].TCBpTCBPriPrev, eax
        mov     [eax].TCBpTCBPriNext, eax
        jmp     sch50                           ; finish with the TC dispatch

;
;       Normal Thread dispatch
;       This dispatch code path is for all thread except Time Critical
;       where dipatch latency is not an important
;

Entry   NTDispatch                      ; for debugger timing
        CCALL32 _SCHGetNextRunner
        cli                             ; block for dispatch P730165
        or      eax,eax                 ; Any thread to run?
        IFDEF   MMIOPH
        jnz     sch40                   ; jump if found someone ready
        ELSE
        jnz     sch40                   ; jump if found someone ready
        ENDIF
sch18:
        ;; Moved this hook two statements up                    ; D159047
        ;; Kernel Idleness Hook: go-to idle                     ; D159047
        HookKI_goidle                                           ; D159047


ifdef PERFVIEW
        PvwEndTm cpu,NotIdle,0          ; PerfView hook
endif
        MMPHPreIdleTask                 ; Performance hook
        mov     [fIdle],-1


sch20:  sti                             ; wait for a thread to go to run state.
        CCALL32 _TKIdleLoop             ; Do Idle loop processing

;       Check for ^C, ^Break pending. We do this here because a process
;       with all threads blocked (e.g. one that will not hit Exit or Enter
;       Kmode) might be awakened by a signal. The signal must actually be
;       dispatched via a context hook because posting a signal can touch a
;       paged-out TIB and swapping is not allowed at this point.

        test    [PendingSignals],SIGINTRF+SIGBREAKF ; Any pending?
        jz      short sch23             ; No. Continue

        bts     [PendingSignals],SIGGCHARMED-1  ; Say we're arming hook
        jc      short sch23             ; Already been armed

        mov     eax,[_pSigGCH]
        CCALL32 KMARMCONTEXTHOOK,<eax, -1>,,pascal

sch23:  test    [_flGlobalActions],KM_FF_GCTXH
        jnz     sch30

sch25:
        cli
        cmp     [_ptcbPriQReady],0            ; Any tasks to run?    AA04693
        jnz     short sch27             ;  no, loop some more  P732214

;       At this point there are no tasks to run.  If an Advanced Power
;       Management device driver has registered it's IdleHookRtn, then
;       we should call it to halt the processor (or step down the clock)
;       rather than issuing a HLT instruction here.  Leave interrupts
;       disabled when calling the APM DD's IdleHookRtn.

        cmp     APM_IdleHookRtn.Segmt32,0; APM DD registered Idle routine?
        jz      short sch26a             ; no, go do HLT ourselves
        or      fAPMidle, 1              ; yes, set idle flag to TRUE
        mov     ax, 0                    ; pass "set idle" parameter
        call  FWORD PTR APM_IdleHookRtn  ; APM DD to halt or step down processor
        sti
        jmp     short sch26b             ; then continue

sch26a:
        sti
Entry   HaltInst                        ; 58287
        hlt                             ; Save the Batteries! Faster Int Respon
sch26b:
        cmp     [_ptcbPriQReady],0            ; Any tasks to run?    AA04693
        jz      short sch20             ;  no, loop some more  P732214

;       End if Power Management

sch27:
        sti
        mov     [fIdle],0

        MMPHPostIdleTask                ; Performance hook
ifdef PERFVIEW
        PvwStartTm cpu,NotIdle,0        ; PerfView hook
endif


        ;; Moved this hook from just below dispatch hook        ; D159047
        ;; Kernel Idleness Hook: go-to busy                     ; D159047
        HookKI_gobusy                                           ; D159047

        jmp     sch15

sch30:  CCALL32 _TKWakeup, <offset FLAT:_kmCTXHDaemon, 0, NULL>
        jmp     sch25                   ;  P732214

;*      Switch context from the current task to a new one.
;
;       It is assumed that the task registers have been
;       saved. If the new task is the same as the old one, no
;       switching is done.
;
;       (AX) = new task number to run

sch40:  cmp     eax,[_pTCBCur]          ; Switch to current thread?
        je      sch95                   ; same, just leave

;aa05304PvwCnt  cpu,Sched,0             ; PerfView hook

sch50:  smsw    ss:[TKOPTDA].MSW        ; save MSW in PTDA of current task
        sidt    fword ptr ss:[TKOPTDA].ptda_lidt ; save the idt limit/base

        btr     [eax].TCBSchFlags,SCH_PAGE_FAULT_BIT ; clear I/O type page fault
                                        ; count  P728371

;       (EAX) = Ascending TCB pointer

        SaveReg <ebx,edi,esi,ebp>       ; Save context
        ; Check TLMA
        cmp     ss:[TKOPTDA].ptda_TLMA,0
        je      cswitch                 ; if not in use don't bother
        xchg    ebx, ecx                ; save ecx
        cld                             ; must set direction
        mov     esi,[_Lis2].is_va       ; infoseg
        add     esi,LIS_LEN
        mov     edi,_pTCBCur
        lea     edi,[edi].TCBTLMA
        mov     ecx,ss:[TKOPTDA].ptda_TLMA
        rep     movsd
        xchg    ebx, ecx                ; save ecx
cswitch:

        CCALL32 _PGSwitchContext,<eax>  ; Switch context
        RestoreReg <ebp,esi,edi,ebx>    ; Restore context
        cli                             ; P723812

sch51:; WARNING: tkSchedNextRet Entry Point.
;       (EAX), (ECX), (EDX) are garbage.   Do not cache values across sch51.
;       (EBX), (EDI), (ESI), (EBP) are set up for exit.  Do not modify.
;       (DS) = (ES) = Flat.
;       (FS) = (GS) = Not saved across a context switch.

;       Load the proper TSS selector (if needed)

        str     ax                      ; (AX) = outgoing TSS selector
        movzx   ecx,ss:[TKOPTDA].seltss ; (CX) = incoming TSS selector
        cmp     ax,cx                   ; are they the same?
        je      short sch55             ;  yes, jump around load

        mov     eax,[_SELGDTLaddr]
        and     [eax+ecx].d_access,D_TSSBUSY_CLR; clear busy bit in tss
        ltr     cx                      ; load new tss

sch55:  mov     ecx,[_pTCBCur]
        mov     ax,[ecx].TCBNumber
        mov     [TaskNumber],ax         ; Set new current task #, and get old
        mov     dh,[ecx].TCBPriClass    ; (DH) = priority class
        mov     dl,[ecx].TCBPriLevel    ; (DL) = priority level
        mov     ss:[TKOPTDA].ptda_infoseg.LIS_CurThrdPri,dx
        mov     fs,[_Lis2].is_sel       ; update infoseg copy
        mov     fs:LIS_CurThrdPri,dx

        cmp     ss:[TKOPTDA].ptda_pTCBCritSec,0
        je      short sch56             ; no Critsec in effect - continue

        cmp     ss:[TKOPTDA].ptda_pTCBCritSec,ecx
        je      short sch56             ; Critsec in effect, but not held

        ; Ascending thread is held by a critical section.  Set
        ; TK_FF_CRITSEC to catch the thread in Exit_Kmode.

        or      [ecx].TCBForcedActions,TK_FF_CRITSEC    ; Check CritSec soon.

sch56:  mov     ax,WORD PTR ss:[TKOPTDA].ptda_Pid ; (AX) = Current PID
        mov     CurProcPID,ax           ; Set current PID

ifdef PERFVIEW
        PvwCnt  cpu,Sched,0             ; PerfView hook                ;aa05304
endif
        MMPHPreThreadDispatch           ; Performance Hook

        mov     dx,[ecx].TCBOrdinal     ; (DX) = Current TID
        mov     ss:[TKOPTDA].ptda_infoseg.LIS_CurThrdID,dx
        mov     fs,[_Lis2].is_sel       ; update infoseg copy
        mov     fs:LIS_CurThrdID,dx

;       (AX) = Current PID, for postSchedNext
;       (DX) = Current TID, for postSchedNext

;***DT  postSchedNext - SchedNext post-invocation internal trace point
;
;    TRACE MINOR = <RAS_POST_INT_SCHEDNEXT>,
;          TP    = .postSchedNext,
;          TYPE  = (POST,INT),
;          GROUP = TK,
;          DESC  = "(OS) Thread Reschedule Post-Invocation",
;          REGS  = (AX,DX),
;          FMT   = "Switched to PID = <FMT_WORD>  TID = <FMT_WORD>"

Entry   postSchedNext,Near              ; Internal DynTrc Tracepoint

;       Edit IDT gate for Int 21 emulation.  Access field is left unchanged,
;       address field is set to either 0 or user-supplied 16:16 address

        mov     eax,ss:[TKOPTDA].Exc_vec + EXCDOSI*4
        mov     dword ptr [Int21Vec],eax

;       Edit IDT gate for NPX not present exceptions.
;       Always set the TS bit for protected mode processes, but only
;       if a NPX is present for VDM processes.

        lidt    fword ptr ss:[TKOPTDA].ptda_lidt ; get the idt limit/base
        mov     ax,ss:[TKOPTDA].MSW     ; (AX) = MSW of new task
        or      ax,CR0_TS               ; set TS bit
        cmp     byte ptr ss:[TKOPTDA].ptda_ProcType,PT_REALMODE
        je      sch115                  ; jump if a real mode process

;       Protect mode process

        test    ax,CR0_EM               ; emulating NPX?
        jz      short sch87             ; jump if not emulating
        cmp     [mathCop],0             ; if chip exists
        jnz     sch87

;       Route npx not present faults to system emulator.  (IDT Edit)

        mov     dx,ss:[TKOPTDA].ptda_NPX_em_cs  ; set selector          ;b726833
        mov     [NPXNAVec].g_sel,dx                                     ;b726833
        mov     [NPXNAVec].g_access,D_TRAP032; set access byte
        mov     edx,ss:[TKOPTDA].ptda_NPX_em_eip                        ;b726833
        mov     [NPXNAVec].g_lowoffset,dx       ; set low word of offset
        shr     edx,16                          ; (AX) = high word of EAX
        mov     [NPXNAVec].g_extoffset,dx       ; set high word of offset
        jmp     sch90
sch87:
        mov     [NPXNAVec].g_sel,seg FLAT:DOSHIGH32CODE ; set sel. to flat CS
        mov     [NPXNAVec].g_access,D_TRAP032   ; set access byte
        mov     edx,offset FLAT:trap07
        mov     [NPXNAVec].g_lowoffset,dx       ; set low word of offset
        shr     edx,16                          ; (DX) = high word of EDX
        mov     [NPXNAVec].g_extoffset,dx       ; set high word of offset
sch90:
        lmsw    ax                      ; load MSW
        ; Restore TLMA

        mov     eax,_pPTDACur           ; (eax) = pointer to current PTDA
        cmp     [eax].ptda_TLMA,0       ; TLMA in use?
        jz      short tlma_ng           ; No, don't play with TLMA
        SaveReg <ecx, esi, edi>
        cld                             ; must set direction
        mov     ecx,[eax].ptda_TLMA     ; dword count
        mov     edi,[_Lis2].is_va       ; update infoseg copy
        add     edi,LIS_LEN
        mov     esi,_pTCBCur
        lea     esi,[esi].TCBTLMA
        rep     movsd
no_tlma:
        RestoreReg <edi, esi, ecx>
tlma_ng:
        mov     eax,ERROR_BLOCKED       ; context switch occurred
        mov     _PGFaultCnt,0           ; zero page fault cnt since last disp DCR1203
        LeaveProc

Entry   SchedNextRet,Near               ; For the debugger.
        sti                             ; B734022
        RETC

sch95:  xor     eax,eax                 ; no context switch
        mov     _PGFaultCnt,0           ; zero page fault cnt since last disp DCR1203
        LeaveProc NoCheck

Entry   SchedNextRet2,Near              ; For the debugger.
        sti                             ; B734022
        RETC

;       this section of code is place here for code optimization
;       According to DCR 1315, code optimization is done by assuming
;       the most frequent path, utilization of 80386 prefetch instruction
;       so below is the least executed code path.

sch100:
        CCALL32 WaitNPX                 ; wait for the NPX to catch up
        jmp     sch15

;       Scheduler has previously called the APM device driver, which has
;       called the APM BIOS to either issue the halt instruction or step
;       down the clock speed of the processor.  The scheduler now knows
;       it has work to do, so it must call the APM device driver so it
;       can call the APM BIOS to step up the clock speed of the processor.
;       Interrupts should be disabled when calling the APM DD's IdleHookRtn.

sch101:
        cmp     APM_IdleHookRtn.Segmt32,0 ; APM DD registered Idle routine?
        jz      short sch102            ; no, cannot step up processor
        mov     ax, 1                   ; pass "set busy" parameter
        call    FWORD PTR APM_IdleHookRtn ; step up processor clock
        and     fAPMidle, 0             ; set idle flag to FALSE
sch102:
        jmp     sch16

sch105:
        mov     eax, [_ptcbPriQDelayed]
        push    ebx                     ; P723994
        mov     ebx,[_ptcbPriQReady]    ; P723209
        mov     bx,[ebx].TCBPriority    ; P723209
        cmp     [eax].TCBPriority,bx    ; P723209
        pop     ebx                     ; P723994
        jbe      $DCR1315_10            ; AZ10473
        CCALL32 _TKUpdateState, <_ptcbPriQDelayed> ; only retrieve one - highest
        jmp     HPTDispatch             ; P723209

;       V86 mode process

sch115: test    ax,CR0_MP               ; math process present?
        jnz     sch87                   ;  yes, leave TS bit set
        and     ax,not CR0_TS           ;  no, clear TS bit
        jmp     sch87

EndProc _tkSchedNext

HIGH32CODE      ENDS                    ; DCR 1315 END


        FARCODE INIT
INITCODE segment

;***    ProcInit - Initialize Scheduler
;
;       ProcInit  is  called  to make thread 1 (SysInit) the currently
;       running  thread  and to do any initialization of the resizable
;       thread  tables  that  is necessary (e.g., setting the links in
;       the  timeout  delta  queue) and to enable Scheduler clock tick
;       processing.
;
;       ENTRY   NONE
;
;       EXIT    NONE
;               INTERRUPTS ENABLED
;
;       USES    AX, BX, CX, DS, ES, Flags

Procedure ProcInit,near
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

        FlatContext <ds,es>,ax
        mov     eax,[_pPTDACur]
        mov     [eax].seltss,GSEL GDT_TSS

;       Put an LDT sel value here so tkSetTIB will not complain
        mov     [eax].ptda_infoseg.LIS_AX,WORDMASK      ; 0xffff

        sidt    fword ptr [eax].ptda_lidt               ; @xxxx

;       Initialize MVDM timer support before we start getting ticks.

        CALLF32 VDMMINITTIMER
        MOVFAROFFSET SchedPtr.Offst,SchedClock,DOSCODE
        sti                             ; ** Scheduler now hooked in
        ret                             ; All done

EndProc ProcInit


;**     ProcDataResize -- Resize the LASTDATA segment
;
;       ProcDataResize will compress data structures which are allocated
;       in the LASTDATA segment and release any leftover space.  This
;       procedure is called after the THREADS= sysinit parameter has been
;       parsed.
;
;       All entries in LASTDATA must be of the form:
;
;               DW      Pointer to array pointer
;               DB      Size of array element
;               DB      255*(element size) DUP(value)
;
;       ENTRY   INTERRUPTS DISABLED
;       EXIT    INTERRUPTS DISABLED
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, Flags

Procedure ProcDataResize,near
        ASSUME  DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:NOTHING

LocalVar pFlatBase,DWORD

        EnterProc

        DosContext <ds,es>,ax           ; Init. segment registers
        SaveReg <ebx,fs>
        FlatContext <fs>,ax

        mov     si,offset _StartLASTDATA; (DS:SI) = 1st entry

; pd ABIOS_present flag not set up at this point on all hardware.
; pd The ABIOS Data/Code to be freed if not needed in LDRINITA.ASM.
; pd    test    ABIOS_present,-1        ; abios on machine?
; pd    jnz     short pdr10
; pd
; pd    mov     di,offset _StartABIOSDATA; overwrite abios only data
; pd    jmp     short pdr20

pdr10:  mov     di,si                   ; (ES:DI) = target address
pdr20:  cmp     si,offset _EndLASTDATA  ; End of segment reached?
        jae     pdr30                   ;  yes, exit loop
        lodsw                           ; (DS:AX) = pointer address
        mov     bx,ax                   ; (DS:BX) = pointer address
        lodsd                           ; (FS:EAX) = flat pointer address
        mov     pFlatBase,eax           ; Save flat pointer address
        lodsb                           ; (AL) = array element size
        xor     ah,ah                   ; (AX) = array element size
        mov     dx,ax                   ; (DX) = array element size
        mov     cx,4096
        SaveReg <ax>
        xor     dx,dx
        mul     cx                      ; (DX) = array element size * 4096
        mov     dx,ax
        RestoreReg <ax>
;       sub     dx,ax                   ; (DX) = old array size
        push    dx
        xor     dx,dx
        mul     MaxThreads              ; (AX) = new array size in bytes
        pop     dx
        sub     dx,ax                   ; (DX) = old size - new size
        mov     cx,ax                   ; (CX) = array size in bytes
;       inc     di                      ; Even-align base
;       and     di,NOT 1

        push    edi                     ; Save base
        sub     di,[bx]                 ; (DI) = new base - old base
        add     [bx],di                 ; Set new base
        movsx   edi,di                  ; (EDI) = new base - old base
        mov     ebx,pFlatBase           ; (FS:EBX) = flat pointer address
        add     fs:[ebx],edi            ; Set new flat base
        pop     edi                     ; (DI) = base

        rep movsb                       ; Copy the array
        add     si,dx                   ; Skip to end of old array
        jmp     short pdr20             ; Loop

pdr30:  RestoreReg <fs,ebx>
        ASSUME  FS:NOTHING

;       Return the excess data in the LastData segment to the system arena
;
;       (DI) = new size of DOSGROUP

        mov     bx,di                   ; (bx) = new size of DOSGROUP
        xor     ax,ax
        mov     di,ds

        CALLFAR SegRealloc              ; SegRealloc(ax:bx=size,di=sel)
        jc      pdrie

        CALLFAR GetDescInfo

        test    si,D_EXPDN
        jnz     pdrie

;
; The following calculation are only correct for expand up selectors
;
        shl     eax,16
        mov     ax,bx                   ; convert AX:BX to EAX
        shl     ecx,16
        mov     cx,dx                   ; convert CX:DX to ECX

; calculate linear address and size of partial page to donate to kernel heap
;
; size =  round_up_to_page_boundary(base of selector + limit of selector)
;   - base of selector + limit of selector
;
; address = base of selector + size


        add     eax,ecx                 ; eax = base + size
        dec     eax                     ; eax = base + limit

        push    eax
        add     eax,PAGESIZE - 1
        and     eax,NOT PAGEMASK        ; eax = (base + limit) rounded up

        pop     ebx                     ; ebx = base + limit
        sub     eax,ebx                 ; eax = size
        jz      prd40                   ; no partial page to donate

        inc     ebx                     ; ebx = address

        CCALL32 _VMDonateKHB <ebx, eax, PG_RESIDENT or PG_W>

        or      eax,eax
        jnz     pdrie

prd40:  LeaveProc
        ret

pdrie:  Panic <"ProcDataResize">
EndProc ProcDataResize

INITCODE ends

        end
