@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.
