.global ulmuldiv,uldiv,ullxul,ulxull
/* UNGETESTET!
typedef struct{unsigned long quot,rem;} uldiv_t;
*/
// Für ATtiny ohne eingebauten Multiplikationsbefehl:
// Multiplikation und Division „in einem Aufwasch“ mit 64-Bit-Zwischenergebnis
// Ohne Rundung; dazu ggf. den Divisionsrest auswerten!
// 1. Argument = Faktor 1 R25:R24:R23:R22
// 2. Argument = Faktor 2 R21:R20:R19:R18
// 3. Argument = Divisor R17:R16:R15:R14
// Return = uldiv_t: Quotient R21:R20:R19:R18
// Rest R25:R24:R23:R22
// Versaut: X,Z (stört nur bei Aufruf von Assembler)
// Takte: ≈ 1200
ulmuldiv:
push YL // Rundenzähler
// 1. Multiplikation
movw XL,r22 // Faktor1 wegschaffen
movw ZL,r24
clr r25 // Ergebnis freimachen (für Produkt = Rest:Quotient)
clr r24
clr r23
sub r22,r22 // löscht C-Flag gleich mit
ldi YL,32
// Faktor1 = R21:R20:R19:R18
// Faktor2 = R31:R30:R27:R26
// Produkt = R25:R24:R23:R22 : R21:R20:R19:R18
0: ror r25 // Produkt High-Teil, C-Flag der Addition einschieben
ror r24
ror r23
ror r22
ror r21 // Produkt Low-Teil = Faktor1
ror r20
ror r19
ror r18
brcc 1f // Faktor1-Bit: Keine Addition wenn gelöscht
add r22,XL // Faktor2 addieren
adc r23,XH
adc r24,ZL
adc r25,ZH
1: dec YL
brne 0b
// Das Produkt liegt (bereits) um 1 Bit nach links geschoben vor, und das Bit 31 ist im C-Flag.
// Damit entfällt das 1. Schieben in der Divisionsroutine, und es wird hineingesprungen.
pop YL
ldi XL,32 // Faktor2 wird nicht mehr gebraucht
rjmp 1f
// Divisionsroutine „wie man DIV vom x86 her kennt“ aber ohne Exception (Divisor 0 oder ≥ EDX)
// 1. Argument = Dividend R25:R24:R23:R22 : R21:R20:R19:R18 (entsprechend EDX:EAX)
// 2. Argument = Divisor R17:R16:R15:R14 (entsprechend DIV-Operand)
// Return = uldiv_t: Quotient R21:R20:R19:R18 (entsprechend EAX)
// Rest R25:R24:R23:R22 (entsprechend EDX)
// Versaut: XL=0 (für Aufruf von Assembler, für gcc dürfte X und Z zerstört sein)
// Takte: ≈ 700
// Sonderfälle: Bei 0÷0 kommt 0 Rest 0 'raus
// Ist der Dividend zu groß (auch bei ≠0/0) kommt 0xFFFFFFFF Rest irgendwas 'raus
uldiv:
ldi XL,32
0: lsl r18 // 64 Bit linksschieben
rol r19
rol r20
rol r21
rol r22
rol r23
rol r24
rol r25
1: brcs 2f // Bei C-Flag immer subtrahieren
cp r22,r14 // Subtraktion prüfen
cpc r23,r15
cpc r24,r16
cpc r25,r17
brcs 3f // Subtrahend zu groß: Nicht subtrahieren
2: sub r22,r14 // Subtraktion ausführen
sbc r23,r15
sbc r24,r16
sbc r25,r17
ori r18,1 // Bit 0 setzen
3: dec XL
brne 0b
ret
// „Asymmetrische“ Multiplikationsroutine, nicht auf Geschwindigkeit getrimmt
// (Für Geschwindigkeit macht man besser eine 64-Bit-Addition in 32 Runden, das braucht 4 weitere Register.)
// 1. Argument = Faktor1 R25:R24:R23:R22 : R21:R20:R19:R18
// 2. Argument = Faktor2 R17:R16:R15:R14
// Return = Produkt Z:X : R25:R24:R23:R22 : R21:R20:R19:R18
// Versaut: -
// Darf nicht überlaufen! High-Teil geht (in Z:X) „verloren“
// Takte: ≈ 1300
ullxul:
push YL // Rundenzähler
clr ZH
clr ZL
clr XH
sub XL,XL // löscht C-Flag gleich mit
ldi YL,65 // 1× mehr schieben um C-Flag auszuschieben
0: brcc 1f
add XL,r14
adc XH,r15
adc ZL,r16
adc ZH,r17
1: ror ZH // 96 Bit (!) schieben
ror ZL
ror XH
ror XL
ror r25
ror r24
ror r23
ror r22
ror r21
ror r20
ror r19
ror r18
dec YL
brne 0b
pop YL
ret
// „Asymmetrische“ Multiplikationsroutine, schneller als ullxul
// Ein Aufrufmakro muss dafür sorgen, dass der 32-Bit-Faktor1 auf 64 Bit erweitert wird.
// 1. Argument = Faktor1 0 : 0 : 0 : 0 : R21:R20:R19:R18
// 2. Argument = Faktor2 R17:R16:R15:R14 : R13:R12:R11:R10
// Return = Produkt Z:X : R25:R24:R23:R22 : R21:R20:R19:R18
// Versaut: -
// Darf nicht überlaufen! High-Teil geht (in Z:X) „verloren“
// Takte: ≈ 800
ulxull:
push YL // Rundenzähler
movw ZL,r26 // Ich verlasse mich darauf, dass Nullen übergeben wurden!
movw XL,r26 // Sonst kommt eh Murks 'raus.
clc
ldi YL,33 // 1× mehr schieben um C-Flag auszuschieben
0: brcc 1f
add r22,r10
adc r23,r11
adc r24,r12
adc r25,r13
adc XL,r14
adc XH,r15
adc ZL,r16
adc ZH,r17
1: ror ZH // 96 Bit (!) schieben
ror ZL
ror XH
ror XL
ror r25
ror r24
ror r23
ror r22
ror r21
ror r20
ror r19
ror r18
dec YL
brne 0b
pop YL
ret
Detected encoding: UTF-8 | 0
|