¿Cómo se hace una división entera (firmada o sin firmar) en el brazo?
-
27-10-2019 - |
Pregunta
Estoy trabajando en Cortex-A8 y Cortex-A9 en particular. Sé que algunas arquitecturas no vienen con la división entera, pero ¿cuál es la mejor manera de hacerlo aparte de convertir a flotar, dividir, convertir a entero? ¿O es esa la mejor solución?
¡Salud! =)
Solución
El compilador normalmente incluye una división en su biblioteca, GCCLIB, por ejemplo, los he extraído de GCC y los uso directamente:
https://github.com/dwelch67/stm32vld/ luego stm32f4d/aventura/gcclib
Ir a flotar y de regreso probablemente no sea la mejor solución. Puede probarlo y ver qué tan rápido es ... esto es una multiplica, pero podría convertirlo fácilmente en una división:
https://github.com/dwelch67/stm32vld/ luego stm32f4d/float01/vectores.s
Sin embargo, no lo hice tiempo para ver qué tan rápido/lento. Entendido, estoy usando una Cortex-M arriba y estás hablando de una Cortex-A, diferentes extremos del espectro, instrucciones flotantes similares y las cosas de GCC LIB son similares, para la Cortex-M que tengo que construir para el pulgar pero puedes con la misma facilidad de construir para el brazo. En realidad, con GCC, todo debería funcionar automáticamente, no debería necesitar hacerlo como lo hice. Otros compiladores también no deberías hacerlo de la forma en que lo hice en el juego de aventuras anteriores.
Otros consejos
La división por un valor constante se realiza rápidamente haciendo una multiplicia de 64 bits y un cambio de derecha, por ejemplo, así:
LDR R3, =0xA151C331
UMULL R3, R2, R1, R3
MOV R0, R2,LSR#10
Aquí R1 se divide por 1625. El cálculo se realiza así: 64BitReg (R2: R3) = R1*0xa151c331, entonces el resultado es que el superior de 32 bits se despliega por 10:
R1*0xA151C331/2^(32+10) = R1*0.00061538461545751488 = R1/1624.99999980
Puede calcular sus propias constantes a partir de esta fórmula:
x / N == (x*A)/2^(32+n) --> A = 2^(32+n)/N
Seleccione la n más grande, para la cual A <2^32
Algunas copias-pasta de otros lugares para una división entera: básicamente, 3 instrucciones por bit. De este Sitio web, aunque también lo he visto en muchos otros lugares.Este El sitio también tiene una buena versión que puede ser más rápida en general.
@ Entry r0: numerator (lo) must be signed positive
@ r2: deniminator (den) must be non-zero and signed negative
idiv:
lo .req r0; hi .req r1; den .req r2
mov hi, #0 @ hi = 0
adds lo, lo, lo
.rept 32 @ repeat 32 times
adcs hi, den, hi, lsl #1
subcc hi, hi, den
adcs lo, lo, lo
.endr
mov pc, lr @ return
@ Exit r0: quotient (lo)
@ r1: remainder (hi)
Escribí mi propia rutina para realizar una división sin firmar, ya que no pude encontrar una versión sin firmar en la web. Necesitaba dividir un valor de 64 bits con un valor de 32 bits para obtener un resultado de 32 bits.
El bucle interno no es tan eficiente como la solución firmada proporcionada anteriormente, pero esto admite la aritmética sin firmar. Esta rutina realiza una división de 32 bits si la parte alta del numerador (HI) es menor que el denominador (den), de lo contrario se realiza una división completa de 64 bits (HI: LO/Den). El resultado está en LO.
cmp hi, den // if hi < den do 32 bits, else 64 bits
bpl do64bits
REPT 32
adds lo, lo, lo // shift numerator through carry
adcs hi, hi, hi
subscc work, hi, den // if carry not set, compare
subcs hi, hi, den // if carry set, subtract
addcs lo, lo, #1 // if carry set, and 1 to quotient
ENDR
mov r0, lo // move result into R0
mov pc, lr // return
do64bits:
mov top, #0
REPT 64
adds lo, lo, lo // shift numerator through carry
adcs hi, hi, hi
adcs top, top, top
subscc work, top, den // if carry not set, compare
subcs top, top, den // if carry set, subtract
addcs lo, lo, #1 // if carry set, and 1 to quotient
ENDR
mov r0, lo // move result into R0
mov pc, lr // return
Se puede agregar una verificación adicional para las condiciones de contorno y la potencia de 2. Los detalles completos se pueden encontrar en http://www.idwiz.co.za/tips%20and%20tricks/divide.htm
Escribí las siguientes funciones para el ARM GNU
ensamblador. Si no tienes una CPU con udiv/sdiv
Soporte de la máquina, simplemente corte las primeras líneas hasta la etiqueta "0:" en cualquier función.
.arm
.cpu cortex-a7
.syntax unified
.type udiv,%function
.globl udiv
udiv: tst r1,r1
bne 0f
udiv r3,r0,r2
mls r1,r2,r3,r0
mov r0,r3
bx lr
0: cmp r1,r2
movhs r1,r2
bxhs lr
mvn r3,0
1: adds r0,r0
adcs r1,r1
cmpcc r1,r2
subcs r1,r2
orrcs r0,1
lsls r3,1
bne 1b
bx lr
.size udiv,.-udiv
.type sdiv,%function
.globl sdiv
sdiv: teq r1,r0,ASR 31
bne 0f
sdiv r3,r0,r2
mls r1,r2,r3,r0
mov r0,r3
bx lr
0: mov r3,2
adds r0,r0
and r3,r3,r1,LSR 30
adcs r1,r1
orr r3,r3,r2,LSR 31
movvs r1,r2
ldrvc pc,[pc,r3,LSL 2]
bx lr
.int 1f
.int 3f
.int 5f
.int 11f
1: cmp r1,r2
movge r1,r2
bxge lr
mvn r3,1
2: adds r0,r0
adcs r1,r1
cmpvc r1,r2
subge r1,r2
orrge r0,1
lsls r3,1
bne 2b
bx lr
3: cmn r1,r2
movge r1,r2
bxge lr
mvn r3,1
4: adds r0,r0
adcs r1,r1
cmnvc r1,r2
addge r1,r2
orrge r0,1
lsls r3,1
bne 4b
rsb r0,0
bx lr
5: cmn r1,r2
blt 6f
tsteq r0,r0
bne 7f
6: mov r1,r2
bx lr
7: mvn r3,1
8: adds r0,r0
adcs r1,r1
cmnvc r1,r2
blt 9f
tsteq r0,r3
bne 10f
9: add r1,r2
orr r0,1
10: lsls r3,1
bne 8b
rsb r0,0
bx lr
11: cmp r1,r2
blt 12f
tsteq r0,r0
bne 13f
12: mov r1,r2
bx lr
13: mvn r3,1
14: adds r0,r0
adcs r1,r1
cmpvc r1,r2
blt 15f
tsteq r0,r3
bne 16f
15: sub r1,r2
orr r0,1
16: lsls r3,1
bne 14b
bx lr
Hay dos funciones, udiv
para la división Integer Unsigned y sdiv
para la división entera firmada. Ambos esperan un dividendo de 64 bits (firmado o sin firmar) en r1
(Palabra alta) y r0
(palabra baja), y un divisor de 32 bits en r2
. Devuelven el cociente en r0
y el resto en r1
, por lo tanto, puedes definirlos en un C header
como extern
Devolver un entero de 64 bits y enmascarar el cociente y el resto después. Un error (división por 0 o desbordamiento) se indica por un resto que tiene un valor absoluto mayor o igual al valor absoluto del divisor. El algoritmo de división firmado usa la distinción del caso por los signos de dividendos y divisores; Primero no se convierte en enteros positivos, ya que eso no detectaría todas las condiciones de desbordamiento correctamente.