Forum: Mikrocontroller und Digitale Elektronik AVR Assembler Frage - Multiplikation in Software


von REdEk (Gast)


Lesenswert?

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?

von spess53 (Gast)


Lesenswert?

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

von H.Joachim S. (crazyhorse)


Lesenswert?

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.

von kopfkratzer (Gast)


Lesenswert?

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 ?

von REdEk (Gast)


Lesenswert?

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:
1
    adc  r1,r19

Könnte man nicht auch folgendes verwenden?
1
    adc r1,0x00

MfG

von REdEk (Gast)


Lesenswert?

Okay, ich merke gerade das geht so nicht.
Der Befehl will ein Register.

Dann danke euch allen für die Hilfe!

MfG

von Amateur (Gast)


Lesenswert?

>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.

von H.Joachim S. (crazyhorse)


Lesenswert?

Richtig.
Du bräuchtest einen Befehl
adci x, i

Aber den gibt es nicht :-)

von Herr M. (herrmueller)


Lesenswert?

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.

von Uwe (Gast)


Lesenswert?

Hi,
>Es gibt ja nicht einmal ein addi.
aber subi ist vorhanden und subi R16,(-5)= addi R16,5

Viel Erfolg, Uwe

von Hannes L. (hannes)


Lesenswert?

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.

...

von c-hater (Gast)


Lesenswert?

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.

von MarioT (Gast)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.