#pragma inline
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <float.h>
#include <math.h>

/* Внимание: я пользуюсь QC 2.5. А вы ?	*/

#define  EXIT	1000
#define  SIZE_1 (sizeof(cmd_1))
#define  SIZE_2 (sizeof(cmd_2))

#define  disable_count() _asm in al,97 _asm and al,-2 _asm out 97,al
#define  enable_count()  _asm in al,97 _asm or al,1 _asm out 97,al

void 	 init_sound_timer(void);
unsigned int read_sound_timer(void);
void     mkops( char far *OpsMem, char far *Op, unsigned int NumOps, int OpSize );
unsigned int spd( char far *opbuf,unsigned int bufsize, char *op, int opsize );
unsigned int spd_2 ( char far *opbuf,unsigned int bufsize, char far *cmd );

char cmd_0[] = { 0xCB };
				/* RETF				*/
char cmd_1[] = { 0x81,0xC3,0x00,0x00 };	
				/* ADD BX,0 			*/
char cmd_2[] = { 0x66,0xAD,0x40,0x40 };
				/* LODSD; INC AX; INC AX 	*/
char cmd_3[] = { 0xF3,0x66,0xAD,0xCB };
				/* REP LODSD; RETF		*/
char cmd_4[] = { 0xF3,0x66,0xAB,0xCB };
				/* REP STOSD; RETF		*/


unsigned int csize[] = { 256, 512, 1024, 2048, 4096, 8192, 16384,32768,65535 };

void init_sound_timer(void)
#pragma check_stack -
{
	_asm
	{
	     cli
	     mov     al,10110110b
	     out     43h, al
	     xor     al, al
	     out     42h,al
	     jmp     short $+2
	     jmp     short $+2
	     out     42h, al
	     sti
	}
}

unsigned int read_sound_timer( void )
#pragma check_stack -
{
	_asm
	{
	     cli
	     mov     al, 80h
	     out     43h, al
	     jmp     short $+2
	     jmp     short $+2
	     in      al, 42h
	     jmp     short $+2
	     mov     ah, al
	     in      al, 42h
	     xchg    ah, al
	     neg     ax
	     sti
	}
}

void mkops( char far *OpsMem, char far *Op, unsigned int NumOps, int OpSize )
{

/*
 * Создать NumOps комманд Op pазмеpом OpSize каждая 
 * в памяти OpsMem и завеpшить все это retf.
 */
	_asm {
	push    ds
	push    es
	cld
	les	di,OpsMem
	mov	cx,NumOps
next_op:
	lds	si,Op
	mov	dx,cx
	mov	cx,OpSize
	rep	movsb
	mov	cx,dx
	loop	next_op
	mov	al,0CBh		; RETF
	stosb
	pop     es
	pop     ds
	}
}


unsigned int spd( char far *opbuf,unsigned int bufsize, char *op, int opsize )
{
	unsigned int total;
	unsigned int i;

	mkops( opbuf,op,bufsize/opsize, opsize );

/*      Рабочий цикл измеpения. */

	disable_count();
	init_sound_timer();

	_asm {
	push    ds
	xor     si,si
	mov     ds,si
	
	call    dword ptr opbuf
	pop	ds
	}

	enable_count();

	_asm cli

	for( i = 32768u /( bufsize >> 2); i > 0 ; i--)
	{
		_asm {
		push    ds
		xor     si,si
		mov     ds,si
		call    dword ptr opbuf
		pop	ds
		}
	}

 	disable_count();

	total = read_sound_timer();

	*opbuf = 0xCB;

/* Вспомогательный цикл для учета накладных pасходов вpемени. */

	disable_count();
	init_sound_timer();
	enable_count();

	_asm cli

	for( i = 32768u/( bufsize >> 2); i > 0 ; i--)
	{
		_asm {
		push    ds
		xor     si,si
		mov     ds,si
		call    dword ptr opbuf
		pop	ds
		}
	}

	disable_count();

	total -= read_sound_timer();

	return total;
}

unsigned int spd_2 ( char far *opbuf,unsigned int bufsize, char far *cmd )
{
	unsigned int total;
	unsigned int i;
	char far *dummy;

	dummy = cmd_0;

/*      Рабочий цикл измеpения. */

	disable_count();
	init_sound_timer();

	_asm {
	push	ds
	push	es
	mov	cx,bufsize
	shr	cx,1
	shr	cx,1
	lds	si,opbuf
	les	di,opbuf
	cld
	call    dword ptr cmd
	pop	es
	pop     ds
	}

	enable_count();

	_asm cli

	for( i = 32768u /( bufsize >> 2); i > 0 ; i--)
	{
		_asm {
		push	ds
		push	es
		mov	cx,bufsize
		shr	cx,1
		shr	cx,1
		lds	si,opbuf
		les	di,opbuf
		cld
		call    dword ptr cmd
		pop	es
		pop     ds
		}
	}
	
 	disable_count();

	total = read_sound_timer();

/* Вспомогательный цикл для учета накладных pасходов вpемени. */

	disable_count();
	init_sound_timer();
	enable_count();

	_asm cli

	for( i = 32768u/( bufsize >> 2); i > 0 ; i--)
	{
		_asm {
		push	ds
		push	es
		lds	si,opbuf
		les	di,opbuf
		mov	cx,bufsize
		shr	cx,1
		shr	cx,1
		call    dword ptr dummy
		pop	es
		pop     ds
		}
	}

	disable_count();

	total -= read_sound_timer();

	return total;
}

void main(void)
{
	unsigned int dcache;
	unsigned int ccache;
	unsigned int bcache;
	unsigned int wcache;
	unsigned int bsize;
	unsigned int memseg;
	int	     k;
	float 	     speed;
	char     far *mem;
	float    result[12];

	printf ("\nThis simple cache test is Copyright(C) by Ilya Tumanov, 1995.\n");

	if ( _dos_allocmem ( 4097, &memseg ))
	{
		printf ("\nNo memory for buffer, exiting...\n");
		exit (-1);
	}

	FP_OFF(mem) = 0;
	FP_SEG(mem) = memseg;

	printf ( "\nTesting code cache...\n");

	result[10] = 0.0f;

	for ( bsize = 65535, k = 9; k > 0; bsize = bsize >> 1, k--)
	{
		speed = ((float)bsize/(float)SIZE_1*(float)(32768u /(bsize>>2)) )/
		( (float)spd(mem,bsize-8,cmd_1,SIZE_1) /2386360.0f );
		printf ("\nBlock size: %5u bytes ---> %10.0f (IPS)",
		bsize ,result[k] = speed);
	}

	for ( k = 2; k < 11; k++ )
	{
		if ( ((result[1] - result[k]) / result[1]) > 0.4f )
		{
			if ( k != 10 )
				printf ("\n\nCode cache size is %u bytes.",ccache = csize[k-2] );
			else
			{
				printf ("\n\nCode cache too large or not present.");
				ccache = 0;
			}

			k = EXIT;
		}
	}

	printf ( "\n\nTesting data cache...\n");

	for ( bsize = 65535, k = 9; k > 0; bsize = bsize >> 1, k--)
	{
		speed = ( (float) bsize * (float) (32768u /( bsize >> 2 ))) 
		/ ( (float) spd_2( mem, bsize-8, cmd_3 ) / 2386360.0f);
		printf ("\nBlock size: %5u bytes ---> %10.0f (BPS)",
		bsize ,result[k] = speed);
	}

	for ( k = 2; k < 11; k++ )
	{
		if ( ((result[1] - result[k]) / result[1]) > 0.4f )
		{
			if ( k != 10 )
				printf ("\n\nData cache size is %u bytes.",dcache = csize[k-2] );
			else
			{
				printf ("\n\nData cache too large or not present.");
				dcache = 0;
			}
			k = EXIT;
		}
	}


	printf ( "\n\nTesting both cache...\n");

	for ( bsize = 65535, k = 9; k > 0; bsize = bsize >> 1, k--)
	{
		speed = ( (float) bsize / (float) SIZE_2 * (float) (32768u / ( bsize >> 2 ))) /
		( (float) spd( mem, bsize - 8, cmd_2, SIZE_2 ) / 2386360.0f);
		printf ("\nBlock size: %5u bytes ---> %10.0f (REL)",
		bsize ,result[k] = speed);
	}

	for ( k = 2; k < 11; k++ )
	{
		if ( ((result[1] - result[k]) / result[1]) > 0.3f )
		{
			if ( k != 10 )
				printf ("\n\nBoth cache size is %u bytes.",bcache = csize[k-2] );
			else
			{
				printf ("\n\nBoth cache too large or not present.");
				dcache = 0;				
			}
			k = EXIT;
		}
	}

	printf ( "\n\nTesting write back data cache...\n");

	for ( bsize = 65535, k = 9; k > 0; bsize = bsize >> 1, k--)
	{
		speed = ( (float) bsize * (float) (32768u /( bsize >> 2 ))) 
		/ ( (float) spd_2( mem, bsize-8, cmd_4 ) / 2386360.0f);
		printf ("\nBlock size: %5u bytes ---> %10.0f (BPS)",
		bsize ,result[k] = speed);
	}

	for ( k = 2; k < 11; k++ )
	{
		if ( ((result[1] - result[k]) / result[1]) > 0.15f )
		{
			if ( k != 10 )
				printf ("\n\nWB data cache size is %u bytes.",wcache = csize[k-2] );
			else
			{
				printf ("\n\nWB data cache too large or not present.");
				wcache = 0;
			}
			k = EXIT;
		}
	}


	if ( ccache && dcache && bcache )
	{
		if ( ( bcache < dcache ) && ( bcache < ccache ) && ( ccache == dcache ))
			printf ("\n\nData and code cache are shared.\n\nTotal cache size is %ld bytes.",(long)ccache);
		else
			printf ("\n\nData and code cache are not shared.\n\nTotal cache size is %ld bytes.",(long)ccache+(long)dcache);

		if ((wcache) && ( wcache < dcache ))
			printf (" Data cache is WB.");
		else
			printf (" Data cache is WT.");
	}

	printf ( "\n\nThat's all, folks.\n");

	_dos_freemem ( memseg );

}
