; -----------------------------------------------------------------------------
; CACHETST.ASM Processor Cache Size Test Low-level Routines	  Version 1.18
;
; Part of TMi0SDGL(tm) CPU/FPU Detection Library
; Copyright(c) 1995 by B-coolWare. Written by Bobby Z.
; -----------------------------------------------------------------------------
; Idea derived from Norbert Juffa's COMPTEST - PC Hardware Test program,
; public domain version 2.60.
;
; getCacheSize result is valid only on 486s and higher CPUs, because 386s
; usually got no cache on chip at all. On 386s it will detect external cache
; size if it's below 64K or will return 64K size.
; known problem: this method doesn't return correct result under multitasking
; environments like Windows or DESQview. The reason is unknown.

	INCLUDE HEADER.ASH

	.386

	.CODE

	PUBLIC	getCacheSize	; exported function

StartTimer	macro		; start hardware timer 2 at max rate
	in	al,61h
	and	al,0FCh
	out	61h,al
	mov	al,0B4h
	out	43h,al
	clr	al,al
	out	42h,al
	jmp	$+2
	out	42h,al
	in	al,61h
	or	al,01h
	out	61h,al
	endm

StopTimer	macro		; stop timer 2 and get clock count
	in	al,42h
	mov	bl,al
	in	al,42h
	mov	bh,al
	neg	bx
	in	al,61h
	and	al,0FDh
	out	61h,al
	endm


TestMemThroughput	proc	near
; writes the same memory block twice, measuring time elapsed for second block
; write. If the whole block fits internal cache, second time write is performed
; very fast. If it doesn't - we get significant speed dropdown (more than 0.5
; times less thruput than previous block).

	mov	es,ax
	mov	ds,ax
	clr	si
	clr	di	
	mov	cx,dx
	rep	movsd
	clr	si
	clr	di
	mov	cx,dx
	StartTimer
	rep	movsd
	StopTimer
	ret
	endp

TestCache   proc near
; performs memory thruput test with blocks of various sizes: 1K, 2K, 4K, 8K,
; 16K, 32K and 64K and saves timings in array.
	pushf
	push	ds
	cli
	cld
	mov	ax,1000h	; safe memory location
	mov	dx,256
	call	TestMemThroughput
	mov	cs:[Time1K],bx
	mov	ax,1000h
	mov	dx,512
	call	TestMemThroughput
	mov	cs:[Time2K],bx
	mov	ax,1000h
	mov	dx,1024
	call	TestMemThroughput
	mov	cs:[Time4K],bx
	mov	ax,1000h
	mov	dx,2048
	call	TestMemThroughput
	mov	cs:[Time8K],bx
	mov	ax,1000h
	mov	dx,4096
	call	TestMemThroughput
	mov	cs:[Time16K],bx
	mov	ax,1000h
	mov	dx,8192
	call	TestMemThroughput
	mov	cs:[Time32K],bx
	mov	ax,1000h
	mov	dx,16384
	call	TestMemThroughput
	mov	cs:[Time64K],bx
	pop	ds
	popf
	ret
	endp

Time1K      DW      ?
Time2K      DW      ?
Time4K      DW      ?
Time8K      DW      ?
Time16K     DW      ?
Time32K     DW      ?
Time64K     DW      ?

getCacheSize	proc far
; Pascal: 
;        function getCacheSize : Word; far; external;
; C/C++:
;        extern far word getCacheSize(void);
; Assembler:
;        EXTRN	getCacheSize : FAR
;
; returns first level data cache size in kilobytes.

	call	TestCache
	clr	bx
	inc	bx
	lsi	Time2K
@@1:
	shl	bx,1		; cache size in kilobytes
	mov	ax,_wp cs:[si]
	mov	dx,_wp cs:[si-2]
	shl	dx,1
	sub	ax,dx		; compare timings
	jb	@@next
	shr	dx,1
	shr	dx,1
	sub	ax,dx
	test	ax,8000h	; negative value?
	jz	@@found		; if no then previous block size fits in cache
@@next:
	add	si,2
	cmp	si,offset Time64k
	jbe	@@1
@@found:
	mov	ax,bx		; bx holds cache size in kilobytes.
	shr	ax,1		; cache size before dropdown
	ret
	endp

	END
