	TITLE MSCLOCK - DOS 3.3
;----------------------------------------------------------------
;								:
;		    CLOCK DEVICE DRIVER 			:
;								:
;								:
;   This file contains the Clock Device Driver. 		:
;								:
;   The routines in this files are:				:
;								:
;	routine 		function			:
;	------- 		--------			:
;	TIM$WRIT		Set the current time		:
;	TIM$READ		Read the current time		:
;	Time_To_Ticks		Convert time to corresponding	:
;				  number of clock ticks 	:
;								:
; The clock ticks at the rate of:				:
;								:
;	1193180/65536 ticks/second (about 18.2 ticks per second):
; See each routine for information on the use.			:
;								:
;----------------------------------------------------------------


	test=0
	INCLUDE MSGROUP.INC	;DEFINE CODE SEGMENT
	INCLUDE MSMACRO.INC

	EXTRN EXIT:NEAR
;
; DAYCNT is the number of days since 1-1-80.
; Each time the clock is read it is necessary to check if another day has
; passed.  The ROM only returns the day rollover once so if it is missed
; the time will be off by a day.
;
	EXTRN DAYCNT:WORD	;MSDATA

;;Rev 3.30 Modification ------------------------------------------------
; variables for real time clock setting
	public	HaveCMOSClock
HaveCMOSClock	db	0	;set by MSINIT.
	public	base_century
base_century	db	19
	public	base_year
base_year	db	80
	public	month_tab
month_tab	db	31,28,31,30,31,30,31,31,30,31,30,31

; The following are indirect intra-segment call addresses. The
;procedures are defined in MSINIT for relocation.  MSINIT will set these
;address when the relocation is done.
	public	BinToBCD
BinToBCD	dw	0	;should point to Bin_To_BCD proc in MSINIT
	public	DaycntToDay
DaycntToDay	dw	0	;should point to Daycnt_to_day in MSINIT

;********************************************************************
; Indirect call address of TIME_TO_TICKS procedure.
;This will be used by the relocatable portable suspend/resume code.

	public	TimeToTicks
TimeToTicks	dw	Time_To_Ticks

;;End of Modification ------------------------------------------------

;--------------------------------------------------------------------
;
; Settime 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.
;
	PUBLIC TIM$WRIT
TIM$WRIT PROC	NEAR
	ASSUME	DS:CODE
	mov	AX,WORD PTR ES:[DI]
	push	AX		;DAYCNT. We need to set this at the very
				;  end to avoid tick windows.
;;Rev 3.30 Modification
	cmp	HaveCMOSClock, 0
	je	No_CMOS_1
	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

;;End of Modification
No_CMOS_1:
	mov	CX,WORD PTR ES:[DI+2]
	mov	DX,WORD PTR ES:[DI+4]
;;Rev 3.30 Modification
	call	time_to_ticks	; convert time to ticks
				;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
;CMOS clock -------------------------------------
	cmp	HaveCMOSClock, 0
	je	No_CMOS_2
	call	DaycntToDay	; convert to BCD format
	cli			; Turn off timer
	mov	AH,05h		; set RTC date
	int	1Ah		; call rom-bios clock routines
	sti
;------------------------------------------------

No_CMOS_2:
	jmp	EXIT
TIM$WRIT ENDP
;;End of Modification



;
; convert time to ticks
; input : time in CX and DX
; ticks returned in CX:DX
;
public time_to_ticks
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

;;Rev 3.30 Modification
;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
;;End of Modification


;
; 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
;
	PUBLIC TIM$READ
TIM$READ PROC	NEAR
				; read the clock
	xor	AH, AH		; set command to read clock
	int	1Ah		; call rom-bios to get time

	or	al,al		; check for a new day
	jz	noroll1 	; if al=0 then don't reset day count
	INC	[DAYCNT]	; CATCH ROLLOVE
noroll1:
	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
	jmp	EXIT

TIM$READ ENDP
CODE	ENDS
	END
