Forum: Mikrocontroller und Digitale Elektronik 16 bit Arithmetik mit zwei 8 bit Registern


von René (Gast)


Lesenswert?

Hallo alle zusammen,

ich schreibe gerade ein Programm auf dem Atmega 8 in Assembler. Eine 
Funktion soll ein Zähler sein, der von 0 bis 50.000 zählt und den 
aktuellen Wert auf einem LCD Display anzeigt.

Ich habe auch schon im Tutorial ein super Kapitel gefunden (LCD: 
Ansteuerung eines LC-Displays im 4bit-Modus), in dem sogar erklärt wird, 
wie man eine 16 bit Zahl, bestehend aus 2 8 bit registern, ausliest und 
anzeigt. Hier mal ein Ausschnitt:

; ** Zehntausender **
           ldi   temp1, '0'-1
lcd_number1:
           inc   temp1
           subi  temp2, low(10000)
           sbci  temp3, high(10000)
           brcc  lcd_number1
           subi  temp2, low(-10000)
           sbci  temp3, high(-10000)
           rcall lcd_data

Ich bin mir mit der 16 bit Arithmetik allerdings noch etwas unsicher und 
habe noch ein paar Fragen:

-Warum wird im obigen Beispiel einmal subi und einmal sbci verwendet?
-Wie würde der Code aussehen, wenn ich zwei 8 bit Register als 16 bit 
Register verwenden möchte und dort +1 rechnen möchte? "inc r16" wird 
wohl nicht gehen.


Ich hoffe meine Beschreibung ist ausführlich genug und ihr versteht, was 
ich meine.

Liebe Grüße
René

von Peter D. (peda)


Lesenswert?

René schrieb:
> wenn ich zwei 8 bit Register als 16 bit
> Register verwenden möchte und dort +1 rechnen möchte?

adiw r24, 1

von m.n. (Gast)


Lesenswert?

René schrieb:
> -Warum wird im obigen Beispiel einmal subi und einmal sbci verwendet?

sbci berücksichtigt den Übertrag (carry-flag) der bei subi entstanden 
sein kann.

> -Wie würde der Code aussehen, wenn ich zwei 8 bit Register als 16 bit
> Register verwenden möchte und dort +1 rechnen möchte? "inc r16" wird
> wohl nicht gehen.

        inc r16
        brne weiter
        inc r17
weiter: ...

von Baruk (Gast)


Lesenswert?

Einige Register können zwecks Addition zu 16-Bit Paaren zusammengefasst 
werden.

Beispiel (addiere 1 zum Registerpaar R24:R25)

ADIW r25:24, 1

http://www.atmel.com/webdoc/avrassembler/avrassembler.wb_ADIW.html

Befehlsübersicht zum Durcharbeiten:

http://www.atmel.com/webdoc/avrassembler/avrassembler.wb_instruction_list.html

von c-hater (Gast)


Lesenswert?

René schrieb:

> -Warum wird im obigen Beispiel einmal subi und einmal sbci verwendet?

Weil's eben die Umsetzung von 16Bit Arithmetik auf einer 8Bit Maschine 
ist.
Da muss man Überträge machen (das passiert bei allen Additions- und 
Subtraktionsbefehlen automatisch) und die in der höherwertigen Stelle 
dann auch berücksichtigen. Das passiert allerdings nicht automatisch, 
sondern nur dann, wenn man eben genau die Operation verwendet, die das 
tut. Also sbci statt subi.

> -Wie würde der Code aussehen, wenn ich zwei 8 bit Register als 16 bit
> Register verwenden möchte und dort +1 rechnen möchte? "inc r16" wird
> wohl nicht gehen.

Doch, geht zur Not auch. Nur leider setzt die inc-Operation das 
Übertragsflag eben gerade nicht. Bei inc noch nicht wirklich schlimm, 
denn man kann hier auf's Zeroflag ausweichen, denn ein Übertrag ist 
genau dann erforderlich, wenn das Ergebnis für die Stelle 0 ist. Nur 
leider gibt es keine Operation, die so einen "Übertrag im Zeroflag" 
mitaddieren könnte. Man muss sich also mit einer zusätzlichen 
Verzweigung behelfen. Also etwa sowas bauen:

 inc R16
 brne PC+2
 inc R17

Damit kostet die 16Bit-Operation allerdings drei Takte statt der 
üblichen zwei. Der Vorteil ist allerdings: dieses Konstrukt funktioniert 
mit allen Registern.

Wenn es nur mit R16..R31 funktionieren muss, dann wird man eher das 
verwenden:

 subi R16,Low(-1)
 sbci R17,High(-1)

Das braucht nämlich nur zwei Takte. Der Trick ist hier, dass statt 1 zu 
addieren -1 subtrahiert wird. Das Ergebnis ist das gleiche, allerdings 
wird das Carryflag nicht so beeinflußt, wie man es von einer Addition 
erwartet, sondern "umgekehrt". Das ist aber kein Problem, wenn es 
entweder keine weiteren Stellen gibt oder für die weiteren Stellen 
einfach nach der gleichen Methode weiter gerechnet wird. Beispiel für 
inc(32Bit-Wert):

 subi R16,Byte1(-1)
 sbci R17,Byte2(-1)
 sbci R18,Byte3(-1)
 sbci R19,Byte4(-1)

Und letztlich kann man für die Registerpaare R25:R24..R31:R30 auch noch 
adiw verwenden, wie im Thread schon gesagt wurde. Diese Operation hat 
allerdings gegenüber der vorigen Methode nur zwei marginale Vorteile: 
sie setzt das Carryflag, wie man es von einer Addition erwartet und sie 
braucht zwei Bytes weniger Flash. Bezüglich der Rechengeschwindigkeit 
ist sie hingegen leider nicht schneller als die subi/sbci-Methode. Und 
dazu kommt noch, dass auch ihr Wertebereich stark eingeschränkt ist, das 
geht nämlich nur mit Operanden im Bereich von 0..63.

All das und auch viele weitere Details findest du in der "Instruction 
set reference". Sozusagen die Bibel für Assemblerprogrammierer. Lesen 
und verstehen musst du sie selber.

von Lothar (Gast)


Lesenswert?

Hier wird die Sache deutlich ausführlicher erklärt. Ist zwar für 8051 
ändert aber am Prinzip nichts:

http://www.edsim51.com/8051Notes/8051/16bitAddition.html

von René (Gast)


Lesenswert?

Danke für die zahlreichen Antworten. Ich fasse zusammen:

1.
> adiw r25:r24, 1
ist der einzige Befehl, der sich entsprechend SUBI und SBCI verhält und 
r25 und r24 zu einem Registerpaar zusammenfasst. Wenn r24 "voll ist" 
incrementiert er automatisch r25. Allerdings kann man nur Summanden bis 
+63 damit rechnen und er funktioniert nur mit den oberen 4 
Registerpaaren.

2.
>        inc r16
>        brne weiter
>        inc r17
>weiter: ...
Die Rechnung wäre so wie bei 1., nur dass ich es dann "von Hand" rechnen 
würde: Sobald r16 übergelaufen ist, addiere ich r17

3.
>Wenn es nur mit R16..R31 funktionieren muss, dann wird man eher das
>verwenden:

> subi R16,Low(-1)
> sbci R17,High(-1)
Ich subtrahiere also, anstatt zu addieren. Dann müsste ich noch mein 
Ergebnis von 65535 abziehen, um den aktuellen Zählerstand zu erhalten.

Habe ich das so richtig verstanden?

Und danke für die nützlichen Links, die habe ich mir gleich mal zum 
Nachschlagen gespeichert.

Liebe Grüße

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Nein

Statt 1+1 rechnest Du 1-(-1) und bei Beiden kommt das Gleiche raus.
Du musst nur Deinen Summanden 'umdrehen'.

MfG

von René (Gast)


Lesenswert?

Patrick J. schrieb:
> Hi
>
> Nein
>
> Statt 1+1 rechnest Du 1-(-1) und bei Beiden kommt das Gleiche raus.
> Du musst nur Deinen Summanden 'umdrehen'.
>
> MfG

ah cool danke hab´s gerade ausprobiert

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.