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

lds r23, (randomNums)

将该数组的第一个 加载到寄存器中。所以,考虑:

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

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


即使在修复之后,使用以下方法提取值:

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.