Index: trunk/Assembly_Library/Inc/Delay.inc
===================================================================
--- trunk/Assembly_Library/Inc/Delay.inc	(revision 286)
+++ trunk/Assembly_Library/Inc/Delay.inc	(revision 287)
@@ -29,13 +29,27 @@
 ;	486:		 7 cycles for jump + 6 cycles for last comparison
 ;
+; LOOP instruction uses two bytes so aligned fetching will require:
+;   8088:		8 cycles (two BYTE reads)
+;   8086:		4 cycles (one WORD read)
+;   286:		2 cycles + wait states (usually 1)
+;   386:		?
+;	486:		Fetched only once to internal cache
+;
+; DELAY_WITH_LOOP_INSTRUCTION_NA	; No JUMP_ALIGN
 ; DELAY_WITH_LOOP_INSTRUCTION
 ;	Parameters
 ;		CX:		Loop iterations (0 is maximum delay with 65536 iterations)
 ;	Returns:
+;		CX:		Zero
+;	Corrupts registers:
 ;		Nothing
-;	Corrupts registers:
-;		CX
 ;--------------------------------------------------------------------
+%macro DELAY_WITH_LOOP_INSTRUCTION_NA 0
+%%StartOfLoop:
+	loop	%%StartOfLoop
+%endmacro
+
 %macro DELAY_WITH_LOOP_INSTRUCTION 0
+ALIGN JUMP_ALIGN
 %%StartOfLoop:
 	loop	%%StartOfLoop
Index: trunk/Assembly_Library/Inc/Display.inc
===================================================================
--- trunk/Assembly_Library/Inc/Display.inc	(revision 286)
+++ trunk/Assembly_Library/Inc/Display.inc	(revision 287)
@@ -179,5 +179,5 @@
 
 ; Cursor shapes
-CURSOR_NORMAL				EQU		0607h		; Two line cursor near or at the bottom of cell
+CURSOR_NORMAL				EQU		0607h		; Two line cursor near or at the bottom of cell (not valid for IBM MDA)
 CURSOR_HIDDEN				EQU		2000h
 
@@ -212,4 +212,5 @@
 
 ; Non ASCII characters (code page 437)
+BETA										EQU		225
 QUOTATION_MARK								EQU		34
 DOUBLE_TOP_RIGHT_CORNER						EQU		187
Index: trunk/Assembly_Library/Inc/Math.inc
===================================================================
--- trunk/Assembly_Library/Inc/Math.inc	(revision 286)
+++ trunk/Assembly_Library/Inc/Math.inc	(revision 287)
@@ -65,4 +65,25 @@
 
 ;--------------------------------------------------------------------
+; SHL_DXAX
+;	Parameters:
+;		%1:		Number of bits to shift
+;	Returns:
+;		DX:AX	Shifted value
+;	Corrupts registers:
+;		CX
+;--------------------------------------------------------------------
+%macro SHL_DXAX 1
+	%ifnidni %1, cx
+		mov		cx, %1
+	%endif
+ALIGN JUMP_ALIGN
+.ShiftNextBit:
+	shl		ax, 1
+	rcl		dx, 1
+	loop	.ShiftNextBit
+%endmacro
+
+
+;--------------------------------------------------------------------
 ; SHR_DXAX
 ;	Parameters:
Index: trunk/Assembly_Library/Inc/SystemTimer.inc
===================================================================
--- trunk/Assembly_Library/Inc/SystemTimer.inc	(revision 286)
+++ trunk/Assembly_Library/Inc/SystemTimer.inc	(revision 287)
@@ -34,5 +34,5 @@
 CONTROL_WORD_REGISTER_out				EQU	43h
 
-; Timer 2 is Connected to PC Speaker that can be controller from port 61h.
+; Timer 2 is connected to PC Speaker that can be controller from port 61h.
 SPEAKER_CONTROL_REGISTER				EQU	61h
 FLG_TIMER_2_OUTPUT_in					EQU	(1<<5)	; AT+ only
Index: trunk/Assembly_Library/Src/Display/DisplayContext.asm
===================================================================
--- trunk/Assembly_Library/Src/Display/DisplayContext.asm	(revision 286)
+++ trunk/Assembly_Library/Src/Display/DisplayContext.asm	(revision 287)
@@ -17,6 +17,7 @@
 DisplayContext_Initialize:
 	mov		WORD [VIDEO_BDA.displayContext+DISPLAY_CONTEXT.fnCharOut], DEFAULT_CHARACTER_OUTPUT
-	mov		WORD [VIDEO_BDA.displayContext+DISPLAY_CONTEXT.wCursorShape], CURSOR_NORMAL
 	mov		BYTE [VIDEO_BDA.displayContext+DISPLAY_CONTEXT.bAttribute], SCREEN_BACKGROUND_ATTRIBUTE
+	mov		ax, [VIDEO_BDA.wCursorShape]
+	mov		[VIDEO_BDA.displayContext+DISPLAY_CONTEXT.wCursorShape], ax
 	; Fall to .DetectAndSetDisplaySegment
 
Index: trunk/Assembly_Library/Src/Time/SystemTimer.asm
===================================================================
--- trunk/Assembly_Library/Src/Time/SystemTimer.asm	(revision 286)
+++ trunk/Assembly_Library/Src/Time/SystemTimer.asm	(revision 287)
@@ -29,20 +29,18 @@
 ; 2. Use START_PRECISE_EVENT_TIMER macro to start timer
 ; 3. Use STOP_PRECISE_EVENT_TIMER to stop timer (optional)
-; 4. Call SystemTimer_ReadNanosecsToDXAXfromPreciseEventTimer to get event duration
+; 4. Call SystemTimer_GetPreciseEventTimerTicksToAX to get event duration
 ;
-; SystemTimer_ReadNanosecsToDXAXfromPreciseEventTimer
+; SystemTimer_GetPreciseEventTimerTicksToAX
 ;	Parameters:
 ;		Nothing
 ;	Returns:
-;		DX:AX:	Event duration in nanosecs
+;		AX:		Event duration in timer ticks
 ;	Corrupts registers:
 ;		Nothing
 ;--------------------------------------------------------------------
 ALIGN JUMP_ALIGN
-SystemTimer_ReadNanosecsToDXAXfromPreciseEventTimer:
+SystemTimer_GetPreciseEventTimerTicksToAX:
 	OUTPUT_COUNTER_COMMAND_TO TIMER_2, LATCH, MODE_0_SINGLE_TIMEOUT, BINARY_COUNTER
 	READ_COUNT_TO_AX_FROM TIMER_2
 	neg		ax					; 0 - count (Mode 0 counts toward zero)
-	mov		dx, TIMER_CYCLE_TIME
-	mul		dx
 	ret
Index: trunk/Assembly_Library/Src/TimerTest.asm
===================================================================
--- trunk/Assembly_Library/Src/TimerTest.asm	(revision 286)
+++ trunk/Assembly_Library/Src/TimerTest.asm	(revision 287)
@@ -12,10 +12,26 @@
 
 ; Program first instruction.
+CPU 486
 ORG	100h						; Code starts at offset 100h (DOS .COM)
 Start:
-	jmp		TimerTest_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
 
 
@@ -24,16 +40,19 @@
 ;--------------------------------------------------------------------
 ALIGN JUMP_ALIGN
-TimerTest_Start:	
+BusMeasurements_Start:
 	CALL_DISPLAY_LIBRARY InitializeDisplayContext
+	xor		ax, ax
+	mov		ds, ax
 
-	call	SystemTimer_IntializePreciseEventTimer
+	call	MeasureRegAndMemMovDurationDifferenceToAX
+	call	GetBusCycleTimeToAXfromRegAndMemMovDurationDifferenceInAX
+	call	GetBusClockRateFromCycleTimeInAX
+	mov		si, g_szBusMeasurements
+	call	PrintBusMeasurements
 
-	START_PRECISE_EVENT_TIMER
-	mov		ax, MICROSECONDS_TO_WAIT
-	call	Delay_MicrosecondsFromAX
-	STOP_PRECISE_EVENT_TIMER
-
-	call	SystemTimer_ReadNanosecsToDXAXfromPreciseEventTimer
-	call	PrintNanosecsFromDXAX
+	call	AssumeVlbCycleTimeToAXfromBusCycleTimeInCX
+	call	GetBusClockRateFromCycleTimeInAX
+	mov		si, g_szVlbAssumption
+	call	PrintBusMeasurements
 
 	; Exit to DOS
@@ -42,13 +61,109 @@
 
 
-ALIGN JUMP_ALIGN
-PrintNanosecsFromDXAX:
-	mov		cx, 1000
-	div		cx					; AX = us waited
-	
+
+;--------------------------------------------------------------------
+; 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
-	ePUSH_T	cx, MICROSECONDS_TO_WAIT
+	push	cx
+	push	dx
 	push	ax
-	mov		si, g_szMicrosecsWaited
 	CALL_DISPLAY_LIBRARY FormatNullTerminatedStringFromCSSI
 	ret
@@ -59,8 +174,10 @@
 SECTION .data
 
-MICROSECONDS_TO_WAIT		EQU		7000
+OFFSET_TO_NEXT_WORD		EQU	32	; Must be at least 32 (Pentium Cache Line size)
 
-g_szMicrosecsWaited:
-	db	"Was supposed to wait %u us but actually waited %u us.",LF,CR,NULL
+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
 
