You are on page 1of 5

School of Computing Sciences

CMPE3D01/SMD01 – Embedded Systems

Laboratory Sheet 3:

MDK-ARM: Assembly Language

1.0 Introduction
This laboratory aims to provide an introduction to writing assembly language
programs. A good introduction to ARM assembly language programming is available
in chapter 3 of the course text [1] and Keil help documents [2,3]. For most
applications it is more appropriate to program in a high-level language such as C or
C++ but occasionally (e.g. for time critical sections of code) the programmer might
need the extra level of control afforded by assembly language. Assembly language
allows the target to be programmed at the lowest level since there is a 1:1 relationship
between assembly language instructions and machine code (as opposed to the many:1
relationship that exists for a high level language). Assembly language programmers
must be familiar with the programmer’s model for the machine they are targeting [4]
so it is an excellent way of learning about the machine’s architecture.

2.0 Objective

To examine simple ARM assembly language subroutines running on the


MCBSTM32C target using the MDK-ARM debugger.

To translate a simple function written in C into assembly language.

3.0 Procedure

1. Download the project asmHelloWorld. This project demonstrates an assembly


language subroutine and to understand it you need to know the basics of assembly
language programming ( see page 96 of the course text [1]). The ARM Cortex-
M3 processor implements the Thumb and Thumb-2 instruction subsets of the
ARM family. A full list of instructions supported by the processor is given in
[4], Table 20, you will need to refer to this during the lab session.

CMPE3D01 Laboratory Sheet 3 Last Updated: 19.09.13 (MHF)


The subroutine uses an SVC (Supervisor Call) instruction to write characters to
the COM port so you’ll need to configure PuTTY as we did in Laboratory 2. The
SCV instruction implements a Software Interrupt, and the interrupt handler
executes the Software Interrupt Function declared in file helloWorld.c. Interrupts
are the subject of Laboratory 4, so don’t be too concerned with parts of the
program that set up the software interrupt vector table; you’ll understand them
soon enough. Run the program and confirm the message “Hello World” is printed
on the PC screen.

2. The assembly language subroutine (Appendix A) illustrates some features of the


ARM assembly language and instruction set.
a. The declaration of the code AREA
b. Use of EXTERN to export the pointer to the subroutine.
c. The = EQU pseudo-op to define the character string to be output.
d. The Branch and Exchange (BX) instruction transfers control back to
the calling function (in this case main() ).
Note: the name of the subroutine “hello” is actually a pointer to the start of the
subroutine code. Examine each instruction in the subroutine, and use [4] to help
you understand what each line achieves.

3. Use debug to single-step


through the code and
examine the code in more
detail. Configure debug
to use the simulator as
shown:

4. Insert a breakpoint at line 36 of helloWorld.c (call to hello( )).


Start the debugger, and run to the breakpoint (F5). Then
single-step (F11) paying particular attention to the contents of
the registers as the assembly language subroutine is executed.
For example, after instruction LDR r0, r[5], #0x01 has been
executed R0 contains the first 4 bytes of the string “Hello”
which are 0x48 = ‘H’, 0x65 = ‘e’, 0x6c = ‘l’, 0x6c = ‘l’ and r5
contains the address of (i.e. a pointer) the next character.

CMPE3D01 Laboratory Sheet 3 Last Updated: 19.09.13 (MHF)


5. Notice as you single step through the assembly code you should see the characters
appear one-by-one on the PC screen.

6. Now it’s time for you to write an assembly language program.

Create a new project named asmBlinky (or a copy of myBlinky) and rebuild the
application you created in Lab 1.

7. Create a new file named delay.a (a or s file extension


denotes assembly language source files) and copy the
assembly language program given in Appendix A.

Remember to include calls to wait( ); in main.

8. As the subroutine generates a fixed delay so the


function prototype in asmBlinky.c will look like this:

/* Function Prototypes */
extern void wait(void);

9. A useful technique for writing assembly language is to first write an equivalent


high level language (or pseudo-code) program and then translate this manually
into assembler. In this case, we might think in terms of:

const unsigned int delay = 100000;


unsigned int value;

value = delay;
while(value > 0)
value = value-1;

The programmer must decide which registers are used to store variables and
constants and which assembly language instructions to use. This sounds daunting
but it’s easy to learn by looking at a few example assembly language programs
[2].

Modify the assembly language subroutine so the delay produced depends on the
value of an input parameter i.e.

/* Function Prototypes */
void wait(unsigned long value);

Calls between separately assembled or compiled modules must comply with the
restrictions and conventions defined by the procedure call standard. See the
Procedure Call Standard for the ARM Architecture specification on the ARM
website for more information. For this exercise it’s sufficient for you to know
that parameters are passed using the register block, starting with R0, so your
assembly language subroutine can expect R0 to contain the delay value.

CMPE3D01 Laboratory Sheet 3 Last Updated: 19.09.13 (MHF)


10. A test harness that uses both delay subroutines is shown in Appendix B.

4.0 References

[1] D. Lewis, Fundamentals of Embedded Software with the ARM Cortex-M3 (2e),
Pearson, 2013.
[2] Keil, RealView Assembler User Guide, Rev. B, 2008.
[3] Keil, ARM InstructionSet User’s Guide, 2004.
[4] ST Microelectronics, STM32F10xxx Cortex-M3 programming manual,
http://www2.cmp.uea.ac.uk/~mhf/embedded/resources/15491.pdf (accessed Sept.
2013).

CMPE3D01 Laboratory Sheet 3 Last Updated: 19.09.13 (MHF)


Appendix A
;**************************************************************;
;* delay: Very simple assembly language delay routine *;
;**************************************************************;
;* *;
;* Dr. Mark Fisher, CMP, UEA, Norwich, UK. *;
;* Last updated 18.09.13 *;
;**************************************************************;
AREA delay, CODE, READONLY
; Name this block of code delay
ENTRY
EXPORT wait ; Make wait visible to main
wait LDR R0, data ;
loop SUB R0, #1 ;
CMP R0, #0 ;
BNE loop ;
BX lr ; leave the function
data DCD 1000000
END

Appendix B

/**************************************************************/
/* asmBlinky: Very Simple LED Flasher with ASM subroutine */
/**************************************************************/
/* waitA() provides a fixed delay - hard coded in assembler */
/* waitB() provides a variable delay set by a passed param. */
/* */
/* Dr. Mark Fisher, CMP, UEA, Norwich, UK. */
/* Last updated 30.06.10 */
/**************************************************************/

#include <stm32f10x_cl.h>

/* Function Prototypes */
void waitA(void);
void waitB(unsigned long value );

int main (void) {


const unsigned long led_mask = 1<<15;
const unsigned long delay = 1000000;
unsigned long value;

SystemInit();

/* Setup GPIO for LEDs */


RCC->APB2ENR |= 1 << 6; /* Enable GPIOE clock */
GPIOE->CRH = 0x33333333; /* Configure the GPIO for LEDs */

for (;;) { /* Loop forever */


GPIOE->BSRR = led_mask; /* Turn LED on */
waitA(); /* wait */
GPIOE->BSRR = led_mask << 16; /* Turn LED off */
value = delay;
waitB(value); /* wait */
}
}

CMPE3D01 Laboratory Sheet 3 Last Updated: 19.09.13 (MHF)

You might also like