21.9. Other Programming Techniques for Assembly Language

21.9.1. Allocating Data Space for Variables

In the previous assembly language function examples, the data processing can be handled with just a few registers, so it does not use any stack memory at all. By default, the stack memory allocation is done for us in the default start-up code. We could reduce the stack size allocated by modifying the Stack_Size definition from 0x200 to other stack size required:
Stack_Size      EQU     0x00000200
                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp
For most applications, there would be fair amount of data variables. For simple applications, we can also allocate memory space in the RAM. For example, we can add a section in our application code to define three data variables “MyData1” (a word size data variable), “MyData2” (a half-word size data variable), and “MyData3” (a byte size data variable).
         PRESERVE8 ; Indicate the code here preserve
                   ; 8 byte stack alignment
         THUMB     ; Indicate THUMB code is used
         AREA    |.text|, CODE, READONLY   ; Start of CODE area
         EXPORT __main ; Make function visible from outside
__main   FUNCTION
         B        main
         ENDFUNC

main     FUNCTION
         LDR     R0,=MyData1
         LDR     R1,=0x00001234
         STR     R1,[R0]  ; MyData1 = 0x00001234

         LDR     R0,=MyData2
         LDR     R1,=0x55CC
         STRH    R1,[R0]  ; MyData2 = 0x55CC

         LDR     R0,=MyData3
         LDR     R1,=0xAA
         STRB    R1,[R0]  ; MyData3 = 0xAA
         B        .   ; while(1)
         ENDFUNC
         ALIGN   4

; --------------------------------------------------
; Allocate data variable space
         AREA    | Header Data|, DATA   ; Start of Data definitions
         ALIGN   4
MyData1  DCD     0    ; Word size data
MyData2  DCW     0    ; half Word size data
MyData3  DCB     0    ; byte size data
         ALIGN   4
; --------------------------------------------------
         END                ; End of file
Once the program is compiled, we can examine the data memory layout by right clicking on the target name (e.g., “Target 1”) in the project window and select “Open Map file”. From the map report file, we can see the address location and size of the variables we allocated:
Image Symbol Table
  Local Symbols
  Symbol Name          Value     Ov Type      Size  Object(Section)
  main.s               0x00000000   Number       0  main.o ABSOLUTE
  startup_stm32l053.s  0x00000000   Number       0  startup_stm32l053.o ABSOLUTE
  RESET                0x08000000   Section    192  startup_stm32l053.o(RESET)
  .text                0x080000c0   Section     24  startup_stm32l053.o(.text)
  .text                0x080000d8   Section     48  main.o(.text)
  main                 0x080000db   Thumb Code  20  main.o(.text)
   Header Data         0x20000000   Section      8  main.o( Header Data)
  MyData1              0x20000000   Data         4  main.o( Header Data)
  MyData2              0x20000004   Data         2  main.o( Header Data)
  MyData3              0x20000006   Data         1  main.o( Header Data)
  STACK                0x20000008   Section   1024  startup_stm32l053.o(STACK)
Since the RAM in microcontroller device used starts at address 0x20000000 onward, the variables are located starting from this address.
In gcc, the same data space allocation can be done by using .lcomm:
/∗ Data in LC, Local Common section ∗/
.lcomm   MyData4 4 /∗ A 4 byte data called MyData4 ∗/
.lcomm   MyData5 2 /∗ A 2 byte data called MyData5 ∗/
.lcomm   MyData6 1 /∗ A 1 byte data called MyData6 ∗/
The .lcomm pseudo-op is used to create an uninitialized block of storage inside the “bss” region. The program code can then access this space using the defined labels MyData4, MyData5, and MyData6.
Another way to allocate memory space is to use the stack memory. In order to allocate memory space for local variables inside a function, we can modify the value of SP at the beginning of a function:
MyFunction
        PUSH   {R4, R5}
        SUB    SP, SP , #8  ; Allocate two words for space for local variables
        MOV    R4, SP ; Make a copy of SP to R0
        LDR    R5,=0x00001234
        STR    R5,[R4,#0] ; MyData1 = 0x00001234
        LDR    R5,=0x55CC
        STRH   R5,[R4,#4] ; MyData2 = 0x55CC
        MOVS   R5,#0xAA
        STRB   R5,[R4,#6] ; MyData3 = 0xAA
        
        ADD    SP, SP, #8 ; Restore SP back to starting value to free space
        POP    {R4, R5}
        BX     LR
The main advantage of using the stack for local variables is that local variables in functions that are not active do not take up any space in RAM. In contrast, many 8-bit microcontroller architectures allocate all data variables in static memory locations, results in larger SRAM requirements.

21.9.2. Complex Branch Handling

When a conditional branch operation is based on a combination of input variables, it can take a complex decision sequence to decide if a branch should be taken. In some cases, it is possible to simplify the decision steps using assembly code.
If the branch condition is based on the variable of 5 bits or less, we can encode the branch condition as a 32-bit constant and extract the decision bit using shift or rotate instruction. For example,
if ((x == 0)||(x == 3)||((x>12)&&(x<19))||(x=23)) goto label; // x is a 5-bit data
The decision can be written as:
LDR   R0,=x          ; Get address of x
LDR   R0,[R0]        ; Read x from memory
LDR   R1,=0x0087E009 ; Encoded branch condition bit 23, 18-13, 3, 0 are set to1
ADDS  R0, R0, #1     ; Shift as least  one bit
LSRS  R1, R1, R0     ; Extract branch condition to carry flag
BCS   label          ; Branch if condition met
Alternatively, the branch condition can be encoded into an array of data bytes if the branch condition is more than 5-bit wide.
   LDR   R0,=x          ; Get address of x
   LDR   R0,[R0]        ; Read x from memory
   LSRS  R1,R1,R0       ; Get byte offset in look up table
   LDR   R2,=BranchConditionTable
   LDRB  R2,[R2,R1]     ; Get encoded condition
   MOVS  R1, #7
   ANDS  R1, R1, R0     ; Get lowest 3 bit of x
   ADDS  R0, R0, #1     ; Shift as least  one bit
   LSRS  R2, R2, R0     ; Extract branch condition to carry flag
   BCS   label          ; Branch if condition met
   
BranchConditionTable
   DCB   0x09, 0xE0, 0x87, 0x00,  ; Byte array of encoded branch condition

21.10. Accessing Special Instructions

21.10.1. CMSIS-CORE

In C/C++ programming, sometimes we might want to access some special instructions that cannot be generated by normal C/C++ code. If you are using CMSIS compliant device drivers, a number of CMSIS-CORE functions are available (Table 21.3) so that you can just use these functions to generate the required assembly instructions.
The C compiler itself might also provide similar feature, which is normally called intrinsic functions. For example, the Keil® MDK-ARM™ and the ARM Development Studio 5 (DS-5) provide the following intrinsic functions showing in Table 21.4. Beware that some of these functions differ from the CMSIS versions by lowercase characters.
In order to allow your application code to be more portable, you should use CMSIS intrinsic functions when possible.

Table 21.3

CMSIS functions support for the Cortex®-M0 and Cortex-M0+ processor

InstructionCMSIS-CORE functions
ISBvoid __ISB(void); // Instruction Synchronization Barrier
DSBvoid __DSB(void); // Data Synchronization Barrier
DMBvoid __DMB(void); // Data Memory Barrier
NOPvoid __NOP(void); // No Operation
WFIvoid __WFI(void); // Wait for Interrupt (enter sleep)
WFEvoid __WFE(void); // Wait for Event (enter sleep / 
                                 // clear event latch)
SEVvoid __SEV(void); // Send Event
REVuint32_t __REV(uint32_t value); // Reverse byte order 
                                                            // within a word
REV16uint32_t __REV16(uint16_t value); // Reverse byte order within
                                                    // each half word independently
REVSHint32_t __REVSH(int16_t value); // Reverse byte order in the 
                                // lower halfword, and then sign extend
                                // the result in a 32-bit word
CPSIE Ivoid __enable_irq(void);  // Clear PRIMASK
CPSID Ivoid __disable_irq(void); // Set PRIMASK

Table 21.4

Keil MDK or ARM DS-5 intrinsic functions support for the Cortex-M0 and Cortex-M0+ processors

InstructionIntrinsic function provided in Keil MDK or ARM DS-5
ISBvoid __isb(void); // Instruction Synchronization Barrier
DSBvoid __dsb(void); // Data Synchronization Barrier
DMBvoid __dmb(void); // Data Memory Barrier
NOPvoid __nop(void); // No Operation
WFIvoid __wfi(void); // Wait for Interrupt (enter sleep)
WFEvoid __wfe(void); // Wait for Event (enter sleep / 
                                 // clear event latch)
SEVvoid __sev(void);// Send Event
REVunsigned int __rev(unsigned int val); // Reverse byte order 
                                                          // within a word
CPSIE Ivoid __enable_irq(void);  // Clear PRIMASK
CPSID Ivoid __disable_irq(void); // Set PRIMASK
RORunsigned int __ror(unsigned int val, unsigned int shift); 
// rotate a value right by a specific number of bit
// "Shift" can be 1 to 31

21.10.2. Idiom Recognitions

Some C compilers also provide a feature called idiom recognition. When the C code is constructed in a particular way, then the C compiler automatically converts the operation into a special instruction when certain optimization levels are used. For example, for ARM tool chain the idiom recognitions listed in Table 21.5 are enabled for optimization level 2 or level 3.

Table 21.5

Idiom recognition in Keil MDK or ARM Compiler for Cortex-M0/M0+ processors

InstructionC language code that can be recognized by Keil MDK or ARM Compiler
REV16/∗ recognized REV16 r0,r0 ∗/
int rev16(int x)
{
     return (((x&0xff)<<8)|((x&0xff00)>>8)|((x&0xff000000)>>8)|((x&0x00ff0000)<<8));
}
REVSH/∗ recognized REVSH r0,r0 ∗/
int revsh(int i)
{
 return ((i<<24)>>16)|((i>>8)&0xFF);
}
If the software is ported to a different C compiler without the same idiom recognition feature, the code will still compile because it is using standard C syntax, although the generated instruction sequence might be less efficient than using idiom recognitions.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.160.61