1
votes

I am starting to get to grips with asm programming but I feel I am missing something regarding the use of registers to store variables.
The issue that I have is that some instructions will modify other registers for internal reasons. In some cases I have used these registers to store application logic.
Is there any golden rule for how to use registers?

For example: the following code changes the DX register and wipes out my current variable (also in DX).

mov ax, 04h
mov bx, 02h
mul bx

I did not want nor state that I wanted the DX register to get wiped out. What's going on here?

2
That's just the way it is. You have to learn what all the instructions (or the ones you intend to use anyway) do, and take that into account when writing your code. In the case you've shown, the most significant word of the 32-bit product ax*bx will be stored in dx, which is stated in Intel's manual (MUL r/m16 Unsigned multiply (DX:AX ← AX ∗ r/m16).) - Michael
Use imul ax, bx if you don't want the full result. But that form of imul was new with 386 (ulukai.org/ecm/insref.htm#insIMUL) - Peter Cordes

2 Answers

6
votes

Welcome to assembly language programming. The short answer is pick the values that are manipulated most often. This is the rule compilers use for register allocation. They score usages numerically with heuristic values, then put the best scores in registers until there are no more.

In 8086 assembler, you have a very small number of registers, and many of them have special purposes. You've discovered one: mul and div implicitly use ax and dx. Just think of the mul instruction as mul dx:ax, operand, and you'll see what's going on. That makes your life harder, but the architecture designer doesn't care much. She's trying primarily to make the hardware implementation small and fast.

Studying x86 assembly with 32-bit operands will make your life easier in this regard because lots of the special usages go away. For example, there are 2-operand mul instructions that take a destination register and source just like add.

Rules of thumb:

  • Put variables used within loop bodies in registers. For nested loops, start with the innermost and work outward.

  • For computations that require special purpose usages: multiply and divide (implicitly use ax and dx as operands), variable shift counts (need cx), address calculations (need bx, si, or di), etc., try to arrange for the required number to be in the necessary register during the previous computation rather than adding an extra mov. This is called register targeting.

  • Look at assembly generated by a good compiler for new ways of thinking. Many person-years of expert thought go into the strategies they use. You can learn from them.

2
votes

Instructions that compute function like mul need a register to store the result of the function. DX is primarily used for that.

You will not be able to store all your variables in registers. You'll need to use the heap and the stack to store variables as well.

The stack is particularly useful for temporary storage of variables. Your code will often look like this:

// some variable is in ax
// needing the register for something else now, pushing current variable to the stack

push ax

// put some new value into ax
// ... do some computations with it ...
// now we need the old value back in ax

pop ax