Come si fa a fare numero intero (con o senza segno) la divisione su ARM?
-
27-10-2019 - |
Domanda
Sto lavorando su Cortex-A8 e Cortex-A9 in particolare. So che alcune architetture non sono dotati di divisione intera, ma qual è il modo migliore per farlo diverso convertito al galleggiante, dividere, convertito intero? O è che in effetti la soluzione migliore?
Cheers! =)
Soluzione
Il compilatore comprende normalmente una frattura nella sua libreria, gcclib per esempio li ho estratto da gcc e utilizzarli direttamente:
https://github.com/dwelch67/stm32vld/ poi stm32f4d / avventura / gcclib
andare a galleggiare e ritorno non è probabilmente la soluzione migliore. si può provare e vedere come veloce è ... Questo è un moltiplicano, ma potrebbe facilmente rendere un divario:
https://github.com/dwelch67/stm32vld/ poi stm32f4d / float01 / vettori. s
I tempo non ha ancora, però per vedere quanto veloce / lento. Capito Sto usando una corteccia m sopra e si sta parlando di una corteccia-a, diverse le estremità dello spettro, le istruzioni galleggiante simili e la roba gcc lib è simile, per la corteccia-m devo costruire per pollice, ma si può altrettanto facilmente costruire per il braccio. In realtà con gcc dovrebbe tutto solo lavorare automagicamente non dovreste aver bisogno di farlo nel modo in cui ho fatto. Altri compilatori così non dovreste aver bisogno di farlo nel modo che ho fatto nel gioco di avventura di cui sopra.
Altri suggerimenti
La divisione per un valore costante è fatto rapidamente facendo un 64bit-moltiplicano e lo spostamento a destra, per esempio, in questo modo:
LDR R3, =0xA151C331
UMULL R3, R2, R1, R3
MOV R0, R2,LSR#10
qui R1 viene diviso per 1625. Il calcolo viene effettuato in questo modo: 64bitreg (R2: R3) = R1 * 0xA151C331, allora il risultato è il 32bit superiore destro spostata di 10:
R1*0xA151C331/2^(32+10) = R1*0.00061538461545751488 = R1/1624.99999980
È possibile calcolare le proprie costanti da questa formula:
x / N == (x*A)/2^(32+n) --> A = 2^(32+n)/N
selezionare il più grande n, per cui A <2 ^ 32
Alcuni copia-paste da altrove per una divisione intera: Fondamentalmente, 3 Istruzioni per bit. Da questo sito , anche se ho visto molti altri posti. Questo sito ha anche una versione bella che può essere più veloce in generale .
@ 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)
ho scritto la mia routine per eseguire una divisione senza segno come non ho potuto trovare una versione senza segno sul web. Ho bisogno di dividere un valore a 64 bit con un valore di 32 bit per ottenere un risultato a 32 bit.
Il ciclo interno non è così efficiente come la soluzione firmata riportata, ma questo non supporto aritmetica senza segno. Questa routine esegue una divisione 32 bit se la parte alta del numeratore (hi) è minore del denominatore (den), altrimenti viene eseguita una divisione completa a 64 bit (hi: lo / den). Il risultato è a 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
controllo extra per condizioni al contorno e potenza di 2 può essere aggiunto. I dettagli completi sono disponibili all'indirizzo http://www.idwiz.co.za /Tips%20and%20Tricks/Divide.htm
ho scritto le seguenti funzioni per l'assemblatore ARM GNU
. Se non si dispone di una CPU con supporto della macchina udiv/sdiv
, basta tagliare le prime righe fino alla "0". Label sia in funzione
.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
Ci sono due funzioni, udiv
per la divisione intero senza segno e sdiv
per la divisione intero con segno. Entrambi si aspettano un dividendo a 64 bit (firmato o non firmato) in r1
(alta parola) e r0
(low word), e una a 32 bit divisore r2
. Restituiscono il quoziente in r0
e il resto in r1
, così si possono definire in C header
come extern
restituendo un intero a 64 bit e mascherare il quoziente e resto in seguito. Un errore (divisione per 0 o overflow) è indicato da un residuo avente un maggiore valore assoluto o uguale al valore assoluto del divisore. L'algoritmo di divisione firmato utilizza caso distinzione per i segni di entrambi dividendo e divisore; che non si converte in numeri interi positivi prima, dal momento che non consentono di rilevare tutte le condizioni di overflow correttamente.