EE314 – Microprocessor Systems

advertisement
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.
Download