News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

TEST vs BT

Started by terb, July 15, 2008, 07:57:34 PM

Previous topic - Next topic

terb

Hey Gang

I was going through the Intel opcode reference when I came across the BT/BTS/BTC/BTR opcodes. I never was aware of these opcodes and I reading the reference just made me more confused !!

If TEST also perform test on the bits, what do we need BT for ? Could some one please explain the difference ?

Thanks in advance

jj2007

TEST is more powerful because you can test for a bit pattern. On the other hand, btc can come in handy: I have a key that toggles the state of a variable that defines a toolbar zone width. It can vary between 0 and 127 bytes. However, the user can temporarily expand by 128:

      btc tbw, 7      ; toggle Alt WideToolbar

If the toolbar is narrow, 128 are added. If it is wide, 128 are subtracted.
You can code the same with jumps or test but it looks less elegant ;-)

Later: Since btc tbw, 7 costs a staggering 8 bytes, I decided to have a closer look at the alternative:


      .if tbw>127
         sub tbw, 128
      .else
         add tbw, 128
      .endif

Hmmmm... 31 bytes. Maybe it's faster, but I am handling a keystroke here, hardly a speed critical task, right?


Address    Hex dump                   Command                               Comments
0040196C   ³. 833D 60C64000 7F        cmp dword ptr [ReTest.40C660], 7F
00401973   ³? 76 0C                   jbe short ReTest.00401981
00401975   ³? 812D 60C64000 80000000  sub dword ptr [ReTest.40C660], 80     ; ³
0040197F   ³? EB 0A                   jmp short ReTest.0040198B
00401981   ³? 8105 60C64000 80000000  add dword ptr [ReTest.40C660], 80


Tedd

terb, you're quite right - BT is pretty redundant, since you can do the same with TEST.
Although, BT alters the carry flag, whereas TEST alters the zero/equal flag - there are a few tricks you can do with adc and sbb.
And BT is a slightly shorter encoding.
The other three are useful as atomic operations.
But yes, there are quite a few instructions that could be cut without much trouble, but they're kept for compatibility.
No snowflake in an avalanche feels responsible.

japheth


BTx is not restricted to 32 bits as it is the case with TEST - you can easily access memory bit-fields of any size with one instruction. So IMO one cannot say that BTx is redundant.

Rainstorm

tedd wrote. . .
Quotethere are a few tricks you can do with adc and sbb.
What are some of the ways adc & sbb can be used .

Thanks

BogdanOntanu

Quote from: japheth on July 17, 2008, 12:01:56 PM

BTx is not restricted to 32 bits as it is the case with TEST - you can easily access memory bit-fields of any size with one instruction.

Yes, but only for a maximum of 256 bits AFAIK

Quote
So IMO one cannot say that BTx is redundant.

They belong to a kind of instruction sets that apparently did try to make the life of ASM programmer more easy. At a certain time Intel did choose this kind of actions. Today this path is not taken anymore. The only thing that is considered are compilers.

Unfortunately from Intel's late point of view programmers should NOT use ASM for big programs.
Of course I disagree but who am I ?

On redundant instructions:

From the electronics point of view they do not matter much. It does matter for the compilers and humans's understanding and it does attack the "sense of abstract perfection" of some people (not myself) but the hardware does not care much. They could be just some left un-decoded wires that got promoted as a feature.

Also to so much claimed "simplification of instruction set" is also kind of irrelevant at silicon level. Some apparent complications for the human eye are in fact much more easy to decode at hardware level and are much better as they are than what people do suggest.

The CPU it is what it is... learn it and live with it until you are able to understand it's inner workings to such an depth that you become capable to make your own ;) (at that point you will see things differently than common sense)

I would choose TEST any time but sometimes BT/BTC/BTR/BTS can be kind of useful.

Ambition is a lame excuse for the ones not brave enough to be lazy.
http://www.oby.ro

bozo

QuoteWhat are some of the ways adc & sbb can be used .

well, i'm sure you could probably write some complex logical conditional tests using these instructions..for example in C

if (a != 0)
 a = b;
else
 a = c;


in assembly code, you could use:

a = eax, b = ebx, c = ecx

cmp     eax, 1
sbb     eax, eax
and     ecx, eax
xor     eax, -1
and     eax, ebx
or      eax, ecx



another example:

a = a > b ? b : a;

sub     ebx, eax
sbb     ecx, ecx
and     ecx, ebx
add     eax, ecx


not sure if that answers your question..



japheth

Quote from: BogdanOntanu on July 17, 2008, 05:32:09 PM
Quote from: japheth on July 17, 2008, 12:01:56 PM

BTx is not restricted to 32 bits as it is the case with TEST - you can easily access memory bit-fields of any size with one instruction.

Yes, but only for a maximum of 256 bits AFAIK

I wouldn't call 256 "any size". IIRC I once successfully tested it up to 65536 bits - of course this is still not "any size", but it's "more any"  :toothy. The docs are telling up to 2^32 bits can be accessed, however, the second operand is interpreted as a signed value!

BogdanOntanu

Japeth,

Yes you are right, "mea culpa".

With a register operand like in:

BT [esi],ecx


You could indeed test a bit range of -2^31 upto +2^31 - 1  if ECX is loaded with the correct bit offset value.

And this feature is a huge advantage when compared with a simple TEST.

However the user should  be aware that with an immediate (number) operand you can only access 15/31/63 bit offsets even if the instruction encoding allows for a 256 bit offset value.
Ambition is a lame excuse for the ones not brave enough to be lazy.
http://www.oby.ro

Rainstorm

kernel, thx for the examples

jj2007

For inspiration.

AnyFlag   equ 55   ; give a name to your flag variable

   fset AnyFlag

   .if fget(AnyFlag)
      invoke MessageBox, NULL, chr$("AnyFlag is set"), addr AppName, MB_OK
   .else
      invoke MessageBox, NULL, chr$("AnyFlag is NOT set"), addr AppName, MB_OK
   .endif

fset and fget cost 7 bytes each.

EDIT: fclr, ftoggle, flgmov added.

include \masm32\include\masm32rt.inc

fset MACRO num:REQ
push num
call fsetP
ENDM

fclr MACRO num:REQ
push num
call fclrP
ENDM

flgmov MACRO num:REQ, src:REQ
.if src
fset num
.else
fclr num
.endif
ENDM

ftoggle MACRO num:REQ
push num
call ftoggleP
ENDM

fget MACRO num:REQ
push num
call fgetP
EXITM <eax>
ENDM

tmpflag equ 0
f1 equ 1
f2 equ 2
f3 equ 3
AnyFlag equ 55
f79 equ 79

.data?
MyFlags dt ?

.code
AppName db "Flags:", 0

fsetP proc
push edx
mov edx, offset MyFlags
mov eax, [esp+8]
bts [edx], eax
pop edx
ret 4
fsetP endp

fclrP proc
push edx
mov edx, offset MyFlags
mov eax, [esp+8]
btr [edx], eax
pop edx
ret 4
fclrP endp

ftoggleP proc
push edx
mov edx, offset MyFlags
mov eax, [esp+8]
btr [edx], eax
pop edx
ret 4
ftoggleP endp

fgetP proc
push edx
mov edx, offset MyFlags
mov eax, [esp+8]
bt [edx], eax
setc al
pop edx
ret 4
fgetP endp

start: fset f1 ; set flag
fset f3 ; set flag
fset f79 ; set flag
fclr f79 ; clear flag
fset AnyFlag ; set flag
ftoggle AnyFlag ; toggle flag
flgmov tmpflag, 123 ; set flag if src is non-zero

.if fget(f1)
invoke MessageBox, NULL, chr$("f1 is set"), addr AppName, MB_OK
.else
invoke MessageBox, NULL, chr$("f1 is NOT set"), addr AppName, MB_OK
.endif

.if fget(f2)
invoke MessageBox, NULL, chr$("f2 is set"), addr AppName, MB_OK
.else
invoke MessageBox, NULL, chr$("f2 is NOT set"), addr AppName, MB_OK
.endif

.if fget(f3)
invoke MessageBox, NULL, chr$("f3 is set"), addr AppName, MB_OK
.else
invoke MessageBox, NULL, chr$("f3 is NOT set"), addr AppName, MB_OK
.endif

.if fget(AnyFlag)
invoke MessageBox, NULL, chr$("AnyFlag is set"), addr AppName, MB_OK
.else
invoke MessageBox, NULL, chr$("AnyFlag is NOT set"), addr AppName, MB_OK
.endif

.if fget(f79)
invoke MessageBox, NULL, chr$("f79 is set"), addr AppName, MB_OK
.else
invoke MessageBox, NULL, chr$("f79 is NOT set"), addr AppName, MB_OK
.endif

.if fget(tmpflag)
invoke MessageBox, NULL, chr$("tmpflag is set"), addr AppName, MB_OK
.else
invoke MessageBox, NULL, chr$("tmpflag is NOT set"), addr AppName, MB_OK
.endif

mov eax, 123
flgmov tmpflag, eax ; set the flag if src is non-zero

.if fget(tmpflag)
invoke MessageBox, NULL, chr$("tmpflag is set"), addr AppName, MB_OK
.else
invoke MessageBox, NULL, chr$("tmpflag is NOT set"), addr AppName, MB_OK
.endif

mov eax, 0
flgmov tmpflag, eax ; set the flag if src is non-zero

.if fget(tmpflag)
invoke MessageBox, NULL, chr$("tmpflag is set"), addr AppName, MB_OK
.else
invoke MessageBox, NULL, chr$("tmpflag is NOT set"), addr AppName, MB_OK
.endif

exit

end start