summaryrefslogtreecommitdiffstats
path: root/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/EXPLASM/DIVC.C
diff options
context:
space:
mode:
authorSven Eisenhauer <sven@sven-eisenhauer.net>2023-11-10 15:11:48 +0100
committerSven Eisenhauer <sven@sven-eisenhauer.net>2023-11-10 15:11:48 +0100
commit33613a85afc4b1481367fbe92a17ee59c240250b (patch)
tree670b842326116b376b505ec2263878912fca97e2 /Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/EXPLASM/DIVC.C
downloadStudium-33613a85afc4b1481367fbe92a17ee59c240250b.tar.gz
Studium-33613a85afc4b1481367fbe92a17ee59c240250b.tar.bz2
add new repoHEADmaster
Diffstat (limited to 'Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/EXPLASM/DIVC.C')
-rw-r--r--Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/EXPLASM/DIVC.C213
1 files changed, 213 insertions, 0 deletions
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 <stdio.h>
+#include <stdlib.h>
+
+#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>\n" );
+ printf( "Generates optimal ARM code for divide-by-constant\n" );
+ printf( "where <n> 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 );
+}