Search code examples
assemblyx86x86-16masmmasm32

Accessing bit of a variable in assembly language


In below code. If I want to access individual bit of the variable and compare it with 1. Like cmp ax,1 and keep increment the location of bit by 1 to get next bit, so that I can calculate the number of bits set to 1 in that variable. Is that possible?

.data
var dw 1234
.code


Solution

  • It's not exactly clear, if you are targeting 16-bit or 32-bit architectures.

    The typical way to count bits one by one would be something like:

    mov cx, 1       // set mask bit
    mov bx, 0       // initialise counter to accumulate bits
    next_bit: 
    test ax, cx     // check if the current bit is set, ZF==1 if not
    jz skip_increment   // if ZF==1, do not increment
    inc bx
    skip_increment:
    add cx, cx      // shift CX left by addition (8000h + 8000h will overflow to zero)
    jnz next_bit   // if the mask did not yet overflow, continue loop
    

    This fragment already has many optimisations:

    • we don't need to calculate 1 << j,
    • nor we need to have an iteration count j
    • overflow of (0x8000 << 1) == 0, which is detected when jumping to loop beginning

    If we are willing to trash ax, we can instead shift ax right, with the benefit that we can jump off the loop immediately when there are no more bits in ax to count.

    mov cx, 0
    next_bit: test ax, 1
    jz skip_increment
    inc cx
    skip_increment:
    shr ax, 1
    jnz next_bit
    

    Can we do better? It does not look efficient that we need to jump forward to skip the increment.

    xor cx, cx   // another way to clear the counter register
    next_bit:
    shr ax, 1    // shift out the LSB in ax to carry
    adc cx, 0    // increment with carry
    test ax, ax  // now we need to re-test ax for zero
    jnz next_bit
    

    Oddly enough x86 instruction set contains jcxz - jump if cx==0, but not the opposite: jump if cx != 0. If it did, we could swap the meaning of ax and cx and jump to beginning of loop without additional test.

    But we can still rearrange the loop so, that we duplicate some code to allow the loop to process elements both from previous and current iteration.

    sub cx, cx   // another way to clear cx, but also ensure CF==0
    next_bit:
    adc cx, 0    // add the carry from previous iteration
    shr ax, 1    // move the LSB to carry, sets ZF, iff ax==0
    jnz next_bit
    adc cx, 0    // add the carry from last iteration