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.
ax*bxwill be stored indx, which is stated in Intel's manual (MUL r/m16 Unsigned multiply (DX:AX ← AX ∗ r/m16).) - Michaelimul ax, bxif you don't want the full result. But that form of imul was new with 386 (ulukai.org/ecm/insref.htm#insIMUL) - Peter Cordes