@Mark Kahrs: No so -- the B6500/6700/7700 CPU had access to and could manipulate all 51
bits of the word. The 3 tag bits had to be manipulated separately from the 48 data bits of
the word, however.
@Antonio Carlini: Actually, on the B6500/6700/7700, normal state programs could AND DID
manipulate tag bits. There were two instructions RTAG (read tag) and STAG (set tag) that
could extract or set the tag bits of the top-of-stack word (RTAG replaced the TOS word
with the extracted tag, right-justified as an integer). The compilers did emit these
instructions for execution in normal-state code.
The Burroughs compilers for the user-level languages did not implement syntax to
manipulate tags directly, however, so in effect, normal users could not write programs
that manipulated tags or control words willy-nilly. Note, however, that there was no
assembler for this system (although I think a couple of unofficial ones were written by
universities), and compilers had to be "blessed" with special privileges in
order to generate code files that the operating system would execute. So that provided
pretty good protection against malefactors.
The languages for writing system software, initially ESPOL and now NEWP, of course have
extensive facilities for manipulating tags and controls words. ESPOL could not generate a
code file that would run under the MCP, however. NEWP can be used as a user-level
language, but using the dangerous stuff causes the resulting code file to be marked as
"unsafe," which means it can't be executed unless given special privileges.
Note that it's the code file that requires the privileges, not the user running it.
The ability to manipulate tags in normal state has been much more heavily restricted in
versions of the architecture that followed the B6500/6700/7700, so now even if you had a
way to generate the code to modify tags in harmful ways, it wouldn't work.
As to how the tags got built and used, that's a complicated subject. Basically, the
odd tags indicated control words and the even tags indicated data words (0=general data or
single-precision numbers, 2=double-precision numbers, 4=step-index word (no longer used
that way, now used as a fault marker), 6=initialized operand (effectively a write-only
data word). It's possible to store data+tags on disk and read them in using a special
mode of I/O, which is used for loading certain types of data structures into memory when a
program is initiated.
Most control word tags are generated automatically by the hardware as certain instructions
are executed. For example, when calling a procedure, you first execute a MKST (mark stack)
instruction, which pushes a tag-3 Mark Stack Control Word onto the stack to preserve the
stack frame linkage. Then you push any parameters onto the stack. To enter the procedure,
you execute a NAMC (Name Call, sort of like a load-address instruction) to push a
reference to the procedure's tag-7 Program Control Word (PCW, an entry-point
descriptor) onto the stack, followed by an ENTR (Enter procedure) instruction. That causes
the processor to effectively branch to the entry point, but it also replaces the PCW
reference in the stack with a tag-3 Return Control Word (RCW) that has the return address.
There's a bunch of automatic register updating that takes place during this process
that's too complex to describe here.
The one place I know of that a normal-state program still manipulates control-word tags
directly is during stack-building code for a procedure when handling array declarations
(they're called arrays, but physically they are 1-D word vectors). Declaration of an
array generates a control word in the stack. The compiler pushes an ordinary (tag-0) data
word in the stack with a certain bit pattern that describes the length and type (SP word,
DP word, 4- or 8-bit character) of the array, then applies a tag of 5 to that word. The
resulting word is effectively a request token for allocation of the actual data, which
won't occur until later when some code attempts to reference the contents of the
array. That causes a Presence Bit (page fault) interrupt, which signals the operating
system to allocate space for the array, initialize it to binary zeroes, build the
necessary internal memory-management information, and update the request token to be a
proper descriptor for accessing the array from then on. If the program never touches the
request token, the array is never allocated.
If all of this sounds strange and complicated, it is. The Burroughs stack machines were
designed for ALGOL-60, with its multi-level addressing scopes and call-by-name semantics.
That stuff is a challenge to implement in hardware.