;       SCCSID = @(#)clockdd.asm        6.7 91/09/25
title   clock device driver -- main  [clockdd.asm]
; ****************************************************************************
; *                                                                          *
; *                       IBM/Microsoft Confidential                         *
; *                                                                          *
; *                 Copyright (c) IBM Corporation  1987, 1991, 1997          *
; *                 Copyright (c) Microsoft Corp.  1987, 1991                *
; *                           All Rights Reserved                            *
; *                                                                          *
; ****************************************************************************
; Change History
;
; 178847 5/27/97 PAT    Fix RTC at 128hz.  Optimize RTCINT codepath.
;
; 182895 11/12/97 WHH   Fixed to use single driver for UNI/SMP.
;                       do NOT build with SMP flag.
;
PAGE    60,132
;
;********************** START OF SPECIFICATIONS *********************
;*
;* SOURCE FILE NAME:    CLOCKDD.ASM
;*
;* DESCRIPTIVE NAME:    CP/DOS 1.1 CLOCK DEVICE DRVIER
;*
;*      IBM/Microsoft Confidential
;*
;*      Copyright (c) IBM Corporation 1987, 1991, 1997
;*      Copyright (c) Microsoft Corporation 1987, 1991
;*
;*      All Rights Reserved
;*
;* STATUS:    RELEASE 1 LEVEL 1
;*
;* FUNCTION:  CLOCK$ is the device driver for the RT/CMOS clock.
;*
;* NOTES:
;*    DEPENDENCIES:
;*    RESTRICTIONS:
;*    PATCH LABEL:
;*
;* ENTRY POINTS:  RTENTR
;*
;* EXTERNAL REFERENCES:
;*    ROUTINES:
;*      DIRECT: DevHlps: dh_GetDOSVar, dh_EOI, dh_SchedClock,
;*                       dh_SetIRQ, dh_PhysToVirt, dh_UnPhysToVirt
;*                       (NOT AT MODEL Only: dh_GetLIDEntry, dh_ABIOSCall)
;*
;*  DATA AREAS:
;*    MODIFIED: Global Information Segment (SysInfoSeg)
;*
;*  CONTROL BLOCKS:
;*      MODIFIED:   Device Driver Request Block
;*
;*  TABLES:  none   ; sparse function table, used case structure instead
;*
;*********************** END OF SPECIFICATIONS **********************

.xlist
        INCLUDE mvdm.inc                ; equates and data for MVDM support
purge PANIC                             ; prevent conflict with osmaca.inc
        INCLUDE osmaca.inc
        INCLUDE devhlp.inc              ; the devhlp function eqs
        INCLUDE infoseg.inc             ; definitions of Global Info Segment
        INCLUDE devsym.inc              ; definition of DD Request Packet
        INCLUDE clkdata.inc
        INCLUDE abios.inc
        INCLUDE vcmospdd.inc            ; vcmos/clk shared data and equates
        INCLUDE clkseg.inc              ; segment definitions
        INCLUDE pvwxport.inc
        INCLUDE cmos.inc
        INCLUDE ioctl.inc
        INCLUDE vtdptd.inc
        INCLUDE devhlpP.inc             ; d182895
        INCLUDE timer.inc               ; d196223
.list

;;MONITOR         EQU     1               ; creates counters for monitoring

        extrn   RTWRIT:Near
        extrn   RTCINT:Near
        extrn   FIXISEG:Near
        extrn   RTINIT:far
        extrn   RTINIT2:far
        extrn   endswapcode:byte
        EXTRNFAR ClkReadCMOS

; publics

        PUBLIC  DevHlp
        PUBLIC  SchedClock
        PUBLIC  InfSeg
        PUBLIC  MonTab
        PUBLIC  Update_Flag                     ; Update Interrupt Flag
        PUBLIC  Use_SMP_PortIO                  ; Use SMP Port IO
        PUBLIC  MS_DeltaPostEOI                 ; ms since last PostEOI call
        PUBLIC  Int_Nested                      ; Number of nested Ints
        PUBLIC  Int_In_Progress                 ; Flag for presently nested

ifdef profile
        PUBLIC  DivDown
endif

        PUBLIC  AccumRTCFreqs
        PUBLIC  ReqRTCFreq
        PUBLIC  Hundred_Frac
        PUBLIC  MS_Fraction
        PUBLIC  MS_DeltaRTC
        PUBLIC  MS_DeltaKcall

        PUBLIC  InSchedClk
        PUBLIC  CMOS_location
        PUBLIC  saveDS
        PUBLIC  pfnEOIRTC                  ; function to EOI d196223
        PUBLIC  pfnEOITMR                  ; function to EOI d196223
        PUBLIC  ATDataEnd

        PUBLIC  pqwTmrRollover
        PUBLIC  qwTmrRollover
        PUBLIC  pqwTmr
        PUBLIC  qwTmr

BREAK <Clock Device Driver DATA SEGMENT Declaration>

ClkData SEGMENT

        extrn   ClkIDC:far              ; 150700
START$  LABEL   BYTE
        dd      -1
        DW      DEV_CHAR_DEV OR DEVLEV_3 OR DEV_CLOCK OR DEV_IOCTL
                                        ; 150700 added "OR DEV_IOCTL"
        DW      offset ClkCode:ClkStrat
        DW      offset ClkCode:ClkIDC   ; 150700
        DB      'CLOCK$  '
        DW      4 DUP  (?)      ; Prot & Real mode CS & DS (filled by sysinit)
        DD      DEV_16MB OR DEV_IOCTL2  ; device can handle > 16MB physical memory
                                        ; 150700 added "OR DEV_IOCTL2"

DevHlp  DD      0                       ; Pointer to DOS "helper" functions
SchedClock      DD      0               ; Pointer to SchedClock address pointer
InfSeg  DD      0                       ; Pointer to InfoSeg Address

        PUBLIC  szClkName,szTmrName
szClkName       db      CLK_NAME,0      ;CLOCK registered name
szTmrName       db      PTD_NAME,0      ;TIMER registered name

        PUBLIC  fpfnVTProc,fsPTFlags
fpfnVTProc      df      0               ;vtimer entry point
fsPTFlags       dw      PTF_OWNT0+PTF_OWNT2;ptimer owns T0/T2 initially

MonTab  DB      31                      ; Days of Month Table - January
        DB      28                      ; - February
        DB      31                      ; - March
        DB      30                      ; - April
        DB      31                      ; - May
        DB      30                      ; - June
        DB      31                      ; - July
        DB      31                      ; - August
        DB      30                      ; - September
        DB      31                      ; - October
        DB      30                      ; - November
        DB      31                      ; - December

; The following are for real time clock housekeeping.  The RTC runs at 128hz,
; thus increments 7.8125ms every tick.  Fractional portions are accumulated.
; The difference between actual RTC freq and kernel requested freq are
; maintained so kernel gets ticks at the requested rate.

AccumRTCFreqs   DW      0   ; Actual-Requested Countdown Freqs
ReqRTCFreq      DW      0   ; Kernel requested RTC frequency
Hundred_Frac    DB      0   ; Fractional portion of hundreds count
MS_Fraction     DW      0   ; Fractional portion of milliseconds count
MS_DeltaRTC     DW      0   ; Number of milliseconds since last RTC tick
MS_DeltaKcall   DW      0   ; Number of milliseconds since last called kernel

Update_Flag     DB      0   ; Update Interrupt flag
Use_SMP_PortIO  DB      0   ; Use SMP Port IO

; Extra Vars for Nested Int fix

MS_DeltaPostEOI dw      0
Int_Nested      db      0
Int_In_Progress db      0

InSchedClk      DB      0               ; flag to indicate entering SchedClock

CMOS_location   db      0               ; location of CMOS TABLE ADDR

        EVEN
pqwTmrRollover  dd      0               ; (16:16) ptr to PIT Ctr 0 rollover
                                        ; count in kernel address space.
qwTmrRollover   qw_s    <0,0>           ; PIT Ctr 0 rollover count in Clock
                                        ; driver address space, where:
                                        ; qw_Lo = current rollover count.
                                        ; qw_Hi = next rollover count.
pqwTmr          dd      0
qwTmr           qw_s    <0,0>

codelockhandle  db      sizeof_lockhandle_s  dup(0)
                                        ; handle returned by DevHlp_VmLock

; 150700 added the following three variables
                PUBLIC  IRQ0_Owner, VTimerRegister,PddDS
IRQ0_Owner      DB      1               ;Flag set high if Clock0X owns IRQ0
VTimerRegister  DB      0               ;Flag set low, will go high once
                                        ; VTimer does an VDHOpenPDD, this
                                        ; ensures all PDDs are initialized
PddDS           DW      0               ;Data Segment of calling PDD
pfnEOIRTC       DW      offset CLKCODE:EOIUniRTC  ; function to EOI   d196223
pfnEOITMR       DW      offset CLKCODE:EOIUniTMR  ; function to EOI   d196223

        READREQ cmos_req

ATDataEnd       EQU     $               ; Used by Sysinit to delete remaining
                                        ;   data segment.

Time_Check      db      0A0h            ; Used for validation
                db      0A6h
                db      0AAh

ClkData         ENDS


BREAK <Clock Device Driver Strategy Routine>
;********************** START OF SPECIFICATIONS *********************
;*
;* MODULE NAME:  RTENTR
;*
;* DESCRIPTIVE NAME:  Strategy routine for Clock Device Driver
;*
;* FUNCTION:    This routine is the task-time entry point to the Clock
;*              Device driver.  Requested functions are dispatched to
;*              worker routines.
;* NOTES:
;*      RTENTR provides eight functions:  RTREAD(5), STREDY(5), STCOMP(6,7)
;*      RTWRIT(8,9), RTINIT(27), RTINIT2(0) and BADCMD (all the rest).
;*      Since a command table would be sparse (only 8 valid entries of
;*      28 options), a case control structure is used.  Functions 6,7
;*      and functions 8,9
;*      have the same effect.
;*
;*   Equivalent Command dispatch table (Highest legal function is 27):
;*
;*   ClkSwap:RTINIT2         ; 0 Initialize (2nd stage, special for clock)
;*   ClkSwap:BADCMD          ; 1 Media Check  these are unsupported
;*   ClkSwap:BADCMD          ; 2 Build BPB    functions.
;*   ClkSwap:BADCMD          ; 3 IOCTL input
;*   ClkSwap:RTREAD          ; 4 Input (Read)
;*   ClkSwap:STREDY          ; 5 Non-destructive read, no wait
;*   ClkSwap:STCOMP          ; 6 Input status
;*   ClkSwap:STCOMP          ; 7 Input flush
;*   ClkSwap:RTWRIT          ; 8 Output (Write)
;*   ClkSwap:RTWRIT          ; 9 Output with verify
;*   ClkSwap:BADCMD          ; 10 BADCMD
;*   ClkSwap:BADCMD          ; 11 BADCMD
;*   ClkSwap:BADCMD          ; 12 BADCMD
;*   ClkSwap:BADCMD          ; 13 BADCMD
;*   ClkSwap:BADCMD          ; 14 BADCMD
;*   ClkSwap:BADCMD          ; 15 BADCMD
;*   ClkSwap:RTIOCT          ; 16 Generic IOCTL
;*   ClkSwap:BADCMD          ; 17 BADCMD
;*   ClkSwap:BADCMD          ; 18 BADCMD
;*   ClkSwap:BADCMD          ; 19 BADCMD
;*   ClkSwap:BADCMD          ; 20 BADCMD
;*   ClkSwap:BADCMD          ; 21 BADCMD
;*   ClkSwap:BADCMD          ; 22 BADCMD
;*   ClkSwap:BADCMD          ; 23 BADCMD
;*   ClkSwap:BADCMD          ; 24 BADCMD
;*   ClkSwap:BADCMD          ; 25 BADCMD
;*   ClkSwap:BADCMD          ; 26 BADCMD
;*   ClkCode:RTINIT          ; 27 Initialize
;*   ClkSwap:BADCMD          ; 28 BADCMD here at table end
;*
;* ENTRY POINT: RTENTR
;*    LINKAGE:  FAR
;*
;* USES: EAX, EBX, ECX, DL, EDI, ESI
;*
;* INPUT:
;*      ES:BX = Address of Device Driver Request Packet
;*      DS    = Points to Clock Device Driver Data Segment
;*
;*      WARNING: Parameter checking on input data to RTWRIT through
;*         this routine must be performed by the caller!
;*
;* OUTPUT:
;*      Device Driver Request Packet filled in (Status word set,
;*      date/time fields, etc).
;*
;* EXIT-NORMAL:
;*      Exit-Normal is via common routines STCOMP or STREDY
;*      Status word set to 'BUSY' and/or 'DONE'
;*
;* EXIT-ERROR:
;*      Exit-Error is via common routine BADCMD, but only if
;*      invalid command is passed in Driver Request Packet.
;*
;* INTERNAL REFERENCES:
;*    ROUTINES:     RTREAD, RTWRIT, RTINIT, STCOMP, STREDY, BADCMD,
;*                  RTIOCT
;*
;* EXTERNAL REFERENCES:
;*    STRUCTURES:  Global Information Segment (SysInfoSeg)
;*    ROUTINES:  none
;*
;*********************** END OF SPECIFICATIONS **********************
page
;********************** START OF PSEUDO-CODE   **********************
;
; switch( packet_command ) {
;
; 0;    call RTINIT2 to grab IRQ0
;       break;
;
; 4:    call RTREAD to read realtime clock
;       break;
;
; 5:    call STREDY to do non-destructive read with no-wait
;       break;
;
; 6:
; 7:    call STCOMP to set status "complete" exit
;       break;
;
; 8:
; 9:    call RTWRIT to update the realtime clock
;       break;
;
; 16:   call RTIOCT to process the IOCTL request
;       break;
;
; 27:   call RTINIT to initialize data structures & realtime clock device
;       break;
;
; default:
;       call BADCMD to express that this function is unsupported
;       break;
; }
;
; mark packet_status operation complete
; return
;
;*********************** END OF PSEUDO-CODE    **********************

ClkSwap SEGMENT

public startswapcode
startswapcode   label   byte


    ASSUME      CS:ClkSwap,DS:ClkData,ES:NOTHING,SS:NOTHING
RTENTR  PROC    FAR
        public  RTENTR

        push    es
        push    bx
        mov     al,es:[bx].PktCmd       ; Get device command
        xor     ah,ah

RTREAD?:
        cmp     ax,4                    ; Is this RTREAD?
        jl      RTINIT2?                ; .. No, might be RTINIT2
        jg      STREDY?                 ; .. No, try next
        call    LOCKSWAP                ; .. Yes, try locking swappable code
        jc      OUTOFMEM                ; .... lock failed, error out
        call    RTREAD                  ; .....lock succeeded, dispatch worker
        call    UNLOCKSWAP
        jmp     ENDCASE

STREDY?:
        cmp     ax,5                    ; Is this STREDY?
        jg      STCOMP?                 ; .. No, try next
        mov     ax,(STDON+STBUI)        ; .. Yes, just return flags
        jmp     ENDCASE

STCOMP?:
        cmp     ax,7                    ; Is this STCOMP?
        jg      RTWRIT?                 ; .. No, try next
        mov     ax,STDON                ; .. Yes, just return flags
        jmp     ENDCASE

RTWRIT?:
        cmp     ax,9                    ; Is this RTWRIT?
        jg      RTIOCT?                 ; .. No, try next
        call    LOCKSWAP                ; .. Yes, try locking swappable code
        jc      OUTOFMEM                ; .... lock failed, error out
        call    RTWRIT                  ; .....lock succeeded, dispatch worker
        call    UNLOCKSWAP
        jmp     ENDCASE

RTIOCT?:
        cmp     ax,16                   ; Is this RTIOC?
        jg      RTINIT?                 ; .. No, try next
        call    LOCKSWAP                ; .. Yes, try locking swappable code
        jc      OUTOFMEM                ; .... lock failed, error out
        call    RTIOCTL                 ; .....lock succeeded, dispatch worker
        call    UNLOCKSWAP
        jmp     ENDCASE

RTINIT?:
        cmp     ax,27                   ; Is this RTINIT?
        jne     BADCMD                  ; .. No, must be a bad command
        call    RTINIT                  ; .. Yes, dispatch worker
        jmp     ENDCASE

RTINIT2?:
        cmp     ax,0                    ; Is this RTINIT2?
        jne     BADCMD                  ; .. No, must be bad command
        call    RTINIT2                 ; .. Yes, dispatch worker
        jmp     ENDCASE

BADCMD:
        mov     ax, STDON+STERR+ERRCMD  ; Done + Error + Bad command
        jmp     ENDCASE

OUTOFMEM:
        mov     ax, STDON+STERR+ERRGFAIL
        ; jmp     ENDCASE

ENDCASE:
        pop     bx
        pop     es
        mov     word ptr es:[bx].PktStatus,ax    ; Mark operation complete
        ret

RTENTR  ENDP


;********************** START OF SPECIFICATIONS *********************
;*
;* MODULE NAME:  LOCKSWAP
;*
;* DESCRIPTIVE NAME:  Lock Swappable Code routine
;*
;* FUNCTION:  Lock the swappable code segment portion of the
;*            clock device driver.
;*
;* ENTRY POINT: LOCKSWAP
;*    LINKAGE:  NEAR from RTENTR
;*
;* NOTE:  See file ?\dos\memory\VMMISC.ASM for masm interface to
;*        _DevHlp_VMLock.
;*
;* INPUT:  none
;*
;* USES: EAX, ECX, DL, EDI, ESI
;*
;* EXIT-NORMAL:
;*     CF = 0; [codelockhandle] saved
;*
;* EXIT-ERROR:
;*     CF = 1
;*
;* INTERNAL REFERENCES:
;*    STRUCTURES:  ClkData:[codelockhandle]
;*
;* EXTERNAL REFERENCES:
;*    ROUTINES:    DevHlp_VirtToLin, DevHlp_VMLock
;*
;*********************** END OF SPECIFICATIONS **********************

    ASSUME      CS:ClkSwap,DS:ClkData,ES:NOTHING,SS:NOTHING
LOCKSWAP PROC NEAR
        public  LOCKSWAP

.386p
        push    ebx             ; others rely on bx,es

        ; do a virttolin DevHlp (linear address)
        mov     ax,cs
        lea     esi,startswapcode
        mov     dl,DevHlp_VirtToLin
        call    [DevHlp]
        mov     edi,eax

        ; do a virttolin DevHlp (lockhandle)
        mov     ax,ds
        mov     esi,offset codelockhandle
        mov     dl,DevHlp_VirtToLin
        call    [DevHlp]

        ; do a VMLock DevHlp
        mov     esi,eax         ; get offset to lock handle
        xor     eax,eax         ; short term lock, etc.
        mov     ebx,edi         ; get offset to area to lock
        mov     edi,-1          ; set to no page list
        lea     ecx,endswapcode ; size to lock
        sub     ecx,offset startswapcode ; size to lock
        mov     dl,DevHlp_VMLock
        call    [DevHlp]       ; CF set by DevHlp_VMLock

        pop     ebx

        RET

LOCKSWAP  ENDP


;********************** START OF SPECIFICATIONS *********************
;*
;* MODULE NAME:  UNLOCKSWAP
;*
;* DESCRIPTIVE NAME:  UnLock Swappable Code routine
;*
;* FUNCTION:  Unlock the swappable code segment portion of the
;*            clock device driver.
;*
;* ENTRY POINT: UNLOCKSWAP
;*    LINKAGE:  NEAR from RTENTR
;*
;* USES: DL, ESI
;*
;* EXIT: Assume success
;*
;* INTERNAL REFERENCES:
;*    STRUCTURES:  ClkData:[codelockhandle]
;*
;* EXTERNAL REFERENCES:
;*    ROUTINES:    DevHlp_VirtToLin, DevHlp_VMUnLock
;*
;*********************** END OF SPECIFICATIONS **********************

    ASSUME      CS:ClkSwap,DS:ClkData,ES:NOTHING,SS:NOTHING
UNLOCKSWAP PROC NEAR
        public  UNLOCKSWAP

        push    eax
        ; do a virttolin DevHlp (lockhandle)
        mov     ax,ds
        mov     esi,offset codelockhandle
        mov     dl,DevHlp_VirtToLin
        call    [DevHlp]

        ; do a VMUnlock DevHlp using previously saved lock handle
        mov     esi,eax         ; get offset to lock handle
        mov     dl,DevHlp_VMUnlock
        call    [DevHlp]
        pop     eax
        RET

UNLOCKSWAP  ENDP


BREAK <RealTime Clock Read Routine>
;********************** START OF SPECIFICATIONS *********************
;*
;* MODULE NAME:  RTREAD
;*
;* DESCRIPTIVE NAME:  Realtime Clock Read routine
;*
;* FUNCTION:  Supports the device commands:
;*            - READ (function 4)
;*      by returning six bytes of date and time information in
;*      the form Date(word), Min, Hrs, Sec/100, Sec.
;*
;*      DevHlp_PhysToVirt converts the real address of the data buffer
;*      in the Device Driver Request Block (PktData) to a virtual
;*      address.  DevHlp_UnPhysToVirt later restores the physical
;*      memory address.
;*
;* NOTES:
;*      For enhanced performance, RTREAD does not actually read the
;*      clock, but instead gets the data from the Global InfoSeg
;*      date/time data area.
;*
;* ENTRY POINT: RTREAD
;*    LINKAGE:  NEAR from RTENTR
;*
;* USES:    AX, CX, DX, BX, ES.  Preserves others.
;*
;* INPUT:
;*      Interrupt is enabled
;*      ES:BX = pointer to Request Block  (PktData)
;*      (SP+4):(SP+2) = pointer to data buffer
;*
;* OUTPUT: (RETURNED)
;*    DESCRIPTION:   Caller's data Buffer
;*
;* EXIT-NORMAL:
;*      Exit is via return to RTENTR (Strategy routine) with
;*      AX = Status word to be stored in request packet status
;*      field.
;*
;* EXIT-ERROR:
;*    RETURN CODE:      AX = ERROR and DONE
;*
;* INTERNAL REFERENCES:
;*    ROUTINES:    GTDAYS
;*
;* EXTERNAL REFERENCES:
;*    STRUCTURES:  Global Information Segment (SysInfoSeg)
;*    ROUTINES:    DevHlp_PhysToVirt, DevHlp_UnPhysToVirt
;*
;*********************** END OF SPECIFICATIONS **********************
page
;********************** START OF PSEUDO-CODE   **********************
;
; if requesting byte length >= 6 byte
;    set requesting byte length = 6
;    save flags for preserving interrupt flag
;    call DevHlp_PhysToVirt to convert data buffer's physical addr to
;         virtual address in ES:DI
;    if DevHlp_PhysToVirt successful
;       save ClkData address( save DS )
;       set DS:BX = SysInfoSeg address
;       disable interrupts for accessing InfoSeg
;       access InfoSeg to get seconds/hundredths, hours/minutes,
;              months/days, & current year to put in caller's data buffer
;       restore ClkData address, DS
;       call GTDAYS to get day count since 1-1-80
;       save day count in caller's data buffer
;       call DevHlp_UnPhysToVirt to get original address mode
;       if DevHlp_UnPhysToVirt successful
;          set return code  "DONE" & "BUSY"
;       endif
;       else goto convert_fail
;    endif
;    else goto convert_fail
;
;    restore flags for original interrupt flag
;    return
; endif
; else goto RT_ERR
;
; convert_fail:
;    restore flags for original interrupt flag
;
; RT_ERR:
;    set requesting byte length = 0
;    set return code  STDON+STERR+READ FAULT
;    return
;
;*********************** END OF PSEUDO-CODE    **********************


    ASSUME      CS:ClkSwap,DS:ClkData,ES:NOTHING,SS:NOTHING
RTREAD  PROC NEAR
        public  RTREAD

        CMP     ES:[BX].IOcount,RW_BYTES        ;  requesting 6 bytes or more
        Jae     READ                            ;  yes, continue....  @3.77

RT_ERR:
        mov     ES:[BX].IOcount,0               ;  return 0 bytes     @3.77
        MOV     AX,(STDON+STERR)+0bh            ;  Else, quit w/ error. @3.77
        RET

READ:   PUSH    BP
        MOV     BP,SP
;; The addresses passed in the request packets are 32-bit physical addresses.
        MOV     CX,6
        mov     ES:[BX].IOcount,cx              ;  return 6 bytes     @3.77
        MOV     AX,word ptr ES:[BX].IOpData+2   ; get hi word of address
        MOV     BX,word ptr ES:[BX].IOpData     ; get lo word of address
        MOV     DH,1                            ; result in ES:DI
        MOV     DL,DevHlp_PhysToVirt            ; call PhysToVirt
        PUSHF                                   ; save the flags
        CALL    [DevHlp]                        ; ES:DI points to buffer
        JNC     GET_DATA

CONVERT_FAIL:
        POPF                                    ; restore flags
        POP     BP
        JMP     SHORT RT_ERR

GET_DATA:
        PUSH    DS                              ; Save ClkData selector
        CLI                                     ; Disable Interrupts while
                                                ; accessing InfoSeg
        LDS     BX,[InfSeg]                     ; DS:BX = InfoSeg address
    ASSUME      DS:NOTHING
        MOV     AX,WORD PTR DS:[BX.SIS_SecTime] ; Get Secs/Hundths
        XCHG    AH,AL
        MOV     WORD PTR ES:[DI+4],AX           ; Stuff Huns/Secs
        MOV     AX,WORD PTR DS:[BX.SIS_HrsTime] ; Get Hrs/Mins
        XCHG    AH,AL
        MOV     WORD PTR ES:[DI+2],AX           ; Stuff Mins/Hrs
        MOV     DX,WORD PTR DS:[BX.SIS_DayDate] ; DX = Mon/Day
        MOV     AX,DS:[BX.SIS_YrsDate]          ; Get current year
;;80291 sti                                     ; enable interrupts
        SUB     AX,1980                         ; Elapsed since 1-1-80
        POP     DS                              ; Restore ClkData selector
;;;;;;;;;;;;;;;;; Ray Andrade Defect# 80291 Change Team ;;;;;;;;;;;;;;;;;;;;;
        sti                                     ; enable interrupts(moved here)
    ASSUME      DS:ClkData                      ;
        CALL    FAR PTR GTDAYS                  ; Convert to day count
        MOV     WORD PTR ES:[DI],AX             ; Stuff it in caller's buffer
        MOV     DL,DevHlp_UnPhysToVirt           ; call UnPhysToVirt
        CALL    [DevHlp]                        ; original addr mode restored
        JC      CONVERT_FAIL
        POPF                                    ; restore flags
        MOV     AX,(STDON OR STBUI)             ; No error
        POP     BP

readabort:
        RET

RTREAD  ENDP


BREAK <RealTime Clock Generic IOCTL routine>
;********************** START OF SPECIFICATIONS *********************
;*
;* MODULE NAME:  RTIOCTL
;*
;* DESCRIPTIVE NAME:  Realtime Clock Generic IOCTL routine
;*
;* FUNCTION:  Supports the category 13 generic IOCTL commands:
;*            - READ CMOS byte (function 60h)
;*      by calling the appropriate worker routine.
;*
;* ENTRY POINT: RTIOCTL
;*    LINKAGE:  NEAR from RTENTR
;*
;* USES:    AX and flags.
;*
;* INPUT:
;*      ES:BX = pointer to Request Block  (PktData)
;*
;* EXIT-NORMAL:
;*      Exit is via return to RTENTR (Strategy routine) with
;*      AX = Status word to be stored in request packet status
;*      field (DONE).
;*
;* EXIT-ERROR:
;*    RETURN CODE:      AX = Invalid command
;*
;* INTERNAL REFERENCES:
;*    ROUTINES:    GTCMOS
;*
;* EXTERNAL REFERENCES:
;*    STRUCTURES:
;*    ROUTINES:
;*
;*********************** END OF SPECIFICATIONS **********************
page
;********************** START OF PSEUDO-CODE   **********************
;
; if requesting IOCTL is supported
;    call worker routine
; else
;    set AX to unknown command
;
; return
;
;*********************** END OF PSEUDO-CODE    **********************


    ASSUME      CS:ClkSwap,DS:ClkData,ES:NOTHING,SS:NOTHING
RTIOCTL PROC NEAR
        public  RTIOCTL

        cmp     byte ptr es:[bx].GIOCategory, IOC_CK ; Correct category?
        jne     rti10                   ; no, error exit
        cmp     byte ptr es:[bx].GIOFunction, IOCK_RD ; Supported function?
        jne     rti10                   ; no, error exit
        call    GTCMOS                  ; call worker routine
        jmp     short rti20             ; then exit

rti10:
        mov     ax, STDON+STERR+ERRCMD  ; indicate unknown command

rti20:
        ret

RTIOCTL ENDP

BREAK <RealTime Clock Get CMOS byte routine>
;********************** START OF SPECIFICATIONS *********************
;*
;* MODULE NAME:  GTCMOS
;*
;* DESCRIPTIVE NAME:  Realtime Clock Get CMOS byte routine
;*
;* FUNCTION:  Reads a byte from standard CMOS memory location 0Ah or
;*            higher.
;*
;* ENTRY POINT: GTCMOS
;*    LINKAGE:  NEAR from RTIOCTL
;*
;* USES:    AX and flags.
;*
;* INPUT:
;*      ES:BX = pointer to Request Block  (PktData)
;*
;* EXIT-NORMAL:
;*      Exit is via return to RTIOCTL with
;*      AX = Status word to be stored in request packet status
;*      field (DONE).
;*
;* EXIT-ERROR:
;*    RETURN CODE:      AX = Invalid parameter.
;*
;* INTERNAL REFERENCES:
;*    ROUTINES:    ClkReadCMOS
;*
;* EXTERNAL REFERENCES:
;*    STRUCTURES:
;*    ROUTINES:
;*
;*********************** END OF SPECIFICATIONS **********************
page
;********************** START OF PSEUDO-CODE   **********************
;
; if can't access parameter or data
;    set AX to invalid parameter
;    return
; if requested byte >= 0Ah
;    call worker routine
; else
;    set AX to invalid parameter
;
; return
;
;*********************** END OF PSEUDO-CODE    **********************


    ASSUME      CS:ClkSwap,DS:ClkData,ES:NOTHING,SS:NOTHING
GTCMOS  PROC NEAR
        public  GTCMOS

        push    bx
        push    cx
        push    dx
        push    di
        push    si

        mov     ax,word ptr es:[bx].GIOParaPack + 2
        mov     di,word ptr es:[bx].GIOParaPack
        mov     cx, 2                   ; parm. length
        xor     dh, dh                  ; read access
        mov     dl, DevHlp_VerifyAccess
        call    [DevHlp]                ;verify access to parameter packet
        jc      gt10

        push    es
        push    bx
        les     bx, es:[bx].GIOParaPack ; get parameter address
        mov     al, byte ptr es:[bx]    ; get the requested CMOS addr.
        inc     bx                      ; point to length byte
        mov     ah, byte ptr es:[bx]    ; get the requested number of bytes
        pop     bx
        pop     es
        cmp     al, ADDR_STATUSA        ; is request one of the RTC bytes?
        jl      gt10                    ; ..yes, so exit with error
        cmp     al, ADDR_CMOSRAMHI      ; is request past the max. value?
        jg      gt10                    ; ..yes, so exit with error
        cmp     ah, 0                   ; is number of bytes requested 0?
        je      gt10                    ; ..yes, so exit with error

        xor     ch, ch                  ; clear high byte
        mov     cl, ah                  ; data length
        push    ax                      ; save CMOS request and length
        mov     ax,word ptr es:[bx].GIODataPack + 2
        mov     di,word ptr es:[bx].GIODataPack
        mov     dh, 1                   ; read/write access
        mov     dl, DevHlp_VerifyAccess
        call    [DevHlp]                ; verify access to data packet
        pop     ax                      ; restore CMOS request and length
        jc      gt10

        les     di, es:[bx].GIODataPack ; get data address
        mov     cmos_req.rdrq_CMOSAddr, al ; put user value in request packet
        mov     cmos_req.rdrq_nbCMOS, ah ; number of CMOS bytes to read
        mov     si, offset cmos_req     ; setup calling parms
        mov     cx, ds                  ; (cx:si)->CMOS ReadReq packet
        mov     dx, es                  ; (dx:di)->byte array for result

        push    eax                     ; save 32 bit register
        CALLFAR ClkReadCMOS             ; get the value from CMOS
        pop     eax                     ; restore 32 bit register
        mov     ax, STDON               ; set good return code
        jmp     short gt20              ; then exit

gt10:
        mov     ax, STDON+STERR+13h     ; invalid parameter status

gt20:
        pop     si
        pop     di
        pop     dx
        pop     cx
        pop     bx
        ret

GTCMOS  ENDP


ClkSwap ENDS


BREAK <Clock Device Driver Utility Subroutines>
;********************** START OF SPECIFICATIONS *********************
;*
;* MODULE NAME: FixDayOfWeek
;*
;* DESCRIPTIVE NAME:  Get the right day of week
;*
;* FUNCTION:
;*      This computes the correct day of week (DOW) if the value read
;*      from the clock was 0 (uninitialized).  The DOW is computed based on
;*      the specified date (Day/Month/Year).  If the date requested is
;*      invalid, the date returned is 1-1-1980 (Tues).
;*
;* NOTE:
;*      ABIOS doesn't keep the DOW.  On NOT AT hdwr' this routine is
;*      always called with DOW = CH = 0.
;*
;* ENTRY POINT: FixDayOfWeek
;*    LINKAGE:  NEAR from FIXISEG
;*
;* USES:  Preserves all registers except CX, Flags
;*
;* INPUT:
;*    AX = Days since 1-1-80
;*    DH =  Month
;*    DL =  Day
;*
;* OUTPUT:
;*    CH =  Day Of Week (0-6)
;*
;* EXIT-NORMAL: CH = day of week (0-6)
;*
;* EXIT-ERROR: none
;*
;* INTERNAL REFERENCES:
;*    ROUTINES: GTDAYS
;*
;* EXTERNAL REFERENCES:
;*    ROUTINES: none
;*
;*********************** END OF SPECIFICATIONS **********************
page
;********************** START OF PSEUDO-CODE   **********************
;
;  day_of_week = ( days + 2 ) % 7
;
;*********************** END OF PSEUDO-CODE    **********************

ClkCode SEGMENT

;
; this data must be in CS for INT xx intercept to work
;
saveDS          dw      ?               ; DS for INT xx entry

;
; The REAL strategy routine is in the swappable code segment
;
    ASSUME      CS:ClkCode,DS:ClkData,ES:NOTHING,SS:NOTHING
ClkStrat        PROC FAR
        public  ClkStrat
        jmp     RTENTR
ClkStrat        ENDP

; start d196223

Public  EOIUniRTC, EOISMPRTC
Public  EOIUniTMR, EOISMPTMR

Procedure EOIUniRTC,near
        DevEOI  <RTCIRQ>,DevHlp         ; use macro for inline EOI
        ret
EndProc   EOIUniRTC

Procedure EOISMPRTC,near
        MOV     AL,RTCIRQ               ;
        MOV     DL,DevHlp_EOI           ;  call devhelp router to EOI
        CALL    [DevHlp]                ;
        ret
EndProc   EOISMPRTC

Procedure EOIUniTMR,near
        DevEOI  <TIMERIRQ>,DevHlp       ; use macro for inline EOI
        ret
EndProc   EOIUniTMR

Procedure EOISMPTMR,near
        MOV     AL,TIMERIRQ             ;
        MOV     DL,DevHlp_EOI           ;  call devhelp router to EOI
        CALL    [DevHlp]                ;
        ret
EndProc   EOISMPTMR

; end d196223

    ASSUME      CS:ClkCode,DS:ClkData,ES:NOTHING,SS:NOTHING
FixDayOfWeek    PROC NEAR
        public  FIXDAYOFWEEK

        SaveReg <AX,BX,DX>
        add     ax,2                    ; 1-1-80 was a Tuesday
        mov     bx,7                    ; 7 days per week
        xor     dx,dx
        div     bx                      ; ax = # of weeks, dx = day of week
        mov     ch,dl
        RestoreReg <DX,BX,AX>
        ret
FixDayOfWeek    ENDP


;********************** START OF SPECIFICATIONS *********************
;*
;* MODULE NAME:  GTDAYS
;*
;* DESCRIPTIVE NAME:  Calculate days since 1-1-80
;*
;* FUNCTION:
;*    This routine calculates from day/month/year the number of
;*    days elapsed since 1-1-80 and returns it to the caller.
;*
;*
;* ENTRY POINT: GTDAYS
;*    LINKAGE:  FAR from RTREAD and FIXISEG
;*
;* USES:  Preserves all registers except AX
;*
;* INPUT: (PARAMETERS)
;*    AX = Years since 1-1-80
;*    DH = Current month
;*    DL = Current day
;*
;* OUTPUT: (RETURNED)
;*    AX = Days since 1-1-80
;*
;* EXIT-NORMAL: AX = 0 to 65535
;*
;* EXIT-ERROR:  none
;*
;* INTERNAL REFERENCES:
;*    ROUTINES: none
;*
;* EXTERNAL REFERENCES:
;*    ROUTINES: none
;*
;*********************** END OF SPECIFICATIONS **********************
page
;********************** START OF PSEUDO-CODE   **********************
;
; if Years is leap year
;    set Feb = 29
; else
;    set Feb = 28
;
; Number of 4 years group = Years / 4
; Number of non 4 years group = Years % 4
; Days = DAYSIN4YR * Number of 4 years group + 366
;        + DAYSINYR * (Number of non 4 years group - 1)
;                    + ( days of months ) + (current day - 1)
; return( Days )
;
;*********************** END OF PSEUDO-CODE    **********************

    ASSUME      CS:ClkCode,DS:ClkData,ES:NOTHING,SS:NOTHING
GTDAYS  PROC    FAR
        public  GTDAYS

        PUSH    CX                      ; Preserve only what we use
        PUSH    DX
        PUSH    BX
; Number of non 4 years group = Years % 4
        MOV     BL,AL                   ; Copy years to CL
        AND     BL,3                    ; CL = Years Modulo 4

; Number of 4 years group = Years / 4
        XOR     AH,AH                   ; Insure no high byte
        SHR     AX,1
        SHR     AX,1                    ; AX = # of 4 Year groups
        JZ      NOADD1                  ; If none, skip over days calculation

; DAYSIN4YR = 5B5x = 1461 = 365 * 3 + 366
; The followings are DAYSIN4YR * Number of 4 years group + 366
        MOV     CX,AX                   ; ...and use # of 4 year groups...
        XOR     AX,AX                   ; ...as add loop counter
ADD4YR: ADD     AX,DAYSIN4YR            ; Add one "quadyear" of days
        LOOP    ADD4YR                  ; Loop till done

; if Years is leap year
;    set Feb = 29
; else
;    set Feb = 28
NOADD1: OR      BL,BL                   ; Any modulo? (0 == Leap year)
        MOV     MonTab+1,29             ; Init Feb to 29 days for leap year
        JZ      NOADD2                  ; Is leap year, leave Feb and skip add
        DEC     MonTab+1                ; Not leap year, DEC Feb and add rmndr

; DAYSINYR = 16Ex = 365
; The followings are DAYSIN4YR * Number of 4 years group + 366
;        + DAYSINYR * (Number of non 4 years group - 1)
;
        ADD     AX,DAYSINYR+1           ; First add the leap year of the rmnd
ADDAGN: DEC     BL                      ; Any more years?
        JZ      NOADD2                  ; No, all years have been added
        ADD     AX,DAYSINYR             ; Yes, add in one normal year
        JMP     SHORT ADDAGN            ; Loop till remainder is zero

; The followings are DAYSIN4YR * Number of 4 years group + 366
;        + DAYSINYR * (Number of non 4 years group - 1)
;                    + (current day - 1)
NOADD2: DEC     DL                      ; DL = DOM, use full days only
        ADD     AL,DL                   ; Add DOM, low byte...
        ADC     AH,0                    ; ...then high

; The followings are DAYSIN4YR * Number of 4 years group + 366
;        + DAYSINYR * (Number of non 4 years group - 1)
;                    + ( days of months ) + (current day - 1)
;
        MOV     BX,OFFSET MonTab        ; Point to beginning of month table
NXMNTH: DEC     DH                      ; Out of months?
        JZ      GOTMOS                  ; Yes, don't add any(more)
        ADD     AL,BYTE PTR [BX]        ; Add in this month's day count...
        ADC     AH,0                    ; ...and carry to high byte
        INC     BX                      ; Point to next month
        JMP     SHORT NXMNTH            ; Keep going

GOTMOS:
        POP     BX                      ; Give him back his stuff
        POP     DX
        POP     CX
        RET

GTDAYS  ENDP


ClkCode ENDS

        END
