/
ptime.asm
784 lines (637 loc) · 19.3 KB
/
ptime.asm
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
;
;------------------------------------------------------------------------------
;M074 - ptime.asm added, containing clock/time routines for power.asm.
;
; 09/11/91 SMR M077: B#2669. Registered POWER's 2f channels in mult.inc
;
; 09/25/91 NSM M090: B#2729. Try to update our time from CMOS under
; WIN ENH mode once in 1024 I1c ticks.
; (approx. once in a minute)
; This update happens only if DOS calls us for time and
; does not exactly happen once in a minute.
;
; (this is changed to 20 secs from 1 minute)
;
; 11/26/91 NSM M101: We lose date sometimes under windows. To fix this
; do Int 1a's to get tick count instead of looking at 40:
; 6ch and if we get rollover, then go & update date
; and time from CMOS.
;------------------------------------------------------------------------------
;
;
.xlist
include version.inc ; set build flags
IFDEF POWER ; generate code only if power management
; is enabled
IFNDEF POWERALONE ; segment declarations for resident version
include biosseg.inc ; establish bios segment structure
ELSE ; segment declarations for standalonde version
.SEQ
Bios_Code segment word public 'Bios_Code'
Bios_Code ends
Bios_Data segment word public 'Bios_Data'
Bios_Data ends
SysInitSeg segment word public 'system_init'
SysInitSeg ends
; following segment definitions used by transient part of code
ENDIF
include msequ.inc
include devsym.inc
include bpb.inc
include ioctl.inc
include mult.inc ; M077
include power.inc
IFDEF INCL_APM
include apmequ.inc ; M001
ENDIF
break macro
endm
include error.inc
.list
include msgroup.inc ; define Bios_Data segment
extrn month_table:word
IFDEF POWERALONE ; standalone device driver version
Bios_Res dw Bios_Code ; Our code segment address
ELSE ; resident BIOS version
extrn Bios_Res:word ; Code segment address supplied externally
extrn ttticks:dword ; far ptr to time_to_ticks routine
extrn bintobcd:dword ; ptr to bin_to_bcd routine
IFDEF INCL_APM
extrn Check_and_Init_APM_Ptr:dword ; ptr to APM init routine
ENDIF ;INCL_APM
extrn P_UpdFromCMOS_Ptr:dword ; ptr to CMOS clock read ; M081
ENDIF ;NOT POWERALONE
extrn daycnt:word ; extrns for both resident and stand-alone
extrn daycnt2:word ; versions
extrn base_century:byte
extrn base_year:byte
extrn month_tab:byte
extrn bin_date_time:byte
extrn CMOSUpdFlg:byte
extrn CMOSPollCount:word
tocode
IFDEF POWERALONE ; stand alone version
Bios_Data_Word dw Bios_Data ; Our data segment
bintobcd proc near ;for real time clock support
;convert a binary input in al (less than 63h or 99 decimal)
;into a bcd value in al. ah destroyed.
push cx
aam ; M048
mov cl, 4 ; M048
shl ah, cl ; M048
or al, ah ; M048
pop cx
ret
bintobcd endp
ELSE ; resident version
extrn Bios_Data_word:word
ENDIF
public tim_read
public tim_writ
;--------------------------------------------------------------------
;
; tim_writ sets the current time
;
; on entry es:[di] has the current time:
;
; number of days since 1-1-80 (word)
; minutes (0-59) (byte)
; hours (0-23) (byte)
; hundredths of seconds (0-99) (byte)
; seconds (0-59) (byte)
;
; each number has been checked for the correct range.
;
tim_writ proc near
assume ds:Bios_Data
mov ax,word ptr es:[di]
push ax ;daycnt. we need to set this at the very
; end to avoid tick windows.
; Set hardware clock time.
mov al,es:[di+3] ;get binary hours
call bintobcd ;convert to bcd
mov ch,al ;ch = bcd hours
mov al,es:[di+2] ;get binary minutes
call bintobcd ;convert to bcd
mov cl,al ;cl = bcd minutes
mov al,es:[di+5] ;get binary seconds
call bintobcd ;convert to bcd
mov dh,al ;dh = bcd seconds
mov dl,0 ;dl = 0 (st) or 1 (dst)
cli ;turn off timer
mov ah,03h ;set rtc time
int 1ah ;call rom bios clock routine
sti
mov cx,word ptr es:[di+2]
mov dx,word ptr es:[di+4]
IFDEF POWERALONE
call time_to_ticks
ELSE
call ttticks
ENDIF
;cx:dx now has time in ticks
cli ; turn off timer
mov ah, 1 ; command is set time in clock
int 1ah ; call rom-bios clock routines
pop [daycnt]
sti
call daycnttoday ; convert to bcd format
cli ; turn off timer
mov ah,05h ; set rtc date
int 1ah ; call rom-bios clock routines
sti
clc
ret
tim_writ endp
;
; gettime reads date and time
; and returns the following information:
; es:[di] =count of days since 1-1-80
; es:[di+2]=hours
; es:[di+3]=minutes
; es:[di+4]=seconds
; es:[di+5]=hundredths of seconds
tim_read proc near
; M090 BEGIN - See if we have to update our time from CMOS before
; returning date and time to caller.
cmp [CMOSUpdFlg],0 ; M090 do we need to update from CMOS
je tr_NoCMOSUpdate ;
tr_CMOSUpd: ; M101
IFDEF POWERALONE ; M081
call far ptr P_UpdFromCMOS
ELSE
call P_UpdFromCMOS_ptr ; M074 update our date and time
; from CMOS RTC
ENDIF
mov [CMOSPollCount],MAXCMOSPOLLCOUNT
mov [CMOSUpdFlg],0 ;
; M090 END
tr_NoCMOSUpdate:
; M101 - BEGIN - get tick count through 1a instead of looking at 40:6ch
; and check for rollover
mov ax,0 ; get tick count
int 1ah ; cx:dx = tick count
or al,al ; al != 0 if midnight passed
jnz tr_CMOSUpd ; rollover; update date and time
; M101 - END
mov si,[daycnt]
; we now need to convert the time in tick to the time in 100th of
; seconds. the relation between tick and seconds is:
;
; 65536 seconds
; ----------------
; 1,193,180 tick
;
; to get to 100th of second we need to multiply by 100. the equation is:
;
; ticks from clock * 65536 * 100
; --------------------------------- = time in 100th of seconds
; 1,193,180
;
; fortunately this fromula simplifies to:
;
; ticks from clock * 5 * 65,536
; --------------------------------- = time in 100th of seconds
; 59,659
;
; the calculation is done by first multipling tick by 5. next we divide by
; 59,659. in this division we multiply by 65,536 by shifting the dividend
; my 16 bits to the left.
;
; start with ticks in cx:dx
; multiply by 5
mov ax,cx
mov bx,dx
shl dx,1
rcl cx,1 ;times 2
shl dx,1
rcl cx,1 ;times 4
add dx,bx
adc ax,cx ;times 5
xchg ax,dx
; now have ticks * 5 in dx:ax
; we now need to multiply by 65,536 and divide by 59659 d.
mov cx,59659 ; get divisor
div cx
; dx now has remainder
; ax has high word of final quotient
mov bx,ax ; put high work if safe place
xor ax,ax ; this is the multiply by 65536
div cx ; bx:ax now has time in 100th of seconds
;rounding based on the remainder may be added here
;the result in bx:ax is time in 1/100 second.
mov dx,bx
mov cx,200 ;extract 1/100's
;division by 200 is necessary to ensure no overflow--max result
;is number of seconds in a day/2 = 43200.
div cx
cmp dl,100 ;remainder over 100?
jb noadj
sub dl,100 ;keep 1/100's less than 100
noadj:
cmc ;if we subtracted 100, carry is now set
mov bl,dl ;save 1/100's
;to compensate for dividing by 200 instead of 100, we now multiply
;by two, shifting a one in if the remainder had exceeded 100.
rcl ax,1
mov dl,0
rcl dx,1
mov cx,60 ;divide out seconds
div cx
mov bh,dl ;save the seconds
div cl ;break into hours and minutes
xchg al,ah
;time is now in ax:bx (hours, minutes, seconds, 1/100 sec)
push ax
mov ax,si ; daycnt
stosw
pop ax
stosw
mov ax,bx
stosw
clc
ret
tim_read endp
assume es:nothing
; the following routine is executed at resume time when the system
; powered on after suspension. it reads the real time clock and
; resets the system time and date
;
; This can be patched to be the INT6C vector so that this can be
; used by other people to update DOS time from CMOS (after taking
; care for IRET)
public P_UpdFromCMOS
P_UpdFromCMOS proc far
assume ds:nothing
push ds
mov ds,cs:Bios_Data_Word
assume ds:Bios_Data
call read_real_date ; get the date from the clock
PUFC_UpdDate: ; M101
mov ds:daycnt,si ; update our copy of date
call read_real_time ; get the time from the rtc
cli
mov ah,01h ; command to set the time
int 1ah ; call rom-bios time routine
sti
; M101 BEGIN - check back to see if date changed when we were reading the
; time; if so, update date and time all over again ( Paranoid?)
call read_real_date ; get the date from the clock
; BUGBUG- nagara - Store the ret.value of date from int 1a in this
; procedure read_real_date so that at the end we don't really have to
; call read_real_date again; we just need to call int 1a and compare the
; date registers against the stored values of these registers.
cmp si,ds:daycnt ; is the date changed ?
jne PUFC_UpdDate ; yes, go back and set the new date
; M101 END
pop ds
ret
P_UpdFromCMOS endp
;************************************************************************
;
; read_real_date reads real-time clock for date and returns the number
; of days elapsed since 1-1-80 in si
;
read_real_date proc near
assume ds:Bios_Data,es:nothing
push ax
push cx
push dx
xor ah,ah ; throw away clock roll over
int 1ah
pop dx
pop cx
pop ax ; cas - bad code!
push ax
push bx
push cx
push dx
mov daycnt2,1 ; real time clock error flag (+1 day)
mov ah,4 ; read date function code
int 1ah ; read real-time clock
jnc read_ok ; jmp success
jmp r_d_ret ; jmp error
read_ok: ; ******* get bcd values in binary *****
mov word ptr bin_date_time+0,cx ; store as hex value
mov word ptr bin_date_time+2,dx ; ...
mov daycnt2,2 ; read of r-t clock successful
call bcd_verify ; verify bcd values in range
jc r_d_ret ; jmp some value out of range
mov daycnt2,3 ; read of r-t clock successful
call date_verify ; verify date values in range
jc r_d_ret ; jmp some value out of range
mov daycnt2,0 ; verify successful
call in_bin ; convert date to binary
; ******* years since 1-1-80 *********
mov al,byte ptr bin_date_time ; get years into century
cbw ;
cmp byte ptr bin_date_time+1,20 ; 20th century?
jnz century_19 ; jmp no
add ax,100 ; add in a century
century_19: ;
sub ax,80 ; subtract off 1-1-80
mov cl,4 ; leap year every 4
div cl ; al= # leap year blocks, ah= remainder
mov bl,ah ; save odd years
cbw ; zero ah
mov cx,366+3*365 ; # of days in leap year blocks
mul cx ; dx:ax is result
mov daycnt2,ax ; save count of days
mov al,bl ; get odd years count
cbw ;
or ax,ax ; is ax= 0?
jz leap_year ; jmp if none
mov cx,365 ; days in year
mul cx ; dx:ax is result
add daycnt2,ax ; add on days in odd years
jmp short leap_adjustment ; account for leap year
leap_year: ; possibly account for a leap day
cmp byte ptr bin_date_time+3,2 ; is month february
jbe no_leap_adjustment ; jan or feb. no leap day yet.
leap_adjustment: ; account for leap day
inc daycnt2
no_leap_adjustment: ; ******* get days of month *******
mov cl,byte ptr bin_date_time+2
xor ch,ch
dec cx ; because of offset from day 1, not day 0
add daycnt2,cx ; ******* get days in months preceeing *****
mov cl,byte ptr bin_date_time+3 ; get month
xor ch,ch
dec cx ; january starts at offset 0
shl cx,1 ; word offset
mov si,offset month_table ; beginning of month_table
add si,cx ; point into month table
mov ax,word ptr [si]; get # days in previous months
add daycnt2,ax
r_d_ret:
mov si,daycnt2 ; result in si
pop dx
pop cx
pop bx
pop ax
ret
r_t_retj:
xor cx,cx
xor dx,dx
jmp short r_t_ret
read_real_date endp
;--------------------------------------------------------------------
; read_real_time reads the time from the rtc. on exit, it has the number of
; ticks (at 18.2 ticks per sec.) in cx:dx.
read_real_time proc near
assume ds:Bios_Data,es:nothing
mov ah,2
int 1ah
jc r_t_retj
oktime:
mov word ptr bin_date_time,cx ; hours + minutes
mov byte ptr bin_date_time+3,dh ; seconds
mov byte ptr bin_date_time+2,0 ; unused for time
call bcd_verify
jc r_t_retj
call time_verify
jc r_t_retj
call in_bin
mov cx,word ptr bin_date_time
mov dx,word ptr bin_date_time+2
; get time in ticks in cx:dx
IFDEF POWERALONE
call time_to_ticks
ELSE
call ttticks
ENDIF
r_t_ret:
ret
read_real_time endp
;--------------------------------------------------------------------
; in_bin converts bin_date_time values from bcd to bin
in_bin proc near
assume ds:Bios_Data,es:nothing
mov al,byte ptr bin_date_time+0 ; years or minutes
call bcd_to_bin
mov byte ptr bin_date_time+0,al
mov al,byte ptr bin_date_time+1 ;century or hours
call bcd_to_bin
mov byte ptr bin_date_time+1,al
mov al,byte ptr bin_date_time+2 ; days (not used for time)
call bcd_to_bin
mov byte ptr bin_date_time+2,al
mov al,byte ptr bin_date_time+3 ; months or seconds
call bcd_to_bin
mov byte ptr bin_date_time+3,al
ret
in_bin endp
;--------------------------------------------------------------------
; bcd_to_bin converts two bcd nibbles in al (value <= 99.) to
; a binary representation in al
; ah is destroyed
bcd_to_bin proc near
assume ds:nothing,es:nothing
mov ah, al ; M048
and al, 0fh ; M048
mov cl, 4 ; M048
shr ah, cl ; M048
aad ; M048
ret
bcd_to_bin endp
;--------------------------------------------------------------------
; date_verify loosely checks bcd date values to be in range in bin_date_time
date_verify proc near
assume ds:Bios_Data,es:nothing
cmp byte ptr bin_date_time+1,20h ; century check
ja date_error ; error
jz century_20 ; jmp in 20th century
cmp byte ptr bin_date_time+1,19h ; century check
jb date_error ; error
cmp byte ptr bin_date_time+0,80h ; year check
jb date_error ; error
century_20:
cmp byte ptr bin_date_time+0,99h ; year check
ja date_error ; error
cmp byte ptr bin_date_time+3,12h ; month check
ja date_error ; error
cmp byte ptr bin_date_time+3,00h ; month check
jbe date_error ; error
cmp byte ptr bin_date_time+2,31h ; day check
ja date_error ; error
cmp byte ptr bin_date_time+2,00h ; day check
jbe date_error ; error
clc ; set success flag
ret
date_error:
stc ; set error flag
ret
date_verify endp
;--------------------------------------------------------------------
; time_verify very loosely checks bcd date values to be in range in bin_date_time
time_verify proc near
assume ds:Bios_Data,es:nothing
cmp byte ptr bin_date_time+1,24h
ja time_error
cmp byte ptr bin_date_time+0,59h
ja time_error
cmp byte ptr bin_date_time+3,59h
ja time_error
clc
ret
time_error:
stc
ret
time_verify endp
;--------------------------------------------------------------------
; bcd_verify checks values in bin_date_time to be valid
; bcd numerals. carry set if any nibble out of range
bcd_verify proc near
assume ds:Bios_Data,es:nothing
mov cx,4 ; 4 bytes to check
mov bx,offset bin_date_time
bv_loop:
mov al,[bx] ; get a bcd number (0..99)
mov ah,al
and ax,0f00fh ; 10's place in high ah, 1's in al
cmp al,10 ; is 1's place in range?
ja bv_error ; jmp out of range
shr ah,1 ; swap nibbles
shr ah,1
shr ah,1
shr ah,1
and ah,0fh ; get rid of any erroneous bits
cmp ah,10 ; is 10's place in range
ja bv_error ; jmp out of range
inc bx ; next byte
dec cx
jnz bv_loop
clc ; set success flag
ret
bv_error:
stc ; set error flag
ret
bcd_verify endp
;--------------------------------------------------------------------
daycnttoday proc near ; for real time clock support
;entry: [daycnt] = number of days since 1-1-80
;
;return: ch - centry in bcd
; cl - year in bcd
; dh - month in bcd
; dl - day in bcd
push di
mov di,daycnt
cmp di,(365*20+(20/4)) ;# of days from 1-1-1980 to 1-1-2000
jae century20
mov base_century,19
mov base_year,80
jmp short years
century20: ;20th century
mov base_century,20
mov base_year,0
sub di,(365*20+(20/4)) ;adjust daycnt
years:
xor dx,dx
mov ax,di
mov bx,(366+365*3) ;# of days in a leap year block
div bx ;ax = # of leap block, dx = daycnt
mov di,dx ;save daycnt left
mov bl,4
mul bl ;ax = # of years. less than 100 years!
add base_year,al ;so, ah = 0. adjust year accordingly.
inc di ;set daycnt to 1 base
cmp di,366 ;the daycnt here is the remainder of the leap year block.
jbe leapyear ;so, it should within 366+355+355+355 days.
inc base_year ;first if daycnt <= 366, then leap year
sub di,366 ;else daycnt--, base_year++;
;and the next three years are regular years.
mov cx,3
regularyear:
cmp di,365 ;for(i=1; i>3 or daycnt <=365;i++)
jbe yeardone ;{if (daycnt > 365)
inc base_year ; { daycnt -= 365
sub di,365 ; }
loop regularyear ;}
; should never fall through loop
leapyear:
mov byte ptr month_tab+1,29 ;leap year. change the month table.
yeardone:
xor bx,bx
xor dx,dx
mov ax,di
mov si,offset month_tab
mov cx,12
months:
inc bl
mov dl,byte ptr ds:[si] ;compare daycnt for each month until fits
cmp ax,dx ;dh=0.
jbe month_done
inc si ;next month
sub ax,dx ;adjust daycnt
loop months
; should never fall through loop
month_done:
mov byte ptr month_tab+1,28 ;restore month table value
mov dl,bl
mov dh,base_year
mov cl,base_century ;now, al=day, dl=month,dh=year,cl=century
call bintobcd ;oh my!!! to save 15 bytes, bin_to_bcd proc
;was relocated seperately from daycnt_to_day proc.
xchg dl,al ;dl = bcd day, al = month
call bintobcd
xchg dh,al ;dh = bcd month, al = year
call bintobcd
xchg cl,al ;cl = bcd year, al = century
call bintobcd
mov ch,al ;ch = bcd century
pop di ;restore original value
ret
daycnttoday endp
IFDEF POWERALONE ; needed only for standalone version
; for resident version use the one in
; mschar.asm
;--------------------------------------------------------------------
; convert time to ticks
; input : time in cx and dx
; ticks returned in cx:dx
time_to_ticks proc near
; first convert from hour,min,sec,hund. to
; total number of 100th of seconds
mov al,60
mul ch ;hours to minutes
mov ch,0
add ax,cx ;total minutes
mov cx,6000 ;60*100
mov bx,dx ;get out of the way of the multiply
mul cx ;convert to 1/100 sec
mov cx,ax
mov al,100
mul bh ;convert seconds to 1/100 sec
add cx,ax ;combine seconds with hours and min.
adc dx,0 ;ripple carry
mov bh,0
add cx,bx ;combine 1/100 sec
adc dx,0
; dx:cx is time in 1/100 sec
xchg ax,dx
xchg ax,cx ;now time is in cx:ax
mov bx,59659
mul bx ;multiply low half
xchg dx,cx
xchg ax,dx ;cx->ax, ax->dx, dx->cx
mul bx ;multiply high half
add ax,cx ;combine overlapping products
adc dx,0
xchg ax,dx ;ax:dx=time*59659
mov bx,5
div bl ;divide high half by 5
mov cl,al
mov ch,0
mov al,ah ;remainder of divide-by-5
cbw
xchg ax,dx ;use it to extend low half
div bx ;divde low half by 5
mov dx,ax
; cx:dx is now number of ticks in time
ret
time_to_ticks endp
ENDIF
Bios_Code ends
ENDIF
END