Homework 5

Due October 7. This is a two-week homework and carries double credit.

Goal

The goal of this homework is to build a four-function calculator capable of adding, subtracting, multiplying, and dividing unsigned binary numbers up to 4000 bits long.

Deliverables

Please place your source code into a single file named email.m (where email is your login-ID on uhunix2) in directory /home/1/esb/312/hw5 on uhunix2, which is world-writable but not readable. For example I would put the program into esb.m. Turn in:
  1. Your name, your email, and the last 4 digits of your ID #.
    Note: You are encouraged to do this project in groups of up to 3 people. If you are turning in the project as a group, turn in a single source file named after one of you, and make sure the (single) homework you turn in specifies all your names and ID numbers. Only the names that appear on the homework you turn in will be given credit.
  2. state whether or not your program appears to work correctly (if not, describe problems. If you claim your program works and we determine it does not, you will get no partial credit).
  3. print out and turn in the first and last 10 digits of the results of running your program with, as input, each of the four files
              /home/1/esb/312/longadd
              /home/1/esb/312/longsub
              /home/1/esb/312/longmul
              /home/1/esb/312/longdiv
    
  4. Your program MUST be able to compile and execute using the following steps (for this example, using esb.m as the name of the program):
              m4 esb.m > esb.s
    	  gcc -o esb -g esb.s
    	  ./esb < /home/1/esb/312/longadd
    
    (if you say your program doesn't work at all, we will skip the last step).
  5. Be sure your code is commented. The TA may deduct points for inadequately commented code.
  6. Be sure email.m is readable by the world. For example, when you are ready to turn in your program (named for example mycalculator.m) do:
              cp mycalculator.m email.m
              chmod og+r email.m
    	  cp email.m /home/1/esb/312/hw5
    	  rm email.m
    

Requirements

  1. Your program must read two numbers separated by one of the four operators, + (ascii 43 = 0x2B), - (ascii 45 = 0x2D), * (ascii 42 = 0x2A), or / (ascii 47 = 0x2F). You may assume that input is terminated when you read any character that is not one of the 10 digits 0 (ascii 48 = 0x30) through 9 (ascii 57 = 0x39) or one of the operators.
  2. Your program will always be called on an input consisting of two numbers separated by one of the 4 operators; the expression will always be terminated by a blank or newline.
  3. After computing and printing the result of the operation, your program should exit.
  4. All input and output is in decimal (base 10).
  5. All internal computation must be in unsigned binary, with numbers of 4000 bits (500 bytes) each.
  6. The intermediate results of multiplication will be 8000 bits long. The less significant part (the lower 4000 bits) are the actual result.
  7. If the computation would overflow, your program should correctly retain the low-order 4000 bits of the result.
  8. If subtraction would give a negative result, your program may do anything at all (crash, loop forever, or give the wrong result). In other words, we only plan to test your program on a-b where a is greater or equal to b.
  9. Division by zero will likewise not be tested, and it does not matter what your program does when the divisor is zero.
  10. If the input is not the correct format, your program may do anything at all (crash, loop forever, or give the wrong result). In other words, we only plan to test your program on correctly formatted inputs.

Getting There

  1. There is a file named /home/1/esb/312/inout.m on uhunix2. This file contains a SPARC assembly language program to read a string. The function "read" returns the number of characters written. inout.m then converts this number of characters into a decimal, printable representation and prints this length calling "WRITE". You can build your program using inout.m as a skeleton, or you can simply study inout.m or chapter 10 of the book to see how input and output are done.
  2. inout.m also shows how to reserve space. Initialized data (using .word declarations) can go between the ".data" and the ".bss" statements. Uninitialized data can go between the ".bss" and the ".text" statements. The ".text" statement goes right before the code. You are welcome to look at Chapter 9 for an explanation of these statements.
  3. inout.m also shows how to index into character arrays using pointers.
  4. once input has been read (as done by inout.m), it must be parsed to convert it to a 4000-bit unsigned binary number. Parsing can be done incrementally by using the formula:
             parsed = parsed * 10 + (new digit - "ascii zero")
    
    where parsed is the number parsed, 10 is the number 10 in the internal representation, and the new digit is the ascii value. Of course the parse must end if the "new digit" is actually one of the operators or a non-digit, non-operator character.
  5. We have not yet covered calling conventions, so I suggest that you do NOT use registers to pass arguments between functions, just use global variables (e.g. a and b). Remember that calling .mul or other built-in functions may change any or all of the %o registers.
  6. If you are calling a subroutine with call, the subroutine MUST return to the caller with the following two-instruction sequence:
             ret
             restore
    
    Remember that the instruction in the delay slot after the call instruction is executed before the transfer of control to the subroutine. Also remember that register values may change across subroutine calls -- do not count on values you placed in registers before the call or ret to be there afterwards. If you prefer, you can use branches instead of calls, or you are welcome to look at chapter 7 of the SPARC book.
  7. While you are parsing the two numbers, save the operator so later, once the two numbers are parsed and placed into global variables (say, a and b), you can use the operator to determine which subroutine to call.
  8. One easy way to implement subtraction (a - b) is to compute the 2's complement of b and then add the result to a.
  9. The result must be converted to decimal to be printed on stdout. The algorithm is already present in inout.m, but instead of using .udiv and .urem, you will have to use your 4000-bit operations to repeatedly compute the single-digit remainder and the quotient when dividing by 10: (remainder(a, b) = a - ((a / b) * b)). If you wish, you may use algorithms other than the one in inout.m.
  10. Note that it will be hard to test your program until you have implemented parsing and printing, but it is impossible to write parsing and printing until your four functions work correctly. To start with, you could test your arithmetic functions by operating on predefined constants and checking the results with the debugger, and when you are satisfied that they work, adding in the parser and printer and then doing more thorough debugging.
  11. Nobody expects you to hand-check the results of a 100-digit operation, but you should at least verify that your program works correctly for 15-digit inputs. The "bc" program on Unix may be helpful for doing this.
  12. You will probably need a routine to set a number to zero, and maybe a function to copy numbers from one variable to another. Again you can look at inout.m for examples, but remember the copy loop in inout.m is also reversing the string, which is usually not what you want, and also is copying bytes. You probably want to use "ld" and "st" instead of "ldub" and "stb", to load and store 32 bits at once. (Notice that if you load or store 32-bit words from addresses that are not multiples of four, you will get a bus error).
  13. Use loops to loop over all the words of a number.
  14. The debugger is your friend.
  15. There is no single way to do this program. Good luck and have lots of fun!