home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / ddkx86v5.zip / DDKX86 / SRC / DEV / CLOCK / CLOCK01 / CLOCK01.ASM next >
Assembly Source File  |  1995-04-14  |  73KB  |  1,959 lines

  1. ;*DDK*************************************************************************/
  2. ;
  3. ; COPYRIGHT (C) Microsoft Corporation, 1989
  4. ; COPYRIGHT    Copyright (C) 1995 IBM Corporation
  5. ;
  6. ;    The following IBM OS/2 WARP source code is provided to you solely for
  7. ;    the purpose of assisting you in your development of OS/2 WARP device
  8. ;    drivers. You may use this code in accordance with the IBM License
  9. ;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
  10. ;    Copyright statement may not be removed.;
  11. ;*****************************************************************************/
  12. ;SCCSID = @(#)clock01.asm       6.22 91/10/22
  13. title   clock device driver -- AT  [clock01.asm]
  14.  
  15. ; ****************************************************************************
  16. ; *                                                                          *
  17. ; *                                                                          *
  18. ; *                                                                          *
  19. ; ****************************************************************************
  20. ;
  21. ; Change History
  22. ;
  23. ; 64012 03/16/93 BJH    Change to retrieve century byte location from the
  24. ;                       DevConfigTbl
  25. ; 71736 08/11/93 TPL    Don't change the periodic rate (register A) when
  26. ;                       updating the date and time
  27.  
  28. PAGE    60,132
  29. ;
  30. .386p                                   ; assemble for iAPX 386
  31. .xlist
  32.  
  33. ; define MONITOR and/or MISCSTRICT to get help when debugging, these are
  34. ; NOT to be defined for a retail build.
  35. ;;MONITOR         EQU     1               ; builds in monitoring code
  36. ;;MISCSTRICT      EQU     1               ; turns on extra checking
  37.  
  38. INCL_ERRORS     EQU     1
  39.         include bseerr.inc              ; error numbers
  40. INCL_MI         EQU     1
  41.         INCLUDE rtc.inc                 ; real time clock information
  42.         INCLUDE mvdm.inc                ; equates and data for MVDM support
  43.         INCLUDE devhlp.inc              ;  the devhlp function eqs
  44.         INCLUDE infoseg.inc             ; definitions of Global Info Segment
  45.         INCLUDE devsym.inc              ; definition of DD Request Packet
  46.         INCLUDE clkdata.inc
  47.         INCLUDE config.inc              ; device config table
  48.         INCLUDE sas.inc                 ; System Anchor Segment (SAS)
  49.         INCLUDE timer.inc               ; timer definitions
  50.         INCLUDE clkseg.inc              ; segment definitions
  51.         INCLUDE pvwxport.inc
  52.         INCLUDE vtdptd.inc              ; vtimer/ptimer interface
  53.         INCLUDE devhlpP.inc             ; 76711
  54.  
  55. .list
  56.         extrn   GTDAYS:Far
  57.         extrn   FIXDAYOFWEEK:Near
  58.         extrn   ClkVDDProc:near         ; Clk router routine
  59.         extrn   PTBeep:near             ; beep service entry point
  60.         extrn   PTBeepOff:near          ; service to turn speaker off
  61.         extrn   PTVDMProc:near          ; vtimer/ptimer interface entry point
  62.         extrn   PTTimer0:near           ; timer 0 ownership arbitration serv.
  63.  
  64. ClkData SEGMENT
  65. ; data segment externals
  66.         extrn   DevHlp:DWORD
  67.         extrn   SchedClock:DWORD
  68.         extrn   InfSeg:DWORD
  69.         extrn   MonTab:BYTE
  70.         extrn   Update_Flag:BYTE
  71.         extrn   Post_Time:WORD
  72.         extrn   Int_Nested:BYTE
  73.         extrn   Int_In_Progress:BYTE
  74.         extrn   CMOS_location:BYTE
  75.         extrn   ATDataEnd:Near
  76.  
  77.         extrn   ReqRTCFreq:BYTE
  78.         extrn   CurRTCFreq:BYTE
  79.         extrn   Hundred_Whole:BYTE
  80.         extrn   Hundred_Frac:BYTE
  81.         extrn   Hundred_Inc:WORD
  82.         extrn   MS_Fraction:WORD
  83.         extrn   MS_Inc_Whole:DWORD
  84.         extrn   MS_Inc_Frac:WORD
  85.         extrn   MS_Delta:WORD
  86.  
  87.         extrn   Hz128To32:BYTE
  88.         extrn   Accum8msTicks:WORD
  89.         extrn   ExpectedMsCount:DWORD
  90.  
  91. ifdef   MONITOR
  92.         extrn   LagCount:DWORD
  93.         extrn   LagTotal:DWORD
  94.  
  95.         extrn   Accm:DWORD
  96.         extrn   Accm8:WORD
  97.         extrn   Accm8p:WORD
  98.         extrn   Accm8mod4:WORD
  99.         extrn   Ticks8:DWORD
  100.         extrn   Ticks32:DWORD
  101. endif
  102.  
  103.         extrn   szClkName:byte          ; device name for PCLK registration
  104.         extrn   szTmrName:byte          ; device name for PTIMER registration
  105.         extrn   fpfnVTProc:FWORD        ; vtimer entry point
  106.         extrn   fsPTFlags:WORD          ; ptimer flags
  107.  
  108.         extrn   pqwTmrRollover:DWORD
  109.         extrn   qwTmrRollover:DWORD
  110.         extrn   pqwTmr:DWORD
  111.         extrn   qwTmr:BYTE
  112.  
  113. ClkData ENDS
  114.  
  115. BREAK <Realtime Clock Write Routine>
  116. ;********************** START OF SPECIFICATIONS *********************
  117. ;*
  118. ;* MODULE NAME:  RTWRIT
  119. ;*
  120. ;* DESCRIPTIVE NAME:  Realtime Clock  Write routine
  121. ;*
  122. ;* FUNCTION:   Supports the Device Driver functions:
  123. ;*             - WRITE (function 8)
  124. ;*             - WRITE WITH VERIFY (function 9)
  125. ;*      by setting the Realtime Clock device with the date/time
  126. ;*      information and updating the Global InfoSeg date/time
  127. ;*      variables accordingly.
  128. ;*      Set the stack frame to reserve the clock information
  129. ;*      as follows:
  130. ;*                +---------------------+
  131. ;*                |  old  BP            |
  132. ;*                +---------------------+
  133. ;*         BP+16->| CURRENT CENTURY YEAR|
  134. ;*                |  BCD     |  BINARY  |
  135. ;*                +---------------------+
  136. ;*         BP+14->| YEAR/BCD | YEAR/BIN |
  137. ;*                +---------------------+
  138. ;*         BP+12->| MONTH/BCD| MON/BIN  |
  139. ;*                +---------------------+
  140. ;*         BP+10->| DAY OF MONTH        |
  141. ;*                |  BCD     |  BINARY  |
  142. ;*                +---------------------+
  143. ;*         BP+8 ->| DAY OF WEEK         |
  144. ;*                |  BCD     |  BINARY  |
  145. ;*                +---------------------+
  146. ;*         BP+6 ->| HOUR/BCD | HOUR/BIN |
  147. ;*                +---------------------+
  148. ;*         BP+4 ->| MIN/BCD  | MIN/BIN  |
  149. ;*                +---------------------+
  150. ;*         BP+2 ->| SEC/BCD  | SEC/BIN  |
  151. ;*                +---------------------+
  152. ;*         BP   ->| CENTURY  |HUNDREDTH |
  153. ;*                |  BINARY  |  BINARY  |
  154. ;*                +---------------------+
  155. ;*
  156. ;*      DevHlp_PhysToVirt converts the real address of the data buffer
  157. ;*      in the Device Driver Request Block (PktData) to a virtual
  158. ;*      address.  DevHlp_UnPysToVirt later restores the physical
  159. ;*      memory address.
  160. ;*
  161. ;* ENTRY POINT: RTWRIT
  162. ;*    LINKAGE:  NEAR from RTENTR
  163. ;*
  164. ;* USES:  AX, DX.  Preserves others.
  165. ;*
  166. ;* INPUT: (PARAMETERS)
  167. ;*      ES:BX = pointer to Request Block  (PktData)
  168. ;*      (SP+4):(SP+2) = pointer to Data Buffer
  169. ;*
  170. ;* OUTPUT: (RETURNED)
  171. ;*      AX = Status word to be stored in request packet status field.
  172. ;*
  173. ;* EXIT-NORMAL:
  174. ;*    RETURN CODE:      AX = BUSY and DONE
  175. ;*
  176. ;* EXIT-ERROR:
  177. ;*    RETURN CODE:      AX = ERROR and DONE and Write Fault
  178. ;*
  179. ;* INTERNAL REFERENCES:
  180. ;*    STRUCTURES:  none
  181. ;*    ROUTINES:         cmos_write_clock
  182. ;*                      calc_year_sec
  183. ;*
  184. ;* EXTERNAL REFERENCES:
  185. ;*    STRUCTURES:  Global Information Segment (SysInfoSeg)
  186. ;*    ROUTINES:         DevHlp_PhysToVirt
  187. ;*                      DevHlp_UnPhysToVirt
  188. ;*********************** END OF SPECIFICATIONS **********************
  189. page
  190. ;********************** START OF PSEUDO-CODE   *********************
  191. ;
  192. ; if caller does not request 6 bytes
  193. ;    goto write_fail
  194. ; call DevHlp_PhysToVirt to convert data buffer's physical addr to
  195. ;      virtual address in ES:DI
  196. ; if DevHlp_PhysToVirt fail
  197. ;    goto write_fail
  198. ; reserve 16 bytes for the clock information
  199. ; get days since 1-1-80 from user buffer
  200. ; day_count = no of days not in 4 year groups = days % 1461
  201. ; year_count = no of years in 4 year groups = ( days / 1461 ) * 4
  202. ; if day_count > 366
  203. ;    day_count -= 366
  204. ;    year_count++
  205. ;    while ( day_count > 365 )
  206. ;       day_count -= 365
  207. ;       year_count++
  208. ;    endwhile
  209. ; endif
  210. ;
  211. ; Normalize year_count ( year_count += 80 )
  212. ; if year_count in 20xx ( year_count >= 100 )
  213. ;    year_count -= 100
  214. ;    Century = 20x
  215. ; else Century = 19x
  216. ;
  217. ; if current date in leap year
  218. ;    set Feb = 29
  219. ; else
  220. ;    set Feb = 28
  221. ;
  222. ; for month = 1 to 12
  223. ;     if day_count >= MonTab[ month ]
  224. ;        break
  225. ;     else
  226. ;        day_count -= MonTab[ month ]
  227. ; endfor
  228. ;
  229. ; day_of_week = ( days + 2 ) % 7 + 1
  230. ; convert year, month, day_count, & day_of_week to BCD
  231. ;
  232. ; get seconds, minutes, & hours from user buffer and convert them to BCD
  233. ;     save both binary and BCD values on reserved stack
  234. ; get hundredths of seconds from user buffer and save on reserved stack
  235. ; call DevHlp_UnPhysToVirt to get original address mode
  236. ; if DevHlp_UnPhysToVirt successful
  237. ;    set return code  "DONE" & "BUSY"
  238. ; endif
  239. ; else goto write_fail
  240. ;
  241. ; disable interrupts
  242. ; initialize register A to 32.768kHz square wave output, and
  243. ;         31.25 millisecond periodic interrupt rate
  244. ; read register B value to get alarm interrupt status
  245. ; set register B to abort any update cycle in progress,
  246. ;     enable periodic interrupt, previous alarm interrupt status,
  247. ;     update-endedinterrupt enable, and 24 hour mode
  248. ; read register C & D values to initialize them
  249. ;
  250. ; call calc_year_sec to get seconds, minutes, hours, day_of_week, day_count,
  251. ;      month, year, & hundredths of seconds from reserved stack to put in
  252. ;      SysInfoSeg
  253. ; write seconds, minutes, hours, day_of_week, day_count,
  254. ;    month, & year to chip
  255. ;
  256. ; read register B value and reset its update cycle normally
  257. ; enable interrupts
  258. ;
  259. ; set return code = DONE+BUSY
  260. ; return
  261. ;
  262. ; write_fail:
  263. ;    set return code = DONE+ERROR+WRITE FAULT
  264. ;    return
  265. ;
  266. ;*********************** END OF PSEUDO-CODE    **********************
  267.  
  268. ClkSwap SEGMENT
  269.  
  270. century_year    equ     [bp+16]
  271. year_bcd        equ     [bp+14]
  272. year_bin        equ     [bp+15]
  273. month_bcd       equ     [bp+12]
  274. month_bin       equ     [bp+13]
  275. day_month_bcd   equ     [bp+10]
  276. day_month_bin   equ     [bp+11]
  277. day_week_bcd    equ     [bp+8]
  278. day_week_bin    equ     [bp+9]
  279. hour_bcd        equ     [bp+6]
  280. hour_bin        equ     [bp+7]
  281. minute_bcd      equ     [bp+4]
  282. minute_bin      equ     [bp+5]
  283. second_bcd      equ     [bp+2]
  284. second_bin      equ     [bp+3]
  285. century_bin     equ     [bp]
  286. hundredth_bin   equ     [bp+1]
  287. ;
  288. ; this extern is in the CS
  289. ;
  290.         extrn   saveDS:WORD
  291.  
  292.     ASSUME      CS:ClkSwap,DS:ClkData,ES:NOTHING,SS:NOTHING
  293. RTWRIT  PROC NEAR
  294.         public  RTWRIT
  295.  
  296.         STI                             ; enable interrupts
  297.         CMP     ES:[BX].IOcount,RW_BYTES        ; requesting 6 bytes ?
  298.         JE      WRITE                           ; yes, continue....
  299. WT_ERR:
  300.         MOV     AX,(STDON+STERR+0ah)            ; Else, quit w/ error.
  301.         RET
  302.  
  303. WRITE:  PUSH    BP
  304. ;       int     3
  305.         sub     sp,18                           ; reserved 16 bytes for clock
  306.         MOV     BP,SP
  307. ;; The addresses passed in the request packets are 32-bit physical addresses.
  308.         MOV     CX,6
  309.         MOV     AX,word ptr ES:[BX].IOpData+2   ; get hi word of address
  310.         MOV     BX,word ptr ES:[BX].IOpData     ; get lo word of address
  311.         MOV     DH,1                            ; result in ES:DI
  312.         MOV     DL,DevHlp_PhysToVirt            ; call PhysToVirt
  313.         CALL    [DevHlp]                        ; ES:DI points to buffer
  314.         JNC     WRITE_OK
  315.  
  316. WRITE_FAIL:
  317.         add     sp,18                   ; restore stack pointer
  318.         POP     BP
  319.         JMP     SHORT WT_ERR
  320.  
  321. ;    get days since 1-1-80 from user buffer
  322. WRITE_OK:
  323.         MOV     AX,ES:[DI]              ; Get days since 1-1-80 from buffer
  324.  
  325. ;    day_count = no of days not in 4 year groups = days % 1461
  326. ;    year_count = no of years in 4 year groups = ( days / 1461 ) * 4
  327.         MOV     CX,DAYSIN4YR            ; No of days in four year group
  328.         XOR     DX,DX                   ; Clear remainder word
  329.         DIV     CX                      ; [AX] = No of four year groups
  330.         SHL     AX,1
  331.         SHL     AX,1                    ; [AX] = No of years in 4 year groups
  332.         PUSH    AX                      ; Save (Note: 120 years max/[AH] = 0)
  333.  
  334. ;    if day_count > 366
  335. ;       day_count -= 366
  336. ;       year_count++
  337. ;       while ( day_count > 365 )
  338. ;          day_count -= 365
  339. ;          year_count++
  340. ;       endwhile
  341. ;    endif
  342.         XOR     CX,CX                   ; [CL] = 0 = extra year counter
  343.         MOV     AX,DAYSINYR+1           ; Year zero is a leap year
  344. YRCMP:  CMP     DX,AX                   ; Is date within year zero?
  345.         JC      INYEAR                  ; Yes, don't subtract
  346.         SUB     DX,AX                   ; No, sub days for this year
  347.         INC     CL                      ; Increment counter
  348.         MOV     AX,DAYSINYR             ; Days in years one and two
  349.         JMP     YRCMP                   ; Loop until date in current year
  350.  
  351. ;    Normalize year_count ( year_count += 80 )
  352. ;    if year_count in 20xx ( year_count >= 100 )
  353. ;       year_count -= 100
  354. ;       Century = 20x
  355. ;    else Century = 19x
  356. INYEAR: POP     AX                      ; Restore partial year count
  357.         MOV     ah,19H                  ; Set century = 19x
  358.         ADD     AL,CL                   ; Add in 1 to 3 remainder years
  359.         ADD     AL,80                   ; Normalize for 19xx
  360.         CMP     AL,100                  ; Year in 19xx?
  361.         JB      IS19XX                  ; Yes, AL = actual year (M003)
  362.         SUB     AL,100                  ; No, normalize for 20xx
  363.         MOV     ah,20H                  ; Set century = 20x
  364.  
  365. IS19XX: mov     century_bin,ah          ; save century byte
  366.         mov     year_bin,al             ; save year in binary
  367.         BN2BCD                          ; Convert year to BCD
  368.         MOV     year_bcd,al             ; Year in BCD
  369.  
  370. ;    if current date in leap year
  371. ;       set Feb = 29
  372. ;    else
  373. ;       set Feb = 28
  374.         MOV     MonTab+1,28             ; Init Feb to 28 days
  375.         OR      CL,CL                   ; Current date in year zero?
  376.         JNZ     NOINC                   ; No, leave at 28
  377.         INC     MonTab+1                ; Yes, add one day for leap year
  378.  
  379. ;    for month = 1 to 12
  380. ;        if day_count >= MonTab[ month ]
  381. ;           break
  382. ;        else
  383. ;           day_count -= MonTab[ month ]
  384. ;    endfor
  385. NOINC:  MOV     CL,1                    ; At least at first month in year
  386.         PUSH    SI                      ; Save SI
  387.         MOV     SI,OFFSET MonTab        ; Table of days in each month
  388.         XOR     AH,AH
  389.         CLD                             ; Insure increment
  390. CHECK1: LODSB                           ; Get count of days this month
  391.         CMP     DX,AX                   ; Date within this month?
  392.         JC      FOUND                   ; Yes, month is found
  393.         SUB     DX,AX                   ; No, sub this month's days and...
  394.         INC     CX                      ; ...count one more month/year
  395.         JMP     SHORT CHECK1            ; Loop until month is found
  396.  
  397. FOUND:  POP     SI                      ; Restore SI
  398.         MOV     AL,CL                   ; Month number to AL
  399.         mov     month_bin,al            ; save month/binary
  400.         BN2BCD                          ; Convert to BCD
  401.         mov     month_bcd,al            ; save month/BCD
  402.  
  403.         INC     DL                      ; Remainder is day of month (1 - 31)
  404.         MOV     AL,DL
  405.         mov     day_month_bin,al        ; save day of month/binary
  406.         BN2BCD                          ; Convert DOM to BCD
  407.         mov     day_month_bcd,al        ; save day of month/BCD
  408.  
  409.         MOV     AX,ES:[DI]              ; Reget total day count
  410.         XOR     DX,DX                   ; Clear MSW
  411.         MOV     CX,7                    ; Divide by days in week
  412.         INC     AX
  413.         INC     AX                      ; First day was Tuesday
  414.         DIV     CX                      ; Compute day of week
  415.         INC     DL                      ; Normalize
  416.         MOV     AL,DL                   ; Remainder is day of week (1 - 7)
  417.         mov     day_week_bin,al         ; save day of week/binary
  418.         BN2BCD                          ; Convert DOW to BCD
  419.         mov     day_week_bcd,al         ; save day of week/bcd
  420.  
  421.         MOV     AL,ES:[DI+5]            ; Get seconds value
  422.         mov     second_bin,al           ; save second in binary
  423.         BN2BCD                          ; Convert it
  424.         mov     second_bcd,al           ; save second in bcd
  425.  
  426.         MOV     AL,ES:[DI+2]            ; Get minutes value
  427.         mov     minute_bin,al           ; save minute in binary
  428.         BN2BCD
  429.         mov     minute_bcd,al           ; save minute in bcd
  430.  
  431.         MOV     AL,ES:[DI+3]            ; Get hours value
  432.         mov     hour_bin,al             ; save hour in binary
  433.         BN2BCD
  434.         mov     hour_bcd,al             ; save hour in bcd
  435.  
  436.  
  437.         MOV     AL,ES:[DI+4]            ; Get hundredths of seconds
  438.         mov     hundredth_bin,al        ; save on reserved stack
  439.  
  440.         MOV     DL,DevHlp_UnPhysToVirt  ; call UnPhysToVirt
  441.         CALL    [DevHlp]                ; original addr mode restored
  442.         JNC     WRITE_clock
  443.         JMP     WRITE_FAIL
  444.  
  445. WRITE_clock:
  446. ; prepare cmos clock to update clock
  447.         cli
  448. ;71736  MOV     AX,(RABYTE SHL 8) OR 8AH        ; Register A
  449. ;71736  cmos_write_clock                ; Stuff init value
  450.         MOV     AX,8B8BH                ; Register B (Init both for later set)
  451.         cmos_read_clock                 ; Get current value
  452.         AND     AL,00100000B            ; Preserve user alarm setting only
  453.         OR      AL,RBWRIT               ; Init all others to correct values
  454.         XCHG    AH,AL                   ; AL=register, AH=value
  455.         cmos_write_clock                ; Stop clock updating
  456.         MOV     AL,8CH                  ; Register C
  457.         cmos_read_clock                 ; Read to initialize only
  458.         MOV     AL,8DH                  ; Register D
  459.         cmos_read_clock                 ; Read to initialize only
  460.  
  461.         cmos_reset                      ; cmos RAM=>read port w/NMI enable
  462.         sti                             ; enable interrupts
  463.  
  464.         call    far ptr calc_year_sec   ; update sysinfoseg(interrupt disable)
  465.                                         ; es:bx=>SysInfoSeg
  466.         mov     al,hundredth_bin        ; get hundredth
  467.         MOV     ES:[BX.SIS_HunTime],AL  ; Stuff it in InfoSeg field
  468.  
  469. ; write cmos clock
  470.         mov     al,CMOS_location        ; AL = 37h -PS/2 OR 32h -AT
  471.         add     al,80h
  472.         MOV     Ah,century_bin          ; write century
  473.         cmos_write_clock
  474.  
  475.         MOV     AL,0+80H                ; set seconds
  476.         mov     ah,second_bcd
  477.         cmos_write_clock
  478.  
  479.         MOV     AL,2+80H                ; set minutes
  480.         mov     ah,minute_bcd
  481.         cmos_write_clock
  482.  
  483.         MOV     AL,4+80H                ; set hours
  484.         mov     ah,hour_bcd
  485.         cmos_write_clock
  486.  
  487.         MOV     AL,6+80H                ; set day_of_week
  488.         mov     ah,day_week_bcd
  489.         cmos_write_clock
  490.  
  491.         MOV     AL,7+80H                ; set day of month
  492.         mov     ah,day_month_bcd
  493.         cmos_write_clock
  494.  
  495.         MOV     AL,8+80H                ; set month
  496.         mov     ah,month_bcd
  497.         cmos_write_clock
  498.  
  499.         MOV     AL,9+80H                ; set year
  500.         mov     ah,year_bcd
  501.         cmos_write_clock
  502.  
  503. ; restore clock state
  504.         MOV     AX,8B8BH                ; Register B (Init both for later set)
  505.         cmos_read_clock                 ; Get current value
  506.         AND     AL,7FH                  ; Reset update disable bit
  507.         XCHG    AH,AL                   ; AH=value, AL=register
  508.         cmos_write_clock
  509.         cmos_reset                      ; cmos RAM=>read port w/NMI enable
  510.         sti                             ; enable interrupts
  511.  
  512.         MOV     AX,(STDON OR STBUI)     ; No errors
  513.         add     sp,18                   ; restore stack pointer
  514.         POP     BP
  515.         RET
  516.  
  517. RTWRIT  ENDP
  518.  
  519. ClkSwap ENDS
  520.  
  521.  
  522. ;***LP  CLKFreq(ulFreq) - Set RTC Periodic Frequency service
  523. ;
  524. ;       CLKFreq is called by the kernel to request a new frequency at
  525. ;       which real time clock periodic "ticks" are generated. (See DCR 1345)
  526. ;       The actual reprogramming of the device is delayed until such time
  527. ;       as it can be done safely: interrupts disabled, pre-EOI, and on the
  528. ;       next 32ms tick boundary
  529. ;
  530. ;       ENTRY
  531. ;           (TOS) = ulFreq  : desired frequency in Hz.
  532. ;                             Only 32 and 128 supported.
  533. ;       EXIT
  534. ;           SUCCESS
  535. ;               (eax) = 0
  536. ;           FAILURE
  537. ;               (eax) = ERROR_INVALID_FREQUENCY
  538. ;
  539. ;       CONTEXT
  540. ;           Any (called by TomTick; see task\tom.c)
  541. ;
  542. ;       PSEUDOCODE
  543. ;           if ulFreq is 32 or 128
  544. ;              record new frequency desired; the actual change
  545. ;                will occur on the next 32ms boundary
  546. ;              return rc = no errors;
  547. ;           else
  548. ;              return rc = invalid frequency
  549. ;
  550.  
  551. ClkCode SEGMENT
  552.  
  553.         ASSUME  CS:ClkCode,DS:NOTHING,ES:NOTHING,SS:NOTHING
  554. Procedure CLKFreq,far
  555.  
  556.         .386p
  557.  
  558.         ?abase  = 8 + 2
  559.         ArgVar  ulFreq,ULONG
  560.  
  561.         EnterProc
  562.         SaveReg <ds>
  563.  
  564.         mov     ds,cs:[selClkData]
  565.         ASSUME  DS:ClkData
  566.  
  567. ; check if requested frequency is allowed
  568.         cmp     WORD PTR [ulFreq],128   ; is frequency 128Hz?
  569.         je      clkfreq_valid           ; .. yes,  its valid
  570.         cmp     WORD PTR [ulFreq],32    ; is frequency 32Hz?
  571.         jne     clkfreq_invalid         ; ...... no, so fail
  572.  
  573. clkfreq_valid:
  574.         mov     ax,WORD PTR [ulFreq]
  575.         mov     ReqRTCFreq,al           ; record requested new frequency
  576.         xor     eax,eax                 ; (eax)=ERROR_NONE
  577.  
  578. clkfreq_exit:
  579.         RestoreReg <ds>
  580.         ASSUME  DS:NOTHING
  581.  
  582.         LeaveProc
  583.         retfd   ?aframe
  584.  
  585. clkfreq_invalid:
  586.         mov     eax,ERROR_INVALID_FREQUENCY  ;  errors
  587.         jmp     clkfreq_exit
  588.  
  589. EndProc CLKFreq
  590.  
  591.  
  592. BREAK <Realtime Clock Interrupt Service Routine>
  593. ;********************** START OF SPECIFICATIONS *********************
  594. ;*
  595. ;* MODULE NAME:  RTCINT
  596. ;*
  597. ;* DESCRIPTIVE NAME:  Interrupt Service routine for RT/CMOS Clock
  598. ;*
  599. ;* FUNCTION:
  600. ;*      This routine services RT/CMOS Clock interrupts, giving
  601. ;*      control to CP/DOS.  The entry point is associated with IRQ 8
  602. ;*      using the DevHlp SetIRQ function in RTINIT.  A periodic timer
  603. ;*      interrupt causes it to be invoked and the routine uses the
  604. ;*      DevHelp_SchedClock function first (before EOI) to inform
  605. ;*      CP/DOS of the timer tick and again later (after EOI) to allow
  606. ;*      SchedClock to perform any lengthy periodic operations.
  607. ;*
  608. ;*      On every update-ended interrupt, the clock device is read
  609. ;*      and the Global InfoSeg is updated with interrupts disabled.
  610. ;*
  611. ;* NOTES:
  612. ;*      Following issue of DevHlp_EOI to the Interrupt Manager (before
  613. ;*      the second SchedClock call) it is possible for this routine
  614. ;*      to be reentered.  Stack usage is kept to a minimum. Re-
  615. ;*      entrance should not cause problems, since no real processing
  616. ;*      is done by this routine after that point.
  617. ;*
  618. ;* ENTRY POINT: RTCINT
  619. ;*    LINKAGE:  FAR call by Interrupt Manager for IRQ Level = 8
  620. ;*
  621. ;* USES:  All registers.
  622. ;*
  623. ;* INPUT:  DS, Cs set up by Intmgr on call to RTCINT
  624. ;*
  625. ;* EXIT-NORMAL:         RET FAR
  626. ;*    RETURN CODE:      CF = 0 if my-interrupt
  627. ;*                      CF = 1 if not-my-interrupt
  628. ;*
  629. ;* EXIT-ERROR:
  630. ;*    RETURN CODE:
  631. ;*    ERROR MESSAGE:
  632. ;*
  633. ;* INTERNAL REFERENCES:
  634. ;*    STRUCTURES:  none
  635. ;*    ROUTINES:         FIXISEG, GTDAYS
  636. ;*
  637. ;* EXTERNAL REFERENCES:
  638. ;*    STRUCTURES:  Global Information Segment (SysInfoSeg)
  639. ;*    ROUTINES:         DevHlp_SchedClock,
  640. ;*                      DevHlp_EOI
  641. ;*
  642. ;*********************** END OF SPECIFICATIONS **********************
  643. page
  644. ;********************** START OF PSEUDO-CODE   **********************
  645. ;
  646. ; enable interrupts
  647. ; call GetRegC to get interrupt flags
  648. ; enable interrupts
  649. ; if update-ended interrupt flag set
  650. ;    update_flag = 1
  651. ; if periodic interrupt flag not set
  652. ;    goto EOI_RET
  653. ; endif
  654. ;
  655. ; disable interrupts to keep hundredths and milliseconds in sync
  656. ; Accum8msTicks++;
  657. ; increment hundredths by time (in hundredths) since last tick
  658. ; increment millseconds by time (in milliseconds) since last tick
  659. ; if Hz128To32 (first 32hz tick since RTC programmed from 128->32hz)
  660. ;    set increments to 1/32 sec and clear Hz128To32 flag
  661. ; if pending request to reprogram RTC frequency
  662. ;    if requested RTC frequency is 128hz
  663. ;        change rtc frequency to 128hz
  664. ;        change interval to 1/128
  665. ;        clear accumulated 8ms tick counter (Accum8msTicks)
  666. ;        clear pending request to reprogram RTC frequency
  667. ;    else (requested RTC frequency is 32hz)
  668. ;        if clock is on a 32ms boundary
  669. ;            change rtc frequency to 32hz
  670. ;            change interval to 2/128ths
  671. ;            set Hz128To32 to indicate transition
  672. ;            clear pending request to reprogram RTC frequency
  673. ; enable interrupts
  674. ;
  675. ; update_flag = 0
  676. ; if previous update_flag != 0
  677. ;    call FIXISEG to read clock to update SysInfoSeg
  678. ; endif
  679. ;
  680. ; call SchedClock to do pre-EOI with AX = interval
  681. ; if we are nested (i.e. Int_In_Progress != 0 )
  682. ;    increment nest_count
  683. ;    goto EOI_RET
  684. ; endif
  685. ; Int_In_Progress = 1
  686. ; call DevHlp to issue EOI
  687. ;
  688. ; nest_count = 0
  689. ; call chkINT15 to process int 15 with previous nest_count value
  690. ;
  691. ; call SchedClock to do post-EOI with AX = post_time
  692. ; Int_In_Progress = 0
  693. ;
  694. ; post_time = 0
  695. ; call timer ticker routine with previous post_time value
  696. ;
  697. ; clear carry flag
  698. ; return
  699. ;
  700. ; EOI_RET:
  701. ;    call DevHlp to issue EOI
  702. ;    clear carry flag
  703. ;    return
  704. ;
  705. ;*********************** END OF PSEUDO-CODE    **********************
  706.  
  707.     ASSUME      CS:ClkCode,DS:ClkData,ES:NOTHING,SS:NOTHING
  708. RTCINT  PROC    FAR
  709.         public  RTCINT
  710.         STI                             ; Run with interrupts enabled
  711.  
  712.                                         ; The interrupt managers does a pusha
  713.         CALL    GetRegC                 ; Get register C,(AX) = interrupt flags
  714.  
  715. ; IS THIS AN UPDATE INTERRUPT ?
  716.         TEST    AL,UIMASK               ; Update interrupt?
  717.         JZ      GotPeriodic             ; NO, go process the Periodic Int.
  718.         MOV     Update_Flag, 1          ; Set the Update flag
  719.         TEST    AL,PIMASK               ; Also a periodic interrupt?
  720.         jz      EOI_RET                 ; .. No, EOI and return...
  721.                                         ; .. Yes, process it...
  722.  
  723. ; disable interrupts to keep hundredths and milliseconds in sync
  724. ; Accum8msTicks++;
  725. ; increment hundredths by time (in hundredths) since last tick
  726. ; increment millseconds by time (in milliseconds) since last tick
  727. ; if Hz128To32 (first 32hz tick since RTC programmed from 128->32hz)
  728. ;    set increments to 1/32 sec and clear Hz128To32 flag
  729. ; if pending request to reprogram RTC frequency
  730. ;    if requested RTC frequency is 128hz
  731. ;        change rtc frequency to 128hz
  732. ;        change interval to 1/128
  733. ;        clear accumulated 8ms tick counter (Accum8msTicks)
  734. ;        clear pending request to reprogram RTC frequency
  735. ;    else (requested RTC frequency is 32hz)
  736. ;        if clock is on a 32ms boundary
  737. ;            change rtc frequency to 32hz
  738. ;            change interval to 2/128ths
  739. ;            set Hz128To32 to indicate transition
  740. ;            clear pending request to reprogram RTC frequency
  741. ; enable interrupts
  742.  
  743. GotPeriodic:
  744.  
  745.         cli     ; disable ints during update; keep everything synchronous
  746.         inc     Accum8msTicks;  ; only needed if at 128hz, but do always
  747.  
  748. ; increment hundredths by time (in hundredths) since last tick
  749.         les     BX,[InfSeg]                     ; ES:BX -> Global InfoSeg
  750.         mov     ah,BYTE PTR ES:[BX.SIS_HunTime] ; Get current hundredths
  751.         mov     al,Hundred_Frac                 ; align fraction part we keep
  752.         add     ax,Hundred_Inc                  ; add in hundredths tick inc.
  753.         mov     Hundred_Frac,al                 ; save new fractional part
  754.  
  755. ;** the following is a      to account for the case where a user sets the
  756. ;  hundredths field ahead, such that ensuing ticks will cause the
  757. ;  hundredths to exceed 100.  We prevent this condidtion.  Note that
  758. ;  this causes the 100ths to "freeze" at 99 until the next 1 second update
  759. ;  interrupt advances the seconds.  Ref: PTM 6670
  760.  
  761.         cmp     ah,100                          ; over 100?
  762.         jb      NoOverflow                      ; ..no,
  763.         mov     ah,99                           ; ..yes, DO NOT allow it > 100
  764. NoOverflow :
  765.         mov     BYTE PTR ES:[BX.SIS_HunTime],ah ; update Hundredths
  766.  
  767. ; increment millseconds by time (in milliseconds) since last tick
  768.         mov     ax,MS_Inc_Frac          ; get fraction of millisecond increment
  769.         add     MS_Fraction,ax          ; add to running millisecond fraction
  770.         mov     eax,ES:[BX.SIS_MsCount] ; get system's ms count (whole)
  771.         mov     ecx,eax                 ; save to determine actual ms delta
  772.         adc     eax,MS_Inc_Whole        ; add in the whole increment part
  773.         mov     ES:[BX.SIS_MsCount],eax ; update the system's ms count
  774.         sub     eax,ecx                 ; compute actual ms delta
  775.         mov     MS_Delta,ax             ; save delta in word
  776.  
  777.         cmp     Hz128To32,0             ; First 32hz tick after reprogramming?
  778.         je      NoAdjust                ; .. no, increment is ok as is.
  779.         mov     Hundred_Inc,((100 SHL 8) / 32); .. yes, set increments to 1/32
  780.         mov     MS_Inc_Whole,(1000 / 32)
  781.         mov     MS_Inc_Frac,(((1000 SHL 16) / 32) AND 0FFFFh)
  782.         mov     Hz128To32,0             ; reset flag
  783.  
  784. NoAdjust:
  785.         cmp     ReqRTCFreq,0    ; pending request to change the rtc frequency?
  786.         je      rtcdone         ; .. no, done with the rtc
  787.         mov     al,ReqRTCFreq   ; Get requested rtc frequency
  788.         cmp     al,CurRTCFreq   ; Are we requesting the current frequency?
  789.         je      hzchanged       ; .. yes, don't reprogram, but mark change
  790.         cmp     al,128          ; request to change to 128hz?
  791.         je      hz128           ; .. yes, safe to do it now
  792.                                 ; .. no, change to 32hz only on 32ms border
  793.         mov     ax,Accum8msTicks  ; get #accumulated 8ms ticks
  794.         and     ax,3            ; are we on 1/32nd tick border?
  795.         jnz     rtcdone         ; .. no, not safe to reprogram clock yet
  796.  
  797. hz32:
  798.  
  799. ; program new periodic interrupt rate to 32hz
  800. ; set frequency to indicate 1/32nd interval
  801. ; set hundreths, milliseconds, frequency to zero
  802. ;   Increment 2/128ths the next tick, rather than a full 1/32 interval,
  803. ;   or the rest of the 1/32 current interval remaining.
  804. ;   Experiment has shown that incrementing time
  805. ;   in this manner keeps MsCount adding up correctly
  806. ;   while changing the RTC's frequency.
  807. ;   Time will increment at 1/32 starting the tick after next
  808. ; clear frequency change request (ReqRTCFreq)
  809.  
  810. ifdef   MONITOR
  811. ; This shows how 8ms ticks accumulate.
  812. ACCM8SIZE EQU 64
  813.         mov     bx,Accum8msTicks        ; get #accumulated 8ms ticks
  814.         cmp     bx,ACCM8SIZE
  815.         jl      inceach
  816.         inc     Accm8p
  817.         jmp     incedone
  818. inceach:
  819.         shl     bx,1
  820.         inc     Accm8[bx]
  821.         shr     bx,1
  822. incedone:
  823.         and     bx,3                    ; ..last 1/32nd tick  (0,1,2 or 3)
  824.         shl     bx,1
  825.         inc     Accm8mod4[bx]
  826.         shr     bx,1
  827. endif
  828.  
  829.  
  830.         MOV     AX,((DV32K OR IN032) SHL 8) OR 0AH  ; rtc to 32hz
  831.         cmos_write_clock                       ; do it
  832.         mov     CurRTCFreq,32   ; indicate new 32hz frequency
  833.         mov     Hundred_Inc,((100 SHL 8) / 64) ; set increment to 2/128
  834.         mov     MS_Inc_Whole,(1000 / 64)       ; .. for the next tick only
  835.         mov     MS_Inc_Frac,(((1000 SHL 16) / 64) AND 0FFFFh)
  836.         mov     Hz128To32,1     ; set flag to indicate 128->32hz transition
  837.         jmp     hzchanged       ; 
  838.  
  839. hz128:
  840. ; program new periodic interrupt rate to 128hz
  841. ; set hundreths, milliseconds, frequency to indicate 1/128nd interval
  842.  
  843.         MOV     AX,((DV32K OR IN128) SHL 8) OR 0AH  ; rtc to 128hz
  844.         cmos_write_clock                       ; do it
  845.         mov     CurRTCFreq,128                 ; indicate new 128hz frequency
  846.         mov     Hundred_Inc,((100 SHL 8) / 128); set increments to 1/128
  847.         mov     MS_Inc_Whole,(1000 / 128)
  848.         mov     MS_Inc_Frac,(((1000 SHL 16) / 128) AND 0FFFFh)
  849.         mov     Accum8msTicks,0                ; start counting 7.8125ms ticks
  850.  
  851. hzchanged:
  852.         mov     ReqRTCFreq,0    ; clear frequency change request indicator
  853.  
  854. rtcdone:
  855.         sti                     ; enable, all timer counts updated
  856.  
  857. ;*************** Do not change FIXISEG and post_time sequence *********
  858. ; Determine if there was an Update Int that occured previously
  859. ;          and needs handling now.
  860.  
  861.         xor     al,al
  862.         xchg    al,Update_Flag          ; set update_flag = 0
  863.         or      al,al                   ; Was there an update interrupt?
  864.         je      Continue                ; No.
  865.  
  866. ; This driver makes every effort to program the clock to 128hz ONLY within
  867. ; the first 7.8125ms after a full 32hz tick.  This is required in order for
  868. ; increments of the running millisecond counter to stay accurate.  In
  869. ; (hopefully rare) cases, higher priority interrupt activity may prevent
  870. ; the clock from being reprogrammed to 128hz within the 7.8125ms window.
  871. ;
  872. ; When the clock is programmed from 32hz to 128hz, system time will
  873. ; increment at 7.8125ms starting the next tick.  In the worst case, up to
  874. ; 31.25ms of real time may have passed just prior to the reprogramming.
  875. ; This can result in up to 24ms of time being "lost".  We attempt to limit
  876. ; the accumulation of this "lost time" within a one second period to no
  877. ; more than 32ms.  We predict what the MSCount should be one second from
  878. ; now.  If it is more than 32ms less, we make a correction.  This also
  879. ; helps keep the MsCount accurate in the event of missed periodic
  880. ; interrupts.
  881. ;
  882. ; It is possible that the independent one-second interrupt arrived just
  883. ; before the last 32hz periodic interrupt.  In that case, the time will be
  884. ; 32ms short of one second.  That is allowed.  But if the time accumulation
  885. ; is more than 32ms short, we assume either periodic interrupt(s) were missed
  886. ; or several reprogrammings of the clock frequency resulted in more than
  887. ; 32ms of "lost time".  In this case, we adjust the running millisecond
  888. ; count to our previously predicted value.  This limits the loss to 32ms.
  889.  
  890.         mov     eax,ExpectedMsCount     ; recall expected millisecond count
  891.         mov     ecx,ES:[BX.SIS_MsCount] ; get the real MsCount
  892.         sub     eax,ecx                 ; Did the real MsCount fall behind?
  893.         jle     NotLagging              ; .. No, continue
  894.         add     ecx,eax                 ; .. Yes, MsCount should be this
  895.         mov     ES:[BX.SIS_MsCount],ecx ; bring it up to speed
  896.         add     MS_Delta,ax             ; This contributes to the delta
  897. ifdef   MONITOR
  898.         add     LagTotal,eax            ; it is nice to know these values..
  899.         inc     LagCount                ; .. to observe timing dynamics.
  900. endif
  901.  
  902. NotLagging:
  903.         add     ecx,968                 ; We expect at least 1000-32ms to pass
  904.         mov     ExpectedMsCount,ecx     ; save for next time
  905.  
  906.         push    cx
  907.         mov     cx,1                    ; only read clock once
  908.         call    FIXISEG                 ; Read clock and update Infoseg
  909.         pop     cx
  910.  
  911. Continue:
  912.         mov     ax,MS_Delta             ; get number of milliseconds transpired
  913.         add     Post_Time,ax            ; Post = n * Pre
  914. ;*************** end of sequence problem ************************
  915.  
  916.         push    DS
  917.         les     BX,SchedClock
  918.         xor     DH,DH                   ; DH=0 to indicate pre-EOI
  919.         call    DWORD PTR ES:[BX]       ; call timer tick routine
  920.         pop     DS
  921.  
  922. ;here we test for nested int or not.  if we are already nested, we issue
  923. ;the EOI disabled and return without doing any Post_EOI processing
  924.  
  925.         MOV     AL,1
  926.         XCHG    Int_In_Progress,AL      ;  set to nested
  927.         OR      AL,AL                   ;  are we nested?
  928.         jz      Not_Nest                ;  if ZR, we are not
  929.         inc     Int_Nested              ;  bump nested counter
  930.  
  931. EOI_RET:
  932.         cli                             ; b720746
  933. ;       MOV     AL,RTCIRQ               ;  interrupt level = 8   remove 76711
  934. ;       MOV     DL,DevHlp_EOI           ;                        remove 76711
  935. ;       CALL    [DevHlp]                ;  call the EOI devhlp   remove 76711
  936.         DevEOI  <RTCIRQ>,DevHlp         ;                        add    76711
  937.         clc                             ;  always clear CY
  938.         ret                             ;  leave
  939.  
  940. ;we are here if we are in a first level Int handler, we have to account
  941. ;for any nesting that may have occured after the EOI
  942.  
  943. Not_Nest:                               ;  we are first level
  944. ;       MOV     AL,RTCIRQ               ;  interrupt level = 8   remove 76711
  945. ;       MOV     DL,DevHlp_EOI           ;                        remove 76711
  946. ;       CALL    [DevHlp]                ;  call the EOI devhlp   remove 76711
  947.         DevEOI  <RTCIRQ>,DevHlp         ;                        add    76711
  948.  
  949. ; call SchedClock to do post-EOI with AX = post_time
  950.         MOV     DH,01H                  ; DH=1 to indicate post-EOI
  951.  
  952.         PUSH    DS                      ;  moved this
  953.         LES     BX,SchedClock
  954.         xor     ax,ax
  955.         xchg    ax,Post_Time            ; zero out for possible nests
  956.  
  957. ; Input: AX = # of ms to catch up on, same as Pre if no nesting
  958.         CALL    DWORD PTR ES:[BX]       ; call timer tick routine
  959.         POP     DS                      ;  and this
  960.  
  961. ; reset all variables used to check for nesting
  962.         cli                             ; b720746
  963.         mov     Int_In_Progress,0       ;  clear nested flag
  964.         CLC
  965.         RET
  966. RTCINT  ENDP
  967.  
  968.  
  969. BREAK <Routine to Update the Global InfoSeg>
  970. ;********************** START OF SPECIFICATIONS *********************
  971. ;*
  972. ;* MODULE NAME:  FIXISEG
  973. ;*
  974. ;* DESCRIPTIVE NAME: Routine to read the RT/CMOS clock and then
  975. ;*      update the Global InfoSeg date/time variables.
  976. ;*
  977. ;* FUNCTION:
  978. ;*      Performs a read request to the Realtime Clock device
  979. ;*      and then updates the global InfoSeg time/date information.
  980. ;*
  981. ;* NOTES:
  982. ;*      This routine is called by three routines of the clock
  983. ;*      device driver.  RTINIT (initialization) and RTCINT (interrupt
  984. ;*      handler) use this routine to initialize and update InfoSeg
  985. ;*      respectively.
  986. ;*
  987. ;*      Time is reduced to seconds and preserved for later adding
  988. ;*      into secs since 1-1-70.
  989. ;*
  990. ;* ENTRY POINT: FIXISEG
  991. ;*    LINKAGE:  Near (from RTINIT, RTCINT)
  992. ;*
  993. ;* USES:  Preserves all registers except AX
  994. ;*
  995. ;* INPUT: CX = # OF RETRY COUNT
  996. ;*
  997. ;* OUTPUT: (RETURNED)
  998. ;*      No interrupt flag modified
  999. ;*      Global InfoSeg time/date data is filled in.
  1000. ;*      AX = status flag is set, CARRY FLAG indicates success
  1001. ;*
  1002. ;* EXIT-NORMAL:
  1003. ;*      CF is cleared
  1004. ;*
  1005. ;* EXIT-ERROR:
  1006. ;*      AX = STATUS word:  DONE, ERROR + DEVICE NOT READY bits set
  1007. ;*      CF is set
  1008. ;*
  1009. ;* INTERNAL REFERENCES:
  1010. ;*    STRUCTURES:  none
  1011. ;*    ROUTINES:  cmos_read_clock, calc_year_sec
  1012. ;*
  1013. ;* EXTERNAL REFERENCES:
  1014. ;*    DATA STRUCTURES:  Global Information Segment (SysInfoSeg)
  1015. ;*    ROUTINES:  none
  1016. ;*
  1017. ;*********************** END OF SPECIFICATIONS **********************
  1018. page
  1019. ;********************** START OF PSEUDO-CODE   **********************
  1020. ;
  1021. ; for try_count = 1 to 300
  1022. ;     read register A
  1023. ;     if register A shows update is done
  1024. ;        break
  1025. ; endfor
  1026. ;
  1027. ; if try_count > 300
  1028. ;    set return code DONE+ERROR+DEVICE NOT READY
  1029. ;    set carry flag on
  1030. ;    return
  1031. ; endif
  1032. ;
  1033. ; save interrupt flag
  1034. ; disable interrupts
  1035. ; call cmos_read_clockto read seconds, minutes, hours, years,
  1036. ;      day of month, & months from chip
  1037. ; restore interrupt flag
  1038. ; convert seconds, minutes, hours, years, day of month, & months to BCD
  1039. ;
  1040. ; call calc_year_sec to update sysinfoseg
  1041. ; hundredth_count_timer = 0
  1042. ; HunTime = 0
  1043. ; restore interrupt flag
  1044. ; return
  1045. ;
  1046. ;*********************** END OF PSEUDO-CODE    **********************
  1047.  
  1048.     ASSUME      CS:ClkCode,DS:ClkData,ES:NOTHING,SS:NOTHING
  1049. FIXISEG PROC    NEAR
  1050.         public  FIXISEG
  1051.         SaveReg <AX,BX,ES>
  1052.                                         ; (begin)
  1053. ; for try_count = 1 to 300
  1054. ;     read register A
  1055. ;     if register A shows update is done
  1056. ;        break
  1057. ; endfor
  1058. ;
  1059. ; if try_count > 300
  1060. ;    set return code DONE+ERROR+DEVICE NOT READY
  1061. ;    set carry flag on
  1062. ;    return
  1063. ; endif
  1064. ;
  1065. CHKAGN: MOV     AL,0AH                  ; Address of 'A' register
  1066.  
  1067.         pushf                           ; save interrupt flag
  1068.         cli                             ; disable interrupts
  1069.         cmos_read_clock                 ; Read register 'A'
  1070.         push    cs
  1071.         call    cmos_popf               ; restore interrupt flag
  1072.  
  1073.         TEST    AL,80H                  ; If bit 7 set, cannot read clock
  1074.         JZ      FIS2                    ; Okay, go read it
  1075.         LOOP    CHKAGN                  ; Try 300 times
  1076.  
  1077.         MOV     AX,(STDON+STERR+ERRDNR)
  1078.         STC
  1079.         JMP     GOBACK                  ; Return Done and Device Not Ready
  1080.  
  1081. FIS2:
  1082.         push    bp
  1083.         sub     sp,18                   ; reserved for clock information
  1084.         mov     bp,sp
  1085.  
  1086. ; call cmos_read_clock to read seconds, minutes, hours, years,
  1087. ;      day of month, & months from chip
  1088. ;*                +---------------------+
  1089. ;*                |  old  BP            |
  1090. ;*                +---------------------+
  1091. ;*         BP+16->| ???                 |
  1092. ;*                +---------------------+
  1093. ;*         BP+14->| YEAR/BCD |          |
  1094. ;*                +---------------------+
  1095. ;*         BP+12->| MONTH/BCD|          |
  1096. ;*                +---------------------+
  1097. ;*         BP+10->| DAY OF MONTH        |
  1098. ;*                |  BCD     |          |
  1099. ;*                +---------------------+
  1100. ;*         BP+8 ->|                     |
  1101. ;*                +---------------------+
  1102. ;*         BP+6 ->| HOUR/BCD |          |
  1103. ;*                +---------------------+
  1104. ;*         BP+4 ->| MIN/BCD  |          |
  1105. ;*                +---------------------+
  1106. ;*         BP+2 ->| SEC/BCD  |          |
  1107. ;*                +---------------------+
  1108.  
  1109.         pushf                           ; save interrupt flag
  1110.         CLI                             ; DISABLE INTERRUPTS
  1111.         MOV     AL,0+80H                ; get seconds
  1112.         cmos_read_clock
  1113.         mov     second_bcd,al
  1114.  
  1115.         MOV     AL,2+80H                ; get minutes
  1116.         cmos_read_clock
  1117.         mov     minute_bcd,al
  1118.  
  1119.         MOV     AL,4+80H                ; get hours
  1120.         cmos_read_clock
  1121.         mov     hour_bcd,al
  1122.  
  1123.         MOV     AL,7+80H                ; get day of month
  1124.         cmos_read_clock
  1125.         mov     day_month_bcd,al
  1126.  
  1127.         MOV     AL,8+80H                ; get month
  1128.         cmos_read_clock
  1129.         mov     month_bcd,al
  1130.  
  1131.         MOV     AL,9+80H                ; get year
  1132.         cmos_read_clock
  1133.         mov     year_bcd,al
  1134.  
  1135.         cmos_reset                      ; cmos RAM=>read port w/NMI enable
  1136.         push    cs
  1137.         call    cmos_popf               ; restore interrupt flag
  1138.  
  1139. ; convert seconds, minutes, hours, years, day of month, & months to BCD
  1140.  
  1141.         mov     al,second_bcd           ; Get seconds
  1142.         BCD2BN                          ; Convert it
  1143.         MOV     second_bin,al           ; Stuff it in table
  1144.  
  1145.         mov     al,minute_bcd           ; get minutes
  1146.         BCD2BN
  1147.         MOV     minute_bin,al           ; Stuff it in table
  1148.  
  1149.         MOV     AL,hour_bcd             ; Same for hours
  1150.         BCD2BN
  1151.         MOV     hour_bin,al             ; Stuff it in table
  1152.  
  1153.         MOV     AL,day_month_bcd        ; Same for day of month
  1154.         BCD2BN
  1155.         MOV     day_month_bin,al
  1156.  
  1157.         MOV     al,month_bcd            ; get month
  1158.         BCD2BN
  1159.         MOV     month_bin,al
  1160.  
  1161.         MOV     al,year_bcd             ; get month
  1162.         BCD2BN
  1163.         MOV     year_bin,al
  1164.  
  1165.         pushf                           ; save interrupt flag
  1166.         call    far ptr calc_year_sec   ; AX:DX=seconds from 1-1-1970
  1167.         mov     ES:[BX.SIS_HunTime],00H
  1168.         push    cs
  1169.         call    cmos_popf               ; restore interrupt flag
  1170.         add     sp,18                   ; restore local stack
  1171.         pop     bp
  1172.         CLC
  1173.  
  1174. GOBACK:
  1175.         RestoreReg <ES,BX,AX>
  1176.         RET                             ; Return AX
  1177. FIXISEG ENDP
  1178.  
  1179.  
  1180. ;********************** START OF SPECIFICATIONS *********************
  1181. ;*
  1182. ;* MODULE NAME:  calc_year_sec
  1183. ;*
  1184. ;* DESCRIPTIVE NAME: Calculate total seconds and current year
  1185. ;*
  1186. ;* FUNCTION:
  1187. ;*      Calculate the total seconds from 1-1-1970, and get the
  1188. ;*      current year in century year, e.g., 1987.
  1189. ;*
  1190. ;* NOTES:
  1191. ;*      This routine is called by three routines of the clock
  1192. ;*      device driver.  FIXISEG initializes and updates InfoSeg.
  1193. ;*      The RTWRIT sub-module uses it to update
  1194. ;*      InfoSeg following a resetting of the Realtime Clock device.
  1195. ;*
  1196. ;*      Time is reduced to seconds and preserved for later adding
  1197. ;*      into secs since 1-1-70.
  1198. ;*
  1199. ;* ENTRY POINT: calc_year_sec
  1200. ;*    LINKAGE:  Far (from FIXISEG, RTWRIT)
  1201. ;*
  1202. ;* USES:  Preserves all registers except AX
  1203. ;*
  1204. ;* INPUT:
  1205. ;*                +---------------------+
  1206. ;*                |  old  BP            |
  1207. ;*                +---------------------+
  1208. ;*         BP+16->|     ???             |
  1209. ;*                |                     |
  1210. ;*                +---------------------+
  1211. ;*         BP+14->| YEAR/BCD | YEAR/BIN |
  1212. ;*                +---------------------+
  1213. ;*         BP+12->| MONTH/BCD| MON/BIN  |
  1214. ;*                +---------------------+
  1215. ;*         BP+10->| DAY OF MONTH        |
  1216. ;*                |  BCD     |  BINARY  |
  1217. ;*                +---------------------+
  1218. ;*         BP+8 ->| DAY OF WEEK         |
  1219. ;*                |  BCD     |  BINARY  |
  1220. ;*                +---------------------+
  1221. ;*         BP+6 ->| HOUR/BCD | HOUR/BIN |
  1222. ;*                +---------------------+
  1223. ;*         BP+4 ->| MIN/BCD  | MIN/BIN  |
  1224. ;*                +---------------------+
  1225. ;*         BP+2 ->| SEC/BCD  | SEC/BIN  |
  1226. ;*                +---------------------+
  1227. ;*         BP   ->| CENTURY  |HUNDREDTH |
  1228. ;*                |  BINARY  |  BINARY  |
  1229. ;*                +---------------------+
  1230. ;*
  1231. ;* OUTPUT: (RETURNED)
  1232. ;*      ES:BX = address of SysInfoSeg
  1233. ;*      Global InfoSeg time/date data is filled in.
  1234. ;*      Interrupt Disabled.
  1235. ;*                +---------------------+
  1236. ;*                |  old  BP            |
  1237. ;*                +---------------------+
  1238. ;*         BP+16->| CURRENT CENTURY YEAR|
  1239. ;*                |  BCD     |  BINARY  |
  1240. ;*                +---------------------+
  1241. ;*         BP+14->| YEAR/BCD | YEAR/BIN |
  1242. ;*                +---------------------+
  1243. ;*         BP+12->| MONTH/BCD| MON/BIN  |
  1244. ;*                +---------------------+
  1245. ;*         BP+10->| DAY OF MONTH        |
  1246. ;*                |  BCD     |  BINARY  |
  1247. ;*                +---------------------+
  1248. ;*         BP+8 ->| DAY OF WEEK         |
  1249. ;*                |  BCD     |  BINARY  |
  1250. ;*                +---------------------+
  1251. ;*         BP+6 ->| HOUR/BCD | HOUR/BIN |
  1252. ;*                +---------------------+
  1253. ;*         BP+4 ->| MIN/BCD  | MIN/BIN  |
  1254. ;*                +---------------------+
  1255. ;*         BP+2 ->| SEC/BCD  | SEC/BIN  |
  1256. ;*                +---------------------+
  1257. ;*         BP   ->| CENTURY  |HUNDREDTH |
  1258. ;*                |  BINARY  |  BINARY  |
  1259. ;*                +---------------------+
  1260. ;*
  1261. ;* EXIT-NORMAL:
  1262. ;*
  1263. ;* EXIT-ERROR:
  1264. ;*
  1265. ;* INTERNAL REFERENCES:
  1266. ;*    STRUCTURES:  none
  1267. ;*    ROUTINES:  FixDayOfWeek, GTDAYS
  1268. ;*
  1269. ;* EXTERNAL REFERENCES:
  1270. ;*    DATA STRUCTURES:
  1271. ;*    ROUTINES:  none
  1272. ;*
  1273. ;*********************** END OF SPECIFICATIONS **********************
  1274. page
  1275. ;********************** START OF PSEUDO-CODE   **********************
  1276. ;
  1277. ; if hours > 12
  1278. ;    factor = 1
  1279. ;    hours -= 12
  1280. ; else
  1281. ;    factor = 0
  1282. ; total_seconds = seconds + minutes * 60 + hours * 3600
  1283. ;
  1284. ; if year > 99 or month > 12 or month = 0 or Day_of_Month = 0 or
  1285. ;    Day_of_Month > 31
  1286. ;    set year = 80
  1287. ;    set date = 1-1
  1288. ;    set Day_of_Week = 2
  1289. ; endif
  1290. ; if year is over 20 centry ( year < 80 )
  1291. ;    year += 100
  1292. ; year += 1900
  1293. ; save year in century_year stack
  1294. ; call GTDAYS to get DAYS since 1-1-80
  1295. ; total_time from 1-1-70 =
  1296. ;      ( (DAYS+# of days from 1-1-70 to 12-31-79) * # of 12 hours in a day +
  1297. ;       factor ) * seconds in 12 hours + total_seconds
  1298. ; disable interrupts
  1299. ; save all the information in SysInfoSeg
  1300. ; return
  1301. ;
  1302. ;*********************** END OF PSEUDO-CODE    **********************
  1303.  
  1304. calc_year_sec PROC    FAR
  1305.     ASSUME      CS:ClkCode,DS:ClkData,ES:NOTHING,SS:NOTHING
  1306.  
  1307.         SaveReg <CX,DX,SI,DI>
  1308.                                         ; (begin)
  1309. ; if hours > 12
  1310. ;    factor = 1
  1311. ;    hours -= 12
  1312. ; else
  1313. ;    factor = 0
  1314. ; total_seconds = seconds + minutes * 60 + hours * 3600
  1315. ; read Day_of_Month, month, & year from chip
  1316.         mov     al,second_bin           ; get seconds
  1317.         XOR     AH,AH
  1318.         MOV     SI,AX                   ; Save sec's
  1319.  
  1320.         mov     al,minute_bin           ; get minutes
  1321.         MOV     CL,60                   ; secs/min
  1322.         MUL     CL
  1323.         ADD     SI,AX                   ; SI = secs in mins and secs
  1324.  
  1325.         xOR     AH,AH
  1326.         mov     AL,hour_bin             ; get hours
  1327.         XOR     CX,CX                   ; ch=day of week; cl=factor
  1328.         CMP     AL,12                   ; >12 hours?
  1329.         JBE     calc10                  ; (M003)
  1330.         SUB     AL,12
  1331.         INC     cl                      ; add factor = 1
  1332.  
  1333. calc10: PUSH    CX
  1334.         MOV     CX,3600
  1335.         MUL     CX                      ; AX = secs in hrs remainder
  1336.         POP     CX
  1337.         ADD     SI,AX                   ; SI = secs in 12 hr time
  1338.  
  1339. ; Here we do a VERY rough bounds check on the Year, Month, Day and DOW
  1340. ;
  1341. ; if year > 99 or month > 12 or month = 0 or Day_of_Month = 0 or
  1342. ;    Day_of_Month > 31
  1343. ;    set year = 80
  1344. ;    set date = 1-1
  1345. ;    set Day_of_Week = 2
  1346. ;
  1347.         mov     dh,month_bin
  1348.         mov     dl,day_month_bin
  1349.         MOV     AL,year_bin             ; get year
  1350.  
  1351.         CMP     AL,99                   ; Year > 99?
  1352.         JA      DATERR
  1353.         OR      DH,DH                   ; Month = 0?
  1354.         JZ      DATERR
  1355.         CMP     DH,12                   ; Month > 12?
  1356.         JA      DATERR
  1357.         OR      DL,DL                   ; Day = 0?
  1358.         JZ      DATERR
  1359.         CMP     DL,31                   ; Day > 31?
  1360.         JLE     DATEOK
  1361.  
  1362. DATERR: MOV     AL,80
  1363.         MOV     DH,1
  1364.         MOV     DL,1                    ; Setup for 1-1-80 if     date in RTC
  1365.         MOV     CH,2                    ; 1-1-80 was a Tuesday (M003)
  1366.         mov     month_bin,dh
  1367.         mov     day_month_bin,dl
  1368.         mov     year_bin,al
  1369.  
  1370. DATEOK:
  1371. ; if year is over 20 centry ( year < 80 )
  1372. ;    year += 100
  1373.         XOR     AH,AH                   ; AX = LOWER PART OF YEAR
  1374.         CMP     AL,80                   ; Is it 20xx?
  1375.         JGE     calc20                  ; No, Add only 1900
  1376.         ADD     AX,100                  ; Yes, add extra 100
  1377.  
  1378. ; year += 1900
  1379. ; save on stack
  1380. calc20: ADD     AX,1900                 ; AX = Year and century
  1381.         MOV     century_year,ax         ; 
  1382.         SUB     AX,1980                 ; Get years since 1980
  1383.  
  1384. ; call GTDAYS to get DAYS since 1-1-80
  1385. ;* INPUT:     AX = Years since 1-1-80
  1386. ;*            DH = Current month
  1387. ;*            DL = Current day
  1388.         CALL    GTDAYS                  ; AX=Get days since 1-1-80
  1389.  
  1390. ; if Day_of_Week != 2
  1391. ;    call FixDayOfWeek to get Day_of_Week
  1392. ;
  1393.         OR      CH,CH                   ; Is day_of_week correct?
  1394.         JNZ     CENT20                  ; Yes, jump
  1395.         CALL    FixDayOfWeek
  1396.         mov     day_week_bin,ch         ; save on stack
  1397.  
  1398. CENT20:
  1399. ; total_time from 1-1-70 =
  1400. ;      ( (DAYS+# of days from 1-1-70 to 12-31-79) * # of 12 hours in a day +
  1401. ;       factor ) * seconds in 12 hours + total_seconds
  1402.         XOR     BX,BX                   ; Clear it
  1403.         ADD     AX,DAYS1                ; Add in days 1-1-70 thru 12-31-79
  1404.         SHL     AX,1                    ; AX = # of 12 hour segments
  1405.         rcl     bl,1                    ; Save overflow (carry) in bl
  1406.         xor     ch,ch                   ; CX = add factor (0/1)
  1407.         add     ax,cx                   ; AX=Low 16 bits of 12 hour segments
  1408.         MOV     CX,SECP12               ; CX = Seconds in 12 hours
  1409.         MUL     CX                      ; [DXAX] = Secs in Low 16 bits of segs
  1410.         shr     bl,1                    ; Any beyond Low 16 bits?
  1411.         jnc     calc30                  ; No, [DXAX] = Total seconds
  1412.  
  1413.         add     dx,cx                   ; Yes, fudge bit 17 multiply
  1414. calc30: add     ax,si                   ; Add in secs in current time
  1415.         adc     dx,0                    ; ...and any carry
  1416.  
  1417.         LES     BX,[InfSeg]             ; ES:BX -> InfoSeg
  1418.         cli                             ; disable interrupts
  1419.         MOV     WORD PTR ES:[BX.SIS_BigTime],AX
  1420.         MOV     WORD PTR ES:[BX.SIS_BigTime+2],DX
  1421.         mov     al,second_bin           ; get seconds
  1422.         MOV     ES:[BX.SIS_SecTime],AL  ; Stuff it in table
  1423.         mov     al,minute_bin           ; get minutes
  1424.         MOV     ES:[BX.SIS_MinTime],AL
  1425.         mov     al,hour_bin             ; get hour
  1426.         MOV     ES:[BX.SIS_HrsTime],AL  ; InfoSeg time is complete
  1427.         mov     al,day_month_bin        ; get day of month
  1428.         MOV     ES:[BX.SIS_DayDate],al
  1429.         mov     al,month_bin            ; get month
  1430.         MOV     ES:[BX.SIS_MonDate],al
  1431.         mov     al,day_week_bin         ; get day of week
  1432.         MOV     ES:[BX.SIS_DOWDate],al  ; save the day (DOW)
  1433.         mov     ax,century_year
  1434.         MOV     ES:[BX.SIS_YrsDate],AX  ; InfoSeg date is complete
  1435.  
  1436.         RestoreReg <DI,SI,DX,CX>
  1437.         RET
  1438. calc_year_sec ENDP
  1439.  
  1440. ; restore interrupt flag
  1441. CMOS_POPF       PROC
  1442.                 IRET                    ; RESTORE FLAGS
  1443. CMOS_POPF       ENDP
  1444.  
  1445. ;********************** START OF SPECIFICATIONS *********************
  1446. ;**     GetRegC - Get interrupt status from register C
  1447. ;*
  1448. ;*      This reads the C register (interrupt ID) TWICE to make
  1449. ;*      sure that the IRQF will be clear.  The IRQF directly corresponds
  1450. ;*      to the hardware IRQ line to the 8259.  Another interrpt can become
  1451. ;*      pending while we are reading the first Interrupt.  If this happens
  1452. ;*      then the IRQF line will REMAIN set and then it needs to be read
  1453. ;*      again.  This routine is to assure that the IRQ line will drop.
  1454. ;*
  1455. ;*      NOTE: on the XT286 the clock is different than the AT clock.
  1456. ;*      The XT clock requires that an OUT is done before each IN to a port.
  1457. ;*      This is because of the Stand-by mode of the XT286.
  1458. ;*
  1459. ;*      ENTRY   none
  1460. ;*
  1461. ;*      EXIT    [AL] = Byte recieved from register C
  1462. ;*
  1463. ;*      USES    Preserves all registers except AX
  1464. ;*
  1465. ;*********************** END OF SPECIFICATIONS **********************
  1466. page
  1467. ;********************** START OF PSEUDO-CODE   **********************
  1468. ;
  1469. ; save interrupt flag
  1470. ; disable interrupts
  1471. ; select register C to get register C 1st value
  1472. ; select register C to get register C 2nd value
  1473. ; restore interrupt flag
  1474. ; combine register C 1st & 2nd values
  1475. ; if register C combine value = 0
  1476. ;    set zero flag
  1477. ; else
  1478. ;    clear zero flag
  1479. ; return
  1480. ;
  1481. ;*********************** END OF PSEUDO-CODE    **********************
  1482.  
  1483. GetRegC PROC    NEAR
  1484.  
  1485.         pushf                           ; save interrupt flag
  1486.         MOV     ax,0C0ch                ; register C
  1487.         cli                             ; disable interrupts
  1488.         cmos_read_clock                 ; Get register value
  1489.         xchg    AH,AL                   ; Save this value
  1490.         cmos_read_clock                 ; Get register c value One more time
  1491.         push    cs
  1492.         call    cmos_popf               ; restore interrupt flag
  1493.  
  1494.         OR      AL,AH                   ; combine 2 register C values
  1495.         RET
  1496.  
  1497. GetRegC ENDP
  1498.  
  1499.  
  1500. ;***LP  PTInt - Timer 0 Interrupt Handler
  1501. ;
  1502. ;       ENTRY
  1503. ;           ds -> ClkData (interrupt manager guarantee this)
  1504. ;
  1505. ;       EXIT
  1506. ;           carry flag clear
  1507. ;
  1508. ;       USES
  1509. ;           All
  1510. ;
  1511. ;       CONTEXT
  1512. ;           Interrupt
  1513. ;
  1514. ;       PSEUDOCODE
  1515. ;           issue EOI to PIC;
  1516.  
  1517.         ASSUME  CS:ClkCode,DS:ClkData,ES:NOTHING,SS:NOTHING
  1518. Procedure PTInt,far
  1519.  
  1520. ;TMR -- Update Rollover count and Tmr value.
  1521.         SaveReg <ds>
  1522.         mov     dx,WORD PTR pqwTmr
  1523.         lds     bx,pqwTmrRollover
  1524.         ASSUME DS:NOTHING
  1525. .386p
  1526.         mov     eax,DWORD PTR [bx].qw_ulLo
  1527.         mov     ecx,eax                         ; (ECX) = current rollover count.
  1528.         mov     eax,DWORD PTR [bx].qw_ulHi      ; Set Current rollover count =
  1529.         mov     DWORD PTR [bx].qw_ulLo,eax      ; Next rollover count.
  1530.  
  1531.         mov     bx,dx                           ; (DS:BX) -> qwTmr.
  1532.                                                 ; ASSUMES SAME DS.
  1533.         add     DWORD PTR [bx].qw_ulLo,ecx
  1534.         adc     DWORD PTR [bx].qw_ulHi,0
  1535.         jo      short pti40
  1536. CPUMode Reset
  1537.  
  1538. pti20:  RestoreReg <ds>
  1539.         ASSUME DS:ClkData
  1540.  
  1541.         test    fsPTFlags,PTF_OWNT0     ; ptimer owns T0?
  1542.         jnz     pti30                   ; YES, skip the rest
  1543.  
  1544.         test    fsPTFlags,PTF_TICKENABLE; vtimer enables tick?
  1545.         jz      pti30                   ; NO, skip the rest
  1546.  
  1547.         push    WORD PTR 0
  1548.         push    VTDCMD_TICKEVENT        ; (TOS+8)=VTDCMD_TICKEVENT
  1549.  
  1550.         push    WORD PTR 0
  1551.         push    WORD PTR 0              ; (TOS+4)=f16p1=0
  1552.  
  1553.         push    WORD PTR 0
  1554.         push    WORD PTR 0              ; (TOS)=f16p2=0
  1555.  
  1556.  
  1557.         .386p
  1558.         call    FWORD PTR fpfnVTProc
  1559.         CPUMode Reset
  1560.  
  1561. pti30: ; mov     al,TIMERIRQ             ; issue EOI to PIC      remove 76711
  1562.        ; mov     dl,DevHlp_EOI                                   remove 76711
  1563.        ; call    [DevHlp]                                        remove 76711
  1564.         DevEOI   <TIMERIRQ>,DevHlp      ;                        add    76711
  1565.  
  1566.         clc
  1567.         ret
  1568.  
  1569. .386p
  1570. pti40:  mov     DWORD PTR [bx].qw_ulLo,QW_MAX_LO
  1571.         mov     DWORD PTR [bx].qw_ulHi,QW_MAX_HI
  1572.         jmp     short pti20
  1573. CPUMode Reset
  1574.  
  1575. EndProc PTInt
  1576.  
  1577. ;***LP  RTINIT2 - 2nd stage init
  1578. ;
  1579. ;      FUNCTION
  1580. ;          Because RIPL with ETHERNET/PC-NET uses IRQ0, we must delay
  1581. ;          hooking IRQ0 until after loadable device drivers/IFS are
  1582. ;          installed.  The kernel will call with command 0 (loadable
  1583. ;          device driver init) when it's OK to hook IRQ 0, and this
  1584. ;          routine will be invoked.
  1585. ;
  1586. ;      ENTRY
  1587. ;          ds -> ClkData
  1588. ;
  1589. ;      EXIT
  1590. ;          carry flag clear
  1591. ;
  1592. ;      USES
  1593. ;          All
  1594. ;
  1595. ;      CONTEXT
  1596. ;          Interrupt
  1597. ;
  1598. ;      PSEUDOCODE
  1599. ;          clear IRQ 0 latch;
  1600. ;          issue EOI to PIC;
  1601.     ASSUME      CS:ClkSwap,DS:ClkData,ES:NOTHING,SS:NOTHING
  1602. RTINIT2 PROC FAR
  1603.         public  RTINIT2
  1604.  
  1605.         SaveReg <bx,cx,dx,di>
  1606.  
  1607. ;Initialize physical timer 0 to proper mode running at the rate of 18.2Hz
  1608.  
  1609.         mov     al,SC_CNT0+RW_LSBMSB+CM_MODE2
  1610.         out     PORT_CW,al              ;program count mode of timer 0
  1611.         IODelay
  1612.         xor     al,al
  1613.         out     PORT_CNT0,al            ; program timer 0 count
  1614.         IODelay                         ; count == 0h actually means 10000h
  1615.         out     PORT_CNT0,al            ; => approx. 18.2Hz
  1616.         IODelay
  1617.  
  1618. ;DevHlp(DevHlp_SetIRQ,TIMERIRQ,pfnPTInt,NO_INTSHARING)
  1619.  
  1620.         mov     ax,offset ClkCode:PTInt ; (ax)=interrupt handler offset
  1621.         mov     bx,TIMERIRQ             ; (bx)=IRQ number
  1622.         mov     dx,DevHlp_SetIRQ        ; (dh)=No interrupt sharing
  1623.         call    [DevHlp]                ; hook timer 0 interrupt
  1624.  
  1625.         RestoreReg <di,dx,cx,bx>
  1626.         mov     AX,STDON                ; Done
  1627.         ret
  1628.  
  1629. RTINIT2  ENDP
  1630.  
  1631. ClkSwap SEGMENT
  1632.  
  1633. public  endswapcode
  1634. endswapcode     label   byte
  1635.  
  1636. ClkSwap ENDS
  1637.  
  1638.  
  1639. BREAK <Realtime Clock Initialization Routine>
  1640. ;********************** START OF SPECIFICATIONS *********************
  1641. ;*
  1642. ;* MODULE NAME:  RTINIT
  1643. ;*
  1644. ;* DESCRIPTIVE NAME:  RTC Initialization Function 0
  1645. ;*
  1646. ;* FUNCTION:    Initializes data structures and the Realtime Clock
  1647. ;*              device.
  1648. ;*
  1649. ;* ENTRY POINT: RTINIT
  1650. ;*    LINKAGE:  FAR from RTENTR
  1651. ;*
  1652. ;* NOTES:  Interrupt flag is modified
  1653. ;*
  1654. ;* USES:  AX, DX.  Preserves others.
  1655. ;*
  1656. ;* INPUT: (PARAMETERS)
  1657. ;*      ES:BX = pointer to Request Packet
  1658. ;*
  1659. ;* OUTPUT: (RETURNED)
  1660. ;*      No interrupt flag modified
  1661. ;*      Request Packet filled in.
  1662. ;*
  1663. ;* EXIT-NORMAL:
  1664. ;*      AX = Status flag is "DONE"
  1665. ;*
  1666. ;* EXIT-ERROR:
  1667. ;*
  1668. ;* INTERNAL REFERENCES:
  1669. ;*    STRUCTURES:  none
  1670. ;*    ROUTINES:   FIXISEG
  1671. ;*
  1672. ;* EXTERNAL REFERENCES:
  1673. ;*    STRUCTURES:  Global Information Segment (SysInfoSeg)
  1674. ;*    ROUTINES:  DevHlp_GetDOSVar, DevHlp_SetIRQ
  1675. ;*
  1676. ;*********************** END OF SPECIFICATIONS **********************
  1677. page
  1678. ;********************** START OF PSEUDO-CODE   **********************
  1679. ;
  1680. ; set the pointer, DevHlp, to the DevHelp Router
  1681. ; call DevHlp to get the address of SysInfoSeg in ES:BX
  1682. ; if DevHlp failed
  1683. ;    goto abort
  1684. ; save the address of SysInfoSeg in InfSeg
  1685. ;
  1686. ; get pointer to DevConfigTbl from SystemAnchorSegment(SAS)
  1687. ; get the TYPE byte from DevConfigTbl
  1688. ; based on TYPE byte, store correct CMOS location in
  1689. ;
  1690. ; call DevHlp to get the address of SchedClock in ES:BX
  1691. ; save the address of SchedClock
  1692. ; initialize register B to periodic interrupt enable,
  1693. ;            update-endedinterrupt enable, and 24 hour mode
  1694. ; initialize register A to 32.768kHz square wave output, and
  1695. ;            31.25 millisecond periodic interrupt rate
  1696. ; read register C to initialize it
  1697. ; read register D to initialize it
  1698. ; call DevHlp to set non-sharing IRQ 8 to routine RTCINT
  1699. ; if DevHlp failed
  1700. ;    goto abort
  1701. ;
  1702. ; call GetRegC to read register C
  1703. ; if zero flag not set
  1704. ;    goto abort
  1705. ; set up end of code & data segment for SysInit
  1706. ;
  1707. ; call FIXISEG to initialize SysInfoSeg from the data on the clock
  1708. ; if carry flag set
  1709. ;    goto abort
  1710. ;
  1711. ; call DevHlp to set int 1A to point to routine int1Artn
  1712. ; if DevHlp failed
  1713. ;    goto abort
  1714. ; save old int 1A vector in Int1AChain
  1715. ;
  1716. ; call DevHlp to set int 15 to point to routine int15rtn
  1717. ; if DevHlp failed
  1718. ;    goto abort
  1719. ; save old int 15 vector in Int15Chain
  1720. ; set return code = DONE
  1721. ; return
  1722. ;
  1723. ; abort:
  1724. ;       set return code = DONE+ERROR+GENERAL FAIL
  1725. ;       set end of code & data segment to zero
  1726. ;       return
  1727. ;
  1728. ;*********************** END OF PSEUDO-CODE    **********************
  1729.  
  1730.     ASSUME      CS:ClkCode,DS:ClkData,ES:NOTHING,SS:NOTHING
  1731. RTINIT  PROC FAR
  1732.         public  RTINIT
  1733.  
  1734.         MOV     AX,WORD PTR ES:[BX].InitDevHlp   ;  Save away the pointer to
  1735.         MOV     WORD PTR DevHlp,AX               ;  the DevHelp Router.
  1736.         MOV     AX,WORD PTR ES:[BX].InitDevHlp+2
  1737.         MOV     WORD PTR DevHlp+2,AX
  1738.  
  1739. ;%    Temporary      to fix loader problem for which the data selector
  1740. ;%    does not get fixed up.
  1741.  
  1742. ES_DI   EQU     1                       ;equate for PhysToVirt DevHlp service
  1743.  
  1744.         extrn   selClkData:word
  1745.  
  1746.         SaveReg <bx,es>
  1747.         SaveReg <ds>
  1748.         push    ds
  1749.         pop     es
  1750.         ASSUME  es:ClkData
  1751.         push    cs
  1752.         pop     ds
  1753.         ASSUME  ds:NOTHING
  1754.  
  1755.         mov     si,offset selClkData    ;(ds:si)->selClkData
  1756.         mov     dl,DevHlp_VirtToPhys
  1757.         call    es:[DevHlp]             ;(ax:bx)=physical address of selClkData
  1758.  
  1759.         mov     cx,2                    ;length of word we need to write into
  1760.         mov     dx,DevHlp_PhysToVirt + ES_DI*100h
  1761.         call    es:[DevHlp]             ;(es:di)->selClkData (writeable!)
  1762.  
  1763.         ASSUME  es:NOTHING
  1764.         RestoreReg <ds>
  1765.         ASSUME  ds:ClkData
  1766.         mov     es:[di],ds              ;save ds into code segment
  1767.  
  1768.         RestoreReg <es,bx>
  1769.         ASSUME  es:NOTHING
  1770.  
  1771.         mov     dl,DevHlp_UnPhysToVirt  ;free selector
  1772.         call    [DevHlp]
  1773.  
  1774. ;% END hack
  1775.  
  1776.         PUSH    ES
  1777.         PUSH    BX
  1778.  
  1779.         MOV     AL,1                     ; Variable number of SysInfoSeg
  1780.         MOV     DL,DevHlp_GetDOSVar
  1781.         CALL    [DevHlp]
  1782.         jnc     rtok
  1783.         jmp     rtabort2                ; if carry flag set, abort clock
  1784.  
  1785. rtok:   MOV     ES,AX                    ; ES:BX -> SysInfoSeg
  1786.         MOV     AX,ES:[BX]               ; AX = InfoSeg segment selector
  1787.         MOV     WORD PTR InfSeg+2,AX     ; Stuff for later
  1788.  
  1789.         mov     ax,SAS_selector          ; AX = SAS selector = DevConfigTbl Sel
  1790.         mov     es,ax                    ; ES = DevConfigTbl selector
  1791.         mov     bx,es:[SAS_config_data]  ; BX = @ of DevConfigTbl off.
  1792.         mov     bx,es:[bx]               ; BX = DevConfigTbl off.
  1793.  
  1794. ;;;     Decide wether to use the AT or the PS/2 CMOS address.
  1795. ;;;     Refer to DCR 1085.
  1796.         mov     al,es:[bx].Config_century_location ;AL = century byte location  64012
  1797.  
  1798.         mov    CMOS_location,al                  ; store CMOS LOCATION in var.
  1799.  
  1800.         MOV     DL,DevHlp_SchedClock     ; ***** FOR DCR 487 *****
  1801.         CALL    [DevHlp]                 ; ES:BX = address of SchedClock pointer
  1802.         MOV     WORD PTR SchedClock,BX   ; Save away the SchedClock ptr address
  1803.         MOV     WORD PTR SchedClock+2,ES ; **
  1804.  
  1805.                                         ; (begin)
  1806.         pushf                           ; save interrupt flag
  1807.         CLI                             ; DISABLE INTERRUPTS FOR CMOS ACCESS
  1808.  
  1809. ; Set hundredths and milliseconds increments to initial values, based on
  1810. ; initial periodic interrupt frequency of 32hz.
  1811.  
  1812.         mov     Hundred_Inc,((100 SHL 8) / RTCHZINIT)
  1813.         mov     MS_Inc_Whole,(1000 / RTCHZINIT)
  1814.         mov     MS_Inc_Frac,(((1000 SHL 16) / RTCHZINIT) AND 0FFFFh)
  1815.  
  1816.         MOV     AX,(RBINIT SHL 8) OR 0BH        ; Register B
  1817.         cmos_write_clock                ; Initialize it
  1818.         MOV     AX,(RABYTE SHL 8) OR 0AH        ; Register A
  1819.         cmos_write_clock                ; Initialize it
  1820.         MOV     AL,0CH                  ; Register C
  1821.         cmos_read_clock                 ; Read to initialize
  1822.         MOV     AL,0DH                  ; Register D
  1823.         cmos_read_clock                 ; Read to initialize
  1824.         cmos_reset                      ; cmos RAM=>read port w/NMI enable
  1825.         push    cs
  1826.         call    cmos_popf               ; restore interrupt flag
  1827.  
  1828.         MOV     BX,RTCIRQ               ; IRQ Number
  1829.         XOR     DH,DH                   ; Not Sharing this IRQ !
  1830.         MOV     AX,OFFSET ClkCode:RTCINT ; Get the handler address
  1831.         MOV     DL,DevHlp_SetIRQ        ; Dev_Hlp Set interrupt function
  1832.         CALL    [DevHlp]                ; Install interrupt handler
  1833.         JNC     Get_RegC                ; if carry flag set, abort clock
  1834.  
  1835. rtabort2:
  1836.         POP     BX
  1837.         POP     ES
  1838. rtabort3:
  1839.         MOV     AX,(STDON+STERR+ERRGFAIL) ; set error code for general failure
  1840.         MOV     es:[bx].InitEcode,0     ; Set up end of Code Segment
  1841.         MOV     es:[bx].InitEdata,0     ; Set up end of Data Segment
  1842.         RET
  1843.  
  1844. Get_RegC:
  1845. ;       Clear out any pending interrupts.  This is here for a     fix.
  1846. ;       After the clock is initialized and BEFORE the 8259 slave is told
  1847. ;       about this IRQ line, the clock can interrupt.  If this happens
  1848. ;       the clock will be waiting for this interrupt to be serviced and
  1849. ;       no one will know about it.  Thus hanging the system since things
  1850. ;       like the floppy driver depend on it.
  1851.         CALL    GetRegC                 ; Get register C (interrupt flags)
  1852.  
  1853.         POP     BX
  1854.         POP     ES
  1855.         MOV     AX,offset RTINIT        ;  Set up end of Code Segment
  1856.         MOV     es:[bx].InitEcode,AX
  1857.         MOV     AX,offset ATDataEnd     ;  Set up end of Data Segment
  1858.         MOV     es:[bx].InitEdata,AX
  1859.                                         ; (end)
  1860.  
  1861.         push    cx
  1862.         mov     cx,200                  ; read 200 times clock
  1863.         CALL    FIXISEG                 ; Initialize InfoSeg from Clock
  1864.         pop     cx
  1865.         JC      rtabort3                ; fixiseg could have failed
  1866.  
  1867. ;% Clk/Tmr registration and Initialization
  1868.  
  1869. ClkInit label near
  1870.         public ClkInit
  1871.  
  1872. ;DevHlp(DevHlp_RegisterPDD,pszClkName,fpfnClkVDDProc)
  1873.         SaveReg <es,bx>
  1874.         mov     dl,DevHlp_RegisterPDD   ;(dl)=DevHlp_RegisterPDD
  1875.         mov     si,offset ClkData:szClkName;(ds:si)->szClkName
  1876.         mov     di,cs
  1877.         mov     es,di
  1878.         mov     di,offset ClkCode:ClkVDDProc;(es:di)->ClkVDDProc
  1879.         call    [DevHlp]                ;(ax)=error code
  1880.         RestoreReg <bx,es>
  1881.         or      ax,ax                   ;error?
  1882.         jnz     rtabort3                ;YES, goto error exit
  1883.  
  1884. ;DevHlp(DevHlp_RegisterPDD,pszTmrName,fpfnPTmrProc)
  1885.         SaveReg <es,bx>
  1886.         mov     dl,DevHlp_RegisterPDD   ;(dl)=DevHlp_RegisterPDD
  1887.         mov     si,offset ClkData:szTmrName;(ds:si)->szTmrName
  1888.         mov     di,cs
  1889.         mov     es,di
  1890.         mov     di,offset ClkCode:PTVDMProc;(es:di)->PTVDMProc
  1891.         call    [DevHlp]                ;(ax)=error code
  1892.         RestoreReg <bx,es>
  1893.         or      ax,ax                   ;error?
  1894.         jnz     rtabort3                ;YES, goto error exit
  1895.  
  1896. ;DevHlp(DevHlp_RegisterBeep,fpfnPTBeep)
  1897.         mov     dl,DevHlp_RegisterBeep  ;(dl)=DevHlp_RegisterBeep
  1898.         mov     cx,cs
  1899.         mov     di,offset ClkCode:PTBeep;(cx:di)->PTBeep
  1900.         call    [DevHlp]
  1901.         or      ax,ax                   ;error?
  1902.         jnz     rtabort3                ;YES, goto error exit
  1903.  
  1904.         call    PTBeepOff               ;disable speaker
  1905.  
  1906. ;DevHlp(DevHlp_RegisterFreq,fpfnCLKFreq)
  1907.         mov     dl,DevHlp_RegisterFreq  ;(dl)=DevHlp_RegisterFreq
  1908.         mov     cx,cs
  1909.         mov     di,offset ClkCode:CLKFreq;(cx:di)->CLKFreq
  1910.         call    [DevHlp]
  1911.         or      ax,ax                   ;error?
  1912.         jnz     rtabort3                ;YES, goto error exit
  1913.  
  1914.  
  1915. ;Initialize TMR device (PIT Ctr 0).
  1916.  
  1917.         SaveReg <bx,cx,dx,di>
  1918.  
  1919.         mov     cx,cs
  1920.         mov     di,offset ClkCode:PTTimer0;(cx:di)->PTTimer0
  1921.         mov     dl,DevHlp_RegisterTmrDD
  1922.         call    [DevHlp]
  1923.  
  1924. ;       di:bx -> qwTmrRollover (16:16 ptr).
  1925. ;       di:cx -> qwTmr (16:16 ptr).
  1926.  
  1927.         or      di,di                   ;Valid Selector?
  1928.         jnz     cinit2                  ; Yes.
  1929.  
  1930. cinit1: mov     di,ds                   ;Invalid, so store valid ptr.
  1931.         mov     bx,offset qwTmrRollover
  1932.         mov     cx,offset qwTmr
  1933. cinit2:                                 ;Store (16:16) pointers.
  1934.         mov     WORD PTR pqwTmrRollover,bx      ;Store offset.
  1935.         mov     WORD PTR pqwTmrRollover+2,di    ;Store segment.
  1936.         mov     WORD PTR pqwTmr,cx      ;Store offset.
  1937.         mov     WORD PTR pqwTmr+2,di    ;Store segment.
  1938.  
  1939. ;Initialize current rollover count = next rollover count = 10000h.
  1940.  
  1941.         SaveReg <ds>
  1942.         mov     ds,di
  1943. .386p
  1944.         mov     DWORD PTR [bx].qw_ulLo,10000h
  1945.         mov     DWORD PTR [bx].qw_ulHi,10000h
  1946. CPUMode Reset
  1947.         RestoreReg <ds>
  1948.  
  1949.         RestoreReg <di,dx,cx,bx>
  1950.  
  1951.         mov     AX,STDON                ; Done
  1952.         ret
  1953.  
  1954. RTINIT  ENDP
  1955.  
  1956. ClkCode ENDS
  1957.  
  1958.         END
  1959.