.alone←declaration(expurgate)=0; .if alone then start .require "<altosource>ttydefs.pub" source!file .end .every heading (|SWAT|,|April 10, 1982|,{page}) .once center ~Swat~, a BCPL-oriented debugger .tabs 5,10,15,20,25,30,35,40,45,50,55,60,65,70,75 .skip 3 Swat is a debugger meant to be used with the Alto operating system. While many of its features are BCPL oriented, it can be used on any Alto program. This document describes version 31 of Swat, which is compatible with Operating System versions 17 and greater. .sec(History) Swat was designed and built by Jim Morris and Allen Brown during the summer of 1973. Bob Sproull added the error file mechanism and parity error logging during 1976. Peter Deutsch rewrote the command processor and added the command file facility in early 1977. David Boggs renovated the program, adding mulitple proceed break points and TeleSwat, and Ed Taft added the help facility in late 1978. Everyone agrees that the human interface is awful. Each person who has worked on Swat has added several more obscure commands while they were at it. .sec(How it works) Swat is an external debugger: with the exception of a small piece of 'resident' code in your address space, it lives in a separate space. When Swat is invoked, the resident saves your state on the file Swatee, and swaps in Swat. References to your memory from within Swat go to the Swatee file. When you tell Swat to proceed, it saves itself on the file Swat, swaps you (the Swatee) in and resumes you. Your state at the time Swat got control is displayed in a window at the bottom of the screen. "AC0", "PC", etc are built-in symbols with which you can manipulate it. .sec(Invocation) Swat may be applied to any program running under the operating system after it has been installed (see Installation below). There are six ways of getting its attention: .begin nofill (1) Hold down the <control> and <left-shift> keys and then press the <Swat> key. (2) Have your program execute the op-code 77400B. (3) Invoke the ~Resume~/S command (see below). (4) Boot the file ~Dumper.Boot~, normally by booting with the "DU" keys depressed. (5) Type <programName>/! to the Alto command processor. (6) Call the function CallSwat. Up to 2 arguments will be printed as BCPL strings. Thus CallSwat("No more memory") .end .sec(Commands) The command scanner has suffix action symbols, all of which are control characters (e.g. ↑C). "n" is any BCPL expression (see Expressions below), "$" is escape except where noted, "cr" means carriage return, "lf" means line-feed. You can abort whatever Swat is doing at any time and get back to the top level command scanner by pressing the <Swat> key. .ssec(Help facility) Most debuggers have a terse and obscure command syntax, and Swat is no different. In fact it's worse since Swat doesn't use the DDT debugger dialect (it uses a subdialect of an MIT debugger dialect--where Allen Brown grew up). Typing "?" prompts you for a command character which Swat looks up in the file "Swat.help". Responding "?" to its prompt gives you a small table of contents for the rest of the help file. .ssec(Displaying cells) .begin group nofill;indent 3;tabs 12 address↑D\ prints the contents of n in decimal address↑I\ prints the contents of n as two 8-bit bytes address↑N\ prints the contents of n as an instruction address↑O\ prints the contents of n in octal address↑S\ prints the contents of n as a pair of characters address↑V\ prints address in octal and decimal .end The last cell printed is called the open cell. ↑O, ↑D, ↑I, ↑N, or ↑S alone re-prints the open cell in the appropriate format. If you wish to print out a number of cells, beginning with the open cell, say n$↑D, n$↑I, etc. The last cell printed becomes the open cell. .begin indent 3,11;tabs 12 lf (↑J)\opens and prints the contents of the next cell (after the open one) in the same mode. ↑W\opens and prints the cell before the open cell. ↑A\opens and prints the cell pointed at by the open cell. ↑E\opens and prints the cell at the effective address of the open cell. .end The last cell that was opened by any command except LF or ↑W is called the last open cell. Often you are stepping through code, follow a pointer with ↑E or ↑A, look around, decide it's not interesting and wish to resume where you were before following the pointer. You can get back to last open cell plus or minus one by: .begin indent 3,19; tabs 20 $lf ($↑J)\open and print last open cell+1. $cr ($↑M)\open and print last open cell. $↑W\open and print last open cell-1 .end .ssec(Changing cells) The contents of the open cell (if there is one) may be changed by typing an expression for the new value followed by a cr, lf or ↑W. A$B followed by cr, lf or ↑W stores A lshift 8 + B into the open cell. .ssec(Searching) .begin indent 3,11;tabs 12 n↑=\searches from the open cell+1 for a cell whose contents is n. Prints and opens that cell. n$↑=\searches from the open cell+1 for a cell whose effective address is n. Prints and opens that cell. .end A search terminates at the end of memory (location 176777b -- the I/O area is not touched) and can take quite a while: abort by hitting <swat>. The argument for a search command is defaulted to the last value searched for if omitted. .ssec(Running the program) .begin indent 3,11;tabs 12 ↑P\resumes the program, i.e. proceeds. address↑G\resumes the program at address, i.e. goes there. <procName>$<e1>$...$<en>↑C calls the BCPL procedure "procName" with parameters <e1>,...,<en> (n<6). If you wish one of the arguments to be a BCPL-format string, merely enclose it in quotes. Thus OpenFile$"Com.Cm."↑C will return a stream on the file. AC2 is assumed to contain a legal stack frame pointer and 'procName' will allocate a new frame on top of it. Often AC2 is not valid (e.g., Swat interrupted the program in the middle of allocating a frame), and calling a procedure at this point may not work. Most of the time Swat can detect this and warn you. ↑U\restores the user's screen. Hitting the <swat> key brings back Swat. ↑K\forces the user program to abort, just as if you had typed <left-shift><swat> while it was running. .end .ssec(Break Points) A Break point can be referred to by its address or by the index assigned by Swat when the break point was set. When printing or deleting a breakpoint, Swat reaches out into the user's address space to check that the break is still there. .begin indent 3,19;tabs 20 address↑B\sets a break at address ↑B\set a break at the open cell 0$address↑B\deletes the break at address proceedCnt$address↑B sets a multiple-proceed break point at address. The breakpoint will take effect when it has been hit proceedCnt times, and then it will be deleted. Passing through a multiple proceed break point without stopping takes about 200 us. index$↑B\deletes the break with index index 0$$↑B\deletes all breaks $$↑B\prints all broken locations. $↑P\removes the current break and proceeds. address$$↑P\sets a one-shot break point at address and then proceeds. A one-shot break point is one that is removed after it is hit. stackIndex$↑P\sets a break at a BCPL return point in the stack somewhere and proceeds from the present break. The parameter n specifies the frame number, where the most recent (top) frame is 0. Thus if ↑T typed out 0:GOO+56 1:HAM+5, 1$↑P would set a break at HAM+6 and proceed. .end .ssec(Stack Study) See Chapter 10 of the BCPL manual and section 4.8 of the Operating System manual for the details of a BCPL stack. .begin indent 3,11;tabs 12 ↑T\prints the current PC and all return addresses in the call stack (symbolically), until an inconsistency in the stack (usually signaling its end) is encountered. After each return address is listed the parameters passed to the procedure that will be returned to. "2: 43752 137 0 Foo+45--(14 177777)" means the 2nd most recent frame at 43752, of length 137 is procedure Foo in bank 0, called with arguments 14 and -1 (fine point: 14 and 177777 are the first two local variables in Foo's frame, which Foo could have modified before Swat was called, in which case they won't be the values passed at call time). n↑T\traces a stack beginning with the frame at location n. index↑F\prints the parameters of the nth latest stack frame and sets the pseudo symbol "$" (not escape) equal to the base of that frame. If ↑T displayed something like 0:FOO+3, 1:BLETCH+10,... Type 1↑F to see the parameters that were passed to BLETCH. $ is set to the base of BLETCH's frame (i.e., $ points at the frame's back link: the first local variable is in $+4. .end .ssec(Symbol table) .begin indent 3,11;tabs 12 ↑Y\prompts you for the name of a symbol file. Type the name of the subsystem that's running. If it can't find a file with the name you typed, Swat appends ".syms" to it and looks up the resulting file name before reporting failure. If BLDR created the file FOO.RUN it also created FOO.SYMS, which gives the locations of all the static names. Only statics can be used in Swat. There are permanent built-in symbols for the interesting page-1 and high memory locations, BCPL runtime routines, and the user's state variables (AC0-3, PC, etc.). .end .ssec(Save/Restore) See 'Resumable files' below for more details: .begin indent 3,11;tabs 12 ↑L\prompts you for a file name on which it saves the current Swatee. ↑Q\prompts you for a file name which it installs as the current Swatee. .end .ssec(The Spy Facility) The spy can be used to estimate where the time is going on a percentage basis. It samples the PC every 30-milliseconds. .begin indent 0,6; tabs 7,17 (1)\Type ↑X and Swat will display how much user memory it needs for the metering code and tables. (2)\Probe around to find a block of storage of the required size, and tell Swat by typing \ \n↑X \where n is the first word of the block. (3)\Proceed to run the program. (4)\Once Swat gets control again you can type \ \$↑X \to display the results and terminate the spying activity, or \ \$$↑X \to display the results so far and continue the spying. .end .ssec(Miscellaneous) .begin indent 3,11;tabs 12 $↑Y\Prompts for the name of a (text) file from which Swat commands should be taken. Reading will continue across "proceeds" from breakpoints, but will be aborted if Swat is invoked by the keyboard (<control><left-shift><swat>) or by the standard break-point trap (77400B). $$↑Y\Puts Swat into TeleSwat server mode. The keyboard is ignored: to regain local control hit the <Swat> key. For more on TeleSwat see the sections on Address Spaces and TeleSwat. n↑R\Prints the value of R or S register n. You must have a RAM for this to work. $↑R\Prints all of the R and S registers. $$↑Z\Repeats the message that was displayed when Swat was invoked. This is sometimes useful if an error message has scrolled away as a result of poking around. $↑Z\Prints statistics on the symbol table and virtual memory caches. This is mostly of interest to Swat maintainers. .end .ssec(Address Spaces) ↑Z\prompts for the target address space. Swat can treat any file created by OutLd, any bank of memory, and any host in the internet (with the host's cooperation) as the Swatee: the address space into which you peer with Swat. The syntax for address spaces is: .begin indent 0,20; tabs 21 filename\this is 'Swatee' for normal debugging, but can be any file created by OutLd (sysOut files (↑L) are in this category), or Dumper. .break Bank0\Swat itself. .break Bank1...3\the extended memory banks. These are only legal on AltoII XMs. No check is made that a bank actually exists. If it doesn't, or if it hasn't been written into since the Alto was powered up, you are likely to get parity errors. .break [host]\a host that implements the server half of the TeleSwat protocol (usually another Swat). [host] can be either a name: [Boggs], or an internet address: [3#241#]. The square brackets are required: this is how Swat decides that you mean a [host] rather than a file. .end .ssec(Examples) .begin indent 0,17;tabs 18 X↑O↑D\prints the value of X in octal, then decimal. func+3↑N lf lf \prints instructions 3, 4, and 5 of func. 1↑O7\sets location 1 to 7. label↑B\sets a break at label 7562↑B\sets a break at location 7562B SQRT$16↑C\calls the (user) function SQRT (the returned value is printed) label+3↑G\transfers to the third instruction after label. 0↑T\prints the PC 0↑F\prints the parameters of the most recent call 2↑F\prints the parameters of the third most recently called procedure; then $↑O\prints the saved stack pointer (frame!0) $+1↑0\prints the return address (frame!1) $+6↑O\prints the first local (if the procedure has 2 parameters). .end .sec(Expressions) Expressions are as in BCPL with the following exceptions .skip 1; .begin nofill; \' \ \means exclusive OR \\ \ \means REMAINDER \| \ \means LSHIFT for positive arguments, RSHIFT for negative \~ \ \means NOT .end A string of digits is interpreted as octal unless suffixed by a "." $ (not escape) is the base of the last opened stack frame (see ↑F above). Initially it is the last frame. ↑<static name>, "↑" followed immediately by a static name, means use the address of the static, not its value, even if it is a procedure- or label-type static. . is the last opened cell PC is the address of the cell containing the user PC. This is the address at which Swat will resume Swatee when you say ↑P. AC1,...,AC3 are the addresses of the user's accumulators. CRY is the address of the user's carry bit. INT = on = non zero if interrupts where on when the Swat trap happened. No function calls in expressions. No relational operators (e.g. EQ) No conditional expressions No lv operator (well...see ↑<static name> above) .ssec(Examples) .begin indent 0,12;tabs 13;nofill .-1↑O\prints the cell before the currently open cell. .+1↑O\is like line-feed. AC1↑O6\sets AC1 to 6 PC↑O72 ↑P\is like 72↑G PC↑O lf lf lf lf \prints the PC and the AC's .end The conventions for expression evaluation are not truly BCPL-like. "F↑0" will print the first instruction of F if BLDR thought it was a procedure or label, but print the contents of static cell F if BLDR thought it was a variable. If F started life as a variable, but had a procedure assigned to it you must call it by "@F↑C" instead of "F↑C". .sec(Resumable Files) The file Swatee is a snapshot of a running program and can be saved for subseqent resumption or examination. You can create a copy of Swatee by using COPY or, if you are in Swat, typing ↑L and giving a file name. This copies Swatee to the named file and appends some information internal to Swat -- the current symbol table and break point data. There are several ways to restart resumable files: .begin indent 5,9,5 1)\Press the boot button while holding down the keys for the file. 2)\Type the command (it is interpreted by the Exec) .once indent 30 ~RESUME~ file \ \If "file" is omitted Swatee is assumed. .once indent 30 RESUME/S file \ \writes file onto Swatee and invokes Swat. 3)\While in Swat, type ↑Q and give a file name. The file is copied onto Swatee and Swat's internal information is restored to whatever was saved by the ↑L command that created the file. If the file was created in some way other than ↑L, the internal information is reset to an empty state. .end .sec(TeleSwat) Swat implements a simple Pup protocol, ~TeleSwat~, by which it can treat a machine anywhere in the internet as the Swatee (with the consent and cooperation of the other machine). The Swatee is made receptive to control from the network by typing $$↑Y. The controlling Swat's attention is directed at it by specifying the Swatee's network address as the target virtual memory (see the ↑Z command). When you tell the Swatee to proceed (↑P, ↑G, ↑U), you loose control: your Swat starts probing the Swatee once per second, but if the Swatee never returns, you must get help from someone at the other end. Each time a packet is sent, the cursor is inverted to let you know something is happening. Executing the opcode 77412b is equivalent to CallSwat(string1 [], string2 []) followed by $$↑Y. .sec(Desperation Debugging) If the resident is broken so you can't use <Left-Shift><Control><Swat> to get to Swat to see what went wrong, then you are desperate. Press the boot button while holding down the keys for the file ~Dumper.Boot~ (the OS and InstallSwat conspire to make this be "DU" normally). This writes the existing memory onto Swatee with the exception of page 0 which is lost (Dumper lands in page 0 when you boot it). Also the display word (420b) is cleared. Finally, Swat is invoked. .sec(Error Message Printing) Swat contains some facilities to aid in printing error messages. Because the Swat resident is almost always present when a program is running, an error message can be printed by simulating a Swat "break," and letting the Swat program decipher the error specification and print a reasonable message. If Swat is invoked by the 77403b trap instruction, the contents of AC0 are taken to be a pointer to a BCPL string for a file name; AC1 is a pointer to table [ errCode%ClearBit; p1; p2; p3; p4.... ], where errCode (0 le errCode le 32000.) is an error code, the p's are "parameters," and ClearBit is either 100000b (clear the Swat screen before printing the message) or 0 (do not clear). The intended use is with a BCPL procedure like: .begin nofill let BravoError(code, p1, p2, nil, nil, nil) be [ code = code%UserClearScreenBit (table [ 77403B; 1401B ])("bravo.errors", lv code) // do a "finish" here if fatal error ] .end The error messages file is a sequence of error messages, searched in a dumb fashion. An error message is: .begin nofill a. An unsigned decimal error number (digits only) b. Followed optionally by: C Always clear the screen before printing the message M (see below) L Log the error via the Ethernet. c. Followed by a <space>. d. Followed by text for the message, including carriage returns, etc. If you wish to refer to a parameter, give: $ followed by a digit to specify the parameter number (1-9) followed optionally by "!<offset>" which treats parameter as a number, adds offset to it, and sets parameter to the contents of the resulting address (i.e. a vector ref). followed by a character to say how to print the parameter: O = octal D = decimal S = string (parameter is pointer to BCPL string) (example: $1D will print parameter 1 in decimal) The quote character is <escape>. e. Followed by $$. .end After the message is typed, if M was specified, the message "Type <control>K to kill, or ~<control>P~ to proceed." is typed out. .sec(Parity Error Information) When the Alto detects a ~parity error~, Swat is usually invoked to print a message about the details of the error. It then attempts to "log" the error with an Ethernet server responsible for keeping maintenance information. If the server is not operating, or if your Alto is not connected to an Ethernet with such a server, simply strike the <Swat> key, and the familiar "#" will appear. In many cases, you will want to continue execution of your program after a parity error is detected. Simply type ~<control>P~ to Swat. .sec(Installation) Get the file ~InstallSwat.Run~. Then invoke it to create ~Swat~ (the debugger), ~Swatee~ (the swap file for the user's memory image), and ~Dumper.Boot~ (the desperation debugger invoker). InstallSwat.Run may be deleted after it has been run once. Use the Exec's BootKeys command to discover the keys to depress for Dumper.Boot; normally they are "DU". InstallSwat.run is the Swat program. When invoked it, it hooks up to the current operating system, initializes itself, and then OutLds all of core including the OS (suitably Junted and slightly patched) onto the file Swat. There are two global switches, /D and /X. /D installs Swat in "debug" mode. In this mode Swat goes into TeleSwat looking at bank 0 (i.e. Swat itself) when anything untoward happens, such as finishing, trapping, calling Swat, calling SysErr, or getting a parity error. If debug mode is not set, Swat will try to recover from these catastrophies rather than go into TeleSwat. When in debug mode, hitting the <Swat> key does not force control back to the top level, and <left-shift><control><Swat> forces Swat into TeleSwat. /X installs Swat in "extended memory" mode, for debugging IFS and other systems built on the IFS base. In this mode, Swat is prepared to follow address pointers which lead to inter-bank jump instructions (code statics and stack return addresses). .sec(Caveats) 1. Swat has about 1k of resident code in high memory. This code is not changed when new subsystems come in. Therefore re-boot if it seems to be in a bad state. Swat can get itself into a bad state too. SYSINing (↑Q) Swatee is a very effective general purgative; ignore the warning message - its doing exactly what you want it to. If all else fails, make sure you have a clean copy of the OS, and then reinstall Swat by running InstallSwat.run. 2. Instructions 77400B - 77777B are used by Swat. The actions of some of these (e.g. 77401B) are published; you get what you deserve if you use the unpublished ones. Location 567B (in the trap vector) is used. 3. Interrupt channel 8 (00400B) is used by the resident for keyboard interrupts (getting to swat via a <control><left-shift><swat> key combination). 4. A program fetching data from a broken location will get 774xxB. 5. While most interrupt routines are reasonably polite and always resume the interrupted code where it left off, the politeness of Swat's keyboard interrupt is entirely in the hands of the person at the controls. If he re-starts by saying ↑P, all goes well; but he may say ↑G or ↑C. Therefore .begin indent 5,9,0; a) You should disable the keyboard interrupt by anding 77377B into 453B during critical sections of code (once they are debugged). b) Expect occasional anomalies after ↑C or ↑G is used. .end 6. The mappings between symbols and addresses are naive about BCPL's block structure. .begin indent 5,9,0 a) If a symbol is defined twice or more you get the lowest address. b) An address is mapped into a procedure name plus a displacement for symbolic type out (e.g. for ↑T). If procedure A is defined inside procedure B, most of B's addresses will be typed as if they were A's. .end 7. If a disk error prevents swapping, the offending disk control block and label are displayed in the "boot-lights" manner. 8. Locations 700b through 707b are used to save the machine state before each swap. 9. If a file created on a different disk is resumed by booting, invoking Swat may not work because Swat and Swatee may not reside at the same disk addresses on the different disks. This difficulty does not occur if the Exec's RESUME command is used, since it will fix up the addresses before resuming it.