見出し画像

RaspberryPi Zero のベアメタル環境で動くMSXエミュレータを作ってみた

私が開発している micro MSX2+ という組み込み用MSXエミュレータで、RaspberryPi Zeroシリーズのベアメタル環境での動作に対応してみました。

対応したのはRaspberryPi Zeroの全シリーズ(無印、W、WH、2W)です。

Zero、Zero W、Zero 2W
(WHはWにピンヘッダを付けただけなので省略)

以下、RaspberryPi Zeroの無印、W、WHを「Zero1」、RaspberryPi Zero 2Wを「Zero2」と表記します。

MSXエミュレータとはいってもエンドユーザ向けのもの(OpenMSXやWebMSXなど)ではなく、自作のMSXゲームを色々なプラットフォーム向けに販売したい同人ゲーム作者がターゲットのOSSです。

そのため、ダウンロードしてすぐ動かせるものではない(ツールチェインをダウンロードしてビルドする必要がある)ため、利用難度が少し高めです。


対応機種

MSX1コア対応版 & MSX2+コア対応版を、Zero1(32bit ARMv6)向け & Zero2(64bit ARMv8)向けにそれぞれリリースしています。

必要なハードウェア

RaspberryPi Zero を HDMI ケーブルでテレビに接続して、テレビで映像と音声の出力を行い、ゲームの操作はUSB接続のゲームパッドで行います。

  • RaspberryPi Zero(2W推奨)

  • HDMI mini→HDMIケーブル(テレビ等に接続用)

  • HDMI入力に対応したディスプレイ(テレビ等)

  • micro USB type-B → 電源ケーブル(900mA程度でOK)

  • micro USB type-B → USB-A メス(USBハブなど)

  • USBゲームパッド

  • micro SDカード(必要空き容量4MB以上)

はんだ付け等は一切不要なので、電子工作未経験のプログラマでも問題なく組み立てられると思います。(必要なのはUSBとHDMIの配線のみです)

必要容量(4MB)

micro SDカードに必要な空き容量は4MB以上です。(4GBではありません)

MSX2+カーネル本体(kernel8.img)が約600KB、VideoCoreIVのファームウェア(bootcode.bin, start.elf)が約3MBです。

kernel8.img, bootcode.bin, start.elf の3つのファイルをFATフォーマットされたSDカードのルートディレクトリに置き、RaspberryPi Zero 2WのSDカードスロットに挿入して電源投入するだけで起動するので、SDカードライター(balenaEtcher等)による書き込みなどの面倒な作業も不要です。

また、起動後にSDカードの書き込み処理は一切しないため、動作中に電源断してもSDカードが壊れることはありません。

Linuxの場合、セーフシャットダウンスクリプト等で停止処理をしないと、ファイル不整合になりOSブートできなくなることがありますが、私のMSXエミュレータならその心配が無く気軽に電源OFFできます。

電源供給(900mA)

電源はパソコンのUSBからのバス電源供給で問題無く動作するので、コンセントの確保に難儀することもないと思われます。

必要な電流の量は、RaspberryPi Zero 2Wが最大400mA程度、USBゲームパッドが(メーカー公称値によると)500mA程度なので、合計900mA(0.9A)以上あれば確実に問題ありません。

ただし、USBハブ経由だと100mA程度しか供給されないので、パソコンのUSBポート直結を推奨します。(※直結の場合、USB3.0なら900mA程度、USB2.0なら500mA程度が最大で供給されます)

ですが、私のパソコン(Mac)には余っているUSBポートが無いので、ハブ経由でRaspberryPi Zero 2Wへ電源供給してますが、それでも特に問題なく動作しています。

一般的なRaspberryPi環境(Linux)とは異なり、動作中に電源断して落ちてもSDカードが壊れたりしないので、電源供給についてあまり神経質にならなくても良いと思われます。

ですが、それで機器が壊れても私は責任は持てないので、公式な推奨としてはPCのUSBポート直結 or コンセントからの電源供給ということにしています。

RaspberryPi Zero 2W(推奨)

日本では2022.06.21発売されたRaspberryPi Zero 2Wは、世界的な半導体不足の影響で供給が安定してなかった時期が続いていましたが、2023.12.14現在は安定供給されています。

なお、RaspberryPi Zero 2Wは無線通信を行うため、日本国内で利用する場合は技適マークが付いているものを利用する必要があるので、Aliexpress等では購入しない方が良いです。

技適ありのRaspberryPi Zero 2Wは、スイッチサイエンスでの購入をオススメします。(3,000円前後ですぐに届くのでAliexpressよりもスイッチサイエンスで買った方がコスパの面でも優れています)

現状、Amazonだと4,000円ぐらいしますが、供給が安定して為替レートも円高方向に触れつつあるため、もう少し待てば本来の販売価格(2,500円)まで下がるかもしれません。

一応、初代RaspberryPi Zeroにも対応していますが、MSX2+以降を安定的に動作させるにはRaspberryPi Zero 2Wが必要です。

ベアメタル対応

通常、RaspberryPiシリーズ(Picoを除く)はRaspberryPi OS(Raspbian)やDietPiなどのLinuxでの動作を前提としていますが、今回はLinuxを用いずベアメタル環境(OS無し)での動作に対応しています。

ベアメタル環境でのプログラミングは、通常ならOSが提供してくれるハードウェアアクセス、メモリ管理、タスク管理などを全部自前実装する必要があり、プログラミング難度が高い難点があります。

その一方、OSのタスク管理に縛られないため、CPUやRAMを全てフルに使って動かすことができるので、ゲームやエミュレータなどのシビアな性能確保が求められるプログラムでは検討の価値があると思われます。

Zero1のCPUはシングルコアARMv6の1GHz、Zero2のCPUはクアッドコア(4コア)ARMv8の1GHz、RAMは両方とも512MBなので、デスクトップ版のLinuxを動かすにはやや貧弱なスペックですが、ベアメタルであれば割りと十分なスペックです。

また、RaspberryPiにはCircleというベアメタル環境向けのC++ライブラリがあるので、それを用いればHDMIによる映像と音声の出力やUSBデバイスのアクセスなど、ベアメタル環境ではハードルが高いプログラム開発もかなり簡単になります。(※ただし、CircleはGPLライセンスなので注意が必要ですが)

なお、Circleを使ってもmapやvectorなど(C++ STLのコンテナ)は利用できないので、STL依存の多いC++プログラムを動かすのは難しいです。

幸い、私が開発しているMSXエミュレータはSTLの使用を避けてきたので、比較的簡単にベアメタル対応ができました。

よりシビアな環境を目指すならC++すら使わずC縛りにした方が良かったりするのですが、C++11以降のラムダ式やenum classが便利なので、私は組み込みプログラミング(ベアメタル開発)でもC++を使う点を許容する派のプログラマです。

はるか昔、とある汎用機向けのプログラムを書いていた時、Cのツールチェインは安定していたけどC++のツールチェインは(主にSTL周りが)バグだらけで苦労させられた経験がトラウマになっているので、今でもSTLの使用を可能な限り避けるようにしています。

ただし、RaspberryPiのベアメタル環境向けにFreeRTOSが既にポーティングされているらしく、FreeRTOSならSTLが普通に使えると思われる(M5StackやRaspberryPi PicoなどのArduino FrameworkがFreeRTOS配下で動いていると思われるがそこでは普通にC++ STLが使用できる)ので、FreeRTOSを導入するのも手かもしれません。

https://github.com/LdB-ECM/Raspberry-Pi/tree/master/FreeRTOSv10.1.1

性能上の問題点

現状、Zero1向けMSX2+コアについては60fpsで動作しません。

60fpsで動作させるには、1フレームの処理時間を16,666μ秒以下にする必要がありますが、MSX2+エミュレーションの1フレームの処理にZero2でも16,060μ秒以下程度の時間を要します。

Zero2実機で測定したMSX2+エミュレーション処理速度
(16,036μ秒〜16,053μ秒)

プリエンプティブなマルチタスク環境(OS支配下)では、動作速度の保証がないのでこの速度ではやや微妙ですが、ベアメタル環境であれば安定的に60fpsで回すことが理論上は可能です。

しかし、USB機器との通信処理やHDMIへの映像と音声の出力処理が加わると確実に16,666μ秒をオーバーしてしまいます。

そこで、Zero2がクアッドコアであることを活かし、I/O処理(USB機器との通信処理やHDMIへの映像と音声の出力処理)をcore0、MSX2+エミュレーション処理をcore1、HDMI出力用の映像加工処理をcore2に専念させるタスク分散により動作が安定しました。

しかし、Zero1はシングルコアなのでそれができないため、現状60fpsで動かすことができません。

なお、MSX1コアについてはESP32という貧弱な環境でもある程度動けるように最適化した甲斐があって、Zero1でも60fpsで快適に動作します。また、使用コア数を抑えた方が消費電力を抑えられるため、Zero2でもMSX1コアはシングルコアで動作させています。

性能改善余地

MSX1コアについては、ESP32で動かすために(ほぼ)限界までチューニングしていますが、MSX2+コアは実のところ限界を目指していないのでチューニング余地は結構ある筈です。

ESP32はRAMの制約が厳しく、高速にアクセスできるSRAMは520KBしかありません。(外付けのPSRAMを数MB追加することもできますが、PSRAMはアクセス速度が遅すぎて使い物にならないので私は常にOFFにしてます)

そのため、ESP32向けにはメモリ所要量を抑えつつ性能向上を図る必要がありました。

一方Zeroシリーズの場合、512MBというほぼ無限に等しいサイズのRAMが搭載されているので、計算処理のテーブル化など、メモリをガリガリ使う方向での性能最適化が良いかもしれません。

ライセンス上の問題点(?)

現状、カーネルイメージにゲームのROMファイルを組み込んでしまっているため、micro MSX2+を用いてRaspberryPi Zero向けのゲームを販売すると、Circleのライセンス(GPLv3)の影響でゲームのソースコードをOSS(GPL、BSD、MIT or ユーザーからの要望に応じてソースコードリストを配布する形)にする必要がある問題(?)があります。

これを問題と呼んで良いかは一考の余地がありますが。

ソフトウェアのOSS化に積極的な私は「別にそれで良いのでは?」と思ってしまっている節があるのですが、クローズドソースにすることで商業的アドバンテージを得られるという考え方も理解できなくはありません。

そこで、ゲームのソースコードをOSS化しなくても良い方向での改修についても一応検討中です。(モチベーションは低いので時間が掛かるかもしれませんが)

Circle→FreeRTOS(MITライセンス)に移行しつつ、VC4アクセスやUSBホストを自前で実装するとかかなと考え中です。

この記事が気に入ったらサポートをしてみませんか?