The purpose of this programming assignment is to learn more SPARC assembly and Standard C Library routines, implement recursion in assembly using the Stack for local variables, use bit masks / bit manipulations, and become more familiar with the IEEE floating point internal representation. Some of the new Standard C Library routines we will use are getopt(), strchr(), strtod(), and strtok_r().
Let's take a look at the help/usage first:
% pa3 Usage: pa3 [-ebh] [-i input_base] [-o output_base(s)] -f input_file | numbers -e -- print number in english -b -- print floating point number (if floating point) in binary -h -- this help/usage message % pa3 -h Usage: pa3 [-ebh] [-i input_base] [-o output_base(s)] -f input_file | numbers -e -- print number in english -b -- print floating point number (if floating point) in binary -h -- this help/usage message
A pa3test is in ~/../public to see how to handle invalid input and for you to check all your valid output. Be sure to test for an invalid base argument to printBase() in printBase.s and include a test for this in your README.
In this assignment you will learn to use recursion and also learn about binary floating point representation. Your program will read numbers from the command line and print them back in several formats as specified below .
It is helpful to start with an example output. (More example
outputs are at the end of this document.)
% pa3 -e 932
932
nine three two
% pa3 -e -o 2,8,10,16 932
932
nine three two
0000 0000 0000 0000 0000 0011 1010 0100
01644
932
0x3A4
With floating point numbers as input this is the sample output.
% pa3 -eb 23.005
23.005
two three dot zero zero five
0 10000011 01110000000101000111101
The sample outputs shown above indicate that your program will read numbers as command line input or from a file (-f option) and print them in specific formats.
Structure of the program
The main.c program will read in the strings from command line or from a file and convert them to integers (using strtol()) or floating point numbers (using strtod()) depending on whether there is a '.' in the command line string. Once read, the respective numbers are processed based on the command line options.
Here is a listing of all the routines that you will be writing:
Note: printEnglish() and printBase() must be implemented using recursion and must use local variable space allocated on the stack to keep track of the right-most digit as outlined in Discussion Section and class. There are many ways to implement these functions. But for this programming assignment, you must implement them as outlined. Recursion and local stack variables in assembly are part of the main purpose of this programming assignment.
Here is my pa3.h
#ifndef PA3_H #define PA3_H #define E_FLAG 0x01 #define I_FLAG 0x02 #define O_FLAG 0x04 #define B_FLAG 0x08 #define H_FLAG 0x10 #define F_FLAG 0x20 #define MIN_BASE 2 #define MAX_BASE 36 void printEnglish( long num ); void printIntBinary( long num ); void printFPBinary( float num ); void printBase( long num, int base ); int checkRange( long value, long minRange, long maxRange ); void processIntToken( char *token, int ibase, int obase, unsigned int mode); void processFPToken( char *token, unsigned int mode ); #endif /* PA3_H */
(void) setvbuf( stdout, NULL, _IONBF, 0 );The function main() uses getopt() (see man -s3c getopt) to read the command line arguments and set mode bits to specify how the input numbers should be processed. [Note: As stated in the getopt man page, to delimit the end of the options, use "--" (two hyphens) on the command line; this is especially helpful in delimiting non-option arguments that begin with "-" (like negative numbers your program should process).] Then parse the line of numbers as command line input or lines from a file if the -f option is given with strtok_r(). Determine whether each number/token is an integer or FP number. It is easy to distinguish between an integer and a floating point number with the presence of the decimal point. So all you need to do is parse each argument to check for the '.' character. This is done using strchr() (see man strchr). The character '.' is passed as the second argument to strchr() which then returns a pointer (call it dotPtr) to the first occurrence of a decimal point. If there is no decimal point strchr() returns a NULL pointer. This will be used to separate your code into two sections -- one that has code to deal with integers using input and output base options using processIntToken() and another that has the code to deal with floating point numbers with processFPToken().
processIntToken() will deal with processing each integer token by converting it to a long int according to the input base (strtol() with base 10 as the default base), call printEnglish() if -e option was specified, call printIntBinary() if the output base is 2 otherwise printBase() with the specified output base (base 10 default).
Likewise, processFPToken() will deal with processing each floating point token by converting it to a double (strtod()), call printEnglish() if the -e option was specified. printEnglish() takes only a long as input -- so you don't have a separate printEnglish() for FP numbers. Instead, to print the FP numbers in English, you call printEnglish() twice, once with the integer part to the left of the decimal and again with the part to the right of the decimal point. To do this you need to make use of the fact that dotPtr points to the location of the decimal point. Since strtol() terminates the conversion to a long the moment it sees a null character, you can slam a nul character in place of the decimal point (*dotPtr == '\0') and then pass argv[i] to strtol(). If the -b option was specified, output the floating point value as binary with the sign bit, exponent bits, and mantissa bits separated by a space.
Note: We are dealing with single precision fixed point numbers only. Your code does not have to account for numbers like 3.45E-3 which deals with exponents.
Dealing with Special Cases
In the case of FP numbers like -0.005, passing the portions on either side of
the decimal point to strtol() will not work. This is because strtol() ignores
the leading zeros in a long and also interprets -0 to be 0. Hence the checking
for a negative sign should be done before calling strtol(). To deal with the
leading zeros after the decimal, you again use dotPtr -- advancing it by one
and printing "zero" while there are zeros before a non-zero character. This can
be done in a while loop:
while (*(++dotPtr) == '0')
(void) printf("zero ");
strtol(dotPtr, &endptr, 10);
Relevant C library functions
Some addition examples of output:
Binary Floating Point Representation
The internal 32-bit representation for single precision floating point numbers is as follows:
Bit 31 represents the sign bit
The next 8 bits (30-23) represent the stored exponent
The last 23 bits (22-0) represent the mantissa or fractional part.
Use the Makefile-PA3 in the public directory. Copy the public/Makefile-PA3 to your pa3/Makefile. Turnin a README file following the guidelines of previous programming assignments. You should know the routine by now.
Extra Credit: Early Turnin and The Unit Tests
1 point for early turnin at least 24 hours in advance.
2 points for early turnin at least 48 hours in advance.
3 points for unit testing for any three print or process functions besides
printIntBinary() (this test case is given).
The unit test for:
You are given testprintIntBinary.c in the public dir.
The public Makefile-PA3 has the rules for just testprintIntBinary and runtestprintIntBinary unit test. Add the rules for the other unit tests similar to what was done in the PA1 and PA2 Makefiles.
Grading Breakdown
The Grading Breakdown is similar to that of PA1.