CSCI 2150 -- Laboratory Experiment
Introduction to Borland's Turbo Assembler
and Assembly Language Programming


Our First Assembly Language Program

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.

0Begin with an empty register
5Add five once
10Add five again (second time)
15Add five again (third time)
20Add 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
?
1
AL
0 1 1 0 1 0 0 1
0 1 1 0 1 0 0 1
BH
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
BL
1 0 1 1 0 1 0 1
0 0 0 1 1 1 1 0

Before adding AL to BL
After adding AL to 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
0
1
2
3
4
5
6
7
8
9
10
11
12
13
CF
?
0
0
0
0
1
1
1
1
0
0
0
0
0
AX
03h94h
03h94h
03h94h
02h94h
02h94h
02h94h
02h94h
01h94h
01h94h
01h94h
01h94h
00h94h
00h94h
01BCh
BX
00h00h
00h94h
00h94h
00h94h
00h94h
00h28h
01h28h
01h28h
01h28h
00hBCh
01hBCh
01hBCh
01hBCh
01hBCh
Comments
Clear BX
Add AL to BL
Add carry to BH
Decrement AH
If AH<>0, loop again
Add AL to BL
Add carry to BH
Decrement AH
If AH<>0, loop again
Add AL to BL
Add carry to BH
Decrement AH
Since AH=0, don't loop again
Copy result to AX

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

Creating an Executable Program

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.