PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Register sichern in eigenen Assembler Funktionen



izaseba
02.03.2006, 16:03
Hallo,

Ich möchte gerne ein paar Funktionen in Assembler auslagern
(in einer .S Datei), was auch an für sich gut geklappt hat.
Was ich mich allerdings frage, muß ich immer den Inhalt von den Registern mit dennen ich arbeite sichern (push, pop) oder ist das nicht notwendig ?
Wenn ich in einer Funktion Parameter erwarte werden sie ja in Registern R24 nach oben hin abgelegt, das ist klar, aber wenn ich keine erwarte, dürfte ich sie doch nach belieben beschreiben, oder muß man zwingend den Inhalt "retten" ?

Gruß Sebastian

SprinterSB
02.03.2006, 16:17
Wie wär's damit:
https://www.roboternetz.de/wissen/index.php/Avr-gcc#Registerverwendung

izaseba
02.03.2006, 16:35
#-o Danke Sprinter,
ich hab in Deinem Artikel schon geguckt, aber das habe ich überlesen :oops:

Das heißt also, daß ich die R18-R27 verwenden darf, und den Inhalt nicht mehr herzustellen brauch.
Und mit dem "runden" meinst Du daß LSB immer auf einem geradem (24,22,20 usw.) Register liegt, zwei chars z.B R24 und R22 ?

Gruß Sebastian

SprinterSB
02.03.2006, 16:44
So ist es.

Zudem kannst du das Z-Register R31:R30 verwenden, ohne es zu restaurieren. Auch Parameterregister kannst du überheinern, ohne sie wieder herzustellen. Es sei denn, du ruftst von deinem asm aus selber Funktionen auf, die Parameter-Register unterhalb von R18 brauchen.

izaseba
02.03.2006, 17:03
super, danke Du hast mir sehr geholfen,
vielleicht könntest Du mir noch eine kleine Frage beantworten, und zwar
hab ich in Peter Fleury I2C Routine folgende Zeilen gefunden, die mir nichts sagen:


.stabs "",100,0,0,i2c_delay_T2

.stabs "i2cmaster.S",100,0,0,i2c_delay_T2

.func i2c_delay_T2 ; delay 5.0 microsec with 16 Mhz crystal


.func ist klar, aber die zwei .stabs ? und vor alem die Parammeter...
Es geht sich nicht um diese speziele Routine, sondern um es allgemein zu verstehen...
*Hoffnungsvoll nach oben guckend*

Gruß Sebastian

SprinterSB
02.03.2006, 17:04
Alternativ könntest du die Funktionen als C-Stubs machen, die nur Inline Assembler enthalten. In dem Falls übernimmt avr-gcc die Registerverwaltung/-sicherung, Einsortierung von Parametern, return-Wert etc.

SprinterSB
02.03.2006, 17:08
Das sind debug-Infos im stabs-Format. Was die sagen weiß ich net, da müsstest du stabs lernen. (dwarf ist nochmal übler).

Übersetz doch mal ein C-File mit -g oder
-gstabs Generate debug information in STABS format
-gstabs+ Generate debug information in extended STABS

Das erste ist glaub die Zeilennummer. Am besten lässt du das den Debugger machen, der weiß, wie das zu lesen ist ;-)

In Code resultiert das nicht, landen tut es in einer .stab*-Section (bei elf) in hex ist AFAIK keine Debug-Info drinne.

izaseba
02.03.2006, 17:22
Danke, nur Debugger-Infos, also nichts was mit Programmablauf zu tun hat...
Ich glaube, ich muß mich auch mal mit dem gdb auseinandersetzen (so ein kurzes Leben, und so viele Sachen zu lernen)
Mit dem Inline Assembler hast Du sicher recht, nur mit seinem Syntax komm ich irgendwie noch nicht so richtig zurecht, aber das kommt noch.
Ich möchte mich nochmal für die schnelle und gute Hilfe bedanken.

Gruß Sebastian

SprinterSB
02.03.2006, 17:28
Ja, die Syntax ist recht vertrackt. Vielleicht hilft ja der neue Artikel über Inline-Assembler weiter.

Längere Funktionen schreibt man jedenfalls bequemer direkt in Assembler, aber gerade wenn man sich selber um die Registerallokierung kümmern muss ist das nervig.

izaseba
02.03.2006, 20:14
Vielleicht hilft ja der neue Artikel über Inline-Assembler weiter.

Bist Du einen am schreiben? =P~
Das fänd ich gut, vor allem, weil es halt irgendwie Artikel dieser Art im Netz nicht zu finden sind.

Gruß Sebastian

SprinterSB
03.03.2006, 08:05
Bei deiner Doku der avr-libc steht auch was zu Inline-Assembler. Zu dem Artikel Inline-Assembler (https://www.roboternetz.de/wissen/index.php/Inline-Assembler_in_avr-gcc) hast du vielleicht hoch Verbesserungsvorschläge, falls es zu fachidiotisch daherkommt oder so? Wenn, dann am besten auf der dortigen Diskussionsseite (https://www.roboternetz.de/wissen/index.php?title=Diskussion:Inline-Assembler_in_avr-gcc&action=edit), damit's hier nicht versackt.

izaseba
03.03.2006, 15:47
Ich werde versuchen, am Wochenende etwas mit Hilfe von Deinem Artikel zu coden, danach kann ich sicher besser beurteilen, ob es aus meiner Sicht zu verbessern gibt.

Gruß Sebastian

SprinterSB
03.03.2006, 15:57
Wozu brauchst du überhaupt Assembler?

izaseba
03.03.2006, 16:21
Wozu brauchst du überhaupt Assembler?

Das ist eine gute Frage... hehe
Klar könnte ich mein vorhaben komplett in C schreiben...

Naja, ich schreibe mir gerade ein LCD Ansteuerprogramm von USB über rs232 auf HD44780 und ein paar Sachen (Busyflag abfrage, Daten und Befehle senden) wollte ich halt in Assembler implementieren.
Und warum, naja um etwas dabei zu lernen, ganz einfach, wie lernt man sonst, als nicht durchs üben.
Ich könnte auch irgendwas fertiges nehmen (wozu das Rad 2 mal erfinden), aber irgendwas selber hinzubekommen macht viel mehr Spaß...

Ist das ein Grund ?

Gruß Sebastian

SprinterSB
03.03.2006, 16:25
Oje... ein USB-Client in Assembler?

izaseba
03.03.2006, 16:34
Oje... ein USB-Client in Assembler?

:cheesy: Neeeeee das wäre glaub ich zu viel für mein kleines Köpfchen,
als USB <-> RS232 dient ein FT232BM mit nachgeschaltetem M8 der wiederum das Display ansteuert...
Nichts besonderes, das klappt auch schon gut in Assembler, aber irgendwie will ich das auch in C mal schreiben

Gruß Sebastian

SprinterSB
03.03.2006, 16:36
Achso, du willst es in C machen, kannst die Finger aber nicht vom Assembler lassen ;-)

izaseba
03.03.2006, 16:53
:oops: jetzt hast Du mich erwischt :oops:

Irgendwie wiederspricht sich das 8-[

Dann mach ich es erst komplett in c und danach werde ich beide verheiraten,

Gruß Sebastian

SprinterSB
03.03.2006, 23:19
Assembler ist ja nix schlimmes. Aber mit nem guten C-Compiler wie avr-gcc bleibt nicht mehr sooo viel zum optimieren übrig. Teilweise kommt es auch drauf an, wie man etwas in C ausdrückt bzw. hinschreibt.

Mit ein ner handvoll kleiner asm-Schnippsel, chirurgisch exakt eingesetzt, kann man aber schon nen spürbaren Code-Schrumpf erzielen. Teilweise sogar durch asm, der keinen(!) Code gibt, sondern nur die Reloads beeinflusst.

izaseba
04.03.2006, 11:22
Aber mit nem guten C-Compiler wie avr-gcc bleibt nicht mehr sooo viel zum optimieren übrig.

Ja das stimmt, gcc ist schon ein tolles Werkzeug man muß nur wissen es richtig einzusetzen, was die wenigsten können.


Teilweise sogar durch asm, der keinen(!) Code gibt, sondern nur die Reloads beeinflusst.

Hast Du dazu auch ein Beispiel ?
Im RN_Wissen Bereich zu suchen ist ein Krampf (oder ich stell mich doof an)

Gruß Sebastian

SprinterSB
04.03.2006, 15:30
Ein Programm unterteile ich in Module, wie man das eben so macht. Zu jedem Modul gibt es (statische) Daten. Diese Daten lege ich nicht einzeln an als Bytes oder Worte, sondern in Strukturen. Damit ist jedes Modul grob so was wie ein Objekt in einer OO Sprache (natürlich viel schwächer).

Soweit ist das ein alter Hut.

Nehmen wir mal so eine Objekt:

foo_t foo;
Funktionen lesen und schreiben diese Daten. Ein Zugriff auf z.B.

foo.bar = blah;
ist dabei 4 Byte lang (lds, sts):

sts foo+6, r24
Man kann nun auf die Idee kommen, in Bereichen, die viel auf foo rumnudeln, indirekt auf foo zuzugreifen, denn ein indirekter Zugriff mit ld(d) oder st(d) kostet nur 2 Bytes und ist gleichschnell:

foo_t * pfoo = &foo;
pfoo->bar = blah;
avr-gcc macht daraus:

sts foo+6, r24
Shit! gcc kann zur Compilezeit die Adresse von foo bestimmen und greift direkt nach foo, aber nicht indirekt, wie wir gerne hätten. Meistens ist das auch ok, Adressregister sind begehrt, wie du vom Assembler her weisst. Zudem muss für nen indirekten Zugriff das Adress-Register erst mit der Adresse vorgeladen werden, und das kostet. Für wenige Instruktionen trifft avr-gcc also die beste Entscheidung, zumal wenn zwischen den Zugriffen noch Funktionsaufrufe passieren. Da müsste als Register eines der call-saved genommen werden, und das kann nur Y sein (ABI von avr-gcc, Link von oben), wo dann im Prolog/Epilog noch Stack-Zugriffe hinzukommen.

Wir brauchen also einen Weg, um zu erzwingen, daß die Adresse von foo im Register bleibt. Das geht, indem wir die Information, daß es sich dabei um die Adresse von foo handelt, wieder vernichten! Dazu habe ich ein Inline-Asm geschrieben (Geht für alle gcc, nicht nur für avr-gcc):

#define RELOAD(reg,var) \
__asm volatile (";RELOAD " reg " with " #var : "=" reg (var) : "0" (var))

Nachdem der Präprozessor darüber gerauscht ist, etwa über

RELOAD ("z", pfoo);
sieht's nicht mehr ganz so kryptisch aus. Im wesentlichen bleibt davon übrig

__asm volatile (";RELOAD z with pfoo" : "=z" (pfoo) : "0" (pfoo));
was sagt, daß pfoo in ein Register der Klasse "z" (also Z) geladen werden soll. Dann wird das asm in den Code eingefügt (Kommentar) und Z bekommt für gcc einen nicht nachvollziehbaren Wert, der aber der gleiche ist wie vorher, weil ja nix passiert im asm.


foo_t * pfoo = &foo;
RELOAD ("z", pfoo);
pfoo->bar = blah;

gibt dann

ldi r30,lo8(foo)
ldi r31,hi8(foo)
/* #APP */
;RELOAD z with pfoo
/* #NOAPP */
std Z+6, r24
Aus Systemsicht segmentiert man das RAM und benutzt Z bzw. Y als Data Segment Pointer. Zum Glück hat man die Wahl, ich würd nicht freiwillig auf ner segmentierten Arechitektur proggen. Für AVR war ursprünglich sogar ein segmentiertes Layout geplant, aber das erwies sich als zu schwer handhabbar für Hochsprachen.

Hier mal ein Beispiel aus dem wahren Leben

dcf_error_t dcf_check_time()
{
// Eine neue Minute hat angefangen:
// DCF-Zeit auf Fehler prüfen und
// in time_dcf-Struktur kopieren, wenn ok.

dcf_t *z = &dcf;
RELOAD ("z", z);

if (parity_even_bit (z->time_bits.minute ^ z->time_bits.minute_parity))
return BAD_PARITY_M;

if (parity_even_bit (z->time_bits.hour ^ z->time_bits.hour_parity))
return BAD_PARITY_H;

if (parity_even_bit (z->time_bits.date_parity
^ z->time_bits.day ^ z->time_bits.day_of_week
^ z->time_bits.month ^ z->time_bits.year
))
return BAD_PARITY_D;

z->time.second = 0;
z->time.minute = z->time_bits.minute;
z->time.hour = z->time_bits.hour;
z->time.year = z->time_bits.year;
z->time.day = z->time_bits.day;
z->time.day_of_week = z->time_bits.day_of_week;
z->time.month = z->time_bits.month;

return DCF_OK;
}
time und time_bits sind unterschiedlich aufgebaut und können daher nicht mit z->time = z->time_bits kopiert werden, ausserdem befinden sich schon viele Werte in Registern.

Mit RELOAD hat das 130 Byte, ohne RELOAD sind es 158 Byte, das sind über 20% mehr!

Natürlich hätte man auch die Adresse von foo als Parameter übergeben können, aber das hilft auch nicht immer, weil gcc einfach zu schlau ist...

Bei den meisten Funktionen schreibe ich die Zugriffe also mit -> , das ist nur eine Zeile Code mehr, um die Adresse zu besorgen. Der generierte Code sieht aber genauso aus, wie mit direktem Zugriff wie oben erklärt, und man vergibt sich nix dabei. Ein Blick ins asm erklärt schnell, ob sich ein explizites RELOAD lohnt.

Hab eben mal die Statistik zu meinem momentanen Projekt geschaut: Es hat über 3900 Zeilen spartanisch kommentierten C-Code, braucht aber nur knapp 7kByte Flash des ATmega8. Und da kommt noch einiges mehr rein! (ein grösserer µC kommt nicht in Frage).

izaseba
04.03.2006, 18:41
Danke Sprinter,
mit so einer detalierten Beschreibung habe ich nicht gerechnet, tolle Sache mit den Strukturen, super Programmierstill,
wenn man es näher überlegt, ist es auch irgendwie logisch, daß man auf diese
Weise schnellen und knappen Code produziert.
Es ist mir nicht aufgefallen, daß man beim Zugriff auf (alle?) Devices von Linux sehr viel mit Strukturen arbeitet, jetzt wird mir klar warum...

Ich hoffe, daß ich es auch schaffe das alles für mich umzusetzen.

Gruß Sebastian

SprinterSB
05.03.2006, 08:53
Mit Kompusiten (also structs und unions) bze Objekten zu arbeiten, ist der einzige Weg, überhaupt sinnvoll programmieren zu können. Wenn z.B. eine Struktuv verschickt werden soll, will man nicht jede Variable einzeln angeben.

Soll z.B ein foo_t verschickt werden -- wie auch immer -- dann interessiert der interne Aufbau von foo_t überhaupt nicht. Das einzige was interessiert ist, wieviele Bytes es hat.

Wenn es also eine Sendefunktion gibt

send_n_bytes (const uint8_t * anfangs_addr, const size_t anzahl_bytes)
dann verschickt man es einfach mit

send_n_bytes ((const uint8_t *) &foo, sizeof (foo_t));

Ausserdem kann man auf ein foo zugreifen indem man eine Adresse übergibt, und nicht, indem man für jedes Element von foo was übergeben muss. Ausserdem kann man nachträglich die Struktur erweitern, ohne daß sich Interfaces ändern (also die Prototypen von Funktionen, etc).

izaseba
05.03.2006, 20:35
Hallo,
Ich habe gestern mein LCD Programm in C umgesetzt, dabei auch mit Strukturen und Zeigern darauf gearbeitet (man will ja was aus diesem Thread lernen)
Fazit:
Der C Code ist um 10 Bytes kleiner, als mein Assembler :oops: also ein Hoch auf gcc, und die tollen Leute, die daran arbeiten.

Gruß Sebastian

SprinterSB
05.03.2006, 21:22
Da sag noch einer, Hochsprachen führten zu schlechterem Code (wobeo, so "hoch" ist C ja nun auch wieder nicht).

Aber wundern tut's mich schon. Mit der 4er Version von gcc wir der Code noch mal dichter, wenn SSA voll zuschlägt (und die 4er Version stabiler und einsetzbar ist).

izaseba
05.03.2006, 23:28
Aber wundern tut's mich schon
Naja, manche Sachen hab ich etwas anders gemacht, vielleicht hab ich teilweise etwas zu kompliziert gedacht, aber Du kennst alle pro und Kontra für und gegen Assembler, es dauert etwas zu lange, um alles 100% zu lösen.
Ja die gcc 4.xx.xx Version...
Dazu ließt man zu viele negative Beiträge in den Foren, bleibt abzuwarten bis sie mal irgendwann stable ist.

Gruß Sebastian