如何将寄存器放入 MASM 中的数组索引中?

Posted

技术标签:

【中文标题】如何将寄存器放入 MASM 中的数组索引中?【英文标题】:How do I put a register into an array index in MASM? 【发布时间】:2015-11-19 07:29:06 【问题描述】:

我在 MASM 中处理数组时遇到了困难。我不明白如何将寄存器的值放入数组的索引中。我似乎找不到 arr[i] 在哪里。我错过了什么或我有什么问题? 感谢您的宝贵时间!

C++ 代码:

    #include <iostream>
    using namespace std;

    extern"C"
    
        char intToBinary(char *, int, int);
    

    int main()
    
        const int SIZE = 16;
        char arr[SIZE] =  '/0' ;
        cout << "What integer do you want converted?" << endl;
        cin >> decimal;

        char value = intToBinary(arr, SIZE, decimal);

        return 0;
    

汇编代码:

.686
.model flat

.code

_intToBinary PROC ; named _test because C automatically prepends an underscode, it is needed to interoperate
    push ebp
    mov ebp,esp ; stack pointer to ebp

    mov ebx,[ebp+8] ; address of first array element
    mov ecx,[ebp+12] ; number of elements in array
    mov edx, 0      ;has to be 0 to check remainder
    mov esi, 2      ;the new divisor
    mov edi, 12

    LoopMe:
        add ebx, 4
        xor edx, edx            ;keep this 0 at all divisions
        div esi                 ;divide eax by 2
        inc ebx                 ;increment by 1
        mov [ebp + edi], edx    ;put edx into the next array index

        add edi, 4              ;add 4 bytes to find next index
        cmp ecx, ebx            ;compare iterator to number of elements (16)
    jg LoopMe

    pop ebp                 ;return 
    ret
_intToBinary ENDP

END 

【问题讨论】:

这是在 Windows 上吗?我想是的,但你能确认一下吗? 【参考方案1】:

在您的 C++ 代码中

decimal 未定义。 '/0' 是无效的字符文字。使用 \,而不是 /,在 C++ 中编写转义序列。 value 未使用。

你的代码应该是这样的:

#include <iostream>
using namespace std;

extern"C"

    char intToBinary(char *, int, int);


int main()

    const int SIZE = 16;
    char arr[SIZE] =  '\0' ;
    int decimal;
    cout << "What integer do you want converted?" << endl;
    cin >> decimal;

    intToBinary(arr, SIZE, decimal);
    for (int i = SIZE - 1; i >= 0; i--) cout << arr[i];
    cout << endl;

    return 0;

在你的汇编代码中

您将“第一个数组元素的地址”通过mov ebx,[ebp+8] 存储到ebx,因此arr 的地址将在那里。 不幸的是,它被add ebx, 4inc ebx 破坏了。 “将 edx 放入下一个数组索引”不,[ebp + edi] 不是下一个数组索引,它正在破坏堆栈上的数据。这很糟糕。 如果 char 的大小为 1 字节,请不要在“查找下一个索引”中添加 4 字节。

你的代码应该是这样的(对不起,这是nasm代码,因为我不熟悉masm):

bits 32

global _intToBinary
_intToBinary:
    push ebp
    mov ebp, esp ; stack pointer to ebp
    push esi ; save this register before breaking in the code
    push edi ; save this, too
    push ebx ; save this, too
    mov ebx, [ebp + 8] ; address of first array element
    mov ecx, [ebp + 12] ; number of elements in array
    mov eax, [ebp + 16] ; the number to convert
    xor edi, edi ; the index of array to store
    mov esi, 2 ; the new divisor

    LoopMe:
        xor edx, edx ; keep this 0 at all divisions
        div esi ; divide eax by 2
        add dl, 48 ; convert the number in dl to a character representing it
        mov [ebx + edi], dl ; put dl into the next array index
        inc edi ; add 1 byte to find next index
        cmp ecx, edi ; compare iterator to number of elements
    jg LoopMe

    xor eax, eax ; return 0
    pop ebx ; restore the saved register
    pop edi ; restore this, too
    pop esi ; restore this, too
    mov esp, ebp ; restore stack pointer
    pop ebp
    ret

请注意,此代码将以相反的顺序存储二进制文本,因此我编写了 C++ 代码以从后到前打印它们。 另请注意arr 中没有终止空字符,因此请不要使用cout &lt;&lt; arr;

【讨论】:

您可以通过调用 pop 和您想要的寄存器将似乎是寄存器的地址推送到堆栈中以便稍后到达? [ebp + 16] 如何等于输入的小数?这不是数组中的第一个元素吗?究竟是如何找到数组的?只是“[ebx]”? 是的,您可以推送和弹出内存地址,因为它们只是 IA-32 上的 32 位值。执行函数序言后,[ebp+0] 上将有 ebp 的旧值,[ebp+4] 上的返回地址,[ebp+8] 上的第一个参数,[ebp+12] 上的第二个参数,[ebp+16] 上的第三个参数如果每个参数为 4 个字节。出于这个原因,数组的(第一个元素的地址)将是[ebp+8],因为它作为第一个参数传递,decimal 将是[ebp+16],因为它作为第三个参数传递。最后,如你所写,第一个数组元素的地址将是[ebp+8],所以访问那里找到数组。【参考方案2】:

你有ebx 中第一个数组元素的地址,edi 是你的循环计数器。所以mov [ebx + edi], edx 会将edx 存储到arr[edi] 中。

另请注意,您的循环条件是错误的(您的 cmp 正在将元素数与数组的起始地址进行比较。)

尽可能避免使用div。除以二,右移一。 div 非常慢(比轮班慢 10 到 30 倍)。


顺便说一句,由于您可以选择使用哪些寄存器(在 ABI 表示您可以在不保存/恢复的情况下破坏的寄存器中),edi 按照惯例用于“目标”指针(即当它不需要任何额外的指令时),而esi 用作“源”指针。

说到ABI,你需要在使用它的函数中保存/恢复ebx,和ebp一样。它在函数调用中保持其值(因为您调用的任何符合 ABI 的函数都会保留它)。我忘记了 32 位 ABI 中还保存了哪些其他寄存器。您可以查看https://***.com/tags/x86/info 中的有用链接。 32位已过时; 64 位具有更高效的 ABI,并将 SSE2 作为基线的一部分。

【讨论】:

32 位 edx 不适合 8 位 arr[edi] "除以二,右移一。"是的,您可以使用and eax, 1 来计算eax / 2 的余数。 @MikeCAT:是的,我只是注意到他代码中的错误,他将数组定义为char。我被将指针增加 4 的 asm 愚弄了,并且将在我的答案中补充说使用 int8_t 数组会更有效!然后意识到他可能想要一个字符串,而不是整数零和一的数组...... 实际上,您可能想要setc dl 或其他东西,根据进位标志将一个字节设置为零或一。如果你屏蔽你正在移动的寄存器,你会破坏仍然存在的值(所以你需要一个mov / and 组合)。 shift 将 CF 设置为移出的最后一位,因此您都可以使用 setc dlsbb edx, edx(将 edx 设置为 0 或 -1)。

以上是关于如何将寄存器放入 MASM 中的数组索引中?的主要内容,如果未能解决你的问题,请参考以下文章

将寄存器用作 DB (MASM) 的参数

Debug和汇编编译器masm对指令的不同处理

在 MASM 中访问数组

如何将参数传递给 MASM64 中的程序?

访问段寄存器 MASM

带 masm 的寄存器 edx::eax 中的 mul 和内存分配