问题描述
我有一个用C编写的Arduino MEGA程序,该程序用随机整数填充数组,然后调用用ASM内联汇编编写的冒泡排序算法。然后将排序后的整数转换为二进制,并点亮八个LED,每个LED对应于二进制数的一位。
首先,声明的全局变量。
const byte arraySize = 10;
volatile byte randomNums[arraySize];
volatile byte limit = arraySize-1;
volatile byte counter = 1;
volatile byte iteration = 1;
接下来,是主程序循环(为简单起见,我将省略二进制转换和LED代码)。
void loop() {
for (int i = 0; i < arraySize; i++) {
randomNums[i] = random(255);
}
// asm inline bubble sort here
}
最后,ASM内联汇编中的冒泡排序算法。
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,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
注释掉该程序集后,程序将工作(保存为排序)。当我用程序集编译程序时,出现以下晦涩的错误。
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.
我已经在线搜索并查看了AVR组装手册,但无法弄清楚这些错误的含义以及发生的位置。我要补充一点,我是AVR组装和ASM内联的新手。
解决方法
“行末垃圾”问题是由于行末缺少\n
个字符引起的。其中有三行,但我怀疑第三行还可以,因为它在末尾。
另外两个问题与以下几行有关,我认为这些行不能具有内存操作数:
eor r27,(limit)
cp r28,(arraySize)
这两行之间的行号差异与生成的错误中的差异(1977 - 1971
匹配,一旦考虑到以下两行,由于缺少这些行,它们被视为 one \n
在第一个:
" brne check%= "
" swap%=: mov r23,r24 ; swap content ... \n'
顺便说一句(正如杰斯特在评论中指出的那样),看起来Atmel没有拥有立即操作数ADD
指令(请参阅here) ,因此请注意以下形式的说明:
add 1,r27
允许这样做的可能性有很多:
- 您当前的错误可能会掩盖问题;
- 在将其视为
add r1,r27
的嵌入式汇编程序中可能是一个缺陷; - 它可能是嵌入式汇编程序中的功能,因为它可能正在生成代码以模仿此操作。
关于最后一点,我已经看过以前做过的事情,例如“多寄存器推送”:
push r1,r7,r42
它仅组合为三个单寄存器推送。
可能是汇编程序足够聪明,可以将add 1,r27
转换为inc r27
。由于您要添加的唯一立即值是1
,因此有可能。
这也指出了可能的解决方案,如果事实证明这是一种缺陷,并且错误地将r1
而不是1
添加到r27
。只需将这些add
指令变成inc
指令即可。
仅对代码 logic 进行注释,而不仅仅是对语法进行注释,我不确定您是否正确地将指针和内容概念正确分离了。看来r23/24
是randomNums
数组中单元格的地址,但指令:
lds r23,(randomNums)
会将该数组的第一个值加载到寄存器中。因此,请考虑:
+---+---+---+---+
randomNums @ 0x1000 | 4 | 2 | 3 | 1 |
+---+---+---+---+
您使用的指令将r23
设为值4
,而不是地址1000
。 ldi
指令是您用来加载立即数的指令。
即使修复了该问题,也可以使用以下方法提取值:
mov r23,r25
mov r24,r26
不起作用,因为您正在传输地址,而不是使用这些地址获取值进行比较。
使用寄存器间接加载值通常是通过将该寄存器加载到lpm
指令(例如Z
(R30/31
)可以使用的寄存器中来完成的。
此外,当您将分支 转到swap
时,您从那里返回意思是即使您设置了正确的地址,您最多也可以交换一对元素。
一种解决方法是将调用 swap
作为子例程,而不是分支到该子例程,然后对其进行修改,以使其仅 交换并返回,从而删除注册操作和比较-应该在主代码中重新完成。
另一个(在我看来,更可取)是将其视为if
语句,而只是跳过交换的代码,例如:
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.