# Tutorials: Microcontroller systems (MICSY)

last updated: 2021-05-18

### Quick links to the subchapters

When loading and storing data, there are several addressing methods available in Assembly language. The AVR microcontroller support 13 different address modes for accessing the `Flash` and `SRAM`.

We distinguish the following addressing modes:

The operands (e.g. constants) are part of the instruction code.
The address (unchangeable) is part of the instruction code.
The address is located in a special 16 bit address register (`X`, `Y` or `Z`) and can be changed! The 16 bit address register is often called index register or pointer.

### Addressing the data memory (SRAM)

The operands (register and constant values) are part of the instruction. These instructions have often an `i` for immediate in their opcode and are used with the working registers `r16`-`r31`.

Examples:

``````    ldi   r18,0xA3        ; load immediate 0xA3 (163) to r18
andi  r24,254         ; logical and immediate with mask 0b11111110
subi  r25,0b00001111  ; subtract immediate r25 = r25 - 15
``````

##### Direct addressing of the working registers (one or two register)
``````    inc    r23      ; r23 = r23 + 1
com    r16      ; one's complement of r16
mov    r5,r20   ; copy content of r20 to r5
sub    r5,r20   ; r5 = r5 - r20
cp     r18,r22  ; compare r18 with r22 (same as sub)
``````

##### Direct addressing of special purpose register

We have six different instructions to access the 64 `SPR`:

``````    in    r19,0x09       ; read PIND to r19 (0x09 = address of PIND)
out   PORTD,r20
sbi   PORTD,4        ; set bit 4 in SPR PORTD
cbi   0x05,PB7       ; clear bit 7 in PORTD (0x05 address PORTB, PB7 = 7)
sbic  PINA,2         ; skip if bit in register cleared
sbis  0x03,PB3       ; skip if bit in register set
``````

##### Direct addressing of the data memory

Only two instructions (`lds` (load direct from data space) and `sts` (store direct to data space)) allow direct addressing of the SRAM:

``````    sts   0x0100,r18    ; store content of r18 direct to SRAM
``````

##### Indirect addressing of the SRAM

When using an indirect addressing mode, the address is not constant but variable!

The address can be changed, because it is located in a register. It can be easily e.g. incremented in a loop to access all the bytes in a table. The AVR controller have 6 working register that can be used as 16 bit register to hold the 16 bit addresses. They are called in `X` (`r26:r27`), `Y` (`r28:r29`) and `Z` (`r31:r30`) in AVR Assembly language.

Before using indirect addressing in Assembly language, the address pointer must be initialised!

Here an example:

``````    ldi  XL,0x02    ;initialise X with the address 0x0102
ldi  XH,0x01
``````

With the help of indirect addressing we can address all bytes of the SRAM (`GPR`, `SPR`, data and stack)!

Two instructions (`ld` (load) and `st`(store)) allow direct addressing of the SRAM:

``````    st  X,r17       ; store content of r17 to data space (address in X)
ld  r16,Y       ; load content from data space to r16 (address in Y)
``````

##### Indirect addressing of the SRAM with with post-increment or pre-decrement

There are 12 convenient instructions to automatically increment or decrement the address pointer when storing or loading. Here two examples:

``````    st  Y+,r16  ; store content of r16 to SRAM (address in Y) and then increment
; the address pointer Y (Y=Y+1)
ld  r16,-X  ; first decrement the address pointer X (X=X-1) and the load
; the content of the data memory (address in Y) to r16
``````
##### Indirect addressing of the SRAM with constant displacement

Other convenient instructions allow to add a constant displacement (0-63) to the address pointer, and so simplifying the access of tables with data sets.

Here two examples:

``````    std  Y+25,r16  ; store the content of r16 to the data memory
; the address is the sum of the address pointer and a
; constant (here 25)
ldd  r17,Y+12  ; load the content of the SRAM-address (Y+12) to r17
``````
##### Indirect addressing of the stack with `push` and `pop`.

As seen in the previous chapter, the address pointer is called stack pointer and resides in the SRAM `SPR` data range at the addresses `0x3D` for `SPL`, and `0x3E` for `SPH`. The stack pointer must be initialised, but is for the rest administrated by the hardware.

### Addressing the program memory (Flash)

We distinguish:

• Relative addressing of the Flash (`rjmp` and `rcall`)
• Direct addressing of the Flash (`jmp` and `call`)
• Indirect addressing of the Flash (`ijmp` and `icall`)
• Indirect addressing of constants in the Flash (`lpm`)

Let's take a closer look at the last point.

#### Indirect addressing of constants in the Flash

As the Flash is a read only memory, we have only instructions to load data from Flash to a working register `lpm` (load program memory). The indirect address has to be stored in `Z`. The addressing will be byte-wise!, so the Flash address has to be multiplied by 2.

Initializing the Z-pointer:

``````    ldi     ZL,LOW(TAB*2)   ; initialize the address pointer with the
ldi     ZH,HIGH(TAB*2)  ;  Flash address (TAB) * 2
``````

Get data from Flash:

``````    lpm    r20,Z   ; load the content from Flash (address in Z) to r20
``````

#### Dynamic memory allocation

In other high-level programming languages then `C` (e.g. java) it is normal to create objects at runtime, meaning during the running of the program. The garbage collector will free the memory automatically when it is no more needed by the program. `C` has no such garbage collector, so dynamic memory allocation must be done manually. Because this is prone to error, and because of our very limited memory, dynamic memory allocation is not recommended when programming in Arduino.

#### Static memory allocation

We have already used arrays in our exercises. Here four examples of creating a static allocated array;

``````byte my_1_array[] = {1,2,3};
char my_2_array[] = "Hello"; // C-string 0 terminated
char * my_3_array = "Text";  // C-string 0 terminated
byte my_4_array[5];
``````
###### "Just do it" AM1:
• Write an Arduino program generating the following output (naturally with your own values for the addresses). Document the code and the output.
``````    ------------- different_arrays -----------------------
*** byte my_1_array[] = {1,2,3}; ***

sizeof(my_1_array):                             3
pointer -> Serial.println(int(my_1_array),HEX): 106
my 1. array:                                    1 2 3

*** char my_2_array[] = "Hello"; // C-string ***

sizeof(my_2_array):                             6
string size -> strlen(my_2_array):              5
pointer -> Serial.println(int(my_2_array),HEX): 100
my 2. array:                                    Hello0

*** char * my_3_array = "Text";  // C-string ***

pointer size! -> sizeof(my_3_array):            2
pointer -> Serial.println(int(my_3_array),HEX): 31C
string size -> strlen(my_3_array):              4
my 3. array:                                    Text0

*** byte my_4_array[5]; ***

sizeof(my_4_array):                             5
pointer -> Serial.println(int(my_4_array),HEX): 4B0
my 4. array:                                    2 5 0 0 7
``````
• Explain the differences between the different arrays. Why does the `sizeof()` function returns only 2 bytes for the third array (search the net)?

In the third array (`my_3_array`) we initialize only a pointer (address register) to the array. Because we assign a text to the pointer and use the `char` type, the compiler knows, a null terminated C-string is needed and reserves the corresponding memory before runtime. We could also initialize the pointer without assignment:

``````    byte * my_5_array; // pointer, but memory not allocated!
``````

Now the compiler does not know the size of the memory to allocate at compile time and we have to do it later in the program (runtime) before using the array with the `malloc()` function:

``````    my_5_array = (byte *)malloc(5);
my_5_array[2] = 22;
my_5_array[4] = 99;
``````
###### "Just do it" AM2:
• Add the lines from above to your program and test it. Generate the following supplementary output and document it. What is the difference between array 4 (`my_4_array`) and 5 (`my_5_array`)?:
``````    *** byte * my_5_array; // memory not allocated! ***

pointer size! -> sizeof(my_5_array):            2
pointer -> Serial.println(int(my_5_array),HEX): 563
my 5. array:                                    5 0 22 5 99
``````
###### "Just do it" AM3:
• Attach the following code to your program. It shows all of our SRAM. Find all five 5 arrays in the output and document the corresponding lines. If needed use the ASCII table of the first chapter.

``````  byte *testpointer;
Serial.println("\n----------------- SRAM ----------------------------------");
int i = 0;
while (testpointer<0x900) {
if (i%16==0) {
Serial.println();
if (testpointer < 0x10) {
Serial.print('0');
}
if (testpointer < 0x100) {
Serial.print('0');
}
Serial.print(int(testpointer),HEX);
Serial.print(": ");
}
if (*testpointer < 0x10) Serial.print('0');
Serial.print(int(*testpointer),HEX);
Serial.print(' ');
testpointer++;
i++;
}
``````
• Find the `SREG` register and the stack pointer `SP`. Document the corresponding lines.