乘两个16位无符号值,而无需使用乘法和除法指令[8086大会]
我目前工作的一个任务,在那里我写一个子程序,其中2张无符号数相乘得到,并在DX产生一个结果:AX对。但我不能使用mul,imul,div和idiv的说明。当我运行我的代码时,下半部分(AX寄存器)总是正确的,但DX寄存器不是。任何人都可以指出我正确的方向,我做错了什么?乘两个16位无符号值,而无需使用乘法和除法指令[8086大会]
;-----------------------------------------------------------
;
; Program: MULTIPLY
;
; Function: Multiplies two 16 bit unsigned values ...
; .... duplicating the MUL instruction
;
; Input: The two values to be multiplied are passed on the stack
; The code conforms to the C/C++ calling sequence
;
; Output: The 32 bit result is returned in the dx:ax pair
; Registers required by C/C++ need to be saved and restored
;
; Owner: Andrew F.
;
; Changes: Date Reason
; ------------------
; 07/20/2013 Original version
;
;
;---------------------------------------
.model small
.8086
public _multiply
.data
;---------------------------------------
; Multiply data
;---------------------------------------
.code
;---------------------------------------
; Multiply code
;---------------------------------------
_multiply:
push bp ; save bp
mov bp,sp ; anchor bp into the stack
mov ax,[bp+4] ; load multiplicand from the stack
mov dx,[bp+6] ; load multiplier from the stack
push bx
push cx
push di
;---------------------------------------
; copy ax to cx, and dx to bx
;---------------------------------------
mov cx,ax ;using bx and cx as my inputs
mov bx,dx
;---------------------------------------
; Check for zeros, zero out ax and dx
;---------------------------------------
start:
xor ax,ax ; check for multiplication by zero
mov dx,ax ; and zero out ax and dx
mov di,cx ;
or di,bx ;
jz done ;
mov di,ax ; DI used for reg,reg adc
;---------------------------------------
; loop/multiply algorithm
;---------------------------------------
loopp:
shr cx,1 ; divide by two, bottom bit moved to carry flag
jnc skipAddToResult ;no carry -> just add to result
add ax,bx ;add bx to ax
adc dx,di ;add the carry to dx
skipAddToResult:
add bx,bx ;double bx current value
or cx,cx ; zero check
jnz loopp ; if cx isnt zero, loop again
;---------------------------------------
; Restore register values, return
;---------------------------------------
done:
pop di ;restore di
pop cx ;restore cx
pop bx ;restore bx
pop bp ; restore bp
ret ; return with result in dx:ax
;
end ; end source code
;---------------------------------------
添加另一个当你使用奇怪转移di
的bx
值。你的算法似乎是这样的:
- 收集值,把它们放入BX和CX。
- 虽然CX> 0:
- CX向右偏移。
- 如果移位有一个,则将BX添加到AX,并将DI(DI中有零)添加到带有进位的DX中。
- 将BX添加到BX。
- 返回DX:AX。
在CX的每次右移后,您缺少DI:BX的左移。你只移动BX(并且我使用shl bx,1
而不是add bx,bx
),而DI保持为零,因此当BX超过16位时,你将丢失应该发送到DX的位。为了补救,使用旋转通过反对DI。
loopp:
shr cx,1 ; divide by two, bottom bit moved to carry flag
jnc skipAddToResult ;no carry -> just add to result
add ax,bx ;add bx to ax
adc dx,di ;add the carry to dx
skipAddToResult:
shl bx,1 ;double bx current value
rcl di,1 ; and put overflow bits to DI
; this together doubles the number in DI:BX
or cx,cx ; zero check
jnz loopp ; if cx isnt zero, loop again
非常感谢您的帮助Vesper!那是确切的问题,而且一个快速而简单的修复帮助我的代码通过了所有的测试用例。 – CadetFayed
它总是更好地使用'测试CX,cx'代替'或CX,cx'([性能以及人类可读性(https://*.com/questions/33721204/test-whether-a-register -is-零与-CMP-REG-0-VS-或-REG-REG/33724806#33724806))。此外,'shl bx,1'会更有效,因为'add bx,bx',但'shl'对人类来说更加清晰。 –
另外,如果您为最终迭代复制循环体,则可以使用shift的ZF结果作为循环出口检查。 (也就是'shr cx,1' /'jnz loopp'设置为下一次迭代,或者从最后一位可能存在非零CF溢出循环)。 –
当加倍“bx”时,您需要考虑进入“di”,并且您还需要将di加倍。我想'adc di,di'会起作用。 PS:学会使用一个调试器,这样你可以浏览你的代码,看看它出错的地方。 – Jester