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> % register
If ‘
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> % register
If ‘
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> % register
If ‘
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> % register
If ‘
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> % register
If ‘
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
Rn
register are shifted right one bit position, copying the Carry Flag into the high-order bit position. IfS
is present, the low-order bit is copied into the Carry Flag. IfRd
is absent, the result is stored inRn
. IfRd
is present, the result is stored there andRn
remains 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.