Debugging A-code gamesSince A-code sources get translated into ANSI C for compiling and linking, debugging can performed on the C level as well as on the A-code level. C-level debuggingC-level debugging rarely required, but if necessary, can be done using standard debugging tools after building derived C sources into an executable with the -g option. If using gcc it is also useful to add -gdwarf-2 -g3, since that makes GNU debugger gdb understand macro names in the C code. However, derived C sources are not human-friendly because they use numerical refnos to reference game entities. To assist code comprehension, the acdc translator has the -d command line option, which adds to the generated C code printout (on stderr) of A-code source lines being executed. This makes game debugging, be it with with gdb (or similar), or just by visual inspection, much easier. A-code level debuggingMost A-code debugging takes place on the level of the A-code language itself. While there is no A-code debugger, there are some useful debugging tools. Runtime procedure call traceAs already noted, acdc's -d command line option has the effect of showing at run-time on stderr A-code lines effectively being executed. This display includes the name of the source file and the line number of the code line being shown. Since the display is on stderr, it can be diverted into a file, regardless of the game's build. In the console mode, where by default both stdout and stderr are shown to the player, game response to a command comes after this listing of source lines, so that the game is still playble. Thus, for example: ? out [...] repeat.acd:934 ifeq context, none repeat.acd:935 ifnear door1 repeat.acd:936 and repeat.acd:937 ifeq waterfall, opened repeat.acd:938 and repeat.acd:939 ifeq dwarven, 0 repeat.acd:943 iflt stage, adventuring repeat.acd:946 input You're at end of road again. ? Cross-reference lists
The acdctranslator's -x option makes it emit a cross-reference file
The .nrefs file list game's named entities in alphabetical order, associating each with the refno assigned to it by the translator. The .rrefs file also lists entity names and their associated refnos, but this time in the refno order. Finally, the .xrefs file is the most useful one of the lot. It is sorted on entity names and shows where each entity occurs in the source code, differentiating between its declaration and its use. Here is a brief extract from adv770.xrefs (the whole file runs to over 41 thousand lines): aurora.borealis TXT 7879 text.acd aurora.borealis txt 11756 at.acd automatic.gate TXT 2590 text.acd automatic.gate txt 690 actions.acd automatic.gate txt 9155 at.acd automatic.gate txt 9177 at.acd available STATE 597 defs.acd available state 15701 at.acd available state 15714 at.acd awarded STATE 598 defs.acd awarded state 15715 at.acd awarded state 15717 at.acd awarded state 2154 procs.acd axe OBJ 799 objects.acd axe obj 702 actions.acd axe obj 726 actions.acd axe obj 778 actions.acd ... ... ... So, for example, the object AXE is declared (type is in capitals!) on line 799 in the file objects.acd and referenced (type in lower case) in lines 799, 702, 726, 778... in the file actions.acd. Game data dumpsThe minor directive DUMPDATA, dumps the current state of the game to standard error. The default display contains no symbolic names (because they are not known to the kernel), so interpreting it requires constant reference to the above mentioned cross-reference files. This is not at all convenient. However, if the game is translated int C with the -d option, or if its source code defines the special variable ENTNAME, entity names are passed on to the kernel and are used in the dump. Here are some fragments of such a dump: ================= OBJECTS ================= .... 5 it 0 1000000000000000 at 0 6 keys 0 1001000000000000 at 65 building 7 lamp 0 1001000000000000 at 65 building .... ================= PLACES ================= 63 road 0 0101110000000000 64 hill 0 0101000000000000 .... ================= WORDS ================= 192 again 193 carry 194 drop .... ================= VARIABLES ================= .... 322 penalties 0 0000000000000000 323 here 63 => road 324 there 63 => road 325 status 1 0100000000000000 .... ================= TEXTS ================= 502 intro 0 503 typo 0 cyclic .... 693 plant.2 0 tied to 27 plant ... For each entity, the dump gives its refno, its name and (if appropriate) its value, followed by (if appropriate) its properties bit screen (a.k.a. flags). For objects, their location is given (as location refno and name, if any). If a variable is a pointer to another entity, the entity pointed to is shown (its refno is the variable's value). Finally, if a text has morphing features, it's type is shown and, for typed texts, the entity to which they are tied. By default all of the game's data is dumped, but one can choose to dump only data pertaining to a particular type of vaue-bearing entity by adding OBJECT, PLACE (or LOCATION), VARIABLE (or VARS) or TEXT as an argument to DUMPDATA. Game data is dumped either to stderr or, if the game is being logged (i.e. was invoked with the -l command line option), to the log file. As the simplest possible use, you can define DUMPDATA as a verb with a corresponding action: verb dumpdata action dumpdata dumpdata quit Or, more sensibly, you can make DUMP one of optional "wizard" actions (see below). The CHECKPOINT minor directiveCHECKPOINT minor directive is another debugging tool. When executed, it reports its own location (file name and line number) in the A-code source. ? n === Checkpoint: procs.acd, line 37 === You are at the end of the road again. ? What makes this useful is the fact that changing executable A-code source has no effect on its ability to restore saved games. Thus it is safe to add CHECKPOINT statements in a suspect piece of code in order to track problems in a saved game. Constructing non-integral "wizard" modeA-code's unusual feature of procedure groups permits construction of debugging commands (a.k.a. the wizard mode) as an optional add-on by using the INCLUDE? major directive. Once again this is made more useful by upward compatibility of saved games. While A-code permits entities being used before being declared, it is useful to place all executable code after all declarative code. (This does not, of course, preclude entities being used before their declarations, since A-code texts can embed references to game entities.) If that's how code is arranged, adding an optional source file (via the INCLUDE? major directive) in between declarations and executable code has two interesting effects:
For example, suppose the game contains verb find action find quip "You'll have to find thing out for yourself. " Suppose further that the optional code contains variable entname action find ifeq status, 2 # Player trying to find something ifflag arg2, object say "f:Object {ARG2} " locate entname, arg2 quip "is at {ENTNAME}." # Terminates command loop else quip "{ARG2} is not an object!" fin fin This would modify the behaviour of the command FIND to show the location of of a nominated object, or, if no object nominated, to do exactly what it would do without the optional code. For ease of debugging, optionally included code should also have commands to toggle "wizard" mode on and off and other wizard mode commands whould simply PROCEED if the wizard mode is off. There can, of course, be more than one optionally included file, positioned at various places in the source code, as appropriate. Nor is it necessarily the case that such includes must come before other code. E.g. my A-code port of Adv350 (written in order to experiment with mobile NPCs) has an extensive set of "wizard" tools, which is included after the NPC movement and actions code. It defines its own vocabulary list, that can be displayed by VOCABULARY WIZARD: ? voc wiz Wizard (i.e. debug) commands: close cave - triggers the next cave closure stage decrement <entity> - decrement state value of object or location data [obj|loc|var|text] - show game's data fetch <object> (obtain) - fetch the object from wherever find <object> - go where the object is first - forces first dwarf, if axe not seen fly <location> (teleport) - go to the named location glow - toggle magical illumination increment <entity> - increment state value of object or location next - go to the next higher location notbeen - show locations not yet visited previous - go to the next lower location runout - sets event clock to zero [show] {numbers|npcs} - toggles loc number/npcs repeated display show <entity> - Displays entity's current value where - shows where one is (and came from) where treasure - shows valued objects (sorted on seen flag) where water - shows water-holes where <object> - shows object's location wizard {on|off} - switches wizard mode on or off ? The optional include file debug.acd can be found in the A-code source of Adv350 available at https://mipmip.org/adv350 in the file opt/debug.acd. If using the advbld script to build Adv350 (or Adv770), the -W script option copies opt/debug.acd to where it will be found by an optional include, builds the game and then deletes the file copy.
|