summaryrefslogblamecommitdiffstats
path: root/private/ntos/nthals/halx86/i386/ixcmos.asm
blob: 5cc391076eca0275b8be187342309ada01cc28fd (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135














































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































                                                                                
        title  "Cmos Access Routines"
;++
;
; Module Name:
;
;    ixcmos.asm
;
; Abstract:
;
;    Procedures necessary to access CMOS/ECMOS information.
;
; Author:
;
;    David Risner (o-ncrdr) 20 Apr 1992
;
; Revision History:
;
;    Landy Wang (corollary!landy) 04 Dec 1992
;    - Move much code from ixclock.asm to here so different HALs
;      can reuse the common functionality.
;
;--

.386p
        .xlist
include hal386.inc
include callconv.inc                    ; calling convention macros
include mac386.inc
include i386\ix8259.inc
include i386\ixcmos.inc
        .list

        EXTRNP  _DbgBreakPoint,0,IMPORT
        extrn   _HalpSystemHardwareLock:DWORD
        extrn   _HalpBusType:DWORD
        extrn   _HalpSerialLen:BYTE
        extrn   _HalpSerialNumber:BYTE


CMOS_STATUS_BUSY        EQU     80H     ; Time update in progress
RTC_OFFSET_SECOND       EQU     0       ; second field of RTC memory
RTC_OFFSET_MINUTE       EQU     2       ; minute field of RTC memory
RTC_OFFSET_HOUR         EQU     4       ; hour field of RTC memory
RTC_OFFSET_DAY_OF_WEEK  EQU     6       ; day-of-week field of RTC memory
RTC_OFFSET_DATE_OF_MONTH EQU    7       ; date-of-month field of RTC memory
RTC_OFFSET_MONTH        EQU     8       ; month field of RTC memory
RTC_OFFSET_YEAR         EQU     9       ; year field of RTC memory
RTC_OFFSET_CENTURY_MCA  EQU     37h     ; Century field of RTC memory for MCA
RTC_OFFSET_CENTURY      EQU     32h     ; Century field of RTC memory
RTC_OFFSET_CENTURY_DS   EQU    148h     ; Bank 1, 48. Century field for DS
BANK1                   EQU    100h

;
; BCD_TO_BIN
;
; Description: Convert BCD value to binary
;
; Parameter:
;     Input: (AL) = 2 digit BCD number to convert
;     Output: (AX) = Binary equivalent (all in AL)
;
; Return: None.
;

BCD_TO_BIN      macro

        xor     ah,ah
        rol     ax,4
        ror     al,4
        aad
endm

;
; BIN_TO_BCD
;
; Description: Convert binary value to BCD.
;
; Parameter:
;     Input: (AL) = binary value to be converted.
;     Output: (AX) = BCD (all in AL)
;
; Return: None.
;

BIN_TO_BCD      macro

        aam
        rol     al, 4
        ror     ax, 4
endm


_DATA   SEGMENT  DWORD PUBLIC 'DATA'

;
; HalpRebootNow is a reboot vector.  Set in an MP system, to
; cause any processors which may be looping in HalpAcquireCmosSinLock
; to transfer control to the vector in HalpRebootNow
;

    public  _HalpRebootNow
_HalpRebootNow           dd      0

;
; Holds the value of the eflags register before a cmos spinlock is
; acquired (used in HalpAcquire/ReleaseCmosSpinLock().
;
_HalpHardwareLockFlags   dd      0

;
; Holds the offset to CMOS Century information.
;

_HalpCmosCenturyOffset   dd      0

_DATA   ends

        subttl  "HalpGetCmosData"

;++
;
; CMOS space read and write functions.
;
;--

CmosAddressPort         equ     70H
CmosDataPort            equ     71H

ECmosAddressLsbPort     equ     74H
ECmosAddressMsbPort     equ     75H
ECmosDataPort           equ     76H


INIT    SEGMENT DWORD PUBLIC 'CODE'
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

;++
;
;   VOID
;   HalpInitializeCmos(
;       VOID
;       )
;
;   This routine reads CMOS and initializes globals required for
;   CMOS access, such as the location of the century byte.
;
;--

cPublicProc _HalpInitializeCmos,0

        push    ebx
        push    esi
        push    edi

;
; Assume default
;

        mov     eax, RTC_OFFSET_CENTURY
        mov     _HalpCmosCenturyOffset, eax

        cmp     _HalpBusType, MACHINE_TYPE_ISA
        jne     short icm40


;
; If control comes here, this is ISA machine.  We need to check if this is
; IBM PS/1 or Pc/ValuePoint machine and use RTC_CENTURY_OFFSET_MCA to get
; Century byte from CMOS.
;

;
; Check if the CMOS 2e and 2f contains memory checksum.  On PS/1 machine
; the check should fail.
;

icm20:  mov     ecx, 2dh                ; from 10h to 2dh
        mov     eax, 0                  ; clear ax
        mov     edx, 0

icm30:  mov     al, cl
        CMOS_READ
        add     edx, eax
        dec     ecx
        cmp     ecx, 0fh
        jne     short icm30

        mov     eax, 2eh
        CMOS_READ
        mov     ah, al
        mov     al, 2fh
        CMOS_READ
        cmp     eax, edx
        je      short icm50             ; NOT PS/1

        mov     eax, RTC_OFFSET_CENTURY_MCA
        mov     _HalpCmosCenturyOffset, eax
        jmp     icm90

icm40:  cmp     _HalpBusType, MACHINE_TYPE_MCA
        jne     short icm50

;
; See if this is a P700 MCA machine
;

        in      al, 07fh                        ; get PD700 ID byte
        and     al, 0F0h                        ; Mask high nibble
        cmp     al, 0A0h                        ; Is the ID Ax?
        jz      short icm50
        cmp     al, 090h                        ; Or an 9X?
        jz      short icm50                     ; Yes, it's a 700

        mov     eax, RTC_OFFSET_CENTURY_MCA
        mov     _HalpCmosCenturyOffset, eax

icm50:

if 0

    - Selecting BANK1 causes some devices to mess up their month value
    - For now, I'm removing this code until this problem can be solved

;
; See if this is a Dallas Semiconductor DS17285 or later
; Switch to BANK 1
;
        mov     al, 0Ah
        CMOS_READ

        and     al, 7fh                         ; Don't write UIP
        mov     ah, al
        mov     esi, eax                        ; save it for restore
        or      ah, 10h                         ; Set DV0 = 1

        mov     al, 0Ah                         ; Write register A
        CMOS_WRITE

;
; Check for RTC serial # with matching crc
; (al)  = current byte
; (ah)  = scratch register
; (bl)  = current crc
; (bh)  = zero, non-zero, flag
; (ecx) = cmos offset
; (edx) = used by cmos_read macro
; (esi) = saved register 0A
;
        mov     ecx, 40h
        xor     ebx, ebx

icm60:  mov     al, cl
        CMOS_READ
        mov     byte ptr _HalpSerialNumber+2+-40h[ecx], al

        or      bh, al                  ; or to check for all zeros

        mov     ch, 8                   ; Bits per byte

icm65:  mov     ah, bl                  ; ah = crc
        xor     ah, al                  ; xor LSb
        shr     bl, 1                   ; shift crc
        shr     ah, 1                   ; mov LSb to carry
        sbb     ah, ah                  ; if carry set 1's else 0's
        and     ah, (118h shr 1)        ; crc polynomial
        xor     bl, ah                  ; apply it

        shr     al, 1                   ; next bit
        dec     ch                      ;
        jnz     short icm65             ; if ch non-zero, loop

        inc     cl                      ; next cmos location
        cmp     cl, 48h                 ; at end?
        jne     short icm60             ; no, loop
;
; (bh) = zero, non-zero flag
; (bl) = crc
;

        mov     eax, RTC_OFFSET_CENTURY_DS      ; Read century byte
        CMOS_READ

        BCD_TO_BIN
        movzx   ecx, ax                         ; save it

;
; Switch back to BANK 0
;

        mov     eax, esi
        mov     al, 0Ah
        CMOS_WRITE

;
; Check for valid DS data
;
        cmp     bh, 0                           ; Was data all zeros?
        je      short icm90

        cmp     bl, 0                           ; was CRC valid?
        jnz     short icm90

        cmp     ecx, 19                         ; Is century before 19?
        jb      short icm90

        cmp     ecx, 20                         ; Is century after 20?
        ja      short icm90

;
; Setup for DS century byte
;
        mov     byte ptr _HalpSerialNumber+0, 'D'
        mov     byte ptr _HalpSerialNumber+1, 'S'
        mov     _HalpSerialLen, 10

        mov     eax, RTC_OFFSET_CENTURY_DS
        mov     _HalpCmosCenturyOffset, eax
endif

icm90:  pop     edi
        pop     esi
        pop     ebx
        stdRET  _HalpInitializeCmos

stdENDP _HalpInitializeCmos


INIT   ends

_TEXT   SEGMENT DWORD PUBLIC 'CODE'
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING


;++
;
;   ULONG
;   HalpGetCmosData(
;       IN ULONG    SourceLocation
;       IN ULONG    SourceAddress
;       IN ULONG    ReturnBuffer
;       IN PUCHAR   ByteCount
;       )
;
;   This routine reads the requested number of bytes from CMOS/ECMOS and
;   stores the data read into the supplied buffer in system memory.  If
;   the requested data amount exceeds the allowable extent of the source
;   location, the return data is truncated.
;
;   Arguments:
;
;       SourceLocation  : where data is to be read from CMOS or ECMOS
;                           0 - CMOS, 1 - ECMOS
;
;       SourceAddress   : address in CMOS/ECMOS where data is to be read from
;
;       ReturnBuffer    : address in system memory for return data
;
;       ByteCount       : number of bytes to be read
;
;   Returns:
;
;       Number of byte actually read.
;
;--

SourceLocation  equ     2*4[ebp]
SourceAddress   equ     3*4[ebp]
ReturnBuffer    equ     4*4[ebp]
ByteCount       equ     5*4[ebp]

cPublicProc _HalpGetCmosData    ,4

        push    ebp
        mov     ebp, esp
        push    ebx
        push    edi

    ;
    ; NOTE: The spinlock is needed even in the UP case, because
    ;    the resource is also used in an interrupt handler (profiler).
    ;    If we own the spinlock in this routine, and we service
    ;    the profiler interrupt (which will wait for the spinlock forever),
    ;    then we have a hosed system.
    ;
        stdCall _HalpAcquireCmosSpinLock

        xor     edx, edx                ; initialize return data length
        mov     ecx, ByteCount

        or      ecx, ecx                ; validate requested byte count
        jz      HalpGetCmosDataExit     ; if no work to do, exit

        mov     edx, SourceAddress
        mov     edi, ReturnBuffer

        mov     eax, SourceLocation     ; cmos or extended cmos?
        cmp     eax, 1
        je      ECmosReadByte
        cmp     eax, 0
        jne     HalpGetCmosDataExit

CmosReadByte:
        cmp     edx, 0ffH               ; validate cmos source address
        ja      HalpGetCmosDataExit     ; if out of range, exit
        mov     al, dl
        out     CmosAddressPort, al
        in      al, CmosDataPort
        mov     [edi], al
        inc     edx
        inc     edi
        dec     ecx
        jnz     CmosReadByte
        jmp     SHORT HalpGetCmosDataExit

ECmosReadByte:
        cmp     edx,0ffffH              ; validate ecmos source address
        ja      HalpGetCmosDataExit     ; if out of range, exit
        mov     al, dl
        out     ECmosAddressLsbPort, al
        mov     al, dh
        out     ECmosAddressMsbPort, al
        in      al, ECmosDataPort
        mov     [edi], al
        inc     edx
        inc     edi
        dec     ecx
        jnz     ECmosReadByte

HalpGetCmosDataExit:
        stdCall _HalpReleaseCmosSpinLock

        mov     eax, edx                ; return bytes read

        pop     edi
        pop     ebx
        pop     ebp

        stdRET    _HalpGetCmosData

stdENDP _HalpGetCmosData


;++
;
;   VOID
;   HalpSetCmosData(
;       IN ULONG    SourceLocation
;       IN ULONG    SourceAddress
;       IN ULONG    ReturnBuffer
;       IN PUCHAR   ByteCount
;       )
;
;   This routine writes the requested number of bytes to CMOS/ECMOS
;
;   Arguments:
;
;       SourceLocation  : where data is to be written to CMOS or ECMOS
;                           0 - CMOS, 1 - ECMOS
;
;       SourceAddress   : address in CMOS/ECMOS where data is to write to.
;
;       ReturnBuffer    : address in system memory for data to write
;
;       ByteCount       : number of bytes to be write
;
;   Returns:
;
;       Number of byte actually written.
;
;--

cPublicProc _HalpSetCmosData    ,4

        push    ebp
        mov     ebp, esp
        push    ebx
        push    edi

        stdCall _HalpAcquireCmosSpinLock

        xor     edx, edx                ; initialize return data length
        mov     ecx, ByteCount

        or      ecx, ecx                ; validate requested byte count
        jz      HalpSetCmosDataExit     ; if no work to do, exit

        mov     edx, SourceAddress
        mov     edi, ReturnBuffer

        mov     eax, SourceLocation     ; cmos or extended cmos?
        cmp     eax, 1
        je      ECmosWriteByte
        cmp     eax, 0
        jne     HalpSetCmosDataExit

CmosWriteByte:
        cmp     edx, 0ffH               ; validate cmos source address
        ja      HalpSetCmosDataExit     ; if out of range, exit
        mov     al, dl
        out     CmosAddressPort, al
        mov     al, [edi]
        out     CmosDataPort, al
        inc     edx
        inc     edi
        dec     ecx
        jnz     CmosWriteByte
        jmp     SHORT HalpSetCmosDataExit

ECmosWriteByte:
        cmp     edx,0ffffH              ; validate ecmos source address
        ja      HalpSetCmosDataExit     ; if out of range, exit
        mov     al, dl
        out     ECmosAddressLsbPort, al
        mov     al, dh
        out     ECmosAddressMsbPort, al
        mov     al, [edi]
        out     ECmosDataPort, al
        inc     edx
        inc     edi
        dec     ecx
        jnz     ECmosWriteByte

HalpSetCmosDataExit:
        stdCall _HalpReleaseCmosSpinLock

        mov     eax, edx                ; return bytes written
        pop     edi
        pop     ebx
        pop     ebp

        stdRET    _HalpSetCmosData

stdENDP _HalpSetCmosData


        page ,132
        subttl  "Read System Time"
;++
;
; VOID
; HalpReadCmosTime (
;    PTIME_FIELDS TimeFields
;    )
;
; Routine Description:
;
;    This routine reads current time from CMOS memory and stores it
;    in the TIME_FIELDS structure passed in by caller.
;
; Arguments:
;
;    TimeFields - A pointer to the TIME_FIELDS structure.
;
; Return Value:
;
;    None.
;
;--

;
; Parameters:
;

KrctPTimeFields equ [esp+4]

cPublicProc _HalpReadCmosTime ,1

if DBG
krctwait0:
        mov     ecx, 100
krctwait:
        push    ecx
else
krctwait:
endif
        stdCall   _HalpAcquireCmosSpinLock
        mov     ecx, 100
        align   4
krct00: mov     al, 0Ah                 ; Specify register A
        CMOS_READ                       ; (al) = CMOS register A
        test    al, CMOS_STATUS_BUSY    ; Is time update in progress?
        jz      short krct10            ; if z, no, go read CMOS time
        loop    short krct00            ; otherwise, try again.

;
; CMOS is still busy. Try again ...
;

        stdCall _HalpReleaseCmosSpinLock
if DBG
        pop     ecx
        loop    short krctwait
        stdCall _DbgBreakPoint
        jmp     short krctwait0
else
        jmp     short krctwait
endif
        align   4
if DBG
krct10:
        pop     ecx
else
krct10:
endif
        mov     edx, KrctPTimeFields    ; (edx)-> TIME_FIELDS structure
        xor     eax, eax                ; (eax) = 0

        mov     al, RTC_OFFSET_SECOND
        CMOS_READ                       ; (al) = second in BCD form
        BCD_TO_BIN                      ; (ax) = second
        mov     [edx].TfSecond, ax      ; set second in TIME_FIELDS

        mov     al, RTC_OFFSET_MINUTE
        CMOS_READ                       ; (al) = minute in BCD form
        BCD_TO_BIN                      ; (ax) = Minute
        mov     [edx].TfMinute, ax      ; set minute in TIME_FIELDS

        mov     al, RTC_OFFSET_HOUR
        CMOS_READ                       ; (al) = hour in BCD form
        BCD_TO_BIN                      ; (ax) = Hour
        mov     [edx].TfHour, ax        ; set hour in TIME_FIELDS

        mov     al, RTC_OFFSET_DAY_OF_WEEK
        CMOS_READ                       ; (al) = day-of-week in BCD form
        BCD_TO_BIN                      ; (ax) = day-of-week
        mov     [edx].TfWeekday, ax     ; set Weekday in TIME_FIELDS

        mov     al, RTC_OFFSET_DATE_OF_MONTH
        CMOS_READ                       ; (al) = date-of-month in BCD form
        BCD_TO_BIN                      ; (ax) = date_of_month
        mov     [edx].TfDay, ax         ; set day in TIME_FIELDS

        mov     al, RTC_OFFSET_MONTH
        CMOS_READ                       ; (al) = month in BCD form
        BCD_TO_BIN                      ; (ax) = month
        mov     [edx].TfMonth, ax       ; set month in TIME_FIELDS

        mov     al, RTC_OFFSET_YEAR
        CMOS_READ                       ; (al) = year in BCD form
        BCD_TO_BIN                      ; (ax) = year
        push    eax                     ; save year in stack

        push    edx                     ; preserve edx
        call    _HalpGetCmosCenturyByte ; (al)= century byte in BCD form
        BCD_TO_BIN                      ; (ax) = century
        pop     edx

        mov     ah, 100
        mul     ah                      ; (ax) = century * 100
        pop     ecx                     ; (cx) = year
        add     ax, cx                  ; (ax)= year

        cmp     ax, 1900                ; Is year > 1900
        jb      short krct40
        cmp     ax, 1920                ; and < 1920
        jae     short krct40
        add     ax, 100                 ; Compensate for century field

krct40:
        mov     [edx].TfYear, ax        ; set year in TIME_FIELDS

        mov     word ptr [edx].TfMilliseconds, 0 ; do not support

        stdCall   _HalpReleaseCmosSpinLock

        stdRET    _HalpReadCmosTime

stdENDP _HalpReadCmosTime

        page ,132
        subttl  "Write System Time"
;++
;
; VOID
; HalpWriteCmosTime (
;    PTIME_FIELDS TimeFields
;    )
;
; Routine Description:
;
;    This routine writes current time from TIME_FILEDS structure
;    to CMOS memory.
;
; Arguments:
;
;    TimeFields - A pointer to the TIME_FIELDS structure.
;
; Return Value:
;
;    None.
;
;--

;
; Parameters:
;

KrctPTimeFields equ [esp+4]

cPublicProc _HalpWriteCmosTime ,1

if DBG
kwctwait0:
        mov     ecx, 100
kwctwait:
        push    ecx
else
kwctwait:
endif
        stdCall   _HalpAcquireCmosSpinLock
        mov     ecx, 100
        align   4
kwct00: mov     al, 0Ah                 ; Specify register A
        CMOS_READ                       ; (al) = CMOS register A
        test    al, CMOS_STATUS_BUSY    ; Is time update in progress?
        jz      short kwct10            ; if z, no, go write CMOS time
        loop    short kwct00            ; otherwise, try again.

;
; CMOS is still busy. Try again ...
;

        stdCall _HalpReleaseCmosSpinLock
if DBG
        pop     ecx
        loop    short kwctwait
        stdCall _DbgBreakPoint
        jmp     short kwctwait0
else
        jmp     short kwctwait
endif
        align   4
if DBG
kwct10:
        pop     ecx
else
kwct10:
endif
        mov     edx, KrctPTimeFields    ; (edx)-> TIME_FIELDS structure

        mov     al, [edx].TfSecond      ; Read second in TIME_FIELDS
        BIN_TO_BCD
        mov     ah, al
        mov     al, RTC_OFFSET_SECOND
        CMOS_WRITE

        mov     al, [edx].TfMinute      ; Read minute in TIME_FIELDS
        BIN_TO_BCD
        mov     ah, al
        mov     al, RTC_OFFSET_MINUTE
        CMOS_WRITE

        mov     al, [edx].TfHour        ; Read Hour in TIME_FIELDS
        BIN_TO_BCD
        mov     ah, al
        mov     al, RTC_OFFSET_HOUR
        CMOS_WRITE

        mov     al, [edx].TfWeekDay     ; Read WeekDay in TIME_FIELDS
        BIN_TO_BCD
        mov     ah, al
        mov     al, RTC_OFFSET_DAY_OF_WEEK
        CMOS_WRITE

        mov     al, [edx].TfDay         ; Read day in TIME_FIELDS
        BIN_TO_BCD
        mov     ah, al
        mov     al, RTC_OFFSET_DATE_OF_MONTH
        CMOS_WRITE

        mov     al, [edx].TfMonth       ; Read month in TIME_FIELDS
        BIN_TO_BCD
        mov     ah, al
        mov     al, RTC_OFFSET_MONTH
        CMOS_WRITE

        mov     ax, [edx].TfYear        ; Read Year in TIME_FIELDS
        cmp     ax, 9999
        jbe     short kwct15
        mov     ax, 9999

        align   4
kwct15:
        mov     cl, 100
        div     cl                      ; [ax]/[cl]->al=quo, ah=rem
        push    eax
        BIN_TO_BCD

        push    eax
        call    _HalpSetCmosCenturyByte

        pop     eax
        mov     al, ah                  ; [al] = Year
        BIN_TO_BCD
        mov     ah, al                  ; [ah] = year in BCD
        mov     al, RTC_OFFSET_YEAR
        CMOS_WRITE

        stdCall   _HalpReleaseCmosSpinLock

        stdRET    _HalpWriteCmosTime

stdENDP _HalpWriteCmosTime


;++
;
; Routine Description:
;
;   Acquires a spinlock to access the cmos chip. The cmos chip is
;   accessed at different irql levels, so to be safe, we 'cli'.
;   We could replace that to raise irql to PROFILE_LEVEL, but that's
;   a lot of code.
;
; Arguments:
;
;    None
;
; Return Value:
;
;    Interrupt is disabled.
;    Irql level not affected.
;    Flags saved in _HalpHardwareLockFlags.
;--

cPublicProc _HalpAcquireCmosSpinLock  ,0
        push    eax

Arsl10: pushfd
        cli
        lea     eax, _HalpSystemHardwareLock
        ACQUIRE_SPINLOCK    eax, Arsl20
        pop     _HalpHardwareLockFlags          ; save flags for release S.L.
        pop     eax
        stdRET    _HalpAcquireCmosSpinLock

Arsl20: popfd

Arsl30:
ifndef NT_UP
        cmp     _HalpRebootNow, 0
        jnz     short Arsl50
endif
        TEST_SPINLOCK       eax, <short Arsl30>
        jmp     short ARsl10

Arsl50:
ifndef NT_UP
        mov     eax, _HalpRebootNow
        call    eax
        int 3                                 ; should not return
endif

stdENDP _HalpAcquireCmosSpinLock


;++
;
; Routine Description:
;
;   Release spinlock, and restore flags to the state it was before
;   acquiring the spinlock.
;
; Arguments:
;
;   None
;
; Return Value:
;
;   Interrupts restored to their state before acquiring spinlock.
;   Irql level not affected.
;
;--

cPublicProc _HalpReleaseCmosSpinLock  ,0
        push    eax
        ;
        ; restore eflags as it was before acquiring spinlock. Put it on
        ; stack before releasing spinlock (so other cpus cannot overwrite
        ; it with their own eflags).
        ;
        push    _HalpHardwareLockFlags          ; old eflags on stack.
        lea     eax, _HalpSystemHardwareLock
        RELEASE_SPINLOCK    eax
        popfd                                   ; restore eflags.
        pop   eax
        stdRET    _HalpReleaseCmosSpinLock
stdENDP _HalpReleaseCmosSpinLock

;++
;
; UCHAR
; HalpGetCmosCenturyByte (
;    VOID
;    )
;
; Routine Description:
;
;    This routine gets Century byte from CMOS.
;
; Arguments:
;
;    None
;
; Return Value:
;
;    (al) = Century byte in BCD form.
;
;--

cPublicProc _HalpGetCmosCenturyByte, 0

        mov     eax, _HalpCmosCenturyOffset

if DBG

;
; Make sure the HalpCmosCenturyOffset is initialized
;

        cmp     eax, 0
        jne     short @f

        int 3
@@:
endif
        test    eax, BANK1
        jnz     short rcb50

        CMOS_READ                       ; (al) = century in BCD form
        stdRET    _HalpGetCmosCenturyByte

rcb50:  mov     edx, eax

        mov     al, 0Ah
        CMOS_READ

        mov     dh, al                          ; save it for restore
        or      al, 10h                         ; Set DV0 = 1

        mov     ah, al
        mov     al, 0Ah                         ; Write register A
        CMOS_WRITE

        mov     al, dl                          ; century offset
        CMOS_READ
        mov     dl, al                          ; save it

        mov     ah, dh                          ; Restore DV0
        mov     al, 0Ah                         ; Write register A
        CMOS_WRITE

        mov     al, dl
        stdRET    _HalpGetCmosCenturyByte

stdENDP _HalpGetCmosCenturyByte


;++
;
; VOID
; HalpSetCmosCenturyByte (
;    UCHAR Century
;    )
;
; Routine Description:
;
;    This routine sets Century byte in CMOS.
;
; Arguments:
;
;    Century - Supplies the value for CMOS century byte
;
; Return Value:
;
;    None.
;
;--

cPublicProc _HalpSetCmosCenturyByte, 1

        mov     eax, _HalpCmosCenturyOffset
if DBG

;
; Make sure the HalpCmosCenturyOffset is initialized
;

        cmp     eax, 0
        jne     short @f

        int 3
@@:
endif

        test    eax, BANK1
        jnz     short scb50

        mov     ah, [esp+4]             ; (ah) = Century in BCD form
        CMOS_WRITE
        stdRET    _HalpSetCmosCenturyByte


scb50:  mov     edx, eax

        mov     al, 0Ah
        CMOS_READ

        mov     dh, al                          ; save it for restore
        or      al, 10h                         ; Set DV0 = 1

        mov     ah, al
        mov     al, 0Ah                         ; Write register A
        CMOS_WRITE

        mov     ah, [esp+4]                     ; (ah) = Century in BCD form
        mov     al, dl                          ; century offset
        CMOS_WRITE

        mov     ah, dh                          ; Restore DV0
        mov     al, 0Ah                         ; Write register A
        CMOS_WRITE
        stdRET    _HalpSetCmosCenturyByte

stdENDP _HalpSetCmosCenturyByte


;++
;
; VOID
; HalpCpuID (
;     ULONG   InEax,
;     PULONG  OutEax,
;     PULONG  OutEbx,
;     PULONG  OutEcx,
;     PULONG  OutEdx
;     );
;
; Routine Description:
;
;   Executes the CPUID instruction and returns the registers from it
;
;   Only available at INIT time
;
; Arguments:
;
; Return Value:
;
;--
cPublicProc _HalpCpuID,5

        push    ebx
        push    esi

        mov     eax, [esp+12]
        db      0fh, 0a2h       ; CPUID

        mov     esi, [esp+16]   ; return EAX
        mov     [esi], eax

        mov     esi, [esp+20]   ; return EBX
        mov     [esi], ebx

        mov     esi, [esp+24]   ; return ECX
        mov     [esi], ecx

        mov     esi, [esp+28]   ; return EDX
        mov     [esi], edx

        pop     esi
        pop     ebx

        stdRET  _HalpCpuID

stdENDP _HalpCpuID


;++
;
; VOID
; HalpFlushTLB (
;     VOID
;     );
;
; Routine Description:
;
;   Flush the current TLB.
;
; Arguments:
;
; Return Value:
;
;--
cPublicProc _HalpFlushTLB, 0
.586p
        pushfd
        push    ebx
        push    esi

        cli
        mov     esi, cr3

        mov     ecx, PCR[PcPrcb]
        cmp     byte ptr [ecx].PbCpuID, 0
        jz      short ftb50

        mov     eax, 1                  ; Get feature bits
        cpuid                           ; (note "cpuid" between CR3 reload fixes
                                        ; P6 B step errata #11)

        test    edx, 2000h              ; see if 'G' bit is supported
        jz      short ftb50

        mov     ecx, cr4                ; 'G' bit is supported, due global flush
        mov     edx, ecx                ; Save orginal cr4
        and     ecx, not CR4_PGE        ; Make sure global bit is disabled
        mov     cr4, ecx
        mov     cr3, esi                ; flush TLB
        mov     cr4, edx                ; restore cr4
        jmp     short ftp99

ftb50:  mov     cr3, esi

ftp99:  pop     esi
        pop     ebx
        popfd
        stdRET  _HalpFlushTLB

.486p
stdENDP _HalpFlushTLB


_TEXT   ends

        end