MSX ゲーム開発 2022年

1980年代から90年にかけて家庭に広く普及した MSX パソコン(当時はマイコンと言ってましたね!)にて、ゲームをつくる活動を 2022年に復活してみました…!

当時の開発は MSX-BASIC もしくは Z80 アセンブラをつかったものでしたが、今回は さまざまな 8bit Z80 系のレトロコンピュータに対応する Z88DK ツールチェインによる C 言語を使っています。

Home: z88dk

z88dk is the only C and assembler development kit that comes ready out-of-the-box to create programs for over 100 z80-family machines.

Z88DK のセットアップやサンプルコードについては、以下の文書にまとめています。C 言語ですが、BASIC より簡単かも、、ですので良ければ遊んでみてください。この記事で紹介しているゲームのソースコードへのリンクもつけています。

Z88DK を使って MSX のゲームをつくるための環境構築メモ

この文書は、Z80 を CPU に持つコンピュータ向けの C コンパイラ・アセンブラツールチェーンである Z88DK を使って MSX のゲームをつくるための環境構築メモです。

てなわけで、この Z88DK を使いまして、新作ゲーム(?) をふたつつくってみましたので紹介したいと思います。ゲームはウェブブラウザーで動作する MSX エミュレータから楽しめますので合わせてリンクをしています。

PONPON for MSX

80年代のコンピュータ誌に投稿された PONPON という名前の投稿プログラムの MSX クローンです。自分はプログラムポシェット誌でみて打ち込んで楽しんだ覚えがあります。

自動的に上下に移動する主人公「◯氏」を赤ブロックに激突しないように左右に操作して $ を取得して点数を競うゲームです。

次のリンクからウェブブラウザで遊べます。

WebMSX で PONPON を遊んでみる…!

記憶だけを頼りにつくっていますが、この MSX 版はプログラムポシェット掲載 PC88 PONPON の “改造版” の移植です。オリジナルは 40 * 25 桁(WIDTH 40,25) でもっと綺麗に壁や赤ブロックが並んでいたと思います。

当時小学生だった自分はごちゃごちゃ改造していて、確かこのような感じになった気がします、、懐かしいです。。

NOBORUNOCA for MSX

MSX の VDP はいわゆるスクロール機能を持たず、スクロールをしたい場合は通常 VRAM ブロック転送による PSG(8ドット) 単位スクロールとなりますが、これを PCG キャラクターをドットずらしで用意することでスムーズスクロールを実装する技がありました。

当時の自分はスムーズスクロールしてみたくても、プログラミング技術もあまりなく実装を諦めており、これまで数十年間心の何処かにひっかかっていた課題のひとつだったのですが、2022年になってようやく実装することができました。

というわけで、構想数十年、製作 5日の NOBORUNOCA です。

強制縦スクロールのワンキーためジャンプアクションゲームです。使うのは SPACE キーのみ…!のぼるのか…のぼらないのか…

次のリンクからウェブブラウザで遊べます。

WebMSX で NOBORUNOCA を遊んでみる…!

操作はシンプル、割と奥が深いを目指してつくってみました。ゲーム特有の落ち着けばなんとかなる…!がうまくつくれたと思いますので、良ければ遊んでみてください…!(ツイッター #NOBORUNOCA タグでみなさまのハイスコアを拝見しております… 😀

裏技的な攻略情報がいくつかあるので書いておきます。

  • 足場生成に関わるゲームの乱数シードはタイトル画面のパワーゲージ値によります。
  • レベルエクステンド時に、必ず穴がない休憩足場が生成されます。
  • ジャンプ上昇中、ジャンプ落下、歩き落下中もパワーゲージがチャージできます。
  • 落下中から、着地前直前十数フレームで、その場再ジャンプが可能な猶予フレームがあります。これは足場がない最下段でも適用可能なので、目押しスペース離しで復活できることがあります。また、成立するとボーナス点が入ってます。
  • ジャンプ落下中は足場判定が横に広がっています。このため左右の縦壁でも再ジャンプ可能なパターンがあります。

いろいろ実装していましたら、当時の BASIC ゲームも面白くなるようにいろいろ調整して楽しんでいたことを思い出してノスタルジー。

ゲームミュージック

マイコンゲームに音楽をつけるのも当時の課題のひとつで、サウンドドライバー問題とシーケンサーどうする問題がありましたが、今回 @aburi6800 さん の MSX Z80 サウンドドライバーと、いちまるまるゲームズさんの、Lovely Composer & あぶり6800さんコンバータにより、見事、課題解決することができました…!

ありがとうございました…!

Lovely Composer (ラブリーコンポーザ)

家庭用ゲーム機の作曲ソフトの方向性を受け継いだ、かわいい作曲ツール!
レトロゲームのようなピコピコサウンドの音楽や効果音を、楽しく手軽に作れます。

https://github.com/aburi6800/msx-PSGSoundDriver

MSX用のPSGサウンドドライバです。
z88dkのz80asmでコンパイルできる形にしています。

楽曲は YAMAHA MODX シンセで曲のスケッチをかいて、Lovely Composer に打ち込む形で楽曲をつくり、lc2asm コンバータでゲームに取り込む手法でつくっています。

今回は MSX 向けで矩形波 2/3ch だけの打ち込みとしていますが、Lovely Composer は矩形波以外も波形メモリ音源的な音などなどピコピコサウンドを手軽なプリセットと操作系で使え楽しいです。:D

MSX ゲーム取り込み前には、YM2149 での鳴り具合を確認するために、自分が以前からつくっていました WebAssembly の ymfm エミュレータを Python からコールして、サウンドドライバー互換で発音させるスクリプトも利用しています。

https://github.com/h1romas4/noborunoca/tree/main/tools/lcconv

楽曲のほうですが、自分は音楽の方が楽器は持っているもののほとんど素人ですが、、レトロっぽいコミカルさが出るように、少ない小節数にすると心に決め、メロディーのリズムに気をつけてかきました。

密かにちょっと気に入っていますので良ければ聴いてみてください…!

最後に

残念ながら手元に MSX 実機がないためまだ実機動作を自分で見れていないのですが、Simple ROM Cartridge を使わせていただいて、いつか動作させたい…!夢の ROM 版ゲーム…

MSX用カートリッジ64K Simple ROM Cartridge

ちなみに PONPON は 16KB で NOBORUNCA はほんの少しだけはみ出て 32KB ROM になっています。

書き込み準備ヨシ…!(ちなみに写っている PSP 版は fMSX エミュレータによる動作です)

ゲームのソースコード

両ゲームとも GitHub Actions で ROM のビルドができるように仕込んでいます。また、リリースページに .rom ファイルを置いています。

PONPON

https://github.com/h1romas4/z88dk-msx-template

NOBORUNOCA

https://github.com/h1romas4/noborunoca


関連

WebAssembly をウェブブラウザーで活用する(2)

この記事はゆるWeb勉強会@札幌 Advent Calendar 2021の 3日目の記事です。

去年のアドベントカレンダーより…

自分は、去年のゆるWebアドベントカレンダーで書きました…

WebAssembly をウェブブラウザーで活用する(1)

次の回では、C/C++ もしくは Rust でかかれたプログラムを WebAssembly に移植して動作させるデモをやってみたいと思います!(続く

次の回になるまで 1年かかりました…(すいませんw

というわけで、今年の記事はここ数ヶ月取り組んでいました C/C++/Rust のプログラムを WebAssembly で動作させた顛末を例に、Wasm(WebAssembly の略です) の周辺技術事情やキーワードを書いていきたいと思います。

WebAssembly とは

WebAssembly はバイナリーの実行コード形式です。C/C++/Rust/AssemblyScript をはじめとするさまざまな言語を .wasm 拡張子のバイトコードにコンパイルすることができ、それらの言語でかかれたプログラムを、ウェブブラウザーや WebAssembly ランタイム上で動作させることができます。

画像

特にウェブブラウザーは、これまでコンピュータ言語として JavaScript のみをネイティブでサポートしていましたが、WebAssembly の仕組みを使うことで、別の言語でかかれたソフトウェアやライブラリーを活用することができるようになりました。

中でも C/C++/Rust はシステムやミドルウェアよりのライブラリーの実装が多くありますので、画像・動画処理、文字列パーサー、仮想マシン、3D 処理、ファイル圧縮展開、そして今回取り上げる音声処理など従来 JavaScript で再実装が必要になっていた部分を、WebAssembly にビルドするという手順だけで手軽にウェブブラウザーに持ち込めます。

まずは、今年2021年に気になった WebAssembly の実装をいくつか紹介します。

⏩ Adobe Photoshop の WebAssembly による実装:

おそらく画像処理部分の C/C++ コードをクライアント版と共有していると思います。

Adobe PhotoshopにWebブラウザ版が登場。何ができる?

配信中では宇宙飛行士のヘルメットガラスの反射を修正ツールで取り除き、また色味を調整するなどの内容が披露。操作に伴うレスポンス遅延等もみられない、スムーズな編集を実現していることが示された。

ちなみに、Chromium 系のブラウザーの開発者ツールに、直接 C/C++ ソースのデバッグができる仕組み(と拡張)が追加されたのは、Web 版 Photoshop をつくっていたからという話がありました。

https://twitter.com/h1romas4/status/1348242121631830017

⏩ WebAssembly で動作可能な Rust でかかれた形態素解析 Goya:

日本語形態素解析がウェブブラウザー上で動作します。形態素解析系はビルドが大変だったりしますので、.wasm 版があるのは大変ありがたいです。

WebAssemblyの形態素解析器GoyaをRustで作った

Goyaという形態素解析器を Rust で作りました。本記事は利用者目線で Goya の紹介をします。技術的な詳細については別途記事を書きます。

⏩ Python の WebAssembly 版の Pyodide:

CPython が C/C++ で実装されていることを利用して、インタープリタ・ランタイムごと Wasm にビルドし、ウェブブラウザー上で Python を実行し、導入の操作なしに Python を使うことができます。

Try Pyodide (no installation needed)

Try Pyodide in a REPL directly in your browser. For further information, see the documentation.

⏩ PHP の WebAssembly 版:

Pyodide と同様に PHP のインタープリタも C/C++ ですので Wasm にビルドしてしまえば、あろうことか PHP がウェブブラウザーで動作します。 😀

PIB: PHP in Browser (and Node.js) aka php-wasm

https://seanmorris.github.io/php-wasm/

WebAssembly で実装されたシンセサイザー

今回の Wasm 実装例とするプログラムは WebAssembly にビルドされたシンセサイザーです。

次のリンクから動作を確認できます。(後述しますがマルチスレッド(SharedArrayBuffer) を使っている関係で iOS/Safari では現在動作しません)

https://chipstream.netlify.app/

画面をクリックするとサンプルの音楽が再生されたと思いますが、この音声波形生成(シンセサイズ)を C++/Rust でかかれた WebAssembly で行っています。

シンセサイザーとは “音” を電子機器を使って生成するハードウェア・ソフトウェアの総称です。

音を電子機器でつくる方式はいろいろあり、現在は PCM と呼ばれる録音した波形をコンピュータで変化させる方式が主流で、イメージとしてはピアノのラの音(440Hz)をマイクで収録し、それをコンピュータの演算で 880Hz にして高いラにして発音させるようなことをしています。(実際にはもっと複雑です)

ここで作成したシンセサイザーは FM 音源と呼ばれるシンセサイザーの方式で、サイン波(口で発声するのなら、”ポー”みたいな電子音)をサイン波で変調することで倍音を加え、デモのような電子音のような、そうでないような美しい不思議な音を発音させることができます。

画像

1980年代に非常に多く使われたシンセサイザーの方式で、日本で一番なじみがあるのが山手線のホームで流れる発車音のエレピの音だと思います。また 1990年台のゲーム基板や、2000年台初頭のガラケーにも搭載されていましたので、ゲームセンターや着メロで聞き覚えがある方もいらっしゃるかもしれません。

WebAssembly でできること

よくWebAssembly は計算しかできないと言われますが、まずはその通りで今回のシンセサイザーの例でも波形の計算を C++/Rust で行っています。

音声波形計算の単純な例をあげると、1秒を 44100(サンプリング周波数)で割った配列をつくり、16bit(量子化ビット数)の -32768 〜 32767 の値で 440Hz のサイン波をつくれば”ポー(ラー)”波形の計算となります。このシンセサイザーに、ラを0.5秒鳴らせといった演奏データ(シーケンス)を渡すことで音楽となる手はずです。

WebAssembly が計算しかできないと言われるゆえんは、WebAssembly からホストとなる(この場合はウェブブラウザー)の機能には直接アクセスできないことで(ユーザと対話できない)、今回の場合も計算された波形はブラウザーの JavaScript を経由して WebAudio API に渡して発音してもらうようなプログラムとなっています。

ソースコードを以下に公開していますので興味がある方は覗いてみてください。

実際には ymfm という C++ でかかれた YAMAHA FM 音源チップのエミュレーションライブラリーを、演奏データの制御やサウンドチップのサンプリングレートのコンバートを行う Rust のプログラムとリンクする形で動作しています。

https://github.com/h1romas4/libymfm.wasm

This repository is an experimental WebAssembly build of the [ymfm](https://github.com/aaronsgiles/ymfm) Yamaha FM sound cores library.

使わせてもらっている ymfm ライブラリーのソースコードは一切触れることなく、そのまま WebAssembly にビルドしています。Rust と C++ をリンクして動作する Wasm の例としても見ることができるかもしれません。

その他、リアルタイム音声処理に関しては処理の遅れが音切れという形で現実世界に悪影響を及ぼしますので、マルチスレッド(WebWorker)を使っていたり、同期するために SharedArrayBuffer を使っていたり、従来から使われている ScriptAudioProcesser ではなく AudioWorklet を使っているなど、 WebAssembly との組み合わせもあまり見ないサンプルになっていますので、JavaScript 的にも、もしかすると参考になるかもしれません。

なお、スレッド同期に使っている SharedArrayBuffer の iOS/Safari での実装が 2021年 12月現在テクニカルプレビュー中です。次のリリースあたりで使えるようになると思いますが、現在は Safari で動作しません。そのうちに動作し始めると思います。

ウェブブラウザーを超えて

さて、JavaScript がウェブブラウザーから飛び出し Node.js や Deno といった形で取り出されたように、WebAssembly にも同様なランタイムがあります。

いろいろな実装があり群雄割拠している状態ですが、自分が使っているものをいくつか紹介します。

wasmtime

Standalone JIT-style runtime for WebAssembly, using Cranelift

Wasmer

Run any code on any client. With WebAssembly and Wasmer.

Wasm3

The fastest WebAssembly interpreter, and the most universal runtime

wasmtime がリファレンス実装的な印象で、Wasmer は高速で各言語バインディングが充実、Wasm3 は JIT を使わないインタープリタ型で JIT 禁止の iOS 環境でも動作可能、また最小構成で Arduino などのマイコンでも動作といった特徴があります。

WebAssembly ランタイムはもちろん単体で .wasm を動作させることができますが、さまざまな言語をホストとして .wasm を実行をする仕組みが備わっています。

“wasmer-python” を利用して、この記事でウェブブラウザーで呼び出したシンセサイザーの .wasm ファイルとまったく同じものを Python から呼び出してサウンドプログラミングした動作例が次の動画となります。(ソースコード: https://github.com/h1romas4/libymfm.wasm/tree/main/examples/python

.wasm は OS を選びませんので、ランタイムと組み合わせることでウェブブラウザー(JavaScript)に限らずさまざまな言語から呼び出して活用することが可能です。

Wasmer の言語対応:

特に C/C++ でかかれたライブラリーのスクリプト言語からの呼び出しは、使われる範囲的にインターネット経由の入力が伴うことが多く、セキュリティーの面で気になる部分がでてきますが、Wasm をかぶせることでサンドボックスとなり安全性が高まり、また OS の依存もなくなりますので扱いが簡単です。 .wasm がひとつあればどこでも動作します。

PHP で画像処理したいと思ったら wasmer-php で Wasm ビルドした C/C++ ライブラリーを安全に使う…なんていうのもアイディアかもしれません。

最近ではソフトウェアのプラグインシステムとしても WebAssembly を活用し、そのソフトウェアがつくられた言語以外でプラグインをかけるようにしているしている例もみられるようになりました。

zellij

Zellij includes a layout system, and a plugin system allowing one to create plugins in any language that compiles to WebAssembly.

まとめ

  • WebAssembly はいろいろな言語でつくられたソフトウェア・ライブラリーを、ウェブブラウザーやランタイム上で OS を選ぶことなく動作させることができる。
  • WebAssembly のランタイムと各言語のバインディングを使うことで、言語を他の言語で拡張して使うこともできる。

WebAssembly の今後は「計算しかできない」を解決していく Interface Type や WASI といった仕様の策定・実装、GC やマルチスレッドの対応の強化が進み、活用範囲や対応言語も増え、目にする機会も増えるかなと思っています。クラウドの Lambda 的な部分での採用も増えていきそうです。

面白いことがあればぜひ教えてください。てなわけで、また来年(!?)…!

ツイッターやってます:

https://twitter.com/h1romas4/status/1460572622475841542

ゆるWeb勉強会

ゆるWeb勉強会とは札幌で開催している @tacck さん主催のWeb系勉強会で、現在はオンライン配信も行っていてどなたでも参加できます。いろんな方の発表を聴き、またわいわい質問もすることができますので、興味がある方は覗いてみてください!

ゆるWeb勉強会@札幌

主に「Web」というキーワードに関する、ゆるい勉強会です。 プログラムを始めたばかりの人も、仕事でバリバリやっている人も、どなたでも歓迎です!

「勉強し初めて、次に何をやればいいんだろう?」 「普段使っていない言語やフレームワーク、知っている人に導入部分を聞いてみたい。」 「いつもはバックエンドだけど、たまに触るフロントエンドの話も知りたい。」 「実際、みんな現場ではどうやって開発してるの?」

仕事・趣味を問わず、普段の仕事の内容も問わず、「Web系」に関することを、ゆるっとみんなで話してみたいです。

関連

M5Stack Core2 SDK でメガドライブエミュレーターをビルドする

はじめに

しばらく品切れが続いていました M5Stack Core2 を買うことができました。嬉しいです。 😀

Core2 には PSRAM が 8M ついているということで、そのあたりを確認しつつ、ハローワールドがてらメガドライブエミュレーター(Genesis Plus GX)を移植してブートさせてみました。

ソースコードを github で公開しています。

Genesis-Plus-GX M5Stack Core2 porting (no optimize and super slow)

https://github.com/h1romas4/m5stack-genplus

まだビルドしてエミュレーターの起動を確認したところまでのソース断面となっていますので、最適化はまったくしておらずとっても遅いです。

後述しますが、単純に不足したメモリーを SRAM から拡張のクロックが 80MHz PSRAM にメモリーを逃したのと、LCD への仮想 VRAM 転送で何も工夫をしていないためです。

(速くする方法を思いついたらちょこちょこいじるかもしれません。また、いい方法があったらぜひお教えください!)

この記事では M5Stack Core2 上でこういった少し大きめのソースをビルドするノウハウと、PSRAM の使い方を紹介してみたいと思います。

esp-idf のビルドシステムを使った M5Stack Core2 アプリのビルド

M5Stack Core2 の標準の C/C++ 開発キットは Arduino IDE となっていますが、移植元となっている Genesis-Plus-GX のソースツリーが比較的大きいのと、各コンパイルオプションなども細かく修正しながら作業したかったので、esp-idf ビルドシステムを使って M5Stack Core2 向けのバイナリーの作成を行っています。

esp-idf が提供する Makefile を用いるとプロジェクトの構成を次のようにすることができます。なお、esp-idf の 4系のバージョンでは cmake を使うように変わっていますが、ここでは esp-idf 3.3 系を使うため Makefile 方式としています。

+ components
    + arduino (https://github.com/espressif/arduino-esp32)
    + m5stack
        + M5Core2 (https://github.com/m5stack/M5Core2)
        component.mk
    + genplus 
        [ソース]
        component.mk
+ esp-idf (https://github.com/espressif/esp-idf)
+ main
    main.cpp
    component.mk
sdkconfig
Makefile

maincomponents ディレクトリの構造と各コンポーネントしたに配置された components.mk とルートの sdkconfigMakefile が esp-idf のビルドシステムが認識する要素です。

そのまま使いたい時は、m5stack-genplusgit clone --recursive していらないファイル消すのが簡単かもです。:D

プロジェクトテンプレートリポジトリーと How to create のドキュメントをつくりました。また、github actions で自動ビルドもかけれるようにしています。使う場合は github にログインして以下から Use This Template ボタンを押して、自身のリポジトリーを作成してプログラミングを開始すると便利です。

https://github.com/h1romas4/m5stack-core2-template

esp-idf build system template for M5Stack Core2.

さて、M5Core2 ライブラリーが依存するプロジェクトは esp-idf と arduino-esp2 ですが、現在 Arduino IDE で提供されている一式がどの断面を使っているのかが分からなかったので、ビルドが通るものを試行錯誤して、以下のバージョンでソースツリーに git submodule で固定して導入しています。

現在リリース版の arduino-esp32 は v1.0.4 の esp-idf 3.2 依存ですが、これではビルドが通らず未リリースの arduino-esp32 v1.0.5 相当で、esp-idf は 3.3 の最終コミットを使っています。 (どうも esp-idf v3.2 系及び v3.3.5 タグだと必要なファイルがなかったり、コンパイルエラーになるなどビルドが通りませんでした)

(2021/6)現在の最新版の組み合わせは次のようになっています。

nameversionhash
esp-idf3.3.5
arduino-esp321.0.6
M5Core20.0.3(latest)54b958b

(2022/10 追記) esp-idf 4.2系 / Arduino 2 系に依存を変更したプロジェクトを以下に作成していますので参考にしてみてください。

https://github.com/h1romas4/m5stack-core2-wasm3-as

M5Stack Core2 With Wasm3/AssemblyScript Demo


git submodule で導入しておくと、バージョンが切り替わったときの上げ下げやヒストリーの確認も容易かと思います。(このようにしておくとプロジェクトごとに依存のバージョンを固定できます)

ビルドは、各 OS に対応した xtensa-esp32 のツールチェイン(gcc)を導入後、make するだけで M5Stack Core2 向けのバイナリーが作成できます。 m5stack-genplus の github をビルドする手順は次のようになります。 (Windows では MSYS2 の窓より…)

# submodule があるので --recursive 指定 
git clone --recursive https://github.com/h1romas4/m5stack-genplus.git
cd m5stack-genplus
# This repository includes eps-idf
export IDF_PATH=$(pwd)/esp-idf
# CPU 4コア CPU の場合 -j4 とコアすると速い
make -j4
# 書き込み & 実行
make flash monitor

sdkconfig ファイル

sdkconfig ファイルは esp-idf や esp-idf に対応した各コンポーネントが使う define の定義です。これは make menuconfig コマンドでメニューから生成することができます。

m5stack-genplus リポジトリーには sdkconfig がコミットされていますので各種設定済みで、変えるのは転送先のシリアルポートの設定くらいですが、esp-idf や arduino-esp32 のどの機能を使うかなどなどの設定ができます。

保存すると sdkconfig に値が書き込まれますが、基本的に make や C/C++ 中の define 定義集のようなものなので、キー名でソースコードを grep すると何が有効になったのかが分かります。

component.mk ファイル

各 component.mk の中にはそのコンポーネントで使われるソースファイルやインクルードファイルの位置、CFLAGS などのコンパイルオプションが定義できます。

Genesis Plus GX モジュールでは次のように定義しています。

COMPONENT_SRCDIRS := core core/z80 core/m68k core/input_hw core/sound core/cart_hw core/cart_hw/svp core/ntsc m5stack
COMPONENT_ADD_INCLUDEDIRS := core m5stack core/cart_hw core/cart_hw/svp core/cd_hw core/debug core/input_hw core/m68k core/ntsc core/sound core/themor core/z80

CFLAGS := \
    -DLSB_FIRST \
    -DUSE_16BPP_RENDERING \
    -DMAXROMSIZE=131072 \
    -DHAVE_ALLOCA_H \
    -DALT_RENDERER \
    -DALIGN_LONG \
    -DM5STACK \
    -fomit-frame-pointer \
    -Wno-strict-aliasing \
    -mlongcalls

利用できるオプション値は次のドキュメントから参照できます。

https://docs.espressif.com/projects/esp-idf/en/v3.3.3/api-guides/build-system.html#optional-project-variables

Optional Project Variables

ライブラリーなどを M5Stack に移植して動作させる時は、CFLAGSCPPFLAGS などが容易に設定できますので便利に使えるのではないかと思います。components ディレクトリに任意の名前でディレクトリを作成してソースをがばっと入れ、component.mk を書いてあげればビルド対象になります。

またライブラリーにユーザー設定がある場合は Kconfig.projbuild をつくって配置することで、make menuconfig(sdkconfig) 用のメニュー定義も入れることができるようです。

PSRAM の使い方 (.bss セグメントを逃がす)

esp-idf で大きめのプログラムをリンクすると、

region `dram0_0_seg' overflowed by 1995968 bytes

このようなエラーメッセージでリンクできない場合があります。これは static の領域を確保する .bss セグメントが足りない場合に出力されます。Genesis Plus GX のビルドでは当初、上記のように 2MB 近く足りませんでした。

この .bss セグメントを PSRAM にもっていくオプションがあり、次の menuconfig の設定から有効にすることができます。

https://docs.espressif.com/projects/esp-idf/en/v3.3.4/api-guides/external-ram.html

→ Component config → ESP32-specific → SPI RAM config 
→ Allow .bss segment placed in external memory

define 値的には CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY となりますので esp-idf を grep してみると分かりやすいです。このオプションを有効にした後、ソースファイル上の static 変数に EXT_RAM_ATTR をつけると、PSRAM に領域を持っていくことができます。

#ifdef M5STACK
#include "esp_attr.h"
#endif

#ifdef M5STACK
EXT_RAM_ATTR static uint32 bp_lut[0x10000];
#else
static uint32 bp_lut[0x10000];
#endif

イメージ的にはこんな感じになります。 EXT_RAM_ATTR は未定義はブランクなので、これでも大丈夫です。

#ifdef M5STACK
EXT_RAM_ATTR
#endif
static uint32 bp_lut[0x10000];

なお、PSRAM は CPU から最大 80MHz の SPI で接続されており、通常の SRAM より速度が遅く、転送レートは最大で SRAM 960MB/sec に対して 40MB/sec ほどのようです。このため、主要なロジックで使われるメモリーを載せると処理速度が低下してしまうはずです。

How slow is PSRAM vs SRAM (anyone have quantitative info?)

Internal SRAM is 32bit @ 240MHz max, so 960MByte/second. PSRAM is 4-bit @ 80MHz, so 40MByte/second.

PSRAM の使い方 (malloc)

PSRAM がコンフィグレーションで有効になっていると、通常の malloc 関数で PSRAM も使ってくれます。

https://docs.espressif.com/projects/esp-idf/en/v3.3.4/api-guides/external-ram.html

Support for external RAM

PSRAM ではなく速い SRAM 側に確定で確保したいなど、明示的にどちらから取得するかを決めたい場合は、次の設定ができます。

→ Component config → ESP32-specific → SPI RAM config

また、M5Stack Core2 には 8MB の PSRAM が搭載されていますが、使えるのは 4MB までのようで、それより上の 4MB を使う場合は himem API で別途取得するようです。(今回は未検証)

https://docs.espressif.com/projects/esp-idf/en/v3.3.4/api-reference/system/himem.html

The himem allocation API

VS Code の設定(おまけ)

あんまり関係ありませんが、開発は VS Code + C/C++ Extention で行いました。インテリセンスの効きもよく大変良いです。

VS Code ではプロジェクトに .vscode/c_cpp_properties.json を配置して次のように設定すると便利です。

{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/build/include",
                "${workspaceFolder}/components/arduino/cores/**",
                "${workspaceFolder}/components/arduino/libraries/**",
                "${workspaceFolder}/components/arduino/variants/esp32",
                "${workspaceFolder}/components/m5stack/M5Core2/**",
                "${workspaceFolder}/esp-idf/components/**"
            ],
            "defines": [
                "ESP32=1",
                "ARDUINO_ARCH_ESP32=1",
                "BOARD_HAS_PSRAM",
                "ARDUINO=10800",
                "M5STACK",
                "LSB_FIRST",
                "USE_16BPP_RENDERING",
                "MAXROMSIZE=131072",
                "HAVE_ALLOCA_H",
                "ALT_RENDERER",
                "ALIGN_LONG"
            ],
            "intelliSenseMode": "gcc-x64",
            "compilerPath": "~/devel/toolchain/xtensa-esp32-elf/bin/xtensa-esp32-elf-gcc",
            "cStandard": "c11",
            "cppStandard": "c++17"
        }
    ],
    "version": 4
}

defines にはコンパイルオプションで指定した define を定義しておくといい感じにパーサーが感知して色分けしてくれます。sdkconfig の define 値については、自動的に生成された build/include/sdkconfig.h を読むことで自動的に反映するようになっています。

例えばですが、上記のようにコンパイルオプションで定義された M5STACK 値の ifdef が有効になって else がグレーアウトしますので分かりやすいです。

おわりに

というわけで、M5Stack Core2 ハローハッピーワールドでした。

他にもいくつかプログラムを動かしてみましたが、タッチセンサーもよく動いて M5Stack Core2 楽しいです。ビルドも固まりましたので、引き続き何かつくってみたいです!

関連記事