第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つの部分からなります。
- コードは標準のコード・セグメントにあります。runsに 1を加え、MSX-DOSのファンクション_STROUTでmsgを表示して終わります。
- 2つのふつうの変数runsとlastkeyは、標準のデータ・セグメントにあります。それぞれが自分の1バイトを持ちます。
- トランジェント・セグメントSCRATCHには、2つのグループがあります。READINGは、読み込み中の行のバッファとその長さで、66バイトです。WRITINGは、書き出し中の行のバッファとその長さで、34バイトです。どちらのグループもSCRATCHの先頭から始まるので、inbufとoutbufは同じアドレスになり、SCRATCHは100バイトではなく66バイトになります。
プログラムは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に、両方のリンクを示します。
どちらのファイルにも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節)。