There are also a couple of ways of including assembler code right inside your Arduino IDE.
By including the assembler in the Arduino IDE, the assembler source is assembled and linked into your project and uploaded to your Arduino from within the IDE - just like any other program.
This makes it easy to get started with Assembler while still retaining the familiarity of the Arduino IDE. Why? Because since you can mix C / C++ code with the Assembler code this means that you do not have to write a complete assembler program that manages all of the hardware initialisation nitty gritty - you can start small and expand your assembler as and when you you feel comfortable or need to do so. Like the example below, you can start with just two lines of assembler that fit into a larger program if you wanted to.
Following are the instructions for creating a mixed C / Assembler program in Arduino. I also give an example of how to pass parameters and get a result from an Assembler function.
To create a mixed C + Assembler program create a new project in the Arduino IDE (call it whatever you like) and enter the following code into the IDE:
extern "C" {
void myInit();
void myLoop();
int myAdd(int, int);
}
void setup() {
Serial.begin(9600);
myInit();
// This is just a dummy example showing inline assembler using the asm "directive"
// Refer to this tutorial, or similar, for details of inline assembler in Arduino:
// https://ucexperiment.wordpress.com/2016/03/11/arduino-inline-assembly-tutorial-5-2/
asm(
"ldi r26,42"
);
}
void loop() {
myLoop();
int ans = myAdd(240, 25);
delay(ans);
Serial.print("Answer: ");
Serial.println(ans);
delay(1000);
}
Next click the little down arrow in the top right corner of the IDE as per the following diagram and select "New Tab":
Enter myFunctions.S
as the name of the new tab as shown below. Note that the name does not really matter, but, it must end with the extension .S and the "S" must be a capital "S" not a lowercase "s":
Paste the following code into the myFunctions.S
tab.
; Example assembler file that blinks an LED.
; and adds two numbers together.
#define __SFR_OFFSET 0
#include "avr/io.h"
// Declare the three function entry points as "globals"
.global myInit
.global myLoop
.global myAdd
myInit:
sbi DDRB,5 ; Set PB5 (the built in LED on Arduino Uno) as output
ret
myLoop:
ldi r20,250 ; Set the delay duration in ms. Maximum value is 255.
call myDelay_ms
sbi PORTB,5 ; Set PB5 HIGH
ldi r20,250 ; Delay for another 250 ms.
call myDelay_ms
cbi PORTB,5 ; Set PB5 LOW
ret
; These symbols are for internal to this assembler file's use only.
; They are private because they are not listed as a global symbol
; Also, they do not conform to the subroutine calling conventions used
; by the Arduino IDE's compiler. i.e. they are not compatible with
; being called directly from C. However, since we are calling them from
; our own assembler, we can (almost) make up any calling convention we like.
myDelay_ms:
; Delay about r20*1ms. Destroys r20, r30, and r31.
; One millisecond is about 16000 cycles at 16MHz.
; The basic loop takes about 5 cycles, so we need about 16,000,000 / 5 = 3000 loops.
; NB: most instructions are 8 bit, so we load the 16 bit counter using
; two 8 bit loads.
ldi r31, 3000>>8 ; high(3000)
ldi r30, 3000&255 ; low(3000)
delaylp:
sbiw r30, 1 ; Decrement our 1 ms counter (this instruction operates on a 16 bit value in R31:R30).
brne delaylp ; If R30 is non zero, loop back
subi r20, 1 ; Otherwise we have passed 1 ms so, decrement the high order byte of our counter.
brne myDelay_ms ; If this is non zero, loop back.
ret ; Otherwise, we have counted down from R20 * 3,000 - so return.
; Function to add two integers and return an integer.
; For few parameter functions, the Arduino IDE uses registers built into the
; Microcontroller (i.e. the CPU) to exchange data between subroutine caller and callee.
; Return values are as follows:
; - single Byte (e.g. char, byte) R24 only
; - double Byte (e.g. int) R25:R24 (R25 is the high order byte)
; - quad Byte (e.g. long) R25:R24:R23:R22 (R25 is the high order byte)
; Parameters are passed in similar ways starting with R25 and working down, but exactly
; what is where will depend upon the parameter types.
; Refer to this tutorial for more details or the AVR compiler documentation from ATMEL
; https://ucexperiment.wordpress.com/2016/04/02/arduino-inline-assembly-tutorial-12-functions/
; In our case, a is passed in R25:R24 and b is passed in R23:R22
myAdd:
add r24, r22 ; Add the low order bytes
adc r25, r23 ; Add the high order bytes PLUS any value carried from the previous addition.
ret ; The result is in R25:R24 which is where it needs to be to pass back to the C code.
Upload it to your Uno, start the serial monitor and you should see some sum's being ouput (well one sum continuously being output). Feel free to change the values in the main loop to see different results.
I've put loads of comments and a few references into the code to get you started on the path to finding more information.
One other reference that will be useful if you are going to mess with assembler on the UNO is the ATMega-328P reference manual/datasheet (all 662 pages of it) which you can obtain from Microchip's ATmega328p reference page. There are a whole bunch more useful resources there that will likely come in handy from time to time.