home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
ddkx86v5.zip
/
DDKX86
/
SRC
/
DEV
/
CLOCK
/
CLOCK02
/
CLOCK02.ASM
< prev
next >
Wrap
Assembly Source File
|
1995-04-14
|
72KB
|
1,920 lines
;*DDK*************************************************************************/
;
; COPYRIGHT (C) Microsoft Corporation, 1989
; COPYRIGHT Copyright (C) 1995 IBM Corporation
;
; The following IBM OS/2 WARP source code is provided to you solely for
; the purpose of assisting you in your development of OS/2 WARP device
; drivers. You may use this code in accordance with the IBM License
; Agreement provided in the IBM Device Driver Source Kit for OS/2. This
; Copyright statement may not be removed.;
;*****************************************************************************/
;SCCSID = @(#)clock02.asm 6.13 91/10/22
title clock device driver -- PS/2 [clock02.asm]
; ****************************************************************************
; * *
; * *
; * *
; ****************************************************************************
PAGE 60,132
;
.386p ; assemble for iAPX 386
.xlist
; define MONITOR and/or MISCSTRICT to get help when debugging, these are
; NOT to be defined for a retail build.
;;MONITOR EQU 1 ; builds in monitoring code
;;MISCSTRICT EQU 1 ; turns on extra checking
INCL_ERRORS EQU 1
include bseerr.inc ; error numbers
INCL_MI EQU 1
INCLUDE mvdm.inc ; equates and data for MVDM support
INCLUDE devhlp.inc ; the devhlp function eqs
INCLUDE oldmaca.inc
INCLUDE infoseg.inc ; definitions of Global Info Segment
INCLUDE devsym.inc ; definition of DD Request Packet
INCLUDE rtc.inc ; Real Time Clock info
INCLUDE clkabios.inc ; Structs for calling ABIOS
INCLUDE clkdata.inc ; Device driver specific
INCLUDE pmode.inc
INCLUDE abios.inc
INCLUDE timer.inc ; timer definitions
INCLUDE clkseg.inc ; segment definitions
INCLUDE pvwxport.inc
INCLUDE vtdptd.inc ; vtimer/ptimer interface
INCLUDE devhlpP.inc ;76711
.list
CPUMode 386
extrn GTDAYS:Far
extrn FIXDAYOFWEEK:Near
extrn ClkVDDProc:near ; Clk router routine
extrn PTBeep:near ; beep service entry point
extrn PTBeepOff:near ; service to turn speaker off
extrn PTVDMProc:near ; vtimer/ptimer interface entry point
extrn PTTimer0:near ; timer 0 ownership arbitration serv.
extrn selClkData:word ; BUGBUG loader should resolve
ClkData SEGMENT
; data segment externals
extrn DevHlp:DWORD
extrn SchedClock:DWORD
extrn InfSeg:DWORD
extrn MonTab:BYTE
extrn Update_Flag:BYTE
extrn Post_Time:WORD
extrn Int_Nested:BYTE
extrn Int_In_Progress:BYTE
extrn ATDataEnd:ABS
extrn ReqRTCFreq:BYTE
extrn CurRTCFreq:BYTE
extrn Hundred_Whole:BYTE
extrn Hundred_Frac:BYTE
extrn Hundred_Inc:WORD
extrn MS_Fraction:WORD
extrn MS_Inc_Whole:DWORD
extrn MS_Inc_Frac:WORD
extrn MS_Delta:WORD
extrn Hz128To32:BYTE
extrn Accum8msTicks:WORD
extrn ExpectedMsCount:DWORD
ifdef MONITOR
extrn LagCount:DWORD
extrn LagTotal:DWORD
extrn Accm:DWORD
extrn Accm8:WORD
extrn Accm8p:WORD
extrn Accm8mod4:WORD
extrn Ticks8:DWORD
extrn Ticks32:DWORD
endif
extrn szClkName:byte ; device name for PCLK registration
extrn szTmrName:byte ; device name for PTIMER registration
extrn fpfnVTProc:FWORD ; vtimer entry point
extrn fsPTFlags:WORD ; ptimer flags
extrn pqwTmrRollover:DWORD
extrn qwTmrRollover:DWORD
extrn pqwTmr:DWORD
extrn qwTmr:BYTE
;
; Data area for calling ABIOS
; BUGBUG -- some of this can go away. Allocate for RTINIT then destroy.
;
ABIOS_Clock_Parms ABIOS_Parms <> ; ABIOS Clock Info
ClockLID DW ? ; ABIOS RTC Logical I.D.
TaskPkt DW ? ; These 4 slots are established to
PeriodPkt DW ? ; hold the actual offsets for each
UpdatePkt DW ? ; request block in the AbiosRB area
ExtraPkt DW ? ;
AbiosRB DB MAXBRB*4 dup (0) ; ABIOS Request Block structure
; Reserve space for 4 RBs here
; and resize the segment at INIT time
ClkData ENDS
BREAK <Realtime Clock Write Routine>
;********************** START OF SPECIFICATIONS *********************
;*
;* MODULE NAME: RTWRIT
;*
;* DESCRIPTIVE NAME: Realtime Clock Write routine
;*
;* FUNCTION: Supports the Device Driver functions:
;* - WRITE (function 8)
;* - WRITE WITH VERIFY (function 9)
;* by setting the Realtime Clock device with the date/time
;* information and updating the Global InfoSeg date/time
;* variables accordingly.
;*
;* DevHlp_PhysToVirt converts the real address of the data buffer
;* in the Device Driver Request Block (PktData) to a virtual
;* address. DevHlp_UnPysToVirt later restores the physical
;* memory address.
;*
;* ENTRY POINT: RTWRIT
;* LINKAGE: NEAR from RTENTR
;*
;* USES: AX, DX. Preserves others. (RU uses ES, DI)
;*
;* INPUT: (PARAMETERS)
;* ES:BX = pointer to Request Block (PktData)
;* (SP+4):(SP+2) = pointer to Data Buffer
;*
;* OUTPUT: (retURNED)
;* AX = Status word to be stored in request packet status field.
;*
;* EXIT-NORMAL:
;* retURN CODE: AX = BUSY and DONE
;*
;* EXIT-ERROR:
;* retURN CODE: AX = ERROR and DONE
;*
;* INTERNAL REFERENCES:
;* STRUCTURES: ABIOS Request Block
;* ROUTINES: calc_year_sec
;*
;* EXTERNAL REFERENCES:
;* STRUCTURES: Global Information Segment (SysInfoSeg)
;* ROUTINES: DevHlp_PhysToVirt, DevHlp_ABIOSCall,
;* DevHlp_UnPhysToVirt
;*********************** END OF SPECIFICATIONS **********************
page
;********************** START OF PSEUDO-CODE **********************
;
; if caller does not request 6 bytes
; goto write_fail
; call DevHlp_PhysToVirt to convert data buffer's physical addr to
; virtual address in ES:DI
; if DevHlp_PhysToVirt fail
; goto write_fail
; get days since 1-1-80 from user buffer
; day_count = no of days not in 4 year groups = days % 1461
; year_count = no of years in 4 year groups = ( days / 1461 ) * 4
; if day_count > 366
; day_count -= 366
; year_count++
; while ( day_count > 365 )
; day_count -= 365
; year_count++
; endwhile
; endif
;
; Normalize year_count ( year_count += 80 )
; if year_count in 20xx ( year_count >= 100 )
; year_count -= 100
; Century = 20x
; else Century = 19x
;
; if current date in leap year
; set Feb = 29
; else
; set Feb = 28
;
; for month = 1 to 12
; if day_count >= MonTab[ month ]
; break
; else
; day_count -= MonTab[ month ]
; endfor
;
; day_of_week = ( days + 2 ) % 7 + 1
; convert year, month, day_count, & day_of_week to BCD
;
; get seconds, minutes, & hours from user buffer and convert them to BCD
; save both binary and BCD values on reserved stack
; get hundredths of seconds from user buffer and save on reserved stack
; invoke ABIOSCALL to write seconds, minutes, hours, day_of_week, day_count,
; month, & year to chip
; if ABIOSCALL fail
; set return code = DONE+ERROR+GENERAL FAILURE
; call DevHlp_UnPhysToVirt to get original address mode
; enable interrupts
; if DevHlp_UnPhysToVirt fail
; goto write_fail
; return
; endif
; get hundredths of seconds from user buffer and save on reserved stack
;
; call DevHlp_UnPhysToVirt to get the original address mode
; if DevHlp_UnPhysToVirt fail
; goto write_fail
;
; call calc_year_sec to get seconds, minutes, hours, day_of_week, day_count,
; month, year, & hundredths of seconds from reserved stack to put in
; SysInfoSeg
; enable interrupts
; set return code = DONE+BUSY
; return
;
; write_fail:
; set return code = DONE+ERROR+WRITE FAULT
; return
;
;*********************** END OF PSEUDO-CODE **********************
ClkSwap SEGMENT
century_year equ [bp+8]
year_bin equ [bp+7]
month_bin equ [bp+6]
day_month_bin equ [bp+5]
day_week_bin equ [bp+4]
hour_bin equ [bp+3]
minute_bin equ [bp+2]
second_bin equ [bp+1]
hundredth_bin equ [bp]
ASSUME CS:ClkSwap,DS:ClkData,ES:NOTHING,SS:NOTHING
RTWRIT PROC NEAR
public RTWRIT
cmp ES:[BX].IOcount,RW_BYTES ; requesting 6 bytes ?
je short WRITE ; yes, continue....
WT_ERR:
mov AX,(STDON+STERR+0ah) ; Else, quit w/ error.
ret
WRITE: push BP
sub sp,10 ; reserved 10 bytes for clock
mov BP,SP
;; The addresses passed in the request packets are 32-bit physical addresses.
mov CX,6
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
call [DevHlp] ; ES:DI points to buffer
jnc short WRITE_OK
WRITE_FAIL:
add sp,10 ; restore stack pointer
pop BP
jmp short WT_ERR
; get days since 1-1-80 from user buffer
WRITE_OK:
mov AX,ES:[DI] ; Get days since 1-1-80 from buffer
; day_count = no of days not in 4 year groups = days % 1461
; year_count = no of years in 4 year groups = ( days / 1461 ) * 4
mov CX,DAYSIN4YR ; No of days in four year group
xor DX,DX ; Clear remainder word
div CX ; [AX] = No of four year groups
shl AX,1
shl AX,1 ; [AX] = No of years in 4 year groups
push AX ; Save (Note: 120 years max/[AH] = 0)
; if day_count > 366
; day_count -= 366
; year_count++
; while ( day_count > 365 )
; day_count -= 365
; year_count++
; endwhile
; endif
xor CX,CX ; [CL] = 0 = extra year counter
mov AX,DAYSINYR+1 ; Year zero is a leap year
YRCMP: cmp DX,AX ; Is date within year zero?
jc short INYEAR ; Yes, don't subtract
sub DX,AX ; No, sub days for this year
inc CL ; Increment counter
mov AX,DAYSINYR ; Days in years one and two
jmp short YRCMP ; Loop until date in current year
; Normalize year_count ( year_count += 80 )
; if year_count in 20xx ( year_count >= 100 )
; year_count -= 100
; Century = 20x
; else Century = 19x
INYEAR: pop AX ; Restore partial year count
push SI ; Save SI
mov SI,TaskPkt ; DS:SI points to ABIOS task-time RB
mov [SI].Parms.century,19H ; Set century = 19x
add AL,CL ; add in 1 to 3 remainder years
add AL,80 ; Normalize for 19xx
cmp AL,100 ; Year in 19xx?
JB short IS19XX ; Yes, AL = actual year (M003)
sub AL,100 ; No, normalize for 20xx
mov [SI].Parms.century,20H ; Set century = 20x
IS19XX: mov year_bin,al ; save year in binary
BN2BCD ; Convert year to BCD
mov [SI].Parms.year,al ; Stuff Year in the ABIOS req blk
; if current date in leap year
; set Feb = 29
; else
; set Feb = 28
mov MonTab+1,28 ; Init Feb to 28 days
OR CL,CL ; Current date in year zero?
JNZ short NOINC ; No, leave at 28
inc MonTab+1 ; Yes, add one day for leap year
; for month = 1 to 12
; if day_count >= MonTab[ month ]
; break
; else
; day_count -= MonTab[ month ]
; endfor
NOINC: mov CL,1 ; At least at first month in year
mov SI,OFFSET MonTab ; Table of days in each month
xor AH,AH
CLD ; Insure increment
CHECK1: LODSB ; Get count of days this month
cmp DX,AX ; Date within this month?
jc short FOUND ; Yes, month is found
sub DX,AX ; No, sub this month's days and...
inc CX ; ...count one more month/year
jmp SHORT CHECK1 ; Loop until month is found
FOUND: mov AL,CL ; Month number to AL
mov month_bin,al ; save month/binary
BN2BCD ; Convert to BCD
mov SI,TaskPkt ; DS:SI points to ABIOS task-time RB
mov [SI].Parms.month,al ; Stuff month in the ABIOS req blk
inc DL ; Remainder is day of month(1-31)
mov AL,DL
mov day_month_bin,al ; save day of month/binary
BN2BCD ; Convert DOM to BCD
mov [SI].Parms.day,al ; Stuff DOM in the ABIOS req blk
mov AL,ES:[DI+5] ; Get seconds value
mov second_bin,al ; save second in binary
BN2BCD ; Convert it
mov [SI].Parms.seconds,AL ; Stuff it in the ABIOS req blk
mov AL,ES:[DI+2] ; Get minutes value
mov minute_bin,al ; save minute in binary
BN2BCD ; Convert it
mov [SI].Parms.minutes,AL ; Stuff it in the ABIOS req blk
mov AL,ES:[DI+3] ; Get hours value
mov hour_bin,al ; save hour in binary
BN2BCD ; Convert it
mov [SI].Parms.hours,AL ; Stuff it in the ABIOS req blk
mov [SI].Function,ABWRITTD ; Function is Write Time/Date
mov AX,ClockLID ; Get LID in AX
xor DH,DH ; Calling ABIOS Start Entry point
push DI
LEA DI, DS:ABIOS_Clock_Parms
CallABIOS
pop DI
; mov DL,DevHlp_ABIOSCall
; call [DevHlp] ; call ABIOS to set clock
pop SI ; Restore SI
jc short ABErr1 ; Jump if carry flag set
; no interrupts during InfoSeg access
mov AL,ES:[DI+4] ; Get hundredths of seconds
mov hundredth_bin,al ; save on reserved stack
mov DL,DevHlp_UnPhysToVirt ; call UnPhysToVirt
call [DevHlp] ; original addr mode restored
jnc short WRITE_INFOSEG
jmp WRITE_FAIL
write_infoseg:
call far ptr calc_year_sec ; update sysinfoseg(interrupt disable)
; es:bx=>SysInfoSeg
mov al,hundredth_bin ; get hundredth
mov ES:[BX.SIS_HunTime],AL ; Stuff it in InfoSeg field
sti ; enable interrupts
mov AX,(STDON OR STBUI) ; No errors
WRITE_DONE:
add sp,10 ; restore stack pointer
pop BP
ret
ABErr1:
mov AX,(STDON+STERR+ERRGFAIL) ; set error code for general failure
mov DL,DevHlp_UnPhysToVirt ; call UnPhysToVirt
call [DevHlp] ; original addr mode restored
jnc short WRITE_DONE
jmp WRITE_FAIL
RTWRIT ENDP
ClkSwap ENDS
;***LP CLKFreq(ulFreq) - Set RTC Periodic Frequency service
;
; CLKFreq is called by the kernel to request a new frequency at
; which real time clock periodic "ticks" are generated. (See DCR 1345)
; The actual reprogramming of the device is delayed until such time
; as it can be done safely: interrupts disabled, pre-EOI, and on the
; next 32ms tick boundary
;
; ENTRY
; (TOS) = ulFreq : desired frequency in Hz.
; Only 32 and 128 supported.
; EXIT
; SUCCESS
; (eax) = 0
; FAILURE
; (eax) = ERROR_INVALID_FREQUENCY
;
; CONTEXT
; Any (called by TomTick; see task\tom.c)
;
; PSEUDOCODE
; if ulFreq is 32 or 128
; record new frequency desired; the actual change
; will occur on the next 32ms boundary
; return rc = no errors;
; else
; return rc = invalid frequency
;
ClkCode SEGMENT
ASSUME CS:ClkCode,DS:NOTHING,ES:NOTHING,SS:NOTHING
Procedure CLKFreq,far
.386p
?abase = 8 + 2
ArgVar ulFreq,ULONG
EnterProc
SaveReg <ds>
mov ds,cs:[selClkData]
ASSUME DS:ClkData
; check if requested frequency is allowed
cmp WORD PTR [ulFreq],128 ; is frequency 128Hz?
je short clkfreq_valid ; .. yes, its valid
cmp WORD PTR [ulFreq],32 ; is frequency 32Hz?
jne short clkfreq_invalid ; ...... no, so fail
clkfreq_valid:
mov ax,WORD PTR [ulFreq]
mov ReqRTCFreq,al ; record requested new frequency
xor eax,eax ; (eax)=ERROR_NONE
clkfreq_exit:
RestoreReg <ds>
ASSUME DS:NOTHING
LeaveProc
retfd ?aframe
clkfreq_invalid:
mov eax,ERROR_INVALID_FREQUENCY ; errors
jmp short clkfreq_exit
EndProc CLKFreq
BREAK <Realtime Clock Interrupt Service Routine>
;********************** START OF SPECIFICATIONS *********************
;*
;* MODULE NAME: RTCINT
;*
;* DESCRIPTIVE NAME: Interrupt Service routine for RT/CMOS Clock
;*
;* FUNCTION:
;* This routine services RT/CMOS Clock interrupts, giving
;* control to CP/DOS. The entry point is associated with IRQ 8
;* using the DevHlp SetIRQ function in RTINIT. A periodic timer
;* interrupt causes it to be invoked and the routine uses the
;* DevHelp_SchedClock function first (before EOI) to inform
;* CP/DOS of the timer tick and again later (after EOI) to allow
;* SchedClock to perform any lengthy periodic operations.
;*
;* On every update-ended interrupt, the clock device is read
;* and the Global InfoSeg is updated with interrupts disabled.
;*
;* NOTES:
;* Following issue of DevHlp_EOI to the Interrupt Manager (before
;* the second SchedClock call) it is possible for this routine
;* to be reentered. Stack usage is kept to a minimum. Re-
;* entrance should not cause problems, since no real processing
;* is done by this routine after that point.
;*
;* ENTRY POINT: RTCINT
;* LINKAGE: FAR call by Interrupt Manager for IRQ Level = 8
;*
;* USES: All registers.
;*
;* INPUT: DS, Cs set up by Intmgr on call to RTCINT
;*
;* EXIT-NORMAL: ret FAR
;* retURN CODE: CF = 0 if my-interrupt
;* CF = 1 if not-my-interrupt
;*
;* EXIT-ERROR:
;* retURN CODE:
;* ERROR MESSAGE:
;*
;* INTERNAL REFERENCES:
;* STRUCTURES: ABIOS Request Block
;* ROUTINES: FIXISEG
;*
;* EXTERNAL REFERENCES:
;* STRUCTURES: Global Information Segment (SysInfoSeg)
;* ROUTINES: DevHlp_SchedClock, DevHlp_ABIOSCall,
;* DevHlp_EOI
;*
;*********************** END OF SPECIFICATIONS **********************
page
;********************** START OF PSEUDO-CODE **********************
;
; enable interrupts
; invoke ABIOSCALL with periodic interrupt request block to
; obtain Register C value for getting interrupt flags
; if update-ended interrupt flag set
; update_flag = 1
; if periodic interrupt flag not set
; goto EOI
; endif
;
; disable interrupts to keep hundredths and milliseconds in sync
; Accum8msTicks++;
; increment hundredths by time (in hundredths) since last tick
; increment millseconds by time (in milliseconds) since last tick
; if Hz128To32 (first 32hz tick since RTC programmed from 128->32hz)
; set increments to 1/32 sec and clear Hz128To32 flag
; if pending request to reprogram RTC frequency
; if requested RTC frequency is 128hz
; change rtc frequency to 128hz
; change interval to 1/128
; clear accumulated 8ms tick counter (Accum8msTicks)
; clear pending request to reprogram RTC frequency
; else (requested RTC frequency is 32hz)
; if clock is on a 32ms boundary
; change rtc frequency to 32hz
; change interval to zero
; set Hz128To32 to indicate transition
; clear pending request to reprogram RTC frequency
; enable interrupts
;
; update_flag = 0
; if previous update_flag != 0
; call FIXISEG to read clock to update SysInfoSeg
; endif
;
; increment system time by Interval
; subtract off IntervalAdjustment
; if IntervalAdjustment wasn't 0
; IntervalAdjustment = 0
;
; post_time += interval
;
; call SchedClock to do pre-EOI with AX = interval
; if we are nested (i.e. Int_In_Progress != 0 )
; increment nest_count
; goto EOI
; endif
; Int_In_Progress = 1
; call DevHlp to issue EOI
;
; nest_count = 0
; call chkINT15 to process int 15 with previous nest_count value
;
; call SchedClock to do post-EOI with AX = post_time
; Int_In_Progress = 0
;
; clear carry flag
; return
;
; EOI:
; call DevHlp to issue EOI
; clear carry flag
; return
;
;*********************** END OF PSEUDO-CODE **********************
ASSUME CS:ClkCode,DS:ClkData,ES:NOTHING,SS:NOTHING
RTCINT PROC FAR
public RTCINT
; The int mgr does a pusha
STI ; Enable interrupts where safe
; SERVICE INTERRUPT
mov AX,ClockLID
mov SI,PeriodPkt ; use the Periodic Interrupt Request Block
mov [SI].returnCode,retCODE_IN
mov DH,1
LEA DI, ds:ABIOS_Clock_Parms
CallABIOS
cmp [si].returnCode,1 ; IF ABIOS returns "My Interrupt"
jne EOI ; EXIT, not a clock device interrupt
mov AL,[SI].Parms.C_Reg ; Set up AL with the C Register Value
; if update-ended interrupt flag set
; update_flag = 1
; if periodic interrupt flag not set
; goto EOI & return
; endif
test AL,UIMASK ; Update Interrupt??
JZ short GotPeriodic ; NO, go process the Periodic Int.
mov Update_Flag,1 ; Set the Update flag
test AL,PIMASK ; Also a periodic interrupt?
jz EOI ; No, EOI and return...
; disable interrupts to keep hundredths and milliseconds in sync
; Accum8msTicks++;
; increment hundredths by time (in hundredths) since last tick
; increment millseconds by time (in milliseconds) since last tick
; if Hz128To32 (first 32hz tick since RTC programmed from 128->32hz)
; set increments to 1/32 sec and clear Hz128To32 flag
; if pending request to reprogram RTC frequency
; if requested RTC frequency is 128hz
; change rtc frequency to 128hz
; change interval to 1/128
; clear accumulated 8ms tick counter (Accum8msTicks)
; clear pending request to reprogram RTC frequency
; else (requested RTC frequency is 32hz)
; if clock is on a 32ms boundary
; change rtc frequency to 32hz
; change interval to zero
; set Hz128To32 to indicate transition
; clear pending request to reprogram RTC frequency
; enable interrupts
GotPeriodic:
cli ; disable ints during update; keep everything synchronous
inc Accum8msTicks; ; only needed if at 128hz, but do always
; increment hundredths by time (in hundredths) since last tick
les BX,[InfSeg] ; ES:BX -> Global InfoSeg
mov ah,BYTE PTR ES:[BX.SIS_HunTime] ; Get current hundredths
mov al,Hundred_Frac ; align fraction part we keep
add ax,Hundred_Inc ; add in hundredths tick inc.
mov Hundred_Frac,al ; save new fractional part
;** the following is a to account for the case where a user sets the
; hundredths field ahead, such that ensuing ticks will cause the
; hundredths to exceed 100. We prevent this condidtion. Note that
; this causes the 100ths to "freeze" at 99 until the next 1 second update
; interrupt advances the seconds. Ref: PTM 6670
cmp ah,100 ; over 100?
jb short NoOverflow ; ..no,
mov ah,99 ; ..yes, DO NOT allow it > 100
NoOverflow :
mov BYTE PTR ES:[BX.SIS_HunTime],ah ; update Hundredths
; increment millseconds by time (in milliseconds) since last tick
mov ax,MS_Inc_Frac ; get fraction of millisecond increment
add MS_Fraction,ax ; add to running millisecond fraction
mov eax,ES:[BX.SIS_MsCount] ; get system's ms count (whole)
mov ecx,eax ; save to determine actual ms delta
adc eax,MS_Inc_Whole ; add in the whole increment part
mov ES:[BX.SIS_MsCount],eax ; update the system's ms count
sub eax,ecx ; compute actual ms delta
mov MS_Delta,ax ; save delta in word
cmp Hz128To32,0 ; First 32hz tick after reprogramming?
je short NoAdjust ; .. no, increment is ok as is.
mov Hundred_Inc,((100 SHL 8) / 32); .. yes, set increments to 1/32
mov MS_Inc_Whole,(1000 / 32)
mov MS_Inc_Frac,(((1000 SHL 16) / 32) AND 0FFFFh)
mov Hz128To32,0 ; reset flag
NoAdjust:
cmp ReqRTCFreq,0 ; pending request to change the rtc frequency?
je rtcdone ; .. no, done with the rtc
mov al,ReqRTCFreq ; Get requested rtc frequency
cmp al,CurRTCFreq ; Are we requesting the current frequency?
je hzchanged ; .. yes, don't reprogram, but mark change
cmp al,128 ; request to change to 128hz?
je short hz128 ; .. yes, safe to do it now
; .. no, change to 32hz only on 32ms border
mov ax,Accum8msTicks ; get #accumulated 8ms ticks
and ax,3 ; are we on 1/32nd tick border?
jnz rtcdone ; .. no, not safe to reprogram clock yet
hz32:
; cancel old periodic interrupt
; program new periodic interrupt rate to 32hz
; set frequency to indicate 1/32nd interval
; set hundreths, milliseconds, frequency to zero
; Increment zero time the next tick, rather than a full 1/32 interval,
; or the rest of the 1/32 current interval remaining.
; Experiment has shown that incrementing time
; in this manner keeps MsCount adding up correctly
; while changing the RTC's frequency.
; Time will increment at 1/32 starting the tick after next
; clear frequency change request (ReqRTCFreq)
ifdef MONITOR
; This shows how 8ms ticks accumulate.
ACCM8SIZE EQU 64
mov bx,Accum8msTicks ; get #accumulated 8ms ticks
cmp bx,ACCM8SIZE
jl short inceach
inc Accm8p
jmp short incedone
inceach:
shl bx,1
inc Accm8[bx]
shr bx,1
incedone:
and bx,3 ; ..last 1/32nd tick (0,1,2 or 3)
shl bx,1
inc Accm8mod4[bx]
shr bx,1
endif
mov SI,PeriodPkt ; DS:SI --> Periodic request block
mov [SI].Function,ABCANPERI ; ready to cancel Periodic Interrupt
mov AX,ClockLID ; Get LID in AX
mov dl,DevHlp_ABIOSCall
xor DH,DH ; Calling ABIOS Start Entry point
call [DevHlp] ; Cancel Periodic Interrupt
ifdef MISCSTRICT
jnc short cancelled32
int 3 ; cancelling the real time clock interrupt should not fail
cancelled32:
endif
mov [SI].Function,ABSETPERI ; Set Periodic Interrupt
mov [SI].Parms.RTC_Prat2,IN032 ; rate is 32Hz
call [DevHlp] ; do it
ifdef MISCSTRICT
jnc short hz32ok
int 3 ; reprogramming the real time clock should not fail
hz32ok:
endif
mov CurRTCFreq,32 ; indicate new 32hz frequency
mov Hundred_Inc,0 ; set all increments to zero..
mov MS_Inc_Whole,0 ; .. they will be zero for the
mov MS_Inc_Frac,0 ; .. next tick ONLY.
mov Hz128To32,1 ; set flag to indicate 128->32hz transition
jmp short hzchanged ;
hz128:
; cancel old periodic interrupt
; program new periodic interrupt rate to 128hz
; set hundreths, milliseconds, frequency to indicate 1/128nd interval
mov SI,PeriodPkt ; DS:SI --> Periodic request block
mov [SI].Function,ABCANPERI ; ready to cancel Periodic Interrupt
mov AX,ClockLID ; Get LID in AX
mov dl,DevHlp_ABIOSCall
xor DH,DH ; Calling ABIOS Start Entry point
call [DevHlp] ; Cancel Periodic Interrupt
ifdef MISCSTRICT
jnc short cancelled128
int 3 ; cancelling the real time clock interrupt should not fail
cancelled128:
endif
mov [SI].Function,ABSETPERI ; Set Periodic Interrupt
mov [SI].Parms.RTC_Prat2,IN128 ; rate is 128Hz
call [DevHlp]
ifdef MISCSTRICT
jnc short hz128ok
int 3 ; reprogramming the real time clock should not fail
hz128ok:
endif
mov CurRTCFreq,128 ; indicate new 128hz frequency
mov Hundred_Inc,((100 SHL 8) / 128); set increments to 1/128
mov MS_Inc_Whole,(1000 / 128)
mov MS_Inc_Frac,(((1000 SHL 16) / 128) AND 0FFFFh)
mov Accum8msTicks,0 ; start counting 7.8125ms ticks
hzchanged:
mov ReqRTCFreq,0 ; clear frequency change request indicator
rtcdone:
sti ; enable, all timer counts updated
;*************** Do not change FIXISEG and post_time sequence *********
; Determine if there was an Update Int that occured previously
; and needs handling now.
xor al,al
xchg al,Update_Flag ; set update_flag = 0
or al,al ; Was there an update interrupt?
je short Continue ; No.
; This driver makes every effort to program the clock to 128hz ONLY within
; the first 7.8125ms after a full 32hz tick. This is required in order for
; increments of the running millisecond counter to stay accurate. In
; (hopefully rare) cases, higher priority interrupt activity may prevent
; the clock from being reprogrammed to 128hz within the 7.8125ms window.
;
; When the clock is programmed from 32hz to 128hz, system time will
; increment at 7.8125ms starting the next tick. In the worst case, up to
; 31.25ms of real time may have passed just prior to the reprogramming.
; This can result in up to 24ms of time being "lost". We attempt to limit
; the accumulation of this "lost time" within a one second period to no
; more than 32ms. We predict what the MSCount should be one second from
; now. If it is more than 32ms less, we make a correction. This also
; helps keep the MsCount accurate in the event of missed periodic
; interrupts.
;
; It is possible that the independent one-second interrupt arrived just
; before the last 32hz periodic interrupt. In that case, the time will be
; 32ms short of one second. That is allowed. But if the time accumulation
; is more than 32ms short, we assume either periodic interrupt(s) were missed
; or several reprogrammings of the clock frequency resulted in more than
; 32ms of "lost time". In this case, we adjust the running millisecond
; count to our previously predicted value. This limits the loss to 32ms.
mov eax,ExpectedMsCount ; recall expected millisecond count
mov ecx,ES:[BX.SIS_MsCount] ; get the real MsCount
sub eax,ecx ; Did the real MsCount fall behind?
jle short NotLagging ; .. No, continue
add ecx,eax ; .. Yes, MsCount should be this
mov ES:[BX.SIS_MsCount],ecx ; bring it up to speed
add MS_Delta,ax ; This contributes to the delta
ifdef MONITOR
add LagTotal,eax ; it is nice to know these values..
inc LagCount ; .. to observe timing dynamics.
endif
NotLagging:
add ecx,968 ; We expect at least 1000-32ms to pass
mov ExpectedMsCount,ecx ; save for next time
push cx
mov cx,1 ; only read clock once
call FIXISEG ; Read clock and update Infoseg
pop cx
Continue:
mov ax,MS_Delta ; get number of milliseconds transpired
add Post_Time,ax ; Post = n * Pre
;*************** end of sequence problem ************************
push DS
les BX,SchedClock
xor DH,DH ; DH=0 to indicate pre-EOI
call DWORD PTR ES:[BX] ; call timer tick routine
pop DS
;here we test for nested int or not. if we are already nested, we issue
;the EOI disabled and return without doing any Post_EOI processing
mov AL,1
XCHG Int_In_Progress,AL ; set to nested
OR AL,AL ; are we nested?
jz short Not_Nest ; if ZR, we are not
inc Int_Nested ; bump nested counter
jmp SHORT EOI ; end of interrupt
;we are here if we are in a first level Int handler, we have to account
;for any nesting that may have occured after the EOI
Not_Nest: ; we are first level
; mov AL,RTCIRQ ; interrupt level = 8 Remove 76711
; mov DL,DevHlp_EOI Remove 76711
; call [DevHlp] ; call the EOI devhlp Remove 76711
DevEOI <RTCIRQ>,DevHlp ; add 76711
; call SchedClock to do post-EOI with AX = post_time
mov DH,01H ; DH=1 to indicate post-EOI
push DS
les BX,SchedClock
xor ax,ax
xchg ax,Post_Time ; zero out for possible nests
; Input: AX = # of ms to catch up on, same as Pre if no nesting
call DWORD PTR ES:[BX] ; call timer tick routine
pop DS
; reset all variables used to check for nesting
cli
mov Int_In_Progress,0 ; clear nested flag
jmp SHORT LOC_retN
EOI:
cli
; mov AL,RTCIRQ ; do EOI. (Interrupt level = 8) remove 76711
; mov DL,DevHlp_EOI remove 76711
; call [DevHlp] remove 76711
DevEOI <RTCIRQ>,DevHlp ; add 76711
LOC_retN:
CLC
ret
RTCINT ENDP
BREAK <Routine to Update the Global InfoSeg>
;********************** START OF SPECIFICATIONS *********************
;*
;* MODULE NAME: FIXISEG
;*
;* DESCRIPTIVE NAME: Routine to read the RT/CMOS clock and then
;* update the Global InfoSeg date/time variables.
;*
;* FUNCTION:
;* Performs a read request to the Realtime Clock device
;* and then updates the global InfoSeg time/date information.
;*
;* NOTES:
;* This routine is called by three routines of the clock
;* device driver. RTINIT (initialization) and RTCINT (interrupt
;* handler) use this routine to initialize and update InfoSeg
;* respectively.
;*
;* Time is reduced to seconds and preserved for later adding
;* into secs since 1-1-70.
;*
;* ENTRY POINT: FIXISEG
;* LINKAGE: Near (from RTINIT, RTCINT)
;*
;* USES: Preserves all registers except AX
;*
;* INPUT: CX = # OF retRY COUNT
;*
;* OUTPUT: (retURNED)
;* No interrupt flag modified
;* Global InfoSeg time/date data is filled in.
;* AX = status flag is set, CARRY FLAG indicates success
;*
;* EXIT-NORMAL:
;* CF is cleared
;*
;* EXIT-ERROR:
;* AX = STATUS word: DONE, ERROR + DEVICE NOT READY bits set
;* CF is set
;*
;* INTERNAL REFERENCES:
;* STRUCTURES: none
;* ROUTINES: cmos_popf, calc_year_sec
;*
;* EXTERNAL REFERENCES:
;* DATA STRUCTURES: Global Information Segment (SysInfoSeg)
;* ROUTINES: none
;*
;*********************** END OF SPECIFICATIONS **********************
page
;********************** START OF PSEUDO-CODE **********************
;
; for try_count = 1 to MAXTRIES
; call ABIOS to read the clock
; if ABIOS error
; set return code DONE+ERROR+GENERAL FAILURE
; set carry flag on
; return
; endif
; endfor
;
; if try_count > MAXTRIES
; set return code DONE+ERROR+DEVICE NOT READY
; set carry flag on
; return
; endif
;
; call ABIOS to read seconds, minutes, hours, years,
; day of month, & months from chip
; convert seconds, minutes, hours, years, day of month, & months to BCD
;
; call calc_year_sec to update sysinfoseg
; hundredth_count_timer = 0
; HunTime = 0
; restore interrupt flag
; return
;
;*********************** END OF PSEUDO-CODE **********************
ASSUME CS:ClkCode,DS:ClkData,ES:NOTHING,SS:NOTHING
FIXISEG PROC NEAR
public FIXISEG
SaveReg <AX,BX,SI,ES>
; (begin)
;* MAXTRIES is declared as 200, constant for ABIOS driver. Until 387
; (12 MHz) is available.
;
mov SI,TaskPkt ; DS:SI --> TaskTime ReqBlk
mov [SI].Function,ABREADTD ; Function is Read Time/Date
xor DH,DH ; Calling ABIOS Start
; mov DL,DevHlp_ABIOSCall ; Read the clock
LEA DI, DS:ABIOS_Clock_Parms
mov AX,ClockLID
CHKAGN: ; LOOP while abios not ready
mov [SI].returnCode,retCODE_IN
CallABIOS
; call [DevHlp]
jnc short ABiosOk2 ; IF ABIOS error
mov AX,(STDON+STERR+ERRGFAIL) ; set error code for gen failure
jmp goback ; carry already set
AbiosOk2: ; ELSE
cmp [SI].returnCode,0 ; IF ABIOS returns 'done'
JZ short FIS2 ; THEN continue
LOOP CHKAGN ; ELSE try again
; if try_count > MAXTRIES
; set return code DONE+ERROR+DEVICE NOT READY
; set carry flag on
; return
; endif
;
STC ; IF no success THEN set carry
mov AX,(STDON+STERR+ERRDNR) ; set error code for device not ready
jmp short goback ; abort clock
FIS2: push bp
sub sp,10 ; reserved for clock information
mov bp,sp
; call ABIOS to read seconds, minutes, hours, years,
; day of month, & months from chip
; convert seconds, minutes, hours, years, day of month, & months to BCD
;* +-------------+
;* | old BP |
;* +-------------+
;* BP+8->| ??? |
;* +-------------+
;* BP+7->| YEAR |
;* +-------------+
;* BP+6->| MONTH |
;* +-------------+
;* BP+5->| DAY OF MONTH|
;* +-------------+
;* BP+4->| DAY OF week |
;* +-------------+
;* BP+3->| hour |
;* +-------------+
;* BP+2->| minute |
;* +-------------+
;* BP+1->| second |
;* +-------------+
;* BP ->| hundredth |
;* +-------------+
mov AL,[SI].Parms.Seconds ; get seconds
BCD2BN ; Convert it
mov second_bin,al ; Stuff it in table
mov AL,[SI].Parms.Minutes ; get minutes
BCD2BN
mov minute_bin,al ; Stuff it in table
mov AL,[SI].Parms.Hours ; get hours
BCD2BN
mov hour_bin,al ; Stuff it in table
mov AL,[SI].Parms.Day ; get day of month
BCD2BN
mov day_month_bin,al
mov AL,[SI].Parms.Month ; get month
BCD2BN
mov month_bin,al
mov AL,[SI].Parms.Year ; get year
BCD2BN
mov year_bin,al
pushf ; save interrupt flag
; call far ptr calc_year_sec ; AX:DX=seconds from 1-1-1970
push cs ; do FARCALLTRANSLATION
call near ptr calc_year_sec ; AX:DX=seconds from 1-1-1970
mov ES:[BX.SIS_HunTime],00H
push cs
call cmos_popf ; restore interrupt flag
add sp,10 ; restore local stack
pop bp
CLC
GOBACK: RestoreReg <ES,SI,BX,AX>
ret ; return AX
FIXISEG ENDP
;********************** START OF SPECIFICATIONS *********************
;*
;* MODULE NAME: calc_year_sec
;*
;* DESCRIPTIVE NAME: Calculate total seconds and current year
;*
;* FUNCTION:
;* Calculate the total seconds from 1-1-1970, and get the
;* current year in century year, e.g., 1987.
;*
;* NOTES:
;* This routine is called by three routines of the clock
;* device driver. FIXISEG initializes and updates InfoSeg.
;* The RTWRIT sub-module uses it to update
;* InfoSeg following a resetting of the Realtime Clock device.
;*
;* Time is reduced to seconds and preserved for later adding
;* into secs since 1-1-70.
;*
;* ENTRY POINT: calc_year_sec
;* LINKAGE: Far (from FIXISEG, RTWRIT)
;*
;* USES: Preserves all registers except AX, ES, BX
;*
;* INPUT:
;* +-------------+
;* | old BP |
;* +-------------+
;* BP+8->| ??? |
;* +-------------+
;* BP+7->| YEAR |
;* +-------------+
;* BP+6->| MONTH |
;* +-------------+
;* BP+5->| DAY OF MONTH|
;* +-------------+
;* BP+4->| DAY OF week |
;* +-------------+
;* BP+3->| hour |
;* +-------------+
;* BP+2->| minute |
;* +-------------+
;* BP+1->| second |
;* +-------------+
;* BP ->| hundredth |
;* +-------------+
;*
;* OUTPUT: (retURNED)
;* ES:BX = address of SysInfoSeg
;* Global InfoSeg time/date data is filled in.
;* Interrupt Disabled.
;*
;* EXIT-NORMAL:
;*
;* EXIT-ERROR:
;*
;* INTERNAL REFERENCES:
;* STRUCTURES: none
;* ROUTINES: FixDayOfWeek, GTDAYS
;*
;* EXTERNAL REFERENCES:
;* DATA STRUCTURES:
;* ROUTINES: none
;*
;*********************** END OF SPECIFICATIONS **********************
page
;********************** START OF PSEUDO-CODE **********************
;
; if hours > 12
; factor = 1
; hours -= 12
; else
; factor = 0
; total_seconds = seconds + minutes * 60 + hours * 3600
;
; if year > 99 or month > 12 or month = 0 or Day_of_Month = 0 or
; Day_of_Month > 31
; set year = 80
; set date = 1-1
; set Day_of_Week = 2
; endif
; if year is over 20 centry ( year < 80 )
; year += 100
; year += 1900
; save year in century_year stack
; call GTDAYS to get DAYS since 1-1-80
; total_time from 1-1-70 =
; ( (DAYS+# of days from 1-1-70 to 12-31-79) * # of 12 hours in a day +
; factor ) * seconds in 12 hours + total_seconds
; disable interrupts
; save all the information in SysInfoSeg
; return
;
;*********************** END OF PSEUDO-CODE **********************
calc_year_sec PROC FAR
ASSUME CS:ClkCode,DS:ClkData,ES:NOTHING,SS:NOTHING
SaveReg <CX,DX,SI,DI>
; (begin)
; if hours > 12
; factor = 1
; hours -= 12
; else
; factor = 0
; total_seconds = seconds + minutes * 60 + hours * 3600
; read Day_of_Month, month, & year from chip
mov al,second_bin ; get seconds
xor AH,AH
mov SI,AX ; Save sec's
mov al,minute_bin ; get minutes
mov CL,60 ; secs/min
mul CL
add SI,AX ; SI = secs in mins and secs
xor AH,AH
mov AL,hour_bin ; get hours
xor CX,CX ; ch=day of week; cl=factor
cmp AL,12 ; >12 hours?
jbe short calc10 ; (M003)
sub AL,12
inc cl ; add factor = 1
calc10: push CX
mov CX,3600
mul CX ; AX = secs in hrs remainder
pop CX
add SI,AX ; SI = secs in 12 hr time
; Here we do a VERY rough bounds check on the Year, Month, Day and DOW
;
; if year > 99 or month > 12 or month = 0 or Day_of_Month = 0 or
; Day_of_Month > 31
; set year = 80
; set date = 1-1
; set Day_of_Week = 2
;
mov dh,month_bin
mov dl,day_month_bin
mov AL,year_bin ; get year
cmp AL,99 ; Year > 99?
ja short DATERR
OR DH,DH ; Month = 0?
JZ short DATERR
cmp DH,12 ; Month > 12?
ja short DATERR
OR DL,DL ; Day = 0?
JZ short DATERR
cmp DL,31 ; Day > 31?
JLE short DATEOK
DATERR: mov AL,80
mov DH,1
mov DL,1 ; Setup for 1-1-80 if date in RTC
mov CH,2 ; 1-1-80 was a Tuesday (M003)
mov month_bin,dh
mov day_month_bin,dl
mov year_bin,al
DATEOK:
; if year is over 20 centry ( year < 80 )
; year += 100
xor AH,AH ; AX = LOWER PART OF YEAR
cmp AL,80 ; Is it 20xx?
JGE short calc20 ; No, add only 1900
add AX,100 ; Yes, add extra 100
; year += 1900
; save on stack
calc20: add AX,1900 ; AX = Year and century
mov century_year,ax ;
sub AX,1980 ; Get years since 1980
; call GTDAYS to get DAYS since 1-1-80
;* INPUT: AX = Years since 1-1-80
;* DH = Current month
;* DL = Current day
call GTDAYS ; AX=Get days since 1-1-80
; if Day_of_Week != 2
; call FixDayOfWeek to get Day_of_Week
;
OR CH,CH ; Is day_of_week correct?
JNZ short CENT20 ; Yes, jump
call FixDayOfWeek
mov day_week_bin,ch ; save on stack
CENT20:
; total_time from 1-1-70 =
; ( (DAYS+# of days from 1-1-70 to 12-31-79) * # of 12 hours in a day +
; factor ) * seconds in 12 hours + total_seconds
xor BX,BX ; Clear it
add AX,DAYS1 ; add in days 1-1-70 thru 12-31-79
shl AX,1 ; AX = # of 12 hour segments
rcl bl,1 ; Save overflow (carry) in bl
xor ch,ch ; CX = add factor (0/1)
add ax,cx ; AX=Low 16 bits of 12 hour segments
mov CX,SECP12 ; CX = Seconds in 12 hours
mul CX ; [DXAX] = Secs in Low 16 bits of segs
shr bl,1 ; Any beyond Low 16 bits?
jnc short calc30 ; No, [DXAX] = Total seconds
add dx,cx ; Yes, fudge bit 17 multiply
calc30: add ax,si ; add in secs in current time
adc dx,0 ; ...and any carry
les BX,[InfSeg] ; ES:BX -> InfoSeg
cli ; disable interrupts
mov WORD PTR ES:[BX.SIS_BigTime],AX
mov WORD PTR ES:[BX.SIS_BigTime+2],DX
mov al,second_bin ; get seconds
mov ES:[BX.SIS_SecTime],AL ; Stuff it in table
mov al,minute_bin ; get minutes
mov ES:[BX.SIS_MinTime],AL
mov al,hour_bin ; get hour
mov ES:[BX.SIS_HrsTime],AL ; InfoSeg time is complete
mov al,day_month_bin ; get day of month
mov ES:[BX.SIS_DayDate],al
mov al,month_bin ; get month
mov ES:[BX.SIS_MonDate],al
mov al,day_week_bin ; get day of week
mov ES:[BX.SIS_DOWDate],al ; save the day (DOW)
mov ax,century_year
mov ES:[BX.SIS_YrsDate],AX ; InfoSeg date is complete
RestoreReg <DI,SI,DX,CX>
ret
calc_year_sec ENDP
; restore interrupt flag
CMOS_POPF PROC
Iret ; RESTORE FLAGS
CMOS_POPF ENDP
;***LP PTInt - Timer 0 Interrupt Handler
;
; ENTRY
; ds -> ClkData (interrupt manager guarantee this)
;
; EXIT
; carry flag clear
;
; USES
; All
;
; CONTEXT
; Interrupt
;
; PSEUDOCODE
; clear IRQ 0 latch;
; issue EOI to PIC;
ASSUME CS:ClkCode,DS:ClkData,ES:NOTHING,SS:NOTHING
Procedure PTInt,far
;TMR -- Update Rollover count and Tmr value.
SaveReg <ds>
mov dx,WORD PTR pqwTmr
lds bx,pqwTmrRollover
ASSUME DS:NOTHING
.386p
mov ecx,DWORD PTR [bx].qw_ulLo ;(ECX) = current rollover count.
mov eax,DWORD PTR [bx].qw_ulHi ;Set Current rollover count =
mov DWORD PTR [bx].qw_ulLo,eax ; Next rollover count.
mov bx,dx ;(DS:BX) -> qwTmr.
;ASSUMES SAME DS.
add DWORD PTR [bx].qw_ulLo,ecx
adc DWORD PTR [bx].qw_ulHi,0
jo short pti40
CPUMode Reset
pti20: RestoreReg <ds>
ASSUME DS:ClkData
test fsPTFlags,PTF_OWNT0 ;ptimer owns T0?
jnz short pti30 ;YES, skip the rest
test fsPTFlags,PTF_TICKENABLE;vtimer enables tick?
jz short pti30 ;NO, skip the rest
push WORD PTR 0
push VTDCMD_TICKEVENT ;(TOS+8)=VTDCMD_TICKEVENT
.386p
push DWORD PTR 0 ;(TOS+4)=f16p1=0
push DWORD PTR 0 ;(TOS)=f16p2=0
call FWORD PTR fpfnVTProc
CPUMode Reset
pti30: in al,PORT_SYSB ;(al)=value of system control port B
or al,SYSB_RESETIRQ0 ;set clearIRQ0 bit
out PORT_SYSB,al ;clear IRQ0 latch
; mov al,TIMERIRQ ;issue EOI to PIC Remove 76711
; mov dl,DevHlp_EOI Remove 76711
; call [DevHlp] Remove 76711
DevEOI <TIMERIRQ>,DevHlp ; add 76711
clc
ret
.386p
pti40: mov DWORD PTR [bx].qw_ulLo,QW_MAX_LO
mov DWORD PTR [bx].qw_ulHi,QW_MAX_HI
jmp short pti20
CPUMode Reset
EndProc PTInt
;***LP RTINIT2 - 2nd stage init
;
; FUNCTION
; Because RIPL with ETHERNET/PC-NET uses IRQ0, we must delay
; hooking IRQ0 until after loadable device drivers/IFS are
; installed. The kernel will call with command 0 (loadable
; device driver init) when it's OK to hook IRQ 0, and this
; routine will be invoked.
;
; ENTRY
; ds -> ClkData
;
; EXIT
; carry flag clear
;
; USES
; All
;
; CONTEXT
; Interrupt
;
; PSEUDOCODE
; clear IRQ 0 latch;
; issue EOI to PIC;
ASSUME CS:ClkSwap,DS:ClkData,ES:NOTHING,SS:NOTHING
RTINIT2 PROC FAR
public RTINIT2
SaveReg <bx,cx,dx,di>
;Initialize physical timer 0 to proper mode running at the rate of 18.2Hz
mov al,SC_CNT0+RW_LSBMSB+CM_MODE2
out PORT_CW,al ; program count mode of timer 0
IODelay
xor al,al
out PORT_CNT0,al ; program timer 0 count
IODelay ; count == 0h actually means 10000h
out PORT_CNT0,al ; => approx. 18.2Hz
IODelay
;DevHlp(DevHlp_SetIRQ,TIMERIRQ,pfnPTInt,NO_INTSHARING)
mov ax,offset ClkCode:PTInt ; (ax)=interrupt handler offset
mov bx,TIMERIRQ ; (bx)=IRQ number
mov dx,DevHlp_SetIRQ ; (dh)=No interrupt sharing
call [DevHlp] ; hook timer 0 interrupt
RestoreReg <di,dx,cx,bx>
mov AX,STDON ; Done
ret
RTINIT2 ENDP
ClkSwap SEGMENT
public endswapcode
endswapcode label byte
ClkSwap ENDS
BREAK <Realtime Clock Initialization Routine>
;********************** START OF SPECIFICATIONS *********************
;*
;* MODULE NAME: RTINIT
;*
;* DESCRIPTIVE NAME: RTC Initialization Function 0
;*
;* FUNCTION: Initializes data structures and the Realtime Clock
;* device, gets a LID for calling ABIOS.
;*
;* ENTRY POINT: RTINIT
;* LINKAGE: FAR from RTENTR
;*
;* NOTES:
;*
;* USES: AX, DX. Preserves others. (RU uses CX)
;*
;* INPUT: (PARAMETERS)
;* ES:BX = pointer to Request Packet
;*
;* OUTPUT: (RETURNED)
;* No interrupt flag modified
;* Request Packet filled in.
;*
;* EXIT-NORMAL:
;* AX = Status flag is "DONE"
;*
;* EXIT-ERROR:
;*
;* INTERNAL REFERENCES:
;* STRUCTURES: ABIOS Request Block
;* ROUTINES: FIXISEG
;*
;* EXTERNAL REFERENCES:
;* STRUCTURES: Global Information Segment (SysInfoSeg)
;* ROUTINES: DevHlp_GetDOSVar, DevHlp_SetIRQ, DevHlp_ABIOSCall
;* DevHlp_GetLIDEntry
;*
;*********************** END OF SPECIFICATIONS **********************
page
;********************** START OF PSEUDO-CODE **********************
;
; set the pointer, DevHlp, to the DevHelp Router
; call DevHlp to get the address of SysInfoSeg in ES:BX
; if DevHlp failed
; goto abort
; save the address of SysInfoSeg in InfSeg
; call DevHlp to get the address of SchedClock in ES:BX
; save the address of SchedClock
;
; call DevHlp to get the ABIOS LID for RTC
; if DevHlp failed
; goto abort
; save RTC ABIOS LID in ClockLID
;
; call DevHlp to get the ABIOS LID clock parameters
; call DevHlp ABIOSCALL function start to setup task request block
; if DevHlp failed
; goto abort
; initialize the task request block, periodic request block,
; update-ended request block, alarm request block,
; call DevHlp ABIOSCALL to disable old periodic interrupt
; if DevHlp failed
; goto abort
; call DevHlp ABIOSCALL to disable the update-ended interrupt
; if DevHlp failed
; goto abort
; call DevHlp ABIOSCALL to disable the alarm interrupt
; if DevHlp failed
; goto abort
; call DevHlp ABIOSCALL to set periodic interrupt to 31.25 ms
; if DevHlp failed
; goto abort
; call DevHlp ABIOSCALL to enable the update-ended interrupt
; if DevHlp failed
; goto abort
;
; set up end of code & data segment for SysInit
; call DevHlp to set non-sharing IRQ 8 to routine RTCINT
; if DevHlp failed
; goto abort
;
; call FIXISEG to initialize SysInfoSeg from the data on the clock
; if carry flag set
; goto abort
;
; call DevHlp to set int 15 to point to routine int15rtn
; if DevHlp failed
; goto abort
; save old int 15 vector in Int15Chain
;
; call DevHlp to set int 1A to point to routine int1Artn
; if DevHlp failed
; goto abort
; save old int 1A vector in Int1AChain
;
; set return code = DONE
; return
;
; abort:
; set return code = DONE+ERROR+GENERAL FAIL
; set end of code & data segment to zero
; return
;
;*********************** END OF PSEUDO-CODE **********************
ASSUME CS:ClkCode,DS:ClkData,ES:NOTHING,SS:NOTHING
RTINIT PROC FAR
public RTINIT
mov AX,WORD PTR ES:[BX].InitDevHlp ; Save away the pointer to
mov WORD PTR DevHlp,AX ; the DevHelp Router.
mov AX,WORD PTR ES:[BX].InitDevHlp+2
mov WORD PTR DevHlp+2,AX
;% Temporary to fix loader problem for which the data selector
;% does not get fixed up.
ES_DI EQU 1 ;equate for PhysToVirt DevHlp service
SaveReg <bx,es>
SaveReg <ds>
push ds
pop es
ASSUME es:ClkData
push cs
pop ds
ASSUME ds:NOTHING
mov si,offset selClkData ;(ds:si)->selClkData
mov dl,DevHlp_VirtToPhys
call es:[DevHlp] ;(ax:bx)=physical address of selClkData
mov cx,2 ;length of word we need to write into
mov dx,DevHlp_PhysToVirt + ES_DI*100h
call es:[DevHlp] ;(es:di)->selClkData (writeable!)
ASSUME es:NOTHING
RestoreReg <ds>
ASSUME ds:ClkData
mov es:[di],ds ;save ds into code segment
RestoreReg <es,bx>
ASSUME es:NOTHING
mov dl,DevHlp_UnPhysToVirt ;free selector
call [DevHlp]
;% END hack
push ES
push BX
mov AL,1 ; Variable number of SysInfoSeg
mov DL,DevHlp_GetDOSVar
call [DevHlp]
LJC rtabort2 ; if carry flag set, abort clock
; No need to check return code here,
; because ABIOS ALWAYS returns 0 for
; the return LIDPARMS function.
mov ES,AX ; ES:BX -> SysInfoSeg
mov AX,ES:[BX] ; AX = InfoSeg segment selector
mov WORD PTR InfSeg+2,AX ; Stuff for later
mov DL,DevHlp_SchedClock ; ***** FOR DCR 487 *****
call [DevHlp] ; ES:BX = address of SchedClock pointer
mov WORD PTR SchedClock,BX ; Save away the SchedClock ptr address
mov WORD PTR SchedClock+2,ES ; **
; DS points to Data Segment...
mov AX,8 ; desired device id is 8
mov BX,1 ; get first LID for the RTC
xor DX,DX ;
mov DL,DevHlp_GetLIDEntry ; get the ABIOS LID for the RTC
call [DevHlp] ;
LJC rtabort2 ; if carry flag set, abort clock
mov ClockLID,AX ; save LID for later use
; GET ABIOS Clock Parameters
lea SI, DS:ABIOS_Clock_Parms
mov DL, DevHlp_ABIOSGetParms
call [DevHlp]
; SET UP TASK REQUEST BLOCK
mov SI,Offset DS:AbiosRB ; SI --> ABios request block area
mov [SI].RBLen,MAXBRB
mov [SI].LogID,AX
mov [SI].Function,ABretLIDP ; function is return Lid Parameters
mov [SI].returnCode,retCODE_IN
xor DH,DH ; calling ABIOS start
mov DL,DevHlp_ABIOSCall
call [DevHlp] ; call ABIOS to get LID parameters
LJC rtabort2 ; if carry flag set, abort clock
; No need to check return code here,
; because ABIOS ALWAYS returns 0 for
; the return LIDPARMS function.
; length of RTC request block is in RTCRBL. Set up RB Pointers
; INITIALIZE the Request Blocks and DISABLE their associated Interrupts
; Complete the Initialization of the first Request Block; no interrupts to disable
mov BX,[SI].Parms.RTCRBL ; Set up the true RB size in BX
mov [SI].RBLen,BX ; Set up the true RB size in 1st RB
mov CX,SI ; CX --> request block area
mov TaskPkt,CX ; Set TaskPkt to point to the 1st RB
add CX,BX ; CX --> 2nd request block area
mov PeriodPkt,CX ; Set PeriodPkt to point to the 2nd RB
add CX,BX ; CX --> 3rd request block area
mov UpdatePkt,CX ; Set UpdatePkt to point to the 3rd RB
add CX,BX ; CX --> 4th request block area
mov ExtraPkt,CX ; Set ExtraPkt to point to the 4th RB
; Initialize PERIODIC Request Block
mov SI,PeriodPkt ; DS:SI --> Periodic request block
mov [SI].RBLen,BX
mov [SI].LogID,AX
mov [SI].ELLOffset,0
mov [SI].returnCode,retCODE_IN
; call DevHlp ABIOSCALL to disable old periodic interrupt
mov [SI].Function,ABCANPERI ; function is Cancel Periodic Interrupt
call [DevHlp] ; Disable the Periodic Interrupt
LJC rtabort2 ; if Carry flag set, abort clock
; return Code ALWAYS ZERO!
; Initialize UPDATE Request Block
mov SI,UpdatePkt ; DS:SI --> Update request block
mov [SI].RBLen,BX
mov [SI].LogID,AX
mov [SI].ELLOffset,0
mov [SI].returnCode,retCODE_IN
; Disable UPDATE Interrupts
mov [SI].Function,ABCANUPDI ; function is Cancel Update-Ended Int'pt
call [DevHlp] ; Disable the Update-Ended Interrupt
LJC rtabort2 ; if carry flag set, abort clock
; Initialize Extra Request Block for Cancel Alarm Function
mov SI,ExtraPkt ; DS:SI --> Alarm request block
mov [SI].RBLen,BX
mov [SI].LogID,AX
mov [SI].ELLOffset,0
mov [SI].returnCode,retCODE_IN
; Disable ALARM Interrupts
mov [SI].Function,ABCANALI ; function is Cancel Alarm Interrupt
call [DevHlp] ; Cancel Alarm Interrupt
LJC rtabort2 ; if Carry flag set, abort clock
; return Code ALWAYS ZERO!
; Set hundredths and milliseconds increments to initial values, based on
; initial periodic interrupt frequency.
mov CurRTCFreq,RTCHZINIT
mov Hundred_Inc,((100 SHL 8) / RTCHZINIT)
mov MS_Inc_Whole,(1000 / RTCHZINIT)
mov MS_Inc_Frac,(((1000 SHL 16) / RTCHZINIT) AND 0FFFFh)
; SET PERIODIC INTERRUPTS
mov SI,PeriodPkt ; DS:SI --> Periodic request block
mov [SI].returnCode,retCODE_IN
mov [SI].Function,ABSETPERI ; function is Set Periodic Interrupt
mov [SI].Parms.RTC_Prat2,IN032 ; rate is 31.25 ms
call [DevHlp] ; Enable the Periodic Interrupt
jc short rtabort2 ; if carry flag set, abort clock
cmp [si].returnCode,1 ; check ABIOS return status
ja short rtabort2 ; if error, then abort
; SET FOR UPDATE INTERRUPTS
mov SI,UpdatePkt ; DS:SI --> UpdatePkt request block
mov [SI].returnCode,retCODE_IN
mov [si].Function,ABSETUPDI ; function is Set Update-Ended Int'pt
call [DevHlp] ; Enable the Update-Ended Interrupt
jc short rtabort2 ; if carry flag set, abort clock
cmp [si].returnCode,1 ; check ABIOS return status
ja short rtabort2 ; if error, then abort
; Set up Code and Data Segment End Pointers for SysInit
pop BX
pop ES
mov CX,ExtraPkt
mov es:[bx].InitEdata,CX ; InitEdata now points to the end
; of the third RB. This is used
; to set up the END of the DATA SEG.
mov AX,offset ClkCode:RTINIT ; set up END of the CODE SEGMENT
mov es:[bx].InitEcode,AX ; (INIT routine goes away...)
; Finally Set up Interrupt Vector...Enable too
mov BX,RTCIRQ
xor DH,DH ; IRQ NOT SHARED
mov AX,offset ClkCode:RTCINT ; AX --> INTERRUPT routine
mov DL,DevHlp_SetIRQ
call [DevHlp] ; set the interrupt level
jc short rtabort3 ; if carry flag set ,abort clock
push cx
mov cx,200 ; read 200 times clock
call FIXISEG ; Initialize InfoSeg from Clock
pop cx
jnc short end_rtinit
jmp short rtabort3 ; if carry flag set ,abort clock
rtabort2:
pop BX
pop ES
rtabort3:
mov AX,(STDON+STERR+ERRGFAIL) ; set error code for general failure
mov es:[bx].InitEcode,0 ; Set up end of Code Segment
mov es:[bx].InitEdata,0 ; Set up end of Data Segment
ret
end_rtinit:
;% Clk/Tmr registration and Initialization
ClkInit label near
public ClkInit
;DevHlp(DevHlp_RegisterPDD,pszClkName,fpfnClkVDDProc)
SaveReg <es,bx>
mov dl,DevHlp_RegisterPDD ;(dl)=DevHlp_RegisterPDD
mov si,offset ClkData:szClkName;(ds:si)->szClkName
mov di,cs
mov es,di
mov di,offset ClkCode:ClkVDDProc;(es:di)->ClkVDDProc
call [DevHlp] ;(ax)=error code
RestoreReg <bx,es>
or ax,ax ;error?
jnz short rtabort3 ;YES, goto error exit
;DevHlp(DevHlp_RegisterPDD,pszTmrName,fpfnPTmrProc)
SaveReg <es,bx>
mov dl,DevHlp_RegisterPDD ;(dl)=DevHlp_RegisterPDD
mov si,offset ClkData:szTmrName;(ds:si)->szTmrName
mov di,cs
mov es,di
mov di,offset ClkCode:PTVDMProc;(es:di)->PTVDMProc
call [DevHlp] ;(ax)=error code
RestoreReg <bx,es>
or ax,ax ;error?
jnz short rtabort3 ;YES, goto error exit
;DevHlp(DevHlp_RegisterBeep,fpfnPTBeep)
mov dl,DevHlp_RegisterBeep ;(dl)=DevHlp_RegisterBeep
mov cx,cs
mov di,offset ClkCode:PTBeep;(cx:di)->PTBeep
call [DevHlp]
or ax,ax ;error?
jnz short rtabort3 ;YES, goto error exit
call PTBeepOff ;disable speaker
;DevHlp(DevHlp_RegisterFreq,fpfnCLKFreq)
mov dl,DevHlp_RegisterFreq ;(dl)=DevHlp_RegisterFreq
mov cx,cs
mov di,offset ClkCode:CLKFreq;(cx:di)->CLKFreq
call [DevHlp]
or ax,ax ;error?
jnz short rtabort3 ;YES, goto error exit
;Initialize TMR device (PIT Ctr 0).
SaveReg <bx,cx,dx,di>
mov cx,cs
mov di,offset ClkCode:PTTimer0;(cx:di)->PTTimer0
mov dl,DevHlp_RegisterTmrDD
call [DevHlp]
; di:bx -> qwTmrRollover (16:16 ptr).
; di:cx -> qwTmr (16:16 ptr).
or di,di ;Valid Selector?
jnz short cinit2 ; Yes.
cinit1: mov di,ds ;Invalid, so store valid ptr.
mov bx,offset qwTmrRollover
mov cx,offset qwTmr
cinit2: ;Store (16:16) pointers.
mov WORD PTR pqwTmrRollover,bx ;Store offset.
mov WORD PTR pqwTmrRollover+2,di ;Store segment.
mov WORD PTR pqwTmr,cx ;Store offset.
mov WORD PTR pqwTmr+2,di ;Store segment.
;Initialize current rollover count = next rollover count = 10000h.
SaveReg <ds>
mov ds,di
.386p
mov DWORD PTR [bx].qw_ulLo,10000h
mov DWORD PTR [bx].qw_ulHi,10000h
CPUMode Reset
RestoreReg <ds>
RestoreReg <di,dx,cx,bx>
mov AX,STDON ; Done
ret
RTINIT ENDP
ClkCode ENDS
END