0% found this document useful (0 votes)
190 views8 pages

252 Project: Calculator: Keyboard Input

This document describes a calculator program that will: - Perform basic arithmetic operations on 4-digit decimal numbers between -9999 and 9999 - Accept keyboard input for operands and operators - Display operands and operators on a 6-region display - Shift digits left and insert new digits as a user enters a multi-digit number - Convert entered operands from ASCII to binary and store in registers - Perform calculations based on entered operators and store results The document provides details on keyboard input, display output, entering operands and operators, and processing calculations. It poses questions to help understand key aspects of the calculator design.
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
190 views8 pages

252 Project: Calculator: Keyboard Input

This document describes a calculator program that will: - Perform basic arithmetic operations on 4-digit decimal numbers between -9999 and 9999 - Accept keyboard input for operands and operators - Display operands and operators on a 6-region display - Shift digits left and insert new digits as a user enters a multi-digit number - Convert entered operands from ASCII to binary and store in registers - Perform calculations based on entered operators and store results The document provides details on keyboard input, display output, entering operands and operators, and processing calculations. It poses questions to help understand key aspects of the calculator design.
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 8

252 Project: Calculator

This calculator will process decimal numbers ranging in size from -9999 to 9999. It will have the ability to perform several traditional calculator operations: addition, subtraction, multiplication, and division. The user of the calculator will be able to chain operations together (e.g., 3+4+6+8=). This document describes the calculator. Please read it thoroughly. You will not be required to implement every portion of the calculator, but you should understand how all of these pieces fit together. The actual homework tasks are given at the end of the document. The document will be updated later for the final homework. In several places in this document there is some text inside of boxes, generally including one or more questions. You do not have to turn in answers to these questions unless requested to do so in the homework document. However, they are good thinking exercises, so you may want to ponder these questions.

Keyboard Input
Calculator input comes from the keyboard. The numeric keys (0 through 9) are used to enter digits, with a possible minus sign in front. The operators are represented by the characters +, -, *, and /. The = character signals the end of a computation, just like in a normal calculator. There is a longer discussion about the calculator input after the next section.

Display Output
The calculator will use six memory locations to represent the information to be shown in six different regions of the graphic display (the black square in the simulator located below the register values). The first, located at xB000, represents the display of the operator (label OP marks a memory location that contains this address, so you can store to xB000 by using STI <register> OP). The next, located at xB001, represents the display of the sign of the value currently shown on the calculator, which is a minus sign if negative, and blank if positive (label SIGN has a pointer to this address). The next four, at addresses xB002 through xB005, refer to the four digits of the calculator display (labels DIGIT_1000, DIGIT_100, DIGIT_10, DIGIT_1 have pointers to these addresses). You should not store to these labels you should use these labels for indirect stores. The indirect store will load the data from the label location, then use that data as the address for the actual store. For example, STI R0 OP will read the location indicated by the label OP. The content of that location is xB000 the location of the operator display. Then, the contents of register R0 will be written to location xB000 (not the address where the xB000 pointer is located!) If you are still not comfortable with the mechanics of an indirect load or store, PLEASE VISIT OFFICE HOURS. You will need to understand these in order to complete the homework! The above are the six memory locations you will need to use. However, we put a x0000 at memory location xB006 so that the convertToBin subroutine can process the four ASCII digits starting at xB002

as a null-terminated string of decimal digits. You do not need to refer to this location explicitly, however, so a pointer to it is not necessary. The above labels will be used as the pointers to the special memory locations used for the display. The picture that follows shows the display with the regions labeled to indicate the corresponding memory location (left) and an example of what the display might look like if the user enters -390* on the keyboard.
DIGIT_1000 SIGN DIGIT_10 DIGIT_1

DIGIT_100

OP

The programmer can control the display by writing values to these memory locations, then calling the updateDisplay subroutine (which will be provided to you). For each of the six regions in the display, the updateDisplay routine draws the appropriate character based on the contents of the memory location that corresponds to that region. The actual shape that updateDisplay draws in each location is dictated by the contents of that location. A zero value in the SIGN memory location will make the sign region blank; a non-zero SIGN will make updateDisplay draw a minus sign. updateDisplay interprets the values in the other five locations as ASCII codes, though it is only able to draw a small subset of the ASCII character set. Valid values for the digit locations are x30 through x39, as well as x20 (space) to show a blank. The ASCII codes for + - * and / are valid values for the OP location. Any value written to one of these locations that falls outside of the permitted range will result the location showing a solid rectangle. REMEMBER: the display does NOT update when you write to the special memory locations you need to call the updateDisplay subroutine afterward to update the display!

Operand and Operator Input


In our simplified calculator, we will not allow the user to enter negative numbers. However, they can use negative numbers by subtracting a larger value from a smaller one. Instead of entering -3 for an operation, the user can subtract 3 from 0. 2

An operand is entered as a sequence of digits, just like a normal calculator. The calculator code will therefore need a loop to input the sequence of digits. Like a normal calculator, the display initially shows a zero in the rightmost position. For now, we will also show zeros in the other positions as well. When a number key is pressed, the display should update such that all digits move over one position to the left, and the new digit is inserted in the rightmost position. What would happen if we cleared the display to be three blanks (spaces) followed by a zero, and then we shifted all the digits to the left by one position when entering the first digit? How might we (correctly) support leading blanks instead of leading zeros? We will use the display memory locations described above as temporary storage to build the operand one digit at a time in response to keyboard input. This means that we will also show these digits on the display, which is helpful to the user of the calculator. After testing that the ASCII code of the value entered with the keyboard is a digit, we move all the memory contents (one at a time) to their new (shifted) locations (i.e., the contents of the memory location pointed to by DIGIT_100 are written to the location pointed to by DIGIT_1000, the contents of the memory location pointed to by DIGIT_10 are written to the location pointed to by DIGIT_100, and the contents of the memory location pointed to by DIGIT_1 are written to the location pointed to by DIGIT_10). Then we write the new ASCII character to the DIGIT_1 location, and call the updateDisplay routine to show the operand, with its new digit, on the display. This provides the calculator user feedback showing them what number they have entered. Something to think about: In what order should we perform the loads and stores needed to move the digits over by one position? Does it matter? What happens if we first copy DIGIT_1 to DIGIT_10 before copying DIGIT_10 to DIGIT_100? What about if we first copy DIGIT_10 to DIGIT_100 before copying DIGIT_1 to DIGIT_10? In our implementation, we will shift all of the digits over by one position whenever we enter a digit. Why might we choose to do this instead of only shifting the digits that arent leading zeros or blanks? Our calculator specification states that we support up to four digits per operand. If the user presses more than four number keys, we will use the four most recent number keys values. This means that the older values shift out of the highest digit position, replaced with the newer values. This is only one way of handling the problem of users attempting to specify values too large for a calculator. What other method might someone choose for handling too many digits? What does your calculator do? What does the Mac or Windows calculator do? Why do you think we have chosen the method we did? The user presses an operator to signify the end of the operand. At this point, we use our new convertToBin subroutine to convert the ASCII string in the four digit memory locations to a numeric value. Note that this means that we need a null character at the memory address just after DIGIT_1. We then place this result into register R6. We will use register R6 as our accumulator. An accumulator is a register that holds the cumulative value (i.e., running total) of all the operations performed thus far. 3

Although some processors have a special accumulator register, we will use one of the general-purpose registers (R6) as an accumulator for this program. The pressed operator should be shown in the OP location so that the user can see what operation they chose. The operator ASCII code should also be saved for later so that we can perform the indicated operation after receiving the second operand. We accomplish both of these goals at once by storing the ASCII code of the operand in the OP memory location, then calling updateDisplay afterwards to show the operator character on the screen (based on that ASCII value). If additional operator keys are pressed, we will assume that we save the new operator instead, overwriting the previously-saved operator, and calling updateDisplay again to show the new operator. Overwriting the previous operator means that in the calculation, we use the last operator entered before the second operand. When the first digit of the second operand is entered, we clear the upper three digits of the display to ASCII zero values (we no longer need the first operand values because we already converted the ASCII and loaded the value into the accumulator). We also place the ASCII value of the entered digit in the DIGIT_1 memory location, then call the updateDisplay routine to show the current value of the second operand. The user can continue to enter digits, and each time they do, the contents of the digit display shift one position to the left and the new digit is inserted on the right (just like for the first operand). When another operator or the equals sign is entered, that signifies the end of the second operand. We convert the second operand to binary using convertToBin, put the result into register R5, and then perform the math operation indicated by our stored operator. We need to make sure not to overwrite the ASCII operator in the OP location with the new operator (or equals sign) until after we perform the stored operation. Instead, the current operator should be in another register or another memory location so that we can store it into OP after performing the math operation described below. You will use write a doMath subroutine that will use the accumulator value (the contents of register R6) along with the contents of register R5 as the two operands, and . It will use the operator code in register R4 (if its not there already, youll need to load it from the OP memory location into R4), and return the result back to register R6, overwriting the previous accumulator value. Once we have the result, we need to display it for the user. We use the convertToASCII routine to convert the binary value stored in register R6 to a sequence of ASCII values for four decimal digits. This function places the values directly into the memory addresses for the digits of our display. Finally, we call the updateDisplay routine to show the result. If it was an operator (not the equals key) that was pressed, the result in the accumulator is now our first operand and we wait for a new second operand from the user, repeating the above steps. If it is an equals, then the user can either enter digits next (to start a completely new computation), or they could enter another operator, which would use the result of the previous computation as the first operand (just like in a normal calculator). The above is a description for the simplified case. There are some additional situations that we need to handle, as described below.

1. An equals may be entered first, before any digits (e.g., =). In this case, we ignore the equals. 2. An operator may be entered at the start of the program, before any other key In this case, we will use 0 as our first operand. We clear the accumulator at the start of the program anyway, so its already 0. So we can continue in the normal way after an operator is entered. 3. An equals may be entered after the first operand (e.g., 3=) In this case, the result is the first operand. Note: like most simple calculators, we will only perform one operation at a time and will not worry about operator precedence. This means that if the user types 12+5*2=, the result will be 34. When the * character is pressed, the calculator will compute 12+5, show the result (17) and the new operator (*). When the 2 is pressed, the 2 is shown, and when = is pressed, the calculator will compute 17*2, then show the result of 34. The running total of the operations is often called an accumulatorit is the cumulative result of the operations thus far. This means that we only ever need to worry about performing a single operation at a time. There is an alternate method of specifying values that some calculators use called Reverse Polish Notation (RPN), but we will not use that here. The following flow diagram gives the basic idea of the operation of the calculator. Note that parts of this diagram are at a high level, and that you will not have to implement the entire diagram at once. In this diagram, two of the subtasks have been decomposed further, though not fully into assembly language. All the tasks and decision boxes are labeled so that the diagram is easier to discuss. First try to understand the flow diagram ignoring the expansion of the getOperand and convertToASCII tasks. It shows register R6 used as the accumulator, R5 used as the second operand, and the use of the stored operand value in the OP memory location. Work through some of the cases of how someone might use the calculator (i.e., what sequence of numbers and operators they might enter), and how that sequence will be handled by the flow chart. For example, at the very least you should be sure you understand what the calculator will do (and why) for sequences such as 3+41= and 2+3+6=, and why the testEq decision box tests the stored operator instead of the new one, and why it checks if the stored operator is an equals or not. Recall that the calculator is designed to keep the running total (accumulator) in register R6, so in a chain of operations such as 2+3+6=, we add each successive value to the accumulator. After you are sure you understand the main flow diagram, examine the expanded getOperand and convertToASCII tasks, and work through these flow diagrams. You will be implementing getOperand as part of HW6.

START initCalc Clear Accumulator (R6) to 0 Clear DIGITs to all 0s (x30) OP ASCII code for =

updateDisp1 Update Display getOperand getKey1 Get Key clearUpper Clear upper three digits: DIGIT_1000 0x30 DIGIT_100 0x30 DIGIT_10 0x30 storeDigit yes DIGIT_1 Key updateDisp2 no
tes D tK1 t igi

te

1 stK

Op Eq Key is equals or operator?

Update Display getKey2 Get Key


igi 2D stK t

no

Key is digit?

getOperand

yes

te

Key is digit?

no

tK tes Key is equals or operator?

no Op q 2E

Get new operand, accepting any digits until an operator or equals is entered, updating display for each key convertToBin Convert ASCII of new operand to a binary value (R5)

yes shiftDigits yes

Shift all digits to left one position

tes

tEq

convertToASCII Stored OP is equals? no doMath R6 R6 OP R5 signNeg signPos Set SIGN to zero Set SIGN to non-zero R6 R6 yes
eg 6N tR s te Is R6 negative?

yes doCopy R6 R5

no

convertToASCII Convert R6 to ASCII Put ASCII digits in DIGIT memory locations storeOp OP new operator

doConvertToASCII Convert R6 to ASCII digits Put ASCII digits in DIGIT memory locations

Homework 6
Instructions on submitting this homework will be posted late this week. Be sure to read the calculator spec before starting this homework. There may be information in the spec that is not repeated here, and it is generally good practice to understand the context in which your code will be used when you program. For this homework you will create a number of subroutines, described below, that you will use as part of the calculator program that you will complete for this semester (some of the code for the calculator will be provided to you). Except for the return value of the subroutine, each subroutine should not appear to alter any of the registers. This means that if you modify a register inside the routine and it is not the return value register, you need to save that register value at the start of the subroutine and restore it at the end, before returning from the subroutine. If the subroutine uses the TRAP instruction or calls another subroutine, it will also need to save register R7, which is where the PC is saved for subroutine calls and the TRAP instruction. Otherwise, when you try to return from your subroutine, the processor will not go back to the instruction it should! To make it easier to independently develop and test the individual subroutines, we will create each subroutine in a separate file. They will be assembled separately, and you can test each separately (except for the getOperand subroutine, which requires several other subroutines). The test code will be provided on Friday, along with instructions on how to use it. If you look at the bottom of the getOperand.asm file, you will see that we have assigned specific memory locations to each of the subroutines in the calculator. Note that the .FILL value for isOp is the same as the .ORIG value in isOp.asm. We use .ORIG statements to start each subroutine at the address that matches the table, and we can call the subroutine by using a JSRR instruction. A couple of examples of how to do this are given in the getOperand.asm file. You have been given subroutines for isEq and isOp. You should look at these files as examples of how to implement a subroutine. Again, the bottom of getOperand.asm shows how we will use the subroutines. Code will be provided to you on Friday that calls your subroutines to help you to test them, along with instructions on how to use the test code. SUBROUTINES TO WRITE FOR HW6: isDigit This subroutine examines the ASCII value in R0 and returns a non-zero value in R1 if R0 represents a digit, and a zero in R1 otherwise. Similar subroutines that test if the value in R0 is an equals and if the value in R0 is an operator (+, -, *, or /) has been provided to you. You will later use this subroutine in two places in the calculator program to implement the testK1Digit decision box and the testK2Digit decision box. **Note that you can verify if R0 is a digit by determining that x30 R0 x39. 7

doMath This subroutine performs R6 R6 <operation> R5, where the <operation> is determined by interpreting the contents of R4 as an ASCII value. In other words, if R4 contains x2A, the ASCII code for *, the doMath subroutine should perform R6 R6 * R5. For now, you should implement the capability to perform +, -, and *. For the final homework you may also implement /. For now, however, if the operator is a divide, just return a zero in register R6. One reason we would want to implement doMath as a separate subroutine from our main program is to make it easier to read the main calculator program as well as the math part. It also means that if we add an operator, we only need to change the doMath and isOp routines, and dont need to change the main calculator code. shiftDigits This subroutine is used by getOperand. It is used when appending a new digit to the right side of the operand (e.g., if the operand is currently 0004 and a 1 is pressed, it should make the four digit display locations contain 0041). The task should shift all of the digits one position to the left in the display (although the display will not be updated until after this subroutine returns, as indicated in the flow chart). To do this, you will need to read from each position (indicated by pointers DIGIT_1, DIGIT_10, etc), and write that value to the memory location that represents the new position. This subroutine does not have any input or output values. The value in the location pointed to by DIGIT_1000 can be overwrittenyou dont need to save it (see the discussion about entering operands and the four digit limit in the calculator specification). **Be careful what order you use to copy the digitsif you are not, you could end up overwriting a digit before you move it to its new location. getOperand This subroutine receives the first digit of the operand in register R0. It places the ASCII value of this digit into the memory location pointed to by DIGIT_1. It is safe to assume that the input value in R0 is a digit, because in the final homework we will test the key to ensure it is a digit before calling this subroutine. The routine then should call the updateDisplay subroutine, which will be provided to you on Tuesday of this week. For now, the updateDisplay subroutine does not do anything. Later, however, it will actually update the graphical display of the calculator. The getOperand subroutine then should use TRAP instruction(s) to receive one or more additional characters until an operator or equals sign is entered. Refer to Chapter 7 of the book and lecture notes for how to use the TRAP instruction. You can use the isEq and isOp subroutines (provided to you) to test the key. Keys that arent digits, arent the equals sign, and arent operators should be ignored. If a digit is entered in the getOperand subroutine (the result of testK2Digit is yes), then the contents of all of the DIGIT memory locations for the display must be shifted over by one position by calling the shiftDigits subroutine described above. The ASCII value of the new digit is then placed in the location indicated by DIGIT_1, and the display is updated again. When an equals or operator key is pressed, the getOperand subroutine should return. **Be sure to save register R7 at the beginning of getOperand, because the TRAP instruction and isEq, isOp, and shiftDigits subroutines will overwrite R7.

You might also like