【问题标题】:ASM 8086 division without div没有 div 的 ASM 8086 除法
【发布时间】:2020-05-30 15:04:16
【问题描述】:

我需要在 asm 8086 中编写一个类似 b=a/6 但没有 DIV 指令的程序。我知道如何使用 SAR,但只有 2、4、8、16...

mov ax,a
sar ax,1 ;//div a by 2
mov b,ax

我的问题是如何将 div 设为 6?

【问题讨论】:

  • 您可以使用减法并计算归零所需的次数,例如。 30/6=5 和 30-6-6-6-6-6=0 所以对于 30,你必须 5 次减去 6 才能达到零
  • 对于一个固定的(编译时常数)除数,有一个使用乘法来进行精确整数除法的定点技巧:Why does GCC use multiplication by a strange number in implementing integer division?。另外,您要签名还是未签名的部门? div 是无符号的,sar 是有符号的(对于负数,舍入方式与 idiv 不同)

标签: assembly intel x86-16 emu8086 integer-division


【解决方案1】:

给定another answer 的方法是简单的暴力循环,对于较大的a 值可能需要一段时间。这是一个使用较大块的版本(像长除法问题一样工作),专门编码为将有符号数除以 6:

; signed divide by 6
    mov ax,a
    mov cx,1000h  ; initial count of how many divisors into ax to check for
    mov bx,6000h  ; value of "divisor * cx"
    xor dx,dx     ; result
top:
    cmp ax,bx
    jl skip
    ; we can fit "cx" copies of the divisor into ax, so tally them
    add dx,cx
    sub ax,bx
    ; optionally can have a "jz done" here to break out of the loop
skip:
    shr bx,1
    shr cx,1
    jnz top

    ; copy result into ax
    mov ax,dx

如果需要除 6 以外的东西,则需要调整初始的 cxbx 值。 cx 是保留第 14 位设置的除数的 2 的幂(因为第 15 位是符号位;对于无符号除法,您希望设置第 15 位)。 bx 是 2 的幂。如果 a 的初始值有限制,您可以调整初始 cxbx 值,但必须小心,因为如果让它们太小。

【讨论】:

    【解决方案2】:

    您可以使用减法并计算归零所需的次数,例如。 30/6=5 和 30-6-6-6-6-6=0 所以对于 30,你必须 5 次减去 6 才能达到零

    类似的东西:

    mov cx,0
    mov ax, dividend
    
    divloop:
      cmp ax, 0
      jle done   
      sub ax, divisor
      inc cx
      jmp divloop
    
    done:
      ;result is in cx
    

    【讨论】:

      【解决方案3】:

      由于您在示例中使用了 sar 指令,我假设您需要 signed 除以 6。

      以下代码使用倒数常数的乘法。它将结果向下舍入为 -∞:

      mov ax,0x2AAB   ; round(0x10000/6)
      imul A          ; DX:AX contains the signed 32-bit result of the multiplication
      mov B,dx        ; take only the upper 16 bits
      

      如果您需要将结果四舍五入为 0,则将负结果加一:

            mov  ax,0x2AAB
            imul A
            test dx,dx  ; set FLAGS according to DX
            jns  skip
            inc  dx      ; increase DX if it was negative
      skip: mov  B,dx
      

      最后,如果你需要无符号除以6,你需要一个更精确的常数和一个移位:

      mov ax,0xAAAB  ; round(0x40000/6)
      mul A          ; DX:AX contains the unsigned 32-bit result of the multiplication
      shr dx,1
      shr dx,1       ; shift DX right by 2 (8086 can't do "shr dx,2")
      mov B,dx
      

      【讨论】:

      • 使用test dx,dx根据DX设置FLAGS,it's more efficient than or在某些CPU上,而不是在8086上更糟。在现代CPU上,编译器通常会生成无分支代码,有条件地用@之类的东西加1 987654331@ / sar ax,15 / sub dx, ax 减去 0 或 -1,但在实际的古代 CPU 上会慢得多。
      • 这些魔法常量是 GCC 用于short:godbolt.org/z/4baG7K。有趣的是,对于已签约的部门来说,它只是占据上半场而无需进一步转移;在其他情况下,需要轮班来处理大红利:Divide by 10 using bit shifts? 是该错误的 32 位示例,它仅适用于足够小的红利。还有Why does division by 3 require a rightshift (and other oddities) on x86?.
      • 负数加一,也可以使用进位标志:cmp dh,0x80/sbb dx,-1
      【解决方案4】:

      小学毕业

      x/6 = x * 1/6;

      看看当你让 C 编译器去做时会发生什么

      unsigned short fun ( unsigned short x )
      {
          return(x/6);
      }
      

      32 位 x86

      0000000000000000 <fun>:
         0:   0f b7 c7                movzwl %di,%eax
         3:   69 c0 ab aa 00 00       imul   $0xaaab,%eax,%eax
         9:   c1 e8 12                shr    $0x12,%eax
         c:   c3                      retq 
      

      32 位臂

      00000000 <fun>:
         0:   e59f3008    ldr r3, [pc, #8]    ; 10 <fun+0x10>
         4:   e0802093    umull   r2, r0, r3, r0
         8:   e1a00120    lsr r0, r0, #2
         c:   e12fff1e    bx  lr
        10:   aaaaaaab
      

      同样的故事。将其转换为 8086。

      所以 6 = 3 * 2,所以我们确实需要除以 3。然后调整

      unsigned short fun ( unsigned short x )
      {
          return(x/3);
      }
      
      00000000 <fun>:
         0:   e59f3008    ldr r3, [pc, #8]    ; 10 <fun+0x10>
         4:   e0802093    umull   r2, r0, r3, r0
         8:   e1a000a0    lsr r0, r0, #1
         c:   e12fff1e    bx  lr
        10:   aaaaaaab
      

      少一点移位。其中一个转变是提高精度,另一个是因为其中有一个除以 2。

      你当然可以做减法循环。否则它是长除法,实际上很容易编码。

      【讨论】:

        猜你喜欢
        • 2015-07-31
        • 1970-01-01
        • 2011-02-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-25
        • 1970-01-01
        相关资源
        最近更新 更多