Forum: Mikrocontroller und Digitale Elektronik 2 16bit zu einer 24bit Zahl addieren (Assembler)


von Sven (Gast)


Lesenswert?

Hallo,

ich habe folgendes Problem: Ich will mehrere vorzeichenbehaftete 
(Zweierkomplement) 16bit Zahlen zu einer vorzeichenbehafteten 24bit-Zahl 
aussummieren:
1
.def Zahl_1_L = r16
2
.def Zahl_1_H = r17
3
.def Zahl_2_L = r18
4
.def Zahl_2_H = r19
5
.def erg_L    = r20
6
.def erg_M    = r21
7
.def erg_H    = r22
8
.def temp     = r23
9
10
main:
11
12
clr temp
13
14
add erg_L, Zahl_1_L
15
adc erg_M, Zahl_1_H
16
adc erg_H, temp
17
18
add erg_L, Zahl_2_L
19
add erg_H, Zahl_2_H
20
adc erg_H, temp
21
22
...

Der Code klappt sehr gut mit vorzeichenlosen, also 
Einer-Komplement-Zahlen. Leider wird das dritte Byte bei 
vorzeichenbehafteten negativen Zahlen nicht richtig gebildet. Ich bin 
Anfänger im Rechnen mit Assembler und weiß nicht so recht, wie ich das 
auf einfache Art und Weise ohne viele zusätzliche Register und 
zusätzlichen Code realisieren kann (Programm ist laufzeitkritisch).
Die Möglichkeit, die mir einfällt ist, das höchste Bit des hohen Bytes 
einer jeden Zahl zu prüfen und dann die 16bit zu ner 24bit-Zahl 
erweitern:

wenn bit7 von Zahl_H =1 dann 3.byte=FF
wenn bit7 von Zahl_H =0 dann 3.byte=00

Das vorzeichenrichtige Aufsummieren sollte dann problemlos klappen.

Aber dazu brauche ich einige Sprungbefehle und neue Register, dann wird 
der Code zu lang.
Ich denke, mal es gibt einen eleganteren Trick, nur komme ich auch nach 
langem Nachdenken einfach nicht drauf.
Vielleicht könnt ihr mir ja helfen.

Danke für eure Antworten

von Ralph (Gast)


Lesenswert?

Programmier in C und lass das den Compiler machen, der weiß wie es geht

von Spess53 (Gast)


Lesenswert?

Hi

Wie zeitkritisch ist die Berechnung? Controller und Taktfrequenz wären 
hilfreich.

@Ralph: Der Compiler (und die meisten Anwender desselben) weiss nicht 
wie es geht, sondern nur die Programmierer des Compilers. Hier geht es 
um Assembler.
Wenn du einen konstruktiven Beitrag leisten kannst, wird sich Sven 
freuen.

MfG Spess

von Johannes M. (johnny-m)


Lesenswert?

Also mal ein bisschen Brainstorming:
Ein Überlauf in das höchstwertige Byte kann bei der Addition von 
Zweierkomplement-Ausgangswerten nur dann auftreten, wenn beide 
Ausgangswerte bereits negativ waren (MSB gesetzt), wodurch das Ergebnis 
auf jeden Fall auch negativ ist. Also muss bei einem Überlauf in jedem 
Fall das komplette H-Byte gesetzt werden (0xFF), damit man wieder auf 
eine Zweierkomplement-Darstellung kommt.

In den Fällen, in denen das Carry-Flag nicht gesetzt wird, ist zu 
überprüfen, ob der Wert in den beiden niederwertigen Bytes positiv oder 
negativ ist. Das kann man sehr elegant mit dem N-Flag im SREG lösen. N 
entspricht nach der Addition dem MSB des Ergebnisses, ist also gesetzt, 
wenn das Ergebnis der adc-Operation negativ ist. Wenn das der Fall ist, 
muss noch zusätzlich überprüft werden, ob es bei der Addition einen 
Zweierkomplement-Überlauf gegeben hat. Dafür wiederum gibts das V-Flag, 
das gesetzt ist, wenn es einen Überlauf in das MSB des Ergebnisses 
gegeben hat. Falls also N und V gesetzt sind, heißt das, es hat einen 
Überlauf auf das MSB gegeben, das scheinbar negative Ergebnis ist also 
in Wirklichkeit positiv und dementsprechend muss das High-Byte des 
Ergebnisses mit Nullen gefüllt werden. Andernfalls handelt es sich 
tatsächlich um ein negatives Ergebnis und das High-Byte wird mit Einsen 
gefüllt (also 0xFF).

Es müssen also im Prinzip drei sukzessive Abfragen durchgeführt werden:
1
Carry gesetzt?
2
    Ja: Ergebnis ist auf jeden Fall negativ, High-Byte = 0xFF, weiter im Programm.
3
    Nein: 
4
    N-Flag gesetzt?
5
        Nein: Ergebnis ist auf jeden Fall positiv, High-Byte = 0x00, weiter im Programm.
6
        Ja: 
7
        V-Flag gesetzt?
8
            Ja: Ergebnis ist positiv, High-Byte = 0x00, weiter im Programm.
9
            Nein: Ergebnis ist negativ, High-Byte = 0xFF, weiter im Programm.
Diese Abfrage ist mit den Branch-Befehlen relativ einfach (und schnell) 
durchführbar und kostet keine zusätzlichen Register.

Viel Spaß beim Weitertüfteln...

von Johannes M. (johnny-m)


Lesenswert?

Ralph wrote:
> Programmier in C und lass das den Compiler machen, der weiß wie es geht
Welcher Compiler kann denn bitteschön ressourcensparend (und das war 
gefordert!) mit 24-Bit-Zahlen rechnen? Also, erst Gehirn hochfahren, 
dann schreiben...

von Matthias L. (Gast)


Lesenswert?

>Die Möglichkeit, die mir einfällt ist, das höchste Bit des hohen Bytes
>einer jeden Zahl zu prüfen und dann die 16bit zu ner 24bit-Zahl
>erweitern:

>wenn bit7 von Zahl_H =1 dann 3.byte=FF
(und bit7 von Zahl_M = 1 lassen)

>wenn bit7 von Zahl_H =0 dann 3.byte=00

Ich denke, anders wird es nicht gehen.
Also, beide 16bit Zahlen auf 24Bit aufdrömeln, dann einfach addieren..

main:
1
clr temp
2
3
clr Zahl_1_HH         // Zahl1: HH:H:L
4
sbis Zahl_1_H, 7
5
rjmp positiv1
6
ldi Zahl_1_HH, 0xFF
7
positiv1:
8
9
clr Zahl_2_HH         // Zahl2: HH:H:L
10
sbis Zahl_2_H, 7
11
rjmp positiv2
12
ldi Zahl_2_HH, 0xFF
13
positiv2:
14
15
add erg_L, Zahl_1_L
16
adc erg_M, Zahl_1_H
17
adc erg_H, Zahl_1_HH
18
19
add erg_L, Zahl_2_L
20
adc erg_H, Zahl_2_H
21
adc erg_H, Zahl_2_HH

Vielleicht so? Bin aber net ganz sicher..

von Dennis (Gast)


Lesenswert?

Matthias L.:

sbis => Skip if Bit in I/o-register is Set
sbrs => Skip if Bit in Register is Set


> clr Zahl_1_HH
> sb*r*s Zahl_1_H, 7
> rjmp positiv1
> ldi Zahl_1_HH, 0xFF
> positiv1:

geht glaube ich einfacher:
clr Zahl_1_HH
sbrc Zahl_1_H, 7
ldi Zahl_1_HH, 0xFF

von Matthias L. (Gast)


Lesenswert?

oh ja, das sbis und sbrs verwechselt..

Ja, hast recht, das spart einen BEfehl..

von ralf (Gast)


Lesenswert?

kenn mich mit AVR-Assembler nicht aus aber vieleicht geht es so?

add erg_L, Zahl_1_L
adc erg_M, Zahl_1_H
adc erg_H, 0
sbrc Zahl_1_H, 7
dec erg_H

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.