A TUTORIAL FOR TMS320C54X DSP ASSEMBLY LANGUAGE

Document Sample
A TUTORIAL FOR TMS320C54X DSP ASSEMBLY LANGUAGE Powered By Docstoc
					ASSEMBLY LANGUAGE TUTORIAL

A TUTORIAL FOR TMS320C54X DSP ASSEMBLY LANGUAGE
INTRODUCTION
One way to gain hands-on digital signal processing (DSP) experience is to purchase a Texas Instruments TMS320C54x Development Kit. This kit contains a demonstration board, an assembler, and a databook. In order to make the demonstration board perform some useful function, you are required to write some software. This is where a problem arises. The TMS320C54x has a large number of instructions. Where is the best place to start? These tutorials have been written to take the first-time user through the TMS320C54x instructions, starting with the most commonly used instructions. No claim is made that the tutorials are exhaustive, but they will give you a good understanding of the operation of the TMS320C54x. From then on it should be possible for you to quickly pick up other more specialized instructions from the databook. The tutorials are aimed at more than one level of readership. At one level, they are for readers who have never used assembly language before, but may have knowledge of C programming. Analogies are therefore made using C code. At the other level, the tutorials are for readers who have used earlier Texas Instruments DSP devices, e.g., TMS320C25 and TMS320C2xx/5x, and wish to take advantage of the benefits offered by the improved architecture of the TMS320C54x. In each case, the tutorials provide detailed descriptions to augment those found in the databook. Elementary mistakes and wrong ways of doing things are also pointed out. As a form of self-test, a series of questions is provided at the end of each tutorial. The answers, unless stated otherwise, are found in the tutorial text. Two assumptions are made in these tutorials: that the reader understands hexadecimal numbering, and has had some exposure to high-level languages such as C or BASIC. The topics covered by the tutorials are:
Tutorial 1 2 3 4 5 6 7 Subject Simple Instructions to Move Data Indirect Addressing Simple Arithmetic and Logical Operations Arithmetic Operations Signed Numbers and Shifting Data Branch Instructions Loop Counters and Repeated Instructions Tutorial 8 9 10 11 12 13 14 Multiplications Multiplications with Accumulation Using Data Stored in ROM Bit operations Stack Operations Subroutine Calls Non-maskable Interrupts Subject

TUTORIAL

T-1

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 1:

Simple Instructions to Move Data

New Instructions Introduced LD LDM ST STM STL STH Overview of Tutorial The first tutorial of this series looks at a fundamental operation, that of moving data between data memory (RAM) and the two working registers named accumulator A and accumulator B. Loading the Accumulators Let us start with a simple operation, that of assignment. In the C language we can assign a value to the 32-bit variable x as follows: Example 1-1. unsigned long x = 1; // Load a 32-bit variable x with 1

In order to implement Example 1-1 using TMS320C54x assembly language, we shall use one of the two 40-bit working registers called accumulator A and accumulator B. For the purpose of this tutorial, we shall treat them both as 32-bit registers. To load either accumulator A or accumulator B with a constant, we use the instruction LD (load accumulator): Example 1-2. LD #1, A ; Load the constant 1 into accumulator A. ; Accumulator A now contains 00000001 ; hexadecimal.

The instruction LD (load accumulator) cannot be used on its own: it needs two additional pieces of information known as operands. The first operand uses the symbol # to indicate that the value is a constant. The second operand is referred to as the destination. By the term destination we mean where we are going to load the data. With the instruction LD (load accumulator) we have the choice of either accumulator A or accumulator B as the destination. In Example 1-2 we have used accumulator A as the destination. Should we wish to load the constant 255 into accumulator B we would write: Example 1-3. LD #255, B ; Load the constant 255 decimal into ; accumulator B.

TUTORIAL

T-2

ASSEMBLY LANGUAGE TUTORIAL Technically, using the instruction LD (load accumulator) with a constant is known as immediate addressing. The value following immediately after the symbol # is the constant to be loaded. The second operand supplying the name of the destination must be either accumulator A or accumulator B. To omit the accumulator type is incorrect, as shown in Example 1-4. Example 1-4. LD #33 ; Incorrect. Missing operand. Either ; accumulator A or accumulator B must be ; specified as the destination.

To represent a constant expressed as a hexadecimal number we write the letter h after the number. The letter h may be in upper case in lower case. Example 1-5. LD #64h, A ; Load the constant 64h (100 decimal) into ; accumulator A. Accumulator A now contains ; 00000064h.

It is illegal to start the first number of the constant with one of the hexadecimal characters A,B,C,D,E or F. These must be preceded by a 0, otherwise the assembler will take them to be labels rather than hexadecimal numbers and will not assemble them. Example 1-6. LD #3Fh, B LD #ACh, B LD #0ACh,A ; ; ; ; Correct. Letter F preceded by the number 3. Incorrect. Letter A must be preceded by 0. Correct. Letter A is preceded by the number 0.

If the letter h is accidentally left out, then the assembler treats the numbers as being of a different format: Example 1-7. LD #33, A ; ; ; ; Load accumulator A with the constant 33 decimal, rather than the constant 33 hexadecimal as was intended. Error. “Illegal octal constant”.

LD #0AA,B

Because the constant can be up to 16 bits long, the range of numbers we may load using the instruction LD (load accumulator) is 0 to FFFFh. Some samples of loading the accumulators are given in Example 1-8: Example 1-8. LD #1000, B ; Load accumulator B with the 16-bit ; constant 1000 decimal. Accumulator B now ; contains 000003E8h.

TUTORIAL

T-3

ASSEMBLY LANGUAGE TUTORIAL LD #0h, B LD #7FFFh, A ; ; ; ; ; Clear accumulator B. Accumulator B now contains 00000000h. Load accumulator A with 16-bit constant 7FFFh (32767 decimal). Accumulator A now contains 00007FFFh.

In Example 1-8 we have used the term clear to mean load the variable with 0. Loading Auxiliary Registers with Constants As part of its architecture, the TMS320C54x has eight auxiliary registers named AR0, AR1, AR2, AR3, AR4, AR5, AR6 and AR7. These are used for general purpose, for counters and for accessing data memory. The auxiliary registers AR0 to AR7 form part of what are known as memory-mapped registers. Within this group are the timers, all peripherals, auxiliary registers AR0 to AR7 and accumulators A and B. Here memorymapped means that the registers are treated as a special series of 16-bit data memory addresses. For the time being, the only memory-mapped registers we will concern ourselves with are the auxiliary registers AR0 to AR7. We can load a 16-bit constant into one of the memory-mapped registers. For this we use the instruction STM (store immediate value into memory-mapped register). Example 1-9. STM #199, AR3 ; ; ; ; ; ; Store the 16-bit constant 199 decimal in the memory-mapped register AR3. AR3 now contains 199 decimal, which in hexadecimal is 00C7h. Store the 16-bit constant 2245h in the memory-mapped register AR7.

STM #2245h, AR7

The operand following immediately after the symbol # is the 16-bit constant. Here the second operand is the destination (i.e., where we are going to store the constant), and is one of the auxiliary registers AR0 to AR7. Two operands are required, so the following is illegal: Example 1-10. STM #44h ; Illegal. Missing operand. An auxiliary ; register AR0 to AR7 must be specified.

When the assembler encounters the operands AR0 to AR7, it converts them to the numbers 10h to 17h, which represent the addresses in the memory-map. The following is therefore legal, but not recommended: Example 1-11. STM #44h, 10h ; Load constant 44h into auxiliary register ; AR0. Legal but not recommended.

TUTORIAL

T-4

ASSEMBLY LANGUAGE TUTORIAL There are two problems with Example 1-11. First, it is not immediately obvious that 10h corresponds to auxiliary register AR0. Anything not obvious increases the likelihood of making mistakes. Second, there is the issue of compatibility. Should a later version of the TMS320C54x use a slightly different memory-mapping, the wrong memory-mapped addresses will be used. Hence, the code will not be completely compatible.

Direct Addressing Normally in a program, the accumulators A and B would be used as working registers. The auxiliary registers AR0 to AR7 would usually be used for loop counters and to access data memory. In order to store more variables, we need access to more data memory addresses. For this we use a slightly different version of the instruction LD (load accumulator). To load accumulator A or accumulator B with the data from a data memory address, we can write: Example 1-12. LD @60h, A LD @61h, B ; ; ; ; Load accumulator A with the value contained at data memory address 60h. Load accumulator B with the value contained at data memory address 61h.

This is known as direct addressing. We load either accumulator A or accumulator B directly with the contents of a specified data memory address. When we load an accumulator, we in fact make a copy of the contents of the data memory address and put the copy into the accumulator. The original contents of the data memory address remain unchanged. Note that the syntax of immediate addressing (with the symbol #) is different to that of direct addressing (without the symbol #). A very easy mistake to make when using direct addressing is to forget to type the symbol # and this completely changes the meaning of the instruction: Example 1-13. LD #10h, A LD 10h, A ; ; ; ; ; ; ; Load the constant 10h into accumulator A using immediate addressing. Erroneous attempt to load the constant 10h into accumulator A. In fact loads the contents of data memory address 10h into accumulator A using direct addressing.

In order to make the code more clear and to reduce the possibility of making mistakes, a second syntax is available for direct addressing, as shown in Example 1-14.

TUTORIAL

T-5

ASSEMBLY LANGUAGE TUTORIAL Example 1-14. LD 20h, A ; ; ; ; ; ; First syntax. Load the contents of the data memory address 20h into accumulator A using direct addressing. Second syntax. Load the contents of the data memory address 20h into accumulator A using direct addressing.

LD @20h, A

Here the symbol @ is been taken to mean “at the address”. Using the symbol @ makes it clear that the intention is to use direct addressing. Both syntaxes are used in the Texas Instruments databooks, so the reader should be aware that two forms exist. The operand providing the direct memory address for the instruction LD must lie in the range 0 to 127 (7Fh). Any value outside this range will be taken as an error, as shown in Example 1-15: Example 1-15. LD -1, A LD @320h, B ; Illegal. Operand less than minimum allowed. ; Illegal. Operand greater than maximum ; allowed.

In the Texas Instruments databooks, the term direct memory address is often abbreviated to dma. Copying the Contents of an Auxiliary Register to an Accumulator Since auxiliary registers AR0 to AR7 are treated as memory-mapped registers, we can load a value from one of auxiliary registers AR0 to AR7 into either accumulator A or accumulator B using the instruction LDM (load from memory-mapped register): Example 1-16. LDM LDM AR0, A AR7, B ; ; ; ; Load the AR0 into Load the AR7 into contents of accumulator contents of accumulator auxiliary register A. auxiliary register B.

The instruction LDM (load from memory-mapped register) takes two operands. The first operand is one of the auxiliary registers AR0 to AR7. The second operand is the destination and can be accumulator A or accumulator B. The Structure of Data Memory Care must be taken when using direct addressing. Each data memory address is 16 bits, and hence lies in the range 0 to 65535 (0 to FFFFh). However, the operand used with the instruction LD provides only the lowest 7 bits of this address, and hence lies in the range 0 to 127 (0 to 7Fh). In short, the operand used for direct addressing only specifies part of the address. So what provides the other 16 - 7 = 9 bits of the data memory address? The remaining 9 bits are provided from another source known as the data memory page pointer (DP).

TUTORIAL

T-6

ASSEMBLY LANGUAGE TUTORIAL

At power up, the data memory page pointer (DP) is initialized to zero, which gives access to data memory addresses in page 0. To change the data memory page pointer (DP) we use a variation of the instruction LD. Example 1-17. LD #1, DP ; ; ; ; ; ; Load data memory page pointer (DP) with a 9-bit constant. The constant must lie in the range 0 to 511. For the TMS320C5402 use a value in the range 0 to 127 decimal for access to internal data memory addresses 0h to 3FFFh.

Note that the sign # is important, otherwise the value will be taken to be direct addressing. If we look at the memory map of the TMS320C5402, we see that the data memory locations are arranged in pages as follows: Table 1-1. Data Memory Addresses of the TMS320C5402
Pages Available Addresses (Decimal) Available Addresses (Hexadecimal)

0 1 2 3 to 126 127

96 to 127 128 to 255 256 to 383 384 to 16255 16256 to 16383

60h to 7Fh 80h to FFh 100h to 17Fh 180h to 3F7F 3F80h to 3FFFh

If using a device other than the TMS320C5402, it is recommended that the databook be checked in order to determine the range of valid data memory addresses. Note that in Page 0, data memory addresses 0 to 5Fh are reserved for memory-mapped registers and are unavailable as general-purpose data memory. Therefore, after we execute the following instruction LD #0, DP ; Load data memory page pointer with zero.

the only addresses available for general purpose data are 60h to 7Fh. As another example, using the instruction LD #4, DP gives us access to Page 4 addresses 200h to 27Fh.

Calculating Data Memory Addresses When using direct addressing, in order to carry out an operation on the data stored at a particular data memory address, we must first determine which memory page to use.

TUTORIAL

T-7

ASSEMBLY LANGUAGE TUTORIAL

Say we wish to use direct addressing to load the accumulator with the contents of data memory address 310h. To calculate the page number we need to divide 310h (784) by 80h (128) and round down to the nearest whole number. This gives us page number 6, which starts at address 718 (300h). We then add the offset supplied by the operand. Example 1-18. LD #6, DP ; ; ; ; ; Set data memory page pointer to Page 6. This gives us access to data memory addresses 300h to 37Fh. Load accumulator A with the contents of data memory address 300h + 10h = 310h.

LD @10h, A

A point of caution. Table 1-1 is for the TMS320C5402 and if a different device is being used, the data memory addresses available for general purpose may be at different addresses on the memory map. It is therefore important to check the actual memory map for the hardware device being used.

Moving Data Words to Data Memory The instruction LD (load) is used to move a value to either accumulator A or accumulator B. To move a value to a data memory address we use variations on the instruction ST (store). We can store an immediate value at a data memory address using the instruction ST (store). For example, to store the immediate value 100h at data memory address 3A0h we would write code as shown in Example 1-19: Example 1-19. LD #7, DP ST #100h, @20h ; ; ; ; Page 7. Gain access to data memory addresses 380h to 3FFh. Store the immediate value 100h at data memory address 380h + 20h = 3A0h.

To move data from an accumulator to a data memory address we use one of the store instructions STH (store accumulator high in data memory) and STL (store accumulator low in data memory). Say we wish to copy the contents of accumulator A to data memory addresses 3A0h and 3A1h. For this we need to select Page 7. Example 1-20. LD #7, DP ; Page 7. Gain access to data memory ; addresses 380h to 3FFh.

TUTORIAL

T-8

ASSEMBLY LANGUAGE TUTORIAL STH A, @20h STL A, @21h ; ; ; ; Store high 16 bits of accumulator A in data memory address 380h + 20h = 3A0h. Store low 16 bits of accumulator A in data memory address 380h + 21h = 3A1h.

The contents of the accumulator remain unchanged. We can also copy the contents of the accumulator to data memory in the reverse order by saving the low word then the high word: Example 1-21. LD #7, DP STL A, @20h STH A, @21h ; ; ; ; ; ; Page 7. Gain access to data memory addresses 380h to 3FFh. Store low 16 bits of accumulator A in data memory address 380h + 20h = 3A0h. Store high 16 bits of accumulator A in data memory address 380h + 21h = 3A1h.

The only difference between the two orders of storage is one of convention. To make the debugging of a project easier, it is best to stay with one convention throughout.

Upgrading from the TMS320C25 and TMS320C2xx/5x to the TMS320C54x. The TMS320C54x has a different architecture to that of the earlier Texas Instruments DSP devices in that it has two accumulators (A and B), rather than just one. Therefore new instructions have been added that support two accumulators. This means the earlier instructions LAC, LACK, LARK, LALK, LRLK and LDPK which only support one accumulator cannot be used. For each of the instructions used by the TMS320C25 and TMS320C2xx/5x there is an equivalent instruction for the TMS320C54x, as shown in Table 1-2: Table 1-2: Comparison of Instructions
Description Load 8-bit immediate Load 16-bit immediate Load direct Load auxiliary register 8-bit Load auxiliary register 16-bit Set data page Store accumulator low Store accumulator high Store immediate in data memory TMS320C25 Instruction LACK x LALK x LAC memory LARK ARx, x LRLK ARx, x LDPK x SACL memory SACH memory Not available TMS320C2xx and TMS320C5x Instruction LACL #x LACC #x LACC memory LAR ARx, #x LAR ARx, #x LDP #x SACL memory SACH memory SPLK #x, memory TMS320C54x Equivalent Instruction LD #x, Acc LD #x, Acc LD memory STM #x, ARx STM #x, ARx LD #x, DP STL Acc STH Acc ST #x, memory

Here the term x is used to represent a constant, memory represents a data memory address, ARx is used to denote one of the auxiliary registers AR0 to AR7 and Acc represents accumulator A or B.

TUTORIAL

T-9

ASSEMBLY LANGUAGE TUTORIAL Load and store instructions on the TMS320C54x require two operands, whereas the earlier devices required only one. The second operand is used to specify either accumulator A or accumulator B. On the TMS320C25/2xx/5x, auxiliary registers AR0 to AR7 are treated as special cases and hence have their own instructions LAR, LARK and LRLK. The architecture of the TMS320C54x has simplified matters by making the auxiliary registers part of the memory-mapped registers. Operations on the auxiliary registers AR0 to AR7 on the TMS320C54x are done in a completely different way. In fact, a load instruction on the TMS320C25/2xx/5x has been replaced with a store instruction on the TMS320C54x. An example of an instruction to load an auxiliary register on the TMS320C25/2xx/5x is shown in Example 1-22: Example 1-22. LARK AR0, 10h ; Load auxiliary register AR0 with ; constant 10h.

The same operation as carried out in Example 1-22 is shown using the TMS320C54x instruction in Example 1-23: Example 1-23. STM #10h, AR0 ; Store immediate value 10h in memory ; mapped register AR0.

Besides the different instructions to carry out operations on auxiliary registers, a further point of unfamiliarity is that the order of operands is reversed. Care also needs to be taken when first using the TMS320C54x to remember to type in the symbol # when using immediate data. If not, direct data will be stored. On the TMS320C25 and TMS320C2xx/5x, the assembler assigns the values 0 to 7 to the auxiliary registers AR0 to AR7. This means we can write: Example 1-24. LARK 0, 10 ; Load AR0 with constant 10.

This should not be used on the TMS320C54x because the memory mapping is different. On the TMS320C54x the auxiliary registers AR0 to AR7 are memory-mapped to locations 10h to 17h. In the following example, using the TMS320C54x mnemonic with reversed operands does not work as expected. Example 1-25. LARK 0, 10h STM #10h, AR0 ; Syntax for earlier devices. Load ; auxiliary register AR0 with 10h. ; Correct TMS320C54x syntax.

TUTORIAL

T-10

ASSEMBLY LANGUAGE TUTORIAL STM #10h, 0 ; ; ; ; ; ; Incorrect syntax for the TMS320C54x. An unsuccessful attempt to load AR0 with the immediate value 10h. In fact loads the immediate value 10h into memory-mapped register 0 which is the interrupt mask register (IMR)!

On the TMS320C2xx/5x, the term data memory address is abbreviated to dma. However, on the TMS320C54x, the abbreviation dma is used to mean direct memory address.

TUTORIAL

T-11

ASSEMBLY LANGUAGE TUTORIAL

Questions
1. 2. 3. What is meant by the term destination? What is meant by the term immediate addressing? The letter h is used as an abbreviation for: a) hexadecimal b) high c) home Which two of the following immediate addressing instructions are legal? a) LD #FF, A b) LD #70h, B c) LD #0E0h, A d) LD #66h, C e) LD #222h f) LD #0E3, B Write code to store the value 100h in auxiliary register AR6. What will be the contents of accumulator B after each of the following three instructions are executed? a) LD #7Fh, B b) LD 7Fh, B c) LD @71h, B Which two of the following instructions using direct addressing are legal? a) LD @123h, A b) LD -23, B c) LD @28h, A d) LD 127, B e) LD 128, C f) LD 14h What does the abbreviation dma stand for? Why do we only use data memory addresses 60h to 7Fh on page 0 for general purpose? Using direct addressing, how would we write 1000h to data memory address 230h? Why might we use the instructions STL then STH in that order? What is the difference between the instructions LD (load) and ST(store)?

4.

5. 6.

7.

8. 9. 10. 11. 12.

TUTORIAL

T-12

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 2:

Indirect Addressing

Overview of Tutorial In the previous tutorial we saw how to transfer data using direct addressing. This tutorial illustrates the usage of another method of moving data known as indirect addressing. Indirect addressing is an important mode of operation of the TMS320C54x and is well worth mastering. In fact, certain instructions only work effectively using indirect addressing. Analogy of Indirect Addressing The concepts of direct and indirect addressing can be easily explained in terms of the real-life scenario of a library. Say we require a certain book. If we know the number of the book then we can go to its location straight away. However, what happens if we do not know the number of the book? We would first look in the index system, find the book number and then go to its location. The book number and the number of a data memory address work in the same way. In terms of TMS320C54x assembly language, when using direct addressing, the operand gives the data memory address. This is the equivalent of the book number in the library analogy. Indirect addressing differs in that the operand does not give the data memory address, but tells us which auxiliary register contains this information. The auxiliary register is equivalent to the index system in the library analogy. The TMS320C54x makes use of auxiliary registers AR0 to AR7 as pointers to data memory addresses. Figure 2-1 shows the mechanism of indirect addressing for the TMS320C54x in diagrammatic form: Figure 2-1. Model of Indirect Addressing
Data Memory (RAM) 0000h 0000h 0000h 0000h 0000h 1000h FFFFh FFFFh FFFFh FFFFh 1FBh 1FCh 1FDh 1FEh 1FFh 200h 201h 202h 203h 204h

Auxiliary Registers AR0 AR1 AR2 AR3 AR4 AR5 AR6 AR7 0000h 1674h 0060h 0200h 0300h 13Fh 0005h 8000h

In order to work upon the contents of data memory (RAM) address 200h, we use auxiliary register AR3 to act as a pointer to this location.

TUTORIAL

T-13

ASSEMBLY LANGUAGE TUTORIAL A Model of Indirect Addressing Using the Analogy of C We can model the behavior of indirect addressing, as shown in Figure 2-1 using the analogy of C code. Let us look at the case of where we wish to load a 32-bit variable (named accumulator_A) with the contents of memory address 200h. Example 2-1. unsigned int *AR3; unsigned long accumulator_A; AR3 = 0x200; accumulator_A = *AR3; // // // // // // // // // AR3 is a pointer to a data memory location. Our 32-bit variable. Load AR3 with the address of memory location 200h. Load accumulator_A with the contents of data memory address 200h.

Please note that this is an analogy only, and we would never actually write code of this type. The important point to remember is that one of the auxiliary registers AR0 to AR7 contains the address of the data memory location we wish to work upon. Using Indirect Addressing Let us now perform exactly the same operation as we saw in Figure 2-1 and Example 2-1, but this time using assembly language. Let us load the accumulator with the contents of data memory address 200h using AR3 as the pointer to data memory. This requires two distinct operations and is shown in Example 2-2: Example 2-2. STM #200h, AR3 LD *AR3, A ; ; ; ; ; Store the address 200h in the memory -mapped register AR3. Load accumulator A with the contents of the data memory location whose address is contained in AR3.

The first operation is to select one of the eight auxiliary registers AR0 to AR7 as our pointer register and load it with the address of the data memory address we wish to use. For this we employ the instruction STM (store immediate value in memory-mapped register). Because AR0 to AR7 are 16-bit registers, the address is therefore 16-bit. This gives us access to any data memory address in the range 0 to 65535 (0 to FFFFh). The second operation in Example 2-2 is to load the accumulator with the contents of the data memory address to be found in the auxiliary register, here AR3. The symbol * is used with the instruction LD (load accumulator) to indicate that the auxiliary register AR3 is used as a pointer to a data memory address. In this particular case, but not always, the symbol * works in the same way as the operator * does in C for pointers.

TUTORIAL

T-14

ASSEMBLY LANGUAGE TUTORIAL Should we forget to put in the symbol * when working with indirect addressing, the meaning of the instruction changes completely and is shown in Example 2-3: Example 2-3. LD *AR4, A ; ; ; ; ; ; ; ; Correct syntax. Load accumulator A with the contents of the data memory address pointed to by AR4. The symbol * is missing. This will be interpreted by the assembler as direct addressing. Load accumulator A with the contents of the data memory location whose address is DP + AR4.

LD AR4, A

Syntax The instruction STM can be used with two forms of syntax, both of which are shown in Example 2-4: Example 2-4. STM #200h, AR3 ; ; ; ; ; ; ; Store the immediate value 200h in memory-mapped register AR3. AR3 now contains 200h. contains ; 200h. Store the immediate value 200h in memory-mapped register AR3. AR3 now contains 200h.

STM 200h, AR3

In both cases, the value supplied as the operand is treated as immediate data and is stored in the auxiliary register. However, what that value means depends upon the context. It may represent a number or it may represent an address in data memory. To distinguish between the two different ways of using the instruction, readers may wish to use the convention that an operand supplied with a # sign is taken to be a number while an operand without the # sign is taken to be an address. However, the clearest way is to comment the code. Example 2-5. STM #200h, AR3 STM #200h, AR3 ; ; ; ; Store immediate value 200h in auxiliary register AR3. AR3 = 200h. Store address 200h in auxiliary register AR3. AR3 = 200h.

Development tools can use either syntax, so readers need to be aware that two forms exist. Differences between Direct and Indirect Addressing We have already seen how to load the accumulator with the contents of a data memory address using the instruction LD (load accumulator) and direct addressing, as shown in Example 2-6:

TUTORIAL

T-15

ASSEMBLY LANGUAGE TUTORIAL Example 2-6. ; Load data pointer (DP) with 1 to address ; page 1. Gain access to data memory ; addresses 80h to FFh. LD 0h, B ; Direct addressing. Load accumulator B ; with the contents of data memory address ; 80h + 0h = 80h. We shall now carry out exactly the same operation as shown in Example 2-6, but this time using indirect address. In this case the instruction LD is used in a slightly different way, as shown in Example 2-7: Example 2-7. STM #80h, AR3 LD *AR3, B ; ; ; ; ; ; ; Store address 80h in auxiliary register AR3. Indirect addressing. Load accumulator B with the contents of the data memory address pointed to by AR3. AR3 contains the address 80h. the address 80h. LD #1, DP

In Example 2-7, the symbol * is used to indicate that the auxiliary register contains the address of a data memory address. However, care needs to be taken not to read too much into the symbol *. It simply means that the operation is based upon the contents of the auxiliary register. How it is used is taken from the context of the instruction. When used with the instruction LD (load accumulator), the symbol * is used to indicate indirect addressing. However, there are other instructions where the symbol * does not mean indirect addressing. Examples of these instructions will be covered in later tutorials? Advantages of Indirect Addressing Indirect addressing offers two advantages over direct addressing. First, the auxiliary register contains the full 16-bit data memory address, whereas direct addressing provides only 7 bits. To guarantee correct operation when using direct addressing, a separate instruction must be executed to set the remaining 9 bits of the address. The high 9 bits of the address are stored in the data memory page pointer (DP). When performing an operation using direct addressing and two different data memory pages, it is possible to forget to change the data memory page pointer and therefore address the wrong data word. The second advantage of using indirect addressing is that it provides a mechanism to modify the contents of the auxiliary register as part of the instruction, an example of which is shown in Example 2-8. Example 2-8. STM #200h, AR4 ; Store the address 200h in auxiliary ; register AR4.

TUTORIAL

T-16

ASSEMBLY LANGUAGE TUTORIAL LD *AR4+, B ; ; ; ; ; Load accumulator B with the contents of the data memory address pointed to by AR4 (200h). Then increment AR4 to point to the next data memory address (201h).

Here we have used the symbol *AR4+ to mean that the auxiliary register contains the value to be used as the basis of our operation and that after doing the operation, we increment the contents of the auxiliary register. Note that the operation *AR3+ increments the address of the data memory location, but does not increment the data contained at the data memory address. The C code analogy of Example 2-8 is shown in Example 2-9.: Example 2-9. unsigned long accumulator_B; unsigned int *AR4 ; AR4 = 0x200; accumulator_B = *AR4++; // // // // // // // // // // Accumulator B. AR4 is a pointer to memory. AR4 points to address 200h. Load accumulator B with the contents of data memory address 200h, then increment the pointer to address 201h.

With TMS320C54x assembly language, should we wish to decrement the contents of the auxiliary register while using indirect addressing to load accumulator A we would write: Example 2-10. STM #300h, AR3 LD *AR3-, A ; ; ; ; ; ; ; Store address 300h in auxiliary register AR3. Load accumulator A with the contents of the data the memory address pointed to by AR3 (300h). Decrement AR3 to point to the adjacent data memory address (2FFh).

The C code analogy would be as shown in Example 2-13. Example 2-11. unsigned long accumulator_A; unsigned int *AR3 ; AR3 = 0x300; // // // // // Accumulator A. AR3 is a pointer to a word in memory. AR3 points to address 300h.

TUTORIAL

T-17

ASSEMBLY LANGUAGE TUTORIAL accumulator_B = *AR3--; // // // // // Load accumulator A with the contents of memory address 300h, then decrement the pointer to 2FFh.

When using the instruction STM (store immediate value in memory-mapped register), the increment and decrement operators cannot be used.

TUTORIAL

T-18

ASSEMBLY LANGUAGE TUTORIAL Example 2-12. STM #500h, *AR1 STM #500h, *AR1+ ; ; ; ; ; ; ; ; ; ; ; ; Correct syntax. Store immediate value 500h in memory-mapped register AR1. Incorrect syntax. Attempt to store the immediate value 500h in auxiliary register AR1 then to increment the value in AR1. Correct syntax. Store immediate value 30h in memory-mapped register AR2. Incorrect syntax. Attempt to store the immediate value 30h in auxiliary register AR2 then to decrement the value in AR2.

STM #30h, *AR2 STM #30h, *AR2-

Saving the Contents of the Accumulator in Data Memory Indirect addressing can also be applied to instructions such as STL (store accumulator low in memory) and STH (store accumulator high in memory). An example of both of these instructions is given in Example 2-13: Example 2-13. STL A, *AR2 ; ; ; ; ; ; Store the low word of accumulator A in the data memory address pointed to by AR2. Store the high word of accumulator A to the data memory address pointed to by AR4.

STH A, *AR4

We can also apply the assembly operators of the type *AR3+ and *AR3- to the instructions STH and STL. For example, to store the low word of the accumulator A at data memory address 300h and the high word of the accumulator B at data memory address 301h we can write: Example 2-14. STM #300h, AR2 STL A, *AR2+ ; ; ; ; ; ; ; ; ; ; Store the address 300h in auxiliary register AR2. Store the low word of accumulator A at the data memory address pointed to by auxiliary register AR2 (300h). Increment AR2 to point to the data memory address 301h. Store the high word of accumulator A at the data memory address pointed to by AR2 (301h).

STH A, *AR2

By incrementing the current auxiliary register as part of the instruction STL (store accumulator low into memory), we are then ready to work on the next data memory address.

TUTORIAL

T-19

ASSEMBLY LANGUAGE TUTORIAL

More Complex Indirect Addressing What would happen if we were to attempt to load accumulator A using indirect addressing but forget to put in the name of the auxiliary register AR0 to AR7, as shown in Example 2-15? Example 2-15. LD *, A ; Use indirect addressing to load accumulator ; A without specifying an auxiliary register.

Even though there is no named auxiliary register, this instruction will assemble correctly and run without error. This is because when no auxiliary register is specified, auxiliary register AR0 is used as the default, that is, under normal circumstances. However, this form of syntax is not recommended and it is better to put in the name of the auxiliary register. Compatibility with Earlier Devices Indirect addressing on the TMS320C25, TMS320C2xx and TMS320C5x functioned in a different way to that of the TMS320C54x. For backwards compatibility, the TMS320C54x can be configured to operate in the same way as earlier devices under the control of the compatibility (CMPT) bit in Status Register ST1. This mode of operation is not normally used with the TMS320C54x but is included here so that readers can understand and make use of application notes written for earlier devices. When the CMPT = 1 for compatibility with earlier devices, the same syntax as shown in Example 2-15 is used, but which of the auxiliary register used for indirect addressing is determined from a configuration variable known as the auxiliary register pointer (ARP). The auxiliary register pointer (ARP) can be loaded with a value between 0 and 7, corresponding to one of the auxiliary registers AR0 to AR7. To make one of the TMS320C54x auxiliary registers AR0 to AR7 the selected (or current auxiliary register) register, we use the instruction LD (load accumulator). This takes two operands. The first operand is an immediate value between 0 and 7. The second operand consists of the letters ARP to indicate that the auxiliary register pointer is being configured. Assuming that the TMS320C54x has been set up to be compatible with the earlier devices, Example 2-16 shows how we can make AR7 the current auxiliary register and then place a value into it. Example 2-16. LD #7, ARP ; ; ; ; ; ; ; ; ; ; Load auxiliary register pointer (ARP) with 7 to make AR7 the current auxiliary register. Any indirect addressing done without specifying an auxiliary register will use AR7. Load accumulator A with the contents of the data memory address pointed to by the auxiliary register AR7. Load accumulator B with the contents of the data memory address pointed to by the current auxiliary register (ARP), here AR7.

LD *, A

LD *, B

TUTORIAL

T-20

ASSEMBLY LANGUAGE TUTORIAL

In simplistic terms, the auxiliary register pointer (ARP) can be viewed as a field that contains the number of the current auxiliary register. The code in Example 2-17 is incorrect: Example 2-17. LD #9, ARP ; Incorrect. The first operand must lie in ; the range 0 to 7.

Modeling the Auxiliary Register Pointer A model of how the auxiliary register pointer ARP works is shown diagrammatically in Figure 2-2. Error! Reference source not found.. Second Model of Indirect Addressing
Data Memory (RAM) 0000h 0000h 0000h 0000h 0000h 1000h FFFFh FFFFh FFFFh FFFFh 1FBh 1FCh 1FDh 1FEh 1FFh 200h 201h 202h 203h 204h

Auxiliary Registers AR0 AR1 ARP 3 AR2 AR3 AR4 AR5 AR6 AR7 0000h 1674h 0060h 0200h 0300h 13Fh 0005h 8000h

In this case we use the number in the auxiliary register pointer (ARP) to select one of the auxiliary registers AR0 to AR7 to be the basis of our operations on data memory. The auxiliary register pointer ARP is a pointer to a pointer to a data memory address. Modeling the Auxiliary Register Pointer in C We can model the behavior of the auxiliary register pointer (ARP) using the analogy of C code, and is shown in Example 2-18: Example 2-18. unsigned char ARP; unsigned int *AR[8]; unsigned long accumulator_A; // Index in range 0 to 7. // An array of 8 pointers. // Accumulator A.

TUTORIAL

T-21

ASSEMBLY LANGUAGE TUTORIAL // Set index to 3. // Use AR[3] to point to // address 200h. accumulator_A = *AR[ARP]; // Load accumulator A with // contents of memory // address 200h. In this case we are treating auxiliary registers AR0 to AR7 as an array of eight pointers. To select AR3 as our current auxiliary register we use AR[3]. We then load accumulator A using the selected pointer. Note that is an analogy only, and in practice we would never write C code of this type. The main point to remember is that the ARP (auxiliary register pointer) is a pointer to a pointer to a data memory address. On the TMS320C54x, the auxiliary register pointer is only really important for complex addressing. Most of the time it is not used. However, it should be remembered that on earlier devices such as the TMS320C2xx and TMS320C5x, it is the main method of using indirect addressing. Upgrading from the TMS320C25 and TMS320C2xx/C5x to the TMS320C54x The data book for the TMS320C25 provides the instruction LARP (load auxiliary register pointer). Both the TMS320C2xx and TMS320C5x can also use this instruction, although it is not in either databook. Table 2-1. Comparison of Instructions
TMS320C2xx and TMS320C5x Instruction MAR *, ARx MAR *, AR3 LD *+

ARP = 3; AR[ARP] = 0x200;

Description Load auxiliary register pointer Load accumulator with contents of data memory address pointed to by AR3. Increment AR3. Load accumulator with contents of data memory address pointed to by AR4. Decrement AR4.

TMS320C25 Instruction LARP x LARP 3 LD *+

TMS320C54x Instruction LD #x, ARP LD *AR3+, Acc

LARP 4 LD *-

MAR *, AR4 LD *-

LD *AR4-, Acc

Here x is a number between 0 and 7 and ARx is one of the current auxiliary registers AR0 to AR7, Acc is accumulator A or accumulator B. The fact that the name of the auxiliary register can be explicitly stated makes the TMS320C54x instructions much easier to read than with the earlier devices. Example 2-19 shows how to load the accumulator with an immediate value and how to copy it to data memory using TMS320C25/2xx/5x.

TUTORIAL

T-22

ASSEMBLY LANGUAGE TUTORIAL Example 2-19. STM #100h, AR3 STM #200h, AR4 LD *AR3, A STL A, *AR4 ; ; ; ; ; ; ; ; TMS320C5rx syntax. Store address 100h in auxiliary register AR3. Store address 200h into memory-mapped register AR4. Load contents of data memory address 100h into accumulator A. Store low word of accumulator A in the data memory address pointed to by AR4.

QUESTIONS
1. Which two of the following instructions is legal? a) STM #400h, AR4 b) STM #20, AR5c) STM #200h, AR1+ d) STM #600, AR7 e) STM #2, –AR5 f) STM 30h, +AR6 Which three of the following instructions is legal? a) LD *AR2, A+ b) LD *AR6, Bc) LD * d) LD *AR4-, B e) LD *AR5+, A f) LD *AR3, A What are the advantages of indirect addressing over direct addressing? How do we increment the contents of an auxiliary register AR4 when using indirect addressing? How do we decrement the contents of an auxiliary register AR3 when using indirect addressing? What effect does the instruction LD *AR2+, A have? a) Load accumulator A with indirect data then increment AR2. b) Load accumulator A with indirect data then increment accumulator A. c) Load accumulator A with indirect data then increment indirect data. The instruction STL A *AR2 means: a) Save total value in accumulator A in AR2. b) Shift left accumulator A and store result in data memory address pointed to by AR2. c) Store low word of accumulator A in data memory address pointed to by AR2. d) Subtract the low word of accumulator A from auxiliary register AR2.

2.

3. 4. 5. 6.

7.

TUTORIAL

T-23

ASSEMBLY LANGUAGE TUTORIAL 8. The instruction STH B, *AR3 means: a) Shift hexadecimal value in accumulator B and put result in AR3. b) Store high word of accumulator B in data memory address pointed to by AR3. c) Store horizontally accumulator B in AR3. d) Subtract value in high word of accumulator B from AR3. What is meant by the term current auxiliary register?

9.

10. What is meant by the term auxiliary register pointer (ARP)? 11. How do we make AR6 the current auxiliary register?

TUTORIAL

T-24

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 3:

Logical Operations

New Instructions Introduced AND OR ANDM ORM CMPL

XOR XORM

Overview of Tutorial In the first two tutorials we saw how to move data to and from accumulator A and accumulator B. We shall now look at some of the ways to manipulate data in accumulators A and B.

Logical Operations Logical operations allow us to alter specific bits of data words. Readers may already be familiar with the C language operators & | ^ and ~. For each of these there is an equivalent TMS320C54x assembly language operator. Logical AND The first logical operation we shall look at is the AND. The truth table is shown in Table 3-1: Table 3-1. Logical AND
1st Value 0 0 1 1 2nd Value 0 1 0 1 Result 0 0 0 1

In order for the Result to be 1, both the 1st Value AND the 2nd Value must be 1. As an example, let us perform the logical AND of the words 695Ch and 00FFh. We apply the truth table for the logical AND to each bit of the two values as shown in Table 3-2: Table 3-2. Logical AND
Decimal 1st Value 2nd Value Result 26972 00255 00092 Hexadecimal 695Ch 00FFh 005Ch Binary 0110 1001 0101 1100 0000 0000 1111 1111 0000 0000 0101 1100

TUTORIAL

T-25

ASSEMBLY LANGUAGE TUTORIAL To use the analogy of C code, if the first value is stored in variable, we could write Table 3-2 as: Example 3-1. unsigned long variable = 0x695C; variable &= 0x00FF; // AND with // variable.

Let us now implement the operation shown in Table 3-2 using TMS320C54x assembly language with immediate addressing: Example 3-2. LD #695Ch, A AND #0FFh, A ; ; ; ; Load accumulator A with 0000695Ch. Perform logical AND of accumulator A with FFh. Accumulator A now contains 0000005Ch.

In Example 3-2 it can be seen that the instruction AND with the immediate value FFh leaves the low byte unaffected, but clears the other bits to zero. The immediate value can be in the range 0 to 65535 (0 to FFFFh). As another example, let us take the case of when accumulator B already contains FFFF1234h. Example 3-3. AND #0FF00h, B ; Perform a logical AND of accumulator B ; (FFFF1234h) with FF00h. Accumulator B ; now contains 00001200h.

Note that the logical AND affects all 32 bits of the accumulator. In this case the high word of the accumulator has been cleared to 0000h. Logical AND with Memory Consider the case when we wish to perform a logical AND of a value in data memory. An example using C code is shown in Example 3-4: Example 3-4. unsigned int value; value &= 0x00FF; // Logical AND of value with // the constant FF00h.

The instruction AND can only be applied to accumulator A or to accumulator B. In order to perform the logical AND of an immediate value with the contents of a data memory address, one way is to copy the value to an accumulator, use it for operation and then to copy the result back to data memory. Let us assume that the variable value is stored at data memory address 71h.

TUTORIAL

T-26

ASSEMBLY LANGUAGE TUTORIAL

Example 3-5. LD #0,DP LD @71h, A ; ; ; ; ; ; ; ; ; Page 0. Gain access to data memory addresses 60h to 7Fh. Using direct addressing, load accumulator A with contents of data memory address 71h. Perform a logical AND with the constant FFh. Copy result back to data memory address 71h.

AND #0FFh, A STL A, @71h

However, a better method is to use the instruction ANDM (logical AND memory with 16-bit immediate), as shown in Example 3-6. This is the assembly language implementation of Example 3-4 and assumes that the variable value is stored at data memory address 71h: Example 3-6. LD #0,DP ANDM #0FFh, @71h ; ; ; ; ; Access to Page 0 addresses 60h to 7Fh. Perform a logical AND of the constant FFh with the contents of data memory address 71h. The result is placed at data memory address 71h.

The instruction ANDM takes two operands. The first operand is the immediate value and can lie in the range 0 to 65535 (0 to FFFFh). The second operand is the destination and is an address in data memory. Note that the usage of the letter M differs between instructions. For example, the letter M in the instructions STM (store immediate value in memory-mapped register) and LDM (load memory-mapped register) means memory-mapped register. However, the letter M when applied to the instruction ANDM (logical AND with memory) applies to a data memory address. In the Texas Instruments databooks, the term Smem is used to describe a 16-bit single data-memory operand. The two forms of data memory addressing we have met so far, namely direct addressing and indirect addressing, as well as some others fall into this category. Logical AND of a Series of Data Memory Addresses Say we wish to perform a logical AND of data memory address A0h with data memory address A1h and put the result at data memory address A2h. We cannot perform a direct logical AND of two values stored in data memory. To do so, we must first copy one of the values to either accumulator A or accumulator B, as shown in Example 3-7:

TUTORIAL

T-27

ASSEMBLY LANGUAGE TUTORIAL Example 3-7. LD #1, DP LD @20h, A ; ; ; ; ; ; ; ; ; ; ; ; Page 1. Gain access to data memory addresses 80h to FFh. Load accumulator A with the contents of data memory address 80h + 20h = A0h. Perform a logical AND of accumulator A with the contents of data memory address 80h + 21h = A1h. Put the result in accumulator A. Store the result in accumulator A at data memory address 80h + 22h = A2h.

AND @21h, A

STL A, @22h

The instruction AND (logical AND with accumulator) can also be used with indirect addressing. For example, to perform the logical AND of the contents of a data memory address A1h with accumulator A we can write: Example 3-8. STM #A1h, AR6 ; ; ; ; ; ; ; Store the address of data memory location A1h in auxiliary register AR6. AR6 = 00A1h. Perform a logical AND of accumulator A with the contents of the data memory address pointed to by AR6. The result is put into accumulator A.

AND *AR6, A

Logical OR The truth table for the logical OR is shown in Table 3-3: Table 3-3. Logical OR
1st Value 0 0 1 1 2nd Value 0 1 0 1 Result 0 1 1 1

If either the 1st Value or the 2nd Value is a 1, then the result will be 1. The above truth table is applied a total of 16 times, once to each bit of a data word. We can perform a logical OR of 695Ch and 00FFh as shown in Table 3-4:

TUTORIAL

T-28

ASSEMBLY LANGUAGE TUTORIAL Table 3-4. Logical OR
st 1 Value nd 2 Value Result Decimal 26972 00255 27135 Hexadecimal 695Ch 00FFh 69FFh Binary 0110 1001 0101 1100 0000 0000 1111 1111 0110 1001 1111 1111

Instead of clearing specific bits of an accumulator to 0, as is the case with the logical AND, the logical OR is used to sets specific bits to 1. We can therefore use the instruction OR (logical OR) to set one or more bits of either accumulator A or accumulator B. We can implement Table 3-4 using the analogy of C code: Example 3-9. unsigned long variable = 0x695C; variable |= 0xFF; // Logical OR // variable // with FFh.

To carry out the operation in Example 3-9 using TMS320C54x assembly language we write: Example 3-10. LD #695Ch, A OR #0FFh, A ; ; ; ; Load accumulator A with 0000695Ch. Perform logical OR of accumulator with FFh. Accumulator A now contains 000069FFh.

The immediate value used as the operand can lie in the range 0 to 65535 (0h to FFFFh). Let us now look at a slightly different example where we wish to perform a logical OR directly on the contents of a data memory location. For this the analogy of C code would be: Example 3-11. unsigned int value; value |= 0xFF00;

// Logical OR of variable value // with the constant FF00h.

Assuming that the variable value is stored at data memory address 82h, one way we can implement Example 3-11 is shown in Example 3-12: Example 3-12. LD #1, DP LD @2h, A ; ; ; ; Page 1. Gain access to data memory addresses 80h to FFh. Load accumulator A with the contents of data memory address 80h + 2h = 82h.

TUTORIAL

T-29

ASSEMBLY LANGUAGE TUTORIAL OR #0FF00h, A ; ; ; ; ; Perform a logical OR of constant FF00h with accumulator A. The result is placed in accumulator A. Copy result back into data memory address 80h + 2h = 82h.

STL A, @2h

The immediate value can lie in the range 0 to 65535 (0 to FFFFh). We have copied the value to the accumulator, performed an operation on it and then put back the updated value. There is a simpler way to carry out this operation using the instruction ORM (logical OR memory with constant). The letter M in the instruction ORM stands for memory and not memory-mapped register: Example 3-13. LD #1, DP ORM #0FF00h, @2h ; ; ; ; ; ; Page 1. Gain access to data memory addresses 80h to FFh. Perform a logical OR of the value contained at data memory address 80h + 2h = 82h. The result is put into data memory address 82h.

Again, the immediate value used as the operand can lie in the range 0 to 65535 (0h to FFFFh). Logical OR of Values in Memory We can perform the logical OR of two or more data words stored in data memory. Example 3-14 gives the analogy in C code of the logical OR of two values: Example 3-14. unsigned unsigned unsigned result = int value_1; int value_2; int result; value_1 | value_2; // 1st variable. // 2nd variable. // Logical OR.

If value_1 is stored at data memory address 63h, value_2 is stored at data memory address 64h and result is stored at data memory address 65h, then we can implement Example 3-14 in assembly language as: Example 3-15. LD #0, DP LD @63h, B OR @64h, B ; ; ; ; ; ; ; ; Page 0. Gain access to data memory addresses 60h to 7Fh. Load accumulator B with contents of data memory address 63h. Perform a logical OR of accumulator B with the contents of data memory address 64h. The result is in accumulator B.

TUTORIAL

T-30

ASSEMBLY LANGUAGE TUTORIAL STL B, @65h ; Store result of logical OR at data ; memory address 65h.

The instruction OR also supports indirect addressing. To perform a logical OR of accumulator B with the contents of data memory address 100h we write: Example 3-16. STM #100h, AR5 ; ; ; ; ; ; ; Store address of data memory location 100h in auxiliary register AR5. AR5 = 100h. Perform a logical OR of the contents of the data memory address pointed to by AR5 with accumulator B. The result is in accumulator B.

OR *AR5, B

Used in this way, the instruction OR affects only the low word of accumulator A or B. If accumulator A already contains the value FFFF1111h, then: Example 3-17. OR #0AA55h, B ; ; ; ; Perform a logical OR of accumulator B (FFFF1111h) with AA55h. Accumulator B now contains FFFF55BBh. The high word of accumulator B remains unchanged.

In this case, the instruction OR (logical OR with accumulator) works differently to the instruction AND which affects the entire accumulator. Logical Exclusive OR (XOR) The most flexible logical operation is the exclusive OR. The truth table for the logical exclusive OR is shown in Table 3-5: Table 3-5. Exclusive OR.
1st Value 0 0 1 1 2nd Value 0 1 0 1 Result 0 1 1 0

For the Result to be 1, the 1st Value and 2nd Value must be different. We apply this truth table 16 times once to each bit of the low word of the accumulator. Let us perform an exclusive OR of 695Ch with 00FFh, as shown in Table 3-6:

TUTORIAL

T-31

ASSEMBLY LANGUAGE TUTORIAL

Table 3-6. Exclusive OR.
Decimal 26972 00255 27043 Hexadecimal 695Ch 00FFh 69A3h Binary 0110 1001 0101 1100 0000 0000 1111 1111 0110 1001 1010 0011

1st Value 2nd Value Result

In this case the high byte is unaltered but the low byte is inverted. Using the analogy of C code we may write Table 3-6 as: Example 3-18. unsigned long variable = 0x695C; variable ^= 0xFF; The assembly language equivalent of Example 3-18 would be: Example 3-19. LD #695Ch, A XOR #0FFh, A ; ; ; ; Load accumulator A with 0000695Ch. Perform an exclusive OR of FFh with accumulator A. Accumulator A now contains 000069A3h.

If an exclusive OR is performed of two identical values, the result will be zero, as shown in Example 3-20. Example 3-20. LD #0FEDCh, B XOR #0FEDCh, B ; ; ; ; Load accumulator B with 0000FEDCh. Perform an exclusive OR of FEDCh with accumulator B. Accumulator B now contains 0.

In this case, the exclusive OR has the same effect as subtracting one value from the other on a bitwise basis. The exclusive OR will be used in later tutorials as a way of comparing two data words.

Logical XOR with Memory We may wish to perform an exclusive OR on a value stored in data memory. Example 3-21 illustrates this operation using C code: Example 3-21. unsigned int value;

TUTORIAL

T-32

ASSEMBLY LANGUAGE TUTORIAL value ^= 0xFF00; // Exclusive OR of value with // constant.

Using TMS320C54x assembly language, in order to perform a logical exclusive OR directly on a value stored at data memory address 82h with an immediate value we could use an instruction sequence such as the one in Example 3-22:

TUTORIAL

T-33

ASSEMBLY LANGUAGE TUTORIAL Example 3-22. LD #1, DP LD @2h, A XOR #0FF00h, A ; ; ; ; ; ; ; ; ; Page 1. Gain access to data memory addresses 80h to FFh. Load accumulator A with the contents of data memory address 80h + 2h = 82h. Perform a logical exclusive OR of the constant FF00h with accumulator A. The result is placed in accumulator A. Copy result back into data memory address 80h + 2h = 82h.

STL A, @2h

We have copied the value to the accumulator, performed an operation on it and then put the result back into the original data memory address. There is a simpler way to do this operation using the instruction XORM (logical exclusive OR memory with constant), the usage of which is shown in Example 3-23: Example 3-23. LD #1, DP XORM #0FF00h, @2h ; ; ; ; ; ; Page 1. Gain access to data memory addresses 80h to FFh. Perform a logical exclusive OR of the value contained at data memory address 82h. The result is put into data memory address 82h.

Note that here the letter M in the instruction XORM means memory, and not memory-mapped register. An Apparent Inconsistency The instructions OR and XOR “seem” to work on the low 16 bits of accumulator A and accumulator B. On the other hand, the instruction AND “seems” to work on the full 32 bits of the relevant accumulator. Why should these instructions behave in apparently different ways? When we perform a logical operation on an accumulator, the immediate value is taken to be 32 bits, even though the operand provides only the low 16 bits. The high 16 bits are taken to be 0000h. When we execute the instructions OR and XOR with a 16 bit value, the high 16 bits of the immediate value are filled with zeroes. An OR or an XOR with an immediate value of zero has no effect. On the other hand, when the instruction AND is used, all bits where the immediate value is zero are cleared to zero. This means that after a logical AND the high word of the accumulator always contains zeroes, regardless of what values were there before. Complementing the Accumulator We can complement the low 16 bits of the accumulators using the instruction XOR (exclusive OR with accumulator). Here complement means that all bits that are 0 are replaced by 1 and all bits that are 1 are replaced by 0. This is also referred to as inversion.

TUTORIAL

T-34

ASSEMBLY LANGUAGE TUTORIAL The instruction XOR does not affect the entire accumulator, so the special instruction CMPL (complement accumulator) is provided to complement the full 40 bits of the accumulator. Using the analogy of C code we could write: Example 3-24. unsigned long variable = 0x55AA; variable = ~variable;

// Complement // variable.

To implement Example 3-24 in assembly language we write: Example 3-25. LD #55AAh, A CMPL A ; Load accumulator A with 000055AAh. ; Complement the value in accumulator A. ; Accumulator A now contains FFFFAA55h.

All bits that were originally 0 are changed to 1 and all bits that were originally 1 are changed to 0. The instruction CMPL (complement accumulator) takes a single operand which is the accumulator being used. It can also be applied to accumulator B. Example 3-26. LD #1234h, B CMPL B ; Load accumulator A with 000055AAh. ; Complement the value in accumulator A. ; Accumulator A now contains FFFFAA55h.

The complement instruction CMPL is used to invert data, for example at an input to convert active low signals to positive logic. Upgrading from the TMS320C25 and TMS320C2xx/5x to the TMS320C54x The TMS320C54x has two accumulators, while earlier devices had only one. This means that earlier logical and arithmetic instructions designed for use with a single accumulator cannot be used on the TMS320C54x. Table 3-7 gives a comparison of instructions: Table 3-7. Comparison of Instructions
Description TMS320C25 Instruction ANDK 00FFh ORK 00FFh XORK 0FFFFh TMS320C2xx and TMS320C5x Instruction AND #00FFh OR #00FFh XOR #0FFFFh TMS320C54x Instruction AND #100h, Acc OR #100h, Acc XOR #0FFFF, Acc

Logical AND constant with accumulator Logical OR constant with accumulator Logical exclusive OR constant with accumulator

TUTORIAL

T-35

ASSEMBLY LANGUAGE TUTORIAL
Logical AND constant with data memory Logical OR constant with data memory Logical XOR constant with data memory No equivalent No equivalent No equivalent No equivalent No equivalent No equivalent ANDM #0FFh, memory ORM #0FFh, memory XORM #0FFh, memory

memory = data memory. Acc = Accumulator A or accumulator B. Note that because the TMS320C54x has two accumulators, it takes an extra operand compared to the single one used with earlier devices. On the TMS320C54x logical operations such as AND, OR and XOR can be performed directly on the contents of data memory addresses. These make use of the instructions ANDM (logical AND memory with long immediate), ORM (logical OR memory with long immediate) and XORM (logical exclusive OR memory with long immediate). To carry out the same operation using an earlier processor was considerably less efficient because logical and arithmetic operations could only be carried out in the accumulator. This meant copying the contents of the data memory address to the accumulator, performing the operation and then putting the result back in the data memory address.

Questions
1. 2. Why should we wish to use a logical AND? If accumulator A already contains FFFF3333h, what will it contain after execution of the following instruction? AND #FFh, A If accumulator B already contains 55555555h, what will it contain after the execution of the following instruction? OR #FFFFh, B If accumulator A already contains 77770000h, what will it contain after the execution of the following instruction? XOR #1111h, A If accumulator B already contains 000055AAh, what will it contain after the execution of the following instruction? XOR #FFFFh, B How do we use the instruction XOR as an inverter? How do we use the instruction XOR to perform a bitwise addition of two values? Why would we not use the instruction XOR to complement the full 32 bits of the accumulator? If accumulator B contains 00007777h, what will be the contents of accumulator B after the instruction CMPL B? Assuming DP=0, perform a logical AND of the contents of the data memory address 61h with the constant 3FFFh using a single instruction.

3.

4.

5.

6. 7. 8. 9. 10.

TUTORIAL

T-36

ASSEMBLY LANGUAGE TUTORIAL 11. 12. Assuming DP=0, Perform a logical OR of the contents of the data memory address 72h with the constant FF00h using a single instruction. Invert the contents of data memory address 64h using a single instruction.

TUTORIAL

T-37

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 4:

Signed Numbers and Shifting Data

New Instructions Introduced RSBX SSBX SFTA SFTL New Flag Introduced C SXM Overview of Tutorial In the first two tutorials, we looked at some of the simpler instructions that can be used to move data. We shall now look at some additional ways in which to use these instructions. In this tutorial we shall also look at how to use positive and negative numbers as well as to shift data. Signed and Unsigned Values All of the instructions up to this point in the tutorials have used unsigned numbers only, that is, numbers that only have positive values. For example, the instruction LD (load accumulator) has been used to load a value between 0 to 65535 (0 to FFFFh). For many DSP applications, we require numbers that are signed, that is, numbers that can take values that are negative or positive. In order to represent a signed number, we use the most significant (left-most) bit of the number to indicate the sign. To make the number positive, we make the most significant (left-most) bit 0, and to make the number negative, we make the most significant (left-most) bit of the number 1. Some examples of signed numbers are given in Table 4-1: Table 4-1. Signed Numbers.
Decimal 0 +32767 -1 -32768 Hexadecima l 0 7FFFh FFFFh 8000h Binary 0000 0000 0000 0000 0111 1111 1111 1111 1111 1111 1111 1111 1000 0000 0000 0000 Description Sign bit (most significant bit) is 0, therefore a positive number. Sign bit (most significant bit) is 0, therefore a positive number. Sign bit (most significant bit ) is 1, therefore a negative number. Sign bit (most significant bit) is 1, therefore a negative number.

Using a 16-bit number, we can represent signed values between -32768 (8000h) and +32767 (7FFFh). Here FFFFh represents -1. Leading zeroes in front of a positive number have no effect. For example, the numbers 1, 01, 001, 0001 all have the same value. In a similar way, negative numbers are padded out with a number of Fs. For example, the number -1 can be written as FFh for an 8-bit number, FFFFh for a 16-bit number and FFFFFFFFh for a 32-bit number.

TUTORIAL

T-38

ASSEMBLY LANGUAGE TUTORIAL Turning on Sign Extension The instruction LD (load accumulator long immediate) supports both signed and unsigned numbers. In order to use the instruction LD with a signed number, we must first turn on sign-extension using a variation of the instruction SSBX (set status register bit). The instruction SSBX is used to set a particular bit in one of the status registers. In this case, the bit we are interested in is SXM, which controls sign-extension mode. To turn on sign-extension mode, we use the instruction SSBX with the operand SXM. Once this instruction has been executed, all operations that are affected by SXM will be taken as signed numbers, until SXM is turned off. Example 4-1. SSBX SXM ; ; ; ; ; ; ; ; ; ; Turn on sign-extension mode. From this point in the program, all instructions affected by sign-extension (SXM) use signed numbers. Load accumulator A with 00000001h. Load accumulator B with 00007FFFh. This is the largest positive value (+32767). Load accumulator A with FFFFFFFFh. The constant is taken to be -1. Load accumulator A with FFFFFFFFh.

LD #1, A LD #7FFFh, B LD #-1, A LD #0FFFFh, A

When a 16-bit positive number is being loaded into an accumulator (i.e. where the most significant bit is 0), the high word of the accumulator is filled with 0000h. On the other hand, when a negative number is being loaded into the accumulator (i.e. where the most significant bit is 1), the high word of the accumulator is filled with FFFFh. Turning off Sign Extension In order to turn off sign-extension mode, we use the instruction RSBX (reset status register bit) with the operand SXM. This has the effect of turning off sign-extension mode until the next instruction SSBX SXM occurs and is illustrated in Example 4-2: Example 4-2. RSBX SXM ; ; ; ; ; ; Turn off sign extension-mode. From this point in the program, all instructions affected by sign-extension (SXM) will use positive values only. Load accumulator A with 0000FFFFh. Load accumulator B with 00007FFFh.

LD #0FFFFh, A LD #7FFFh, B

TUTORIAL

T-39

ASSEMBLY LANGUAGE TUTORIAL SSBX SXM ; ; ; ; ; Turn on sign-extension mode. From this point in the program, all instructions affected by sign-extension mode (SXM) will use signed numbers. Load accumulator A with FFFFFFFFh (-1).

LD #0FFFFh, A

Shifting to the Left Shift instructions are used to move bits in accumulator A or accumulator B to the left or to the right. Readers may already be familiar with shifts in C. For example, to shift a 32-bit variable one place to the left we can write: Example 4-3. unsigned long variable; unsigned long variable; variable <<=1; // // // // Shift the contents of shift the contents of variable one place to the left.

The operation of a shift to the left of a 16-bit variable is shown in Table 4-2: Table 4-2. Shifts to the Left
Decimal +43356 +86712 +173424 Hexadecimal A95Ch 152B8h 2A570h Binary 1010 1001 0101 1100 1 0101 0010 1011 1000 10 1010 0101 0111 0000

Original Value Shift left Shift left again

Shifting one place to the left has the same effect as multiplying by 2. The TMS320C54x provides the instruction SFTL (shift accumulator logically). Note that the letter L in SFTL does not mean left. This instruction moves each bit of an accumulator a number of places to the left or right, as specified by the operand. For example, if accumulator A already contains 00008421 and we wish to shift it one place to the left we write: Example 4-4. SFTL A, 1 ; ; ; ; Shift accumulator A one place to the 1 left (multiply by 2 = 2). Accumulator A now contains 10842h. The Carry (C) bit now contains 0.

When we shift an accumulator one place to the left, then the most significant (left-most) bit of the accumulator (bit 31) is moved into the Carry (C) flag and a 0 is moved into the lowest bit (right-most) of the accumulator (bit 0).

TUTORIAL

T-40

ASSEMBLY LANGUAGE TUTORIAL The Carry (C) flag is also used to indicate that a number has exceeded the maximum value that can be stored in the variable while carrying out an addition. When using the instruction SFTL, the amount of left shift can be between 1 and 15 places. Example 4-5. SFTL A, 2 ; ; ; ; ; Shift accumulator A two places to the 2 left (multiply by 2 = 4). Accumulator A now contains 21084h. The Carry (C) bit contains 0. Illegal. Maximum allowed shift is 15.

SFTL A, 16

Since the instruction SFTL (shift accumulator logically) takes two operands, it is illegal to write: Example 4-6. SFTL A SFTL B, 0 ; ; ; ; ; ; ; ; Illegal. The number of shifts must be specified. Shift accumulator B zero places. This instruction has no effect other than to clear the Carry (C) flag. Illegal. Either accumulator A or accumulator B must be specified as the source.

SFTL 1

We cannot use the instruction SFTL A, 15 to shift the low word of the accumulator into the high word. For that, 16 shifts would be required. In order to obtain a shift left of 16 places, we execute the SFTL instruction twice, as shown in Example 4-7, so that the sum of the shifts adds up to 16: Example 4-7. SFTL A, 8 SFTL A, 8 ; ; ; ; ; Shift accumulator A 8 places to the left. Shift accumulator A a further 8 places to the left. Total number of shifts is now 16.

Shifting to the Right To shift a 32-bit variable one place to the right, in C code we can write: Example 4-8. unsigned long variable; variable >>=1; // Shift the contents of // variable one place // to the right.

TUTORIAL

T-41

ASSEMBLY LANGUAGE TUTORIAL

The operation of a shift to the right is shown in Table 4-3: Table 4-3. Shifts to the Right
Decimal +43356 +21678 +10839 Hexadecimal A95Ch 54AEh 2A57h Binary 1010 1001 0101 1100 0101 0100 1010 1110 0010 1010 0101 0111

Original Value Shift right Shift right again

A shift to the right is a quick way of carrying out a division by 2. However, each time a shift to the right is executed, the least significant (right-most bit) is lost. This means that there is a loss of resolution. Shifting to the right is often used after a multiplication to scale down the product. In order to shift the value in an accumulator to the right, we again use the instruction SFTL (shift accumulator logically), but this time with an operand in the range -16 to -1. This moves each bit of an accumulator a number of places to the right, as specified by the operand. For example, if accumulator A already contains 8421h and we wish to shift accumulator A one place to the right (divide by 2) we can write: Example 4-9. SFTL A, -1 ; ; ; ; Shift accumulator A one place to the -1 right (multiply by 2 ). Accumulator A now contains 0421h. The Carry (C) bit now contains 1.

When shifting to the right, the most significant (right-most) bit is shifted into the Carry (C) flag. Two further examples of right shift are given in Example 4-10, and assume accumulator A already contains 00008421h. Example 4-10. SFTL A, -2 ; ; ; ; ; Shift accumulator A two places to the -2 right (multiply by 2 ). Accumulator A now contains 210h. The carry (C) bit now contains 0. Illegal. Maximum allowed shift is -16.

SFTL A, -17

We can use a shift of 16 to the right to move the high word of accumulator A or B to the low word of that accumulator. If accumulator A already contains 23456789h then: Example 4-11. SFTL A, -16 ; ; ; ; Shift accumulator A 16 places to the -16 right (multiply by 2 ). Accumulator A now contains 00002345h. The Carry (C) bit now contains 0.

TUTORIAL

T-42

ASSEMBLY LANGUAGE TUTORIAL Note that the instruction SFTL is not affected by sign-extension mode SXM, and therefore carries out exactly the same operation, whether the value in the accumulator is negative or positive. Shifting Signed Values When using signed numbers, if we shift the value to the left or the right, we still expect positive numbers to remain positive and negative numbers to remain negative. With the logical shift SFTL this is not the case. Assuming that accumulator A already contains the signed number 80008421h:

TUTORIAL

T-43

ASSEMBLY LANGUAGE TUTORIAL Example 4-12. RSBX SXM SFTL A, -1 ; ; ; ; Turn off sign-extension mode. Logical shift accumulator A one place to the right (divide by 2). Accumulator A now contains 40004210h.

In this case, a negative number before the shift has become a positive number after the shift. To maintain the status of the sign bit during a shift, the instruction SFTA (shift accumulator arithmetically) is provided. The behavior of this instruction does depend on whether sign-extension mode (SXM) is set. If the accumulator already contains 80008421h (a negative number) then: Example 4-13. SSBX SXM SFTA A, -1 ; ; ; ; ; Turn on sign-extension mode. Shift accumulator A one place to the right (divide by 2). The sign bit (bit 31) is unchanged. Accumulator A now contains 80004210h.

In this case, a negative number remains negative after the shift. When sign-extension mode is turned on, the most significant (left-most) bit which controls the sign of the number, is maintained. Loading an Accumulator with Data Shifts We have seen how to load the accumulator with a 16-bit constant using the instruction LD (load accumulator). In the data book, the full description given of the instruction LD is in fact load accumulator with shift. To avoid confusion in the earlier tutorials, the concept of shift was deliberately not mentioned. When applied to the instruction LD, shift means that each bit is moved one or more places to the left and the least significant bit(s) are filled with zero(es). For example: 00000001h shifted 1 place to the left is 00000002h 00000001h shifted 2 places to the left is 00000004h 00000001h shifted 16 places to the left is 0001000h The instruction LD (load accumulator with shift) does not provide shifts to the right. Note that the description of the instruction LD is slightly misleading because the shift is done before the load. Let us load the accumulator A with FFFFh using the instruction LD (load accumulator with shift), but not specify any shift: Example 4-14. RSBX SXM ; Turn off sign-extension mode.

TUTORIAL

T-44

ASSEMBLY LANGUAGE TUTORIAL LD #0FFFFh, A ; Load accumulator A with the immediate ; value FFFFh. Accumulator A now contains ; 0000FFFFh.

We can also add an extra operand and write Example 4-14 as: Example 4-15. RSBX SXM LD #0FFFFh, 0, A ; ; ; ; ; Turn off sign-extension mode. Load accumulator A with immediate value FFFFh shifted zero places to the left. Accumulator A now contains 0000FFFFh.

The middle operand denotes the amount of shift to the left and is optional. If no shift is specified, the default of 0 is used. The number of shifts can be in the range 0 to 16. In order to load accumulator A with FFFFh shifted one place to the left we would write: Example 4-16. RSBX SXM LD #0FFFFh, 1, A ; ; ; ; ; Turn off sign-extension mode. Load accumulator A with the immediate value FFFFh shifted one place to the left. Accumulator A now contains 0001FFFFEh.

Similarly, to load accumulator B with FFFFh shifted two places to the left we would write: Example 4-17. RSBX SXM LD #0FFFFh, 2, B ; ; ; ; ; Turn off sign-extension mode. Load accumulator B with the immediate value FFFFh shifted two places to the left. Accumulator B now contains 0003FFFCh.

The ability of the instruction LD (load accumulator with shift) to shift the constant before loading allows us to scale a constant before loading it. Loading all 32 bits of the Accumulator So far we have only loaded the low 16 bits of accumulator A or accumulator B. Consider the following simple problem. How do we load accumulator A (32 bits) with the constant 6789ABCD hexadecimal? The answer is not as straightforward as it might first appear. We have already seen how to load the low 16 bits of the accumulator using the instruction LD (load accumulator with shift). The high word of the accumulator is filled with 0000h. We need an instruction to load the high word.

TUTORIAL

T-45

ASSEMBLY LANGUAGE TUTORIAL However, the TMS320C54x instruction set provides no special instruction to load the high 16 bits of an accumulator. This is because it is not needed. To load the high 16-bits of accumulator A with a constant, we use the instruction LD in conjunction with 16 shifts to the left. We can therefore write: Example 4-18. LD #6789h, 16, A ; ; ; ; Load accumulator A with the immediate value 6789h shifted 16 places to the left. Accumulator A now contains 67890000h.

We have now loaded the high word of the accumulator with 6789h and have filled the low 16 bits with 0000h. Now we need to fill the low 16 bits of accumulator A with ABCDh. Unfortunately, the instruction LD #ABCDh, A cannot be used. This would load accumulator A with 0000ABCDh, thus overwriting the high 16 bits with zeroes. We need to load the low 16 bits of the accumulator in a way that does not affect the high 16 bits already loaded. We have more than one way to do this. Assuming that both accumulator A and accumulator B already contain 67890000h: Example 4-19. OR #0ABCDh, A ; ; ; ; ; ; ; ; First method. Logical OR low word of accumulator A with the immediate value ABCDh. Accumulator A now contains 6789ABCDh. Second method. Logical XOR low word of accumulator B with the immediate value ABCDh. Accumulator B now contains 6789ABCDh.

XOR #0ABCDh, B

In Example 4-19 we see two different ways in which to fill the low 16 bits of accumulators A and B. The method of using the instruction OR (logical OR with accumulator) is the most commonly used. Combining Examples 4-18 and 4-19, we now have a method to load the constant 6789ABCDh into accumulator A, and this is shown in Example 4-20: Example 4-20. LD #6789h, 16, A ; ; ; ; ; ; ; ; Load accumulator A with the immediate value 6789h shifted 16 places to the left. Accumulator A now contains 67890000h. Logical OR low word of accumulator A with the immediate value ABCDh. Accumulator A now contains 6789ABCDh.

OR #0ABCDh, A

TUTORIAL

T-46

ASSEMBLY LANGUAGE TUTORIAL

Loading a 32-bit number into the accumulator requires a load with shift and then an operation such as a logical OR. The shift is carried out before the value is loaded into the accumulator.

TUTORIAL

T-47

ASSEMBLY LANGUAGE TUTORIAL Upgrading from the TMS320C25 and TMS320C2xx/5x to the TMS320C54x A comparison of instructions used to change sign-extension mode and to perform shifts is shown in Table 4-4: Table 4-4. Comparison of Instructions
Description Turn off sign-extension mode Turn on sign-extension mode Logical shift left Logical shift right Arithmetic shift right Arithmetic shift left TMS320C25 Instruction RSXM SSXM SFL SFR SFR Not supported TMS320C2xx and TMS320C5x Instruction CLRC SXM SETC SXM SFL SFR SFR Not supported TMS320C54x Instruction RSBX SXM SSBX SXM SFTL Acc, n SFTL Acc, -n SFTA Acc, -n SFTA, Acc, n

Here Acc = Accumulator A or B and n is the number of shifts. On the earlier devices, shifts of only one place to the left or right were allowed. This meant that to achieve certain shifts, the instruction had to be repeated, whereas the TMS320C54x can carry out multiple shifts using a single instruction. The earlier shift instructions required no operands, whereas the TMS320C54x requires two; one operand for the source and the other operand for the number of shifts. On the TMS320C25, the maximum shift available when loading the accumulator is 15. This makes loading a 32bit value into the accumulator quite difficult. It is much easier on the TMS320C54x because a shift of 16 is allowed.

Questions
1. 2. 3. 4. What is the difference between an unsigned number and an signed number? What instruction do we used to turn on sign-extension mode? What instruction do we use to turn off sign-extension mode? The mnemonic SFTL means: a) Shift accumulator laterally b) Shift accumulator left c) Shift accumulator logically. The instruction SFTL A, 1 shifts accumulator A a) One place to the left b) One place to the right The instruction SFTL B, -1 shifts accumulator B a) One place to the left b) One place to the right How do we shift the contents of accumulator B two places to the right? How do we shift the high word of accumulator A into the low word of accumulator A? How does sign-extension mode (SXM) affect the instruction SFTL?

5.

6.

7. 8. 9.

TUTORIAL

T-48

ASSEMBLY LANGUAGE TUTORIAL 10. 11. 12. How does sign-extension mode (SXM) affect the instruction SFTA? What is the difference between the instructions SFTL and SFTA? When the instruction LD is used with a shift operand, is it a) shift constant then load b) load constant then shift. What is the default shift for the instruction LD #5555h, A (load accumulator with shift)? Shift the value #00007777h from the low word of accumulator B into the high word of accumulator B. In this tutorial we have used two different instructions that can be used to load the immediate value 5555h into the low word of an accumulator, without affecting the high word. Which instructions are they? Assume that the low word of the accumulator already contains 0000h. How do we load the constant FEDC9876h into accumulator B?

13. 14. 15.

16.

TUTORIAL

T-49

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 5:

Arithmetic Operations

New Instructions Introduced ADD ADDM SUB MAR NEG New Flags Introduced OVM Overview of Tutorial In earlier tutorials we saw how to load data and carry out logical operations. We shall now see how to carry out additions and subtractions on the contents of accumulators, data memory addresses and auxiliary registers . Simple Addition To add an immediate value to accumulator A or accumulator B, we use the instruction ADD (add to accumulator). Example 5-1. LD #1000h, A ADD #3Fh, A ; Load accumulator A with 00001000h. ; Add the value 3Fh to accumulator A. ; Accumulator A now contains 103Fh.

The instruction ADD takes two operands. The first operand is a constant and the second operand is the accumulator to be used. The instruction ADD is affected by sign-extension mode. The constant is taken to be unsigned when signextension mode is off (SXM = 0)and signed when sign-extension mode is on (SXM = 1). This means that the constant can be either be unsigned (in the range 0 to 65535 decimal) or signed (in the range -32768 to +32767 decimal). For the time being, we shall only consider unsigned values. When the constant lies in the range 0 to +32767 (0 to 7FFFh) then sign-extension mode makes no difference. We can also add the contents of a data memory address to an accumulator using the instruction ADD (add to accumulator) with direct addressing. Say that data memory address 61h already contains the value 3Fh, then to carry out an addition we write:

TUTORIAL

T-50

ASSEMBLY LANGUAGE TUTORIAL Example 5-2. LD #0, DP LD #1000h, B ADD @61h, B ; ; ; ; ; ; ; Page 0. Gain access to data memory addresses 60h to 7Fh. Load accumulator B with 00001000h. Add the value stored at data memory address 61h to accumulator B. Accumulator B now contains 1000h + 3Fh = 103Fh.

The instruction ADD (add to accumulator) also supports indirect addressing. We can therefore carry out the same operation as carried out in Example 5-2, but this time using indirect addressing. Assuming that data memory address 61h contains the value 3Fh: Example 5-3. STM #61h, AR3 ; ; ; ; ; ; ; ; Load auxiliary register AR3 with the address of data memory location 61h. AR3 = 61h. Load accumulator A with 00001000h. Add the value contained in the data memory address pointed to by AR3 to accumulator A. Accumulator A now contains 1000h + 3Fh = 103Fh.

LD #1000h, A ADD *AR3, A

When adding values greater than +32767 (7FFFh), sign-extension mode must be turned off, otherwise the constant will be treated as a negative number. Example 5-4. RSBX SXM LD #1000h, A ADD #0FFFFh, A ; ; ; ; Turn off sign-extension mode. Load accumulator A with 00001000h. Add 65535 (FFFFh) to accumulator A. Accumulator A now contains 00010FFFh.

Signed Additions When sign-extension mode is turned on, the immediate value used with the instruction ADD is treated as a signed number. Any values between 8000h and FFFFh are taken to be negative numbers; 8000h represents 32768 and FFFFh represents -1. Whereas Example 5-4 has been used to add the positive number 65535 to accumulator A, Example 5-5 shows how to add the negative number -1 (FFFFh) to accumulator A. Example 5-5. SSBX SXM LD #1000h, A ; Turn on sign-extension mode. ; Accumulator A now contains 00001000h.

TUTORIAL

T-51

ASSEMBLY LANGUAGE TUTORIAL ; Add -1 to accumulator A. Accumulator A ; now contains 00000FFFh. When sign-extension mode is turned on, the largest positive value we can add to an accumulator is +32767 (7FFFh). Example 5-6. SSBX SXM LD #7FFFh, B ADD #7FFFh, B ; ; ; ; ; Turn on sign-extension mode. Load accumulator B with maximum positive value (+32767). Add +32767 (7FFFh) to accumulator B. Accumulator B now contains 0000FFFEh. ADD #0FFFFh, A

Simple Subtraction In a similar way to which we carried out addition, we can subtract an immediate value from either accumulator A or accumulator B. For this we can use the instruction SUB (subtract from accumulator). Again, the instruction SUB is affected by sign-extension mode. If the value being subtracted lies in the range 0 to +32767, then sign-extension mode has no effect. Example 5-7. LD #7EDCh, A SUB #56h, A ; Load accumulator A with 00007EDCh. ; Subtract 56h from accumulator A. ; Accumulator A now contains 00007E86h.

When sign-extension mode is turned off, the constant lies in the range 0 to 65535 (0 to FFFFh). When signextension mode is turned on, the constant to be subtracted must lie in the range -32768 to +32767 (8000h to 7FFFh). This means that the largest positive value that can be subtracted is 7FFFh. We can subtract the contents of a data memory address from an accumulator using the instruction SUB (subtract from accumulator). For example, to subtract the contents of data memory address 71h from accumulator B we can write: Example 5-8. RSBX SXM LD #1, DP ST #8000h, @71h ; ; ; ; ; ; ; ; ; ; ; ; Turn off sign-extension mode. Page 1. Access to data memory addresses 80h to FFh. Store the value 8000h (a positive number) at data memory address 80h + 71h = F1h. Load accumulator B with 0000FF00h (a positive number). Subtract the unsigned contents of data memory address 80h + 71h = F1h from accumulator B. The result 00007F00h is now in accumulator B.

LD #0FF00h, B SUB @71h, B

TUTORIAL

T-52

ASSEMBLY LANGUAGE TUTORIAL We can perform a similar operation as shown in Example 5-8, but instead using indirect addressing and with a signed value in data memory:

TUTORIAL

T-53

ASSEMBLY LANGUAGE TUTORIAL Example 5-9. SSBX SXM STM #0F1h, AR4 ST #3000h, *AR4 ; ; ; ; ; ; ; ; ; ; ; ; Turn on sign-extension mode. Store the address of data memory location F1h in AR4. AR4 = F1h. Store 3000h (a positive number) at the data memory address pointed to by auxiliary register AR4. Load accumulator B with FFFF8F00h (a negative number). Subtract the signed contents of the data memory address pointed to by AR4 from accumulator B. The result FFFF5F00 is now in accumulator B.

LD #8F00h, B SUB *AR4, B

Adding Directly to Memory We may wish to perform an addition on the contents of a memory address. An example of such an operation is given using C code in Example 5-10: Example 5-10. unsigned int value; value += 0x00FF;

// Add constant FFh to value.

The instruction ADD can only be applied to either accumulator A or to accumulator B. To add an immediate value to a data memory address, we must use the instruction ADDM (add long immediate value to memory). Example 5-10 has been implemented in assembly language in Example 5-11 and assumes that the variable value is stored at data memory address 222h: Example 5-11. LD #4, DP ADDM #0FFh, @22h ; ; ; ; ; ; Page 4. Access to data memory addresses 200h to 27F. Add FFh to the contents of data memory address 200h + 22h = 222h. The result is placed at data memory address 222h.

Here the letter M in the instruction ADDM means memory, not memory-mapped register. The instruction ADDM takes two operands. The first operand is the immediate value and the second operand is the data memory address. The result of the addition is placed in the data memory address. The instruction ADDM is affected by sign-extension mode. When sign-extension mode is turned off, the constant is taken to be positive in the range 0 to 65535 (0 to FFFFh). When sign-extension mode is on, the constant is taken as being a signed value in the range -32768 to +32767 decimal (8000h to 7FFFh). We can carry out the same operation in Example 5-11, but this time using indirect addressing:

TUTORIAL

T-54

ASSEMBLY LANGUAGE TUTORIAL Example 5-12. STM #222h, AR1 ADDM #0FFh, *AR1 ; ; ; ; ; ; Store address of data memory location 222h in auxiliary register AR1. Add FFh to the contents of data memory address pointed to by auxiliary register AR1. The result is placed at data memory address pointed to by AR1.

Subtraction from Data Memory We may also wish to perform a subtraction of a constant from the contents of a memory location. Example 5-13 shows how this would be done in C code: Example 5-13. signed int value; value -= 0x0001;

// Subtract constant 1 from value.

The TMS320C54x does not provide an instruction to subtract an immediate value (constant) directly from the contents of a data memory address. Instead we must use the instruction ADDM (add long immediate value to memory) with a negative constant. An implementation of Example 5-13 using assembly language is shown in Example 5-14. Assuming the variable value is stored at data memory address 284h: Example 5-14. ; Turn on sign-extension mode. ; Page 5. Access to data memory ; addresses 280h to 2FFh. ADDM #0FFFFh, 4h ; Add FFFFh (-1) to the contents ; of data memory address 280h + 4h = ; 284h. The result is placed in data ; memory address 284h. The same operation as shown in Example 5-14 can be carried out using indirect addressing. This is shown in Example 5-15: Example 5-15. SSBX SXM STM #284h, AR2 ; ; ; ; ; ; ; ; ; Turn on sign-extension mode. Store the address of data memory location in auxiliary register AR2. AR2 = 284h. Add FFFFh (-1) to the contents of data memory address pointed to by auxiliary register AR2. The result is placed in data memory address pointed to by AR2. SSBX SXM LD #5, DP

ADDM #-1, *AR2

TUTORIAL

T-55

ASSEMBLY LANGUAGE TUTORIAL Preventing Overflow During Addition and Subtraction Consider the following simple problem. Accumulator A contains 7FFFFFFFh and we add 1. The result will be 80000000h. If the number in accumulator A represents a signed number, then adding 1 to 7FFFFFFFh (a large positive number) will produce the result of 8000000h (a large negative number). The addition causes the contents of the accumulator to overflow beyond the upper limit of signed values. The TMS320C54x provides a mechanism to prevent overflows and inadvertent sign changes. This is done by setting the overflow flag in status register ST1 to 1 to turn on overflow-mode (OVM = 1). In all the examples given in this tutorial so far, it has been assumed that overflow-mode has been switched off (OVM = 0). The instructions ADD, SUB and ADDM are all affected by overflow-mode. When overflow mode is turned on, the instruction ADD imposes an upper value of 7FFFFFFFh in the accumulators, whether or not sign-extension mode is on. Assuming that accumulator A already contains 7FFFFFFFh (the maximum signed value that can be held in the 32 bits of an accumulator) then: Example 5-16. SSBX OVM ADD #1, A ; ; ; ; ; Turn on overflow-mode. Add 1 to accumulator A. Accumulator A still contains 7FFFFFFFh. The sum has been limited to a maximum value of 7FFFFFFFh.

When an addition occurs that would take the value in an accumulator over its maximum, then the accumulator limits itself to 7FFFFFFFh. This is may be preferable to going to 80000000h which can represent a negative number. In a similar way, the instruction SUB imposes a lower limit of 80000000h, whether or not signextension mode is on. Assuming that accumulator A already contains 80000000h (the most negative signed value that can be held in the 32 bits of an accumulator) then: Example 5-17. SSBX OVM SUB #1, A ; ; ; ; ; ; Turn on overflow-mode. Subtract 1 from accumulator A. Accumulator A still contains 80000000h. The subtraction has been limited to a minimum value of 80000000h.

When an subtraction occurs that would take the value in an accumulator below its minimum, then the accumulator limits itself to 80000000h. Again this may be preferable to the value in the accumulator changing sign and becoming 7FFFFFFFFh which can represent a positive number. When using the instruction ADDM, overflow mode limits the contents of a data memory address to a maximum of 7FFFh and a minimum of 8000h.

TUTORIAL

T-56

ASSEMBLY LANGUAGE TUTORIAL Example 5-18. SSBX OVM LD #3, DP ST #7FFFh, @30h ADDM #1h, @30h ; ; ; ; ; ; ; ; Turn on overflow mode. Page 3. Gain access to data memory addresses 180h to 1FFh. Store the value 7FFFh in data memory address 180h + 30h = 1B0h. Add 1 to the contents of data memory address 1B0h. Data memory address 1B0h now contains 7FFFh.

Incrementing and Decrementing Auxiliary Registers We have seen how to carry out additions and subtractions using accumulators and values stored in data memory. We shall now carry out additions and subtractions on the auxiliary registers AR0 to AR7. Should we wish to increment or decrement the contents of an auxiliary register without affecting the contents of the data memory address, we use the instruction MAR (modify auxiliary register). This takes a single operand as shown in Example 5-19: Example 5-19. MAR MAR MAR MAR *AR3+ *AR3*AR3 AR3+ ; ; ; ; Increment AR3. Decrement AR3. Neither decrements nor increments AR3. Illegal. Missing *.

The analogy of C code would be: Example 5-20. unsigned int *AR3; AR3++; AR3--; // AR3 is a pointer to memory. // Point to next higher address. // Point to next lower address.

Here the letter M in the instruction MAR means modify, rather than move, as is the case with some other processors. Please note that the description given here of the instruction MAR only covers some of the capabilities of the instruction MAR. More details as to the usage of the instruction MAR are given in the TMS320C54x databook. An important point to remember is that when using the instruction MAR (modify auxiliary register), the symbol * does not refer to indirect addressing. It simply means an operation is carried out on the auxiliary register. Modifying Auxiliary Registers In order to add a constant from the value in an auxiliary register, we use a special variation of the instruction MAR that adds the value in AR0 to the auxiliary register. Example 5-21 shows how to add together the contents of two auxiliary registers.

TUTORIAL

T-57

ASSEMBLY LANGUAGE TUTORIAL Example 5-21. STM #100, AR4 STM #10, AR0 MAR *AR4+0 ; ; ; ; Store the constant 100 decimal in AR4. Store the constant 10 decimal in AR0. Add AR4 to AR0 and store sum in AR4. AR4 now contains 100 + 10 = 110 (6Eh).

The instruction MAR *AR4+0 modifies auxiliary register AR4 by adding the contents of AR0 to AR4 and then putting the sum in AR4. Similarly, we can subtract a value from an auxiliary register. Example 5-22. STM #100, AR2 STM #20, AR0 MAR *AR2-0 ; ; ; ; Store the constant 100 decimal in AR2. Store the constant 20 decimal in AR0. Subtract AR0 from AR2 and store result in AR2. AR2 now contains 100-20 = 80 (50h).

The value to be added or subtracted must always be put in AR0. The sum or difference will always be stored in the register listed first in instruction MAR. Example 5-23. MAR *AR7+0 MAR *AR6-0 MAR *AR0+0 MAR *AR3+4 ; ; ; ; ; Add AR0 to AR7 and store sum in Subtract AR0 from AR6 and store AR6. Add AR0 to AR0 and store sum in Illegal attempt to subtract AR4 AR7. result in AR0. from AR3.

The operator on the auxiliary register of the type *AR5+0 can also be used with some of the other instructions already introduced. Example 5-24. STL A, *AR4+0 ; ; ; ; ; ; ; ; ; ; ; ; ; Store the low word of accumulator A in the data memory address pointed to by AR4. Add contents of AR0 to AR4. Store the high word of accumulator A in the data memory address pointed to by AR5. Subtract the contents of AR0 from AR5. Store 400h at the data memory address pointed to by AR6. Add the contents of AR0 to AR6. Store 32h at the data memory address pointed to by AR7. Subtract the contents of AR0 from AR7.

STH A, *AR5-0

ST #400h, *AR6+0

ST #32h, *AR7-0

TUTORIAL

T-58

ASSEMBLY LANGUAGE TUTORIAL STM #99h, *AR1-0 ; Illegal. Not allowed to modify auxiliary ; register with instruction STM.

In each of the sample instructions in Example 5-24, the auxiliary register is modified after the store operation has been done. Differences Between the Instructions STM and MAR When first learning TMS320C54x assembly language, the differences between the instructions STM (store in memory-mapped register) and MAR (modify auxiliary register) may not be immediately obvious. The instruction STM stores a value in a memory-mapped register AR0 to AR7. It is not possible to modify an auxiliary register as part of this instruction. Example 5-25. STM #33h, AR0 STM #33h, AR0+ ; Store the immediate value 33h in ; memory-mapped register AR0. ; Illegal. Not allowed to modify AR0.

By contrast, the instruction MAR modifies an auxiliary register but does not store any data. Example 5-26. MAR *AR0; Decrement the contents of auxiliary ; register AR0. No data has been ; stored.

A practical example of how we would use the instructions STM and MAR is to implement a for loop in C using an auxiliary register to store our variable. Consider part of the for loop shown in Example 5-27: Example 5-27. unsigned int x; for ( x = 0 ; x < 10; x++) { /* Body of for loop */} To implement part of this in assembly language we will use auxiliary register AR4 to store the variable x. Example 5-28. STM #0, AR4 ; ; ; ; ; ; Store the value zero in memorymapped register AR4. This is equivalent to x = 0. Increment the contents of auxiliary register AR4. This is equivalent to x++.

MAR *AR4+

In the case where we use an auxiliary register to store the C variable, no operations are performed on data memory.

TUTORIAL

T-59

ASSEMBLY LANGUAGE TUTORIAL Converting Numbers from Unsigned to Signed Within a program, there is sometimes the need to convert a positive number to a negative number. For example, as part of an algorithm we sometimes have a variable that is positive at times, and negative at others. As a simple problem, how do we convert the positive number +1 to the negative number -1? Expressed as a 16-bit number, +1 is 0001h and -1 is FFFFh. To convert a positive number to a negative number, one way is to complement the number and then add 1 to it. This is shown in Example 5-29:

TUTORIAL

T-60

ASSEMBLY LANGUAGE TUTORIAL Example 5-29. LD #1h, A CMPL A ; ; ; ; ; ; Load accumulator A with 00000001h. Complement accumulator A. Accumulator A now contains FFFFFFFEh. Add 1 to accumulator A. Accumulator A now contains FFFFFFFFh (-1).

ADD #1, A

The instruction CMPL is not affected by sign-extension mode. In order to convert an unsigned number to a signed number, the TMS320C54x provides the instruction NEG (negate accumulator): Example 5-30. LD #1h, A NEG A ; ; ; ; Load accumulator A with 00000001h. Negate accumulator A. Accumulator A now contains FFFFFFFFh (-1 decimal).

Again the instruction NEG is not affected by sign-extension mode. Upgrading from the TMS320C25 and TMS320C2xx/5x to the TMS320C54x The TMS320C54x has two accumulators, whilst earlier devices had only one. This means that earlier logical and arithmetic instructions designed for use with a single accumulator cannot be used on the TMS320C54x. Table 5-1 gives a comparison of instructions: Table 5-1. Comparison of Instructions
Description TMS320C25 Instruction ADDK 100h SUBK 100h No equivalent MAR *,ARx+ MAR *, ARxNEG TMS320C2xx and TMS320C5x Instruction ADD #100h SUB #100h No equivalent MAR *,ARx+ MAR *, ARxNEG TMS320C54x Instruction ADD #100h, Acc SUB #100h, Acc ADDM #0FFh, memory MAR *ARx+ MAR *ARxNEG Acc

Add constant to accumulator Subtract constant from accumulator Add constant to memory Increment ARx Decrement ARx Negate accumulator

Here x is a number between 0 and 7 and ARx is one of the current auxiliary registers AR0 to AR7, memory = data memory address and Acc = accumulator A or B. Because the TMS320C54x has two accumulators, it takes and extra operand compared to the single one used with earlier devices.

TUTORIAL

T-61

ASSEMBLY LANGUAGE TUTORIAL

The usage of the instruction MAR (modify auxiliary register) has changed between the earlier devices and the TMS320C54x. On the earlier devices, the instruction was mostly used to change the current auxiliary register. However, on the TMS320C54x it is mostly used to alter the value of an auxiliary register. Unlike earlier devices, the TMS320C54x can now directly add a positive or negative constant to the contents of a data memory address using the instruction ADDM (add long constant to memory). To carry out the same operation using an earlier processor was considerably less efficient because arithmetic operations could only be carried out in the accumulator. This meant copying the contents of the data memory address to the accumulator, performing the operation and then putting the result back in the data memory address.

Questions
1. 2. How do we add the constant 7Fh to accumulator A? What value will be in accumulator A after the following sequence of instructions has been executed? RSBX SXM LD #1, A ADD #0FFFFh, A Which of the following does the instruction SUB #100h, A perform? a) constant 100h - accumulator A b) accumulator A - constant 100h What value will accumulator B contain after the following instructions have been executed? RSBX SXM LD #0FFFFh, B SUB #1, B The instruction ADDM #10, *AR4 means: a) Add long immediate value 10 to accumulator A b) Add long immediate value 10 to memory address pointed to by AR4 c) Add long immediate value 10 to memory-mapped register AR4? What is the difference in meaning in the letter M in the instructions STM and ADDM? How do we add 1 to the contents of data memory address 400h without using an accumulator? There is no instruction to subtract a value from the contents of a data memory address. How do we subtract 1 from the value contained at data memory address 300h without using an accumulator? Assume the data pointer DP already contains 6. The instruction MAR means: a) move auxiliary register b) modify auxiliary register c) multiply auxiliary register? How does the usage of the symbol * differ between the following instruction: a) LD *AR2+ b) MAR *AR2+

3.

4.

5.

6. 7. 8.

9.

10.

TUTORIAL

T-62

ASSEMBLY LANGUAGE TUTORIAL 11. 12. What is the difference between the instruction MAR and the instruction STM? What is the difference between the instructions CMPL and NEG?

TUTORIAL

T-63

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 6:

Branch Instructions

New Instructions Introduced B BC

New Test Conditions Introduced AEQ BEQ ANEQ BNEQ AGT BGT ALT BLT ALEQ BLEQ AGEQ BGEQ Overview of Tutorial In the course of a program there is often the need for decision making, the outcome of which controls the flow of the program. In a high level language, decision making can be written in terms of an IF - THEN - ELSE type of structure. To implement decision making using TMS320C54x assembly language, we employ two types of branch instructions: conditional branches and unconditional branches. This tutorial shows how to use both types of branch instructions. Unconditional Branches Readers may well be familiar with the concept of the goto statement in BASIC. When a goto is encountered, rather than falling through to the next line, the program execution is redirected to the program memory address specified as part of the instruction goto. On the TMS320C54x, the instruction B (branch unconditionally) is the assembly language equivalent of the goto in BASIC. When an instruction B is encountered, the program execution continues at the address specified by the operand. An example of how to use the instruction B is shown in Example 6-1: Example 6-1. LD #100h, A B end STM #20, AR2 LD #30h, B ; ; ; ; ; ; ; Load accumulator A with 100h. Branch to the label end. This instruction is skipped. This instruction is the one executed immediately after the instruction B (branch unconditionally).

end:

TUTORIAL

T-64

ASSEMBLY LANGUAGE TUTORIAL

The instruction B takes a single operand - the address to which program execution is redirected, which is specified by a label. In Example 6-1, when the instruction B end is encountered, the next instruction to be executed is the one at the label end. All instructions between the instruction B and the associated label end are skipped (not executed). The label to which the program branches must appear in the first column of the page. A special symbol $ may be used as the label and indicates that the branch is to the same address as the instruction B; in other words, the instruction B $ branches to itself. The two unconditional branches in Example 6-2 are therefore equivalent: Example 6-2.

done:

B $ B done

; Branch to current address. ; Branch to the label done.

In either case, the branch will be to the same address as the instruction itself, so we have infinite loops. Branching to the same address may be used at the end of a program or to lock-up the program when a fatal error occurs. The label $ can also be used in the expression B $+2 (branch to the address at the program counter + 2) or B $2(branch to the address at program counter - 2). Conditional Branches We may wish for a branch to occur under certain circumstances but not under others. Whether we branch depends upon the value of a certain variable. This implies that some form of test is built into the process. To implement a branch that only occurs when a certain condition is met, we use the instruction BC (branch conditionally). This takes two operands, and a typical usage is shown in Example 6-3: Example 6-3. BC label, condition ; If condition is true then ; branch to label, otherwise drop ; through to the next instruction.

The first operand is the address (as denoted by a label). When a branch occurs, this is where the next instruction will be executed. It works in the same way as the label used with the instruction B. The second operand is the test and this determines whether a branch occurs. On the TMS320C54x, the most convenient variables to test are accumulator A and accumulator B. Tests for other parameters e.g. the carry (C) flag and overflow flags (OVA or OVB) are possible and will be used in later tutorials. For the purposes of this tutorial, we shall restrict all tests to accumulator A and accumulator B.

TUTORIAL

T-65

ASSEMBLY LANGUAGE TUTORIAL Note that we would never actually type in the word condition, but would in fact type in one of a range of special operands. An example of these special operands is AEQ. This will evaluate to TRUE when the contents of accumulator A are zero. For all values of accumulator A except zero, the condition AEQ will evaluate to FALSE. Testing for Equality Let us start with a simple example of an if statement. If the value of variable x is 5, then increment x. In C code we could write this as: Example 6-4. unsigned int x; if ( x == 5) { x++; } // Allocate a variable x. // Test if the value of x is 5. // If so, increment variable x.

Depending upon the value of the variable x we carry out an action. To implement Example 6-4 in assembly language, we must first assign a data memory location to store the variable x. Let us use data memory address 130h. In order to test the value of the variable x, we must copy it from data memory to either accumulator A or accumulator B. An implementation is given in Example 6-5: Example 6-5. LD #2, DP LD 30h, A ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Page 2. Gain access to data memory addresses 100h to 17Fh. Copy variable x, stored at data memory address 100h + 30h = 130h into accumulator A. Subtract 5 from variable x. If the value in accumulator A is equal to zero (the condition AEQ is TRUE), then branch to the label incr. Otherwise, drop through to the next line. No action to be taken. Skip next instruction. Increment variable x. Processing complete.

SUB #5, A BC incr, AEQ

B done incr: done: ADDM #1, 30h

In Example 6-5, the instruction B (branch conditionally) uses the label incr as the first operand. When a branch occurs, it will be to this label. The second operand is the particular test, in this case the condition AEQ (accumulator A is equal to zero). If the condition AEQ evaluates to TRUE, then program execution continues at the program address specified by the label incr. On the other hand, if the test condition does not evaluate to TRUE, the branch does not occur and program execution continues at the next line.

TUTORIAL

T-66

ASSEMBLY LANGUAGE TUTORIAL

We can in fact write the code in Example 6-5 in a slightly neater way that saves one instruction. Let us this time use accumulator B for the test. The operand we shall use with the instruction BC (branch conditional) is BNEQ (accumulator B not equal to zero). Example 6-6. LD #2, DP LD 30h, B ; ; ; ; ; ; ; ; ; ; ; ; ; ; Page 2. Gain access to data memory addresses 100h to 17Fh. Copy variable x at data memory address 100h + 30h = 130h into accumulator B. Subtract 5 from the copy of variable x in accumulator B. If the value in accumulator B is not equal to zero (the condition BNEQ is TRUE), then branch to the label done. Otherwise, drop through to the next line. Increment variable x. Processing complete.

SUB #5, B BC done, BNEQ

ADDM #1, 30h done:

In the case of Example 6-6, we branch on the opposite condition to which we wish to carry out the operation. We have used the test for the condition BNEQ (accumulator B not equal to zero) to go straight to the label done if the value of the variable is not 5. This has eliminated the instruction B (branch unconditionally). Branching on the opposite condition is often used by the TMS320C54x C compiler to reduce the amount of code required. In Examples 6-4, 6-5 and 6-6, we have performed a comparison of the variable x with 5 using the instruction SUB (subtract from accumulator). Another way to perform the comparison is to use the instruction XOR (logical exclusive OR accumulator). In this case, we could have written Example 6-6 as: Example 6-7. LD #2, DP ; ; ; ; ; ; ; ; Page 2. Gain access to data memory addresses 100h to 17Fh. Copy variable x at data memory address 100h + 30h = 130h into accumulator B. Compare 5 with the copy of variable x.

LD 30h, B

XOR #5, B

TUTORIAL

T-67

ASSEMBLY LANGUAGE TUTORIAL BC done, BNEQ ; ; ; ; ; ; ; If the value in accumulator B is not equal to zero (the condition BNEQ is TRUE), then branch to the label done. Otherwise, drop through to the next line. Increment variable x. Processing complete.

ADDM #1, 30h done:

Care does need to be taken using the instruction XOR (exclusive OR) for comparisons. The instruction XOR tests only the low word of the accumulator. Therefore, if the value to be tested in the accumulator is greater than 0000FFFFh, the instruction SUB (subtract from accumulator) should be used instead. Testing for Inequality In Example 6-4, we carried out an action if variable x was a certain value. We can also test for the opposite condition, as shown in Example 6-8: Example 6-8. unsigned int x; if ( x != 5) { x++; } // Allocate a variable x. // If the value of x is not 5 then // increment variable x.

The code to implement this is similar to that of Example 6-6, except that in this case we use the operand BEQ (accumulator B equal to zero). We shall also use indirect addressing: Example 6-9. STM #130h, AR5 ; ; ; ; ; ; ; ; ; ; ; ; ; ; Store address in data memory of variable x in auxiliary register AR5. AR5 = 130h. Copy variable x into accumulator B. Subtract 5 from copy of variable x. If the value in accumulator B is equal to zero (the condition BEQ is TRUE), then branch to the label done. Otherwise, drop through to the next line. Increment variable x. Processing complete

LD *AR5, B SUB #5, B BC done, BEQ

ADDM #1, *AR5 done:

TUTORIAL

T-68

ASSEMBLY LANGUAGE TUTORIAL Using an IF-ELSE Structure Let us now make the program flow slightly more complex. If the value of variable y is equal to 20, then increment y. Otherwise, decrement y. In C code we would write this as shown in Example 6-10: Example 6-10. unsigned int y; if ( y == 20 ) { y++ ; } else { y--; } // Declare a variable. // Test if x is equal to 20. // Increment variable y. // Decrement variable y.

Depending upon the value of the variable y we carry out an action. To implement Example 6-10 in assembly language, to test the value of the variable y, we must first copy it to the accumulator. If the variable y is stored at data memory address 2FFh, then the assembly language implementation of Example 6-10 is shown in Example 6-11:

TUTORIAL

T-69

ASSEMBLY LANGUAGE TUTORIAL Example 6-11. LD #5, DP LD 7Fh, A ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Page 5. Access to data memory addresses 280h to 2FFh. Copy variable y at data memory address 280h + 7Fh = 2FFh into accumulator A. Compare variable y with 20. If equal, accumulator A will then contain zero. If the value in accumulator A is not equal to zero (the condition ANEQ is TRUE), then branch to the label next. Otherwise, drop through to the next line. Increment variable y. No more processing to do. Turn on sign-extension mode. Subtract 1 from y. y = y + (-1). End of processing.

XOR #20, A

BC next, ANEQ

next: done:

ADDM #1, 7Fh B end SSBX SXM ADDM #0FFFFh, 7Fh

Note that in Example 6-11 we have used the instruction ADDM (add long immediate value to memory) to subtract 1 from the variable y. There is no such instruction as SUBM (subtract long immediate from memory). Therefore we have turn on sign-extension mode to add -1 (FFFFh) to the contents of the data memory address in order to decrement variable y. Testing for Greater Than So far we have tested for equality and inequality when branching. There are cases where we need to test for other conditions such as greater than or less than. Let us take a practical example. We have an input that we wish to give a maximum limit to avoid overloads. We can limit the input as shown in Example 6-12: Example 6-12. unsigned int input; if ( input > 1000 ) { input = 1000; } // Declare a variable. // Range check input. // Truncate if too great.

Assuming our variable input is stored in data memory address 310h, we can implement the C code in Example 6-12 as shown in Example 6-13:

TUTORIAL

T-70

ASSEMBLY LANGUAGE TUTORIAL Example 6-13. LD #6, DP LD 10h, A ; ; ; ; ; ; ; ; ; ; ; ; ; Page 6. Access to data memory addresses 300h to 37Fh. Copy variable input at data memory address 300h + 10h = 310h into accumulator A. Accumulator A - 1000. If input is greater than 1000 then branch to label limit. No processing if variable input is not greater than 1000. Limit value in variable input to a maximum of 1000. No more processing to do.

SUB #1000, A BC limit, AGT B done limit: done: ST #1000, 10h

The operand used with the instruction BC that causes a branch with opposite sense to that of operand AGT (accumulator A greater than zero) is ALEQ (accumulator A is less than or equal to zero). Let us repeat the operation as shown in Example 6-13, but this time using indirect addressing with accumulator B. In order to eliminate the instruction B (branch unconditional) to reduce the code size, we shall use the instruction BC (branch conditional) with the operand BLEQ (accumulator B is less than or equal to zero): Example 6-14. STM #310h, AR2 ; Store address of variable input ; in auxiliary register AR2. AR2 = ; 310h. ; Copy variable input into ; accumulator B. ; Accumulator B - 1000. ; If input is less than or equal ; to 1000 (the condition BLEQ is ; TRUE) then branch to the label ; done. ; Limit value in variable input to ; 1000. ; No more processing to do.

LD *AR2,

B

SUB #1000, B BC done, BLEQ

ST #1000, *AR2 done: Testing for Less Than

Let us take another example. Consider the case where we have a variable to which we wish to apply a lower limit of 1000.

TUTORIAL

T-71

ASSEMBLY LANGUAGE TUTORIAL Example 6-15. // Declare a variable. // Range check input. // Apply lower limit if // too small. Assuming our variable input1 is stored at data memory address 181h, we can implement the C code in Example 6-15 as: unsigned int input1; if ( input1 < 1000 ) { input1 = 1000; } Example 6-16. LD #3, DP LD 1h, A ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Page 3. Access to data memory addresses 180h to 1FFh. Copy variable input1 at data memory address 180h + 1h = 181h into accumulator A. Accumulator A - 1000. If input1 is less than 1000 (the condition ALT is TRUE) then branch to the label limit. No processing if variable input1 is greater than or equal to 1000. Limit value in variable input1 to 1000. No more processing to do.

SUB #1000, A BC limit, ALT

B done

limit: done:

ST #1000, 1h

The operand causing a branch with the opposite sense to the operand ALT (accumulator A less than zero) is AGEQ (accumulator A greater or equal to zero). Let us repeat the operation in Example 6-16, this time using indirect addressing with accumulator B and the operand BGEQ (accumulator B greater or equal to zero):

Example 6-17. STM #181h, AR5 ; ; ; ; ; ; Store address of variable input1 in auxiliary register AR5. AR5 = 181h. Copy variable input1 into accumulator B. Accumulator B - 1000.

LD *AR5, B SUB #1000, B

TUTORIAL

T-72

ASSEMBLY LANGUAGE TUTORIAL BC done, BEQ ; ; ; ; ; ; ; If input1 is greater than or equal to 1000 (the condition BEQ is TRUE) then branch to the label done. Apply lower limit of 1000 to variable input1. No more processing to do.

ST #1000, *AR5 done: Testing for Greater or Equal

This time we shall test the condition where the variable under test is greater than or equal to a certain value, and if so, take action accordingly. Consider the case of a water heating system where if the water temperature is 95 Centigrade degrees or more, we require the heater output to turn down to half power (50%). This is shown in the C code in Example 6-18: Example 6-18. unsigned int temperature; unsigned int heater; if ( temperature >= 95 ) { heater = 50; } // // // // Input variable. Output variable. Range check input. Set output accordingly.

Assuming our variable temperature is stored in data memory address 144h and the variable heater at 145h, we can implement Example 6-18 as: Example 6-19. LD #2 ,DP ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Page 2. Gain access to data memory addresses 100h to 17Fh. Copy variable temperature at data memory address 100h + 44h = 144h into accumulator A. Accumulator A - 95. If temperature is greater than 95 (the condition AGEQ is TRUE) then branch to the label act. No processing if temperature is less than 95. Set value in variable heater to 50. No more processing to do.

LD 44h,

A

SUB #95, A BC act, AGEQ

B done act: done: ST #50, 45h

The operand that causes a branch with the opposite sense to the operand AGEQ (accumulator A greater or equal to zero) is ALT (accumulator A less than zero). By using the instruction BC (branch conditional) with the operand of opposite sense we can reduce the size of the code by doing away with the instruction B. This

TUTORIAL

T-73

ASSEMBLY LANGUAGE TUTORIAL time we shall use indirect addressing with accumulator B, so we need the corresponding operand BLT (accumulator B less than zero) Example 6-20. STM #144h, AR0 ; ; ; ; ; ; ; ; ; ; ; ; ; ; Store address of variable temperature in auxiliary register AR0. AR0 = 144h. Copy variable temperature into accumulator B. Increment AR5 to contain the address of variable heater. AR5 = 145h. Accumulator B - 95. If temperature is less than 95 (the condition BLT is TRUE) then branch to the label done. Set value in variable heater to 50. No more processing to do.

LD *AR0+,

B

SUB #95, B BC done, BLT

ST #50, *AR0 done: Testing for Less than or Equal to

Finally, we shall test the condition where the variable under test is less than or equal to a certain value, and if so, take action accordingly. A practical application might be a frost alarm, where if the air temperature is 5 degrees Centigrade or lower then we turn the heater on to full (100%). The C code to implement this is shown in Example 6-21. Example 6-21. unsigned int temperature; unsigned int heater; if ( temperature <= 5 ) { heater = 100; } // // // // Input variable. Output variable. Range check temperature. Set heater to 100 percent.

Assuming our variable temperature is stored in data memory location 144h and heater at 145h, we can implement the C code in Example 6-21 as: Example 6-22. LD #2, DP LD 44h, A ; ; ; ; ; ; Page 2. Gain access to data memory addresses 100h to 17Fh. Copy variable temperature at data memory address 100 + 44h = 144h into accumulator A. Accumulator A - 5.

SUB #5, A

TUTORIAL

T-74

ASSEMBLY LANGUAGE TUTORIAL BC frost, ALEQ ; ; ; ; ; ; ; ; If temperature is less than or equal to 5 degrees then branch to the label frost. No processing if variable temperature is greater than 5. Set value in variable heater to 100 percent . No more processing to do.

B done frost: done: ST #100, 45h

The operand with the opposite sense to the operand ALEQ (accumulator A less than or equal to zero) is AGT (accumulator A greater than zero). By using the instruction BC (branch conditionally) with the operand of opposite sense we can reduce the code by getting rid of the instruction B done. This time we shall use indirect addressing with accumulator B, so we use the operand BGT (accumulator B greater than zero), as illustrated in Example 6-23. Example 6-23. STM #144h, AR1 ; ; ; ; ; ; ; ; ; ; ; ; ; ; Store address in data memory of variable temperature in auxiliary register AR1. AR1 = 144h. Copy variable temperature into accumulator B. Increment AR1 to point to the variable heater. Accumulator B - 5. If variable temperature is greater than to 5 degrees then branch to the label done. Set value in variable heater to 100. No more processing to do.

LD *AR1+, B

SUB #5, B BC BGT, done

ST #100, *AR1 done:

Upgrading from the TMS320C25 and TMS320C2xx/5x to the TMS320C54x The syntax of the conditional branch instructions does vary across the devices. A table of the equivalent branch instructions is given in Table 6-1: Table 6-1. Comparison of Instructions
Description TMS320C25 Instruction Branch unconditional Branch if accumulator is equal to zero B BEQ B BCND EQ TMS320C2xx and TMS320C5x Instruction B BC AEQ or BC BEQ TMS320C54x Instruction

TUTORIAL

T-75

ASSEMBLY LANGUAGE TUTORIAL
Branch if accumulator is not equal to zero Branch if accumulator is less than zero Branch if accumulator is less than or equal to zero Branch if accumulator is greater than zero Branch if accumulator is greater than or equal to zero BGT BGE BCND GT BNCD GE BLE BCND LE BLT BCND LT BNE BCND NE BC ANEQ or BC BNEQ BC ALT or BC ALT BC ALEQ or BC BLEQ BC AGT or BGT BC AGEQ or BC BGEQ

Because the TMS320C54x has two accumulators, there are two sets of conditions for each branch instruction.

Questions
1. 2. 3. 4. What is the difference between a conditional branch and an unconditional branch? What is meant by the symbol $? The instruction BC loop4,AEQ (branch accumulator A equal) means equal to what? Why do we use the instruction SUB (subtract from accumulator) rather than the instruction XOR (exclusive OR with accumulator) in order to test the value in an accumulator? How would we test if the value stored at data memory address 75h is equal to 21? How would we test if the value stored at data memory address 76h is not equal to 5? Write code to test whether the contents of data memory address 77h are greater than 29, and if so, load accumulator A with 0. Write code to test if the contents of data memory address 78h are less than 300h, and if so, load accumulator B with 33h. How would we test if the value stored at data memory address 79h is greater than or equal to 1000h? How would we test if the value stored at data memory address 80h is less than or equal to 1Fh? Using accumulator A, what is the opposite condition to: a) ANEQ b) AEQ c) AGT d) ALT e) AGEQ f) ALEQ In several of the examples in this tutorial we have chosen to branch on the opposite condition to the one required to carry out an action. Why should this be the case?

5. 6. 7. 8. 9. 10. 11.

12.

TUTORIAL

T-76

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 7:

Loop Counters and Repeated Operations

New Instructions Introduced BANZ NOP RPT RPTZ Overview of Tutorial A commonly encountered task within a program is to execute an operation multiple times. Users of high level languages will be familiar with the way this is implemented using the FOR, WHILE and REPEAT-UNTIL (DOWHILE) constructions. This tutorial shows the usage of some of the special TMS320C54x instructions designed for controlling loops. Implementing FOR and WHILE loops A typical way to implement a control loop is to use a counter that starts at a preset value and is incremented every time the body of the loop is executed. When the counter reaches a certain value, execution of the loop terminates. We can implement a control loop in the C language using a FOR loop: Example 7-1. unsigned int i, j; for ( i = 0 ; i < 20 ; i++) { j++; } // 20 iterations. // Some operation.

The code in Example 7-1 can also be written in terms of a WHILE loop: Example 7-2. unsigned int i, j; i = 0; while ( i < 20 ) { i++; j++; }

// 20 iterations. // increment counter. // Some operation.

We can implement a FOR or a WHILE loop in Examples 7-1 and 7-2 using the accumulator for variable i and the instruction BC (branch conditionally). Assuming that variable j is stored at data memory address 70h:

TUTORIAL

T-77

ASSEMBLY LANGUAGE TUTORIAL

Example 7-3. LD #0, DP LD #0, A SUB #20, A BC done1, AGEQ ADD #20, A ADD #1, A ADDM #1, 70h B loop1 done1: Performing a comparison of the counter i with the terminating value, here 20, is not the most efficient of operations. Implementing a REPEAT-UNTIL (DO-WHILE) Loop The other type of loop we can use is the REPEAT-UNTIL type of loop, known as the DO-WHILE loop in C. Example 7-4 shows the implementation of a DO-WHILE loop using a counter that counts down and terminates when the count reaches zero: Example 7-4. unsigned int i = 20; do { i--; j++; } while ( i > 0 ) // Assign variable. // Start of loop. // Decrement counter. // Some operation in loop. // Test condition. ; ; ; ; ; ; ; ; ; ; ; Page 0. Gain access to data memory addresses 60h to 7Fh. Initialize variable i to 0. Accumulator A - 20. Test if variable i is greater than or equal to 20. Restore accumulator A to value it was before the subtraction. Increment variable i. Increment variable j. Go round again.

loop1:

To implement Example 7-4 in TMS320C54x assembly language, we assign variable i to accumulator A and variable j to a data memory address, in this case data memory address 70h: Example 7-5. LD #0, DP LD #20, A SUB #1, A ADDM #1, 70h ; ; ; ; ; Page 0. Gain access to data memory addresses 60h to 7Fh. Initialize variable i to 20. Decrement variable i. Increment variable j.

loop2:

TUTORIAL

T-78

ASSEMBLY LANGUAGE TUTORIAL BC loop2, ANEQ ; ; ; ; ; If variable i is not equal to zero, then branch to the label loop2, otherwise terminate loop by dropping through to the next line.

If a comparison is made of Examples 7-3 and 7-5, it will be seen that as far as code efficiency is concerned, the DO-WHILE in Example 7-5 loop requires less instructions. Implementing a Loop Counter using the TMS320C54x Another way to write a DO-WHILE loop to carry out 20 operations is shown in Example 7-6: Example 7-6. unsigned int i = 19; do { j++; } while ( i-- != 0 ) // Assign variable. // Start of loop. // Some operation in loop. // Decrement and test.

In order to carry out 20 operations we need to load the counter i with 20 - 1 = 19 because the counter is decremented after the test. The special branch instruction BANZ (branch on auxiliary register non-zero) is provided for control loops and uses one of the auxiliary registers AR0 to AR7. This leaves accumulators A and B free for other purposes. If we assign variable i to auxiliary register AR3, then we can implement the DO-WHILE loop from Example 7-6 using the instruction BANZ (branch on auxiliary register non-zero): Example 7-7. LD #0, DP LD #19, AR3 loop3: ADDM #1, 70h BANZ loop3, *AR3; ; ; ; ; ; ; ; ; ; ; ; Page 0. Gain access to data memory addresses 60h to 7Fh. Start with 20 - 1 = 19 in AR3. Increment variable j. If the value in auxiliary register AR3 is non-zero, then branch to the label loop3. Otherwise, drop through to the next line of the program. In either case, decrement AR3.

The instruction BANZ takes two operands. The first operand is the label representing the address in program memory to which the program execution is redirected. The second operand is the auxiliary register to be

TUTORIAL

T-79

ASSEMBLY LANGUAGE TUTORIAL tested. In this case, the symbol * does not indicate indirect addressing; it simply means that the operation is carried out on an auxiliary register. It is important to put one less than the desired number of counts in the auxiliary register to be tested. In Example 7-7, the instruction BANZ tests the auxiliary register AR3, and if the contents are non-zero, branches (jumps) to the label loop3. If the contents of the current auxiliary register AR3 are zero, then the program execution continues linearly to the next line. Whether or not a branch occurs, the minus sign in the operator *AR3- decrements auxiliary register AR3. Branches to the Same Address Should we wish the program to branch to the current address we can write: Example 7-8. BANZ $, *AR3; Branch to current address until the ; value in AR3 is decremented to zero.

So that the loop terminates, it is necessary to either increment or decrement the auxiliary register used as the second operand. Example 7-9 does not alter the auxiliary register and therefore will remain in the loop forever: Example 7-9. BANZ $, *AR5 ; Auxiliary register AR5 is neither ; incremented nor decremented. This ; loop will never terminate.

The following are incorrect syntax and are not allowed: Example 7-10. BANZ loop2 BANZ loop2, AR4 BANZ *AR4Incrementing the Count In the usage of the instruction BANZ to date, we have chosen to decrement the auxiliary register. We also have the possibility of incrementing the auxiliary register: Example 7-11. STM #0FFF7h, AR3 ; Start with -9 in AR3. ; ; ; ; ; An auxiliary register is required as the second operand. Missing symbol * before the name of auxiliary register. Missing label for the first operand.

TUTORIAL

T-80

ASSEMBLY LANGUAGE TUTORIAL label3: BANZ label3, *AR3+ ; ; ; ; ; ; ; ; If the value in the auxiliary register (AR3) is non-zero, then branch to the label label3. Otherwise, execute the next line of the program. In either case, increment AR3.

It is more common to use the *AR3- variation to count down rather than to count up. The desired number of counts can then be put directly into the current auxiliary register. When counting up, a negative number must be put into the current auxiliary register which may be more difficult to read and debug. Note that the current auxiliary register is modified every time the instruction BANZ is executed, whether or not there is a branch. When the loop terminates, the current auxiliary register will no longer contain zero. Working in Non-Unit Values So far, every time we have executed the body of the loop, we have always changed the count by +1 or -1. We may wish to use a counter that works in some other units, for example 10. Consider the following example of C code that counts down from 100 in units of 10: Example 7-12. unsigned int i = 100; while ( i != 0) { i -= 10; } // Initialise counter to 100. // Loop while counter is not zero. // Decrement counter by 10.

The equivalent to Example 7-12 in TMS320C54x assembly language would be: Example 7-13. STM #10, AR0 ; ; ; ; ; ; ; ; ; ; Load auxiliary register AR0 with the amount that the loop counter is to be decremented by, in this case 10. Load counter AR3 with 100. Test the auxiliary register (AR3). If non-zero, branch to the same address, otherwise execute the next line of code. In both cases, subtract the value in AR0 from the auxiliary register (AR3).

STM #100, AR3 BANZ $, *AR3-0

Here the operand *AR3-0 means decrement the auxiliary register AR3 by the value in register AR0. The value in AR3 will be decremented by units of 10. The first time through, the auxiliary register AR3 contains 100

TUTORIAL

T-81

ASSEMBLY LANGUAGE TUTORIAL decimal. After the instruction BANZ, it will then contain AR3 - AR0 = 100 - 10 = 90. The instruction BANZ will be executed a total of 10 times before dropping through to the next line. In a similar way, we can also count up in units other than 1, as shown in Example 7-14. Example 7-14. STM #10, AR0 ; ; ; ; ; ; ; ; ; ; Load auxiliary register AR0 with the amount that the loop counter AR3 is to be incremented. Load counter AR3 with -90. Test the auxiliary register (AR3). If non-zero, branch to the same address, otherwise drop through to the next line. In either case, add the value in AR0 to auxiliary register AR3.

STM #0FFA6h, AR3 BANZ $, *AR3+0

Note that the instruction BANZ tests for zero, so that if the count does not go exactly to zero, the loop will not terminate as expected. This is shown in Example 7-15. Example 7-15. STM #3, AR0 ; ; ; ; ; ; ; ; ; Load auxiliary register AR0 with the amount that the loop counter AR2 is to be decremented by, in this case 3. Load counter AR2 with 11. Test the auxiliary register AR2. If non-zero, branch to the same address, otherwise execute the next line. In both cases, subtract the value in AR0 from the auxiliary register AR3.

STM #11, AR2 BANZ $, *AR2-0

In Example 7-15, the count in AR2 will be 11, 8, 5, 2, -1, -4 etc. and hence will not reach zero when expected. This means that the number of iterations performed will be greater than that intended. Filling Data Memory Addresses So far we have seen how to use the instruction BANZ for loop counters. Let us now look at a practical example. Say we wish to load the 16 successive data memory addresses starting at 300h with AAAAh, for example as part of a RAM test. This is shown in the following C code: Example 7-16. unsigned int *AR6; unsigned int AR5 = 16; AR6 = 0x300h; // Pointer to memory. // 16 iterations. // First memory address.

TUTORIAL

T-82

ASSEMBLY LANGUAGE TUTORIAL do { *AR6 = 0xAAAA; AR6++; } while (AR5-- != 0); // Load memory location. // Point to next address. // Count down until AR5 is // zero.

Because we are going to work on a series of different data memory addresses, it is easier to use indirect addressing than direct addressing. We shall use AR5 as our loop counter and AR6 as the pointer to the data. This is implemented in Example 7-17 and illustrates how to use multiple auxiliary registers in the same code fragment. Example 7-17. STM #300h, AR6 ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Use auxiliary register AR6 as a pointer to the start of data at data memory address 300h. Use auxiliary register AR5 as the loop counter. Operation to be executed 16 times. Save AAAAh at the data memory address pointed to by AR6. Point to the next higher address. If AR5 contains zero then terminate, otherwise branch to the label loop4. In either case, decrement AR5.

STM #16-1, AR5

loop4:

ST #0AAAAh, *AR6+

BANZ loop4, *AR5-

Note that the operand *AR6+ increments the address contained in auxiliary register AR6, not the contents of the data memory address pointed to by AR6. After the code in Example 7-17 has been executed, the data memory addresses 300h to 310h will each contain the value AAAAh. Repeated Operations Say we wish to generate a short time delay, for example to allow an input to settle before taking a reading. We need to execute some instructions a fixed number of times that have no effect upon the program execution. Ideal for this purpose is the instruction NOP (no operation). Example 7-18. NOP ; No operation. This instruction has no ; effect except to take up execution time.

To make up a delay might require the instruction NOP to be written out 100 times. This is wasteful use of program memory. Instead we combine the instruction NOP with the instruction RPT (repeat next instruction).

TUTORIAL

T-83

ASSEMBLY LANGUAGE TUTORIAL Example 7-19. RPT #99 NOP ; Repeat the next instruction 99 times. ; No operation.

The instruction RPT takes one operand which is the number of repeats. This is where it is easy to make a mistake. The constant gives the number of repeats, not the total number of times the instruction following the instruction RPT is executed. The above code in fact causes the instruction NOP to be executed a total of 100 times. It is easy to have an “out by one” error. With this in mind, we might write an operation to execute the instruction NOP a total of 1000 times as: Example 7-20. RPT #(1000-1) NOP ; Repeat the next instruction 999 times. ; No operation.

The instruction RPT is a very effective way of causing an instruction to be executed many times while writing a minimum amount of code. There is however, a limitation. Only one instruction following the instruction RPT will be repeated. Should it be necessary to repeat a block of code, then we would use the instruction BANZ. Not all instructions can be used with the instruction RPT. Most instructions that carry out simple operations such as load and store can be repeated. However, instructions that change the program counter, such as the instructions B (branch unconditionally) and BANZ are not repeatable. Using Repeats to Fill Data Words in Memory. Consider the following task. We wish to fill data memory addresses 300h to 3FFh with the value 0000h. In a practical situation, this may be used as part of a start-up routine to fill the data memory with known values. To use the analogy of C code, we could write: Example 7-21. unsigned int i; unsigned int *AR3 ; AR3 = 300h ; for (i = 0 ; i < 255 ; i++) { *AR3++ = 0x00; } // // // // // Iterations counter. Pointer to memory. Point to start address. Perform action 255 times. Fill memory with 00h.

In order to write to successive addresses we need to use indirect addressing combined with the instruction RPT, as shown in Example 7-22: Example 7-22. STM #300h, AR3 LD #0, A RPT #(255-1) ; ; ; ; Store block Clear Total address of start of data memory in AR3. AR3 = 300h. accumulator A. 255 operations.

TUTORIAL

T-84

ASSEMBLY LANGUAGE TUTORIAL STL A, *AR3+ ; ; ; ; ; Load low word of accumulator A (value = 0) into the data memory address pointed to by AR3. Increment value in AR3 to point to the next data memory address.

We initially set up auxiliary register AR3 as a pointer to the first data memory address we wish to load. There are 255 locations to fill with 0 so we require 255 - 1 = 254 repeats. After we load the low word of the accumulator into the data memory address, we also increment AR3 to point to the next data memory address. The important point here is that indirect addressing that makes it possible to load successive data memory locations with a single instruction STL. We can make a code reduction to Example 7-22. The instructions to clear an accumulator and to repeat the following instruction can be condensed into the single instruction RPTZ (repeat next instruction and clear accumulator). Example 7-23. ; Store the address of the start of data ; memory block in auxiliary register ; AR3. AR3 = 300h. RPTZ A, #(255-1) ; Clear accumulator A then repeat ; following operation 254 times. STL A, *AR3+ ; Load low word of accumulator A (value ; = 0) into the data memory address ; pointed to by AR3. Increment the value ; in AR3 to point to the next data ; memory address. Unlike the instruction RPT, the instruction RPTZ (repeat next instruction and clear accumulator) takes two operands. The first operand is the accumulator to be cleared and the second operand specifies the number of repeats. Note that the instruction RPTZ (repeat next instruction and clear accumulator) clears the accumulator once only, and not during every repeat. We can also apply the instruction RPTZ to accumulator B: Example 7-24. RPTZ B, #(10-1) ADD #1, B ; ; ; ; ; Clear accumulator B then repeat following operation 9 times. Increment accumulator B each repeat. When the repeat terminates, accumulator B will contain 9, not 1. STM #300h, AR3

Upgrading from the TMS320C25 and TMS320C2xx/5x to the TMS320C54x The instruction BANZ (branch on auxiliary register non-zero) is supported by the TMS320C25/2xx/5x and 54x. However, the way the instruction is used does differ.

TUTORIAL

T-85

ASSEMBLY LANGUAGE TUTORIAL On the TMS320C54x, the instruction BANZ always takes two operands, whereas the earlier devices were able to use between one and three. The differences between the various devices are illustrated in Table 7-1. Table 7-1. Comparison of BANZ Instructions
Description TMS320C25 Instruction BANZ loop5 BANZ loop6, *-, AR2 BANZ loop7, *+ BANZ loop8, *0BANZ loop9, *0+ TMS320C2xx and TMS320C5x Instruction BANZ loop5 BANZ loop6, *-, AR2 BANZ loop7, *+ BANZ loop8, *0BANZ loop9, *0+ TMS320C54x Instruction(s) BANZ loop5, *ARxBANZ loop6, *ARxLD #2, ARP BANZ loop7, *ARx+ BANZ loop8, *ARx-0 BANZ loop9, *ARx+0

Loop using decrement of auxiliary register. Loop using decrement of auxiliary register and set new ARP. Loop using increment of auxiliary register. Subtract AR0 from auxiliary register. Add AR0 to auxiliary register.

ARx = One of auxiliary registers AR0 to AR7. For the earlier devices, if no second operand is entered, it is taken to be the default operand of *-. On the TMS320C54x, the second operand must always be entered. Unlike the earlier devices, the TMS320C54x does not allow the current auxiliary register (ARP) to be changed as part of the instruction. The repeat instructions are used in a similar way across the families, but there is a small difference in syntax. In order to implement the equivalent of the TMS320C54x instruction RPTZ on an earlier processor, two instructions are required. Table 7-2. Comparison of Repeat Instructions
Description TMS320C25 Instruction RPTK 10 LACK 0 RPTK 99 TMS320C2xx and TMS320C5x Instruction RPT #10 LACL #0 RPT #99 TMS320C54x Instruction(s) RPT #10 RPTZ Acc, #99

Repeat following command 10 times. Clear accumulator and then repeat following instruction 99 times.

Here Acc = Accumulator A or accumulator B.

Questions
1. Why might we use a DO-WHILE construct in preference to a FOR construct?

TUTORIAL

T-86

ASSEMBLY LANGUAGE TUTORIAL 2. Add comments to the following code fragment: loop1: STL A, *AR2+ BANZ loop1, *AR3If using the instruction BANZ for a loop counter, why would we tend not use AR0 for our iterations counter? How many times will the following loop execute? STM #19, AR5 loop3: STL B, *AR6BANZ loop3 What will be the value in auxiliary register AR3 when the line after the instruction BANZ is reached? STM #10, AR3 BANZ $, *AR3What is meant by the expression LD A, *AR6+0? What is meant by the expression STL B,*AR2-0? How do we use the instruction NOP? It is intended to do 9 repeats using the following code: RPT 9 BANZ $, *AR5When debugging, the instruction BANZ is not repeated. Why is this? It is intended to execute the instruction STL a total of 100 times using the following code: RPT 100 STL A, *AR2+ When debugging, the instruction STL is executed 101 times. Why is this? How many times is accumulator B cleared to zero in the following example: RPTZ B, 100 There are cases where we cannot use the instruction RPT and have to use the instruction BANZ instead. Why is this?

3. 4.

5.

6. 7. 8. 9.

10.

11. 12.

TUTORIAL

T-87

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 8:

Multiplications

New Instructions Introduced MPY MPYU MPYA Overview of Tutorial Multiplication is one application where digital signal processing devices (DSPs) offer a considerable benefit in performance over microcontrollers (MCUs) and microprocessors (MPUs). For example, on the TMS320C54x, a 16-bit by 16-bit multiply can be executed in a single clock cycle, whereas MCUs and MPUs may take 10 times as many clock cycles. The TMS320C54x offers a range of multiplication instructions, of which we shall now look at three of the most simple. Loading the T Register The majority of multiplication instructions make use of a special register known as the T register (temporary register). The T register can be multiplied by a constant or by the contents of a data memory address. The T register is treated as a memory-mapped register, and therefore to store an immediate value in the T register we write: Example 8-1. STM #33h, T ; Store the immediate value 33h in the T ; register.

To load a value from a data memory address into the T register we use the instruction LD. There is the restriction when using the instruction LD that the value loaded into the T register must be from a data memory address. Some ways of loading the T register are shown in Example 8-2: Example 8-2. LD 70h, T ; ; ; ; ; ; ; ; ; Direct addressing. Load the T register with the contents of the data memory address 70h. Indirect addressing. Load the T register with the contents of the data memory address pointed to by auxiliary register AR7. Not allowed. Immediate addressing not supported.

LD *AR7, T

LD #33h, T

Say we wish to load a data memory address with the constant 1234h, then copy this value to the T register. Using direct addressing to load the T register we would write:

TUTORIAL

T-88

ASSEMBLY LANGUAGE TUTORIAL Example 8-3. LD #1, DP ; ; ; ; ; ; ; ; ; Set data memory page pointer (DP) to page 1 to give access to data memory addresses 80h to FFh. Store the constant 1234h at the data memory address 80h + 0h = 80h. Data memory address 80h now contains 1234h. Load the T register with the contents of the data memory address 80h + 0h = 80h. The T register now contains 1234h.

ST #1234h, 0h

LD 0h, T

To carry out the same operation as shown in Example 8-3, but this time using indirect addressing we would write: Example 8-4. STM #80h, AR3 LD #1234h, *AR3 ; ; ; ; ; ; ; ; ; ; Store address of data memory location 80h in AR3. AR3 = 80h. Store constant 1234h at data memory address pointed to by auxiliary register AR3. Data memory address 80h now contains 1234h. Load the T register with the contents of the data memory address pointed to by the auxiliary register AR3. The T register now contains 1234h.

LD *AR3, T

In Example 8-4, we have used AR3 as a pointer to the data memory address, but we could have achieved the same results using any other of the auxiliary registers AR0 to AR7. Multiplying by a Constant So far, all we have done is to load the T (temporary) register. In order to carry out the multiplication itself, we have a range of instructions at our disposal. To multiply the T register by a constant, we can use the instruction MPY (multiply) with immediate data. In this context, the instruction MPY means “multiply the contents of the T register by”. The instruction MPY (multiply) takes two operands. The first operand is the immediate value and lies in the range 8000h to +7FFFh (-32768 to +32767). It is not affected by sign-extension mode. The second operand is the destination, that is, where we wish to store the product. The destination can be either accumulator A or accumulator B. Example 8-5 shows the instruction MPY being used with three different values:

TUTORIAL

T-89

ASSEMBLY LANGUAGE TUTORIAL Example 8-5. STM #10, T MPY #10, A ; ; ; ; ; ; ; ; ; ; ; ; Store the value 10 decimal in the T register Multiply the contents of the T register by the constant 10 decimal and put the product into accumulator A. Accumulator A now contains 100 decimal (00000064h). Multiply the contents of the T register by the constant -10 decimal and put the product into accumulator B. Accumulator B now contains -100 decimal (FFFFFF9Ch). Multiply the contents of the T register by zero. Accumulator A now contains 0000h.

MPY #0FFFFh, B

MPY #0, A

Note that when using the instruction MPY, no reference is made in the mnemonic to the T register. Multiplying the T Register by the Contents of a Data Memory Address As well as multiplying the T register by an immediate value, we can also multiply the T register by a value stored in data memory: Example 8-6. LD #2, DP ; ; ; ; ; ; ; Set data memory page pointer (DP) to page 2. Gain access to data memory addresses 100h to 17Fh. Multiply the contents of the T register by the contents of data memory address 100h + 3h = 103h. Put the product into accumulator A.

MPY 3h, A

We can also carry out the same operation as shown in Example 8-6, but using indirect addressing. This time we shall use auxiliary register AR6 as a pointer to a data memory and put the product into accumulator B. Example 8-7. STM #103h, AR6 MPY *AR6, B ; ; ; ; ; ; Store the address of data memory location 103h in AR6. AR6 = 103h. Multiply the T register by the contents of the data memory address pointed to by AR6. The product is put into accumulator B.

TUTORIAL

T-90

ASSEMBLY LANGUAGE TUTORIAL Unsigned Multiplication When using the instruction MPY with immediate data, it should be remembered that the constant is always signed, that is, it can be positive or negative. Whether sign-extension mode is on or off has no effect. Example 8-8 shows an unsuccessful attempt to multiply the contents of the T register by FFFFh (65535). Example 8-8.

MPY #0FFFFh, A

; ; ; ; ;

The intention was to multiply the contents of the T register by 65535. The immediate value FFFFh has been taken to be the negative number -1, rather than the positive number 65535.

Unsigned Multiplications To carry out a multiplication of two unsigned numbers, either of which is greater than 7FFFh (32767), we use the instruction MPYU (multiply unsigned). The instruction MPYU (multiply unsigned) takes two operands. The first operand is a data memory address and the second operand is the destination, which can be either accumulator A or accumulator B. Example 8-9. LD #4, DP MPYU 70h, A ; ; ; ; ; ; ; Page 4. Gain access to data memory addresses 200h to 27Fh. Direct addressing. Multiply the unsigned contents of the T register by the unsigned contents of data memory address 200h + 70h = 270h. The product is put into accumulator A.

We can carry out the same operation as shown in Example 8-9, but using indirect addressing: Example 8-10. STM #270h, AR4 ; ; ; ; ; ; ; Store the location of data memory address 270h in auxiliary register AR4. AR4 = 270h. Multiply the contents of the T register by the contents of the data memory address pointed to by AR4. The product is put into accumulator A.

MPYU *AR4, A

The instruction MPYU can only be used with the contents of a data memory location, so the following is not allowed:

TUTORIAL

T-91

ASSEMBLY LANGUAGE TUTORIAL Example 8-11. MPYU #0FFFFh, A ; Illegal instruction. Immediate ; addressing not supported.

Multiplying Two Variables We have seen how to use two of the multiply instructions and are now ready to look at a more practical application. Let us multiply the 16-bit variable at data memory address 300h by the 16-bit variable at data memory address 301h. The product will be 32-bit. We will store the low 16 bits of the product at data memory address 302h and the high 16 bits of the product at data memory address 303h. Using direct addressing we would write: Example 8-12. LD #6, DP LD 0h, T ; ; ; ; ; ; ; ; ; ; ; ; Page 6. Gain access to data memory addresses 300h to 37Fh. Load the T register with the value contained at data memory address 300h + 0h = 300h. Multiply the T register by the contents of the data memory address 300h + 1h = 301h. The product is stored in accumulator A. Store the low word of the product at the data memory address 300h + 2h = 302h. Store the high word of the product at the data memory address 300h + 3h = 303h.

MPY 1h, A

STL A, 2h STH A, 3h

We shall now repeat the same operation as carried out in Example 8-12, this time using indirect addressing and accumulator B as the destination. Example 8-13. STM #300h, AR5 ; ; ; ; ; ; ; ; ; ; ; ; Store the address of data memory location 300h in auxiliary register AR5. AR5 = 300h. Load the T register with the contents of the data memory address pointed to by AR5. Increment AR5 to point to the next data memory address (301h). Multiply the T register by the contents of the data memory pointed to by AR5. The product is stored in accumulator B. Increment AR5 to point to the next data memory address (302h).

LD *AR5+, T

MPY *AR5+, B

TUTORIAL

T-92

ASSEMBLY LANGUAGE TUTORIAL STL B, *AR5+ ; ; ; ; ; ; ; Store the low word of the product at data memory address 302h. Increment AR5 to point to the next data memory address (303h). Store the high word of the product at the data memory address pointed to by AR5. No need to increment pointer.

STH B, *AR5

In Example 8-13, we have used the operand *AR5+ to increment the pointer each time to point to the next higher address. There are one or two differences between direct and indirect addressing that the programmer should be aware of. When the data pointer (DP) is already set to the correct page, then direct addressing uses one less instruction. However, indirect addressing is preferred when there is a series of multiplications. This is because it is possible to increment the auxiliary register to point to the next data memory address.

Combining Instructions Consider the code shown in Example 8-14 that is used to multiply the contents of the T register by an immediate value: Example 8-14. LD #3, DP LD 2h, T ; ; ; ; ; ; ; ; Page 3. Gain access to data memory addresses 180h to 1FFh. Load the T register with the value contained at data memory address 180h + 2h = 182h. Multiply the contents of the T register by the constant 492h. The product is stored in accumulator A.

MPY #492h, A

We can in fact combine the operation to load the T register and carry out a multiplication into a single instruction. For this we use the instruction MPY (multiply) with three operands. The first operand is the source data memory address to be multiplied. The second operand is the constant and the third operand is the destination which can be accumulator A or accumulator B. Example 8-15 shows the usage of the instruction MPY with three operands: Example 8-15. LD #3, DP ; Page 3. Gain access to data memory ; addresses 180h to 1FFh.

TUTORIAL

T-93

ASSEMBLY LANGUAGE TUTORIAL MPY 2h, #492h, A ; ; ; ; ; ; Load the T register with the value contained at data memory address 180h + 2h = 182h. Multiply the contents of the T register by the immediate value 492h and put the product into accumulator A.

In this case the operation of transferring the contents of the data memory address to the T register is done automatically as part of the instruction MPY. We can carry out the same operation as shown in Example 8-15, but this time using indirect addressing and accumulator B as the destination. Example 8-16. STM #182h, AR2 ; ; ; ; ; ; ; ; ; Store the address of data memory location 182h in auxiliary register AR2. AR2 = 182h. Load the T register with the value at the address pointed to by auxiliary register AR2. Multiply the contents of the T register by the immediate value 492h and put the product into accumulator B.

MPY *AR2, #492h, B

In Example 8-15, the use of the T register has become totally transparent to the user. As far as the user is concerned, the contents of the first operand are being multiplied by an immediate value and the result is placed in the destination. In practical terms, a multiplication using three operands is the most efficient way to use the instruction MPY. Multiplication using an Accumulator This variation of the multiply instruction allows us to multiply the T register by a value in accumulator A. For this we use the instruction MPYA (multiply by accumulator A). Example 8-17. MPYA B ; Multiply the contents of the T register ; by the high word of accumulator A. Put ; the product into accumulator B. The instruction MPYA (multiply by accumulator A) takes one operand only, and that is the destination of accumulator A or B. We are also restricted to working with accumulator A only as the source, and cannot multiply the contents of the T register by accumulator B. An easy mistake to make would be to expect the instruction MPYA to work on the low word of accumulator A. This is not the case. The instruction MPYA (multiply by accumulator A), multiplies the T register by the high word of accumulator A.

TUTORIAL

T-94

ASSEMBLY LANGUAGE TUTORIAL

To load accumulator A with a 16-bit constant before carrying out a multiplication, we must use the load instruction LD with a shift of 16. Example 8-18. STM #2, T LD #300h,16, A ; ; ; ; ; ; ; ; Store the value 2 in the T register. Load accumulator A with the value 300h shifted 16 places to the left. Accumulator A now contains 03000000h. Multiply the T register by the high word of accumulator A. Put the product into accumulator B. Accumulator B now contains 00000600h.

MPYA B

Example 8-19 shows an unsuccessful attempt to multiply the 16-bit constant in accumulator A by the contents of the T register: Example 8-19. ; Store the immediate value 2 in the T ; register. LD #300h,A ; Load accumulator A with the value 300h. ; Accumulator A now contains 00000300h. MPYA B ; Multiply the T register by the high ; word of accumulator A. Put product ; into accumulator B. Accumulator B now ; contains 0h. Omitting the shift when loading accumulator A prior to using the instruction MPYA will cause the incorrect product to be generated. The instruction MPYA is not affected by sign-extension mode. Upgrading from the TMS320C25 and the TMS320C2xx/5x to the TMS320C54x On the TMS320C54x, the use of the T register has become less important for multiplications than with earlier devices. On earlier devices such as the TMS320C25 and TMS320C5x, the T register had to be used for multiplications. The multiplication instructions on the TMS320C54x do differ considerably from the earlier devices. Details are given in Table 8-1. Table 8-1: Comparison of Instructions Description TMS320C25 Instruction TMS320C2xx and TMS320C54x Instruction TMS320C54x Instruction STM #2, T

TUTORIAL

T-95

ASSEMBLY LANGUAGE TUTORIAL Load T register Store immediate value in T register Multiply T register by constant Multiply T register using direct addressing Multiply T register using indirect addressing Multiply dmad by constant and store in accumulator Transfer P register to accumulator Transfer high word of P register to data memory. Transfer low word of P register to data memory . Multiply high word of accumulator by T register LT * Not supported MPYK 30 MPY 80h LT * Not supported MPY #30 MPY 80h LD *ARx, T STM #40h, T MPY #30, Acc or MPY #30, Acc MPY 80h, Acc or MPY 80h, Acc MPY *ARx, Acc or MPY *ARx, Acc MPY *ARx, #300h, Acc Not used Not used

MPY *

MPY *

Not supported PAC SPH 80h

Not supported PAC SPH 80h

SPL 81h Not supported

SPL 81h Not supported

Not used MPYA Acc

ARx = An auxiliary register AR0 to AR7. Acc = Accumulator A or Accumulator B. The earlier devices placed the product in the P register (product register). This meant that the product had to be transferred to either the accumulator or a data memory location using the instructions PAC, SPH and SPL. The TMS320C54x no longer has a P register (32-bit product), being able to place the product directly in accumulator A or accumulator B. This means the instructions PAC, SPH and SPL are no longer supported. On the TMS320C54x, an improvement has been made to multiplication by an immediate value. On earlier devices, this operation was restricted to the use of a 13-bit constant. This meant it was only possible to multiply by a value in the range 2000h to 1FFF (-4096 to +4095). The TMS320C54x supports 16-bit constants. It is now possible to multiply by a constant in the range 8000h to 7FFFh (-32768 to +32767). A further improvement offered by the TMS320C54x is that an immediate value can be stored directly in the T register, something not possible on earlier devices. A point of extreme confusion is that on the TMS320C54x, the instruction MPYA means multiply by accumulator A. However, on the earlier devices, the instruction MPYA meant multiply with accumulate. The two are quite different functions. The mnemonic MPYA should therefore be treated with some caution.

Questions

TUTORIAL

T-96

ASSEMBLY LANGUAGE TUTORIAL 1. What does the letter T in T register stand for? a) target b) temporary c) tertiary d) timing. Which three of the following TMS320C54x instructions are legal? a) STM #45h, T b) LD #22h, T c) LD 5h, T c) LD *AR4, T d) LD *AR1+, T How do we clear the T register to zero ? When would we use the instruction MPYU in preference to the instruction MPY? Which three of the following instructions are legal? a) MPYU #100, A b) MPYU *AR3, B c) MPYU 70h, A d) MPYU *AR6-, A How do we multiply two 16-bit variables together? Why might we use indirect addressing to multiply a series of values rather than direct addressing? Reduce the following two instructions to a single instruction: LD 0h, T MPY #10h, B Why do we not use the instruction MPYA to multiply accumulator B? When loading a variable with the instruction MPYA , why must we use a shift?

2.

3. 4. 5.

6. 7. 8.

9. 10.

TUTORIAL

T-97

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 9:

Multiplications with Accumulation

New Instructions Introduced MAC MACA New Flags Introduced OVM Overview of Tutorial There are several DSP applications, such as finite impulse filters (FIRs) that require a large number of sum-ofproducts terms. This is achieved on the TMS320C54x by carrying out a series of multiplications, sometimes hundreds, and adding the products together. This process is referred to as multiplication with accumulation and its implementation using the TMS320C54x is described in this tutorial.

Generating a Sum-of-Products To implement a finite impulse filter (FIR) we multiply a series of inputs with a series of constants. A typical format is as shown in Equation 9-1: Equation 9-1.

y ( n ) = ∑ h ( k ). x ( n − k )
k=0

N

Where N = number of coefficients, values h(0), h(1) … are the coefficients, x(n) is the input at time n and x(n1)… , x(n-N) are delayed input samples. Consider the case where we have eight inputs, each of which is to be multiplied by the corresponding entry from a table. We can implement this in C code as shown in Example 9-1: Example 9-1. unsigned int input[8]; unsigned int table[8]={10,15,22,27, 33,47,68,82 }; unsigned int i; unsigned long accumulator_A; unsigned long accumulator_B = 0; for ( i = 0; i < 8 ; i++) { accumulator_A = input[I] * table[i]; accumulator_B += accumulator_A; } // Eight inputs. // Table of constants. // Iterations counter.

// Multiply one element. // Accumulate in accumulator B. // Final result in accumulator B

TUTORIAL

T-98

ASSEMBLY LANGUAGE TUTORIAL

We have a series of eight input samples that we multiply by eight constants. The usual way to do this in C code is to use two arrays, one for the inputs and one for the constants. We multiply each input by the corresponding constant and then add the product to the accumulated total. Accumulation Using Direct Addressing Let us take an example of multiplication with accumulation using TMS320C54x assembly language. We wish to multiply a series of 16 data words beginning at data memory address 80h by another series of data words beginning at data memory address 90h. The result of each multiplication is added to the previous and the final result is to be stored in accumulator B. Using direct addressing we may write part of the code sequence as shown in Example 9-2: Example 9-2. LD #1, DP ; Page 1. Gain access to data memory ; addresses 80h to FFh. ; ; ; ; ; ; ; ; ; ; ; ; Load the T register with the contents of the data memory address 80h + 0h = 80h. Multiply the T register by the contents of the data memory address 80h + 10h = 90h. The product is placed in accumulator B. Load the T register by the contents of the data memory address 80h + 1h = 81h. Multiply the T register by the contents of the data memory address 80h + 11h = 91h. Product is placed in accumulator A. Add accumulator A to accumulator B and put result in accumulator B.

LD 0h, T MPY 10h, B

LD 1h, T MPY 11, A

ADD A, B

; Repeat LD / MPY / ADD A instructions as ; required. There is a major disadvantage in using direct addressing when carrying out a large number of multiplications with accumulation. Each data memory address must be written out in full and this means a large number of lines of code are required. A better choice is offered by indirect addressing. Using Indirect Addressing Let us now repeat the same operation we carried out in Example 9-2, but this time using indirect addressing. For this we will use auxiliary register AR1 as a pointer to the data memory block at 80h and auxiliary register AR2 as a pointer to the data memory block at 90h. The first task is to clear the registers and initialize the pointers:

TUTORIAL

T-99

ASSEMBLY LANGUAGE TUTORIAL Example 9-3. STM #80h, AR1 STM #90h, AR2 LD #0, A ; ; ; ; ; ; Store block Store block Clear start address of start of data memory in AR1. AR1 = 80h. address of start of data memory in AR2. AR2 = 90h. accumulator A so accumulations at zero.

Initialization has been done. Now we start a sequence of multiplications with accumulation: Example 9-4. LD *AR1+, T ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Load the T register with the contents of the data memory address pointed to by auxiliary register AR1. AR1 = 80h. Increment auxiliary register AR1 to point to the next data memory address AR1 = 81h. Multiply the T register by the contents of the data memory address pointed to by AR2. AR2 = 90h. Put the product in accumulator A. Increment AR2 to point to the next data memory address. AR2 = 91h. Add the product of this multiplication to the total sum-of-products in accumulator B.

MPY *AR2+, A

ADD A, B

In Example 9-4, we have used a variation of the instruction LD to load the contents of a data memory address into the T register. The block of instructions in Example 9-4 would be executed 16 times to take us through successive data memory addresses. When the final multiplication and accumulation has been done, AR1 will contain the value 90h and AR2 will contain the value A0h. Hence AR1 and AR2 will be pointing to the data memory addresses one past their relevant data blocks. This does not normally pose a problem, provided that AR1 and AR2 are re-initialized before they are next used. Using a Loop Counter for Multiple Accumulations The method of writing each multiplication as inline code will give the fastest execution time, but in terms of code efficiency, a better method is to use a loop counter. The instruction BANZ (branch if auxiliary register non-zero) provides a convenient means of executing a control loop. We shall combine together the operations shown in Examples 9-3 and 9-4 and use auxiliary register AR3 as a loop counter, as illustrated in Example 9-5:

TUTORIAL

T-100

ASSEMBLY LANGUAGE TUTORIAL Example 9-5. LD #0, B STM #80h, AR1 ; Clear accumulator B ; Store address of start of first ; data block in auxiliary register ; AR1. AR1 contains 80h. ; Store address of start of first ; data block in auxiliary register ; AR2. AR2 contains 90h. ; AR3 is our loop counter. 16 ; iterations (16 - 1 = 15 ; loops). ; Load the T register with the ; value from the first data block. Point to next address in first ; data block. ; Multiply the T register by the ; value from second data block. ; Put product into accumulator A. ; Point to next address in second ; data block. ; Accumulate product into ; accumulator B. ; Test if auxiliary register AR3 ; contains zero. If so, the loop ; is complete. Otherwise branch to ; the label lp1. In both cases, ; decrement the loop counter AR3.

STM #90h, AR2

STM #15, AR3

lp1:

LD *AR1+, T

;

MPY *AR2+, A

ADD A, B BANZ lp1, *AR3-

Here lp1 is a label indicating the beginning of the loop and is used by the instruction BANZ (branch auxiliary register non-zero). Note that we have used three auxiliary registers: AR1 as a pointer to the first block, AR2 as a pointer to second block and AR3 as the loop counter. Combining Instructions Consider the following two instructions used as part of a multiplication with accumulation sequence: Example 9-6. MPY *AR2, A ; ; ; ; ; Multiply the T register by the value pointed to by AR2. Put the product into accumulator A. Add product in accumulator A to total in accumulator B.

ADD A, B

TUTORIAL

T-101

ASSEMBLY LANGUAGE TUTORIAL In the case of Example 9-6 we have put the product of the multiplication into accumulator A then added it to accumulator B. These two instructions can be combined into the single instruction MAC (multiply and accumulate), as shown in Example 9-7: Example 9-7. MAC *AR2, B ; ; ; ; ; Multiply the T register by contents of the data memory address pointed to by AR2. Add the product to the value in accumulator B and store that sum in accumulator B.

The instruction MAC (multiply with accumulate) takes two operands. The first operand is the address in data memory of the source. The second operand is the destination accumulator where we are carrying out the accumulation. Not only does the method used in Example 9-7 save an instruction, but it has the additional benefit of only using one accumulator. Preventing Overflow One of the potential problems when carrying out a large number of multiplications with accumulation is that an overflow can occur in the accumulator. This happens when the result of the addition exceeds the greatest value that can be stored in that accumulator. For example, say accumulator A contains the largest signed 32-bit value of 7FFFFFFFh (+2147483647) and then we add one. Accumulator A will now contain the value 80000000h (-2147483648). This represents a negative number. The largest signed 32-bit number has been exceeded and the counter has wrapped around to the most negative number. This is known as overflow. Similarly, if accumulator B contains the smallest 32-bit value of 8000000h (-2147483647) and we subtract one, then accumulator B wraps around to contain 7FFFFFFFh (+247483647). This represents a positive number. An overflow has occurred in the opposite direction, and is sometimes referred to as underflow. When an overflow of an accumulator occurs, the contents become unreliable; the result cannot be trusted to be the correct value or even to have the right sign. There is a defensive mechanism available on the TMS320C54x of being able to put a limit on the value that is stored in an accumulator. This is known as saturation. Saturation means that the largest positive value that an accumulator can contain is 7FFFFFFFh and the most negative value that an accumulator can contain is 80000000h. To avoid overflow we make use of the OVM bit in status register ST0, as shown in Example 9-8: Example 9-8. RSBX OVM SSBX OVM ; ; ; ; ; Turn off overflow mode (OVM). Accumulators are allowed to wrap around. Turn on overflow mode (OVM). Prevent the accumulators overflowing by saturating them at either 7FFFFFFFh or 80000000h.

TUTORIAL

T-102

ASSEMBLY LANGUAGE TUTORIAL

Let us now look at Example 9-9 which shows what happens when overflow mode is turned off:

TUTORIAL

T-103

ASSEMBLY LANGUAGE TUTORIAL Example 9-9. SSBX SXM RSBX OVM LD #7FFFh, 16, A ; ; ; ; ; ; ; ; ; ; ; Turn on sign-extension mode. Turn off overflow mode. Allow overflows. Load high word of accumulator A with 7FFFh. Accumulator A contains 7FFF0000h. Logical OR with FFFFh. Accumulator A now contains 7FFFFFFFh (+247483647). Add 1 to accumulator A. Accumulator A now contains 80000000h (-2147483648). There has been an overflow.

OR #0FFFF, A ADD #1, A

Now let us see what happens when overflow mode (OVM) is turned on, as is the case in Example 9-10: Example 9-10. SSBX SXM SSBX OVM ; ; ; ; ; ; ; ; ; ; ; ; ; Turn on sign-extension mode. Turn on overflow mode. Saturate accumulator rather than allow overflow. Load high word of accumulator A with 7FFFh. Accumulator A contains 7FFF0000h Logical OR accumulator with FFFFh. Accumulator A now contains +247483647 (7FFFFFFFh). Add 1 to accumulator A. Accumulator A still contains 7FFFFFFFh. Saturation has occurred.

LD #7FFFh, 16, A

OR #0FFFFh, A

ADD #1, A

When overflow mode is turned on using the instruction SSBX OVM, overflow is prevented. This means that the value in the accumulator being used is limited to a maximum of 7FFFFFFFh or a minimum of 80000000h. Note that when saturation occurs, the accumulator does not contain the correct value, but the percentage error will be small, and most important, the sign will be correct. In a later tutorial we shall see how to test for overflow, assuming overflow mode is turned off, and thus take appropriate action. From this point on in the tutorial, we will recognize that overflow could occur on accumulation and therefore set OVM (overflow mode). Final Code Reduction So far we have seen how to use the instruction MAC (multiply accumulate) to multiply the T register by a value stored in data memory. We can also use the instruction MAC to multiply together two variables stored in data memory and accumulate the result in one of the accumulators. Example 8-11 shows how to multiply together the contents of two data memory addresses and then accumulate the result:

TUTORIAL

T-104

ASSEMBLY LANGUAGE TUTORIAL Example 9-11. LD *AR1, T ; ; ; ; ; ; ; ; ; ; Load the T register with the contents of the data memory address pointed to by auxiliary register AR1. Multiply the T register by the contents of the data memory address pointed to by AR2. Put the product in accumulator A. Add the product of this multiplication to the total sum-of-products in accumulator B.

MPY *AR2, A

ADD A, B

The three instructions in Example 9-11 can be condensed into a single instruction: Example 9-12. MAC *AR1, *AR2, B ; ; ; ; ; ; ; ; Load the T register with the contents of the data memory address pointed to by auxiliary register AR1. Multiply the T register by the contents of the data memory address pointed to by AR2. Add this product to the sum-of-products in accumulator B.

The fact that we have been able to combine the instructions LD, MPY and ADD into a single instruction is significant. In order to repeat a series of three instructions in a loop means we must make use of an instruction such as BANZ (branch on auxiliary register non-zero to control the loop. However, to repeat a single instruction, we can use the instruction RPTZ (clear accumulator then repeat next instruction). Let us revisit the earlier problem of multiplying a series of 16 data words beginning at data memory address 80h with another series of data words beginning at data memory address 90h and put the sum-of-products in accumulator B. We can now do this in a more efficient way, as shown in Example 9-13: Example 9-13. SSBX OVM STM #80h, AR1 ; ; ; ; ; ; ; ; Prevent overflows by allowing saturation. Store the address of the start of the first data block in auxiliary register AR1. AR1 = 80h. Store the address of the start of the second data block in auxiliary register AR2. AR2 = 90h.

STM #90h, AR2

TUTORIAL

T-105

ASSEMBLY LANGUAGE TUTORIAL RPTZ B, #(16-1) ; ; ; ; ; ; ; ; ; ; ; ; Clear accumulator B prior to accumulation and execute the following instruction 16 times. Load the T register with the contents of the data memory address pointed to by auxiliary register AR1. Multiply the T register by the contents of the data memory address pointed to by AR2. Add this product to the sum-of-products in accumulator B. Increment AR1 and AR2.

MAC *AR1+, *AR2+, B

The operand used with the instruction RPTZ is the number of repeats, and hence is one less than the total number of operations. We have used the operands *AR1+ and *AR2+ so that the two auxiliary registers being used as pointers are incremented each time the instruction MAC is executed. This final code reduction illustrates the power of the TMS320C54x instruction set. We have been able to implement a complex function using very few instructions. Multiplication with Accumulation using an Accumulator There is a second variation on the multiply and accumulate instruction, this time using accumulator A instead of the T register: Example 9-14. STM #100h, AR4 ; ; ; ; ; ; ; Store the address of the data memory location in auxiliary register AR4. AR4 = 100h. Multiply the contents of the data memory address pointed to by AR4 by the high word of accumulator A then accumulate in accumulator B.

MACA *AR4+, B

instruction MACA (multiply by accumulator A and accumulate) takes two operands. The first operand is the source data memory address and the second operand is the accumulator used as the destination. When using the instruction MACA, accumulator A is taken to be a 17-bit unsigned value. Note that the high word of accumulator A is used, rather than the low word. When accumulator A is loaded with a constant, a shift of 16 to the left must be used, otherwise the product of the multiplication will be incorrect. The correct usage is shown in Example 9-15:

TUTORIAL

T-106

ASSEMBLY LANGUAGE TUTORIAL Example 9-15. LD #0, DP RSBX SXM LD #100h, 16, A ; ; ; ; ; ; ; ; ; ; Page 0. Gain access to data memory addresses 60h to 7Fh. Turn off sign-extension mode. Load accumulator A with value 100h shifted 16 places to the left. Accumulator A now contains 01000000h. Multiply the contents of the data memory address 70h by the high word of accumulator A and accumulate in accumulator B.

MACA 70h, B

Should the shift be omitted when loading the accumulator, then the product of the multiplication contains the wrong value. If sign-extension mode is turned on, then the high word of accumulator A can contain FFFFh, rather than the immediate value. Example 9-16. LD #0, DP RSBX SXM LD #8000, A ; ; ; ; ; ; ; ; ; ; ; Access to data memory addresses 60h to 7Fh. Turn off sign-extension mode. Incorrect. Shift omitted. Load accumulator A with 8000h. Accumulator A now contains FFFF8000h. Multiply the contents of the data memory address 70h by the high word of accumulator A (FFFFh). The wrong value will be accumulated in accumulator B.

MACA 70h, B

If sign-extension mode is turned off, the high word of the accumulator will be loaded with 0000h and the product accumulated will always be zero. Example 9-17. LD #0, DP RSBX SXM LD #8000h, A ; ; ; ; ; ; Access to data memory addresses 60h to 7Fh. Turn off sign-extension mode. Incorrect. Shift omitted. Load accumulator A with 8000h. Accumulator A now contains 00008000h.

TUTORIAL

T-107

ASSEMBLY LANGUAGE TUTORIAL MACA 70h, B ; ; ; ; Multiply the contents of the data memory address 70h by the high word of accumulator A (0000h). Zero will be accumulated in accumulator B.

Upgrading from the TMS320C25 and TMS320C2xx/5x to the TMS320C54x As far as multiplications with accumulations are concerned, the architecture of the TMS320C54x differs from the earlier devices. There is no longer a P register so that the instructions LTA (load T register and accumulate previous product), APAC (add product register to accumulator), and SPM (set product register shift mode) are no longer required. Table 9-1. Comparison of Instructions
Description TMS320C25 Instruction LTA APAC SPM MAC Not available TMS320C2xx and TMS320C5x Instruction LTA APAC SPM MAC Not available TMS320C54x Instruction Not used Not used Not used MAC *ARx, Acc MACA *ARx, Acc

Load T register and accumulate previous product Add product register to accumulator Set product register shift mode Multiply and accumulate Multiply accumulator and accumulate

ARx = One of the auxiliary registers AR0 to AR7. Acc = Accumulator A or accumulator B. On the earlier devices, one way to avoid overflow was to shift the product six places to the right before 6 accumulating. This effectively divided the product by 2 before accumulating it and so caused a loss of resolution. The behavior during overflow has been improved on the TMS320C54x by the use of 40-bit accumulators. This means that it is possible to carry out multiplication with accumulation on variables greater than 32 bits.

Questions
1. 2. 3. 4. 5. 6. 7. 8. 9. What is meant by the term accumulation? When carrying out a series of multiplications with accumulations, why might we use indirect addressing in preference to direct addressing? What is meant by the term overflow? Why can overflow be a problem when carrying out accumulation? How do we prevent overflow when carrying out accumulation? What are the advantages of the instruction MAC over the instructions MPY and ADD? What is meant by the term saturation? How do we turn on overflow mode? How do we turn off overflow mode?

TUTORIAL

T-108

ASSEMBLY LANGUAGE TUTORIAL 10. Combine the following two instructions into a single instruction: MPY *AR3+, A ADD A,B Combine the following three instructions into a single instruction: LD *AR3, T MPY *AR4, A ADD A, B Why will the following code always produce a product of zero in accumulator B? RSBX SXM LD #200h, A MAC 80h, B

11.

12.

TUTORIAL

T-109

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 10: Using Data Stored in ROM
New Instructions Introduced MACP MVPD READA Overview of Tutorial So far we have worked exclusively on data stored in data memory (RAM). However, data memory is a limited resource. Certain variables need only to be read and not written. These can be stored in program memory (ROM) rather than in data memory. In this tutorial we shall see how to copy variables from program memory to data memory and how to work directly on variables stored in program memory. Copying a Block of Data from ROM to RAM using Direct Addressing One way to copy a word of data from a program memory address to a data memory address is to use the instruction MVPD (move data from program memory to data memory). This can be used with direct addressing as shown in Example 10-1: Example 10-1. LD #3, DP MVPD 1000h, 8h ; ; ; ; ; Page 3. Gain access to data memory addresses 180h to 1FFh. Move word of data from program memory address 1000h to the data memory address 180h + 8h = 188h.

When using the instruction MVPD (move data from program memory to data memory) we need to specify two addresses as operands: the source address in program memory we wish to copy from and the destination address in data memory we wish to copy to. Just as the term data memory address is abbreviated to dmad in the Texas Instruments databooks, the term program memory address is often abbreviated to pmad. Using direct addressing in order to copy two sequential words of data from program memory to data memory we would repeat the instruction MVPD, as shown in Example 10-2: Example 10-2. LD #3, DP MVPD 1000h, 10h ; ; ; ; ; Page 3. Gain access to data memory addresses 180h to 1FFh. Move the word of data from program memory address 1000h to the data memory address 180h + 10h = 190h.

TUTORIAL

T-110

ASSEMBLY LANGUAGE TUTORIAL ; Move the word of data from program ; memory address 1001h to the data ; memory address 180h + 11h = 191h. A line of code is required for each value to be copied. In order to copy a large number of words of data, we would require a large number of instructions. This is both laborious and wasteful of code. A more efficient way is to use indirect addressing. Copying a Block of Data from ROM to RAM using Indirect Addressing The instruction MVPD (move data from program memory to data memory) also supports indirect addressing. Example 10-3 shows how to copy a single word from a program memory address to a data memory address: Example 10-3. STM #185h, AR6 ; ; ; ; ; ; ; ; ; Store the address of data memory location in auxiliary register AR6. AR6 = 185h. Copy the word at program memory address 1000h to the data memory address pointed to by AR6. Data memory address 185h now contains the same value as that contained at program memory address 1000h. MVPD 1001h, 11h

MVPD 1000h, *AR6

In order to efficiently, copy a block of data from program memory to data memory, we can use the instruction RPT (repeat). In this case we shall copy 10 words of data from the program memory address 1000h to the block of data memory starting at the address 200h. Example 10-4. STM #200h, AR6 ; ; ; ; ; ; ; ; ; ; ; ; Store address of start of data memory block in auxiliary register AR6. AR6 = 200h. Execute next instruction and then repeat nine times. Starting at program memory address 1000h, copy 10 successive words to the block of data memory starting at the data memory address 200h. The last word is written to the data memory address 209h.

RPT #(10-1) MVPD 1000h, *AR6+

We place the instruction RPT before the instruction MVPD. The operand used with the instruction RPT needs to be one less than the total number of words to be copied. In Example 10-4, ten data words are to be copied from program memory to data memory, so 10-1 = 9 repeats are required.

TUTORIAL

T-111

ASSEMBLY LANGUAGE TUTORIAL The instruction MVPD has its own program address register (PAR). When the instruction MVPD is executed, the program memory address supplied as the operand is passed to the program address register. The PAR is increased automatically once every repeat. On the first repeat, the contents of program memory address 1001h will be copied. On the second repeat, a word will be copied from program memory address 1002h. This means a word is copied from a different program memory address every time. The final word will be copied from program memory address 1009h. We use the operand *AR6+ to increment the value of the pointer AR6 every time we execute the instruction MVPD. Each successive word from program memory will be stored at the next higher data memory address. Copying a block of data using the instruction RPT (repeat) does not function correctly when used with direct addressing. The fragment of code in Example 10-5 is intended to use direct addressing to copy 10 words from the program memory address 1000h to the data memory addresses starting at 200h: Example 10-5. LD #4, DP RPT #(10-1) MVPD 1000h, 0h ; ; ; ; ; ; ; ; ; ; ; ; ; Use data page 4. Gain access to data memory addresses 200h to 27Fh. Execute the next instruction ten times. Starting at program memory address 1000h, attempt to copy 10 successive words to the data memory addresses starting at 200h. The last word is in fact written to the data memory address 200h. When used with direct addressing, there is no automatic increment of the program memory address.

Unfortunately, this fragment of code will always write to the data memory address 200h. Direct addressing does not provide a mechanism to increment the data memory address. Every value will be written to data memory address 200h. At the end of the repeat sequence, data memory address 200h will contain the 10th value from the table in program memory. Hence, when moving multiple program words from program memory to data memory using the instruction MVPD, indirect addressing should be used. Using Look-up Tables Consider the case of a system where the output is derived mathematically from the input. If there is no simple relationship between the input and the output, this makes the calculation of the output difficult. However, it may be possible to describe a non-linear relationship between an input and an output in the form of a look-up table. This has the advantage over a complex calculation in that it uses few instructions and is therefore quick to execute. In DSP, look-up tables are widely used to generate sine / cosine functions, where the calculation would be much slower and more wasteful of code.

TUTORIAL

T-112

ASSEMBLY LANGUAGE TUTORIAL One way we can implement a look-up table in C is to put the table into an array. In order to look-up an element from the table, we read the specific element using an index as shown in Example 10-6: Example 10-6. unsigned int table[12]={10,12,15,18, 22,27,33,39, 47,56,68,82}; unsigned int index; unsigned int output; index = 7; output = table[index]; // Array of 12 // elements with // constants. th Work on 8 element Read from table using index.

// // // //

In the case of a look-up table containing 12 elements, the elements are numbered from 0 to 11. To address a particular element, we put the number of the element in the variable index. The first element of the array is to be found in table[0] and the last element in table[11]. We shall now implement Example 10-6 using TMS320C54x assembly language. We shall adhere to the C convention for our look-up table by storing the first value of the table at the starting address of the table. One way to read a value from the table is to use the instruction MVPD. This instruction is really designed to copy blocks of data. The problem here is that the exact address of the table entry must be known and cannot be calculated in the course of the program. To extract a single value from a look-up table in program memory, a better way is to use the instruction READA (read program memory addressed by accumulator A and store in data memory). The instruction READA takes a single operand which is the destination address in data memory where the value is to be written. We cannot use accumulator B. The assembly language implementation of Example 10-6 is given in Example 10-7: Example 10-7. table: .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD 10 12 15 18 22 27 33 39 47 56 68 82 ; ; ; ; ; ; ; ; ; ; ; ; element element element element element element element element element element element element 0 1 2 3 4 5 6 7 8 9 10 11

TUTORIAL

T-113

ASSEMBLY LANGUAGE TUTORIAL lookup: LD #8, DP ST #7, 20h LD 20h, A

ADD #table, A

READA 21h

; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

Page 8. Gain access to data memory addresses 400h to 47Fh. Store a number in the range 0 to 11 in the variable index. We shall use 7. Our index is in data memory address 400h + 20h = 420h. Copy to accumulator A. Accumulator A contains the value at the address table + index, in this case table + 7. The value 39 is stored at this address. Read the value from table corresponding to index and store at data memory address 421h. Data memory address 421h now contains 39.

The constants for our look-up table are stored in program memory by the using the .WORD assembler directive. In order to read a constant from the look-up table, we load accumulator A with the starting address of the table then add the index. For example, to read the constant 27 we would use an index of 5. We could also write the portion of the code in Example 10-7 that extracts a data word from the lookup table as follows: Example 10-8. LD #table, A ADD 20h, A ; ; ; ; ; ; ; ; ; Load accumulator A with the address of the start of the table. Add index in data memory address 421h to the starting address of table. Accumulator A contains address of table + index. Read value from table corresponding to index and store at data memory address 421h.

READA 21h

The only difference between Examples 10-7 and 10-8 are the order in which accumulator A has been loaded. Care does need to be taken when using look-up tables. There is no range check for the index, so if this were to contain an out-of-range value, then the instruction READA would fetch a value from outside of the look-up table. A value from program memory not intended to be part of the look-up table would be used.

TUTORIAL

T-114

ASSEMBLY LANGUAGE TUTORIAL Keeping Look-up Tables Separate from Code When the assembler reaches a look-up table in program memory, it has no way of telling whether the code contains a look-up table or simply instructions to be executed. Consider the fragment of code shown in Example 10-9 which contains a table of four constants: Example 10-9. table1: .WORD .WORD .WORD .WORD 0 1 2 3

lookup: The code in Example 10-9 can in fact be interpreted by the assembler as:

TUTORIAL

T-115

ASSEMBLY LANGUAGE TUTORIAL Example 10-10. table1: ADD ADD ADD ADD 0h, 1h, 2h, 3h, A A A A

lookup: Caution should therefore be taken when reading a value from a look-up table. If the index used is greater than the number of entries in the table, then executable code will be read rather than constants. Should the number of entries in a look-up table be reduced, then the maximum value of the index should also be reduced. Normally, look-up tables would be kept separate from executable code, for example at the end of the program. However, it may be necessary to place the look-up table within a block of executable code. In this case, the instruction B (branch) should also be used: Example 10-11. B lookup2 ; ; ; ; ; ; ; Skip the code in table2 by branching to the label lookup2. These four words are only accessible using the instructions MVPD or READA.

table2:

.WORD .WORD .WORD .WORD

0h 1h 2h 3h

lookup2: Using the instruction B (branch), the look-up table table2 is skipped, except when it is being read as a table. Multiplication with Accumulation using Data in Program Memory In the previous tutorial we saw how to multiply and accumulate. We always multiplied a value in data memory with another value also in data memory. However in a program, numbers such as filter constants would normally be stored in program memory. We could copy them to data memory and then manipulate them, but it is often more convenient to work on them directly. The instruction MACP (multiply by program memory and accumulate) allows us to multiply a value stored in data memory by a variable stored in program memory, then accumulate the product in one of the accumulators. Example 10-12 illustrates how this is done. Example 10-12. LD #2, DP ; Page 2. Access to data memory ; addresses 100h to 17Fh.

TUTORIAL

T-116

ASSEMBLY LANGUAGE TUTORIAL MACP 25h, table1, A ; ; ; ; ; Multiply the value at data memory address 125h by the value contained at program memory address table1. Add the product to accumulator A.

The instruction MACP takes three operands. The first operand is the address in data memory and the second operand is the address in program memory. The third operand is the destination and can be either accumulator A or accumulator B. We can also use indirect addressing, this time with accumulator B as the destination: Example 10-13. STM #125h, AR3 ; ; ; ; ; ; ; ; ; ; Store address of data memory location in auxiliary register AR3. AR3 = 125h. Multiply the value at the data memory address pointed to by AR3 by the contents of the program memory address table1. Add the product to accumulator B. Automatically point to the next address in table1.

MACP *AR3, table1, B

Like the instruction MVPD, the instruction MACP also uses the special register known as the Program Address Register (PAR). This is loaded with the second operand. When used with the instruction RPT (repeat), every time the instruction MACP is executed, the value in PAR is incremented. This means that the address in program memory is automatically updated. To copy a block, all we need to do is to specify the starting address of the table in program memory. The address in program memory is automatically updated for us by the instruction RPT. To multiply and accumulate 16 words of a data block beginning at data memory address 280h with a set of constants starting at program memory address table1 we write: Example 10-14. STM #280h, AR4 ; ; ; ; ; ; ; ; Store the address of the start of the data memory block in auxiliary register AR4. AR4 = 280h. Start accumulation with zero in accumulator A. Execute following operation and repeat 15 times.

LD #0, A RPT #(16-1)

TUTORIAL

T-117

ASSEMBLY LANGUAGE TUTORIAL MACP *AR4+, table1, A ; ; ; ; ; ; ; ; ; ; Multiply the value at the data memory address pointed to by AR4 by the value at program memory address table1. Add the product to accumulator A. Increment the auxiliary register AR4 to point to the next address in data memory. The program memory address is incremented automatically.

When we carry out the first multiplication with accumulation, we add the product to whatever value is in accumulator A. We therefore start by clearing accumulator A to zero so that our accumulation begins at zero. We use the expression *AR4+ to indicate that auxiliary register AR4 is used as a pointer to a block of data memory and the address is incremented after each read. Each accumulation will therefore work on a different data memory address. We could also carry out the same operation as shown in Example 10-14, this time using accumulator B. This time we have combined the instructions LD #0, A and RPT #(16-1) into the single instruction RPTZ B, #(16-1). Example 10-15. STM #280h, AR4 ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Store address of start of block of data memory in auxiliary register AR4. AR4 = 280h. Clear accumulator B then execute the following operation 16 times. Multiply the value at the data memory address pointed to by AR4 by the contents of program memory address table1. Add the product to accumulator B. Increment auxiliary register AR4 to point to the next address in data memory. The program memory address is incremented automatically.

RPTZ B, #(16-1)

MACP *AR4+, table1, B

Example 10-15 shows how well the TMS320C54x architecture is suited to multiplications with accumulation using values from a table in program memory.

TUTORIAL

T-118

ASSEMBLY LANGUAGE TUTORIAL Upgrading from the TMS320C25 and TMS320C2xx/5x to the TMS320C54x The syntax of instructions to work directly on values stored in program memory does vary between the devices: Table 10-1. Comparison of Instructions
Description TMS320C25 Instruction BLKP TBLR MAC TMS320C2xx and TMS320C5x Instruction BLPD TBLR MAC TMS320C54x Instruction MVPD READA MACP

Move data from program memory to data memory Read program memory addressed by accumulator A and store in data memory Multiply accumulate

The earlier devices abbreviated the term program memory address to pma. Instead, the TMS320C54x databook uses the abbreviation pmad.

TUTORIAL

T-119

ASSEMBLY LANGUAGE TUTORIAL The operation of multiplying a block of data memory by a set of constants in program memory is simpler on the TMS320C54x than on earlier devices. There is no longer the need to relocate a block of data in program memory to data memory.

Questions
1. 2. Why would we wish to store constants in program memory rather than in data memory? What does the abbreviation pmad stand for? a) positive mode address b) previous memory accumulate c) program memory address Why do we use indirect addressing rather than direct addressing when moving multiple words of data from program memory to data memory using the instruction MVPD? How do we copy a word of data from program memory address 400h to data memory address 100h? Why would we use a look-up table? Both the instructions MVPD and READA move data from program memory to data memory. How does the usage of the two instructions differ? How do we prevent the values in a look-up table being executed as code? How do we increment the address of the program memory when using the instruction MACP? The instruction RPTZ (repeat next instruction and clear accumulator means: a) Clear the accumulator once then repeat next instruction b) Repeat next instruction, every time clearing the accumulator? Why do we use the instruction RPTZ rather than RPT before a series of multiplications with accumulate MACP?

3. 4. 5. 6. 7. 8. 9.

10.

TUTORIAL

T-120

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 11: Bit Operations
New Instructions Introduced BIT BITT BITF CMPR New Test Conditions Introduced AOV BOV ANOV BNOV TC NTC Overview of Tutorial So far in these tutorials we have worked mostly with 16-bit values. However, certain variables, flags in particular, can be of the type Boolean. Boolean variables have only two values; typical examples are TRUE / FALSE and ON / OFF. To use a word of data memory for this type of variable would be wasteful as one data bit is sufficient. If we use a single data bit for each Boolean variable, we can then pack 16 variables into a data word. This tutorial covers the usage of individual data bits and highlights some of the special instructions used to manipulate them. Testing Bits Let us look at a simple problem. We wish to test whether the word stored at data memory address 70h is an odd number or an even number. All we need to do is to test the least significant (right-most) bit. If it is 1, then we have an odd number. If it is 0, we have an even number. Example 11-1 shows how to carry out this operation using C code: Example 11-1. unsigned int value; if ( value & 0x01) { /* Odd number */ } else { /* Even number */ } // Value in memory. // Test least significant bit. // Process odd number. // Process even number.

We shall now implement Example 11-1 using TMS320C54x assembly language. Let us assume that the variable value is stored at data memory address 70h. In order to test the least significant bit, we use the instruction AND (logical AND) to clear to zero all the other bits, followed by a test. This is shown in Example 11-2:

TUTORIAL

T-121

ASSEMBLY LANGUAGE TUTORIAL Example 11-2. LD #0, DP LD 70h, A AND #0001h, A ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Data page 0. Gain access to data memory addresses 60h to 7Fh. Copy of variable value into accumulator A. Clear to zero all bits of accumulator A except the least significant (right-most) bit. If the least significant bit is equal to zero (condition AEQ is TRUE) then branch to the label even. Action to be carried out when the variable value is an odd number (the least significant bit is 1). Action to be carried out when value is an even number (the least significant bit is 0).

BC even, AEQ

odd:

even:

In Example 11-2 we have copied the variable under test from data memory to accumulator A in order to test it. Working Directly on Bits in Data Memory The TMS320C54x provides a mechanism whereby the individual bits of a word stored in data memory can be tested directly, without the need to copy them to accumulator A or accumulator B. This saves an operation and leaves the accumulators free for other purposes. To test a bit of a data word, the instruction BIT (bit test) is provided. However, this instruction is unusual in that it does not support direct addressing. The usage shown in Example 11-3 is therefore illegal: Example 11-3. LD #3, DP BIT 6h, 15 ; ; ; ; ; ; Page 3. Gain access to data memory addresses 180h to 1FFh. An illegal attempt to perform a bit test on data memory address 180 + 6 = 186h. Direct addressing not supported by this instruction.

Example 11-4 shows the correct usage of the instruction BIT using indirect addressing:

TUTORIAL

T-122

ASSEMBLY LANGUAGE TUTORIAL Example 11-4. STM #186h, AR4 ; ; ; ; ; ; ; ; Indirect addressing. Store the address of the data memory location in auxiliary register AR4. AR4 = 186h. Copy bit from the data memory address pointed to by auxiliary register AR4 to the bit TC (test/control flag) in the status register ST0.

BIT *AR4, 6

The instruction BIT (bit test) takes two operands. The first operand is the data memory address of the variable, the second operand is the number of the bit to be tested. In the Texas Instrument databook, the description of the instruction BIT (bit test) is in fact somewhat misleading. No test is actually performed. The instruction BIT copies the bit under test to the bit TC (test/control flag) in the control register ST0. Now for the less-than-obvious part guaranteed to catch the unwary. The usual convention is to number data bits of a word from 0 (the least significant or right-most bit) to 15 (the most significant or left-most bit). As far as the operand used with the instruction BIT is concerned, the bits are numbered in reverse order to the usual convention. On the TMS320C54x, the bits of a data word are numbered from 15 (representing the least significant or rightmost bit) to 0 (representing the most significant or left-most bit). In this series of tutorials we shall adhere to the standard convention and always refer to bit 0 as the least significant (right-most) bit. The code required to use the instruction BIT to copy bit 0 of data memory address 103h to bit TC (test/control flag) using direct addressing is shown in Example 11-5. Example 11-5. STM #103h, AR5 ; ; ; ; ; ; ; ; Indirect addressing. Store the address of the data memory location 103h in auxiliary register AR5. AR5 = 103h. Copy bit 0 from the data memory address pointed to by auxiliary register AR5 to the bit TC (test/control flag) in the status register ST0.

BIT *AR5, (15-0)

To remain with the convention that bit 0 represents the least significant (right most) bit, when entering the operand, we subtract the number of the bit to be tested, here 0, from 15. The significance of the bit TC is that it can be tested with the instruction BC (branch conditionally).

TUTORIAL

T-123

ASSEMBLY LANGUAGE TUTORIAL Now let us combine the instruction BIT (bit test) with the instruction BC. Say we wish to test if the least significant (right-most) bit of the word at data memory-address 103h is 1, and if so, branch to the label action2 (somewhere else in code): Example 11-6. STM #103h, AR2 BIT *AR2, (15-0) ; ; ; ; ; ; ; ; ; ; Store address of data memory location 103h in AR2. AR2 = 103h. Copy bit 0 (the least significant or right-most bit) from the data memory address pointed to by AR2 to the bit TC (test/control flag) in the status register ST0. If bit TC is 1 (the condition TC = 1 is TRUE) then branch to the label action2.

BC action2, TC

Clarifying Code Because the bits within a word are numbered in reverse order to the usual convention, mistakes can occur. To prevent error, we have already seen how to write the operand as (15 - bit to be tested), another sample of which is shown in Example 11-7: Example 11-7. STM #220h, AR3 ; ; ; ; ; Store address of data memory location 220h in auxiliary register AR3. AR3 = 220h. Copy bit 13 of data memory address 220h to the TC bit.

BIT *AR3, (15-13)

A more foolproof method is to use the assembler directive .set: Example 11-8.

Symbol for standard bit numbering convention BIT_0 BIT_1 BIT_2 to BIT_14 BIT_15

Assembler directive .set .set .set .set

TMS320C54x bit numbering convention 15 14 13 to 1 0

At the beginning of the program we write a series of 16 directives of type .set to equate the numbers to a series of symbols BIT_0 to BIT_15. When we require to do work on a particular bit we use the symbol defined using the .set directive. For example, to test bit 8 of the word at data memory address 38Ch we can write:

TUTORIAL

T-124

ASSEMBLY LANGUAGE TUTORIAL Example 11-9. BIT_8 .set (15-8) STM #38Ch, AR2 ; ; ; ; ; ; ; ; ; ; ; Assembler directive to generate symbol for bit 8. Store address of data memory location in auxiliary register AR2. AR2 = 38Ch. Copy bit 8 of the word at data memory address pointed to by AR2 to the test/control bit TC. When bit TC = 1 (the condition TC = 1 is TRUE) then branch to the label action.

BIT *AR2, BIT_8

BC action, TC

The assembler replaces the symbol BIT_8 by the number 7. Bit Testing Using Variables The instruction BIT (bit test) is limited in that it can only be used with an immediate operand. For more flexibility, we may wish to test a bit using a variable rather than immediate data. This gives us the possibility of calculating the bit to be tested. The T (temporary) register is normally used for multiplications, but is also used with the instruction BITT (test bit specified by T register). The format of the instruction BITT is shown in Example 11-10: Example 11-10. LD #5, DP BITT 30h ; ; ; ; ; ; Page 5. Gain access to data addresses 280h to 2FFh. Copy the bit in data memory 280h + 30h = 2B0h specified value in the T register (in 15) to the test/control bit memory address by the range 0 to TC.

The instruction BITT (test bit specified by T register) takes a single operand which is the address in data memory of the variable to be tested. The bit to be tested (in the range 0 to 15) is loaded into the T register. Note that the instruction BIT differs from the instruction BITT in that the latter supports direct addressing. Example 11-11 shows how we test bit 0 of data memory address 2A0h using the instruction BITT, this time with indirect addressing. Example 11-11. STM #2A0h, AR6 STM #(15-0), T ; ; ; ; Store the address of data memory location 2A0h. AR6 = 2A0h. Store value required for bit test to the T register.

TUTORIAL

T-125

ASSEMBLY LANGUAGE TUTORIAL BITT *AR6 ; Copy bit 0 from data memory address ; 2A0h to the bit TC (test/control ; flag) in the status register ST0.

Testing Multiple Bits There are times when we may wish to test more than one bit of a word in data memory. Say we wish to test whether each of bits 0 and 1 of a word in data memory is set to 1. Example 11-12 shows how we would do this in C code: Example 11-12. unsigned int value; if ( (value & 0x0003) == 0x0003)) { /* Action when bits set */ } // A data word. // Test bits 0 and 1. // Some action.

First, we use a logical AND to clear to zero all the bits in which we have no interest. We then compare the remaining value with 0003h. Say that the variable value contains ABCDh. After the logical and with 0003h we are left with 0001h which we then test against the value 0003h. To implement Example 11-12 using TMS320C54x assembly language, we cannot use the instruction BIT (bit test) because it can only be used to test a single bit. Instead we must use a logical AND together with some form of compare instruction such as an exclusive OR. Assuming that the variable value is stored at data memory address 85h then we can write: Example 11-13. LD #1, DP LD 5h, A ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Page 1. Gain access to data memory addresses 80h to FFh. Copy the word from data memory address 80h + 5h = 85h to accumulator A. Clear to zero all bits of accumulator A except bits 0 and 1. Compare the value in accumulator A with 0003h. If bits 0 to 1 are not set to 1, then branch to the label not_set. Action to be carried out when bits 0 to 1 are set to 1. Bits not set. No action.

AND #0003h, A

XOR #0003h, A BC not_set, ANEQ

bits_set: not_set:

Again we have not written any code for what is to be done when a bit is set or not, but simply put a template in place.

TUTORIAL

T-126

ASSEMBLY LANGUAGE TUTORIAL Testing Specific Bits for Non-Zero Let us look at another example. Say we wish to test if bits 4 to 7 of a particular data word are all cleared to zero. For example, the numbers 0000h, FF0Fh, 3303h, 1204h would meet this criteria. The numbers 00F0h, FFAFh, 33E0h and 1234h would not. We can carry this operation out in C code as shown in Example 11-14: Example 11-14. unsigned int value; if ( value & 0x00F0) { /* Action when bits zero */ } // A data word. // Test bits 4 to 7. // Some action.

We clear all the bits to zero except bits 4 to 7 using a logical AND. We then test if the remaining value is zero and act accordingly. The C code shown in Example 11-14 can be implemented using TMS320C54x assembly language, and this is shown in Example 11-15. Assuming that the variable value is stored at data memory address 425h: Example 11-15. LD #8, DP ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Page 8. Gain access to data memory addresses 400h to 47Fh. Copy variable value at data memory address 400h + 25h = 425h to accumulator A. Clear to zero all bits of accumulator A, except bits 4 to 7. If bits 4 to 7 are all cleared to zero, then branch to the label not_set. Action to be carried out when any or all of bits 4 to 7 are set to 1. Bits not set. No action.

LD 25h, A

AND #00F0h, A

BC not_set, AEQ

bits_set:

not_set:

Note that in Example 11-15 we do not need to carry out any comparison using the instruction XOR or the instruction SUB. This is because we can directly test for zero in accumulator A or B using the instruction BC with the condition AEQ or ANEQ. The TMS320C54x also provides a way of carrying out a logical AND of a word at a data memory address, but without the need to use the accumulator. For this, we use the special instruction BITF (test bit field specified by immediate value). The usage of the instruction BITF with direct addressing is shown in Example 11-16.

TUTORIAL

T-127

ASSEMBLY LANGUAGE TUTORIAL Example 11-16. LD #5, DP ; ; ; ; ; ; ; ; Direct addressing. Page 5. Gain access to data memory addresses 280h to 2FFh. Test if bits 0 and 1 of data memory address 280h + 30h = 2B0h are set to 1. If either bit is 1, then make bit TC = 1. If neither bit is 1 then make bit TC = 0.

BITF 30h, #0003h

In this case we are testing the two least significant (right-most) bits of the value. Unlike the instruction BIT, the instruction BITF supports direct addressing. We can carry out the same operation as shown in Example 11-17, but this time using indirect addressing. Example 11-17. STM #2B0h, AR2 ; ; ; ; ; ; ; ; Indirect addressing. Store address of data memory location in auxiliary register AR2. AR2 = 2B0h. Test bits 0 and 1 of the data memory address pointed to by AR2. If the result of the test is 0, then make bit TC = 0. Otherwise make bit TC = 1.

BITF *AR2, #0003h

The instruction BITF (bit test field specified by immediate value) performs a logical AND of the data memory address and the immediate value. If all of the bits under test are 0 then the result will be 0, then the bit TC (test/control flag) is loaded with 0. If the result of the logical AND is non-zero, which is the case if any of the bits is 1, then the bit TC is loaded with 1. The syntax of the instruction BITF (bit test field specified by immediate value) is unusual in that the immediate operand comes after the data memory address. Normally, the immediate value follows the instruction. Using the instruction BITF (bit test field specified by immediate value), we can re-write the code in Example 11-14 using the conditional branch instruction with the condition NTC (test control bit TC not set to 1). Example 11-18. LD #8, DP ; ; ; ; ; ; Page 8. Gain access to data memory addresses 400h to 47Fh. Test bits 4 to 7 of data memory address 425h and set bit TC accordingly.

BITF 25h, #00F0h

TUTORIAL

T-128

ASSEMBLY LANGUAGE TUTORIAL BC none_set, NTC ; ; ; ; ; ; ; ; ; ; If bit TC is not set to 1 (the condition TC = 1 is FALSE), then branch to the label not_set. (Skip action). Action to be carried out when any or all of bits 4 to 7 are set to 1 goes here. No bits are set to 1. No action.

bit_set:

none_set:

Note that the instruction BITF (bit test field specified by immediate value) can only be used to test the contents of a data memory address for zero or non-zero. It cannot distinguish between one or all the bits being set to 1. Testing for Overflow In previous tutorials we saw how to carry out multiplication with accumulation. It was mentioned that overflow can be a potential problem. By overflow we mean that the process has caused the contents of accumulator A or accumulator B to be greater than 7FFFFFFFh or less than 80000000h. When an overflow occurs, the value in an accumulator is unreliable. Using the instruction BC (branch conditionally), we have the facility to test for an overflow (or for no overflow) in accumulator A or accumulator B. The operand used with the instruction BC can be of four types of test condition: AOV (overflow from accumulator A), BOV (overflow from accumulator B), ANOV (no overflow from accumulator A) and BNOV (no overflow from accumulator B). If the test condition evaluates to true, then a branch occurs. Example 11-19. ; If an overflow has occurred in ; accumulator A (condition AOV is TRUE) ; then branch to the label action3. ; If an overflow has occurred in BC action4, BOV ; accumulator B (condition BOV is TRUE) ; then branch to the label action4. ; If no overflow has occurred in BC action5, ANOV ; accumulator A (condition ANOV is TRUE) ; then branch to the label action5. ; If no overflow has occurred in BC action6, BNOV ; accumulator B (condition BNOV is TRUE) ; then branch to the label action6. We may therefore use a conditional branch to take action when an overflow has occurred. Example 11-20 shows where an overflow test might be used with a multiply/accumulate loop. In this case, two blocks of 10 values in data memory are being multiplied and accumulated. BC action3, AOV

TUTORIAL

T-129

ASSEMBLY LANGUAGE TUTORIAL Example 11-20. RSBX OVM STM #10-1, AR3 STM #200h, AR1 ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Turn off overflow mode. AR3 used as the loop counter. 10 iterations. Store address of start of first data block in AR1. AR1 = 200h. Store address of start of second data block in AR2. AR2 = 210h. Clear accumulator A to zero before carrying out accumulations. Load the T register with word from the first block. Point to the next element. Multiply the contents of the T register by the value from the second block. Accumulate product in accumulator A. Point to next block address. Test for overflow of accumulator A. If the condition AOV is TRUE then branch to the label overflow. Decrement the loop counter. There has been no overflow. Accumulator A contains valid data. There has been an overflow. The data in accumulator A is invalid.

STM #210h, AR2

LD #0, A

loop1:

LOAD *AR1+, T

MAC *AR2+, A

BC overflow, AOV

BANZ loop1, AR3*no_overflow:

overflow:

After the instruction MAC (multiply with accumulate), the overflow flag OVA in status register ST0 is checked using the instruction BC with the condition AOV (branch on overflow). Should an overflow occur, the multiply / accumulate loop will terminate and branch to the error handler at label overflow.

TUTORIAL

T-130

ASSEMBLY LANGUAGE TUTORIAL Using the Test/Control Flag TC to Test Auxiliary Registers We have already seen how to compare an accumulator with an immediate value. There are cases when we wish instead to use one of the auxiliary registers AR0 to AR7 as a general purpose register and compare the contents of the auxiliary register with a known value. The instruction CMPR (compare auxiliary register with AR0) is used to compare the value in one of auxiliary registers AR1 to AR7 with the value in auxiliary register AR0. Four different tests are available. If the outcome of the particular test is true, then the bit TC in control register ST0 set to 1. Otherwise the bit TC is cleared to zero. The instruction CMPR (compare auxiliary register with AR0) takes two operands. The first operand is the kind of comparison to be made. It can be EQ (equals), LT (less than), GT (greater than) or NEQ (not equal). The second operand is the auxiliary register to be tested. Example 11-21 shows typical ways to use the instruction CMPR: Example 11-21. CMPR EQ, AR3 CMPR LT, AR2 ; ; ; ; ; ; ; ; ; ; ; ; If AR3 is equal to AR0 (condition AR3 = AR0 is TRUE), then set bit TC to 1. If AR2 is less than AR0 (condition AR2 < AR0 is TRUE), then set bit TC to 1, otherwise clear bit TC to 0. If AR7 is greater than AR0 (condition AR7 > AR0 is TRUE), then set bit TC to 1, otherwise clear bit TC to 0. If AR4 is not equal to AR0 (condition AR4 not equal to AR0 is TRUE), then set bit TC to 1, otherwise clear bit TC to 0.

CMPR GT, AR7

CMPR NEQ, AR4

Instead of writing the operand EQ, we could write the number 0, as shown in Example 11-22. Example 11-22. CMPR 0, AR3 ; If AR3 is equal to AR0, then set bit TC ; to 1.

Instead of the operands LT, GT and NEQ we could write the numbers 1,2 and 3 respectively. However, it is not immediately obvious that the operand 0 means EQ, which makes the code is more difficult to read. For clarity, it is better to use the symbols EQ, NEQ, LT, GT rather than the numbers 0 to 3. The instruction CMPR makes a comparison of an auxiliary register (as specified by the operand) with AR0. If AR0 is used as the second operand, the instruction will not function correctly. This is shown in Example 11-23.

TUTORIAL

T-131

ASSEMBLY LANGUAGE TUTORIAL Example 11-23. CMPR NEQ, AR0 ; ; ; ; If AR0 is not equal to AR0 then set bit TC to 1. The bit TC will never be set to 1 because a comparison is being made of two identical values.

We can follow the instruction CMPR with the instruction BC to test for an auxiliary register containing a specific value. Example 11-24. CMPR EQ, AR3 BC action, TC ; ; ; ; ; If the condition that AR3 is equal to AR0 is TRUE then set the bit TC to 1. If bit TC (test/control flag) is 1 (the condition TC = 1 is TRUE) then branch to the label action.

A typical usage of a comparison of an auxiliary register with a known value is in a control loop. To use the analogy of C code, we do this using a for loop: Example 11-25. unsigned int i; for ( i = 0 ; i < 10; i++) { /* No action required */ } // iterations counter. // 10 iterations

We can implement Example 11-25 in TMS320C54x assembly language using one of the auxiliary registers, say AR5 to store the value i. This is shown in Example 11-26: Example 11-26. STM #0, AR5 STM #10, AR0 MAR *AR5+ CMPR LT, AR5 ; ; ; ; ; ; ; ; ; ; ; Initialise the iterations counter AR5 to zero. Value to test AR5 against. Increment contents of AR5. If AR5 is less than AR0 then set the bit TC to 1, otherwise clear the bit TC to 0. If the bit TC is set to 1 (the condition is TRUE) then branch to the label loop2. Loop now terminated.

loop2:

BC loop2, TC

done2:

The instruction CMPR (compare auxiliary register with AR0) is used to test for the condition AR5 is less than AR0. If the condition that AR5 is less than AR0 is true, then the bit TC is set to 1 and the conditional branch instruction BC will branch to the label loop2. On the tenth time the loop is executed, the value in AR5 will be equal to AR0 so the loop will be terminated and the next instruction to be executed will be at the label done2.

TUTORIAL

T-132

ASSEMBLY LANGUAGE TUTORIAL

There are two limitations of using an auxiliary register for comparison purposes. First the instruction CMPR (compare auxiliary register with AR0) is that it only allows four types of comparison. It does not support the comparisons less than or equal to (LTE) or greater than or equal to (GTE). Second, in order to carry out a comparison, auxiliary register AR0 must also be used as well as the auxiliary register under test, which may be wasteful of resources. Upgrading from the TMS320C25 and TMS320C2xx/5x to the TMS320C54x The TMS320C25, TMS320C2xx and TMS320C5x all support operations on bits, although using a slightly different syntax. Table 11-1. Comparison of Instructions
Description Bit test immediate Bit test by T Bit field Clear bit TC Set bit TC Branch on TC clear Branch on TC set TMS320C25 Instruction BIT BITT Not supported RTC STC BBZ BBNZ TMS320C2xx and TMS320C5x Instruction BIT BITT Not supported CLRC TC SETC TC BCND TC BCND NTC TMS320C54x Instruction BIT BITT BITF RSBX TC SSBX TC BC NTC BC TC

Because the TMS320C54x allows direct storage of a value in the T register using the instruction STM, the instruction BITT is more effective than was the case with earlier devices. The TMS320C54x has introduced the new instruction BITF (test bit field specified by immediate value) which can be used to test multiple bits.

Questions
1. 2. 3. 4. 5. What is a Boolean variable? How many Boolean variables can be stored in a data word? How can a particular bit of accumulator A or accumulator B be tested? Why is it desirable to be able to test a particular bit of a data word directly, without using an accumulator? The instruction BIT 60h, 0 is used to test: a) The least significant (right-most) bit of data memory address 60h. b) The most significant (left-most) bit of data memory address 60h. What test does the instruction BIT carry out? How is the bit TC (test control flag) used in ST0? How can testing the wrong bit of a data memory address be avoided when using the instruction BIT? What are the differences between the instructions BIT and BITT?

6. 7. 8. 9.

TUTORIAL

T-133

ASSEMBLY LANGUAGE TUTORIAL 10. 11. What are the differences between the instructions BIT and BITF? Which two of the following instructions are correct? a) BITF *AR3, #22h b) BITF #0Fh, AR1 c) BITF 14h, #0FF00h d) BITF #55h, 16h e) BITF AR4, #16h. Why can overflow be a problem? How can a test to determine if a multiplication has caused an overflow be performed? How can a comparison that auxiliary register AR5 is greater than auxiliary register AR0 be performed using the instruction CMPR? What other instruction is used with the instruction CMPR to test the bit TC (test / control flag)? Which of the following instructions support direct addressing? a) BIT b) BITT c) BITF

12. 13. 14. 15. 16.

TUTORIAL

T-134

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 12: Stack Operations
New Instructions Introduced PSHD PSHM POPD POPM Overview of Tutorial The stack is a part of the TMS320C54x that is essential for the operation of subroutine calls and interrupts. This tutorial looks at the structure of the stack and how it is used to save and restore data. The Stack The stack is an area of data memory put aside for use with subroutine calls, interrupts and the temporary storage of variables. It is normally placed at a high address in data memory so as to remain separate from the memory-mapped registers and program variables. The stack has its own dedicated memory-mapped register known as the stack pointer (SP), which points to the start of the stack. This is shown in Figure 12-1. Figure 12-1. Model of the Stack Pointer

Figure 12-1. Model of the Stack Pointer Data Memory 0000h 1377h 0000h 1378h 0000h 1379h 0000h 137Ah 0000h 200h FFFFh FFFFh FFFFh FFFFh 137Bh 137Ch 137Dh 137Eh 137Fh 1380h

Stack Pointer SP 137Ch

TUTORIAL

T-135

ASSEMBLY LANGUAGE TUTORIAL In Example 12-1, the stack pointer points to data memory address 137Ch. One of the first tasks in the initialization of the processor is to set the stack pointer to point to the start of data memory allocated for the stack. The stack pointer is set to a specific value using the instruction STM (store immediate value in memory mapped register): Example 12-1. STM #137Ch, SP ; Set stack pointer (SP) to point to the ; data memory address 137Ch.

The value to which the stack pointer is initialized depends upon the hardware being used. For example, on the TMS320C541, the data memory lies between 0h and 13FFh. We might therefore use a value of 13F0h for the stack pointer. The size of the stack depends upon the application, but a typical size is 100 words of data memory. When initializing the stack pointer (SP), care needs to be taken if doing prototype work on one TMS320C54x device and then using another device with lower capacity for production. What may be a data memory location on one device may be outside the data memory space on another. For example, on the TMS320C542, the address 1800h is a valid address in data memory. However, on the TMS320C541, the address 1800h lies outside the data memory space. Pushing Data Onto the Stack The process of storing a variable on the stack is referred to as pushing. Figure 12-2 shows how we push the value 7777h onto the stack: Figure 12-2. Pushing a Data Memory Variable Onto the Stack Figure 12-2. Pushing a Data Memory Variable onto the Stack Data Memory Stack Pointer SP 137Ch 0000h 0000h 0000h 0000h 0000h 200h FFFFh FFFFh FFFFh FFFFh 1377h 1378h 1379h 137Ah 137Bh 137Ch 137Dh 137Eh 137Fh 1380h Stack Pointer SP 137Bh Data Memory 0000h 0000h 0000h 0000h 7777h 200h FFFFh FFFFh FFFFh FFFFh 1377h 1378h 1379h 137Ah 137Bh 137Ch 137Dh 137Eh 137Fh 1380h

Before Push

After Push

TUTORIAL

T-136

ASSEMBLY LANGUAGE TUTORIAL When a push occurs, the stack pointer is decremented by one and the value being pushed is copied to the data memory address pointed to by the stack pointer. The stack pointer points to the last value pushed onto the stack. To push the contents of a data memory address onto the stack we use the instruction PSHD (push data memory value onto the stack). This instruction takes a single operand which is the address in data memory of the value to be pushed onto the stack. Example 12-2. LD #7, DP PSHD 43h ; ; ; ; ; ; ; Page 7. Gain access to data memory addresses 380h to 3FFh. Direct addressing. Decrement the stack pointer. Copy the contents of data memory address 380h + 43h = 3C3h to the data memory address pointed to by the stack pointer.

With each push, the stack pointer is decremented by one. We can also carry out the operation shown in Example 12-2, but use indirect addressing instead. Example 12-3. STM #3C3h, AR4 PSHD *AR4 ; ; ; ; ; ; Store address of data memory location 3C3h in auxiliary register AR4. AR4 = 3C3h. Decrement the stack pointer. Push the contents of the data memory address pointed to by auxiliary register AR4 onto the stack.

The instruction PSHD offers a quick and convenient way to temporarily store a value in data memory. A typical usage is for saving a value stored in data memory prior to a destructive test. Popping a Value from the Stack to Data Memory The opposite instruction to push is pop. For this we use the instruction POPD (pop the top of stack to data memory). By top of stack we mean the most recent value pushed onto the stack. The effect upon the stack of the instruction POPD is shown in Figure 12-3:

TUTORIAL

T-137

ASSEMBLY LANGUAGE TUTORIAL Figure 12.3. Popping a Variable from the Stack

Figure 12-3. Popping a Variable from the Stack Data Memory 0000h 0000h 0000h 0000h 7777h 200h FFFFh FFFFh FFFFh FFFFh Data Memory 0000h 0000h 0000h 0000h 7777h 200h FFFFh FFFFh FFFFh FFFFh

Stack Pointer SP 137Bh

Before Pop

1377h 1378h 1379h 137Ah 137Bh 137Ch 137Dh 137Eh 137Fh 1380h

Stack Pointer SP 137Ch

After Pop

1377h 1378h 1379h 137Ah 137Bh 137Ch 137Dh 137Eh 137Fh 1380h

When a pop occurs, the value is copied from the data memory address pointed to by the stack pointer to the location supplied by the operand. The stack pointer is then increased. The instruction POPD takes the most recent value pushed onto the stack and stores it in the data memory address supplied by the operand. The stack pointer is then incremented. Example 12-4 LD #8, DP POPD 50h ; Page 8. Gain access to data memory ; addresses 400h to 47Fh. ; Direct addressing. Pop the top of stack to data memory address 400h + 50h = ; 450h. Increment the stack pointer.

; the

With each pop of a word from the stack, the stack pointer is incremented by one. We can also carry out the operation shown in Example 12-4 using indirect addressing. Example 12-5 STM #450h, AR6 POPD *AR6 ; ; ; ; ; ; Store the address of data memory location 450h in auxiliary register AR6. AR6 = 450h. Pop the contents of the top of the stack to the data memory address pointed to by auxiliary register AR6. Increment the stack pointer.

TUTORIAL

T-138

ASSEMBLY LANGUAGE TUTORIAL The instruction POPD carries out the opposite action to the instruction PSHD and is used to restore a value that has been temporarily saved on the stack.

Pushing and Popping Let us look at what happens when we use the instruction PSHD followed by the instruction POPD. Example 12-6 LD #10, DP ST #9999h, 12h PSHD 12h ; ; ; ; ; ; ; ; ; ; ; ; ; ; Page 10. Gain access to data memory addresses 500h to 57Fh. Store the immediate value 9999h at data memory address 500h + 12h = 512h. Decrement the stack pointer. Copy the value stored at data memory address 512h to the top of stack. Add the immediate value 1111h to the contents of data memory address 512h. Data memory address 512h now contains AAAAh. Pop the contents of the top of stack to the data memory address 512h. Increment the stack pointer. Data memory address 512h now contains 9999h.

ADDM #1111h, 12h

POPD 12h

When we carry out the PSHD instruction in Example 12-6, we temporarily store the value at data memory address 512h on the stack. We then carry out some operation to modify the contents of the data memory location. The instruction POPD is used to restore the value in the data memory address to its original value. We can also carry out the same operation as shown in Example 12-6, but instead by using indirect addressing: Example 12-7 STM #512h, AR7 ; ; ; ; ; ; ; ; ; ; ; ; Store the address of data memory location 512h in auxiliary register AR7. AR7 = 512h. Store the immediate value 9999h at the data memory address pointed to by AR7. Decrement the stack pointer. Copy the value stored at the data memory address pointed to by AR7 onto the top of stack. Add the immediate value 1111h to the contents of data memory address 512h. Data memory address 512h now contains AAAAh.

ST #9999h, *AR7 PSHD *AR7

ADDM #1111h, *AR7

TUTORIAL

T-139

ASSEMBLY LANGUAGE TUTORIAL POPD *AR7 ; ; ; ; ; Copy the contents of the top of stack to the data memory address pointed to by auxiliary register AR7. Increment the stack pointer. Data memory address 512h now contains 9999h.

When both a push and a pop occur, the stack pointer is restored to its original value. It is recommended that push and pop instructions be used in pairs. In this way, it is ensured that the stack pointer will return to its original value. Example 12-8 shows how the stack pointer can be set to a value outside of data memory when a pop occurs without a corresponding push. In this case, any further operations using the stack will not function correctly. Example 12-8 STM #27FFh, SP ; ; ; ; ; ; ; ; ; Set the stack pointer to the highest address in data memory for the TMS320C542. Save the value from the top of stack in the data memory address pointed to by AR2. Increment the stack pointer. The stack pointer now points to data memory address 2800h which is outside the data memory space of the TMS320C542.

POPD *AR2

Example 12-8 has set the stack pointer to a value outside the internal data memory for the TMS320C542. Any subsequent operation that uses the stack will then try to push a value to an address outside data memory. This may cause the program to crash. Carrying out pop instructions without the preceding push instruction is the realm of expert programmers when writing operating systems or clever returns from interrupts. Pushing Memory-mapped Registers We have already seen how to push a value stored in data memory onto the stack and how to pop it. We can also push the values stored in memory-mapped registers onto the stack, in particular the auxiliary registers AR0 to AR7. To push the contents of a memory-mapped register onto the stack we use the instruction PSHM (push memorymapped register onto the stack). This instruction takes a single operand and is the name of the memorymapped register to be stored: Example 12-9. PSHM AR0 ; Decrement the stack pointer. Push the ; contents of auxiliary register AR0 ; onto the stack.

TUTORIAL

T-140

ASSEMBLY LANGUAGE TUTORIAL PSHM AR1+ ; ; ; ; ; ; Illegal. Not allowed to increment or decrement an auxiliary register when pushing its contents onto the stack. Illegal. Operand must be the name of the memory-mapped register, not a data memory address.

PSHM *AR1

We are not restricted to pushing auxiliary registers onto the stack. We can push any memory-mapped register, including the stack pointer itself. Example 12-10. PSHM ST0 ; ; ; ; ; ; ; ; ; ; Push the onto the flags in flag. Push the onto the flags in flag. Push the onto the contents of status register ST0 top of the stack. This saves the ST0 including the Carry (C) contents of status register ST1 top of the stack. This saves the ST1 including the overflow (OVM) contents of the stack pointer stack.

PSHM ST1

PSHM SP

In a later tutorial, we shall see how pushing the status registers ST0 and ST1 is important when using interrupts. Popping Memory-mapped Registers To pop a value from the stack to a memory-mapped register we use the instruction POPM (pop top of stack to memory-mapped register). Again this takes a single operand which is the name of the memory-mapped register. Example 12-11. POPM AR0 POPM AR6; ; ; ; ; ; ; ; Pop top of stack to auxiliary register AR0. Illegal. Not allowed to modify an auxiliary register as part of this instruction. Illegal. Not allowed to pop the contents of a data memory address using this instruction.

POPM *AR4

We can also pop memory-mapped registers other than the auxiliary registers AR0 to AR7. This is shown in Example 12-12.

TUTORIAL

T-141

ASSEMBLY LANGUAGE TUTORIAL Example 12-12. POPM ST0 ; ; ; ; ; ; ; ; ; ; ; Pop the top of stack to status register STO. This restores the status of the flags in ST0, including the Carry (C) flag. Pop the top of stack to status register ST1. This restores the status of the flags in ST1, including the overflow flag (OVM). Pop the top of stack to the stack pointer. This restores the value stored in the stack pointer.

POPM ST1

POPM SP

Pushing and Popping Multiple Values We have seen how to push a value onto the stack to save it temporarily and then how to pop it to restore the original value. We can also push and pop multiple values; however, the order in which values are popped is important, and is shown in Example 12-13: Example 12-13 LD #2, DP ST #1234h, 10h ST #5678h, 11h PSHD 10h ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Page 2. Gain access to data memory addresses 100h to 17Fh. Store 1234h at data memory address 100h + 10h = 110h. Store 5678h at data memory address 100h + 11h = 111h. Push the contents of data memory address 110h onto the stack. The top of stack now contains 1234h. Push contents of data memory address 111h onto the stack. The top of stack now contains value 5678h. Pop top of stack to data memory address 110h. Data memory address 110h now contains 5678h. Pop top of stack to data memory address 111h. Data memory address 111h now contains 1234h.

PSHD 11h

POPD 10h

POPD 11h

The code in Example 12-13 swaps the values in data memory addresses 110h and 111h. This is because the instruction POPD pops the most recent value pushed onto the stack. In this case the value stored at data memory address 111h is pushed onto the stack and then popped to data memory address 110h.

TUTORIAL

T-142

ASSEMBLY LANGUAGE TUTORIAL In order to push multiple values onto the stack and then restore them to their original statuses, the values must be pushed onto the stack then popped in reverse order. Example 12-14 shows how to do this using indirect addressing: Example 12-14. STM #200h, AR1 ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Store the address of data memory location 200h in auxiliary register AR1. AR1 = 200h. Push the contents of data memory address pointed to by AR1 onto the stack. Increment AR1. AR1 = 201h. Push the contents of data memory address pointed to by AR1 onto the stack. Increment AR1. AR1 = 202h. Push the contents of data memory address pointed to by AR1 onto the stack. No increment of AR1 is required. Pop top of stack to data memory address pointed to by AR1. Decrement AR1. AR1 = 201h. Pop top of stack to data memory address pointed to by AR1. Decrement AR1. AR1 = 200h. Pop top of stack to data memory address 200h. No need to decrement AR1.

PSHD *AR1+

PSHD *AR1+

PSHD *AR1

POPD *AR1-

POPD *AR1-

POPD *AR1

In Example 12-14 we have pushed three values onto the stack an popped them in reverse order. In this way the content of each data memory address is saved and then restored to its original value. Mixing Push and Pop Instructions When carrying out push and pop instructions, the TMS320C54x does not check what type of variable is being pushed and popped. It is therefore legitimate to push the contents of a data memory address onto the stack and then later pop this value to a memory-mapped register. Example 12-15. LD #4, DP PSHM AR4 PSHD 44h POPM AR0 ; ; ; ; ; ; ; ; ; ; Page 4. Gain access to data memory addresses 200h to 27Fh. Push the contents of auxiliary register AR4 onto the stack. Push the contents of data memory address 200h + 44h = 244h onto the stack. Pop the top of stack to status register AR0. This operation transfers the contents of data memory address 244h to the auxiliary register AR0.

TUTORIAL

T-143

ASSEMBLY LANGUAGE TUTORIAL POPD 45h ; ; ; ; Pop the top of stack to data memory address 245h. This operation transfers the value in auxiliary register AR4 into data memory address 245h.

In Example 12-15,the contents of AR4 are transferred to data memory address 245h and the contents of data memory address 244h are transferred to auxiliary register AR0. It is quite legal to push the same value onto the stack several times. Example 12-16. LD #9, DP PSHD 2h PSHD 2h PSHD 2h LD #4, DP POPD 45h POPD 46h POPD 47h ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Page 9. Gain access to data memory addresses 480h to 4FFh. Push contents of data memory address 480h + 2h = 482h onto the stack. Push the contents of data memory address 482h onto the stack. Push the contents of data memory address 482h onto the stack. Page 4. Gain access to data memory addresses 200h to 27Fh. Pop the top of stack to data memory address 245h. Pop the top of stack to data memory address 246h. Pop the top of stack to data memory address 247h.

In Example 12-16 we have pushed the value stored at data memory address 482h onto the stack three times. We have then popped this value to data memory addresses 245h, 246h and 247h. Pushing and Popping the Contents of Accumulators The TMS320C54x does not provide specific instructions to push or pop the contents of an accumulator. In practice, this does not pose a problem because the accumulators are generally used for the transient results of mathematical, logical and other operations. Variables are normally stored in data memory and these can be pushed or popped. Upgrading from the TMS320C25 and TMS320C2xx/5x to the TMS320C54x. Table 12-1: Comparison of Instructions
Description TMS320C25 Instruction TMS320C2xx and TMS320C5x Instruction TMS320C54x Equivalent Instruction

TUTORIAL

T-144

ASSEMBLY LANGUAGE TUTORIAL
Push value stored in data memory address onto stack Push value stored in memory mapped register onto stack Pop value from top of stack to data memory address. Pop value from top of stack to memory mapped register Push low word of accumulator onto stack Pop top of stack to low word of accumulator PSHD dmad PSHD dmad POPD dmad POPD dmad PUSH POP PSHD dmad PSHD dmad POPD dmad POPD dmad PUSH POP PSHD dmad PSHM mmr POPD dmad POPM mmr Not supported Not supported

Here mmr = memory mapped register dmad = data memory address On the earlier devices, the only way to push a data memory address or a register was to use the instruction PSHD. The TMS320C54x has the new instruction PSHM to push the contents of a memory-mapped register. Similarly, on earlier devices, the only instruction to pop the contents of a data memory address or a register was the instruction POPD. The TMS320C54x has the new instruction POPM to pop the contents of a memorymapped register. The TMS320C54x differs from earlier devices in that it does not allow the contents of accumulators to be pushed and popped.

TUTORIAL

T-145

ASSEMBLY LANGUAGE TUTORIAL

Questions
1. 2. 3. 4. What is meant by the term stack? What is meant by the term push? What is meant by the term pop? Which three of the following instructions are correct? a) PSHD 2h b) PSHD AR0 c) PSHD AR0+ d) PSHD *AR1e) PSHD *AR2+ f) PSHD SP Which four of the following instructions are correct? a) POPD 34h b) POPD AR1 c) POPD AR4+ d) POPD AR6e) POPD *AR2 f) POPD *AR3+ g) POPD *AR7 Which four of the following instructions are correct? a) PSHM *AR1 b) PSHM AR4 c) PSHM AR3+ d) PSHM *AR2e) PSHM SP f) PSHM ST0 g) PSHM ST1 Which four of the following instructions are correct? a) POPM 22h b) POPM AR4 c) POPM AR0d) POPM *AR1+ e) POPM SP f) POPM ST0 g) POPM ST1 What is meant by the term top of stack? Why is it best to use push and pop instructions in pairs? How do we swap the values in data memory addresses 100h and 101h without using an accumulator? How do we copy the contents of a memory mapped register to a data memory address using push and pop instructions with direct addressing? How do we push the contents of an accumulator onto the stack?

5.

6.

7.

8. 9. 10. 11. 12.

TUTORIAL

T-146

ASSEMBLY LANGUAGE TUTORIAL

TUTORIAL

T-147

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 13: Subroutine Calls
New Instructions Introduced CALL RET FRAME MVMM CALA CC

Overview of Tutorial It is normal programming practice to divide a large project into a series of smaller tasks. For this subroutines are used. This tutorial shows how to call subroutines and pass parameters to them. Subroutine Calls Within the design of a program it is usually possible to break down the design into a series of smaller units known as subroutines. Example 13-1 shows a C program that has been divided into two subroutines named subroutine1() and subroutine2(): Example 13-1. void main(void) { for ( ; ; ) { subroutine1(); subroutine2(); } }

// Infinite loop. // Call first subroutine. // Call second subroutine.

The code in Example 13-1 contains an infinite loop that repeatedly executes the subroutine subroutine1() followed by the subroutine subroutine2(). We shall now implement the code shown in Example 13-1 using assembly language. For this we shall use the instruction CALL (call unconditionally). The instruction CALL takes a single operand, the address in program memory where the body of the subroutine is to be found. Normally the operand takes the form of a label.

TUTORIAL

T-148

ASSEMBLY LANGUAGE TUTORIAL Example 13-2. main: CALL subroutine1 ; ; ; ; ; ; ; Call the subroutine to be found at the address given by the label subroutine1. Call the subroutine to be found at the address given by the label subroutine2. Loop back to the label main.

CALL subroutine2

B main subroutine1: RET subroutine2: RET

; Body of subroutine goes here. ; Return from subroutine ; Body of subroutine goes here. ; Return from subroutine

During program execution, when the instruction CALL is encountered, some internal processing is done and a branch occurs to the address provided by the operand. Normally the code for the subroutine is put somewhere else in the program so as to make the main flow less cluttered. Example 13-2 shows two minimal subroutines named subroutine1 and subroutine2. The body of each subroutine is not shown. At the end of each subroutine, there must be the instruction RET (return from subroutine). The instruction RET causes execution to resume at the main program at the line following the instruction CALL. If the instruction RET is inadvertently omitted, there will be no return to the calling subroutine and program execution will incorrectly continue at the line after the subroutine itself. The Mechanism of Subroutine Calls and Returns When a subroutine call occurs, how does the program remember where to continue execution after the subroutine is completed? Part of the internal processing at the time of executing the instruction CALL is to push the address of the instruction following the CALL instruction onto the stack. This address is referred to as the return address and is shown in Example 13-3. Example 13-3. 3000 CALL subroutine1 ; ; ; ; ; ; Call the subroutine at the program address which has the label subroutine1. The operation following immediately after the instruction CALL.

3002

ADD #5, A

TUTORIAL

T-149

ASSEMBLY LANGUAGE TUTORIAL The left hand column of Example 13-3 shows the address in program memory of the code. The instruction CALL at program address 3000h takes two words of program memory. The instruction following immediately afterwards, here ADD #5, A, is located at address 3002h. This is the return address. When the instruction CALL subroutine1 is executed, the value 3002h is pushed onto the stack. When the instruction RET is reached, program execution continues at the address contained in the return address. The mechanism is shown in Figure 13-1. Figure 13-1. The Effect on the Stack of the Instruction CALL Figure 13-1. The Effect on the Stack of the Instruction CALL Data Memory 0000h 1377h 0000h 1378h 0000h 1379h 0000h 137Ah 0000h 137Bh FFFFh 137Ch FFFFh 137Dh FFFFh 137Eh FFFFh 137Fh FFFFh 1380h Data Memory 0000h 0000h 0000h 0000h 3002h FFFFh FFFFh FFFFh FFFFh FFFFh

Stack Pointer SP 137Ch

Stack Pointer SP 137Bh

Before Subroutine Call

After Subroutine Call

1377h 1378h 1379h 137Ah 137Bh 137Ch 137Dh 137Eh 137Fh 1380h

When the instruction CALL has been executed, the return address, here 3002h, is pushed onto the stack. At the end of a subroutine, the instruction RET pops the return address from the stack to the program counter (PC) and then increments the stack pointer. This is shown in Figure 13-2: Figure 13-2. The Effect on the Stack of the Instruction RET

TUTORIAL

T-150

ASSEMBLY LANGUAGE TUTORIAL

Figure 13-2. The Effect on the Stack of the Instruction RET Data Memory 0000h 0000h 0000h 0000h 3002h Memory (RAM) 0000h 1377h 0000h 1378h 0000h 1379h 0000h 137Ah 3002h 137Bh FFFFh 137Ch FFFFh 137Dh FFFFh 137Eh FFFFh 137Fh FFFFh 1380h

Stack Pointer SP 137Bh

Before RET

1377h 1378h 1379h 137Ah 137Bh FFFFh 137Ch FFFFh 137Dh FFFFh 137Eh FFFFh 137Fh FFFFh 1380h

Stack Pointer SP 137Ch

After RET

After a call and a return, the stack pointer is restored to its original value. It is important that any stack operations within the subroutine such as PSHD, PSHM, POPD and POPM leave the stack pointer in the same condition it was when the subroutine was called. If not, some value other than the return address will be popped off the stack by the instruction RET. Passing Parameters to a Subroutine Consider the simple case in C code where we wish to pass two parameters to a subroutine: Example 13-4. int subroutine2(int a, int b); // // // // // // // Function prototype of a subroutine that takes two parameters. Call made to subroutine2 passing two parameters of value AAAAh and BBBBh. Body of subroutine.

subroutine2(0xAAAA, 0xBBBB);

subroutine2 (int a, int b) { return(a + b); }

// Add variables a and b.

In assembly language, we have seen how to call a subroutine using the instruction CALL. How do we pass values to an assembly language subroutine? Several choices are available to us. We can put the parameters into accumulator A or accumulator B.

TUTORIAL

T-151

ASSEMBLY LANGUAGE TUTORIAL Example 13-5. LD #0AAAAh, A LD #0BBBBh, B CALL subroutine2 ; ; ; ; Load accumulator A with AAAAh. Load accumulator B with BBBBh. Call the subroutine at program address corresponding to the label subroutine2.

Within subroutine2, the two parameters a and b passed to the subroutine are available in accumulator A and accumulator B. Another way to pass the parameters is to use two of the auxiliary registers AR0 to AR7. Example 13-6. STM #0AAAAh, AR1 STM #0BBBBh, AR2 CALL subroutine2 ; ; ; ; Load auxiliary register AR1 with AAAAh. Load auxiliary register AR2 with BBBBh. Call the subroutine at program address corresponding to the label subroutine2.

Within, subroutine2, parameter a is contained in AR1 and parameter b is contained in AR2.

Passing Parameters on the Stack A different method that is widely used is to pass parameters on the stack. This has two advantages. First, a copy of each of the original values is passed to the subroutine, and therefore the originals remain unchanged. Second, using the stack allows us to pass a list of parameters. The length of the list can be variable. Let us now implement Example 13-4, this time passing parameters on the stack: Example 13-7. LD #4, DP ST #0AAAAh, 64h ST #0BBBBh, 65h PSHD 64h PSHD 65h CALL subroutine2 ; ; ; ; ; ; ; ; ; ; ; ; Page 3. Gain access to data memory addresses 200h to 27Fh Store the value AAAAh at the data memory address 200h + 64h = 264h. Store the value BBBBh at the data memory address 200h + 65h = 265h. Push parameter at data memory address 200h + 64h onto the stack. Push parameter at data memory address 200h + 65h onto the stack. Call the subroutine at program address corresponding to the label subroutine2.

When the code at subroutine2 is executed, parameters a and b are on the stack.

TUTORIAL

T-152

ASSEMBLY LANGUAGE TUTORIAL

The effect of the code in Example 13-7 upon the stack is shown in Figure 13-3. Figure 13-3. Passing a Parameter on the Stack to a Subroutine
Figure 13-3. Passing a Parameter on the Stack to a Subroutine Data Memory 0000h Stack Pointer SP 137Ch 0000h 0000h 0000h 0000h FFFFh FFFFh Before Subroutine Call FFFFh FFFFh FFFFh 1377h 1378h 1379h 137Ah 137Bh 137Ch 137Dh 137Eh 137Fh 1380h After Subroutine Call Stack Pointer SP 1379h Memory (RAM) 0000h 0000h 3002h BBBBh AAAAh FFFFh FFFFh FFFFh FFFFh FFFFh 1377h 1378h 1379h Return Address 137Ah 137Bh 137Ch 137Dh 137Eh 137Fh 1380h Parameters

The two parameters AAAAh and BBBBh are pushed onto the stack. The return address 3002h is also pushed onto the stack.

Working on Variables Stored on the Stack As part of the subroutine subroutine2, we shall want to gain access to the parameters passed on the stack. There is a slight problem here. After the subroutine call has been made, the stack pointer does not point to the last parameter passed on the stack, rather it returns to the return address. Before we can work on the parameters, some form of adjustment must be made to the stack pointer. One way to work upon the parameters pushed onto the stack is to use indirect addressing. This is shown in Example 13-8. Example 13-8. subroutine2: MVMM SP, AR0 ; ; ; ; ; ; ; ; ; ; Copy stack pointer to auxiliary register AR0. AR0 points to the return address. Increment AR0 to point to first parameter AAAAh. Carry out some operation using the first variable. Decrement AR0 to point to the second parameter; Carry out some operation using the second parameter BBBBh.

ADDM #2, AR0 LD *AR0-, A

ADD *AR0, A

TUTORIAL

T-153

ASSEMBLY LANGUAGE TUTORIAL RET ; Return to calling subroutine. ; The return value is now in ; accumulator A.

In Example 13-8 we have introduced the new instruction MVMM (move data from memory-mapped register to memory-mapped register). This takes two operands. The first operand must be an auxiliary register AR0 to AR7 or the stack pointer (SP). The second operand must also be an auxiliary register AR0 to AR7 or the stack pointer. This instruction allows us to copy values between auxiliary registers and the stack pointer. The first operation in Example 13-8 is to copy the stack pointer to AR0. This allows us to use indirect addressing. After the instruction MVMM, auxiliary register AR0 points to the same address in data memory as does the stack pointer, which is the return address. In order to work on the first parameter passed on the stack, we must increment the copy of the stack pointer by the number of parameters passed. Here we have incremented AR0 by two. We then carry out some operation using the parameters that have been passed. In this case we simply added them together. Finally we return from the subroutine using the instruction RET. Note that we do not need to decrement AR0 before returning because it has no effect on the stack pointer. The return value (AAAAh + BBBBh) is in accumulator A. By copying the stack pointer to an auxiliary register we can use indirect addressing to work on the values passed on the stack and leave the stack pointer unaffected. This is an inherently safe way of working because no correction to the stack pointer is required before the subroutine returns. Correcting the Stack Pointer after a Subroutine Call When parameters are passed on the stack to a subroutine, the stack pointer needs to be corrected once the subroutine has been completed. Example 13-9 shows the code needed around a subroutine call. Example 13-9. PSHD *AR2+ PSHD *AR2 CALL subroutine2 POPD *AR2 POPD *AR2 ; ; ; ; ; ; ; Push the first parameter onto the stack. Point to second parameter Push the second parameter onto the stack. Call to subroutine subroutine2. Increment the stack pointer. Increment the stack pointer.

In Example 13-9 we push two parameters on the stack then call the subroutine subroutine2. This decrements the stack pointer by two. We then use two POPD instructions to increment the stack pointer to the value it was before the two values were passed on the stack. Note that when parameters are passed on the stack to a subroutine, it is important to correct the stack pointer after the subroutine call. If not, two words of the stack will be lost (pushed but never popped) every time the subroutine is called. Eventually, there will be no more stack available.

TUTORIAL

T-154

ASSEMBLY LANGUAGE TUTORIAL In Example 13-9 we have used the instruction POPD to correct the stack pointer. We could equally well have used the instruction POPM. Instead of using two POPD or POPM instructions we could also use the instruction FRAME (stack pointer immediate offset), as shown in Example 13-10: Example 13-10. PSHD *AR2+ ; ; ; ; ; ; ; ; ; ; Push the first variable onto the stack. Point to the next space on the stack. Push the second variable onto the stack. Call to subroutine. After return from subroutine, increment the stack pointer by two to restore it to the value it was before the subroutine call.

PSHD *AR2+ CALL subroutine2 FRAME 2

The instruction FRAME takes a single operand and this is an offset in the range -128 to +127. With this instruction it is possible to increment or decrement the stack without moving any data or using an auxiliary register. To increment the stack (similar to popping) we use a positive constant. To decrement the stack (similar to pushing) we use a negative constant. Creating a Stack Frame The C language makes use of local variables. These variables exist only while the subroutine is active and the memory allocated is freed for general purpose when the subroutine returns. Example 13-11. void subroutine3(void) { unsigned int a; unsigned long b; ... } // Subroutine // Local variable. One word. // Local variable. Two words. // Body of code.

Within subroutine3, there are two local variables which require a total of three words of data memory. If these variables are on the stack, then the instruction FRAME can be used to create what is known as a stack frame. This is an area of the stack temporarily assigned for the storage of local variables, which exists only for the duration of the subroutine.

TUTORIAL

T-155

ASSEMBLY LANGUAGE TUTORIAL Example 13-12. subroutine3: FRAME -3 ; ; ; ; ; ; ; ; Decrement the stack pointer by 3. Make space for 3 words. Body of subroutine goes here. Increment the stack pointer by 3. Correct the stack pointer before returning to calling subroutine. Increment the stack pointer by 3. Return to calling subroutine.

FRAME +3

RET

At the beginning of the subroutine, we make room on the stack for three local variables using the instruction FRAME with a negative constant. To gain access to the local variables in the stack frame, we can use indirect addressing in a similar way to that given in Example 13-8. At the end of the subroutine the three words allocated for local variables are given back to the stack by executing the instruction FRAME with a positive operand. Conditional Subroutine Calls There are cases when we wish to call a subroutine upon the outcome of a test. In Example 13-11, the subroutine subroutine4() is only called if the variable x is 5. Example 13-13. unsigned int x; if ( x == 5) { subroutine4(); } // Variable. // Test variable. // Call subroutine when x // is equal to 5.

To implement Example 13-11 in assembly language, assuming that the variable x is stored at data memory address 460h then:

TUTORIAL

T-156

ASSEMBLY LANGUAGE TUTORIAL Example 13-14. LD #8, DP LD 60h, A XOR #5, A BC ANEQ, $+2 ; ; ; ; ; ; ; ; ; Page 8. Gain access to data memory addresses 400h to 47Fh. Copy variable x into accumulator A. Compare value in accumulator A with the constant 5. If accumulator A does not contain 0 (the condition ANEQ evaluates to TRUE) then skip the subroutine call. Call subroutine.

CALL subroutine4

In Example 13-14, the instruction BC requires two words of program memory so that the address of the next instruction is at program counter + 2. Here we have used the symbol $+2 to indicate the current program address + 2. This saves the need to use a label. For cases like this, the TMS320C54x provides the special instruction CC (call conditionally). The instruction CC takes two operands. The first operand is the address in program memory of the subroutine to be called. The second operand is the test to be carried out. If the condition evaluates to TRUE, then the subroutine is called. This is shown in Example 13-5: Example 13-15. LD #8, DP LD 60h, A XOR #5, A CC subroutine4, AEQ ; ; ; ; ; ; ; ; ; Page 8. Gain access to data memory addresses 400h to 47Fh. Copy variable x into accumulator A. Compare value in accumulator A with the constant 5. If accumulator A contains zero (the condition AEQ evaluates to TRUE) then call the subroutine at the address given by the label subroutine4.

The instruction CC combines together the instruction BC (branch conditionally) and the instruction CALL (call unconditionally). The range of tests that can be used with the conditional call are the same as those available for the conditional branch BC. Using the instruction CC we may therefore test the following: Carry (C) flag set or clear, Overflow flags OVA or OVB, the test control flag ( TC) or numeric comparisons with values in accumulators A or B. Let us take a second example. Say we wish to call a subroutine if the result of a multiplication has not produced an overflow, as shown in Example 13-16.

TUTORIAL

T-157

ASSEMBLY LANGUAGE TUTORIAL Example 13-16. RSBX OVM MPY *AR2, #2000, B ; ; ; ; ; ; ; ; ; ; Turn off overflow mode to allow overflows to occur. Multiply the value in the data memory address pointed to by AR2 by 2000 and put the product in accumulator B. If there has been no overflow from accumulator B (the condition BNOV evaluates to TRUE) then call the subroutine at the address identified by the label subroutine5.

CC subroutine5, BNOV

Calculated Subroutine Calls There are cases where we wish to call a subroutine dependant upon the value of a variable. When programming in C, we can do this using a series of if-else statements or with a switch statement as shown in Example 13-17. Example 13-17. unsigned int selector; switch(selector) { case 0: subroutine1(); break; case 1: subroutine2(); break; case 2: subroutine3(); break; case 3: subroutine4(); break; } // Controlling variable.

// // // // // // // //

Selector is 0. subroutine1(); Selector is 1. subroutine2(); Selector is 2. subroutine3(). Selector is 3. subroutine4().

Call Call Call Call

This can be implemented using the instruction CALA (call subroutine at location specified by accumulator A). Assuming the variable selector is stored at data memory address 202h: Example 13-18 . LD #4, DP ; ; ; ; ; ; Page 4. Gain access to data memory addresses 200h to 27Fh. Load contents of data memory address 202h into accumulator A.

LD

2h, A

TUTORIAL

T-158

ASSEMBLY LANGUAGE TUTORIAL SFTA 1 ; ; ; ; ; ; ; ; ; ; ; ; ; ; Multiply the value in accumulator A by 2. (Each subroutine call takes 2 words of program memory). Add starting address of subroutines. Call the subroutine at the address contained in accumulator A. Skip subroutines. 2 words. 2 words. 2 words. 2 words.

ADD #subroutine1, A CALA

subroutine1: subroutine2: subroutine3: subroutine4: done:

B B B B B

done body_subroutine1 body_subroutine2 body_subroutine3 body_subroutine4

To better explain how the code in Example 13-18 works, the program memory addresses have been written in Example 13-19. Example 13-19. Program Memory Address 0555 0556 0557 0558 055A 055B 055D 055F 0561 0563 0565 LD #4, DP LD 2h, A ; selector SFTA 1 ; multiply by 2. ADD #subroutine1, A CALA B done B body_subroutine1 B body_subroutine2 B body_subroutine3 B body_subroutine4

subroutine1: subroutine2: subroutine3: subroutine4: done:

This instruction CALA works in a similar way to a look-up table. We load accumulator A with the starting address of the subroutines starting at the address subroutine1 (here 055Dh). Then we add the offset provided by the variable to accumulator A. Note that because each branch instruction takes 2 words of data memory, we must multiply the offset by 2. In Examples 13-18 and 13-19, the four subroutines are at addresses body_subroutine1, body_subroutine2, body_subroutine3 and body_subroutine4 respectively. Each subroutine must contain the instruction RET. Using a calculated call to a subroutine offers two advantages over a series of conditional calls. First, the calculated call requires less code. Second, with a series of conditional calls (if-else) there will be more

TUTORIAL

T-159

ASSEMBLY LANGUAGE TUTORIAL instructions to be executed to reach the final subroutine than will be the case with the first. The code using the switch statement will always take the same number of instructions. Upgrading from the TMS320C25 and TMS320C2xx/5x to the TMS320C54x All the devices support the instructions CALL, RET, CALA and CC. Table 13-1. Comparison of Instructions
Description TMS320C25 Instruction CALL RET CALA CC TMS320C2xx and TMS320C5x Instructions CALL RET CALA CC TMS320C54x Instructions CALL RET CALA CC

Call unconditionally Return from subroutine Call subroutine at location specified by accumulator Call conditionally

Questions
1. 2. What is meant by the expression subroutine call? Which one of the following is correct: a) CALL b) CALL A c) CALL 3000h d) CALL *AR2 e) CALL *AR0f) CALL subroutine4? Which one of the following is correct: a) RET b)RET A c) RET 16h d) RET #49 e) RET AR1 f) RET *AR1? What happens if we omit the instruction RET in a subroutine? What is meant by the term return address? The instruction MVMM means: a) Move data memory to data memory b) Move multiple memory words c) Move memory-mapped register to memory-mapped register d) Move multiple memory-mapped registers?

3.

4. 5. 6.

TUTORIAL

T-160

ASSEMBLY LANGUAGE TUTORIAL 7. Which two of the following are correct: a) MVMM AR0, SP b) MVMM *AR0, SP c) MVMM *AR0, *SP d) MVMM AR4, *SP e) MVMM ST0, AR2 f) MVMM SP, AR3 g) MVMM AR1, *AR2 After passing parameters on the stack to a subroutine, why do we need to correct the stack pointer (SP) within the subroutine? The instruction FRAME +2 carries out which of the following: a) Increments the stack pointer by 2 b) Decrements the stack pointer by 2 c) Increments auxiliary register AR2 by 2 d) Decrements auxiliary register AR2 by 2? What is meant by the term stack frame? How can room be created on the stack for local variables? The instruction CC means: a) Clear Carry b) Calculate Carry c) Call conditionally d) Call if Carry Clear e) Calculate Constant f) Compute Constant? Which of the following is correct: a) CC subroutine2, BEQ b)CC BEQ, subroutine2? The instruction CALA means: a) Calculate with left shift A b) Call Always c) Call address in accumulator A d) Clear accumulator A?

8. 9.

10. 11. 12.

13.

14.

TUTORIAL

T-161

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 14: Non-Maskable Interrupts
New Instructions Introduced RETE RETED PORTR PORTW BD INTR RESET Overview of Tutorial One of the important facets of real-time software is the ability to handle events from external sources without affecting the performance of the system. For this task interrupts are used, and their usage is described in this tutorial. The Concept of Interrupts In everyday life, we are familiar with the concept of interrupts. An interrupt is simply a task that must be done in the middle of another task. Let us take a simple example. Say we are working at our desk. The phone rings. We temporarily stop what we are doing, answer the phone and when the telephone conversation is complete, continue with what we were doing. The interruption of the telephone call does not affect the execution of the task, other than adding slightly to the time required to carry it out. The whole basis of an interrupt is that it allows other processes to go on rather than having to wait for a particular event to finish. The TMS320C54x has a range of interrupt sources. Interrupts can be made to occur under a variety of different circumstances such as a timer expired, a character being received at a serial buffer, an external line changing state or a specific instruction being executed. Maskable and Non-Maskable Interrupts There are two types of TMS320C54x interrupts - maskable and non-maskable. A maskable interrupt can be turned on or off under the control of software. A non-maskable interrupt is an interrupt that cannot be turned off globally by software. Non-maskable interrupts are normally used for high priority events. Readers will be familiar with the reset button on their computer - it is a high-priority non-maskable interrupt that causes the computer to restart execution from the beginning. The only way to turn it off is to physically disconnect the reset button. Two of the non-maskable interrupts on the TMS320C54x are driven by a change of state at the input pins. These are the RESET line and the non-maskable interrupt (NMI). As we shall see in this tutorial, non-maskable interrupts can also take the form of software instructions. The Interrupt Table The code that determines what happens when an interrupt occurs resides in what is known as the interrupt vector table. When an interrupt occurs - be it from a hardware or a software source - the program branches to the appropriate address in the interrupt vector table.

TUTORIAL

T-162

ASSEMBLY LANGUAGE TUTORIAL

The structure of an interrupt vector table is shown in Table 14-1. Table 14-1. Interrupt Vector Table for the TMS320C5402
K Number 0 1 Name sintr NMI/sint16 Offset in Program Memory 00h 04h Description Hardware reset and software reset Non-maskable hardware interrupt and software interrupt 16 Software interrupt 17 Software interrupts 18 to 30 External user interrupt 0 External user interrupt 1 External user interrupt 2 Timer interrupt Other interrupts

2 3-15 16 17 18 19 20-31

sint17 sint18 to sint30 INT0/sint0 INT1/sint1 INT2/sint2 TINT0/sint3 Others

08h 0Ch to 3Ch 40h 44h 48h 4Ch 70h to 7Fh

The interrupt vector table is situated in program memory (ROM). We normally refer to the interrupts by their offset address, so the first interrupt is referred to as being at program memory location 0h. Some of the interrupts can be driven from both hardware and software sources. For example, non-maskable interrupt NMI can be driven from the NMI pin of the device. The same interrupt can also be made to occur through software. Interrupts that can be driven by software are denoted by the term sint which is used as an abbreviation for software interrupt. Each entry in the interrupt vector table has four words of program memory allocated to it. For example, the nonmaskable interrupt NMI has its interrupt table between 04h and 07h. When a certain change of state occurs at the NMI pin, program execution is immediately redirected to location 4h. Interrupts can also be referred to by name. For example, reset is also known as sintr. Another convenient way to refer to a particular interrupt is by a number in the range 0 to 31. The first column of Table 14-1 gives the K numbers of each interrupt. Therefore, we can refer to RESET as interrupt 0. The K number is the value used as an operand with the instruction to generate a software interrupt. Note that the interrupt vector table is device dependant. Table 14-1shows the interrupt vector table for the TMS320C5402, and the table does differ between devices in the TMS320C54x family. Interrupt Service Routines When an interrupt occurs, a special subroutine is executed that is known as the interrupt service routine. This is often abbreviated as isr. An interrupt service routine differs from a standard subroutine in that we never call it directly from the body of the program. It is automatically called when a particular event occurs. The shortest interrupt service routine (isr) we can use contains only the instruction RETE (return and enable interrupts). This is shown in Example 14-1.

TUTORIAL

T-163

ASSEMBLY LANGUAGE TUTORIAL

TUTORIAL

T-164

ASSEMBLY LANGUAGE TUTORIAL Example 14-1. sint17: RETE ; ; ; ; ; ; ; Return from interrupt and enable interrupts. 5 cycles. 1 word padding. 1 word padding. 1 word padding. Next interrupt service routine.

NOP NOP NOP sint18:

We have an interrupt service routine at the program memory address sint17 (software interrupt 17 at location 44h). To fill the unused three words of the interrupt service routine we have inserted three NOP instructions. These act as padding so that the next interrupt service routine starts at the correct program memory address. It is important that every interrupt service routine contains an instruction that causes a return from interrupt such as RETE. If such an instruction is not present, when an interrupt occurs, the program execution will not return from interrupt, but drop through to the next line of code and carry out some operation that was not intended. Note that we do not use the instruction RET to return from an interrupt service routine. During an interrupt service routine, further interrupts are prevented by temporarily turning the interrupts off. When the instruction RETE occurs, not only is there a return but the interrupts are also turned on again. For the first time in this series of tutorials, we have shown the number of execution cycles required. Each instruction takes a certain amount of time to execute and this time is expressed in terms of a system clock. A typical cycle time is 25 ns. As an example, the instruction RETE requires 5 cycles. This is the minimum time required to process the interrupt service routine. In fact, the time will be greater because the current instruction must be completed before the interrupt can occur. Four words of program memory are very limited, but may be enough code space to carry out a simple task such as to read in value from a parallel input port. Example 14-2. timer: PORTR 17h, *AR2 ; ; ; ; ; ; ; ; Read value at port address 17h into the data memory address pointed to by auxiliary register AR2. 2 words. Return from interrupt. 1 word. 1 word padding.

RETE NOP

TUTORIAL

T-165

ASSEMBLY LANGUAGE TUTORIAL The instruction PORTR (read data from port) takes two operands. The first operand is the address of the external parallel input / output port. The second operand is the address in data memory to which the value is to be written. A port is physical hardware that allows us to interface to the outside world. An example is the parallel port LPT1 on a computer that takes signals from the computer to the peripheral device, in this case a printer. On the TMS320C54x, a port usually takes the form of a memory-mapped address. If interrupts are not being used, the code space can be used for other purposes. However, care needs to be taken. Should the interrupt be executed, then the code in place of the interrupt service routine will be taken to be the interrupt service routine. If there is program memory available, the safest way is to fill each unused interrupt vector location with the instruction RETE. Software Interrupts Whereas certain interrupts can be generated from hardware derived events, it is possible to generate an interrupt within a program using a software interrupt. This is done using the special instructions INTR and RESET. The instruction INTR (software interrupt) takes a single operand which is a constant in the range 0 to 31. Example 14-3 shows how to generate a call to an interrupt service routine under the control of software. Note that there is a difference between the terms sint and intr. The former is the name of an interrupt and the latter is an executable instruction. Example 14-3. INTR 2 ; ; ; ; Execute interrupt number K = 2 (software interrupt number 17). When this instruction is used, the interrupt service routine at address 8h will be executed.

The instruction INTR is non-maskable. This means it cannot be turned off under the control of software. Forcing a Reset from Software Another software-controlled interrupt is the instruction RESET (software reset). Again this is non-maskable and cannot be turned off by software. The effect of the instruction RESET is drastic; it is the same as CTLALT-DELETE on a computer. The program running is aborted and the TMS320C54x starts again from the very beginning, as it did at power up. Example 14-4. RESET ; ; ; ; Force the TMS320C54x to the beginning of its sequence. Overflow mode is turned off (OVM = 0), sign extension is turned on (SXM = 1) and DP = 0.

TUTORIAL

T-166

ASSEMBLY LANGUAGE TUTORIAL Because of the drastic nature of this command, it would normally be used when the software has failed completely. The instructions INTR 0 and RESET both cause a call to the interrupt service routine at interrupt vector location 0. However, there are some differences between them. The instruction INTR 0 does not reset the flags as does the instruction RESET. This can be used for a warm reset (where we wish to start the program from the beginning but do not wish to change any variables). On the other hand, should be wish to do a reset and put all the registers and flags in their condition at power up, we would use the instruction RESET. This is known as a cold reset. Larger Interrupt Service Routines Whereas four words of program memory are sufficient for simple interrupt service routines, for more practical applications we need to either branch or call a subroutine outside the interrupt vector space. For example, say we wish to read port A at input/output address 17h and copy the value to accumulator A. Example 14-5. sint16: CALL isr_sint16 ; ; ; ; ; ; ; ; ; Call subroutine at program memory address given by the label isr_sint16. 2 words, 4 cycles Return from interrupt. Enable interrupts. 1 word, 5 cycles. 1 word padding.

RETE

NOP isr_sint16: PORTR 17h, *AR2

LD *AR2, A SFTL A,-1 RET

; Read word from external ; input/output port 17h ; and copy to data memory ; address pointed to by ; auxiliary register AR2. ; 2 words. ; Copy word to accumulator ; A. ; Shift accumulator A one ; place to the right. ; Return from subroutine. ; 5 cycles.

Example 14-5 shows the interrupt service routine outside the four words allocated in the interrupt vector table. We use the instruction CALL to reach the subroutine isr_sint16. When the subroutine is completed, the program execution returns to the interrupt vector table using the instruction RET, which also corrects the stack pointer. Finally, the interrupt service routine is completed with the instruction RETE to return execution to the main program and to turn the interrupts on again.

TUTORIAL

T-167

ASSEMBLY LANGUAGE TUTORIAL There is a better way to write Example 14-5. Instead of using the instruction CALL to gain access to the subroutine, we use the instruction B (branch unconditionally). This is shown in Example 14-6.

TUTORIAL

T-168

ASSEMBLY LANGUAGE TUTORIAL Example 14-6. sint16: B isr_sint16 ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Branch to subroutine at program memory address given by the label isr_sint16. 2 words, 5 cycles 1 word padding. 1 word padding. Read word from external input / output port 17h and copy to data memory address pointed to by auxiliary register AR2. Copy word to accumulator A. Shift accumulator A one place to the right. Return from interrupt. 5 cycles.

NOP NOP isr_sint16: PORTR 17h, *AR2

LD *AR2, A SFTL A, -1 RETE

Using the instruction B (branch unconditionally) offers a major advantage over the instruction CALL (call unconditionally). The instruction B does not push a return address onto the stack. This means that we do not need to execute the instruction RET to correct the stack pointer and can return directly from the interrupt service routine using the instruction RETE. This has the effect that the interrupt service routine runs 5 cycles faster. This may not seem a long period of time, but for interrupts, execution time may be critical. An interrupt may be called thousands of times per second. While the interrupt service routine is being processed, the execution of the main program is suspended. Saving Registers When using interrupts, it is important to remember that an interrupt can occur at any point in the program. Therefore, if there are any instructions used within the interrupt service routine that affect flags, then these flags should be saved early in the interrupt service routine. For example, say the interrupt service routine uses the instruction SFTL. This instruction affects the Carry (C) flag and therefore the Carry (C) flag should be saved prior to executing the instruction SFTL. The data pointer (DP) and the sign-extension mode bit (SXM) may also be changed. Fortunately, the majority of flags that are affected by instructions are stored in the status registers ST0 and ST1. It is in fact common practice to save the status registers ST0 and ST1 as part of an interrupt service routine. In order to temporarily save the contents of these registers within the body of the interrupt service routine we write:

TUTORIAL

T-169

ASSEMBLY LANGUAGE TUTORIAL

Example 14-7. isr_sint17: PSHM ST0 ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Push the contents of status register ST0 onto the stack. Temporarily save the Carry (C), OVA, OVB flags and DP. Push the contents of status register ST1 onto the stack. Temporarily save the OVM and SXM flags. Page 6. Gain access to data memory addresses 300h to 37Fh. Turn on sign-extension mode. Read value from port at input/output address 17h and store at data memory address 300h + 0h = 300h. Subtract offset by adding a negative value. Copy value to accumulator A. Shift value left 5 places. Save at data memory address 300h + 1h = 301h. Restore flags. Restore flags. Return from interrupt and enable interrupts.

PSHM ST1

LD #6, DP

SSBX SXM PORTR 17h,0h

ADDM #FF00h,0h LD 0h, A SFTL A, 5 STL A, 1h POPM ST1 POPM ST0 RETE

In Example 14-7 we carry out three operations that affect flags. The first is the instruction LD #6, DP which affects the data pointer and the second is to turn on sign-extension mode by changing the status of the SXM flag. Then we execute the instruction SFTL which affects the Carry (C) flag. The original value of all these flags can be saved by using the instructions PSHM ST0 and PSHM ST1, then restored using the instructions POPM ST1 and POPM ST0 before the interrupt service routine returns. Using Delayed Instructions Consider the case of an interrupt service routine that modifies auxiliary registers AR0 and AR1. In order to leave these two auxiliary registers unaffected in the main program, it is necessary to temporarily save these values as part of the interrupt service routine. This is shown in Example 14-8.

TUTORIAL

T-170

ASSEMBLY LANGUAGE TUTORIAL Example 14-8. timer: B timer_isr ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Branch to subroutine at the program memory address given by the label timer_isr. 2 words, 5 cycles 1 word padding. 1 word padding. Temporarily save AR0. Temporarily save AR1. Some operation on AR0. Some operation on AR1. Restore AR1. Restore AR0. Return from interrupt. 5 cycles.

NOP NOP timer_isr: PSHM AR0 PSHM AR1 MAR *AR0+ MAR *AR1POPM AR1 POPM AR0 RETE

Note that when we push AR0 and AR1, we must pop them in reverse order to restore the original values. We can make two improvements to Example 14-8 by using two variations on the B and RETE instructions, as shown in Example 14-9. Example 14-9. timer: BD timer_isr ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Delayed branch to subroutine at program memory address given by the label timer_isr. 2 words, 3 cycles Save AR0. 1 word, 1 cycle. Save AR1. 1 word, 1 cycle. Some operation on AR0. Some operation on AR1. Delayed return from interrupt. 3 cycles. Restore AR1. 1 word, 1 cycle. Restore AR0. 1 word, 1 cycle.

PSHM AR0 PSHM AR1 timer_isr: ADDM #4, AR0 ANDM #0FFh, AR1 RETED POPM AR1 POPM AR0

Example 14-9 makes use of the instructions BD (branch unconditionally with delay) and RETED (enable interrupts and return from interrupt with delay). In both cases, the delay has the effect of speeding program execution. An explanation is in order.

TUTORIAL

T-171

ASSEMBLY LANGUAGE TUTORIAL

The word delay does not mean that the instruction puts a delay in program execution as would the instruction NOP. Here the word delay means that the instruction does not act immediately, but there is a delay before the instruction acts. While waiting for the delay to expire, other instructions can be executed. For example, when the instruction BD is encountered, the branch does not occur immediately. The two single word instructions or one dual word instruction following immediately afterwards are executed and then the branch occurs. The operation of the instruction BD is shown in Example 14-10. Example 14-10. BD timer_isr PSHM AR0 PSHM AR1 ; ; ; ; Branch unconditionally after the next two instructions have been executed. 3 cycles. Temporarily save AR0. 1 cycle. Temporarily save AR1. 1 cycle.

The code in Example 14-10 can be thought of as follows: Example 14-11. PSHM AR0 PSHM AR1 B timer_isr ; Temporarily save AR0. 1 cycle. ; Temporarily save AR1. 1 cycle. ; Branch unconditionally.

The two PSHM instructions following immediately after the instruction BD are executed before the branch occurs. The time from reaching the instruction BD to the branch is still 5 cycles, but in this time two extra instructions are executed. In a similar way, when the instruction RETED is encountered, the return from interrupt does not occur immediately, but rather the next two single word instructions are executed. Then the return from interrupt occurs. Example 14-12. RETED POPM AR1 POPM AR0 ; ; ; ; Return from interrupt after following two instructions have been executed. 3 cycles. Temporarily save AR1. 1 cycle. Temporarily save AR0. 1 cycle.

The operation of the instruction RETED can be thought of as shown in Example 14-14. Example 14-13. POPM AR1 POPM AR0 RETE ; Temporarily save AR1. 1 cycle. ; Temporarily save AR0. 1 cycle. ; Return from interrupt. 3 cycles.

TUTORIAL

T-172

ASSEMBLY LANGUAGE TUTORIAL These instructions with delay have an unusual effect upon program execution in that instructions are not executed sequentially. The ability to execute two single word instructions before the branch and return is very useful. Instructions commonly placed here are PSHM ST0 and PSHM ST1 to save the flags. Using the instructions BD and RETED allow us to rewrite Example 14-8 in a way that saves 4 cycles. Example 14-14. timer: BD timer_isr ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Delayed branch to the subroutine at program memory address given by the label timer_isr. 2 words, 3 cycles Temporarily save AR0. 1 cycle. Temporarily save AR1. 1 cycle. Some operation on AR0. Some operation on AR1. Delayed return from interrupt. 3 cycles. Restore AR1. 1 cycle. Restore AR0. 1 cylce.

PSHM AR0 PSHM AR1

timer_isr:

MAR *AR0+ MAR *AR0RETED POPM AR1 POPM AR0

The code in Example 14-8 and Example 14-14 carry out the same operation, but the code in the second example uses 4 fewer cycles. Upgrading from the TMS320C25 and TMS320C2xx/5x to the TMS320C54x. There are considerable differences in syntax between the TMS320C54x and earlier devices. These are shown in Table 14-2. Table 14-2: Comparison of Instructions
Description TMS320C25 Instruction RET Not supported IN OUT Not supported Not supported Not Supported Not supported TMS320C2xx and TMS320C5x Instruction RET Not supported IN OUT Not supported INTR K INTR K NMI TMS320C54x Equivalent Instruction RETE RETED PORTR PORTW BD RESET INTR K INTR 1

Return from interrupt Return from interrupt with delay. Input from Port Output to Port Branch with delay Force reset from software Call interrupt service routine Non-maskable interrupt

TUTORIAL

T-173

ASSEMBLY LANGUAGE TUTORIAL

K is a constant between 0 and 31. The TMS320C54x has a different interrupt vector table structure to that of earlier devices. The TMS320C25/2xx/5x only allocates two words per interrupt, whereas the TMS320C54x uses four words. This means that on earlier devices it is necessary to vector to the interrupt service routine. However, with the four words that are available on the TMS320C54x, it is possible to execute three single-word instructions and a RETE. The interrupt vector numbers have also changed between the devices. For example, on the TMS320C2xx, interrupt 24 was non-maskable interrupt NMI. On the TMS320C54x it is interrupt 1.

Questions
1. 2. 3. What is the difference between a maskable interrupt and a non-maskable interrupt? What is meant by the term interrupt service routine? The instruction RETE means: a) Return from exception b) Return and execute c) Return from interrupt and enable interrupts d) Return except when condition is TRUE e) Return except when condition is FALSE? Why is it important that every interrupt service routine contains a return from interrupt instruction such as RETE? What is the difference between the instructions RET and RETE? What is a port? How do we call a particular interrupt service routine during the course of a program? How do we force a reset using a software instruction? What is the difference between a cold reset and a warm reset. Why do we use the instruction B rather than the instruction CALL to reach an interrupt service route? Why might we need to save flags before executing an interrupt service routine? What does the term delay mean when applied to a branch instruction? What are the differences between the instructions B and BD? What are the differences between the instructions RETE and RETED? Which of the following instructions offers the faster operation: a) RETE b) RETED?

4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15.

TUTORIAL

T-174

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 15: Answers Tutorial 1:
1. 2. 3. 4.

Simple Instruction to Move Data

5. 6.

7.

8. 9.

10.

11.

12.

The term destination means where we copy data to. This term is usually used with accumulator A and accumulator B. The term immediate addressing means that the value following immediately after the symbol # is taken to be a constant. The word hexadecimal is often abbreviated to h. a) LD #FFh, A ; Incorrect. Should be LD #0FFh, A b) LD #70h, B ; Correct. c) LD #0E0h, A ; Correct. d) LD #66h, C ; Incorrect. There is no accumulator C. e) LD #222h ; Incorrect. Missing destination. f) LD #0E3, B ; Incorrect. Missing letter h. To store the value 100h in auxiliary register AR6 we write: STM #100h, AR6 a) LD #7Fh, B ; Accumulator B now contains 0000007Fh b) LD 7Fh, B ; Accumulator B now contains contents of ; data memory address 7Fh. c) LD @7FH, B ; Accumulator B now contains contents ; of data memory address 7Fh. a) LD @123h, A ; Incorrect. Operand must be in the ; range 0h to 7Fh. b) LD -23, B ; Incorrect. Negative operand. c) LD @28h, A ; Correct. d) LD 127, B ; Correct. e) LD @128, B ; Incorrect. Operand greater than 127. f) LD 14h ; Incorrect. Missing operand. The term direct memory address can be abbreviated to dma. On page 0, memory locations 0 to 5Fh are reserved for memory mapped registers, including auxiliary register AR0 to AR7. This means these registers are not available for general purpose data storage. To write #1000h to data memory address 230h we write: LD #4, DP ; Gain access to data memory ; addresses 200h to 27Fh. ST #1000h, 30h ; Store 1000h at data memory address ; 200 + 30h = 230h. To store the contents of an accumulator in data memory we use the instructions STL and STH. We can use either order of instructions, but for consistency in a project we should maintain the same order throughout. The instruction LD (load) uses either accumulator A or accumulator B as the destination. The instruction ST (store) uses a data memory address as the destination.

TUTORIAL

T-175

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 2:
1.

Indirect Addressing

2.

3.

4. 5. 6. 7. 8. 9. 10.

11.

a) STM #400h, AR4 ; Correct. b) STM #20, AR5; Should be STM #20, AR5. c) STM #200h, AR1+ ; Should be STM #200h, AR1. d) STM #600, AR7 ; Correct. e) STM #2, -AR5 ; Should be STM #2, AR5. f) STM 30h, *AR6+ ; Should be STM #20h, AR6. a) LD *AR2, A+ ; Incorrect. Should be LD *AR2, A b) LD *AR6-, B- ; Incorrect. Should be LD *AR6-, B c) LD * ; Incorrect. Missing operands. d) LD *AR4-, B ; Correct. e) LD *AR5+, A ; Correct f) LD *AR3, A ; Correct Indirect addressing has the following advantages over direct addressing: a) The full 16-bit address is specified so the DP does not have to be used. b) The auxiliary register used as the pointer can be incremented or decremented as part of the instruction. This is useful for working on blocks of data. To increment auxiliary register AR4 as part of the instruction LD we write: LD *AR4+, A ; Load accumulator A and increment AR4. To decrement auxiliary register AR3 as part of the instruction LD we write: LD *AR3-, A ; Load accumulator A and decrement AR3. The instruction LD *AR2+, A is used to load accumulator A with the contents of the data memory address pointed to by auxiliary register AR2 then increment AR2. The instruction STL A, *AR2 stores the low word of accumulator A at the data memory address pointed to by auxiliary register AR2. The instruction STH B, *AR3 stores the high word of accumulator B at the data memory address pointed to by auxiliary register AR3. When the TMS320C54x is made to operate in a way compatible with earlier devices (CMPT = 1), the current auxiliary register determines which auxiliary register is used for indirect addressing. When the TMS320C54x is made to operate in a way compatible with earlier devices (CMPT = 1), the auxiliary register pointer (ARP) is the register that contains the number of the auxiliary register to be used for indirect addressing. To make auxiliary register AR6 the current auxiliary register we write: LD #6, ARP.

TUTORIAL

T-176

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 3:
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12.

Logical Operations

The logical AND is used to clear to zero any bits in which we are not interested. This operation is often used before a test. The result of performing a logical AND of FFFF3333h with FFh will result in the value 00000033h in accumulator A. The result of performing a logical OR of 55555555h with FFFFh will result in the value 5555FFFFh in accumulator B. The result of performing a logical XOR of 77770000h with 1111h will result in the value 77771111h in accumulator A. The result of performing a logical XOR of 000055AAh with FFFFh will result in the value 0000AA55h in accumulator B. To use the logical XOR as an inverter, the corresponding bits in the immediate value are set to 1. The logical XOR of two values performs a bitwise addition of them. For example, 01234567h XOR 89ABCDEFh = 88888888h. The instruction XOR only affects the low word of an accumulator and therefore cannot be used to invert the full 32 bits. If accumulator B contains the value 00007777h, then after the instruction CMPL B it then contains FFFF8888h. To perform a logical AND of data memory address 61h with the constant 3FFFh we use the instruction ANDM #3FFFh, @61h. To perform a logical OR of data memory address 72h with the constant FF00h using a single instruction we use the instruction ORM #0FF00h, @72h. To invert the contents of data memory address 64h we use the instruction XORM #0FFFFh, @64h.

TUTORIAL

T-177

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 4:
1. 2. 3. 4. 5. 6. 7. 8. 9. 10.

Signed Numbers and Shifting Data

11. 12. 13. 14.

15.

16.

An unsigned number can be zero or positive only. A signed number can take negative, zero or positive values. The instruction SSBX SXM turns on sign-extension mode. The instruction RSBX SXM turns off sign-extension mode. The instruction SFTL means shift accumulator logically. The instruction SFTL 1, A shifts the accumulator one place to the left. This is equivalent to multiplying by 2. The instruction SFTL -1, A shifts the accumulator one place to the right. This is equivalent to dividing by 2. To shift the contents of accumulator B two places to the right we write: SFTL -2, B. To shift the high word of accumulator A into the low word of accumulator A we use the instruction SFTL -16, A. This produces a shift of 16 places to the right. Sign-extension mode does not affect the instruction SFTL (shift accumulator logically). When sign-extension mode is turned on, and the instruction SFTA is executed, the value of the sign bit (the most significant or left-most bit) is maintained. When signed extension mode is turned off, a simple shift is performed. The logical shift SFTL is used for general purpose shift while the arithmetic shift SFLA must be used with signed numbers to preserve the sign bit. When the instruction LD is used with a shift, the constant is shifted then loaded into the accumulator. The default shift for the instruction LD is zero. Therefore the instructions LD #5555h, A and LD #5555h, 0, A are equivalent. To shift the value 00007777h from the low word of accumulator B into the high word of accumulator B, we use the instruction LD #7777h, 16, B. This produces a shift of 16 places to the left. To load the low word of an accumulator with a constant there are two instructions we can use: a) OR #5555h, A b) XOR #5555h, A To load accumulator B with the constant FEDC9876h we write: LD #0FEDCh, 16, B ; Load high word of accumulator B. OR #9876h, B ; Load low word only.

TUTORIAL

T-178

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 5:
1. 2. 3. 4. 5. 6. 7. 8.

Arithmetic Operations

9. 10.

11. 12.

To add the constant 7Fh to accumulator A we write ADD #7Fh, A. With sign-extension mode turned off, if the constant FFFFh is added to 1h in accumulator A, the result will be 10000h. The instruction SUB #100h, A subtracts the constant 100h from accumulator A. When sign-extension mode is turned off, if the constant 1 is subtracted from accumulator B, accumulator B then contains FFFEh. The instruction ADDM #10, *AR4 adds the immediate value 10 to the contents of the data memory address pointed to by auxiliary register AR4. The letter M in the instruction STM means store in memory-mapped register. The letter M in the instruction ADDM means (data) memory. To add the constant 1 to the contents of data memory address 400h without using an accumulator we write: LD #8, DP then ADDM #1, @0h. To subtract the constant 1 from the contents of a data memory address we add the value -1 (FFFFh) e.g. ADDM #0FFFFh, *AR2. However, we must first turn on sign-extension mode using the instruction SSBX SXM. The instruction MAR means Modify Auxiliary Register. The symbol * is used to indicate that the auxiliary register is used as part of the operation: a) With the instruction LD, the symbol * is used to indicate indirect addressing. b) With the instruction MAR, the symbol * is used to indicate that the register itself is being modified. The instruction MAR is used to alter the contents of an auxiliary register by incrementing or decrementing it. The instruction STM is used to store a value in an auxiliary register. The instruction CMPL is a logical operation used to convert all the bits that are 0 to 1 and vice versa. The instruction NEG is used with signed arithmetic to convert a positive number to a negative number.

TUTORIAL

T-179

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 6:
1.

Branch Instructions

2. 3.

4. 5.

6.

7.

8.

9.

An unconditional branch causes program execution to always be redirected to the program memory address given by the label provided as the operand. An unconditional branch causes program execution to be redirected only if the result of the associated test is TRUE. The symbol $ indicates the current program memory address. For example, the instruction BRA $ causes an infinite loop because program execution is always redirected to itself. The condition AEQ will evaluate to TRUE when the contents of accumulator A are equal to zero. When the contents of accumulator are non-zero, then the condition AEQ will equate to FALSE. Hence the instruction BC loop4,AEQ will branch to the label loop4 when the value in accumulator A is equal to zero. The instruction XOR only affects the low word of an accumulator. Therefore, to test the full 32 bits of an accumulator, the instruction SUB must be used. To use direct addressing to test if the value stored at data memory address 75h is equal to 21 we would write: LD #0, DP LD 75h, A XOR #21, A BC equal_to_21, AEQ To use indirect addressing to test if the value stored at data memory address 76h is not equal to 5 we would write: STM #76, AR3 LD *AR3, A XOR #5, A BC not_equal_to_5, ANEQ To use direct addressing to test if the contents of data memory address 77h are greater than 29, and if so load accumulator A with zero we can write: LD #0, DP LD #77h, AR0 SUB #29, A BC $+2, ALEQ ; Skip next instruction when FALSE. LD #0, A To use indirect addressing to test if the contents of data memory address 78h are less than 300h, and if so load accumulator B with 33h we can write: STM #78h, AR5 LD *AR5, B SUB #300h, B BC $+2, BGEQ ; Skip next instruction when FALSE. LD #33h, B To use direct addressing to test if the value stored at data memory address 79h is greater or equal to 1000h we can write: LD #0, DP LD 79h, A SUB #1000h, A BC greater_than_or_equal_to_1000, AGEQ

TUTORIAL

T-180

ASSEMBLY LANGUAGE TUTORIAL 10. To use indirect addressing to test if the value stored at data memory address 80h is less than or equal to 1Fh we can write: LD #80h, AR6 LD *AR6, B SUB #1Fh, B BC less_than_or_equal_to_1F, BLEQ Using accumulator A: a) The opposite condition to ANEQ is AEQ b) The opposite condition to AEQ is ANEQ c) The opposite condition to AGT is ALEQ d) The opposite condition to ALT is AGEQ e) The opposite condition to AGEQ is ALT f) The opposite condition to ALEQ is AGT By branching on the opposite condition to that being tested, one instruction less is used, and therefore saves code.

11.

12.

TUTORIAL

T-181

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 7:
1.

Loop Counters

2.

3.

4.

5. 6. 7.

8. 9.

10.

11.

The do-while loop is more code-efficient to implement than the for loop. This is because when using the do-while loop, the counter can be decremented and tested using a single BANZ instruction. loop1: STL A, *AR2+ ; Store low word of the ; accumulator A in the dmad ; pointed to by AR2. ; Increment AR2. BANZ loop1,*AR3; Decrement loop counter AR3. ; If non-zero, branch to the ; label loop1, otherwise ; terminate. If a control loop is using an increment or decrement value other than 1, then the auxiliary register AR0 is used to contain this value. In such cases, AR0 cannot be used as the loop counter. The code in the following loop will execute 20 times. STM #19, AR5 loop3: STL B, *AR6BANZ loop3, *AR6The instruction BANZ decrements the auxiliary register after the test and therefore the value with which the auxiliary register is initialized must be one less than the required number of loopss. When the line after the instruction BANZ $, *AR3- is reached, the auxiliary register AR3 will contain -1 (FFFFh) i.e. decremented after reaching zero. The instruction LD *AR6+0, A means that accumulator A is loaded with the contents of the data memory address pointed to by AR5 and afterwards the value in AR0 is added to AR5. The instruction STL B, *AR2-0 means that the low word of accumulator B is stored at the data memory address pointed to by AR2 and afterwards AR2 is decremented by the value in AR0. The instruction NOP carries out no function but requires execution time. It is often used to slow down operations e.g. for a delay loop or to give a slow external peripheral time to respond. The following instruction does not repeat because the instruction BANZ in non-repeatable: RPT 9 BANZ $, *AR5The following instruction sequence executes the instruction STL A, AR2+ then repeats it 100 times. RPT 100 STL A, *AR2+ In order to execute the instruction STL A, *AR2+ a total of 100 times, the following instruction sequence is required: RPT 99 STL A, *AR2+ The instruction RPTZ B, 100 clears accumulator B to zero once only then carries out 100 repeats.

TUTORIAL

T-182

ASSEMBLY LANGUAGE TUTORIAL 12. The instruction RPT can only be used to repeat the instruction following; it cannot be used to repeat a block of instructions. On the other hand, the instruction BANZ is used to repeat a block of instructions.

TUTORIAL

T-183

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 8:
1. 2.

Multiplications

3. 4.

5.

6.

7.

8.

9. 10.

The temporary register is often abbreviated to the T register. a) STM #45h, T ; Correct. b) LD #22h, T ; Should be STM #22h, T. c) LD 5h, T ; Correct. Direct addressing. d) LD *AR4, T ; Correct. Indirect addressing. e) LD *AR1+, T ; Incorrect. Modification not allowed. To clear the T register to zero we can write: STM #0, T The instruction MPY is used to multiply two signed numbers together. The largest positive number that can be multiplied is 7FFFh. The instruction MPYU is used to multiply two unsigned numbers when either of the values is in excess of 7FFFh. a) MPYU #100, A ; Immediate addressing not supported. b) MPYU *AR3, B ; Correct c) MPYU 70h, A ; Correct d) MPYU *AR6-, A ; Correct To multiply two 16-bit variables together we must first load one of them into the T register e.g.: LD *AR2, T ; Load first variable into T register. MPY *AR3,B ; Multiply second variable by T register. When multiplying a series of values, it is more code-efficient to use indirect addressing to increment the pointer to the value in data memory e.g.: MPY *AR2+, #100h, A ; Point to next dmad. MPY *AR2, #200h, B. The following two instructions: LT 0h, T MPY #10h, B can be reduced to: MPY 0h, #10h, B The instruction MPYA (multiply by accumulator A) can only be used with accumulator A and therefore cannot be used with accumulator B. The instruction MPYA multiplies the high word of accumulator A by the T register. When loading a value into accumulator A, a shift of 16 to the left must be used e.g.: LD *AR2, T ; Load the T register. LD #300h, 16, A ; Load high word of accumulator A. MPYA B ; Multiply high word of accumulator ; A by the T register. Put the ; product in accumulator B.

TUTORIAL

T-184

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 9:
1. 2.

Multiplications with Accumulation

3. 4.

5. 6. 7.

8. 9. 10.

11.

11.

Accumulation means that the result of an operation is added to the on-going result of the previous operations. When using indirect addressing, as part of an instruction we can increment or decrement the auxiliary register used as the pointer to a data memory address. We can also put the data handling instructions into a control loop. To do the same using direct addressing, each individual instruction would have to be written out in full. Overflow means that the result of an operation has exceeded either the upper or lower limit allowed by that operation. Accumulation can cause an accumulator to overflow. When overflow occurs in an accumulator, the contents of the accumulator become unreliable. A positive number can overflow to a negative number and a negative number can overflow in the opposite direction to generate a positive number. To prevent overflow during accumulation we turn on overflow mode. The instruction MAC is more code-efficient than the instructions MPY and ADD. Furthermore it can be used with a repeat instruction such as RPT, where the MPY and ADD must use BANZ. The term saturation when applied to multiplication means that the product is limited to an upper thresholds of 7FFFFFFFh and a lower threshold of 80000000h. This prevents a multiplication overflowing to turn a positive number into a negative number or a overflowing in the opposite direction to turn a negative number into a positive number. To turn on overflow mode we use the instruction SSBX OVM. To turn off overflow mode we use the instruction RSBX OVM. The following two instructions MPY *AR3+,A ADD A,B can be condensed into MAC *AR3+, B The following three instructions: LD *AR3, T MPY *AR4, A ADD A, B can be condensed into: MAC *AR3, *AR4, B The instruction MACA (Multiply by accumulator A and accumulate) uses the high word of accumulator A. Correct usage is as follows: RSBX SXM LD #200h, 16, A ; Load accumulator A with shift. MACA 80h, B ; Multiply and accumulate.

TUTORIAL

T-185

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 10:
1.

Using Data Stored in ROM

2. 3.

4.

5.

6.

7.

8.

9.

10.

The TMS320C54x has more program memory available than data memory. To conserve data memory, read-only variables such as filter constants can be stored in program memory and worked on directly. The term program memory address can be abbreviated to pmad. When copying a block of data from program memory to data memory using the instruction MVPD, indirect addressing allows the pointer to data memory to be incremented or decremented. Direct addressing does not allow successive data memory addresses to be worked upon. To copy a word from program memory address 400h to data memory address 100h we write: MVPD 400h, 100h A look-up table offers a convenient method of deriving a non-linear output from an input. It can be used where the mathematical relationship between input and output is complicated e.g. sine law. The instruction MVPD is designed to copy a block of data from program memory to data memory. On the other hand, the instruction READA is designed to copy an individual word from program memory to data memory, for example, as part of a look-up table. The assembler cannot differentiate between the values in a look-up table and executable code. Normally, the look-up table would be put in a block of its own, outside the executable code. Should it be necessary to put the look-up table within the executable code, then a B instruction should be put at the beginning of the look-up table so that it is skipped during program execution. When using the instruction MACP, we only need to specify the starting address in program memory. The instruction MACP automatically increments the pointer to program memory so that successive locations are worked upon. The instruction RPTZ means clear to zero the accumulator specified by the first operand and then repeat the following instruction the number of times specified by the second operand e.g.: RPTZ A, 99 ; Clear accumulator A then do 99 repeats. When carrying out a series of multiplications with accumulation using the instruction MACP, we need to start with zero as our accumulated total. The instruction RPTZ clears the specified accumulator to zero before doing the repeats, while the instruction RPT does not.

TUTORIAL

T-186

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 11:
1. 2. 3.

Bit Operations

4.

5. 6. 7.

8.

9.

10. 11.

12.

13.

14.

A Boolean variable takes one of two values that can be represented by 0 or 1. Examples of Boolean variables are WET/DRY and HOT/COLD. Since each Boolean variable can be expressed as 0 or 1, a bit can be used to store each. Therefore in a word 16 Boolean variables can be stored. To test a particular bit of accumulator A or accumulator B, a logical AND must be carried out on the accumulator and then a conditional branch. e.g.: AND #0100h, A ; Clear to zero all bits except bit 8. BC bit_not_set, AEQ ; Branch if bit 8 not set to 1. To copy a word from data memory to an accumulator and then to test it is inefficient use of code. It also means that the contents of the accumulator may have to be saved before doing the test on the data word. The instruction BIT 60h, 0 is used to test the most significant (right-most) bit of the word at data memory address 60h. The instruction BIT does not actually carry out a test. It copies the specified bit to the bit TC (test / control flag) which can then be tested using a conditional branch BC. The bit TC (test / control flag in ST0) can be used with the instruction BC: BC label1, TC ; If TC = 1 then branch to label1. BC label2, NTC ; If TC = 0 then branch to label2. When using the instruction BIT, the bits are numbered backwards to the standard convention. Two ways to avoid errors are: BIT *AR4, (15-0) ; Copy left-most bit of dmad to TC. BIT_3 .set (15-30; Create a symbol for bit 3. The instruction BIT copies the bit specified by the operand to the bit TC (test / control flag). The instruction BITT copies the bit specified by the T register to the bit TC ( test / control flag). The instruction BIT can only be used as part of a test for a single bit. The instruction BITF can be used to test if multiple bits are non-zero. a) BITF *AR3, #22h ; Correct b) BITF #0Fh, AR1 ; Incorrect. Operands reversed. c) BITF 14h, #0FF00h ; Correct. d) BITF #55h, 16h ; Incorrect. Operands reversed. e) BITF AR4, #16h ; Should be BITF *AR4, #16h. When overflow occurs the result of an operation becomes unreliable. For example, a positive number can become a negative number while a negative number can become a positive number. After a multiplication, the appropriate overflow flag can be tested e.g.: MPY *AR2 , A ; Multiplication. BC no_overflow, ANOV ; Test if accumulator A has ; overflowed. The value in auxiliary register AR5 can be compared with the value in auxiliary register AR0 as follows: CMPR EQ, AR5 ; Compare AR5 with AR0.

TUTORIAL

T-187

ASSEMBLY LANGUAGE TUTORIAL 15. The instruction CMPR is often followed by a test of the bit TC (test /control flag): CMPR NEQ, AR3 ; Compare AR3 and AR0. BC different ,TC ; Test bit TC (test /control flag). The instructions BITT and BITF both support direct addressing. The instruction BIT does not.

16.

Tutorial 12:
1. 2. 3. 4.

Stack Operations

5.

6.

7.

8.

The stack is an area of data memory used for temporary storage of variables and in particular for subroutine calls. The term push means to temporarily store a variable from either a data memory address or a memory-mapped register on the stack. The stack pointer is decremented as part of the process. The term pop means to restore a value from the top of the stack to either a data memory address or a memory-mapped register. The stack pointer is incremented as part of the process. For correct operation, the operand must be a data memory address. a) PSHD 2h ; Correct. b) PSHD AR0 ; Cannot use a memory-mapped register. c) PSHD AR0+ ; Incorrect. Missing *. d) PSHD *AR1- ; Correct. e) PSHD *AR2+ ; Correct. f) PSHD SP ; Cannot use a memory-mapped register. For correct operation, the operand must be a data memory address. a) POPD 34h ; Correct. b) POPD AR1 ; Cannot use a memory-mapped register. c) POPD AR4+ ; Incorrect. Missing *. d) POPD AR6- ; Incorrect. Missing *. e) POPD *AR2 ; Correct. f) POPD *AR3+ ; Correct. g) POPD *AR7 ; Correct. For correct operation, the operand must be a memory-mapped register. a) PSHM *AR1 ; Cannot use data memory address. b) PSHM AR4 ; Correct. c) PSHM AR3+ ; Incorrect. Cannot use + operator. d) PSHM *AR2- ; Cannot use data memory address. e) PSHM SP ; Correct. f) PSHM ST0 ; Correct. For correct operation, the operand must be a memory-mapped register. a) POPM 22h ; Cannot use a data memory address. b) POPM AR4 ; Correct. c) POPM AR0- ; Incorrect. Cannot use - operator. d) POPM *AR1+ ; Cannot use a data memory address. e) POPM SP ; Correct. f) POPM ST0 ; Correct. g) POPM ST1 ; Correct. The top of stack refers to the last value pushed onto the stack.

TUTORIAL

T-188

ASSEMBLY LANGUAGE TUTORIAL 9. 10. When push and pop instructions are used in pairs, the stack pointer is restored to its original value. To swap the values in data memory addresses 100h and 101h without using an accumulator we can write: LD #2, DP ; Page 2. Gain access to data memory ; addresses 100h to 101h. PSHD 0h ; Push first value. PSHD 1h ; Push second value. POPD 0h ; Pop first value. POPD 1h ; Pop second value. To copy the contents of a memory-mapped register to a data memory address we can use the instructions PSHM and POPD e.g.: PSHM AR0 ; Push AR0 onto the stack. POPD 2h ; Pop top of stack to data memory address. The TMS320C54x does not have an instruction to push the contents of an accumulator onto the stack. To do so, the contents of the accumulator would have to be stored in data memory and then these values pushed: STL A, 2h ; Save low word of accumulator A. STH A, 3h ; Save high word of accumulator A. PSHD 2h ; Push low word of accumulator A. PSHD 3h ; Push high word of accumulator A.

11.

12.

TUTORIAL

T-189

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 13:
1.

Subroutine Calls

2.

3.

4.

5.

6. 7.

8.

9. 10.

A subroutine call uses the instruction CALL to redirect program execution to a piece of code at the label supplied as the operand. When the code is complete, program execution resumes at the instruction following the instruction CALL. For correct operation the instruction CALL must be used with a label. a) CALL ; Incorrect. Missing operand. b) CALL A ; Incorrect. Operand must be a label. c) CALL 3000h ; Incorrect. Not a label. d) CALL *AR2 ; Cannot be a data memory address. e) CALL *AR0- ; Cannot be a data memory address. f) CALL subroutine4 ; Correct. For correct operation, the instruction RET takes no operand. a) RET ; Correct. b) RET A ; Incorrect. No operand required. c) RET 16h ; Incorrect. No operand required. d) RET #49 ; Incorrect. No operand required. e) RET AR1 ; Incorrect. No operand required. f) RET *AR1 ; Incorrect. No operand required. Should the instruction RET be omitted from a subroutine, program execution will not be returned back to the calling program, but will instead incorrectly continue at the instruction following the subroutine. The term return address is the address pushed onto the stack when a subroutine is called. The return address contains the address of the next instruction to be executed when the subroutine has completed. The instruction MVMM means move memory-mapped register to memory-mapped register. For correct operation with the instruction MVMM, each of the operands must be either the SP or AR0, AR1, AR2, AR3, AR4, AR5, AR6 or AR7. a) MVMM AR0, SP ; Correct. b) MVMM *AR0, SP ; Cannot be a data memory address. c) MVMM *AR0, *SP ; Both operands incorrect. d) MVMM AR4, *SP ; Incorrect second operand. e) MVMM ST0, AR2 ; Incorrect first operand. f) MVMM SP, AR3 ; Correct. g) MVMM AR1, *AR2 ; Cannot be a data memory address. When parameters are passed on the stack, the stack pointer is decremented each time a word is pushed. The return address is also pushed. To work on each parameter, the stack pointer must be incremented so it no longer points to the return address. The instruction FRAME +2 increments the stack pointer by 2. A stack frame is a local stack set up as part of a subroutine for the storage of variables used only by that subroutine. When the subroutine is complete, the words allocated for the stack frame are released for general usage.

TUTORIAL

T-190

ASSEMBLY LANGUAGE TUTORIAL 11. To create room on the stack for local variables, the instruction FRAME is used with the number of words for local variables being used as the operand. For example, to allocate space for 4 words, the instruction FRAME 4 would be used. The instruction CC means call conditionally. When using the conditional call instruction CC, the test must follow the label: a) CC subroutine2, BEQ ; Correct b) CC BEQ , subroutine2 ; Operands reversed. The instruction CALA means call address in accumulator A. The subroutine whose address is to be found in accumulator A is executed.

12. 13.

14.

TUTORIAL

T-191

ASSEMBLY LANGUAGE TUTORIAL

Tutorial 14:
1. 2. 3. 4.

Non-Maskable Interrupts

5. 6. 7.

8. 9.

10.

11.

12.

13. 14.

15.

A maskable interrupt can be turned off globally using software. A non-maskable interrupt cannot be turned off globally in software. The interrupt service routine is the name given to the subroutine executed when an interrupt occurs. Each interrupt has its own interrupt service routine. The instruction RETE means return from interrupt and enable interrupts. If an interrupt service routine does not contain a return from interrupt instruction such as RETE, then program execution will not be returned to the main program but will incorrectly continue at the next interrupt service routine. The instruction RET is used to return from a subroutine called by the program. The instruction RETE is used to return from an interrupt. It also turns on the interrupts again. A port is an interface to the outside world. The TMS320C54x has a serial port and can also be configured to have parallel ports (rather like a personal computer). We would never call an interrupt service routine in the same way as a standard subroutine. To call a particular interrupt service routine we would use the instruction INTR K where K is the number of the interrupt service routine. In order to force a reset from software, the instruction RESET is used. A cold reset occurs at power up, and restarts the TMS320C54x right at the very beginning, reinitialising all the variables. A warm reset may be initiated in response to an external event and would go to the beginning of the program but would leave variables in their state before the interrupt occurred. Using the instruction B (branch) rather than the instruction CALL in an interrupt service routine saves the use of an instruction RET which means the interrupt service routine runs faster. Operations within the interrupt service routine such as addition, subtraction and shifts affect flags. In order that all the flags are in the same condition as before the interrupt occurred, then these flags (mostly in ST0 and ST1) should be saved before entering the interrupt service routine and restored afterwards. The term delay when applied to a branch instruction does not mean that a time delay is inserted. Instead it means that there is a delay before the instruction is executed, in which time other instructions can be processed. The instruction B means branch unconditionally. The instruction BD means branch unconditionally with delay and executes faster than the instruction B. The instruction RETE and RETED both mean return from interrupt and enable interrupts. However, the instruction RETED also has a delayed action and allows other instructions to be executed before the return from interrupt occurs. The instruction RETED requires less execution cycles and hence provides faster operation than the instruction RETE.

TUTORIAL

T-192