The pymdl book
21. Machine words and bits
The MDL facility for dealing with uninterpreted machine words and bits involves
two data TYPEs: WORD and BITS. A WORD is simply an uninterpreted machine
word, while a BITS is a "pointer" to a set of bits within a WORD. Operating
on WORDs is usually done only when compiled programs are used (chapter 33).
pymdl's own. This is the chapter where MDL stops being machine-independent, and it is therefore the one a reimplementation is most tempted to fudge. pymdl does not: a
WORDhere is 36 bits, the octal it prints is the era's twelve digits, and aBITSis a real PDP-10 byte pointer with the position and size in the fields the hardware used. It has to be, because chapter 36 runs actual PDP-10 instructions on these same words, and chapter 35's assembler builds them. A program that reaches into this chapter is asking about the machine, and the answer it gets here is the machine's.
18.1. WORDs
A WORD in MDL is a PDP-10 machine word of 36 bits. A WORD always PRINTs in
"# format", and its contents are always printed in octal (hence preceded and
followed by *). Examples:
#WORD 0 ⇒ #WORD *000000000000* ;"all 0s"
#WORD *2000* ⇒ #WORD *000000002000* ;"one bit 1"
#WORD *525252525252* ⇒ #WORD *525252525252* ;"every other bit 1"
Twelve octal digits is exactly 36 bits, which is why that is the width shown.
WORD is its own PRIMTYPE; it is also the PRIMTYPE of FIX, FLOAT,
CHARACTER, and any other TYPE which can fit its data into one machine word.
<TYPEPRIM WORD> ⇒ WORD
<TYPEPRIM FIX> ⇒ WORD
<TYPEPRIM FLOAT> ⇒ WORD
<TYPEPRIM CHARACTER> ⇒ WORD
<TYPEPRIM BITS> ⇒ WORD
A WORD cannot be an argument to +, -, or indeed any SUBRs except for
CHTYPE, GETBITS, PUTBITS and several bit-manipulating functions, all to be
described below. Thus any arithmetic bit manipulation must be done by CHTYPEing
a WORD to FIX, doing the arithmetic, and then CHTYPEing back to WORD.
However, bit manipulation can be done without CHTYPEing the thing to be played
with to a WORD, so long as it is of PRIMTYPE WORD; the result of the
manipulation will be of the same TYPE as the original object or can be
CHTYPEd to it.
<+ #WORD 1 1> ⇒ *ERROR* ARG-WRONG-TYPE
<+ <CHTYPE #WORD 1 FIX> 1> ⇒ 2
<ANDB 12 10> ⇒ #WORD *000000000010*
The second and third lines are the two halves of that paragraph: arithmetic needs
a CHTYPE in, and bit work does not need one at all, because a FIX is already
PRIMTYPE WORD. The bit SUBRs take the FIXes 12 and 10 happily and
answer a WORD.
18.2. BITS
An object of TYPE BITS is of PRIMTYPE WORD, and PRINTs just like a
WORD. The internal form of a BITS is precisely that of a PDP-10 "byte
pointer", which is, in fact, just what a BITS is.
For purposes of explaining what a BITS is, assume that the bits in a WORD are
numbered from right to left, with the rightmost bit numbered 0 and the
leftmost numbered 35, as in
35 34 33 ... 2 1 0
(This is not the "standard" ordering: the "standard" one goes from left to right.)
A BITS is most conveniently created via the SUBR BITS:
<BITS width:fix right-edge:fix>
returns a BITS which "points to" a set of bits width wide, with rightmost bit
right-edge. Both arguments must be of TYPE FIX, and the second is optional,
0 by default.
Examples: the indicated application of BITS returns an object of TYPE BITS
which points to the indicated set of bits in a WORD:
| Example | Bits pointed to |
|---|---|
<BITS 7> |
35 ... 7 6 ... 0 |
<BITS 4 18> |
35 ... 22 21 20 19 18 17 ... 0 |
<BITS 36> |
35 ... 0 |
And here is what those three actually are:
<TYPE <BITS 7>> ⇒ BITS
<BITS 7> ⇒ #BITS *000700000000*
<BITS 4 18> ⇒ #BITS *220400000000*
<BITS 36> ⇒ #BITS *004400000000*
"Precisely that of a PDP-10 byte pointer" is worth decoding once, since the
manual states it and shows nothing. Reading the octal in the hardware's fields:
the leftmost two digits are the position (the right-edge, in bits) and the
next two are the size (the width). So <BITS 7> is position 00, size 07;
<BITS 4 18> is position 22 octal, which is 18 decimal, size 04; and
<BITS 36> is position 00, size 44 octal, which is 36 decimal. Those are
the same fields a LDB or DPB instruction reads in chapter 36.
18.3. GETBITS
<GETBITS from:primtype-word bits>
where from is an object of PRIMTYPE WORD, returns a new object whose
TYPE is WORD. This object is constructed in the following way: the set of
bits in from pointed to by bits is copied into the new object, right-adjusted,
that is, lined up against the right end (bit number 0) of the new object. All
those bits of the new object which are not copied are set to zero. In other
words, GETBITS takes bits from an arbitrary place in from and puts them at the
right of a new object. The from argument to GETBITS is not affected.
Examples:
<GETBITS #WORD *777777777777* <BITS 3>> ⇒ #WORD *000000000007*
<GETBITS *012345670123* <BITS 6 18>> ⇒ #WORD *000000000045*
Note the second argument's form in the second line: *012345670123* with no
#WORD in front of it is read directly as a FIX in octal (section 2.6.2), and
GETBITS is content because a FIX is PRIMTYPE WORD.
18.4. PUTBITS
<PUTBITS to:primtype-word bits from:primtype-word>
where to and from are of PRIMTYPE WORD, returns a copy of to, modified as
follows: the set of bits in to which are pointed to by bits are replaced by the
appropriate number of rightmost bits copied from from (optional, 0 by default).
In other words: PUTBITS takes bits from the right of from and stuffs them into
an arbitrary position in a copy of to. None of the arguments to PUTBITS is
affected.
Examples:
<PUTBITS #WORD *777777777777* <BITS 6 3>> ⇒ #WORD *777777777007*
<PUTBITS #WORD *666777000111* <BITS 5 15> #WORD *123*> ⇒ #WORD *666776300111*
<PUTBITS #WORD *765432107654* <BITS 18>> ⇒ #WORD *765432000000*
The first and last show the default third argument doing its work: with no from,
zeros are stuffed in, so PUTBITS with two arguments is how you clear a
field.
18.5. Bitwise Boolean Operations
Each of the SUBRs ANDB, ORB, XORB, and EQVB takes arguments of
PRIMTYPE WORD and returns a WORD which is the bitwise Boolean "and",
inclusive "or", exclusive "or", or "equivalence" (inverse of exclusive "or"),
respectively, of its arguments. Each takes any number of arguments. If no
argument is given, a WORD with all bits off (ORB and XORB) or on (ANDB
and EQVB) is returned. If only one argument is given, it is returned unchanged
but CHTYPEd to a WORD. If more than two arguments are given, the operator is
applied to the first two, then applied to that result and the third, etc. Be sure
not to confuse AND and OR with ANDB and ORB.
<ANDB> ⇒ #WORD *777777777777*
<ORB> ⇒ #WORD *000000000000*
<XORB> ⇒ #WORD *000000000000*
<EQVB> ⇒ #WORD *777777777777*
<ANDB 5> ⇒ #WORD *000000000005*
<ANDB 12 10> ⇒ #WORD *000000000010*
<ORB 12 10> ⇒ #WORD *000000000016*
<XORB 12 10> ⇒ #WORD *000000000006*
<EQVB 12 10> ⇒ #WORD *777777777771*
The identities follow the same logic as chapter 6's MIN and MAX: the identity
for "and" is all-ones, because anding with it changes nothing. EQVB of two
values is the complement of XORB of them, which is why *6* and *771* are
complements in 36 bits. The manual's warning is real and the spelling is the
only thing distinguishing them: <AND 12 10> is 10 and <ANDB 12 10> is
#WORD *000000000010* -- octal ten, which is eight.
18.6. Bitwise Shifting Operations
<LSH from:primtype-word amount:fix>
returns a new WORD containing the bits in from, shifted the number of bits
specified by amount (mod 256, says the hardware). Zero bits are brought in at the
end being vacated; bits shifted out at the other end are lost. If amount is
positive, shifting is to the left; if amount is negative, shifting is to the
right. Examples:
<LSH 8 6> ⇒ #WORD *000000001000*
<LSH 8 -6> ⇒ #WORD *000000000000*
<ROT from:primtype-word amount:fix>
returns a new WORD containing the bits from from, rotated the number of bits
specified by amount (mod 256, says the hardware). Rotation is a cyclic bitwise
shift where bits shifted out at one end are put back in at the other. If amount
is positive, rotation is to the left; if amount is negative, rotation is to the
right. Examples:
<ROT 8 6> ⇒ #WORD *000000001000*
<ROT 8 -6> ⇒ #WORD *100000000000*
Those four lines are the whole difference between the two, and the last one is
the one to keep: shifting 8 right by six loses the bits, while rotating it right
by six brings them round to the top of the word, at bit 35 -- which is only a
meaningful place to arrive if the word really is 36 bits wide.
pymdl's own. "(mod 256, says the hardware)" is the manual admitting that this
SUBRis a PDP-10 instruction with a thin wrapper, and pymdl keeps the modulus for the same reason it keeps the width. Chapter 36 is where these stop being emulated: an MDLWORDhanded across the boundary is a word in the emulator's memory, soLSHhere andLSHthere have to agree bit for bit, and chapter 37's oracle is how that was checked. Chapter 33'sSQUOZEsymbol packing is the best example of this chapter being used in earnest -- up to six characters packed into one word withLSHandORB, which is how every era symbol name reaches the assembler.