ELET2100 - Microprocessors I

What is Assembly Language?

Inside the 8085, instructions are really stored as binary numbers, not a very good way to look at them and extremely difficult to decipher. An assembler is a program that allows you to write instructions in, more or less, English form, much more easily read and understood, and then converted or assembled into hex numbers and finally into binary numbers.

The program is written with a text editor (NOTEPAD or similar), saved as an ASM file, and then assembled by the assembler (TASM or MASM or similar) program. The final result is an OBJ file you download to the 8085. Here is an example of the problem of adding 2 plus 2:

mvi A,2          ; move 2 into the A register
mvi B,2          ; move 2 into the B register
add B       ;add reg. B to reg. A, store result in reg. A

 

The first line moves a 2 into register A. The second moves a 2 into register B. This is all the data we need for the program. The third line adds the accumulator with register B and stores the result back into the accumulator, destroying the 2 that was originally in it. The accumulator has a 4 in it now and B still has a 2 in it. In the program above all text after the ‘;’ are treated as comments, and not executed. This is a very important habit to acquire.

Assembly language follows some rules that I will describe as they come up. With most instructions, especially those involving data transfer, the instruction is first, followed by at least 1 space, then the destination followed by a comma, and then the source. The destination is where the result of the instruction will end up and the source is where the data is coming from.

Next we will read a switch, and light an LED if the switch is pressed. This happens quite often in your lab experiments. Bit 0 of Port 0 will be the switch. When the switch is closed or pressed, bit 0 will be a 1, and if the switch is open or not pressed, bit 0 will be a 0.

Bit 0 of Port l be the LED. If bit 0 is a 0 the LED is off and if bit 0 is a 1, the LED will be on. All the other bits of reg. A will be ignored and assumed to be all 0's, for the sake of discussion

start:      IN            0                              ; read Port 0 into reg. A

                CMP       1                              ; compare reg. A with the value 1

                JNZ         start                       ; jump to start if the comparison does not yield 0

                OUT       1                              ; send a 1 to Port 1, turning the LED on

                JMP        start

The first line has something new. It's called a label. In this case it is start: . A label is a way of telling the assembler that this line has a name that can be referred to later to get back to it. All labels are followed by the symbol : , which tells the assembler that this is a label. In the first line we also read the switch by reading Port and putting it into the accumulator. Reg. A is the only register that can read in/send out data via ports or perform compares. Thus, we need not write ‘A’ in the command…it’s implied!

The next line compares the value in reg. A with the value 1. If they are equal, the Zero flag is set (to 1). The next line then jumps to start: only if the Zero flag is not set ie: the value in reg. A is not 1 therefore the switch was not pressed. The program will therefore keep looping until the switch is pressed!

If the switch is pressed then the penultimate line writes the value 1 to the accumulator, therefore bit 0 = 1, and the LED comes on.

The last line jumps back to start. This completes the loop of reading the switch and writing to the LED.

This particular problem could have been solved with just a switch connected to an LED, like a light is connected to a wall switch in your house. But with a micro in the loop, much more could be done. We could have a clock that also turns on and off the LED based on time. Or we could monitor the temperature and turn the LED on and off based on what temperature it is. Or we could monitor several switches and turn the LED on and off based on a combination of switches, etc….it’s up to the imagination what can be controlled.

In the above example we assumed that the other bits of ports 0 and 1 were all zeros. But in reality, each of these bits could have a function assigned to them. Then we would need to look only at bit 0 in port 0 and bit 0 in port 1. This further complicates the problem.  Also, we assume that port 0 was previously defined as an input port whereas port 1 was defined as an output port.

In assembly we can assign a name to a port and refer to it by that name, instead of port 0 or port 1. This is done with an equate directive. Directives are assembler commands that don't result in program but instead direct the assembler to some action. All directives start with a period.

       .equ  switch, 0       ;port 0 is now called switch

       .equ  LED,1           ;port 1 is now called LED

start:      IN            switch                    ; read Port 0 into reg. A

                CMP        1                              ; compare reg. A with the value 1

                JNZ         start                       ; jump to start if the comparison does not yield 0

                OUT       LED                        ; send a 1 to Port 1, turning the LED on

                JMP        start

This has the same result as the previous program. Also the equate only has to be made once at the start of the program, and thereafter the name or label is used instead of the port number. This makes things much simpler for the programmer. All equates must be defined before they are used in a program. This holds true for labels also. Another advantage of naming ports with an equate is that if, later in the design process, you decide to use a different port for the LED or the switch, only the equate has to be changed, not the program itself.

Please note that comments are very important. When you initially write a program, the tendancy is not to write much in the comment field because you're in a hurry. But if you have to come back to it a few weeks later, it's much easier to understand what you've written if you've taken the time to write good comments. Also good comments help in debugging.

To digress just a little here, an instruction like add B is a one byte instruction. In other words this instruction would end up inside the 8085 as one byte. Part of the byte is the opcode and the other part is which register is affected or used. The reason for this is that a prime concern in programming a micro is how may bytes the program will actually take up inside the micro, after it's been assembled. The idea is to cram as much as possible into as few bytes as possible. This is why implied addressing is used. It limits choices in the use of the instruction, you always have to use the accumulator as either the source or the destination, but it shrinks the size of the instruction, so that more instructions can fit inside the micro. This is a choice made by the maker of the micro, and is not up for discussion. It's a trade off of flexibility vs. size. That's why you'll see lots of instructions that use the accumulator. This is the best way to describe implied addressing.

In the case of an instruction like mvi A,1 ,two bytes are assembled. The first byte says that this is an move instruction and that the accumulator is the destination. The second byte is the immediate data itself. Thus we see that an instruction can have it’s data ‘next’ to it. It is transparent to the programmer where the bytes are actually stored in memory. Once we can ‘find’ it by an instruction is all that matters. We will get into this again, later.

Another form of addressing variables is called register indirect or just plain indirect addressing. This is a little more complicated. Here the address is held in a register, usually the H & L registers but sometimes the B & C or D & E. Since an address is 16 bits long, we need two registers (a register pair) to store an address. We will also get into this again, later.

Lastly, I want to explain something else about the assembler. The source file is what the above program, or any program that has been written, is referred to. It is the source for the assembler, or the file that is going to be read by the assembler to generate the object file (the object of the assembler) from. The object file is the file that will be download to the 8085 kit in the lab. They are two different files. One you've written with a text editor (the source or ASM file) and the other is created by the assembler (the object or OBJ file) when you assemble the source file. You use an assembler with the object in mind of generating a file to download to the micro, hence the name, object file.

I've left out some directives, for simplicities sake, that I need to mention now. One is the .org directive. It is the originate or origin directive. This tells the assembler at what address the first byte of assembled code is to be placed inside the 8085. It is the origin of the program or the beginning. Here's how this would look for our last example program:

        .org 2000H      ; begin using memory address 2000H

Well we've covered quite a lot in this lesson, and I hope you've gotten most of it. If not, I would suggest re-reading it until you do. I would also suggest that you print out all of these lessons so you can refer to them later. In the next lesson we will actually be assembling some programs and looking at the object files.

On to lesson 7

Table of Contents