Chapter 19
The symbol table dump and the diagnostic switches
/S makes TATARA print the symbol table when the assembly ends: every name the source defined, with its value, grouped by segment. It is the quickest way to check where a label ended up, what value an EQU was given, or which names a module makes public and which it expects from others.
The chapter also describes /F, /M and /H, the three options that show how TATARA read a source. They are not needed to write programs, but their output is what to send with a report of a problem in TATARA.
19.1 The symbol table
/S prints the table after the last line has been assembled and before the summary line, so that the summary line is still the last thing on the screen. The table always goes to the screen, even when a listing file is written.
This source defines a name of each kind:
public start,count,limit,level extrn print,exit limit equ 100 level defl 1 level defl 2 tmp defl 3 cseg start: ld hl,msg call print call MIXED jp exit wait macro local loop loop: djnz loop endm wait wait Mixed: ret after equ start+1 dseg count: ds 1 msg: db 'Hi$' cseg music tune: db 0 end
A:\>tatara /s syms.as syms.tro Tatara MSX Macro-Assembler v1.2.0 Copyright (C) 2026 Javier Lavandeira https://tatara.tools ASEG - absolute 0002h public var level 0003h var tmp 0064h public limit CSEG - default code segment, 0011h bytes 0000h public start 0001h after 000Ch ??0000 000Eh ??0001 0010h Mixed DSEG - default data segment, 0004h bytes 0000h public count 0001h msg music - named code segment, 0001h bytes 0000h tune EXTERNAL - resolved by the linker exit print ended at SYMS.AS(26)
With /Q as well, the banner and the summary line are left out and only the table is printed.
19.2 Reading the table
19.2.1 Sections
The table has one section for each segment of the module (chapter 10), then one for the external names, then one for the names declared PUBLIC and never defined. Each section starts with a heading line; table 19.1 lists the forms it takes.
Heading |
Section |
ASEG - absolute |
the absolute segment, and every absolute value |
CSEG - default code segment, 0011h bytes |
the default code segment |
DSEG - default data segment, 0004h bytes |
the default data segment |
music - named code segment, 0001h bytes |
a named code segment |
scratch - named data segment, transient, group reading, 0042h bytes |
a named data segment; transient and with a group (section 10.6) |
EXTERNAL - resolved by the linker |
|
UNDEFINED - named by PUBLIC, never defined |
The size in a heading is the number of bytes the segment takes in this module. The absolute segment has no size, because its bytes can be anywhere in memory.
The three default segments always have a section, even when they have no symbols. A source that defines nothing still prints these:
ASEG - absolute (no symbols) CSEG - default code segment, 0001h bytes (no symbols) DSEG - default data segment, 0000h bytes (no symbols)
The sections for named segments, external names and undefined names appear only when there is something to put in them.
19.2.2 Symbol lines
Each line in a segment’s section has the symbol’s value, its type and its name. The lines are in order of value.
- The value is an offset in the segment: Mixed is 10h bytes from the start of the code segment, wherever TANREN puts it. In the absolute segment it is the value itself. An EQU whose value is relocatable, such as after equ start+1, belongs to the segment of that value. A DEFL name shows the last value it was given: level is 2.
- The type is one of the words in table 19.2.
- The name is spelled as it was written where it was defined. The source uses MIXED before it defines Mixed:, and the table shows Mixed. With /C, these would be two different names (chapter 7).
The names ??0000 and ??0001 are the ones LOCAL made up for the two uses of wait (chapter 16). They are ordinary labels, one for each use.
19.2.3 Names with no value
External names have no value in the module that uses them: TANREN finds it in another module (chapter 25). They are listed with no value and no type, in no particular order.
A name declared PUBLIC and never defined stops TATARA when it writes an object file (chapter 12). Without an object file, the source is accepted, and /S lists the name in a section of its own. This source declares two public names and defines only one:
public here,nowhere here: ret end
A:\>tatara /s undef.as ... CSEG - default code segment, 0001h bytes 0000h public here DSEG - default data segment, 0000h bytes (no symbols) UNDEFINED - named by PUBLIC, never defined nowhere
19.3 The table and the listing
A listing file ends with a page of symbols of its own (section 18.2.1). For the source in section 19.1, it is this:
Macros: wait Symbols: 000E' ??0001 000C' ??0000 0000* exit 0003 tmp 0000' tune 0001' after 0010' Mixed 0000' count 0001' msg 0000' start 0002 level 0000* print 0064 limit
It holds the same symbols, in M80’s layout: three to a line, in no particular order, with ’ after a relocatable value and * after an external name. It does not say which segment a value belongs to, or whether a name is public: tune and start are both at 0000’, one in music and one in the default code segment. The table that /S prints says both, and is the one to read when that matters.
The two can be asked for together, with /L and /S: the page goes into the listing file, and the table to the screen.
19.4 Diagnostic switches
/F, /M and /H show how TATARA read a source, rather than what it made of it. If TATARA does something unexpected with a line, run the source again with the option that concerns it, and send the output with the report. MSX-DOS can save the output in a file with >:
tatara /f prog.as prog.tro > fields.txt
19.4.1 /F: the fields of each line
/F prints each line as TATARA divided it into fields (chapter 6), instead of listing it. This source has a comment, a line skipped by a conditional, a macro, an include file and a string that holds a semicolon:
; FIELDS.AS - what /F shows. two macro x db x,x endm cseg start: ld hl,msg ; a comment if 0 nop endif two 5 include finc.inc msg: db 'a;b' end
FINC.INC holds a comment line and ret.
A:\>tatara /f fields.as fields.tro ... [][][][; FIELDS.AS - what /F shows.][00][00:0001] [two][macro][x][][01][00:0002] [][cseg][][][1A][00:0005] [start][ld][hl,msg][; a comment][00][00:0006] [][if][0][][08][00:0007] [][nop][][][00][00:0008] [][endif][][][13][00:0009] [][two][5][][00][00:000A] [][db][5,5][][20][00:0003] [][include][finc.inc][][14][00:000B] [][][][; FINC.INC - included by FIELDS.AS.][00][01:0001] [][ret][][][00][01:0002] [msg][db]['a;b'][][20][00:000C] [][end][][][15][00:000D] ended at FIELDS.AS(13)
Each line of the output has six parts in square brackets:
- the label, without its colon;
- the operation;
- the operands;
- the comment;
- a number that TATARA gives each directive, in hexadecimal, or 00 for a line that is not a directive;
- where the line came from: the number of the file, 00 for the source named on the command line and 01 and so on for include files in the order they were opened, then the line number in that file, in hexadecimal.
The output shows each line as it is read on the first pass. Lines skipped by a conditional are shown, as nop is here. A line from a macro expansion shows the line of the macro’s body it came from: db 5,5 is line 3, where db x,x was written. The lines of a macro’s body are shown only when it is used, not when it is defined. The semicolon inside ’a;b’ is part of the operand, not the start of a comment.
/F prints to the screen, with or without /P.
19.4.2 /M: macro definitions
/M prints each macro definition as TATARA stored it, when the definition is read:
; MDUMP.AS - what /M shows. put macro x,y local skip ld a,x ; kept jr skip ;; dropped lab&y: db '&x' skip: endm cseg put 1,2 rept 2 nop endm end
A:\>tatara /m mdump.as mdump.tro ... MACRO put: 2 parameters, 1 locals, 4 lines, MDUMP.AS(2) 4 | ld a,<0> ; kept 5 | jr <L0> 6 | lab<1>: db '<0>' 7 | <L0>: ended at MDUMP.AS(14)
The first line gives the macro’s name, the number of parameters, of LOCAL names and of lines in its body, and where the definition starts. Each line of the body follows, with its line number in the source:
- a parameter is shown as <0>, <1> and so on, in the order of the MACRO line, and a LOCAL name as <L0>, <L1> and so on. lab&y has become lab<1>: the & has done its work (section 16.3);
- the LOCAL line and the ENDM line are not stored;
- a comment that starts with ;; has been dropped (section 16.4).
The REPT block at the end of the source is not printed: /M shows macros only, not repeat blocks (chapter 17).
19.4.3 /H: memory in use
TATARA keeps its symbols and macros in blocks of memory in the memory mapper, which it takes as it needs them. /H prints, after the summary line, how many blocks are still in use when the assembly ends:
A:\>tatara /h heap1.as heap1.tro ... ended at HEAP1.AS(8) HEAP: 7 blocks.
The figure is never zero: each symbol takes a block, each macro definition takes blocks for its name and its body, and TATARA’s own records take a few more. It is useful only when compared with the figure for another source. A macro with a LOCAL adds a block each time it is used, because each use defines a new ?? name: the same wait used three times instead of once gives HEAP: 9 blocks.