EECS 2021AB LABTEST I Programming Question
File name Q1
swmul.asm
File name Q2
funcalc.asm
Submit command
through moodle/eclass
Weight Q1
10 points
Weight Q2
30 points
Resources
Ch. 2 notes
Resources
Ch. 3 notes
Description Q1
Write a short RISC-V assembly function swmul that emulates the hardware command mul. This is meant to be used in versions of RISC-V that do not have a multiplication instruction. Use the following C code as guide:
long long int swmul(long long int x, long long int y)
{
long long int i, res;
res = 0;
for (i=0; i<64; i++)
{
if (y&1) res += x;
x = x<<1;
y = y>>1;
}
return res;
}
To test your code write a main program that invokes function swmul and print the returned value and the same result using mul. Function swmul should obviously not use mul. Test it using positive numbers. Do not wory about overflow (the C function above does not worry either).
Hints
Your program should have a DD statement that defines a few constants like
X: DD 12
Y: DD 40
When debugging use small positive numbers and use ecalls statements to print intermediate results. But remove them before submitting.
Since this code uses several temporary variables you need to include a small block of comments stating what you use the temporary or other registers for (very brief!). Here is a suggestion:
;; a0 -> x
;; a1 -> y
;; t0 -> i
;; t1 -> res
Style
Your program should include a header that includes your name, section, today’s date and a very brief description of what the program does (no more than 2 lines).
Indentation is very important. Labels start at column zero, and instructions/directives start one tab in. The instruction and the first argument may or may not be separated by a tab, as long as it is the same everywhere. Comments at the end of a line should be aligned and so should the comments in a block that occupy a line of their own. Debugging statements should be removed before submitting the code.
Remember that functions should follow strict conventions. Some of them are
• Integer arguments are in registers a0, a1 etc. You cannot expect these registers to contain the original values after the call. Similarly for float or double (but be careful the RVS assembler does not recognize all of the nicknames).
• A function makes no assumptions about any register other than a0, a1 etc (the ones it actually uses), sp, and ra. This means that it does not expect the other registers to be zeroed, or contain some other values.
• Follow the conventions on which floating point registers are temp, saved, argument, etc. You can find them on the second page of the Green Card.
• A function should not have hardwired constants unless specified by the function description or the original C code. So, although it may be more convenient to use ARG1 instead of f10, it is a very bad idea.
• A function should respect the conventions about saved registers. Functions that do not, often go through debugging fine but fail when used in production code (or being marked!).
The above conventions are not just style. Code that does not observe them will fail at some point.
Most lines of code should have a very brief explanation of what they do as a comment. Preferably as Java or C code. (like res = 0).
Marking
Submissions that have code that works correctly but do not follow the stylistic or other conventions will not get more that 30%. A program that does not work but has some resemblance to a working program will get no more than 30%.
Description Q2
Write a short RISC-V assembly function funcalc that evaluates expression in a form that has an operator (“*” and “+” here) that is followed by two operands (like 10, 132 or even *12 2). The function is recursive so that an expression like “+ 3 *12 2” is evaluated by first calculating “*12 2”, which is 24 and then “+3 24” which is 27. Translate the following C code:
void eatblanks(char *s[])
{
char c;
c = (*s)[0];
while (c==’ ‘) {
*s += 1;
c = (*s)[0];
}
}
long long int funcalc(char *s[])
{
char c;
long long int res;
eatblanks(s);
c = (*s)[0];
if ( (‘0’<=c) && (c>=9) ){ /* Evaluate a number */
res = 0;
while ( (‘0’<=c) && (c<='9') ) {
res = res*10 + c - '0';
*s += 1;
c = (*s)[0];
}
return res;
}
if ( c=='+' ) { /* Addition part */
*s += 1;
res = funcalc(s);
res = res + funcalc(s);
return res;
}
else if ( c=='*' ) {
*s += 1;
res = funcalc(s);
res = res * funcalc(s);
return res;
}
else return -1;
}
Hints
Start by implementing the eatblanks function. Notice that both eatblanks and funcalc accept as argument a pointer to a string. You can define a pointer to a string with
STR: DC "+ 10 21\0"
SPTR: DD STR
....
addi a0, x0, SPTR
so a0 now contains this pointer to a string. To read the current first character you do an ld to bring the string address (pointer to its first element) and then an lb to bring the first character.
After you have written and tested eatblanks you develop the part that evaluates a number and test it with simple expressions like
STR: DC "21\0"
that should simple return 21. Then attempt the addition part. If this works then the multiplication is easy (cut and paste and slightly edit).
As always you should have a block, one per function, that indicates the use of local variables. Be careful with this since this function will be using many temporary and/or saved registers. Here is a suggestion:
;; a0 is s
;; t0 = '0' or '+' or '*'
;; t1 = '9'
;; t2 = *s
;; t3 = c
;; t4 = 10
;;
;; s0 = res
The whole program is about 100 lines of code.
To test your code use at least these input/output pairs
120 -> 120
* 120 2 -> 240
* 120 2 -> 240
+ 120 1 -> 121
* + 12 11+2 1 -> 69
+ 11 *2 5 -> 21
+ 11 * 2 +1 5 -> 23
+11*2+1 5 -> 23
* 11 2 -> 22
Style and Marking
The same style and other conventions apply for both Q1 and Q2 with regards to marking.