2

I am trying to solve these simple multiplication and division operations in assembly.

5*4 + 35/7

I have done this

mov al,5
mov bl,4 
mul bl 

mov ax,35 
mov bl,7 
div bl

My previous value for multiplication in the AX register is overwritten with a new result from division. How do I fix this?

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Nasim Rony
  • 519
  • 5
  • 22

4 Answers4

2

Emu8086 is based on 8086 instructions so you only have the single operand versions of MUL and DIV. Just to make you aware, IMUL is the signed version of MUL, and IDIV is the signed version of DIV. Assuming you intended to do unsigned arithmetic your code could look like this:

mov al,5
mov bl,4 
mul bl 
mov cx, ax     ; Save AX to CX

mov ax,35 
mov bl,7 
div bl

xor ah, ah     ; Result(quotient) of DIV in AL, ensure AH is zero
add ax, cx     ; AX=AX+CX

There is no way to prevent AX from being destroyed with the 8086 specific instructions of MUL and DIV. You'll have to move the value to a scratch area. We'll use the register CX for that in the code above. Since we save the result of the MUL instruction to CX we just need to add it to the result of the DIV instruction (where the quotient is in AL). We zero out the remainder stored in AH, leaving the quotient in AL. AX would now contain the quotient (as a 16-bit value) that we add to CX, and the final result of the equation is stored in AX.


A simplification for multiplying by 4 is to shift a value left 2 bits. This code:

mov al,5
mov bl,4 
mul bl 
mov cx, ax     ; Save AX to CX

Can be reduced to:

mov cx, 5
shl cx, 2      ; Multiply CX by 4

The 8086 processor doesn't support SHL shifting more than 1 bit except through the CL register. EMU8086's assembler automatically translates the SHL reg, imm instruction to one or more SHL, reg, 1 instructions. In this case SHL CX, 2 was translated to:

shl cx, 1      ; Multiply CX by 2
shl cx, 1      ; Multiply CX by 2 again

Most 8086 assemblers will not do this translation for you. Alternatively, the number of bits to shift can be specified in the CL register. This (or the equivalent) would work with most 8086 assemblers:

mov cl, 2      ; Number of bits to shift left by in CL
mov dx, 5
shl dx, cl     ; Shift DX to the left by CL(2) is equivalent to multiply by 4

mov ax,35 
mov bl,7 
div bl

xor ah, ah     ; Result(quotient) of DIV in AL, ensure AH is zero
add ax, dx     ; AX=AX+DX
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • The `shl cx, 2` is __not__ a 8086 instruction! Only single shifts are allowed. – Fifoernik Jan 30 '16 at 14:33
  • 1
    @Fifoernik That is true, but the question is also tagged EMU8086 (per the OPs comment). One place EMU8086 diverges from the 8086 is that the assembler supports SHL by an immediate value (other than 1), but internally produces multiple SHL by 1 instructions. I amended my answer to make that clarification – Michael Petch Jan 30 '16 at 17:18
  • So when you say "internally", you mean at assemble time? If you were single-stepping your code, you'd see repeated `shl cx` instructions? Your wording makes it sound like it handles the `imm8` count encoding. If it's just syntactical sugar in the assembler, any assembler could do that for 8086 targets if it wanted to. It may happen that emu8086 is the only 8086 assembler that does this, but it works in emu8086 because of its assembler, not because it's "not a real 8086 processor". – Peter Cordes Jan 30 '16 at 18:52
  • Yes if you bring it up in the debugger you'll discover that the one instruction gets translated to multiple shift instructions. It isn't that the underlying architecture supports the enhanced shift instruction, the assembler takes liberties to generate the equivalent although potentially inefficient translation – Michael Petch Jan 30 '16 at 19:37
  • Emu8086 is an anomaly since it is an integrated assembler and runtime environment. The same software that assembles also debug and emulates. A typical user who isn't aware of this likely would never notice the translated instruction unless they look at the raw machine code generated. – Michael Petch Jan 30 '16 at 19:40
2

My previous value for multiplication in the AX register is overwritten with a new result from division. How do I fix this?

Simple enough. Put the result from the multiplication in an extra register (I used DX), calculate the next term of the expression, finally add both results. It's important to note that the quotient is only in the AL register, so you need to clear the AH register before doing the addition! (The numbers used in the example "35/7" yield a remainder of 0 in AH but you should not count on this when asked to write a program!)

mov al,5
mov bl,4
mul bl     ; -> Result is in AX
mov dx, ax

mov ax,35 
mov bl,7 
div bl     ; -> Result is in AL

mov ah, 0
add ax, dx ; -> Final result is in AX
Fifoernik
  • 9,779
  • 1
  • 21
  • 27
1

You will need to multiply 5 * 4 and then push the value onto the stack. Then do your second calculation, the division. Then pop the first value off of the stack and add the values together.

E Lewis
  • 62
  • 6
  • Actually, now that I think about it, and it's been awhile, but I believe that the problem may be that al is half of ax. So when you move a value into ax it overwrites al. In other words, al and ah are the low and high bits of ax. – E Lewis Jan 30 '16 at 07:02
  • There's no need to use the stack, but your general point is correct that the OP clobbers the mul result (which does ax=al*src) with `mov ax,35`. x86 has 7 general purpose registers (not counting the stack pointer, which can be used as general-purpose, but never is). This isn't tagged as 8086 either, so there's no reason not to use 2 or 3 operand (`imul dest, src, imm`) to save on moving things aside out of eax / edx (used implicitly by `mul` and `div`). – Peter Cordes Jan 30 '16 at 07:14
  • I agree. Once I realized that al and ah are the low and high of ax then the solution is obviously simply to use another register. – E Lewis Jan 30 '16 at 07:16
  • mov al,5 mov bl,4 mul bl mov cx,35 mov dl,7 div dl – E Lewis Jan 30 '16 at 07:29
1

To answer the primary question: You can use imul reg, reg, imm to do your multiply into a different register. Or you can just mov ecx, eax to save your result in a register you don't need for div.


Since you didn't say where the data has to come from, the obvious thing to do is perform the calculation at assemble time.

mov eax,  5*4 + 35/7

If you still wanted to compute at run time, you need to start with the values in immediate constants, in registers, or in memory. It's convenient to illustrate a mix of those here (since div has no immediate-operand form)

It's usually best to use the default operand size, which is 32bits in 32 and 64bit mode. 8bit ops are not usually slower, though.

mov    eax, 35
div    byte [seven]     ; al = ax/7, ah=ax%7.  wider operand sizes divide edx:eax by the src.

mov    cl, 5
lea    eax, [ecx*4 + eax]  ; al = cl*4 + al.  Ignore the garbage in the high bits.
; result in al

section .rodata
seven:   db 7

div is slow, so compilers replace divides by constants with multiplies by funky constants, and shift the result, because that's possible with 2's complement overflow.

See Which 2's complement integer operations can be used without zeroing high bits in the inputs, if only the low part of the result is wanted? for justification for using lea to multiply by 4 and add, without clearing the high bits.

Community
  • 1
  • 1
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • I should point out that my edit didn't alter the question any. The addition of tag x86 doesn't say explicitly whether it is 16-bit, 32-bit, or 64-bit code. The tag itself is worded to encompass all three of them. I'd add a specific processor tag once the OP answers the question I posed under the question. Still possible they are targeting 16-bit code. I have a suspicion it is 16-bit code based on his university and some of the curriculum. – Michael Petch Jan 30 '16 at 07:52
  • @MichaelPetch: I realized that after looking at the edit more carefully. I hadn't figured out from the garbled original that the OP knew the problem was clobbering ax. Also, yeah I figured he was probably writing 16bit code, but 16bit is dumb so I purposely avoided encouraging it. I didn't look at his user page to try to figure out what university he was attending. multi-operand `imul` is still available in 16bit mode on normal CPUs, and I had been guessing it was a DOS question, not crappy 8086 without 386 features. – Peter Cordes Jan 30 '16 at 08:12
  • He added a comment, and I'm not surprised it was emu8086, so the target is 16-bit code. As for the multioperand versions of imul didn't start appearing until 80286 (some were added in the 386). I don't believe any of the multi operand variants work on emu8086 as it was designed as an 8086 environment. – Michael Petch Jan 30 '16 at 08:32
  • @MichaelPetch: yup, I know multi-operand `imul` wasn't in 8086. https://en.wikipedia.org/wiki/X86_instruction_listings says imul immedate was added with 186. It doesn't say anything about 2-operand imul. :/ My phrasing was clumsy, but like I said, I was guessing the OP was using DOS, but not that he was stuck with 8086. So I was trying to point out some neat ways of doing stuff that 8086 doesn't have. Answers that do things the old, slow way don't clue people in. If people want crippled answers, they need to specifically ask for them. And then I usually don't bother answering. – Peter Cordes Jan 30 '16 at 08:42