
ゲーム機のハードウェア・ソフトウェアが抱える問題点:
PROJECT VGCE 8ビットゲーム機仕様 2004.10.04
■メモリマップ
+--------------------------------------------------+ 0000h
| |
| Bank #0 ROM/RAM |
| |
+--------------------------------------------------+ 4000h
| |
| Bank #1 ROM/RAM |
| |
+--------------------------------------------------+ 8000h
| |
| Bank #2 ROM/RAM |
| |
+--------------------------------------------------+ c000h
| |
| Bank #3 ROM/RAM |
| |
+--------------------------------------------------+ ffffh
- 4000h を1バンクとして扱う。
- バンクごとに 0-255 のマップ番号でセグメントを指定可能。
0-127: ROM (<=2MB) 128-255: RAM (64kB)
- バンクごとに ROM/RAM の切り替えが可能。
- メインRAM 64KB (CPUアドレス空間の全領域をRAMにすることが可能)
■I/Oマップ
- 00h 〜 0Fh --- VPU
- 10h 〜 1Fh --- Palette
- 20h 〜 2Fh --- SPU
- 30h 〜 3Fh --- Memory Mapper
- 40h 〜 4Fh --- Timer
- 50h 〜 5Fh --- I/O
- 60h 〜 BFh --- Reserved
- C0h 〜 FFh --- Free
■CPU ---- Z80 21.47727[MHz]
- 割り込みモード #0 (IM 0) を使用する。
■VPU ---- 5.369317[MHz]
- BG 256色, SP256色
- Vブランク割り込み (RST 08h)
- ラスタ割り込み (RST 10h)
- スプライトオーバー割り込み (RST 18h)
- VRAM - VRAM 間 DMA 終了割り込み (RST 20h)
画面解像度は 320×240 固定
- バックグラウンド3枚
- 1タイルは8x8
→1画面分は40×30タイル=1200タイル
- タイルにパレットの下位4ビットを定義する
- タイルインデックスにパレットインデックスの上位4ビットを定義する
→タイルインデックス12ビット、パレットインデックス4ビット
- パレットは 256 エントリ、 RGB555 で指定する
○許可フラグ/設定値
コントロールレジスタ#1(00h)に設定できる許可フラグ
下位バイト:
- Vブランク時に割り込み(RST 08h)発生許可 (D0)
- ラスタ検出時に割り込み(RST 10h)発生許可 (D1)
- スプライトオーバー時に割り込み(RST 18h)発生許可 (D2)
- VRAM - VRAM 間 DMA 終了時に割り込み(RST 20h)発生許可 (D3)
- 予約 (D4 - D7)
上位バイト:
- BG1 の表示許可 (D8)
- BG2 の表示許可 (D9)
- BG3 の表示許可 (D10)
- スプライトの表示許可 (D11)
- 予約 (D12-D15)
コントロールレジスタ#2(01h)に設定できる値
下位バイト:
- VRAM アドレスインクリメント (D0-D2)
000b: 1
001b: 32
010b: 64
011b: 128
100b: 256
101b - 111b: 256
- BGメモリ幅 (D3-D4)
00b: 32
01b: 64
10b: 128
11b: 256
- BGメモリ高さ (D5)
0b: 32
1b: 64
- 予約 (D6-D7)
上位バイト:
- 予約 (D8-D15)
○VRAM
VRAM #1 = 128kB (BG #1)
VRAM #2 = 128kB (BG #2)
VRAM #3 = 128kB (BG #3)
VRAM #4 = 128kB (SP)
--------------------------
Total = 512kB
○SPRAM
SPRAM = 2048 bytes
○パターン定義
VRAM #1 〜 VRAM #3
+----------------------------------------------+
| |
| PATTERN INDEX TABLE |
| |
+----------------------------------------------+
| |
| BACKGROUND PATTERN TABLE |
| |
+----------------------------------------------+
VRAM #4
+----------------------------------------------+
| |
| SPRITE PATTERN TABLE |
| |
+----------------------------------------------+
○ PATTERN INDEX TABLE
後述の BACKGROUND PATTERN TABLE のインデックスを定義する。
このテーブルのサイズは VPU のレジスタから
変更することができる。
32 x 32 〜 256 x 64 まで。
32 x 32 (1024タイル)の設定だと、
+----------------- ... -----------------+
| 0| 1| 2| 3| ... | 1C| 1D| 1E| 1F|
+----------------- ... -----------------+
| 20| 21| 22| 23| ... | 3C| 3D| 3E| 3F|
+----------------- ... -----------------+
| 40| 41| 42| 43| ... | 5C| 5D| 5E| 5F|
+----------------- ... -----------------+
: : :
: : :
+----------------- ... -----------------+
|3A0|3A1|3A2|3A3| ... |3BC|3BD|3BE|3BF|
+----------------- ... -----------------+
|3C0|3C1|3C2|3C3| ... |3DC|3DD|3DE|3DF|
+----------------- ... -----------------+
|3E0|3E1|3E2|3E3| ... |3FC|3FD|3FE|3FF|
+----------------- ... -----------------+
の番号で指定されたタイルで1画面を構成することになる。
○ BACKGROUND PATTERN TABLE
(1タイルは8x8ピクセルで構成される。)
BG PATTERN TABLE は PATTERN INDEX TABLE の直後からはじまる。
このタイルに表示するパターン(8ビット/ピクセル)のうち、
下位4ビットをこのテーブルに定義する。
1タイルの定義の仕方は以下の通り。
数字 --- VRAMのタイルの先頭番地からのオフセット値 (16進数)
L ------ 下位4ビットの意味 (D0 - D3)
H ------ 上位4ビットの意味 (D4 - D7)
例)
0L --- VRAM のタイルの先頭番地+ゼロバイトの下位4ビットに
背景のパターンの下位4ビットを定義する。
1FH --- VRAM のタイルの先頭番地+$1Fバイトの上位4ビットに
背景のパターンの下位4ビットを定義する。
+----+----+----+----+----+----+----+----+
| | | | | | | | |
| 0L | 0H | 1L | 1H | 2L | 2H | 3L | 3H |
+----+----+----+----+----+----+----+----+
| | | | | | | | |
| 4L | 4H | 5L | 5H | 6L | 6H | 7L | 7H |
+----+----+----+----+----+----+----+----+
| | | | | | | | |
| 8L | 8H | 9L | 9H | AL | AH | BL | BH |
+----+----+----+----+----+----+----+----+
| | | | | | | | |
| CL | CH | DL | DH | EL | EH | FL | FH |
+----+----+----+----+----+----+----+----+
| | | | | | | | |
|10L |10H |11L |11H |12L |12H |13L |13H |
+----+----+----+----+----+----+----+----+
| | | | | | | | |
|14L |14H |15L |15H |16L |16H |17L |17H |
+----+----+----+----+----+----+----+----+
| | | | | | | | |
|18L |18H |19L |19H |1AL |1AH |1BL |1BH |
+----+----+----+----+----+----+----+----+
| | | | | | | | |
|1CL |1CH |1DL |1DH |1EL |1EH |1FL |1FH |
+----+----+----+----+----+----+----+----+
1タイルのパターン定義に、32 バイト使用する。
1画面は40×30タイル=1200タイルで構成されるから、
1画面分のユニークなパターンを定義するには、1200 x 32 = 38400
バイトの VRAM が必要。
■スプライト(SP)
- 256個
- X、Y幅を独立に 8/16/32/64 に設定可能
- メインRAM上にテーブルを作成→SPRAMに転送
- 回転角度を12ビット(000h - fffh)で指定可能
スプライトのパターン(パレットインデックスの下位4ビット)は
VRAM #4 に定義する。
SPのサイズによって、VRAM上のパターンテーブルの
スタート位置を調整する必要がある。
8×8ピクセルのスプライトの場合は、16 ワードで1個分の
パターンを定義するので、16 ワード境界にパターンの先頭を配置する。
2進数で表すと %xxxx xxxx xxxx 0000 のアドレスに配置する
(x は 0 または 1 の値をとる)。
64×64のスプライトの場合は、8×8タイルで1個分を
構成するから、8 * 8 * 16 = 1024 ワードでパターンを定義する。
この場合は 1024 ワード境界(2進数で表すと %xxxx xx00 0000 0000 のアドレス)
にパターンの先頭を配置する。このとき、上位ビットが6ビット
なので、64k ワードの VRAM 内にユニークに定義できる64×64
スプライトの最大数は 2^6 = 64 個になる。
-----------+-------------------------------+-------------------------
SP Size | Pattern Table Start Address | Max Patterns Definable
-----------+-------------------------------+-------------------------
8x8 | %xxxx xxxx xxxx 0000 | 4096
8x16 | %xxxx xxxx xxx0 0000 | 2048
16x8 | %xxxx xxxx xxx0 0000 | 2048
16x16 | %xxxx xxxx xx00 0000 | 1024
32x8 | %xxxx xxxx xx00 0000 | 1024
32x16 | %xxxx xxxx x000 0000 | 512
32x32 | %xxxx xxxx 0000 0000 | 256
64x8 | %xxxx xxxx x000 0000 | 512
64x16 | %xxxx xxxx 0000 0000 | 256
64x32 | %xxxx xxx0 0000 0000 | 128
64x64 | %xxxx xx00 0000 0000 | 64
-----------+-------------------------------+-------------------------
○SPの設定項目
- パレットの上位4ビット
- パターンインデックス12ビット
- 回転角度12ビット(画面に垂直な軸まわりの回転)
- 背景/SP優先1ビット (P)
○X方向設定項目
- X座標10ビット
- X幅2ビット (XW)
○Y方向設定項目
- Y座標10ビット
- Y幅2ビット (YW)
○SPRAM
スプライトの定義テーブル(1個分)
15 12 8 0
+----------------------+------------------------------------+
| PALETTE INDEX D4-D7 | PATTERN INDEX |
+--------------------+-+------------------------------------+
| |P| ROTATION ANGLE |
+--------------------+-+--+---------------------------------+
| |YW| Y COORDINATE |
+----------------------+--+---------------------------------+
| |XW| X COORDINATE |
+----------------------+--+---------------------------------+
- 4ワード/スプライト→1024ワード(2048byte)/256スプライト
■SPU ---- 3.579545[MHz]
- 波形メモリ型PSG4チャネル
- ノイズ2チャネル
- FM音源4チャネル(OPM相当)
- 8-bit ADPCM2チャネル
- ADPCM RAM 64kB (32kB x 2)
○波形メモリ型PSG
- 符号付き 8-bit × 64 波形メモリ
○ADPCM
- リピート再生機能あり
- ストリーミング再生機能なし
■Memory Mapper
- システムリセット時は Bank #0, Bank #1 が ROM(0000h-7fffh)、
Bank #2, Bank #3 が RAM (0000h-7fffh)になる。
- I/O の 30h - 33h に Bank #0 - Bank #3 のマップ番号を書き込む。
- レジスタは書き込みのみ対応。読み出し値は常に 0ffh。
- マップ番号 0 - 127 は ROM になり、128 - 255 は RAM になる。
ただし ROM のサイズが 4000h x 128 よりも小さい場合は、
ROM の容量が繰り返し現れる。
例えば最小の 4000h (16kB) のときはどのマップ番号を指定しても
ROM の 0000h-3fffh のイメージが繰り返し現れる。
ROM のサイズが 8000h のときは、
マップ番号 0 に ROM 0000h-3fffh,
マップ番号 1 に ROM 4000h-7fffh
マップ番号 2 に ROM 0000h-3fffh,
マップ番号 3 に ROM 4000h-7fffh
:
:
というように繰り返される。
■I/O
- 未定
画面表示出ました。
BG3枚による3重スクロール+8×8スプライトのテストです。
まだ8×8スプライトしか実装できてません。
画面にちらばっているちっちゃいのがスプライトです。
動きがバグいです。(←アセンブラへたくそ)

現在、CPUコアに Marat Fayzullin 氏の "Z80 Emulation Package" を 使わせて頂いています。RunZ80 関数を変更していますが、コア部分の変更はありません。
[2004.10.17]SDCC で上とほぼ同じテストプログラムをCで書き直しました。 ついでに画面を320×240モードにしました(上の ASM 版は256×240)。 ついでにバグもちょこっと修正。 out() 関数が見当たらなかったので、インラインアセンブラを使ってI/Oアクセス関数をテキトウにでっち上げたんですが、 call/ret/レジスタ退避のオーバーヘッドでかなり遅くなってしまいました。 そのためVRAM連続書き込みやSPRAM書き込み処理がやたら遅く、いまのところ実用的でないです。 ・・・というわけで、スプライト処理部分がまだ書き直せていません。(^_^;

まだ SDCC を Z80 のコンパイラとして使うための情報が少なく、 割り込みの公式な(?)実装方法がわからなかったので、 スタートアップコード (crt0.s) から直接Cの関数を名指しでコールするようにしました。
あと、グローバル変数の初期化コード(gsinit)がなぜかRAM領域に配置されてしまい、 実行時に制御がスタートアップコードから未初期化のRAM領域へぶっ飛んでしまいます (gsinit が CODE セグメントではなく DATA セグメントに配置されている感じ?)。 いろいろ試してみたんですが、解決方法がみつかりませんでした。 このあたり、どなたかご存知の方がいましたら教えてください。
とりあえず、グローバル変数の初期化コードをスキップするようにして、上の問題を 回避しました。ですので、現状ではグローバル変数に初期値を指定しても、実行時にその値に初期化されません。
とはいえ、開発にC言語が使えるのはかなりでかいです。 かなり普通にCでプログラムが書けてしまいます。Z80 はいったいどこへ!?っていう感じです。 あとはタイルのパターンとかインデックスとかパレットをブラックボックス化して 使えるツールを用意すれば、なんとかゲームの製作ができそうです。
とりあえず今回作成したプログラム一式をアップロードしておきます。 なお、 SDCC はコンパイル・リンク後に インテルHEX形式で出力するので、コンパイルしたコードをVGCEで実行するには、 インテルHEX→バイナリ変換を行なう必要があります。変換ツールは検索すればいろいろと出てきますが、 私はBHCというプログラムを利用させていただきました(ソースがついていたので)。
[2004.10.17]追記
SDCC で I/O 入出力を行なうには、sfr文を用いて
I/O アドレスを定義すればよいと sdccman に書いてありました。まず sfr at 0x00 VPU_PORT; と定義し、
以後たとえば VPU_PORT = 0xff; と書けばコンパイラは
LD A, #0xff
OUT (VPU_PORT), A
というコードを生成してくれます。これで、I/O 入出力の問題は解決しました。
画面表示テストプログラム一式(vgcetest.asm, vgcetest.rom, vgce.exe)
以下のページを作成された方々に感謝します。