STANFORD ARTIFICIAL INTELLIGENCE LABORATORY Rr DDT FOR THE PDP-11 Jeff Rubin (with additions by Bo Eross) DDT is a symbolic debugger written for the PDP-11. Its command structure is quite similar to that of PDP-10 DDT except where machine differences dictated otherwise. The current version of DDT runs on a PDP-11/45 with a floating point processor (FPP) but with no memory management unit. It is expected that DDT will be revised when the page map is installed on the 11/45 and also for the 11/40 which will come with the KL-10. All numbers in this manual are assumed to be in decimal unless otherwise indicated. The letter  will be used indicate alt-mode unless the text specifies differently. The character ^ will be used directly in front of an uppercase letter to indicate that that letter should be typed with the control key held down. { and } are used as meta parentheses and are not meant to be typed in or typed out. 1 Examining and Altering Locations 1.1 Expressions DDT parses arithmetic expressions in either integer or floating point. Mixed mode is not allowed. Expressions may be fully parenthesized and are interpreted using a strict precedence scheme. The operators are: Precedence Operator Operation 0 ,, high byte,,low byte 2 + or space binary addition 2 - binary subtraction 3 * binary multiplication 3 ! binary division 4 - unary minus + used as a unary operator is ignored. Operations associate to the left where the precedence is the same for both operations. Operands may be integer, floating point or text constants, symbol names or special symbols. 1.1.1 Integers Integers are assumed to be in octal unless they are immediately followed by a decimal point. Integers must fit into a 16 bit field. 1.1.2 Floating Point Numbers DDT accumulates floating point numbers in double precision. Floating point is indicated either by having digits both before and after a decimal point or by using the standard E notation. In E notation, there must first be an integer optionally followed by a decimal point and another integer followed immediately by an E and an (optionally signed) integer power of 10 to scale the number by. All the integers in a floating point number are assumed to be decimal. The following are all valid floating point numbers: 0.12 105.0 3.14E-6 0.21718E1 105.69E+4 1.1.3 Text Constants There are three types of text constants: single character ascii, double character ascii and radix 50. '{char} has the value of the ascii character char. "{char1}{char2} has the ascii value of char1 in the low byte and the ascii value of char2 in the high byte. &{char1}{char2}{char3} has the value of the radix 50 equivalent of the symbol {char1}{char2}{char3}. If it is desired to not have all three characters in the value then fewer characters may be given as long as they are followed by some non radix 50 character. In this case the resulting radix 50 value will be left justified within a word. The following table lists the correspondence between ascii and radix 50: ASCII RADIX 50 (in octal) A - Z 1 - 32 $ (dollar) 33 . 34 % 35 0 - 9 36 - 47 1.1.4 Symbols and Register Values Symbols may be constructed with from 1 to 6 radix 50 characters. The first character of a symbol may not be an integer and it may not be . if all the remaining characters are integers. DDT maintains a symbol table and the symbol typed by the user is looked up in this table. If it is found, then the corresponding value from the table is used in the expression evaluation. If not then the message ?U? is typed and the current expression evaluation is aborted. Symbol values may be either integer or register values. A register value, when used in an integer expression, causes a flag to be set so that the resulting expression value is considered a register value. Register values must be between 0 and 7 inclusive. Register values can only be typed in as a result of typing a symbol which has a register value. See section 1.1.5 on special symbols for a description of predefined symbols which have register values. Symbols may be defined in two ways. First, typing FOO: defines FOO to have the value of . (see section 1.1.5 on special symbols). If . is a register value then FOO will be defined as a register symbol. The second way is to type valB, which clears the breakpoint at , if there is one. The command B will clear all the breakpoints. If you try to set a breakpoint without specifying a number and all seven are in use, then the message ?TMB? is typed and no action is taken. 2.2 Starting Programs The command addrG starts the program at addr. addr may be any expression whose value is a non-register integer. If the program had a starting address (i.e., if there was a label after the end statement in the source code) then the command G will start the program at its starting address. Finally, the command addrG will start the program at addr and will also set the starting address of the program to addr so that future G commands will start the program at this same address. 2.3 Proceeding Programs Once a program has been started it may reenter DDT in three ways. First it may hit a breakpoint. Second, it may execute a BPT instruction. Third, it may transfer to DDT's starting address, which at this time is 50000 (octal) although this may change when the map "arrives". The second method is generally preferable to the third, for the following reasons: you don't have to know the right starting address, and the breakpoint trap procedure saves your program's status so that you can tell where in your program the call to DDT was. If DDT is entered via a breakpoint, it will type the breakpoint number followed by the instruction at that address followed by the contents of the location that was in parentheses in the breakpoint command. Entering by a BPT causes a typeout similar to that for a breakpoint, identified by the letters BE at the left margin. You can proceed from a breakpoint by using the P command, which will resume execution at the interrupted instruction. There are two variations on this command. First, nP will proceed from this breakpoint n times; however, it will break at any other breakpoint it may hit before the n times are up. After breaking the nth time, control will stay in DDT as if it had hit that breakpoint normally. The second form is P which will proceed all breakpoints indefinitely. However, the standard breakpoint information is typed out at each breakpoint and the process may be stopped by typing any character at DDT (the character itself is ignored). There is a second proceed command included for convenience. Typing ^P has the effect of planting a breakpoint at . and then proceeding. The breakpoint will be automatically removed when it is hit. Typing ^P sets the temporary breakpoint at the location addressed by (without changing the value of .) and proceeding. Note: there is only one such temporary breakpoint. When your program runs into it, DDT will identify it as breakpoint 8. Also, if you already had a breakpoint set at the location that ^P affects, the original breakpoint is lost, along with the specification (if any) of a location to type out. If you hit the ^P breakpoint while P is in effect, it will stop as if you had typed something. ^Q does like P, continuing automatically from all breakpoints, but it doesn't type out. Typing anything will cause it to break at the next breakpoint. 2.4 Single Stepping After a program hits a breakpoint, it is often desirable to watch what the code does one instruction at a time. It would be cumbersome to plant breakpoints at each succeeding instruction, proceed and then remove the breakpoints. To ease this problem, there is a single step feature. Typing ^X will execute the next sequential instruction, then type out "SS;" followed by the new value of the PC and the next instruction to be executed. The value of . is not changed. If there is a breakpoint at the next instruction, DDT will type the breakpoint message instead of the one-step message. The commands ^N and ^S are exactly the same as ^X. Single stepping is legal after having hit a breakpoint or can be begun by typing ^S (or ^X or ^N), which sets PC_, then one-steps. 2.5 Debugging interrupt code DDT attempts to be as flexible as possible in the way it handles breakpoints and one-stepping, so things may get pretty confusing at times. For example, suppose you just said ^S when your program's priority level is low and an interrupt is pending. What happens is (1) DDT sets the Trace Trap bit and does a RTT to get back to your program. (2) The 11 is about to fetch your instruction, but it notices the interrupt and answers it. (3) The interrupt procedure starts out by loading a new PSW which doesn't have the Trace bit on, so the service routine just runs away at full speed. (4) The interrupt routine ends by doing a RTI. This loads up your PSW again, with the Trace bit on. The 11 is again about to fetch your instruction, but it sees the Trace bit so it takes the trace trap. (5) And now we're back in DDT, and the PC is still pointing at the instruction you wanted to step, which was never executed. You might think, all right, let the interrupt routine return using RTT instead of RTI. That will have the right effect if the interrupt was pending when you did the ^S, but if it happens after your instruction was fetched the result will be that the next one will be stepped, too! It's a design flaw in the hardware... complain to DEC. However, you can have a breakpoint in an interrupt routine while you're one-stepping your main program. Then when you step and the interrupt strikes and hits its breakpoint, DDT will astonish you by typing out the breakpoint message instead of the single step you expected. You can step the interrupt routine if you like, or proceed it, and the trace trap will happen when it returns and DDT will finish up the one-step you started way back there. One warning, though... if you have a breakpoint at the RTI or RTT that ends the interrupt routine, you should either remove it or STEP that instruction. If you proceed it, DDT will forget about the original one-step and wind up proceeding your main program. If you set a breakpoint at a TRAP or EMT instruction, you should clear it before you proceed or step. Otherwise, the trap service routine may see a BPT opcode instead of the TRAP or EMT that you specified. By the way, the SPL instruction has the side effect of suppressing all interrupts including the trace trap during the next instruction fetch. Because of this effect, DDT checks to see whether the target of a ^S is a SPL. If it is, DDT simulates the SPL by changing your priority without returning to your program, and pending interrupts can't strike during that process. 3 Special Locations in DDT Several locations inside DDT are available by way of predefined symbols, to give you some control over DDT's workings. 3.1 Program Status Word (%PS) If you refer to %PS you get your program's status word. You may modify any part of this cell except bit 4 (the Trace Trap bit, octal 20). DDT uses bits 14-15 to decide which of the three stack pointers to give you when you ask for %6, and bit 11 to decide which of the two general register sets you mean when you talk about %0 through %5. There is no check for whether you set the processor state to a legal value - that's up to you. 3.2 DDT's Status Word (%DDTS) This word is the PSW that DDT uses. You may change bits 5-7 only, determining the priority level that DDT runs at. This allows you to run an interrupt-driven routine in the background while you're talking to DDT. WARNING! The background code that is capable of interrupting DDT had better not have any breakpoints or BPT instructions, or a re-entrant call to DDT will happen and things will get very confused. %DDTS is assigned the absolute location HCOR-12, where HCOR is what DDT thinks is the first nonexistent memory location (100000 on our 16K system). 3.3 Teletype switch (%TT10) This word, located at HCOR-6, determines which way DDT's input and output will go. If the contents of %TT10 are zero, I/O goes to your console via the 11TTY program; if nonzero, I/O goes to the PDP-11's console terminal. You may change this switch at any time, either by command or by program, and DDT will immediately begin using the device you specified. A great convenience if you left 11TTY's "V" switch in the wrong state when you loaded your program. 3.4 Floating-point Registers (%AC0 through %AC5 and %FPS) The symbols %AC0 through %AC5 refer to the locations (four words each) where DDT stores the floating-point accumulators. Note that these are memory locations and not the registers themselves. If you refer to these locations it is up to you to make sure that the typeout mode is appropriate to the data. %FPS gets you the location where DDT stored your program's floating-point status register. If you change the contents of these locations the right thing will happen when you proceed or step your program. 3.5 Maximum symbolic offset (%MXOFF) This location contains the upper limit on the numeric offset that DDT will type out when printing an address. The default value is 100 - that is, if FOO=1000 and there are no other symbols defined between 1000 and 1100, DDT will type the address 1077 as FOO+77 and 1100 as 1100. You can change the limit on these offsets by changing the value in %MXOFF.