【问题标题】:Smallest code-size loop to count bits in a register (decrementing another register)?对寄存器中的位进行计数的最小代码大小循环(递减另一个寄存器)?
【发布时间】:2018-04-07 10:29:21
【问题描述】:

我需要在8086*中写如下C代码sn-p 组件尽可能短(小于 10 个字节),但我只能设法将其写入 12 个字节。

有什么想法吗?

while (ax) {
    bx--;
    ax &= ax-1;
}

【问题讨论】:

  • Stackoverflow 不一定是市场。也许如果您在这里分享了您的尝试,社区会更快地在您需要帮助的地方提供帮助。 :)
  • @johan :为了公平起见,我要指出该用户无法查询已删除的问题,而原始问题实际上显示了解决方案的尝试。不得不承认我投票重新打开了原版。我对原版唯一真正的反对意见是它可能属于代码高尔夫方面。
  • 好的,我会尝试回答。
  • 重命名并描述了循环的作用,因此未来的读者可以从标题中了解它的含义。

标签: assembly x86 micro-optimization code-size


【解决方案1】:

您尝试的操作称为人口计数,即计算寄存器中设置的位数。

在较新的 CPU 中实际上只有一条指令。而且由于在 2018 年瞄准原始 8086 确实没有那么有趣。

简单的答案是:

f3 0f b8 c0             popcnt eax,eax
29 c3                   sub    ebx,eax 

6 个字节,如果您愿意在 ebx 中允许一个正值,并且可以假设/确保 ebx 一开始就为零,则可以减少到 4 个。

请注意,16位寄存器没有必要使用,已经很多年没有了。

如果您希望代码在原始 8086(不支持 popcnt)上运行,您必须保留循环。

以下非常简单的代码占用 12 个字节:

85 c0                   test   ax,ax          ;is AX zero?
74 08                   je     <done>         ;yes, bail out
<loop>:
4b                      dec    bx             ;bx--
89 c1                   mov    cx,ax
49                      dec    cx             ;cx = ax - 1
21 c8                   and    ax,cx          ;ax = ax & (ax-1) 
75 f8                   jne    <loop>         ;repeat until done
<done>:

您可以通过以稍微低效的方式计算位来将其减少到 9 个字节。这里我们简单地测试一下 ax 的所有 16 位。

<loop>:
d1 e0                   shl    ax,1       ;MSb to carry flag (CF)
83 db 00                sbb    bx,0       ;bx=bx-CF
85 c0                   test   ax,ax      ;is AX zero?
75 f9                   jnz    <loop>     ;if not then loop until done

诀窍是了解代码的作用,然后以不同的方式重新表述它。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-28
  • 2012-06-24
相关资源
最近更新 更多