MUL function in assembly
Asked Answered
R

2

29

I am trying to execute simple multiplication in Assembly. However, I do not see the registers change when the MUL function is called.

mov bx, 5    
mov cx, 10

mul cx
Rebeccarebecka answered 30/11, 2016 at 16:12 Comment(4)
AX and DX will change, unless they happened to contain the result already. When an instruction doesn't do what you expect, read the manual. felixcloutier.com/x86/MUL.htmlCommorant
mov bx, 5 ==> mov ax, 5Bianco
It's called an «instruction»Dimarco
Related: problem in understanding mul & imul instructions of Assembly language / MUL instruction doesn't support an immediate value re: multiply by a constant (as an immediate) with imul bx, bx, 10Commorant
I
75

These are called instructions, and they specify operations that are to be performed by the processor. mov is a mnemonic for move, while mul is a mnemonic for multiply. Other common instructions include add, sub, and div. I trust you can figure out what operation these specify!

Most instructions take two parameters. In the technical jargon, these are often known as operands. The first (on the left) is the destination, and the second (on the right) is the source. So, in the case of mov bx, 5, this moves the literal value 5 into the destination register bx. The order of these parameters matters, of course, because you could not move the contents of the register bx into the literal value 5!

The mul instruction is a little bit strange because some of its operands are implicit. That is, they are not explicitly specified as parameters. For the mul instruction, the destination operand is hard-coded as the ax register. The source operand is the one that you pass as a parameter: it can be either a register or a memory location.

Therefore, you could imagine that mul cx means mul ax, cx, but you don't write it that way because the ax destination register is implicit.

Now, a mul instruction commands the processor to multiply the destination operand by the source operand, and store the result in the destination. In code, you can imagine that mul cx would translate into ax = ax * cx. And now you should see the problem: you have not initialized the contents of the ax register, so you are multiplying 10 (which is the value you placed in cx) by whatever garbage was left in ax. As such, the result is meaningless!

If you in fact want to do 5 * 10, then you just have to change one character in your code:

mov  ax, 5     ; ax = 5
mov  cx, 10    ; cx = 10
mul  cx        ; ax = ax * cx   ; actually dx:ax = ax * cx

The result will be stored in ax, which is the implicit destination register.

Well, technically, the result will be stored in dx:ax. This is a register pair, and means that the high portion of the result will be stored in dx, while the low portion of the result will be stored in ax. Why this extra complication? Because multiplying two 16-bit values may result in a value that is larger than 16 bits! Returning the full multiply result in a pair of 16-bit registers allows the mul instruction to return a 32-bit result. When you're just learning, though, you don't need to worry about this. You can just ignore the possibility of overflow, and extract the low portion of the result from ax. (But remember that 16-bit mul overwrites dx whether you want it or not. On 386 and later you can use imul ax, cx to really do ax *= cx without wasting time writing dx.)

And while I'm sure that this is just a toy example, there is really no reason to write code that multiplies two constants together. This can be done at build time, either using a calculator and hard-coding the value, or writing out the multiplication of the constants symbolically and letting your assembler do the computation. That is, mov ax, 50. Or let your assembler do it for you with mov ax, 5 * 10. But like I said, I'm sure you knew this already!

If all else fails, do consult the documentation for the instruction that is giving you trouble. You can almost always find this online by Googling the name of the instruction and "x86". For example, the mul documentation can be found here, as well as several other sites. This information can be kind of complicated, but with a bit of effort, you should be able to extract the information you need. You will also find lots of other great information and links in the tag wiki.

but for some reason i do not see the registers change when the MUL function is marked.

I should also point out that, if you are using a debugger to step through your code, the currently marked/highlighted line is the line that is about to execute. It has not executed yet, so its effects on registers, memory, etc. will not be visible yet. You have to step over the instruction, so that the mark/highlight is on the next line, and then you will see the effects of the previous (just-executed) instruction.

If you understood my explanation above, after a mul instruction, you should see the contents of the ax and dx registers change. You will also see flags and the instruction pointer change, if your debugger shows either of them. Nothing else should change! (Intel's instruction reference manual entry for mul doesn't list any other effects on the architectural state of the machine.)

Inexhaustible answered 2/12, 2016 at 14:51 Comment(1)
Great answer. Also worth noting; the resulting product will zero out the high-order register, even when the product is small enough to fit in the first of the two registers holding the product.Freehearted
M
6

The mul instruction has 2 operands: one is specified and the other one is implicit.

When you write mul cx it means something like: ax = ax * cx.

Actually it means dx:ax = ax * cx - the high half of the full 32-bit product is always written to dx. dx will be zero for small products where the result "fits" in ax. If you only want the low 16 bits of the result, you can just think of it as destroying dx1.

The answer of your question, if you really want to multiply 5 with 10, you can do something like this:

  mov  ax, 5   ; ax = 5
  mov  cx, 10  ; cx = 10

  mul  cx      ; dx:ax = ax * cx
; So   ax = ax * cx
; and dx=0 because the product happens to be small.

Footnote 1: 186 allows imul dst, src, constant like imul ax, cx, 5, allowing any registers with no implicit use of AX or DX.
386 allows imul reg,reg non-widening multiply with no implicit registers.
https://www.felixcloutier.com/x86/imul covers both forms.

Of course if you can use 386 features, you'd use lea instead of imul to multiply by 3, 5, or 9, using 32-bit addressing modes that allow a scaled index (i.e. built-in shift count).

  mov  ecx, 10
  lea  ax, [ecx + ecx*4]
Madagascar answered 30/10, 2020 at 19:1 Comment(3)
if the product is greater than 16 bits. No, the full 32-bit result is always stored in dx:ax, whether you want it or not. For small results that means DX is zeroed. Your phrasing implies that small results would leave DX unwritten, but that isn't the case. For that, you need 386 imul ax, cx to do a non-widening multiply (only the 16-bit low half). Or 186 mov cx, 10 / imul ax, cx, 5 to do ax = cx*5 using an immediate source. (Or 386 lea ax, [ecx + ecx*4] to multiply by 5 more cheaply.)Commorant
Thanks, for your comment. I'll attach it as a note.Madagascar
Easier and better to just describe it accurately the first time, instead of adding a correction. I made an edit that I think is just as easy to read, but doesn't imply anything that needs correcting.Commorant

© 2022 - 2024 — McMap. All rights reserved.