Multiplikation_in_Assembler

OP #6683653
Lesenswert?

Guten Morgen,
hier gibt es Artikel: Multiplikation_in_Assembler.
https://www.mikrocontroller.net/articles/Multiplikation_in_Assembler

Ich wollte eine Funktion ausprobieren, und komme leider nicht zurecht...
Zuerst die Funktion aus dem Artikel:
1
;******************************************************************************
2
;*  FUNCTION  muls24x8_32.inc
3
;*  Mutiply   24x8 -> 32 bit     (signed * signed)
4
;*
5
;*      AH  AM  AL    BL     E3 E2 E1 E0
6
;*      R22:R21:R20 x R16 -> R5:R4:R3:R2
7
;*  
8
;*  Scrached Register:  -
9
;*  19 cycles + 4 (RET) = 23 Cycles
10
;*
11
;******************************************************************************
12
muls_24x8:
13
    push  R10
14
    clr    R10
15
    mulsu  R16,R20      ; AL x BL -> BL x AL
16
    movw  R3:R2,R1:R0    ; r1:r0 -> E1:E0
17
    muls  R22,R16      ; AH x BL
18
    movw  R5:R4,R1:R0    ; r1:r0 -> E3:E2
19
    mul    R21,R16      ; AM x BL
20
    add    R3,R0      ; R0 -> E1
21
    adc    R4,R1      ; R1 -> E2
22
    adc    R5,R10      ; T0 -> E3
23
    pop    R10  
24
    ret

Nun was ich bekomme:
32767*100, d.h. 0x007fff*0x64, Ergebnis 0x0031ff9c, d.h. 3276700. alles 
richtig.
Nun anders: -32767*100, d.h. 0xffff8001*0x64, Ergebnis 0xffce0064, 
d.h.-3276700. Auch richtig.
Nun andersrum: 32767*-100, d.h.0x007fff*0x9c. Zu erwarten ist hier 
eigentlich auch 0xffce0064, d.h.-3276700. Aber es kommt 0x004e0064, 
5111908. Minus auf Plus gibt Plus???

Vielleicht gibt es in diesem Artikel ein Fehler. Leider kann ich den 
nicht finden. Könnt Ihr mir bitte helfen?

Natürlich kann ich das Problem entschärfen: auf Zeichen kucken und 
gegebenfalls NEG machen, ohne Zeichen multiplizieren und danach wieder, 
falls notwendig, NEG. Aber das kostet Zeit, und wenn die Funktion 
angeblich signed*signed heißt, wäre es schön, sie in Ordnung zu 
bringen...
Beitrag #6683719 wurde von einem Moderator gelöscht.
OP #6684101
Lesenswert?

Danke!
Das funktioniert! Oben gegebene Test zeigt richtiges Ergebnis!

Ich habe den Artikel 
https://www.mikrocontroller.net/articles/Multiplikation_in_Assembler 
gelesen und möchte universelle *.inc machen, wo mit #define alle 
möglichen Varianten von mul für Mega und Tiny zu wählen sind. Dann kann 
ich File einfach in allen Projekten includen und wählen, was ich genau 
brauche. Um leichter zu testen, habe ich vorher ebenfalls universelle 
bindec.inc gemacht, das funktioniert.
So kann ich die Zahlen in Programm decimal dem Assembler futtern und 
dann auch decimal in Register sehen. Dann habe ich mit Testen von 
mult.inc begonnen. Alle mul sind in Ordnung, manche muls auch, aber bei 
muls_24x8 kam das Problem.

Vielen Dank für die Lösung! Uch werde weiter testen, vielleicht gibt es 
noch irgendwo Bug...
Angehängte Dateien:
Gast #6684108
Lesenswert?

Ja, schön. Zwischenzeitlich hatte ich meine Routine auch getestet und 
keinen Fehler gefunden.
  Bei der ursprünglichen hat nichts gestimmt, Rechtschreibung, Anzahl 
Takte, die Routine selbst, und keinem fiel es auf - aber je nun, sowas 
kommt vor.
Gast #6684184
Lesenswert?

S. Landolt schrieb:

> Ja, schön. Zwischenzeitlich hatte ich meine Routine auch getestet und
> keinen Fehler gefunden.

Ist auch keiner drin. Weiss ich deshalb, weil sie prinzipiell genauso 
aussieht wie meine eigene zu diesem Thema.

> Bei der ursprünglichen hat nichts gestimmt, Rechtschreibung, Anzahl
> Takte, die Routine selbst, und keinem fiel es auf - aber je nun, sowas
> kommt vor.

Die Routine selbst lag ja sogar in mehrfacher Hinsicht daneben. Aber das 
ist halt OpenSource. Jeder darf irgendwo was veröffentlichen und jeder 
darf es unbesehen C&P-en...

Hier hatten wir halt mal die Ausnahme von der Regel: Jemand, der 
letzteres eben nicht getan hat. Von solchen Leuten müsste es viel mehr 
geben. Gibt es aber leider nicht...

Deswegen kann die klare Konsequenz nur sein: traue keinem OSS-Code. Das 
genau ist auch, was meine Erfahrung diesbezüglich sagt. Und diese 
winzige AVR8-Multiplikation ist ja noch vergleichweise sehr trivial. 
Wenn man sich anschaut, was manche sich für ungeheure Mengen an Code 
einfach mal in ihre Projekte reinziehen, ohne auch nur andeutungsweise 
zu prüfen, was die eigentlich wirklich tun, kann einem nur Angst und 
Bange werden...
Gast #6684198
Lesenswert?

> prinzipiell genauso aussieht
Nun ja, ein Stück weit hatte ich mich durchaus auf Optik&Analogie 
verlassen, sonst hätte ich sie erst gar nicht vorgestellt. Aber manchmal 
ist der Teufel ein Eichhörnchen ...
OP #6684246
Lesenswert?

ich habe inzwischen in meinen bindec einen Fehler entdeckt: bei 
Minuszeichen war r17 nicht gepusht. Korrigiert.
Ja, man darf nicht zu kritisch sein zu dem, was man als Freizeit 
macht...

Ich habe jetzt Lust für Assembler. Um besser zu verstehen, was 
C-Compiler macht. Auch wäre schön, beides zusammen zu verwenden. 
Besonders ISR kann man oft gut optimieren, da Compiler nicht alles weiß 
wie ich, was gebraucht wird.

c-hater schrieb:
> Jeder darf irgendwo was veröffentlichen

Ich habe auf Ihrer Idee macro_basic.inc gemacht. Das sieht schrecklich 
aus, macht aber die Arbeit. Das sind ja nur Macros, in Code kommt nur, 
was gebraucht wird.
Aber keine Garantie, daß alles überall stimmt!
Angehängte Dateien:
#6684260
Lesenswert?

c-hater schrieb:
> Und diese
> winzige AVR8-Multiplikation ist ja noch vergleichweise sehr trivial.
> Wenn man sich anschaut, was manche sich für ungeheure Mengen an Code
> einfach mal in ihre Projekte reinziehen, ohne auch nur andeutungsweise
> zu prüfen, was die eigentlich wirklich tun, kann einem nur Angst und
> Bange werden...

Sehe ich auch so! Ich finde, es sollte so sein, dass man sich Anregungen 
für die Lösung eines Problems sucht, mit dem man nicht weiterkommt. Da 
schaut man also mal, wie hat der das denn gemacht und schon setzt man, 
setze ich, das in meinen eigenen Code um und freue mich, wenn es auf 
Anhieb klappt...ist aber eher selten :-)
Gruß Rainer
#6685209
Lesenswert?

Maxim B. schrieb:
> Zuerst die Funktion aus dem Artikel:

Nein, das ist nicht die Funktion aus dem Artikel. Da sind auch ein paar 
Unterstriche drin, die Du nicht verwendet hast.
1
;***************************************************
2
;* Mutiply 24x8 -> 32 bit
3
;*       AH  AM  AL   BL     E3 E2 E1 E0
4
;*      R22:R21:R20 x R16 -> R5:R4:R3:R2
5
;*
6
;*  19 cycles + 4 (RET) = 23 Cycles
7
;*
8
muls24x8_32:
9
    push  R10
10
    clr  R10
11
    mulsu_  R16,R20      ; AL x BL -> BL x AL
12
    movw  R3:R2,R1:R0    ; r1:r0 -> E1:E0
13
    muls_  R22,R16      ; AH x BL
14
    movw  R5:R4,R1:R0    ; r1:r0 -> E3:E2
15
    mul_  R21,R16      ; AM x BL
16
    add  R3,R0      ; R0 -> E1
17
    adc  R4,R1      ; R1 -> E2
18
    adc  R5,R10      ; T0 -> E3
19
    pop  R10  
20
    ret

mulsu_
muls_
mul_

Niemand von uns weiß, wie DU das umgesetzt hast.

Maxim B. schrieb:
> Aber keine Garantie, daß alles überall stimmt!

Kann ich nachvollziehen.
#6685372
Lesenswert?

S. Landolt schrieb:
> Maxim B. bezog sich auf die Hardwareversion.

Nö. Das ist die "Hardware-Version":
1
;******************************************************************************
2
;*  FUNCTION  mul24x8_32.inc
3
;*  Mutiply   24x8 -> 32 bit     (unsigned * unsigned)
4
;*
5
;*     AH  AM  AL    BL     E3 E2 E1 E0
6
;*    R22:R21:R20 x R16 -> R5:R4:R3:R2
7
;*  
8
;*  Scrached Register:  -
9
;*  17 cycles + 4 (RET) = 21 Cycles
10
;*
11
;******************************************************************************
12
mul_24x8:
13
    push  R9
14
;   R5:R2 = R16 * R22:R20
15
    sub    R9,R9        ; NULL
16
    mul   R20,R16        ; AL*BL
17
    movw   R3:R2,R1:R0
18
    mul   R22,R16        ; AH*BL
19
    movw   R5:R4,R1:R0
20

21
    mul    R21,R16        ; AM*BL
22
    add    R3,R0
23
    adc    R4,R1
24
    adc    R5,R9
25
    pop    R9    
26
    ret

Die wäre mit

mul
movw
mul
movw
mul

oder?

Der liebe Maxim ist halt kreativer, wie mancher denkt :-)
#6685524
Lesenswert?

S. Landolt schrieb:
> Nö, allmählich gewöhne ich mich an die ubiquitäre Duzerei, auch wenn es
> schwerfällt. Aber selbst wildfremde Menschen zu duzen, das schaffe ich
> denn doch nicht.

Kulturen, Sprachen in denen diese Unterscheidung wichtig ist, sind 
konservativ und legen großen Wert auf Etikette. Sprachen, Kulturen die 
demonstrativ gegen diese Konventionen arbeiten sind genauso konservativ 
und haben natürlich auch ihre heiligen Etikette. Man muß halt manchmal 
genau hinhören...sie Arschloch, resp. du Arschloch...kommt immer darauf 
an wann, wo und wer das sagt...ist doch klar oder :-)
Gruß Rainer
OP #6685544
Lesenswert?

Hugo H. schrieb:
> Niemand von uns weiß, wie DU das umgesetzt hast.

Ich habe zuerst immer Hardware-mul getestet und erst danach Ergebnis mit 
Software-mul verglichen. Es gab nie Unterschiede. Wenn Fehler, dann bei 
beiden Varianten. Wenn richtig, dann auch bei beiden.
1
.macro m_mul
2
    #ifdef _HARDMULT_
3
      mul @0,@1
4
    #else
5
      mov r24,@0
6
      mov r25,@1
7
      rcall mul_pr
8
    #endif
9
.endm
10

11
.macro m_muls
12
    #ifdef _HARDMULT_
13
      muls @0,@1
14
    #else
15
      mov r24,@0
16
      mov r25,@1
17
      rcall muls_pr
18
    #endif
19
.endm
20

21
.macro m_mulsu
22
    #ifdef _HARDMULT_
23
      mulsu @0,@1
24
    #else
25
      mov r24,@0
26
      mov r25,@1
27
      rcall mulsu_pr
28
    #endif
29
.endm
30

31
#ifndef _HARDMULT_
32

33
_muls_:  sub    r1,r1      ; clear result High byte and carry
34
    ldi    r18,8      ; init loop counter
35
    brcc  PC+2      ; if carry (previous bit) set
36
    add    r1,r16      ; add multiplicand to result High byte
37
    sbrc  r0,0      ; if current bit set
38
    sub    r1,r16      ; subtract multiplicand from result High
39
    asr    r1        ; shift right result High byte
40
    ror    r0        ; shift right result L byte and multiplier
41
    in    r17,SREG
42
    dec    r18        ; decrement loop counter
43
    brne  PC-8      ; if not done, loop more
44
    ret
45

46

47
_mulsu_:clr    r1      ;clear result High byte
48
    ldi    r18,8    ;init loop counter
49
    lsr    r0      ;rotate multiplier  
50
    brcc  PC+2    ; carry set 
51
    add   r1,r16    ; add multiplicand to result High byte
52
    ror    r1      ; rotate right result High byte
53
    ror    r0      ; rotate right result L byte and multiplier
54
    in    r17,SREG
55
    dec    r18      ; decrement loop counter
56
    brne  PC-6    ; if not done, loop more
57
    ret
58

59
        ;---------------------------------
60
        ;mul Befehl in Software als call
61
        ;---------------------------------
62

63
  ; @0 = r24, @1 = r25
64
mul_pr:
65
    clr     r1      ;Ergebnis = 0
66
        sec             ;Multiplikant ersetzt gleichzeitig den Schleifenzaehler
67
        ror     r24      ;Eine 1 rein, LSB raus
68
        rjmp    PC+3    ;Beim 1. Durchgang, gleich LSB auswerten
69
    lsr     r24      ;Bit (von 1..7 Niederwertige zuerst) ins Carry (Multiplikand)
70
        breq    PC+6    ;Wenn die 1 von sec (oben) wieder im Carry ist, Ende
71
    brcc    PC+2    ;Wenn 0, Addition ueberspringen
72
        add     r1,r25   ;Ansonsten Multiplikator ins Highbyte addieren
73
    ror     r1      ;Produkt mit evt. Uebertrag nach rechts schieben
74
        ror     r0
75
        rjmp    PC-6    ;Noch ne Runde (von insgesamt 8)
76
    push  r16
77
    in    r16,SREG
78
    andi  r16,0x80
79
    out    SREG,r16
80
    sbrc  r1,7
81
    sec
82
    pop    r16
83
    ret
84

85
muls_pr:
86
    push  r16
87
    push  r17
88
    push  r18
89
    clt
90
    mov    r1,r24
91
    mov    r0,r25
92
    clr    r17  
93
    sbrc  r1,7
94
    inc    r17
95
    sbrc  r0,7
96
    inc    r17
97
    sbrc  r17,0
98
    set
99
    mov    r16,r1
100
    rcall  _muls_
101
    andi  r17,0x82
102
    sbrc  r17,SREG_Z  
103
    rjmp  PC+3
104
    brtc  PC+2
105
    sbr    r17,(1<<SREG_C)
106
    out    SREG,r17
107
    pop    r18
108
    pop    r17
109
    pop    r16
110
    ret
111

112
mulsu_pr:
113
    push  r16
114
    push  r17
115
    push  r18
116
    mov    r1,r24
117
    mov    r0,r25
118
    mov    r16,r1
119
    clt
120
    sbrc  r16,7
121
    set
122
    sbrc  r16,7
123
    neg    r16
124
    rcall  _mulsu_
125
    andi  r17,0x83
126
    brtc  PC+6
127
    com    r1
128
    com    r0
129
    ldi    r16,1  
130
    add    r0,r16
131
    adc    r1,r18
132
    sbrc  r1,7   
133
    sbr    r17,(1<<SREG_C)
134
    out    SREG,r17  
135
    pop    r18
136
    pop    r17
137
    pop    r16
138
    ret
139

140

141
#endif /* _HARDMULT_ */
OP #6685557
Lesenswert?

Richtig funktionierende Variante nach S. Landolt:
1
#ifdef _MULS_24X8_32_
2
;******************************************************************************
3
;*  FUNCTION  muls24x8_32.inc
4
;*  Mutiply   24x8 -> 32 bit     (signed * signed)
5
;*
6
;*      AH  AM  AL    BL     E3 E2 E1 E0
7
;*      R22:R21:R20 x R16 -> R5:R4:R3:R2
8
;*  
9
;*  Scrached Register:  -
10
;*   26/378 Cycles
11
;*
12
;******************************************************************************
13
muls_24x8:
14

15
  #ifndef _HARDMULT_
16
    push r24
17
    push r25
18
  #endif /* _HARDMULT_ */
19

20
    push    r10
21
      clr     r10
22

23
      m_muls    r22,r16         ; AH x BL
24
      movw    r5:r4,r1:r0     ; r1:r0 -> E3:E2
25

26
      m_mulsu   r16,r20         ; AL x BL -> BL x AL
27
      movw    r3:r2,r1:r0     ; r1:r0 -> E1:E0
28
      sbc     r4,r10
29
      sbc     r5,r10
30

31
      m_mulsu   r16,r21         ; BL x AM
32
      sbc     r5,r10
33
      add     r3,r0           ; r0 -> E1
34
      adc     r4,r1           ; r1 -> E2
35
      adc     r5,r10          ; T0 -> E3
36
      pop     r10    
37

38
  #ifndef _HARDMULT_
39
    pop r25
40
    pop r24
41
  #endif /* _HARDMULT_ */
42

43
    ret
44

45
#endif /* _MULS_24X8_32_ */

Ich habe Software-mul-routinen aus genanntem Artikel genommen. Die 
arbeiten richtig. Einzig habe ich sie aus reinen Macros in Macro mit 
Call umgeschrieben, damit Programmcode nicht zu groß wird.
Gast #6685559
Lesenswert?

Wenn hier schon Takte gezählt werden ...
Kann mir jemand sagen, was das Gepusche und Gepoppe soll? Zu dem 
Zeitpunkt, wo das Null-Register gebraucht wird, findet sich bereits ein 
nicht mehr gebrauchtes Eingangsregister.
OP #6685570
Lesenswert?

bitflipser schrieb:
> Wenn hier schon Takte gezählt werden ...
> Kann mir jemand sagen, was das Gepusche und Gepoppe soll? Zu dem
> Zeitpunkt, wo das Null-Register gebraucht wird, findet sich bereits ein
> nicht mehr gebrauchtes Eingangsregister.

Ziel ist, Software-Variante möglichst ähnlich zu Hardware-Variante zu 
machen. Hardware-mul kann beliebige Register als Quelle nehmen, bei 
Software-Variante sollte ich dafür zwei Register benutzen, die bei 
Hardware-Variante unberührt bleiben. Deshalb push und pop. Da 
Software-Variante sowieso ca.15 mal langsamer ist, spielen hier 8 Cycles 
für push und pop kaum noch eine Rolle.

Die Software-Variante wird nur in seltenen Fällen gebraucht, wenn man 
Tiny nimmt und doch mul braucht. Heute kann man fast immer Mega nehmen, 
es sei denn, man braucht USI statt I2C. Zwar kann man auch dann 
ATmega645 u.Ä. nehmen.
Gast #6685883
Lesenswert?

Hugo Hurtig schrieb:
> Daran gewöhnst Du Dich in "Internationalen Konzernen"
Als ich noch bei HP Deutschland arbeitete, war der Standard Sie+Vorname 
- das ist aber, zugegeben, schon eine Weile her.

bitflipser fragte:
> Kann mir jemand sagen, was das Gepusche und Gepoppe soll?
In Bezug auf Hardware-'muls24X8_32' müsste das Steffen H. beantworten. 
Vielleicht sollen die Originalwerte erhalten bleiben.
#6686421
Lesenswert?

bitflipser schrieb:
> Zu dem
> Zeitpunkt, wo das Null-Register gebraucht wird, findet sich bereits ein
> nicht mehr gebrauchtes Eingangsregister.

Du solltest es unter dem Aspekt der "universellen" Registernutzung 
sehen. Wenn du mit deinem Progrämmchen nichts anderes tust, als 5 
Byte(Register) zu verwursten, dann mußt du nichts pushen und pollen. 
Wenn deine Routine aber als Unterprogramm laufen soll, dann sorgst du 
einfach dafür, dass die benötigten Register vorher gesichert und 
hinterher wieder hergestellt werden. Das ist bewährter guter 
Programmierstil...
Gruß Rainer
OP #6704203
Lesenswert?

Nun habe ich die Sache etwas gelernt...
Ich kam zum Schluß, daß die auf der Seite 
https://www.mikrocontroller.net/articles/Multiplikation_in_Assembler 
vorgeschlagene Art, bei Software-Mult die Befehle mul, muls und mulsu 
mit macro zu emulieren, bringt nichts. Im Vergleich mit "klassischen" 
Varianten gibt das viel mehr Code und ist auch viel langsamer.
Außerdem gibt es auf der Seite 
https://www.mikrocontroller.net/articles/Multiplikation_in_Assembler zu 
viele Fehler und Schreibfehler, so kann man dort angebotene Code leider 
nur teilweise und mit Vorsicht genießen...

Ich habe hier meine Variante für Soft- und Hardmult. Ich habe die 
Funktionen als Macro geschrieben. Das hat Vorteil: man kann diese Macro 
in Funktionen einbauen, die mehr machen als einfache Multiplikation. 
Dabei hat man mehr Freiheit bei Registerauswahl. Natürlich sollte man 
diese Macros für Gebrauch als "reine" Mult in Funktionen umwandeln. Aber 
das ist ja nicht schwer.
Ich denke, auch bei Mega hat Software-Mult ab und zu Sinn, da bei 
Hardware-Mult mit Vorzeichen nicht alle Register für Argumente zu 
gebrauchen sind: oft sind nur r16-r23 dafür geeignet. Auch Code ist bei 
Softmult oft (nicht immer) sparsamer. Z.B. multsoft_24x24_48 hat 64 
bytes, multhard_24x24_48 dagegen 84 bytes; multsoft_24x40_64 hat 76 
bytes, multhard_32x32_64 dagegen 162 bytes, wenn auch viel schneller.
Als Null-Register paßt bei Hardware-Mult hervorragend @0, da @0 nach 
Multiplikation von LSB nicht mehr geändert wird. Allerdings braucht die 
Folge push @0 - clr @0 - pop @0 schon 5 cycles, deshalb bleibt oft auch 
andere Folge von Vorteil: mul @x,@y - add @a,r0 - adc @b,r1 - clr r0 - 
adc @c,r0 usw. Auf Befehle MOVW habe ich verzichtet, obwohl sie ein paar 
cycles sparen könnten: in Macro solcher Art würde das Registerauswahl 
zusätzlich beschränken.
Also, hier sind beide INC. Ich habe die Macros sorgfältig geprüft, ich 
kann die Fehler natürlich nicht ausschließen. Aber vielleicht wird das 
für jemand nützlich.
Viele Grüße.
Angehängte Dateien:

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren