Nabend zusammen,
werden im Tutorial absichtlich kleine Fehler eingebaut um den
Anfängern das denken beizubringen?
Konkret im Tutorial Uhr und CTC Modus.
Im rcall lcd_number wird das Register temp1 nicht gesichert.
In der Folge zeigt das LCD "Chinesische" Ziffern an.
Original im Tutorial:
lcd_number:
push temp2
ldi temp2, '0'
lcd_number_10:
subi temp1, 10
brcs lcd_number_1
inc temp2
rjmp lcd_number_10
lcd_number_1:
rcall lcd_data
subi temp1, -10
ldi temp2, '0'
add temp1, temp2
rcall lcd_data
pop temp2
ret
Bis man als Anfänger darauf kommt, dauert es etwas.
Richtigerweise müsste dort stehen:
lcd_number:
push temp2
push temp1
mov temp2, temp1
ldi temp1, '0'
lcd_number_10:
subi temp2, 10
brcs lcd_number_1
inc temp1
rjmp lcd_number_10
lcd_number_1:
rcall lcd_data
subi temp2, -10
ldi temp1, '0'
add temp1, temp2
rcall lcd_data
pop temp1
pop temp2
ret
Gerade Anfängern wird es hier schwer gemacht (fehlende Erfolge):-)
Kann jemand den Code im Tutorial anpassen?
Gruß Pebez
Ich kann ehrlich gesagt nicht wirklich nachvollziehen, was daran falsch
sein soll.
temp1 ist das Eingangsregister dieser Funktion. Der Wert in temp1 soll
ausgegeben werden. Und wenn man sich die aufrufenden Stellen ansieht,
ist klar dass es keine Rolle spielt, wenn das Register in der Funktion
zuerstört wird.
Deine chinesischen Zeichen müssen woanders herkommen.
Leute, da ist wirklich ein Fehler drin :-o
lcd_data erwartet das auszugebende Zeichen im temp1, und deshalb geht
das schief, weil der 10er ja im temp2 gezählt wurde...
1
:
2
ldi temp2, '0'
3
lcd_number_10:
4
subi temp1, 10 ; abzählen wieviele Zehner in
5
brcs lcd_number_1 ; der Zahl enthalten sind
6
inc temp2 ; Zehner mitzählen
7
rjmp lcd_number_10
8
lcd_number_1:
9
rcall lcd_data ; die Zehnerstelle ausgeben
10
:
Ich habe da mal einen schnellen Würgaround mit einem lokalen push/pop
ins Tut gehackt, aber mir gefällt die Lösung von Peter Bednarz
eigentlich besser...
Und das könnte man noch ein wenig abkürzen:
1
subi temp1, -10 ; 10 wieder dazuzählen, da die
2
; vorhergehende Schleife 10 zuviel
3
; abgezogen hat
4
; das Subtrahieren von -10
5
; = Addition von +10 ist ein Trick
6
; da kein addi Befehl existiert
7
ldi temp2, '0' ; die übrig gebliebenen Einer
8
add temp1, temp2 ; noch ausgeben
9
rcall lcd_data ;
Denn -(-10) und +'0' zusammen ergeben 10+'0'
1
ldi temp2, 10+'0' ; die übrig gebliebenen Einer
2
add temp1, temp2 ; korrigieren und ausgeben
3
rcall lcd_data ;
Oder noch kürzer:
1
subi temp1, -(10+'0') ; die übrig gebliebenen Einer korrigieren und ausgeben
Lothar Miller schrieb:
> Leute, da ist wirklich ein Fehler drin :-o> lcd_data erwartet das auszugebende Zeichen im temp1, und deshalb geht> das schief, weil der 10er ja im temp2 gezählt wurde...
Ah. Nach unten hin, zu den LCD Routinen, hab ichs nicht mehr
kontrolliert. Ich hab mich auf dioe Aufrufe dieser Funktion
konzentriert.
> Ich habe da mal einen schnellen Würgaround mit einem lokalen push/pop> ins Tut gehackt, aber mir gefällt die Lösung von Peter Bednarz> eigentlich besser...
Eigentlich sollte diese ganze temp-erei bereinigt werden und vernünftige
Namen für die Register vergeben werden. Dann passiert so etwas nicht.
Das Problem ist, daß man sich als Assemblerprogrammierer erstmal
Programmierkonventionen festlegen muß.
Also welche Register sind für Argumente und Returnwerte reserviert und
denen dann entsprechende Namen gibt.
Und dann sollte man sich auch einige Scratchpadregister definieren, die
niemals gepusht werden müssen.
Wenn der Aufrufer Variablen später noch braucht, muß er sie selber
sichern oder im SRAM ablegen.
Und dann ist es ne gute Idee, für Interrupts Register zu reservieren,
damit auch dort Push/Pop weitgehend überflüssig sind.
Generell sollte man globale Variablen nicht in Registern halten.
Register sollten nur die lokalen Arbeitspferde sein, sonst pusht/popt
man sich nur tot.
Gerade als Assemblerprogrammierer kann man nicht so einfach drauflos
programmieren.
Peter
Peter Dannegger schrieb:
> Gerade als Assemblerprogrammierer kann man nicht so einfach drauflos> programmieren.
Ich geb dir absolut recht.
Das Problem im Tut ist, dass man irgendwo anfangen muss.
Globale Variablen nicht in Register geht in den Kapiteln vor dem SRAM
gar nicht.
Auf der anderen Seite will man im Tut natürlich auch ein paar
interessante Programme zeigen ohne lange drumherum reden zu müssen. D.h.
die scheinbare Einfachheit der ersten Tut-Kapitel rächt sich, sobald es
darum geht möglichst wiederverwendbare Module zu schaffen (wie zb LCD
Ansteuerung). Zum Zeitpunkt, wenn die LCD Routinen eingeführt werden,
möchte man im Tut natürlich nicht die großen, eigentlich notwendigen,
Gedankengänge einführen und erläutern, die aber notwendig wären.
Es ist halt schwer. Im Grunde läuft es immer wieder aufs Gleiche raus:
Man müsste alles gleichzeitig vermitteln können:-)
Mal sehen, ob man da mit klammheimlich eingeführten Registerkonventionen
noch etwas retten kann. Im Tut wird ja r16 praktisch überall benutzt, in
Ermangelung von vorausschauender Arbeitsweise, die auch berücksichtigt,
was in späteren Kapiteln noch dazu kommt und welche Register dort
benutzt werden.