1

I need to write the following C code snippet in 8086* assembly as short as possible (less than 10 bytes) but I can only manage to write it in 12 bytes.

any ideas?

while (ax) {
    bx--;
    ax &= ax-1;
}
Johan
  • 74,508
  • 24
  • 191
  • 319
  • 2
    Stackoverflow is not necessarily a marketplace. Maybe if you have shared your attempts here, community would be more quick in helping where you need assistance. :) – Sivaprasanna Sethuraman Apr 07 '18 at 11:37
  • 1
    @johan : out of fairness I'd point out that this user can't query deleted questions, and the original question actually showed an attempt at a solution. Have to admit I voted to reopen the original. My only real objection to the original was that it probably fell towards the codegolf side of things. – Michael Petch Apr 07 '18 at 17:08
  • OK fine, I'll attempt an answer. – Johan Apr 07 '18 at 17:14
  • Retitled with a description of what the loop does, so future readers will know what it's about from just the title. – Peter Cordes Apr 08 '18 at 03:51

1 Answers1

2

The operation that you're attempting is called a population count, i.e. count the number of set bits in a register.

There is actually a single instruction for that in newer CPU's. And since targeting the orginal 8086 is really not that interesting in 2018.

The simple answer is:

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

6 bytes, which can be reduced to 4 if you're willing to allow a positive value in ebx and can assume/assure that ebx is zero to begin with.

Note that there is no need to work with the 16-bit registers, there has not been for many years.

If you want the code to work on the original 8086 (which does not support popcnt) you'll have to retain the loop.

The following perfectly straightforward code takes 12 bytes:

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>:

You can cut this down to 9 bytes by counting the bits in a slightly less efficient way. Here we simply test all 16 bits of ax.

<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

The trick is to understand what the code does and then rephrase it in a different way.

Johan
  • 74,508
  • 24
  • 191
  • 319
  • Please don't recommend obsolete and sub-optimal idioms like `or ax,ax` instead `test ax,ax` for setting flags according to a value in a register. They're the same length, but [`test` is better in every possible way (except sometimes to avoid register-read stalls on nearly-obsolete Nehalem and earlier)](https://stackoverflow.com/questions/33721204/test-whether-a-register-is-zero-with-cmp-reg-0-vs-or-reg-reg/33724806#33724806). Neat trick with `pushf`/`popf`; but wouldn't `test ax,ax` achieve *exactly* the same thing here, setting ZF according to the value `shl` left in `ax`? – Peter Cordes Apr 08 '18 at 03:44
  • 1
    @PeterCordes, I seem to recall test reg,reg was introduced in the 386 and thus does not work on the original 8086. Having done some googling it seems I was mistaken. `test` has been with us from the beginning. – Johan Apr 08 '18 at 20:38
  • Yup, it existed in 8086, but not 8080, [where `ora, a` was a common idiom, apparently.](https://stackoverflow.com/questions/33721204/test-whether-a-register-is-zero-with-cmp-reg-0-vs-or-reg-reg/33724806#comment86128868_33724806) – Peter Cordes Apr 08 '18 at 22:05
  • 2
    The 8086 can't address using `[ax-1]`. The dump `8d 48 ff` corresponds to `lea cx, [bx+si-1]`. – Sep Roland Apr 08 '18 at 22:41