MegaDrive Mega-CD エミュレータ Gens

だめもとで試してみた Gens Linux port が動作した

というわけで ビルドしてインストールするまでの様子をレポートします
以下の評価環境で実験しました NASMアセンブラがビルドに必要のようです
評価環境

OS Linux kernel-2.6.24
CPU Turion64 MT-37 (2GHz)
グラフィック GeForce6600GT NVIDIA-Linux-x86-96.43.05
OpenGL Mesa-6.5.2
サウンド ALSA SoundBlaster Live Gamer
ゲームパッド USB ELECOM JC-PS201U (プレステコントローラアダプタ)
Gens Gens/GS Milestone 6 (Source Code)
コンパイラ gcc バージョン 4.1.2
アセンブラ NASM version 0.98.39

Gensのソースは SONIC RETRO GENS/GS から取得します
以下のように ソースファイルを一時ディレクトリに展開してビルドを開始します
$ tar -xzf Gens-2.15.5-gs-m6.tar.gz
$ cd gens-2.15.5-gs-m6
$ ./configure --prefix=/usr/local
checking build system type... i686-pc-linux-gnu
checking host system type... i686-pc-linux-gnu
checking target system type... i686-pc-linux-gnu
checking for a BSD-compatible install... /bin/install -c
〜
config.status: creating src/libpng/Makefile
config.status: creating src/mp3_dec/Makefile
config.status: creating src/starscream/Makefile
config.status: creating src/starscream/main68k/Makefile
config.status: creating src/starscream/sub68k/Makefile
config.status: creating config.h
config.status: executing depfiles commands
$ make
検証の環境においては 実際は以降に述べるようなコンパイルエラーが出ました
〜
if g++ -DHAVE_CONFIG_H -I. -I. -I../.. -I. -I./../ -I./ui/   -I./ui/gtk/    -DGENS_DATADIR=\"/home/admin/share/gens\" -I/usr/local/include/SDL -D_GNU_SOURCE=1 -D_REENTRANT -I/usr/local/include/gtk-2.0 -I/usr/local/lib/gtk-2.0/include -I/usr/local/include/atk-1.0 -I/usr/local/include/cairo -I/usr/local/include/pango-1.0 -I/usr/local/include/glib-2.0 -I/usr/local/lib/glib-2.0/include -I/usr/local/include -I/usr/local/include/freetype2 -I/usr/local/include/libpng12 -I/usr/X11R7/include   -DGTK_DISABLE_DEPRECATED -O2 -Wall -Wextra -g  -g -O2 -O2 -Wall -Wextra -g  -g -O2 -MT ui/gtk/gens/gens-gens_window_callbacks.o -MD -MP -MF "ui/gtk/gens/.deps/gens-gens_window_callbacks.Tpo" -c -o ui/gtk/gens/gens-gens_window_callbacks.o `test -f 'ui/gtk/gens/gens_window_callbacks.cpp' || echo './'`ui/gtk/gens/gens_window_callbacks.cpp; \
then mv -f "ui/gtk/gens/.deps/gens-gens_window_callbacks.Tpo" "ui/gtk/gens/.deps/gens-gens_window_callbacks.Po"; else rm -f "ui/gtk/gens/.deps/gens-gens_window_callbacks.Tpo"; exit 1; fi
ui/gtk/gens/gens_window_callbacks.cpp: In function 'void gens_window_drag_data_received(GtkWidget*, GdkDragContext*, gint, gint, GtkSelectionData*, guint, guint, void*)':
ui/gtk/gens/gens_window_callbacks.cpp:127: error: 'g_uri_unescape_string' was not declared in this scope
ui/gtk/gens/gens_window_callbacks.cpp: At global scope:
ui/gtk/gens/gens_window_callbacks.cpp:93: 警告: unused parameter 'widget'
ui/gtk/gens/gens_window_callbacks.cpp:93: 警告: unused parameter 'x'
ui/gtk/gens/gens_window_callbacks.cpp:93: 警告: unused parameter 'y'
ui/gtk/gens/gens_window_callbacks.cpp:93: 警告: unused parameter 'target_type'
ui/gtk/gens/gens_window_callbacks.cpp:93: 警告: unused parameter 'data'
ui/gtk/gens/gens_window_callbacks.cpp:149: 警告: unused parameter 'x'
ui/gtk/gens/gens_window_callbacks.cpp:149: 警告: unused parameter 'y'
ui/gtk/gens/gens_window_callbacks.cpp:149: 警告: unused parameter 'user_data'
ui/gtk/gens/gens_window_callbacks.cpp:171: 警告: unused parameter 'widget'
ui/gtk/gens/gens_window_callbacks.cpp:171: 警告: unused parameter 'event'
ui/gtk/gens/gens_window_callbacks.cpp:171: 警告: unused parameter 'user_data'
ui/gtk/gens/gens_window_callbacks.cpp:185: 警告: unused parameter 'widget'
ui/gtk/gens/gens_window_callbacks.cpp:185: 警告: unused parameter 'event'
ui/gtk/gens/gens_window_callbacks.cpp:185: 警告: unused parameter 'user_data'
make[3]: *** [ui/gtk/gens/gens-gens_window_callbacks.o] エラー 1
make[3]: Leaving directory `/home/admin/gens-2.15.5-gs-m6/src/gens'
make[2]: *** [all-recursive] エラー 1
make[2]: Leaving directory `/home/admin/gens-2.15.5-gs-m6/src'
make[1]: *** [all-recursive] エラー 1
make[1]: Leaving directory `/home/admin/gens-2.15.5-gs-m6'
make: *** [all] エラー 2
$
‘g_uri_unescape_string’ was not declared in this scope と怒られました
g_uri_unescape_string() 関数とは Glib で提供されていた関数で
文字列中のエスケープ文字を処理する機能を提供するようです
最近の Glib のバージョンのためか 現在はこの機能が削除されたのでしょう

src/gens/ui/gtk/gens/gens_window_callbacks.cpp で発生したエラーなので
このソースコードの 空いている箇所に 以下のコードを追加します
 static int
 unescape_character (const char *scanner)
 {
   int first_digit;
   int second_digit;

   first_digit = g_ascii_xdigit_value (*scanner++);
   if (first_digit < 0)
     return -1;

   second_digit = g_ascii_xdigit_value (*scanner++);
   if (second_digit < 0)
     return -1;

   return (first_digit << 4) | second_digit;
 }

 static char *
 g_uri_unescape_segment (const char *escaped_string,
                         const char *escaped_string_end,
                         const char *illegal_characters)
 {
   const char *in;
   char *out, *result;
   gint character;

   if (escaped_string == NULL)
     return NULL;

   if (escaped_string_end == NULL)
     escaped_string_end = escaped_string + strlen (escaped_string);

   result = (char*) g_malloc (escaped_string_end - escaped_string + 1);

   out = result;
   for (in = escaped_string; in < escaped_string_end; in++)
     {
       character = *in;

       if (*in == '%')
         {
           in++;

           if (escaped_string_end - in < 2)
             {
               /* Invalid escaped char (to short) */
               g_free (result);
               return NULL;
             }

           character = unescape_character (in);

           /* Check for an illegal character. We consider '\0' illegal here. */
           if (character <= 0 ||
               (illegal_characters != NULL &&
                strchr (illegal_characters, (char)character) != NULL))
             {
               g_free (result);
               return NULL;
             }

           in++; /* The other char will be eaten in the loop header */
         }
       *out++ = (char)character;
     }
 
   *out = '\0';

   return result;
 }

 char *
 g_uri_unescape_string (const char *escaped_string,
                        const char *illegal_characters)
 {
   return g_uri_unescape_segment (escaped_string, NULL, illegal_characters);
 } 
これら 3つの関数を直接記述することで GLibの g_uri_unescape_string() を代用します

ふたたび make したところ 今度は
mkdir -p gens_core/gfx/ gens_core/io/ gens_core/mem/ gens_core/misc/ ;
mkdir -p gens_core/sound/ gens_core/cpu/sh2/ gens_core/vdp/ gens_core/cpu/z80/ ;
/usr/local/bin/nasm -O3 -f elf -D __GCC2 -D __OBJ_ELF -g -F dwarf -w-orphan-labels -I./gens_core/ -I./gens_core/io/ -I./gens_core/misc/ -I./gens_core/vdp/ -I./gens_core/sound/ -I./gens_core/gfx/ -I./gens_core/cpu/sh2/ -I./gens_core/cpu/z80/ -I./gens_core/mem/ gens_core/gfx/fastblur_16_x86.asm -o gens_core/gfx/fastblur_16_x86.o
nasm: fatal: unrecognized debug format `dwarf' for output format `elf'
type `nasm -h' for help
make[3]: *** [gens_core/gfx/fastblur_16_x86.o] エラー 1
make[3]: Leaving directory `/home/admin/gens-2.15.5-gs-m6/src/gens'
make[2]: *** [all-recursive] エラー 1
make[2]: Leaving directory `/home/admin/gens-2.15.5-gs-m6/src'
make[1]: *** [all-recursive] エラー 1
make[1]: Leaving directory `/home/admin/gens-2.15.5-gs-m6'
make: *** [all] エラー 2
elfフォーマットでは dwarfデバッグフォーマットは使えない と言われました
確かに ELFは Linuxなどでよく使われる実行ファイルのフォーマットで
DWARFはデバッグ情報のフォーマットです

仕方がないので DWARF機能を無効化してみます
うまくビルドできたとしても デバッガ上での動作で不具合を起こす可能性が高くなります

src/gens/Makefile 中に 以下のように NASMのオプションんを設定している箇所があるので
 NASM = /usr/local/bin/nasm -O3 -f elf -D __GCC2 -D __OBJ_ELF -g -F dwarf -w-orphan-labels $(NASM_INCLUDES)
次のように書き換えます
 NASM = /usr/local/bin/nasm -O3 -f elf -D __GCC2 -D __OBJ_ELF -g -w-orphan-labels $(NASM_INCLUDES)
-F dwarf のオプションを削除して 回避します
で make コマンドでビルド続行

またまたエラーが出ました
mkdir -p gens_core/gfx/ gens_core/io/ gens_core/mem/ gens_core/misc/ ;
mkdir -p gens_core/sound/ gens_core/cpu/sh2/ gens_core/vdp/ gens_core/cpu/z80/ ;
/usr/local/bin/nasm -O3 -f elf -D __GCC2 -D __OBJ_ELF -g -w-orphan-labels -I./gens_core/ -I./gens_core/io/ -I./gens_core/misc/ -I./gens_core/vdp/ -I./gens_core/sound/ -I./gens_core/gfx/ -I./gens_core/cpu/sh2/ -I./gens_core/cpu/z80/ -I./gens_core/mem/ plugins/render/hq2x/mdp_render_hq2x_16_x86.asm -o plugins/render/hq2x/mdp_render_hq2x_16_x86.o
plugins/render/hq2x/mdp_render_hq2x_16_x86.asm:2066: error: phase error detected at end of assembly.
make[3]: *** [plugins/render/hq2x/mdp_render_hq2x_16_x86.o] エラー 1
make[3]: Leaving directory `/home/admin/gens-2.15.5-gs-m6/src/gens'
make[2]: *** [all-recursive] エラー 1
make[2]: Leaving directory `/home/admin/gens-2.15.5-gs-m6/src'
make[1]: *** [all-recursive] エラー 1
make[1]: Leaving directory `/home/admin/gens-2.15.5-gs-m6'
make: *** [all] エラー 2
「phase error」!… あまり見かけない類のエラーです… しかも NASMでのエラー
インターネットで検索すると エラーの理由と対処方法が書かれてました

「NASMのバグにより -O2 -O3 などのオプティマイズに失敗し jmp系のジャンプ命令の距離計算を間違える」
-O1 や -O999 にすると改善される

-O999 というのも意味不明なので -O1 に書き換えてみます
src/gens/Makefile のやはり先程と同じ NASMの定義行を 今度は次のように書き換えます
  NASM = /usr/local/bin/nasm -O1 -f elf -D __GCC2 -D __OBJ_ELF -g -w-orphan-labels $(NASM_INCLUDES)
-O3 だったところを -O1 に書き換えて再度 make します
〜
Making all in pixmaps
make[2]: Entering directory `/home/admin/gens-2.15.5-gs-m6/pixmaps'
make[2]: `all' に対して行うべき事はありません。
make[2]: Leaving directory `/home/admin/gens-2.15.5-gs-m6/pixmaps'
Making all in xdg
make[2]: Entering directory `/home/admin/gens-2.15.5-gs-m6/xdg'
make[2]: `all' に対して行うべき事はありません。
make[2]: Leaving directory `/home/admin/gens-2.15.5-gs-m6/xdg'
make[2]: Entering directory `/home/admin/gens-2.15.5-gs-m6'
make[2]: Leaving directory `/home/admin/gens-2.15.5-gs-m6'
make[1]: Leaving directory `/home/admin/gens-2.15.5-gs-m6'
$ su
# make install
/bin/sh ./git_version.sh -k -s . -o git_version.h
git_version.sh: Not a git repo, keeping existing git_version.h
make  install-recursive
make[1]: Entering directory `/home/gens-2.15.5-gs-m6'
Making install in src
〜
make[2]: Entering directory `/home/admin/gens-2.15.5-gs-m6'
make[3]: Entering directory `/home/admin/gens-2.15.5-gs-m6'
make[3]: `install-exec-am' に対して行うべき事はありません。
make[3]: `install-data-am' に対して行うべき事はありません。
make[3]: Leaving directory `/home/admin/gens-2.15.5-gs-m6'
make[2]: Leaving directory `/home/admin/gens-2.15.5-gs-m6'
make[1]: Leaving directory `/home/admin/gens-2.15.5-gs-m6'
うまくコンパイルが通りました
インストールされるファイルは /usr/local/bin/gens 本体と
/usr/local/share/applications/gens.desktop
/usr/local/share/gens/ 配下にアイコンファイルがいくつかです

Gens を使ってみる

使用感をレポートします

Gens画面 ROM

GUIはこんな感じです Windows版に準拠しています
画面は ハードドライビン です
メガドライブ版はポリゴン黎明期ゲームに過ぎませんが
アーケード版は 4段変速ギア クラッチ エンジンキー まで搭載した筐体で
まさしく ドライブシミュレータのようなリアルさが心を打ちます

Gens画面 ファイルメニュー

BootCD の項目があるとおり MEGA-CDが使えるようです
またステートセーブもできそうです
画面は カダッシュです アーケード版ではパスワードシステムが斬新でした

Gens画面 グラフィック

Linux版ということで OpenGLの項目があります
検証環境は十分高速な環境なので OpenGLを使う体感速度の差異は感じられません (むしろ遅くなった?)
画面は アドバンスド大戦略です 名作です
昔プレイしたときは シナリオが進むにつれ資金難となっていき ついには頓挫しました コツがあるのかな

Gens画面 CPU

CPU選択も Windows版に準拠しています
メニュー下にある SegaCD Perfect Sync をチェックしないと MEGA-CDがうまく動きませんでした
画面は スーパーハイドライドです
パソコンのハイドライド3をベースにして グラフィック や サウンド をパワーアップしています
アイテムに重さの概念があるため 敵を倒して小金を稼いでいるとそのうち動きが鈍くなります

Gens画面 サウンド

個別のチャンネル毎に ON/OFF できるようです
検証環境では MEGA-CDの CD演奏が鳴りませんでした
画面は クラック版?ソニック ! 誰かが作ったのでしょうか よくできてます

Gens画面 ジョイスティック

オプション項目です ジョイスティックも使えました キー配置も定義できます
画面は ダイナマイトヘッディー メガドライブ後期のアクションゲームです 名作です
伸びる首を使って 敵を倒したり 移動したりします

Gens画面 ディレクトリ

オプション項目です スクリーンショットや セーブデータの保存先を指定できます
デフォルトは ホームディレクトリにできる .gens ディレクトリが指定されます
画面は ワイアラエの奇蹟 当時 T&Eがリアルな 3Dゴルフシミュレーションを製作していました
ワイアラエの奇蹟 は 遥かなるオーガスタ ぺブルビーチの波涛 に続く 3作目です

Gens画面 BIOS

オプション項目です Windows版もそうですが MEGA-CD と 32X をプレイするには
BIOSのファイルが別途必要です
画面は 32X版 メタルヘッド 32Xらしくポリゴンが強化された 3Dロボットシューティングです
検証環境では 32Xのソフトは速度の関係からか サウンドがプツプツ切れます

Gens画面 MEGA-CD

MEGA-CDの動作検証もしました
前述したとおり SegaCD Perfect Sync を指定しないと ゲームが起動しませんでした
また CD音源が音が聞こえませんでした
画面は ソル・フィース 当時の グラディウスIII を対抗して作ったらしいシューティングゲームです
プログラム技術の高さと 自機の上下に付く可変アームが特徴的です