概要
Z80 のプログラミング修行用に、macOS で MSX のアプリ開発環境(※オールマシン語)を構築する方法を紹介します。
- 言語: Z80アセンブリ言語
- メディア形式: 32KB ROM (標準ROM)
- 実行環境: WebMSX
恐らく、Linux でもほぼ同じ手順でできます。
1. アセンブラを準備
アセンブラは z88dk 付属の z80asm
を使います。
z88dk は、MSX 以外にもゲームギア、ゲームボーイ、X1、PC-8801 など、 Z80 系の色々なゲーム機やパソコン に対応した SDK です。とても便利。
各種プラットフォームの C ライブラリが充実しているので、どちらかというと C言語 で組む場合に使うケースが多いかもしれませんが、今回は C コンパイラやライブラリは一切使わず、オールマシン語で組みます。ハンドアセンブルでも良かったのですが流石にニーモニックは使います。(要するに、必ずしも z88dk である必要はありません)
1-1. インストール
mkdir ~/msx-work
cd ~/msx-work
curl -O http://nightly.z88dk.org/z88dk-osx-latest.zip
tar xvf z88dk-osx-latest.zip
export PATH=${PATH}:${HOME}/msx-work/z88dk/bin
1-2. チェック
z80asm
コマンドが実行できれば準備 OK です。
% z80asm
Z80 Module Assembler 16923-cc7a9072f-20200910
(c) InterLogic 1993-2009, Paulo Custodio 2011-2020
2. メモリマップ
8bit系、16bit系のプラットフォームでアプリ開発をする時に 一番重要な情報はメモリマップ(と、I/Oマップ)と言っても過言では有りません。
という訳で、プログラムを組む前に今回作る 32KB 標準 ROM カートリッジで MSX が動作する時のメモリマップ について理解しておきましょう。
MSX のメモリマップはかなり自由度の高い構造になっています。
実のところ、MSX や X1 などの PC 系はメモリマップが複雑で面倒くさいので、初学者向けにはゲーム系(マスターシステムやゲームギアあたり)の方がオススメかもしれません。
2-1. スロット構成
MSX は 16KB 区切り 4 ページのメモリレイアウトになっています。
(スロットと呼ばれています)
32KB の ROM を MSX の スロット1 に挿入した状態で電源を入れると、BIOS のブート処理が完了した時、以下のようなメモリレイアウトになり、ROM ヘッダに書き込まれているスタートアドレスからプログラムが動き始めます。
アドレス | 内容 |
---|---|
0x0000 ~ 0x3FFF |
BIOS (ブート処理、割り込み処理、BASIC) |
0x4000 ~ 0x7FFF |
ROM の前半 16KB |
0x8000 ~ 0xBFFF |
ROM の後半 16KB |
0xC000 ~ 0xFFFF |
RAM |
メガロム(32KBより大きいサイズのROM)の場合もこのメモリレイアウトになります。メガロムには、ROM 領域を 8KB 区切りでバンク切り替えするものと、16KB 区切りでバンク切り替えするものが存在します。(ROM 領域への書き込み (LD) でカセット内蔵のバンクコントローラにバンク切り替えのリクエストを行う形になっています)
なお、MSX 実機で動作できるメガロムを開発することは可能ですが、MSX エミュレータにメガロムの種別を認識させる手段がないので、メガロム対応の MSX アプリ開発は基本的にできません。(例えば、WebMSX の場合はこのような形で ROM ファイルのチェックサムのようなものからメガロムの種別を認識しています)
BIOS は、ROM のヘッダ領域(先頭 16 バイト)の先頭 2 バイトの内容を見て、ROM カセットが挿入されていることを認識します。
今回、ROM のスタートアドレスを 0x4010
にするので、ヘッダの内容は次のようにします。
- 1バイト目:
A (0x41)
BIOS に ROM と認識させるプレフィクス - 2バイト目:
B (0x42)
BIOS に ROM と認識させるプレフィクス - 3バイト目:
0x10
スタートアドレスの下位8bit - 4バイト目:
0x40
スタートアドレスの上位8bit - 5バイト目 ~ 16バイト目:
0x00
2-2. RAM について
前項のスロット構成だと 16KB の RAM が使用できる形になっています。
MSX の搭載 RAM サイズは、8KB、16KB、32KB、64KB (※MSX2以降は標準装備)など色々なバリエーションがありますが、32KB 以上の RAM を使用する場合、BIOS 処理の呼び出し(インタースロットコール)でスロット構成を切り替えて使用します。
MSX の最低 RAM サイズは 8KB なので、その想定でプログラムを開発すればより多くの実機環境で動作できるメリットがあります。
そのため、大手が開発した MSX 用の市販ゲームソフトは 8KB RAM で動作するものが大半です。
8KB RAM を搭載している機種としては カシオの PV-7 あたりが有名です。
8KB RAM の場合、
-
0xC000 ~ 0xDFFF
: RAM -
0xE000 ~ 0xFFFF
:0xC000 ~ 0xDFFF
のミラー
というメモリマップになります。
8bit ゲーム機の大半が、RAM は多くても 8KB 程度しか搭載していないので、8KB もあれば十分かと思われます。
参考:
- SG-1000: 1KB
- ファミコン: 2KB(※拡張RAM搭載のマッパーなら+8KB)
- マスターシステム: 8KB
- ゲームボーイ: 8KB
3. VDP (TMS9918A)
MSX は TMS9918A と呼ばれるテキサス・インスツルメンツ社の VDP; Video Display Processor(映像表示装置) を搭載しています。
一部、MSX2 に搭載されている VDP (V9938) を搭載している MSX も存在します。(例: Yamaha YIS-503II)
TMS9918A は、MSX 以外にも SG-1000、コレコビジョンなど、様々なコンピュータで使われています。
TMS9918A は、テキサス・インスツルメンツ社の TI-99/4 に搭載された TMS9918 を改良したもので、後継機の TI-99/4A に最初に搭載されました。
TMS9918 と TMS9918A の大きな違いは、mode 2 と呼ばれる画面モードの有無です。
TI-99/4 は、1979年 に発売された世界初の 16bit CPU (TMS9900) を搭載したホームコンピュータ(パソコン)として知られていますが、殆ど売れなかった(出荷台数: 2万台以下)ようです。しかし、1981年 に発売された後継機のTI-99/4A はヒットした(出荷台数: 280万台)ので、TMS9918A は MSX が発売された当時(1983年)は、比較的安価に入手できたものと考えられます。
8bit ~ 16bit の SEGA ハードの VDP は、TMS9918A をベースに機能強化(mode 4 や mode 5 を追加)する形で進化しているので、TMS9918A を扱えるようになれば、比較的カンタンに SEGA ハードのゲーム開発ができるようになると思います。
MSX2 の VDP V9938 (MSX-VIDEO) も TMS9918A をベースに機能強化されていますが、どちらかといえばビットマップ系の機能に注力しているので、ゲームよりもグラフ表示などの実用用途向けに進化したものといえます。
TMS9918A には色々な画面モードがありますが mode 2 だけ覚えれば大丈夫 です。
私の知る限り、MSX の市販ゲームソフトは全て mode 2 で作られています。「これなら mode 0 で作れるんじゃないか?」と思われるゲームですら、mode 2 で作られています。
mode 1 や mode 3 を使っている MSX か SG-1000 のゲームソフトが存在するのかが謎...(mode 3 の鬼のように荒いビットマップでゴリゴリ動くゲームとかがあるなら一見の価値があるかも?)
なお、TMS9918A の詳しい使い方は、本記事では省略します。
以下のドキュメントを参考にしてください。
若干意訳混じりですが、以下に上記を翻訳したものがあります。
https://github.com/suzukiplan/tinymsx/blob/master/doc/TMS9918A.md
MSX の BIOS のブート処理が完了してスタートアドレスが実行される時点の TMS9918A の状態は、以下のようになっています。
- 画面モード:
mode 0
- VRAMサイズ:
16KB
(メモリマップ)
用途 | アドレス | 補足事項 |
---|---|---|
キャラクタパターン | 0x0000 ~ 0x07FF |
mode 2 なら 0x0000 ~ 0x17FF
|
ネームテーブル | 0x1800 ~ 0x1AFF |
|
スプライト属性(OAM) | 0x1B00 ~ 0x1B7F |
|
カラーテーブル | 0x2000 ~ 0x27FF |
mode 2 なら 0x2000 ~ 0x37FF
|
スプライトパターン | 0x3800 ~ 0x3FFF |
mode 0 と mode 2 の違いは、キャラクタパターン数です。
- mode-0: 全部で 256 パターン
- mode-2: 全部で 768 パターン
- ネームテーブル上部 8 行 に 256 パターン
- ネームテーブル中間 8 行 に 256 パターン
- ネームテーブル下部 8 行 に 256 パターン
256パターン以下で問題無ければ、mode 2 ではなく mode 0 でも OK です。
キャラクタパターンは、ASCII コードに対応する文字の画像が設定された状態になっているので、テキストベースの MSX アプリを開発するのであれば、キャラクタパターンは未設定でも大丈夫です。 (この辺がゲーム機でアプリを開発する場合よりも楽で良いですね)
MSX BIOS が設定しているデフォルトの VRAM マップは、mode 0 と mode 2 のどちらでも使える形になっているので、VRAM のメモリマップを VDP レジスタで変更する必要は特に無さそうです。
4. Hello, World!
それでは Hello, World!
を表示する ROM を作ってみます。
4-1. プログラム本体 (hello.asm)
org $4000
.Header
; MSX の ROM ヘッダ (16 bytes)
defb 'A', 'B', $10, $40, $00, $00, $00, $00
defb $00, $00, $00, $00, $00, $00, $00, $00
.Start
; スタックポインタを初期化
; 参考: https://qiita.com/suzukiplan/items/9d4b814d53ce96e4ea35
ld sp, $F380
; VRAM の アクセスアドレス を NameTable の先頭 ($1800) に書き込みモードで設定
; ※VDP アドレスポート を書き込む時は di ~ ei で割り込みを禁止
ld a, ($0007)
inc a
ld c, a
di
ld a, $00
out (c), a
ld a, $18 + $40
out (c), a
ei
; 画面を ' ' で埋める
dec c
ld a, ' '
ld b, $00
ld d, $03
ClearLoop:
out (c), a
djnz ClearLoop
dec d
jnz ClearLoop
; VRAM の アクセスアドレス を NameTable の $1989 に書き込みモードで設定
; (ココから Hello, World! を書き込めばだいたい中心に描画される)
inc c
di
ld a, $89
out (c), a
ld a, $19 + $40
out (c), a
ei
; NameTable に Hello, World! を書き込む
dec c
ld hl, Hello
ld b, 13
otir
; プログラム終了 (無限ループしておく)
.End
jmp End
.Data
Hello:
defb "Hello, World!"
4-2. アセンブル & ROM 出力
# hello.asm をアセンブルして hello.bin を出力
z80asm -b hello.asm
# dd で hello.bin を 32KB にした hello.rom を作成
dd bs=32k conv=sync if=hello.bin of=hello.rom
hello.rom
のダンプはこんな感じです。
% hexdump -C hello.rom
00000000 41 42 10 40 00 00 00 00 00 00 00 00 00 00 00 00 |AB.@............|
00000010 31 80 f3 3a 07 00 3c 4f f3 3e 00 ed 79 3e 58 ed |1..:..<O.>..y>X.|
00000020 79 fb 0d 3e 20 06 00 16 03 ed 79 10 fc 15 c2 29 |y..> .....y....)|
00000030 40 0c f3 3e 89 ed 79 3e 59 ed 79 fb 0d 21 47 40 |@..>..y>Y.y..!G@|
00000040 06 0d ed b3 c3 44 40 48 65 6c 6c 6f 2c 20 57 6f |.....D@Hello, Wo|
00000050 72 6c 64 21 00 00 00 00 00 00 00 00 00 00 00 00 |rld!............|
00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00008000
4-3. WebMSX で実行
以下の手順で実行できます。
- https://webmsx.org を開く
-
Cartridge 1
のLoad ROM Image
でhello.rom
を読み込む
こんな感じで表示されます。