ANSI Standard PL/B Language and Visual PL/B
Binary Logic
When someone mentions "binary logic" or "bit masks" or even "hex notation", most programmers will groan. Nobody but the hard core hackers and systems types deal with that stuff any more! It's for the "C" jockies and the folks in Redmond.
In the 1960's and 1970's it was different. Everyone could deal with binary code. Programmers suffered with 200 page printed memory dumps in HEX on a daily basis. Many could do hex and binary arithmetic as easily as they could decimal.
(If you are of a certain age you may well remember the infamous and invaluable "IBM Green Card". An 8½x22 inch card folded into 12 pages to fit the shirt pocket. It had tables for everything you might need to live in the decimal, hex, binary, EBCDIC, and punch card world)
Today's modern languages and visual programming tools have eliminated most of the need to deal with binary or hex data. But it's still there at the core of everything we do and there are times when it's really useful.
So why is it used at all. Mainly because it's the native tongue of the computer. Everything under the covers is done at the binary level. For systems programmers, "bit flags" provide a super compact, fast and efficient way to control data. For them the "bit mask" is often the tool of choice.
The BIT MASK
For the purposes of this discussion we're going to use the PL/B
GETFILE instruction and the
FILETYPE keyword. This is a version 9.x feature of the language.
OPEN ISIFILE, "filename"
GETFILE ISIFILE, FILEFORMAT={nvar}
The GETFILE instruction will return a single 8-bit byte. Each bit in that byte has a meaning. The following table shows the bits and their representation as DECIMAL, HEX, and BINARY numbers:
dec hex binary Meaning
1 0x1 0000 0001 IFILE
2 0x2 0000 0010 AFILE
4 0x4 0000 0100 -----
8 0x8 0000 1000 -----
16 0x10 0001 0000 version 9.0x format
32 0x20 0010 0000 version 8.7x format
64 0x40 0100 0000 the ISI file supports file larger than 4BG.
128 0x80 1000 0000 -----
As an example let's use an IFILE indexed under the version 8.7x runtime. The bit mask for that would be:
33 0x21 0010 0001 IFILE and Version 8.7x
The PL/B language does a lot for the programmer by converting values to match the coding the programmer is using. In the case of the FILEFORMAT, the return variable needs to be a {nvar} or "numeric variable".
In this case the {nvar} can be a FORM field or an INTEGER. PL/B will convert the one byte bitmask to the type of variable you specifiy. A FORM field needs to be 3 bytes wide to accept a decimal number from 0 to 255. An Integer can be one byte because it will be a binary number of 8 bits (0 to 255).
TESTING THE BIT MASK
OK, we have the fileformat byte now. What can we do with it.
First, if you're looking for a very specific configuration (AFILE indexed with 9.0 and supporting large files) you could figure out the decimal equivalent of the bits and test that value:
82 0x52 0101 0010 IFILE and Version 8.7x
You can now simply say
IF (FILETYPE = 82). This will work if the you're using an INTEGER or a FORM variable.
Of course most of the time you'll want to be more specific. Say you just want to know if the file is an IFILE or not. Or you know it's an IFILE and you just need to know how it was indexed.
Testing single bits (or combinations of bits) can be done with binary logic using the binary AND instruction.
This is the part everyone hates because it gives them a headache to think about it, but it's really pretty easy.
The AND instruction works at the bit level. It takes two bytes, or two equal length strings of bytes, and logically adds the bits together without a carry. The "truth table is":
0 + 0 = 0
0 + 1 = 0
1 + 0 = 0
1 + 1 = 1
The AND instruction returns the results via the ZERO and EOS flags.
If either string is nul the EOS is set.
If NO bits produce a 1 the ZERO flag is set.
So to test a pair of bytes you can say
AND TEST_MASK, FILETYPE
IF NOT ZERO
.... test was true, the bit you tested is SET
ENDIF
To be more practical let's use an example. We will test an ISI file to see which version it was indexed with. The only bits we want to test are:
dec hex binary Meaning
1 0x1 0000 0001 IFILE
2 0x2 0000 0010 AFILE
We start by doing the GETFILE. We could use a FORM variable or an INTEGER variable. If we use the FORM we'll need a 3 byte number. We'll then need to convert that to a one byte integer, or a one byte string. (The AND will work against either INTEGER or DIM variables). By using an integer we'll get a one byte binary number immediately.
NWK03 FORM 3
INT_WORK INTEGER 1
IFILE_MASK INIT 0x1
AFIlE_MASK INIT 0x2
GETFILE ISIFILE, FILEFORMAT=NWK03 ;Example 1 using FORM variable
MOVE NWK03, INT_WORK ;Convert 3 bytes number to 1 byte integer
AND IFILE_MASK, INT_WORK ;Do the AND
IF ZERO ;Test zero flag
DISPLAY "bit was NOT set"
ELSE
DISPLAY "bit IS set"
ENDIF
GETFILE ISIFILE, FILEFORMAT=INT_WORK ;Example 1 using INTEGER variable
AND IFILE_MASK, INT_WORK ;Do the AND
IF ZERO ;Test zero flag
DISPLAY "bit was NOT set"
ELSE
DISPLAY "bit IS set"
ENDIF
NOTE: the AND instruction is destructive. It combines the bits into the second operand. You'll need to refresh that second operand if you do more than one AND or if you intend to use the second operand for anything else.
The language provides a TEST instruction that does the AND but is non-destructive. Both operands are left unchanged.
VARIATIONS
There are a number of ways to specify the mask to be used with the AND. It can be an EQUATE, or a literal, or a variable. For example all of the following work:
INTMASK INTEGER 1 ;No value... you have to move something
BYTE_1 INIT 0x1
B_EQU_1 EQU 0b00000001
B_DEF_1 DEFINE 1
AND 0b00000001, INT_WORK
AND 0x1, INT_WORK
AND B_EQU_1, INT_WORK
AND B_DEF_1, INT_WORK
CALC INTMASK = (1)
MOVE "1", INTMASK
AND INTMASK, INT_WORK
COMBINING ATTRIBUTERS
The PL/B language has the ability to combine bit masks using most of the definition types shown above. This can be interesting and useful. It's best used when you have defined your bit masks by name and want to combine several. For example:
MASK_IFILE EQU 0x1
MASK_AFILE EQU 0x2
MASK_VER_9 EQU 0x16
MASK_VER_87 EQU 0x32
MASK_LARGE EQU 0x64
INTMASK INTEGER 1 ;No value... you have to move something
CALC INTMASK = (MASK_IFILE + MASK_VER_9)
AND INTMASK, FILETYPE
IF NOT ZERO
DISPLAY "ISI file AND/OR version 9"
ENDIF
In this example notes that the
AND instruction will set the zero flag only if NONE of the bits match. If ANY of them match you get the not zero.
If you want a combined flag to test for ALL bits set use one of the other logical operations, OR, XOR, NOT AND, etc. You'll have to decide which si the best to use.
PLBEQU.INC SUNBELT DEFINED ATTRIBUTES
Sunbelt pre-defines many of the bit strings in the PLBEQU.INC include unit. These will show up in the language reference. This makes it possible to use the names without worrying about the specific bit configuration. We recommend looking at the definitions and understanding exactly what they are.
OPINION
Most of the Sunbelt documentation, and that for other systems too, tend to use the hex notation for bit flags. We've used that through this discussion. But we've also shown the binary representation in many cases.
In our opinion, a programmer would be well served by using the binary constructions. We work in what's called a "visual" programming environment. HEX notation is not visual. BINARY is.
Our preference for defining the FILETYPE bits would be this:
0 0 0 0 0 0 0 0
| | | | | | | |-- IFILE
| | | | | | |----- AFILE
| | | | | |--------
| | | | |-----------
| | | |-------------------- version 9.0x
| | |----------------------- version 8.7x
| |-------------------------- large file support
|-----------------------------
OR
0 0 0 0 0 0 0 1 IFILE
0 0 0 0 0 0 1 0 AFILE
0 0 0 1 0 0 0 0 version 9.0x
0 0 1 0 0 0 0 0 version 8.7x
0 1 0 0 0 0 0 0 large file support
AND 0b00000001, FILETYPE ;Test for IFILE
AND 0b00000010, FILETYPE ;Test for AFILE
AND 0b00010000, FILETYPE ;Test for version 9.0x
AND 0b00100000, FILETYPE ;Test for version 8.7x
v1.10