2

64ビットLinuxで実行されるNASMで選択ソートの配列を実装しようとしています。

配列は次のように宣言されます。

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

ソートアルゴリズム自体は非常に単純ですが、使用可能なレジスタの数と、イミディエート(つまりブラケット)を交換できないという2つの点で制限されていると感じています。

並べ替えられていない配列の境界、そのインデックス、現在の最小値の場所、およびそのインデックスを追跡する必要があります。これはすでに4つのレジスタです。また、2つのループカウンターを追跡する必要があります。1つはソートされていない配列境界を表す外側のループ用で、もう1つは各パスの反復用(つまり内側のループ)です。これはさらに2つ、合計6つのレジスタです。

mov [var1], [var2]2つの要素を交換する必要があるときはいつでも、一時的なプレースホルダーとしてレジスターを使用する必要があるなど、イミディエートを相互に移動することはできません。これは、どのレジスタがどの情報を保持しているかを追跡するという点で、すぐに扱いにくくなります。

以下はこれまでの私の試みです。これは、セグメンテーション違反を引き起こす機能しないコードであることに注意してください。しかし、おそらくあなたは私がやろうとしていることを理解し、私のアプローチがどこで間違っているのかを指摘することができます。

.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 ======
4

2 に答える 2

2

これが 64 ビット モードで実行されることになっている場合は、64 ビット アドレスを使用する必要があります。つまり、それらのアドレスを取得してレジスタに配置する場合、それらの受信者レジスタも 64 ビットである必要があります。そうしないと、切り捨てられます。アドレスとアクセスメモリが意図した場所にありません。

また、コードをステップ実行するデバッガーはありませんか?

于 2012-04-29T07:38:50.823 に答える
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 つある場合) の場合、レジスタが不足することはほとんどありません。その場合は、ルーチンを複数のルーチンに分割する必要があることを示している可能性があります。

于 2012-04-30T03:31:36.660 に答える