在x86 NASM或YASM Assembly中实现选择排序的困境

时间:2012-04-29 07:35:14

标签: assembly x86 x86-64 nasm yasm

我正在尝试在64位Linux上运行的NASM中实现一个选择类型的数组。

数组声明为:

section .bss
strbuf resb 10
small  resb  1  ; current minimum value

排序算法本身很简单,但我觉得受到两件事的限制:可用的寄存器数量,以及无法交换立即数(即括号)

我需要跟踪未排序的数组边界,索引,当前最小值的位置及其索引。那已经是四个寄存器了。我还需要跟踪两个循环计数器,一个用于表示未排序数组边界的外循环,另一个用于每次传递(即内循环)的迭代。那是另外两个,总共六个寄存器。

由于immediates不能相互移动,例如mov [var1], [var2]我需要在需要交换两个元素时使用寄存器作为临时占位符。在跟踪哪些寄存器保存哪些信息方面,这很快变得笨拙!

以下是我到目前为止的尝试。请注意,这是非工作代码,会触发分段错误。但也许你可以看到我想要做的事情,并指出我的方法出错了。

我不希望使用简化构造的宏,例如那些提供.IF和.ELSE的宏。

; ====== Sorting begins here ======
sorting:
    mov edi, strbuf  ; outer loop pointer
    mov esi, strbuf  ; inner loop pointer
    mov eax, 0  ; inner loop counter
    mov ebx, 0  ; outer loop counter

innerloop:
    ; store the value of first element in [small]
    mov edx, [esi]
    mov [small], edx

    ; compare the current small value with the value pointed by esi
    mov edx, [esi]  
    cmp [small], edx

    jg  new_small
    inc esi
    inc eax
    cmp eax, 9
    jle innerloop
    cmp eax, 9
    jg  innerloop_done

new_small:
    mov [small], edx  ; save the new small value
    mov ecx, esi      ; save its index in ecx
    inc esi
    inc eax
    cmp eax, 9

    jle     innerloop

innerloop_done:
    ; When the inner loop is completed...
    ; First, do the swap
    push    rax
    mov eax, [edi]
    mov edx, [small]
    mov [ecx], edx
    pop rax

    inc edi  ; move the outer loop pointer forward
    inc esi  ; move the inner loop pointer forward
    inc ebx  ; increment the outer loop counter (the unsorted array becomes smaller)
    inc eax  ; increment the inner loop counter (same reason as above)
    cmp ebx, 9

    jle innerloop   

; ====== Sorting ends here ======

2 个答案:

答案 0 :(得分:2)

如果这应该在64位模式下执行,你必须使用64位地址,这意味着当你取这些地址并将它们放入寄存器时,那些接收寄存器也必须是64位,否则你将截断地址并访问内存并不是你想要的地方。

另外,您是否有调试器来逐步执行代码?

答案 1 :(得分:1)

对于64位代码,有16个通用寄存器:RAX,RBX,RCX,RDX,RSI,RDI,RSP,RBP,R8,R9,R10,R11,R12,R13,R14,R15。

其中,RSP有特殊用途,只能用于此目的(当前堆栈地址)。 RBP寄存器通常由编译器用于跟踪堆栈帧(不包括“-fomit-frame-pointer”可能性),但您不是编译器,可以将它用于您喜欢的任何内容。

这意味着您可以使用的15个寄存器中只有6个。

如果你确实用完了寄存器,那么你可以把一些东西转移到堆栈空间。例如:

foo:
    sub rsp,8*5        ;Create space for 5 values
%define .first   rsp
%define .second  rsp+8
%define .third   rsp+8*2
%define .fourth  rsp+8*3
%define .fifth   rsp+8*4
%define .first   rsp+8*5

    mov [.first],rax
    mov [.second],rax
    mov rax,[.first]
    add rax,[.second] 
    mov [.third],rax
...
    add rsp,8*5        ;Clean up stack
    ret

希望您可以看到堆栈上可能有数百个值,并且如果需要,可以使用一些寄存器(临时)保存这些值。通常,您可以计算出最常使用的值(例如,在内部循环中)并尝试使用寄存器,并将堆栈用于最不常用的变量。但是,对于64位代码(你可以使用8个以上的寄存器),很少用完寄存器,如果你这样做,可能表明你需要将例程拆分成多个例程。