Just like shifting, there are other opertions on bits such as and, or, xor and not. With those operations, one can manipulate each bit of a data.
| Operation | Usage | Description |
|---|---|---|
and |
and eax, edx |
Each i-th bit in eax and edx will be compared and the i-th bit of eax will be 1 iff both compared bits are 1 |
or |
or eax, edx |
Each i-th bit in eax and edx will be compared and the i-th bit of eax will be 1 if at least one of the compared bits is 1 |
xor |
xor eax, edx |
Each i-th bit in eax and edx will be compared and the i-th bit of eax will be 1 iff one of the compared bits is 1 and the other 0 |
not |
not eax |
Inverts each bit in eax |
test |
test eax, edx |
Same as and eax, edx, changes the FLAGS only, but not the registers |
signchanger.asm
segment .text
global changeSign
changeSign:
mov eax, [esp + 4] ; get argument
NOT eax ; invert arg
INC eax ; eax++
ret
This code multiplies any number with -1 much faster than a multiplication operation by just using a bit operation and an increment. So the function changes signs of any number in two's complement.
signChanger-alt.asm
segment .text
global changeSign
changeSign:
mov eax, [esp + 4] ; get argument
NEG eax ; negate
ret
This is the recommended and actually used way to change the sign. The first example was only to show an example for logical operations.
NOTE: All functions in this section do not enter and leave the function. That is not necessary as only EAX is changed and EAX, ECX and EDX may be changed freely anyways.
Sometimes it might be necessary to change a specific bit of a value. So let us assume our value is stored in eax.
| Operation | Code |
|---|---|
| Turn on bit i | OR eax, n where n = 2^i |
| Turn off bit i | AND eax, n where n = NOT (2^i) |
| Invert bit i | XOR eax, n where n = 2^i |
| Clear register | XOR eax, eax |
NOTE: It is recommended to use XOR eax, eax or sub eax, eax for clearing the eax register (or any other) instead of mov eax, 0 as those instructions do not consume much. "Furthermore, they do not consume an issue port or an executon unit. So using zero idioms are preferable than moving 0’s into the register"(Intel® 64 and IA-32 Architectures Optimization Reference Manual, section 3.5.1.8).
Let us take the following function size_t getAbsolute(uint32_t value) which returns the absolute value of the entered value as an example.
absolute.asm
segment .text
global getAbsolute
getAbsolute:
mov eax, [esp + 4] ; get argument
TEST eax, 0x80000000
JZ return
NEG eax
return:
ret
NOTE: The TEST eax, 0x80000000 instruction does the same as AND eax, 0x80000000 while only changing the flags, but not the registers. This way, it is possible to AND eax,0x80000000 (which keeps only the sign bit turned on) and later check if the result is zero (positive) or not.