开发者

Add two 32-bit integers in Assembler for use in VB6

I would like to come up with the byte code in assembler (assembly?) for Windows machines to add two 32-bit longs and throw away the carry bit. I rea开发者_StackOverflow社区lize the "Windows machines" part is a little vague, but I'm assuming that the bytes for ADD are pretty much the same in all modern Intel instruction sets.

I'm just trying to abuse VB a little and make some things faster. So as an example of running direct assembly in VB, the hex string "8A4C240833C0F6C1E075068B442404D3E0C20800" is the assembly code for SHL that can be "injected" into a VB6 program for a fast SHL operation expecting two Long parameters (we're ignoring here that 32-bit longs in VB6 are signed, just pretend they are unsigned).

Along those same lines, what is the hex string of bytes representing assembler instructions that will do the same thing to return the sum of two 32-bit unsigned integers?

The hex code above for SHL is, according to the author:

mov eax, [esp+4]
mov cl, [esp+8]
shl eax, cl
ret 8

I spit those bytes into a file and tried unassembling them in a windows command prompt using the old debug utility, but I figured out it's not working with the newer instruction set because it didn't like EAX when I tried assembling something but it was happy with AX.

I know from comments in the source code that SHL EAX, CL is D3E0, but I don't have any reference to know what the bytes are for instruction ADD EAX, CL or I'd try it. (Though I know now that the operands have to be the same size.)

I tried flat assembler and am not getting anything I can figure out how to use. I used it to assemble the original SHL code and got a very different result, not the same bytes. Help?


I disassembled the bytes you provided and got the following code:

(__TEXT,__text) section
f:
00000000    movb    0x08(%esp),%cl
00000004    xorl    %eax,%eax
00000006    testb   $0xe0,%cl
00000009    jne     0x00000011
0000000b    movl    0x04(%esp),%eax
0000000f    shll    %cl,%eax
00000011    retl    $0x0008

Which is definitely more complicated than the source code the author provided. It checks that the second operand isn't too large, for example, which isn't in the code you showed at all (see Edit 2, below, for a more complete analysis). Here's a simple stdcall function that adds two arguments together and returns the result:

mov  4(%esp), %eax
add  8(%esp), %eax
ret  $8

Assembling that gives me this output:

(__TEXT,__text) section
00000000 8b 44 24 04 03 44 24 08 c2 08 00 

I hope those bytes do what you want them to!

Edit: Perhaps more usefully, I just did the same in C:

__attribute__((__stdcall__))
int f(int a, int b) 
{
  return a + b;
}

Compiled with -Oz and -fomit-frame-pointer it generates exactly the same code (well, functionally equivalent, anyway):

$ gcc -arch i386 -fomit-frame-pointer -Oz -c -o example.o example.c
$ otool -tv example.o
example.o:
(__TEXT,__text) section
_f:
00000000    movl    0x08(%esp),%eax
00000004    addl    0x04(%esp),%eax
00000008    retl    $0x0008

The machine code output:

$ otool -t example.o
example.o:
(__TEXT,__text) section
00000000 8b 44 24 08 03 44 24 04 c2 08 00 

Sure beats hand-writing assembly code!

Edit 2:

@ErikE asked in the comments below what would happen if a shift of 32 bits or greater was attempted. The disassembled code at the top of this answer (for the bytes provided in the original question) can be represented by the following higher-level code:

unsigned int shift_left(unsigned int a, unsigned char b)
{
    if (b > 32)
        return 0;
    else
        return a << b;
}

From this logic it's pretty easy to see that if you pass a value greater than 32 as the second parameter to the shift function, you'll just get 0 back.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜