1

I am trying to write a MASM macro that takes a number and divides it by two:

mCalculateDiscount MACRO originalPrice
    PUSH EDX
    PUSH ECX
    MOV  EDX, 0
    MOV  EAX, originalPrice
    MOV  ECX, 2
    DIV  ECX
    POP  ECX
    POP  EDX
ENDM

And the answer will be saved inside of EAX, is this approach wrong and what approach is right, using macros or procedures?

Seek
  • 27
  • 1
  • 5
  • 2
    It is not a matter of "wrong" vs. "right". It has more to do with your intention, which you have not stated, and how you use it. Macros will avoid `call` & `ret`, which can improve performance, but if this macro is used often (from a static perspective, i.e. call site count) can cause the code to be larger than the same using function invocation, which will reduce performance. (Btw, to divide by 2 we can shift instead.) – Erik Eidt Aug 10 '21 at 15:31
  • @ErikEidt: I'd agree with Nate's answer and say that this *is* the wrong approach for writing assembly. If you think a macro like this is a good idea, you should just write in C or Rust and leave the asm to a compiler. Register save/restore should be around whole functions, not single instructions. ([What are callee and caller saved registers?](https://stackoverflow.com/a/56178078)). Especially where this macro destroys the remainder, which would be a partial excuse for very inefficiently using `div` with a known constant power of 2, we can say it's the wrong approach for any use case. – Peter Cordes Aug 10 '21 at 18:50
  • (Not a bad Stack Overflow question, though: clear and easy to answer.) – Peter Cordes Aug 10 '21 at 18:54

1 Answers1

2

For an operation that really boils down to a single instruction (not counting the code to load the operands), I think you would normally just write it directly, without resorting to a macro.

A macro like this is inefficient because it doesn't take into account the surrounding code. What if you didn't have any important data in EDX? Then the push/pop of EDX is a waste. Likewise for ECX. Or maybe in your surrounding code, ECX was used for something else, but ESI was available; if coding directly you could just use ESI for the divisor instead. With the macro you lose that opportunity.

If your code becomes so complex that you can't keep it organized without macros like this, then it is probably time to port it to a higher-level language like C. Your macros with built-in push/pop are doing a feeble version of register allocation, which an actual compiler can do much more effectively.

Finally, as Erik Eidt points out, using DIV to divide by two is very inefficient; you should right shift instead (probably about 10 times faster), and it also avoids needing a bunch of extra registers. This whole macro could be replaced by

    MOV EAX, originalPrice
    SHR EAX, 1
Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82