-1

Im new with an assembler.Couldn't find the solution in the ethernet so asking here. This code outputs wrong numbers and i can't get why.

#include <iostream>
long DotProduct(short int* vec1, short int* vec2, int l)
{
long result;
_asm
{
    mov esi, [vec1]; 
    mov edi, [vec2]; 
    mov ecx, [l]; 
    xor eax, eax; 
loop_start:
    movzx edx, word ptr[esi]; 
    imul edx, word ptr[edi]; 
    add eax, edx; 
    add esi, 2; 
    add edi, 2; 
    sub ecx, 1; 
    jnz loop_start; 

}
return result;
}
int main() {
short int vec1[] = { 1, 2, 3 };
short int vec2[] = { 5, 6, 7 };
int l = 3;
long result = DotProduct(vec1, vec2, l);
std::cout << "Dot product: " << result << std::endl;

}
4
  • What values do you expect, and what do you see? Commented May 15, 2024 at 10:32
  • @500-InternalServerError If I remember correctly whatever is in ax when the function returns, is stored in the result field. Commented May 15, 2024 at 10:33
  • I expect to see result of (1*5+2*6+3*7)=38, but i see -858993460 Commented May 15, 2024 at 10:39
  • imul edx, word ptr[edi] There is no instruction that encodes a multiplication of edx by a 16-bit word value. Don't know what your assembler makes of this but it is certainly wrong. Commented May 15, 2024 at 15:07

3 Answers 3

1

You need to change the values from short int to int, and adjust the increments to esi and edi in the assembler to 4. You also need to move the final number from eax into the result variable. So the code should be:

#include <iostream>
long DotProduct(int* vec1, int* vec2, int l)
{
    long result;
    _asm
    {
        mov esi, [vec1]; 
        mov edi, [vec2]; 
        mov ecx, [l]; 
        xor eax, eax; 
    loop_start:
        movzx edx, word ptr[esi]; 
        imul edx, word ptr[edi]; 
        add eax, edx; 
        add esi, 4; 
        add edi, 4; 
        sub ecx, 1; 
        jnz loop_start; 
        mov[result], eax

    }
    return result;
}
int main() {
    int vec1[] = { 1, 2, 3 };
    int vec2[] = { 5, 6, 7 };
    int l = 3;
    long result = DotProduct(vec1, vec2, l);
    std::cout << "Dot product: " << result << std::endl;

}

Since the value in eax is the default returned from a function, you can remove the last line of the assembler code (mov[result], eax), and also the return result statement.

Sign up to request clarification or add additional context in comments.

2 Comments

short should be ok. Because the asm code movzx edx, word ptr[esi] will extend a word into a dword. The root cause might be the return a uninitial value.
Since it's int here, you might need to change this: movzx edx, Dword ptr[esi]; and imul edx, Dword ptr[edi]; .
1

Update:

I tried this code just to cover all possible results.

Each multiplication of elements should be in range [-32768, 32767].

long DotProduct(short int* vec1, short int* vec2, int l)
{
    long result;
    _asm
    {
        mov esi, [vec1]
        mov edi, [vec2]
        mov ecx, [l]
        xor eax, eax

    loop_start:

        mov dx, word ptr[esi]
        imul dx, word ptr[edi]
        movsx edx, dx
        add eax, edx

        add esi, 2
        add edi, 2
        sub ecx, 1
        jnz loop_start

        mov [result], eax
    }
    return result;

}

This instruction is invalid: imul edx, word ptr[edi]

imul r32, r/m16, no such thing in intel's manual.

For two-operand form only:

imul r16, r/m16
imul r32, r/m32

If changed to imul edx, dword ptr[edi] instruction is ok but now instead of 2 bytes it takes 4 bytes, so result will be incorrect.

    mov dword ptr -14[ebp],eax      ; 0018FE28 - vec1 address
    mov dword ptr -10[ebp],edx      ; 0018FE20 - vec2 address
    mov dword ptr -0C[ebp],ebx      ; l = 3
    
    _asm

    mov esi, dword ptr -14[ebp]     ; esi = 0018FE28
    mov edi, dword ptr -10[ebp]     ; edi = 0018FE20
    mov ecx, dword ptr -0C[ebp]     ; ecx = 3
    xor eax,eax                     ; eax = 0
    
00401043_loop:                      ; 3              | 2        | 1 
    movzx edx, word ptr [esi]       ; edx = 00000001 | 00000002 | 00000003
    imul edx, dword ptr [edi]       ; edx = 00060005 | 000E000C | 00C00015 
    add eax, edx                    ; eax = 00060005 | 00140011 | 00D40026
    add esi,00000002                ; esi = 0018FE2A | 0018FE2C | 0018FE2E
    add edi,00000002                ; edi = 0018FE22 | 0018FE24 | 0018FE26
    sub ecx,00000001                ; ecx = 2        | 1        | 0
    jne 00401043
    
    mov dword ptr -04[ebp],eax      ; eax = 0x00D40026 = 13893670

If You clear higher word of eax result will be as expected 38

    and eax,0ffffh
    mov [result],eax

Better use 16-bit version, imul dx, word ptr[edi] and mov [result],eax after loop.

2 Comments

If that instruction is invalid, how does it still create the correct result?
I tried vs19 - Illegal size for operand and watcom2.0 - Operand must be the same size. I need to correct above answer because imul truncates data. TMP_XP := DEST ∗ SRC (* Signed multiplication; TMP_XP is a signed integer at twice the width of the SRC *), DEST := TruncateToOperandSize(TMP_XP) . Max value of elements in both arrays should be [-104, 104] to show correct result I think.
0

Reference this pic, there is no dest=Reg16, and Source=Mem16

Your compiler might show a warning message.

Try to move word ptr[edi] into a 32 bits register for imul .

I guess your compiler might compile it into : imul edx, Dword ptr[edi];

And you didn't return anything after finish the asm block !?

This function return a uninitial value long result ...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.