第33章
16 KBのROMカートリッジ
カートリッジは、電源を入れたときにMSXが自分で見つけて起動する、ROMの中のプログラムです。この章では、A:\TATARA\EXAMPLES\ROMにある例、ROMを作ります。16 KBのカートリッジのイメージを作る例で、MSXが探すヘッダー、1行を表示するルーチン、そしてファイルを正しい大きさにする詰め物からなります。そのあと、イメージをエミュレーターで実行し、3通りに変えてみます。
33.1 この例
カートリッジは、MSX-DOSやMSX-BASICよりも前に起動します。MSXは各スロットを調べてカートリッジを見つけ、まだ起動の途中でそれを呼び出します。プログラムはいつでもあるBIOSを使えますが、MSX-DOSやBASICが用意するものは使えません。ROMは、テキスト画面を用意して1行を表示し、そのまま止まります。
この例には、ROM.ASとBUILD.BATの2つのファイルがあります。結果は、カートリッジの16 KBをそのままバイト単位で持つファイルROM.ROMです。これはMSX-DOSが実行できるプログラムではなく、エミュレーターにカートリッジとして入れるか、フラッシュ・カートリッジに書き込んで使います。
33.2 ソース
; ROM.AS - a 16 KB cartridge. ; ; A cartridge in page 1 begins at 4000h with a sixteen-byte header. ; The BIOS looks for the letters AB, and calls the INIT address if it ; is not zero - before there is an operating system of any kind. ; ; NOTHING HERE MAY CALL MSX-DOS. There is no MSX-DOS yet. The screen ; and the printing are the BIOS, and the code never returns, because ; there is nothing to return to. ; ; THE LAST TWO LINES ARE WHAT MAKES THE FILE 16 KB. TANREN writes the ; span from the lowest byte of content to the highest, and a gap ; inside that span comes out as zeros. One byte at 7FFFh therefore ; makes the file exactly 16384 bytes, which is what a cartridge has ; to be. A DS would not do it: the space a DS reserves after the last ; byte of content is deliberately not in the file. include bios.inc ; INITXT, CHPUT, and the rest include ascii.inc ; CHR_CR and CHR_LF aseg org 4000h db "AB" ; a cartridge, says the BIOS dw init ; INIT: called at once dw 0 ; STATEMENT: no CALL statements dw 0 ; DEVICE: no device dw 0 ; TEXT: no BASIC program in here dw 0,0,0 ; reserved, and zero init: call INITXT ; a text screen, from nothing ld hl,msg ini.lp: ld a,(hl) or a jr z,ini.end push hl call CHPUT pop hl inc hl jr ini.lp ini.end: jr ini.end ; a cartridge has nowhere to go msg: db "Assembled by Tatara. Linked by TANREN.",CHR_CR,CHR_LF,0 org 7fffh ; the last byte of the 16 KB... db 0 ; ...so that the file is all of it end
33.2.1 ヘッダー
16 KBのカートリッジは4000hから7FFFhにあり、その最初の16バイトは、カートリッジに何が入っているかをMSXに伝えるヘッダーです(図33.1)。
- IDは2バイトで、文字ABです。これがないと、MSXはそのROMをカートリッジとして扱いません。
- INITは、MSXが起動するときに呼び出すルーチンのアドレスで、なければ0000hです。ここではinitです。
- STATEMENT、DEVICE、TEXTは、BASICにCALL文やデバイスを加えるルーチンと、ROMの中のBASICプログラムのアドレスです。このカートリッジにはどれもないので、0000hです。
- 予約された6バイトは、00hでなければなりません。
MSXはヘッダーを4000hと8000hで探します(26.6節)。ASEGとORG 4000hがヘッダーを4000hに置き、TANRENが動かすものは何もありません。
33.2.2 INIT
initは、テキスト画面を用意するBIOSのルーチンINITXTを呼び、CHPUTでメッセージを表示してから、ini.end: jr ini.endで自分自身へ永久にジャンプし続けます。MSXは、カートリッジが残した画面のままになります。たとえばゲームは、このようにしてマシンを占有します。
33.2.3 最後の2行
カートリッジのイメージには、16 KBのすべてが入っていなければなりません。TANRENは内容のある最初のバイトから最後のバイトまでを書き出すので(24.1.1節)、プログラムだけでは77バイトのファイルになります。org 7fffhとdb 0が最後のアドレスに1バイトを置き、メッセージから7FFFhまでの間はすべて0として書き出されます。
33.3 作る
rem BUILD.BAT - EXAMPLE: ROM rem rem A 16 KB cartridge image. No MSX-DOS anywhere in it: the code runs rem from a cartridge slot before there is an operating system, so it rem calls the BIOS and never returns. rem rem ASEG and ORG 4000h page 1, where a 16 KB cartridge lives rem no /B a ROM has no header of any kind rem ORG 7FFFH and DB 0 what makes the file exactly 16384 bytes rem rem THIS IS THE ONE EXAMPLE A SUCCESSFUL BUILD DOES NOT PROVE. Load rem ROM.ROM as a cartridge in an emulator, or write it to a flash rem cartridge. It is not a program whose name you can type. 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 tatara /q rom.as rom.tro echo === Linking, with no header of any kind tanren /q /o:rom.rom rom.tro echo === Done. ROM.ROM should be exactly 16384 bytes. set TATARA=
リンクには/Bを付けません。ファイルはカートリッジ自身のバイトで、その前にBLOADのヘッダーはありません。ヘッダーは、ソースの中にあるものだけです。
A:\TATARA\EXAMPLES\ROM>build === Assembling === Linking, with no header of any kind === Done. ROM.ROM should be exactly 16384 bytes.
/Qなしで実行すると、TANRENの要約で大きさを確かめられます。
A:\TATARA\EXAMPLES\ROM>tanren /o:rom.rom rom.tro ... 1 modules, 6 records, ends at EOF. Wrote ROM.ROM, 4000-7FFF (16384 bytes).
ファイルは41 42 10 40で始まります。ABと、下位バイトが先のinitのアドレス4010hで、そのあとにほかの欄の10バイトの0が続きます。initは4010hからCD 6C 00、つまりcall INITXTで始まります。
33.4 実行する
ROM.ROMをカートリッジとしてエミュレーターに入れ、MSXをリセットします。たとえばopenMSXでは、コンソールのコマンドcartaがROM イメージを1つ目のカートリッジ・スロットに入れ、resetがマシンを再起動します。画面には、テキスト画面にメッセージが表示されるだけです。
Assembled by Tatara. Linked by TANREN.
MSXは4000hにABを見つけてinitを呼び、そこから戻ってきませんでした。MSX-DOSもBASICも起動しません。
33.5 試してみること
次のファイルは例には含まれていません。BUILD.BATと同じようにTATARAを設定して例のディレクトリに作り、ROM.ASと同じようにリンクしてください。
33.5.1 INITから戻る
ROMRET.ASは、ROM.ASの永久ループをretにしたものです。
ini.end: ret ; and the MSX goes on starting
メッセージが少しの間表示され、そのあとMSXは、カートリッジがないときと同じように起動を続けます。ディスク・ドライブのあるMSXでは、MSX-DOSが起動します。ROM.ASの先頭のコメントには戻る先がないとありますが、MSXはINITが戻るのを待っています。システムに何かを加えるだけで、残りの起動はシステムに任せるカートリッジは、必要な準備をしてから戻ります。
33.5.2 IDを壊す
ROMBAD.ASは、ROM.ASの"AB"を"AC"にしたものです。
db "AC" ; not a cartridge ID
ほかと同じように16 KBのファイルにリンクできますが、MSXはこれをカートリッジとして扱いません。何も表示されず、MSXはスロットが空であるかのようにMSX-DOSを起動します。カートリッジが何もしないときは、ファイルの最初の2バイトを確かめてください。
33.5.3 カートリッジをTANRENに置かせる
カートリッジはASEGを使わなくてもかまいません。ROMREL.ASは同じカートリッジを標準のコード・セグメントに置いたもので、/P:で4000hに置かれます(24.3節)。
; ROMREL.AS - the cartridge as a relocatable module, placed by /P:4000. include bios.inc include ascii.inc cseg header: db "AB" dw init dw 0,0,0 dw 0,0,0 init: call INITXT ld hl,msg ini.lp: ld a,(hl) or a jr z,ini.end push hl call CHPUT pop hl inc hl jr ini.lp ini.end: jr ini.end msg: db "Placed by TANREN /P:4000.",CHR_CR,CHR_LF,0 ds 3FFFh-($-header) db 0 end
リロケータブルなセグメントをORGで決まったアドレスまで埋めることはできないので、詰め物の量は大きさから計算します。$-headerはそこまでのバイト数で、DSは16 KBから1バイトを引いた残りを確保します。そのあとのDBが最後のバイトを埋めるので、DSはファイルの終わりのあとではなく、ファイルの中に入ります。
A:\TATARA\EXAMPLES\ROM>tatara /q romrel.as romrel.tro A:\TATARA\EXAMPLES\ROM>tanren /p:4000 /o:romrel.rom romrel.tro ... 1 modules, 7 records, ends at EOF. Wrote ROMREL.ROM, 4000-7FFF (16384 bytes).
カートリッジとして入れると、自分の行を表示してそのまま止まります。
Placed by TANREN /P:4000.
このように書けば、カートリッジのコードも、ほかのプログラムと同じように複数のモジュールに分けることができ、TANRENがそのすべてを置きます。