Programming Assignment Two: PA2


Due Thursday, Feb. 16 @ 11:59pm

The UCSD Theatre and Dance department needs our help to manage a bank of 64 stage lights just installed. You will write a program that will allow the light control person to input commands to set, clear, toggle, shift, rotate, and ripple lighting patterns across this bank of 64 stage lights.

[Note: It would be better to not assume we have only 2 banks of lights, but for various reasons this assignment would be more difficult (especially combining C and Assembly). So for the purposes of this assignment, we can assume we have only 2 banks of lights.]

The purpose of this assignment is to learn more SPARC assembly and become familiar with more useful Standard C Library routines. In particular, we will use load and store instructions to access memory locations, allocate local variables on the runtime stack, perform 64-bit operations across two 32-bit registers maintaining this data in an array in two ints in memory, convert some C code you wrote in PA1 into equivalent assembly routines in PA2 (strToLong.c --> strToULong.s), access global data across C and assembly, read input from either stdin or a file, and just lots of good C Preprocessor and C language uses.

We will use Standard C Library routines to open [fopen()] and read lines of character data [fgets()] from a disk file or the keyboard, tokenize the input data [strtok()], and convert/parse data/commands to be used in this system. Some of the Standard C Library routines we called from C in PA1 we will use/call from assembly this time [strtoul(), fprintf(), perror()].

Grading Breakdown

README: 10 points
Compiling (our Makefile; no warnings): 10 points
Style [See pa1 "Code Style"] (Including Comments); 30 points
Correctness: 50 points

-10 points (minus 10 points) for each module written in the wrong language (C vs. Assembly and vice versa)
Optimization: 5 Points
Must correctly pass at least 70% of test cases to be eligible for Extra Credit
Filling delay slots with useful instructions (eliminating nops)
At least 80% of nops need to be filled to get full "filling delay slots" Extra Credit

Strategy

The function prototypes for the various C and Assembly functions are as follows:

C routines

/* int main( int argc, char *argv[] ); */
int checkCmd( const char * const cmdString, const char * const commands[] );

Assembly routines:

unsigned long strToULong( const char* str, const int base );
void displayLights( const unsigned int lightBank[] );
void set( unsigned int lightBank[], const unsigned int bank0Pattern, const unsigned int bank1Pattern );
void clear( unsigned int lightBank[], const unsigned int bank0Pattern, const unsigned int bank1Pattern );
void toggle( unsigned int lightBank[], const unsigned int bank0Pattern, const unsigned int bank1Pattern );
void shift( unsigned int lightBank[], const int shiftCnt );
void rotate( unsigned int lightBank[], const int rotateCnt );
void ripple( unsigned int lightBank[], const int rippleCnt );
/* void printChar( const char ch ); */

In addition, a sample pa2.h file is available in ~/../public/pa2.h. The discussion sections will go over some of the more complex constructs found in pa2.h.

Here is some information on the various Standard C Library functions (be sure to read the man pages for more info):

The C routines
main.c
int main( int argc, char *argv[] );
This is the main program driver. Check for a filename on the command line and open it (fopen()) if there is one. Otherwise read input from stdin. Output usage message if argc > 2. Loop reading a line at a time (fgets()) parsing each line (strtok()) for a command (set, clear, toggle, shift, rotate, ripple, help, quit) and zero, one, or two arguments to these commands depending on the command. The arguments to these bit commands will need to be converted to unsigned long ints (strToULong()). Output appropriate error messages per the pa2test. Ignore extra arguments to these command (no error message).

The two banks of lights are stored in an array of two 32-bit unsigned ints. They are all initially off (0).

Note: Put the following line as one of the first statements in your main(). This will make the output to your terminal and output redirected to an output file look the same with stdout and stderr output intermixed properly. This statement turns off buffering in stdout so the stdout stream is unbuffered just as stderr is.

(void) setvbuf( stdout, NULL, _IONBF, 0 );

Display the initial bank of lights (all off). If reading input interactively from the keyboard (stdin), display the prompt ("> "). If reading input from a file, do not display the prompt. See the example output at the end of the write-up and pa2test.

checkCmd.c
int checkCmd( const char * const cmdString, const char * const commands[] );
This function checks to see if the first token on the line passed as cmdString is one of the bit commands defined for this lightboard controlling program listed in commands. Returns the index associated with that command (index of the command string in commands or -1 if there was no match. Only lowercase commands will be accepted.

The Assembly routines
strToULong.s
unsigned long strToULong( const char* str, const int base );

This function is pretty much a direct translation of your strToLong.c from PA1 into assembly except we will call strtoul() instead of strtol(). Using a base of 0 allows us to enter either octal (leading 0), decimal (leading 1-9), or hexadecimal (leading 0x) values for our bit patterns and shift/rotate/ripple counts. Use snprintf() and perror() for errno errors and fprintf() to stderr for *endptr errors. See the end of this write-up and the lecture notes on how to set and access errno and access stderr in assembly.
Note: You will need to allocate endptr and your errorString buffer on the stack as local variables.

displayLights.s
void displayLights( const unsigned int lightBank[] );

This function displays the light banks. '*' indicates the light is on; '-' indicates the light is off. Cycle through the 2 banks of lights using your printChar() from PA1 to display each light one at a time. Output a space char. (' ') between every four lights (bits). Be sure to terminate each line with a newline character -- printChar( '\n' );

set.s
void set( unsigned int lightBank[], const unsigned int bank0, const unsigned int bank1 );

This function sets the lights based on which bits are set in the bit patterns specified. bank0 represents the first 32 lights (bits). bank1 represents the second 32 lights (bits). You cannot use bset, bclr, or btog.

clear.s
void clear( unsigned int lightBank[], const unsigned int bank0, const unsigned int bank1 );

This function clears the lights based on which bits are set in the bit patterns specified. bank0 represents the first 32 lights (bits). bank1 represents the second 32 lights (bits). You cannot use bset, bclr, or btog.

toggle.s
void toggle( unsigned int lightBank[], const unsigned int bank0, const unsigned int bank1 );

This function toggles the lights based on which bits are set in the bit patterns specified. bank0 represents the first 32 lights (bits). bank1 represents the second 32 lights (bits). You cannot use bset, bclr, or btog.

shift.s
void shift( unsigned int lightBank[], const int shiftCnt );

This function shifts the current light patterns in the light banks by shiftCnt places. If the shiftCnt is positive, shift left; if the shiftCnt is negative, shift right. Only the lower 6 bits of the shiftCnt should be used for the shiftCnt.

rotate.s
void rotate( unsigned int lightBank[], const int rotateCnt );

This function rotates the current light patterns in the light banks by rotateCnt places. If the rotateCnt is positive, rotate left; if the rotateCnt is negative, rotate right. Only the lower 6 bits of the rotateCnt should be used for the rotateCnt.

ripple.s
void ripple( unsigned int lightBank[], const int rippleCnt );

This function rotates the current light patterns in the light banks by rippleCnt places displaying the light banks after each single bit rotate. If the rippleCnt is positive, rotate left; if the rippleCnt is negative, rotate right. The rippleCnt may be of any valid int value. Call displayLights() after each single bit rotate giving the ripple effect.

printChar.s
void printChar( const char ch );

This function is just your printChar() from PA1.

See the sample pa2test in ~/../public.

The Unit Tests

The unit test for this assignment are:

testcheckCmd.c
teststrToULong.c - very similar to pa1's but unsigned with base 0; test octal and hex
testdisplayLights.c - set lightBank[] and manually check output
testset.c
testclear.c
testtoggle.c
testshift.c
testrotate.c - given to you
testripple.c

You are given testrotate.c in the public dir.

The public Makefile-PA2 has the rules for all the unit tests similar to what was done in PA1.

Turn In, Due Thursday night, Feb. 16 @ 11:59pm

Start early and proceed with baby steps. Don't try to write the entire program all at once. Use small sample/test input data and files as you test/debug your program.

Use the turnin program to turn in the following:


Using stderr and errno in Assembly:

In main.c:

Define a ***global*** variable

	FILE *stdError = stderr;

A *global* variable is defined *above* main() and not inside the body of main().

Then in your Assembly file:

	set stdError, %o0
	ld  [%o0], %o0

Now stderr is in %o0 as the first argument for fprintf().

A symbol in assembly is an address. Therefore in assembly, the name of a
global variable is the address of where that variable has been allocated.

The same is true with the global variable errno defined in errno.h.


Example interactive mode with various error checks at the end.

% pa2
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- 
> help
The available commands are:
        set    bank0BitPattern bank1BitPattern
        clear  bank0BitPattern bank1BitPattern
        toggle bank0BitPattern bank1BitPattern
        shift  shiftCount
        rotate rotateCount
        ripple rippleCount
        help
        quit
> set 0x12345678 1
---* --*- --** -*-- -*-* -**- -*** *--- ---- ---- ---- ---- ---- ---- ---- ---* 
> toggle 1 037
---* --*- --** -*-- -*-* -**- -*** *--* ---- ---- ---- ---- ---- ---- ---* ***- 
> clear 0x20 10
---* --*- --** -*-- -*-* -**- -*-* *--* ---- ---- ---- ---- ---- ---- ---* -*-- 
> shift 9
-**- *--- *-*- **-- *-** --*- ---- ---- ---- ---- ---- ---- --*- *--- ---- ---- 
> shift -9
---- ---- --** -*-- -*-* -**- -*-* *--* ---- ---- ---- ---- ---- ---- ---* -*-- 
> toggle -1 0xF0F0F0F0
**** **** **-- *-** *-*- *--* *-*- -**- **** ---- **** ---- **** ---- ***- -*-- 
> rotate 8
**-- *-** *-*- *--* *-*- -**- **** ---- **** ---- **** ---- ***- -*-- **** **** 
> rotate -8
**** **** **-- *-** *-*- *--* *-*- -**- **** ---- **** ---- **** ---- ***- -*-- 
> ripple 6 
**** **** *--* -*** -*-* --** -*-- **-* ***- ---* ***- ---* ***- ---* **-- *--* 
**** **** --*- ***- *-*- -**- *--* *-** **-- --** **-- --** **-- --** *--* --** 
**** ***- -*-* **-* -*-- **-* --** -*** *--- -*** *--- -*** *--- -*** --*- -*** 
**** **-- *-** *-*- *--* *-*- -**- **** ---- **** ---- **** ---- ***- -*-- **** 
**** *--* -*** -*-* --** -*-- **-* ***- ---* ***- ---* ***- ---* **-- *--* **** 
**** --*- ***- *-*- -**- *--* *-** **-- --** **-- --** **-- --** *--* --** **** 
> ripple -6
**** *--* -*** -*-* --** -*-- **-* ***- ---* ***- ---* ***- ---* **-- *--* **** 
**** **-- *-** *-*- *--* *-*- -**- **** ---- **** ---- **** ---- ***- -*-- **** 
**** ***- -*-* **-* -*-- **-* --** -*** *--- -*** *--- -*** *--- -*** --*- -*** 
**** **** --*- ***- *-*- -**- *--* *-** **-- --** **-- --** **-- --** *--* --** 
**** **** *--* -*** -*-* --** -*-- **-* ***- ---* ***- ---* ***- ---* **-- *--* 
**** **** **-- *-** *-*- *--* *-*- -**- **** ---- **** ---- **** ---- ***- -*-- 
> shift 64
**** **** **-- *-** *-*- *--* *-*- -**- **** ---- **** ---- **** ---- ***- -*-- 
> shift 65
**** **** *--* -*** -*-* --** -*-- **-* ***- ---* ***- ---* ***- ---* **-- *--- 
> shift -64
**** **** *--* -*** -*-* --** -*-- **-* ***- ---* ***- ---* ***- ---* **-- *--- 
> shift -65
-*** **** **-- *-** *-*- *--* *-*- -**- **** ---- **** ---- **** ---- ***- -*-- 
> error

        Bad command. Type "help" for more info.

> set

        Argument(s) required for this command. Type "help" for more info.

> set 1

        A 2nd argument is required for this command.

> clear 999999999999999999999999 3

        Converting "999999999999999999999999" base "0": Result too large

> toggle 3 123abc

        "123abc" is not an integer

> toggle 99999999999999999999999999999 123abc

        Converting "99999999999999999999999999999" base "0": Result too large

> quit


Example reading commands from a file.

% cat input
set 2 3
clear 5
toggle 0x55 3
shift -3
set 5
quit

% pa2 input
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- 
---- ---- ---- ---- ---- ---- ---- --*- ---- ---- ---- ---- ---- ---- ---- --** 

        A 2nd argument is required for this command.

---- ---- ---- ---- ---- ---- -*-* -*** ---- ---- ---- ---- ---- ---- ---- ---- 
---- ---- ---- ---- ---- ---- ---- *-*- ***- ---- ---- ---- ---- ---- ---- ---- 

        A 2nd argument is required for this command.


Example reading commands from a file and redirecting stdout and stderr.

% pa2 input >& output
% cat output
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- 
---- ---- ---- ---- ---- ---- ---- --*- ---- ---- ---- ---- ---- ---- ---- --** 

        A 2nd argument is required for this command.

---- ---- ---- ---- ---- ---- -*-* -*** ---- ---- ---- ---- ---- ---- ---- ---- 
---- ---- ---- ---- ---- ---- ---- *-*- ***- ---- ---- ---- ---- ---- ---- ---- 

        A 2nd argument is required for this command.