Re: Mouse?!
From: Beth (BethStone21_at_hotmail.NOSPICEDHAM.com)
Date: 11/20/04
- Next message: Alex R: "Re: [OT] Paging Frank K."
- Previous message: arargh411NOSPAM_at_NOW.AT.arargh.com: "Re: You want MASM? You got it."
- In reply to: PurpleMonkeyDW: "Re: Mouse?!"
- Next in thread: Annie: "Re: Mouse?!"
- Reply: Annie: "Re: Mouse?!"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Sat, 20 Nov 2004 02:49:36 GMT
PurpleMonkeyDW wrote:
> Right, these answers are a bit to complex. I've never used test and it
> doesn't need to be that accurate with a number of mouse clicks, int 33, 5
> also works so If anyone knows a way to do it simular to this it would be
> helpfull(just so I can understand it and learn from it).
Ah, it's simple, really...what Herbert is trying to point out is two
things...first, the "TEST" instruction can test individual bits in a single
instruction...second, the buttons are actually "bit flag"...that is, bit 0
is set if the left button is clicked, bit 1 if the right button is
clicked...if you look for just "01h" then this only looks for "left button
is pressed and right button is NOT pressed"...if, on the other hand, you
"TEST" bit 0 then it'll pick up both "01h" (left only pressed) and "03h"
(left and right pressed)...and even "05h" and "07h" if you've got three
mouse buttons (left and middle, left and middle and right respectively
;)...
The way it works is like this, if we look at it in binary:
bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
X X X---- left button
| +------- right button
+---------- middle button
Each bit is binary, so it can only be zero (not pressed) or one
(pressed)...if we put these into a table, we get:
value left right middle
00h -- -- --
01h XX -- --
02h -- XX --
03h XX XX --
04h -- -- XX
05h XX -- XX
06h -- XX XX
07h XX XX XX
Where "XX" means "pressed" and "--" means "not pressed"...
So, if you were interested in "left button only", you look for 01h...BUT,
if you wanted "left button (don't care what whether the other buttons are
or aren't pressed)" then 01h, 03h, 05h and 07h all have the left button
pressed...
The easiest way, though, to deal with this is to simply see if a particular
bit is clear or set rather than doing "cmp ax, 01h", "cmp ax, 03h", "cmp
ax, 05h" and "cmp ax, 07h" all in a row...the "TEST" instruction does this
in a single instruction...
The "TEST" instructions is, in fact, the same as the "AND" instruction
except that it only alters the FLAGS register and does not save the result
of the "AND" operation...otherwise, the two instructions are the same...
Now, the "AND" instruction is simple "boolean" binary logic...each bit in
the source and destination are paired with each other and the simple
boolean logic is applied: "are both bits set? If yes, then the result is
set...if not, then the result is clear"...it looks like this in a table:
| 0 1
---+---------
0 | 0 0
1 | 0 1
Being symmetrical, it doesn't actually matter whether the "source" or
"destination" is either the row or column (if you look, the answer ends up
the same regardless because it's a "symmetrical" table down the diagonal
:)...
Anyway, this operation is done with each pair of bits in the "source" and
"destination" of "AND"...bit 0 of the source is "AND'd" with bit 0 of the
destination...bit 1 with bit 1...and so forth, through all the bits...the
result of the "AND" is _stored_ into the "destination" using the "AND"
instruction...the sole difference between "AND" and "TEST" is that "TEST"
does the operation, sets the flags but DOES NOT store the result into the
destination (just does the comparison but does not save the result)...while
the "AND" instruction is the same except that it _DOES_ take the result and
store it in the destination...
What's useful about this "AND" operation is that if you supply a constant
to "AND" against a register, this constant can be used to "mask out" only
the bits you're interested in...
For example, if I was to "AND" a value against a "mask" constant of all
ones, then the result ends up unchanged:
AX 0101101001011010
constant 1111111111111111
result: 0101101001011010
If I "AND" it against all zeroes, then the answer is _always_ zero, by
definition:
AX 0101101001011010
constant 0000000000000000
result: 0000000000000000
Now, where "AND" gets useful is in "masking" out particular bits...if I
choose a constant where all the bits in the high byte are one and all the
bits in the low byte are zero, then I can "mask out" the lower byte:
AX 0101101001011010
constant 1111111100000000
result: 0101101000000000
And this result is where "AND" (and "TEST", which does the same operation
as "AND", just that it doesn't store the result in the destination...merely
sets FLAGS according to the result but then throws the result away and
doesn't store it anyway) comes in useful...if you're only interested in bit
0 (the left button) then you simply "AND" (or "TEST") by the constant 1:
AX 0000000000000111 (left, middle and right are all pressed)
constant 0000000000000001 (we're "masking" out only bit 0)
result: 0000000000000001 (the answer is 1; the left button is pressed)
So, using "AND", we can use this instruction to do a comparison against any
particular bits we're interested in (set them to one) or ignore other bits
we're not interested in (set them to zero)...where we've set the constant
bits to zero, the answer is always zero by definition...where we've set the
constant bits to one, the answer is "passed through" (that is, if bit 0 is
set in AX then it's set in the result...if bit 0 is clear in AX then it's
clear in the result)...this gives us the capability to "mask out" those
bits we don't care about...to use "AND" (or "TEST") to just "ignore" those
bits which aren't interesting to us...
The values in the "button status" returned by the mouse calls are designed
so that you can use "AND" or "TEST" on them easily...you can create a
constant which "masks out" any buttons but the buttons you're interested in
and then simply "JZ" or "JNZ" according to the result (if those buttons are
pressed, then the answer is going to be non-zero, otherwise zero :)...
"TEST" is probably more useful than "AND", simply because "AND" would
actually change the register you're testing...well, if you wanted to make
more than one test, then this wouldn't be useful, as you'd change the
"button status" so further "AND" comparisons wouldn't work...
Hence, as Herbert suggested, use the "TEST" instruction...this way you
can - all in one instruction - "mask out" the bits you're interested in and
perform an "AND"...But the result itself is discarded...instead, the FLAGS
register is set according to that result (for example, if the result was
zero, then the "zero flag" ZF would be set...if non-zero, then this flag
would be clear :)...
In a nutshell, the "formula" is:
TEST <register>, <constant>
JNZ <address to jump if specific bit is set>
Where "constant" is a constant number whose binary value has the bits
you're interested in set and all the bits you're not interested in clear...
In this specific example, this means "1" for the left button, "2" for the
right button and "4" for the middle button (note: powers of two...put the
numbers into a calculator and switch to "binary" display of the numbers to
see that only one bit - bit 0, bit 1 or bit 2 - is set in each example
while all the rest of the bits are zero :)...
Using "TEST" like this, you can check for a specific button _irrespective_
of whether the other buttons are or aren't pressed...
Example:
------------------------------------
mov ax, 0003h
int 33h
test ax, 01h
jnz LeftButtonPressed ; irrespective of the other buttons
test ax, 02h
jnz RightButtonPressed ; irrespective of the other buttons
test ax, 03h
jz LeftAndRightButtonsNotPressed
------------------------------------
And that kind of thing...blah-blah-blah...
Herbert was just trying to point out that this method is the most efficient
way...also, it accounts for "left button (don't care what the other buttons
are)" in a single test...while, using "cmp", if you wanted the same thing,
you'd need to "cmp" against 01h, 03h, 05h and 07h to cover all possible
"left button pressed", ignoring the status of the other buttons...that's
four "cmp" instructions, so Herbert is suggesting that "TEST" would be more
useful for this because it can do this all in one instruction instead...
Using little "binary logic" tricks like this is one of the ways good
assembly coders (like Herbert...he knows what he's talking about, even if
he doesn't always express it well...English is not his first language,
though...so, all things considered, he does a better job than I would in
return ;) manage to do things more efficiently...in a HLL, you might be
used to a series of "IF" statements and then, in assembly, think of "CMP"
as being a bit like "assembly's IF" and code it in much the same way...this
will work, of course, but it isn't the most efficient use of the
machine...the machine thinks in "binary" so you can often use "binary
tricks" with "AND", "OR" or just "shifting" bits up and down in registers
to do things in a machine-friendly way which it can process more
efficiently...
Also, Herbert noted that you're checking for a 1 bit and then shifting the
value...there isn't any need for this with "TEST" because you can simply
directly test the second or third bit (in fact, any combination of bits you
like ;) in just the one instruction...
Does any of this now make sense or have I just confused the issue even
more? ;)
Beth :)
- Next message: Alex R: "Re: [OT] Paging Frank K."
- Previous message: arargh411NOSPAM_at_NOW.AT.arargh.com: "Re: You want MASM? You got it."
- In reply to: PurpleMonkeyDW: "Re: Mouse?!"
- Next in thread: Annie: "Re: Mouse?!"
- Reply: Annie: "Re: Mouse?!"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]