丘と水路と橋と火を

言葉と技術

おれの短歌・オン・ゲームボーイアドバンス

TL;DR

  • 西田亙『Linuxから目覚めるぼくらのゲームボーイ!』(ソフトバンククリエイティブ、2003年)がめっちゃいい。
  • 手っ取り早く書籍のコード用に環境構築したいなら、パッケージマネージャーなどでgcc-arm-none-eabiをインストールしよう。
  • Windows 10(64bit)環境で書籍同梱のブートケーブルUSBを利用して実機にプログラムを転送するなら、移植版の転送ソフトを利用すること。
  • GBA用の画像変換はOpenCVPythonで行うのが(個人的には)やりやすい気がする。
  • visualboyadvance-mだと一部書籍内のプログラムが動作しないみたい。

はじめに

NINTENDO64ゲームボーイといった、幼少期にかぶりついて遊んでいたハードやソフトがどんな仕組みで動いていたのかを知りたくて、先人たちがつくってくれた動画や記事をいろいろ漁っていた。

そんななか、Rustから目覚めるぼくらのゲームボーイ!で西田亙『Linuxから目覚めるぼくらのゲームボーイ!』(ソフトバンククリエイティブ、2003年)の存在を知る。

GBAというハードを題材にしつつ、GNU開発ツールを用いたクロスコンパイラ開発について理解を深められるらしい。 各所での評価が高いものの、既に絶版扱いで復刊ドットコムでも投票が開始されている。 良品だとAmazonで2万円ほどするということに悩みつつも、「でもまさにあなたのやりたいことが書かれてる本なんだから買ったら?」と妻に後押ししてもらい、えいやっと購入。

届いてさっそく意気揚々と読み始めたのだが、書籍に記載されている内容も付録のブートケーブルUSBも2003年のものであるということで、2022年現在に実際に手を動かしてやってみようとするとハマリどころが多かった。

まずは第1章から第2章途中までを読んだので、本記事ではそこまでの範囲で実際に手を動かしてみて得た気づきや、ハマった箇所と解決策について記載する。

なお、今回は以下の環境で作業を実施した。

  • Windows 10(64bit)
    • GBAに送信するプログラムのコーディングやビルドは、WSL2によるUbuntu 20.04環境下で主に実施。
    • その他、後述の画像変換スクリプトの実行や、付録のブートケーブルUSBを介したGBAへのプログラムの転送はWindows側で実施。

binutilsgccを用意する

GBAはCPUとしてゲームボーイ用のカスタムZ80のほか、GBA用の32bitARM7TDMI(現在でいうところのARMv4系らしい)が搭載されている。 そのためGBA向けの環境をGNUの開発ツールでそろえようとするなら、ARM7(ARMv4)向けに以下の2つを用意することになる。

学習のためもあり、書籍内ではソースコードをダウンロードして自前でbinutils-2.14gcc-core-3.3.2をビルドしている。

2022年時点ではより新しいバージョンのbinutils-2.38gcc-12.1.0が利用可能だが、その際には書籍内のtargetがarm-gba-elfと表記されていることには注意が必要。 arm-*-elfを指定しているものがWeb上でも多く見られるのだが、2022年現在Host/Target specific installation notes for GCCをみるとarm-*-elfは記載がなくarm-*-eabiのみの表記となっている。 "Note that this list of install notes is not a list of supported hosts or targets..."とあるので、arm-*-elfももしかしたら利用できるのでは? とも思ったが、gcc-12.1.0ではさらにgcc/config.gcc内に"Unsupported targets list"という記載があり、arm*-*-*eabi*は"Avoid special cases that are not obsolete"としてぎりぎり許容されているものの、arm*-*-elfはがっつりUnsupportedという扱いになっている。

上記から、2022年時点で新しめのバージョンのbinutils/gccを利用するなら、arm-none-eabiをtargetにすること。もしくは素直に書籍と同じバージョンのものを利用する方が良い。

ただし、(特にGCCについて)自前でのmake install時に失敗することも多数。そのためパッケージマネージャなどで管理されているARM用のbinutilsgccをそれぞれインストールするのが手っ取り早いようだった。私はしばらく自前ビルドに挑んでいたものの、上記のtarget問題にもその時は気が付いておらず、どうしてもビルド時のエラーが解消しなかったため、すごすごと以下を利用させてもらうことに……。

# for linux(debian)
# binutils-arm-none-eabiなども一緒にインストールされる
sudo apt install gcc-arm-none-eabi

# for macOS
brew install --cask gcc-arm-embedded

これは以下のようなツールを/usr/binにインストールしてくれる。

ls /usr/bin | grep arm

arm-none-eabi-addr2line
arm-none-eabi-ar
arm-none-eabi-as
arm-none-eabi-c++
arm-none-eabi-c++filt
arm-none-eabi-cpp
arm-none-eabi-elfedit
arm-none-eabi-g++
arm-none-eabi-gcc
arm-none-eabi-gcc-9.2.1
arm-none-eabi-gcc-ar
arm-none-eabi-gcc-nm
arm-none-eabi-gcc-ranlib
arm-none-eabi-gcov
arm-none-eabi-gcov-dump
arm-none-eabi-gcov-tool
arm-none-eabi-gprof
arm-none-eabi-ld
arm-none-eabi-ld.bfd
arm-none-eabi-nm
arm-none-eabi-objcopy
arm-none-eabi-objdump
arm-none-eabi-ranlib
arm-none-eabi-readelf
arm-none-eabi-size
arm-none-eabi-strings
arm-none-eabi-strip

こちらで書籍内の操作を実施してみると、ちゃんと書籍に掲載されているのと同じバイナリが得られる。

自前ビルドがうまくいかなかったのが悔しい……未検証だが、いま再度試してみるなら以下のようなconfigureの設定で行けるような気もする。書籍のほか、gccをソースからビルド/インストールするを一部参考にしている。

# build binutils-2.38
mkdir build_arm
cd build_arm
../configure --prefix=/usr/local/gnu \
  --program-suffix=-arm \
  --target=arm-none-eabi

# build gcc-12.1.0
./contrib/download_prerequisites # ビルドに必要なモジュールのダウンロード
mkdir build_arm
cd build_arm 
../configure --target=arm-none-eabi \
  --enable-languages=c \ # C向けのコンパイラのみ
  --prefix=/usr/local/gnu \
  --program-suffix=-arm
  --disable-shared \
  --disable-threads \
  --disable-nls \
  --disable-bootstrap \ # ビルド時間短縮のためビルド回数を1回にする
  --disable-multilib # 64bit環境下でのビルド時のエラー回避

付録のケーブルをWindows 10(64bit)環境下で利用できるようにする

付録として同梱されているオプティマイズのブートケーブルUSBを利用することで、GBA実機で動作を確認することができる。 WSL2上からUSBデバイスを認識させる方法もあるようだが、それはそれで手間がかかりそうに見えたので、まずは転送作業はWindows側ですることに。

これで難なく接続できる……はずなのだが、Windows 10(64bit)の環境であるせいか、どうしてもデバイスドライバが正常にインストールされない。 どうやらWindows 7(32bit)くらいまでは古いWindows向けのドライバも利用できることが多かったようだが、Windows 10以降は署名制限などの厳格化によって一筋縄ではいかない様子……(古いUSBドライバーをWindows 10 で使いたい

もうGBA実機への転送は諦めてエミュレータでの検証までにしようかと思った矢先、主専攻実験を手元でやるためにGBA開発環境を構築するという記事を発見。 この記事で紹介されているGitHubのリポジトリに、WinUSB汎用ドライバーによってブートケーブルUSBを動作させる場合に利用可能な移植版の転送ソフト(optusbx)と転送のための手順が記載されている。 READMEに記載の手順の通りに実施することで、無事にGBA実機に作成したプログラムを転送できた。

WinUSBとlibusbでUSBデバイスを動作させるというのは、また別の機会に学習してみたいと思う。こういう移植がさっとできるというのは憧れる。

画像を用意する

第1章ではGBAのディスプレイ上にドット単位で色を指定して画像を表示させるのだが、もちろん通常の画像ファイルをそのまま送れるわけではなくて、画像をGBAで描画可能なバイナリに変換して、それをプログラムとリンクしたうえで転送しなくてはならない。

GBAでの色指定は、最上位ビットを未使用で0と固定し、そのあと上位ビットから順に青(B)、緑(G)、赤(R)をそれぞれ5ビットずつ、計16ビット(2バイト)の0BBBBBGGGGGRRRRRという形式で表すものとなっている。またGBAはリトルエディアンでのデータ格納を行うため、この2バイトの色指定の連なりによる画像のバイナリも当然リトルエディアンにする必要がある。

書籍ではRGB汎用フォーマット(24ビットRGB)をGBAで表示可能な形式に変換するためのC言語によるサンプルコードも掲載されているのだが、もう少し楽に変換できないものかと思い、今回はOpenCVを利用してBMP形式の画像をGBAで表示可能な形式に変換するPythonスクリプトを書いて対応した。

OpenCVは読み込んだ画像をRGBではなくBGRの形式で扱うため、今回の用途にはちょうどよかった。横240px・縦160pxのサイズのBMP形式の画像データを用意したうえで、以下のようなスクリプトで目的のバイナリを保存するようにした。なお画像のリサイズにはImageMagickが便利だった。

gist4412d65056eb82115eb8db2efb59ea6d

ちなみに今回は以下の画像を用意した。

うつくしい金具はうつくしい椅子にわたして、といふ遠い風鳴り

おわりに

上記を経て作成されたのが、以下の動画のプログラムだった。

ちなみにだが、エミュレータvisualboyadvance-mではなぜかこの画像表示のプログラムがうまく処理できなかった。もう少し余裕があるときに、こちらも調査したりGitHubで報告したりしてみたい。

普段なかなか利用してこなかったツールや知識を理解しつつの作業なので骨の折れる部分も多いが、その分非常に学びも多いなあと思う。 まだ書籍はまだまだ続きがあるので、時間を見つつこの先も手を動かしていきたい。