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
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.
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:
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.
Use the turnin program to turn in the following:
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.