3

I am getting an unwanted answer from the above assembler call.

  • Expected answer: -33
  • Actual answer: 65503

I am using MASM and Visual Studio 2019 Community Edition.

; assembler code
Tester2 proc

mov ax, -100
cwd
mov cx, 3
idiv cx

ret 
Tester2 endp

end
// c++ code
#include <iostream>

extern "C" int Tester2();

int main() {

    auto rr = Tester2();
    std::cout << rr;

    return 0;
}
5
  • 65503 == -33, when using 16-bit quantities. The former is unsigned, the latter is signed. Commented Sep 24, 2024 at 12:00
  • I missed the following line that is just after the iostream include: extern "C" int Tester2(); Commented Sep 24, 2024 at 12:00
  • Your asm code is returning 16 bits but your C++ code expects 32. Fix one or the other so they match. Commented Sep 24, 2024 at 12:05
  • 2
    Thank you Jester! I declared rr as int16_t and now it works. Commented Sep 24, 2024 at 12:08
  • 1
    Normally you'd want to use 32-bit operand-size (in 32 or 64-bit code), only using 16-bit partial registers if you actually need to ignore high garbage in input args, or replace the low 16 bits of a value while keeping the high 16 (e.g. a bitfield or struct loaded into a wider register.) Commented Sep 24, 2024 at 21:13

2 Answers 2

3

You’ve declared Tester2 as returning an int, but that is most likely a short since you’re only filling in ax and ignoring the rest of eax. If you declare your function as returning a short, the problem should go away.

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

Comments

3

When proc Tester2 returns, the ax register does indeed contain the value -11. This register is 16 bits long, and the number -11 expressed in 16-bit hexadecimal is 0xFFF5.

However:

You have declared Tester2() as returning an int.

You are using the [masm64] tag, so you are presumably targeting x64.

In C and C++ code compiled for 64-bit architectures an int is 32 bits long. (Same as in 32-bit architectures.)

So, here is where the problem begins: your assembly is returning a 16-bit value but your C++ is expecting a 32-bit value.

When the result of invoking Tester2() is assigned to rr, the entire 32-bit long eax register gets stored in rr.

The lower half of eax is ax, which is 0xFFF5, but the upper half is zero1, so rr receives the value 0x0000FFF5.

0x0000FFF5 is 65503 in decimal, and that is what you observe.

To fix this, you have three options:

Option #1:

Switch your tooling to target a 16-bit architecture instead of a 64-bit (or 32-bit) architecture. Then, your int will be 16-bits wide.

Option #2:

Declare Tester2() as returning a 16-bit value by using #include <cstdint> and replacing int with int16_t.

Option #3:

Write Tester2() to use 32-bit arithmetic, so that it returns a 32-bit result in register eax.

Needless to say, Option #3 is the best option.

32-bit operand-size typically has the smallest machine-code size (no prefixes necessary), and is at least as fast as other operand-sizes for math instructions. That's one reason why it was a good choice for int on x86-64, as well as being the same size as in 32-bit C and C++ implementations.


1 The upper half of eax is zero most probably due to coincidence. I am saying this because modifying the ax register is known to leave the upper part of eax unmodified. This means that your idiv instruction left the upper part of eax unchanged, which means that eax must have been zero before your main() was invoked, which is just pure luck.

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.