Chapter 6 Introduction to pointers The following represents byte addressable data memory: Data memory address increments by 2 because this memory is byte-addressable.
Chapter 6
Introduction to pointers
The following represents byte addressable data memory:
Data memory address increments by 2 because this memory is byte-addressable.
Example: Describe the contents of each address in the data memory below.
Address 0x0800 contains 0x45Address 0x0801 contains 0xA2Address 0x0802 contains 0xC1Address 0x0803 contains 0x9F
Example: Find the contents of data memory after each instruction in the following C-program:
unsigned int k, j;unsigned int* a; //pointer declarationk = 0xA245;j = 0x9FC1;a = &j;k = *a;
After the 1st and 2nd instructions, data memory contains the following:
After the 3rd instruction, data memory contains the following:
After the 4th instruction, data memory contains the following:
Example: Convert the C-program below into PIC24 Assembly code.
unsigned int k, j;unsigned int* a; //pointer declarationk = 0xA245;j = 0x9FC1;a = &j;k = *a;
The assembly program is as follows:
k: .space 2j: .space 2; w1 is used for a
mov #0xA245, w0mov w0, k ; k = 0xA245mov #0x9FC1, w0mov w0, j ; 0x9FC1 → mem (j)mov #j, w1 ; j → Reg [w1]mov [w1], w0 ; mem {Reg [w1]} = mem (j) → Reg [w0]mov w0, k ; mem (j) → mem (k)
After the 1st and 2nd instructions data memory contains the following:
After mov #0xA245, w0 data memory contains the following:
After mov w0, k data memory contains the following:
After mov #0x9FC1, w0 data memory contains the following:
After mov w0, j data memory contains the following:
After mov #j, w1 data memory contains the following:
After mov [w1], w0 data memory contains the following:
After mov w0, k data memory contains the following:
PIC24 indirect addressing modes
There are six Register Indirect (RI) addressing modes.
[wn] mem {Reg [wn]}
[++wn] wn + 2 → wn wn + 1 → wn if byte operationmem {Reg [wn]}
[- -wn] wn - 2 → wn wn - 1 → wn if byte operationmem {Reg [wn]}
[wn++] mem {Reg [wn]}wn + 2 → wn wn + 1 → wn if byte operation
[wn- -] mem {Reg [wn]}wn - 2 → wn wn - 1 → wn if byte operation
[wn + wb] mem {Reg [wn + wb]}
Syntax Meaning Additional notes
0x90B30x4E81 0x0802
0x0800
0xC7A6 0x0804
0x08020x0002 w1
w0
0xFBC4 w2
0x08020x0800
0xC7A6 0x0804
0x0804w1w0
0xC7A6 w2
0x4E81 0x08020x0800
0x0804
0x0804w1w0
0x4E81 w2
Example: Suppose the data memory and the RF has the following:
(a) Execute mov [++ w0], w2Reg [w0] = 0x0802 + 2 = 0x0804mem (0x0804) = 0xC7A6→ Reg [w2]
(b) Execute mov [w0 ++], w2mem {Reg [w0]} → Reg [w2]Reg [w0] = 0x0802 + 2 = 0x0804
0x08020x0800
0x0804
0x0801w1w0
0xFB w20x810x4E 0x81
(c) Execute mov.b [- - w0], w2Reg [w0] = 0x0802 - 1 = 0x0801mem {Reg [w0]} → Reg [w2] (only LSB)
(d) Execute mov.b [w0 - -], w2mem {Reg [w0]} → Reg [w2] (only LSB)Reg [w0] = 0x0802 - 1 = 0x0801
0x4E81 0x08020x0800
0xC7A6 0x0804
0x08020x0002 w1
w0
0xC7A6 w2
0x08020x0800
0x0804w1w0
0xFB w20xA6 0xC7 0xA6
(e) Execute mov [w0 + w1], w2mem {Reg [w0] + Reg [w1]} → Reg [w2]
(f) Execute mov.b [w0 + w1], w2mem {Reg [w0] + Reg [w1]} → Reg [w2] (only LSB)
Arrays and pointers
Pointers are commonly used with groups of elements such as arrays.To explain how arrays are placed in the data memory, examine thefollowing examples.
Example: Place the elements in the program in the data memory.
unsigned short x [4] = {0x05, 0xAB, 0x72, 0x36}; //all array elements are 8 bits
unsigned int *y; //pointer y is 16 bitsx [2] = x [1]; //copy x [1] to x [2]y = & x [2]; //copy the address of x [2] to the
pointery ++; //increment the pointer
After placing the elements of the array and the pointer in the data memory, the data memory looks like the following:
0xAB0x08000x08020x08040x0806
0x050x36 0x72
x [1], x [0]x [3], x [2]y
0xAB0x08000x08020x08040x0806
0x050x36 0xAB
x [1], x [0]x [3], x [2]y
0xAB
0x0802
0x08000x08020x08040x0806
0x050x36 0xAB
x [1], x [0]x [3], x [2]y
0xAB
0x0804
0x08000x08020x08040x0806
0x050x36 0xAB
x [1], x [0]x [3], x [2]y
After the 1st instruction is executed, the data memory becomes:
After the 2nd instruction is executed, the pointer gets the address of x [2]:
After the 3rd instruction is executed, the pointer is incremented by 2 since it is a word long:
Example: Repeat the same example with 16 bit variables.unsigned int x [4] = {0x38A0, 0xC9F5, 0xB861, 0x724D};unsigned int *y; //pointer y is 16 bitsx [2] = x [1]; //copy x [1] to x [2]y = & x [2]; //copy the address of x [2] to the pointery ++; //increment the pointer
After placing the elements of the array and the pointer in the data memory, the data memory looks like the following:
After the 1st and 2nd instructions, the data memory contains the following:
After the 3rd instruction, the data memory contains the following:
It is important to know that elements of an array is placed in the data memory in “ascending” order.Therefore, the address of the ith element = i + address of element [0].
Example: Sum of 2 arrays of 8 elements in C and Assembly programs.
In C, the program is as follows:
unsigned int a [8] = {0x0000, 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777};
unsigned int b [8] = {0x8888, 0x9999, 0xAAAA, 0xBBBB, 0xCCCC, 0xDDDD, 0xEEEE, 0xFFFF};
unsigned int c [8];unsigned short i;
for (i = 0; i < 8; i++)c [i] = a [i] + b [i];
In Assembly, the program is as follows:a .space 8*2 ;a has 8 spaces of 2 bytes eachb .space 8*2 ;b has 8 spaces of 2 bytes eachc .space 8*2 ;c has 8 spaces of 2 bytes each; write instructions to move a[0] through a[7] to memory; write instructions to move b[0] through b[7] to memory
; w1 will be used to hold the address of array amov #a, w1 ;addr a [0] → Reg [w1]
; w2 will be used to hold the address of array bmov #b, w2 ;addr b [0] → Reg [w2]
; w3 will be used to hold the address of array cmov #c, w3 ;addr c [0] → Reg [w3]
; w4 will be reserved for index Iclr.b w4 ;Reg [w4] = 0top-loop:cp.b w4, #8 ;compare Reg [w4] with #8bra GEU, end-loop ;if Reg [w4] ≥ 8 then go to end-loopmov [w1++], w0 ;mem {Reg [w1]} = mem {addr a [0]} → Reg [w0]
Reg [w1] + 2 = a + 2 → Reg [w1] = addr a [1]add w0, [w2++], [w3++] ;mem {addr a [0]} + mem {addr b [0]} → mem {addr c [0]}
mem {a} + mem {b} → mem {c}Reg [w2] + 2 = b + 2 → Reg [w2] = addr b[1]Reg [w3] + 2 = c + 2 → Reg [w3] = addr c[1]
inc w4, w4 ;Reg [w4] + 1 = 0 + 1 → Reg [w4]bra top-loopend-loop:.<rest of the code>
This example shows the range of 3 pointers in registers w1, w2 and w3, and the modifications of the array data a [8], b [8] and c [8] using pointers.
Graphically, the program progresses as follows:
After allocating data memory space for mov #a, w1, mov #b, w2, and mov #c, w3 instructions:
After clr.b instruction:
After mov [w1++], w0 instruction (1st loop):
After inc w4, w4 instruction (1st loop):
Subroutines
A subroutine is a small program within the main program. Its structure is as follows:
Example 1:
Example 2:
Data memory stack
Data stack resembles to First-In-Last-Out (FILO) memory.Suppose there are 3 data packets, A, B, C, that need to be pushed into the data stack:
Data is pushed onto the data stack using the Stack Pointer (SP), which acts as an address pointer for the stack.w15 is a special purpose register in the RF and it used to store the stack address, thus it represents SP.
(a) Push operationThe push operation steps are as follows:data A → mem [SP] = mem {Reg [w15]}SP + 2 → SP
Before pushing data A onto the stack:
After pushing data A onto the stack:
(b) Pop operationThe pop operation steps are as follows:SP - 2 → SPmem [SP] = mem {Reg [w15]} → destination
Before popping data A from the stack:
After popping data A from the stack:
(c) Push and pop instructions
push ws Reg [ws] → mem [SP] = mem {Reg [w15]}Reg [w15] + 2 → Reg [w15] (SP + 2 → SP)
push f mem [f] → mem [SP] = mem {Reg [w15]}Reg [w15] + 2 → Reg [w15] (SP + 2 → SP)
push.d ws Reg [ws] → mem [SP] = mem {Reg [w15]}SP + 2 → SPReg [ws+1] → mem [SP] where ws+1 is the next register in the RFSP + 2 → SP
pop wd SP - 2 → SPmem [SP] → Reg [wd]
pop f SP - 2 → SPmem [SP] → mem [f] where f is a memory address
pop.d wd SP - 2 → SPmem [SP] → Reg [wd]SP - 2 → SPmem [SP] → Reg [wd+1] where wd+1 is the next register in the RF
Example: Assume that the contents of data memory and RF are as follows before a push operation:
Find out the contents of data memory and RF after the following push operation:push w0 Reg [w0] → mem [SP] = mem {Reg [w15]} then SP + 2 → SP
Example: Assume that the contents of data memory and RF are as follows before a pop operation:
Find out the contents of data memory and RF after the following pop operation:pop 0x0800 SP - 2 → SP then mem [SP] → mem [0x0800]
Call and Return instructions and the use of data stack
The data memory stack can be used to store the “return” address following a subroutine call. This instruction stores the return address on the data stack before branching to the “target” address.
call <target>
This instruction returns the program to its original location following the subroutine call.
return
The following are the complete list of call and return instructions:
call <label> PC + 4 (return address) → mem [SP]SP + 4 → SP<label> → PC where SP = Reg [w15]}
call wn PC + 4 → mem [SP]SP + 4 → SPReg [wn] → PC where SP = Reg [w15]}
return SP - 4 → SP mem [SP] → PC where SP = Reg [w15]}
Example: Determine the contents of the data memory and the RF at each step of the following program:
Assume that SP is initialized at the address 0x0900.Before call sub-a:
After call sub-a:PC + 4 = 0x0240 + 4 = 0x0244 → mem [SP] = mem [0x0900]SP + 4 = 0x0900 + 4 = 0x0904 → SP = Reg [w15]sub-a = 0x0268 → PCThus:
After call sub-b:PC + 4 = 0x026A + 4 = 0x026E → mem [SP] = mem [0x0904]SP + 4 = 0x0904 + 4 = 0x0908 → SP = Reg [w15]sub-b = 0x033A → PCThus:
After call sub-c:PC + 4 = 0x033C + 4 = 0x0340 → mem [SP] = mem [0x0908]SP + 4 = 0x0908 + 4 = 0x090C → SP = Reg [w15]Thus:
After the 1st return:SP - 4 = 0x090C - 4 = 0x0908 → SP = Reg [w15]mem [SP] = mem [0x0908] = 0x0340 → PC
After the 2nd return:SP - 4 = 0x0908 - 4 = 0x0904 → SP = Reg [w15]mem [SP] = mem [0x0904] = 0x026E → PC
After the 3rd and final return:SP - 4 = 0x0904 - 4 = 0x0900 → SP = Reg [w15]mem [SP] = mem [0x0900] = 0x0244 → PC