From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001 From: Sven Eisenhauer Date: Fri, 10 Nov 2023 15:11:48 +0100 Subject: add new repo --- .../ARM202U/EXAMPLES/EXPLASM/DIVC.C | 213 +++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/EXPLASM/DIVC.C (limited to 'Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/EXPLASM/DIVC.C') diff --git a/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/EXPLASM/DIVC.C b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/EXPLASM/DIVC.C new file mode 100644 index 0000000..3a5d85c --- /dev/null +++ b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/EXPLASM/DIVC.C @@ -0,0 +1,213 @@ +/* Optimal divide-by-contstant code generator + Advanced RISC Machines +*/ + + +#include +#include + +#define BANNER "generated by divc 1.02 (Advanced RISC Machines)" +#define DATE "27 Jan 94" + +#define DIVIDE_BY_2_MINUS_1 0 +#define DIVIDE_BY_2_PLUS_1 1 + + +typedef unsigned int uint; + + +uint log2( uint n ) +{ + uint bit, pow, logn; + + for( bit = 0, pow = 1; bit < 31; bit++, pow <<= 1 ) + { + if( n == pow ) logn = bit; + } + + return( logn ); +} + + +int powerof2( int n ) +{ + return( n == ( n & (-n) ) ); +} + + +void dodiv2m1( uint logd, uint logmsb ) +{ + /* Output instructions to do division by 2^n - 1 */ + printf( "\tMOV a1, a1, lsr #1\n" ); + + while( logd < 32 ) + { + printf( "\tADD a1, a1, a1, lsr #%d\n", logd ); + logd <<= 1; + } + printf( "\tMOV a1, a1, lsr #%d\n", logmsb - 1 ); +} + + +void dodiv2p1( uint logd, uint logmsb ) +{ + /* Output instructions to do division by 2^n + 1 */ + printf( "\tSUB a1, a1, a1, lsr #%d\n", logd ); + + while( logd < 16 ) + { + logd <<= 1; + printf( "\tADD a1, a1, a1, lsr #%d\n", logd ); + } + + printf( "\tMOV a1, a1, lsr #%d\n", logmsb ); +} + + +void loada4( uint type, uint lsb, uint msb ) +{ + /* Constant is too big to be used as an immediate constant, */ + /* so load it into register a4. */ + printf( "\tMOV a4, #0x%x\n", msb ); + + switch( type ) + { + case DIVIDE_BY_2_MINUS_1: + printf( "\tSUB a4, a4, #0x%x\n", lsb ); + break; + + case DIVIDE_BY_2_PLUS_1: + printf( "\tADD a4, a4, #0x%x\n", lsb ); + break; + + default: + fputs( "Internal error", stderr ); + } +} + + +void divideby2( uint type, uint n, uint lsb, uint msb ) +{ + uint loglsb; + uint logmsb; + uint usinga4; + + loglsb = log2( lsb ); + logmsb = log2( msb ); + + printf( "; %s [%s]\n\n", BANNER, DATE ); + printf( "\tAREA |div%d$code|, CODE, READONLY\n\n", n ); + printf( "\tEXPORT udiv%d\n\n", n ); + printf( "udiv%d\n", n ); + printf( "; takes argument in a1\n" ); + printf( "; returns quotient in a1, remainder in a2\n" ); + printf( "; cycles could be saved if only divide or remainder is required\n" ); + + usinga4 = ( n >> loglsb ) > 255; + if( usinga4 ) + { + loada4( type, lsb, msb ); + printf( "\tSUB a2, a1, a4\n" ); + } + else + { + printf( "\tSUB a2, a1, #%d\n", n ); + } + + /* 1/n as a binary number consists of a simple repeating pattern */ + /* The multiply by 1/n is expanded as a sequence of ARM instructions */ + /* (there is a rounding error which must be corrected later) */ + switch( type ) + { + case DIVIDE_BY_2_MINUS_1: + dodiv2m1( logmsb - loglsb, logmsb ); + /* Now do multiply-by-n */ + printf( "\tRSB a3, a1, a1, asl #%d\n", logmsb - loglsb ); + break; + + case DIVIDE_BY_2_PLUS_1: + dodiv2p1( logmsb - loglsb, logmsb ); + /* Now do multiply-by-n */ + printf( "\tADD a3, a1, a1, asl #%d\n", logmsb - loglsb ); + break; + + default: + fputs( "Internal error", stderr ); + } + + /* Subtract from adjusted original to obtain remainder */ + printf( "\tSUBS a2, a2, a3, asl #%d\n", loglsb ); + + /* Apply corrections */ + printf( "\tADDPL a1, a1, #1\n" ); + if( usinga4 ) + { + printf( "\tADDMI a2, a2, a4\n" ); + } + else + { + printf( "\tADDMI a2, a2, #%d\n", n ); + } + + /* Additional test required for divide-by-3, as result could be */ + /* off by 2 lsb due to accumulated rounding errors. */ + if( n == 3 ) + { + printf( "\tCMP a2, #3\n" ); + printf( "\tADDGE a1, a1, #1\n" ); + printf( "\tSUBGE a2, a2, #3\n" ); + } + + printf( "\tMOV pc, lr\n\n" ); + printf( "\tEND\n" ); +} + + +int main( int argc, char *argv[] ) +{ + if( argc != 2 ) + { + printf( "Usage: divc \n" ); + printf( "Generates optimal ARM code for divide-by-constant\n" ); + printf( "where is one of (2^n-2^m) or (2^n+2^m) eg. 10\n" ); + printf( "Advanced RISC Machines [%s]\n", DATE ); + } + else + { + int num; + + num = atoi( argv[ 1 ] ); + if( num <= 1 ) + { + fprintf( stderr, "%d is not sensible\n", num ); + } + else + { + uint lsb = 1; + + /* find least-significant bit */ + while( ( num & lsb ) == 0 ) + { + lsb <<= 1; + } + + if( powerof2( num ) ) + { + fprintf( stderr, "%d is an easy case\n", num ); + } + else if( powerof2( num + lsb ) ) + { + divideby2( DIVIDE_BY_2_MINUS_1, num, lsb, num + lsb ); + } + else if( powerof2( num - lsb ) ) + { + divideby2( DIVIDE_BY_2_PLUS_1, num, lsb, num - lsb ); + } + else + { + fprintf( stderr, "%d is not one of (2^n-2^m) or (2^n+2^m)\n", num ); + } + } + } + return( 0 ); +} -- cgit v1.2.3