UMBC CS 201, Fall 08
CMSC 201
Programming Project Five
Instruction Set Simulator
Out: Monday 11/24/08
Due Date: Sunday 12/07/08, before midnight
The design document
for this project, design5.txt, is due:
Before Midnight, Sunday 11/30/08
|
The Objective
The purpose of this assignment is to give you practice using command
line arguments, guarding header files, and working with linked-list
implementations of both stacks and queues.
Background
When using C, we code in a high-level language. Many people think of C as
a low-level language when considering the heirarchy of programming languages.
However, it is a high-level language since it has to be compiled down to
machine code. Before the machine code can be created, C source code gets
compiled down to a language named Assembly and then into the machine code.
Assembly is a language of simple instructions which uses registers to hold
values, and executes commands based on the contents of those registers.
Wikipedia defines an instruction set simulator (ISS) to be a simulation model,
usually coded in a high-level programming language, which mimics the behavior
of a mainframe or microprocessor by "reading" instructions and maintaining
internal variables which represent the processor's registers.
If you are interested in Assembly language coding, you'll have an opportunity
to do that in CMSC 313. Some of our upper level classes and research labs
give students a chance to work with operating systems, machine devices,
and assembly code, in an attempt to solve issues with performance and
usability of the systems we use today.
The Task
The Structures
You will need to implement two different data structures for this project
(however you will only use one per execution) :
- Stack - you will create a stack implemented with a linked-list, which will
Execute INSTRUCTIONs in a Last In First Out manner.
- Queue - you will create a queue implemented with a linked-list, which will
Execute INSTRUCTIONs in a First In First Out manner.
The Commands
The file will contain any order of commands with the following
instructions, operands and results:
- ADD <destination> <operand 1> <operand 2> - adds operand 1
and operand 2 storing the result in the destination REGISTER
- SUB <destination> <operand 1> <operand 2> - subtracts
operand 2 from operand 1 storing the result in the destination REGISTER
- MUL <destination> <operand 1> <operand 2> - multiplies
operand 1 by operand 2 storing the result in the destination REGISTER
- DIV <destination> <operand 1> <operand 2> - divides operand 1
by operand 2 storing the result in destination REGISTER
- MOV <destination> <operand 1> - assigns operand 1 to the
destination REGISTER
- PRINT - prints the contents of all registers in a formatted matter.
(See sample output)
- EXECUTE - executes all commands in the DATA STRUCTURE and prints out
the contents of all REGISTERs, as well as the number of intructions
executed.
The Operands
- An operand may either be a REGISTER or a floating point value. You will
have to determine if an operand is either a floating point value or a
REGISTER name.
Execution
- Each command will need to be inserted into the Stack or Queue using either
a Push() or Enqueue() operation. To execute a command, you will Pop() or
Dequeue() a command and then execute that command.
The Data File
- The data file, called commands.txt is in Evans' pub directory. You may
copy it as usual.
- Each line of the data file will contain a command. You are to read in all
the instructions at once, storing them in the execution data structure
stipulated by the command-line argument.
Specifications
- You must use the following structure definitions, #defines, and typedefs:
#define MAX_NUMBER_OPERANDS 2
#define MAX_WORD_LENGTH 8
#define MAX_STRING_LENGTH 32
typedef float REGISTER;
typedef struct node* NODEPTR;
typedef struct command
{
char instruction[MAX_WORD_LENGTH];
char destination[MAX_WORD_LENGTH];
char operand1[MAX_WORD_LENGTH];
char operand2[MAX_WORD_LENGTH];
int numOperands;
}COMMAND;
typedef struct node
{
COMMAND data;
NODEPTR next;
}NODE;
where instruction is the command to be executed, destination is where the
operation result is stored, operand1 and operand2 are strings holding
the first and second operands of the command.
- You must have 3 REGISTERS declared named EAX, EBX, and ECX. These
registers will be the only registers used in this project and are the
only REGISTERs which need to be printed. These registers MUST be
initialized to 0.0.
- You must dynamically allocate every NODE in this program.
This entails using malloc() or calloc() any time a NODE is created.
You also need to prevent memory leaks by using free() any time a NODE
is no longer needed.
- YOU MUST have a function to parse and create commands with the
following protoype:
COMMAND CreateCommand(char* commandString);
this function takes in the command read from a file as a string.
(i.e. "ADD EAX 2.5 5.6") and creates a COMMAND structure with its members
assigned values based on the words in the string passed to this function.
You must parse this command string based on the location of whitespace
characters.
HINT: You can count the number of whitespaces in the string to determine
the number of items to parse. For example "ADD EAX 2.5 5.6" has 3 white
spaces indicating that there are 2 operands.
You should use the sscanf() function to parse the command string. This
function works exactly like fscanf except it takes a string instead of a FILE*.
HINT: You can have different sscanf() statements inside a switch()
statement that switches on the number of whitespace characters.
NOTE: Commands other than the seven commands shown above are invalid and
should not be created or executed. The user should be shown an error message
and the program should exit.
- YOU MUST have a function to execute commands with the prototype:
void ExecuteCommand(COMMAND command, REGISTER* EAXPtr,
REGISTER* EBXPtr, REGISTER* ECXPtr);
This function will take the COMMAND just removed from the data structure
(Pop()ed or Dequeue()d), and execute the instruction held in it. Since an
operand can either be a floating point number or REGISTER, you will need to
determine what each operand actually is before execution.
NOTE: You can convert a string to a floating point number using atof()
which is found in the stdlib.h.
- You MUST print out the number of instructions executed every time
an EXECUTE command is encountered. See output below.
HINT: You can determine what each operand is easily based on its FIRST
character.
- You MUST guard all your header files.
- You can use any code we have written for you in the lecture notes,
however, you must modify it to work with the NODE structure defined in
this project.
Guarantees
- The command file will not contain any instructions with more than two
operands when an instruction requires operands.
- All operands will be valid operands.
- If the command is one of the seven acceptable commands, then it will
be well-formed.
Sample Output
We have created a sample run using the
commands.txt.
We have created sample runs which shows results of using commands.txt with a:
Stack in
Stack Output
a.out commands.txt STACK
Queue in Queue Output
a.out commands.txt QUEUE
EXTRA CREDIT
- You can earn 5 points of extra credit if you can create a command file of
no less than 20 commands, not counting PRINT and EXECUTE, which will produce
the exact same result for both a Stack and a Queue execution. You MUST
have a PRINT command every 5 instructions and your output must match line per
line. Also, your file can NOT be all the same command.
Submitting the Program
To submit your program, type the following command at the Unix prompt
submit cs201 Proj5 followed by the .c and .h files necessary for compilation
To verify that your project was submitted, you can execute the
following command at the Unix prompt. It will show all files that
you submitted in a format similar to the Unix 'ls' command.
submitls cs201 Proj5