3

I want to check that bits 0, 3, 6, 7 of AL are all 1 or not with TEST instruction.

I know I can check bit 0 with this code:

TEST AL,1

But how can I check bits 0, 3, 6, 7 together?

Fifoernik
  • 9,779
  • 1
  • 21
  • 27
hanie
  • 318
  • 5
  • 19
  • 3
    If you need to know if _all_ of those bits are set you need `AND` followed by `CMP`. `TEST` could be used if you want to check if _at least one_ of those bits are set. – Michael Nov 29 '17 at 16:09
  • 3
    @Michael: You can also `not al` / `test al, xxx` / `jz` to check that all those bits are zero after you invert them. (Saves a byte of code-size for registers other than `al`, because `not` doesn't need an immediate). – Peter Cordes Nov 29 '17 at 17:50

1 Answers1

2

Your title is asking the opposite question from the body. For a single bit, it's nearly identical (test al,1<<bit_position / jz or jnz). But they're really different problems when you need to test multiple bits.

To check for all zero, it's still easy, because test sets ZF when the result of the AND operation is 0. So mask the bits you want to look at, and check ZF. Remember that you can jz or jnz, or setz/nz al, or cmovz/nz ecx, edx.

test  al, mask
jz   all_bits_selected_by_mask_were_zero
;; fall through path: at least one non-zero bit

To check for all one, the usual idiom is x & mask == mask. This isn't ideal in asm, especially with an immediate constant, because you need the constant twice for and and cmp. The other way is to invert all the bits and then check for zero using the previous way. (This saves code bytes for registers other than AL, but there are special 2-byte encodings for most instructions with op al, imm8)

not   al
test  al, mask
jz   all_bits_selected_by_mask_were_one
;; fall through path: at least one unset bit


;;; or if mask has only a single bit set:
test  al, single_bit_mask
jnz  the_bit_was_set

Another optimization: if you need to test some bits that are all in the 2nd byte of EAX, it saves code bytes to read ah, but this adds a cycle of latency on Haswell/Skylake.
e.g. test eax, 3<<8 vs. test ah, 3.

For a single bit, bt eax, 11 tests bit 11 (setting/clearing CF, so you can do fun stuff like adc edx, 0 if you want to take that bit and add it somewhere else.) But bt can't macro-fuse with JCC, so test eax, imm32 is more efficient.


In either case, the mask is 1<<0 | 1<<3 | 1<<6 | 1<<7 for your case. The number represented by a set bit in given position is 1<<n, and assembler constants are specified in terms of numbers.

Of course, you can write constants in binary (using a b suffix in NASM for example), so you can do test al, 11001001b.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847