392 lines
18 KiB
Plaintext
392 lines
18 KiB
Plaintext
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 <infile
|
||
>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.
|
||
|
||
|