ASM 内联组装冒泡排序;需要常量值,行尾有垃圾
ASM inline assembly bubble sort; constant value required, garbage at end of line
我有一个用 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, 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
当程序集被注释掉时,程序运行(保存排序)。当我用程序集编译程序时,出现以下难懂的错误。
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
)相匹配,一旦考虑到以下两行就被视为一个 由于第一个缺少 \n
:
" brne check%= "
" swap%=: mov r23, r24 ; swap content ... \n'
顺便说一句(正如 Jester 在评论中指出的那样),Atmel 似乎没有 immediate-operand ADD
指令(参见 here),所以我会谨慎对待以下形式的说明:
add 1, r27
允许这样做的原因有多种:
- 问题可能只是被您当前的错误所掩盖;
- 可能是将其视为
add r1, r27
; 的内联汇编程序的缺陷
- 它可能是内联汇编器中的一个特性,因为它可以生成代码来模拟这个操作。
关于最后一点,我以前见过这种事情,例如“multi-register 推送”:
push r1, r7, r42
这只是组装成三个 single-register 推。
可能是汇编程序足够聪明,可以将add 1, r27
变成inc r27
。由于您要添加的唯一直接值是 1
,这是有可能的。
这也指出了一个可能的解决方案,如果它被证明是一个缺陷并且错误地将 r1
而不是 1
添加到 r27
。只需将那些 add
指令变成 inc
指令。
并且只是评论您的代码 逻辑 而不仅仅是语法,我不确定您是否正确分离了指针和内容概念。看起来 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
时,你从那里return意味着,即使你如果设置了正确的地址,您最多可以交换一对元素。
一个解决方法是 调用 swap
作为子例程,而不是分支到它,然后修改它,使其 仅 交换和 returns,删除寄存器操作和比较 - 它们应该在主代码中完成。
另一种(我认为更可取)是将其视为 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.
我有一个用 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, 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
当程序集被注释掉时,程序运行(保存排序)。当我用程序集编译程序时,出现以下难懂的错误。
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
)相匹配,一旦考虑到以下两行就被视为一个 由于第一个缺少 \n
:
" brne check%= "
" swap%=: mov r23, r24 ; swap content ... \n'
顺便说一句(正如 Jester 在评论中指出的那样),Atmel 似乎没有 immediate-operand ADD
指令(参见 here),所以我会谨慎对待以下形式的说明:
add 1, r27
允许这样做的原因有多种:
- 问题可能只是被您当前的错误所掩盖;
- 可能是将其视为
add r1, r27
; 的内联汇编程序的缺陷
- 它可能是内联汇编器中的一个特性,因为它可以生成代码来模拟这个操作。
关于最后一点,我以前见过这种事情,例如“multi-register 推送”:
push r1, r7, r42
这只是组装成三个 single-register 推。
可能是汇编程序足够聪明,可以将add 1, r27
变成inc r27
。由于您要添加的唯一直接值是 1
,这是有可能的。
这也指出了一个可能的解决方案,如果它被证明是一个缺陷并且错误地将 r1
而不是 1
添加到 r27
。只需将那些 add
指令变成 inc
指令。
并且只是评论您的代码 逻辑 而不仅仅是语法,我不确定您是否正确分离了指针和内容概念。看起来 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
时,你从那里return意味着,即使你如果设置了正确的地址,您最多可以交换一对元素。
一个解决方法是 调用 swap
作为子例程,而不是分支到它,然后修改它,使其 仅 交换和 returns,删除寄存器操作和比较 - 它们应该在主代码中完成。
另一种(我认为更可取)是将其视为 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.