Wie arbeitet ein Assembler?



  • erik.vikinger schrieb:

    ...Alle die ich mir bisher angesehen hab sind bereits reichlich komplex...

    Ich weiss nicht, wie komplex Dein Assembler werden soll, aber vielleicht wäre folgender Ansatz besser. Vielleicht kennst Du Blackfin DSPs Assembler und hast deren Syntax gesehen. Ungefähr kann man das in C++ nachbilden, die Register, CPU Operationen, Variablen in C++ Klassen kapseln, Operatoren wie =, +, ++ usw. überladen. Dann kann man mit diesen Klassen ein C++ Programm schreiben, das wie ein Assembler Programm aussieht. Ungefähr so:

    int main(int argc, char *argv[])
    {
    	CRegister R0;
    	CRegister R1;
    	CInputOutput Port;
    	CVariable MeineVar;
    
    	R0 = 10;
    	R1 = R0;
    	--R1;
    	Port = R0;
    	MeineVar = R0;
    	MeineVar--;
    
    	return 0;
    }
    

    Die Operatoren überschreibst Du so, dass sie richtige Operationscodes ausgeben, in eine Datei schreiben o.ä, also was immer gewünscht.
    Dann kompiliert man das Programm und lässt es am PC laufen, das Programm läuft und erstellt für Dich eine exe oder Objektdatei o.ä.
    Vorteil: Man benutzt fertigen Compiler 😉



  • Hallo,

    Ich weiss nicht, wie komplex Dein Assembler werden soll

    Nicht allzu komplex. Er soll vor allem eine lineare Kette von Assembler-Befehlen in ihre Binär-Form übersetzen, dazu Labels auflösen und Variablen (also Labels in Nicht-Code-Segmenten) verwalten. Später sollen noch ".extern" und ".public" dazu kommen um linkbare Object-Files zu erstellen.

    Vielleicht kennst Du Blackfin DSPs Assembler und hast deren Syntax gesehen.

    Nein, kannst Du ein hübsches Beispiel verlinken?

    Ungefähr kann man das in C++ nachbilden, ....

    Deinen Vorschlag verstehe ich irgendwie nicht. Sollen die Operator-Methoden quasie meine Zwischencode-Liste erzeugen? Das ist vielleicht etwas einfacher als ein Parser aber danach müsste doch trotzdem das selbe Procedere kommen. Außerdem wüsste ich nicht wie ich diesen Operator-Methoden zusätzliche Parameter für den zu erzeugenden Assembler-Befehl mitgebe. Die meisten Befehle meiner CPU haben 3 oder 4 Parameter, dazu kommt das jeder Befehl bedingt ausführbar ist und viele Befehle die Größe (8Bit, 16Bit, 32Bit oder 64Bit) ihrer Parameter wissen müssen.

    Meine Ziel-CPU ist der ARM-Architektur nicht unähnlich, kennt z.B. auch Shift-Operanden.

    Grüße
    Erik



  • erik.vikinger schrieb:

    Nein, kannst Du ein hübsches Beispiel verlinken?

    Hier z.B, eine Assembler Datei für einen Blackfin DSP: http://www.google.com/codesearch/p?hl=de&sa=N&cd=1&ct=rc#3C3saXwSWsQ/trunk/linux-2.6.24.3/arch/blackfin/mach-bf537/head.S&q=blackfin startup

    erik.vikinger schrieb:

    Deinen Vorschlag verstehe ich irgendwie nicht. ...

    Dann lass es sein 🙂 Ist nur ein Vorschlag. Es müssen nicht unbedingt Operatoren sein, "einfache" Funktionen z.B.:

    void CCpu::mov(CRegister dst, int param);
    void CCpu::mov(CRegister dst, int param1, int param2);
    void CCpu::mov(CRegister src, CRegister dst);
    

    Also irgendwie so...



  • Hallo,

    Hier ...

    Danke. Sieht sehr nett aus. An diese Syntax könnte ich mich durchaus gewöhnen. Vielleicht baue ich das in meinen Parser auch ein. 🙄

    Ist nur ein Vorschlag.

    Schon klar. Ich bin Dir auch dankbar für Deine Mühe aber so wirklich verstanden hab ich, glaube ich, immer noch nicht was Du eigentlich meinst.
    Soll ich (bzw. der Benutzer meines Assemblers, also hoffentlich mal der GCC) ein C++ Programm schreiben/erzeugen das so ähnlich aussieht wie ein "normaler" Assembler-Quell-Code und diesen (zusammen mit ner speziellen Header-Datei o.ä.) durch nen Compiler schicken und dessen Executable generiert mir dann das gewünschte Object(Binär)-File? 😕
    Wie gesagt, das einzigste was mir das spart ist der Parser der manuell aus "normalem" Assembler-Quell-Code die interne Zwischencode-Liste generiert (an dem bin ich grad dran und es geht auch einigermaßen vorwärts). Diese Zwischencode-Liste würde mit Deinem Vorschlag durch die Methoden generiert. (Hab ich das richtig verstanden?) Das weiterverarbeiten dieser Liste (z.B. das Auflösen der Labels) bleibt IMHO in jedem Fall erhalten und das ist wahrscheinlich das schwerste am ganzen.

    Grüße
    Erik



  • erik.vikinger schrieb:

    Soll ich (bzw. der Benutzer meines Assemblers, also hoffentlich mal der GCC) ein C++ Programm schreiben/erzeugen das so ähnlich aussieht wie ein "normaler" Assembler-Quell-Code und diesen (zusammen mit ner speziellen Header-Datei o.ä.) durch nen Compiler schicken und dessen Executable generiert mir dann das gewünschte Object(Binär)-File? 😕

    Ja! 🙂 Genau das meine ich damit. Aber das wäre natürlich nichts für einen GCC...

    erik.vikinger schrieb:

    Wie gesagt, das einzigste was mir das spart ist der Parser der manuell aus "normalem" Assembler-Quell-Code die interne Zwischencode-Liste generiert (an dem bin ich grad dran und es geht auch einigermaßen vorwärts). Diese Zwischencode-Liste würde mit Deinem Vorschlag durch die Methoden generiert.

    Ja! 🙂 Und damit ersparst Du nicht nur einen Parser, der Compiler prüft ja z.B. ob die Parameter vom Typ passen usw. oder ob es überhaupt eine "mov" Operation gibt, wie Du sie im Quellcode eingegeben hast. Also, insgesamt hast Du eine Fehlerbehandlung, die vom Compiler gemacht wird und Dein "Quasi"-Assembler-Programm kompiliert nicht, wenn Fehler drin sind...

    erik.vikinger schrieb:

    (Hab ich das richtig verstanden?)

    Ja! 🙂

    erik.vikinger schrieb:

    Das weiterverarbeiten dieser Liste (z.B. das Auflösen der Labels) bleibt IMHO in jedem Fall erhalten und das ist wahrscheinlich das schwerste am ganzen.

    Ja, das muss man dann irgendwie noch implementieren, am Besten auch gekapselt durch ein Objekt, das alle Labels, alle Adressen von Funktionen kennt o.ä. Variablen kann man vielleicht so machen, dass sie ihre Adressen selbst kennen.
    Das wäre mein Vorschlag.



  • Hallo,

    Genau das meine ich damit.

    Die Idee ist strange aber gefällt mir irgendwie. Ich werde es aber trotzdem erst mal mit nem klassischen Parser versuchen. Trotzdem Danke für Deine Gedanken.

    Aber das wäre natürlich nichts für einen GCC...

    Nunja, es währe auf jeden Fall recht ungewöhnlich einen vom GCC erzeugten Assembler-Quelltext noch mal vom GCC verarbeiten zu lassen. Für dieses Vorgehen bräuchte man aber eben auch 2 GCC, einen für das eigentliche Target und zusätzlich einen für den verwendeten Host. Eine Cross-Compiler-Tool-Chain ist eigentlich schon komplex genug.

    Also, insgesamt hast Du eine Fehlerbehandlung, die vom Compiler gemacht wird ...

    Der Gedanke ist reizvoll aber es gibt noch mehr als nur einfache syntaktische Fehler. Nicht jeder Befehl kann mit jeder Kombination an Registern umgehen oder was ist wenn ein Speicherzugriff nur ein Byte laden soll obwohl die Variable als Word definiert wurde. Ich denke einen Großteil der möglichen Fehler muss ich trotzdem manuell finden, nachdem die Zwischencode-Liste aufgebaut wurde.

    Grüße
    Erik



  • @erik.vikinger:
    Also ein nicht-optimierender nicht-makro Assembler sollte nun wirklich kein so grosses Ding sein, dass man dafür so einen ... seltsamen Zwischenschritt brauchen würde.

    Im Prinzip tut das Ding dann ja nicht mehr als ein paar Adressen/Offsets zu verwalten, und Mnemonics (fast) 1:1 in Binärcode zu übersetzen.



  • Hallo,

    hustbaer schrieb:

    Also ein nicht-optimierender nicht-makro Assembler sollte nun wirklich kein so grosses Ding sein

    Prinzipiell hast Du sicher recht aber mein Assembler wird (später) schon einiges optimieren müssen wenn dabei vernünftiger Binär-Code raus kommen soll. Die meisten Befehle gibt es in unterschiedlichen Varianten die jeweils unterschiedlich Groß sind und damit auch unterschiedlich flexibel, trotzdem sieht der Mnemonic immer gleich aus und der Assembler muss entscheiden welchen Binär-Code er benötigt. Bei der Addition z.B. gibts die Varianten r0 := r0 + r1 (ein kleiner 2 Operanden-Befehl : "add.w r0,r0,r1") oder r0 := r1 + r2 (ein größerer 3 Operanden-Befehl : "add.w r0,r1,r2"). Noch komplexer wird es bei Sprüngen, dort muss der Assembler schauen wie weit das Ziel entfernt ist (was von der Anzahl und Größe der dazwischen liegenden Befehle, was wiederum auch Sprünge sein können deren Größe erst ermittelt werden muss, abhängt) um zu ermitteln obs ein kleiner Sprung-Befehl tut oder ob doch ein größerer Sprung-Befehl, mit mehr Reichweite, ran muss. Da gibts übrigens meistens 3 verschiedene Größen so das die Auswahl schon recht komplex werden kann.

    hustbaer schrieb:

    seltsamen Zwischenschritt

    Was meinst Du mit seltsam? Meine Zwischencode-Liste?

    hustbaer schrieb:

    und Mnemonics (fast) 1:1 in Binärcode zu übersetzen

    Selbst auf den meisten Primitiv-Instruction-Set-Architectur-CPUs (wie z.B. die 8Bit AVR von Atmel) ist das oft mehr als nur "fast 1:1". Ein Blick in die äußerst umfangreichen Sourcen der binutils lässt erahnen wie komplex so eine scheinbar simple Aufgabe in Wirklichkeit sein kann.

    Grüße
    Erik



  • Mit "seltsamen Zwischenschritt" meinte ich den Vorschlag von abc.w. Also quasi erst ein C++ Programm erstellen, welches dann compiliert und ausgeführt wird, und dann den Assembler-Code ausspuckt.

    Was den Rest angeht: ich verstehe was du meinst. Trotzdem würde ich sagen dass das alles halbwegs trivial ist.



  • hustbaer schrieb:

    und dann den Assembler-Code ausspuckt.

    Kleine Korrektur: und dann Objekt-Code oder eine "exe" ausspuckt.

    Und ich würde den "seltsamen Zwischenschritt" als "Quasi-Assembler Programm/Generator" bezeichnen 😃



  • Hallo,

    hustbaer schrieb:

    Trotzdem würde ich sagen dass das alles halbwegs trivial ist.

    Diese Einschätzung hatte ich auch mal, heute denke ich darüber anders.

    abc.w schrieb:

    als "Quasi-Assembler Programm/Generator" bezeichnen 😃

    Falls ich später mal Zeit hab, wenn mein Assembler einigermaßen funktioniert, kann ich ja mal probieren sowas umzusetzen. Allzu schwer sollte es hoffentlich nicht sein den Parser gegen was anderes auszutauschen. Das einzigste was mich an dieser Variante stört ist das man den Assembler-Quell-Text kompilieren und ausführen muss (auf dem verwendeten Host-System) damit die Binär-Daten raus kommen.

    Grüße
    Erik



  • abc.w schrieb:

    hustbaer schrieb:

    und dann den Assembler-Code ausspuckt.

    Kleine Korrektur: und dann Objekt-Code oder eine "exe" ausspuckt.

    Und ich würde den "seltsamen Zwischenschritt" als "Quasi-Assembler Programm/Generator" bezeichnen 😃

    rofl 😃
    Ja, sorry, mein Fehler. Natürlich meinte ich Objekt-Code/Binary/Byte-Code 🙂



  • Dieser Thread wurde von Moderator/in rüdiger aus dem Forum Rund um die Programmierung in das Forum Assembler verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.


Anmelden zum Antworten