CHAPTER 13 ASSOCIATED TOOLS XREF Cross-reference and Symbol Listing Facility XREF is a tool that creates a cross-referenced symbol table listing of your program. To invoke XREF, you must provide a program invocation line, either typed to the console when the DOS command prompt appears, or included in a batch file. The program invocation line consists of the program name XREF, followed by the name of a .SYM symbol table file produced by A86 when you assembled your program. You do not need to give the .SYM extension. Note that if you follow normal methodology, the name of the symbol table file is the same as the name of the program. XREF will obtain the list of source files from the symbols file, read those source files, and create a listing file with the same name as the .SYM file, but with a .XRF extension. Prior to V3.12, XREF required the list of source files to be explicitly given, and allowed you to specify the name of the output file. To retain compatibility with batch files invoking the old XREF, the current version ignores anything on the command tail after the symbols file name. This means you can no longer specify a different output file name. Sorry about that-- I couldn't think of any other way to be compatible without overwriting somebody's source files with XREF output. You can always rename the file after XREF is completed. For example, you can type XREF myprog to obtain the cross- reference for the assembly that produced myprog.SYM. The output will be in the file myprog.XRF. The output of XREF is an alphabetical listing of all the non-local symbols in your program. For each symbol, XREF gives its type, the file in which it was defined, its value, and a list of all procedures in which the file was used. If you print this file, you typically use the TCOLS tool to obtain a multi-column listing from XREF's single-column output. Note the use of procedure names to identify references -- this is unique to the A86 package, and makes the cross-reference listing truly readable. Other cross-reference listings give either line numbers, which are meaningless unless you go find the associated line; or a file name, which doesn't give you as much useful information. Here is a more detailed description of the various pieces of information provided for each symbol: 1. TYPE. Labels are indicated by a colon immediately following the symbol name. Special symbols such as macro names are denoted by an appropriate word such as "macro" in place of the value on the following line. Other symbol types are described by one or two characters, following the symbol name. Possibilities for the first character are: 13-2 m for a simple memory variable + for an index memory quantity c for a constant i for an interrupt-equate s for a structure If there is a second letter, it is a size attribute: b for byte, w for word, f for far (or doubleword). 2. FILE in which the symbol was defined. The name is stripped of its extension, which is presumably the same for all your source files. The name is preceded by = or period, which denotes a definition of, not a reference to the symbol. 3. VALUE, given as 4 hex digits, on the line following the symbol. For memory variables, this is the location of the variable. For indexed quantities, this is the constant-displacement part of the quantity. For structures, it is the size of the structure. For interrupt equates, it is the number of the interrupt. 4. REFERENCES, given on indented lines following the symbol name. All occurrences of the symbol in your program produce a reference. If the symbol is the first thing on a line, it is considered a "definition" of that symbol, the reference listed is the source file name. The name is preceded by a period if the definition was via a colon (i.e., a label); it is preceded by an equals sign otherwise. If the symbol is not the first thing on the line, then it is not a definition. The reference listing consists of the name of the last definition that XREF scanned (which, if your program is organized in a standard way, will be the name of the procedure in which the reference occurred. Observe that you must use the local-label facility of A86 to make this work. If you don't use local labels as your "place-marker" symbols, the symbol XREF gives you will often be the name of the last "place-marker" symbol, not the name of the last procedure. To save space, duplicate reference entries are denoted by a single entry, followed by "*n", where n is the decimal number of occurrences of that entry. EXMAC Macro Expansion Tool There is a tool called EXMAC which will help you troubleshoot A86 program lines that call macros. If you are not sure about what code is being generated by your macro calls, EXMAC will tell you. To use this tool, you must first assemble your macro definitions, to produce a symbol table file. A86 will produce a .SYM file even if there were errors. If the errors weren't too catastrophic, the SYM file should be good enough to enable EXMAC to do its job. 13-3 EXMAC can be used in two different ways. First, it can be used as an interactive program. You invoke the program in this way by typing just "EXMAC myprog", where myprog.SYM is the name of the symbols file. Then you can type in any number of macro-call lines. After each line, the program will display the expanded program text it produces. If the program does not think your line is a macro call, it will simply echo the line back to you. In this mode, you exit the program by typing control-Z at the beginning of a line, then terminating the line with the ENTER (RETURN on some computers) key. On most IBM-compatible computers, the control-Z code is also generated by the F6 key, for convenience. The second way of using EXMAC is to feed a source file to it. It will output the equivalent source file with the macros expanded. You may then, if you wish, rename the new file as the original source file, and assemble the new file. This method is useful if you get an error on a macro expansion line, and you don't know where the error came from. To use EXMAC in this second way, you simply redirect standard input and output: "EXMAC myprog outfile". With the redirection, EXMAC will take its input from the file "infile" instead of the keyboard; and it will send its output to the file "outfile" instead of the screen. (If you are not familiar with redirection of standard input and output, you might want to read about it in Chapter 6, "Standard Input and Standard Output", of the MS-DOS reference manual.) A86LIB Source File Library Tool There is a tool, A86LIB.COM, available only if you are registered, that lets you build libraries of source files. To use A86LIB, you must first code and debug the A86 source files that you wish to include in your library. Then you issue the command A86LIB followed by the names of the source files. Wildcards are accepted; so you will typically want to gather the source files into a single directory, and use the wildcard specification. For example, if you use the filename extension .8 for your source files, you can issue the command A86LIB *.8 to create the library. The library created consists of a catalog file, always named A86.LIB, together with the source files that you fed to A86LIB to create the catalog. The following observations about A86LIB are in order: 1. Unlike object-code libraries, A86.LIB contains only symbol names and file names; it does not contain the code itself. You MUST retain the source files used to create A86.LIB, because A86 will read those files that it needs after consulting A86.LIB to read their names. 13-4 2. A86LIB records all non-local symbols that start a line, and are followed by a colon or an EQU. (Recall that local symbols are those names consisting of a single letter followed by one or more decimal digits.) A86LIB also records all symbols appearing on lines starting with the word PUBLIC. 3. If a symbol appears in more than one library source file, it will be logged for the first file A86LIB sees, and not the subsequent ones. No error will be reported, unless and until A86 tries to assemble both files in one assembly, and sees a conflict. 4. A86LIB is simple-minded. A86LIB does NOT recognize or expand macros; nor does it recognize conditional-assembly directives. This is because the library files do not stand by themselves; the macros and conditional-assembly variables being used might well be defined in the main program of the programs accessing the library files. You may update A86.LIB by running A86LIB again; either with new files or previously-recorded ones. If A86LIB is given a file it had already read in a previous run, then A86LIB marks all the symbols it had logged for the file as deleted, before rereading the file. Those symbols that are still in the file are then "unmarked". Thus, symbols that have been deleted from the file disappear functionally from A86.LIB, but still occupy space within A86.LIB. What I'm getting at is this: A86LIB will tolerate alterations in library files quite nicely; but for optimum storage efficiency you should delete A86.LIB and rebuild it from scratch any time you delete anything from the library. A86LIB is so fast that this is never very painful. Using A86.LIB in A86 Assemblies Once you have created a library with A86LIB, you access it simply by calling the procedures in it from your A86 program. When A86 finishes an assembly and sees that there are undefined symbols in your program, it will automatically look for copies of A86.LIB in the current directory (then in other directories, as described in the next section). If any of the undefined symbols are found in the A86.LIB catalog, the files containing them are assembled. You see this in the list of files output to the console by A86. The subroutines in your library or libraries are effectively a permanent part of the A86 language. They can be called up effortlessly in your A86 programs. In time you can build up an impressive arsenal of library modules, making A86 as easy to program in as most high-level languages. 13-5 Environment Variable A86LIB You can set an environment variable A86LIB to specify which drives or subdirectories contain A86.LIB files. The variable consists of a sequence of path names separated by semicolons, just like the PATH variable used by the operating system. For example, if you include in your AUTOEXEC.BAT file the line SET A86LIB=C:\bin\lib;\tools\a86lib then A86 will look for A86.LIB in the current directory, then it will look for C:\bin\lib\A86.LIB, then \tools\a86lib\A86.LIB. A86 will keep looking in all three catalog files, assembling the appropriate source files from any or all of them, until there are no more undefined symbols, or there are no more source files to assemble. For every symbol in an A86.LIB catalog, there is recorded the name of the library file containing the symbol. The library file is assumed to be in the same directory as its A86.LIB file, unless a complete path name (starting with \ or a drive specifier) was fed to A86.LIB when A86.LIB was created. Forcing a Library Search You may force A86 to assemble library files before moving on to more of your program's source files. You do this by placing a hash sign # (hex code 23) between file names in your invocation line. For example, suppose your program has two modules FIRST.8 and LAST.8. FIRST.8 calls subroutines from your library; but you need the library files assembled before LAST.8 is assembled. (You might want this because LAST.8 allocates memory space beyond the end of your program, which would be the end of LAST.8 if it were truly the last module.) You accomplish this by the invocation line: A86 FIRST.8 # LAST.8 Note that there is never any need to force a library search at the end of your program modules: A86 always makes a library search there, if you have any undefined symbols. Listings with A86 A86 does not produce a .LST file, or anything similar to it! (We now pause, to allow traditionalists to recover from their swooning shock.) OK, everybody back to consciousness? Good. Now let's all try to strip away our preconceptions, and look at things with a fresh viewpoint. In particular, let's consider what we use a listing file for, and see how A86 meets those needs. I've been programming for 20 years; I have generated literally tons of listings. Historically, here's what I have used listings for: 13-6 1. To find out what my error messages are. In the early days of Intel, the text editor was so bad that it was actually faster to march across the building and physically print the list file, than it was to use an editor to find error messages! But even with a fast editor, what a pain it is to go into the list file, enduring its 120-column wide format on your 80-column screen, copy down the errors on paper, then go back to the source file to find where the errors were. Why doesn't the assembler just stick the messages directly into your source file, where you can view them and edit the source simultaneously? That's what A86, and only A86, does, if you want it to. 2. To see what code was generated; those hexadecimal bytes at the left of the listing. That was a real necessity, back in the days of hexadecimal debuggers. There we were, furiously patching those hex object bytes. We needed the listings to find our way around the program, while debugging. Today, we have symbolic, disassembling debuggers, such as D86. The power of today's debuggers means that you seldom need to look at hex object bytes. If you do, the debugger can show them to you. 3. To get a symbol-table listing. The necessity of this diminishes a great deal when you have a SYMBOLIC debugger; but I still like to have a listing from time to time. So I have devised a separate program, XREF, that goes through another pass of the source file(s), and creates the most useful cross-reference listing. You may ask, "Why am I being forced to essentially re-assemble my code to get a symbol table, when other assemblers will give it to me in the original assembly?" Don't be fooled. Those other assemblers go through all your source files twice, or even three times. They just do it behind your back, every time you want an assembly. That's one reason why my assembler is so much faster than everyone else's. 4. To just look at the code. I have often in the past needed to see that program, spread out on paper, just to get a handle on what the program is doing. But I have needed this less and less lately. Why? For two reasons. First, text editors have improved. It's much, much easier than it was before to cruise through a file on the screen. Second, my programs have adapted to the screen-viewing methodology. Almost subconsciously, I have started making the conceptual "chunks" of my code fit into 1 or 2 24-line screens, rather than 1 or 2 60-line pages. This, of course, makes better, more modular programs. (Spaghetti tends to untangle when you chop it up.) It's gotten to the point where I can develop (and have developed) a 5000-line application, fully debugged, without ever making a listing! 13-7 5. For archival purposes. I still do this; you should never put 100% trust in magnetic media. But I've stripped away the reasons for having anything but the source code and the symbol table. So I just copy the source files and the cross-reference listing to the printer. I haven't looked at the listings too much; so I haven't bothered with pagination control. If you want to, you can insert form feeds into your source; A86 will ignore them. Or, you can write a simple listing tool that recognizes the PAGE directive; A86 ignores that directive, also. As a partial remedy to those who have not been convinced by the above arguments, I now have a D86 command that sends a disassembly to a file. The disassembly is formatted in the style of an assembler listing file, with locations and hex codes at the left. See the D86 manual for details. Mimicking Tool: FAKE.EXE As of this writing, Turbo C is aware only of the existence of Microsoft's MASM for assembling source files it generates. I hope to persuade Borland to provide a switch to Turbo C that causes it to invoke A86 directly. Until that happens, I offer the tool FAKE.EXE, that convinces Turbo C that A86 is really MASM. To use FAKE.EXE, it must be renamed MASM.EXE in your disk system. I would have named it MASM myself, except that 1. Bill Gates would probably get mad at me if I did, and 2. You need to decide what to do with your real MASM if you have it, before installing FAKE. You could either place FAKE (named MASM.EXE) into the individual directories containing Turbo C programs, or you can rename MASM to something like MSM.EXE or REALMASM.EXE. Having renamed FAKE.EXE to MASM.EXE, you may now use the Turbo C's switch, -B, that allows you to place A86 statements into your C program. You don't need to worry about the gory details of what FAKE does. If you like gory details, here they are: FAKE filters the command line handed to it, replacing switches: /D becomes = /ml becomes +c /mx becomes +C /E becomes +f FAKE also eliminates the semicolon, appends .ASM to the source file name, and turns on the O and S switches. It then feeds the resulting filtered command line to A86 for assembly.