Hallo Leute ,
wie im Betreff schon geschrieben suche ich eine
Assembler Routine für eine Multiplikation
signed24bit oder signed32bit Zahl * signed16bit(1.15)fractional
Laufen soll das ganze mal auf einem AVR m328 ,
also mit Hilfe des Hardware multiplier.
Danke für jede Hilfe bzw. Tipps ...
BG Gerhard
Hallo,
danke für die Links.
Konnte aber nicht um's Verrecken eine Multiplikationsroutine für
vorzeichenbehaftete Zahlen finden:
Hier meine funktionierende "Lösung":
Hab eine u16*u24bit Routine im Netz gefunden (...danke) und um meinen
Funktionswunsch erweitert.
Bitte nicht schimpfen :) , bin kein Assembler Profi.
Bin aber für Verbesserungsvorschläge sehr dankbar.
Code ist für Bascom Inline Assembler.
Hier die Multiplikationsroutine für signed16bit * signed24bit
Das Ergebnis liegt nun in r20:r19:r18:r17:r16
Ich brauche den Integer Teil als vorzeichenbehaftete 4Byte Zahl.
(In Bascom als "Long")
Hier meine Umwandlung:
1
'Test neg_pos
2
clt
3
tst r20
4
brpl _r20_is_positiv
5
ser r21
6
rjmp _ende_r20_pos_neg
7
_r20_is_positiv:
8
clr r21
9
_ende_r20_pos_neg:
10
11
12
'1 x Alles linksschieben , (wegen fract s.15)
13
lsl r16
14
rol r17
15
rol r18
16
rol r19
17
rol r20
18
'...und die unteren 2 Bytes vergessen ....(wegen fract s.15)
19
STS {s},R18
20
STS {s+1},R19
21
STS {s+2},R20
22
STS {s+3},R21
[MOD: bitte beim nächsten Mal eigenständig die avrasm Tags verwenden.
Siehe "Wichtige Regeln - erst lesen, dann posten!"]
Als ich das letzte Mal bei AVRs zuhause war, wurde ich mit
Beispielroutinen regelrecht erschlagen. Ist das seit MicroChip nicht
mehr so?
Den jeweiligen Ansatz kann man aber auch in jede andere,
prozessorspezifische Sprache übersetzen. Natürlich nur, wenn man den
Befehlssatz kennt.
Gerhard schrieb:> Konnte aber nicht um's Verrecken eine Multiplikationsroutine für> vorzeichenbehaftete Zahlen finden:
Hat das Ergebnis die gleiche Breite, wie die Operanden, dann spielt das
Vorzeichen keine Rolle. Der Fehler tritt erst in den höherwertigen Bits
auf.
Peter D. schrieb:> Hat das Ergebnis die gleiche Breite, wie die Operanden, dann spielt das> Vorzeichen keine Rolle. Der Fehler tritt erst in den höherwertigen Bits> auf.
Das ist korrekt, aber auch C, wie es leibt und lebt. Praktisch ziemlich
unbrauchbar. Denn natürlich braucht man auch für diesen Fall wenigstens
eine zuverlässige Information, dass der mögliche Zahlenbereich des
Ergebnisses verlassen wurde...
In Asm ist es jedenfalls ziemlich einfach (und nicht allzu teuer),
das/die störenden Vorzeichen vor der eigentlichen Multiplikation mit der
vollen Bitbreite zu eleminieren und hinterher wieder einzufügen. Man
braucht nur ein einziges Bit, um die Information über die eigentliche
Multiplikation hinweg zu retten, wofür sich natürlich das T-Flag
anbietet.
Der worst case ist, dass ein Faktor und das Produkt negiert werden
müssen. Dafür braucht man aber für die eigentliche Multiplikation nur
eine Inkarnation (das ist besonders relevant für Targets ohne
Hardware-Multiplikation, erleichtert es aber auch für Targets mit
selbiger, denn da entfällt in der Multiplikation selber immerhin etwas
Overhead und die Wahl der Register ist freier, was den Verlust durch die
evtl. nötigen Negationen vor und nach der Multiplikation zumindest zum
Teil kompensiert).
Wie auch immer, grundsätzlich ist die Struktur so:
mov tmp, faktor1msb
xor tmp, faktor2msb
bst tmp,7 ;save result sign in T-Flag
sbrc faktor1msb,7
rjmp chkfaktor2
[negate faktor1]
chkfaktor2:
sbrc faktor2msb,7
rjmp mulu
[negate faktor2]
mulu:
[perform the unsigned multiplikation]
btrc done ;check result sign
[negate the product]
done:
c-hater schrieb:> Das ist korrekt, aber auch C, wie es leibt und lebt. Praktisch ziemlich> unbrauchbar. Denn natürlich braucht man auch für diesen Fall wenigstens> eine zuverlässige Information, dass der mögliche Zahlenbereich des> Ergebnisses verlassen wurde...
Nenne mir auch nur einen Programmierer, der nach jedem Rechenschritt auf
Überlauf prüft. Und was soll die CPU dann machen, anhalten?
Das Ergebnis ist in jedem Fall falsch.
Man macht daher beim Code planen eine Worst-Case Betrachtung, ob ein
Überlauf möglich ist und begrenzt dann z.B. die Eingangswerte oder nimmt
ein geeignetes Zahlenformat.
C wurde eben für den Praktiker geschrieben und nicht für Erbsenzähler.
Gerhard schrieb:> Kleiner Hinweis: anstatt sbrc -->> sbrs faktor1msb,7> sbrs faktor2msb,7
Jepp, das war falsch, außerdem auch:
> xor tmp, faktor2msb
muss natürlich heißen:
eor tmp, faktor2msb
Kommt davon, wenn man's einfach auf die Schnelle im Browser eintippt...
die Multiplikation ist aber etwas altbacken, das geht besser:
Term1 und Term3 (oder Term2 und Term4) (oder Term4 und Term6) sind
voneinander unabhängig, da braucht man nicht mehrfach addieren
Beitrag "Re: Rechnen mit AVR"Beitrag "Re: Rechnen mit AVR"
hier:
' r25:24 * r23:22:21 = r20:19:18:17:16
25x23 20:19 term1
25x22 19:18 term2
25x21 18:17 term3
24x23 19:18 term4
24x22 18:17 term5
24x21 17:16 term6
Der Code bezieht sich auf den Beitrag von Gerhard 03.11.2020 15:44
Habe aus dem Stegreif getippt und glatt die Register bei beiden MOVW
vertauscht, daher ausgiebig testen :-) Korrektur:
1
'; Multiply
2
mul r24,r21 '; term 6
3
movw R16,R0
4
mul r24,r23 '; term 4
5
movw R18,R0
Da der Inhalt von R2 beim Carry-Save-rol-r2 egal ist, kann der erste clr
r2 (vor '; Multiply) entfallen, damit ist der Code um 27-21=6 Befehle
kürzer und schneller.
Es sind sogar 10 Befehle weniger, da man auch R16-R19 nicht löschen
muss, nur R20. Und wenn der MUL das Carry in Ruhe lassen würde, wären es
nochmal 2 weniger.
Weitere Befehle lassen sich einsparen durch:
-- Weglassen von Term6, da die unteren beiden Bytes in dieser Anwendung
nicht ins Endergebnis eingehen (nur ein evtl. Überlauf daraus -> geringe
Ungenauigkeit)
-- Verwendung von MULS (signed), FMUL (fractional) und/oder FMULS
dafür lieber 2 Befehle zum (Auf-)Runden investieren:
nach den MULS und ADDs: