Forum: Mikrocontroller und Digitale Elektronik signed 16bit * usigned 16 bit = signed 32 bit Multiplikation??


von Ronny H. (ronny003)


Lesenswert?

Ich versuche seit ein paar Tagen eine relativ simple Berechnung auf 
einem Atmega88 in Assembler zu programmieren.
Ich möchte den Wert des Timer1 (16bit unsigned) mit einer 16bit sigend 
Zahl multiplizieren.

Dabei habe ich schon etliche Beiträge hier im Forum durch gelesen aber 
nichts gefunden.

Ich habe es mit einigen Zahlen getestet aber mit den Folgenden konnten 
immer Fehler erzeugt werden
65217*712
3289*-341
769*-27819
Dabei ist mir aufgefallen, das in bestimmten situation in meinem Code 
das MSB des Highbyte falsch ist. Immer um genau eine Stelle.
1
;           u16bit * s16bit multiplikation
2
;           r19:r18 * r21:r20 = r11:r10:r9:r8
3
    clr     r10
4
    clr     r11
5
6
    mul     r20,r18
7
    mov     r8 , r0
8
    mov     r9 , r1
9
10
    mulsu   r21,r18
11
    add     r9 , r0
12
    adc     r10, r1
13
14
    mul     r20,r19
15
    add     r9 , r0
16
    adc     r10, r1
17
18
    mulsu   r21,r19
19
    sbc     r11, null ;manchmal notwendig
20
    add     r10, r0
21
    adc     r11, r1
22
    sbc     r11, null ;manchmal notwendig
Dieses Dokument habe ich mir auch schon angeschaut aber genau der fall 
ist eben nicht aufgeführt
http://www.atmel.com/Images/doc1631.pdf

die sbc befehle habe ich einfach von hier Kopiert.
Beitrag "Assembler 16bit signed Multiplikation"
In bestimmten Fällen bringt der erste sbc Befehl den richtigen Wert und 
in anderen fällen halt der andere sbc Befehl.

Die unsigned Multiplikation verstehe ich aber die signed leider nicht 
wirklich. Vermutlich ist es nur eine kleinigkeit die geändert werden 
muss.

Über eine kleine Hilfe wäre ich sehr dankbar.

von Hulk (Gast)


Lesenswert?

Kann es sein, dass Du das Carry für den beidseitigen Fall des Überlaufs 
(beide Terme) nicht mitnimmst?

von wire (Gast)


Lesenswert?

du solltest eine signed 24 oder 32 bit multiplikation vornehmen, da der 
unsigned 16 bit (16 bit value) den wertebereich des signed 16 bit (15 
bit value) übersteigen kann.

von Reinhard Kern (Gast)


Lesenswert?

Hallo,

warum eliminierst du nicht einfach das Vorzeichen und führst eine U16 * 
U16 Multiplikation durch? Das Ergebnis bekommt dann einfach das 
eliminierte Vorzeichen.

Gruss Reinhard

von c-hater (Gast)


Lesenswert?

Ronny H. schrieb:

> Dabei habe ich schon etliche Beiträge hier im Forum durch gelesen aber
> nichts gefunden.

Nunja, manchmal hilft es auch, die existierenden Beispiele wirklich zu 
VERSTEHEN. Dann kann man sie nämlich auch korrekt abwandeln und ist 
nicht mehr darauf angewiesen, irgendwo im Netz eine exakt passende 
Kopiervorlage zu finden...

> ;           u16bit * s16bit multiplikation
> ;           r19:r18 * r21:r20 = r11:r10:r9:r8
>     clr     r10
>     clr     r11
>
>     mul     r20,r18
>     mov     r8 , r0
>     mov     r9 , r1

Bis hierhin richtig, aber zwei Takte nutzlos verschwendet.

>     mulsu   r21,r18
>     add     r9 , r0
>     adc     r10, r1

Falsch. Da fehlen zwei Operationen.

>     mul     r20,r19
>     add     r9 , r0
>     adc     r10, r1

Falsch. Da fehlt eine Operation.

>     mulsu   r21,r19
>     sbc     r11, null ;manchmal notwendig
>     add     r10, r0
>     adc     r11, r1
>     sbc     r11, null ;manchmal notwendig

Falsch. Zwei Operationen zuviel. Und wenn man sich ansieht, was dann 
überbleibt, hat man auch die Erklärung dafür, warum die zwei Takte am 
Anfang nutzlos verschwendet sind.

von Ronny H. (ronny003)


Lesenswert?

>> ;           u16bit * s16bit multiplikation
>> ;           r19:r18 * r21:r20 = r11:r10:r9:r8
>>     clr     r10
>>     clr     r11
>>
>>     mul     r20,r18
>>     mov     r8 , r0
>>     mov     r9 , r1
>
> Bis hierhin richtig, aber zwei Takte nutzlos verschwendet.

Ich sehe leider nicht welche takte da zu viel sind.
Das clr muss ich machen, da ich ja nicht weiß was in r10,r11 steht.
Und das Ergebnis des mul muss ich doch wo anderst ablegen da das nächste 
Ergebnis wieder in r0,r1 steht.

>>     mulsu   r21,r18
>>     add     r9 , r0
>>     adc     r10, r1
>
> Falsch. Da fehlen zwei Operationen.

OK und welche? Ich vermute mal zwei sbc befehle

>>     mul     r20,r19
>>     add     r9 , r0
>>     adc     r10, r1
>
> Falsch. Da fehlt eine Operation.

Wohl wieder ein sbc.

>>     mulsu   r21,r19
>>     sbc     r11, null ;manchmal notwendig
>>     add     r10, r0
>>     adc     r11, r1
>>     sbc     r11, null ;manchmal notwendig
>
> Falsch. Zwei Operationen zuviel. Und wenn man sich ansieht, was dann
> überbleibt, hat man auch die Erklärung dafür, warum die zwei Takte am
> Anfang nutzlos verschwendet sind.

Verstehe ich überhaupt nicht da in r11,r10 immer was anderes Steht in 
abhängigkeit von der Rechnung natürlich.

Ich werde es aber so machen wie Herr Kern es vorgeschlagen hat.
Da bin ich heute im laufe des Tages auch drauf gekommen.

Ich überprüfe ob der zweite Multiplikator negativ ist.
Wenn ja dann wird er gedreht und ich füre eine
u16*u16 durch.
Danach wird das Ergebnis dann einfach entsprechend des zweiten 
Multiplikators gedreht.

Das kann ich noch sehr gut verstehen.
Leider bin ich nicht in der lagen jeden Code zu verstehen.
Dazu fehlt mir das nötige Wissen und die Erfahrung.

Aber danke an alle für die schnelle Hilfe.

von c-hater (Gast)


Lesenswert?

Ronny H. schrieb:

> Ich sehe leider nicht welche takte da zu viel sind.

> Das clr muss ich machen, da ich ja nicht weiß was in r10,r11 steht.

Nein, mußt du nicht.

> OK und welche? Ich vermute mal zwei sbc befehle

Nein. Einmal sbc und einmal adc.

> Wohl wieder ein sbc.

Nein. Einmal adc.

> Verstehe ich überhaupt nicht da in r11,r10 immer was anderes Steht in
> abhängigkeit von der Rechnung natürlich.

Kommutativgesetz der Addition. Oder anders ausgedrückt: Es spielt keine 
Geige, in welcher Reihenfolge Additionen ausgeführt werden. Der Trick 
ist, diesen Term als ersten oder zweiten zu berechnen. Dann wäre er nur 
zu Null zu addieren, denn keine vorherige Operation hat bereits zum 
Inhalt von R11:R10 beitragen können, sprich: er stellt selber bereits 
das Ergebnis der Addition dar und kann deshalb direkt als initialer 
Inhalt von R11:R10 verwendet werden. Im Prinzip genau dasselbe, wie du 
es bei deinem ersten Term machst. Aber naja, das machst ja nicht du, 
sondern der von dir "adoptierte" Code fremder Leute, den du sowieso 
niemals verstanden hast.

> Ich werde es aber so machen wie Herr Kern es vorgeschlagen hat.

Funktioniert sicher, ist aber suboptimal. Mindestens drei weitere 
sinnlos verbratene Takte. Aber das auch nur bei intelligenter Umsetzung 
des Ansatzes, ansonsten werden es noch mehr...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Du kannstr es einfach machen wie avr-gcc, dort sind die Operationen 
korrekt implementiert, siehe

http://gcc.gnu.org/viewcvs/gcc/trunk/libgcc/config/avr/lib1funcs.S?revision=196431&view=markup#l464

Die Funktion, die du suchst, heißt dort __usmulhisi3.

C-Code, der die Funktion verwendet, wäre zB
1
long mul (int a, unsigned b)
2
{
3
    return (signed long) a * (unsigned long) b;
4
}
wird zu:
1
mul:
2
  movw r18,r22
3
  movw r26,r24
4
  call __usmulhisi3
5
  ret

von Ronny H. (ronny003)


Lesenswert?

c-hater schrieb:
> Ronny H. schrieb:
>
>> Ich sehe leider nicht welche takte da zu viel sind.
>
>> Das clr muss ich machen, da ich ja nicht weiß was in r10,r11 steht.
>
> Nein, mußt du nicht.
>
>> OK und welche? Ich vermute mal zwei sbc befehle
>
> Nein. Einmal sbc und einmal adc.
>
>> Wohl wieder ein sbc.
>
> Nein. Einmal adc.
>
>> Verstehe ich überhaupt nicht da in r11,r10 immer was anderes Steht in
>> abhängigkeit von der Rechnung natürlich.
>
> Kommutativgesetz der Addition. Oder anders ausgedrückt: Es spielt keine
> Geige, in welcher Reihenfolge Additionen ausgeführt werden. Der Trick
> ist, diesen Term als ersten oder zweiten zu berechnen. Dann wäre er nur
> zu Null zu addieren, denn keine vorherige Operation hat bereits zum
> Inhalt von R11:R10 beitragen können, sprich: er stellt selber bereits
> das Ergebnis der Addition dar und kann deshalb direkt als initialer
> Inhalt von R11:R10 verwendet werden. Im Prinzip genau dasselbe, wie du
> es bei deinem ersten Term machst.

Ok das habe ich verstanden und versuche es mal um zu setzen. Natürlich 
gefällt mir die Methode mit dem hin un her drehen nicht aber im notfall 
funktioniert es. Ich werde aber versuchen die Multiplikation richtig 
aufzustellen.

>Aber naja, das machst ja nicht du,
> sondern der von dir "adoptierte" Code fremder Leute, den du sowieso
> niemals verstanden hast.

Absolut unötig. Aber diskriminierungen sind hier in dem Forum leider des 
öfteren anzutreffen.

>> Ich werde es aber so machen wie Herr Kern es vorgeschlagen hat.
>
> Funktioniert sicher, ist aber suboptimal. Mindestens drei weitere
> sinnlos verbratene Takte. Aber das auch nur bei intelligenter Umsetzung
> des Ansatzes, ansonsten werden es noch mehr...

Ich als eingeschränkter Mensch würdes natürlich nicht in 3 Takten 
schaffen.

Danke Johann L. Ich bin schon dabei das Script zu analysieren.

von Ronny H. (ronny003)


Lesenswert?

c-hater schrieb im Beitrag #3204695:
> Ronny H. schrieb:
>
>> Absolut unötig. Aber diskriminierungen sind hier in dem Forum leider des
>> öfteren anzutreffen.
>
> Wer das Aussprechen eines offensichtlich wahren Sachverhaltes als
> Diskriminierung bezeichnet, ist der schlimmste Zensor, den es gibt.

Ich habe in meinem Post VOR deinem schon erwähnt, dass ich nicht in der 
Lage bin jeden Quell-Code zu verstehen. Dadurch ist deine Aussage eine 
unnötige Diskriminierung geworden weil ich den Sachverhalt schon selber 
erkannt habe.


Dank deiner Hilfe und der von Johann konnte ich den Code verbessern und 
habe auch bedeutend mehr davon verstanden.
1
;           u16bit * s16bit multiplikation
2
;           r19:r18 * r21:r20 = r11:r10:r9:r8
3
4
    mul     r20,r18
5
    mov     r8 , r0
6
    mov     r9 , r1
7
8
    mul     r21,r19
9
    mov     r10, r0
10
    mov     r11, r1
11
12
    mul     r20,r19
13
    add     r9 , r0
14
    adc     r10, r1
15
    adc     r11, null
16
17
    mul     r21,r18
18
    add     r9 , r0
19
    adc     r10, r1
20
    adc     r11, null 
21
    
22
    sbrs    r21, 7
23
    rjmp    mul_2
24
    sub     r10, r18
25
    sbc     r11, r19
26
mul_2:

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ronny H. schrieb:
> Dank deiner Hilfe und der von Johann konnte ich den Code verbessern und
> habe auch bedeutend mehr davon verstanden.
>
> ;           u16bit * s16bit multiplikation
> ;           r19:r18 * r21:r20 = r11:r10:r9:r8
>
>     mul     r20,r18
>     mov     r8, r0
>     mov     r9, r1

hier geht auch "movw r8, r0" denn jeder Core, der MUL kann, beherrscht 
auch MOVW.

>     mul     r21,r19
>     mov     r10, r0
>     mov     r11, r1

Ditto.

von Ronny H. (ronny003)


Lesenswert?

Das ich movw anstatt von mov verwenden kann wusste ich schon aber ist es 
denn auch schneller? Wenn ich jetzt so drüber nachdenke Verbraucht es in 
jedenfall weniger Speicher.

Werde diese Änderung noch mit einbeziehen. Sonst vom ablauf scheint es 
zu funktionieren. Ich konnte keinen Fehler produzieren.

Besten dank Johann L.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ronny H. schrieb:
> Das ich movw anstatt von mov verwenden kann wusste ich schon aber ist es
> denn auch schneller?

Nein, MOVW beaucht 2 Ticks.  Es hilft aber Flash zu sparen.

von c-hater (Gast)


Lesenswert?

Johann L. schrieb:

> Nein, MOVW beaucht 2 Ticks.  Es hilft aber Flash zu sparen.

Nein. movw braucht nur einen Tick und hilft somit sehr wohl auch beim 
Rechenzeit sparen.

Siehe "avr instruction set reference" Seite 96.

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.