Tatara

Chapter 28
A program in several modules

This chapter builds TWOMOD, the example in A:\TATARA\EXAMPLES\TWOMOD: a program of two modules, linked from a link file. It goes through the files one at a time, builds the program, looks at what TANREN made of it, and then changes it in three ways to show what goes wrong, and why. The reference chapters named along the way describe each part in full.

28.1 The example

TWOMOD prints two lines and ends:

A:\TATARA\EXAMPLES\TWOMOD>twomod 
Two modules, one program. 
The linker joined them.
 

The printing is done by a routine, putstr, that is in a module of its own. The example has four files:

28.2 The two modules

MAIN.AS calls putstr twice, once for each line, and then ends the program:

; MAIN.AS - one of two modules. 
; 
; It knows that putstr exists. It does not know where putstr is, and 
; it does not need to: EXTRN says the name is somebody else's, and the 
; linker fills the address in. 
 
                extrn   putstr          ; defined in PUTSTR.AS 
 
                include msxdos.inc      ; _TERM0 and "system" 
                include ascii.inc       ; CHR_CR and CHR_LF 
 
                cseg 
 
start:          ld      hl,msg1 
                call    putstr 
                ld      hl,msg2 
                call    putstr 
                system  _TERM0 
 
msg1:           db      "Two modules, one program.",CHR_CR,CHR_LF,0 
msg2:           db      "The linker joined them.",CHR_CR,CHR_LF,0 
 
                end     start
 

extrn putstr tells TATARA that putstr is defined in some other module (section 12.5). TATARA does not know where, so it leaves the address in each call putstr to be filled in by TANREN. The END line names start, the first instruction.

PUTSTR.AS defines putstr, which prints the characters of a string one at a time until it reaches a zero byte:

; PUTSTR.AS - the other module. 
; 
; PUBLIC is what makes the name visible outside this file. Without it 
; the name would still assemble here and the link of MAIN.AS would 
; stop with an undefined symbol - which is worth trying once. 
 
                public  putstr 
 
                include msxdos.inc      ; BDOS, _CONOUT and "system" 
 
                cseg 
 
; putstr - print a string. 
; 
;   HL is pushed around the BDOS call because MSX-DOS promises nothing 
;   about the registers it hands back. 
; 
; Input:        HL -> the string, ending in a zero byte 
; Output:       it is printed 
; Modifies:     AF, BC, DE, HL 
 
putstr:         ld      a,(hl) 
                or      a 
                ret     z 
                push    hl 
                ld      e,a 
                system  _CONOUT 
                pop     hl 
                inc     hl 
                jr      putstr 
 
                end
 

public putstr offers the name to the other modules. Neither module knows anything else about the other: MAIN.AS knows only the name putstr, and PUTSTR.AS does not know who calls it. Each is assembled on its own, into an object file of its own.

28.3 The link file

TWOMOD.LNK holds the rest of the link (chapter 22):

; TWOMOD.LNK - the link, in a file instead of on the command line. 
; 
; TANREN reads this when it is given @TWOMOD, and the words in it are 
; the words it would have read from the command line. A ; starts a 
; comment and the rest of the line is ignored. 
; 
; MSX-DOS gives a command line 127 characters and cuts a longer one 
; without saying so. That is the whole reason this exists: the 
; assembler is itself linked from a file of nineteen module names. 
 
/o:twomod.com 
 
main.tro putstr.tro
 

After the comment come two lines of words: /o:twomod.com, the name of the output file, and the two object files. MAIN.TRO is named first, because MSX-DOS starts a .COM program at 0100h, and TANREN puts the code of the first module there (section 21.1).

28.4 Building and running it

BUILD.BAT makes the program:

rem BUILD.BAT - EXAMPLE: TWOMOD 
rem 
rem Two modules, one program. MAIN.AS declares putstr EXTRN and 
rem PUTSTR.AS declares it PUBLIC; the linker matches the two. 
rem 
rem The link itself is in TWOMOD.LNK rather than on this line. @NAME 
rem reads a file of words, and .LNK is assumed when the name has no 
rem extension. Everything in it could have been typed here instead. 
 
rem The headers are in A:\TATARA\INCLUDE and are not copied here. 
rem TATARA is rule 3 of the include search - see the DIRS example. 
rem CHANGE THE PATH BELOW if you put the tree somewhere else. 
 
set TATARA=a:\tatara\include 
 
echo === Assembling both modules 
tatara /q main.as main.tro 
tatara /q putstr.as putstr.tro 
 
echo === Linking from TWOMOD.LNK 
tanren /q @twomod 
 
echo === Done. Type TWOMOD to run it. 
 
set TATARA=
 

It sets TATARA, so that TATARA finds MSXDOS.INC and ASCII.INC (section 13.3), assembles each module into its object file, links them with @twomod, and clears TATARA again. Figure 28.1 shows the files it reads and makes.

PIC

Figure 28.1: How BUILD.BAT makes TWOMOD.COM.

Run it from the example’s directory. /Q keeps TATARA and TANREN quiet, so only the batch file’s own lines are printed:

A:\TATARA\EXAMPLES\TWOMOD>build 
=== Assembling both modules 
=== Linking from TWOMOD.LNK 
=== Done. Type TWOMOD to run it. 
A:\TATARA\EXAMPLES\TWOMOD>twomod 
Two modules, one program. 
The linker joined them.
 

The same link without /Q shows TANREN’s summary. It needs TATARA only for assembling, so this link can be typed as it is:

A:\TATARA\EXAMPLES\TWOMOD>tanren @twomod 
Tatara MSX Linker v1.2.0 
Copyright (C) 2026 Javier Lavandeira 
https://tatara.tools 
 
2 modules, 14 records, ends at EOF. 
Wrote TWOMOD.COM, 0100-0154 (85 bytes), entry 0100.
 

28.5 What TANREN did

/M shows where TANREN put everything (section 25.7):

A:\TATARA\EXAMPLES\TWOMOD>tanren /m @twomod /o:tm.com 
... 
Modules: 2 
Groups: 
Segments: 
  flags 00 group FF base 0100 size 0055 end 0154  C 
  flags 01 group FF base 0155 size 0000 end -----  D 
Symbols: 
  DR 0147 putstr 
2 modules, 14 records, ends at EOF. 
Wrote TM.COM, 0100-0154 (85 bytes), entry 0100.
 

Both modules put everything in the default code segment, so the program is one code segment of 55h bytes, from 0100h to 0154h: the 71 bytes of MAIN first, then the 14 of PUTSTR. The data segment is empty. putstr is defined (D) by PUTSTR, referenced (R) by MAIN, and at 0147h, the first byte after MAIN. TANREN wrote 0147h into both call putstr instructions (figure 28.2).

PIC

Figure 28.2: TWOMOD.COM in memory. Both calls in MAIN go to putstr, at 0147h.

28.6 Things to try

Each change below shows one of the mistakes that TANREN finds. The files it uses are not part of the example: make them as copies of its files, in the same directory. The assembling needs TATARA, as in BUILD.BAT:

set tatara=a:\tatara\include
 

28.6.1 Leave out PUBLIC

NOPUB.AS is PUTSTR.AS without its public putstr line. It still assembles: putstr is an ordinary label in it. But no module offers the name any more, and the link fails (section 25.3):

A:\TATARA\EXAMPLES\TWOMOD>tatara /q nopub.as nopub.tro 
A:\TATARA\EXAMPLES\TWOMOD>tanren main nopub /o:np.com 
... 
Never defined: 
  putstr 
ERROR: the symbols above were never defined.
 

28.6.2 Name the modules the other way round

SWAP.LNK is TWOMOD.LNK with putstr.tro main.tro on its last line, and /o:swap.com. It links without an error, but with a warning:

A:\TATARA\EXAMPLES\TWOMOD>tanren /m @swap 
... 
Symbols: 
  DR 0100 putstr 
WARNING: SWAP.COM is entered at 0100, not at 010E. 
2 modules, 14 records, ends at EOF. 
Wrote SWAP.COM, 0100-0154 (85 bytes), entry 010E.
 

But putstr is now at 0100h, and start at 010Eh. MSX-DOS starts SWAP.COM at 0100h, so it runs putstr with whatever HL happens to hold, and never reaches the code of MAIN. Do not run it. The entry address, 010Eh, is right, but a .COM program cannot start there, and TANREN says so in its warning (section 26.5).

28.6.3 Assemble one module with /C

MAIN.AS assembled with /C, and PUTSTR.AS without it, cannot be linked together (section 25.5):

A:\TATARA\EXAMPLES\TWOMOD>tatara /q /c main.as mainc.tro 
A:\TATARA\EXAMPLES\TWOMOD>tanren mainc putstr /o:mix.com 
... 
ERROR: one module was assembled /C and another was not.