How can I find how many times does 101b show in the number?

荒凉一梦 提交于 2019-12-02 13:10:39

somthing like this (not tested):

mov dx, yourNumber
mov cx, 16
xor ax, ax
count:
  mov bx, dx
  and bx, 0b111
  cmp bx, 0b101
  jne nope
     inc ax
  nope:
  rol dx, 1
  dec cx
  jnz count

so basically, you rotate the register 16 times and on each iteration to test, if the lowest 3 bits by masking the register with 0b111 are equal to 0b101. after this calculation, the result is in ax and the number you tested should be still in dx

The logic:

If you XOR the value with 1010101010101010b (0xAA), any 4-bit group that was 0101b will become 1111b, and any group that wasn't won't.

If you add 1 to 1111b it will overflow, and if you add 1 to any other number it won't overflow. You can arrange this so when the addition overflows it causes the carry flag to be set.

You can use the adc instruction to add the carry flag to a count/sum to get the total number of groups that overflowed (the number of groups that were 0101b originally).

This will only find 0101b that is aligned to the start of the group. To make it work for "0101b anywhere" you'd need to do it 4 times, with a rotate between each time.

The code:

    mov cx,4           ;Number of times to rotate/repeat
    xor ax,ax          ;ax = current sum of 4-bit groups that were 0101b = 0

.nextRotation:
    mov bx,dx          ;bx = the value
    xor bx,0xAAAA      ;dx = any 4-bit group that was 0101b is now 1111b
    add bx,0x1000      ;Carry set if fourth/highest group was originally 0101
    adc ax,0           ;Add carry to the sum
    add bl,0x10        ;Carry set if second group was originally 0101b
    adc ax,0           ;Add carry to the sum
    shl bx,4
    add bx,0x1000      ;Carry set if third group was originally 0101b
    adc ax,0           ;Add carry to the sum
    add bl,0x10        ;Carry set if first/lowest group of 4 bits was originally 0101b
    adc ax,0           ;Add carry to the sum
                       ;ax = number of 4-bit groups that were originally 0101b

    rol dx,1
    loop .nextRotation

I'm not sure I've understood where the 4th copy is. I (and @sivizius) were thinking it was composed of bits 7, 0, and 1 (wrapping around to the top bit). But apparently that's not what you want.

(Also your bit numbering makes no sense. You say the top bit of your 8-bit number is bit 0, but in comments you insist you want to work with 16-bit numbers. So what bit-number is 1<<15?)

Anyway, I think you want to find patterns like 1.0.1 and 1..0..1? (Where . is a don't-care placeholder in the pattern that can match either a 0 or 1).

You can look for those with something like this (inside a shr dx, 1 loop).

; dumb brute force method.
countloop:                   ; do {
    mov  ax, dx
    and  al, 111b               ; select the bits that matter in 101
    cmp  al, 101b               ; check that it's the pattern we want
    ; do count += ZF somehow

    mov  ax, dx
    and  al, 10101b             ; select the bits that matter in 1.0.1
    cmp  al, 10001b             ; check that it's the pattern we want
    ; do count += ZF somehow

    mov  ax, dx
    and  al, 1001001b           ; select the bits that matter in 1..0..1
    cmp  al, 1000001b           ; check that it's the pattern we want
    ; do count += ZF somehow

   ... for all the rest of the patterns.
   ; Wider patterns will need to use AX instead of AL

    shr  dx, 1
    cmp  dx, 101b
    jae  countloop           ; }while(x >= 0b101);

;;; exit the loop as soon as DX contains no bits high enough to possibly match
;;; with a big loop, this is worth the extra cmp
;;; instead of just using jnz on the  shr dx,1  result

So exactly like you're doing for 101b except you force the don't-care bits to zero.

I haven't thought of anything better than checking separately for every possible pattern separately inside the loop body.

The "count += ZF" could be (386) setz al / add cl, al, or a simple jnz over inc cx. (I think the max possible count is less than 255, so an 8-bit counter is fine.)

Note that I'm not using cx as a loop counter, instead ending the loop after shifting all the bits out of dx

Another way to do it branchlessly without SETCC would be

   mov   ax, dx
   and   al, 10101b
   xor   al, 10001b
   sub   al, 1            ; set CF only if AL==0
   adc   cx, 0            ; cx += (al == 0b10001)

It seems really complicated, but does it really? I think it might just be a little tricky.

Yes, it is really complicated. Here's how far I've got:

Step 1: Do "run-length encoding", ignoring leading and trailing zeros. Examples (for 8-bit numbers):

01010101 = 1(1s), 1(0s), 1(1s), 1(0s), 1(1s), 1(0s), 1(1s)
11001011 = 2(1s), 2(0s), 1(1s), 1(0s), 2(1s)
11111101 = 6(1s), 1(0s), 1(1s)

Step 2: Find permutations of "run of ones, run of zeros, run of ones" and multiply each run's count. Examples (for 8-bit numbers):

01010101 = 1(1s), 1(0s), 1(1s), 1(0s), 1(1s), 1(0s), 1(1s)
         = 1     *1     *1
         + 1     *1                   *1
         + 1     *1                                 *1
         + 1                   *1     *1
         + 1                   *1                   *1
         + 1                                 *1     *1
         +               1     *1     *1
         +               1     *1                   *1
         +               1                   *1     *1
         = 1+1+1+1+1+1+1+1 = 9

11001011 = 2(1s), 2(0s), 1(1s), 1(0s), 2(1s)
         = 2     *2     *1
         + 2     *2                   *2
         + 2                   *1     *2
         +               1     *1     *2
         = 4+8+4+2 = 18

11111101 = 6(1s), 1(0s), 1(1s)
         = 6     *1     *1
         = 6

At this point; it's easy to conclude that an efficient implementation would involve lists, and would probably be easier to implement if recursion is used (find permutations that involve the first run of ones in the list, then discard the first run of ones and the first run of zeros and recurse).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!