Bitwise operations
Bitwise operations fundamentally deal with two bits at a time. There are three such operations. Bear in mind that a bit can have only two values, 0 and 1.
These can be interpreted in terms of logic. We sometimes think of 0 as representing "false" and 1 as representing "true". In that case, OR matches the way we use the ordinary word "or" in English: a compound statement "StatementA or StatementB" is regarded as true as long as at least one of the two is true. Likewise, AND matches the way we use the ordinary word "and" in English: a compound statement "StatementA and StatementB" is regarded as true only if both of the true are true. XOR corresponds to the "either-or" construction in English, that is, "one or the other but not both".
In our assembly language, there are four formats for bitwise instructions:
The fourth format is probably used more often the others.
What can we do with bitwise operations? There are several common uses.
Suppose I have a byte in which each bit is a separate piece of information. (For instance, there are LINUX programming situations in which we have an "attribute byte".) We may want to change the value of one bit at a time.
Example: Suppose we have
BYTE DC B'01110100'
and we would like to change the third bit of BYTE (counting from the left) to 0. We can do so with NI:
NI BYTE,B'11011111'
Likewise, if we would like to change the 7th bit of BYTE to 1, we can do so with OI:
OI BYTE,B'00000010'
(End of example)
There are two common uses of XOR. One is "XOR swapping", which looks lile this:
XC WORD1(10),WORD2 XC WORD2(10),WORD1 XC WORD1(1),WORD2
which has the effect of swapping the two values. (Try working through an example.)
The other common use of XOR is "XOR encryption", which looks like this: To encrypt, we use
XC MESSAGE(8),KEY
where MESSAGE and KEY are each (arbitrarily) 8 bytes long, and to decrypt, we use the same line of code. XOR encryption is so easy to code that is often used, although it is not regarded as an especially secure system
Shifts
We ran into shifts with packed decimal numbers, but here we are dealing with the bits in registers. There are several ways to do this.
The format looks like this:
SLL R,N
Here N is between 0 and 63. This will shift all the bits in R to the left by N positions. Bits on the left end are discarded, and 0s appear on the right.
The format looks like this:
SRL R,N
Here N is between 0 and 63. This will shift all the bits in R to the right by N positions. Bits on the right end are discarded, and 0s appear on the left.
The format looks like this:
SLA R,N
Here N is between 0 and 63. This is slightly more complex, as it treats the value in R as a number and not as just a sequence of bits. The sign bit (leftmost) stays in place. All the other bits will shift to the left by N positions. Bits on the left end are discarded, and 0s appear on the right.
However, if a significant bit, that is a bit not matching the sign bit, is lost, we have a Fixed-Point Overflow exception.
This is equivalent to multiplying the value in R by 2^N. It has the advantage of only involving one register. It will set the condition code.
The format looks like this:
SLA R,N
Here N is between 0 and 63. This is slightly more complex, as it treats the value in R as a number and not as just a sequence of bits. The sign bit (leftmost) stays in place. All the other bits will shift to the right by N positions. Bits on the right end are discarded, and copies of the sign bit appear on the left.
This is equivalent to dividing the value in R by 2^N. It has the advantage of only involving one register. It will set the condition code.
These are very much like the first four kinds of shifts, but they operate on 64 bit values in an even-odd pair of registers. The register specified must be an even-numbered register.
How are the shifts encoded? The only novel feature here is the number N. It is encoded as if if is N(0,0).
Example: Suppose register 7 contains X'ABCD0123'. If we execute
SLL 7,4
then register 7 will contain X'BCD01230.
If instead we execute
SRL 7,8
then register 7 will contain X'00ABCD01'.
(End of example)
Inserting and storing individual bytes
We are familiar with Load and Store, which move 4 bytes at a time between a register and a fullword. Here we have some variations.
The format for this Is:
IC R,D(X,B)
Here D(X,B) does not have to be on a fullword boundary.
The byte at D(X,B) is copied into the 4th byte of R.
The format for this Is:
IC R,D(X,B)
Here D(X,B) does not have to be on a fullword boundary.
The 4th byte of R is copied into the byte at D(X,B).
The format for this is:
ICM R,M,D(B)
Here M is a 4-bit value usually expressed in binary, and D(B) does not have to be on a fullword boundary.
The mask is made of 0s and 1s. The 1s indicate which bytes of the register are involved in the operation. Consecutive bytes at D(B) are copied, left to right, into indicated bytes of the register.
If the mask is all 0s, nothing happens. If the mask is all 1s, we are copying all 4 bytes into the register without having to worry about a fullword.
The format for this is:
STCM R,M,D(B)
Here M is a 4-bit value usually expressed in binary, and D(B) does not have to be on a fullword boundary.
The mask is made of 0s and 1s. The 1s indicate which bytes of the register are involved in the operation. The indicated bytes of the register are copied, left to right, into consecutive bytes at D(B), a fancier version of Load.
If the mask is all 0s, nothing happens. If the mask is all 1s, we are copying all 4 bytes into D(B) without having to worry about a fullword, a fancier version of Store.
Example: Suppose register 7 contains X'EF019876' and a variable called OURDATA contains X'A0B1C2D3'.
If we execute
IC 7,OURDATA
then register 7 will contain X'EF0198A0'.
If instead we execute
ST 7,OURDATA
then OURDATA will contain X'76B1C2D3'.
If instead we execute
ICM 7,B'0110',OURDATA
then register 7 will contain X'EFA0B176'.
If instead we execute
STCM 7,B'1110',OURDATA
then OURDATA will contain X'EF0198D3'.
(End of example)
Comparing bytes
As with ICM and STCM, we are sometimes interested in just some of the bytes in a register. We can use Compare Logical Under Mask. The format for this is:
CLM R,M,D(B)
Here M and D(B) are as in ICM or STCM. As before, 1s in the mask indicate which bytes in the register are of interest. We compare indicated bytes in the register to consecutive bytes at D(B) and set the condition code as in any other comparison instruction.
Example: Suppose register 8 contains X'AA04BBDD' and a variable called YOURDATA contains X'04CC0123'.
If we execute
CLM 8,B'0100',YOURDATA
then we are comparing X'04' to X'04', and the condition code will be 0.
If instead we execute
CLM 8,B'0101',YOURDATA
then we are comparing X'04DD' to X'04CC', and the condition code will be 2.
(End of example)
Comparing bits
As mentioned above, we can use OI and NI to set the values of specific bits in a byte. Sometimes we may want to find out the value of a specific bit. We can use Test Under Mask. The format for this is:
TM D(B),I
Here the immediate byte I is treated as an 8-bit mask. The 1s in the mask indicate which bits in the byte at D(B) are of interest.
All TM does is set the condition code:
Example: Suppose a variable called BYTE contains B'11110000'.
If we execute
TM BYTE,B'00011000'
then the condition code will be 1.
If instead we execute
TM BYTE,B'11000000'
then the condition code will be 3.
(End of Example)
If I wanted to find out about the 4th bit of a byte, I could use a mask of B'00010000'.