Je travaille en particulier sur Cortex-A8 et Cortex-A9. Je sais que certaines architectures ne comportent pas de division entière, mais quel est le meilleur moyen de le faire, à part convertir en float, diviser, convertir en entier? Ou est-ce bien la meilleure solution?
À votre santé! =)
Le compilateur inclut normalement une division dans sa bibliothèque, gcclib par exemple, je les ai extraites de gcc et les utilise directement:
https://github.com/dwelch67/stm32vld/ then stm32f4d/adventure/gcclib
flotter et revenir n’est probablement pas la meilleure solution. vous pouvez l'essayer et voir à quelle vitesse il est ... Ceci est une multiplication mais pourrait tout aussi bien en faire une fracture:
https://github.com/dwelch67/stm32vld/ puis stm32f4d/float01/vectors.s
Je n'ai pas eu le temps de voir à quelle vitesse/lent. Compris que j'utilise un cortex-m ci-dessus et que vous parlez d'un cortex-a, de différentes extrémités du spectre, d'instructions float similaires et du contenu de gcc lib est similaire, car le cortex-m doit être construit pour le pouce tout aussi facilement construire pour le bras. En fait, avec gcc, tout devrait fonctionner automatiquement, vous ne devriez pas avoir à le faire comme je l’ai fait. D'autres compilateurs également ne devraient pas avoir besoin de le faire comme je l'ai fait dans le jeu d'aventure ci-dessus.
La division par une valeur constante se fait rapidement en effectuant une multiplication de 64 bits et un décalage à droite, par exemple, comme suit:
LDR R3, =0xA151C331
UMULL R3, R2, R1, R3
MOV R0, R2,LSR#10
ici, R1 est divisé par 1625. Le calcul se fait comme suit: 64bitreg (R2: R3) = R1 * 0xA151C331, le résultat est le décalage supérieur droit de 32bit de 10:
R1*0xA151C331/2^(32+10) = R1*0.00061538461545751488 = R1/1624.99999980
Vous pouvez calculer vos propres constantes à partir de cette formule:
x / N == (x*A)/2^(32+n) --> A = 2^(32+n)/N
sélectionnez le plus grand n pour lequel A <2 ^ 32
Quelques copies de pâtes d’ailleurs pour une division entière: Fondamentalement, 3 instructions par bit. De this site web, même si je l’ai déjà vu bien d’autres endroits. This site comporte également une version Nice qui peut être plus rapide en général.
@ 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)
J'ai écrit ma propre routine pour effectuer une division non signée car je ne pouvais pas trouver une version non signée sur le Web. Je devais diviser une valeur de 64 bits avec une valeur de 32 bits pour obtenir un résultat de 32 bits.
La boucle interne n'est pas aussi efficace que la solution signée fournie ci-dessus, mais cela prend en charge l'arithmétique non signée. Cette routine effectue une division de 32 bits si la partie haute du numérateur (hi) est inférieure au dénominateur (den), sinon une division complète de 64 bits est effectuée (hi: lo/den). Le résultat est dans 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
Une vérification supplémentaire des conditions aux limites et de la puissance 2 peut être ajoutée. Vous trouverez tous les détails sur http://www.idwiz.co.za/Tips%20and%20Tricks/Divide.htm
J'ai écrit les fonctions suivantes pour l'assembleur ARM GNU
. Si vous n'avez pas de CPU avec le support machine udiv/sdiv
, coupez les premières lignes jusqu'au label "0:" dans l'une ou l'autre fonction.
.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
Il existe deux fonctions, udiv
pour la division entière non signée et sdiv
pour la division entière signée. Ils s’attendent tous deux à recevoir un dividende 64 bits (signé ou non signé) en r1
(mot élevé) et r0
(mot faible), ainsi qu’un diviseur 32 bits en r2
. Ils renvoient le quotient dans r0
et le reste dans r1
; vous pouvez donc les définir dans un C header
en tant que extern
renvoyant un entier de 64 bits et masquant ensuite le quotient et le reste. Une erreur (division par 0 ou dépassement de capacité) est indiquée par un reste ayant une valeur absolue supérieure ou égale à la valeur absolue du diviseur. L'algorithme de division signée utilise la distinction de casse par les signes de dividende et de diviseur; il ne convertit pas d'abord en nombres entiers positifs, car cela ne détecterait pas correctement toutes les conditions de débordement.