Chapter 25
Symbols across modules
A module offers names to the others with PUBLIC, and uses theirs with EXTRN (section 12.5). TATARA leaves every external name unfinished, and TANREN finishes it with the value the other module gave it. This chapter describes how TANREN matches the names, what it does with names that no module defines or that two modules define, and what /M prints.
25.1 How names are matched
In the program of chapter 21, MAIN.AS declares greet with EXTRN and calls it, and SUB.AS declares it with PUBLIC and defines it:
; MAIN.AS - calls greet, in another module, and ends the program. include msxdos.inc extrn greet cseg start: call greet system _TERM0 end start
; SUB.AS - greet prints a line. include msxdos.inc include ascii.inc public greet cseg greet: ld de,msg system _STROUT ret dseg msg: db 'Hello from TANREN.',CHR_CR,CHR_LF,'$' end
TANREN keeps one table of names for the whole link, and reads the modules twice:
- On the first pass, it reads every module’s public and external names into the table. A public name is defined, with its value: greet is the first byte of SUB’s code, and SUB’s code comes after MAIN’s eight bytes, so greet is at 0108h. An external name is referenced: some module wants its value. At the end of the pass, every referenced name must be defined.
- On the second pass, it writes the bytes of each module, and fills in every place where an external name is used with the value from the table.
Figure 25.1 follows greet through the two passes. TATARA wrote call greet as CD 00 00, and TANREN turns it into CD 08 01, a call to 0108h.
All the names are in the table before any byte is written, so the order of the modules does not matter for the names: a module can use a name defined in a module named before it or after it. The order matters only for where each module goes in memory (chapter 24).
/M prints the table, among other things. For this program it has one name:
A:\>tanren /m main sub ... Symbols: DR 0108 greet 2 modules, 16 records, ends at EOF. Wrote MAIN.COM, 0100-0125 (38 bytes), entry 0100.
D says that greet is defined, R that it is referenced, and 0108 is its value. Section 25.7 describes the rest of /M.
25.2 What an external’s value can be
An external name can have a number added to it or taken away from it (section 8.6). TATARA writes that number in the place where the value goes, and TANREN adds the name’s value to it. These two modules use names in the code and in the data:
; USE.AS - uses two names from VALS.AS, with and without an addend. extrn table,count cseg start: ld hl,table+2 ld bc,count ld de,count+1 ret dseg ptrs: dw table,table+4 end start
; VALS.AS - a table in the data segment, and an absolute value. public table,count count equ 10 dseg table: db 1,2,3,4,5,6 end
A:\>tanren /m use vals ... Symbols: DR 000A count DR 010E table 2 modules, 17 records, ends at EOF. Wrote USE.COM, 0100-0113 (20 bytes), entry 0100.
table is at 010Eh, after the data of USE, so in USE.COM ld hl,table+2 is 21 10 01, and dw table,table+4 is 0E 01 12 01.
count is defined with EQU, so it is not an address in a segment but an absolute value (section 8.6). TANREN does not move it: it is 000Ah wherever the modules go, and ld de,count+1 is 11 0B 00.
25.3 Names that no module defines
A name that is referenced and never defined stops TANREN at the end of the first pass, and no output file is written. Linked alone, MAIN has nobody to define greet:
A:\>tanren main ... Never defined: greet ERROR: the symbols above were never defined.
TANREN lists every name that is missing, not only the first, so that one run shows them all. This module uses three names, and only beta is defined, in BETA.AS:
; MISS.AS - three externals; nobody defines alpha or gamma. extrn alpha,beta,gamma cseg ld hl,alpha ld de,beta ld bc,gamma ret end
; BETA.AS - defines beta. public beta cseg beta: ret end
A:\>tanren miss beta ... Never defined: gamma alpha ERROR: the symbols above were never defined.
The names are listed in no particular order.
Warning. A name declared with EXTRN is referenced even if the module never uses it. TANREN still wants a definition for it:
; UNUSED.AS - declares an external and never uses it. extrn nothere cseg start: ret end startA:\>tanren unused ... Never defined: nothere ERROR: the symbols above were never defined.When a module stops using an external name, remove it from the EXTRN line too.
25.4 Names defined twice
A name can be defined by only one module. The second definition stops TANREN while it reads the modules, and the line before the error names the name and the module where the second definition was found. Here DUP2.AS defines a second greet, and was assembled into OTHER.TRO:
; DUP2.AS - a second greet. public greet cseg greet: ret end
A:\>tatara /q dup2.as other.tro A:\>tanren main sub other /o:dup.com ... greet is defined again in module dup2 ERROR: that public symbol is already defined.
The module is named after the source it was assembled from, not after the object file, so the message says dup2, not other.
The same happens when one object file is named twice, for example on the command line and in a link file (chapter 22):
A:\>tanren main sub sub /o:dup.com ... greet is defined again in module sub ERROR: that public symbol is already defined.
TANREN stops at the first name defined twice, and does not look for missing names after it. With both mistakes in one link, correct the name defined twice first, and link again.
25.5 Capitals and small letters
Without /C, TATARA does not tell capitals from small letters in names (section 7.3), and neither does TANREN. This module calls GREET, in capitals:
; UPPER.AS - calls GREET, in capitals. include msxdos.inc extrn GREET cseg start: call GREET system _TERM0 end start
It links with SUB, whose name is greet. /M shows the name as TANREN first saw it:
A:\>tanren /m upper sub ... Symbols: DR 0108 GREET 2 modules, 16 records, ends at EOF. Wrote UPPER.COM, 0100-0125 (38 bytes), entry 0100.
Assembled with /C, the two modules have two different names, one defined and one only referenced, and the link fails:
A:\>tatara /q /c upper.as upperc.tro A:\>tatara /q /c sub.as subc.tro A:\>tanren /m upperc subc ... Symbols: D- 0108 greet -R ---- GREET Never defined: GREET ERROR: the symbols above were never defined.
A module assembled with /C and one assembled without it cannot be linked together (section 7.3):
A:\>tanren upperc sub /o:mix.com ... ERROR: one module was assembled /C and another was not.
25.6 Every module is linked
TANREN links exactly the modules it is given. A module that no other module refers to is still linked, and its bytes are in the output file. Nothing refers to spare, in EXTRA.AS:
; EXTRA.AS - a module nobody refers to. public spare cseg spare: ld a,1 ret end
A:\>tanren /m main sub extra /o:ex.com ... Symbols: D- 0111 spare DR 0108 greet 3 modules, 22 records, ends at EOF. Wrote EX.COM, 0100-0128 (41 bytes), entry 0100.
spare is defined and not referenced, and its three bytes make EX.COM three bytes longer than MAIN.COM. TANREN does not search a library for the modules a program needs: every module named on the command line or in the link file is linked, whether it is used or not.
25.7 /M in full
/M prints four parts, after the banner and before the summary line. For tanren /m main sub:
A:\>tanren /m main sub Tatara MSX Linker v1.2.0 Copyright (C) 2026 Javier Lavandeira https://tatara.tools Modules: 2 Groups: Segments: flags 00 group FF base 0100 size 0011 end 0110 C flags 01 group FF base 0111 size 0015 end 0125 D Symbols: DR 0108 greet 2 modules, 16 records, ends at EOF. Wrote MAIN.COM, 0100-0125 (38 bytes), entry 0100.
- Modules is how many object files TANREN read.
- Groups lists the groups of the transient segments, one name to a line, numbered from 00 in the order they are listed (section 24.4). This program has none.
- Segments lists every segment, where it was placed and its size (section 24.1).
- Symbols lists every public and external name, one to a line, in no particular order. The first column is two letters: D if a module defines the name, and R if a module declares it external, with - in place of either letter that does not apply. Then comes the value, or –– for a name no module defines, and then the name.
/M prints its tables even when names are missing, before the list of missing names. A missing name is -R ––:
A:\>tanren /m main /o:m1.com ... Modules: 1 Groups: Segments: flags 00 group FF base 0100 size 0008 end 0107 C flags 01 group FF base 0108 size 0000 end ----- D Symbols: -R ---- greet Never defined: greet ERROR: the symbols above were never defined.
A segment of size 0000 has no last address, and end shows ––-.
25.8 Messages
Table 25.1 lists the messages about names. Chapter 27 lists all of TANREN’s messages.