{"id":105,"date":"2024-01-17T12:08:33","date_gmt":"2024-01-17T12:08:33","guid":{"rendered":"https:\/\/madgyver.de\/?p=105"},"modified":"2024-02-01T13:59:01","modified_gmt":"2024-02-01T13:59:01","slug":"arduino-assembler-tutorial-das-erste-programm","status":"publish","type":"post","link":"https:\/\/madgyver.de\/de\/arduino-assembler-tutorial-das-erste-programm\/","title":{"rendered":"Arduino Assembler Tutorial : Das erste Programm"},"content":{"rendered":"<p>Die meisten fangen beim Arduino mit dem \u201eBlink\u201c Programm an, welches man bei den Beispielen unter 01.Basics\u2013&gt;Blink findet. Dieses Programm setzt den Pin 13 als Ausgang und schaltet diesen dann in der Loop-Schleife abwechselnd auf HIGH oder LOW, mit jeweils einer Sekunde Wartezeit zwischen dem Umschalten. Dieses einfache Einsteigerprogramm ist gut geeignet um zu zeigen wie man dieses Programm auch in Assembler (ASM) l\u00f6sen kann.<\/p>\n<p>Zuallererst sollte man wissen, dass es 2 M\u00f6glichkeiten gibt um C\/C++ code mit ASM zu mischen. Die erste besteht in der Verwendung von sog. Inline Assembler und die zweite ist das einbinden von Funktionsaufrufen die in ASM geschrieben sind. Wir besch\u00e4ftigen uns hier mit der letzteren M\u00f6glichkeit, weil diese mit der Arduino IDE deutlich angenehmer ist.<\/p>\n<p>Die Prozedur\u00a0ist \u00e4hnlich wie beim Schreiben einer Library. Wir brauchen auch hier eine Headerdatei in dem wir unsere Funktionen zuerst deklarieren und eine zweite Datei in der wir die Funktionen dann implemtieren. Fangen wir also an:<\/p>\n<p>Alle Dateien sind auch auf Github zu finden:\u00a0https:\/\/gist.github.com\/92540e0f8621bc8b0566.git<\/p>\n<ol>\n<li>Einen Ordner mit Namen\u201cAssembler\u201c im Library Ordner\u00a0anlegen<\/li>\n<li>In diesem Ordner die Dateien \u201eASM_Blink.H\u201c und \u201eASM_Blink.S\u201c erstellen<\/li>\n<\/ol>\n<p>ASM_Blink.H beinhaltet lediglich unsere Funktionsdeklarationen und sieht so aus:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"avrasm\">void ASM_digitalWrite(char pin);\r\nvoid ASM_delay();\r\n<\/pre>\n<p>Die Anweisung<br \/>\n<code class=\"EnlighterJSRAW\" data-enlighter-language=\"avrasm\">extern \"C\" { }<\/code><br \/>\nIst ein kleines Detail, welches dazu f\u00fchrt das AVR-GCC die Funktionsdeklarationen in C statt C++ kompiliert. Dies gew\u00e4hrleistet, dass die Funktionsaufrufe korrekt aufgel\u00f6st werden. Ansonsten passiert hier nicht mehr, als das \u00a0man 2 Funktionen deklariert.<\/p>\n<p>In der Datei ASM_Blink.S steht nun die Implementierung<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"avrasm\" data-enlighter-theme=\"enlighter\">.global ASM_digitalWrite \r\n\r\nASM_digitalWrite: \r\n    out 0x05,r24 ;write pin_value to port \r\n    reti ;\r\n\r\n\r\n\r\n    delay for 1 sec \r\n.global ASM_delay \r\nASM_delay: \r\n    ldi R17, 0x53 \r\ndelay_loop: \r\n    ldi R18, 0xFB \r\ndelay_loop1: \r\n    ldi R19, 0xFF \r\ndelay_loop2: \r\n    dec R19 brne \r\n    delay_loop2 \r\n\r\n    dec R18 \r\n    brne delay_loop1 \r\n\r\n    dec R17 \r\n    brne delay_loop \r\n\r\n    ret<\/pre>\n<p>&nbsp;<\/p>\n<p>Die Direktive<\/p>\n<p><code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">.global<\/code><br \/>\nmacht den dahinter liegenden Funktionsnamen global sichtbar und sorgt f\u00fcr Fehlerfreies verlinken (\u201eeinbauen\u201c unseres codes). Die Funktion\u00a0ASM_digitalWrite erwartet ein einziges 1 Byte Argument welches beim Funktionsaufruf\u00a0ins\u00a0Register R24 geladen wird. Die Funktions schreibt den Inhalt nur an den PortB. Nat\u00fcrlich kommt<\/p>\n<p>Bei der Funktion ASM_digitalWrite fragt man sich vielleicht, wie die Argumenten\u00fcbergabe\u00a0funktioniert. Nun, AVR-GCC folgt einem Algorithmus der festlegt wo etwaige Argument\u00a0gespeichert werden, den man\u00a0<a href=\"https:\/\/web.archive.org\/web\/20151102035547\/https:\/\/gcc.gnu.org\/wiki\/avr-gcc#Calling_Convention\">hier in\u00a0English nachlesen kann<\/a>. Meine \u00dcbersetzung ins Deutsche:<\/p>\n<blockquote>\n<h2 id=\"Calling_Convention\">Aufrufkonvention<\/h2>\n<ul>\n<li>Ein Argument wird entweder vollst\u00e4ndig\u00a0in Registern \u00fcbergeben oder vollst\u00e4ndig \u00fcber den\u00a0Speicher.<span id=\"line-126\" class=\"anchor\"><\/span><\/li>\n<li>\n<p class=\"line862\">Um heraus zu finden, welche Register bei der \u00dcbergabe der Argumente verwendet werden denken wir uns eine Registernummer\u00a0<em>R<sub>n<\/sub><\/em>\u00a0die wir als\u00a0R26 initieren:<span id=\"line-127\" class=\"anchor\"><\/span><\/p>\n<ol type=\"1\">\n<li>Falls das Argument eine ungerade Anzahl an Bytes gro\u00df ist, rundet man die Gr\u00f6\u00dfe zur n\u00e4chsten geraden Zahl auf.<span id=\"line-128\" class=\"anchor\"><\/span><\/li>\n<li>\n<p class=\"line862\">Subtrahiere die gerundete Gr\u00f6\u00dfe von der Registernummer\u00a0<em>R<sub>n<\/sub><\/em>.<span id=\"line-129\" class=\"anchor\"><\/span><\/p>\n<\/li>\n<li>\n<p class=\"line862\">Falls das neue\u00a0<em>R<sub>n<\/sub><\/em>\u00a0mindestens \u00a0R8 ist und die Gr\u00f6\u00dfe ungleich null, dann wird das Low-Byte des Arguments in\u00a0<em>R<sub>n<\/sub><\/em>\u00a0gespeichert. Evtl. Weiter folgende Bytes des Argumentes werden der Reihe nach in den n\u00e4chsten, aufsteigenden Registern gespeichert.<span id=\"line-130\" class=\"anchor\"><\/span><\/p>\n<\/li>\n<li>\n<p class=\"line862\">Sollte das neue Register kleiner als R8 sein oder die gr\u00f6\u00dfe des Arguments null sein, dann wird das Argument in vollst\u00e4ndig \u00fcber den\u00a0Speicher.<span id=\"line-131\" class=\"anchor\"><\/span><\/p>\n<\/li>\n<li>Sollte das momentane Argument \u00fcber den Speicher \u00fcbergeben werden, kann man aufh\u00f6ren: Alle weiteren Argumente werden ebenfalls \u00fcber den Speicher \u00fcbergeben.<span id=\"line-132\" class=\"anchor\"><\/span><\/li>\n<li>Falls noch Argumente \u00fcbrig sein, dann fange wieder bei Punkt 1 und mache mit dem n\u00e4chsten Argument weiter.<span id=\"line-133\" class=\"anchor\"><\/span><\/li>\n<\/ol>\n<\/li>\n<li>R\u00fcckgabewerte mit einer Gr\u00f6\u00dfe von 1 bis einschlie\u00dflich 8 Byte werden \u00fcber die Register zur\u00fcck gegeben.\u00a0R\u00fcckgabewerte die diese Grenze \u00fcberschreiten werden \u00fcber den Speicher zur\u00fcckgeben.<span id=\"line-134\" class=\"anchor\"><\/span><\/li>\n<li>Falls ein R\u00fcckgabewert nicht \u00fcber die Register \u00fcbergeben werden kann, dann wird auf dem Stack Speicherplatz reserviert und die dazugeh\u00f6rige Adresse beim Funktionsaufruf \u00fcbergeben. Die Funktion \u00fcbergibt den R\u00fcckgabewert \u00fcber den Speicher an diese Adresse.<span id=\"line-135\" class=\"anchor\"><\/span><\/li>\n<li>Sollten die R\u00fcckgabewerte \u00fcber die Register zur\u00fcck gegeben werden, dann werden die selben Register verwendet als w\u00e4re der Wert der erste Paramter einer nicht-varargs function. Zum Beispiel: Ein 8-Bit Wert wird in R24 zur\u00fcckgegeben und ein 32-Bit Wert wird \u00fcber R22\u2026R25 zur\u00fcckgegeben.<\/li>\n<li>Argumente einer varargs Funktion werden \u00fcber den Speicher \u00fcbergeben. Dies gilt auch f\u00fcr diejenigen Argumente mit festen Bezeichnern.<\/li>\n<\/ul>\n<p>Anm.: Die \u00dcbergabe\u00a0<strong>\u00fcber den Speicher<\/strong>\u00a0meint konkret, dass die Argumente auf den\u00a0<strong>Stack<\/strong>\u00a0geschoben werden. Dazu aber sp\u00e4ter mehr.<\/p><\/blockquote>\n<p>Schon ein bischen kompliziert, oder? F\u00fcr unser konkretes beispiel bedeutet dies folgendes: Wie man in der Funktionsdeklaration sehen kann, hat die Funktion ASM_digitalWrite nur einen einzigen Parameter namens pin und dieser ist genau ein Byte gro\u00df. Da 1\u00a0ungerade ist, runden wir die Gr\u00f6\u00dfe auf 2 und ziehen dies von R26 ab. Das bedeutet, dass unser Argument sich in R24 befindet.<\/p>\n<p>Um unseren ASM Code zu nutzen brauchen wir nur noch einen Sketch in der Arduino IDE anlegen, mit folgendem Inhalt:<\/p>\n<p>&nbsp;<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">#include \"ASM_Blink.h\" \r\n\r\n\/\/ change LEDPIN based on your schematic \r\n#define LEDPIN PINB1 \r\n\r\nvoid setup(){ \r\npinMode(13, OUTPUT); \r\n} \r\n\r\nvoid loop(){ \r\nASM_digitalWrite(0x20); \r\nASM_delay(); \r\nASM_digitalWrite(0x00); \r\nASM_delay(); \r\n}<\/pre>\n<p>&nbsp;<\/p>\n<p>Wie man sieht unterscheidet sich das Einbinden und Aufrufen der Funktionen nicht anders, als h\u00e4tte man es mit einer gew\u00f6hnlichen C\/C++ Bibliothek zu tun.<\/p>","protected":false},"excerpt":{"rendered":"<p>Die meisten fangen beim Arduino mit dem \u201eBlink\u201c Programm an, welches man bei den Beispielen unter 01.Basics\u2013&gt;Blink findet. Dieses Programm&hellip;<\/p>\n","protected":false},"author":1,"featured_media":130,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false}}},"categories":[12],"tags":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/madgyver.de\/wp-content\/uploads\/2024\/01\/Arduino_Assembler_Tutorial_with_Matrix_Hint.jpg","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p5OAKZ-1H","_links":{"self":[{"href":"https:\/\/madgyver.de\/de\/wp-json\/wp\/v2\/posts\/105"}],"collection":[{"href":"https:\/\/madgyver.de\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/madgyver.de\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/madgyver.de\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/madgyver.de\/de\/wp-json\/wp\/v2\/comments?post=105"}],"version-history":[{"count":6,"href":"https:\/\/madgyver.de\/de\/wp-json\/wp\/v2\/posts\/105\/revisions"}],"predecessor-version":[{"id":164,"href":"https:\/\/madgyver.de\/de\/wp-json\/wp\/v2\/posts\/105\/revisions\/164"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/madgyver.de\/de\/wp-json\/wp\/v2\/media\/130"}],"wp:attachment":[{"href":"https:\/\/madgyver.de\/de\/wp-json\/wp\/v2\/media?parent=105"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/madgyver.de\/de\/wp-json\/wp\/v2\/categories?post=105"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/madgyver.de\/de\/wp-json\/wp\/v2\/tags?post=105"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}