Cond SOffset
5.20 Instruction Set Examples
The following examples show ways in which the THUMB instructions may be used to generate small and efficient code. Each example also shows the ARM equivalent so these may be compared.
5.20.1 Multiplication by a constant using shifts and adds
The following shows code to multiply by various constants using 1, 2 or 3 Thumb instructions alongside the ARM equivalents. For other constants it is generally better to use the built-in MUL instruction rather than using a sequence of 4 or more
instructions.
Thumb ARM
1 Multiplication by 2^n (1,2,4,8,...)
LSL Ra, Rb, LSL #n MOV Ra, Rb, LSL #n
2 Multiplication by 2^n+1 (3,5,9,17,...) LSL Rt, Rb, #n ADD Ra, Rb, Rb, LSL #n ADD Ra, Rt, Rb 3 Multiplication by 2^n-1 (3,7,15,...) LSL Rt, Rb, #n RSB Ra, Rb, Rb, LSL #n SUB Ra, Rt, Rb 4 Multiplication by -2^n (-2, -4, -8, ...)
LSL Ra, Rb, #n MOV Ra, Rb, LSL #n MVN Ra, Ra RSB Ra, Ra, #0
5 Multiplication by -2^n-1 (-3, -7, -15, ...)
LSL Rt, Rb, #n SUB Ra, Rb, Rb, LSL #n SUB Ra, Rb, Rt
6 Multiplication by any C = {2^n+1, 2^n-1, -2^n or -2^n-1} * 2^n
Effectively this is any of the multiplications in 2 to 5 followed by a final shift. This allows the following additional constants to be multiplied.
6, 10, 12, 14, 18, 20, 24, 28, 30, 34, 36, 40, 48, 56, 60, 62 ...
(2..5) (2..5)
Open Access
5.20.2 General purpose signed divide
This example shows a general purpose signed divide and remainder routine in both Thumb and ARM code.
Thumb code
signed_divide
; Signed divide of R1 by R0: returns quotient in R0, ; remainder in R1
; Get abs value of R0 into R3
ASR R2, R0, #31 ; Get 0 or -1 in R2 depending on sign of R0 EOR R0, R2 ; EOR with -1 (0xFFFFFFFF) if negative SUB R3, R0, R2 ; and ADD 1 (SUB -1) to get abs value
; SUB always sets flag so go & report division by 0 if necessary ; BEQ divide_by_zero
; Get abs value of R1 by xoring with 0xFFFFFFFF and adding 1 ; if negative
ASR R0, R1, #31 ; Get 0 or -1 in R3 depending on sign of R1 EOR R1, R0 ; EOR with -1 (0xFFFFFFFF) if negative SUB R1, R0 ; and ADD 1 (SUB -1) to get abs value ; Save signs (0 or -1 in R0 & R2) for later use in determining ; sign of quotient & remainder.
PUSH {R0, R2}
; Justification, shift 1 bit at a time until divisor (R0 value) ; is just <= than dividend (R1 value). To do this shift dividend ; right by 1 and stop as soon as shifted value becomes >.
LSR R0, R1, #1 MOV R2, R3 B %FT0 just_l LSL R2, #1 0 CMP R2, R0 BLS just_l
MOV R0, #0 ; Set accumulator to 0 B %FT0 ; Branch into division loop div_l LSR R2, #1
0 CMP R1, R2 ; Test subtract
BCC %FT0
SUB R1, R2 ; If successful do a real ; subtract
Open Access
0 ADC R0, R0 ; Shift result and add 1 if ; subtract succeeded
CMP R2, R3 ; Terminate when R2 == R3 (ie we have just BNE div_l ; tested subtracting the 'ones' value).
; Now fixup the signs of the quotient (R0) and remainder (R1) POP {R2, R3} ; Get dividend/divisor signs back
EOR R3, R2 ; Result sign
EOR R0, R3 ; Negate if result sign = -1 SUB R0, R3
EOR R1, R2 ; Negate remainder if dividend sign = -1 SUB R1, R2
MOV pc, lr
ARM code
signed_divide
; effectively zero a4 as top bit will be shifted out later ANDS a4, a1, #&80000000
RSBMI a1, a1, #0
EORS ip, a4, a2, ASR #32 ; ip bit 31 = sign of result ; ip bit 30 = sign of a2
RSBCS a2, a2, #0
; central part is identical code to udiv
; (without MOV a4, #0 which comes for free as part of signed ; entry sequence)
MOVS a3, a1
BEQ divide_by_zero just_l
; justification stage shifts 1 bit at a time CMP a3, a2, LSR #1
MOVLS a3, a3, LSL #1
; NB: LSL #1 is always OK if LS succeeds BLO s_loop
div_l
CMP a2, a3 ADC a4, a4, a4 SUBCS a2, a2, a3 TEQ a3, a1
Open Access
BNE s_loop2 MOV a1, a4
MOVS ip, ip, ASL #1 RSBCS a1, a1, #0 RSBMI a2, a2, #0 MOV pc, lr
5.20.3 Division by a constant
Division by a constant can often be performed by a short fixed sequence of shifts, adds and subtracts. For an explanation of the algorithm seeThe ARM Cookbook (ARM DUYI-0005B), section entitiledDivision by a constant.
Here is an example of a divide by 10 routine based on the algorithm in the ARM Cookbook in both Thumb and ARM code.
Thumb code
udiv10
; takes argument in a1
; returns quotient in a1, remainder in a2 MOV a2, a1 LSR a3, a1, #2 SUB a1, a3 LSR a3, a1, #4 ADD a1, a3 LSR a3, a1, #8 ADD a1, a3 LSR a3, a1, #16 ADD a1, a3 LSR a1, #3 ASL a3, a1, #2 ADD a3, a1 ASL a3, #1 SUB a2, a3 CMP a2, #10 BLT %FT0 ADD a1, #1 SUB a2, #10 0 MOV pc, lr
Open Access
ARM code
udiv10
; takes argument in a1
; returns quotient in a1, remainder in a2 SUB a2, a1, #10
SUB a1, a1, a1, lsr #2 ADD a1, a1, a1, lsr #4 ADD a1, a1, a1, lsr #8 ADD a1, a1, a1, lsr #16 MOV a1, a1, lsr #3 ADD a3, a1, a1, asl #2 SUBS a2, a2, a3, asl #1 ADDPL a1, a1, #1
ADDMI a2, a2, #10 MOV pc, lr