So we're going to begin programming in assembly language. We've done a little of this in class, but there's nothing like doing it for yourself to reinforce the lecture material.
The process will go something like this:
Your assignment is to write a program that assumes the x86 has no multiply function. Your program will multiply the value in AL (low half of AX) by the value in AH (high half of AX) and place the result in the combined register AX. This will be done by adding AL AH times. As an example, the table below multiplies 5 times 4 by adding 5 four times.
0 | Begin with an empty register |
5 | Add five once |
10 | Add five again (second time) |
15 | Add five again (third time) |
20 | Add five fifth and last time for final result |
We, however, are going to use the registers of the 8086 to do this. When the processor begins executing your code, it will assume that the values to multiply will already be in AH and AL. I will start you out by doing a quick example of how I would do the code.
My example will perform the additions in the register BX, so the first thing I need to do is clear the BX register. Once it's cleared, I can add the number in AL to BX by the number of times represented by the number in AH. In pseudo code, this would look something like:
store BX in a temporary location (stack)
clear BX
do while AH not equal to zero
{
BX = BX + AL
decrement AH
}
copy BX to AX
restore old value of BX from the stack
The first step is simple. Simply copy the constant zero into the register BX. Once BX is clear, I can add AL to it AH times. There is a problem though.
Addition in the 8086 processor is either 8-bit or 16-bit, but not both. In other words, an 8-bit register can be added to an 8-bit register with an 8-bit result or a 16-bit register can be added to a 16-bit register with a 16-bit result, any carry bit being placed in the carry flag, CF.
AL is 8 bits wide and BX is 16 bits wide, and therefore, the two registers cannot be added together. AL can, however, be added to BL. This posses a problem. Any carry from the addition of AL and BL will not move into the lowest bit of BH as it should. Huh? Take a look at the example below where I add a 16-bit number to an 8-bit number:
1 1 1 1
0 0 0 0 0 0 0 0 1 0 1 1 0 1 0 1
+ 0 1 1 0 1 0 0 1
--------------------------------
0 0 0 0 0 0 0 1 0 0 0 1 1 1 1 0
Notice that a carry occured from the 8th bit position to the 9th bit position. If these two numbers were added in the 8086 processor as 8 bit values, it would have the following result.
CF
|
AL
|
BH
|
BL
|
|
Notice that the carry that was supposed to be added to the low bit of BH went to the carry flag instead. Your code will have to handle this.
There is an instruction that will add two values and also add the carry. Your challenge is to figure out how to use this instruction to add the carry to BH.
Another challenge for the new assembly language programmers is to figure out how to perform the loop. More importantly, how do we perform the loop when AH is not equal to zero, but don't loop if AH is equal to zero.
In your list of instructions, you should find a command to compare two values. CMP dest, src compares the "src" to the "dest" and sets the processor flags to indicate the result of the compare. If the two values are equal, ZF is set. You should then find a list of possible "jump" commands. JMP jumps regardless of the condition of the flags. The remaining jump commands jump depending on the setting of the flags.
For example, JE jumps if ZF=1. After a compare, if the two values were equal, ZF will be one. Therefore, JE jumps if equal.
At the end of your loop, you should be performing a compare between AH and 0 to see if AH is equal to zero. If it isn't equal, use one of the jump commands to perform another addition.
In the end, your code should be able to perform something like the process shown in the table below:
Step
|
CF
|
AX
|
BX
|
Comments
|
In the end, AX contains 1BCh, the result of multiplying 3 times 9416. Note that at the point where AH equaled 0, we stopped looping and went to the last step where we copied BX to AX for the final answer. This will be done with those conditional jumps we discussed earlier.
There is one thing I did not included in the process above. If you use any other registers, make sure you save their values before changing them so that you can restore them at the end of the program. For example, I used BX, but in the end, I need to leave BX the way it was before I cleared it. You may use the instructions PUSH and POP to temporarily store the values on the stack.
One more thing: to denote hexadecimal numbers, end your number with a lowercase "h". For example, to load A1C516 into the AX register, use the command mov ax,0A1C5h.
There is a "blank" template for this program in your TD directory. It is called EX2.ASM, and it is duplicated below for your convenience. Use the DOS program EDIT to add your code to it.
DOSSEG
.MODEL SMALL
.STACK 100h
.DATA
.CODE
; Place your program in the area provided below. Your
; program should assume that the x86 has no multiply
; function. Multiply value in AL by the value in AH
; and return the result in the combined register AX.
; DO NOT MODIFY ABOVE THIS LINE!!!!!!!!!!!
; DO NOT MODIFY BELOW THIS LINE!!!!!!!!!!!
mov ah,4ch ;DOS terminate program function #
int 21h ;terminate the program
END
Now that we have our assembly language program, it is time to create an executable file from your code. In the TD directory of the hard drive of the PC you are working at, there should be two files for assembling and linking your code:
To assemble your code, you will use TASM. The syntax is:
TASM FILENAME
where FILENAME is the name of your assembly language file without the asm extension. For example, to assemble our file, you should type tasm ex2. This should result in the following output:
C:\TD>tasm ex2
Turbo Assembler Version 2.0 Copyright (c) 1988, 1990 Borland International
Assembling file: ex2.ASM
Error messages: None
Warning messages: None
Passes: 1
Remaining memory: 443k
It is quite possible that your first assembly will have errors. Next to each error is the line number that the error occurred on. Edit the file again examining the bad line to see what the error was. Fix it and assemble the file with TASM again.
Once you've successfully assembled the file, link it using TLINK. Like TASM, TLINK has the syntax TLINK FILENAME where FILENAME is the name of your object file without the obj extension. (Note that your object file will have the same filename as your assembly language file.) A successful link (and you should have one of those) will look like the following:
C:\TD>tlink ex2
Turbo Link Version 3.0 Copyright (c) 1987, 1990 Borland International
Lastly, run TD and load your program into memory. To test your code, load AX with the number 846h. (If you use a different number, keep AH small or we'll be here forever debugging code.) Execute your code by using F7 to step through the instructions.
Once you feel you have a successful program, call me over to verify it.
Developed by David Tarnoff for the Spring 2002 sections of CSCI 2150 at ETSU