Guten Tag,
ich habe mir zwei Makros gebastelt, die zum ausgeben von Zeichenketten
über USART dienen sollen.
Im 1. Makro wird eine Routine aufgerufen die im 2. Makro steht. Hinter
dem 1. Makro wird dann direkt per .db die Zeichenkette abgelegt. Damit
das Programm weiß wo es nach der routine hinspringen soll, wird vorher
noch die Sprungadresse in den z-pointer geladen.
Die Subroutine soll dann durchs 2. makro in den code eingesetzt werden.
Im prinzip könnte man die routine auch so in den code schreiben, aber
ich habe es als Makro gemacht, weil ich die routine dann beliebig
parametrieren kann. D.h. ich kann mir aussuchen welche register
verwendet werden sollen etc.
Im prinzip läd die routine nur die Adresse vom 1.Zeichen der
Zeichenkette in den "String Pointer" Der String Pointer besteht nur aus
2 Speicherzellen im RAM.
Danach aktiviert er das "USART Data register empty interrupt". Das
interrupt wird immer ausgelöst wenn der USART für das nächste byte
bereit ist.
Der Rest passiert dann in der Interrupt routine. Ich denke nich das die
was mit dem Problem zu tun hat, aber ich schreibe sie einfach mal dabei:
1
USART_UDR:pushr16
2
inr16,SREG
3
pushr16
4
pushZH
5
pushZL
6
ldszh,String_Pointer_HIGH_RAM
7
ldszl,String_Pointer_LOW_RAM
8
nop
9
nop
10
lpmr16,z+
11
cpir16,0
12
breqString_complete
13
outUDR,r16
14
stsString_Pointer_HIGH_RAM,zh
15
stsString_Pointer_LOW_RAM,zl
16
popzl
17
popzh
18
popr16
19
outSREG,r16
20
popr16
21
reti
22
23
String_complete:popzl
24
popzh
25
popr16
26
outSREG,r16
27
popr16
28
cbiUCSRB,UDRIE
29
reti
Die Macros werden dann folgendermaßen eingesetzt:
1
string_out_setr16,r17
2
3
loop:
4
string_outr16,string_end
5
.db"Test !",0x0A,0x0D,0
6
string_end:
Das ganze hat ohne makros so funktioniert, aber wie es scheint, kann das
1. makro nicht auf die routine im 2. makro zugreifen. Der compiler gibt
folgende Fehlermeldung aus:
"C:\Dokumente und Einstellungen\Administrator\Eigene Dateien\AVR
Projekte\UART_string_routine.asm(71): error: Undefined symbol:
string_out_routine"
Er tut so als würde es die routine gar nicht geben. Ich denke ein Makro
wird in den Quellcode einfach nur eingefügt, folglich müsste an der
Stelle
1
2
string_out_setr16,r17
die routine stehen die mit dem makro "string_out" aufgerufen wird. Oder
wird der code doch nicht einfach nur eingefügt?
Über ein bischen Hilfe währe ich sehr erfreut =)
mfg Steffen
Hallo,
sowas habe ich früher auf dem Z80 gemacht. :-))
Gibt es einen Grund, das 2. Macro überhaupt zu benutzen und nicht direkt
als "Subroutine" abzulegen?
Kostet doch nur Flash, weil der Code ja bei jeder Benutzung angelegt
wird.
Das Deine "Subroutine dann mit einem ijmp statt ret endet verwirrt doch
nur die anderen... ;-))
Gruß aus Berlin
Michael
Labels in Makros genießen eine Sonderbehandlung. Sie haben zwar einen
Namen (irgendwas muss man ja hinschreiben), aber man muss bedenken, dass
Makros in der Regel mehrfach verwendet werden und bei Sprüngen innerhalb
des Makros jedesmal eine andere Adresse benötigt wird.
Inerhalb von Makros kann man daher gleich mit relativen anonymen
Adressen vom Typ PC+x arbeiten, das kommt der Sache am nächsten.
Feste Adressen in Makros, die von außerhalb angesprungen werden können,
kann es daher nicht geben.
Grüße, Peter
Michael U. schrieb:
> sowas habe ich früher auf dem Z80 gemacht. :-))>> Gibt es einen Grund, das 2. Macro überhaupt zu benutzen und nicht direkt> als "Subroutine" abzulegen?>> Kostet doch nur Flash, weil der Code ja bei jeder Benutzung angelegt> wird.> Das Deine "Subroutine dann mit einem ijmp statt ret endet verwirrt doch> nur die anderen... ;-))
Ja, ich wollte ein Makro haben, damit ich die subroutine in eine .inc
packen kann. Es ist so gedacht das ich die routine dann bequem per makro
in meinen quellcode einfügen kann. Ansonsten müsst ich die routine
jedesmal manuell in den code einfügen und die register umschreiben. Das
heißt auch das makro nur einmal in den quellcode eiingefügt wird, daher
auch der Name "string_out_set".
Michael U. schrieb:
> Das Deine "Subroutine dann mit einem ijmp statt ret endet verwirrt doch> nur die anderen... ;-))
die routine kann nicht mit ret enden, weil der der programmcounter dann
auf den string zeigen würde. Der String wird ja direkt hinter den aufruf
der subroutine angehängt. Ich habe da einen kleinen Trick angewendet.
Beim call befehl wird die adresse der nachfolgenden Speicherzelle auf
den stack geworfen. Beim ret wird die adresse dann wieder vom stack in
den programmcounter geladen. Normalerweise steht dort dann der nächste
befehl. Ich hab aber direkt nach dem call befehl den string abgelegt.
D.h. es wird dann die adresse vom Stringanfang auf den stack gelegt. Die
routine benutzt die Adresse dann. Damit die subroutine dann wieder
zurück ins programm findet hab ich vorher den nächsten befehl in den
z-pointer geladen, der dann per ijmp angesprungen wird.
Peter Roth schrieb:
> Labels in Makros genießen eine Sonderbehandlung. Sie haben zwar einen> Namen (irgendwas muss man ja hinschreiben), aber man muss bedenken, dass> Makros in der Regel mehrfach verwendet werden und bei Sprüngen innerhalb> des Makros jedesmal eine andere Adresse benötigt wird.
Das ergibt natürlich Sinn. Da fällt mir jetzt auch keine elegantere
methode ein als das Label "string_out_routine" vor das Makro zu setzen.
Andererseits müsst ich die routine bei jedem programm das ich schreibe
manuell einfügen. Das wollt ich mit dem makro ja eigentlich vermeiden.
Steffen P. schrieb:
> D.h. ich kann mir aussuchen welche register> verwendet werden sollen etc.
Damit wirst Du bald selber nicht mehr durchsehen und Schiffbruch
erleiden.
Du mußt Dich einmal hinsetzen und Dir Registerkonventionen festlegen.
Z.B. A0..A3 als Arbeitsregister und Parameterübergabe, I0..I1 als
Schleifenzähler, ZERO als Nullregister (R2).
Und diese müssen dann für das ganze Projekt auch eingehalten werden.
Genau dazu ist ja der .DEF Befehl gedacht, um das bequem an einer
zentralen Stelle zu machen.
Register als Klartext (R0..R31) haben nirgends was im Quelltext
verloren, da werden nur die definierten Namen verwendet.
Außnahmen sind nur Register, die festgelegt sind, z.B. R0,R1 für SPM-
und MUL-Befehle oder die Pointerregister X,Y,Z.
Peter
Peter Dannegger schrieb:
> Du mußt Dich einmal hinsetzen und Dir Registerkonventionen festlegen.> Z.B. A0..A3 als Arbeitsregister und Parameterübergabe, I0..I1 als> Schleifenzähler, ZERO als Nullregister (R2).>> Und diese müssen dann für das ganze Projekt auch eingehalten werden.> Genau dazu ist ja der .DEF Befehl gedacht, um das bequem an einer> zentralen Stelle zu machen.
Nunja, ich möchte mir eine Laufzeitbibliothek anlegen. Die
laufzeitbibliothek möchte ich ja allgemein für projekte nutzen, nicht
nur für dieses. Ich müsste mich also immer an die genormten Register
halten, wenn ich die Bibliothek benutze. Das wollte ich eigentlich
vermeiden. So wie ich es jetzt gemacht habe muss ich ins makro nur die
Arbeitsregister eingeben die fürs Makro verwendet werden sollen. So bin
ich wesentlich flexibler beim verteilen von registern.
Außerdem gibts da noch ein ganz anderes problem: Wenn ich die subroutine
nicht als makro sondern halt als normale subroutine in meine include
datei packe, wird sie immer mit in den speicher geschrieben.
Aber zurück zum problem: Gibt es eine möglichkeit ein Label in einem
Makro global zu machen? Kann der Assembler (avr Studio) das Label im
Makro auslesen und mit der Adresse ein neues globales Label erstellen?
string_out_routine_global enthält jetzt die Adresse vom label
string_out_routine. Jetzt kann ich von außerhalb auf das label im makro
springen. Natürlich würds jetzt probleme geben wenn ich das makro
mermals aufrufe, aber dazu ist es ja nicht gedacht.
So etwas wie smart linker gibt es beim assembler nicht oder?
Steffen P. schrieb:
> Nunja, ich möchte mir eine Laufzeitbibliothek anlegen. Die> laufzeitbibliothek möchte ich ja allgemein für projekte nutzen, nicht> nur für dieses. Ich müsste mich also immer an die genormten Register> halten, wenn ich die Bibliothek benutze.
Du verstehst nicht den Sinn dahinter.
Das Ausdenken von Regeln und sich daran halten ist kein Mumpitz, sondern
soll Dir das Programmieren wesentlich erleichtern.
Man nimmt unnötige Flexibilität raus, um Übersicht zu gewinnen und damit
weniger Fehler zu machen.
Wenn Du kein System hast, wie Du die Register verwendest, kannst Du nie
größere Projekte bearbeiten. Du wirst Dich unweigerlich in Deinem
Macro-Parameter-Wust verirren.
Das Vereinbaren von Konventionen ist nicht auf meinem Mist gewachsen,
sondern jeder Compiler macht das so und fährt nicht schlecht dabei.
Das bedingte Assemblieren macht man üblicher Weise über Symbole:
Hi
>Register als Klartext (R0..R31) haben nirgends was im Quelltext>verloren, da werden nur die definierten Namen verwendet.>Wenn Du kein System hast, wie Du die Register verwendest, kannst Du nie>größere Projekte bearbeiten.
Das ist mir doch etwas zu hanebüchen. 'defs' sind vielleicht in
kleineren Programme hilfreich. Bei grösseren Programmen ist spätestens
beim dritten Unterprogramm der gewählte Bezeichner total irreführend
oder man verbringt seine Zeit mit Registerdefinieren und Redefinieren
statt mit Programmieren. Total tödlich und als einziges verwerflich ist
die gemischte Verwendung von Registerdefinitionen und Registernamen.
Das führt unweigerlich zum Chaos.
MfG Spess
spess53 schrieb:
> Das ist mir doch etwas zu hanebüchen. 'defs' sind vielleicht in> kleineren Programme hilfreich. Bei grösseren Programmen ist spätestens> beim dritten Unterprogramm der gewählte Bezeichner total irreführend> oder man verbringt seine Zeit mit Registerdefinieren und Redefinieren> statt mit Programmieren.
Hä?
Die Register werden nur einmal gobal definiert und dann natürlich in
sämtlichen Funktionen beibehalten.
Sonst klappt ja die Parameterübergabe nicht mehr, wenn da jede Funktion
ihr eigenes Süppchen kocht.
Z.B. A0 ist das low-Byte des ersten Parameters bzw. ersten Returnwertes
usw.
Hier mal ein Beispiel:
http://www.mikrocontroller.net/attachment/16260/UDIV32.ASM
Peter
Hi
>Z.B. A0 ist das low-Byte des ersten Parameters bzw. ersten Returnwertes>usw.
Wenn ich, nur mal zum Beispiel, den oder die Eingangswerte eines
Unterprogramms nicht verändern will, weil ich die noch brauche, haut
dieses System schon per Definition nicht nicht mehr hin. Oder ich muss
zusätzlichen Overhead, wie diese MOV-Orgien, die ich aus
disassemblierten C-Code kenne, erzeugen.
Anderer Fall: Dieses 'temp'-Gedödel. Diese Register sollten universell
sein, also >=r16. Nach Abzug von X,Y,Z bleiben davon 10 Register übrig,
die man aber auch für die Parameter haben möchte. Kann sehr schnell eng
werden. Also wieder entweder inkonsequent oder Byteschubsen.
Dieses Prinzip lässt sich mit etwas Diszilin auch ohne DEFs
verwirklichen. Und man ist bleibt flexibel.
Von mir aus kann das jeder halten wie er will, aber ein absolutes Muss
ist es keinesfalls.
MfG spess
spess53 schrieb:
> Wenn ich, nur mal zum Beispiel, den oder die Eingangswerte eines> Unterprogramms nicht verändern will, weil ich die noch brauche, haut> dieses System schon per Definition nicht nicht mehr hin. Oder ich muss> zusätzlichen Overhead, wie diese MOV-Orgien, die ich aus> disassemblierten C-Code kenne, erzeugen.
Du brauchst in jedem Fall "MOV-Orgien" (2* MOVW bei 32 Bit ist für mich
aber noch lange keine Orgie).
Der AVR hat ja keine 3 Operanden Befehle (a = b + c), es kann also kein
anderes Ergebnisregister angegeben werden.
Der ursprüngliche Wert wird zerstört, Ausnahme ist nur der
Comparebefehl, da er kein Ergebnis speichert.
Wenn also mit einem Wert gerechnet werde soll und der gleiche Wert noch
benötigt wird, dann mußt Du eine Kopie anlegen!
Ich kann mir auch nicht vorstellen, daß es besser ist, mehrere
Rechenfunktionen für verschiedene Registerkombinationen im Code
anzulegen. Die paar MOVW sind dagegen nur Pinats.
Peter
Hi
>Der AVR hat ja keine 3 Operanden Befehle (a = b + c), es kann also kein>anderes Ergebnisregister angegeben werden.
Es ist aber wohl ein Unterschied (r16 ist vorgegeben) wenn ich schreibe:
....
ldi r17,5
add r16,r17
oder
....
ldi r17,5
add r17,r16
oder?
>Ich kann mir auch nicht vorstellen, daß es besser ist, mehrere>Rechenfunktionen für verschiedene Registerkombinationen im Code>anzulegen.
Da hast du schon recht. Allerdings kein Argument für 'DEFs'. Ob ich in
einem Unterprogramm r16...r23 oder A0...A7 herumwuseln habe macht für
mich keinen Unterschied. Wenn ich allerdings gerade nicht weiß ob X1
gerade r15 oder r16 ist, dann schon.
MfG Spess