Tatara

第29章
トランジェント・セグメントで
共有する作業用変数

プログラムには、その一部だけが、しかもその部分が動いている間だけ使う変数がよくあります。読み込み中の行のバッファと、書き出し中の行のバッファなどです。2つの部分が同時に動くことがなければ、2つのバッファは同じメモリを共有できます。それを行うのがトランジェント・セグメント(10.6節)です。この章では、A:\TATARA\EXAMPLES\LAYOUTにある例、LAYOUTを使って、ソース、TATARAが作るシンボル表、そして TANRENが2つの異なるアドレスでリンクしたときに作るマップを順に見ていきます。

29.1 この例

LAYOUTは、1つのモジュールLAYOUT.ASとBUILD.BATからなります。プログラム自体は1行を表示するだけです。

A:\TATARA\EXAMPLES\LAYOUT>layout 
Code here, data elsewhere.
 

この例の目的は、バイトがどこに置かれるかを見ることです。コード、2つのふつうの変数、そしてトランジェント・セグメントSCRATCHがあり、SCRATCHには同じメモリを共有する2つの変数のグループがあります。BUILD.BATは、シンボル表と2つのマップをファイルに書き出すので、作ったあとでそれを読むことができます。

29.2 ソース

; LAYOUT.AS - where the bytes go. 
; 
; Three kinds of relocatable space, and the options that place them: 
; 
;   CSEG                        code            TANREN /P:<addr> 
;   DSEG                        data            TANREN /D:<addr> 
;   DSEG <name>,TRANSIENT       overlaid data   one GROUP at a time 
; 
; A SEGMENT MAY BE NAMED, and a named one keeps its own counter: two 
; CSEG MUSIC lines in different files contribute to one segment. 
; 
; A TRANSIENT DSEG is space that is used by one part of a program at a 
; time. Every GROUP in it starts at the same address, so the segment is 
; as large as its largest group and not as large as their sum. Labels 
; in two different groups cannot be subtracted from each other, 
; because nothing says which of them is in memory. 
; 
; The absolute kind, ASEG, is in the BINARY and ROM examples - a fixed 
; address in a .COM would make the file span everything from 0100h up 
; to it. 
 
                include msxdos.inc      ; _STROUT, _TERM0 and "system" 
                include ascii.inc       ; CHR_CR and CHR_LF 
 
                cseg 
 
start:          ld      hl,runs         ; a byte in the plain DSEG 
                inc     (hl) 
                ld      de,msg 
                system  _STROUT 
                system  _TERM0 
 
msg:            db      "Code here, data elsewhere.",CHR_CR,CHR_LF,"$" 
 
                dseg                    ; the plain data segment: the 
runs:           ds      1               ;   linker puts it after the code 
lastkey:        ds      1               ;   unless /D: says where 
 
                dseg    SCRATCH,TRANSIENT 
                group   READING         ; 66 bytes... 
inbuf:          ds      64 
inlen:          ds      2 
                group   WRITING         ; ...and 34, at the same address, 
outbuf:         ds      32              ;   so SCRATCH is 66 bytes and not 
outlen:         ds      2               ;   100 
 
                end     start
 

ソースは、セグメントの種類ごとに3つの部分からなります。

プログラムは2つのグループを使いません。置かれる場所を見せるためにあるだけです。実際のプログラムなら、行を読み込んでいる間はREADINGを、行を書き出している間はWRITINGを使い、両方を同時に使うことはありません。outbufに書き込むと、inbufが上書きされるからです(10.6節)。

29.3 作る

rem BUILD.BAT - EXAMPLE: LAYOUT 
rem 
rem This example is about the two maps it writes, not about the 
rem program, which only prints a line. 
rem 
rem   TATARA /S   the symbol table: every name, by segment, with its 
rem               offset inside that segment 
rem   TANREN /M   the segment and group tables: where each segment 
rem               ended up, how big it is, and which groups share it 
rem 
rem It links twice. The first is an ordinary .COM at 0100h. The second 
rem puts the code at 8000h and the data at C000h - a real MSX layout, 
rem code in the top pages with its variables above it, and not 
rem something MSX-DOS can run as a command, so it goes to a .BIN that 
rem nobody is asked to run. 
 
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, keeping the symbol table 
tatara /q /s layout.as layout.tro > layout.sym 
 
echo === Linking at 0100h, keeping the map 
tanren /q /m /o:layout.com layout.tro > layout.map 
 
echo === Linking again, code at 8000h and data at C000h 
tanren /q /m /o:layout8.bin /p:8000 /d:c000 layout.tro > layout8.map 
 
echo === Done. Read LAYOUT.SYM, LAYOUT.MAP and LAYOUT8.MAP. 
 
set TATARA=
 

各コマンドは、表示する内容を>でファイルに送ります。/Qを付けると、TATARAとTANRENはバナーも要約も表示しないので、ファイルに入るのはシンボル表(/S、19章)とマップ(/M、25.7節)だけです。2回目のリンクでは、コードを8000hに、データをC000hに置きます(24.3節)。その出力ファイルLAYOUT8.BINは、MSX-DOSが実行できるプログラムではなく、実行するためのものでもありません。そのマップを見るためにあります。

A:\TATARA\EXAMPLES\LAYOUT>build 
=== Assembling, keeping the symbol table 
=== Linking at 0100h, keeping the map 
=== Linking again, code at 8000h and data at C000h 
=== Done. Read LAYOUT.SYM, LAYOUT.MAP and LAYOUT8.MAP.
 

29.4 シンボル表を読む

LAYOUT.SYMは、アブソリュートのセクションで始まります。そこにはMSXDOS.INCとASCII.INCのすべてのEQUが入っていて、100行以上あるので、ここでは省略しています。そのあとに、プログラム自身の名前のセクションが続きます。

A:\TATARA\EXAMPLES\LAYOUT>type layout.sym 
ASEG - absolute 
 
0000h             CHR_NUL 
0000h             _TERM0 
... 
007Fh             CHR_DEL 
 
CSEG - default code segment, 002Eh bytes 
 
0000h             start 
0011h             msg 
 
DSEG - default data segment, 0002h bytes 
 
0000h             runs 
0001h             lastkey 
 
SCRATCH - named data segment, transient, 0000h bytes 
 
(no symbols) 
 
SCRATCH - named data segment, transient, group READING, 0042h bytes 
 
0000h             inbuf 
0040h             inlen 
 
SCRATCH - named data segment, transient, group WRITING, 0022h bytes 
 
0000h             outbuf 
0020h             outlen
 

それぞれの値は、そのセクションの中のオフセットです。SCRATCHには 3つのセクションがあります。どのグループにも入らない部分のセクション(ここでは空です)と、グループごとのセクションです。inbufとoutbufは、どちらも自分のグループの中の0000hにあります。グループを置くのはTANRENなので、この時点のシンボル表には、2つが同じアドレスになることはまだわかりません。

29.5 マップを読む

LAYOUT.MAPは、.COMのプログラムのマップです。

A:\TATARA\EXAMPLES\LAYOUT>type layout.map 
Modules: 1 
Groups: 
  READING 
  WRITING 
Segments: 
  flags 00 group FF base 0100 size 002E end 012D  C 
  flags 01 group FF base 012E size 0002 end 012F  D 
  flags 03 group 00 base 0130 size 0042 end 0171 SCRATCH 
  flags 03 group 01 base 0130 size 0022 end 0151 SCRATCH 
Symbols:
                                                                    

                                                                    
 

コードは0100hから始まって2Ehバイトあり、2つのふつうの変数がそのあとの 012Ehに続き、最後にSCRATCHが、グループごとに1行ずつ並びます。2行とも同じ0130hから始まります。READINGは0171hまで、小さいほうのWRITINGは0151hまでです。モジュールがパブリックな名前も外部の名前も宣言していないので、Symbols:は空です。

LAYOUT8.MAPは、2回目のリンクのマップです。

A:\TATARA\EXAMPLES\LAYOUT>type layout8.map 
Modules: 1 
Groups: 
  READING 
  WRITING 
Segments: 
  flags 00 group FF base 8000 size 002E end 802D  C 
  flags 01 group FF base C000 size 0002 end C001  D 
  flags 03 group 00 base C002 size 0042 end C043 SCRATCH 
  flags 03 group 01 base C002 size 0022 end C023 SCRATCH 
Symbols:
 

セグメントもその大きさも同じで、変わるのはアドレスだけです。コードは 8000hに、データはすべて、ふつうの変数もSCRATCHもC000hにあります。図29.1に、両方のリンクを示します。

PIC

図 29.1: 0100hでリンクしたLAYOUTと、コードを 8000h、データをC000hにしてリンクしたLAYOUT。どちらでも、SCRATCHの2つのグループは同じアドレスから始まります。

どちらのファイルにもSCRATCHは入っていません。そのバイトはDSで確保されていて、DSはファイルに何も書きません(24.1.1節)。LAYOUT.COMは、0100hから 012Dhまでの46バイトです。

29.6 試してみること

次のファイルは例には含まれていません。BUILD.BATと同じようにTATARAを設定して、例のディレクトリに作ってください。

29.6.1 別のグループのラベルどうしを引き算する

; SUBGRP.AS - a label from each group of one transient segment, 
; in one expression. 
                dseg    SCRATCH,TRANSIENT 
                group   READING 
inbuf:          ds      64 
                group   WRITING 
outbuf:         ds      32 
                cseg 
                ld      hl,outbuf-inbuf 
                end
 
A:\TATARA\EXAMPLES\LAYOUT>tatara subgrp.as subgrp.tro 
... 
SUBGRP.AS(9): ERROR: relocation error - a segment-relative value is not allowed here.
 

どちらのラベルもオフセットは0000hですが、グループが違うので、TATARAは 2つを関係のないアドレスとして扱います(10.6節)。

29.6.2 別のモジュールでグループを加える

PARSE.ASは、80バイトの3つ目のグループPARSINGを加えます。

; PARSE.AS - a third group of SCRATCH, larger than the other two. 
                public  tokens 
                dseg    SCRATCH,TRANSIENT 
                group   PARSING 
tokens:         ds      80 
                end
 
A:\TATARA\EXAMPLES\LAYOUT>tatara /q parse.as parse.tro 
A:\TATARA\EXAMPLES\LAYOUT>tanren /m /o:lp.com layout parse 
... 
Groups: 
  READING 
  WRITING 
  PARSING 
Segments: 
  flags 00 group FF base 0100 size 002E end 012D  C 
  flags 01 group FF base 012E size 0002 end 012F  D 
  flags 03 group 00 base 0130 size 0042 end 0171 SCRATCH 
  flags 03 group 01 base 0130 size 0022 end 0151 SCRATCH 
  flags 03 group 02 base 0130 size 0050 end 017F SCRATCH 
Symbols: 
  D- 0130 tokens 
2 modules, 20 records, ends at EOF. 
Wrote LP.COM, 0100-012D (46 bytes), entry 0100.
 

PARSINGも0130hから始まり、いちばん大きいグループなので、SCRATCHは017Fhまでの80バイトになります。トランジェント・セグメントのグループは、モジュールをまたいで重なります(24.4節)。

29.6.3 別のモジュールからグループに加える

READ2.ASは、LAYOUT.ASにすでにあるグループREADINGに変数を1つ加えます。

; READ2.AS - more variables for the group READING, from another module. 
                public  linenum 
                dseg    SCRATCH,TRANSIENT 
                group   READING 
linenum:        ds      2 
                end
 
A:\TATARA\EXAMPLES\LAYOUT>tatara /q read2.as read2.tro 
A:\TATARA\EXAMPLES\LAYOUT>tanren /m /o:lr.com layout read2 
... 
Segments: 
  flags 00 group FF base 0100 size 002E end 012D  C 
  flags 01 group FF base 012E size 0002 end 012F  D 
  flags 03 group 00 base 0130 size 0044 end 0173 SCRATCH 
  flags 03 group 01 base 0130 size 0022 end 0151 SCRATCH 
Symbols: 
  D- 0172 linenum 
2 modules, 20 records, ends at EOF. 
Wrote LR.COM, 0100-012D (46 bytes), entry 0100.
 

ほかのモジュールにある同じ名前のグループは、同じグループです。その変数は、どのセグメントでも同じように前のものに続きます。linenumはinbufとinlenのあとの0172hに置かれ、READINGは 44hバイトになります。大きなプログラムの各部分は、こうして作業用のメモリを共有します。読み込み中の行を扱うモジュールは、それぞれ自分の変数をREADINGに加え、ほかのモジュールについて知る必要はありません。

29.6.4 TRANSIENTを忘れる

PLAIN.ASは、TRANSIENTなしでSCRATCHを宣言しています。

; PLAIN.AS - SCRATCH again, but not transient. 
                public  plainv 
                dseg    SCRATCH 
plainv:         ds      10 
                end
 
A:\TATARA\EXAMPLES\LAYOUT>tatara /q plain.as plain.tro 
A:\TATARA\EXAMPLES\LAYOUT>tanren /m /o:lpl.com layout plain 
... 
PLAIN.TRO: ERROR: segment SCRATCH is declared differently.
 

セグメントは、どのモジュールでも同じように宣言しなければなりません(27.4節)。