The purpose of this assignment is to help you learn about computer
architecture, assembly language programming, and testing
strategies. It will also give you the opportunity to learn more about
the GNU/Unix programming tools,
especially bash
, gcc
, and gdb
for assembly
language programs.
In this work, you are required to write the dc
(desk
calculator) program in assembly language. We provide you the skeleton
code for dc
written in assembly language, a startup file
(details are in the below). You will start from reviewing this code
and modifying and adding the code with respect to the requirement
specified below. Good luck!
dc
(desk calculator) is a tool on
Unix-like operating systems. In its simplest form, dc
reads a list of numbers from the standard input (stdin) and uses a set
of command keys to display the results of the user-specified
operations on the standard output (stdout).
In dc
, the operands (numbers) and operators are added
in dc
is
used.
The result will be printed (by p
command) to the standard output stream as:
dc
uses a stack to store numbers in LIFO order
(last-in, first-out). Whenever it encounters an arithmetic operator,
it first pops out the last 2 operands from the stack, runs the
operation on those numbers and then pushes the result back into the
stack. In the example above, 567
and 343223
are pushed in the stack one after the other. Once the
operator '+'
is entered, dc
first
pops 343223
and then
567
from the stack. It then adds the two integers and finally
pushes the result (343790
) back in the stack. The command
p
is used to print the value that sits on the top of the
stack. Please note that p
only retrieves the value without
popping (this is also known as a peek operation). The user can
either quit the program by entering q
or EOF
character to the program. In other words, if the annotated text mentioned
above is stored in a file named values.txt then dc
can also be
executed in the following manner:
which will print the result to the standard output stream as:
Thedc
tool supports a number of operators and subsidiary
commands which you can study on the man
page. For this assignment, you are required to implement only the
following operations.
p
+, -, *, /, %, ^
+, -, *
computes addition, subtraction, and multiplication, respectively.
/
computes the quotient (in integer) in integer division. For example, 3/2 = 1
.
%
performs remainder operation.
^
performs exponentiation (e.g., 2^4 = 16). You don't need to implement negative exponent.
q
To make the assignment tractable in assembly programming, we make some simplifying assumptions:
You might want to use rsp
and rbp
registers or temporary registers (r12
, r13
, and r14
)
to implement your stack for computation in your code. For simplicity, you can assume that the maximum number of element in the stack is 64 (i.e., your program should be able to handle at least 64 elements.)
We provide skeleton code and several utilities
(e.g., Makefile and tests)
in assignment4.tar.gz.
As usual, please check README.md
.
Please go through the
pseudo-code before you begin writing the program. It is acceptable to
use global (i.e. bss section or data section) variables
in mydc.s
. Please make sure that you create your own function to implement the power (^) arithmetic operator. In dc, negative numbers can be added by pre-appending '_' symbol to the number. For example
p
) as below, and quit the program (q
).
dc
tool also provides additional operations that
manipulate the input. You are required to implement the following
operators for this assignment.
Advanced Operations | Short description |
---|---|
f
|
Prints the contents of the stack in LIFO order. This is a useful command to use if the user wants to keep track of the numbers he/she has pushed in the stack. |
c
|
Clears the contents of the stack. |
Please note that 'f'
does not pop out any
numbers out of the stack. The following example run of dc shows how a
combination of different dc
operators can be used:
which will print the result to the standard output stream as:
dc
keeps on pushing the integers on the stack (53,
48, 35
) till it encounters the first '+'
operator.
It pops out 35
and 48
, computes the addition and
inserts 83
back in the top of the stack. When the second
'+'
is inserted, it repeats the same process with the integers
83
and 53
and inserts back 136
in
the empty stack. Later when 'f'
is entered, dc
prints out all the contents of the stack in LIFO order.
123abc
as 123
, pabc
as p
, and
abc123
as a
(not an operator, do nothing). Additionally, the words pre-appending '_'
symbol to the characters, such as _abc, can be treated as 0.
+, -, *, /, %, ^
operations.
If there are not enough operands, dc
should print out
'dc: stack empty
' to standard error
and should not be terminated (i.e., keep continuing).
p
operators, dc
should again print 'dc:
stack empty
' to standard error if the stack does not contain at least one
operand.
f, c
operators),
dc
should do nothing if the stack is empty.
dc
has to divide by 0,
it should print 'dc: divide by zero
' to standard error
and should be terminated.
dc
has to compute remainder by 0,
it should print 'dc: remainder by zero
' to standard error
and should be terminated.
dc
should print
'dc: overflow happens
' to standard error and should be terminated.
Refer to the table below for semantics of each operator.
Operator | Name | Pseudocode |
---|---|---|
+ |
Add |
if len(stack) < 2: fprintf(stderr, "dc: stack empty\n") continue op2 = stack.pop() op1 = stack.pop() stack.push(op1+op2) if overflow: fprintf(stderr, "dc: overflow happens\n") exit(1) |
- |
Subtract |
if len(stack) < 2: fprintf(stderr, "dc: stack empty\n") continue op2 = stack.pop() op1 = stack.pop() stack.push(op1-op2) if overflow: fprintf(stderr, "dc: overflow happens\n") exit(1) |
* |
Multiply |
if len(stack) < 2: fprintf(stderr, "dc: stack empty\n") continue op2 = stack.pop() op1 = stack.pop() stack.push(op1*op2) if overflow: fprintf(stderr, "dc: overflow happens\n") exit(1) |
/ |
Divide |
if len(stack) < 2: fprintf(stderr, "dc: stack empty") continue op2 = stack.pop() op1 = stack.pop() if op2 == 0: fprintf(stderr, "dc: divide by zero\n") exit(1) stack.push(op1/op2) if overflow: fprintf(stderr, "dc: overflow happens\n") exit(1)
|
% |
Remainder |
if len(stack) < 2: fprintf(stderr, "dc: stack empty\n") continue op2 = stack.pop() op1 = stack.pop() if op2 == 0: fprintf(stderr, "dc: remainder by zero\n") exit(1) stack.push(op1%op2)
|
^ |
Power |
if len(stack) < 2: fprintf(stderr, "dc: stack empty\n") continue op2 = stack.pop() op1 = stack.pop() stack.push(power(op1,op2)) if overflow: fprintf(stderr, "dc: overflow happens\n") exit(1)
|
q |
Quit |
exit(0) |
p |
Peek |
if len(stack) == 0: fprintf(stderr, "dc: stack empty\n") continue printf("%d\n", stack[len(stack)-1]) |
f |
Print all |
for (i = len(stack)-1; i >= 0; i--): printf("%d\n", stack[i]) |
c |
Clear |
stack.clear() #len(stack) should be 0 after this line
|
Develop on lab machines. Use your favorite editor to create source code. Use gdb
to debug.
Your code must be written in x86-64 assembly (64-bit), not IA-32 (32-bit). Please do not use flags such as -m32
.
You will get zero point if your code does not compile in x86-64.
If you want to figure out whether your binary is 32-bit or 64-bit, use file
command.
Do not use a C compiler to produce any of your assembly language code. Doing so would be considered an instance of academic dishonesty. Instead, you should write your assembly language code manually.
We encourage you to develop "flattened" pseudo-code (as described in EE209 and EE485) to bridge the gap between the given pseudo-code and your assembly language code. Using flattened pseudo-code as a bridge can eliminate logic errors from your assembly language code, leaving only the possibility of translation errors.
We also encourage you to use your flattened pseudo-code as comments in your assembly language code. Such comments can clarify your assembly language code substantially.
Your README.md
file should contain:
Use KLMS submission link to submit your assignments. Your submission should be one gzipped tar file whose name is
YourStudentID_assign4.tar.gz
Your submission need to include the following files:
mydc.s
file.README.md
file.mydc
program with.Your submission file should look like this:
As always, we will grade your work on quality from the user's and programmer's points of view. To encourage good coding practices, we will deduct points if gcc209
generates warning messages.
Comments in your assembly language programs are especially important. Each assembly language function -- especially the main
function -- should have a comment that describes what the function does. Local comments within your assembly language functions are equally important. Comments copied from corresponding "flattened" C code are particularly helpful.
Your assembly language code should use .equ
directives to avoid "magic numbers." In particular, you should use .equ
directives to give meaningful names to:
.equ TRUE, 1
..equ OADDEND1, 8
..equ UICARRY, -4
.Note that these are examples, and you should handle any magic number like this.