Assembly Arithmetic and Logic Instruction
Data processing instructions can be classified into seven categories:
Arithmetic Instructions
Reorder Instructions
Extension Instructions
Bitwise Logic Instructions
Shift Instructions
Comparison Instructions
Data Copy Instructions
Program Status Register
Cortex-M processors have five status flags: N, Z, V, C, Q
The negative flag (N) is set if the result of ALU is negative and is cleared otherwise.
The zero flag (Z) is set if the ALU result is zero and is cleared otherwise.
The carry flag (C) is set if a carry occurs in unsigned addition and is cleared otherwise.
For unsigned subtraction, it is set if no borrow has occurred, and is cleared otherwise.The overflow flag (V) is set if an overflow takes place when performing a signed addition or subtraction and is cleared otherwise.
The saturation flag (Q) is set if an SSAT or USAT instruction causes saturation and is cleared otherwise.
Most data processing instructions of Cortex-M processors have an option to update these ALU status flags. These flags are stored in the program status register (PSR).
The program status register is a combination of three special registers:
The application program status register (APSR)
The interrupt program status register (IPSR)
The execution program status register (EPSR)
Because APSR, IPSR, and EPSR have no overlap in bit fields, the processor combines them into one register PSR, or called xPSR to allow convenient accesses.
In APSR, the GE flags indicate whether the corresponding results are greater than or equal to zero. The GE flags are only available on Cortex-M4 and M7.
In EPSR, the T flag indicates whether the processor is in Thumb state or ARM32 state. Since Cortex-M processors only support Thumb-2/Thumb instructions, the T flag has a fixed value of 1 in Cortex-M.
Additionally, the IT bit fields (IT [ 7: 6] and IT [ 5: 0]) in EPSR hold the condition states associated with the current IF-THEN (IT) block. An IT block is a convenient approach to implementing conditionally executed instructions.
In IPSR, the least significant 9 bits are zero if the processor is in the thread mode, or the exception or interrupt number if the processor is in the handler mode. On reset, the processor is in the thread mode.
These special registers can only be accessed by using two special instructions:
MRS (move from a special register to a general register)
MSR (move from a general register to a special register)
Specifically, MRS reads these registers, and MSR writes to these registers. The following gives a few examples.
MRS r0, apsr ; Read APSR
MRS r0, ipsr ; Read IPSR
MRS r0, epsr ; Read EPSR
MRS r0, xpsr ; Read APSR, IPSR, and EPSR
MSR apsr_nzcvq, r0 ; Change N,Z,C,V,Q flags in APSR
MSR apsr_g, r0 ; Copy r0{19:16) to GE{3:0] in APSR (Not on Cortex-M3)
MSR apsr_nzcvqg, r0 ; Change N,Z,C,V,Q and GE flags (Not on Cortex-M3)
Updating Program Status Flags
It is an option for an arithmetic or logic instruction to set the processor status flags. If the 'S' suffix is appended to an instruction mnemonic, the processor modifies the status flags based on the computation result.
For example, the ADDS
instruction changes the N, Z, C, and V flags when performing addition.
On the contrary, ADD
cannot change these flags. If an instruction does not update these flags, the existing value of each flag, set by a previous instruction, is preserved.
Data comparison instructions, such as CMP
(compare), CMN
(compare negative), TST
(test), and TEQ
(test equivalence), set these flags even though they do not have the 'S' suffix.
ADD r1, r2, r3 ; r1 = r2 + r3, but will not update N, z, c, and v flags
ADDS r1, r2, r3 ; r1 = r2 + r3, and will update N, Z, C, and V flags
While the first instruction ADD
does not change the N, Z, C, and V flags, the second instruction ADDS
modifies the flags in the following ways:
The overflow flag by assuming that r2 and r3 hold signed integers represented in two's complement.
The carry flag by assuming that r2 and r3 hold unsigned integers.
The zero flag by checking whether the result saved in the destination register r1 is zero or not.
The negative flag by checking the sign bit of r1 (the most significant bit of r1).
If the Barrel shifter is used, the source operand may update the program status flags.
For example, the bitwise logical ANDS instruction can update the N, Z, and C flags. In the following instruction, the N flag is set if the most significant bit of r1 is 1, and the Z flag is set if r1 equals 0.
ANDS r1, r2, r3 ; r1 = r2 AND r3
It is easy to understand that most logical instructions do not update the overflow flag. How does a logical instruction update the carry flag? The answer lies in the second operand of a logical instruction. If the second operand uses the Barrel shifter, then the processor updates the carry flag based on the shift or rotation result.
ANDS r1, r2 , r3, LSL #3 ; r1 = r2 AND (r3 << 3)
When MOVS
uses the Barrel shifter, the processor also updates the Z, N, and C flags.
MOVS r2, r1, LSR #3 ; r2 = r1 << 3
However, the Barrel shifter does not change the flags if it is employed in an arithmetic instruction. For example, in the following instruction, the flags depend on the result of addition, instead of logical shift left.
ADDS r1, r2, r3, LSL #3 ; r1 = r2 AND (r3 << 3)
If the program is written in assembly, it is the programmer's responsibility to interpret and use these flags correctly. For programs written in high-level languages, compilers automatically interpret these flags.
Shift and Rotate
As shown in the below figure, the second ALU operand is equipped with a Barrel shifter, which is a special digital circuit for quick shift and rotation. Barrel shifters are usually not available on other processors such as PIC and AVR.
There are five types of shift and rotate operations: LSL, LSR, ASR, ROR, and RRX
LSL (logical shift left) moves all bits of a register value left by n bits and zeros are shifted in at the right end.
LSL is equivalent to multiplication by 2**n (<<
operation in C).LSR (logical shift right) moves all bits of a register value right by n bits and zeros are shifted in at the left end.
LSR is equivalent to unsigned division by 2**n (>>
operation on signed numbers in C).ASR (arithmetic shift right) moves all bits right by n bits and copies of the left most bit (the sign bit) are shifted in at the left end.
ASR is equivalent to signed division by 2**n (>>
operation on signed numbers in C).ROR (rotate right) is the circular shift, in which all 32 bits are shifted right simultaneously as if the right end of the register is joined with its left end. The bit shifted out from the right end of the register is copied into the carry bit. The carry bit can be optionally used to update the carry flag of the processor status register.
RRX (rotate right with extend) works similarly to ROR except that the carry bit joins the rotating circle, and RRX can rotate the data by only one bit.
LSL r1, r2 ; r1 = r1 << r2
LSL r1, #3 ; r1 = r1 << 3
LSL r1, r2, #3 ; r1 = r2 << 3
LSL r1, r2, r3 ; r1 = r2 << r3
ROR r1, r2 ; r1 = rotate r1 by r2 bits
RRX r1, r2 ; rotate r2 right by one bit (with extension)
The C language does not provide rotate operations (ROR and RRX). The compiler automatically uses a rotation instruction if it can improve the performance. Besides, ARM assembly language does not provide a rotate left assembly instruction. However, a rotate left by (n) bits can be replaced with a rotate right by (32 - n) bits. For example, rotating left by 6 bits has the same result as rotating right by 26 bits.
Note the carry bit is not the carry flag of the processor status register. Therefore, none of these shift and rotate instructions updates the status flags by default. If these flags need to be updated, a shift or rotate instruction must have the suffix S specified. What's more, these instructions cannot modify the overflow flags.
LSL r1, #3 ; r1 = r1 << 3, but will not update the flags
LSLS r1, #3 ; r1 = r1 << 3, and will update the N, Z, C flags
; LSLS does not update the V flag
Programs often use the Barrel shifter to replace slow multiplication and division instructions to improve the speed, as shown below.
ADD r0, r2, r1, LSL #1 ; r0 = r2 + r1 << 1 = r2 + 2 x r1
ADD r1, r0, r0, LSR #3 ; r1 = r0 + r0 >> 3 = r0 + r0/8
The Barrel shifter used in a move (MOVS and MVNS), and logical/bitwise instruction with the 'S' suffix (such as ANDS, ORRS, EORS, BICS) updates the carry flag.
Arithmetic Instructions
The below table lists arithmetic instructions that produce 32-bit results.
Instruction | Description |
ADD {Rd,} Rn, Op2 | Add. Rd = Rn + Op2 |
ADC {Rd,} Rn, Op2 | Add with Carry. Rd = Rn + Op2 + Carry |
SUB {Rd,} Rn, Op2 | Subtract. Rd = Rn - Op2 |
SBC {Rd,} Rn, Op2 | Subtract with Carry. Rd = Rn - Op2 + Carry - 1 |
RSB {Rd,} Rn, Op2 | Reverse Subtract. Rd = Op2 - Rn |
MUL {Rd,} Rn, Rm | Multiply. Rd = Rn * Rm |
MLA Rd, Rn, Rm, Ra | Multiply with Accumulate. Rd = Ra + (Rn * Rm) |
MLS Rd, Rn, Rm, Ra | Multiply and Subtract. Rd = Ra - (Rn * Rm) |
SDIV {Rd,} Rn, Rm | Signed Divide. Rd = Rn/Rm |
UDIV {Rd,} Rn, Rm | Unsigned Divide. Rd = Rn/Rm |
SSAT Rd, #n, Rm{,shift #s} | Signed Saturate |
USAT Rd, #n, Rm{,shift #s} | Unsigned Saturate |
Addition and Subtraction Instructions
Most of these instructions take two source operands, and the 32-bit result is saved in a destination register. While the first source operand is a register, the second source operand is flexible and can be a register, an immediate constant, or an inline Barrel shifter.
SUB r3, r2, r1 ; r3 = r2 - r1
RSB r3, r2, #987 ; r3 = 987 - r2
ADD r0, r0, r0, LSL #3 ; r0 = r0 + (r0 << 3)
If an instruction has three operands, the second operand cannot be a constant number in most instructions (except SSAT and USAT).
SUB r0, #1, r3 ; Not allowed, causing a syntax error
The example below shows the implementation of subtracting two 96-bit integers by using SUB and SBC. A 96-bit integer is saved in three registers.
; C = A - B
; Subtracting two 96-bit integers A (r2:r1:r0) and B (r5:r4:r3).
; Three registers to hold a 96-bit integer: upper, middle, Lower word
; Result C (r8:r7:r6)
, A = 00001234,00000002,FFFFFFFF
; B = 12345678,00000004,00000001
LDR r0, =0xFFFFFFFF ; A's Lower 32 bits
LDR r1, =0x00000002 ; A's middle 32 bits
LDR r2, =0x00001234 ; A's upper 32 bits
LDR r3, =0x00000001 ; B's Lower 32 bits
LDR r4, =0x00000004 ; B's middle 32 bits
LDR r5, =0x12345678 ; B's upper 32 bits
; Subtract A from B
SUBS r6, r0, r3 , C[31:0] = A[31:0] - B[31:0], update carry
; Carry f Lag is 1 if no borrow has occurred in the previous subtraction
SBCS r7, r1, r4 ; C[64:32] = A[64:32] - B[64:32] + carry - 1, update carry
SBC r8, r2, r5 ; C[96:64] = A[96:64] - B[96:64] + carry - 1
Short Multiplication and Division Instructions
The result of a multiplication may have more than 32 bits. However, the destination register only holds the least significant 32 bits (LSB32) of the result.
MUL r6, r4, r2 ; signed multiply, r6 = LSB32( r4 x r2 )
UMUL r6, r4, r2 ; unsigned multiply, r6 = LSB32( r4 x r2 )
MLA r6, r4, r1, r0 ; r6 = LSB32( r4 x rl ) + r0
MLS r6, r4, r1, r0 ; r6 = LSB32( r4 x rl ) - r0
SDIV r3, r2, r1 ; signed divide, r3 = r2/r1
UDIV r3, r2, r1 ; unsigned divide, r3 = r2/r1
Long Multiplication Instructions
Long multiplication instructions that produce 64-bit result.
Instruction | Description |
UMULL RdLo, RdHi, Rn, Rm | Unsigned long multiply. RdHi, RdLo = unsigned (Rn * Rm) |
SMULL RdLo, RdHi, Rn, Rm | Signed long multiply. RdHi, RdLo = signed (Rn * Rm) |
UMLAL RdLo, RdHi, Rn, Rm | Unsigned long multiply with accumulate. |
RdHi, RdLo = unsigned (RdHi, RdLo + Rn * Rm) |
| SMLAL RdLo, RdHi, Rn, Rm | Signed long multiply with accumulate.
RdHi, RdLo = signed (RdHi, RdLo + Rn * Rm) |
Two registers are used to store a 64-bit result, with the high register (RdHi) holding the most significant 32 bits, and the low register (RdLo) holding the least significant 32 bits.
UMULL r3, r4, r0, rl ; r4:r3 = r0 x r1, r4 = MSB bits r3 = LSB bits
SMULL r3, r4, r0, rl ; r4:r3 = r0 x r1
UMLAL r3, r4, r0, rl ; r4:r3 = r4:r3 + r0 x r1
SMLAL r3, r4, r0, rl ; r4:r3 = r4:r3 + r0 x r1
Saturation Instructions
The saturation instructions limit a given input to a configurable signed or unsigned range. When the input value exceeds the specified range, its output is then set as the maximum or minimum value of the selected range. Otherwise, the output is equal to the input. The saturation instructions take one immediate source operand and one register source operand.
$$SSAT(x) = \left\{ \begin{array}{cl} 2^{n-1} - 1 & : \ x \gt 2^{n-1} - 1\\ -2^{n-1} & : \ x \lt 2^{n-1} \\ x & : \ otherwise \end{array} \right.$$
$$USAT(x) = \left\{ \begin{array}{cl} 2^{n} - 1 & : \ x \gt 2^{n} - 1\\ x & : \ otherwise \end{array} \right.$$
The following gives two examples in which n is 11. Note the second operand is an immediate number in SSAT and USAT.
SSAT r2, #11, r1 ; output range: -2^10 : 2^10
USAT r2, #11, r1 ; output range: 0 : 2^11
Barrel Shifter
The key advantage of Barrel shifters is that it can shift or rotate a register by a specified number of bits in one clock cycle.
Typically, a Barrel shifter is implemented as a cascade of parallel 2-to-1 multiplexers. The below figure gives an example implementation of a four-bit Barrel shifter that performs rotate right. The S1 S0 indicates the amount of rotation. The implementation of logic shift is similar, except that a zero bit is shifted in either from the right end or the left end.
The Barrel shifter is special hardware that can perform shift and rotation on the second ALU source operand. Therefore, not only can a shift and rotate instruction be used as a standalone assembly instruction, but it can also be utilized in other instructions to make changes to the second source operand.
ASR and LSR differ on whether the sign is preserved. For example:
ADD r1, r0, r0, LSL #3 ; r0 = r0 + r0 << 3 = r0 + 8 * r0
ADD r1, r0, r0, LSR #3 ; r1 = r0 + r0 >> 3 = r0 + r0/8 (unsigned)
ADD r1, r0, r0, ASR #3 ; r1 = r0 + r0 >> 3 = r0 + r0/8 (signed)
We can leverage Barrel shifter to speed up the application.
Without Barrel shifter, two separate instructions would be required to carry out each of the above instructions. This would not only increase the size of a binary program but also take more processor cycles to complete the same task.
Barrel shifter can also replace slow multiplication instructions, as shown in the following example.
ADD r1, r0, r0, LSL #3 r1 = r0 + r0 * 8 = r0 * 9
; Instead of
MOV r2, #9 ; r2 = 9
MUL r1, r0, r2 ; r1 = r0 * 9
Bitwise Logic Operations
Bitwise operations treat input operands as a sequence of binary bits, rather than as integer numbers. The computation is carried out at the bit level. For example, we can reset a specific bit of a register to zero or set a specific bit a register to one, leaving the other bits unchanged.
There are four commonly used bitwise Boolean operators: AND, OR, Exclusive OR (EOR), and negation (NOT).
The following table shows the bitwise assembly instructions supported in Cortex-M.
Instruction | Description |
AND {Rd,} Rn, Op2 | Bitwise logic AND. Rd = Rn & Op2 |
ORR {Rd,} Rn, Op2 | Bitwise logic OR. Rd = Rn I Op2 |
EOR {Rd,} Rn, Op2 | Bitwise logic XOR. Rd = Rn ^ Op2 |
ORN {Rd,} Rn, Op2 | Bitwise logic NOT OR. Rd = Rn I (NOT Op2) |
BIC {Rd,} Rn, Op2 | Bitwise clear. Rd = Rn & (NOT Op2) |
BFC Rd, #lsb, #width | Bit field clear. Rd[(width+lsb-1) : lsb] = 0 |
BFI Rd, Rn, #lsb, #width | Bit field insert. Rd[(width+lsb-1) : lsb] = Rn[(width-1) : 0] |
MVN Rd, Op2 | Logically negate all bits. Rd = 0xFFFFFFFF EOR Op2 |
These instructions operate at the bit level. They perform logic operations for each pair of bits that are at the same position of inputs.
Bit Mask
We often use bit masks to manipulate a particular subset of binary bits in a single bitwise operation conveniently. For an integer N, its bit mask is constructed as follows:
The mask has the same number of bits in binary as the integer N.
Bit mask(i) is set if bit N(i) is to be operated; otherwise, mask(i) is 0.
If N(i) is 1, we say bit N(i) is masked.
The mask can separate the binary bits of an integer into two parts. The part selected by the bit mask is examined or modified, and the other part is ignored. If a bit in the bit mask is 1, the corresponding bit in the target variable is chosen. For example, a mask of 0b00110100 (0x34) selects bits 2, 4, and 5 of the target variable.
N = 0xA2 = 0b10100010
Mask = 0x34 = 0b00110100
Bitwise Operator | Symbol | Example |
AND | & | C = N & Mask; |
OR | I | C = N I Mask; |
EXCLUSIVE-OR (EOR) | ^ | C = N ^ Mask; |
NOT | ~ | C = ~N; |
SHIFT RIGHT | >> | C = N >> 2; |
SHIFT LEFT | << | C = N << 2; |
The following gives C and assembly example programs to set, clear, toggle and check bits in a variable.
Checking a Bit (&)
We can check whether a bit is 1 by performing bitwise AND operation with the corresponding mask. In this example, register r2, representing variable b, is non-zero only when bit 5 in register r0 is 1.
char a = 0x34;
char mask = 1<<5;
charb;
// Check bit 5
b = a & mask;
LDR r0, #0x34 ; r0 = a
LDR r1, #(1<<5) ; r1 = mask
ANDS r2, r0, r1 ; r2 = b
; Another solution
LDR r0, #0x34
ANDS r2, r0, #(1<<5)
Setting a Bit
ORR a bit with 1 sets this bit. ORR a bit with 0 does not change it. Therefore, ORR a variable with the mask sets all bits marked by the mask, while keeping all the other bits unchanged.
char a = 0x34;
char mask = 1<<5;
// Set bit 5
a |= mask;
LDR r0, #0x34 ; r0 = a
LDR r1, #(1<<5) ; r1 = mask
ORR r0, r0, r1
; Another solution
LDR r0, #0x34
ORR r0, r0, #(1<<5)
Clearing a Bit (&, ~)
AND a bit with 0 clears this bit. AND a bit with 1 does not change it. Therefore, AND a variable with the negation of the mask clears all data bits marked by the mask.
char a = 0x34;
char mask = 1<<5;
// Reset bit 5
a &= ~mask;
LDR r0, #0x34 ; r0 = a
LDR r1, #(1<<5) ; r1 = mask
MVN r1, r1 ; NOT
ANDS r0, r0, r1
; Another solution
LDR r0, #0x34
BIC r0, #(1<<5)
Toggling a Bit (^)
Exclusive OR (EOR) between 1 and a bit inverts this bit, and exclusive OR between 0 and a bit keeps the bit unchanged. Therefore, exclusive OR between a data and its mask toggles all data bits masked.
char a = 0x34;
char mask = 1<<5;
// Toggle bit 5
a ^= mask;
LDR r0, #0x34 ; r0 = a
LDR r1, #(1<<5) ; r1 = mask
EOR r0, r0, r1
; Another solution
LDR r0, #0x34
EOR r0, r0, #(1<<5)
In C, the Boolean operations are A && B
(Boolean and), A || B
(Boolean or), and !B
(Boolean not), which are different from the above bitwise operations.
The Boolean operators perform word-wide operations, not bitwise. For example,
0x10 & 0x01
equals0x00
, but0x10 && 0x01
equals0x01
.The bitwise negation expression
~0x01
equals0xFFFFFFFE
, but Boolean NOT expression!0x01
equals0x00
.
Using EQU to Define a Mask in Assembly
To make programs easier to read, we often give a name to a mask. For example, we define the bit masks for the clock enable and disable bits for GPIO ports.
RCC_AHB2ENR_GPIOAEN EQU (0x00000001) ; GPIO port A clock enable
RCC_AHB2ENR_GPIOBEN EQU (0x00000002) ; GPIO port B clock enable
RCC_AHB2ENR_GPIOCEN EQU (0x00000004) ; GPIO port C clock enable
LDR r7, =RCC_BASE ; Address of reset and clock control (RCC)
LDR r1, [r7, #RCC_AHB2ENR] ; Load AHB2ENR from memory into r1
ORR r1, r1, #RCC_AHB2ENR_GPIOAEN ; Enable clock of GPIO port A
ORR r1, r1, #RCC_AHB2ENR_GPIOBEN ; Enable clock of GPIO port B
ORR r1, r1, #RCC_AHB2ENR_GPIOCEN ; Enable clock of GPIO port C
STR r1, [r7, #RCC_AHB2ENR] ; Save to RCC->AHB2ENR
By using EQU, the program defines three constants (such as RCC_AHB2ENR_GPIOAEN). These constants are bit masks, which make it easier to manipulate individual bits. It is not a good programming style to set or clear bits directly by using constants instead of a named mask, such as the following instruction.
ORR r1, r1, #0x7 ; Set bits 0, 1, and 2
Updating Program Status Flags in Assembly
The logic operations with S suffix, including ANDS, ORRS, EORS, ORNS, and MVNS update the N, Z, C flags in APSR. None of them affects the V flag. Neither BFC nor BFI updates these four flags.
It is understandable that a logical instruction with S suffix can update the negative and zero flags in APSR. You may wonder how a logic operation can change the carry flag. The carry flag is updated when the second source operand uses the Barrel shifter. For example,
ANDS r0, r1, r2, LSL #3 ; Update N, z, c flags. (V is unchanged)
The carry flag of the above ANDS operation is, in fact, the carry of the LSLS r2, #3
operation.
Reversing the Order of Bits and Bytes
Instructions for reversing the bit or byte orders are useful, particularly when data exchanged between two systems have different formats. For example, the REV instruction is useful to convert data that are exchanged between different endian systems.
Instruction | Description |
RBIT Rd, Rn | Reverse bit order in a word. |
for (i = e; i < 32; i++) Rd[i] = Rn[31- i] |
| REV Rd, Rn | Reverse byte order in a word.
Rd[31:24] = Rn[7:0]
Rd[23:16] = Rn[15:8]
Rd[15:8] = Rn[23:16]
Rd[7:0] = Rn[31:24] |
| REV16 Rd, Rn | Reverse byte order in a word.
Rd[15:8] = Rn[7:0]
Rd[7:0] = Rn[15:8]
Rd[31:24] = Rn[23:16]
Rd[23:16] = Rn[31:24] |
| REVSH Rd, Rn | Reverse byte order in bottom halfword and sign extend.
Rd[15:8] = Rn[7:0]
Rd[7:0] = Rn[15:8]
Rd[31:16] = Rn[7] & 0xFFFF |
The following gives a few examples of changing the bit or the byte order of a value stored in register r0.
LDR r0, =0x12345678 ; r0 = 0x12345678
RBIT r1, r0 ; Reverse bits, r1 = 0x1E6A2C48
LDR r0, =0x12345678 ; r0 = 0x12345678
REV r1, r0 ; Reverse byte order, r2 = 0x78563412
REV16 r2, r0 , ; Reserve byte order in halfwords, r2 = 0x34127856
LDR r0, =0x33448899 ; r0 = 0x33448899
REVSH r1, r0 ; Reverse bytes in Lower halfword and extend sign
; r0 = 0xFFFF9988
Sign and Zero Extension
Most computers represent signed integers in two's complement. When a signed integer is converted to another signed integer with more bits, the sign bit (i.e., the most significant bit or the leftmost bit) should be duplicated to maintain the integer's sign. Duplicating the sign bit is called sign extension.
When an unsigned integer is converted to another unsigned integer with more bits, zero extension is deployed to place zeros in the upper bits of the output.
int_8 a = -1; // a signed 8-bit integer, a = 0xFF
int_16 b = -2; // a signed 16-bit integer, b = 0xFFFE
int_32 c; // a signed 32-bit integer
c = a; // sign extension, c = 0xFFFFFFFF
c = b; // sign extension, c = 0xFFFFFFFE
uint_8 d = 1; // an unsigned 8-bit integer, d = 0x01
uint_32 e; // an unsigned 32-bit integer
e = d; // zero extension, e = 0x00000001
The below table shows the assembly instructions that perform sign and zero extension.
Instruction | Description |
SXTB {Rd,} Rm {,ROR #n} | Sign extend a byte. |
Rd[31:0] = Sign Extend ((Rm ROR(8 * n))[7:0]) |
| SXTH {Rd,} Rm {,ROR #n} | Sign extend a halfword.
Rd[31:0] = Sign Extend ((Rm ROR(8 * n))[15:0]) |
| UXTB {Rd,} Rm {,ROR #n} | Zero extend a byte.
Rd[31:0] = Zero Extend ((Rm ROR(8 * n))[7:0]) |
| UXTH {Rd,} Rm {,ROR #n} | Zero extend a halfword.
Rd[31:0] = Zero Extend ((Rm ROR(8 * n))[15:0]) |
The SXTB
(Sign Extend Byte) instruction takes an 8-bit value from a register, sign-extends it to 32 bits, and stores the result in a destination register. The SXTB {Rd,} Rm {, ROR #n}
variant of this instruction allows for an optional rotation of the source register's value before the sign extension is applied.
; r0 = 0x11228091
SXTB r1, r0 ; r1 = 0xFFFFFF91, sign extend a byte
SXTH r1, r0 ; r1 = 0xFFFF8091, sign extend a halfword
UXTB r1, r0 ; r1 = 0x00000091, zero extend a byte
UXTH r1, r0 ; r1 = 0x00008091, zero extend a halfword
Data Comparison
There are four different data comparison instructions.
Command | Function | Flags |
CMP Rn, Op2 | Compare | Set NZCV flags on Rn - Op2 |
CMN Rn, Op2 | Compare Negative | Set NZCV flags on Rn + Op2 |
TST Rn, Op2 | Test | Set NZC flags on Rn AND Op2 |
TEQ Rn, Op2 | Test Equivalence | Set NZC flags on Rn EOR Op2 |
The
CMP
instruction subtracts the value of Op2 from the value in Rn. It is the same as aSUBS
instruction, except that the processor discards the result. CMP updates the N, Z, C, and V flags per the subtraction result.The
CMN
instruction adds the value of Op2 to the value in Rn.CMN Rn, Op2
is likeADDS Rn, Op2
except that the result is discarded. CMN updates N, Z, C, and V.The instruction
TST Rn, Op2
performs a bitwise AND operation on Rn and Op2. Different fromANDS Rn, Op2
, theTST
instruction discards the result.TST
updates the N and Z flags. If Op2 uses the Barrel shifter,TST
also updates the C flag during the calculation of Op2. However, it does not affect the V flag.The
TEQ
instruction performs a bitwise exclusive OR operation on Rn and Op2.TEQ Rn, Op2
is the same asEOR Rn, Op2
except that the result is discarded.TEQ
updates the N, Z, and C flags.
TEQ and TST have different usages.
TEQ is to check whether two values are equal. After TEQ completes, the zero flag is set if two operands are equal; otherwise, the zero flag is clear.
TST is to exam whether target bits set by the second operand are clear.
TST cannot check the equivalence of two operands.
For example, whenr0 = 0b1010
andr1 = 0b0101
, the instructionTST r0, r1
sets the zero flag because the result of AND is 0. However, these two operands are not equal.
Instruction | r0 | r1 | Action |
TST r0, r1 | 0b1010 | 0b0101 | Set Z flag |
TEQ r0, r1 | 0b1010 | 0b0101 | Clear Z flag |
TST r0, r1 | 0b1010 | 0b1010 | Clear Z flag |
TEQ r0, r1 | 0b1010 | 0b1010 | Set Z flag |
The following gives a few examples of data comparison.
CMP r0, #3 ; Compare r0 with 3
CMN r0, #10 ; Compare r0 with -10
CMP r0, r1 ; Compare r0 with r1
TEQ r0, #'?' ; Compare r0 with ASCII value of '?' (0x3F)
MOV r1, #(1<<31) ; r1 = 0x80000000
TST r0, r1 ; check whether the sign bit is 1
Data Movement between Registers
We can classify instructions for moving data between registers into two categories:
Move data between two general-purpose registers
Move data between a general-purpose register and a special-purpose register
MOV (move) and MVN (move not) are used to copy data between two general-purpose registers.
MRS and MSR move content between special registers and general registers.
Special registers include APSR, IPSR, EPSR, IEPSR, IAPSR, EAPSR, PSR, MSP, PSP, PRIMASK, BASEPRI, BASEPRI_MAX, FAULTMASK, and CONTROL.
Instruction | Description |
MOV Rd, Op2 | Rd = Op2 |
MVN Rd, Op2 | Rd = NOT Op2 |
MRS Rd, spec_reg | Move from special register to general register |
MSR spec_reg, Rm | Move from general register to special register |
MOV and MVN can also load an immediate value into a register.
; examples of MOV and MVN
MOV r4, r5 ; Copy r5 to r4
MVN r4, r5 ; r4 = bitwise Logical NOT of r5
MOV r1, r2, LSL #3 ; r1 = r2 « 3
MOV r0, PC ; Copy PC (r15) to r0
MOV r1, SP ; Copy SP (r14) to r1
; copy a special-purpose register to a general-purpose register
MRS r0 , APSR ; Read flag state into r0
MRS r0, IPSR ; Read exception/interrupt state into r0
MRS r0 , EPSR ; Read execution state into r0
MRS r0, PSR ; Copy combined CPSR, EPSR, and SPSR into r0
; copy a general-purpose register to a special-purpose register
MSR APSR, r 0 ; Write flag state
MSR BASE PRI, r0 ; Write to base priority mask register; Disable
; exceptions with same or Lower priority level
Bit Field Extract
There are two instructions that extract adjacent bits from one register.
Instruction | Description |
SBFX Rd, Rn, #lsb, #width | Signed Bit Field Extract |
Rd[(width-1):0] = Rn[(width+lsb-1):lsb]
Rd[31:width] = Replicate (Rn[width+lsb-1]) |
| UBFX Rd, Rn, #lsb, #width | Unsigned Bit Field Extract
Rd[(width-1):0] = Rn[(width+lsb-1):lsb]
Rd[31:width] = Replicate (0) |
The
#lsb
parameter, ranging from 0 to 31, specifies the starting position.The
#width
parameter, ranging from 1 to(32 - #lsb)
, indicates the number of contiguous bits to be extracted.
UBFX simply places zero in the upper bits, while SBFX duplicates the sign bit. The sign bit, in this case, is not the most significant bit; instead, it is the bit at the position of #width + #lsb - 1
.
The following shows two examples of extracting 8 bits from register r3, starting at bit 4. One has no sign extension, and the other has sign extension.
; Assume r3 = 0x1234CDEF
UBFX r4, r3, #4, #8 ; r4 = 0x000000DE (zero extension)
SBFX r4, r3, #4, #8 ; r4 = 0xFFFFFFDE (sign extention)
Subscribe to my newsletter
Read articles from Ahmed Gouda directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Ahmed Gouda
Ahmed Gouda
Embedded Software Engineer who are interested in Embedded Systems Design, Embedded Software, Electrical Engineering, and Electronics, C\C++ programming, Assembly, Computer Architecture, Microprocessors, Microcontrollers, and Communication Protocols.