1

我在 Visual Studio 2005 下使用程序集(我是新手),我想创建一个程序,使用该规则计算算术级数:A n = 2*A n-1 + An n-2 但是。我真的不知道如何使用寄存器,我只需要你的一个例子来继续我的练习。

这是我的代码:

.386
.MODEL flat,stdcall

.STACK 4096

extern ExitProcess@4:Near

.data                               
arraysize DWORD 10

setarray  DWORD 0 DUP(arraysize)
firstvar  DWORD 1
secondvar DWORD 2

.code                               
_main:                              
mov eax,[firstvar]
mov [setarray+0],eax        
mov eax,[secondvar]
mov [setarray+4],eax

mov ecx, arraysize              ;loop definition
mov ax, 8

Lp:
mov eax,[setarray+ax-4]
add eax,[setarray+ax-4]
add eax,[setarray+ax-8]
mov [setarray+ax],eax

add ax,4;
loop Lp

add ax,4;

    push    0                   ;Black box. Always terminate
    call    ExitProcess@4       ;program with this sequence

    end   _main              ;End of program. Label is the entry point.
4

3 回答 3

1

您不能同时使用 ax 作为索引寄存器和 eax 作为数据寄存器。对于 32 位代码,坚持使用 32 位寄存器,除非您现在正在做什么。您无意中使用了您可能不想要的 16 位寻址模式。

mov ecx, arraysize-1              ;loop definition
mov ebx, 8

Lp:
mov eax,[setarray+ebx-4]
add eax,[setarray+ebx-4]
add eax,[setarray+ebx-8]
mov [setarray+ebx],eax

add ebx,4
dec ecx
jnc Lp

永远不要使用循环指令,即使一些现代处理器可以快速执行(大多数不能)。

于 2010-11-22T15:31:54.470 回答
0

我也是汇编程序的初学者,但我的算法有点不同:


    A   dword   1026 dup (0)          ; declare this in the data segm.

; ...

    mov     esi, offset A         ; point at the results array
    mov     [esi], 1               ; initialize A(0)
    mov     [esi + 4], 2           ;  and A(1)
    xor  ecx, ecx

lp: add esi, 8
mov eax, [esi - 4] ; get A(n-1) add eax, eax ; double it add eax, [esi - 8] ; computes A(n) mov [esi], eax ; and save it inc ecx ; next n cmp ecx, n ; be sure n is a dword, use ; cmp ecx, dword ptr n ; if it isn't jb lp ; loop until ecx < n ; ; now you should have the results in the A array with ; esi pointing to the end of it

我没有编译它以查看是否运行良好,但它应该..

于 2010-11-28T14:51:22.280 回答
0

A n = 2*A n-1 + A n-2与斐波那契数列的公式几乎相同,因此您可以通过搜索找到很多有用的东西。(例如这个问答)。但是,我们需要 2a + b 而不仅仅是一个加法,而 x86 可以在一条 LEA 指令中做到这一点。

您永远不需要将循环变量存储在内存中,这就是寄存器的用途。因此,与其每次迭代都需要将数据从内存中拉回(往返约 5 个周期延迟),它可以只使用寄存器(0 个周期额外延迟)。

您的数组可以放入 .bss,而不是 .data,因此您不会将这些零存储在目标文件中。

arraysize equ 10     ; not DWORD: this is an assemble-time constant, not a value stored in memory

.bss
seq  DWORD 0 DUP(arraysize)   ; I think this is the right MASM syntax?
; NASM equivalent:  seq RESD arraysize

.code
_main:

    mov  edx, 1         ; A[0]
    mov  [seq], edx
    mov  eax, 2         ; A[1]
    mov  [seq+4], eax

    mov  ecx, 8         ; first 8 bytes stored
    ; assume the arraysize is > 2 and even, so no checks here
seqloop:
    lea  edx, [eax*2 + edx]  ;    edx=A[n],   eax=A[n-1]
    mov  [seq + ecx], edx    ; or edx=A[n-1], eax=A[n-2]
    lea  eax, [edx*2 + eax]  
    mov  [seq + ecx + 4], eax
    ; unrolled by two, so we don't need any MOV or XCHG instructions between registers, or any reloading from memory.

    add  ecx, 8             ; +8 bytes
    cmp  ecx, arraysize*4   ; (the *4 happens at assemble time)
    jb   seqloop

    ret

将数组索引用于循环条件意味着我们总共只使用了 3 个寄存器,并且仍然不需要保存/恢复任何通常的调用保留寄存器 ESI、EDI、EBX 或 EBP。(当然调用者的 ESP 也会恢复)。

如果您关心性能,那么在 Intel SnB 系列 CPU 上,循环只有 6 uops(融合域)。对于更大的数组大小,它可以在每个时钟运行一个结果(每 2 个时钟一次迭代)。

于 2016-10-27T04:30:26.363 回答