//----------------------------------------------------------------
//	divtest.cpp
//
//	This program uses "inline assembly language" to test our
//	external 'softdivb' software division function, based on
//	comparing all its possible outputs with results produced
//	when the same divisions are performed in hardware.  Note 
//	that we have installed signal-handlers here to catch the 
//	SIGSEGV and SIGFPE signals which are generated by divide
//	overflow exceptions and software interrupt instructions, 
//	respectively.  Our signal-handler counts the type of the
//	signal and advances the return-address past the two-byte
//	instruction which caused the fault to avoid retriggering
//	the very same signal all over again in an infinite loop.
// 
//	 compile using: $ g++ divtest.cpp softdivb.s -o divtest
//
//	programmer: ALLAN CRUSE
//	written on: 12 OCT 2003
//	revised on: 15 MAR 2006 -- incorporated signal-handlers
//	revised on: 15 MAR 2009 -- for x86_64 Linux environment
//----------------------------------------------------------------


#include <stdio.h>		// needed for printf()
#include <signal.h>		// needed for signal()


extern void softdivb( void );	// function symbol


unsigned int	a, b, c, d;	// global variables
int	signaled, segv, fpe;	// shared variables


void upon_signal( int signo )
{
	// update our counters 

	++signaled;
	if ( signo == SIGFPE ) ++fpe;
	if ( signo == SIGSEGV ) ++segv;

	// then adjust the image of the RIP register which the
	// Linux operating system will have saved on our stack 
	// so that on return from this signal-handler we shall
	// avoid encountering the very same instruction again
	// (Here we use the fact that the Pentium's 'divb %bl' 
	// and 'int $0' instructons are each 2-bytes in size)

	unsigned long	*tos; 
	asm(" mov %%rbp, %0 " : "=m" (tos) :: );
	tos[23] += 2;
}


int main( void )
{
	// install signal-handlers for SIGFPE and SIGSEGV 
	signal(  SIGFPE, upon_signal ); 
	signal( SIGSEGV, upon_signal ); 

	// display output legend
	printf( "\n---a--- --b--  hardware  software \n" );
	
	// iterate over every possible combination 
	// of an 8-bit divisor and a 16-bit dividend  
	int	errors = 0;
	for (b = 0; b < 256; b++)
		{
		for (a = 0; a < 65536; a++)
			{
			signaled = 0;
			c = d = 0;
			asm(" movw a, %ax ");
			asm(" movb b, %bl ");
			asm(" divb %bl ");
			asm(" movw %ax, d ");
			asm(" movw a, %ax ");
			asm(" movb b, %bl ");
			asm(" call softdivb ");
			asm(" movw %ax, c ");
			printf( " a=%04X  b=%02X ", a, b );
			printf( "  c=%04X    d=%04X ", c, d );

			if ( ( signaled &&( signaled == 2 ))
			    ||( !signaled &&( c == d )) ) 
				{
				printf( "     ok \n" );
				}
			else	{
				++errors;
				printf( "<--- error #%d \n", errors );
				}
			}
		}
	
	printf( "\n\tNumber of errors = %d \n\n", errors );
}