📄 divc.c
字号:
/* Optimal divide-by-contstant code generator
Advanced RISC Machines
*/
#include <stdio.h>
#include <stdlib.h>
#define BANNER "generated by Thumb divc 1.00 (Advanced RISC Machines)"
#define DATE "4 Apr 95"
#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( "\tLSR a1, #1\n");
while( logd < 32 )
{
printf( "\tLSR a3, a1, #%d\n", logd );
printf( "\tADD a1, a3\n");
logd <<= 1;
}
printf( "\tLSR a1, #%d\n", logmsb - 1);
}
void dodiv2p1( uint logd, uint logmsb )
{
/* Output instructions to do division by 2^n + 1 */
printf( "\tLSR a3, a1, #%d\n", logd);
printf( "\tSUB a1, a3\n");
while( logd < 16 )
{
logd <<= 1;
printf( "\tLSR a3, a1, #%d\n", logd );
printf( "\tADD a1, a3\n");
}
printf( "\tLSR a1, #%d\n", logmsb);
}
void divideby2( uint type, uint n, uint lsb, uint msb )
{
uint loglsb;
uint logmsb;
loglsb = log2( lsb );
logmsb = log2( msb );
printf( "; %s [%s]\n\n", BANNER, DATE );
printf( "\tCODE16\n\n" );
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" );
if (n > 255) {
fprintf(stderr, "Constant %d too big for divc to handle", n);
exit(1);
}
printf( "\tMOV a2, a1\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( "\tASL a3, a1, #%d\n", logmsb - loglsb );
printf( "\tSUB a3, a3, a1\n");
break;
case DIVIDE_BY_2_PLUS_1:
dodiv2p1( logmsb - loglsb, logmsb );
/* Now do multiply-by-n */
printf( "\tASL a3, a1, #%d\n", logmsb - loglsb );
printf( "\tADD a3, a1\n" );
break;
}
/* Subtract from adjusted original to obtain remainder */
printf( "\tASL a3, #%d\n", loglsb);
printf( "\tSUB a2, a3\n");
/* Apply corrections */
printf( "\tCMP a2, #%d\n", n);
printf( "\tBLT %%FT0\n");
printf( "\tADD a1, #1\n");
printf( "\tSUB a2, #%d\n", n);
printf( "0\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, #%d\n", n);
printf( "\tBLT %%FT0\n");
printf( "\tADD a1, #1\n");
printf( "\tSUB a2, #%d\n", n);
printf( "0\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 );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -