Section 14.3 Shifting Bits
You may wonder why the mov instruction does not include the shift forms that you have seen in most other instructions. Actually, you could use a mov instruction and follow the pattern used for other instructions (for example, add), but because shifts are so commonly used, the ARM instruction set provides specific shift instructions. Using either a shift instruction or a mov with the corresponding shift options will produce the same machine code. But the explicit shift instructions are considered standard usage.
ASR-
Arithmetic Shift Right.
ASR{S}{<c>} {<Rd>,} <Rn>, #<const> % immediate ASR{S}{<c>} {<Rd>,} <Rn>, <Rm> % registerIf ‘
S’ is present the condition flags are updated according to the result. If absent, the condition flags are not changed.<c>is the condition code, Table 9.2.1.<Rd>specifies the destination register,<Rn>is the source register, and<Rm>is the shift register.\(1 \le const \le 32\text{.}\)
All 32 bits in the
<Rn>register are shifted right, copying the sign bit into the high-order bit positions. The amount of shift is<const>in the “immediate” form or the value in<Rm>in the “register” form. If<Rd>is absent, the result is stored in<Rn>. If<Rd>is present, the result is stored there and<Rn>remains unchanged. LSR-
Logical Shift Right.
LSR{S}{<c>} {<Rd>,} <Rn>, #<const> % immediate LSR{S}{<c>} {<Rd>,} <Rn>, <Rm> % registerIf ‘
S’ is present the condition flags are updated according to the result. If absent, the condition flags are not changed.<c>is the condition code, Table 9.2.1.<Rd>specifies the destination register,<Rn>is the source register, and<Rm>is the shift register.\(1 \le const \le 32\text{.}\)
All 32 bits in the
<Rn>register are shifted right, storing zeros in the vacated high-order bit positions. The amount of shift is<const>in the “immediate” form or the value in<Rm>in the “register” form. If<Rd>is absent, the result is stored in<Rn>. If<Rd>is present, the result is stored there and<Rn>remains unchanged. LSL-
Logical Shift Left.
LSL{S}{<c>} {<Rd>,} <Rn>, #<const> % immediate LSL{S}{<c>} {<Rd>,} <Rn>, <Rm> % registerIf ‘
S’ is present the condition flags are updated according to the result. If absent, the condition flags are not changed.<c>is the condition code, Table 9.2.1.<Rd>specifies the destination register,<Rn>is the source register, and<Rm>is the shift register.\(1 \le const \le 32\text{.}\)
All 32 bits in the
<Rn>register are shifted left, storing zeros in the vacated low-order bit positions. The amount of shift is<const>in the “immediate” form or the value in<Rm>in the “register” form. If<Rd>is absent, the result is stored in<Rn>. If<Rd>is present, the result is stored there and<Rn>remains unchanged. ROR-
Rotate Right.
ROR{S}{<c>} {<Rd>,} <Rn>, #<const> % immediate ROR{S}{<c>} {<Rd>,} <Rn>, <Rm> % registerIf ‘
S’ is present the condition flags are updated according to the result. If absent, the condition flags are not changed.<c>is the condition code, Table 9.2.1.<Rd>specifies the destination register,<Rn>is the source register, and<Rm>is the shift register.\(1 \le const \le 32\text{.}\)
All 32 bits in the
<Rn>register are shifted right, copying the low-order bits into the high-order bit positions as they are shifted. The amount of shift is<const>in the “immediate” form or the value in<Rm>in the “register” form. If<Rd>is absent, the result is stored in<Rn>. If<Rd>is present, the result is stored there and<Rn>remains unchanged. RRX-
Rotate Right with eXtend.
RRX{S}{<c>} {<Rd>,} <Rn>, <Rm> % registerIf ‘
S’ is present the condition flags are updated according to the result. If absent, the condition flags are not changed.<c>is the condition code, Table 9.2.1.<Rd>specifies the destination register,<Rn>is the source register, and<Rm>is the shift register.
All 32 bits in the
Rnregister are shifted right one bit position, copying the Carry Flag into the high-order bit position. IfSis present, the low-order bit is copied into the Carry Flag. IfRdis absent, the result is stored inRn. IfRdis present, the result is stored there andRnremains unchanged.
In the solution to Exercise 4.5.1 we used an algorithm for converting a hexadecimal text string to an integer. The algorithm shifts the integer subtotal four bits to the left, converts the next hexadecimal character to the corresponding integer, and adds it to the subtotal. In this section we separate the algorithm into its own function, as shown in Listing 14.3.1. We use the writeLn function from Exercise 13.3.2 and the readLn function from Exercise 13.3.4. You will learn how to convert an integer to its corresponding decimal text string in Section 14.5 so we will use printf in this program.
/* hexConvert1.c
* Prompts user for hex number and converts
* it to an int.
* 2017-09-29: Bob Plantz
*/
#include <stdio.h>
#include "hexToInt.h"
int writeStr(char *);
int readLn(char *, int);
int main()
{
int theNumber;
char theString[9];
writeStr("Enter up to 32-bit hex number: ");
readLn(theString, 9);
theNumber = hexToInt(theString);
printf("The integer is: %i\n", theNumber);
return 0;
}
hexToInt function in Listing 14.3.3. (C)/* hexToInt.h * Converts hex character string to int. * 2017-09-29: Bob Plantz */ #ifndef HEXTOINT_H #define HEXTOINT_H int hexToInt(char *); #endif
hexToInt function in Listing 14.3.3. (C)/* hexToInt.c
* Converts hex character string to int.
* 2017-09-29: Bob Plantz
*/
#include "hexToInt.h"
#define GAP 7
#define NO_ASCII 0xf
int hexToInt(char *stringPtr)
{
int accumulator = 0;
char current;
current = *stringPtr;
while (current != '\0') {
accumulator = accumulator << 4;
if (current > '9') // check for alpha
current -= GAP;
current &= NO_ASCII; // strip ASCII part
accumulator += (int)current;
stringPtr++;
current = *stringPtr;
}
return accumulator;
}
The compiler-generated assembly language for the conversion algorithm is shown in Listing 14.3.4.
.arch armv6
.file "hexToInt1.c"
.text
.align 2
.global hexToInt
.syntax unified
.arm
.fpu vfp
.type hexToInt, %function
hexToInt:
@ args = 0, pretend = 0, frame = 16
@ frame_needed = 1, uses_anonymous_args = 0
@ link register save eliminated.
str fp, [sp, #-4]!
add fp, sp, #0
sub sp, sp, #20
str r0, [fp, #-16]
mov r3, #0
str r3, [fp, #-8]
ldr r3, [fp, #-16]
ldrb r3, [r3]
strb r3, [fp, #-9]
b .L2
.L4:
ldr r3, [fp, #-8]
mov r3, r3, asl #4 @@ left shift accumulator
str r3, [fp, #-8]
ldrb r3, [fp, #-9] @ zero_extendqisi2
cmp r3, #57 @@ check for alpha
bls .L3
ldrb r3, [fp, #-9]
sub r3, r3, #7 @@ alpha, remove gap
strb r3, [fp, #-9]
.L3:
ldrb r3, [fp, #-9]
and r3, r3, #15 @@ mask off ASCII part
strb r3, [fp, #-9]
ldrb r3, [fp, #-9] @ zero_extendqisi2
ldr r2, [fp, #-8]
add r3, r2, r3 @@ add in new four bits
str r3, [fp, #-8]
ldr r3, [fp, #-16]
add r3, r3, #1 @@ increment string pointer
str r3, [fp, #-16]
ldr r3, [fp, #-16]
ldrb r3, [r3]
strb r3, [fp, #-9]
.L2:
ldrb r3, [fp, #-9] @ zero_extendqisi2
cmp r3, #0
bne .L4
ldr r3, [fp, #-8]
mov r0, r3
sub sp, fp, #0
@ sp needed
ldr fp, [sp], #4
bx lr
.ident "GCC: (Raspbian 6.3.0-18+rpi1) 6.3.0 20170516"
The algorithm used by the function in Listing 14.3.1 is straightforward, but it uses local variables on the stack and does not take advantage of the conditional execution feature of the ARM architecture. My assembly language solution in Listing 14.3.5 uses registers for local variables and the conditional execution option to implement a simple if-else construct.
@ hexToInt2.s
@ Converts a hex text string to an int.
@ Calling sequence:
@ r0 <- address of string
@ bl hexToInt
@ returns equivalent int
@ 2017-09-29: Bob Plantz
@ Define my Raspberry Pi
.cpu cortex-a53
.fpu neon-fp-armv8
.syntax unified @ modern syntax
@ Constant for assembler
.equ gap,7 @ between alpha and numerical
.equ NUL,0
.equ no_ascii,0xf
@ The program
.text
.align 2
.global hexToInt
.type hexToInt, %function
hexToInt:
sub sp, sp, 24 @ space for saving regs
@ (keeping 8-byte sp align)
str r4, [sp, 4] @ save r4
str r5, [sp, 8] @ r5
str r6, [sp,12] @ r6
str fp, [sp, 16] @ fp
str lr, [sp, 20] @ lr
add fp, sp, 20 @ set our frame pointer
mov r4, r0 @ string pointer
mov r5, 0 @ accumulator = 0;
loop:
ldrb r6, [r4] @ get char
cmp r6, NUL @ end of string?
beq allDone @ yes
lsl r5, r5, 4 @ room for four bits
cmp r6, '9 @ alpha char?
subhi r6, r6, gap @ yes, remove gap
and r6, r6, no_ascii @ strip off acsii
add r5, r5, r6 @ add in the four bits
add r4, r4, 1 @ next char
b loop
allDone:
mov r0, r5 @ return accumulator;
ldr r4, [sp, 4] @ restore r4
ldr r5, [sp, 8] @ r5
ldr r6, [sp,12] @ r6
ldr fp, [sp, 16] @ fp
ldr lr, [sp, 20] @ lr
add sp, sp, 24 @ sp
bx lr @ return
The cmp instruction in the sequence:
cmp r6, '9 @ alpha char? subhi r6, r6, gap @ yes, remove gap and r6, r6, no_ascii @ strip off acsii
compares the current character with the ASCII ‘9’. The sub instruction includes the hi option, so it is only executed if the character is higher in the ASCII sequence than a ‘9’. Thus the gap in the ASCII code between ‘9’ and ‘a’ is only subtracted if the character is a hexadecimal alpha character.
Conversion of an integer to the hex text string that represents it is a little more complex. We need to allocate memory space for the text string. The program in Listing 14.3.6 asks the user to enter a hexadecimal number, adds \(1\) to the number, and displays the result in hexadecimal. This problem could, of course, be solved using only hexadecimal characters, but the addition is made much simpler by converting to the binary integer format, and then converting the result to hexadecimal characters.
@ incrementHex.s
@ Prompts user for hex number and adds 1 to it
@ 2017-09-29: Bob Plantz
@ Define my Raspberry Pi
.cpu cortex-a53
.fpu neon-fp-armv8
.syntax unified @ modern syntax
@ Constant for assembler
.equ maxChars,9 @ max input chars
.equ inString,-16 @ for input string
.equ outString,-28 @ for output string
.equ locals,24 @ space for local vars
@ Constant program data
.section .rodata
.align 2
prompt:
.asciz "Enter up to 32-bit hex number: "
display:
.asciz "Adding 1 gives: "
@ The program
.text
.align 2
.global main
.type main, %function
main:
sub sp, sp, 8 @ space for fp, lr
str fp, [sp, 0] @ save fp
str lr, [sp, 4] @ and lr
add fp, sp, 4 @ set our frame pointer
sub sp, sp, locals @ for local vars
ldr r0, promptAddr @ prompt user
bl writeStr
add r0, fp, inString @ place for user input
mov r1, maxChars @ limit input size
bl readLn
add r0, fp, inString @ user input
bl hexToInt @ convert it
add r1, r0, 1 @ add one to the int
add r0, fp, outString @ place for result
bl intToHex @ convert to string
ldr r0, displayAddr @ show user result
bl writeStr
add r0, fp, outString
bl writeStr
bl newLine @ looks nicer
mov r0, 0 @ return 0;
add sp, sp, locals @ deallocate local var
ldr fp, [sp, 0] @ restore caller fp
ldr lr, [sp, 4] @ lr
add sp, sp, 8 @ and sp
bx lr @ return
promptAddr:
.word prompt
displayAddr:
.word display
intToHex function is shown in Listing 14.3.7 and the hexToInt function in Listing 14.3.5. (prog asm)@ intToHex.s
@ Converts 32-bit int to a hex text string.
@ Calling sequence:
@ r0 <- address of place to store string
@ r1 <- int to convert
@ bl intToHex
@ returns equivalent int
@ 2017-09-29: Bob Plantz
@ Define my Raspberry Pi
.cpu cortex-a53
.fpu neon-fp-armv8
.syntax unified @ modern syntax
@ Constant for assembler
.equ gap,7 @ between alpha and numerical
.equ to_ascii,0x30
.equ NUL,0 @ NUL for end of string
@ The program
.text
.align 2
.global intToHex
.type intToHex, %function
intToHex:
sub sp, sp, 24 @ space for saving regs
@ (keeping 8-byte sp align)
str r4, [sp, 4] @ save r4
str r5, [sp, 8] @ r5
str r6, [sp,12] @ r6
str fp, [sp, 16] @ fp
str lr, [sp, 20] @ lr
add fp, sp, 20 @ set our frame pointer
mov r4, r0 @ string pointer
mov r5, r1 @ number to convert
mov r6, 0 @ counter = 0;
mov r0, '0 @ hex prefix in string
strb r0, [r4] @ store the char
add r4, r4, 1 @ next char
mov r0, 'x
strb r0, [r4] @ store the char
add r4, r4, 1
loop:
cmp r6, 8 @ all bits?
beq allDone @ yes
ror r5, r5, 28 @ no, get next 4 bits
and r0, r5, 0xf @ isolate the four bits
cmp r0, 9 @ need alpha char?
addhi r0, r0, gap @ yes, add gap
add r0, r0, to_ascii @ convert to acsii
strb r0, [r4] @ store the char
add r4, r4, 1 @ next char location
add r6, r6, 1 @ next four bits
b loop
allDone:
mov r0, NUL
strb r0, [r4] @ NUL char
mov r0, 0 @ return 0;
ldr r4, [sp, 4] @ restore r4
ldr r5, [sp, 8] @ r5
ldr r6, [sp,12] @ r6
ldr fp, [sp, 16] @ fp
ldr lr, [sp, 20] @ lr
add sp, sp, 24 @ sp
bx lr @ return
intToHex function converts a 32-bit int to the corresponding hexadecimal text string.The text string must be ordered from left to right, so we start with the high-order four bits. The ror instruction here:
ror r5, r5, 28 @ no, get next 4 bits and r0, r5, 0xf @ isolate the four bits
moves the high-order four bits into the lowest four-bit position in the register, while keeping the 32-bit pattern the same. We isolate the four bits with the and instruction and place the result in r0, where we can “build” the ASCII character. Some instruction set architectures have a rotate left instruction (e.g., rol on the Intel x86) but it really is not needed because rotation is a circular operation.
