Tatara

第25章
モジュール間のシンボル

モジュールは、PUBLICでほかのモジュールに名前を提供し、EXTRNでほかのモジュールの名前を使います(12.5節)。TATARAは外部の名前をすべて未完成のまま残し、TANRENがそれを、ほかのモジュールが与えた値で仕上げます。この章では、TANRENが名前をどのように対応させるか、どのモジュールも定義していない名前や2つのモジュールが定義している名前をどう扱うか、そして/Mが何を表示するかを説明します。

25.1 名前の対応のさせかた

21章のプログラムでは、MAIN.ASがgreetをEXTRNで宣言して呼び出し、SUB.ASがそれをPUBLICで宣言して定義しています。

; 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は、リンク全体で1つの名前の表を持ち、モジュールを2回読みます。

  1. 1回目のパスでは、すべてのモジュールのパブリックな名前と外部の名前を表に読み込みます。パブリックな名前は、値とともに定義されます。greetはSUBのコードの最初のバイトで、SUBのコードはMAINの8バイトのあとに来るので、greetは0108hです。外部の名前は参照されます。つまり、どこかのモジュールがその値を必要としています。パスの終わりには、参照されている名前がすべて定義されていなければなりません。
  2. 2回目のパスでは、各モジュールのバイトを書き出し、外部の名前が使われている場所すべてに、表の値を入れます。

図25.1は、2つのパスでのgreetの流れを示しています。TATARAはcall greetをCD 00 00として書き出し、TANRENがそれを0108hを呼ぶCD 08 01に変えます。

PIC

図 25.1: MAINでは外部、SUBではパブリックなgreetが値を得るまで。

どのバイトを書き出す前にも、すべての名前が表に入っているので、名前についてはモジュールの順番は関係ありません。モジュールは、自分より前に指定したモジュールの名前も、あとに指定したモジュールの名前も使えます。順番が関係するのは、各モジュールがメモリのどこに置かれるかだけです(24章)。

/Mは、ほかの情報とともにこの表を表示します。このプログラムでは名前は1つです。

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はgreetが定義されていること、Rは参照されていることを示し、0108がその値です。/Mのそのほかの表示は25.7節で説明します。

25.2 外部の名前の値

外部の名前には、数を加えることも引くこともできます(8.6節)。TATARAはその数を値が入る場所に書いておき、TANRENがそれに名前の値を加えます。次の2つのモジュールは、コードとデータの中で名前を使っています。

; 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はUSEのデータのあとの010Ehにあるので、USE.COMの中ではld hl,table+2は21 10 01に、dw table,table+4は0E 01 12 01になります。

countはEQUで定義されているので、セグメントの中のアドレスではなくアブソリュートな値です(8.6節)。TANRENはこれを動かしません。モジュールがどこに置かれても000Ahで、ld de,count+1は11 0B 00になります。

25.3 どのモジュールも定義していない名前

参照されているのに定義されていない名前があると、TANRENは1回目のパスの終わりで止まり、出力ファイルは書き出されません。MAINだけをリンクすると、greetを定義するモジュールがありません。

A:\>tanren main 
... 
Never defined: 
  greet 
ERROR: the symbols above were never defined.
 

TANRENは、最初の1つだけでなく、足りない名前をすべて表示するので、1回の実行ですべてがわかります。次のモジュールは3つの名前を使い、そのうちbetaだけが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.
 

名前は、特に決まっていない順で表示されます。

警告.  EXTRNで宣言した名前は、モジュールがそれを一度も使わなくても参照されていることになり、TANRENはやはりその定義を求めます。

; UNUSED.AS - declares an external and never uses it. 
                extrn   nothere 
                cseg 
start:          ret 
                end     start
 
A:\>tanren unused 
... 
Never defined: 
  nothere 
ERROR: the symbols above were never defined.
 

モジュールで外部の名前を使わなくなったら、EXTRNの行からもその名前を消してください。

25.4 2回定義された名前

1つの名前を定義できるのは1つのモジュールだけです。2つ目の定義があると、TANRENはモジュールを読んでいる途中で止まり、エラーの前の行に、その名前と、2つ目の定義が見つかったモジュールを表示します。次の例では、DUP2.ASが2つ目のgreetを定義していて、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.
 

モジュールには、オブジェクト・ファイルではなく、アセンブルしたソースの名前が付くので、メッセージにはotherではなくdup2と表示されます。

1つのオブジェクト・ファイルを2回指定したときも、たとえばコマンド・ラインとリンク・ファイルの両方に書いたときも(22章)、同じことが起きます。

A:\>tanren main sub sub /o:dup.com 
... 
  greet is defined again in module sub 
ERROR: that public symbol is already defined.
 

TANRENは、2回定義された最初の名前で止まり、そのあとで足りない名前を探すことはしません。1つのリンクに両方の誤りがあるときは、まず2回定義された名前を直してから、もう一度リンクしてください。

25.5 大文字と小文字

/Cを付けないと、TATARAは名前の大文字と小文字を区別せず(7.3節)、TANRENも区別しません。次のモジュールは、大文字でGREETを呼びます。

; UPPER.AS - calls GREET, in capitals. 
                include msxdos.inc 
                extrn   GREET 
                cseg 
start:          call    GREET 
                system  _TERM0 
                end     start
 

このモジュールは、名前がgreetであるSUBとリンクできます。/Mは、TANRENが最初に見た形で名前を表示します。

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.
 

/Cを付けてアセンブルすると、2つのモジュールの名前は別の名前になり、一方は定義されているだけ、もう一方は参照されているだけなので、リンクは失敗します。

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.
 

/Cを付けてアセンブルしたモジュールと付けずにアセンブルしたモジュールは、一緒にリンクできません(7.3節)。

A:\>tanren upperc sub /o:mix.com 
... 
ERROR: one module was assembled /C and another was not.
 

25.6 すべてのモジュールがリンクされる

TANRENは、渡されたモジュールをそのままリンクします。ほかのどのモジュールからも参照されないモジュールもリンクされ、そのバイトは出力ファイルに入ります。EXTRA.ASのspareは、どこからも参照されていません。

; 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は定義されていて参照されておらず、その3バイトの分だけ、EX.COMはMAIN.COMより3バイト長くなります。TANRENは、プログラムに必要なモジュールをライブラリから探すことはしません。コマンド・ラインやリンク・ファイルで指定したモジュールは、使われるかどうかにかかわらず、すべてリンクされます。

25.7 /Mの表示のすべて

/Mは、バナーのあと、要約行の前に、4つの部分を表示します。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.
 

/Mは、足りない名前があるときも、その一覧の前に表を表示します。足りない名前は-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.
 

大きさが0000のセグメントには最後のアドレスがないので、endは––-と表示されます。

25.8 メッセージ

名前に関するメッセージを表25.1に示します。TANRENのすべてのメッセージは27章にあります。

メッセージ

意味

ERROR: the symbols above were never defined.

Never defined:の下に表示された名前は、どこかのモジュールが外部として宣言していますが、どのモジュールも定義していません。

ERROR: that public symbol is already defined.

その前の行のNAME is defined again in module MODが示すパブリックな名前を、2つ目のモジュールも定義しています。

ERROR: one module was assembled /C and another was not.

すべてのモジュールを/C付きで、またはすべてを/C なしでアセンブルする必要があります。

表 25.1: 名前に関するメッセージ。