; Project name	:	Assembly Library
; Description	:	Tests for Assembly Library.

;
; XTIDE Universal BIOS and Associated Tools
; Copyright (C) 2009-2010 by Tomi Tilli, 2011-2013 by XTIDE Universal BIOS Team.
;
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
; Visit http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
;

; Include .inc files
%define INCLUDE_DISPLAY_LIBRARY
%define INCLUDE_TIME_LIBRARY
%include "AssemblyLibrary.inc"	; Assembly Library. Must be included first!


; Section containing code
SECTION .text

; Program first instruction.
CPU 486
ORG	100h						; Code starts at offset 100h (DOS .COM)
Start:
	jmp		BusMeasurements_Start

; Include library sources
%include "AssemblyLibrary.asm"

%macro MOV_TIMING_LOOP 1
	xor		bx, bx					; Offset for memory transfers
	mov		dx, OFFSET_TO_NEXT_WORD	; Must be at least 32 (Pentium Cache Line size)
	wbinvd							; Flush and invalidate internal cache
	cli								; Disable interrupts
	START_PRECISE_EVENT_TIMER
ALIGN 8
%%ReadNextWord:
	mov		ax, %1					; 2 bytes
	add		bx, dx					; 2 bytes
	jnc		SHORT %%ReadNextWord	; 2 bytes
	STOP_PRECISE_EVENT_TIMER
	sti
%endmacro


;--------------------------------------------------------------------
; Program start
;--------------------------------------------------------------------
ALIGN JUMP_ALIGN
BusMeasurements_Start:
	CALL_DISPLAY_LIBRARY InitializeDisplayContext
	xor		ax, ax
	mov		ds, ax

	call	MeasureRegAndMemMovDurationDifferenceToAX
	call	GetBusCycleTimeToAXfromRegAndMemMovDurationDifferenceInAX
	call	GetBusClockRateFromCycleTimeInAX
	mov		si, g_szBusMeasurements
	call	PrintBusMeasurements

	call	AssumeVlbCycleTimeToAXfromBusCycleTimeInCX
	call	GetBusClockRateFromCycleTimeInAX
	mov		si, g_szVlbAssumption
	call	PrintBusMeasurements

	; Exit to DOS
	mov 	ax, 4C00h			; Exit to DOS
	int 	21h



;--------------------------------------------------------------------
; MeasureRegAndMemMovDurationDifferenceToAX
;	Parameters:
;		DS:		Segment for memory read tests
;	Returns:
;		AX:		Difference in register and memory access durations
;				(Precise Event Timer Ticks)
;	Corrupts registers:
;		BX, CX, DX
;--------------------------------------------------------------------
MeasureRegAndMemMovDurationDifferenceToAX:
	call	SystemTimer_IntializePreciseEventTimer

	MOV_TIMING_LOOP	bx
	call	SystemTimer_GetPreciseEventTimerTicksToAX
	xchg	cx, ax		; Duration now in CX

	MOV_TIMING_LOOP [bx]
	call	SystemTimer_GetPreciseEventTimerTicksToAX
	sub		ax, cx		; AX = Difference in durations
	sub		ax, cx
	ret


;--------------------------------------------------------------------
; We must divide the duration by 4 since the timing loop loads
; whole cache line (4 times the bus width) instead of single BYTE.
;
; GetBusCycleTimeToAXfromRegAndMemMovDurationDifferenceInAX
;	Parameters:
;		AX:		Difference in register and memory access durations
;				(Precise Event Timer Ticks)
;	Returns:
;		AX:		Duration for single BYTE in nanosecs
;	Corrupts registers:
;		CX, DX
;--------------------------------------------------------------------
GetBusCycleTimeToAXfromRegAndMemMovDurationDifferenceInAX:
	mov		dx, TIMER_CYCLE_TIME
	mul		dx			; DX:AX = Duration in nanosecs
	mov		cx, (65536 / OFFSET_TO_NEXT_WORD) * 4
	div		cx			; AX = Duration for single BYTE in nanosecs
	ret


;--------------------------------------------------------------------
; GetBusClockRateFromCycleTimeInAX
;	Parameters:
;		AX:		Bus Cycle Time in nanosecs
;	Returns:
;		CX:		Bus Cycle Time in nanosecs
;		DX:		Bus Clock Rate (MHz)
;		AX:		Clock Rate tenths
;	Corrupts registers:
;		Nothing
;--------------------------------------------------------------------
GetBusClockRateFromCycleTimeInAX:
	xchg	cx, ax
	xor		dx, dx
	mov		ax, 1000
	div		cl			; AL = MHz, AH = Remainder
	xchg	al, dl		; DX = Bus Clock Rate, AL = 0
	aad					; AX = 10 * AH (+AL)
	div		cl
	xor		ah, ah		; AX = Tenths
	ret


;--------------------------------------------------------------------
; AssumeVlbCycleTimeToAXfromBusCycleTimeInCX
;	Parameters:
;		CX:		Bus Cycle Time in nanosecs
;	Returns:
;		AX:		Assumed VLB Cycle Time in nanosecs
;	Corrupts registers:
;		Nothing
;--------------------------------------------------------------------
AssumeVlbCycleTimeToAXfromBusCycleTimeInCX:
	mov		ax, cx
	cmp		al, 24		; 25 = 40 MHz
	jb		SHORT .AssumePentiumSoDivideBy2
	ret
.AssumePentiumSoDivideBy2:
	shr		ax, 1
	ret


;--------------------------------------------------------------------
; PrintBusMeasurements
;	Parameters:
;		CX:		Bus Cycle Time in nanosecs
;		DX:		Bus Clock Rate (MHz)
;		AX:		Clock Rate tenths
;		SI:		Offset to string to format
;	Returns:
;		Nothing
;	Corrupts registers:
;		AX, DX, BP
;--------------------------------------------------------------------
PrintBusMeasurements:
	mov		bp, sp
	push	cx
	push	dx
	push	ax
	JMP_DISPLAY_LIBRARY FormatNullTerminatedStringFromCSSI



; Section containing initialized data
SECTION .data

OFFSET_TO_NEXT_WORD		EQU	32	; Must be at least 32 (Pentium Cache Line size)

g_szBusMeasurements:
	db	"Detected bus cycle time of %u ns (%u.%u MHz) ",LF,CR,NULL
g_szVlbAssumption:
	db	"Assuming VLB (if exists) cycle time of %u ns (%u.%u MHz) ",LF,CR,NULL
g_szDashForZero:		db		"- ",NULL



; Section containing uninitialized data
SECTION .bss
