内联asm缓冲循环

时间:2009-12-19 07:32:14

标签: c++ arrays assembly loops

好的,这是我的问题。我想循环使用一个简单的char缓冲区 内联asm和VC ++;

我的目标是创建一个循环,尽可能快地从内存中读取信息

继承我的代码

char buffer[howmany];
memset(buffer,33,howmany);
char arr = 0;
__asm
{
MOV     eax,seg buffer ;operand size conflict
MOV     eds,eax
MOV ecx,[howmany]
MOV     ebx,ip
MOV     arr, ES::buffer[ecx] ;operand size conflict
                             ;inline assembler syntax error in 'third operand'
                             ;found 'bad token'
LOOP    ebx ;inline assembler syntax error in 'opcode'; found 'bad token'
                ; 'LOOP' : identifier is reserved word
}

我对装配很新,但这似乎是正确的,但它不起作用?我不知道为什么。提前谢谢。

最终解决方案

__asm
{
    LEA         esi, buffer
    MOV      ecx,howmany
    buf_loop:   
    mov         eax, [esi]
    inc         esi
    dec         ecx
    jnz         buf_loop
}

5 个答案:

答案 0 :(得分:4)

首先,没有EDS,只有DS。即使在32位模式下,段寄存器仍为16位。

其次,除非您正在使用像DOS扩展程序这样的古老系统,或者某些非常不寻常的东西(与Windows,Linux,OS / X,BSD等典型的桌面/服务器操作系统有很多不同),否则你不应该这样做在任何情况下都不要修改任何段寄存器。大多数当前系统使用“平坦”存储器模型,其中OS设置所有 1 段寄存器,其基数为0且存储器顶部的限制,因此您无需任何理由进行修改他们中的任何一个。

不幸的是,虽然很容易说你的代码是错误的,但是说出什么是正确的有点困难 - 你还没有说明你想要做什么。现在,它看起来像是从缓冲区复制,但每次循环都会覆盖你在上一次迭代中写入的值,所以你也可以只复制最后一个单词并完成。要循环缓冲区以实现更多功能,您需要将其复制到相同(或更大)大小的目标缓冲区:

mov ecx, howmany
mov esi, offset FLAT:source
mov edi, offset FLAT:dest
rep movsd

正如其他人已经指出的那样,循环指令的操作数是标签,而不是寄存器。他们似乎没有指出的是,对于现代CPU(比原来的Pentium更新),你通常要避免使用LOOP指令。然而,仅仅为了论证,像上面那样进行移动的循环看起来像:

    mov ecx, howmany
    mov esi, offset FLAT:source
    mov edi, offset FLAT:dest
move_loop:
    lodsd
    stosd
    loop move_loop

对于现代CPU,通常最好使用更多但更简单的指令。

; same setup as above
move_loop:
    mov eax, [esi]
    mov [edi], eax
    inc esi
    inc edi
    dec ecx
    jnz move_loop

事情的另一面是,在这种情况下,它不太可能 - 除非它都适合缓存,这样的块移动几乎总是受到内存带宽的限制 - 移动不会得到更多更快,但为了获得最后一点改进,你想使用SSE指令/寄存器。

编辑:最后一个细节。 VC ++(以及其他)不允许您在_asm块中定义标签,因此如果您需要标签,您可以执行以下操作:

_asm {
    mov ecx, howmany
    mov esi, offset FLAT:source
    mov edi, offset FLAT:dest
}

move_loop:

_asm {
    lodsd
    stosd
    loop move_loop
}

1 好吧,并非所有 - FS和可能的GS不会这样,但CS,DS,ES和SS将是。你不想改变它们中的任何一个(事实上,试图这样做通常会让你的程序关闭)。

答案 1 :(得分:2)

循环指令需要一个目标标签(它使用相对跳转,汇编程序自动计算差值),所以更像是这样:

char buffer[howmany];
memset(buffer,33,howmany);
char arr = 0;
__asm
{
MOV     eax, buffer ; buffer is a pointer, thus eax contains address
MOV     eds, eax
MOV     ecx, howmany ; You probably want the value of howmany, not address
buf_loop:
MOV     arr, [eax] ; segments are for 16bit DOS code, just dereference the array pointer in
INC     TYPE buffer ; Move to next element
LOOP    buf_loop
}

答案 2 :(得分:0)

线LOOP ebx看起来很可疑,你不应该指定要跳转的标签吗?

label:
//some code
LOOP label

此外,如果您想避免循环,请查看

REP MOVS

REP STOS

的指令。

答案 3 :(得分:0)

这是受保护的模式代码吗?如果是这样,请不要混淆EDS

循环指令必须给出跳转目标标签。无法使用ebx(或ip)加载eip,因为ip不是正确的操作数。

答案 4 :(得分:0)

我没有时间把它写出来,但是使用SSE2寄存器你可以读取每个循环16个字节。您必须首先使用基于字节的循环来对齐(和之后)