8.6 Addressing modes
Usually the programs for the dsPIC devices are written in some of the higher programming languages. However, sometimes it is required that a section of the code is written in the assembler. This need is particularly emphasized for the processes involving complex processing. The optimization is possible in some of the higher programming languages, but it is most efficient when using the assembler, assuming that the architecture of the dsPIC30F devices is known. In these cases the core of the algorithm (most demanding part of the program) is programmed using the assembler. The basic problem when using the assembler instruction set is data memory access, which could be done in several ways. Each of these has its merits and defficiencies. It is thus very important to know the addressing modes and use them correctly while writing a program.
Each assembler instruction can be divided into two parts. The first part of an instruction is the operation which is carried out (like
mov,
add, etc.) and the second is the operand(s). The operand is a value undergoing the operation. E.g. the instruction
DEC W0, W1 carries out the operation of decrementing, defined by DEC, and the operands are the values in the registers
W0 and
W1.
For the family of dsPIC30F devices there are four addressing modes:
- direct memory addressing,
- direct register addressing,
- indirect register addressing, and immediate adrressing.
8.6.1 Direct memory addressing
Direct memory addressing is the mode where the operand is at the memory location specified by the instruction. This means that the operation is accompanied by the address in the memory where the value undergoing the operation is located. An example of this mode can be when the task is to take the value located in an addess in the memory and transfer it to a general purpose register (W0...W15).
Example:
MOV 0x0900, W0.
This defines the operation of moving MOV (MOVe) while the operands are the value at the adrress 0x0900 in the data memory and the value kept in the register W0. The contents of the data memory at location 0x0900 and the register W0 before and after this instruction are shown in Fig. 8-5.
Fig. 8-5 Contents of the data memory at location 0x0900 and the register W0 before and after the instruction
When using direct memory addressing, there are restrictions depending on the type of the operation used.
The majority of operations can use direct memory addressing only for the lowest 8KB of the memory, while some (like MOV) can use all 64KB. The operation MOV therefore can access any memory location and read the value from it or write in a new value.
The second restriction is that during execution of one instruction only one memory location is accessible (the exception are DSP operations to be discussed later). In an istruction having several operands only one can be addressed by direct memory addressing.
Direct memory addressing supports 8-bit and 16-bit access to the memory. From the memory can be read or in the memory can be written in an 8-bit (byte) or a 16-bit (basic size for the dsPIC30F family) value.
Direct memory addressing is most often used when a value shoud be read from the memory or a result saved in it.
8.6.2 Direct register addressing
Direct register addresing is the addressing mode where the operand is in the register cited in the instruction. Any general purpose register (W0...W15) can be used.
Example:
ADD W0, W1, W2
The consequence of this instruction will be taking the values from the registers W0 and W1, adding them, and saving the result in the register W2. Here there are three operands: the values of W0, W1, and W2. The third operand will be overwritten by the result of adding of the first two. The values of the registers W0, W1, and W2 before and after the instruction are shown in Fig.8-6.
Fig. 8-6 Contents of the registers W0, W1, and W2 before and after the instruction
The advantage of direct register addressing over direct memory addressing is that there are no restrictions in its use. All operations support direct register addressing. For this reason direct register addressing is most often used; it is also very suitable for looping (
DOO and
LOOP).
8.6.3 Indirect register addressing
Indirect register addressing means that the operand is in the memory location whose address is written in one of the general purpose registers (W0...W15). The value in the register in this case is the pointer to the memory location where the operand is saved. Indirect register addressing is very useful because it allows the value in the register to be changed before or after the operation is carried out (within the same instruction). This allows that the data, saved sequentially in the memory (one after the other), are processed very efficiently.
Example:
MOV [W1], W3
The consequence of this instruction is that the value in the memory pointed by the register W1 will be written in the register W2. The register values and memory locations before and after the instruction are shown in Fig. 8-7.
Fig. 8-7 Values of the registers and memory locations before and after the instruction
The flow of operations is the following:
- The value in the register W1 is read.
- The value in the memory location pointed by the register W1 is read,
- The value read from the memory is written in the register W2.
As already mentioned, indirect register addressing allows processing several data, sequentially saved in the memory. This is accomplished by changing automatically the value of the register used for indirect register addressing.
There are four methods of changing the register value in the process of indirect register addressing:
- pre-increment [++W1],
- pre-decrement [--W1],
- post-increment [W1++],
- post-decrement [W1--].
In the pre-increment and pre-decement addressing modes the value of the register is changed (increased or decreased) first and then the operand address is read. In the post-increment and post-decrement addressing modes the value of the register is read first and then changed (increased or decreased). In this way the register values are increased or decreased in two steps. The reason that there are two steps is because 16-bit words are in question. Of course, it is possible to read only one byte, but it should be specified that an 8-bit operation is being executed. Otherwise, 16-bit operation is understood.
Example:
MOV $0900, W1
MOV #0, W2
REPEAT #5
MOV W2, [W1++]
This example writes zeros to six sequential locations in the memory. The instruction REPEAT has the consequence that the subsequent operation is executed the specified number of times plus one, i.e. MOV W2, [W1++] will be executed six times.
Attention!!!
All operations are 16-bit, unless specified otherwise. This means that one memory location contains two bytes. Even if the operation was 8-bit, the value of the register would be incremented by two, not by one.
The values of all relevant registers and memory locations before the execution of the above program, before the loop (after the first two instructions) and after the execution of the loop are shown in Fig. 8-8.
AT START |
BEFORE THE LOOP |
AFTER THE LOOP |
W1 0xFFFF |
W1 0x0900 |
W1 0x0900 |
W2 0xFFFF |
W2 0x0000 |
W2 0x0000 |
0x0900 0xFFFF |
0x0900 0xFFFF |
0x0900 0x0000 |
0x0902 0xFFFF |
0x0902 0xFFFF |
0x0902 0x0000 |
0x0904 0xFFFF |
0x0904 0xFFFF |
0x0904 0x0000 |
0x0906 0xFFFF |
0x0906 0xFFFF |
0x0906 0x0000 |
0x0908 0xFFFF |
0x0908 0xFFFF |
0x0908 0x0000 |
0x090A 0xFFFF |
0x090A 0xFFFF |
0x090A 0x0000 |
Fig. 8-8 The values of all relevant registers and memory locations before, during and after the execution of the program
There is another mode of indirect register addressing. This is the shift register mode. It is very useful for accessing members of an array.
Example:
MOV [W1+W2], W3
The address of the first operand is calculated by adding the values in the registers W1 and W2. The obtained value is the address in the memory where the operand is saved (the value which should be written into the register W3). The location with this address is read and the value written into the register W3. All this is performed in one instruction.
Of all described modes of indirect register addressing (without register modification, with register modification, and shift register), the majority of instructions supports only indirect rgeister addressing without modification. The instruction MOV supports all the modes. DSP instruction set supports only post-increment and post-decrement mode, but the value by which a register is modified can be selected. The increment/decrement value can, in this case, be ±2, ±4, and ±6. For more details concerning DSP instructions, see
Chapter 11.
8.6.4 Literal adrressing
Literal addressing is the addressing mode where the operand is located immediately after the operation. It is not required to read any register or memory location. The operand is carried together with the operation code as a constant to be used during the execution of the instruction.
The size of the constant depends on the operation to be executed. The constant can be signed or unsigned and can be saved with a different number of bits. It is customary to specify the limitations in one of the following ways:
- #lit4, which specifies a 4-bit unsigned constant. This means that the range of values of the constant is 0...15. The last number denotes the number of bits and Lit denotes that the constant has no sign.
- #bit4, which specifies a 4-bit unsigned constant. The difference between #lit4 and #bit4 is that #bit4 denotes bit position within a word. It is used in the instructuions setting certain bit to logic zero or logic one (BCLR, BSET, BTG,...).
- #Slit4, which specifies a signed 4-bit constant. The range of #Slit4 is from –8 to +7.
Table 8-9 gives a list of all possible formats of the constant for literal addressing, together with the instructions where the constant is used. These are the assembler instructions which are very seldom used if the programming is performed in a higher level language, but it is of considerable importance to know the limitations in order to use correctly certain instructions of the higher level languages. E.g. shift operand (instructions
ASR, LSR, SL) can be done within the range 0-16, which is logical since the 16-bit data are involved.
OPERAND |
INSTRUCTION WHERE IT IS USED |
RANGE |
#bit4 |
BCLR, BSET, BTG, BTSC, BTSS, BTST, BTST.C, BTST.Z, BTSTS, BTSTSS.C, BTSTS.Z |
0 ... 15 |
#lit1 |
PWRSAV |
0 ... 1 |
#lit4 |
ASR, LSR, SL |
0 ... 15 |
#lit5 |
ADD, ADDC, AND, CP, CPB, IOR, MUL.SU, MUL.UU, SUB, SUBB, SUBBR, SUBR, XOR |
0 ... 31 |
#lit8 |
MOV.B |
0 ... 255 |
#lit10 |
ADD, ADDC, AND, CP, CPB, IOR, RETLW, SUB, SUBB, XOR |
0 ... 1023 |
#lit14 |
DISI, DO, LNK, REPEAT |
0 ... 16383 |
#lit16 |
MOV |
0 ... 65535 |
#Slit4 |
ADD, LAC, SAC, SAC.R |
-8 ... +7 |
#Slit6 |
SFTAC |
-32768 .. +32767 |
#Slit10 |
MOV |
-512 ... +512 |
Table 8-9 Immediate addressing operands
Example:
ADD W1, #4, W2
In the example the value of the register W1 is added 4 and the result is written into W2. For the second operand the literal addressing is used. From the table it can be seen that with the instruction ADD one can use constants within the range 0...31.
NOTE: Individual instructions can use different ranges. E.g. the instruction ADD has three forms for literal addressing. This should be taken care of only while writing a part of the program using the assembler. When using higher programing languages (PASCAL, C, BASIC), the compiler takes care of the form that should be alocated to a given instruction.