awayneff
awayneff

Reputation: 11

Assembly. How to check for the number of zeros in word?

I have a task:

Given the array of 7 bytes. Representing it as an array of 8 7-bit words, count the number of words with an odd number of zeros in them.

Here's my code so far. I have a code sample with the check for the number of 1's oddity but can't understand how to check for an odd number of zeros in the word. How can I do it?

data segment
result dw ?
NB dw 04h, 07h, 14h, 23h, 04h, 38h, 3Fh, 2Ah
data ends

code segment
assume cs: code, ds: data
start: mov ax, data
mov ds, ax 
lea bx, NB
mov cx, 7

BEG:        
mov al, [bx]
test al, 0ffh
jp OK 
or add result, 1


loop BEG

quit: mov ax, 4C00h
int 21h
exit: ret
code ends
end start

Upvotes: 1

Views: 542

Answers (1)

Sep Roland
Sep Roland

Reputation: 39411

Given the array of 7 bytes. Representing it as an array of 8 7-bit words, count the number of words with an odd number of zeros in them.

Your current solution is misinterpreting the task description!
For now, you have given the array 8, word-sized elements, whereas the task wants you to deal with 8 elements that are 7 bits wide and importantly those 8 elements have to be packed together in just 7 bytes of memory!
The array in effect is a bitstring that has 56 bits (8*7).

What does it mean to 'count the number of words with an odd number of zeros in them'?
word here is referring to a group of 7 bits, it's confusing I know.
The below snippet uses the bt (BitTest) instruction to test if a certain bit in a string of bits is ON (CF=1) or OFF (CF=0).

  xor bx, bx      ; Indexing the bitstring, BX=[0,55]
  mov [result], bl
OuterLoop:
  mov cl, 7
  xor ax, ax
InnerLoop:
  bt  [array], bx   ; Sets CF if bit is ON
  cmc               ; Makes CF=1 if bit is OFF
  adc al, 0         ; Tallies the OFF bits
  inc bx
  dec cl
  jnz InnerLoop
  and al, 1         ; Becomes 1 if tally is odd
  add [result], al
  cmp bx, 56        ; {7, 14, 21, 28, 35, 42, 49, 56}
  jb  OuterLoop

The above code can easily be written without using that cmc instruction (see Peter's comment).
Doing so will reveal a substantial speedgain. On my computer, executing the code a thousand times made the execution time drop from 597 µsec to 527 µsec (11% better).
This code will tally based on the inverse condition and subtract (the lowest bit of) the tally from the maximum achievable result.

  xor bx, bx      ; Indexing the bitstring, BX=[0,55]
  mov byte [result], 8
OuterLoop:
  mov cl, 7
  xor ax, ax
InnerLoop:
  bt  [array], bx   ; Sets CF if bit is ON
  adc al, 0         ; Tallies the ON bits
  inc bx
  dec cl
  jnz InnerLoop
  and al, 1         ; Becomes 1 if tally is odd
  sub [result], al
  cmp bx, 56        ; {7, 14, 21, 28, 35, 42, 49, 56}
  jb  OuterLoop

An alternative solution would extract 7 consecutive bits from the bitstring into a byte and (just like you did) inspect the parity flag PF. An odd number of zeroes in a group of 7 bits means that there will be a complementary even number of ones (PF=1). This solution is more than 4 times faster than the above snippets!

Next is an implementation of this:

result  db   0                                      ; (*)
array   db   12h, 34h, 56h, 78h, 9Ah, 0BCh, 0DEh
        db   0                                      ; (*)
...

        lea  bx, [array-1]       ; Start before the array
        mov  byte [bx], 0        ; Result=0
        mov  cl, 8               ; 8 elements
NextGroupOf7:
        mov  ax, [bx]
        shr  ax, cl
        and  al, 7Fh             ; Keep just 7 bits and define parity flag
        setp al                  ; If parity then AL=1 else AL=0
        add  [result], al
        inc  bx
        dec  cl
        jnz  NextGroupOf7

(*) The above code reads one byte before the array and reads one byte after the array. We have to make sure that these are addressable.

At the expense of increased codesize, we could split off the first and last iterations and not read outside of the array at all.


In case this should be 8086 code, you can not use the bt or setp instructions. Then next change to the 3rd snippet will work:

From:

    setp al                  ; If parity then AL=1 else AL=0
    add  [result], al
    inc  bx

to:

    jnp  .a
    inc  byte [result]
.a: inc  bx

ps. Don't use this jnp on anything better than 8086 as it will make the loop more than 10 times slower!

Upvotes: 2

Related Questions