I have an Arduino MEGA program written in C that populates an array with random integers, then calls a bubble sort algorithm written in ASM inline assembly. The sorted integers are then converted into binary and eight LEDs are lit with each LED corresponding to one bit of the binary number.
Firstly, the declared global variables.
const byte arraySize = 10;
volatile byte randomNums[arraySize];
volatile byte limit = arraySize-1;
volatile byte counter = 1;
volatile byte iteration = 1;
Next, the main program loop (for simplicity, I will omit the binary conversion and LED code).
void loop() {
for (int i = 0; i < arraySize; i++) {
randomNums[i] = random(255);
}
// asm inline bubble sort here
}
Lastly, the bubble sort algorithm in ASM inline assembly.
asm volatile(
" lds r20, (limit) ; position before end of array \n"
" lds r21, (counter) ; counter for loop is defined i=0 \n"
" lds r22, (iteration) ; counter set for iteration of sort algorithm k=1 \n"
" mov r21, r27 ; i=1 \n"
" mov r22, r28 ; iteration number in r28=k \n"
" lds r23, (randomNums) ; point to beginning of array by r23 'element' \n"
" mov r23, r24 "
" add 1, r24 ; point to 'neighbour' \n"
" check%=: mov r23, r25 ; get 'element' and place in r25 \n"
" mov r24, r26 ; get 'neighbour' in array in r26 \n"
" cp r25, r26 ; compare both values \n"
" brge swap%= ; swap the numbers \n"
" add 1, r23 ; increment pointer r23 \n"
" add 1, r24 ; increment pointer r24 \n"
" add 1, r27 ; increment loop counter \n"
" eor r27, (limit) ; xor check if not exceeding array size \n"
" brne check%= "
" swap%=: mov r23, r24 ; swap content where index r23 is pointing to where index r24 is pointing \n"
" mov r26, r23 ; move greater number to position after smaller number \n"
" add 1, r23 ; increment pointer r23 \n"
" add 1, r24 ; increment pointer r24 \n"
" add 1, r28 ; increment loop counter \n"
" cp r28, (arraySize) ; check not exceeding array capacity \n"
" ret "
::: "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28"); // clobbered registers
The program works (save for the sorting) when the assembly is commented out. When I compile the program with the assembly, I get the following obscure errors.
C:\Users\USERNAME\AppData\Local\Temp\cc2d2uAp.s: Assembler messages:
C:\Users\USERNAME\AppData\Local\Temp\cc2d2uAp.s:1963: Error: garbage at end of line
C:\Users\USERNAME\AppData\Local\Temp\cc2d2uAp.s:1971: Error: constant value required
C:\Users\USERNAME\AppData\Local\Temp\cc2d2uAp.s:1972: Error: garbage at end of line
C:\Users\USERNAME\AppData\Local\Temp\cc2d2uAp.s:1977: Error: constant value required
lto-wrapper.exe: fatal error: E:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-gcc returned 1 exit status
compilation terminated.
e:/program files (x86)/arduino/hardware/tools/avr/bin/../lib/gcc/avr/7.3.0/../../../../avr/bin/ld.exe: error: lto-wrapper failed
collect2.exe: error: ld returned 1 exit status
exit status 1
Error compiling for board Arduino Mega ADK.
I've searched online and taken a look at the AVR assembly manual and I can't figure out what these errors mean and where they are occurring. I'll add that I'm new to AVR assembly and ASM inline.
The "garbage at end of line" problems are caused by missing \n
characters at the end of the lines. There are three of these lines but I suspect the third is okay because it's at the end.
The other two problems are with the following lines, I don't believe these are allowed to have memory operands:
eor r27, (limit)
cp r28, (arraySize)
The line number differences between those two lines match the difference (1977 - 1971
) in the generated errors, once you take into account the following two lines are considered as one due to the missing \n
on the first:
" brne check%= "
" swap%=: mov r23, r24 ; swap content ... \n'
As an aside (as pointed out by Jester in the comments), it looks like Atmel don't have an immediate-operand ADD
instruction (see here), so I'd be circumspect about instructions of the form:
add 1, r27
There are a number of possibilities why this might be allowed:
add r1, r27
;On that last point, I've seen this sort of thing done before, such as a "multi-register push":
push r1, r7, r42
which just assembles to three single-register pushes.
It may be that the assembler is smart enough to turn add 1, r27
into inc r27
. Since the only immediate value you're adding is 1
, this is a possibility.
This also points to a possible solution should it turn out to be a deficiency and it's erroneously adding r1
rather than 1
, to r27
. Just turn those add
instructions into inc
instructions.
And just commenting on your code logic rather than just the syntax, I'm not sure you've correctly separated the pointer and content concepts correctly. It looks like r23/24
are meant to be the addresses of cells in the randomNums
array but the instruction:
lds r23, (randomNums)
will load the first value of that array into the register. So, consider:
+---+---+---+---+
randomNums @ 0x1000 | 4 | 2 | 3 | 1 |
+---+---+---+---+
The instruction you use would have r23
being the value 4
rather than he address 1000
. The ldi
instruction is what you would use to load up an immediate value.
Even after fixing that, extracting the values using:
mov r23, r25
mov r24, r26
won't work because you're transferring the addresses, not using those addresses to get the values for comparison.
Loading values indirectly using a register is usually done by loading that register into one that can be used by the lpm
instruction, such as Z
(R30/31
).
Additionally, while you branch to swap
, you return from there meaning, even if you had the right addresses set up, you would swap at most one pair of elements.
One fix is to call swap
as a subroutine, rather than branching to it, and modify it so that it only swaps and returns, removing the register manipulations and comparison - they should be done back in the main code.
The other (preferable in my opinion) is to treat it like an if
statement and just skip over the code that swaps, something like:
cp r25, r26 ; compare both values.
brle noswap%= ; skip swap if already ordered.
@swap (r25), (r26) ; actual code to swap goes here.
noswap%=:
add 1, r23 ; carry on with loop.