{"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\/en\/arduino-assembler-tutorial-das-erste-programm\/","title":{"rendered":"Arduino Assembler Tutorial : Our First Programm"},"content":{"rendered":"<p>In this series I want to show you, how to use the Assembler (ASM) language on your Arduino and AVR. Instead of using plain assembly however, I will show you how to merge it with your C++ Code. Keep in mind, that ASM code is not very portable. This tutorial is only for AVR processors (like the Atmega168 or Atmega328 for example). Your code will not work on an Arduino Due (except for trivial cases), since it uses an ARM processor with vastly different architecture. It is also assumed, that you use the AVR-GCC compiler, which is the standard compiler used by the Arduino IDE. Anyway, a good place to start is to modify the \u201cBlink\u201d example sketch which comes with the Arduino.<\/p>\n<div class=\"googlepublisherpluginad\"><ins class=\"adsbygoogle\" data-ad-client=\"ca-pub-2961321300718895\" data-ad-slot=\"4015081196\" data-ad-format=\"auto\" data-ad-channel=\"WordPressSinglePost\" data-tag-origin=\"pso\" data-adsbygoogle-status=\"done\"><ins id=\"aswift_0_expand\"><ins id=\"aswift_0_anchor\"><iframe id=\"aswift_0\" name=\"aswift_0\" width=\"620\" height=\"60\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\" data-mce-fragment=\"1\"><\/iframe><\/ins><\/ins><\/ins><\/div>\n<p>First of all, you should know that there are (at least) 2 ways to merge C\/C++ Code with ASM. The first one ist to use Inline Assembler and the second one is to write function calls in assembly. I will show you the second option, since Inline Assembler uses (in my opinion) a horrible syntax which makes the already hard to read ASM even harder to debug.<\/p>\n<p>The workflow is similar to writing a library. We will need to create a header file in which we will declare our functions and a second .S file which will contain our implementation.\u00a0You can find all files on Github:\u00a0<a href=\"https:\/\/web.archive.org\/web\/20151211224510\/https:\/\/gist.github.com\/madgyver\/92540e0f8621bc8b0566\">https:\/\/gist.github.com\/madgyver\/92540e0f8621bc8b0566<\/a><\/p>\n<p>Lets get started:<\/p>\n<ol>\n<li>Create a new folder called \u201cAssembler\u201d in your library folder<\/li>\n<li>In this folder, create the files \u201cASM_Blink.H\u201d and \u201cASM_Blink.S\u201d<\/li>\n<\/ol>\n<p><strong>ASM_Blink.H<\/strong>\u00a0contains our declarations and looks like this:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"avrasm\">void ASM_digitalWrite(char pin);\r\nvoid ASM_delay();\r\n<\/pre>\n<p>This part:<\/p>\n<p><code class=\"EnlighterJSRAW\" data-enlighter-language=\"avrasm\">extern \"C\" { }<\/code><\/p>\n<p>is a small detail, that makes AVR-GCC compile the declarations as C Code instead of C++. It smoothes out some issues with the function calls\u00a0<del datetime=\"2015-12-08T23:01:09+00:00\">that I don\u2019t understand completely but has never been a problem so far<\/del>\u00a0that\u00a0<a href=\"https:\/\/web.archive.org\/web\/20151211224510\/https:\/\/www.reddit.com\/r\/arduino\/comments\/3vyt9h\/here_is_a_tutorial_on_how_to_use_assembler_on_the\/cxrzh51\">Yamitenshi at reddit<\/a>\u00a0explained to me as follows:<\/p>\n<blockquote><p>Because C++ allows overloading (defining multiple functions with the same name but different arguments), function names are mangled, meaning (as a simple example)\u00a0<code>int foo(int a, int b)<\/code>\u00a0might end up being named\u00a0<code>foo_riaiai\u00a0<\/code>when the compiler is done with it, as opposed to\u00a0<code>int foo(int a)<\/code>\u00a0might be called\u00a0<code>foo_riai<\/code>\u00a0so the two can be told apart.<\/p>\n<p>C does not allow this \u2013 therefore both\u00a0<code>int foo(int a, int b)<\/code>\u00a0and\u00a0<code>void foo(int a, char *b, float c)<\/code>\u00a0will be named\u00a0<code>foo<\/code>\u00a0when the compiler is done with it.<\/p>\n<p><code>extern \"C\" {}<\/code>\u00a0makes sure that the compiler knows that whatever library has defined foo was compiled in such a way that it\u2019s still called\u00a0<code>foo<\/code>, and not some variation thereof, which has the added benefit of allowing you to define\u00a0<code>foo<\/code>\u00a0in assembly, because you know the linker will be looking for\u00a0<code>foo<\/code>, and not\u00a0<code>foo_riaiai<\/code>.<\/p><\/blockquote>\n<p>Besides that, this is a pretty standard declaration of 2 functions.<\/p>\n<p>The file\u00a0<strong>ASM_Blink.S<\/strong>\u00a0contains the implementation:<\/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>The directive<\/p>\n<p><code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">.global<\/code><\/p>\n<p>makes the following function names globally visible so that the linker can find it. The function ASM_digitalWrite uses a 1 Byte argument, which will be placed into register R24 by AVR-GCC. The function itself will then output that byte to PortB. You may be wondering, why AVR-GCC places our argument at R24 of all places. Turns out, there is an algorithm it uses to determine the placement of arguments, which you can read up on\u00a0<a href=\"https:\/\/web.archive.org\/web\/20151211224510\/https:\/\/gcc.gnu.org\/wiki\/avr-gcc#Calling_Convention\">in the official wiki.<\/a>.<\/p>\n<p>It is a little bit complicated, when you read it for the first time.<br \/>\nFor our example it boils down to: Our function\u00a0<em>ASM_digitalWrite<\/em>\u00a0only uses one argument with the size of 1 Byte. Since 1 is an odd number, we round it to 2 and subtract that from R26, which gives us R24 where our argument will reside.<\/p>\n<div class=\"googlepublisherpluginad\"><ins class=\"adsbygoogle\" data-ad-client=\"ca-pub-2961321300718895\" data-ad-slot=\"8839094393\" data-ad-format=\"auto\" data-ad-channel=\"WordPressSinglePost\" data-tag-origin=\"pso\" data-adsbygoogle-status=\"done\"><ins id=\"aswift_1_expand\"><ins id=\"aswift_1_anchor\"><iframe id=\"aswift_1\" name=\"aswift_1\" width=\"620\" height=\"60\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\" data-mce-fragment=\"1\"><\/iframe><\/ins><\/ins><\/ins><\/div>\n<p>Now we just need to put everything inside a sketch to make it work on our Arduino:<\/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>As you can see, it is not different from including and using a \u201cnormal\u201d C++ library.<\/p>","protected":false},"excerpt":{"rendered":"<p>In this series I want to show you, how to use the Assembler (ASM) language on your Arduino and AVR.&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\/en\/wp-json\/wp\/v2\/posts\/105"}],"collection":[{"href":"https:\/\/madgyver.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/madgyver.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/madgyver.de\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/madgyver.de\/en\/wp-json\/wp\/v2\/comments?post=105"}],"version-history":[{"count":6,"href":"https:\/\/madgyver.de\/en\/wp-json\/wp\/v2\/posts\/105\/revisions"}],"predecessor-version":[{"id":164,"href":"https:\/\/madgyver.de\/en\/wp-json\/wp\/v2\/posts\/105\/revisions\/164"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/madgyver.de\/en\/wp-json\/wp\/v2\/media\/130"}],"wp:attachment":[{"href":"https:\/\/madgyver.de\/en\/wp-json\/wp\/v2\/media?parent=105"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/madgyver.de\/en\/wp-json\/wp\/v2\/categories?post=105"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/madgyver.de\/en\/wp-json\/wp\/v2\/tags?post=105"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}