ARM Cortex-M3 Assembly Language



Similar documents
(Refer Slide Time: 00:01:16 min)

An Introduction to Assembly Programming with the ARM 32-bit Processor Family

An Introduction to the ARM 7 Architecture

CS201: Architecture and Assembly Language

Graded ARM assembly language Examples

=

Computer Science 281 Binary and Hexadecimal Review

MACHINE ARCHITECTURE & LANGUAGE

Binary Adders: Half Adders and Full Adders

Advanced Computer Architecture-CS501. Computer Systems Design and Architecture 2.1, 2.2, 3.2

MICROPROCESSOR AND MICROCOMPUTER BASICS

Microprocessor & Assembly Language

Digital System Design Prof. D Roychoudhry Department of Computer Science and Engineering Indian Institute of Technology, Kharagpur

THUMB Instruction Set

The ARM Architecture. With a focus on v7a and Cortex-A8

what operations can it perform? how does it perform them? on what kind of data? where are instructions and data stored?

Instruction Set Architecture (ISA)

Exception and Interrupt Handling in ARM

A-level COMPUTER SCIENCE

Chapter 4 Register Transfer and Microoperations. Section 4.1 Register Transfer Language

Notes on Assembly Language

Chapter 5 Instructor's Manual

Take-Home Exercise. z y x. Erik Jonsson School of Engineering and Computer Science. The University of Texas at Dallas

CHAPTER 7: The CPU and Memory

1 Description of The Simpletron

Base Conversion written by Cathy Saxton

Software based Finite State Machine (FSM) with general purpose processors

UNIVERSITY OF CALIFORNIA, DAVIS Department of Electrical and Computer Engineering. EEC180B Lab 7: MISP Processor Design Spring 1995

Oct: 50 8 = 6 (r = 2) 6 8 = 0 (r = 6) Writing the remainders in reverse order we get: (50) 10 = (62) 8

How It All Works. Other M68000 Updates. Basic Control Signals. Basic Control Signals

MICROPROCESSOR. Exclusive for IACE Students iacehyd.blogspot.in Ph: /422 Page 1

CHAPTER 4 MARIE: An Introduction to a Simple Computer

plc numbers Encoded values; BCD and ASCII Error detection; parity, gray code and checksums

İSTANBUL AYDIN UNIVERSITY

ARM Instruction Set. ARM7TDMI-S Data Sheet. Final - Open Access

LSN 2 Computer Processors

X86-64 Architecture Guide

Keil Debugger Tutorial

CS101 Lecture 26: Low Level Programming. John Magee 30 July 2013 Some material copyright Jones and Bartlett. Overview/Questions

DIGITAL-TO-ANALOGUE AND ANALOGUE-TO-DIGITAL CONVERSION

Binary Representation. Number Systems. Base 10, Base 2, Base 16. Positional Notation. Conversion of Any Base to Decimal.

CSE 141L Computer Architecture Lab Fall Lecture 2

Administrative Issues

ASSEMBLY LANGUAGE PROGRAMMING (6800) (R. Horvath, Introduction to Microprocessors, Chapter 6)

The AVR Microcontroller and C Compiler Co-Design Dr. Gaute Myklebust ATMEL Corporation ATMEL Development Center, Trondheim, Norway

Asynchronous counters, except for the first block, work independently from a system clock.

MACHINE INSTRUCTIONS AND PROGRAMS

for the Bill Hanlon

Lecture 2. Binary and Hexadecimal Numbers

Name: Class: Date: 9. The compiler ignores all comments they are there strictly for the convenience of anyone reading the program.

To convert an arbitrary power of 2 into its English equivalent, remember the rules of exponential arithmetic:

EECS 427 RISC PROCESSOR

EC 362 Problem Set #2

So far we have investigated combinational logic for which the output of the logic devices/circuits depends only on the present state of the inputs.

Useful Number Systems

Programming Logic controllers

Instruction Set Design

2011, The McGraw-Hill Companies, Inc. Chapter 3

TIMING DIAGRAM O 8085

PROGRAMMABLE LOGIC CONTROLLERS Unit code: A/601/1625 QCF level: 4 Credit value: 15 TUTORIAL OUTCOME 2 Part 1

3. Programming the STM32F4-Discovery

CPU Organization and Assembly Language

PROGRAMMABLE LOGIC CONTROLLERS Unit code: A/601/1625 QCF level: 4 Credit value: 15 OUTCOME 3 PART 1

Special Notice. Rules. Weiss Schwarz Comprehensive Rules ver Last updated: October 15 th Outline of the Game

Chapter 2 Basic Structure of Computers. Jin-Fu Li Department of Electrical Engineering National Central University Jungli, Taiwan

Today. Binary addition Representing negative numbers. Andrew H. Fagg: Embedded Real- Time Systems: Binary Arithmetic

1.6 The Order of Operations

Measuring Resistance Using Digital I/O

Comp 255Q - 1M: Computer Organization Lab #3 - Machine Language Programs for the PDP-8

Z80 Instruction Set. Z80 Assembly Language

Pigeonhole Principle Solutions

Chapter 7 Lab - Decimal, Binary, Octal, Hexadecimal Numbering Systems

Preliminary Mathematics

DEPARTMENT OF COMPUTER SCIENCE & ENGINEERING Question Bank Subject Name: EC Microprocessor & Microcontroller Year/Sem : II/IV

Section 1.5 Exponents, Square Roots, and the Order of Operations

Developer Suite ARM. Assembler Guide. Version 1.2. Copyright 2000, 2001 ARM Limited. All rights reserved. ARM DUI 0068B

arrays C Programming Language - Arrays

Traditional IBM Mainframe Operating Principles

COMP 303 MIPS Processor Design Project 4: MIPS Processor Due Date: 11 December :59

The 104 Duke_ACC Machine

Divide: Paper & Pencil. Computer Architecture ALU Design : Division and Floating Point. Divide algorithm. DIVIDE HARDWARE Version 1

Chapter 9 Computer Design Basics!

CS311 Lecture: Sequential Circuits

Section 1.4 Place Value Systems of Numeration in Other Bases

Central Processing Unit (CPU)

Chapter 2 Logic Gates and Introduction to Computer Architecture

Chapter 11 Number Theory

A s we saw in Chapter 4, a CPU contains three main sections: the register section,

Keil C51 Cross Compiler

Numbering Systems. InThisAppendix...

Intel 8086 architecture

Basic numerical skills: FRACTIONS, DECIMALS, PROPORTIONS, RATIOS AND PERCENTAGES

Let s put together a Manual Processor

Assembly Language Programming

Formal Languages and Automata Theory - Regular Expressions and Finite Automata -

Systems I: Computer Organization and Architecture

Q. Consider a dynamic instruction execution (an execution trace, in other words) that consists of repeats of code in this pattern:

Decimals and other fractions

Click on the links below to jump directly to the relevant section

Transcription:

ARM Cortex-M3 Assembly Language When a high level language compiler processes source code, it generates the assembly language translation of all of the high level code into a processor s specific set of instructions. The target processor s instruction set is the set of capabilities that the core knows how to execute. For example, it is safe to say that every processor has an ADD instruction that takes two numbers, sums them, and stores the result somewhere. The implementation might be very different between various micros, but the net result is the same. ARM licenses their processor cores to other vendors. They do not manufacture their own microcontrollers or microprocessors. That means the documentation organization is different than with a proprietary core and micro that a different vendor may produce. ARM produces a set of documentation specific to their cores and the company that integrates the core into a micro produces their own documents as well. Atmel happens to include a section in their user guide that explains the ARM instruction set in their own words, which is quite handy. If you are really interested in the details of the ARM core, you would still want to look around at the arm.com website. What we need to know to learn a little bit of Cortex-M3 assembly language is all available in the SAM3U user guide in section 13: ARM Cortex M3 Processor which is mostly repeated information from the ARM Cortex-M3 Technical Reference Manual (available on the MPG website for your reference). As you build more complicated programs, you may have to review this section more carefully. For now, you need two things: 1. Core register map (see below) 2. Instruction set summary (see the SAM3U user guide) Core Registers ARM cores have what is referred to as a load-store architecture. That basically means that if you want to work on any variable from RAM or peripheral memory, you have to load it into the core, do what you want to it, then store it back to memory. The instruction set is designed for this architecture, and the core registers provide the memory locations for the core to do its work. This is all hidden if you are writing in C or C++, but is right in your face if you are writing code in assembler. The good news is that it is actually really simple, and other than the syntax of the instructions, you should have no problem writing some basic assembly code to make things happen on the processor. notes_mpgl1_firmware_assembler.docx Release 1.0 Page 1 of 16

Cortex-M3 Core Registers Source: SAM3U User Guide 6430F-ATARM-21-FEB-12, p, 57 The first 16 registers are general purpose and used to hold data or addresses that will be used with arithmetic or logical operations. Of these, the first 13 are generic and denoted R0 thru R12. Register R13 is reserved as a stack pointer and R14 is the link register that is used to hold the most recent return address for a function call. The Program Counter in R15 is the main pointer that addresses your program in flash memory. Whatever address is here will be the next instruction executed by the core. The program counter will continually be updating automatically from hardware every time the clock ticks. There are a few other registers shown, but the only one we need to worry about is the Program Status Register (PSR). If you read the documentation on the PSR, you see it is actually a combination of three registers that all sort of overlap each other but are effectively mutually exclusive (we are not sure why ARM does it this way, but there is probably a good reason). What we care about are the top four MSBs of the Application Program Status Register (APSR). notes_mpgl1_firmware_assembler.docx Release 1.0 Page 2 of 16

Application Program Status Register: most-significant bits of interest Modified from source: SAM3U User Guide 6430F-ATARM-21-FEB-12, p, 59 These four bits are essentially how the processor makes every decision that it has to make. In the context of looking at individual functionality of bits in a register, the bits are often referred to as flags or flag bits. We say a flag is set when it is logic 1, and we say a flag is clear when it is logic 0. Depending on the instruction that is executing and the result of that instruction, some of these bits may change to indicate to the processor what has just happened as a result of the instruction executing. The bits are: N: Negative / less-than. Set then the instruction result is negative. In comparison instructions, this is used to indicate less-than. The bit is cleared for all positive/greater-than or zero/equal-to results. Z: Zero. Set when the instruction result was zero. C: Carry / borrow. Set when V: Overflow. Set when bit 30 carries for use with signed values. The processor logic can examine these flags and make simple binary decisions based on their values. The easiest example is deciding if two numbers are equal. Instructions would be used to load two registers with the values of interest, and then a subtraction instruction would be used. If the numbers are the same, the result is 0 and the Z flag would be set. Therefore, after this instruction is complete, the processor can look at the Z bit in the APSR and make a branch (i.e. a decision) on what to do next. This is an if-then scenario in its most basic form. Every processor that we have ever worked with has some form of status register like the APSR, and literally every processor (that we have worked with) has the same N, Z, C and V flags. If you are coding in assembler, you must understand how they work and how to use instructions that can use them. Instructions Instructions are the binary machine language that makes the processor do everything it does. This is the essence of the logical-physical translation that occurs inside the processor. The bits that make up an instruction in flash memory are logic high and logic low voltage levels that appear on the bus when the instruction is being executed. These ones and zeroes cause the logic gates inside the processor to turn on or off depending on the ones and zeros. The net result is that the processor does something in hardware which results in more logic high and notes_mpgl1_firmware_assembler.docx Release 1.0 Page 3 of 16

logic low voltages levels propagating through logic to create the result in whichever destination location was configured by the instruction. This happens through several cycles called fetch, decode, execute and write-back. Each of these cycles requires one clock tick which advances the signals through flip-flops and the logic that is configured (decoded) by the instruction. The instruction is executed and the result written back to memory. So technically, one instruction cycle requires four clock ticks. However, instructions are almost always pipelined which means that while one instruction is at the write-back phase, the next instruction is already executing, the instruction after that is decoding, and the instruction after that is being fetched. So after the first four instructions in a program have finished, the processor is essentially running one instruction per clock tick. There should be a big asterisk there, because it of course this depends on whether or not the pipeline can stay full and execution does not require other operations but that goes beyond what we want to cover here. For Cortex-M3 cores, the instruction set is a mix of 16-bit and 32-bit instructions that can be used simultaneously. This is actually quite amazing and has a long history that is well beyond the scope of MPG. You will get a small taste of everything in the section exercise. Another (maybe) amazing thing is that there are only about 100 total instructions. So somehow, some way, even devices as complicated as game-playing smart phones can break down those tasks into combinations of just a few different actions. Now would be a great time to quickly glance at the instruction set summary in the SAM3U2 user guide. Assembly Language Syntax Just like in C, there are rules for syntax in assembly and standard ways of formatting lines of code. In assembly language, there are a few less rules because you have a limited number of expressions you can write. Each line of assembly code always has four fields, each of which are tab-delimited from one another: Label, Instruction Mnemonic, Operands and Comments. The format looks like this: Label Instruction Mnemonic Operands ; Comment The Label field is optional, as is the comment field other than the semi-colon. Labels will help organize your code and allow you to branch / jump around in the program because every label actually gets assigned to a corresponding program memory address for the line of code it tags. Comments are just text and are entirely ignored by the assembler, but as already eluded to, their value is extremely high for writing good code. The Instruction Mnemonic is any one of the reserved words listed in the Instruction set (e.g. ADC, ADD, MOV). Most mnemonics are self-explanatory for what they do, but in case you cannot figure it out, they are described in text in the instruction listing. What is not selfexplanatory are the optional flags or conditions that you can set to each instruction. To start an notes_mpgl1_firmware_assembler.docx Release 1.0 Page 4 of 16

example, look at a simple move instruction that has the mnemonic MOV. Reading the instruction set for MOV, you see the following detail: Label Mnemonic Operands Comment move_eg MOV[cond][s] Rd, <Op2> ; Move syntax Any text written in square brackets [ ] is optional, and any text written in angle braces <> is required but has several options. Almost every instruction has a condition field [cond] that makes that line conditionally executable based on the APSR flags. If you specify a condition, the processor will determine if the instruction should be carried out or skipped. This happens before the instruction is fetched, and does not cost any cycles to evaluate which is actually quite incredible, especially if you have worked with other processors that have to use separate instructions for condition testing. To use the cond code in an instruction, the instruction is preceded by an IT (if-then) instruction that sets up the logic for the possible branch. Label Mnemonic Operands Comment simple_move MOV <operands> ; Move version 1 setup_cond IT PL ; Setup for condition cond_move MOVPL <operands> ; Move version 2 The first MOV instruction will move the values specified in the operands and regardless of the result in the destination register, the APSR flags will not be touched. The second MOV instruction is suffixed with PL which tells the processor to only execute the move if the last instruction that updated the APSR flags resulted in a non-negative (positive or zero) value. This instruction must have the IT instruction with PL as an argument to set up the logic. Many instructions also have a flag that tells the processor whether or not to update the APSR flags after the instructions has been executed. This is another very powerful feature of the ARM architecture. Label Mnemonic Operands Comment setup_cond IT PL ; Setup for condition cond_move_s MOVPLS <operands> ; Move version 3 This MOV instruction is suffixed with both PL and S so it too will be conditionally executed and depending on the result stored in the destination register, will update the N, Z or C flags in the APSR. This shows that one instruction can have at least 3 variations even before considering the operands and it is very important to correctly specify the mnemonics. Note that order of the conditions and flags is indeed important MOVPLS is valid whereas MOVSPL is not. The operands that must be used with each instruction type can be fairly complicated this is why you must have your instruction set readily available when writing code. In general, you notes_mpgl1_firmware_assembler.docx Release 1.0 Page 5 of 16

specify a destination register and one or two source registers (or more, depending on the instruction). Building on the example move instruction, look at the operands that are specified for MOV: Rd, <Op2> This states that a move instruction requires a specified destination register, Rd, and some sort of second operand, Op2, which must be the source of the data to move. You will see in other instructions registers denoted Rs (source register) and Rm and Rn (other register arguments). Recalling the registers available, Rd can be anything from r0 to r15, though remember that r13, r14 and r15 are reserved and should not be used in most cases. That leaves r0 through r12 as options. Depending on what you intend to do with the data, you might strategically choose Rd to support the next instruction. For this standalone example, r0 will be used. The second operand has several optional forms and thus takes a bit longer to explain. Notice that nearly all the Data Processing instructions involve <Op2> as an operand. Fortunately, once you learn all of the variations of Op2 for the MOV instruction, you can apply it verbatim to the others. The table below summarizes what Op2 can be. It also describes the conditions that have to be imposed on some of the options. Notice that other than the first case where Op2 is an immediate value, Op2 is actually two arguments. Furthermore, the second option for Op2 (i.e. Rm {, <opsh>} contains even more options as described by the second half of the table. Operand 2 Immediate value #<imm8m> Register, logical shift left by number Rm, LSL # Register, logical shift right by number Rm, LSR # Register, arithmetic shift right by number Rm, ASR # Register, rotate right by number Rm, ROR # Optional shifts by constant (opsh): No shift: Rm (same as Rm, LSL #0) Logical shift left: Rm, LSL #<shift> where allowed shifts are 0-31 Logical shift right: Rm, LSR #<shift> where allowed shifts are 1-32 Arithmetic shift right: Rm, ASR#<shift> where allowed shifts are 1-32 Rotate right: Rm, ROR #<shift> where allowed shifts are 1-31 Rotate right with extend: Rm, RRX Table of Operand 2 options notes_mpgl1_firmware_assembler.docx Release 1.0 Page 6 of 16

If you simply wanted to move a number into a register, the operand is specified as #<imm8m> in the table. This syntax looks overly complicated and you are rightfully suspicious. If you wanted to load the value 10 into r0, you would write the instruction like this: Label Mnemonic Operands Comment move_eg MOV r0, #10 ; r0 = d 10 So why does the table show #<imm8m> instead of just saying any number? Well, to make this even more exciting there are conditions on the value imm8m because the number itself must be stored as part of the instruction while still leaving room for the other information that the instruction must contain. If the number was stored directly, only about 10 bits-large numbers could be stored, so instead, they are encoded and thus only certain numbers can be loaded directly into a register. These numbers are: any constant that can be produced by shifting an 8-bit value left by any number of bits within a 32-bit word any constant of the form 0x00XY00XY any constant of the form 0xXY00XY00 any constant of the form 0xXYXYXYXY. It turns out that the numbers available able are quite useful and there are not too many times during a regular program that the number you want cannot fit in the instruction. If you try to use a number that is not available, the assembler will give you a warning. In this case, you have to use multiple instructions and a memory location to store a constant and retrieve it for use. If you are writing code in C, the compiler will figure all of this out for you. The next choice for Operand 2 is slightly more flexible (Rm{, <opsh>}) even though it might look just as strange. First, you must specify a register that already has a value in it (you would have loaded this value in a previous instruction). If you want the value as-is, just specify the register name. If you want to shift it by a certain amount that you provide in the instruction, indicate the shift type and amount per the guidelines at the bottom of the table for opsh. The number of shifts is restricted to a maximum of either 31 or 32, as anything more would become redundant on a 32-bit signed or unsigned number. Here are some examples assuming that r1 will be the Rm register try this out in the code for this chapter: Label Mnemonic MOV MOV MOV MOV Operands Comment r0, #10 ; r0 = 10 r1, r0 ; r1 = r0 r0, r1, LSL #4 ; r0 = r1 * 16 r0, r1, ASR #1 ; r0 = signed(r1 / 2) notes_mpgl1_firmware_assembler.docx_assembler.docx Release 1.0 Page 7 of 16

The last four choices in the Op2 table are probably self-explanatory explanatory now. You must have a value in Rm that you want to shift. Then choose the shift type and reference another register, Rs, that holds the number of shifts to make (assumes Rm and Rs have both been loaded previously, where Rm is r1 and Rs is r2; the value in Rs should not exceed the maximum allowed shifts specified). Examples are as follows: Label Mnemonic MOV MOV MOV Operands Comment r2, #8 ; r2 = 8 r0, r1, LSL r2 ; r0 = r1 x 2^r2 r0, r1, ASR r2 ; r0 = signed(r1 / 2^r2) Right now it will not be clear as to why you would need so many similar options (and some that may seem pointless) for a simple move instruction. However, if you start thinking about some more complex operations or scenarios where you are moving numbers around, you might start to see how this can come in handy. Now we look at a longer example. As each step is taken, the register contents will be shown here in the notes but you really should follow along with the debugger to see it working. This a big part of this exercise so you get familiar with using the IAR debugger. Note we use two Watch windows plus a Register window so we can see the core register locations in hex, decimal and binary. The project should debug on your development board, or you can change the project options and work on the simulator here. To be able to view a register value in multiple formats (e.g. to look at R0 in decimal and binary), you must use two different types of Watch windows (e.g. a Watch windows and a Live Watch window). You cannot just specify the same variable twice in the same window, nor can you use different windows of the same type because IAR will automatically show the variables with the same representation. You might think you have it set up so it will work with the same type of window, but as soon as you step in the code it will change. notes_mpgl1_firmware_assembler.docx_assembler.docx Release 1.0 Page 8 of 16

Debug environment setup Register, Watch and Live Watch windows As with any uninitialized volatile memory, the start state is garbage, so before we write any code, our memory looks like this:????? First, initialize all four registers to 0. Do r0, r1 and r2 like this: MOV r0, #0 ; r0 = 0 MOV r1, #0 ; r1 = 0 MOV r2, r1 ; r2 = r1 So the memory looks like this: notes_mpgl1_firmware_assembler.docx Release 1.0 Page 9 of 16

0 0 0?? Now zero r3 and use the S flag to force an update to the APSR flags: MOVS r3, #0 ; r3 = 0, update APSR 0 0 0 0 0100 Now we have the registers of interest initialized to known values. This of course is exactly what you do in a high level language get your system in a known state. Now we can safely proceed with the example to use different instructions to change the memory locations and demonstrate the instruction set and processor operation. Next, fill up r0, r1 and r2 with more interesting numbers. MOV instructions could be used, but we will make life more interesting with some ADDs. It is acceptable to use one of the source registers as the destination register as shown: ADD r0, r0, #256 ; r0 = r0 + 256 ADD r1, r1, r0, LSL #2 ; r1 = r1 + (4 x r0) MOV r2, r0, LSR #6 ; r2 = r0 / 64 256 1024 4 0 0100 Now try a subtract with the S flag: SUBS r3, r1, r0 ; r3 = r1 r0, update APSR 256 1024 4 768 x0xx The other flags will update, but focus on the zero flag for now: the x represents don t care. So the following instruction uses a condition code and will only be executed if the result of the previous instruction (that is, the previous instruction that update APSR) was zero. Since it was not, this line of code will not actually be executed and r3 will remain 768: IT EQ ; Get ready for a conditional MOVEQ r3, #1024 ; if {Z}, r3 = 1024 notes_mpgl1_firmware_assembler.docx Release 1.0 Page 10 of 16

256 1024 4 768 x0xx Now try a reverse subtraction with a shift by the number in r2. Reverse just means that instead of doing operand1 operand2, the processor will do operand2 operand1 which might be important to you one day. RSBS r3, r1, r0, LSL #4 ; r3 = (16 x r0) r1 256 1024 4 3072 x0xx Here is a multiplication instruction conditionally executed as long as the RSB did not result in 0. IT NE ; Get ready for a conditional MULNE r3, r2, r0 ; r3 = r2 x r0 256 1024 4 1024 x0xx Now try a comparison. Comparisons simply look at the values in the register to update flags, so no registers will change their value as a result of this instruction other than the APSR (even though the processor will actually subtract the two numbers). You do not have to specify the S flag as it is implied by the instruction that you want to update APSR. CMP r3, r1 ; Set flags if r1 == r3 256 1024 4 1024 0100 For the last example case, use a logical bit-wise OR conditionally executed on the CMP being equal, and update the flags on the result. IT EQ ; Get ready for a conditional notes_mpgl1_firmware_assembler.docx Release 1.0 Page 11 of 16

ORREQS r3, r0, r1, LSR #10 ; r3 = r0 (r1 / 1024) r0 r1 r2 r3 NZCV b'100000000' 1024 4 b'100000001' 0000 The memory is shown in binary for r0 and r3 for clarity. This example has shown most of the different options available to code an instruction and given lots of samples of Data Processing instructions that operate only on the core registers. Until we are actually working towards a goal for a program, it will not be entirely clear how all of these instructions are going to come together to make something useful happen. In fact, Data Processing instructions by themselves probably will not suffice to write a program. We have to get into the memory access instructions to load and store memory to and from RAM, ROM and peripheral registers. Load and Store Instructions The ARM is classified as a load-and-store architecture and this functionality is fundamental to its operation. The instruction set allows data processing to occur only on the core s registers, so there must be a way to get data in and out of the core from RAM or ROM. There are quite a few different load and store instructions but we will just look at the two simplest cases. What should be obvious is that if you want to access a memory location, you have to know where it is. The 32-bit address of the memory location you want then has to be loaded into a register, which requires an immediate to specify the address. What is not so obvious is how to load that number due to the limitations of what numbers can be stored in an instruction vs. numbers that must be stored as constants and accessed by reference. LDR is load register from memory and STR is store register to memory. In their simplest forms, they use a register and a pointer to a memory location as arguments. For notation, a pointer is shows as the register in square brackets, like this: [r1]. The example shown here will load the current value in 0x4000 0000, multiply it by 4, then store it back to the next 32-bit location in RAM, 0x4000 0004. Register r1 will hold the address, and r0 will be used during the arithmetic instructions. Let us use an example to see how load and store instructions are used. The example code continues in the same program as the previous exercise. Adjust the debug environment so you can see a Register window and both the Memory and Symbolic Memory windows. In both memory windows, enter 0x20000000 0000000 in the Go to areas so that the windows show you the memory addresses we want to look at. notes_mpgl1_firmware_assembler.docx_assembler.docx Release 1.0 Page 12 of 16

The memory starts out like this: New Symbolic Memory and Memory windows for this exercise r0 r1 0x2000 0000 0x2000 0004 NZCV???????? First, make r1 point to the RAM space by loading the RAM address: MOV r1, #0x20000000 ; r1 = the target address (this ; is a valid immediate) Note that there is nothing that tells r1 that the value it has been loaded with is a pointer we just know that is what that number will be used for. We want the number 5000 to be loaded into the RAM location 0x20000000. The first step is to get the number 5000 into a register, so can we do the following? notes_mpgl1_firmware_assembler.docx Release 1.0 Page 13 of 16

MOV r0, #5000 ; r0 = 5000? No! If you try it, the assembler returns: Error[400]: Expression out of range The number 5000 is not a valid number that can be stored in an instruction. So how do we get that number? We can use an LDR instruction that will automatically do two things: store the number 5000 in a flash location, and then update the instruction to access that location to get the value into a core register. The location where this number is stored is known as the literal pool where a bunch of constants hang out in flash memory (they are written there as data locations by the assembler when you build code that requires constants). The syntax is: Now we have: LDR r0, =5000 ; r0 = 5000 r0 r1 0x2000 0000 0x2000 0004 NZCV 5000 0x2000 0000?? 0000 Now dereference the pointer in r1 to store the value from r0 into RAM. The syntax requires square brackets around the r1 mnemonic to indicate you want to use its contents as an address pointer. The typical destination, source operand order is also reversed for a STR instruction: STR r0, [r1] ; *r1 = r0 r0 r1 0x2000 0000 0x2000 0004 NZCV 5000 0x2000 0000 5000? 0000 If you look at your memory windows (and have correctly set them to show you address 0x20000000), you should see the new value written. Notice two things: 1. 0x1388 is hex for 5000 (this is where number conversions start to get handy) 2. The values are in Little Endian notes_mpgl1_firmware_assembler.docx Release 1.0 Page 14 of 16

RAM location 0x20000000 after 5000 is written Now we will do a bunch of other operations to ensure you are comfortable with loading and storing. Multiply the value in r0 using a shift, update r1 to the address of the second RAM register we want to use, and store the value back by once again dereferencing r1: LSL r0, r0, #2 ; Multiply r0 by 4 ADD r1, r1, #4 ; r1 += 4 (new RAM address) STR r0, [r1] ; *r1 = r0 r0 r1 0x2000 0000 0x2000 0004 NZCV 20000 0x2000 0004 5000 20000 0000 Lastly get the value stored in RAM at 0x20000004 and put it in r3: LDR r3, [r1] ; r3 = *r1 LDR and STR in their simplest form are indeed easy using the simplest of register-indirect addressing with pointers. Any variable that is stored in RAM that is needed or needs to change, must be loaded to the core registers, used as required, then stored back. That is enough notes_mpgl1_firmware_assembler.docx Release 1.0 Page 15 of 16

information to give us what we need to be able to load peripheral registers in the next section, so we will leave it at that even though you could easily spend a whole course learning all of the ARM instructions and figuring out how to use them effectively to write large programs. notes_mpgl1_firmware_assembler.docx Release 1.0 Page 16 of 16