DDT is the top-level command interpreter most often used on the Incompatible Timesharing System (ITS). It provides three main types of service to the user: invocation of system programs, manipulation of files, and aid in debugging. ITS itself has no command processor, and can be used only via a program. What appears to a user as the "monitor command level" of ITS is actually DDT.
This file (the primer, INFO;DDT >) treats the general aspects of DDT, grouping commands according to function; then, in the "reference section" (.INFO.;DDT ORDER), all the commands will be described in detail, in alphabetical order. Before using a command in a complicated way, refer to the detailed description to verify that it will function as intended.
When examples of DDT commands are given, upper case letters are intended to be typed in exactly as shown. Angle-brackets ("<" and ">") enclose "meta-variables" which stand for arguments to be supplied by the user. Uparrow ("^"), known to some as circumflex, is used for representing control characters: "^A" signifies a control-A. Other non-alphanumeric characters are to be typed in as shown; some special characters that would be confusing if represented by themselves are represented by "meta-constants", such as "<cr>" for a carriage-return. An alternate representation for a <cr> would be "^M". Other characters sometimes represented by meta-constants include <backspace>, <tab>, <lf>, <rubout>, <space>, and <comma>. Altmode is represented by itself, and its appearance is $. These conventions may be violated in some places, but there will be a note to that effect near the scene of the crime.
Numbers are octal, unless followed by a decimal point ("."), in which case they are decimal (this is the same convention as DDT uses for input). Following the ITS 1.5 manual, "bit <m>.<n>" means the <n>'th bit from the bottom of the <m>'th 9-bit byte from the bottom, out of the four 9-bit bytes in the 36.-bit word. Thus, bit 1.1 is 1, bit 1.3 is 4, bit 1.7 is 100, and bit 2.2 is 2000. Bit 3.1 is 1,, and bit 4.9 is the sign bit, 400000,, . The last sentence illustrates another convention, that <lh>,, is <lh> shifted left 18. bits, or put in the left halfword.
The term "ref <command>" will mean "see under <command> in the appropriate part of the reference section". "See the reference section <part>" will mean to refer to the named part of the reference section. See the reference section (ddtord), for the reference section.
DDT has several special action characters that have a specific effect on whatever command is being typed, instead of simply becoming part of the command. They are used for editing input, for aborting operations, or for controlling where DDT's output goes.
The most important input editing character is <rubout> (ASCII code 177), which deletes the last input character. If possible, it will be erased from the screen; otherwise, it will be typed back out to show what is happening (sometimes <rubout> deletes several characters. When that happens, it types them all out). Other input editing characters are ^D, which cancels ALL unprocessed type-in (in other words, as much as it's possible to cancel), and ^L, which clears the screen and then retypes the unprocessed input.
^S turns off DDT output to the terminal for the command in progress, and other all commands before the ^S itself. ^S is the right character to use if you don't want to see all of a file or directory listing you have printed. Many DDT commands will stop executing if they are silenced, if they think that they are no longer doing anything useful.
^G is a powerful abort command. It will return DDT instantly to its main command-reading level at almost any time. It is so strong that it is dangerous, because it can catch DDT in the middle of updating a data base, such as a job's symbol table or the list of jobs. ^G should be used as a last resort, when DDT hangs up in the middle of a command and ignores other input. In less extreme situations, ^S is usually enough.
One time when ^G is ineffective is when DDT has given control of the terminal to another program. To stop the other program and tell DDT to resume reading commands, the character ^Z may be typed on the terminal (<CALL>, on TV terminals). ^Z or <CALL> is interpreted by ITS itself, and causes a special unconditionally fatal interrupt to the job that has control of the terminal. DDT notices the fatal interrupt and takes control; it never sees the ^Z per se. There is no reason to type ^Z when DDT itself has control of the terminal (assuming the DDT is top level, as usual), and it causes an error message "??" to be printed.
The characters ^W and ^V are used to turn terminal output off and on for long periods of time – for example, if it is desired to execute several commands without any printout on the terminal. ^W turns typeout off, and ^V turns it back on. Typeout will also be turned on if any DDT command reports an error, or if any other program run by DDT returns abnormally. There are also characters ^E and ^B that turn off and on output to a script file. See the reference section (ddtord), for more details.
^_ (<BACK-NEXT>, on TV's) is another character that is interpreted by ITS directly. It is used for communicating with other users, and for specifying various options connected with ITS's handling of the terminal. See the file .INFO.;ITS TTY for details.
This section's title is purposefully ambiguous.
Since ITS has no internal command processor, it is impossible to do anything with a terminal unless some job is deliberately taking input from it. A free terminal is not being read by any job and what is typed on it is ignored. There is an exception, of course, or else it would be impossible to log in. When the character ^Z is typed on a free terminal (or CALL, on a TV) ITS itself loads a copy of DDT to read commands from the terminal. Through DDT, all of the system's facilities are accessible.
On ITS, every running copy of a program must reside in a job, which is a virtual machine much like a PDP-10. Every job has two names: the UNAME which identifies the user it belongs to, and the JNAME which distinguishes between the jobs belonging to one user. The first job any user has is the one that ITS puts his DDT in; that job's JNAME is always "HACTRN" (a parody of "FORTRN"). Other jobs may be created by DDT to run other programs in. Note that "HACTRN" is the name of the job; "DDT" is the name of the program that normally runs in it. It is easy to make another copy of DDT run in a different job, and not very hard to get some other program running in the job HACTRN.
The first thing DDT does when it starts up is print some status information about the system, including the name of the machine being used (AI, ML, DM, or MC), the version numbers of the DDT and ITS that are running, and the number of users on the system. This information can be obtained at any later time with the :SSTATUS and :VERSION commands. In addition, DDT prints the files SYS:SYSTEM MAIL and (except for network users) SYS:LOCAL MAIL, which contain notices so urgent that everyone should see them before using the system.
The "fair share" included in the startup statistics is an indication of what fraction of the total CPU time is available to any one user. It is a measure of the load level on the system. If it is down to 10% or so, you might prefer to wait for the load to be less.
Logging in is the way a user identifies himself to the system and the other users. One should generally log in soon after the beginning of any session. Although being logged in is not actually necessary for most things, it is both convenient (mail you send will contain your name, etc.) and respectful to the other users.
Logging in involves specifying a UNAME, or user name, of 6 characters or less (preferably letters). Each user has his own customary UNAME (or several). If you don't have your own UNAME, pick one that nobody else uses (do :WHOIS <uname><cr> to find out whether a UNAME is in use already). Most users use their initials or one of their names for a UNAME. For more information on what DDT does with the UNAME specified, see the reference section DDT's UNAMEs (ddtord), for more details.
There are two commands for logging in: :LOGIN, which is a "colon command", and $U, which is a "DDT command". Colon commands all start with a colon, which is followed by the name of the command. After the command name come the arguments, if any. In the case of :LOGIN, the argument is the UNAME to log in as. After the arguments, a <cr> ends the command. If DDT is given a colon command with an unrecognized name, it tries to run the system program (if any) with that name. "DDT commands" all contain either a special character or at least one altmode (or both); they can take either prefix or suffix arguments depending on the command. As you see, the term "DDT command" is ambiguous, and can mean any command that DDT understands, or can exclude colon commands.
The dichotomy of colon commands and DDT commands reflects DDT's evolution. Although most often used nowadays for file manipulation and for running system programs, DDT's original use was in debugging. The other functions were added when ITS was written and DDT was chosen as its executive program. When DDT was used only for debugging, there were no colon commands. Nowadays, the more cryptic DDT commands are (usually) assigned to operations that either pertain to debugging or are frequently used, while the longer but mnemonic colon commands are assigned to operations that are either obscure and infrequent or needed by unsophisticated users. Many important operations have both a DDT command to save typing for expert users and a colon command for new users. In such cases, the DDT command is called the "short form". Logging in is such an operation, with the colon command ":LOGIN" and the short form "$U".
ITS normally knows the characteristics of hardwired terminals. For non-local terminals, it may not know them automatically. In that case, before you log in, you should tell ITS the terminal characteristics using the TCTYP program. In the simplest case, that is done by :TCTYP <terminal type><cr>. For example, :TCTYP EXECUPORT<cr> tells ITS to treat the terminal as an execuport. :TCTYP<cr> by itself tells what ITS belives about the terminal at the moment. :TCTYP HELP<cr> will print more information on the TCTYP program.
When you log in, if you have received mail from other users, or if there are announcements on the system that you have not yet seen (see :MSGS), DDT will normally offer to show them to you. The offer will look like "–Mail–" or "–MSGS–", and the proper responses are <space> meaning "yes, show me the mail or the MSGS", or anything else, meaning "save them for later". This scheme has the property that an unexpected offer never causes interference: if you type a command after logging in, without waiting to see whether DDT offers mail or not, DDT will obey the command and skip the printing of the mail. DDT makes other offers at various times. They all begin and end with two dashes, and all are answered approximately the same way (<space> for "yes", anything else for "no"). See the reference section Unsolicited Offers (ddtord), for full details. Meanwhile, :PRMAIL will print mail at any time, and :MSGS will print new system messages.
When the :LOGIN or $U command is finished, it prints a "*". Many commands do this, to indicate that they are finished. The "*" is known as the "prompt character", but, unlike the prompt characters of many other programs, it means "operation completed" rather than "ready for more input". Input may be typed at any time, and will be saved by ITS until it is wanted. Some people like to change the "*" to some other string. Ref ":DDTSYM PROMPT" for how.
DDT has an "init file" feature that allows a user to customize his DDT. The init file is a file of DDT commands that will be executed automatically on logging in. For details, see the section DDT Commands from Files.
Here is a description of the :LOGIN command, in the format that will be used throughout this document:
DDT allows the user to have an "exit file" of DDT commands to be
executed when he logs out. See :LOGOUT and :OUTTEST in the reference
section for full details. One commonly used exit file prints an
amusing message or fortune; the file is called STAN.K;ML EXIT and you
can use it by making your exit file be a link to it (see :LINK,
below).
If something "goes wrong" with the console, a tree may be detached automatically by the system. For example, if a user coming over the ARPA network closes his connection, his tree will be detached. The same thing happens to all users of TV terminals if the TV front-end PDP-11 crashes. When this happens, the detached tree will be destroyed by the system after an hour goes by, unless it is attached first. As described above, logging back in will automatically tell the new DDT to look for the old detached one and offer to attach it.
There is a program called REATTACH designed specifically for detaching jobs from consoles and attaching jobs to consoles. It can be used to move your jobs to another console, from either the old console, the new console, or someone else's console. :REATTACH HELP<cr> will print its documentation.
DDT provides commands for renaming, deleting, and copying files, as well as many other file operations. File commands are followed by the names of the files they operate on. If a command has several arguments, they should be separated by commas. The whole command must be ended with a <cr>. If not enough arguments are given before the <cr>, DDT will read another line of input, after describing what it wants for the next argument. Examples of file commands are
:DELETE FOO BAR<cr>
which deletes the file named FOO BAR in the current default directory, and
:PRINT INFO;DDT > <cr>
which prints the file DDT > in the directory INFO (this file!).
An ITS file's name has four components: the device name, the SNAME (pronounced "S-name"), and two filenames, called the FN1 and the FN2 (The term "filename" is ambiguous, and can refer either to an FN1 or FN2 or to the whole set of four components). This document is a file, and its FN1 is "DDT", its FN2 is "DOC", its SNAME is ".INFO.", and its device name is "DSK". Commonly used device names include DSK which specifies the machine's disk file structure, and AI, ML, MC and DM, each specifying the disk file structure of the named machine (accessed via the ARPA network if necessary). The meaning of the SNAME depends on the device. For devices DSK and AI, ML, MC and DM, the SNAME is the name of the disk directory that the file is stored in. The FN1 and FN2 identify the file in the selected directory. More generally, the device name and SNAME are called the "directory". A directory listing on ITS lists all the FN1-FN2 combinations that happen to exist, at the moment, under a specific device-SNAME pair.
A "filespec" is the character string that specifies a file's name. In DDT, a filespec can specify any or all of the four components of filenames. A device name should be followed by a colon; an SNAME, by a semicolon. The FN1 and FN2 have no special delimiter, but are identified by their order of appearance. Thus,
DSK:RMS;FOO 100specifies DSK as the device name, RMS as the SNAME, FOO as the FN1 and 100 as the FN2.
On ITS, the FN1 of a file usually identifies the program or information it pertains to, and the FN2 is the type of file or the version number of the file. Thus, DDT's source file is SYSENG;DDT 626. Its binary file is SYSBIN;DDT BIN. When a listing is made, it has the file name DDT @XGP. The names BIN and @XGP indicate the type of file (binary and @-listing, respectively), while the common FN1 of the files indicates that they are all logically associated. The FN2 of the source file, namely 626, is composed of just digits; that identifies it as a version number. By convention, when DDT is edited the new version is written out with an FN2 that is 1 larger. To make this convenient, ITS interprets an FN2 of ">" specially: when reading, it refers to the largest version number that exists; when writing, it creates a new file with a version number 1 greater than the largest existing one. The name "<" is also special. It can be used to delete the oldest (actually, lowest-numbered) version of a file. Note that if there is no numbered version of a file that these will find but there are other files with that FN1, ">" or "<" will chose one of these to refer to, so you can screw yourself by deleting non-numeric ones if you are not careful. This can also be convenient however, if you're trying to refer to a single file where the FN1 is enough to be unambiguous.
A filename component omitted in a filespec will be given a default value by DDT, usually the last value specified for that component in either the same command or an earlier one (filenames are "sticky"). For example, after specifying a particular SNAME, all file operations will use that SNAME until a new one is specified. To refer to the last file operated on, a null filespec will serve.
New users are often afraid to rely on filename defaults because they aren't sure how exactly the defaults work. Such fear is reasonable, but DDT has a feature to help dispel it. If altmode is typed when DDT is reading a filename, DDT will print the defaults it is using, and go back to reading. If the altmode FOLLOWS a filespec, DDT will print the full name of the specified file, as obtained by merging the filespec with the defaults. Then it will read another filespec using the printed file names as the defaults. If the printed names themselves are satisfactory, a null filespec is enough. This altmode feature makes it easy to learn just what DDT will do with any filespec, and what defaults it uses.
More information on features available in filespecs may be found in the reference section "Reading of Filenames". More information on filename defaulting is in that section and in the section "Defaulting of Filenames".
Here are the simpler DDT file commands. Notice that some operations have colon commands, some have DDT commands, and some (such as the first one, :PRINT) have both. When there are both, they are equivalent unless otherwise noted. Since all commands that take a filespec as an argument must be terminated by a <cr>, the <cr> is not explicitly mentioned.
The display you see is controlled by the table of two-word sixbit entries beginning at DIRFN1 (and DIRFN2) inside your DDT. For numeric argument n, the nth FN1 and FN2 are used as DIR: arguments. If the numeric arg is omitted it is assumed to be zero (and DDT uses the arguments at DIRFN1+0 and DIRFN2+0).
If a DIRFN2 slot is zero: if there is no argument, we use the default :PRINT FN1; else if there is an argument we use it and set the default :PRINT FN1. If the DIRFN2 slot has something in it: if there is an argument, we use that argument instead.
Here follow the DIR: arguments provided in DIRFN1. You can patch them with :SELF if you have another preference.
DIRFN1: SIXBIT /NAME1/ ;Table of $$^F DIR: search options. DIRFN2: SIXBIT /UP/ SIXBIT /FIRST/ ;$$1^F finds FN1 0 SIXBIT /SECOND/ ;$$2^F finds FN2 SIXBIT /BIN/ SIXBIT /CDATE/ ;$$3^F ascending in creation age SIXBIT /DOWN/ SIXBIT /SIZE/ ;$$4^F descending in size SIXBIT /DOWN/ SIXBIT /NOT/ ;$$5^F not backed up SIXBIT /DUMPED/ SIXBIT /ONLY/ ;$$6^F just link pointers SIXBIT /LINKS/
Examples of use:
FOO$$1^F Shows DIR:FIRST FOO and sets FN1 to FOO LISP$$2^F Shows DIR:SECOND LISP $$2^F Shows DIR:SECOND BIN $$3^F Shows DIR:CDATE DOWN UP$$3^F Shows DIR:CDATE UP PACK13$$6^F Shows DIR:ONLY PACK13
As an additional feature, if you set the DDT variable DIRDIR to -1, $$^F (without a numeric argument) sets your default :PRINT SNAME and otherwise ignores the argument. This lets you look at other directories.
Examples:
$$0^F Does DIR:NAME1 UP $$^F Still does DIR:NAME1 UP FOOBAR$$^F Changes default dir to FOOBAR and does DIR:NAME1 UP DOWN$$0^F Changes no defaults (now FOOBAR), does DIR:NAME1 DOWN
The commands :SFDATE, :SFAUTH, :SFREAP, and :SFDUMP set various attributes of a file: its creation date, its author's name, its don't-reap bit, and its backed-up-on-tape bit. The commands ^T, $^T, $$^T, ^U, $^U, and $$^U control "filename translations". See the reference section for those commands. The :REAP command marks a file as unimportant, and worthy of deletion as soon as it is backed up on mag tape. There are several commands for handling microtapes: :UINIT, :ASSIGN, :DESIGN, and :FLAP. Since microtapes are hardwarily and softwarily unreliable on the ITS systems, their use is discouraged.
The simplest way to tell DDT to run a system program is to use the program's name as a colon command; for example, :TECO<cr> will load and start a copy of the text editor TECO. Of course, this will not work for a program whose name is identical to the name of one of DDT's built-in colon commands (such as the DUMP program). DDT looks for the program to be loaded as a file named TS <prgm name>, on any of several disk directories: the user's home directory first, then the several system program directories. The command :NFDIR (which see) can be used to add any other directories to the list.
Once the other program is loaded and started, DDT gives it control of the terminal. Commands typed on the terminal will all go to the other program and not to DDT. DDT will not read any more commands until the program decides to "return to DDT" or gets a fatal error. However, you can at any time tell DDT to seize control and stop the program by typing ^Z (CALL, on TV's). ^Z acts instantaneously, and makes no attempt to be sure that the program has finished acting on the last command you gave it. If you tell TECO to write out your file, it is your responsibility to wait until the TECO says it is done before typing ^Z; otherwise the file may not really exist yet. If you don't want to wait, you might prefer to use ^_D (Control-_ followed by D; on TV's, Control-CALL), which stops the program only when the program "tries to read" the ^_D. Thus, the program will not be stopped until it finishes processing all previous input. So you can type the ^_D in advance, as well as commands for DDT to execute after the ^_D takes effect.
Every user should know about the INFO program for perusing documentation files. Do :INFO<cr> and follow the instructions, which will teach you how to find the topic you are interested in. The INFO program is one of the two places to look for documentation on a program; the other is the .INFO.; directory (See File Manipulation) which contains the older documentation written before :INFO existed.
As described above, every program resides in a job, which is identified by its UNAME and its JNAME. All the jobs created by DDT to run programs in are "inferiors" of the DDT's job; their UNAMEs are the same as DDT's, which was set by logging in, and their JNAMEs are normally the same as the names of the programs that were run in them. Thus, if user FOO runs TECO, it will be loaded into a new job named FOO TECO. He can also do :DDT<cr> to load an "inferior DDT" into the job FOO DDT, and that DDT can then do anything that the top-level DDT in the job HACTRN can do (except log out). The name of a program, such as TECO, is often used as a concrete noun to refer to jobs running copies of it, as in "Now kill your LISPs and start another TECO".
Running one program has no effect on any other jobs the user may have, with other programs in them. Thus, after doing :TECO<cr>, using ^Z to get back to DDT, and doing :LISP<cr>, there will be two inferior jobs: TECO and LISP. LISP will be running, and will have the terminal; TECO will be stopped. The next section of this manual tells how to go back to using the TECO again (with the :JOB and :CONTINUE commands).
When running a program, it can be given arguments by putting them between the program name and the <cr>. For example, :MIDAS FOO<cr> would run the MIDAS assembler and give it the string "FOO" as an argument. Programs treat their arguments in various ways; in this example, MIDAS would assemble the file FOO > and call the binary FOO BIN. Some programs ignore their arguments entirely.
What happens if :TECO<cr> is done when there is already a job named TECO? That question is complicated, because the user has several options. The best way to explain them is to describe several other commands for running programs, which differ from :<prgm><cr> mainly in what they do in this problematical situation.
The command <prgm>^H will run <prgm> if it isn't already loaded, but simply simply give control back to an existing copy of <prgm> if there is one. It can be thought of as giving control to the program <prgm>, after loading it if necessary. The command <prgm>^K, on the other hand, always loads a fresh copy of <prgm>, and destroys any old copy. For naive users (see ..CLOBRF and :LOGIN), ^K asks for confirmation when it is about to destroy a program. Note that "old copy" really means "anything in a job named <prgm>". TECO^K when there is already a job named TECO will overwrite whatever is in that job, no matter what program it was. :RETRY <prgm><cr> is equivalent to ^K, but makes it possible to specify an argument between <prgm> and the <cr>. Finally, there is :NEW <prgm><cr>, which always loads a fresh copy of the program, but never overwrites any old one! It does this by choosing for the new job a JNAME that isn't in use yet. If there is a job named TECO already, :NEW TECO<cr> will load another copy of TECO into a job named TECO0; if TECO0 too is already in use, it will make a job called TECO1, etc.
So what does :<prgm><cr> itself do when a job <prgm> exists? It acts either like :RETRY <prgm><cr> or like :NEW <prgm><cr>, according to a switch that the user can set (..GENJFL – see the reference section). Normally, :NEW is chosen, but for naive users :RETRY is done instead, on some machines. That is to prevent them from unwittingly making many copies of programs.
The commands to run programs have features that can be used to load programs from any directory, and to request loading of the program's symbols. The latter is useful mainly when the program is to be debugged. See the reference section "Colon Commands" for full details.
After using the commands :<prgm>, :NEW, ^K, or ^H of the previous section to load programs, one might want to start them and stop them, and eventually get rid of them. DDT has commands for all of those operations.
Jobs in ITS are arranged into trees. Each job is either "top level" or has a specific "superior" job. A job can have up to eight "inferiors"; it is their superior. The HACTRN job is always top-level, and the jobs created by it are its inferiors (unless DDT disowns them - see :DISOWN). DDT is capable of examining any job in the system, but full control (starting, stopping, and depositing in memory) is available only for direct inferiors. Even indirect inferiors (inferiors of DDT's inferiors, etc.) can only be examined.
In order to act on a job, DDT must know which of the up to eight jobs it has it should act on. For brevity of typing, the commands don't say which job. Instead, most of them act implicitly on the job DDT calls the "current" job. The :JOB command can be used to make any of the existing jobs current, so it can be operated on:
Whenever the new current job is not a direct inferior of DDT, DDT
types a "#" to tell the user. Whenever :JOB selects a job that DDT
didn't already know about, "!" is typed.
Once a job is current, it can be acted on with these commands:
^P is not the only way to make a job run without the terminal, but
it is the most common way, so jobs running without the terminal are
often described as "^P'd" regardless of how they actually got that
way.
Although DDT can handle up to eight jobs at once, it is good usership to kill jobs that are no longer needed to free their resources for others. Some programs will commit suicide after finishing their work; when that happens, DDT informs the user by printing out ":KILL " just as if the user had killed the program explicitly.
TECO P 34 * DEBUG - 25 SRCCOM R 7TECO is stopped, DEBUG has never been started, and SRCCOM is running. See the reference section for more details. For users on slow terminals, the $$J command prints out just the line describing the current job.
When DDT creates a job, it gives the job the name of your working directory (in the variable .SNAME, which ref). Most programs will use that variable as the default SNAME for files they reference. Of course, many allow the SNAME to be specified explicitly in their commands. The working directory name is known as the MSNAME (for "Master SNAME"), and it is used also for many other purposes, to be described when they are relevant (ref ..MSNAME). For convenience's sake, 0^F is equivalent to <msname>^F; it lists the working directory. The MSNAME can be set explicitly:
When DDT runs a program, it gives control of the terminal to that program. From then on, DDT expects that your commands are intended for the program instead of for DDT. Three things can change DDT's mind:
If any of those three happens, DDT takes the terminal back from the inferior job and resumes reading commands. We say that the job has "returned to DDT" (which does not imply that the return was voluntary). To inform the user of what has happened, DDT prints a description of the job's status and why the it returned to DDT. The conditions that can cause the job to return are called "fatal interrupts", and each one has a name. DDT usually simply prints the names of whichever fatal interrupts happened, but DDT understands some interrupts more and can provide a more hand-tailored response.
When a job returns to DDT, DDT's actions depend on the reason for the job's return. If the job is returning because it requested to do so, DDT simply does whatever the program wanted it to do (but the user can disable this; ref ..PERMIT). In this case, DDT is likely just to print a "*" indicating completion of a command, or ":KILL " indicating that the job decided it was done or useless, and DDT got rid of it. If the job returned because it tried to read past a ^_D, DDT just types a "[DDT]". If the job was stopped by a ^Z, DDT normally prints [DDT], but if ..C.ZPRT is set to zero, or if the job is stopped for an error, DDT prints a message containing the job's PC and the next instruction it will execute if $P'd. This information is provided for debugging's sake, and you should not be worried by it.
105) JRST 105is the sort of message DDT prints when a program is stopped with a ^Z (or a ^X!).
ILOPR; 0>> 0indicates that the program ran into trouble: namely, an ILOPR (illegal operation, in this case executing a zero). In general, the ">>" indicates that the program encountered an error, and the type of error is named. A complete list of error condition names can be found in the reference section "Returning to DDT".
Instructions which cause errors are usually "aborted", which means that any side effects are undone and the PC is left pointing at the instruction itself (instead of the next one). As a result, the instruction printed by DDT is not only the next to be executed, but also the one responsible for the problem. An unfortunate exception is the PDLOV (stack overflow or underflow) error, which does not abort the instruction; either the offending instruction is the one before the instruction printed, or the .JPC (address of last jump instruction) points to it.
If you see other names in parentheses, as in
ILOPR; (REALTM;) 0>> 0they are names of other non-fatal interrupts which are pending for the job. They are printed for debuggers' convenience, and have no necessary relationship to the reason the job returned.
If a program has been ^P'd and is running without control of the terminal, that doesn't stop it from getting into trouble or finishing, and then trying to return. But since the terminal is being used for DDT or some other program, DDT doesn't allow the program to return just yet. Instead, the program has to wait, and in a :LISTJ or $$V its status will be "W" for "waiting". DDT announces this development to the user with a message such as "Job <jname> interrupting: ILOPR", "Job <jname> finished", or "Job <jname> wants the TTY", or something else similar. When an $J with no argument is done, the job will be allowed to return, and DDT will print the appropriate message. "Job <jname> wants the TTY" indicates that the job tried to read from or type on the terminal when the terminal belonged to DDT or some other job. "... finished" means that the program has finished its task and has asked DDT to kill its job. In some cases, the job will in fact be killed at the same time you receive the "finished" message. "... interrupting ..." means that the job has encountered an error condition, whose name is printed.
DDT's role as a debugging aid requires that it be able to serve as a desk calculator. Expressions involving numbers (fixed or floating point) and arithmetic operators such as +, -, etc. are evaluated, and can be used as arguments to appropriate commands. One command that makes it easy to learn how expressions in DDT behave is the "=" command, which prints the value of the expression preceding it.
For convenience in debugging a machine which uses binary arithmetic, numbers input to DDT are normally interpreted as octal. However, if a number is followed by a decimal point then it is (naturally) treated as decimal. Thus, 12 and 10. are equal. A number which has a decimal point in the middle or at the front is interpreted as floating point. Floating point numbers are always read as decimal, and can end with "E" followed by an exponent of ten, as in 1.1e5 which is the same as 110000.0 . These conventions NEVER vary.
DDT has separate operators for fixed point arithmetic and floating point arithmetic. This is because once a number has been read in DDT does not remember which one it was. There is no way for DDT to know whether two numbers must be added using fixed point arithmetic or floating point arithmetic, so the user has to specify one or the other for every arithmetic operation. In this matter DDT resembles the machine language that it was intended to debug, rather than high level languages. Specifying the mode is not difficult, however, because the floating point arithmetic operators are just the fixed point operators with a single extra altmode. Thus, floating point addition is done with $+, and floating point multiplication with $*. The = command always prints its argument as a fixed point number. Its floating point equivalent, $=, always prints its argument as a floating point number. Feeding fixed point numbers to the floating point arithmetic operators or $=, and the reverse, will produce seemingly insane results. This are a good way to learn about the PDP-10's floating point number representation and the quirks of its floating point arithmetic instructions.
Here is a list of all DDT arithmetic operators:
+ fixed addition $+ floating addition - fixed subtraction $- floating subtraction * fixed multiplication $* floating multiplication ! fixed division $! floating division # bitwise exclusive-or & bitwise and $_ logical shift $$_ floating scale
The &, # and _ operations are done first, multiplication and division second, and addition and subtraction last. The subtraction and division operators may be unary, as in "-50" which has the obvious meaning. Unary fixed point division is rather useless, but "$!2.0" is 0.5. The logical shift and floating scale operations are defined by the PDP-10 instructions LSH and FSC. They LSH or FSC their first operands by a number specified by their second operands.
DO NOT use extra spaces inside arithmetic expressions. They are not ignored and will alter the value. This results from the PDP-10 instruction type-in features, to be described later.
Terms in expressions can include symbols and some special quantities, as well as numbers. Symbols always have numeric values if they are defined at all, so they are used just like numbers. Since they are useful mainly for debugging, their detailed description is postponed.
This section describes other things that DDT can do with jobs, that are less often useful or more difficult to understand than those in the previous section.
A disowned job can be reowned by selecting it with :JOB. What's
more, any user can reown a job no matter who disowned it, using the
:UJOB command and specifying the UNAME of the disowned job, as in
:UJOB FOOSH TECO to reown the TECO that user FOOSH disowned. This
makes it possible to hand a job to another user.
The $L command has two other options, which may be used with either
$L or $0L. Two altmodes instead of one ($$L or $$0L) cause a
merge-load, which does not throw away the core and symbols the job
already has. The data in the file replace the data in core, but
locations not loaded by the file are unchanged. Symbols already
defined are kept, along with any symbols in the file. Also, the job's
system variables are not reinitialized. An infix 1 ($1L or $$1L)
loads a binary file without its symbols.
The three forms of inter-user communication commonly found on various timesharing systems all exist on ITS. They are known as "sending", "linking", and "mailing". In sending, you compose a message, and when finished cause it to appear all at once on the other user's terminal. Linking (NOT to be confused with file links or :LINK) puts two or more users into a "com link", after which any character typed by any of the users appears on all of the linked terminals. Mailing writes a message that another user will see when he next logs in; unlike sending and linking, it does not require that the other user be logged in when it is done. Com links are good for carrying on a conversation, especially a many-way one, but they have the disadvantage of interfering more with doing work at the same time. Com links really have nothing to do with DDT, since they are implemented by ITS directly. For information on them, see the file .INFO.;ITS TTY.
^C -- Sends the message ^D -- Flushes the message ^K -- Retypes the message, without clearing the screen ^L -- Clears the screen and retypes the message ^R -- Retypes the current line ^U -- Kills the an input line ^W -- Kills a word
The message can be any number of lines long. When the ^C is typed, <user> will see printed on his console:
[MESSAGE FROM <sender> at <machine> <time of day>] <message><sender> is your UNAME, and <machine> is the name of the machine you are on (MIT-AI, MIT-ML, MIT-MC or MIT-DM). <machine> is included in case <sender> is using one machine from another over the ARPA network; the recipient needs to know which machine he got the message from.
All the messages you are sent will be put in your "SENDS file",
.TEMP.;<your uname> SENDS (or COMMON;<your, uname> SENDS if the
machine you are on doesn't have the .TEMP.; directory) which is
deleted when you log out. If you miss a send because it is
overwritten on the screen, just print that file to see it. In
addition, if you are not in DDT when you receive the message, the
message will be printed again when you return to DDT.
There is also a program QSEND that can be used to send. It is less efficient than :SEND, but it can send to more than user at once, and can send to users logged in on other machines. QSEND is really the same program that does mailing. If ..SNDFLG is non-zero, :SEND will get you the SEND program (normally simply a link to the QSEND program) QSEND (MAIL).
There are times when it would be embarrassing to receive a message (for example, when printing a copy of this file). For those times, :GAG is available.
In emergencies, such as when the disk is almost full, it may be necessary to send to all users at once. The :SHOUT command does that – see the reference section.
To communicate with a user who is not currently logged in, :MAIL must be used. :MAIL is not actually a DDT command; it runs the MAIL program. :MAIL has many features; for example, it is easy to mail to several users, on any ARPA network hosts. For full details, see MAIL (INFO;MAIL >). The simplest usage, though, looks exactly like :SEND:
There are several ways to read mail you have received. Normally, mail goes in a file <hsname>;<xuname> MAIL, on the machine specified by the Network Address field in INQUIR. Knowing that, you can use :PRINT to read it. In addition, there is a special command to do that:
:PRMAIL <user> or <user>$^A sets a default, that ^A and $^A with no
prefix argument use.
<its>$^A and <its>$$^A are similar. They use the user who would be the default for a $^A or $$^A without any prefix, and look only on the specified ITS.
Normally, DDT does a :PRMAIL automatically when you log in. If you have a DDT INIT file, no :PRMAIL is done unless the INIT file calls for one.
There is also a sophisticated program for reading, answering and distributing mail, called RMAIL. It is intended primarily for display terminals, but can be used from printing ones. Run :INFO and look under RMAIL to see its documentation. If you use RMAIL, you may not want DDT ever to delete your mail, but might still want to do :PRMAIL once in a while for a quick glance at your mail when you don't want to edit it. Putting the commands :DDTSYM OMAILF/ 1<cr> in your init file will make :PRMAIL<cr> just print your mail file, without deleteing it. Also, for compatibility because some people liked it, you can do :DDTSYM OMAILF/-1, and :PRMAIL will rename your mail to OMAIL after printing it.
One other user you might want to comunicate with is yourself at another time. The :ALARM command tells DDT to notify you when a specific time arrives:
Alarm set for .+<duration as hours:minutes:seconds>telling how far away the specified time is. If it says "Alarm reset" instead of "Alarm set", there was a previously specified alarm in effect. That alarm is forgotten, as DDT can remember only one alarm at a time. When the alarm comes due, DDT will begin printing a message on the console frequently until the alarm is explicitly cleared.
Messages of general interest to the user community, but not urgent enough to be printed out by DDT when it starts up, are distributed as "announcements". Announcements are really just disk files, and can be :PRINTed, but they are normally read with a special command, :MSGS, that contrives to show any given user each announcement exactly once. It does so by keeping track, for each user, of the creation date of the most recent announcement he has seen; only announcements more recent than that are eligible for being printed. The date information is kept in the file called <hsname>;<xuname> MSGS, so don't delete it.
The –MSGS– offer is unlike all others in that <rubout> means "yes", just like <space>. Any other character still means "no". This is so that by typing nothing but <rubout>s one can see the filenames and first lines of all the announcements.
Normally, DDT will do a :MSGS for you automatically when you log
in, if you either have a file directory or have ever done a :MSGS
before. However, if you have a DDT init file, to be executed when you
log in, this default is overridden; it is the init file's
responsibiliy to do a :MSGS if that is desired. An alternative to
:MSGS is the program :GMSGS, which copies any newly created
announcements into your mail file. You can then read the
announcements along with your mail, using RMAIL or any other
mechanism. See .INFO.;GMSGS ORDER for information on GMSGS. Because
:MSGS and :GMSGS remember the date of the most recent announcement
seen in the same place, it is possible to switch between :GMSGS and
:MSGS without losing track of anything.
Since there is only one remembered date for announcements, instead of one for each keyword, announcements can be missed if you do not consistently use one set of keywords in every :MSGS or :GMSGS you do. For example, if your last :MSGS was a :MSGS * three days ago, and you do :MSGS<cr>, you will see any announcements intended for the machine that you are on, created in the last three days, and your remembered date will be updated to the current date. As a result, if there were any announcements NOT intended for the machine you are on, created in the last three days, you will not be shown them by :MSGS *, since it will assume you have seen them.
Announcements are submitted with the MAIL program, by using the desired keywords as the "recipient names". Thus, :MAIL *AI<cr> would create an announcement with the single keyword *AI. :MAIL *ITS<cr> creates an announcement with all four keywords *AI, *DM, *MC and *ML - this announcement will be seen by everyone on all four machines. The MAIL program will request all appropriate information such as the expiration date of the announcement, the desired file name, and the subject, which will appear on the first line of the announcement. Most announcements should expire in 7 days, but announcements of changes in system programs should expire in 30 days. Of course, announcements that are of no interest after a specific day should expire then.
Although DDT normally reads its commands from the terminal, it can be told to take them from a file (called an "execute file" or "xfile"), and a running program can order DDT to execute a specific string of commands (The operation is called "valretting" and the string is a "valret string"). Although for the most part commands in execute files and valret strings are written exactly the same way they would be typed, there are a few exceptions. This section describes those exceptions, as well as some commands that are useful primarily in files or valrets.
The most common use of execute files is as init or exit files, which are executed automatically (if they exist) upon logging in or out, respectively (but $0U and $$0U allow you to log in or out as if you had no init or exit file). Init files are identified to DDT not by a user command but by having the appropriate filenames. An init file is called <hsname>;<xuname> LOGIN . An exit file has LOGOUT instead of LOGIN in its name.
However, the user can explicitly command the execution of a command file at any time:
Valret strings are given to DDT by the execution of a .VALUE instruction in an inferior job. .VALUE is described in the section "DDT Services for Programs Running Under DDT".
Note that the execute file affects only DDT; it does not also supply input to other programs. Programs that normally read input from the terminal will continue to do so, even when invoked by an execute file. However, command strings of programs (a la :<prgm> <command>) are really read by DDT and can be specified by execute files.
Normally, the commands read from an execute file will be typed on the terminal as they are executed, making the typescript appear as if the user had typed the commands on the terminal when it was time. However, the file can use the characters ^V and ^W to turn off output to the terminal, and that affects echoing of the commands as well. In fact, most execute files and valret strings start with a ^W and end with a ^V, so that they print nothing at all when they execute. The characters ^V, ^W, ^B and ^E in execute files are interpreted only when DDT has finished handling everything before them. This is unlike the way they are treated when typed on the terminal; then they are interpreted immediately when typed, even if DDT is still processing previous input. Also, some of them have slightly different effects in an execute file, to make programming more convenient. ^W-^V pairs in files can be nested, and nothing is printed on the terminal if it is within at least one ^W-^V pair. See the reference section.
Since files almost always have a ^L at the end, DDT ignores the character ^L in execute files and valret strings. To clear the screen, the :CLEAR command must be used. DDT commands are often terminated by stray <cr>'s, but stray <cr>'s look ugly in files and in assembly sources. So in files and valrets DDT allows each <cr> to be followed by a <lf>, which is ignored. If the character sequence <cr> <lf> is actually intended, the file must contain <cr><lf><lf>; only the first <lf> is ignored.
The character ^C is the traditional end-of-file indicator on ITS, so any ^C will be treated as the end of the file. This may change with planned improvements in the ITS file system. ^C does not terminate valret strings; they should be in ASCIZ format.
Execute files and valret strings work recursively. That is, one execute file or valret string can do a :XFILE of another execute file, or run a program that submits another valret string. When that happens, the inner file or string is executed, and at its end the execution of the outer one resumes.
Errors during the execution of the commands in an execute file or valret string do not irrevocably prevent the execution of the rest of the file or string. Instead, DDT postpones or "pushes" the rest of it (typing ":INPUSH " to inform the user), and starts taking commands from the terminal again. The user can recover from the abnormal situation and then cause the rest of the file or string to be executed, with the :INPOP command:
If the error shows that the rest of the file is not wanted, the command :INFLS will discard all the suspended files and valret strings. ^G has the same effect. It is wise to do this because DDT is limited in the depth to which it can nest execute files and valret strings; leaving unwanted ones stacked can interfere with normal operation later. When the maximum nesting depth is exceeded, the error message "INPDL OVERFLOW" results and all the suspended files and strings are discarded. Note that a :XFILE at the very end of an execute file or valret string does not use any extra stack space; the file that is ending is popped before the new one is pushed.
Complicated programs can be written for DDT, since execute files allow both conditionals and loops.
Conditionals use the :IF command:
:IF <condition> <argument> $( <conditionalized commands> $)
executes <conditionalized commands> or does not execute them, according to <condition> and <argument>. The simplest conditions are the numeric sign conditions: L, E, LE, N, G, GE. Those conditions expect <argument> to be a numeric expression and test the sign of its value. Thus, :IF E 1 would fail and not execute the <conditionalized commands>. <conditionalized commands> must be balanced in parentheses - that is how DDT knows when they end. The $( and $) commands are ignored by DDT except for their effect on the parenthesis counting, so commands containing unbalanced parentheses can be conditionalized by including an extra $( or $) to balance the string. Conditionals are strong enough to affect even the output-control characters ^V, ^W, ^B, ^E. Thus,
^W :IF N $Q $( :$^VThis is a printed message ^W$ $)^V
conditionally prints "This is a printed message<cr>". The <cr> before the $) is necessary to end the :$ ... $<cr> comment construction. The :IF argument can be ended by ">" instead of <cr>, if the user wishes.
:EXISTS <file><cr> returns 0 if <file> can be successfully opened for reading. If it can't be, the I/O channel status containing the error code is returned (it will be nonzero). Thus,
:EXISTS FOO =
would print 0 if FOO exists. :EXISTS is very useful in arithmetic conditionals:
^W :IF E :EXISTS FOO NOTE > $( :PRINT FOO NOTE^V ^W $) ^V
prints the file FOO NOTE if it exists. The ^V<cr>^W construct causes typeout to be enabled when the :PRINT is executed - otherwise, the file would not actually be printed! The <cr> after NOTE ends the :EXISTS. It is used up thereby, and does not end the argument to :IF (you are allowed, for example, to have an arithmetic operator there). The :IF argument is ended by the ">", although a second <cr> would have done just as well.
If you want to have an else clause in your conditional, use the :ELSE command.
:ELSE $( <commands> $)
is a conditional which succeeds if the preceding conditional failed. The "preceding conditional" is the last one which ended; it may have contained others, but they don't matter. Successive :ELSE's will alternate between success and failure.
:ALSO is like :ELSE, but succeeds if the preceding conditional SUCCEEDED. A common thing for a conditional to do is to examine the contents of a location in DDT itself. Many DDT locations are useful for conditionals, but are not useful enough to have user-visible symbols that refer to them. The :DDTSYM command makes it easy to open such locations:
:DDTSYM TTYTYP/ (this tests for STY or dialup) :IF N $Q&<%TYSTY+%TYDIL> $( :DDTSYM TCTYP/ (this makes SUPDUP terminals :IF N $Q-%TNSFW not count) $( :TCTYP LINEL 69. etc. $)$)
To test, in a conditional, whether a symbol is defined, the command
:SYMTYP can be used - see the reference section.
^W :$ ^V--Tektronix--^W$ :IF MORE 0 $( :TCTYP TEKTRONIX $) ^V
If a Yes-No answer won't do, there is a program INLINE which will allow
you to complete a command line. (INLINE.)
General transfers of control are available in execute files and
valret strings with the :TAG and :JUMP commands.
^V :TAG LOOP <do something> ^W :SLEEP 5.*30. :JUMP LOOP
Init and exit files often want to perform the normal actions in addition to the particular actions which make the init or exit file necessary. They can use the :INTEST and :OUTTEST commands, which perform DDT's default logging-in or logging-out actions (what DDT does if there is no init/exit file). Beware: :INTEST has that meaning only when used in an execute file! See the reference section. :MSGS and :PRMAIL are also likely to be useful in init files. The programs GMSGS and RMAIL offer alternative ways of reading messages and mail - see [GMSGS (RMAIL).]
Note that using login init files to automatically run the CRTSTY program involves some special difficulties, due to running the init file twice, once to run the CRTSTY, and once to log in under the CRTSTY. See [CRTSTY (CRTSTY).] for more details, instructions, and examples.
Symbols in DDT are used primarily for debugging, and to better serve that purpose they do not work as they might in a typical interpreted programming language. In DDT, all defined symbols are either predefined system symbols that are always available to all users, or job-specific symbols that are associated with a specific program, and were (probably) loaded along with the program. Thus, the meaning of a symbol in DDT while debugging a program is about the same as the meaning it has in the assembler when the program finished assembling. DDT's predefined symbols, for the most part, are the same as MIDAS's predefined symbols; they include all the PDP-10 instructions, all the UUOs of ITS, and many quantities useful as arguments to ITS system calls. Of course, assembler macros and pseudo-ops will not be known to DDT at all.
The syntax of a symbol in DDT is the same as that in MIDAS: anything made of letters, digits, and ".", "$", and "%", which does not make sense as a number, is a legitimate symbol, and only the first six characters of a symbol are significant. Note, however, that the set of reasonable numbers in DDT is not the same as in MIDAS, since DDT uses "E" to signal a floating point exponent, while MIDAS uses "^".
Almost any symbol defined in DDT at all is defined numerically. Therefore, symbols are used exactly as numbers are used. Just as in MIDAS, the symbol "." is special; in DDT it refers to the address of the last location examined.
In limited contexts, DDT allows you to make "forward references" to
a symbol which you are going to define later. This makes DDT in
effect a complete one-pass assembler. Forward references may be used
only to deposit into memory, and only in the address field of a word
or the left half. Furthermore, aside from adding a known value to the
forward reference, no arithmetic is allowed.
When you type in a reference to an undefined symbol, DDT does not detect that until the following operator is typed in. This is because that operator tells DDT how to interpret the symbol (imagine what happens if the operator is :, or even ^F). When DDT does realize that you have typed an undefined symbol, the special error message "?U?" is given. This error message does not discard all of your type-in, just the undefined symbol and the following operator. If you wish to finish the command you started, you should continue with the correct spelling of the symbol and go on from there. Alternatively, if you wish to make a forward reference to the undefined symbol, you can type just "?"; you need not retype the symbol's name. Of course, if the command so far is a complete mistake, you can use ^D to cancel all of it.
In its attempt to print PDP-10 instructions in a form intelligible to the user, DDT tries to find symbolic representations for addresses that it types out (see "Typeout Modes). But this introduces the problem of how to decide which symbol is appropriate. If the address 405 is to be typed out, and there are two symbols, START and FOOFLG, with value 400, either START+5 or FOOFLG+5 might be used. DDT can't tell which one is better, but if the author of the program knows that START is an address and FOOFLG the name of a bit, he might prefer to see START used. He can tell DDT never to use FOOFLG ever for symbolic typeout by "half-killing" it. Bit typeout mode is an exception; it is willing to use half-killed symbols, and must be, since bit names usually are half-killed. Half-killing is so useful that MIDAS provides commands to define and half-kill a symbol all at once ("::" and "=="); MIDAS communicates the halfdeadness of the symbol to DDT along with the symbol's value. In addition, there are DDT commands to half kill a symbol:
Symbols normally accompany programs. A binary file usually contains a symbol table, and when DDT loads a binary file into an inferior job it usually also remembers the symbol table from that file as the symbol table of the job. When DDT dumps the core of a job into a binary file, the job's symbol table is also written. At times this process does not do the right thing automatically, so commands are provided for manipulating symbol tables:
A program's symbol table may be arranged in a block structure which limits each symbol's scope. There may then be several symbols with the same name, defined in different blocks. Block structure will exist in the symbol table of a MIDAS program only if the program explicitly makes use of MIDAS block structure with the .BEGIN and .END pseudo-ops.
DDT handles block structured symbol tables by remembering, at all times, a currently selected symbol table block for each job. All references to symbols use the selected block as the scope. However,
for ease of use, if a symbol is not, strictly speaking, accessible from the selected block, but it is defined in some other block, the other block's definition is still visible. Use of such a symbol as input will select the block that the symbol is defined in, leaving the previously selected block. The user will be notified with a message such as "<newblock>$:". Not surprisingly, <newblock>$: is a command that selects the specified block; see the reference section. :LISTP prints the block structure of the current job's symbol table, and :PRGM prints the name of the selected block.
At times it is necessary to store a copy of a job's symbol table into the job's memory, or to read symbol definitions or a whole symbol table into DDT out of the job's memory (the linking loader STINK does this to give DDT the symbol table constructed by the loading process). The commands ^Y, $^Y and $$^Y serve those functions. The command :SYMTYP takes a symbol as argument and returns information on whether the symbol is defined. See the reference section.
Finally, some files do not contain any symbol table, but instead contain an "indirect symbol table pointer" to another file. This means that attempting to load the symbol table of the file containing the pointer will load the symbol table of the file pointed at. If you load a system program with ^K (no symbols), then dump it after running it, it will automatically be given an indirect symbol table pointer to the system program file that was loaded.
In debugging, the most important operations are examining the job's memory locations and depositing new data in them. In DDT, words are examined by commands to "open" them. Once a location is open, new contents can be deposited in it; it is impossible to deposit in a location unless it is open. At any time there may be at most one location open; opening one location automatically "closes" the previously open location. Many commands (including all commands that print a "*" when they are done) close any open location without opening another. This is to make sure that you do not accidentally deposit in a location which had remained open so long that that fact was no longer obvious.
Opening a location usually prints out the contents of the location. Just how the contents are printed is up to the user, who can select from several built-in "typeout modes" including symbolic mode, numeric mode, ascii text mode, etc. (See the section on typeout modes).
The simplest and most frequently used way to open a location is the "/" command. It is preceded by the address of the location to open. The contents of the location are typed out, using whatever typeout mode is currently selected. In addition, the address and the contents are remembered as the values of the special symbols "." and $Q, respectively. Here is an example, which assumes that symbolic typeout mode is selected (as is usually the case):
107/ MOVE A,FOO+3"107/" was typed by the user, and the contents of 107 were printed, as a PDP-10 instruction, by DDT. After this command, the value of "." will be 107, and the value of $Q will be MOVE A,FOO+3.
If you try to examine a location at which the current job has no memory, the error message "??" will be printed. No location will be open.
Having opened location 107 and seen what is stored there, you can now deposit new contents with the <cr> command.
<cr>, with or without arguments, has another important effect: it undoes any temporary typeout mode selections, reverting to the permamently selected typeout modes. The significance of this will become clear in the section on typeout modes. Only <cr> performs this function. The other commands that deposit do not do so.
Often when one location is interesting, the one after it is also interesting. The command <lf> opens the location following the last opened location (that is, the one whose address is 1 larger). It moves to a new line and prints the address of the location it is opening, followed by a slash, before it prints the contents. For example, if after opening 107 as above you type <lf>, the typescript might appear as follows:
107/ MOVE A,FOO+3 START+10/ JRST START1
This assumes that START has the value 100 (a likely possibility), so that START+10 is just the symbolic expression of 110. The entire line starting with START has been printed by DDT, but since your type-in can't be distinguished from DDT's output, you could have typed in <cr> and then START+10/ and produced an identical script. That's a feature, not a bug, though: <lf> is defined to do exactly START+10/, so it might as well look the part. Commands which open a location whose address is not immediately visible as an argument in the command often print the address just as <lf> does.
To "undo" a <lf> command, use the ^ command, which opens the word before the last word opened, instead of after. Doing <lf>^ when location 107 is open will open first 110 and then 107 again. ^ with an argument can be used for depositing, just like <lf>.
After seeing the instruction MOVE A,FOO+3 typed out, you might wonder what is in FOO+3. DDT saves you the trouble of typing FOO+3 in again by providing the <tab> (or ^I) command. <tab> opens the location addressed by the right half of $Q, which, after the 107/, would be FOO+3. <tab> prints the address it is opening on a new line, just like <lf>. Alternatively, you can use the command / with no argument, which will also open the location addressed by the right half of $Q, but will not print that address or go to a new line. Giving an argument to <tab> will deposit that argument (if there is still a location open) and then open the location pointed to by the argument; this is very different from what / does with an argument.
More hairy examination commands exist, which either specify a typeout mode for printing the contents of the location, or obtain the address to open in an unusual way. The first class of commands includes ], which prints the opened word's contents symbolically no matter which mode is current, and [, which always prints the contents numerically. The second class actually consists of one- and two-altmode variants of the commands already described: /,[,] and <tab>. Those commands all open a location whose address is taken from either an argument or $Q. With no altmodes, they get the address from the right half of the argument or $Q. The one-altmode variants, such as $/ and $<tab>, all take the address from the left half of the argument or $Q. Even more useful, the two altmode variants such as $$/ perform a PDP-10 effective address calculation on the argument or $Q, and open the addressed location. For example, suppose that A contains 5 and 1734 contains ADD T,FOO(A). Then after 1734/, $$/ would open location FOO+5. @A$$/ would open location 5.
The commands to access the address ring buffer also examine and deposit memory locations, but in view of their special functions they are described in a later section (The Address and Value Ring Buffers).
DDT also provides primitives for manipulation of a job's page map. Already mentioned is :UNPURE <addr>, which unpurifies the page <addr> was on. :CORTYP <addr> prints information about the page <addr> is on, including how many times the page is shared, and whether it's writable, etc. :CORPRT prints the information given by :CORTYP for each existing page in a job.
:CORBLK <type> <addr> is provided to manipulate in a general way pages of a job's memory. <type> is one of the following:
PURE -- Makes the page at <addr> pure, that is not writable. FRESH -- Puts a fresh page at <addr> NONE -- Deletes the page containing <addr> IMPURE -- Same as :UNPURE <addr>
:CORBLK PURE <loc> is good for re-purifying pages of jobs that have been patched, prior to :PDUMPing.
One thing that one often wishes to deposit in memory is a PDP-10 instruction. DDT allows them to be expressed almost as they would be in the MIDAS assembler, with some exceptions: <tab>s are NOT allowed in place of spaces, since <tab> is a DDT command in itself; literals too are not allowed. However, spaces, commas, parentheses and angle brackets have about the same meaning as in MIDAS. An instruction is made up of one or more "fields" separated by spaces or commas. Each field is a number, a symbol, or an arithmetic expression. The fields are combined to form a word in a way that depends on the instruction's "format", which is the pattern of spaces and commas around the fields. Each format has a fixed meaning in DDT, usually the same as the format's default meaning in MIDAS. The formats include:
Notice the two formats, "<ac>," and "<ac>,<addr>", whose meanings in DDT are not the same as their default meanings in MIDAS. Many people redefine those formats in MIDAS to mean the same thing they mean in DDT, and perhaps someday MIDAS will be changed to be compatible.
Just as in MIDAS, @ can be included in an instruction to set the indirect bit, and a parenthesized expression can be used to specify the index field. It makes no difference where in the instruction the @ goes, although it cannot be put in the middle of a number, symbol, or operator without causing syntactic trouble. A parenthesized expression's meaning depends on whether it follows directly an arithmetic operator. If it does, it acts like a term in the expression, whose value is the halves-swap of the expression inside the parentheses. If there is no arithmetic operator in front of the "(", then the parenthesized expression, like an @, has a global effect rather than a local one: the expression inside has its halves swapped and the result is added into the entire word, after the fields are merged according to the format. Thus, 2*(1) is 2*1000000, or <2,,>, and if it appears as the address of an instruction, it is truncated to 18. bits, giving 0. But 2(1) appears, in its context, as just 2, and the entire instruction has 1,, added to it putting a 1 in the instruction field. Since the 1 in the (1) bypasses the format-processor, truncation of addresses to 18. bits has no effect on it.
Typing Gives FOO$> MOVE A,FOO(C) FOO(0)$> MOVE A,FOO MOVNS$> MOVNS A,B(C) MOVNS 0,$> MOVNS B(C) @@$> MOVE A,@@B(C) 0,,$> B ,0$> MOVE A,(C)
Literals are just as useful when typing in code with DDT as they are in an assembler. However, because DDT can't assemble a word of code without knowing where it is to be deposited, literals in DDT don't work quite the way they would in an assembler.
DDT stores literals in the job's patch area. The symbol PATCH points to the next word available for use by a literal; as words are used for literals PATCH is updated. See the section on patching for how to create a patch area (Patching.). Every program ought to have one.
But when do you supply the data for the literal? Usually, as soon as
you deposit the expression that contained it, DDT will ask you to do so.
But if you use nested literals, or use a literal while making a patch
(Patching.), DDT will have to wait for a while before
asking for the data. In any case, DDT will ask for the data eventually, by
typing out "$LT001/ 0 ". The symbol name typed tells you which literal DDT
is asking for - just match it up with the symbol printed by the $$( before.
At this time, a location in the patch area is open, and you should start by
depositing the first word of the literal. Deposit as many words as you
like, or do other things. When you are finished, open the word AFTER the
end of the literal and type $$). This tells DDT where it should start the
next literal or patch.
If you rub out a $$(, DDT will still think that the literal needs to be defined and will eventually ask you to define it. But when that happens, you need only type $$) immediately. The literal will have been defined to be zero words long, and DDT will be satisfied.
When altering text strings, you can use DDT's commands that compute the numerical representation of text according to the conventions used most often by PDP-10 programs: ASCII, SIXBIT and SQUOZE.
The "2" in $2" has two effects: it distinguishes this command from
the $" command, which selects a typeout mode, and its low bit
specifies the low bit of the value. Thus, 0 could be used instead of
2 with no effect, but 2 is easier to type.
The contents of a memory location may be interpreted by a program in many different ways. DDT knows several of the most common ways, in that it can print the value of a word by showing what it would mean given a desired method of interpretation. For example, "symbolic typeout mode" prints a word as a symbolic address or as a PDP-10 instruction containing one; "ASCII typeout mode" prints a word as five ASCII characters; "Constant typeout mode" prints a word as a number. Some typeout modes have sub-options. For example, those that print numbers will use whatever output radix has been specified.
At any time, one typeout mode is "selected" or "current". Most DDT commands that print the value of a numerical quantity will use the current typeout mode (some commands, that know the significance of what they are printing, always use the appropriate mode regardless of what mode is selected). DDT commands exist for selecting various typeout modes either temporarily or permanently. If a new mode is selected permanently, it remains selected until explicitly replaced. If the current mode is changed temporarily, the new selection remains in effect only until the next <cr> command (<cr>'s that are the argument-terminators of other commands, such as commands that read filenames, do not count in this regard). At that time, the last permanently selected mode will be reselected.
Each typeout mode that DDT has, has a command to select it. The commands to select a mode temporarily all have exactly one altmode. If a second altmode is used, the selection is made permanent. For example, $F selects typeout as floating-point numbers, temporarily. $$F selects the same mode permanently.
When a typeout mode has sub-options (such as whether to print component addresses numerically or symbolically, or what bit-name prefix to use), those sub-options may also be set either temporarily or permanently. When the sub-option is set by an argument in the command that selects the mode, the sub-option is set temporarily if the mode is being selected temporarily; permanently, if the selection is permanent.
In addition to the temporarily and permanently selected modes (also known as the "temporary mode" and the "permanent mode"), DDT remembers which mode was most recently explicitly specified. This variable is updated just as the temporary mode is, except that it is NOT reset by carriage returns. The most recent mode can be used to type one value with the ";" command, or can be selected temporarily or permanently with "$;" or "$$;". Thus, after temporarily selecting halfword mode with $H and then reverting to the permanent mode with <cr>, ";" will still type its argument in halfword mode.
The user may define typeout modes of his own, by supplying DDT with an instruction which, when executed in the inferior itself, will print a value in his favorite manner. DDT has several typeout-mode selecting commands that select a variable mode instead of a fixed one; if the user's instruction is specified as the definition of one of those modes, the user's instruction will be used for typeout when that mode is selected. The commands that select a variable mode are $", $#, $$, $%, $&, and $'. The terms "# mode", "$ mode", etc. mean "the mode that $# currently specifies", etc. Some of the variable modes are initially set to useful built-in typeout modes such as ASCII mode, SQUOZE mode, etc. Others ($$ and $%) are useless unless given meaning by the user. The meanings of the variable mode commands are controlled by locations inside DDT: each job has one location for each of the six commands. The locations' addresses are called, respectively, ..TDQUOT, ..TNMSGN, ..TDOLLAR, ..TPERCE, ..TAMPER and ..TPRIME. Each variable can contain either -1,,<addr in DDT> or an instruction to be executed in the inferior job. No addresses in DDT should be used except those designed for such use, which have names starting with ..TM: ..TMSQ is the address of the SQUOZE typeout mode, for example (ref ..TMSQ). The only user instructions that are likely to be useful are subroutine calls (including user UUOs); the subroutine should expect to find the value to be typed out in location 37 (but ref .40ADDR). In addition, the address of the open location will be in 25 (but ref .40ADDR), but depending on this makes the typeout mode less versatile (it can't be used in a Raid register, for example).
With the exception of $T mode, all of the built-in DDT typeout modes arrange for their output to be in a form suitable for being typed back in. That is automatic for $C, $H, $F and $S modes. For the ASCII, SQUOZE, and SIXBIT typeout modes, it requires that the data typed out be preceded by an appropriate DDT operator for reading the data back in, and that it be printed in the syntax used by that operator.
Here follow descriptions of the fixed built-in typeout modes.
Here follow the descriptions of the built-in typeout modes that are the initial settings of the variable commands $", etc.
Here are the commands that control how addresses are printed:
Here are the commands that select the output radix, in which numbers are printed:
Several DDT commands print a numeric argument back out using a specific typeout mode. They exist for convenience, since it would be possible anyway to select that typeout mode and then use the ; command to print the argument in that mode. When one of these commands has no argument, it prints the value of $Q.
Type-out commands interact specially with the ..TTYFLG variable and the ^W and ^V special characters. While a single ^W inhibits almost all terminal output (except :send messages, etc.), output requested by a type-out command is still performed unless two levels of ^W output-inhibition are in effect. This makes it possible for valret strings and command files to perform output without having the type-out commands echo.
Bit typeout mode makes it possible to interpret a word in terms of particular sets of related symbols. For example, a word can be decomposed into a sum of several symbols that are the names of flag-bits in a particular location. The restriction is that all of the symbols' names must start with the same prefix, since that prefix is how DDT is told which symbols to use. When defining a set of bit names in a program, it is wise to make them start with a common prefix for the sake of bit typeout mode.
In addition to the prefix, bit typeout mode requires a byte decomposition pattern, such as the $T typeout mode uses. This tells DDT how to divide the quantity being typed into bytes. The symbols typed are not allowed to overlap the byte boundaries, and each one must completely account for the value of one of the bytes. This restriction usually prevents any trouble from unrelated symbols that happen to begin with the specified prefix.
The convention ITS uses for naming flag bits gives each flag word two prefixes, one for LH bits and one for RH bits. Therefore, bit typeout mode is actually applied to one halfword at a time. Each bit typeout prefix can specify that it applies only to a single halfword. In addition, it is possible to have two different bit typeout prefixes selected in DDT at a time, one for RH bits and one for LH bits. The mechanism is this: in DDT, there is a "main selected bit typeout mode" variable, and an "alternate selected bit typeout mode" variable. Each one can contain a prefix and a byte decomposition pattern. When bit typeout mode itself is enabled, the main bit typeout mode is used for whatever halfwords it applies to; when it does not apply, the alternate bit typeout mode is used if it applies.
When a new bit typeout prefix is selected, it normally becomes the main selected bit typeout mode. The previous main selected mode becomes the alternate.
The main and alternate bit-typeout prefixes are in ..BITS and ..BITS+1 as SQUOZE values.
The main and alternate byte-decomposition patterns are in ..BITP and ..BITP+1.
Assume from now on that the prefix is "%TX". It in fact may be any number of characters long, and is a prefix for bit names.
When %TX bit-typeout mode is set, the byte-decomposition mask is determined. This is the value of the symbol "%TX", if it is defined; otherwise, the value of "..B%TX", if that is defined; otherwise 525252,,525252 octal. (The byte-decomposition mask may also be set explicitly by specifying it as an infix argument to $? or $$?.) The byte-decomposition mask divides the word into fields in much the same manner as the $T mask does. If the byte-decomposition mask is negative, then it divides the word into fields. If it is positive, then its right half is divided into fields, and bit 3.1 determines the half (0 = RH, 1 = LH) which the mask applies to.
Bit-typeout mode is actually superimposed on other modes:
$H typeout types the left half using left-half bits, and the right half using right-half bits.These modes use bit-typeout iff the bit-typeout flag is set.$S typeout, if it converts itself to $H, follows $H rules. Otherwise, the instruction is typed as usual, except for the address field, which may or may not be typed as as bits, according to the type of instruction. For example, TLNE uses left-half bits, TRNE uses right-half bits, HRLI uses left-half, HRRI uses right half, HRRZ does not use bits, JRST does not use bits. These op-codes use left-half bits:
MOVSI HRLI HRLZI HRLOI HRLEI TL--These op-codes use right-half bits:MOVEI SETCMI SETMI HRRI HRRZI HRROI HRREI ANDI ANDCAI ANDCMI ANDCBI IORI ORCAI ORCMI ORCBI XORI EQVI TR--Op-codes which do not use bits always use the most recent setting by $A or $R. "?", which means "$H;", can always be used to see bits explicitly as left-half,,right-half, if $S doesn't give exactly what is desired. To see "the other kind" of bits, $$Q? can always be used.# mode uses $S mode after extracting the low seven bits, and so follows $S rules.
The bit-typeout algorithm proceeds as follows: for each field of the byte-decomposition mask, examined in order from left to right, which contains nonzero in the value being printed, use that field to mask the quantity to be typed out. Look up this value in the symbol table. If a symbol starting with %TX is found with that value, print it. Otherwise, look up the value consisting of a 1 right-justified in the field; if a symbol beginning with %TX is found, type out <n>*%TXFOO where <n> is the value in the field, typed as a number in the current output radix, and %TXFOO is the symbol found. Otherwise, look up a mask for the whole field; if a symbol beginning with %TX with that value is found, type out <n>&%TXFOO, where <n> is the number being printed in bit mode, AND'ed with the field being handled. In this case, <n> and <n>&%TXFOO have the same value. The &%TXFOO is there to indicate what the <n> means. If none of those alternatives is successful, the field can't be typed with bit typeout mode. After trying to use bit typeout mode on each of the nonzero fields, those that failed, if any, are typed as a single address, absolutely or relatively according to the current typeout mode.
If left-half bits from a full-word byte-decomposition mask are being used to print out a half-word, the names of the bits are enclosed in parentheses. Thus:
TLNE TT,(%QXABC+%QXDEF+%QXGHI)Example:
201140000701 %TX$?#would type out
MOVEI C,%TXMTA+%TXCTL $1#Aor something like that.
Example: consider these definitions in a program:
%QXSYM==400000 ;funny bits %QXLET==200000 %QXNUM==100000 ;some bits not defined %QXCNT==7000 ;a field $QXERS==700 ;another field %QXERS==100 ;name for its low bit $QXQTY==77 ;another field %QXQTY==1 ;name for its low bitThen these quantities would type out as follows:
Quantity Bit Typeout
43 43*%QXQTY 123456 %QXNUM+3000&%QXCNT+4*%QXERS+56*%QXQTY+20000 500000 %QXSYM+%QXNUM 665400 %QXSYM+%QXLET+5000&%QXCNT+4*%QXERS+60000
There are predefined bit typeout prefixes for all the the series of system symbols starting with "%"; for example, "%TS" for the symbols %TSFRE, etc., for the bits in TTYSTS variables. In addition, there are the prefixes .R and .S which make it easy to find out which variable a .SUSET or .USET is reading or setting. Prefixes ..R and ..S serve the same function for .BREAK 12,'s.
Besides its memory, a job has many other variables which contain part of its status. Both DDT and ITS keep information about the job. DDT makes this information accessible to the user by allowing "pseudo-locations" that appear to contain the information, and which can be examined and deposited in as if they were part of the job's actual address space. Each frequently useful variable has its own symbol, whose value is the pseudo-location containing that variable. For example, .PIRQC is defined to be the pseudo-address of the current job's interrupt request word, which can be examined with .PIRQC/ and altered with <newstuff><cr>.
These "funny symbols" are the only symbols whose values are not precisely like numbers; the value of a funny symbol includes both a number and a flag indicating whether the value is an ITS variable or a DDT variable. In the case of a DDT variable, the numeric part of the value is the address in DDT where the information is actually stored. Some of the DDT pseudo-locations that have predefined symbols contain information associated with a single job, while others apply to all jobs or have nothing to do with specific jobs. The symbols for job-specific pseudo-locations have different values (point at different words in DDT) depending on which job is current. Funny symbols can't be defined by the user; the predefined ones are all there are. A complete list is in the reference section "Specially Used Symbols".
Another command that provides information about a job useful in debugging is :ERR, which can be used to decode the last system call error received by the job, or to tell the meaning of a specific system call error code. See the reference section.
DDT remembers several of the quantities most recently read or printed. The user can access those saved quantities without going to the trouble of typing them in full. The remembered quantities are stored in two "ring buffers", one for addresses opened, and one for values found by examining, printed out, or deposited into memory. Addresses or values that would otherwise be thrown away are pushed onto the front of a ring buffer, where they remain until squeezed out the back by later arrivals. Thus, the ring buffers always contain a record of a certain amount of recent history, ordered latest frontmost. The saved history can then be accessed by commands specially provided for that purpose.
Every value found by examining memory, deposited into memory, or printed out by a "retype using ... mode" command such as "=", ";", or "_", is pushed onto the value ring buffer. The contents of the ring buffer are accessible via the various forms of $Q:
One use for $<n>Q is to move a series of words up or down one word. That is done by depositing $2Q into each word:
103/ 51 0 104/ 57 $2Q 105/ 3 $2Q
When depositing into 104, $Q would be 57, $1Q would be 0, and $2Q is 51. The 104 does not count because it is used as an address, and will go on the address ring buffer instead of the value ring buffer.
The address ring buffer works in a more complicated way, because of heuristics designed to maximize its usefulness. When a location is opened, it is usually pushed onto the address ring buffer, but there are some exceptions. For one, if the address is the same as the one already at the front of the ring buffer, it is not pushed a second time. For another, the <lf> and ^ commands do not push a new address; they just replace the address at the front of the ring buffer with the new address, 1 larger or 1 smaller. These actions are designed to make the ring buffer remember history at a slightly higher level, ignoring small changes to have room for more big ones.
The address at the front of the address ring buffer is always the value of the special symbol ".". Thus, "./" will reopen the last word opened, and ".+2/" will open the second word down from it. Aside from that, the address ring buffer is accessed destructively, by discarding recent addresses from the front to get at the earlier addresses behind them.
This is an overview of the commands useful for controlling the execution of a job being debugged.
The basic commands for controlling execution are still important: $G to start the job at the program's start address, ^Z and ^X to stop it, and $P and ^P to resume execution.
DDT can impose on an inferior conditions under which it should stop executing. There are several types of conditions available. Breakpoints stop the inferior if it tries to execute a specific instruction; the "MAR" stops the inferior if it tries to refer in any way to a specific location. The user can supply further restrictions on a breakpoint or the MAR - that is, cause the breakpoint or MAR condition to be ignored unless certain other requirements are met - but cannot alter the fundamental way in which the breakpoint or MAR is triggered. Each job has eight breakpoints, and only one MAR (a hardware limitation), each of which can be set or disabled. The breakpoints are numbered from 1 to 8. The MAR and breakpoints are described in detail in later sections.
Also, the user can make the job stop before each system call by putting zero in the pseudo-location ..SYSUUO. When the job stops for such a reason, DDT gives "SYSUUO;" as the reason. $P will make it proceed on, but the next system call will make it stop again. Similarly, putting 0 in ..PERMIT will make the job stop before all .VALUEs, suicide attempts (.BREAK 16,), and .BREAK 12,'s that would write in DDT, with "DDTWRITE;" as the reason.
"Stepping" is running a job "slowly", so that its actions can be observed. DDT has commands to step either one instruction or one subroutine call, and to run through a program by repeated stepping. They are described in the section "Stepping".
Some other execution-control commands are these:
For each inferior of DDT, there is one MAR, with which the user can make the inferior stop on referencing one particular word of memory. The MAR can be restricted to certain types of references, and an arbitrary conditional instruction can be supplied.
When the MAR traps (or "is hit"), the job may be stopped either before or after executing the instruction, according to the mood of the hardware (on KL's, it is always before, which is useful; on KA's it is usually after but can sometimes be before). In either case, the job's PC will be "correct", so that it will not skip an instruction or execute one twice. But since the next instruction to be executed might not be the one which tripped the MAR, DDT in addition to that instruction prints the instruction which hit the MAR, and its address. Those two instructions might or might not be the same. When the MAR aborts the instruction that trips it, that instruction will be temporarily immune to MAR when the job is restarted (otherwise, it would be impossible to pass by that instruction).
An additional condition can be imposed on the MAR by depositing an instruction in ..MARCON. When the MAR is tripped, DDT will put that instruction in the job's memory and execute it; if it fails to skip, DDT will ignore this particular triggering of the MAR. One common thing to do is to test the sign of the word that a write-catching MAR is set on. ..MARCON is reset to 0 by $I commands to minimize confusion.
In addition, ..MARXCT allows you to specify DDT commands to be executed when the MAR is hit. ..MARXCT, if nonzero, should be the address in the job's memory of the ASCIZ string of commands. Like ..MARCON, ..MARXCT is zeroed by $I commands.
As said above, DDT gives every job eight breakpoints, each of which allows the user to make the job stop if it tries to execute one particular instruction. The breakpoints are numbered 1 through 8, and each one can be set on an instruction or disabled.
<addr>$B chooses the lowest-numbered breakpoint that is not already
in use; if the breakpoints are all in use, it is an error. In that
case, you can use :LISTB to find out what breakpoints are set, and
then clear some of them, or simply change their settings with
<addr>$<n>B. There are several ways to clear breakpoints:
When a job stops at a breakpoint (breakpoint 3, say) and returns to DDT, DDT's message to the user looks like
$3B; <pc> >> <insn><pc> is the job's PC, and will usually be the address where the breakpoint was set (but might be the address of an XCT instruction pointing there, etc). The status of this job in a :LISTJ would now be "3B".
Unless DDT is told otherwise, it will stop the job whenever a breakpoint is hit. However, for each breakpoint, you can change that. Giving a breakpoint a "proceed count" will make it count a certain number of hits before stopping the job. In addition, the breakpoint can have a conditional instruction, which will be tested each time the breakpoint is hit, and can make DDT stop the job sooner than the proceed count would require.
These more esoteric breakpoint features are accessible by modifying the four-word block in DDT that controls the breakpoint. There is a special way to refer to the beginning of breakpoint <n>'s four-word block: $<n>B is its address. $<n>B+2 holds the proceed count, and $<n>B+1 holds the conditional instruction. Ref $<n>B. In addition, after stopping at a breakpoint, <n>$P will continue and give that breakpoint a proceed count of <n> - it restarts the program not just until the breakpoint is next hit, but until the <n>'th time it is hit (of course, other things can still stop the program).
When a bug has been brought to bay, the simplest way to flush it out is to step through the program one instruction or a few instructions at a time, watching things happen. DDT has commands to make this easy to do. The Raid register feature (described in a later section) is especially useful while stepping.
The basic stepping command is ^N, which runs the current job for one instruction, and then stops it and prints the next instruction (the one that will be executed first if the job is started again). This is calle "one-proceeding". One-proceeding through a jump still executes only the jump; the job returns to DDT with the PC set to the address jumped to. ^N uses a special hardware feature that interrupts the inferior as soon as an instruction is completed.
Often one of the instructions in a path will be call to a
subroutine which is above suspicion. When that happens, one wishes to
regard the entire subroutine call as a single instruction and step
through it all at once.
Another useful operation is to run a program to a certain point. For this, temporary breakpoints are just the thing:
Lazy debuggers can tell DDT to step again and again, as if multiple ^N's or $^N's were being typed. This is called "multi-stepping" and is invoked with the command ^^. When DDT is multi-stepping, it prints one instruction and executes it, then prints the next instruction and executes it, until either the user types a command or a prespecified stop condition is met. There is a pause between the printing of an instruction and its execution, so that the user has a chance to see it and perhaps type a space to stop stepping. The available presettable stop-conditions include stopping before subroutine calls, stopping before system calls, stopping before jump instructions, and stopping before subroutine returns. In addition, multi-stepping can either step over subroutine calls instantly with a $^N, or step through the whole subroutine body with ^N's. The presettable options are called the stepping flags, and each job has its own settings of them. In addition, there are master settings used to initialize the flags of newly created jobs. The commands to alter the stepping flags are $^^ and $$^^; see the reference section for details.
Raid registers are DDT's display feature, named after the display- oriented debugger at SAIL which suggested them. Every job has several Raid registers, each of which can be used to cause automatic display of the contents of one location in a specified typeout mode. Every time the job returns to DDT, all of the Raid registers that are set are displayed at the top of the screen. Each register is displayed both in the mode remembered in the Raid register, and as a constant in the remembered output radix. The current mode has no effect, and is not changed.
The main Raid-register control command is $V. Depending on the arguments it can set or clear a Raid register, or simply change the typeout mode associated with a register already set. In addition, $V always redisplays the Raid registers, even if it does nothing else. <addr>$V sets a Raid register to display the contents of <addr>. Whatever typeout mode is current when the $V is done is remembered by the Raid register. If <addr> has the indirect bit or an index register in it, the address calculation will be done again each time the Raid register is to be displayed; this makes it easy to display, for example, the second word down on the stack (but you must use ",-1(P)", not simply "-1(P)". -1(P) equals -1+(P), which is NOT what you want. This screw is because, with no opcode preceding the -1, it is not truncated to 18. bits). <addr>$0V clears a Raid register set on <addr>. $<n>V sets the typeout mode remembered by Raid register <n> to the current mode. $V by itself simply redisplays the Raid registers. Other options exist; see the reference section.
Raid registers can be used for dynamic examination of the rate of processing in a running program, by displaying the rate of change of the contents of a location (assumed to contain an integer). :RATE <addr> sets a Raid register to display the rate of change of <addr>'s contents, in terms of increments per millisecond. :ATB <addr> displays the average time between increments, or the inverse of the rate of change. When watching a running program, the :RAIDRP command is very useful. :RAIDRP <#sec> tells DDT to redisplay the raid registers every <#sec> seconds, stopping if you type any character.
Also relevant are :RAIDFL, which deallocates all of a job's Raid registers, and the block of storage starting at ..RAID in DDT, which contains three words whose contents direct DDT's actions (see the reference section).
Sometimes DDT will think that a raid register does not need to be redisplayed (its contents have not changed), when in fact it has been erased from the screen by other typing. When this happens, you should type $V with no arguments. This command is special in that it always redisplays all of the Raid registers, even those which have not changed.
There are DDT commands to find all words in the memory of a job whose contents meet a specified condition. The condition may be that certain bits do or do not all match a pattern, or that the word, regarded as a PDP-10 instruction, have a particular effective address (using the current contents, in the job, of any index registers and indirect address words required in the address calculation).
There are actually eight different masks for word searches. Unless otherwise specified, mask number zero is used; that is what has been referred to as "the mask". But one can specify any of the other seven masks explicitly in an $M, $W or $N command with an infix arg: <mask>$<n>M sets mask <n> and <value>$<n>W searches using mask <n>. The other seven masks are useful mainly because they are initially set up to important subfields of a word. Mask 1 is set up to the right half; mask 2, to the left half. Masks 3, 4 and 5 are initially the AC field, index field and opcode. The eight masks are stored in a block in DDT starting at address $M, so that $M+3/ will examine mask 3 and allow you to change it.
The "patch feature" makes it easy to "insert" instructions into programs without reassembling them. In fact, what happens is that jump instructions are used to replace one instruction with several (possibly including a copy of the instruction replaced). Patches are inserted "carefully" so that there is no danger that a running program will try to execute a "half-made" patch and crash. Also, provision is automatically made for instructions that skip. The instructions in the patch are stored in the job's "patch area", a spare area allocated specifically to such use. Every program should allocate one. The beginning of the patch area is the value of PATCH if it is defined, or the value of PAT if it is defined, or 50 . As patches are made, PATCH will be redefined to point to the next free location in the patch area. If symbol table block structure is in use, PATCH must be in the global block to make sure that at any instant the same value of PATCH obtains in all blocks. To make sure of this, define it as global, even in an absolute assembly.
An ordinary patch is begun with the ^\ command, and ended (and made effective) with either ^] or $^]. The two main schemes of patches, patches "before" an existing instruction and patches "after" one, will now be described:
To patch "before" an existing instruction, open that location and type ^\. DDT will now open the patch area and reprint the instruction being patched over. Type a <rubout> to get rid of it. Then simply deposit the instructions to be inserted before the existing one, depositing the first instruction in the location opened by the ^\. After typing the last instruction, don't deposit it with <cr> or <lf>; instead, use $^]. $^] will deposit the new instruction, followed by a copy of the instruction being replaced, and the two JUMPA's back to the two locations after the one being patched. When that is done, the patch is finished. Here is an example: assume that 105 contains ADDI A,1 before which LSH A,1 must be inserted. Typing 105/^\<rubout>LSH A,1$^] produces this printout:
105/ ADDI A,1 ^\ PATCH/ 0 ADDI A,1<ADDI A,1> LSH A,1$^] PATCH+1/ 0 ADDI A,1 PATCH+2/ 0 JUMPA 1,106 PATCH+3/ 0 JUMPA 1,107 105/ JUMPA 2,PATCHTo patch "after" an instruction, two things must be changed: The instruction must be deposited as the first of the patch, and it must not be put in at the end. Avoiding a copy at the end is done by using ^] instead of $^]. Putting a copy at the beginning is easy since DDT is already trying to supply one; just deposit it with <lf> instead of rubbing it out. Consider putting the LSH A,1 after the ADDI A,1 instead of before it. Typing 105/^\<lf>LSH A,1^] will do it, and produce this printout:
105/ ADDI A,1 ^\ PATCH/ 0 ADDI A,1 PATCH+1/ 0 LSH A,1^] PATCH+2/ 0 JUMPA 3,106 PATCH+3/ 0 JUMPA 3,107 105/ ADDI A,1 JUMPA 2,PATCHNotice that JUMPA 3, is used to return from the patch, instead of JUMPA 1,. JUMPA ignores its AC field, and the different numbers are used only as flags describing how to unmake the patch. The command $$^\ exists to do that - see the reference section.
If you forget to follow the last instruction of the patch with ^] or $^], and deposit it instead with <cr> or <lf>, you need not worry; just type the ^] or $^] and it will contrive to do the right thing. What is the right thing? After <insn><cr>, no location is open, and the right place for the first JUMPA is .+1. After <insn><lf>, the open location is the right place for the first JUMPA. So ^] with no argument, if there is a location open, stores the first JUMPA there; otherwise, it stores the first JUMPA in .+1. $^] acts similarly, except that it is the instruction being patched over, rather than the first JUMPA, that goes in the appropriate location. You can always count on being able to end a patch, no matter what has transpired, by opening the first word of the patch area whose contents should be clobbered by the return sequence, and then doing the ^] or $^].
DDT offers programs executing under DDT's control several services. There are defined conventions for normal termination, error reporting, reading or writing the DDT's information about the job, and passing DDT commands to execute.
Passing DDT a string of commands to execute is known as "valretting". It is done with the .VALUE instruction, actually a system call to cause a fatal interrupt which DDT responds to in a conventional way. The effective address of the .VALUE should point to an ASCIZ string containing the DDT commands. The conventions for those commands have been discussed above ("DDT Commands from Files"). Valretting is the most general way for a program to make use of DDT, and for that reason it is the ugliest way. Valretting can be done only by a job that has been given the control of the terminal; if a job which is running without the terminal tries to valret, the job is stopped and cannot actually valret until $P'd by the user. In addition, programs which valret are dependent on running under a DDT, since other superiors will probably be unable to make any sense of the valretted DDT commands. For these reasons, valretting should be used only when necessary. The ..PERMIT pseudo-location can be used to prevent a misbehaving program from valretting, or obtaining any service from DDT which might make DDT do unpredictable things. See the reference section.
A program can indicate normal termination to DDT with the .BREAK 16, instruction. The address field of the .BREAK 16, can be used to request various termination services from DDT. Unlike valretting, .BREAK 16, operations are understood to some degree by many of the programs that handle inferiors, and are thus safe to use in most programs. The address field is decoded bit by bit. Here is a list of what the bits mean. Bits that are good for general use are starred.
bit meaning if on 2.9 $X return, used by DDT to implement the $X command. * 2.8 type an extra carriage return in DDT. Normally, .BREAK 16, causes DDT to type <cr><lf>*. With this bit, two <crlf>'s are printed. This is used to indicate that the program "did something" - it looks like what $X does when an instruction skips (can you guess why?). * 2.7 do not reset teletype input (effective only if job has TTY) If this bit is not set, any typed-ahead input will be discarded, and any execute file or valret in progress will be suspended. This bit should always be used unless the program is reporting some sort of error. * 2.6 kill this job, because it is finished. The job is killed immediately if it owns the terminal. Otherwise, it is killed when it is either selected with $J (with no arguments) or given control of the terminal. In any case, when the job is killed, DDT types ":KILL ", unless bit 2.5 or 2.3 is set. * 2.5 kill this job as soon as possible. The job is killed instantly unless it is current but doesn't own the terminal. That exception is because it is very embarrassing for the current job to vanish while you are in the middle of typing a command. If the job is current and has the terminal, DDT prints ":KILL " as for bit 2.6. If the job is not current, it is killed immediately, with no notification to the user. 2.5 and 2.6 both set are slightly different from just bit 2.5: if the job is not current, it is killed instantly, but the user is informed with a "Job <jname> Finished" message. 2.4 conditional breakpoint return, used by DDT to implement MAR and breakpoint conditional instructions. Bit 2.8, if on says that the condition is true. * 2.3 inhibits all typeout associated with the .BREAK 16,. Bit 2.3 may be combined with the other options. It prevents the normal printout of <crlf>*; it prevents the printout of ":KILL " if the job is killed. In addition, if this bit is set, the open location is not closed. DDT uses bit 2.3 when invoking user-defined typeout modes, but its effects are simple enough to be described, so it is O.K. to use for other reasons.
If ..PERMIT is nonnegative, .BREAK 16,'s that kill the job or type nothing out are illegal, and DDT treats them like .BREAK 0,'s.
When a disowned job wishes to kill itself, it can do .LOGOUT. When an inferior wishes to kill itself, it can do .BREAK 16,160000 . If a program does not know whether it is disowned, and wants to kill itself in any case, it can do
.LOGOUT 1,
which acts like an ordinary .LOGOUT if the job is disowned, but interrupts the superior if there is one. DDT treats it just like .BREAK 16,160000 when it is executed by an inferior.
Often a program encounters an unexpected error in a system call and has no idea how to recover from it. In such a situation, the error should be reported to the user, so that he can eliminate the source of the trouble and tell the program to try again. ITS provides a system call LOSE and a UUO .LOSE as the conventional way to report such an error, and DDT responds to those instructions by describing the error to the user. Other superiors, such as batch job controllers, might want to try on their own to recover from the problem as reported. When .LOSE is suitable, it is preferred to alternatives which involve use of the terminal.
The .LOSE UUO is intended to follow an instruction which skips if there is no error. .LOSE backs up the PC to point once more at that instruction which failed to skip, and then causes a fatal interrupt which brings DDT onto the scene. Because the PC has been backed up, if the job is $P'd the problematical instruction will be executed once more, and if the obstruction has been removed in the meanwhile the program will proceed with its task. The most common instruction to put before a .LOSE is a system call:
.OPEN CHN,[SIXBIT / DSK/ ? SIXBIT /FOO/ ? SIXBIT />/] .LOSE %LSSYS ;report failure of the .OPENthe %LSSYS, or the address field of the .LOSE in general, says what kind of error happened, or where to find that information. %LSSYS in particular tells DDT to print an error message based on the last system-call error code. The allowed codes are described below.
For situations where .LOSE is too restrictive, the symbolic system call LOSE exists. Symbolic LOSE allows the new PC value to be specified explicitly, and therefore is suitable for use inside an error-handling routine. In addition, the address of the "culpable" instruction can be specified, although it defaults to the new PC value. Thus, the program can provide more complicated error recovery than simply restarting at the losing instruction. For details on symbolic LOSE, see .INFO.;ITS .CALLS.
The permissible values of the address field are subject to and given their meanings by a convention enforced by DDT's interpretation of them. They are:
The .VALUE instruction, with address 0, is the usual way to report an "impossible" occurrence. Unlike .LOSE 0, it leaves the PC pointing to the instruction after it, so the program can be $P'd with mimimum effort. Thus, .VALUE 0 is also useful for errors which are not very important, so the user might wish to proceed despite them. It is also used when the preceding instruction is of no particular relevance to the error, and there is no point in backing up the PC to it.
Inferiors may read and write various information from and into DDT using the .BREAK 12, instruction. The format of the arguments to .BREAK 12, is like that for .SUSET; the difference is that .SUSET is used for variables in ITS, and .BREAK 12, is used for variables in DDT. The .BREAK 12, should point at a word that has a sub-operation code in the left half, and the address of the area to be read or written in the right half. Alternatively, the word may be an AOBJN pointer to a block of words, each containing a sub-operation code and an address. The sub-operation code consists of a small number describing the type of information being read or written, and a read vs. write bit, which is 400000. Thus 400001 specifies writing the starting address. Writing with .BREAK 12, is not allowed if the job's ..PERMIT variable holds a positive number. The valid sub-operation codes have customary symbols defined in both DDT and MIDAS, starting with "..", followed by "S" or "R" indicating setting or reading, followed by three more characters mnemonic of the type of information. Here is a list of the defined sub-operation codes, followed by a more detailed description of them.
# symbols meaning
0 illegal 1 ..RSTA,..SSTA read or write job's starting address. 2 ..RLFI loaded file name (4 words: device, SNAME, FN1, FN2) (read only). 3 ..RSTP read DDT's symbol table pointer for this job. The left half is minus the length of the symbol table. 4 ..RSYM,..SSYM read or set value of a symbol. 5 ..RJCL,..SJCL read or clear the job's :JCL command. 6 ..RPFILE,..SPFILE read or set DDT's default :PRINT filenames. 7 ..RSTB,..SSTB read or write the whole symbol table. 10 ..RCONV read the symbolic equivalent of a number. 11 and 12 illegal (their old meanings were obsolete) 13 ..RLJB read the job number of the previously selected job. 14 ..RRND,..SRND read or set per-job miscelaneous flags. 15 & up illegal
Types 1, 3, 11 and 12 read or write one word, at the address pointed to by the right half of the operation word. The other types read or write more words.
Type 1 reads or writes the start instruction, whose right half is the start address, and whose left half should be JRST or JUMPA. The start instruction may also be 0, meaning that there is no start address.
Type 2 stores 4 words starting where the right half points.
Type 3 stores a quantity whose left half is minus the size of the inferior's symbol table in DDT. The right half is the address in DDT of the symbol table, but it is a mistake to use that since DDT can shift the symbol table at any time.
Type 4 assumes, on read, that the right half points to a word with a SQUOZE symbol in it. If the symbol is defined, its value is stored in the location following the symbol. If the symbol is ".", the value of "." is returned. If the symbol is undefined, zero is stored where the symbol was and the following location is unaffected. A type 4 write defines the symbol pointed to by the right half to have the value specified in the location following it.
Type 5 allows an inferior to read its command string (set by :JCL <string> or :<prgm> <string>) from DDT. The job's .OPTION variable's bit 4.6 (OPTCMD bit) will be set by DDT if a command is available. If you don't try to get a command when that bit is off, you'll have no trouble being run by programs other than DDT. See "Running Programs", and the :JCL command, for information on how the contents of the command string are set. The string is transferred to the inferior as packed ASCII. The first word is always transferred. Successive words are transferred until either the previous word transferred was zero or the word about to be transferred into is nonzero. Note that the terminating character of the JCL will be either ^M, ^C or ^_, and that command string is not necessarily in upper-case. Type 5 write zeros the command string.
Type 6 reads or writes a block of 4 words as follows: device, SNAME, FN1, FN2 (all left-justified SIXBIT).
Type 7 is like $$^Y, with the argument in the call used as the argument to the $$^Y (ie as the address of the AOBJN pointer to the table to be replaced). Writing info type 7 is like doing a ^Y, with the arg to the call pointing to the AOBJN pointer to feed to the ^Y.
Type 10 provides the essential part of DDT's symbolic typeout. The argument is a number. It is replaced by the SQUOZE code for the symbol whose value is closest to but not larger than the number, or 0 if there is no such symbol. The word after the one containing the argument receives the difference between the argument and the value of the symbol, or the argument unchanged if there was no symbol.
Type 13 allows a program to find out what job was current when it was invoked. Thus, you can write programs which examine the data structure of the current job, as if they were DDT commands. Type 13 returns the job number of the job which was selected just before the one selected now. If you do this at random times (not just after being invoked with a colon) then that value has no significance.
Type 14 reads or writes a word of miscelaneous flags in DDT. At the moment, there is only two flags:
Bit 1.5, which, if set, means that :NOMSG 0 should be in effect while this job has the tty. Programs which print listings or graphs on terminals might want to set this bit to make sure that they are not spoiled by messages. Any unsolicited typeouts blocked by this flag are printed when next the job returns to DDT.Bit 1.6, which, if set, means that while this job has the TTY, other jobs will not be allowed to type out. It does this by clearing its %TBINF bit in its .TTY variable. (see ITS USETS documentation of .TTY) Thus, jobs with their %TBOUT set (I.e. $$^P'd) will be inhibited from typing out.
An illegal .BREAK 12, (this does not include undefined symbols) causes DDT to give a %PIILO interrupt to the inferior. For a block mode .BREAK 12, the AOBJN pointer is counted out and stored back.
Often in writing a DDT init, you want to read a single line of input, and do something based on that. INLINE is a program (not a "DDT Command"!) to do just that.
A couple of examples will make this clear. in a init file:
:$^VWhere Are You? ^W$:INLINE :TTYLOC@key{cr}will type out
"Where Are You? "and the user may then type "836A Hacker's Haven<cr>" and it will do a
:TTYLOC 836A Hacker's Haven
Note that the space is inserted between the ":TTYLOC" and the "836A Hacker's Haven". This can be overcome by ending in a rubout.
using the :X program:
:X :INLINE :DELETE FOO;BARwill save away the :INLINE :Delete FOO;BAR in a file _X <uname> (see the documentation on the X program) and run it. :INLINE will let you add second file-names onto the end of the :Delete FOO;BAR Thus, after typing the line above beginning with ":X", you can type BUZZ and it will do :DELETE FOO;BAR BUZZ After the first time, you can type X^K BAZ and it will do :DELETE FOO;BAR BAZ
There are a few characters that are special in typein: