Файл: Introduction to Microcontrollers. Architecture, Programming, and Interfacing of the Motorola 68HC12 (G.J. Lipovski, 1999).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 14.06.2025
Просмотров: 1413
Скачиваний: 0
88 |
Chapter 4 Assembly Language Programming |
An assembler is a program someone else has written that will help us write our own programs. We describe this program by how it handles a line of input data. The assembler is given a sequence of ASCII characters. (Table 4.1 is the table of ASCII characters.) The sequence of characters, from one carriage return to the next, is a line of assembly-language code or an assembly-language statement. For example,
( space) LDAA ( space) #$ 10 (carriage return) |
(I) |
would be stored as source code in memory for the assembler as:
The assembler outputs the machine code for each line of assembly-language code. For example, for line (1), the assembler would output the bytes $86 and $10, the opcode byte and immediate operand of (1), and their locations. The machine code output by the assembler for an assembly-language program is frequently called the object code. The assembler also outputs a listing of the program, which prints each assembly-language statement and the hexadecimal machine code that it generates. The assembler listing also indicates any errors that it can detect (assembly errors). This listing of errors is a great benefit, because the assembler program tells you exactly what is wrong, and you do not have to run the program to detect these errors one at a time as you do with more subtle bugs. If you input an assembly-language program to an assembler, the assembler will output the hexadecimal machine code, or object code, that you would have generated by hand. An assembler is a great tool to help you write your programs, and you will use it most of the time from now on.
In this chapter you will look at an example to see how an assembly-language program and assembler listing are organized. Then you will look at assembler directives, which provide the assembler with informationabout the data structure and the location of the instruction sequence but do not generate instructions for the computer in machine code. You will see some examples that show the power of these directives. The main discussion will focus on the standard Motorola assembler in their MCUez freeware.
At the end of this chapter, you should be prepared to write programs on the order of 100 assembly-languagelines. You should be able to use an assembler to translate any
92 |
Chapter 4 Assembly Language Programming |
||||||
1 |
1 0000 |
ORG |
$868 |
||||
2 |
2 |
0868 |
0000 0003 |
N: |
EQU |
3 |
|
3 |
3 |
0868 |
RESULT: |
DS.B |
2 |
||
4 |
4 |
086A |
Z: |
DS.B |
50 |
||
5 |
5 089C CE086A |
LDX |
#Z |
; Point X to Z |
|||
6 |
6 089F CD0003 |
LDY |
#N |
; get count |
|||
7 |
7 08A2 EC31 |
LDD |
2,X+ |
; Z(0) into D |
|||
8 |
8 08A4 |
181A31 |
LOOP: |
EMAXD 2,X+ |
; D- Z(i) |
||
9 |
9 08A7 |
0436FA |
DBNE |
Y,LOOP |
; Another number? |
||
10 |
10 |
08AA |
7C0868 |
STD |
RESULT |
; Store result |
|
11 |
11 |
08AD |
00 |
BGND |
; Halt |
||
Figure 43. Assembler Listing for the Program MAX
The listing, shown in Figure 4.3, generally mirrors the source code but includes machine code and storage information. The listing line begins with a pair of line numbers. The first number is an absolute line number used for error messages, and the second is a relative line number used for include files and macro expansions discussed in the next chapter. The hexadecimal location of the instruction is given next; then the hexadecimal machine code is displayed. Finally,the source code line is shown.
4.2 Assembler Directives
Before looking more closely at how the assembler works, we describe the simplest assembler directives. These are instructions to the assembler that do not result in any actual executable machine coded instructions but are, nevertheless, essential to providing information to the assembler. A number of these will be introduced in this section and are listed in Table 4.3 for your convenience.
If we go back to the example at the beginning of the chapter, we recall that what we wanted was to just write down the mnemonics column and let the assembler generate the memory locations and their contents. There must be some additional information given to the assembler; in particular, you have to tell the assembler where to start putting the program or store the variables. This is the purpose of the ORG(for ORiGin) directive. The mnemonic ORG appears in the operation column, and a number (or expression) appears in the operand column. The number in the operand column tells the assembler where to start putting the instruction bytes, or reserved bytes for variables, that follow. For example, if the assembler puts the three bytes for LDX #123 in locations 100,101, and 102, the bytes for the instructions that follow are put consecutively in locations 103, 104, . . .. The operand can be described in decimal, hexadecimal, or binary, following Motorola's usual conventions. Thus we could replace the ORG directive above by
ORG 256
If there is no ORGdirective at the beginning of your program, the assembler will start at memory location 0. There can be more than one ORG directive in a program. ABSENTRY sets the entry point, the initial value of the PC, in the HIWAVE debugger, when a program is loaded, so you don't have to enter the PC each time you load it.
96 |
Chapter 4 Assembly LanguageProgramming |
||||||
1 |
1 0000 |
ORG |
$868 |
||||
2 |
2 |
0868 |
* this program squares the number N between 0 and 15 |
||||
3 |
3 |
0868 |
0001 |
N: |
EQU |
I |
|
4 |
4 |
0868 |
NSQ: |
DS.B |
1 |
||
5 |
5 0869 00010409 |
TABLE: |
DC.B |
0,1,4,9,16,25,36,49,64,81 |
|||
086D |
10192431 |
||||||
0871 |
40516479 |
||||||
0875 |
90A9C4E1 |
||||||
6 |
6 |
0879 |
CE0869 |
LDX |
#TABLE |
; POINT X TO TABLE |
|
7 |
7 |
087C C601 |
LDAB |
#N |
; PUT N INTO B |
||
8 |
8 087E |
A6E5 |
LDAA |
B,X |
; PUT N**2 INTO A |
||
9 |
9 |
0880 |
7AO868 |
STAA |
NSQ |
; STORE RESULT |
|
12 |
12 |
088F |
00 |
BOND |
|||
Figure 4.4. Assembler Listing for the Program Square
4.3 Mechanics of a Two-Pass Assembler
Some questions will soon arise about how symbolic addresses can be used without error. These questions have to be answered in terms of forward references, and their answers have to be understood in terms of how an assembler generates its output in two passes. Although we do not study how to write an assembler program (except in problems at the end of the chapter), we do want you to get a feeling for how it works so that you can understand how forward references are limited by what a two-pass assembler can do.
How does an assembler work? We begin by reading down through the instructions, called a pass. The first pass builds a symbol table, a list of all the symbolic addresses for labels and their values. The second pass will generate both the listing that shows the effects of each assembler line and the object code that is used to run the program.
We have earlier used the symbol "*" for the location counter. The location counter keeps track of the address where the assembler is when it is reading the current assemblylanguage statement, somewhat like the program counter does when the program runs. The location counter symbol "*" is always the address of the first byte of the instruction. In both passes, the location counter advances as code is generated.
The assembly-language program of Figure 4.5 finds all the odd, negative, 1-byte integers in the array COLUMN and puts them into the array ODD. On the first pass, the ORG statement sets the location counter to $800. Thus the label N has the value $800, the label M has the value $801, the label COLUMN has the value $802, and the label ODD has the value $834. The instruction CLR M will take three bytes (and we know what they are), the instruction LDAB N will take three bytes (and we know what they are), and so forth. Similarly, we see that the first byte of instruction
LOOP: LDAA 1,X+
will be at location $872. Thus the symbolic address (the container) LOOP has the value $872. Continuing in this way, we come to
BPL JUMP
4.3 Mechanics of a Two-Pass Assemblers |
97 |
*This program searches the array COLUMN looking for odd, negative,
*one-byte numbers which then are stored in array ODD. The length of
*COLUMN is N and the length of ODD is M, which the program calculates,
ORG $800
N:DS 1
M:DS 1
COLUMN: |
DS |
50 |
||
ODD: |
DS |
50 |
||
* |
||||
CLR |
M |
initialize M |
||
LDAB |
N |
Put N into B |
||
LDX |
#COLUMN |
Point X to COLUMN |
||
LDY |
#ODD |
Point Y to ODD |
||
LOOP: |
LDAA |
1, X+ |
Next number of COLUMN into A |
|
BPL |
JUMP |
Go to next number if positive |
||
BITA |
#1 |
Z = 1 if, and only if, A is even |
||
BEQ |
JUMP |
Go to next number if even |
||
STAA |
1, Y+ |
Store odd, negative number |
||
INC |
M |
Increment length of ODD |
||
JUMP: |
DBNE |
B, LOOP |
; |
Decrement counter; loop if not done |
BGND |
; |
Halt |
Figure 4.5. Program to Select Negative Odd Numbers
which takes two bytes in the program. We do not know the second byte of this instruction because we do not know the value of the address JUMP yet. (This is called a forward reference, using a label whose value is not yet known.) However, we can leave this second byte undetermined and proceed until we see that the machine code for DBNE is put into location $87f, thus giving JUMP the value $87f. As we continue our first pass downward, we allocate three bytes for DBNE B,LOOP. We do not find this instruction's offset yet, even though we already know the value of LOOP.
Scanning through the program again, which is the second pass, we can fill in all the bytes, including those not determined the first time through, for the instructions BPL JUMP, BEQ JUMP, and DBNE B,LOOP. At this time, all object code can be generated, and the listing can be printed, to show what was generated.
What we have described is a two-pass assembler. On the first pass it generates the symbol table for the program, and on the second pass it generates the machine code and listing for the program.
We have been using the prefix "<" in instructions like LDAA <N or a postfix ". B" such as in LDAA N. B to indicate an 8- or 9-bit addressing mode. If the prefix "<" or postfix ". B" is omitted, the assembler will still try to use 8-bit or 9-bit addressing when possible. Specifically, on the first pass, if the assembler knows the value of N when the instruction LDAA N is encountered, it will automatically use page zero addressing if N is on page zero. If it does not know the value of N yet, or if N is known but is not on page zero, it will then use direct addressing.