EE314 – Microprocessor Systems Laboratory Project 04 Spring 1999 Decimal Arithmetic and Subroutines Note: This lab will take two lab periods. Prelab part 1 will be due at the beginning of the first session, and the prelab 2 will be due at the beginning of the second session. You will receive one grade with double value for this lab. Objectives 1. To practice working with decimal arithmetic in an assembly language program 2. To learn to use the stack for parameter passing Note: This lab will take two laboratory sessions to complete. Introduction Review the information in your textbook on ASCII and BCD arithmetic (pp. 115-118) and recall the lecture discussion on stack-based parameter passing. Arithmetic calculations such as addition and subtraction are fundamental to computer software. When calculations are performed, numeric data are often translated from ASCII into binary format because the arithmetic computations are much faster in binary format than in ASCII or BCD. Unfortunately, the conversions from ASCII to binary and vice versa are fairly complex and require more computation. So whether you choose binary or ASCII representation depends a lot on the amount of arithmetic needed for each IO operation. If there is a small amount of IO and a lot of arithmetic, then binary is better. If there is a lot of IO and only a little arithmetic to do, then ASCII or BCD is better. Some applications, such as business computations for example, tend to do a lot of reading and writing of numbers, but relatively little computation. In these cases, it makes sense to do the arithmetic with decimal numbers in BCD or ASCII format. This project deals with decimal arithmetic and provides more experience in writing programs. An arithmetic calculation with two arrays containing multi-digit numbers can be written in a subroutine so that it can be re-used wherever needed. To work with any size array however, the number of digits (or array elements) must be indicated in some way. Two possible alternatives are: 1) use an end-marker appended after the last element of the digit-array (as is often done with character arrays in C), and 2) pass the value to the subroutine as a parameter. When using the first approach, the value represented by the end-marker must be unique: in other words, it cannot represent any BCD or ASCII number. Alternative 2 removes this restriction, but requires that an additional parameter be passed to the subroutine. Another question is how to pass the parameters to the subroutine. This can be done by storing the information in CPU registers or by pushing it on the stack. However the number of registers available for passing parameters is limited and as a result, so are the number of parameters. A much more general method is to pass parameters by way of the stack. Stack based parameter passing also plays key roles in recursive subroutines (ones which call themselves) and reentrant subroutines where multiple calls from different sources are "simultaneously" processed. Laboratory Procedure This lab project requires two weeks. The report for this project is due at the beginning of the lab period following the second week. Please observe the required report format. The laboratory project is to write and run a program which will perform the addition of 2 sets of 16-digit decimal integers. In your program, you should include at least one subroutine named A16BCD, which can perform the BCD addition of two numbers of up to 16 decimal digits. The steps should include: 1) 2) 3) 4) 5) Define 2 sets of 16 decimal digits in ASCII code in the memory. Convert them into packed BCD digits. Pass on the stack the number of digits in the numbers to be added, named BCDN, the locations of the two numbers to be added, and the location where the sum should be stored. Call the subroutine A16BCD to perform the BCD addition. Convert the results back into ASCII and save in memory. The results should be visible from CodeView. Pre-lab This is a fairly in-depth and lengthy project, which means you will learn a lot from it. Therefore, it is recommended that you do it well ahead of time. The best way to answer these questions is to experiment with the steps using PWB and CodeView. Part 1. Decimal arithmetic (due at the beginning of the first lab period)(15 pts) The 80x86 family instruction set provides several instructions to aid in performing decimal arithmetic. Among these are AAA (ASCII Adjust AL after Addition) and DAA (Decimal Adjust AL after Addition). AAA is used when adding ASCII digits or unpacked BCD digits, and DAA is used for adding packed BCD digits. Both work only with the AL register, so we can add only one-byte numbers in each step. In both cases, the adjust instruction is used after an ADD or ADC instruction to convert a 4 or 8 bit hex number to a 4 or 8 bit BCD number (with a possible carry). When adding unpacked BCD digits or ASCII digits and a number greater than 10 results, the AAA instruction will add 1 to AH, set the C flag, and clear the upper 4 bits of AL. This allows the programmer to convert the result back to ASCII by adding 3030H, or leave it in unpacked BCD form in AX. For adding multi-byte packed BCD numbers, you must take into account the value of the C flag when adding the digits to the left of the first 2. Here is an example of a program to add two 3-digit unpacked BCD numbers: U1 U2 DB DB 0H, 5H, 2H, 1H 0H, 4H, 8H, 4H MOV MOV AX,CS DS,AX MOV ADD AAA MOV MOV ADC AAA MOV MOV ADC MOV AAA MOV MOV AL,U1+3 AL,U2+3 ;the first 0H is to store the carry ; add U2+3 to U1+3 ; this makes ds equal to cs U1+3,AL AL,U1+2 AL,U2+2 ;add U2+2 to U1+2 U1+2,AL AL,U1+1 AL,U2+1 AH,0H ;add U2+1 to U1+1 U1+1,AL U1,AH ;note: use add with carry ;clear AH before adjustment ;set the leftmost digit of U1 Similar techniques can be used for adding multi-byte packed BCD numbers, with a few minor differences. First, you must use DAA instead of AAA. Second, you can process two digits at a time. Third, unlike AAA, DAA does not add 1 to AH if there is a carry out of AL. Thus, you must depend on the C flag to tell you if the last addition produced a carry that must be copied to the leading digit of the result. Modify the above program for adding two 3-digit packed BCD numbers such as the following: P1 DB 00H, 56H, 43H, 21H P2 DB 00H, 45H, 68H, 54H Part 2. Using the stack to pass parameters. (due at the beginning of the second lab period) (15 points) 1. Before using a stack, you must define a stack segment and allocate some memory to it. Write as many statements as necessary to define a stack with 256 bytes of memory. Remember to tell the assembler about the existence of a stack segment. 2. One of the daunting tasks in using a stack is to keep track of the stack pointer (i.e. SP register). Erroneous results may be obtained without knowing it if SP is not handled correctly. The SS register defines the base address of the stack segment, while the SP register indicates where the top of the stack is. The storage and retrieval of data from the stack is done in a Last-InFirst-Out (LIFO) manner. When a datum is pushed onto the stack, the SP is decremented by 2. On the other hand, when a datum is popped, the SP is incremented by 2. Experiment with the stack and stack pointer with the following steps: 1. LOAD AX = AAAAH, BX = 5555H, CX = 0003H 2. What is the content of SP initially? 3. What is the content of SP and CX after executing each of the following statements? PUSH AX PUSH BX POP CX 4. It may be helpful to keep track of the data stored on the stack by means of a memory map drawn with the SS at the bottom, the initial SP at the top, and the data filled in at each location as data is pushed and popped to/from the stack. Draw such a map for step three. 5. Repeat the tasks in step 3 and 4 to keep track of SP and data in the stack after executing the following statements: PUSH CX CALL proc1 POP CX ;define a procedure called proc1 proc1 PROC POP CX PUSH AX RET proc1 ENDP Expect to see the SP changed after the CALL and RET statements. Two important observations may be made here. First, you may notice that the POP CX statement inside proc1 will actually get something you may not expect. Explain what that data is. If what you really want to get is the value in CX before the CALL statement, what value of SP should be used to get the correct data? Second, after executing the RET statement, you may notice that the program doesn’t return to the POP CX statement, but to location AAAAH (i.e. AX). Why is that? Can you suggest a solution to solve the above two problems? 3. You see, it is not an easy task to use just the SP when using subroutines. It has been suggested, however, that the use of an additional register called BP (base pointer) would make things easier. Look carefully at the following example, which illustrates a process called setting up a stack frame. PUSH CX CALL proc1 POP CX proc1 proc1 PROC PUSH BP ;save old BP MOV BP,SP ;save old SP SUB SP,n ;reserve n (even) bytes of local storage if needed PUSH BX ;save other registers if they are to be used PUSH DX MOV CX, [BP+4] MOV [BP+4], AX POP DX ;restore other registers if saved POP BX MOV SP,BP ;restore old SP POP BP ;restore old BP RET ENDP Several things are done here in order to make the procedure as general and error-proof as possible: First, the BP is saved on the stack in case the calling program had something important in there. (This step can be omitted if you are sure that using BP won’t cause a problem somewhere else.) Second, the old content of SP is copied to BP so that it can be restored later, after the subroutine is finished. Third, an even number is subtracted from the new SP to reserve space for local variables on the stack, if they are needed. By doing this, the local variables will not be lost if the subroutine is interrupted before it is finished. ( This step can be omitted if not needed.) Fourth, if other registers are used by the subroutine, they are saved on the stack to be restored later. Fifth, only a normal indirect move with the BP is used to access local variables and the parameters placed on the stack by the calling program. In this way, the old SP can be restored at the end, and the RET statement can find the return address accurately. In the lab project, you will need to pass 3 pieces of information to the subroutine and get 2 pieces of information back. Modify the above program to do just that. (You may leave out the optional steps if you think you don’t need them, but be sure you understand what their purpose is.) Use variable operands instead of registers to specify the location of the data.