Tatara

第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)。

PIC

図 33.1: 4000hにあるROM.ROMのヘッダーと、各欄の値。

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がそのすべてを置きます。