ASM内联汇编气泡排序;需要恒定值,行尾有垃圾

问题描述

我有一个用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/24randomNums数组中单元格的地址,但指令:

lds r23,(randomNums)

会将该数组的第一个加载到寄存器中。因此,请考虑:

                    +---+---+---+---+
randomNums @ 0x1000 | 4 | 2 | 3 | 1 |
                    +---+---+---+---+

您使用的指令将r23设为值4,而不是地址1000ldi指令是您用来加载立即数的指令。


即使修复了该问题,也可以使用以下方法提取值:

mov r23,r25
mov r24,r26

不起作用,因为您正在传输地址,而不是使用这些地址获取进行比较。

使用寄存器间接加载值通常是通过将该寄存器加载到lpm指令(例如ZR30/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.