Hallo zusammen, ich arbeite gerade fleißig das AVR Assembler Tutorial
durch (welches neben bei bemerkt wirklich klasse ist!).
Momentan bin ich bei der Multiplikation / Division in Software stecken
geblieben da dies ja ein nicht ganz so einfaches Thema für Neueinsteiger
ist.
Genauer gesagt habe ich eine Frage zur Multiplikation.
Auf der Website wird folgendes Beispiel genannt:
1
ldi r16, 0b00100011 ; Multiplikator
2
ldi r17, 0b10001001 ; Multiplikand
3
; Berechne r16 * r17
4
5
ldi r18, 8 ; 8 mal verschieben und gegebenenfalls addieren
6
clr r19 ; 0 wird für die 16 Bit Addition benötigt
7
clr r0 ; Ergebnis Low Byte auf 0 setzen
8
clr r1 ; Ergebnis High Byte auf 0 setzen
9
10
mult:
11
lsl r0 ; r1:r0 einmal nach links verschieben
12
rol r1
13
lsl r17 ; Das MSB von r17 ins Carry schieben
14
brcc noadd ; Ist dieses MSB (jetzt im Carry) eine 1?
15
add r0,r16 ; Wenn ja, dann r16 zum Ergebnis addieren
16
adc r1,r19
17
18
noadd:
19
dec r18 ; Wurden alle 8 Bit von r17 abgearbeitet?
20
brne mult ; Wenn nicht, dann ein erneuter Verschiebe/Addier Zyklus
21
22
; r0 enthält an dieser Stelle den Wert 0b10111011
23
; r1 enthält 0b00010010
24
; Gemeinsam bilden r1 und r0 also die Zahl
25
; 0b0001001010111011
Meine erste Frage richtet sich an Zeile 6.
Ich verstehe nicht so ganz wofür das r19 gebraucht wird.
In Zeile 16 steht zudem folgender Inhalt: "adc r1,r19"
Nach langen überlegen stelle ich fest: Mit und ohne der Zeile kommt man
auf das Selbe Ergebnis. Wieso steht die Zeile dort?
Als zweites ist mir aufgefallen, dass das Kommentar in Zeile 19 vom r17
handelt, im Befehl allerdings von r18 die Rede ist.
Verstehe ich da was falsch oder wird ein falsches Register im Kommentar
genannt?
Hi
>In Zeile 16 steht zudem folgender Inhalt: "adc r1,r19">Nach langen überlegen stelle ich fest: Mit und ohne der Zeile kommt man>auf das Selbe Ergebnis. Wieso steht die Zeile dort?
Wenn es in der Zeile
add r0,r16
zu einem Übertrag kommt wird dieser zu r1 addiert.
>Als zweites ist mir aufgefallen, dass das Kommentar in Zeile 19 vom r17>handelt, im Befehl allerdings von r18 die Rede ist.
r18 ist der Zähler für die 8 Bit von r17.
MfG Spess
REdEk schrieb:> Nach langen überlegen stelle ich fest: Mit und ohne der Zeile kommt man> auf das Selbe Ergebnis. Wieso steht die Zeile dort?
Aber nur, wenn in r19 tatsächlich eine Null steht.
Der Übertrag der ersten Addition (add r0,r16) muss berücksichtigt
werden. Und genau das passiert mit adc xxx, 0
Mag ja sein, dass im Simulator auch so schon eine Null drinsteht, in der
Realität aber eben nicht unbedingt.
kopfkratz
Was verstehst Du nun nicht ?
r18 ist das Zählregister also die Variable die anzeigt wie oft noch mult
ausgeführt werden soll.
Bei welcher Bedingung passiert das ?
Und r19 steht ja auch in der mult Unterroutine, was wird dort damit
gemacht ?
Wieviele Bits hat den ein Register ?
Was passiert denn wenn Du die letzte Zeile bei mult wegläßt und beide
Werte 0b11111111 sind ?
Was steht denn im letzen Kommentar ?
Danke soweit :)
Also r19 steht die ganze zeit auf 0 wenn ich das richtig verstehe?
Denn es wird mit dem Register nichts gemacht außer einmal auf 0 gesetzt
und dann in Zeile 16 steht da noch dieser Befehl:
>Könnte man nicht auch folgendes verwenden?> adc r1,0x00
Manche verstehen da aber nur Bahnhof.
add rXX,CCCC heißt nicht, dass
adc rXX,CCCC auch existiert.
Es gibt ja nicht einmal ein addi.
Bei längeren Assembler Programmen definiere ich mir ein 'niedriges'
Register mit NULL und lasse dort immer eine 0 gespeichert. Das kann man
dann immer in solchen Fällen nehmen. Da kommt dann meist einiges
zusammen, was an Befehlen eingespart werden kann.
Herr Mueller schrieb:> Bei längeren Assembler Programmen definiere ich mir ein 'niedriges'> Register mit NULL und lasse dort immer eine 0 gespeichert.
Das mache ich auch so, das rentiert sich bereits bei der
Adressberechnung für Tabellen im Flash. Dazu noch ein unteres Register
für sie Sicherung des SREG in ISRs, sowie ein paar Exklusiv-Register für
die ISR, was Push/Pop-Orgien spart und die ISRs kürzer und schneller
macht.
...
REdEk schrieb:> Hallo zusammen, ich arbeite gerade fleißig das AVR Assembler Tutorial> durch (welches neben bei bemerkt wirklich klasse ist!).
Ja, ist es wirklich. Jedem Asm-Einsteiger kann nur empfohlen werden, es
wirklich durchzuarbeiten, solange, bis er wirklich versteht, was da
jeweils passiert.
> Meine erste Frage richtet sich an Zeile 6.> Ich verstehe nicht so ganz wofür das r19 gebraucht wird.> In Zeile 16 steht zudem folgender Inhalt: "adc r1,r19">> Nach langen überlegen stelle ich fest: Mit und ohne der Zeile kommt man> auf das Selbe Ergebnis. Wieso steht die Zeile dort?
Weil R19 beim Eintritt in die Multiplikation höchstens mal zufällig den
Inhalt Null hat, zur korrekten Funktion des Algorithmus es aber zwingend
erforderlich ist, daß der Wert in dem Register Null ist. Also muß
expizit initialisiert werden. Man kann das natürlich (meistens) auch
anders lösen, indem man ein Register für den Wert Null programmweit
reserviert, sinnvollerweise eins zwischen R2 und R15. Man muß halt bloß
strengstens darauf achten, dieses "reservierte" Register auch
tatsächlich nirgendwo mit einem anderen Inhalt zu beschreiben, sonst
geht plötzlich garnix mehr...
Wie auch immer, bei der Laufzeit der Routine aus dem Tutorial (zwischen
67 und 75 Takten) spielt der eine Takt für die Initialisierung von R19
sowieso nur noch eine recht untergeordnete Rolle.
Das geht deutlich schneller. Dieser Algorithmus ist nämlich geradezu ein
Paradebeispiel für die sinnvolle Anwendung einer Optimierungstechnik
namens "loop unrolling". Die Hinweise darauf sind sehr deutlich: der
eigentliche Nutzcode in der Schleife umfaßt nur sehr wenige
Instruktionen, die Zahl der Schleifendurchläufe ist 1) zur Entwurfszeit
bekannt und 2) ziemlich klein.
Nach (etwas trickreicher) Anwendung dieser Optimierung kommt man zu
einem Code, der vom Speicherplatzbedarf her zwar ca. 3,5mal größer ist,
dafür aber auch nur ca. 60% der Rechenzeit benötigt. Und die ist dann
sogar konstant, egal welche Werte miteinander multipliziert werden.
Nettes Feature, ganz nebenbei gewonnen...
Und das geht so:
.MACRO MULDIGIT ;ich bin schreibfaul, deswegen ein macro
sbrc R16,@0
add R0,R17
adc R1,R15
.IF @0 > 0
lsl R0
rol R1
.ENDIF
.ENDMACRO
;->R16,R17: Faktoren
;<-R1:R0: Produkt
mulu8x8:
clr R0
clr R1 ;nötig für Trick "konstante Laufzeit ohne Straftakte"
clr R15 ;NULL
clc ;ebenfalls nötig für den Trick
MULDIGIT 7
MULDIGIT 6
MULDIGIT 5
MULDIGIT 4
MULDIGIT 3
MULDIGIT 2
MULDIGIT 1
MULDIGIT 0
ret
Die (fast-) Halbierung der Rechenzeit ist sehr interessant, die (fast-)
Vervierfachung der Codegröße hingegen natürlich nicht so nett.
Wenn man nun aber diese optimierte Routine als Grundlage für
Multiplikationen größerer Wortbreiten benutzt (in erster Näherung durch
call der immer gleichen Routine, aber Vorsicht: calls sind auch relativ
"teuer", auch das geht wieder noch besser, die Mul-Routine endet dann
mit einem "ijmp"...), wird es noch viel spannender. Dann wirkt sich
nämlich (über alles betrachtet), die Codevergrößerung mit zunehmender
Wortbreite immer weniger schädlich aus, die Rechenzeitersparnis aber
geht relativ dazu nur sehr viel weniger zurück, weil der Hauptteil der
Rechenzeit halt in der eigentlichen Multiplikation zugebracht wird.
> Als zweites ist mir aufgefallen, dass das Kommentar in Zeile 19 vom r17> handelt, im Befehl allerdings von r18 die Rede ist.> Verstehe ich da was falsch oder wird ein falsches Register im Kommentar> genannt?
Tippfehler im Kommentar. Sowas passiert. In dem Moment, wo du erkannt
hast, daß hier etwas nicht stimmen kann, war die schlimmste Gefahr schon
vorbei.
REdEk schrieb:> dec r18 ; Wurden alle 8 Bit von r17 abgearbeitet?c-hater schrieb:> Tippfehler im Kommentar. Sowas passiert.
Wirklich Tippfehler?
REdEk
Glaube nicht alles was hier geschrieben wird.
MarioT schrieb:>> dec r18 ; Wurden alle 8 Bit von r17 abgearbeitet?> Wirklich Tippfehler?
Nein, Lesefehler meinerseits. R18 ist der Schleifenzähler. Und der
kontrolliert natürlich letztlich tatsächlich, wieviel Bits von R17
verwendet werden. Der Kommentar stellt also korrekt den Sachverhalt dar.