;//---------------------------------------------------------------
;//	rtupdate.s
;//
;//	This program uses the CPU's TimeStamp Counter to measure
;//	the duration of the Real-Time Clock's update-cycle.
;//
;//	   assemble with:  $ as86 rtupdate.s -b rtupdate.b 
;//	   install using:  $ dd if=rtupdate.b of=/dev/fd0 seek=1 
;//
;//	NOTE: This code begins executing with CS:IP = 1000:0002. 
;//
;//	programmer: ALLAN CRUSE
;//	date begun: 02 MAY 2004
;//---------------------------------------------------------------


	MACRO	rdtsc
	.BYTE	0x0F, 0x31
	MEND


	.SECT	.TEXT
;-----------------------------------------------------------------
	.WORD	0xABCD			; programming signature 
;-----------------------------------------------------------------
main:	mov	ax, cs			; address this segment 
	mov	ds, ax			;   with DS register  
	pop	dword return_address	; store return-address 
	mov	ss, ax			;  adjust SS register 
	lea	esp, tos0		; and set new stacktop 

	call	record_update_counts
	call	compute_microseconds
	call	display_microseconds 

	push	dword return_address	; recover our exit-address 
	retf				; exit to program launcher 
;-----------------------------------------------------------------
return_address:	.LONG	0		; to store exit-address 
;-----------------------------------------------------------------
compute_microseconds:
 
	; determine the fraction of a second, in millionths
	mov	eax, duration_1
	mul	dword onemillion	; times one million
	mov	ecx, duration_2
	div	ecx

	; save quotient, rounding to the nearest millionth	
	add	edx, edx		; double the remainder
	cmp	ecx, edx		; is 2*rem < divisor?
	cmc				; yes, carry was set
	adc	eax, #0			; but now is cleared 
	mov	micro_secs, eax		; save duration (us)
	ret	
;-----------------------------------------------------------------
;-----------------------------------------------------------------
duration_1:	.LONG	0		; cycles during update
duration_2:	.LONG	0		; cycles during second
micro_secs:	.LONG	0		; ratio, in millionths
onemillion:	.LONG	1000000		; the ratio multiplier 
;-----------------------------------------------------------------
record_update_counts:

	; wait for an update-cycle to begin
	mov	al, #0x0A		; RTC Register A
	out	#0x70, al		; selected for access
wait1:	in	al, #0x71		; read register A
	test	al, #0x80		; update in progress?
	jnz	wait1			; yes, wait till done
wait2:	in	al, #0x71		; read register A
	test	al, #0x80		; update in progress?
	jz	wait2			; no, wait till begin
	
	; immediately read the current TimeStamp
	rdtsc				; get the TimeStamp
	mov	ebx, eax		; save LoTimeStamp 
	mov	ecx, edx		; save HiTimeStamp

	; then wait for that update-cycle to finish
wait3:	in	al, #0x71		; read register A
	test	al, #0x80		; update in progress?
	jnz	wait3			; yes, wait till done

	; store the processor's cycle-count 
	rdtsc				; get new TimeStamp
	sub	eax, ebx		; minus saved value
	sbb	edx, ecx		; of Stamp quadword
	mov	duration_1, eax		; store cycle-count

	; now wait for the next update-cycle to begin
wait4:	in	al, #0x71		; read register A
	test	al, #0x80		; update in progress?
	jz	wait4			; no, wait till begin

	; store the processor's cycle-count 
	rdtsc				; get new TimeStamp
	sub	eax, ebx		; minus saved value
	sbb	edx, ecx		; of Stamp qyadword
	mov	duration_2, eax		; store cycle-count

	; depart with read-only RTC register selected
	mov	al, #0x0D		; select register D
	out	#0x70, al		; in case of access 
	ret
;-----------------------------------------------------------------
msg:	.ASCII	"\n\r         "		; report message-string
	.ASCII	"Duration of Real-Time Clock\'s update-cycle: "
buf: 	.ASCII	"     microseconds \n\n\r"	; output-buffer
len:	.WORD	* - msg			; length of the message
att:	.BYTE	0x0E			; video attribute-byte
;-----------------------------------------------------------------
;-----------------------------------------------------------------
display_microseconds: 

	; convert update-cycle's duration to a decimal value
	mov	eax, micro_secs		; get duration (in us)	
	lea	di, buf+4		; point to output buffer
	call	ax2dec			; conversion to decimal 

	; display our report on the RTC update-cycle's duration
	mov	ax, ds			; address data segment
	mov	es, ax			;   with ES register
	mov	ah, #0x0F		; display-page in BH
	int	0x10			; request BIOS service
	mov	ah, #0x03		; cursor locn in DH, DL
	int	0x10			; request BIOS service
	lea	bp, msg			; setup message offset
	mov	cx, len			; setup message length
	mov	bl, att			; setup display colors
	mov	ax, #0x1301		; write_string function
	int	0x10			; request BIOS service

	ret
;-----------------------------------------------------------------
ax2dec:
	pusha
	mov	bx, #10			; decimal-system radix
	mov	cx, #5			; maximum digit-count
.L0:	xor	dx, dx			; setup 32-bit dividend
	div	bx			; divide by the radix
	or	dl, #0x30		; convert to a numeral
	dec	di			; adjust buffer pointer
	mov	[di], dl		; put digit into buffer
	or	ax, ax			; quotient was zero?
	loopnz	.L0			; no, get another digit
	popa				
	ret
;-----------------------------------------------------------------
	.ALIGN	16			; alignment at paragraph
	.SPACE	512			; reserved for stack use	
tos0:					; label fop top-of-stack 
;-----------------------------------------------------------------
	END