        PAGE    ,132

__TONYI__ = 1           ; Space/speed hacks  D1198

ifdef __TONYI__
if1
        %out    __TONYI__ hacks present and active
endif
endif

;*******************************************************************************
;*  POWER.SYS - MSDOS 5.0 IDLE DETECTION DEVICE DRIVER
;*
;*  IBM Confidential
;*  Copyright (C) IBM Corporation 1991-1993
;*  All Rights Reserved.
;*
;*  Revision History
;*  90/7/19  [BR]       Completed beta DRIVER V0.30
;*  04/29/91 SR         Modified to make it ROMmable. Separated CODE,
;*                      DATA & INIT segments.
;*  06/03/91 MD         Merged standalone and resident versions,
;*                      support idle checking on INT 2F Idle
;*  07/01/91 JRCB       Changed STANDALONE to POWERALONE to remove confusion
;*                      with ROMDRIVE variable of same name.
;*  08/12/91  NSM       added APM support (M001)
;*
;*  08/21/91  NSM       M002: Removed the comment marks in Int14 hook
;                       (bug #    : printing slowed when POWER is present)
;                       Added a call to CPU_BUSY after resume
;
;   08/29/91  NSM       M003: removed rest of IOCTL support
;
;   09/05/91  NSM       M004: Bug# 2587
;                       Moved CheckV86 calls only for DOING HLT and not
;                       for OEM_IDLE or for APM_IDLE (2587)
;                       Also fixed the problem with multiple resumes.
;                       If we get a resume without a initial suspend notifi.
;                       then we ignore this resume command now.
;                       Bug#2574 Count CPU_IDLE time at Int 1c
;
;   09/08/91  DBO       M005: Straighten out far calls in BIOS-resident
;                       driver:
;                        Do_APM_Enable_Disable  -- make near
;                        Check_and_Init_APM     -- list in seg_reinit
;                        time_to_ticks          -- reference via ttticks
;                                                  (already in seg_reinit)
;                        Do_APM_Connect         -- make near
;
;   09/11/91  NSM/DBO   M074: Replace clock driver functions, so we can manage
;                       manage system date & time updates after resume events.
;
;   09/11/91  SMR       M075: B#2670. Get Idle Algorithm was trashing IDLE_FLG
;
;   09/11/91  SMR       M076: B#2668. Statistics were not being copied if the
;                       user buffer is bigger than the required size.
;
;   09/11/91  SMR       M077: B#2669. Registered POWER's 2f channels in mult.inc
;
;   09/11/91  SMR       M078: Use HLT even if the processor is in V86 mode
;
;   09/11/91  SMR       M079: PWR_API returns 0 in AX instead of no carry flag
;                               in case of no error
;
;   09/11/91  DBO       M080: Power management clock hooks int 6C for
;                       resume events.
;
;   09/12/91  SMR       Call P_UpdFromCMOS directly if power is standalone
;
;   09/12/91  SMR       Load ES:DI before dispatching a CLOCK$ call.
;
;   09/17/91  NSM       M084: Don't zero out AX for version check int 2f call
;
;   09/19/91  SMR       M085: Return break address from clock driver init also
;
;   09/19/91  SMR       M086: Save/Restore SI in get/Set statistics calls
;
;   09/23/91  NSM       M087: Change signature to 504DH
;
;   09/25/91  NSM       M088: (B# 2730) Hang in GET_APM_STAT - missing "push si"
;
;   09/25/91  NSM       M089: I2F service routine changes related to UI change.
;                       (definition of POWER STD needed some extra code)
;
;   09/25/91  NSM       M090: CLOCK driver related changes to take care of
;                       WIN 3 ENH mode eating up I1Cs.  Soln. is to update our
;                       time from CMOS once in 1024 I1c ticks. Do this only
;                       when win enh mode starts up and discontinue once win
;                       ends. (Bug# 2729)
;
;   09/25/91  NSM       M091: Also update our time from CMOS when POWER STD
;                       is selected on an APM machine.
;
;   10/25/91  NSM       M093: CX reg is getting trashed while making suspend
;                       standby APM call in Int1C.
;                       While changing APM_MAX_POLLCOUNT, we should update
;                       the current APMPoll Counter also (APM_POLL_COUNT).
;
;   10/29/91  NSM       M094: AX and BL regs were getting trashed in some of
;                       the INT 2f service routines.
;
;   11/05/91  NSM       M096: Eat the rollover flag in Int 1C before command
;                       gets it.
;
;   11/19/91  SMR       M098: Load power into UMB automatically
;
;   11/26/91 SMR        M099: Remember PSP of 2f instead of maintaining a
;                       flag in the app's PSP
;
;   11/26/91 SMR        M100: Instance the PSPOf2fApp variable
;
;   11/27/91 SMR        M102: Reverse the order of instancing
;                       Do definite idle on INT 2a ah=84
;
;   12/03/91 NSM        M103: Check for POWER OFF before doing IDLEs.
;
;   12/03/91 SMR        M104: Do APM Command line settings only in the
;                               second copy of POWER if moved into UMB
;
;   12/04/91 SMR        M105: Fake out arena header & sub-arena header
;                               so that MEM.EXE will see POWER properly.
;
;   12/06/91 NSM        M106: Change the averaging algorithm while adapting
;                        Instead of using a running avg, collect 16 samples
;                        at a time, find avg on them, and use that new avg
;                        for the next 16 samples  and so on till end of adapt.
;
;   12/06/91 SMR        M107: Issue int 1A to clear BIOS rollover flag
;
;   12/10/91 NSM        M108: Put APM related I2F service code within IFDEF
;                       INCL_APM directives
;
;   03/21/94 kam001  -  Added APM11_FLAG to indicate presence of APM 1.1 BIOS
;                       In DEV_INTERRUPT routine the Banner routine is called
;                       after verifying DOS version and APM version.
;                    -  Included the APM Driver Version call in
;                       Check_and_Init_APM to verify the APM 1.0/APM 1.1
;                       modal BIOS behavior
;   03/26/94 kam002  -  Added DoAPMPoll11 routine for APM 1.1 BIOS
;                       utilization, implementation of two phase protocol
;                    -  Added save_event_flag, battery related data variables.
;   04/04/94 kam003  -  Added PS_GetSet_DevPWState, PS_EnableDisable_DevPWState
;                       and PS_EngageDisengage_PM to Pwr_Services which is
;                       the INT 2F ISR routine for POWER services that
;                       implements POWER.EXE 1.01 API. For the definition of
;                       these APIs refer to the APM I0/I1, Version 0.1 document
;   04/04/94 kam003  -  Added GetDevPowStatus routine to get the current
;                       device power status
;                    -  Added Do_DevPWState, Do_EnaDisDevPWState and
;                       Do_EngDisDevPWState routines that utilizes Get
;                       Power Status, Enable/Disable device Power Management,
;                       APM Driver_Version and Engage/Disengage Power
;                       Management, INT 15h calls defined in APM BIOS 1.1
;                       Specification, September 1993
;  NOTE: Tags kam004, kam005 and kam006 have been added from the Code
;        Inspection (I2) held on 04/08/94.
;  04/11/94  kam004  -  Removed APM11_FLAG. One of the APM 1.01 I2 Inspection
;                       issue to use APM_VER flag.
;                    -  The banner message will display Version 1.01 since
;                       POWER.EXE is upgraded to utilize the APM 1.1 BIOS
;                       features and provides 1.01 API support.
;  04/12/94  kam005  -  Changed the Pwr_Services FUNCTION 0 - POWER install
;                       check call (PS_Install_Chk) to return the APM version
;                       number for Detect POWER.EXE INT 2FH call (AX=5400)
;  04/12/94  kam005  -  Changed the Pwr_Services FUNCTION 4 -
;                       Get/Set Device POWER state
;                       - verifying the device ID is done prior to calling
;                         the GetDevPowStatus routine.
;                       - the "test bl,DEVICEID_BITS" instruction is changed
;                         to "test bh,DEVICEID_BITS" since bh contains the
;                         device ID.
;                       - the flow on a Get Device POWER state is corrected
;                         to call the GetDevPowStatus routine only once.
; 04/12/94  kam005   -  The Do_DevPWState, Do_EnaDisDevPWState and
;                       Do_EngDisDevPWState routines are replaced with a
;                       single routine Do_DevCommand routine.
; 04/14/94  kam006   -  The implementation of two phase protocol with
;                       APM 1.1 BIOS connection is done as follows
;                       -  Notify apps of a Resume event if any app has
;                          objected to a System Standby, User System Standby,
;                          System Suspend or User System Suspend request.
;                       -  While processing the System Standby, User System
;                          Standby, System Suspend or User System Suspend
;                          request during the first broadcast POWER.EXE
;                          indicates the APM 1.1 BIOS via a Set Power state
;                          call with Request Processing every 5 seconds.
;  04/18/94 kam007    -    Bug in HummingBird Enhanced, APM Driver Version
;                          INT 15h (AX=530E) reboots the system. Only issue
;                          this call if running on APM 1.1 BIOS.
; 04/19/94  kam008    -    Changed PS_GetSet_DevPWState into PS_Get_DevPWState
; 04/27/94                 and PS_Set_DevPWState to appropriately map the
;                          INT 2Fh (AX=5404 and AX=5405) functions to the
;                          APM 1.1 BIOS functions.
;                     -    Removed battery related data variables since it
;                          is already defined in LPC.ASM file.
;                     -    Defined two additional error codes of
;                          ERROR_PM_INVALID_DEVICE_ID (06h) for invalid
;                          device ID and ERROR_PM_DEVIDNOTSET (07h) for
;                          device ID not set if a Get Device Power State was
;                          issued without a Set Device Power State
; 05/02/94  kam009    -    Added a new event code APM_SUSPEND_OK for two phase
;                          protocol. This gives the application the oppurtunity
;                          to go in suspend mode on the second broadcast if
;                          no apps rejected it without actually saving the
;                          state on the first broadcast.
; 05/09/94  kam010    -    DEFECT 7554 - Code reduction logic for POWER.EXE
; 05/16/94                 on non APM & APM 1.0 system
;                          Code reduction logic added. Rearranged the code.
;                          Additional code is located in the beginning of the
;                          SysInitSeg. This way it keeps the segment ordering
;                          intact and includes the addtional code whether
;                          POWERALONE is defined or not. The reason for
;                          relocating code is if running on APM 1.0 BIOS or
;                          non APM systems then don't really need the extra
;                          luggage of new defined INT 2Fh APIs and processing
;                          of APM 1.1 BIOS PM events since these APIs cannot
;                          be used neither the PM events will be received so..
;                          relocate it in SysInitSeg and initialize the end
;                          address if no connection is established with the
;                          APM 1.1 BIOS.
;                          Also chop the INT 2Fh API processing table at the
;                          location where new INT 2Fh APIs (function 5404,
;                          5405, 5406 and 5407) are defined if no connection
;                          is established with the APM 1.1 BIOS.
; 05/18/94  kam011         DEFECT 7489 - Invalid Parm Not Detected
;           kam012         DEFECT 7482 - Set Power - Processing Omitted
;           kam013         DEFECT 7486 - Invalid Set Power Rcd at Int 15
;           kam014         DEFECT 7488 - Invalid Device ID Not Detected
;                          The following device IDs are considered as valid
;                          device ID.
;                          - 06xx or less
;                          The following OEM device IDs are considered as
;                          valid device ID.
;                          - E0 or greater
;           kam015         DEFECT 7541 - Attempting new APIs on non APM &
;                          APM 1.0 H/W do not set C flag
; 05/20/94  kam016         DEFECT 7586 - OEM defined device IDs (E000h-EFFFh)
;                          not supported (comments from Japan)
; 06/09/94  kam017         DEFECT 8036 - INT 2Fh fn 5405, 5406 and 5407 do not
;                          recognize OEM device ID.
; 06/10/94  kam018         DEFECT 8084 - INT 2Fh API not returning correct
;                          error code.
;                          The error code from the INT 15h call is returned
;                          in AH, however the comparison was done with AX
;                          which resulted in POWER.EXE returning wrong error
;                          code. This also fixes the following defects.
;                          DEFECT 7490 - Disable while Disengaged not Handled
;                          properly.
;                          DEFECT 7491 - Disable while Disabled not Handled
;                          properly.
; 06/10/94  kam019         DEFECT 8108 - Set Power State (Last Request
;                          Processing) not called correctly.
;                          DEFECT 7482 - Set Power - Processing Omitted
;                          DEFECT 7486 - Invalid Power Rcd at Int 15
; 06/22/94  kam020         DEFECT 8319 - Disable while Disengaged not Handled
;                          properly.
; 06/22/94  kam021         DEFECT 8320 - Disable while Disabled not Handled
;                          properly.
; 07/13/94  kam022         DEFECT 8820 - Carry flag not cleared with succ op
;                          following unsucc op
; 07/13/94  kam023         DEFECT 8821 - Set Power - Processing Omitted
; 07/21/94  kam024         DEFECT 8947 - S.Kawano san comments on Power.EXE
; 07/26/94  kam025         DEFECT 9085 - Suspend not working correctly
;                                        (more comments from Japan)
;                          Removed APM_SUSPEND_OK event and defined a new
;                          INT 2Fh API (530C) - Get PM Event First Phase Broadcast
; 08/09/94  kam026         DEFECT 9291 - Resume not working after two phase
;                                        protocol
; 09/02/94  kam027         DEFECT 10185 - Version Number returned is incorrect.
; 09/07/94  kam028         DEFECT 10281 - Detect POWER.EXE function call
;                          returning incorrect version no.
; 10/10/94  kam029         DEFECT 10462 - Reboot/Hang w/POWER.EXE Load Fail
; 10/10/94  kam030         DEFECT 10769 - POWER.EXE not displaying Banner
;                                         message
;*******************************************************************************


        TITLE   POWER$ IDLE DETECTION V1.0

        .xlist

        include version.inc     ; M004 set build flags

break   macro                   ; satisfy mult.inc
        endm

        include mult.inc        ; get our int 2f function equates       ; M077
        include win386.inc      ; NSM2
        include devmark.inc

IFDEF   POWER

IFNDEF  POWERALONE              ; segment declarations for resident version

        include biosseg.inc     ; establish bios segment structure

ELSE                            ; segment declarations for standalonde version

.SEQ                            ;

Bios_Code       segment word public 'Bios_Code'
Bios_Code       ends

Bios_Data       segment word public 'Bios_Data'
Bios_Data       ends

SysInitSeg      segment word public 'system_init'
SysInitSeg      ends

Trans_Code      segment word public 'CODE'
                extrn   ParseCmdLine:far
Trans_Code      ends

Trans_Data      segment word public 'DATA'
                extrn   BadDOSMsg:byte
                extrn   PW_Low:byte, PW_Mode:byte, PW_Savings_Value:word ; M104
IFDEF   DEBUG
                extrn   PW_Debug:byte                                    ; M104
ENDIF

Trans_Data      ends

ENDIF


        include msequ.inc
        include devsym.inc
        include bpb.inc
        include ioctl.inc
        include power.inc

IFDEF   POWERALONE                      ; M098
        include umb.inc                 ; M098
        include syscall.inc             ; M098
ENDIF ; POWERALONE                      ; M098


IFDEF INCL_APM
        include apmequ.inc              ; M001
ENDIF

break   macro
        endm

        include error.inc
        .list

        include msgroup.inc     ; define Bios_Data segment

IFDEF   POWERALONE                      ; standalone device driver version
Bios_Res        dw      Bios_Code       ; Our code segment address
ELSE                                    ; resident BIOS version
        extrn   Bios_Res:word           ; Code segment address supplied externally
        extrn   ttticks:dword           ; far ptr to time_to_ticks routine
        extrn   P_UpdFromCMOS_Ptr:dword ; ptr to CMOS clock read        ; M081
IFDEF   INCL_APM
        extrn   Check_and_Init_APM_Ptr:dword    ; ptr to APM init routine
ENDIF   ;INCL_APM
ENDIF   ;NOT POWERALONE
;
; PUBLIC declaration for DATA
;
ifdef __TONYI__
; Don't need MSW anymore.  AX is used on the fly.
public  RHPTR, KYC, I28, CONTROL, INFO                  ; D1198
else
public  RHPTR, KYC, I28, CONTROL, INFO, MSW
endif

public  IDLTIC, TOTTIC, SPDUP, SPDUP_DLY, SPDUP_CNT    ; M004
public  I2F_VEC, I28_TMR0, I28_VEC, KYC_TMR0, IN_KYC
public  I16_VEC, SWITCH_CNT, I9_COUNT, I9_VEC, I10_VEC, I13_VEC
public  I14_VEC, I17_VEC, I21_VEC,I25_VEC,I26_VEC,I2A_VEC, I08_VEC

public  POW_MAJ_VERSION, POW_MIN_VERSION
; PUBLIC declarations for APM related Data
;
IFDEF INCL_APM                                  ; M001
public  fAPM_PRESENT,APM_FLAGS,APM_VER,APM_POLL_COUNT,fAPM_CONNECT
public  APM_MAX_POLLCOUNT,fAPM_STATE,APM_RESUME_COUNT
public  APMBIOS_CMD                             ; kam005
public  SETPOWER_PROC, SETPOWER_POLLCOUNT       ; kam006
public  SETDEV_FLG                              ; kam008

ENDIF

RHPTR   dd 0

; DEBUG Macro
; Prints the argument (dispchar) on screen
;
dbg_printchar   macro dispchar
IFDEF   DEBUG
        push    ax
        mov     ah,0eh
        mov     al,dispchar
        int     10h
        pop     ax
ENDIF
endm

POWER_DATA label byte
;
; Period Info structure for Keyboard (KYC) and Int 28 (I28)
; This holds the values for BASE,NOISE, AVG, SPREAD etc.
; (look at power.inc for PERIOD_INFO structure
;
;                                                                     ;AC000;
; Default values for base and                                         ;AC000;
;                                                                     ;AC000;
KYC  PERIOD_INFO    < 0, 0, 0, 0, 0,  20, 20, 0, 0, 0>                ;AC000;
;                                                                     ;AC000;
;    noise are (random)conservative.                                  ;AC000;
;                                                                     ;AC000;
I28  PERIOD_INFO    < 0, 0, 0, 0, 0, 100, 20, 0, 0, 0>                ;AC000;
;                                                                     ;AC000;
; CONTROL holds constant/tunable parameters for (mainly) int 16/int 28 idle
; detection strategies.
;
CONTROL CONTROL_INFO    < 00011111b, 8*2, 31*1024, 4,  40, 0,  1,   8,    0>
                                                                ; M078
;                       idle_flg, sw_dly,thres,ad_dl,mx,sp_dl,ramp,sp_mx,386flg
;
; INFO holds the idle detection statistics such as no of i16 idles detected etc.
INFO    IDLE_INFO       < 0, 0, 0, 0, 0, 0, 0>

ErrSampleCount  dw      0       ; no of errors encountered so far in adapt cycle

ifndef __TONYI__
; Not needed.............       ; D1198
MSW             dw      0       ; store machine status word
                                ; used by ChkV86 procedure
                                ; bit 0 = 1 indicates protected mode
endif

IDLTIC          dw      0       ; M004 used as a flag that says CPU was idle
                                ; in the last (I08) tick period

TOTTIC          dw      0       ; running total of halt time    ; counted in Int8
                                ; in 1/1.19 MHZ increments 65536=55ms

;
; ACTIVITY MONITOR DATA
;
SPDUP           dw      0       ; Activity indicator; 1=> active =>avoid halting
SPDUP_DLY       dw      0       ; MAX DELAY set by activity monitor
SPDUP_CNT       dw      0       ; incremented via INT 08

;
; M100 - begin
;

; Data to track Int2F aware apps.
; Instance data structure for instancing PSPOf2fApp
;
Pwr_InstData    label   byte
 Pwr_SIVersion  dw      3       ; ignored
 Pwr_SINextDev  dd      0       ; ptr to next Instance packet
 Pwr_SIVirtDev  dd      0       ; ignored
 Pwr_SIRefData  dd      0       ; ignored
 Pwr_SIInstData dw      offset Bios_Data:Pwr_SITable, Bios_Data
                                ; pointer to instance table

Pwr_SITable     label   byte
Inst_PSPOf2fApp dw      offset Bios_Data:PSPOf2fApp, Bios_Data
                dw      2       ; 2 bytes
Inst_End        dd      0

;
; M100 - end
;

PSPOf2fApp      dw      0       ; PSP of the current 2f-1680 App        ; M099
PSPsegment      dw      (?)     ; JAH3 - Dos DATA seg  used to
                                ; access Current PSP

; Data to store prev. vectors of interrupts we hook
; At startup these hold our ISRs for corresponding ints, and exchanged with
; the previous ISR addresses at Device init time
;
I2F_VEC dw I2F_IDLE, 0          ; holds next pointer in chain

; BUGBUG - Nagara - I2F_TMR0 is not used at all and can be deleted    ;AC000;
;I2F_TMR0        dw   0          ;+0, holds temporary PC timer 0      ;AC000;
;                                ;    values used to compute PERIOD   ;AC000;
;                dw   2          ;+2, Counts number of times PC       ;AC000;
;                                ;    timer has Overflowed            ;AC000;
;BUGBUG END                                                           ;AC000;

I2A_VEC dw I2A_IDLE, 0          ; holds next pointer in chain

; I28_TMR0 is used to store TMR0 readings whenever Int 28 occurs.
I28_TMR0        dw 0         ;+0, holds temporary PC timer 0
                                ;    values used to compute PERIOD
                dw 2            ;+2, Counts number of times PC
                                ;    timer has Overflowed

I28_VEC dw I28_IDLE, 0

KYC_TMR0        dw 0            ;+0, holds temporary PC timer 0
                                ;    values used to compute PERIOD      for Int 16s
                dw 2            ;+2, Counts number of times PC
                                ;    timer has Overflowed


IN_KYC  dw 0                    ; flags re-entry of key board check

I16_VEC dw I16_IDLE, 0          ; stores next key routine in chain

; SWITCH_CNT keeps track of when to switch idle detect method (whether int16
; or int 28)
SWITCH_CNT      dw 0


I08_VEC dw Pwr_i08_isr,0        ; our Int 8 ISR ; NSM5

I9_COUNT        dw 2            ; for every key press more
                                ; a minimum of 2 INT 9's occur

; Application activity monitor interrupts

I9_VEC          dw I9_APP,0

I10_VEC dw I10_APP,0

I13_VEC dw I13_APP,0

I14_VEC dw I14_APP,0

I17_VEC dw I17_APP,0

I21_VEC dw I21_APP,0

I25_VEC dw I25_APP,0

I26_VEC dw I26_APP,0

; Our hook for Int 6c- update system date and time
I6C_VEC dw I6C_RESUME, 0        ;M080

POW_MAJ_VERSION db  0
POW_MIN_VERSION db  0

;------------------------------------------------------------------------
; APM SPECIFIC DATA
;
IFDEF   INCL_APM                ; M001

fAPM_PRESENT    db      0       ; set to 1 if APM present
fAPM_CONNECT    db      0       ; set to 1 if we are connected
APM_MAX_POLLCOUNT       dw      50 ; interval bet PM event polls (in tmr tics)
APM_FLAGS       dw      0       ; flags returned by APM detect call
APM_VER         dw      0       ; APM BIOS ver returned by APM detect call
APM_POLL_COUNT  dw      50      ; interval bet PM event polls (in tmr tics)
                                ; counter for APM polling ; counted in int 8 time

fAPM_STATE      db      0       ; 0 if APM disabled and 1 if APM enabled

APM_RESUME_COUNT        dw      0   ; counter for no of resumes from last
                                ; APM enable
save_event_flag    dw   0       ; save event flag                      kam001
APMBIOS_CMD        dw   0       ; kam005
SETPOWER_PROC      db   0       ; kam006
SETPOWER_POLLCOUNT db   0       ; kam006
SETDEV_FLG         db   0       ; kam008
STANDBY_OR_SUSPEND dw   0       ; kam013

ENDIF                   ;INCL_APM
;-------------------------------------------------------------------
; DATA for CLOCK$ driver

        PUBLIC  CMOSUpdFlg      ; M090
        PUBLIC  CMOSPollCount   ; M090

CMOSUpdFlg      db      0       ; M090 set to 1 if we need to update from CMOS
                                ; set when we see a rollover in BDA (40:70h) or periodically
                                ; in POWER STD mode in an APM machine
CMOSPollCount   dw      MAXCMOSPOLLCOUNT        ; M090
                                ; counted at int 8 time and used in POWER STD mode to update
                                ; system date and time periodically
CMOSFlg         db      0       ; M091 bit 0 set to 1 in POWER STD mode on APM
                                ; machines

;-------------------------------------------------------------------
; BASE LINE calculation data for Int 16 idle detection

BaseLineRef   dw      0         ; counters for counting no of i16s we can get
                                ; through in about 4 secs  (done at each
                                ; exec)
BaseLineOvf     dw      0       ; also used to store previous calculated
                                ;  BaseLineRef
ifdef __TONYI__
        .errnz (BaseLineOvf - BaseLineRef - 2)  ; D1198
endif

; SMR7  - Baseline Max calculation
                public  BL_Samples
BL_NUMSAMPLES   equ     9               ; no of samples collected at EXEC time
BL_Samples      dw      BL_NUMSAMPLES dup ('SM') ; data to hold i16 samples
BL_EndSamples   label   byte
; SMR7 END


;-------------------------------------------------------------------
; CLOCK$ driver DATA

IFDEF   POWERALONE              ; needed only for standalone version
        public  daycnt          ; no of days from 1-1-80 (ABS)
daycnt  dw      0
        public  daycnt2
daycnt2 dw      0               ; used as a temp while calculating daycnt
        public  base_century
base_century    db      19
        public  base_year
base_year       db      80
        public  month_tab
month_tab       db      31,28,31,30,31,30,31,31,30,31,30,31

        public  bin_date_time
bin_date_time:
        db      0               ; year in century (0...99) or minutes (0-59)
        db      0               ; century (19 or 20) or hours (0-23)
        db      0               ; day in month (1...31)
        db      0               ; month in year (1...12) or seconds (0-59)

        public  month_table

month_table:                    ; no of days in year upto this month
        dw      0               ; january
        dw      31              ; february
        dw      59              ; march
        dw      90              ; april
        dw      120             ; may
        dw      151             ; june
        dw      181             ; july
        dw      212             ; august
        dw      243             ; september
        dw      273             ; october
        dw      304             ; november
        dw      334             ; december

ELSE                            ; for resident version
        extrn   bin_date_time:byte
        extrn   month_table:word
        extrn   daycnt:word
        extrn   daycnt2:word

ENDIF                   ; POWERALONE

;-------------------------------------------------------------------
; Used by Int 21 application activity monitor to filter those sysem calls
; that should not inhibit idles and those calls that have to be special cased
;
; 1 byte for each int 21 api from functions 0 thro 5fh
; an entry of 0ffh means ignore this api for false idle monitoring
; 0 -> this api means the system is busy (NOT idle)
; any other number -> special cases
i21_table       label   near
        db      0,   0,   0,   0,   0,   0,   5,   0    ; fns 0-7
        db      0,   0,   0,0ffh,   0,   0,   0,   0    ; fns 8-0f
        db      0,   0,   0,   0,   0,   0,   0,   0    ; fns 10-17
        db      0,   0,   0,   0,   0,   0,   0,   0    ; fns 18-1f
        db      0,   0,   0,   0,   0,0ffh,   0,   0    ; fns 20-27
        db      0,   0,0ffh,   0,0ffh,   0,   0,   0    ; fns 28-2f
        db      0,   0,   0,   0,   0,0ffh,   0,   0    ; fns 30-37
        db      0,   0,   0,   0,   0,   0,   0,   0    ; fns 38-3f
        db      0,   0,   0,   0,043h,   0,   0,   0    ; fns 40-47
        db      0,   0,   0,04ah,   0,   0,   0,   0    ; fns 48-4f
        db   0ffh,0ffh,   0,   0,   0,   0,   0,   0    ; fns 50-57
        db      0,   0,   0,   0,   0,   0,   0,05eh    ; fns 58-5f
MAX_I21_ENTRY   equ 5fh
;
;-------------------------------------------------------------------
;
; MISCELLANEOUS FLAGS and TABLES
;

POWER_STATUS    db      1       ; default ;bit 0 -  S/W pw.mgmt always enabled
                                ; bit 1 - Take control of APM/FIRMWARE  mgmt
                                ; (or get connected to APM)
                                ; M001
                                ; M089 If bit1 is set, We are connected to APM
                                ; but not neccessarily enabled APM
                                ; (look at fAPM_State)
;NSM1 BEGIN
; NSM9 BEGIN    - 8 sets of savings modes with 6 being the default
Savings_Mode    dw      6       ; 3 = MIN savings
                                ; 6 = Reg savings (default)
                                ; 7 = Max savings

; Savings_Table used for NOISE/PSPREAD ratio calculation for the 8 levels of
; savings mode
;
Savings_Table   label   byte
;       bit     7 6 5 4 3   2    1    0
;                              12.5  25  50%  for NOISE/SPREAD RATIO
        db      4               ; 12.5 %  saving  mode 1
        db      2               ; 25 %          - mode 2
        db      6               ; 37.5 %        - mode 3
        db      1               ; 50%           - mode 4
        db      5               ; 62.5%         - mode 5 - MIN
        db      3               ; 75%           - mode 6 - REG
        db      7               ; 87.5%         - mode 7 - MAX
        db      0               ; 100%          - mode 8  - special case
;
; Table used to calculate BaseLineRef for the current level of savings mode
; from the BaseLineMax value calculated at EXEC time.
;
BaseLine_Table  label   byte
;       Bit     7 6   5   4       3     2    1   0
;                   .03  .063    .125  .25  .5  1
; for levels upto 6, the calculated value from this table is substracted from
; BaseLineMax, whereas from level 6, it is added
;
        db      0       ; dummy ; to make this 1-based
        db      1ch     ; for mode 1 - 45% of baseline freq
        db      2       ; mode 2 - 40% of baseline freq ( or 1/2 of current max)
        db      12h     ; mode 3 - approx 35% of baseline
        db      1ah     ; mode 4 - approx 30% of baseline    - MIN
        db      16h     ; mode 5 - approx of 25% of baseline
        db      0       ; mode 6 - 20% of baseline           - REG (+from here)
        db      2       ; mode 7 - 12% of baseline           - MAX
        db      1       ; mode 8 - 10% of baseline      ; special case
;
; NSM9 END
;NSM1 END

NoBaseLineCalc  db      0       ; = 1-> no baseline calc at EXEC time ; NSM2
                                ; set by WIN enh.mode startup call
                                ; BaseLineMax is not calculated if this is set

;-------------------------------------------------------------------
; Ptrs to previous ISRs for each of the interrupts we hook.
; these have to be in Data seg for ROMDOS.
;
Pwr_i2f_next    dw      Pwr_i2f_lab
                dw      Bios_Data
Pwr_i2f_lab:
        pop     ds
        jmp     dword ptr cs:I2f_Vec

;
; M102 - begin
;
Pwr_call_i2f    dw      Pwr_calli2f_lab
                dw      Bios_Data

Pwr_calli2f_ret dw      Calli2fret
                dw      0

Pwr_calli2f_lab:
        pop     ds

        pushf
        call    dword ptr I2f_Vec

        push    ds
        jmp     dword ptr Pwr_calli2f_ret
;
; M102 - end
;
Pwr_i2a_next    dw      Pwr_i2a_lab
                dw      Bios_Data

Pwr_i2a_lab:
        pop     ds
        jmp     dword ptr cs:I2a_Vec

IFDEF  POWERALONE
Pwr_i28_next    dw      Pwr_i28_lab
                dw      Bios_Data
Pwr_i28_lab:
        pop     ds
        jmp     dword ptr cs:i28_Vec
ENDIF

Pwr_i16_next    dw      Pwr_i16_lab
                dw      Bios_Data
Pwr_i16_lab:
        pop     ds
        jmp     dword ptr cs:I16_Vec

Pwr_call_i16    dw      Pwr_calli16_lab
                dw      Bios_Data

Pwr_calli16_ret dw      Calli16ret
                dw      0

Pwr_calli16_lab:
        pop     ds

        pushf
        call    dword ptr I16_Vec

        push    ds
        jmp     dword ptr Pwr_calli16_ret

kb_call_i16     dw      kb_calli16_lab
                dw      Bios_Data
kb_calli16_ret  dw      kbChkRet
                dw      0

kb_calli16_lab:
        pushf
        call    dword ptr I16_Vec
        jmp     dword ptr kb_calli16_ret

Pwr_i9_next     dw      Pwr_i9_lab
                dw      Bios_Data
Pwr_i9_lab:
        pop     ds
        jmp     dword ptr cs:I9_Vec

Pwr_i10_next    dw      Pwr_i10_lab
                dw      Bios_Data
Pwr_i10_lab:
        pop     ds
        jmp     dword ptr cs:I10_Vec

Pwr_i13_next    dw      Pwr_i13_lab
                dw      Bios_Data
Pwr_i13_lab:
        pop     ds
        jmp     dword ptr cs:I13_Vec

Pwr_i14_next    dw      Pwr_i14_lab
                dw      Bios_Data
Pwr_i14_lab:
        pop     ds
        jmp     dword ptr cs:I14_Vec

Pwr_i17_next    dw      Pwr_i17_lab
                dw      Bios_Data
Pwr_i17_lab:
        pop     ds
        jmp     dword ptr cs:I17_Vec

Pwr_i21_next    dw      Pwr_i21_lab
                dw      Bios_Data
Pwr_i21_lab:
        pop     ds
        jmp     dword ptr cs:I21_Vec

Pwr_i25_next    dw      Pwr_i25_lab
                dw      Bios_Data
Pwr_i25_lab:
        pop     ds
        jmp     dword ptr cs:I25_Vec

Pwr_i26_next    dw      Pwr_i26_lab
                dw      Bios_Data
Pwr_i26_lab:
        pop     ds
        jmp     dword ptr cs:I26_Vec

Pwr_i6c_next    dw      Pwr_i6c_lab     ;M080
                dw      Bios_Data       ;M080
Pwr_i6c_lab:                            ;M080
        pop     ds                      ;M080
        jmp     dword ptr cs:I6c_Vec    ;M080

; NSM5 BEGIN - i08 hook
; This is the main interrupt used by POWER to do the following:
;       used for time outs on idle interrupts
;       used for counting idle statistics
;       used for polling for / Broadcasting PM event
;       used for periodically update date&time in POWER STD mode
;       used for switching between I16 and I28
;
IN_I08  db      0                       ; flag  to avoid  I08 reentrancy
;
I08_IdleControl_ptr     label   word
        dw      I08_IdleControl
        dw      0                       ; needs to be filled in

Pwr_i08_isr     proc    far

        pushf
        call    dword ptr cs:I08_Vec
        call    dword ptr cs:I08_IdleControl_ptr
        iret

Pwr_i08_isr     endp
;
; NSM5 END

; JAH2 ====================================================================
; JAH2  - Need to keep our own stack when calculating baseline on int 21 exec
;       and while polling APM for PM event and broadcasting it.

Old_SS          dw      0               ; JAH2
Old_SP          dw      0               ; JAH2

StackInUse      db      0               ; JAH2

I21_Stack       dw      STACK_WORDS dup ('TS') ; JAH2
        EVEN                            ; JAH2
StackTop        LABEL   WORD            ; JAH2

; JAH2  - End
; JAH2 ====================================================================
; NSM4 ===================================================================
; NSM4 address of INDOS flag
INDOS_Offset    dw      0               ; INDOS flag set while we use
INDOS_Seg       dw      0               ; different stack  to avoid task

ifdef __TONYI__
        .errnz  (INDOS_Seg - INDOS_Offset - 2)  ; D1198
endif

; NSM4 END ===============================================================

End_Data        label   near

        tocode                  ;Bios_Code segment starts

IFDEF   POWERALONE              ; device header needed here only for
                                ; standalone version - in MSBIO1 for resident
        assume  ds:nothing

        org 0
DEV_HDR dd CDEV_HDR             ; address of next driver in chain
        dw 8000H                ; character device      ; M003
        dw JDEV_STRATEGY        ; DOS entry points to driver
        dw JDEV_INTERRUPT
        db 'POWER$  '           ; driver name

CDEV_HDR        dd      -1      ; address of next dev hdr in chain
        dw      8008h           ; CLOCK device (& character)
        dw      JDEV_STRATEGY
        dw      JCDEV_INTERRUPT
        db      'CLOCK$  '      ; driver name

;
; M098 - begin
;
StratPtr        dw      offset DEV_STRATEGY
                dw      BIOS_CODE
PwrIntPtr       dw      offset DEV_INTERRUPT
                dw      BIOS_CODE
ClkIntPtr       dw      offset CDEV_INTERRUPT
                dw      BIOS_CODE

JDEV_STRATEGY:
                jmp     dword ptr StratPtr
JDEV_INTERRUPT:
                jmp     dword ptr PwrIntPtr
JCDEV_INTERRUPT:
                jmp     dword ptr ClkIntPtr

ENDOFSTUB       label   byte

BreakAddr       dw      offset bios_data:end_data
                dw      bios_data

ENDOFSTUB11     label   byte

BreakAddr11     dw      offset Sysinitseg:End_Code11
                dw      Sysinitseg

                public  SecondCopy
SecondCopy      db      0               ; 1 => second copy of POWER just loaded
                                ; in UMB, and so no need to display messages
;
; M098 - end
;
;*************** DOS DEVICE REQUEST DEFINITIONS **************

; void DEV_STRATEGY( ES:BX.DOS_REQUEST_HEADER)
; purpose:   Store DOS REQUEST HEADER (ES:BX) for
;            DEV_INTERRUPT routine

DEV_STRATEGY proc far
        push    ds
        mov     ds,cs:Bios_Data_Word
        assume  ds:Bios_Data
        mov     word ptr [RHPTR]+0,bx
        mov     word ptr [RHPTR]+2,es
        pop     ds
        assume  ds:nothing
        ret
DEV_STRATEGY endp

; void DEV_INTERRUPT( [REQHDR].DOS_REQUEST_HEADER)
; purpose:   Executes DOS request; only one fuction is  ; M003
;               supportted by this driver: INIT         ; M003
; for POWER$ driver

DEV_INTERRUPT proc far

        push    ax
        push    dx
        push    bx
        push    cx
        push    es
        push    ds

        mov     ds,cs:Bios_Data_Word
        assume  ds:Bios_Data
        les     bx,[RHPTR]
        cmp     es:[bx].RH_B_CMD,DEVINIT        ; DEVICE INIT?
        jne     dstxxx

;
; M00x
;


        call    far ptr Banner                  ; Put up our signon Banner kam030
        call    far ptr ChkDOSVer_DispErr       ; Check for DOS version


        jae     di_continue                     ; run on DOS 5.0 and above


        mov     word ptr es:[bx].RH_D_BREAKPTR+0, 0
        mov     word ptr es:[bx].RH_D_BREAKPTR+2, cs
        mov     BreakAddr+0, 0
        mov     BreakAddr+2, cs
        mov     BreakAddr11+0, 0                ; kam010
        mov     BreakAddr11+2, cs               ; kam010
di_continue:
        call    far ptr Check_APMBIOS_Ver       ; kam029
        call    far ptr DoCmdLine       ; process CONFIG switches
        jnz     StayHere                ; /low not specified ?

;
; M098 - begin
;
        call    far ptr UMBInit         ; try loading ourselves in UMB
        jc      StayHere                ; no UMB space ?
        mov     StratPtr+2, ax
        mov     PwrIntPtr+2, ax
        mov     ClkIntPtr+2, ax

        mov     word ptr es:[bx].RH_D_BREAKPTR+0, offset ENDOFSTUB
        mov     word ptr es:[bx].RH_D_BREAKPTR+2, cs

        mov     ax, 0100h                       ; DONE!
        jmp     short dstex0
StayHere:
; cannot load in UMB or /LOW specified; init our driver and ret
; M098 - end
;
        call    far ptr Power_Init
;       call    far ptr Banner                  ; Put up our signon Banner kam001

        jmp     short dstex0

dstxxx: mov     ax,STERR+03H            ; RETURN DEVICE ERROR,
                                        ; ILLEGAL COMMAND
dstex0: les     bx,[RHPTR]
        mov     es:[bx].RH_W_STATUS,ax
        pop     ds
        assume  ds:nothing
        pop     es
        pop     cx
        pop     bx
        pop     dx
        pop     ax
        ret
DEV_INTERRUPT endp

Bios_Data_Word  dw      Bios_Data               ; Our data segment

;*************** CLOCK$ DEVICE DRIVER STRAT/INT **************
        extrn   tim_read:near
        extrn   tim_writ:near

        extrn   P_UpdFromCMOS:far               ; M081

        public  tim_table

; dispatch table for CLOCK$ driver
;
tim_table       label   byte
        db      (((offset tim_table_end) - (offset tim_table) -1)/2)
        dw      Normal_exit     ; 00    init
        dw      Normal_exit     ; 01
        dw      Normal_exit     ; 02
        dw      Cldev_cmderr    ; 03
        dw      tim_read        ; 04
        dw      Cldev_BusyErr   ; 05
        dw      Normal_exit     ; 06
        dw      Normal_exit     ; 07
        dw      tim_writ        ; 08
        dw      tim_writ        ; 09
tim_table_end:


CDEV_INTERRUPT  proc    far
        assume  cs:Bios_Code,ds:nothing
        push    si
        push    ax
        push    bx
        push    cx
        push    dx
        push    di
        push    ds
        push    es

        lea     si,tim_table

        mov     ds,cs:Bios_Data_Word
        assume  ds:Bios_Data

        les     bx,[RHPTR]
        mov     al,es:[bx].RH_B_CMD             ; al = cmd
        cmp     al,cs:[si]
        jae     Cldev_cmd_error
        or      al,al                   ; is it init call
        jz      Cldev_Init

        cbw                             ; note that al <= 15 means ok
        shl     ax,1

        add     si,ax

        les     di, es:[bx].trans       ; M082
        cld                             ; ***** always clear direction
        call    cs:word ptr [si+1]      ;go do command
        assume  ds:nothing
        jc      Cldev_set_stat          ; if function returned status, don't
Cldev_Success:
        mov     ax,100h                 ;  load with normal completion

Cldev_set_stat:
        mov     ds,Bios_Data_Word       ; cas///// note: shouldn't be needed!
        assume  ds:Bios_Data
        les     bx,[RHPTR]
        mov     word ptr es:[bx].status,ax ;mark operation complete

        pop     es
        pop     ds
        pop     di
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        pop     si
        ret

Cldev_cmd_error:
        mov     ax,STERR+03H            ; RETURN DEVICE ERROR,
                                        ; ILLEGAL COMMAND
        jmp     short Cldev_set_stat

Cldev_Init:

IFDEF   POWERALONE
        call    far ptr ChkDOSVer
        jb      Cldev_skipinit          ; run on DOS 5.0 and above
ENDIF

        call    far ptr Clock_Init

IFDEF   POWERALONE                      ; M085
        ; Return DEVICE DRIVER end address
Cldev_skipinit:
        les     bx,[RHPTR]
        mov     word ptr es:[bx].RH_D_BREAKPTR+0,offset Bios_Data:End_Data
        mov     word ptr es:[bx].RH_D_BREAKPTR+2,ds
;
; M098 - begin
;

        assume  ds:Bios_Data                            ; kam010
        cmp     word ptr ds:APM_VER,0101h ; Are we running on APM 1.1 BIOS kam010
        assume  ds:nothing

        push    ds
        mov     ax, bios_code
        mov     ds, ax
        assume  ds:bios_code
ifdef __TONYI__
        jz      brkaddr11
        lds     ax, dword ptr BreakAddr                 ; D1198
        mov     word ptr es:[bx].RH_D_BREAKPTR+0,ax     ; D1198
        mov     word ptr es:[bx].RH_D_BREAKPTR+2,ds     ; D1198
        jmp     short endbreakaddr                      ; kam010
brkaddr11:                                              ; kam010
        lds     ax, dword ptr BreakAddr11               ; kam010
        assume  ds:nothing                              ; kam010
        mov     word ptr es:[bx].RH_D_BREAKPTR+0,ax
        mov     word ptr es:[bx].RH_D_BREAKPTR+2,ds
else
        jz      brk_addr11                              ; kam010
        mov     ax, BreakAddr
        mov     word ptr es:[bx].RH_D_BREAKPTR+0,ax
        mov     ax, BreakAddr+2
        mov     word ptr es:[bx].RH_D_BREAKPTR+2,ax
brk_addr11:                                             ; kam010
        mov     ax, BreakAddr11                         ; kam010
        mov     word ptr es:[bx].RH_D_BREAKPTR+0,ax     ; kam010
        mov     ax, BreakAddr11+2                       ; kam010
        mov     word ptr es:[bx].RH_D_BREAKPTR+2,ax     ; kam010
endif

endbreakaddr:                                           ; kam010
        pop     ds
        assume  ds:bios_data
;
; M098 - end
;
ENDIF                                   ; M085
        jmp     short Cldev_Success

CDEV_INTERRUPT  endp

Cldev_cmderr    proc    near
        mov     al,3            ; unknown command error
        mov     ah,81h          ; error return
Cldev_Ret:
        stc                     ; indicate abnormal end
        ret

Cldev_BusyErr   label   near
        mov     ah,3            ; indicate busy status
        jmp     short Cldev_Ret

Cldev_cmderr    endp

ELSE                                            ; resident driver version

        extrn   Bios_Data_Word:word             ; supplied externally

        public  power_table
power_table:
        db      (((offset power_table_end) - (offset power_table) - 1) / 2)
        dw      Normal_exit
        dw      Normal_exit
        dw      Normal_exit
; M003 : BEGIN ; remove ioctl support
;       dw      Power_ioctl_read
        dw      Normal_exit
; M003 : END

power_table_end:


ENDIF                                           ; resident driver version

Normal_exit      proc    near

        clc
        ret

Normal_exit      endp

;******************** INT 2F ISR routine for POWER services *************
;  Entry: AL = function code
;       0       Install check
;
;               EXIT:
;               BX = 504dH (PM) signature if installed  /* M087 */
;
;       1       GET/(Enable/Disable) Power management
;               BH = 0  -> get POWER enabled status
;                  = 1 -> ENABLE DISABLE POWER
;                  BL:bit 0 =T enable Idle detection
;                           =F disable Idle detection
;                  BL:bit 1 =T enable FW/APM
;                           =F disable FW/APM
;               EXIT:
;                  BH = Prev. Power status (bit 0 & 1 as defined above)
;                  BL = Current power status
;                  CY set if function failed
;
;       2       Get/Set Idle Detection Alg
;               BH = 0 -> Get Current idle detection alg.
;                  = 1 -> set Current alg.
;                    BL:bit 0 -> T - KeyBoard idle (Int 16 )
;                       bit 1 -> T - Dos Yield (int 28)
;                       bit 2 -> T - App Idle (int 2f 1680)
;                       bit 3 -> T - DOS idle  (int 2a)
;                       bit 15 -> T - (DEBUG ONLY) toggle SOUND
;                    BL = 0 -> disable all strategies
;                    BL = 0ffh -> enable all strategies
;               EXIT:
;                       BL = current alg if Get_ALG
;
;       03      Get/Set POWER savings level
;               BL = 0 -> get power savings level
;                   1-8 -> Set power savings level
;               EXIT:
;                       BL = current savings level
;
;       80      Get/Set Idle Detection Tuning parameters
;               BX = 0 -> get
;               else Set
;               CX = size of buffer
;               DS:SI -> ptr to buffer
;       81      Get Statistics
;               CX = size of buffer
;               DS:SI -> ptr to buffer
;
;       82      Get/Set APM polling count
;               BX = 0 -> get APM polling count
;               else new polling count to be set
;               EXIT:
;                       BL = new polling count
;
;       8F      Get/Set current BaseLineRef
;               ENTRY:
;                       BX = 0 -> get BaseLineRef
;                           else Set BaseLineRef
;               EXIT:
;                       BX = current BaseLineRef
;                       CX = Previous calculated BaseLineRef
;
; M001: Created - NSM
;
Pwr_Services    proc    near

; use a jump table to switch to appropriate services depending on AL value
        push    ds
        push    di
        lea     di,PS_JmpTable
PS_ChkNxtCmd:
        cmp     byte ptr cs:[di],0ffh   ; end of table ?
        je      PS_Inv_fn
        cmp     al,cs:[di]
        je      PS_CmdFound
ifdef __TONYI__
        add     di, 3                   ; D1198
else
        inc     di
        inc     di
        inc     di
endif
        jmp     PS_ChkNxtCmd
PS_CmdFound:
        inc     di
        jmp     word ptr cs:[di]

PS_JmpTable     label   near
; Service jmp table
; Format:       db      Function no
;               dw      offset of service entry point for this function
;       a value of 0ffh for function no denotes end of jump table
;
        db      0
        dw      PS_Install_Chk
        db      1
        dw      PS_GetSet_PWState
        db      2
        dw      PS_Select_Alg
        db      3
        dw      PS_GetSet_Savings
        db      80h
        dw      PS_Parm_Tune
        db      81h
        dw      PS_Get_Stats
IFDEF   INCL_APM                        ; M108 APM specific service
        db      82h
        dw      PS_APM_PollCount
ENDIF                                   ; M108 END
        db      8fh
        dw      PS_GetSet_BaseLine
strtfn  db      4                              ; kam003 offset 24 (18h)
        dw      PS_Get_DevPWState              ; kam008
        db      5                              ; kam003
        dw      PS_Set_DevPWState              ; kam008
        db      6                              ; kam008
        dw      PS_EnableDisable_DevPWState    ; kam003
        db      7                              ; kam008
        dw      PS_EngageDisengage_PM          ; kam003
        db      0ffh                    ; end of table
;
;
PS_Inv_fn:
        mov     ax,ERROR_PM_FUNCTION_NOT_SUPPORTED
PS_FRet:
        stc
        jmp     PS_Ret


;=============================================================================
;
; FUNCTION 0 - POWER install check call
;
;=============================================================================
PS_Install_Chk:
        mov     ds,cs:Bios_Data_Word    ; kam005
        assume  ds:Bios_Data
        mov     bx,504dH                ; "PM"  ;M087
        cmp     word ptr ds:APM_VER,0101h
                                        ; Are we running on APM 1.1 BIOS   kam028
        jnz     indicate_ver10          ; if not then indicate version 1.0 kam028
        mov     al,POWER_VER_MINOR      ; minor version no                 kam028
        jmp     short indicate_ver11    ; indicate ver 1.01                kam028
indicate_ver10:                         ;                                  kam028
        mov     al,00h                  ; set minor version to 0           kam028
indicate_ver11:                         ;                                  kam028
        mov     ah,POWER_VER_MAJOR      ; major version no                 kam027
        clc                             ; M084
        jmp     PS_Ret                  ; M084

;=============================================================================
;
; FUNCTION 2 - Get/set Idle detection strategy
;
;=============================================================================
PS_Select_Alg:
        mov     ds,cs:Bios_Data_Word
        assume  ds:Bios_Data

IFDEF DEBUG
        cmp     bl,80h          ; SOUND toggle
        je      PS_Toggle_Sound
ENDIF
        or      bh,bh           ; get current algorithm ?
        je      PS_Get_Alg
                                ; no, set alg.
        cmp     bl,0ffh         ; all bits set ?
        je      PS_Alg_OK
        test    bl,ALG_RESERVED_BITS    ; invalid bits set ?
        jz      PS_Alg_OK
        jmp     PS_Inv_Parm_Err
;
PS_Alg_OK:
        and     bl,NOT ALG_RESERVED_BITS
        xor     bh,bh
        and     [CONTROL].IDLE_FLG,ALG_RESERVED_BITS ; clear current strategey
                                        ; except SOUND bit
        or      [CONTROL].IDLE_FLG,bx
        and     bl,(DOSYIELD_ACTIVE + KYC_ACTIVE)
        and     [CONTROL].IDLE_FLG, NOT AUTO_ACTIVE     ; turn off AUTO
        cmp     bl,DOSYIELD_ACTIVE + KYC_ACTIVE
        jne     PS_Get_Alg              ; set AUTO bit if both I16 & I28
        or      [CONTROL].IDLE_FLG,AUTO_ACTIVE  ; selected

; M094 - Fall through to return the current algorithm in BL

PS_Get_Alg:
        mov     bx,[CONTROL].IDLE_FLG
        and     bx,NOT ALG_RESERVED_BITS
        test    [CONTROL].IDLE_FLG,AUTO_ACTIVE
        jz      PS_SRet                 ; if AUTO, then say both I16 & I28 are
        or      bx,(KYC_ACTIVE+DOSYIELD_ACTIVE) ; enabled
        jmp     short PS_SRet

IFDEF DEBUG                     ;Toggle sound only for DEBUG VERSION
PS_Toggle_Sound:
        xor     [CONTROL].IDLE_FLG,SOUND_ACTIVE ; toggle the sound flag
        jmp     short PS_SRet
ENDIF

;=============================================================================
; FUNCTION 82 - get set APM poll count
;
;=============================================================================
IFDEF   INCL_APM                        ; M108 APM specific service
PS_APM_PollCount:                               ; 7 change APM poll count
        call    Do_APM_GetSet_PollCount
        jmp     short PS_SRet

ENDIF                                   ; M108 END
        assume  ds:nothing
;=============================================================================
;
; FUNCTION 80 - GET/SET tuning parameters (CONTROL)
;
;=============================================================================
PS_Parm_Tune:                           ; 4 Tune various POWER.SYS parameters
        cmp     cx, size CONTROL_INFO - SWITCH_DLY
        jb      PS_Buff_Ovf_Err
        push    si                      ; M086
        push    di
        push    es
        mov     es,Bios_Data_Word
        assume  es:Bios_Data
        lea     di,[CONTROL].SWITCH_DLY
        assume  es:nothing
        or      bx,bx                   ; get parm or set parm ?
        jnz     PS_DoCopy               ; if set parm
ifdef __TONYI__
        dec     bx      ; BX == -1 (we *know* it was 0) ; D1198
else
        mov     bx,-1
endif
PS_Exch_ESDS_SIDI:
        push    es
        push    ds
        pop     es
        pop     ds
        xchg    si,di
        or              bx,bx
        jz      PS_EndCopy
PS_DoCopy:
        rep     movsb                   ; transfer data
        inc     bx
        jz      PS_Exch_ESDS_SIDI
PS_EndCopy:
        pop     es
        pop     di
        pop     si                      ; M086
        jmp     short PS_SRet

;=============================================================================
; FUNCTION 81 - Get IDLE Statistics
;
;=============================================================================
PS_Get_Stats:

IFDEF INCL_APM
        cmp     bx,PW_GET_APM_STATS     ;                               ; M076
        ja      PS_Inv_Parm_Err         ; invalid info level ?
        je      PS_Get_APM_Stats        ; APM statistics (resume count ) ?
ELSE
        or      bx,bx                   ; only Idle detection stats will be
        jnz     PS_Inv_Parm_Err         ; returned
ENDIF
        cmp     cx,size IDLE_INFO       ; No, Idle detection statistics
        jb      PS_Buff_Ovf_Err         ; Buffer not sufficient         ; M076
        push    si                      ; M086
        push    di
        lea             di,     INFO
PS_Transf_Stats:
        push    es
        mov     es,Bios_Data_Word
        assume  es:Bios_Data
        assume  es:nothing
        mov     bx,-1                   ; get stats ; there is no set stats
        jmp     short PS_Exch_ESDS_SIDI

IFDEF   INCL_APM

PS_Get_APM_Stats:
        cmp     cx, APM_STATS_STRUC_SIZE
        jb      PS_Buff_Ovf_Err         ; Buffer not sufficient         ; M076
        push    si                      ; M088
        push    di
        lea     di,APM_RESUME_COUNT
        jmp     short PS_Transf_Stats

ENDIF   ; of IFDEF INCL_APM

PS_Inv_Parm_Err:
        mov     ax,ERROR_PM_INVALID_PARAMETER
        stc
        jmp     short PS_Ret

PS_SRet: xor    ax,ax                   ; M084 : Clear CY and  put
                                        ; 0 in AX to mean no error
PS_Ret:
        pop     di
        pop     ds
        ret

;=============================================================================
; FUNCTION 3 - Get/Set POWER Savings level
;NSM1 BEGIN
;=============================================================================
PS_GetSet_Savings:
        mov     ds,cs:Bios_Data_Word
        assume  ds:Bios_Data
        test    [POWER_STATUS],SW_IDLE_ACTIVE
        jz      PS_Inv_Parm_Err
        or      bx,bx
        jz      PS_Get_Savings
        cmp     bx,MAX_SAVINGS_VALUE
        ja      PS_Inv_Parm_Err
        mov     [Savings_Mode],bx
        call    SetSavingsValues
PS_Get_Savings:
        mov     bx,[Savings_Mode]
        jmp     short PS_SRet

PS_Buff_Ovf_Err:
        mov     ax,ERROR_PM_BUFFER_TOO_SMALL
        jmp     PS_FRet

;=============================================================================
; FUNCTION 1 - Get/Set POWER state
;
;=============================================================================
PS_GetSet_PWState:
        mov     ds,cs:Bios_Data_Word
        assume  ds:Bios_Data
        or      bh,bh           ; get power state ?
        call    GetPowStatus    ; M089 : old state in AL
        mov     bh,al           ; get old status
        mov     ax,0            ; M084 : Assume no error
        jz      PS_APM_Ret      ; M084
        test    bl,PWSTATE_RESERVED_BITS
        jnz     PS_Inv_Parm_Err
        push    bx              ; save old status on stack
; set power status
        test    bl,1            ; check for POWER.SYS/EXE enable
        jnz     PS_Enable_Power
; disable POWER.SYS
        test    POWER_STATUS,SW_IDLE_ACTIVE
        jz      PS_Test_APM_Bit         ; already disabled
;
        and     POWER_STATUS,NOT(SW_IDLE_ACTIVE)     ; turn off S/W PM
        jmp     short PS_Test_APM_Bit
PS_Enable_Power:
        test    POWER_STATUS,SW_IDLE_ACTIVE
        jnz     PS_Test_APM_Bit         ; already enabled
        or      POWER_STATUS,SW_IDLE_ACTIVE          ; turn on S/W PM
;
PS_Test_APM_Bit:
;
IFDEF   INCL_APM
        call    Do_APM_Control
ENDIF
;
        pop     bx                      ; get back old status
;
; M084: NSM
; At this point :
; CY set if error
;       AX = error code
; NC -> AX = 0
;
PS_APM_Ret:
        push    ax                      ; M089  BEGIN
        call    GetPowStatus            ; status in al
        mov     bl,al                   ; return with current status
        pop     ax                      ; M089  END
        jmp     PS_Ret

;=============================================================================
; FUNCTION 8F - get/set BaseLineRef
;=============================================================================
PS_GetSet_BaseLine:
        mov     ds,Bios_Data_Word
        assume  ds:Bios_Data
        or      bx,bx                   ; get base line ?
        je      PS_GetBaseLine
        mov     [BaseLineRef],bx
        mov     [KYC].BASE,bx           ; also change our current Kbd base
                                        ; for immediate effect
;
PS_GetBaseLine:
ifdef __TONYI__
; Note: PS_SRet pops off DS, so it's "OK" to whack it here.
        lds     bx, dword ptr BaseLineRef       ; D1198
        mov     cx, ds                          ; D1198
else
        mov     bx,[BaseLineRef]
        mov     cx,[BaseLineOvf]
endif
        jmp     PS_SRet


;=============================================================================
; FUNCTION 4 - Get Device POWER state
;
; This function is same as defined in I0/I1 document as
; DoAPM_GetDevPWState()
; INT 2F AX=5404 API
;=============================================================================
 PS_Get_DevPWState:                                            ; kam008

        mov     ds,Bios_Data_Word                              ; kam010
        assume  ds:Bios_Data                                   ; kam010
        call   far ptr PS_GetDevPWState                        ; kam010
        jmp     PS_Ret                                         ; kam003


;=============================================================================
; FUNCTION 5 - Set Device POWER state
;
; INT 2F AX=5405 API
;=============================================================================
 PS_Set_DevPWState:                                            ; kam008

        mov     ds,Bios_Data_Word                              ; kam010
        assume  ds:Bios_Data                                   ; kam010
        call    far ptr PS_SetDevPWState                       ; kam010
        jmp     PS_Ret                                         ; kam003


;=============================================================================
; FUNCTION 6 - Enable/Disable Device Power State
;
; This function is same as defined in I0/I1 document as
; DoAPM_Enable_Disable_DevPM ()
; INT 2F AX=5406 API
;=============================================================================

 PS_EnableDisable_DevPWState:                                   ; kam003

        mov     ds,Bios_Data_Word                              ; kam010
        assume  ds:Bios_Data                                   ; kam010
        call    far ptr PS_EnableDisableDevPWState             ; kam010
        jmp     PS_Ret                                         ; kam003


;=============================================================================
; FUNCTION 7 - Engage/Disengage Power Management
;
; This function is same as defined in I0/I1 document as
; DoAPM_Engage_Disengage_PM ()
; INT 2F AX=5407 API
;=============================================================================

 PS_EngageDisengage_PM:                                         ; kam003

        mov     ds,Bios_Data_Word                              ; kam010
        assume  ds:Bios_Data                                   ; kam010
        call    far ptr PS_EngageDisengagePM                   ; kam010
        jmp     PS_Ret                                         ; kam003

Pwr_Services    endp

; FAR call for PWR_SERVICES procedure - used for init time settings
;
FPwr_Services   proc    far
        call    Pwr_Services
        ret
FPwr_Services   endp

;****************************** GetPowStatus **********
; Get the current power status
; Entry: none
; Exit:  AL:Bit 0 - 1 if S/W PowMgmt enabled
;           Bit 1 - 1 if H/W PowMgmt enabled
; Regs: AX
; M089: Created
; NOTE: we have no way of knowing whether we are in STD mode or not
; The only way we can find that out is by looking at our flags to see
; if APM was in enabled state last time when we were connected to it
; and IF we are not connected to APM at present. This is the reason
; for this procedure's existence.
;******************************************************************
GetPowStatus    proc    near
        pushf
        mov     al,POWER_STATUS
        test    al,FW_IDLE_ACTIVE           ; test for H/W enabled state
        jnz     GPS_Ret
; H/W not connected state; let us see if it is atleast enabled from what
; we know of before last disconnect.
IFDEF   INCL_APM
        test    fAPM_STATE,1
        jz      @f
        or      al,2            ; H/W pow.mgmt enabled - POWER STD mode
@f:
ENDIF                   ; IFDEF INCL_APM
GPS_Ret:
        popf
        ret
GetPowStatus    endp


IFDEF   INCL_APM
;****************************** Do_APM_GetSet_PollCount **********
; INT 2F subfunction service procedure
; To get/set the APM pollcount
; Entry: BX = 0 -> get APM poll count
;               otherwise, New poll count to be set
; Exit:  BX = new APM Poll count
; Regs: BX,DS,Flags
;******************************************************************
Do_APM_GetSet_PollCount Proc    NEAR

        mov     ds,cs:Bios_Data_Word
        assume  ds:Bios_Data
        or      bx,bx
        jz      DAPC_Get_PollCount
        mov     [APM_MAX_POLLCOUNT],bx          ; ref.counter
        mov     [APM_POLL_COUNT],bx             ; M093 current counter
ifdef __TONYI__
        ret                             ; D1198
else
        jmp     short DAPC_Ret
endif
DAPC_Get_PollCount:
        mov     bx,APM_MAX_POLLCOUNT
DAPC_Ret:
        ret

Do_APM_GetSet_PollCount endp

;****************************** Do_APM_Control *********************
; INT 2F subfunction service procedure
; To (enable-connect to)/(disable-disconnect from) APM
; Entry:  BL = 3 -> connect and enable APM              ; M089
;         BL = 2 -> stay disconnected but enable it if needed
;         BL = 1 or 0 -> disconnect and disable
;
; Exit:  CY if operation failed
;               ax = error code
;        ELSE AX = 0
;
;       (POWER_STATUS flags updated properly
;        fAPM_STATE also updated)
;       APM_RESUME_COUNT initialised to zero if enable successful
;
; Regs: ax,bx,cx,dx
;******************************************************************

Do_APM_Control  proc    near

        cmp     bl,2            ; check for F/w PM bit enable
        jae     DACt_Enable_APM

; M089 BEGIN
; With the new definition of POWER STD, we need to enable APM but not
; connected to it (and sometime disable APM but we would not have been
; connected to it for doing it). Enable/disable can only be done if we
; are connected to APM and so We first connect to APM to enable/disable
; APM and depending on the requested state, we disconnect from APM
;
; Disconnect and disable APM
        and     [CMOSFlg],NOT(1)        ; M091 reset STD mode+APM bit in CMOSFlg
        test    POWER_STATUS,FW_IDLE_ACTIVE     ; are we  connected already ?
        jnz     DACt_Dis_con_able       ; yes, just disable and disconnect
; we are not connected ; so go connect to it first, enable it
; and then disable it!

IFDEF POWERALONE
        call    far ptr Check_and_Init_APM ; M003: always check for APM presence
ELSE
        call    Check_and_Init_APM_Ptr
ENDIF
; M089 END
DACt_Dis_con_able:
        test    fAPM_PRESENT,1          ; M003:no need to do anything if APM not
        jz      DACt_End                ; M003:present
; disable APM
        mov     cx,APM_DISABLE_FUNC
        call    Do_APM_Enable_Disable   ; disable APM ;M005
        jc      DACt_End
        mov     fAPM_STATE,0            ; flag APM disabled state
        call    Do_APM_DisConnect
        and     POWER_STATUS,NOT (FW_IDLE_ACTIVE)
        jmp     short DACt_End
DACt_Enable_APM:
        push    bx                      ; save the desired state (input)

;M089 BEGIN
        test    POWER_STATUS,FW_IDLE_ACTIVE
        jz      DACt_Connect            ; Go connect and enable
        test    fAPM_STATE,1            ; are we also enabled ?
        jnz     DACt_ChkForDiscon       ; yes, go see if we have to disconnect
        mov     cx,APM_ENABLE_FUNC
        call    Do_APM_Enable_Disable   ; enable APM
        mov     fAPM_STATE,1            ; flag APM enabled state

DACt_Connect:
;M005 BEGIN - for resident driver call Check_and_Init_APM through pointer

IFDEF POWERALONE
        call    far ptr Check_and_Init_APM ; M003: always check for APM presence
ELSE
        call    Check_and_Init_APM_Ptr
ENDIF ; POWERALONE

;M005 END

        jnc     DACt_ChkForDiscon       ;  before connecting to it
        jz      DACt_APM_ConnErr        ; ignore if APM not present
        clc

DACt_ChkForDiscon:
        pop     bx                      ; get back requested APM state
        and     [CMOSFlg],NOT(1)        ; assume not stdmode+apm
        cmp     bl,3                    ; both S/W and F/W pwmgmt ON ?
        je      DACt_End                ; Yes, all done

; User requested APM be ON but we should stay disconnected (POWER STD)

        call    Do_APM_DisConnect
        and     POWER_STATUS,NOT (FW_IDLE_ACTIVE)
        test    fAPM_PRESENT,1          ; M091
        jz      DACt_End                ; M091
        or      [CMOSFlg],1             ; M091 set POWER_STDMODE+APM
        mov     [CMOSPollCount],MAXCMOSPOLLCOUNT ; M091
; M089 END

DACt_End: xor   ax,ax                   ; M094  - ax=0 -> no error
DACt_Ret:
        ret

DACt_APM_ConnErr:
        pop     bx
        mov     ax,ERROR_PM_ALREADY_CONNECTED   ; put valid error code here
        stc
ifdef __TONYI__
        ret                             ; D1198
else
        jmp     short DACt_Ret
endif

Do_APM_Control  endp



ENDIF
;********************** TIMER TIC READ ROUTINE *******************
;********************** OEM EXTENDABLE ***************************
; ax = READ_TMR0( void)
; purpose: to return tic stored in timer0
;
; M0FF:
; OEMs NOTE:
; This procedure should always return a count between 64k and 1
; This code assumes the existence of 8253 chip only and does not care for
; the double countdown in mode 3. The algorithm in Chk_delay will take care
; of that problem to quite some extent.
;
; There is an alternate code for 8254 chip included here (within IFDEFs).
; Using this code, if the machine has 8254, will result in accurate timings.
;
; Further CAVEAT: WIN 3.0 does not support the readback command used in the
; 8254 code here and so POWER will not work properly if 8254 code is used
; with win 3.0 even if the machine has an 8254.
;
; DOS usually uses mode 3 in both 8253/54. ( mode 2 counts the MAXCOUNT
; just once whereas mode counts it twice in a tic period). But in 8254,
; we can get half cycle we are in, in mode 3 whereas we can't get that
; info in 8253.

PUBLIC Read_tmr0
Read_tmr0 proc near
        assume  ds:nothing, es:nothing

IFNDEF  _8254
;
; 8253 code

        cli
        mov     al,0
        out     43h,al
        jmp     $+2
        in      al,40h
        jmp     $+2
        mov     ah,al
        in      al,40h
        sti
        xchg    ah,al
        ret
ELSE                            ; 8254 specific code
        push    bx
        mov     al,0c2h         ; read mode as well as count of tmr0
        cli
        out     43h,al
        jmp     $+2

        in      al,40h          ; status byte
                                ; bit 7 -> first half cycle
                                ; bit 1-3 -> mode
        mov     bl,al           ; save status in bl&bh
        mov     bh,al
        jmp     $+2

        in      al,40h          ; low byte of count
        jmp     $+2
        mov     ah,al

        in      al,40h          ; high byte of count
        sti
        xchg    ah,al           ; ax = current tmr count

        and     bl,0eh          ; mask everything but mode bits
        cmp     bl,6            ; mode 3 ?
        jne     rt_ret          ; if mode 2 no need for adjustments
        shr     ax,1            ; halve for mode 3 cycle 1
        test    bh,80h          ; is this cycle 2 ?
        jz      rt_ret          ; yes, the count is already correct
        add     ax,8000h        ; first cycle, so add 32k to the count
rt_ret:
        pop     bx
        ret
ENDIF

Read_tmr0 endp

; ******************************* SOUND routines **********************
IFDEF DEBUG                  ; only use this code on test version

START_SOUND proc

        mov     al,0
        out     42h,al
        jmp     $+2
        mov     al,50h
        out     42h,al

        mov     al,3
        in      al,61h
        jmp     $+2
        or      al,3
        out     61h,al
        ret

START_SOUND endp

END_SOUND proc
        in      al,61h
        jmp     $+2
        and     al,not 3
        out     61h,al
        ret
END_SOUND endp

ENDIF                                   ; IFDEF DEBUG

; *****************************************************

; Z.flag CheckV86()
; return - NZ, if 386 V86 or protected  mode
;          ZR  if real mode
; OEMS: NOTE:
; the flag CV86FLG in CONTROL data controls checking 86 mode.
; This procedure is just there so that we can avoid executing a CPU HLT in
; protected mode, if the protected mode software does not support HLT properly.
; Most DOS extenders/EMS and protected mode software supports HLT instruction
; and so, the default is : WE DON'T CHECK for Protected mode for doing HLTs.
;

PUBLIC CheckV86
CheckV86 proc near
        assume  ds:Bios_Data, es:nothing

        test    [CONTROL].CV86FLG,1     ; Avoid  test
        jz      cv8ret                  ; if bit cleared

        test    [CONTROL].CV86FLG,2             ; is it a 386?
        jz      cv8all                  ; don't know yet ...
cv8386:
ifdef __TONYI__
; Saves several bytes....
        push    ax              ; D1198
        .286p                   ; D1198
        smsw    ax              ; D1198
        .8086                   ; D1198
        test    al, 1           ; D1198
        pop     ax              ; D1198
else
        .386p
        smsw    [MSW]

        .8086
        test    [MSW],1                 ; check for protected mode
endif
        ret                             ; NZ, yes

cv8all: push    ax                      ; run through all proccesors

        push    sp                      ; below 286?
        pop     ax
        cmp     sp,ax
        jne     cv8ex0                  ; yes, disable check

        pushf                           ; check TS
        pop     ax                      ; should be zero for 286
        or      ax,4000h                ; try setting TS high
        push    ax
        popf
        pushf
        pop     ax                      ; restore TS

        test    ax,4000h                ; did it remain 0?
        jz      cv8ex0                  ; yes, 286

        pop     ax
        or      [CONTROL].CV86FLG,2     ; Set 386 bit to save time
        jmp     short cv8386

cv8ex0: xor     ax,ax                   ; We don't have to test again
        mov     [CONTROL].CV86FLG,ax    ; return ZF
        pop     ax

cv8ret: ret
CheckV86 endp

;***************** DO_IDLE_DOS ****************
; BUGBUG - nagara - this procedure is never called and can safely be
; removed  for POWER version 1.0. (note that there is a reference to this
; procedure in power_init procedure at the end of this file)
;
PUBLIC Do_idle_dos
Do_idle_dos proc far
        assume  ds:nothing, es:nothing

        push    ds
        mov     ds,Bios_Data_Word
        assume  ds:Bios_Data
        call    DO_IDLE
        pop     ds
        assume  ds:nothing
        ret

Do_idle_dos endp

; ***************************  DO_IDLE ***************************
; ************************ OEM EXTENDABLE ************************
;
; CY.flag DO_IDLE()
; purpose: To put CPU in a low power state via a halt or
;          OEM supplied OEM_IDLE routine.
; passed:  control.IDLE_FLG - enables/disables DO_IDLE
; return:  Carry Set, if IDLE was executed
;
; EntryPoints: I2fIdleEntry
; PseudoCode:
;       Do_idle:
;               if (I2f_aware app) ret;
;       I2fIdleEntry:
;               if (Idle_detection disabled) ret;
;               if (activity detected)
;                       ret;
;               if (int flags disbled) ret;
;               update idle statistics counter;
;               if (SOUND-ACTIVE)
;                       start sound;
;               set IDLTIC flag;
;               if (APM enabled)
;                       call APM_CPU_IDLE
;               else
;                       if (OEM_EXTENDED)
;                               call OEM_EXTENSION
;                       else
;                               if (not protected mode)         \\ checkV86
;                               do CPU_HLT
;               // end of idle
;               if (SOUND_ACTIVE)
;                       end sound;
;               ret.

PUBLIC Do_idle
Do_idle proc near
        assume  ds:Bios_Data, es:nothing

; JAH3 Start - If app supports IDLE only idle on int 2f idle entry point below


        push    ax                      ; M099
        push    ds
        mov     ds,PSPsegment           ; DS:BX --> Current PSP in Dosdata
        assume  ds:nothing
        mov     ax,WORD PTR ds:[CUR_PSP_OFFSET] ; ax -> current app's PSP M099
        pop     ds                      ; M099
        assume  ds:Bios_Data
        cmp     ax, PSPOf2fApp          ; M099
        pop     ax                      ; M099

        je      didex1                  ; Idle aware app so don't idle

        PUBLIC I2fIdleEntry
I2fIdleEntry:                           ; Entry point from int 2fh handler

; M103 - BEGIN - (code moved from do_idle entry)
; Check for POWER OFF. If IDLE DETECTION is disabled, we should not do idles.

        test    POWER_STATUS,SW_IDLE_ACTIVE ; do this only if IDLE_DETECT enabled
        jnz     didbgn
didex1: clc                             ; idle failed  to execute
        ret

didbgn:
; M103 END
        push    ax

        ; if activity has been detected DO_IDLE will be avoided
        ; for a period measured in timer interrupts

        cmp     [SPDUP],0               ; is system speed up?
        je      did1ot

        mov     ax,[SPDUP_DLY]          ; check if enough time
did3ot: cmp     [SPDUP_CNT],ax          ; has passed to return
        jae     did2ot                  ; to normal idle

didxxx: pop     ax
        jmp     short didex1

did2ot: mov     [SPDUP],0
        mov     [SPDUP_DLY],0

did1ot: in      al,21h                  ; get interrupt mask
        test    al,3h                   ; make sure timer and
        jnz     didxxx                  ; keyboard aren't masked

        add     word ptr [INFO].IDLE_TOT,1
        adc     word ptr [INFO].IDLE_TOT+2,0


IFDEF DEBUG

        test    [CONTROL].IDLE_FLG,SOUND_ACTIVE
        jz      did0ot

        call    START_SOUND

ENDIF                                   ; IFDEF DEBUG

did0ot: mov     [IDLTIC],1              ; set IDLE flag ; M004

didhlt:

IFDEF INCL_APM
; Note that we should also see if we are connected to APM or not. But it is not
; needed because fAPM_STATE will be 1 for only STD mode or ADV mode.
; And we will come here only for ADV mode, and SW_IDLE_DETECTION_ONLY cases
; in the second case, we should disabled APM and so fAPM_STATE will be 0
;
        cmp     fAPM_STATE,0            ; is APM present and enabled ?
        je      didNoAPM
        call    Do_APM_Idle
        jmp     short didResume
didNoAPM:
ENDIF   ; for IFDEF INCL_APM

IFDEF OEM_EXTENSION
        call    OEM_IDLE
ELSE
        call    CheckV86                ; M004 AVOID HLT IN V86
        jnz     didNoHlt
        sti                             ; interrupts must be enabled
        hlt                             ; or system will halt ...
didNoHlt:
ENDIF

didResume:

didot2:
IFDEF DEBUG
        test    [CONTROL].IDLE_FLG,SOUND_ACTIVE
        jz      didex0

        call    END_SOUND
ENDIF                                    ; IFDEF DEBUG

didex0: pop     ax
        stc                             ; CY.set halt executed
        ret
DO_IDLE endp

;********************* APM functions ***********************************

IFDEF INCL_APM

;********************* APM IDLE routine ********************************
; Called by Do_Idle procedure when APM BIOS is present
;
; Psuedocode:
;       call APM_IDLE API
;       if (clock changes for CPU_IDLE API) {
;               call CPU_BUSY API
;       }
;
; Entry: nothing
; exit: nothing
;
;***********************************************************************
; Do_APM_CPUBUSY        Entry Point             (M002 addition)
;       Calls APM CPUBUSY API to return CPU to full speed after a resume
;               or a CPU_IDLE
; Entry: none
; Exit: none
;***********************************************************************

Do_APM_Idle     Proc    near

        push    bx
        mov     ax,APM_CPUIDLE_FUNC
        int     15h             ; make CPU_IDLE API call
        pop     bx
;
Do_APM_CPUBUSY  label   near            ; M002; added this entry point
;
; Check if we need to make a CPU_BUSY call
        test    APM_FLAGS,APM_SLOW_CLOCK        ; does CPU_IDLE slows clock ?
        jz      dai_End

; need to make CPU_BUSY call
        push    bx
        mov     ax,APM_CPUBUSY_FUNC
        int     15h             ; make CPU_BUSY call to speedup CPU
        pop     bx
dai_End:
        ret

Do_APM_Idle     endp

;********************* Do_APM_Connect **********************************
; Purpose: to connect to APM BIOS as the coop.process (real mode only)
; Entry: none
; exit: CY      - connect failed
;               ax = error code
;       NC      - connection succeeded
;***********************************************************************

Do_APM_Connect  proc    near            ;M005

        test    fAPM_PRESENT,1          ; Do this only if APM is present
        jz      DAC_End
        mov     ax,APM_CONNECT_FUNC
        mov     bx,APM_SYSTEM_BIOS
        int     15h
        jc      DAC_End
        mov     fAPM_CONNECT,1
DAC_End:
        ret
Do_APM_Connect  endp

;********************* Do_APM_Disconnect **********************************
; Purpose: to disconnect from APM BIOS
; Entry: none
; exit: CY      - Disconnect failed (can this ever happen ?)
;               ax = error code
;       NC      - disconnect succeeded
;***********************************************************************

Do_APM_Disconnect       proc    near

        test    fAPM_CONNECT,1          ; disconnect only if WE are
        jz      DAD_End                 ; connected
        mov     ax,APM_DISCONNECT_FUNC
        mov     bx,APM_SYSTEM_BIOS
        int     15h
        jc      DAD_End
        mov     fAPM_CONNECT,0
DAD_End:
        ret
Do_APM_Disconnect       endp
;********************* Do_APM_Enable_Disable **********************************
; Purpose: to enable/disable all power management
;
; Entry: CX = APM_DO_DISABLE -> disable all power management
;           = APM_DO_ENABLE  -> enable all power management
;
; exit: CY      - function unsuccessful
;               ax = error code
;       NC      - function succeeded
;***********************************************************************


Do_APM_Enable_Disable   proc    near    ;M005

        mov     ax,APM_ENABLE_DISABLE_FUNC
        mov     bx,APM_ALL_DEVICES
        int     15h
        jnc     DAED_Ret
        mov     ax,ERROR_PM_NOT_CONNECTED
DAED_Ret:
        ret

Do_APM_Enable_Disable   endp


ENDIF           ; for IFDEF INCL_APM

;********************* General Idle Check Routine **********************
; GUTS of INT16/INT28 IDLE DETECTION CODE
;
; purpose: called by INT 28 and 16 interrupt handlers
;       This is the common routine which checks elapsed time between
;       interrupts in order to adjust idle signaling.
;
;       Entry : SI = base address of PERIOD_INFO structure for this interrupt
;               DI = address of total delay accumulator for this interrupt
;               BX = address of time accumulator for this interrupt
;               DS = our data segment
;       Exit  : nothing
;
;       REGS USED: AX,CX,FLAGS
;
;       (Look at the POWER.EXE documentatiion for a complete alg. description
;       and pseudocode).


public Chk_Delay
Chk_Delay       proc
        assume  ds:Bios_Data, es:nothing
;entry point for I28 idle checking
        xor     cx,cx

; entry point for i16 idle checking
; for i16: cx = 1

Chk_i16idle     label   near
;
        test    [POWER_STATUS],SW_IDLE_ACTIVE  ; is idle detection on ?
        jz      cd_Ret                  ;
;
; if we see a timer tic bet last int and this int, we probably waited too
; long (or we cannot accurately calculate the time between them)
        push    ax
        cmp     word ptr [bx]+2,1      ; waited too long?
        jae     cd_ClrTmr               ;NSM3 yes, do a recount

cd_readtime:
        call    READ_TMR0               ; read return time (in AX)
        sub     [bx],ax                 ; NSM3
        jc      cd_ClrTmr               ; Go recount on overflow

; check for a very high value of time interval
;
cd_1:   mov     ax,[si].BASE            ; is TIC >= BASE+THRESHOLD?
        add     ax,[CONTROL].THRESHOLD
        jnc     cd_2
        mov     ax,0FFFFh

cd_2:
        cmp     [bx],ax
        jae     cd_transf_period        ; yes, ignore reading
;
; branch off for adapt/idle cycles
;
        cmp     word ptr [si].ADAPT,0   ; are we in adapt cycle ?
        jne     cd_doAvg                ; yes go do avg.

; IDLE CYCLE:
; we are in idle cycle. check to see if we are within allowed limites for
; idle
        mov     ax,[si].BASE            ; is TIC <= BASE+NOISE?
        add     ax,[si].NOISE
        cmp     [bx],ax
        ja      cd_adapt                 ; no, go for adapt
;
;BUGBUG: we are only checking for upper limit and do not check for lower limit
; Checking lower limits will help us avoid some aliasing problems with the 8253
; timer reading code. (nagara dec/91)

; idle cycle processing

cd_doidle:
        call    DO_IDLE
ifdef __TONYI__
; Saves a byte....
        mov     ax, 0                   ; Warning: No XOR here!!! ; D1198
        adc     [di], ax                ; accumulate idle time    ; D1198
        adc     [di]+2, ax                                        ; D1198
        mov     [bx]+2, ax              ; avoid speed up error    ; D1198
else
        adc     word ptr [di],0         ; accumulate idle time
        adc     word ptr [di]+2,0
        mov     word ptr [bx]+2,0       ; avoid speed up error
endif

cd_transf_period:
        mov     ax,[bx]                 ; take current interval and store it
        mov     [si].PERIOD,ax          ; as PERIOD
cd_ClrTmr:
        call    ClearTimer              ; do APP_speedup if needed, clear
                                        ; timer accumulator for this int.
        pop     ax

cd_Ret:
        ret



; ADAPT CYCLE PROCESSING:
;
; try calculating avg.
; [bx] = current period
; [si].PERIOD = avg so far
; if the current period is not within 50% of prev. avg we check to see if
; we can ignore this period for avg and go for next one. If we have seen
; a no of such high(or low) values in the cycle so far, then start adapt
; cycle all over again

cd_doAvg:
        mov     ax,[CONTROL].ADAPT_DLY
        cmp     [si].DELAY,ax           ; is delay over?
        jae     cd_end_adapt            ; yes, go cmp our avg with baselineref
        mov     ax,[si].PERIOD
        push    ax
        add     ax,[si].PSPREAD         ; ax = Higher limit for spread
        cmp     ax,[bx]                 ; is current period more than spread
        pop     ax                      ; above our prev. period/avg
        jb      cd_throw_sample
        sub     ax,[si].PSPREAD         ; ax =  lower limit for spread
        cmp     ax,[bx]                 ;
        ja      cd_throw_sample         ; if current period < lower spread limit
                                        ; then throw sample
;IFDEF OLDVERSION
;       mov     ax,[bx]                 ; get current period
;       add     ax,[si].PERIOD          ; add previous period
;       jnc     cd_4
;
;current avg + period is >64k.
; no point in trying to find an avg with these figures. go take new avg
;
;       mov     ax,[BaseLineRef]
;       jmp     short cd_5
;
;cd_4:  shr     ax,1
;
;cd_5:  mov     [bx],ax
;ELSE
;
; M106 - BEGIN  - collect 16 sample timings and do avg on them ; use this avg
; for the next 16 samples.
        mov     ax,[bx]                 ; get current period
        add     [si].ACC_COUNTER,ax     ; add it to accumulated value
        adc     [si].ACC_HI,0
        inc     [si].SAMPLE_COUNT
        cmp     [si].SAMPLE_COUNT,MAX_SAMPLES_PER_AVG
        jb      cd_ClrTmr
        push    dx
        mov     dx,[si].ACC_HI
        mov     ax,[si].ACC_COUNTER
        shr     dx,1
        rcr     ax,1                    ; /2
        shr     dx,1
        rcr     ax,1                    ; /4
        shr     ax,1                    ; /8
        shr     ax,1                    ; /16
        add     ax,[si].PERIOD          ; add previous avg
        shr     ax,1                    ; / 2
        pop     dx
;ENDIF
; M106 END

cd_SetNewAvg:
        mov     [si].PERIOD,ax
        call    CalcNewSpread
        mov     [si].PSPREAD,ax
; M106 BEGIN - initialise the time accumulation counters for avg.
;IFDEF OLDVERSION
;ELSE
ifdef __TONYI__
; Saves a byte...
        xor     ax, ax                  ; D1198
        mov     [si].ACC_COUNTER, ax    ; D1198
        mov     [si].ACC_HI, ax         ; D1198
        mov     [si].SAMPLE_COUNT, ax   ; D1198
else
        mov     [si].ACC_COUNTER,0
        mov     [si].ACC_HI,0
        mov     [si].SAMPLE_COUNT,0
endif
;ENDIF
; M106 END
        jmp     short cd_ClrTmr         ; go for next int


; START ADAPT_CYCLE:
; we are above the allowed idle limits; go for adapt cycles

cd_adapt:
ifdef __TONYI__
; Saves 3 bytes
        xor     ax, ax                          ; D1198
        mov     word ptr [si].DELAY, ax         ; D1198
        mov     [ErrSampleCount], ax            ; D1198
        inc     ax                              ; D1198
        mov     word ptr [si].ADAPT, ax ; start measuring increase ; D1198
else
        mov     word ptr [si].ADAPT,1   ; start measuring increase
        mov     word ptr [si].DELAY,0
        mov     [ErrSampleCount],0
endif
        mov     ax,[bx]
        jmp     short cd_SetNewAvg

cd_end_adapt:
        mov     word ptr [si].ADAPT,0
        mov     ax,[si].PERIOD             ; for next compare ...
        jcxz    cd_setbase              ; for i28 and i2f, no baseline ref.
        cmp     ax,[BaseLineRef]        ; for i16, have a max limit for period
        jbe     cd_setbase              ;
        mov     ax,[BaseLineRef]
        mov     [si].BASE,ax            ; go up to the max.allowed base
        call    CalcNewNoise
        mov     [si].NOISE,ax
        jmp     cd_transf_period

; Count the errors while in adapt cycle; if the error count exceeded the
; max allowed value, start the adap cycle all over again with the last error
; interval as the new AVG
;
cd_throw_sample:
        inc     [ErrSampleCount]
        mov     ax,[ErrSampleCount]
        cmp     ax,[CONTROL].MAXERRSAMPLE
        ja      cd_adapt                ; go try adapting again
        jmp     cd_ClrTmr

cd_setbase:
        mov     [si].BASE,ax
        call    CalcNewNoise
        mov     [si].NOISE,ax           ; noise = some % of base depending on
                                        ; current savings mode
        jmp     cd_doidle

Chk_Delay       endp


;*************** Timer clear and app speedup adjustment
;       Helper routine for Chk_Delay.  Called independently
;       by I16_IDLE when it detects a key waiting.  Calls APP_SPDUP
;       if required, and clears out timer overflow
;
;       Entry : BX = address of timer accumulator for this interrupt
;       Exit  : nothing
;       Uses AX -- caller must preserve

public  ClearTimer
ClearTimer         proc
        assume  ds:Bios_Data
        cmp     word ptr [bx]+2,3       ; BUGBUG - nagara; this code is
                                ; strange; it looks for 3 timer tics ?
        jb      ct_0
        call    APP_SPDUP               ; speedup proportional
                                        ; to delay...
ct_0:
        mov     word ptr [bx]+2,0       ; clear key timer
        call    READ_TMR0               ; read current timer value and
        mov     [bx],ax                 ; store it
        cmp     word ptr [bx]+2,0
        jne     ct_0

        ret

ClearTimer         endp


;********************* APPLICATION IDLE CHECK *******************
; purpose: check application idle (INT 2F Function 1680H)
;          if interrupt is detected than DO_IDLE should be called
;          the interrupt should be absorbed.
; PseudoCode:
;       if (win enh mode startup)
;               set flag to avoid BaseLineRef calculation at EXEC time
;       if (TaskSwapper startup or Win Enh mode startup)
;       ( call previous int 2f guy
;         store the prev. inst.data ptr
;         pass back our inst data ptr
;         iret
;       }
;       if (win enh mode end)
;               reset flag to start BaseLineRef calc at EXEC time
;       if (idle interrupt) {
;               if (idle detection/i2f strategy disabled) chain to next guy;
;               remember current PSP as I2f_Aware app
;               call do_idle;
;               iret
;       }
;       if (POWER API call)
;               service the API call;
;               iret
;       else
;               chain to next i2f guy
;

PUBLIC I2f_idle
I2f_idle proc   far
        assume  ds:nothing, es:nothing

        push    ds
        mov     ds, Bios_Data_Word
        assume  ds:Bios_Data
;
        cmp     ax, 4b05h               ; TaskSwapper startup ? M100
        je      Instance_pwr            ; M100
;
        cmp     ax,(MultWin386 * 256 + Win386_Init)     ; NSM2
        je      I2fWinEnhStart                          ;NSM2
        cmp     ax,(MultWin386 * 256 + Win386_Exit)     ;NSM2
        je      I2fWinEnhEnd                            ;NSM2
;
        cmp     ax,1680h                ; Any app idles?
        jne     i2F_chk_ours            ; check for a service call to us

; JAH3 Set PSPOf2fApp to the current app's PSP

        test    [POWER_STATUS],SW_IDLE_ACTIVE   ;Make sure idle_detect is on
        jz      i2Fnxt                  ; Power is off so just chain
        test    [CONTROL].IDLE_FLG,APP_ACTIVE ; Make sure power is on
        jz      i2Fnxt                  ; Power is off so just chain


        push    ax                      ; M099
        push    ds
        mov     ds,PSPsegment           ; DS --> Current PSP in Dosdata
        assume  ds:nothing
        mov     ax,WORD PTR ds:[CUR_PSP_OFFSET] ; ax -> current app's PSP M099
        pop     ds
        assume  ds:Bios_Data
        mov     PSPOf2fApp, ax          ; M099
        pop     ax

; JAH3 end

; load up registers and call do_idle
        push    di
        lea     di,[INFO].APP_TOT

; JAH   call    DO_IDLE
ifdef __TONYI__
MergePoint1:                            ; D1198
endif
        call    I2fIdleEntry            ; JAH Use new idle entry point

        adc     word ptr [di],0         ; accumulate idle time
        adc     word ptr [di]+2,0

I2FIdleCleanup:                         ; JAH New label
        pop     di
        pop     ds
        iret

i2Fnxt:
        jmp     dword ptr pwr_i2f_next

i2f_chk_ours:
        cmp     ah, MultPWR_API         ; is this a call for one of our ; M077
        jne     i2fnxt                  ; services ?
        pop     ds                      ; restore original vector
        call    Pwr_Services            ; one of our services

        push    dx                      ; save dx and bp since will be used  ; kam015
                                        ; as temporary storage               ; kam015
        push    bp                      ;                                    ; kam015
        mov     bp,sp                   ; BP->SP                             ; kam015
        mov     dx,[bp+08h]             ; retrieve the flags of the caller   ; kam015
                                        ; that are saved on the stack        ; kam015
                                        ; bp+08 accounts to the following    ; kam015
                                        ; DX + BP + Ret Addr(Seg:Off)        ; kam015
                                        ; 2  +  2  +   4                     ; kam015
        jnc     clear_carryflag
        or      dx,01h                  ; set the carry flag bit (bit 0)     ; kam015
        jmp     short restore_flags
clear_carryflag:
        and     dx,11111110b            ; clear the carry flag bit (bit 0)   ; kam022
restore_flags:
        mov     [bp+08h],dx             ; restore the flags on the stack     ; kam015
        pop     bp                      ; restore bp and dx                  ; kam015
        pop     dx                      ;                                    ; kam015

        iret                            ; no propagation

; If Win Enh mode starts  we don't need to recalc our baseline anymore
; just set a flag so that we avoid baseline calc oper at exec time
i2fWinEnhStart:
        test    dx,WIN286STDMODE        ; is this a standard mode win ?
        jnz     i2fnxt                  ; yes, don't worry about it
; enhanced mode: set win3Flg
        inc     [NoBaselineCalc]
        jmp     short Instance_Pwr      ; M100
i2fWinEnhEnd:
        dec     [NoBaselineCalc]
        jmp     i2fnxt
;
; M100 - begin
;
Instance_Pwr:
        jmp     dword ptr Pwr_call_i2f          ; M102
Calli2fRet:                                     ; M102
        mov     ds,Bios_Data_Word               ;reinit DS to Bios_Data M102
        assume  ds:Bios_Data

        mov     word ptr Pwr_SINextDev+0, bx
        mov     word ptr Pwr_SINextDev+2, es
        mov     bx, offset Bios_Data:Pwr_InstData
        push    ds
        pop     es

        pop     ds
        iret

;
; M100 - end
;
I2f_idle endp

;************************ I28_IDLE *********************

PUBLIC I28_idle
I28_idle proc
        assume  ds:nothing, es:nothing

        push    ds
        mov     ds,Bios_Data_Word
        assume  ds:Bios_Data

        inc     [I28].PCOUNT

        test    [CONTROL].IDLE_FLG,DOSYIELD_ACTIVE
        jz      i28ret

                ; load up registers and call general delay checker
        push    cx
        push    bx
        push    si
        push    di
        lea     bx,I28_TMR0
        lea     si,I28
        lea     di,[INFO].DOS_TOT
        call    Chk_Delay
        pop     di
        pop     si
        pop     bx
        pop     cx

i28ret:

IFDEF   POWERALONE                      ; chain to next
        jmp     dword ptr pwr_i28_next
ELSE
        pop     ds
        assume  ds:nothing
        iret                            ; don't need to chain if BIOS resident
ENDIF

I28_idle endp

;********************* Shell idle check ******************************
; purpose: check idle
;          if interrupt is detected than DO_IDLE should be called
;          the interrupt should be absorbed.
; CAVEAT: This interrupt should never be used, is version bound and subject
; to change in future versions.

PUBLIC I2A_idle
I2A_idle proc   far
        assume  ds:nothing, es:nothing

        push    ds
        mov     ds, Bios_Data_Word
        assume  ds:Bios_Data

        cmp     ah,84h
        jne     i2Anxt                  ; check for a service call to us

; load up registers and call do idle
        test    [CONTROL].IDLE_FLG,DOSIDLE_ACTIVE ; Make sure power is on
        jz      i2Anxt                  ; Power is off so just chain
        push    di
        lea     di,[INFO].SHELL_TOT
;;      call    DO_IDLE                 ; M102
ifdef __TONYI__
        jmp     MergePoint1             ; D1198
else
        call    I2fIdleEntry            ; M102

        adc     word ptr [di],0         ; accumulate idle time
        adc     word ptr [di]+2,0
        pop     di
        pop     ds
        iret
endif

i2Anxt:
        jmp     dword ptr pwr_i2a_next

I2A_idle endp
;************************ I16_IDLE *********************
; Our Int16 handler
; PseudoCode:
;       if (reentering) jmp to prev.handler;
;       if (fn NOT keyboard_poll calls or
;               NOT blocked get key calls)
;               jmp to prev.handler
;       if (fn == keyboard poll call) {
;               call the prev. handler
;               if key_waiting, ret to caller.
;               call chk_delay for possible idles;
;               ret to caller.
;       }
;       if (fn == blocked get key call) {
;               while (no key_hit)
;                       call do_idle    // directly trigger idles
;       }
;

PUBLIC I16_IDLE
I16_IDLE proc far
        assume  ds:nothing, es:nothing

        push    ds
        mov     ds,Bios_Data_Word
        assume  ds:Bios_Data


        cmp     [IN_KYC],0              ; reentering ?
        jne     kybxxx

; check for keyboard poll calls
        cmp     ah,01h
        je      kbpchk
        cmp     ah,11h
        je      kbpchk
;
; check for Blocked get_key calls
        or      ah,ah
        jz      kbChkIdle
        cmp     ah,10h
        jz      kbChkIdle
kybxxx:
        jmp     dword ptr pwr_i16_next  ; chain to prev handler

kybClrTmr:
        push    ax
        push    bx
        lea     bx,KYC_TMR0
        call    ClearTimer
        pop     bx
        pop     ax
        jmp     short kybxxx


kbpchk: inc     [KYC].PCOUNT
        test    [CONTROL].IDLE_FLG,KYC_ACTIVE
        jz      kybClrTmr

        mov     [IN_KYC],1              ; ONLY ALLOW ONE RE-ENTRY

        jmp     dword ptr pwr_call_i16  ;call old int 16h handler
;
;We return to the label below after calling the previous int 16h handler
;
calli16ret:
        mov     ds,Bios_Data_Word               ;reinit DS to Bios_Data
        assume  ds:Bios_Data

        pushf
        push    bx
        lea     bx,KYC_TMR0

                ; if a key is ready, just clear out our timer and
                ; return.  If no key is ready, go through the full
                ; delay check.  Here we check the flags returned
                ; by the INT 16 handler we called.

        jz      i16_ChkDelay

                ; Just clear the timer
        push    ax
        call    ClearTimer
        pop     ax
        jmp     short kbpret

i16_ChkDelay:
                ; load up registers and call delay check
        push    cx
        push    si
        push    di
        lea     si,KYC
        lea     di,[INFO].KEY_TOT
        mov     cx,1
        call    Chk_i16idle
        pop     di
        pop     si
        pop     cx

kbpret:
        pop     bx
        popf

        mov     [IN_KYC],0              ; can start up again

        pop     ds
        assume  ds:nothing
        ret     2

; Blocked get_key calls
; see if key is available ; if not, just do idle directly
;
kbChkIdle:
        assume  ds:Bios_Data
; see if there is a key available
        inc     ah                      ; make it to fn 1 or 11
kbChkKey:
        push    ax                      ; save function no (1 or 11h)
        jmp     dword ptr kb_call_i16  ;call old int 16h handler
kbChkRet:
        pop     ax                      ; get back orig. polling fn no
        jz      kbDoIdle                ; no key available
        dec     ah                      ; get back original get key fn(0 or 10)
        jmp     kybxxx                  ; go get the key
; no key available do idle
kbDoIdle:
        push    ax
        push    bx
        push    di
        lea     bx,KYC_TMR0
        lea     di,[INFO].KEY_TOT
;       call    DO_IDLE
        call    I2fIdleEntry            ; JAH4
ifdef __TONYI__
; Saves a byte....
        mov     ax, 0                   ; Warning: No XOR here!!! ; D1198
        adc     [di], ax                ; accumulate idle time    ; D1198
        adc     [di]+2, ax                                        ; D1198
        mov     [bx]+2, ax              ; avoid speed up error    ; D1198
else
        adc     word ptr [di],0         ; accumulate idle time
        adc     word ptr [di]+2,0
        mov     word ptr [bx]+2,0       ; avoid speed up error
endif
        call    ClearTimer
        pop     di
        pop     bx
        pop     ax
        jmp     short kbChkKey          ; go look for key

I16_IDLE endp


;************************ I08_IdleControl *********************
; POWER's main Timing/CONTROL interrupt
; PseudoCode:
;       check for rollover flag
;       if (rollover)
;               set our local date_update flag
;               do an int 1a to eat the rollover flag // in case?
;       if (time_update_counter is 0)
;               set our local date_update_flag  // if in POWER STD mode
;       if (SW idle detection enabled)
;       {
;               update Timer counters for i16,i28
;               update idle statistics counters
;               if (AUTO active && time_to_switch strategies)
;                       check i16 , i28 counts for the last period
;                       enable the int that has the max count and
;                         disable the other one.
;
;       }
;       if (FW idle detection enabled)          // APM enabled
;       {
;               if (time to poll APM) {
;                       increment INDOS flag
;                       switch stacks to our local stack
;                       call Do_APM_POLL  // poll APM for a pm event
;                                       // if there is, broadcast it
;                                       // and take necessary action
;                       switch back to old stack
;                       decrement INDOS flag
;               }
;       }
;       RET;
;

PUBLIC I08_IdleControl
I08_IdleControl proc    far
        assume  ds:nothing, es:nothing

        push    ds
        push    ax
        mov     ax,BIOSDATASEG          ; M092 check rollover flag in BIOS
        mov     ds,ax                   ; M092
        xor     al,al                   ; M096
        xchg    al,ds:[ROLLOVERFLG]     ; M096 get Bios rollover flg and
                                        ;   reset it
        mov     ds,Bios_Data_Word
        assume  ds:Bios_Data

;
; M107 - begin
;
        or      al, al
        jz      i08Skip1a
        or      [CMOSUpdFlg],al         ; M092 set update flag if rollover
        push    cx
        push    dx
        xor     ah, ah
        int     1ah
        pop     dx
        pop     cx
i08Skip1a:
;
; M107 - end
;
        dec     [CMOSPollCount]         ; M090
        jz      i08SetCMOSFlg           ; M090

i08NoTmUpdate:
        inc     [SPDUP_CNT]             ; speedup delay

        test    [POWER_STATUS],SW_IDLE_ACTIVE        ; is idle detection on ?
        jz      i08_to_end

        inc     [KYC_TMR0]+2            ; timer over flow
        inc     [KYC].DELAY             ; adjust delay

        inc     [I28_TMR0]+2            ; timer over flow
        inc     [I28].DELAY             ; adjust delay


ifdef __TONYI__
        xor     ax,ax                                   ; D1198
        add     word ptr [INFO].CPU_ON_TIME,1           ; D1198
        adc     word ptr [INFO].CPU_ON_TIME+2, ax       ; D1198
else
        add     word ptr [INFO].CPU_ON_TIME,1
        adc     word ptr [INFO].CPU_ON_TIME+2,0

        xor     ax,ax                   ; M004  BEGIN
endif
        xchg    [IDLTIC],ax             ; If we were idle during the last
        add     word ptr [INFO].CPU_IDLE_TIME,ax        ; tick period, let's
        adc     word ptr [INFO].CPU_IDLE_TIME+2,0       ; count it
                                        ; M004 END

        test    [CONTROL].IDLE_FLG,AUTO_ACTIVE  ; no AUTO adjust
i08_to_end:
        jz      i08xxx

i08ot1: inc     [SWITCH_CNT]            ; time to switch methods?
        mov     ax,[CONTROL].SWITCH_DLY
        cmp     [SWITCH_CNT],ax
        jb      i08xxx                  ; not yet ...

i08ot2: mov     [SWITCH_CNT],0
        mov     ax,[I28].PCOUNT         ; If( KYC.COUNT < I28.COUNT)
        cmp     ax,[KYC].PCOUNT         ; occurs in windows ...
        ja      i08dos

                ; keyboard count is higher than INT 28 count
                ; keyboard is highest count, it wins

        cmp     [KYC].PCOUNT,0          ; avoid thrashing
        je      i08xxx
        mov     ax,KYC_ACTIVE
        jmp     short i08set

i08SetCMOSFlg:
        mov     al,[CMOSFlg]            ; M090 ; need to update from CMOS
        or      [CMOSUpdFlg],al         ; M090 ; only if POWER STD mode in APM
        mov     ax,MAXCMOSPOLLCOUNT     ; machines
        mov     [CMOSPollCount],ax      ; start counting again
        jmp     i08NoTmUpdate

i08dos:         ; INT 28 count higher than keyboard count
                ; INT 28 count is highest, it wins
        or      ax,ax                   ; don't bother if count is 0
        jz      i08xxx
        mov     ax,DOSYIELD_ACTIVE

i08set: and     [CONTROL].IDLE_FLG,NOT ( DOSYIELD_ACTIVE+KYC_ACTIVE)
        or      [CONTROL].IDLE_FLG,ax

i08skp: xor     ax,ax                   ;reset for next round
        mov     [KYC].PCOUNT,ax
        mov     [I28].PCOUNT,ax

i08xxx:
        test    [POWER_STATUS],FW_IDLE_ACTIVE        ; M089 is F/W mgmt on ?
;       jz      i08ret                ; M089 No need to poll APM in POWER
        jnz     is_apm_enabled
        jmp     i08ret

IFDEF INCL_APM
; now check to see if we need to call APM for a PMEVENT

is_apm_enabled:
        cmp     fAPM_STATE,0            ; is APM enabled ?
        jne     set_power_proc          ;                       kam006
        jmp     i08ret                  ;                       kam006

set_power_proc:
        cmp     SETPOWER_PROC,01h       ; Are we to process the standby/suspend
                                        ; request with SetPowerState call or
                                        ; do APM polling        kam006
        jnz     do_apmpolling           ; go do the polling     kam006

        inc     SETPOWER_POLLCOUNT      ;                       kam006 kam019
        cmp     SETPOWER_POLLCOUNT,01h  ; first time            kam012
        je      issue_setpower_proc     ; issue set power state kam019
        cmp     SETPOWER_POLLCOUNT,91h  ; five seconds          kam019
        jne     i08ret                  ;                       kam019
        mov     SETPOWER_POLLCOUNT,01h  ; initialize counter to 1

issue_setpower_proc:

        push    cx
        push    bx
        mov     cx,LAST_REQ_PROC        ; required REQ PROC mode in CX (0004) ; kam006
        mov     bx,APM_SYSTEM_DEV       ; full system (BX = 0001)             ; kam006
        mov     ax,APM_SETPWSTATE_FUNC  ; Set Power State (AX = 5307h)        ; kam006
        int     15h                     ;                       kam006
        pop     bx
        pop     cx

;       xor     bx,bx                   ;                       kam024
        jmp     short i08ret            ;                       kam019

do_apmpolling:                          ;                       kam006
        dec     APM_POLL_COUNT
        jnz     i08ret
;
; NSM5 - BEGIN
        mov     ax,[APM_MAX_POLLCOUNT]  ; first reset the counter
        mov     APM_POLL_COUNT,ax

; time to poll APM for a PM Event
; First check to see if we are reentering our code
; if so, just quit;
; else, change stacks and poll APM
;
        cli
        cmp     StackInUse,0            ; See if called recursively
        jne     i08ret
;
; change stacks
;
        inc     StackInUse              ; Show stack is being used
        mov     Old_SS,SS               ; Save old SS:SP in bios data area
        mov     Old_SP,SP

        mov     SS,Bios_Data_Word
        mov     SP,OFFSET StackTop
;
; NSM4 - set indos flag so that we won't be swapped while doing our
; baseline calculation
        push    ds
        push    si                      ; NSM4
ifdef __TONYI__
        .errnz  (INDOS_Seg - INDOS_Offset - 2)  ; D1198
        lds     si, dword ptr INDOS_Offset      ; D1198
else
        mov     si,[INDOS_Offset]
        mov     ds,[INDOS_Seg]
endif
        assume ds:nothing
        inc     byte ptr ds:[si]
        pop     si
        pop     ds
        assume  ds:Bios_Data
; NSM4 -END
        sti
;
        cmp     word ptr ds:APM_VER,0101h; Is APM 1.1 supported ? kam002
        jnz     poll10                  ;                         kam002
        call    far ptr DoAPMPoll11     ; poll APM for an 1.1 event,
                                        ; broadcast it            kam002
        jmp     short procees           ;                         kam002
poll10:                                 ;                         kam002
        call    DoAPMPoll               ; poll APM for an 1.0 event,
                                        ; broadcast it
procees:                                ;                         kam002
;
        cli
; NSM4 - reset indos flag ; we are safe now and can be swapped out
        push    ds
        push    si
ifdef __TONYI__
        .errnz  (INDOS_Seg - INDOS_Offset - 2)  ; D1198
        lds     si, dword ptr INDOS_Offset      ; D1198
else
        mov     si,[INDOS_Offset]
        mov     ds,[INDOS_Seg]
endif
        assume ds:nothing
        dec     byte ptr ds:[si]
        pop     si
        pop     ds
        assume  ds:Bios_Data
; NSM4 -END
; restore stack
        mov     SS,Old_SS       ; Get entry stack from bios data area
        mov     SP,Old_SP
        mov     StackInUse,0    ; Clear value to show not being used.

; NSM5 -END
;
ENDIF                                   ; ifdef INCL_APM
i08ret:
        pop     ax
        pop     ds
        ret

I08_IdleControl endp

; ******************************** DoAPMPoll ***********************
; Poll APM for a PM event
; If there is an event broadcast it
; if the event == request for suspend/stand-by
;       look for rejection by an app
;       if no rejection, call APM back to switch to req. mode.
; if event == resume
;       Update system date and time from CMOS
;
IFDEF INCL_APM

DoAPMPoll       proc    near

; time to poll APM for any PM Event

        push    bx
;
DAPPoll:
        mov     ax,APM_GETPMEVENT_FUNC
        int     15h
        jc      DAP_End                 ; no events -> no work to do
;
; BX = PM Event
;       1 = stand-by request
;       2 = suspend request
;       3 = normal resume
;       4 = crit. resume
;       5 = battery low
        cmp     bx,APM_NORM_RESUME
        je      DAP_Set_time
        cmp     bx,APM_CRIT_RESUME
        jne     DAP_broadcast

DAP_Set_time:
        INC     APM_RESUME_COUNT
        call    Do_APM_CPUBUSY          ; M002 bring CPU to full speed
        mov     [CMOSUpdFlg],1          ; M090

DAP_broadcast:
        mov     ax,I2F_APM_BROADCAST
        int     2fh                     ; broadcast the pm events to other
                                        ; apps
; BUGBUG: Warning: the function codes for APM BIOS GetPMEvent and
; the multiplex API broadcast functions are assumed to be the same
        cmp     bl,APM_NORM_RESUME      ; was it a stand-by/suspend request
        jae     DAP_End                 ; if not, nothing else to do
;
; this was an APM request for system stand-by/suspend
; if no apps/tsrs objected to this request, let us call the APM to suspend/
; stand-by
        or      bh,bh
        jnz     DAP_End                 ; somebody objected ? if so quit
        push    cx                      ; M093
        mov     cx,bx                   ; required power mode in CX
        mov     bx,APM_SYSTEM_DEV       ; full system
        mov     ax,APM_SETPWSTATE_FUNC
        int     15h
        pop     cx                      ; M093
IFDEF DEBUG
        jnc     DAP_dbg_Success
        dbg_printchar   'f'             ; CY for a set power state call
DAP_dbg_Success:
ENDIF                                   ; IFDEF DEBUG
; Resumed from a suspend/stand-by; we should get a resume notification
; from APM so that we can go and update the date and time
        jmp     DAPPoll                 ; go back and poll APM for
                                        ; a resume notification event
;
DAP_End:pop     bx
        ret

DoAPMPoll       endp
ENDIF



;************* I6C_RESUME *****************************
;
; On interrupts 6C, update system date and time from the hardware
; clock.  Int 6C is one way a system may signal a resume event.
; Entire routine is part of modification M080.

I6C_Resume      proc

        assume  ds:nothing,es:nothing

        push    ds
        mov     ds,cs:Bios_Data_Word
        assume ds:Bios_Data

        push    ax
        push    bx
        push    cx
        push    dx
        push    si
        push    di

IFDEF   POWERALONE                      ; M081
        call    far ptr P_UpdFromCMOS
ELSE
        call    P_UpdFromCMOS_ptr       ; M074 update our date and time
                                        ; from CMOS RTC
ENDIF

        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax

        jmp     dword ptr pwr_i6c_next

I6C_Resume      endp

;************* APPLICATION ACTIVITY MONITORS ***********
; ISRs for the application activity monitor interrupts that we hook and
; related procedures
;
;************************** APP_SPDUP *******************************
; called by all our activity monitoring interrupt handlers
; SPEED UP by setting DOIdle delay count relitive to TIMER interrupts
; that have occurred between polling interrupt ...
; ** REQUIRES TESTING -- At this point APP_SPDUP tends to inhibit idle

PUBLIC APP_SPDUP
App_Spdup proc near
        assume  ds:Bios_Data, es:nothing

        push    ax
        mov     ax,[SPDUP_DLY]          ; machine speed dependent
        add     ax,[CONTROL].SPDUP_RAMP
        cmp     ax,[CONTROL].SPDUP_MAX  ; machine speed dependent
        jb      asdot3
        mov     ax,[CONTROL].SPDUP_MAX
asdot3: mov     [SPDUP],1               ; indicate activity
        mov     [SPDUP_DLY],ax
        mov     [SPDUP_CNT],0
        pop     ax
asdex0: ret

App_Spdup endp

;
; Hardware Keyboard interrupt - called for each key press twice
;
I9_App  proc
        assume  ds:nothing, es:nothing

        push    ds
        mov     ds,Bios_Data_Word
        assume  ds:Bios_Data

        dec     [I9_COUNT]
        jnz     i9ex0
        mov     [I9_COUNT],2
        call    APP_SPDUP
i9ex0:
        jmp     dword ptr Pwr_i9_next

I9_App  endp

; BIOS screen I/O interrupt
I10_App         proc
        assume  ds:nothing, es:nothing

        push    ds
        mov     ds,Bios_Data_Word
        assume  ds:Bios_Data

        cmp     ah,04h                          ; avoid cursor set
        jb      i10ex0                          ; calls
        call    APP_SPDUP
i10ex0:
        jmp     dword ptr Pwr_i10_next

I10_App         endp

;
; DISK I/O BIOS interrupt
;
I13_App         proc
        assume  ds:nothing, es:nothing

        push    ds
        mov     ds,Bios_Data_Word
        assume  ds:Bios_Data

        call    APP_SPDUP

        jmp     dword ptr Pwr_i13_next

I13_App         endp

;
; Communication I/O BIOS interrupt
;
I14_App         proc
        assume  ds:nothing, es:nothing

        push    ds
        mov     ds,Bios_Data_Word
        assume  ds:Bios_Data

        cmp     ah,3                            ; avoid status calls
        je      i14ex0
        call    APP_SPDUP                       ; M002
i14ex0:
        jmp     dword ptr Pwr_i14_next

I14_App         endp

; Printer I/O BIOS interrupt
;
I17_App         proc
        assume  ds:nothing, es:nothing

        push    ds
        mov     ds,Bios_Data_Word
        assume  ds:Bios_Data

        cmp     ah,2                            ; avoid status calls
        je      i17ex0
        call    APP_SPDUP
i17ex0:
        jmp     dword ptr Pwr_i17_next

I17_App         endp

;
; DOS system calls
;
I21_App         proc
        assume  ds:nothing, es:nothing

        push    ds
        mov     ds,Bios_Data_Word
        assume  ds:Bios_Data

        push    ax

; upper bound check
        cmp     ah,MAX_I21_ENTRY        ; anything above speeds up
        ja      i21SpdUp

; get action from the lookup table
        push    bx
        mov     bx,offset i21_table             ; look up table for APIs
        xchg    ah,al                   ; get function code  in al
        xlat
        pop     bx
        or      al,al                   ; a busy call ?
        jz      i21SpdUp
        inc     al                      ; idle calls ?
        jz      i21ex0

; special cases
        cmp     ax,644h                 ; ioctl get input status call ?
        je      i21ex0

        cmp     al,4bh                  ; is it an exec call ?
        je      i21GetNewBL             ; go get a new base line for int 16s

        cmp     ax,505fh                ; is it network assign call ?
        je      i21ex0                  ; ignore it

; only one special case remains now - the direct console io (fn 6) now
; if dl = ff it is an input call and otherwise output call

        cmp     dl,0ffh                 ; Direct console input ?
        je      i21ex0

; fall through for other special cases - should be changed if we add
; other special cases
i21SpdUp:
        call    APP_SPDUP
i21ex0:
        pop     ax
        jmp     dword ptr Pwr_i21_next

i21GetNewBL:
; Reset I2f Apps's PSP so that the child is not marked as an INT 2f aware App
        mov     PSPOf2fApp, 0           ; M099
;
; IF we are within Win ENH mode, no need to adjust BaseLine again
;
        cmp     [NoBaseLineCalc],0      ; NSM2 do we need to recalc baselinemax?
        jne     i21SpdUp                ; NSM2 no, just do SpeedUp
;
        call    GetNewBaseLine
        jmp     short i21SpdUp

I21_App         endp

; ABSOLUTE DISK READ/WRITE interrupts
;
I25_App         proc
        assume  ds:nothing, es:nothing

        push    ds
        mov     ds,Bios_Data_Word
        assume  ds:Bios_Data

        call    APP_SPDUP

        jmp     dword ptr Pwr_i25_next

I25_App         endp

I26_App         proc
        assume  ds:nothing, es:nothing

        push    ds
        mov     ds,Bios_Data_Word
        assume  ds:Bios_Data

        call    APP_SPDUP

        jmp     dword ptr Pwr_i26_next

I26_App         endp

; ****************************** GetNewBaseLine ***************************
; Called at each exec time  (except within win enh mode)
; calculates BaseLineRef for Int 16 idles by:
;       pumping 9 int16s in a tight loop
;       throwing the first and last 2 int16 intervals
;       throwing the highest and lowest 2 int16 intervals
;       and adding up the remaining five int16 intervals
;       using this added value as the New BaseLineRef.
;
; NOTE: stack is switched to our local stack and INDOS flag is set during
;       this operation so that task swapper may not swap us out and to
;       avoid stack overruns in case of certain apps.
;
        PUBLIC GetNewBaseLine           ; JAH2 - made public for debugging
GetNewBaseLine  proc    near

                ; JAH2 - Setup new stack to fix problems with small exec stack


        pushf
        push    si                      ; NSM4
        cli
        cmp     StackInUse,0            ; See if called recursively
        je      SetupStack
        jmp     NewBaseLineExit         ; Stack is being used so don't recalc
SetupStack:
        inc     StackInUse              ; Show stack is being used
        mov     Old_SS,SS               ; Save old SS:SP in bios data area
        mov     Old_SP,SP

        mov     SS,Bios_Data_Word
        mov     SP,OFFSET StackTop
; NSM4 - set indos flag so that we won't be swapped while doing our
; baseline calculation
        push    ds
ifdef __TONYI__
        .errnz  (INDOS_Seg - INDOS_Offset - 2)  ; D1198
        lds     si, dword ptr INDOS_Offset      ; D1198
else
        mov     si,[INDOS_Offset]
        mov     ds,[INDOS_Seg]
endif
        assume ds:nothing
        inc     byte ptr ds:[si]
        pop     ds
        assume  ds:Bios_Data
; NSM4 -END
        sti
                ; JAH2 - End change
; SMR7 BEGIN - BaseLine calculation based on a few samples
;
; Save all regs we use
;
        push    ax
        push    bx
        push    cx
        push    dx
        push    si
        push    di

        mov     si, offset BL_Samples
        push    si
        xor     di, di                          ; Cumulative sum=0
;
; Pump in INT 16s and till we get 9 samples
;  & accumulate the total period for 9 samples in DI
;

        mov     ah, 1
        int     16h
        mov     bx, [KYC_TMR0]                  ; Our INT 16 handler would
                                                ;  have read TMR0
bl_loop1:
        mov     ah, 1
        int     16h
        sub     bx, [KYC_TMR0]                  ; Get period for this sample
        jc      bl_ThrowSample                  ; ignore it in case of overflow
        mov     [si], bx                        ; else stow it away
        add     di, bx                          ;  and it to the total
        inc     si                              ; bump to next sample
        inc     si
bl_ThrowSample:
        mov     bx, [KYC_TMR0]                  ; remember current time stamp
        cmp     si, offset BL_EndSamples        ; have we got 9 samples ?
        jb      bl_loop1                        ; no, try again
;
;
; Throw off the 2 highest & lowest samples and sum up the rest 5
;
; always ax,bx contain the highest 2 values so far (such that ax < bx)
;  & cx,dx contain the lowest 2 values so far (such that cx > dx)
; si contains the ptr to the current element
;
;
        pop     si

        xor     ax, ax                          ; 2 MAXs = 0
        mov     bx, ax
        mov     cx, ax
        dec     cx
        mov     dx, cx                          ; 2 MINs = ffffh
        push    di                              ; save the total
bl_loop2:
        mov     di, [si]                        ; get sample

        cmp     ax, di                          ; > lo Max ?
        jae     bl_1                            ; no, chk against hi max
        mov     ax, di                          ; yes, update least max
        jmp     short bl_2
bl_1:
        cmp     bx, di                          ; > hi max ?
        jae     bl_2                            ; no
        mov     bx, di                          ; update hi max
bl_2:
        cmp     ax, bx                          ; is hi max < lo max
        jb      bl_MaxOk                        ; no
        xchg    ax, bx                          ; make hi max > lo max

bl_MaxOk:
        cmp     cx, di
        jbe     bl_3
        mov     cx, di
        jmp     short bl_4
bl_3:
        cmp     dx, di
        jbe     bl_4
        mov     dx, di
bl_4:
        cmp     cx, dx
        ja      bl_MinOk
        xchg    cx, dx
bl_MinOk:
        inc     si
        inc     si
        cmp     si, offset BL_EndSamples
        jb      bl_loop2
;
        pop     di
        sub     di, ax
        sub     di, bx
        sub     di, cx
        sub     di, dx

        mov     [BaseLineOvf], di       ; this is the new calculated
                                        ; BaseLine ref value for REG mode

; NSM9 - set baelineref depending on the current savings mode value
        call    SetSavingsValues        ; set the correct BaseLineRef depending
                                        ; on the current Savings mode
; NSM9 END

        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
;
; SMR7 END
;
                ; JAH2 - Restore stack and AX pushed at entry to function
        cli                     ; Popf at end will reset interrupt enable
; NSM4 - reset indos flag ; we are safe now and can be swapped out
        push    ds
ifdef __TONYI__
        .errnz  (INDOS_Seg - INDOS_Offset - 2)          ; D1198
        lds     si, dword ptr INDOS_Offset              ; D1198
else
        mov     si,[INDOS_Offset]
        mov     ds,[INDOS_Seg]
endif
        assume ds:nothing
        dec     byte ptr ds:[si]
        pop     ds
        assume  ds:Bios_Data
; NSM4 -END
        mov     SS,Old_SS       ; Get entry stack from bios data area
        mov     SP,Old_SP
        mov     StackInUse,0    ; Clear value to show not being used.
NewBaseLineExit:
        pop     si              ; NSM4
        popf
                ; JAH2 - End

        ret

GetNewBaseLine  endp

; NSM9 - BEGIN - 8 levels of savings values/noise spreads
; *******************************************************************
; SetSavingsValues
; Entry:        none    (BaseLineOvf and Savings_mode set)
; Exit:         Current BaseLineRef set
;               Current Noise set
;               Current AvgSpread Set
;
; Regs affected: AX
;
; Uses the shift-right count in BaseLine_Table to shift out the current
; calculated baselineref to arrive at the BaseLineRef for the currently
; selected Savings level.
; also sets NOISE/SPREAD values
;
;       called by GetNewBaseLine at each EXEC time to set proper values
;               for BaseLineRef, NOISE, SPREAD etc for I16.
;
SetSavingsValues        proc    near
        push    cx
        push    bx
        xor     cx,cx
        lea     bx,BaseLine_Table
        mov     ax,[Savings_Mode]
        cmp     ax,REGSAVINGSMODE       ; is it REG ?
        xlat
        mov     cl,al
        mov     ax,[BaseLineOvf]
        mov     bx,[BaseLineOvf]
        jae     SSV_AddLoop
        xor     bx,bx                   ; start with 0% for all other modes
                                        ; below REG
SSV_AddLoop:
        jcxz    SSV_SetBLRef
        shr     cl,1                    ;
        jnc     SSV_HalveBL
        add     bx,ax
SSV_HalveBL:
        shr     ax,1
        Jmp     SSV_AddLoop
; BX = has the new BaseLineRef
SSV_SetBLRef:
        mov     [BaseLineRef],bx        ; set new baseline Ref
        push    si
        lea     si,KYC
        call    CalcNewNoise
        mov     [si].NOISE,ax
        call    CalcNewSpread
        mov     [si].PSPREAD,ax
        pop     si
        pop     cx
        pop     bx
        ret

SetSavingsValues        endp

; *************************** CalcNewNoise/Spread ********************
;
; Entry:        [Savings_Mode]  set
;               si = Period structure for i16 or i28
; Exit:         ax = New Noise (or Spread Value) depending on the current
;               savings_mode
;
; Regs affected: AX
;
; EntryPoint:   CalcNewSpread
;
; Uses the shift-right count in  Savings_Table to shift out current BASE/AVG
; to arrive at the correct NOISE/SPREAD for the currently selected savings
; value.

CalcNewNoise    proc    near
        mov     ax,[si].BASE
        jmp     short   CNN_CalcPC

CalcNewSpread   label   near
        mov     ax,[si].PERIOD          ; take current avg
; ax = current Base /AVG
CNN_CalcPC:
        push    bx
        push    cx
        mov     cx,ax
        mov     ax,[Savings_Mode]
        dec     al                      ; 0 based table
        lea     bx,Savings_Table
        xlat
        xchg    ax,cx           ; cl = mode bits and ax = BASE/AVG
        xor     bx,bx
CNN_AddLoop:
        or      cl,cl
        jz      CNN_RetValue
        shr     ax,1
        shr     cl,1
        jnc     CNN_AddLoop
        add     bx,ax
        jmp     CNN_AddLoop
CNN_RetValue:
        or      bx,bx
        jz      CNN_Ret
        mov     ax,bx           ; ax = new value for NOISE/SPREAD
CNN_Ret:
        pop     cx
        pop     bx
        ret

CalcNewNoise    endp

; NSM9 END

IFDEF INCL_APM

; *********************************** Check_APMBIOS_Ver***************

public  Check_APMBIOS_Ver

Check_APMBIOS_Ver       proc    far

        push    ax
        push    bx
        push    cx

        mov     ax,APM_INSTALLCHK_FUNC
        mov     bx,APM_SYSTEM_BIOS
        int     15h                     ; Check for APM BIOS presence

; CY set if no APM present
        jc      noAPM
        cmp     bx,APM_SIGNATURE
        jne     noAPM
        mov     APM_FLAGS,cx            ; store the APM flags
        mov     APM_VER,ax              ; store APM BIOS ver no
        mov     POW_MAJ_VERSION,ah      ; store APM BIOS major ver no
        mov     POW_MIN_VERSION,al      ; store APM BIOS major ver no
noAPM:
        pop     cx
        pop     bx
        pop     ax
        ret

Check_APMBIOS_Ver       ENDP

; ********************************************************************


; *********************************** Check_and_Init_APM ***************
; This procedure is called at init time ; checks for the presence of
; APM BIOS , if present connects to it (real mode only).
; If the connection is successful, we set APM_PRESENT flag so that
; later we can make use of APM
;
; Input: none
; output: CY +NZ if NO APM
;         CY +ZR if cannot connect to it
;               or if APM cannot be enabled
;         NC = success (APM found+connected to APM+APM enabled state)
;       (fAPM_Present set to TRUE if APM present)
;       (APM_RESUME_COUNT initialised to 0)
;       (fAPM_CONNECT set to TRUE if connected to APM)
;       (fAPM_STATE set to TRUE if APM connected+enabled)
;               -> this is referred to before polling APM, before calling APM
;                       for idle
;
; regs affected: AX,BX,CX,DX,flags
;
; M001  : Created; APM support code

        public  Check_and_Init_APM

Check_and_Init_APM      proc    far

;       mov     ax,APM_INSTALLCHK_FUNC
;       mov     bx,APM_SYSTEM_BIOS
;       int     15h                     ; Check for APM BIOS presence
        cmp     APM_VER,0
; CY set if no APM present
        jz      CAIA_NoAPM

; NC; check for APM signature (BH = "P" and BL = "M")   ; M003
; AX = version no (in BCD)
; CX = APM flags        (bit 0 - 16bit prot.mode intrfc. supported
;                        bit 1 - 32bit "  "      "  "      "  "
;                        bit 2 - set if CPU_IDLE call slows proc.clock
;                        bit 3 - set if BIOS power management disabled.

;       cmp     bx,APM_SIGNATURE        ; M003
;       jne     CAIA_NoAPM
;       mov     APM_FLAGS,cx            ; store the APM flags
;       mov     APM_VER,ax              ; store APM BIOS ver no
;       mov     POW_MAJ_VERSION,ah      ; store APM BIOS major ver no
;       mov     POW_MIN_VERSION,al      ; store APM BIOS major ver no

        mov     fAPM_PRESENT,1          ; set APM flag  ; M002

; connect to APM
        call    Do_APM_Connect          ;M005
        jc      CAIA_APM_Err            ; cannot connect ? assume APM
                                        ; not present/functional

; Do APM Driver Version call
; This function allows to establish an APM 1.1 connection between the
; APM 1.1 BIOS and POWER.EXE if successful. Refer to APM 1.1 BIOS
; Interface Specification, Sept 1993 for more details.

; bug in HummingBird Enhanced, APM Driver Version call, INT 15h (AX=530E)
; reboots the system. Only issue this call if running on APM 1.1 BIOS.  kam007
        cmp     word ptr ds:APM_VER,0101h ; Are we running on APM 1.1 BIOS kam010
        jnz     chop11table             ; kam010

        mov     ax,APM_DRIVER_VERSION   ; (function 0Eh)         kam001
        mov     bx,APM_SYSTEM_BIOS      ; (0000h - APM BIOS)     kam001
        mov     ch,APM11_VER_MAJOR      ; APM version major      kam001
        mov     cl,APM11_VER_MINOR      ; APM version minor      kam001
        int     15h                     ; Do APM Driver Version  kam001

        jc      chop11table             ; if unsuccessful must be APM 1.0     kam001
        mov     APM_MAX_POLLCOUNT,18    ; 1 second polling req for APM 1.1    kam001
        mov     APM_POLL_COUNT,18       ; the bios timer tick count is        kam001
                                        ; incremented 18.2 times per second;  kam001
        jmp     short apmtest_continue
chop11table:                            ;                                     kam001
        mov     strtfn,0ffh             ;    kam010

                                        ; discard the INT 2Fh API support
                                        ; that are defined for POWER.EXE
                                        ; 1.01 since this stuff is not
                                        ; required if running on APM 1.0
apmtest_continue:
;
; check for APM BIOS disabled state
        test    APM_FLAGS,APM_PWMGMT_DISABLED
        jz      CAIA_APM_Success        ; APM in enabled state; all success
;
        mov     cx,APM_ENABLE_FUNC
        call    Do_APM_Enable_Disable   ;M005
        jnc     CAIA_APM_Success
        mov     fAPM_PRESENT,0          ; reset APM flag since we couldn't
                                                ; connect to it
        mov     fAPM_STATE,0            ; disabled state
        mov     APM_RESUME_COUNT,0      ; initialise RESUME_COUNT
        jmp     short CAIA_APM_Err

CAIA_APM_Success:
        mov     fAPM_STATE,1            ; APM in enabled state
        mov     APM_RESUME_COUNT,0      ; initialise RESUME_COUNT
        or      POWER_STATUS,FW_IDLE_ACTIVE     ; M003: indicate APM is enabled
CAIA_End:
        ret

CAIA_NoAPM:
        mov     al,1                    ; APM not there; CY + NZ
;       mov     PS_JmpTable+BEGIN_101TABLE,0ffh
        mov     strtfn,0ffh             ;    kam010
                                                            ;   kam010
                                        ; discard the INT 2Fh API support
                                        ; that are defined for POWER.EXE
                                        ; 1.01 since this stuff is not
                                        ; required on non APM system.
CAIA_FEnd:
        or      al,al                   ; set Zero flag if APM error
        stc
ifdef __TONYI__
        ret                             ; D1198
else
        jmp     short CAIA_End
endif
CAIA_APM_Err:
        xor     al,al                   ; APM is there but connect or some
        jmp     short CAIA_FEnd         ; other error => CY + Z

Check_and_Init_APM      endp

ENDIF   ; for INCL_APM conditional
; M001 END


Bios_Code       ends                    ; Bios Code segment end


;********************** INITIALIZE CODE **************************************


;********************************* DoAPMPoll11 *******************************
; DoAPMPoll11 ()
;  Poll APM for a 1.1 PM event
;  if event is normal resume or critical resume notification
;     update system date and time from CMOS
;     go to first broadcast
;  if event is update time notification or system standby resume
;     notification
;     update time from real time clock form CMOS
;     go to first broadcast
;  if event is power status change notification
;     call Get Power Status to determine the change to the power status
;     do the first broadcast
;  if event is critical system suspend notification
;     call Set Power State () to set system state to suspend without delay
;     do not broadcast
;  if event is system standby request or user system standby request
;     notification
;     do the first broadcast
;     call Set Power State () with Request Processing every 5 seconds
;     call Set Power State () to set system state to standby or reject
;     the standy.
;     go to second broadcast
;  if event is system suspend request or user system suspend request
;     notification
;     do the first broadcast
;     call Set Power State () with Request Processing every 5 seconds
;     call Set Power State () to set system state to suspend or reject
;     the suspend
;     go to second broadcast
;  first broadcast:
;     do first broadcast
;     look for rejection by an app
;     if no rejection, call APM back to switch to requested mode.
;  second broadcast:
;     do second broadcast for two phase protocol implementation

; BX = PM Event
;      01 = System Standy request
;      02 = System Suspend request
;      03 = Normal Resume
;      04 = Critical Resume
;      05 = Battery Low
; POWER.EXE driver will process the following power management
; events for APM 1.1 connection when a power management event occurs.
;      06 - Power Status Change Notification
;      07 - Update Time Notification
;      08 - Critical System Suspend Notification
;      09 - User System Standby Request Notification
;      0A - User System Suspend Request Notification
;      0B - System Standby Resume Notification
; NOTE:     The following event is currently not supported and needs
;           to be defined by the OEM.
;      0200 - 02FF - OEM defined APM Events

;IFDEF INCL_APM

Sysinitseg      segment
       assume  cs:Sysinitseg, ds:nothing, es:nothing

DoAPMPoll11       proc    far           ; kam002  kam010

        assume  ds:Bios_data            ; Everywhere this procedure is called
                                        ; DS points to Bios_data so it is
                                        ; safe to assume that here.
        push    bx                      ; time to poll APM for any PM Event kam002

DAPPoll11:
        mov     ax,APM_GETPMEVENT_FUNC  ; Get PM Event (530Bh)        ; kam002
        int     15h                                                   ; kam002
        jnc     proceed
        jmp     DAP_End11               ; no events                   ; kam002

proceed:
        mov     save_event_flag,bx      ; save PM event for implementing kam002
                                        ; two phase protocol          ; kam002

        cmp     bx,APM_NORM_RESUME      ; normal resume event?        ; kam002
        je      DAP_Set_time11          ; update system date and time from CMOS kam002

        cmp     bx,APM_CRIT_RESUME      ; critical resume event?
        je      DAP_Set_time11          ; update system date and time from CMOS kam002

        cmp     bx,APM_SYSTEM_STANDBY_RESUME ; system standby resume  ; kam002
        je      DAP_Set_time11          ; update system date and time from CMOS kam002

        cmp     bx,APM_UPDATE_TIME      ; update time notification ?  ; kam002
        jne     powstat_change                                        ; kam002

DAP_Set_time11:
        inc     APM_RESUME_COUNT        ; increment resume count      ; kam002 kam010
;       call    far ptr Do_APM_CPUBUSY  ; bring CPU to full speed     ; kam026

                                        ; Do we need to make a CPU_BUSY call ; kam026
        test    APM_FLAGS,APM_SLOW_CLOCK   ; does CPU_IDLE slows clock ? ; kam026
        jz      updflg                  ; kam026

; need to make CPU_BUSY call            ; kam026
        push    bx                      ; kam026
        mov     ax,APM_CPUBUSY_FUNC     ; kam026
        int     15h                     ; make CPU_BUSY call to speedup CPU ; kam026
        pop     bx                      ; kam026

updflg:                                 ; kam026
        mov     [CMOSUpdFlg],1          ;                             ; kam002 kam010
        jmp     short DAP_Fbroadcast11  ; do the broadcast            ; kam002

powstat_change:                                                       ; kam002
        cmp     bx,APM_POWER_STATUS_CHANGE ; power status change ?    ; kam002
        jne     crit_susp

        push    cx                                                    ; kam002
        push    bx                                                    ; kam002
        mov     bx,APM_SYSTEM_DEV       ; full system                 ; kam002
        mov     ax,APM_GETPWSTATUS_FUNC ; Get Power Status ( 530Ah)   ; kam002
        int     15h                                                   ; kam002
        pop     bx                                                    ; kam002
        pop     cx                                                    ; kam002
        jmp     short DAP_Fbroadcast11                                ; kam002

crit_susp:                                                            ; kam002
        cmp     bx,APM_CRITICAL_SYSTEM_SUSPEND ; critical suspend ?   ; kam002
        jne     standby_req

        push    cx                                                    ; kam002
        push    bx                                                    ; kam002
        mov     cx,SUSPEND                   ; required SUSPEND mode in CX
        mov     bx,APM_SYSTEM_DEV            ; full system
        mov     ax,APM_SETPWSTATE_FUNC       ; Set Power State (5307h)
        int     15h                                                   ; kam002
        pop     bx                                                    ; kam002
        pop     cx                                                    ; kam002
        jmp     short D_End11                ; do not broadcast,      ; kam002
                                             ; do the suspend and exit

standby_req:                                 ; standy request ?       ; kam002
        cmp     bx,APM_STANDBY_REQ                                    ; kam002
        je      set_standbyvar                                        ; kam013
        cmp     bx,APM_USER_SYSTEM_STANDBY_REQ ; user system standby ?; kam002
        je      set_standbyvar                                        ; kam013
        cmp     bx,APM_SUSPEND_REQ           ; suspend request ?      ; kam002
        je      set_suspendvar                                        ; kam013
        cmp     bx,APM_USER_SYSTEM_SUSPEND_REQ ; user system suspend ?; kam002
        je      set_suspendvar                                        ; kam013

D_End11:                                                              ; kam002
        jmp     short DAP_End11              ; No other event, exit   ; kam002
DAP_Fbroadcast11:
        jmp     short DAP_broadcast11   ; do the broadcast            ; kam002

set_standbyvar:                                                       ; kam013
        mov     STANDBY_OR_SUSPEND,0001h                              ; kam013
        jmp     short req_proc                                        ; kam013 kam019
set_suspendvar:
        mov     STANDBY_OR_SUSPEND,0002h
req_proc:
        mov     SETPOWER_PROC,01h            ; initialize set power   ; kam010
                                             ; processing flag to 1
        mov     ax,PM_EVENT_FIRST_PHASE_BROADCAST
        int     2fh                          ; Broadcast first SUSPEND_OK event

        mov     SETPOWER_PROC,00h            ; initialize flag to 0   ; kam010
        mov     SETPOWER_POLLCOUNT,00        ; initialize set power   ; kam010
                                             ; processing poll count to 0
        or      bh,bh
        jz      Second_Broadcast             ;
        push    cx                                                    ; kam025
        mov     cx,LAST_REQ_REJECT                                    ; kam025
        mov     bx,APM_SYSTEM_DEV       ; full system                 ; kam025
        mov     ax,APM_SETPWSTATE_FUNC  ; Set Power State (AX = 5307h); kam025
        int     15h                                                   ; kam025

        pop     cx                                                    ; kam025
        jmp     short DAP_End11              ;

Second_Broadcast:                            ; Second Broadcast for two
                                             ; phase protocol kam002
        mov     bx,save_event_flag                                    ; kam002
        mov     ax,I2F_APM_BROADCAST                                  ; kam002
        int     2fh                                                   ; kam002

        or      bh,bh                                                 ; kam002
        jz      req_accepted                 ;
        mov     ax,save_event_flag                                    ; kam002
        cmp     ax,APM_STANDBY_REQ                                    ; kam025
        je      broadcast_standbyresume                               ; kam025
        cmp     ax,APM_USER_SYSTEM_STANDBY_REQ ; user system standby ?; kam025
        jne     broadcast_normalresume                                ; kam025
broadcast_standbyresume:                                              ; kam025
        mov     bx,APM_SYSTEM_STANDBY_RESUME ; send a system standby resume ; kam0025
        jmp     short DAP_broadcast11                                 ; kam025
broadcast_normalresume:                                               ; kam025
        mov     bx,APM_NORM_RESUME           ; send a normal resume event kam025
        jmp     short DAP_broadcast11

req_accepted:                                                         ; kam002
        push    cx
        mov     cx,STANDBY_OR_SUSPEND                                 ; kam013
        mov     bx,APM_SYSTEM_DEV       ; full system                 ; kam002
        mov     ax,APM_SETPWSTATE_FUNC  ; Set Power State (AX = 5307h); kam002
        int     15h                                                   ; kam002

        pop     cx
        jmp     DAPPoll11               ; go back and poll APM for    ; kam002
                                        ; a resume notification event

DAP_broadcast11:                                                      ; kam002
        mov     ax,I2F_APM_BROADCAST                                  ; kam002
        int     2fh                     ; broadcast the PM events to other apps

DAP_End11:
        pop     bx                                                  ; kam002
        mov     save_event_flag,0       ; clear event flag for next event  kam002
        ret                                                           ; kam002

DoAPMPoll11       endp                                                ; kam002


;****************************** Do_DevCommand*********************
;
; Entry:  BX = Power Device ID
;
; Exit:  CY if operation failed
;               ax = error code
;        ELSE AX = 0
;
;******************************************************************

Do_DevCommand  proc    far              ; kam003                   ; kam010

        assume  ds:Bios_data            ; Everywhere this procedure is called
                                        ; DS points to Bios_data so it is
                                        ; safe to assume that here.

;       mov     ax,ds:APMBIOS_CMD       ; INT 15h (53xxh)          ; kam005 kam010
        mov     ax,APMBIOS_CMD          ; INT 15h (53xxh)          ; kam005 kam010
        int     15h                     ;                          ; kam003
        cmp     ax,00h                  ;                          ; kam003
        jz      ind_success             ;                          ; kam003
        cmp     ah,01h                  ;                          ; kam003 kam018
        jnz     int_not_connec          ;                          ; kam003
        mov     ax,ERROR_PM_NOT_ENABLED ;                          ; kam021
        jmp     short set_carry         ;                          ; kam003
int_not_connec:                         ; interface not connected  ; kam003
        cmp     ah,03h                  ;                          ; kam003 kam018
        jnz     unrec_devid             ;                          ; kam003
        mov     ax,ERROR_PM_NOT_CONNECTED                          ; kam003
        jmp     short set_carry         ;                          ; kam003
unrec_devid:                            ; unrecognized device ID   ; kam003
        cmp     ah,09h                  ;                          ; kam003 kam018
        jnz     value_out_range         ;                          ; kam020
        mov     ax,ERROR_PM_INVALID_DEVICE_ID                      ; kam003
        jmp     short set_carry         ;                          ; kam003
value_out_range:                                                   ; kam020
        cmp     ah,0Ah                  ;                          ; kam020
        jnz     int_not_engaged         ;                          ; kam020
        mov     ax,ERROR_PM_INVALID_PARAMETER                      ; kam020
        jmp     short set_carry         ;                          ; kam020
int_not_engaged:                        ; interface not engaged    ; kam003
        cmp     ah,0Bh                  ;                          ; kam003 kam018
        jnz     set_carry               ;                          ; kam003
        mov     ax,ERROR_PM_NOT_ENGAGED ;                          ; kam003
set_carry:                                                         ; kam003
        stc                                                        ; kam003
        jmp     short dev_exit                                     ; kam003

ind_success:                                                       ; kam003
        cmp     APMBIOS_CMD,APM_SETPWSTATE_FUNC                    ; kam008 kam010
        jnz     indnotsetdevcmd                                    ; kam008
        mov     ds:SETDEV_FLG,01h                                  ; kam008
indnotsetdevcmd:                                                   ; kam003
        xor     ax,ax                                              ; kam003

dev_exit:                                                          ; kam003
        ret                                                        ; kam003

Do_DevCommand  endp                                                ; kam003 ; kam010


;****************************** GetDevPowStatus *******************
; Get the current device power status
;******************************************************************
GetDevPowStatus    proc    far          ; kam003   kam010

        assume  ds:Bios_data            ; Everywhere this procedure is called
                                        ; DS points to Bios_data so it is
                                        ; safe to assume that here.
;       mov     al,ds:POWER_STATUS      ; kam003  kam010
        mov     al,POWER_STATUS         ; kam003  kam010
        test    al,FW_IDLE_ACTIVE       ; test for H/W enabled state ; kam003
        jnz     GPSDev_Ret              ; kam003
IFDEF   INCL_APM                        ; kam003
;       test    ds:fAPM_STATE,1         ; kam003  kam010
        test    fAPM_STATE,1            ; kam003  kam010
        jz      @g                      ; kam003
        or      al,2                    ; H/W pow.mgmt enabled - POWER STD mode ; kam003
@g:                                     ; kam003
ENDIF                   ; IFDEF INCL_APM; kam003
        push    bx                      ; kam003
        mov     ax,APM_GETPWSTATE_FUNC  ; Get Power State (530Ch)   kam003
        int     15h                     ; kam003
        pop     bx                      ; kam003

GPSDev_Ret:                             ; kam003
        ret                             ; kam003
GetDevPowStatus    endp                 ; kam003  kam010

;ENDIF                                  ; kam002

;=============================================================================
; FUNCTION 4 - Get Device POWER state
;
; This function is same as defined in I0/I1 document as
; DoAPM_GetDevPWState()
; INT 2F AX=5404 API processing
;=============================================================================

IFDEF   POWERALONE
PS_GetDevPWState   proc    far          ; kam010
ELSE
PS_GetDevPWState   proc    near
ENDIF

        assume  ds:Bios_data                                   ; kam003

;       test    bh,DEVICEID_BITS;                              ; kam003
        cmp     bh,06H                                         ; kam016
        ja      test_for_oemid                                 ; kam016
        jmp     short valid_deviceid                           ; kam016
test_for_oemid:                                                ; kam016
        cmp     bh,0E0H                                        ; kam016
        jae     valid_oem_deviceid                             ; kam016 kam017
error_oemid:                                                   ; kam017
        mov     ax,ERROR_PM_INVALID_DEVICE_ID                  ; kam008
        jmp     short setcarry                                 ; kam008
valid_oem_deviceid:                                            ; kam017
        cmp     bh,0EFH                                        ; kam017
        ja      error_oemid                                    ; kam017
valid_deviceid:                                                ; kam014
        cmp     SETDEV_FLG,01h                                 ; kam008
        jnz     device_notset                                  ; kam008
        call    GetDevPowStatus                                ; kam003
        cmp     ah,01h                                         ; kam003
        jnz     get_next                                       ; kam003
        mov     ax,ERROR_PM_NOT_CONNECTED                      ; kam003
        jmp     short setcarry                                 ; kam008
get_next:                                                      ; kam003
        cmp     ah,09h                                         ; kam003
        jnz     get_ret                                        ; kam003
device_notset:                                                 ; kam008
        mov     ax,ERROR_PM_DEVIDNOTSET                        ; kam008
setcarry:                                                      ; kam008
        stc                                                    ; kam008
        ret                                                    ; kam008
get_ret:                                                       ; kam003
        xor     ax,ax           ; indicate success             ; kam003
        ret                                                    ; kam008

PS_GetDevPWState   endp                   ; kam010

;=============================================================================
; FUNCTION 5 - Set Device POWER state
;
; INT 2F AX=5405 API processing
;=============================================================================

IFDEF   POWERALONE
PS_SetDevPWState  proc   far                                   ; kam008  kam010
ELSE
PS_SetDevPWState  proc   near                                 ; kam008
ENDIF

        assume  ds:Bios_data                                   ; kam003

        mov     APMBIOS_CMD,APM_SETPWSTATE_FUNC                ; kam003
                                ; set power state (5307h)

PS_FUNC567:
;       test    bh,DEVICEID_BITS;                              ; kam003
        cmp     bh,06H                                         ; kam017
        ja      test_for_oemdevid                              ; kam017
        jmp     short valid_oemdevid                           ; kam017
test_for_oemdevid:                                             ; kam017
        cmp     bh,0E0H                                        ; kam017
        jae     valid_oemdevid                                 ; kam017
erroroemid:                                                    ; kam017
        mov     ax,ERROR_PM_INVALID_DEVICE_ID                  ; kam008
        stc                                                    ; kam008
        ret                                                    ; kam017
valid_oemdevid:                                                ; kam014
        cmp     bh,0EFH                                        ; kam017
        ja      erroroemid                                     ; kam017
        cmp     APMBIOS_CMD,APM_SETPWSTATE_FUNC                ; kam003
        jnz     func_6or7
        call    GetDevPowStatus ; old state in AL              ; kam003

func_6or7:
        push    cx                                             ; kam003
        test    POWER_STATUS,SW_IDLE_ACTIVE                    ; kam003
        jnz     PSDev_Test_APM_Bit ; already enabled           ; kam003
        or      POWER_STATUS,SW_IDLE_ACTIVE ; turn on S/W PM   ; kam003

PSDev_Test_APM_Bit:                                            ; kam003

        call    Do_DevCommand                                  ; kam003
        pop     cx                                             ; kam003
        ret

PS_SetDevPWState  endp                                         ; kam008 kam010


;=============================================================================
; FUNCTION 6 - Enable/Disable Device Power State
;
; This function is same as defined in I0/I1 document as
; DoAPM_Enable_Disable_DevPM ()
; INT 2F AX=5406 API processing
;=============================================================================

IFDEF   POWERALONE                                            ; kam010
PS_EnableDisableDevPWState      proc    far
ELSE
PS_EnableDisableDevPWState      proc    near
ENDIF

;       mov     ds,cs:Bios_Data_Word                           ; kam003
        assume  ds:Bios_data                                   ; kam003

        mov     APMBIOS_CMD,APM_ENABLE_DISABLE_FUNC
                                ; Enable/Disable PM (5308h)
        cmp     cx,02h          ; Is command type below 02    ; kam011
        jb      valid_cmd       ; if so it is a valid command.; kam011
invalid_cmd:                                                  ; kam011
        mov     ax,ERROR_PM_INVALID_PARAMETER                 ; kam011
        stc                                                   ; kam011
        ret                                                   ; kam011
valid_cmd:                                                    ; kam011
        jmp     PS_FUNC567

PS_EnableDisableDevPWState      endp                           ; kam010

;=============================================================================
; FUNCTION 7 - Engage/Disengage Power Management
;
; This function is same as defined in I0/I1 document as
; DoAPM_Engage_Disengage_PM ()
; INT 2F AX=5407 API processing
;=============================================================================

IFDEF   POWERALONE                                             ; kam010
PS_EngageDisengagePM    proc    far
ELSE
PS_EngageDisengagePM    proc    near
ENDIF

;       mov     ds,cs:Bios_Data_Word                           ; kam003
        assume  ds:Bios_data                                   ; kam003

        mov     APMBIOS_CMD,APM_ENGAGE_DISENGAGE_PM
                                ; Engage/Disengage PM (530fh)
        cmp     cx,02h          ; Is command type below 02     ; kam011
        jb      valid_cmd       ; if so it is a valid command. ; kam011
        jmp     short invalid_cmd                              ; kam011
;       jmp     PS_FUNC567

PS_EngageDisengagePM    endp                                   ; kam010


End_Code11      label   near    ; chop here if running on APM 1.1 BIOS kam010

assume  ds:nothing
;******************************************************************
;******************************************************************
;******************************************************************


; AX.ReturnStatus = DEV_INIT( ES:BX.DRIVER_INIT_REQUEST)

        public  Power_Init
IFDEF   POWERALONE
Power_Init proc far
ELSE
Power_init proc near
ENDIF

        assume  ds:nothing, es:nothing
        pushf
        sti
        push    si
        push    ds
        push    dx
        push    ax

        mov     ax, Bios_Data
        mov     ds,ax
        assume  ds:Bios_Data

; JAH Start - Get address of DOS data area
        push    bx
        push    es
        mov     ah,52h                  ; Get the DOS data segment
        int     21h
        mov     WORD PTR PSPsegment,es
        pop     es
        pop     bx
; JAH End

IFDEF   POWERALONE
        ; Return DEVICE DRIVER end address
        cmp     word ptr ds:APM_VER,0101h ; Are we running on APM 1.1 BIOS kam010
        jz      addr11
        mov     word ptr es:[bx].RH_D_BREAKPTR+0,offset Bios_Data:End_Data
        mov     word ptr es:[bx].RH_D_BREAKPTR+2,ds
        jmp     short retdoidle
addr11:
        mov     word ptr es:[bx].RH_D_BREAKPTR+0,offset Sysinitseg:End_Code11
        mov     word ptr es:[bx].RH_D_BREAKPTR+2,cs     ; kam010

retdoidle:

        ; Return DO_IDLE address to DOS
        mov     ax,Bios_Res                     ; get resident code segment
        mov     word ptr es:[bx].RH_D_IDLEPTR+0,offset Bios_Code:DO_IDLE_DOS
        mov     word ptr es:[bx].RH_D_IDLEPTR+2,ax

; M092 BEGIN
; see if POWER is already loaded ; If so, disable the previous POWER
; and load ourselves.

        xor     bx,bx
        mov     ax,(MultPWR_API*256)+00h; POWER detect mult.call
        int     2fh
        cmp     ax,(MultPWR_API*256)+00h; MultAPI code unchanged ?
        jz      pi_NoPrevPower
        cmp     bx,504dh                ; M087 signature correct ?
        jne     pi_NoPrevPower

; There is a previously loaded POWER; disable it.
        mov     bx,100h                 ; disable all Power management
        mov     ax,(MultPWR_API*256)+I2F_PW_GET_SET_PWSTATE
        int     2fh                     ; we don't expect any error here
;M092 END

ENDIF

; Continue init.
pi_NoPrevPower:
        mov     dx,Bios_Res             ;code segment of the BIOS
        mov     Pwr_calli16_ret+2,dx            ;init CS for this pointer
        mov     Pwr_calli2f_ret+2,dx            ;init CS for this pointer
        mov     kb_calli16_ret+2,dx            ;init CS for this pointer
        mov     I08_IdleControl_ptr+2,dx

        mov     si,offset I28_VEC               ; chain 28h
        mov     ax,28h                          ; DOS IDLE interrupt
        call    XVECT

        mov     si,offset I2F_VEC               ; chain 2Fh
        mov     ax,2Fh                          ; APP IDLE interrupt
        call    XVECT

        mov     si,offset I2A_VEC               ; chain 2Fh
        mov     ax,2Ah                          ; APP IDLE interrupt
        call    XVECT

        mov     si,offset I16_VEC               ; chain 16h
        mov     ax,16h                          ; keyboard intrrupt
        call    XVECT

        mov     si,offset I9_VEC                ; chain 8h
        mov     ax,9h                           ; PC TIMER interrupt
        call    XVECT

        mov     si,offset I10_VEC               ; chain 10h
        mov     ax,10h                          ; application activity
        call    XVECT

        mov     si,offset I13_VEC               ; chain 13h
        mov     ax,13h                          ; application activity
        call    XVECT

        mov     si,offset I14_VEC               ; chain 14h
        mov     ax,14h                          ; application activity
        call    XVECT

        mov     si,offset I17_VEC               ; chain 17h
        mov     ax,17h                          ; application activity
        call    XVECT

        mov     si,offset I21_VEC               ; chain 21h
        mov     ax,21h                          ; application activity
        call    XVECT

        mov     si,offset I25_VEC               ; chain 25h
        mov     ax,25h                          ; application activity
        call    XVECT

        mov     si,offset I26_VEC               ; chain 26h
        mov     ax,26h                          ; application activity
        call    XVECT

        mov     si,offset I6C_VEC               ; chain 6Ch             ;M080
        mov     ax,6Ch                          ; resume event          ;M080
        call    XVECT                                                   ;M080

; NSM4 - BEGIN get address of INDOS flag and store it for future use
        push    es
        mov     ah,34h                          ; get address of indos flag
        int     21h                             ; es:bx = address of INDOS
        mov     [INDOS_Offset],bx
        mov     [INDOS_Seg],es                  ; store them away for later use
        pop     es
; NSM4 - END

        mov     dx,ds                           ; I08 vec is in our data seg
        mov     si,offset I08_VEC               ; chain Int 08
        mov     ax,08h                          ;
        call    XVECT

IFNDEF  POWERALONE
        push    ds
        mov     ax,cs                           ; DS=CS for the message
        mov     ds,ax
        assume  ds:nothing
        mov     dx,offset INIT_MSG
        mov     ah,9                            ; report init complete
        int     21h
        pop     ds                              ; ds = bios_data
ENDIF ; NOT POWERALONE

        assume  ds:Bios_Data

;M001 BEGIN
;
IFDEF  INCL_APM

; Check and initialize APM.

;M005 BEGIN

IFDEF   POWERALONE
        call    Check_and_Init_APM
        call    InitCmdLineParms                ; M104
ELSE
        call    Check_and_Init_APM_Ptr
ENDIF   ;NOT POWERALONE

;M005 END

ENDIF           ; INCL_APM

; M001 END

        pop     ax
        pop     dx
        pop     ds
        assume  ds:nothing
        pop     si

        popf
        clc
        ret

Power_Init      endp

INIT_MSG:                               ; Display 1.01 message kam004
include msbio.cl9


; void XVECT( ds:[si].vecp, ax.vec#,dx.Bios_Code_Seg)
; purpose: xchange a vector with vector pointed to by ds:si

Xvect   proc    near
        assume  ds:Bios_Data, es:nothing

        push    es
        push    di

        mov     di,ax
        shl     di,1
        shl     di,1

        xor     ax,ax
        mov     es,ax

        pushf
        cli                             ; disable interrupts
        mov     ax,[si]
        xchg    es:[di],ax
        mov     [si],ax
        mov     ax,dx                   ;dx contains Bios_Code seg
        xchg    es:[di]+2,ax
        mov     [si]+2,ax
        popf

        pop     di
        pop     es
        ret

Xvect   endp

;M074   BEGIN

        public  Clock_Init

Clock_init      proc far

IFDEF   POWERALONE                      ; M081
        call    far ptr P_UpdFromCMOS
ELSE
        call    P_UpdFromCMOS_ptr       ; M074 update our date and time
                                        ; from CMOS RTC
ENDIF

;       Ints 1c and 6c are hooked during main power management init, above.

        clc
        ret

Clock_Init      endp

;M074   END


;
; M098 - begin
;
IFDEF   POWERALONE

;
;------------------------------------------------------------------------------
;
; UMBInit : Try moving ourself into UMB by Allocating memory in UMB and
;               Loading ourselves into it using 4b03 call (We are in EXE
;               format)
;
;       Input : ES:BX is the pointer to the original Request packet
;               passed in by DOS
;
;       Output: Carry Clear if a copy of POWER was succesfully initialized
;               in an UMB
;               Carry set if POWER was not initialized in an UMB
;               AX=segment of the UMB copy (if carry clear)
;
;------------------------------------------------------------------------------
;
UMBInit proc    far

        assume  ds:bios_data

        call    AreWeInUMB                      ; Are we already in a UMB ?
        jc      StayLow                         ; if so stay where we are
        call    GetProgName                     ; extract the device driver name
                                                ;  with full path
        call    TryGettingIntoUMB               ; and try loading the program
                                                ;  into an UMB
        jc      StayLow                         ; try staying low if we failed
        call    SetupDDStub                     ; Initialize the device driver
                                                ;  loaded in the UMB
StayLow:
        ret

UMBInit endp

;
;------------------------------------------------------------------------------
;
; AreWeImUMB : Check whether we are already in a UMB
;               Check if we are devicehighed into an UMB
;               This will also help the second copy of POWER which will
;               be EXECed in the UMB not to re-exec itself again
;
;       Input : None
;       Output: Carry clear if we are not in UMB
;               Carry set if we are already in a UMB
;
;------------------------------------------------------------------------------
;
AreWeInUMB      proc    near

        assume  ds:bios_data

        push    es
        push    bx

        mov     ah, GET_IN_VARS                 ; get DOS data
        int     21h
        mov     ax, cs

        mov     bx, word ptr es:[UMB_ARENA]     ; get first UMB arena
                                                ; Note : UMB_ARENA is initial-
                                                ;        zed to ffff if no UMBs
                                                ;        are available
        cmp     bx, ax                          ; Sets/clears carry flag

        pop     bx
        pop     es
        ret

AreWeInUMB      endp

ProgName        db      129 dup (0)             ; Storage for devicer driver
                                                ;  name.
;
;------------------------------------------------------------------------------
;
; GetProgName : Extracts the device driver file name (with full path) from
;               device driver command line
;
;       Input : ES:BX = pointer to the request packet
;       Output: The device driver name is copied into ProgName
;
;------------------------------------------------------------------------------
;
GetProgName     proc    near

        assume  ds:bios_data

        push    ds
        push    es
        push    si
        push    di
        lds     si, es:[bx].RH_D_IDLEPTR        ; command line
        cld
gpn_loop1:                                      ; skip all leading spaces/delims
        lodsb
        call    IsDelim
        jz      gpn_loop1

        dec     si                              ; get back to the 1st char
        mov     di, offset ProgName             ; destination of copy
        push    cs
        pop     es
gpn_loop2:                                      ; copy till the first space/delim
        lodsb
        call    IsDelim
        jz      gpn_copied
        stosb
        jmp     gpn_loop2
gpn_copied:
        pop     di
        pop     si
        pop     es
        pop     ds
        ret

GetProgName     endp

;
;------------------------------------------------------------------------------
;
; IsDelim : Check whether a character is a delimiter or not
;
;       Input : AL = character to be checked
;       Output: Zero flag set if the character is a delimiter
;               Zero flag clear if the character is not a delimiter
;
;       Note : Takes the same Delimiter character set as SYSINIT
;
;------------------------------------------------------------------------------
;
IsDelim         proc    near

        cmp     al, ' '
        jz      id9
        cmp     al, 0dh                         ; CR ?
        jz      id9
        cmp     al, 0ah                         ; LF ?
id9:
        ret

IsDelim         endp

;
; Packet for Load Overlay
;
LoadArea        dw      ?                       ; UMB where POWER is going to be
RelocFactor     dw      ?


CallthruPtr     dw      ?                       ; Temporary space to call the
                dw      ?                       ;  new copy of POWER


;
;------------------------------------------------------------------------------
;
; TryGettingImtoUMB : Try to allocate UMB and load ourself into UMB using
;                       4b03 call
;
;       Input : ProgName contains the file name with full path
;       Output: Carry clear if the prog was loaded succesfully into UMB
;               Carry set if the operation failed
;
;------------------------------------------------------------------------------
;

TryGettingIntoUMB       proc    near

        assume  ds:bios_data

        push    es
        push    ds
        push    bx
        push    dx

        push    cs
        pop     ds

        push    cs
        pop     es

        assume  ds:sysinitseg

;
; Open the file
;
        mov     dx, offset ProgName
        mov     ah, OPEN
        int     21h
        jc      tgiu9
;
; Seek to end of file to get the size of file
;
        mov     bx, ax
        mov     ax, (LSEEK shl 8)+2             ; lseek from end of file
        xor     cx, cx
        mov     dx, cx
        int     21h
;
; Close the file
;
        pushf
        push    dx
        push    ax
        mov     ah, close
        int     21h
        pop     ax
        pop     dx
        popf
        jc      tgiu9                           ; fail if seek failed

        or      dx, dx                          ; > 64K ?
        jnz     tgiu9f                          ; we cannot be > 64K
                                                ; lets abort this process
        mov     cl, 4
        add     ax, 15
        shr     ax, cl
        inc     ax                              ; One for the Device sub-arena
                                                ; M105
;
; Allocate UMB space : We know that during SYSINIT the only memory available
;                       thru INT 21 ah=48 is in the UMb space
;
        mov     bx, ax
        mov     ah, ALLOC
        int     21h
        jc      tgiu9
;
; Update all segment values
;
        inc     ax                              ; skip past Device sub-arena
                                                ; M105
        mov     LoadArea, ax
        mov     RelocFactor, ax
        mov     CallThruPtr+2, ax
;
; Exec the program
;
        mov     dx, offset ProgName
        mov     bx, offset LoadArea
        mov     ax, (EXEC SHL 8) + 03           ; Load overlay
        int     21h
        jc      RelMemory                       ; if EXEC failed remember to
                                                ; release the memory we alloced
;
; M105 - begin
;
        mov     ax, LoadArea
        push    ds
        assume  ds:nothing
        dec     ax
        mov     ds, ax
        mov     byte ptr ds:[devmark_id], devmark_device
IFNDEF  DEBUG
        mov     word ptr ds:[devmark_filename+0], 'OP'
        mov     word ptr ds:[devmark_filename+2], 'EW'
        mov     word ptr ds:[devmark_filename+4], 'R'
        mov     word ptr ds:[devmark_filename+6], 0
ELSE
        mov     word ptr ds:[devmark_filename+0], 'PD'
        mov     word ptr ds:[devmark_filename+2], 'WO'
        mov     word ptr ds:[devmark_filename+4], 'RE'
        mov     word ptr ds:[devmark_filename+6], 0
ENDIF
        dec     ax
        mov     ds, ax
        mov     word ptr ds:[arena_name+0], 'DS'
        mov     byte ptr ds:[arena_name+2], 0
        inc     ax
        inc     ax
        mov     word ptr ds:[devmark_seg+10h], ax
        pop     ds
        clc
;
; M105 - end
;
tgiu9:
        pop     dx
        pop     bx
        pop     ds
        pop     es
        ret
RelMemory:
        mov     ax, LoadArea                    ; M105
        dec     ax                              ; We have a arena subheader M105
                                                ; above us                  M105
        mov     es, ax                          ; M105
        mov     ah, 49h
        int     21h
tgiu9f:
        stc
        jmp     short tgiu9

TryGettingIntoUMB       endp

;
;------------------------------------------------------------------------------
;
; SetupDDStub : Initialize the driver loaded in UMB and resize the UMB to
;               whatever is needed. For initializing we call the copy of
;               the device driver in UMB thru its Strategy & Interrupt
;               entry points with the same init request packet which we
;               received from sysinit.
;               Also we set the variable BreakAddr in the UMB copy to the
;               break address of the low copy, because when the clock driver
;               initialized it should return the break address of the low
;               memory copy, else sysinit will think that our drivers
;               are trying to use memory from the Low memory to the UMB !
;
;       Input : ES:BX pointer to the request packet paased to us by DOS
;       Output: Carry set if Device driver did not initialize
;               Carry clear if Device driver succesfully initialized
;               AX=segment of the UMB copy (if carry clear)
;
;       Note : The Clock driver will get initialized when DOS calls the
;               stub which has been re-directed to the copy in UMB
;
;------------------------------------------------------------------------------
;
SetupDDStub     proc    near

        assume  ds:bios_data

        push    es
        mov     es, LoadArea
        assume  es:nothing
        mov     byte ptr es:[SecondCopy], 0ffh
        pop     es

        mov     ax, offset JDEV_STRATEGY
        mov     CallThruPtr, ax                 ; call the strategy entry
        call    dword ptr CallThruPtr

        mov     ax, offset JDEV_INTERRUPT
        mov     CallThruPtr, ax                 ; call the interrupt entry
        call    dword ptr CallThruPtr

        mov     ax, word ptr es:[bx].RH_D_BREAKPTR+0
        add     ax, 15
        mov     cl, 4
        shr     ax, cl
        add     ax, word ptr es:[bx].RH_D_BREAKPTR+2
        sub     ax, LoadArea                    ; get the new size of the mem block
        jz      sds9f                           ; if ZERO the device driver failed

        push    es
        push    bx
        mov     bx, ax
        mov     ax, LoadArea
        mov     es, ax

        cmp     word ptr ds:APM_VER,0101h ; Are we running on APM 1.1 BIOS kam010
        jz      setup11
        mov     word ptr es:[BreakAddr], offset bios_code:endofstub
        mov     word ptr es:[BreakAddr+2], bios_code
        jmp     short proceed_setup
setup11:
        mov     word ptr es:[BreakAddr11], offset bios_code:endofstub11
        mov     word ptr es:[BreakAddr11+2], bios_code  ; kam029
proceed_setup:                                  ; kam010

        dec     ax                              ; device sub-arena      M105
        mov     es, ax                          ;                       M105
        mov     word ptr es:[devmark_size], bx  ; Put in the size of    M105
                                                ; device driver         M105
        inc     bx                              ; device sub-arena      M105

        mov     ah, SETBLOCK                    ; resize the memory block
        int     21h
        mov     bx, es                          ;                       M105
        dec     bx                              ; point to sub-arena    M105
        mov     es, bx                          ;                       M105
        mov     word ptr es:[arena_owner], 8    ; SYSTEM owner          M105
        pop     bx
        pop     es

        mov     ax, LoadArea                    ; return the segment address
        clc
        ret
sds9f:
        stc
        ret

SetupDDStub     endp

;
; M098 - end
;
;
;------------------------------------------------------------------------------
;
; DoCmdLine : process the commadn line and set the power mode
;
;       Input : ES:BX=request packet
;       Output: Zero flag set if /LOW was not specified
;               Zero flag set if /LOW was specified
;
;------------------------------------------------------------------------------
;
DoCmdLine       proc    far

        push    es
        push    bx
        les     di, es:[bx].RH_D_IDLEPTR        ; command line
        cld

dcl_loop1:
        mov     al, es:[di]
        inc     di
        call    IsDelim
        jz      dcl_loop1

dcl_loop2:
        mov     al, es:[di]
        inc     di
        call    IsDelim
        jnz     dcl_loop2
        dec     di
        call    far ptr ParseCmdLine
        or      dl, dl                          ; M104
        pop     bx                              ; M104
        pop     es                              ; M104
        ret

DoCmdLine       endp
;
; M104 - begin
;

InitCmdLineParms        proc    near

        push    ds
        push    dx
        push    bx
        push    ax

        mov     ax, Trans_Data
        mov     ds, ax
        assume  ds:Trans_Data

        cmp     PW_Mode, 0ffh
        je      SPM_SkipMode

        mov     bh,1                    ; set  power state
        mov     bl, PW_Mode
        mov     ax,(MultPWR_API*256)+I2F_PW_GET_SET_PWSTATE     ; M007
        call    far ptr FPwr_Services
;       int     2fh

        mov     ax, PW_Savings_Value
        or      ax, ax
        jz      SPM_SkipMode

        mov     bx,ax
        mov     ax,(MultPWR_API * 256)+I2F_PW_GETSET_SAVINGS
        call    far ptr FPwr_Services
;       int     2fh

SPM_SkipMode:

IFDEF   DEBUG
        cmp     PW_Debug, 0
        jz      SPM_SkipDbg

        mov     ax,(MultPWR_API*256)+02h; change allocation strategy
        mov     bl,80h                  ; special value for SOUND toggle
        call    far ptr FPwr_Services
;       int     2fh
SPM_SkipDbg:
ENDIF
        pop     ax
        pop     bx
        pop     dx
        pop     ds
        assume  ds:nothing
        ret

InitCmdLineParms        endp

        public  Banner

Banner  proc    far

        push    ds
        mov     ax, Bios_Code
        mov     ds, ax
        assume  ds:nothing
        cmp     byte ptr ds:[SecondCopy], 0
        jne     b_exit
        mov     ax,cs                           ; DS=CS for the message
        mov     ds,ax
        assume  ds:nothing
        mov     ah,STD_CON_STRING_OUTPUT        ; display power banner
        mov     dx,offset INIT_MSG              ;                      kam001
        int     21h
b_exit:
        pop     ds                              ;
        ret
Banner  endp


;=========================================================================

ChkDOSVer_DispErr       proc    far
        call    far ptr ChkDOSVer
        jae     cdv_exit
        push    ds
        mov     ah, STD_CON_STRING_OUTPUT
        mov     dx, Trans_Data
        mov     ds, dx
        mov     dx, offset Trans_Data:BadDOSMsg
        int     21h
        pop     ds
        mov     al, 1
        or      al, al
cdv_exit:
        ret
ChkDOSVer_DispErr       endp

ChkDOSVer       proc    far
        push    bx
        mov     ah, GET_VERSION
        int     21h
        cmp     ax, 0005
        pop     bx
        ret
ChkDOSVer       endp

ENDIF ; POWERALONE
Sysinitseg      ends

ENDIF   ;For POWER conditional

        end
