PICO-8で遊んでみる!その2

先日からPICO-8で遊んでいる!


世界中のユーザーが作ったゲームで遊ぶのも楽しいけれど、PICO-8の魅力はなんといっても「なんとなく自分でも作れるカモ?」と感じさせる規模感があると思う。

 

128x128ドット16色の画面構成、スプライト128枚、重ね合わせ出来るBGとメモリマッピングされたVRAMなどに加えて、このスペックを駆動しても有り余るCPUパワー!

8bitマシンが大好きな人にはホントたまらない構成だと思う。

まぁCPUパワーは使うマシンによって変わるけれど!(^^;;

 

ちょっとムリな画面描き換えをしても平然と動いてしまうのが楽しすぎる!

編集環境を改善!

前回のブログで「Emacsで編集できるのが便利すぎる!」と書いた。

これはこれで便利なのだが…慣れないLua言語を扱うのにはもう少しだけ工夫をしたい。

作っている過程であれこれ試そうとすると、いくつかの便利な機能が欲しくなる。

 

 コメントに日本語を使いたい

 条件によってプログラムを変えたい

 マクロを使いたい

 

これらを整えるために、ツールも含めて環境を整備してみる事にした!

 

まずソースコードは書きやすいフォーマットで、かつコメントはふんだんに書いておきたい。

EmacsLuaメジャーモードで編集すればフォーマットは任せておける。

もう少しLuaに慣れたらコメント少なめでも良いかなーと思うけれど、慣れてない状態でコメントがないとどーにもならない(^^;;

 

これをcpp(プリプロセッサ)に通し、PICO-8の都合の良い形式に変換する。

こんな感じのテキストになるので、PICO-8本体で見た時にはコメントが一切無い。

なんかの都合でソースを公開する事があったら、元ファイルも同時に出さないと、解析がほぼ出来ない事になっちゃうね…汗

 

この変換作業で、PICO-8上で編集したドット絵やマップ、サウンドとマージしている。

PICO-8に読み込むとこんな感じ。

インデントもコメントもない殺風景なソースだ!(^^;;

 

流れ的にはMacEmacsで編集→コマンドラインでmake→PICO-8上でLOAD、RUN。

makeする手順が増えたけれど、自分が書くソースが自由になるメリットは計り知れない!

 

動くものを作ってみる!

PICO-8の開発環境にも慣れたいし、Lua言語も思い出したい。

簡単なゲームっぽいプログラムを作ってみようと思った。

過去に何度か作っているゲームを作ることにした!

多分、途中までww

 

1980年代にハドソンソフトから発売していたキャノンボールというゲームを作ってみる。

www.youtube.com

YouTubeのリンクを貼っておくので、画面イメージとかルールとか参考にしてください。

 

まずは画面に丸を描いてみる。

丸形のグラフィック(スプライト)を用意しようとも思ったが、プログラマさんなら分かってくれると思うけど、私はきれいな丸をドットで描けない(^^;;;

 

PICO-8にCIRCFILLという中塗りの円を描く命令がある。これを使って描画したら、遅すぎてゲームにならないかな??…うむむ、速度はどうだろう??と思ってやってみたら、とても速くて十分使える事が分かった!

色を黄色にして、複数のボールを表示してみる。

 

プレイヤーはスプライトで表示しつつ、アニメーションも入れる。

こんな感じにちょっとずつ作ってみる。

特にLuaでどう書くのが効率が良いのか…がさっぱり分からないので、手探り状態が続く…。

純粋にLua言語として覚えた方が良いのは分かるけど、とりあえず動かしてみたい気持ちが強くて、効率よりは動かす事優先だ(^^;;

 

キャラクタはYouTubeの動画を参考してドットを打った!

多分、このゲームを作る流れで、一番時間がかかった作業かも知れない!(T-T)

 

ここまで作ったところで飽きてしまった…汗

というか、この先はシーケンスを組み上げる事が大半で、新しい事を覚える必要がなさそうなのだ。とりあえず実験する事は出来ただろう…と自己解釈して、ここで終わり(^^;;

 

PICO-8を使ってみて

実際にPICO-8でプログラムを組んでみて思ったが、Mac上で動かしている分にはCPUパワー足りないと感じる事が一度も無かった。

 

表示(_drawファンクション)は全画面クリアしてから描き直してるのだが、まだまだ速度は足りてる感じ。Macを基準で動かしてしまうと、Miyoo miniで動かしたらどーなるのか…とか思うけど、そこは動かすの優先で後回しにしてる(^^;;

 

少し気にしていたプログラムサイズの制限(8192ワード)だけど、この中途半端なプログラムで148行、854/8192と余裕ありまくりだ!

 

さて、PICO-8にも少し慣れてきたので、もう1つだけ何か作ってみようと思う!

それはMacでもMiyoo miniでも動かしてみたいと思う!

うまくいくかなー(^^)

 

ではまた次回!(^-^)ノ

 

PICO-8で遊んでみる!その1

f:id:PocketGriffon:20220417080353p:plain

PICO-8……

何度となく名前は見たことあったけれど、それが何なのかは良く分かってなかった。

なんとなく「レトロな画面で動くレトロな環境」くらいのイメージ??

今まではあんまり調べようと思ってなかったのもあったけど!(^^;

 

そんな時、流れてきたツイートに目を奪われた。

え…これ、すごくない???

PICO-8ってこんなのが動いちゃうんだ!

そうと分かったら興味津々!

ぜひ手元で動くようにしてみたいと思った!(^-^)

 

まずは動かしてみる

そういえばFunkey Sであれこれ動かそうと調べていた時に、PICO-8らしき文字を見た覚えがあったと思い出す → もう一度調べてみる事に。

List of third-party OPK applications - The FunKey Wiki

あったあった、これこれ!

PICO-8のエミュレータでその名もFAKE 08…。

……その名前でいいの??(T-T)

 

このFunkey SのFAKE 08で、PICO-8の事を初めて見る私。

…困ったことに…言葉(PICO-8特有の言葉)がさっぱり分からない!(TOT)

CARTSってなに?(T^T)

一番最初に物事を覚えようとすると大変だよね…。そのうち、こういうのが覚えられなくて何も出来なくなっていくのかな…(寂

ともかく、何かプログラムを動かしてみよう!

 

PICO-8の公式ページに移動してみる。

www.lexaloffle.com

ここの右側にある「MORE CARTS」をクリックして、なにか適当なゲームを選ぶ。

f:id:PocketGriffon:20220417081709j:plain

試しにこのゲームを選択!

これはあのゲームですな、懐かしい!(^-^)

 

f:id:PocketGriffon:20220417081822j:plain

この画像のここにあるCartをクリック。

 

f:id:PocketGriffon:20220417081847j:plain

表示される画像をファイルとしてダウンロードする。

これでプログラムやキャラクタデータ等も含めてダウンロードされる!

これは賢い!!(^-^)

PNGファイルをコピーすれば、プログラムもデータもサムネイル!も、まるっとコピー出来るのはとても便利だ。

 

気になったのでPNGファイルのバイナリを覗いてみたけれど、多分IDATチャンク(イメージデータ)の中にプログラムやデータも含まれているんだろう…と思うけど、細かく解析出来てない(^^;;

 

f:id:PocketGriffon:20220417084245j:plain

f:id:PocketGriffon:20220417084259j:plainf:id:PocketGriffon:20220417084314j:plainf:id:PocketGriffon:20220417084328j:plain

紆余曲折ありまくったけど、Funkey SでなんとかFAKE 08を動かす事が出来た!

このサイズで動くと、PICO-8画面のドット数(128x128)が気にならないのがすごい(^^;;

これは楽しいぞ、PICO-8!

 

PICO-8は有料ソフト

PICO-8についてもう少し詳しく調べてみると、どうやらPICO-8本体は有料のソフトウェアらしい。そうか、動かすだけなら互換ソフトを使う事が出来るけれど、プログラムを作ったりするには正規品が必要って事か(^-^)

 

そしてLua言語を使ってプログラムしていくという。

うむむ…Lua…いろいろと思い出が蘇るw

 

今から15年ほど前、海外のプロジェクトに協力する事があった。実際にその国へ出向き、1年弱ほど仕事をしていた。

言葉も文化も理解できないけど、プログラムだったら世界共通じゃん!大丈夫だよ…とか思ったが、ソースに書かれたコメントもそっちの文字で…しまったコレは盲点!!(^^;;

Emacsが多国語言語対応でマジで救われたw

 

で、そこの環境で使われていたのがC/C++/Luaだった。私はシステム担当だったのでLua自体はほぼ覚えなかったが、ソースの大枠は見ていたので、今回もなんとかなるでしょ!

 

イマドキ風の開発?

PICO-8を起動すると良い感じの画面が出てくる!

初期はフルスクリーンで起動するので、でっかい画面に荒いドット画面が…(^^;;

f:id:PocketGriffon:20220417091736p:plain

私的にはこの粗さがタマラン画面なのですが、他の人から見たらどうなんだろう?(^^;;

 

ここでESCキーを押すとテキストエディタが起動する。

f:id:PocketGriffon:20220417091835p:plain

ホントだったらこの画面でプログラムを書いていくのだけど……生産性の面でこれは厳しいかも??慣れたら十分行けるのかも知れないけど!(^^;

なんとかしてMacで編集→PICO-8に取り込むことは出来ないだろうか?(T^T)

f:id:PocketGriffon:20220417092115p:plain

編集画面で全選択→コピー、Emacsでペーストすることが出来た!

おお、これならばEmacs編集が出来るね!(^-^)

 

何度か運用してみたけど、意外にEmacs←→PICO-8のやりとりが面倒くさい。

もう少し良い方法がないかな…と思い調べてみたら、FOLDER というコマンドがあった。

試してみると、ふつーにフォルダが開く。

場所を見てみると、Macの場合は$HOME/Library/Application Support/pico-8/cartsだった。

この場所にテキスト形式としてセーブされるようだ!

このファイルを直接編集→PICO-8でLOADすれば良い事が分かった!

こっちの方が便利だ!!(^-^)

 

しかもグラフィックデータも含めてテキストになっているので、簡単な画像編集だったらEmacsで行えるのも便利!

f:id:PocketGriffon:20220417093001j:plain

↑なんとなく画面下の方にうさぎがいるのが見えるだろうか(^^)

 

よし、なんとなくプログラミングする準備が整った気がする!

 

この先はLua言語の話になっていくと大変なのでどどーと割愛すると思うけど、大雑把に遊んだこととか書いてこうと思う!(^^)

 

ではまた次回!(^-^)ノ

Miyoo miniにFM-7を移植してみた!

f:id:PocketGriffon:20220411092913j:plain

Miyoo miniを手に入れたので、その上で動くプログラムを作ってみた!

何度も利用しているけれども、今回もFM-7エミュレータを移植してみたよ(^-^)

単に表示されるだけなので、コレを動いたと言って良いのかどうか……(-_-;;

 

何も分からない手探り状態から始めたので、動かすまでにはだいーぶ苦労した(T-T)

 

Wi-Fi無し、有線LAN無し、ssh出来ない…と分かった時、さてどうしようか…と(^^;;

たとえネットワークが繋がったとしてもキーボードもマウスもタッチパネルも無いのだ…。

そういう用途のマシンじゃないから!がというのが答えだろうけど!(^-^;

 

動いてる感じを見つつ、Miyoo miniの構造を想像して、なんとかビルドしたプログラムを動かせるところまでこぎつけられた!

今のところ、私の理解としては以下な感じにまとまった。

 

Linux環境は32bit

SDL1を使用

スクリーンの座標0,0は右下

 

 

Linux環境は32bit

最近、Linuxを使おうとするとデフォルトの環境が64bitな事がある。

RaspberryPiを使う時も64bitがふつーに使えるようになってるし、特に32bitにこだわりがなければ64bitを選択する人が多いだろう。

私も多くのLinux環境は64bitで構築していた。

RaspberryPi4、Pi400、DevTerm、reTerminalなど。

 

Miyoo miniで動くバイナリを作るために(ある程度パワフルな)32bit環境が欲しかった。

しかし手元にあるのはポメラとBanana Pi。Banana Piのパワーがあれば問題なく作れるだろうな…と思ったけれど、今後のことも考えて新規に構築する事にした!

 

余ってたRaspberryPi3を使い、コンパクトな環境を用意!

f:id:PocketGriffon:20220411101815j:plain

この写真を撮りながら思ったが、Raspberry Pi400のSDカードを取り替えて32bitにしたら良かったんじゃないの?って思った!(T-T)

MacのParalelles Desktop上でもいいじゃんとも…orz

…思うところはたくさんあったが!(T^T)

とにかく物理的な32bit環境を準備した!(^^;;

 

SDL1を使用

これが今回一番ハマってしまった!

Miyoo miniのSDカード内(${SD}/miyoo/lib)に動的ライブラリが格納されているんだけど、そこにはSDL1しかなく…。

SDL2はおそらく本体内部に入ってるのかな…とか楽観的に考えていた…。

そしたらホントにSDL1しか無いようだ!(^^;;

f:id:PocketGriffon:20220411111424j:plain

試しにSDL2をSDカード内に入れて、SDL2用のプログラムが動かないか実験してみたが、芋づる式に必要なライブラリが出てきてしまい、うまく解決が出来なかった。すべてstaticなライブラリで動かそうとしても(なぜか…これはホントに何故か…)うまくいかない。

うーん……

 

仕方ないので、SDL2用に書かれたプログラムを、SDL1で動くように修正変更した!!

え?そんな手間が掛かる事までする??と思われそうだが、どーしてもどーしても!自分で書いたプログラムをMiyoo miniで動かしてみたかったのだ(^^;;

 

スクリーンの原点座標は右下

無事にSDL1に対応し、Miyoo mini本体で動かしてみて気がついたのだが、スクリーン座標の0,0が左上ではなく右下になっていた。

f:id:PocketGriffon:20220411103652j:plain

確かSDL2では画面の反転回転が出来た気がするけど、SDL1ではどうやってやるんだ??

調べてみたが良く分からなかったので、プログラムで反転させてしまった!(^^;;

ザッツ力技!

エレガントな解決じゃないなぁ…orz

 

実作業

今回は以下のような手順で作業を進めてみた。

 

まずはMac上で動いていたSDL2対応のFM-7をオリジナルとした。

 

これをSDL1対応に修正

私はSDL1を使ったことがなかったので、チュートリアルのようなサンプルから見始める流れになった(^^; うーん、やっぱり後発のSDL2の方が機能的にまとまってるのね!それを知れただけでも今回は収穫だったかも!!

 

Mac上のSDL1で問題なく動くようになったソースコードを、RaspberryPi3のX-Windowで動かしてみるMakefileの書き方(特にincludeパスなど)に違いはあったけれど、本体プログラムに影響は及ばず。無変更でそのまま動いた!

SDLを使うおかげで、MacLinux(というかUNIX+X-Window)の親和性はとても高い…と今回も感じたね(^-^)

 

RaspberryPi3でビルドして出来た実行バイナリをMiyoo miniのSDへコピーし、Miyoo mini本体から起動

この作業を何度やったことか!(TOT)

動作に問題がある(プロセスが終了する)と何事もなくMiyoo miniのメニューに戻ってしまうため、内部で何が起きて落ちているのかがはっきりしない。

 

さいわい、launch.sh に 「./fm7 >& log.txt」などと書いておけば、動作時のログを残せたので、それを見ながら修正を加えていった。

むしろ何度も何度もSDカードを抜き差しするので、カードスロットは大丈夫かな…と心配になったぞ!(^^;;

 

書いてしまえば数行で済む作業だけど、作業自体の手探り感は半端なかった(T^T)

f:id:PocketGriffon:20220411111658j:plainf:id:PocketGriffon:20220411111639j:plain

f:id:PocketGriffon:20220411111728j:plainf:id:PocketGriffon:20220411111745j:plain

 

おわりに

とりあえずMiyoo miniで何かを動かしてみよう!と思った目的は達成できた!

Funkey Sではほとんど何も出来ずにいたので、自分なりの進展があった気がする!

pocketgriffon.hatenablog.com

 

そういえば先日、ゲームカートリッジのイメージを吸い出せる機材を手に入れた!

普段からあまりゲームをする人ではないのだけど、エミュレータを作りたいと思うマシンはいくつかあるので、そのために…という感じだ!(^-^)

 

吸い出したイメージを利用して、Miyoo miniで動かすという事は是非やってみたい!

 

FM-7開発中、Miyoo miniは一度も充電しなかった。

フル充電状態で開発を始め、終了するまでに4〜5時間くらいはあったと思うけれど、その間何度も電源を入れては動作確認をしていた。

開発終わって充電をしてみたけど、ほとんど満充電状態だった…。

小さな筐体だけど電池は持ちそうだね!(^^)

 

Onion OSやTakiiiiiiii環境でも動かせるのかなぁ…。

フォルダ構成が同じ、または似ていれば動かせる気はするけれども!

どっちかまたは両方を近いうちに試してみたい(^^)

 

ではまた次回!(^-^)ノ

Miyoo miniが届いた!

f:id:PocketGriffon:20220409101318j:plain

Miyoo miniというハードをご存知だろうか?(^^)

パット見た感じはゲームボーイみたいな形をしている、少し小型なマシンだ!

名前で検索すると山のようにヒットするので、ちゃんとした使い方を知りたい方はそちらを参照してみて欲しい(^^;;

 

年末から年明けに掛けて、この手のハードが面白いと思っていた時に注文していたが、それが3ヶ月掛かって手元に届いた。

 

サイズ的にはこんな感じ。

f:id:PocketGriffon:20220409102333j:plain

f:id:PocketGriffon:20220409102357j:plain

f:id:PocketGriffon:20220409102413j:plain

手のひらには入り切らないが、十分に握れるくらいのサイズ。

ワリとコンパクトな筐体でありながら、画面サイズやCPUパワーがそれなりにあるため、楽しい使い方ができそうだ!

 

f:id:PocketGriffon:20220409103828j:plain

f:id:PocketGriffon:20220409103848j:plain

届いたときから箱が無く、代わりに専用ケースが付いてきた!

まぁどうせ箱はすぐに処分しちゃうし困る事はないんだけど、なんとなく溢れる中古感…汗

でも新品でしたよ、多分(^^;;

 

ところで……頼んでないSDカードが入ってきちゃったのですが…滝汗

SD無し、保護シート無し、ケースなしの一番安いやつを頼んだはずなのに、なんか全然違う感じに届いちゃうのも、最近の忙しい業者なのかな…。

 

SDカードの中身は送ってはいけない、受け取ってはいけないものが入っているようなので、中のファイルは削除して使う事にする。

 

X68000動くかな?

Miyoo miniの中身がどんな構成なのかを見てみたい!

いつもの流れでsshでログインして…をしてみたかったが、Miyoo miniはWi-Fiがない(T-T)

USB TYPE-Cに有線LANを繋げてみたが、うんともすんとも言わない。

この…肝心なところに手が届かない感覚は…なんとなくFunkey Sを思い起こす…(T-T)

 

仕方ないので、別のアプローチを検討してみる。

試しに付属のSDカードを入れて起動する。

SDカードの中に入ってたイメージファイルは抹消してしまったが、フォルダ構造などは参考になりそうだったのでそのまま残してあるのだ(フォーマットはしていない)。

 

Appというフォルダの中に便利なアプリが入っていたので、これを利用してみる!

f:id:PocketGriffon:20220409105924j:plainf:id:PocketGriffon:20220409105939j:plain

ここにあるRetroArchを利用してX68000を起動してみよう!

#以下、付属のSDカードが手元にある事を前提としていますが、

#後日SDカードが無い状態で起動する方法も模索してみます。

 

フォルダの中を覗いて見ると、RetroArchで使われるコアは、

${SD}/RetroArch/.retroarch/cores 以下に入っている事が分かった。

お!なんとPC-9801エミュレータは入っていた!(np2kai_libretro.so)

じゃあX68000は……と思ったら、px68k_libretro.infoしかなく、肝心のsoファイルが存在しなかった(ToT)

 

うーん……じゃあsoファイルをコピーしてみるか!

np2kai_libretro.soを調べると、32bitバイナリのようだ!

f:id:PocketGriffon:20220409112758j:plain

 

32bitバイナリの px68k_libretro.so は、過去に BananaPie でX68000を動かした時にビルドしている。

pocketgriffon.hatenablog.com

このバイナリを利用して、X68000を立ち上げてみよう!(^^)

 

まず、どのファイルをどこに置いたら良いのかを調べる。

PC-9801エミュレータ(np2kai)があったので、これを参照にしながら設定する。

 

設定ファイルは ${SD}/RApp/np2kai に入っていた。

同じフォルダにある launch.sh とconfig.json を見ると、そこにフォルダ情報が書かれていた。

f:id:PocketGriffon:20220409114422j:plain

↑launch.sh

 

f:id:PocketGriffon:20220409114446j:plain

↑config.json

 

これを見ると、RetroArchはconfig.jsonを参照し、そこに書かれている情報を参照しつつ、launch.shを呼び出す…のかな!?

 

分かった情報と設定する情報は以下の通り。

 

設定ファイル:${SD}/RApp/x68000

libretro.so格納フォルダ:${SD}/RetroArch/.retroarch/cores

romイメージ:${SD}/Roms/x68000

BIOSデータ:${SD}/RetroArch/.retroarch/system/keropi

 

ファイルが足りないもの(アイコンの画像データなど)は、PC-9801のものをそのまま利用してしまった!絵は化けるだろうけど、まずは動かすのが先決よ(^^)

 

あとはプログラム→RetroArchでRetroArchを起動し、それ以降は通常の流れだ。

f:id:PocketGriffon:20220409120528j:plainf:id:PocketGriffon:20220409120604j:plain

f:id:PocketGriffon:20220409120546j:plainf:id:PocketGriffon:20220409120620j:plain

 

驚いたことに、一発であっさり起動してしまった!(^o^)

f:id:PocketGriffon:20220409120756j:plain

f:id:PocketGriffon:20220409120814j:plainf:id:PocketGriffon:20220409120833j:plain

 

Miyoo miniは、CPUクロックが1.2GHzのデュアルコア、メモリは128MBあるのでRaspberryPi Zero2の半分くらいのパワーがある。

X68000がちゃんと動くかな…と思ったが、いくつかのイメージを動かしてみた感じでは、まぁ遊べる…ちょっとだけ遅くなる時がある?くらいの感じだった!(^-^)

 

むしろこのサイズでX68000が動くのが驚く!

サウンドも鳴るし、BGM聞くマシンとしてもいいなぁこれ(^^)

 

試しにPC-9801も動かしてみたが、キーボードが繋がらないので何も出来なかった…汗

f:id:PocketGriffon:20220409121709j:plain

同じような事情で、PC-8801は動くだけ…になるかも。
キーボード繋げる方法はないのかな…??

 

とりあえず今回はX68000動かしてみたよ、的な報告でした!

この先は、SDカード無しで動かせないか試してみようと思う。

 

ではまた次回!(^-^)ノ

RaspberryPi Picoで動くBASIC その2

f:id:PocketGriffon:20220407201206j:plain

ここんところ、RaspberryPi Picoで遊ぶのが楽しくて仕方がない!ヽ(=´▽`=)ノ

 

半導体不足でRaspberryPiが手に入らない現在であっても、Picoは比較的簡単に手に入る。

しかも税込み550円!(ピンヘッダ付きは990円)

www.switch-science.com

 

そして480x320ドットの大型液晶は税込みで2530円。

www.switch-science.com

 

両方あわせて3000円ほどのマシンで、これほどまで遊べるなんてスゴイ!(^-^)

まだまだPicoを使いこなしてる感覚は無く、いまだに「あ、こんな速度で動いちゃうのね」と驚くことが多い。奥が深いぞ、Pico!(^o^)

 

さて、先日からRaspberryPi PicoにArduino BASICを移植してる!

前回のブログの最後にチョロっと書いた、グラフィック命令の追加をしてみる。

もはや移植というよりは拡張と言った方が良いのかも知れない(^^;;

 

追加できるのか検討

まずは想定しているグラフィック画面を定義をしてみる。

 

グラフィック画面の広さは480x320ドットとする

やっぱり画面全体をグラフィック画面として使えるようにしたい。

ただし下の12ドットはステータス表示がされてしまうので、基本は見えない。

CONSOLE命令でも追加して、ステータスのON/OFFでも入れようかな…?

 

カラーは256色にする

これは主にメモリの制限によるスペックダウン。

480x320ドットで65536色を表示させるために必要なメモリは300KB。

RAMが256KBしかないPicoには残念ながら入らない。

そこで、色数を256色に落とす事で必要なメモリは150KBとなる。

カラー番号と実際の色の対応は、MSXの256色モードを参考にさせていただいた。

 

テキスト画面とグラフィック画面は分離

テキストをスクロールさせてもグラフィックには影響が出ないような構造にしたい。

これはPC-8801を想像してもらえれば良いかなーと思う。

グラフィックとテキストの合成処理が必要となるが、これは難しくない。

 

BASICのコマンドは変更ではなく追加

今ある命令を拡張して機能を追加していくのは、いらぬバグを引き起こす可能性もある。

グラフィックを扱う命令は新規追加する方向で命令を考えてみる。

追加する命令はGCLS、PSET、LINEの3つのみ。

余裕があったらCIRCLEやPAINTも検討したい。

 

実装してみる!

方針は決まったので、さあやってみよう!

150KBのメモリ確保は、固定でしてしまえば良いので簡単としても…

やっぱり気になるのは、256色→65536色の変換が現実的な速度で行う事が出来るのだろうか…という事だ。

 

色数が256しかないので、ここは256個のテーブルを生成してメモリに置いておくのが良いかと思う。気をつけないといけないのは、メモリアクセスはキャッシュから外れると極端に遅くなる場合があり、計算で求められるデータはその都度計算した方が速い場合がある。

コードを想像してみよう。

  uint8_t *gvram = グラフィックRAM;
  uint16_t *buff = 変換後の格納バッファ;
  for(int i = 0; i < 横幅; i++)

   *buff++ = gColorTable[*gvram++];

うん、このくらいプログラムが局所的であればキャッシュの効果は期待できそう(^^)

 

出来上がったバッファの上に、テキストデータを合成していく。

現在のBASICは、テキスト文字が変更されると画面が更新される仕組みになっている。この仕組みを上手に利用しようと思う。

 

グラフィックデータが更新されたらテキストデータを更新しましたフラグを立て、テキストを更新するついでにグラフィックも表示するようにした!(意味通じる?^^;)

 

つまり、絵のデータが1ドット変更されたとしても、縦12ドット横480ドット更新されるw

これで現実的な速度が出るのかなぁ…と思ったが、やってみたら全然平気だった!(^^)

f:id:PocketGriffon:20220407201206j:plain

このテストをしていた時に表示していた画面が↑これだった。

矩形の色はBASICで書いたのではなく、C++プログラムで書いている。画面をクリアするとこの画面が出るようにしておき、テキストとの合成も同時にテストした。

 

よし!これでグラフィック画面が追加できそうな見込みが立った!(^o^)

グラフィック画面をRAMに保持する事で、RAMの使用状況は73%となった。

 

BASIC命令の追加

BASICの命令を追加してみるテストとして、まずはフォントデータ幅の切り替え命令を追加してみた。

 FONT

とすればフォントサイズが横8ドットと6ドットが切り替わるだけの簡単な仕様(^^)

f:id:PocketGriffon:20220407213156j:plainf:id:PocketGriffon:20220407213211j:plain

これでいちいちビルドオプションを変更してビルドし直さなくても、動かしながら動的に変更する事ができるようになった。引数もないので簡単実装(^^)

 

 GCLS

グラフィック画面をクリアする命令を追加。引数はなし。

画面全てをクリアするためにはCLS:GCLSと2つ呼ぶ必要がある。

 

 PSET(x, y),color

これで画面上にドットを打つことが出来る。ドットを消すPRESET命令はないが、PSETでカラーに0を指定すればドットは消える。

 

整数の引数を3つ取る命令が存在しなかったため、新たにParserを書くこととなった。なんか構造的にキレイに書けそうな雰囲気があったのだけど、どうにもうまくいかなかったので、べったりなコードを書くことになった(T-T)

f:id:PocketGriffon:20220407214401j:plain

1000個のドットを描画するのに一瞬!速すぎますよ!!(^^;;

そこで雰囲気を出すために、1ドット描くごとにグラフィック画面を更新するようにしたところ、ちゃんと遅くなったw

 

 LINE(x1, y1)-(x2, y2),color

画面に指定色の線を引く。

うーん…このカッコとマイナスのある文法、BASIC特有かなーと思うけれど、Parser書くのが面倒だ(^^; 今回はソースをベタっと書いたけど、もう1つ命令を追加するチャンスがあったらキレイに直そうと思うほどのコードになった(^^;

f:id:PocketGriffon:20220407214546j:plain

LINEの描画は、有名なブレゼンハムのアルゴリズムを採用した。そのおかげもあってか、めちゃくちゃ速い!(^-^)

こちらも線を1本引くたびにグラフィック画面を更新したら、それっぽい表示になった!

 

おわりに

グラフィック命令が使えるようになると、昔テクノポリスという雑誌に載っていたBASICで描くグラフィック画像とかを試したくなる!

そのためにはPAINTが必要だし、プログラムによってはWINDOW / VIEWPORTなどが必要になりそうで大変だなー…。いや、やりませんよ?(^^;;

 

BASICが動くようになってくると、スプライト命令あったらゲーム作れそうだよね、とかPCG機能は簡単に入りそうだな…とか、もういろんな事を考えてしまう!(^^;

終わりがなさそうな気がするので、一旦キリにしようと思う!

 

ではまた次回!(^-^)ノ

RaspberryPi Picoで動くBASIC その1

f:id:PocketGriffon:20220406182738j:plain

先日からRaspberryPi Picoに大きな液晶を取り付けて遊んでいる!

 

液晶を取り付けた当初は、Picoが扱えるメモリサイズに比べて画面の画素数が多くて、果たしてPicoで使い切れるんだろうか…とビビっていたが、思った以上に使えてる!

 

ブログには書いてないがBad Apple!!を動かしてみたりもした!

残念ながら画面更新の速度が1/30秒以上掛かってしまい、画像としてはコマ落ち状態となった。そのため自分の中では移植失敗な位置づけ(T-T)

 

Bad Apple!!以降、もっと液晶サイズを活かす何かが出来ないかと実験を続けていた(^^)

 

気になっていたBASIC

以前、SMART Response XEを使った時、良く出来たBASICを使ったことがあった。

このBASICで作れるプログラムサイズは10KBとそんなに大きくないが、実際に使ってみても10KBを超えるプログラムなんてめったに書かない(^^)

それよりも動的にフォントサイズを変えられたりなど、使いやすいカスタマイズが出来たので便利に使っていた。

 

pocketgriffon.hatenablog.com

元がArduino用のBASICらしいので、すでにM5Stackにも移植されてるのかなぁ…まだだったら移植しようかなぁ…とか、いろいろと考えていた。

 

でも…どの機種でもそうなんだけど、CP/Mを移植してMBASICが動くようになると満足しちゃう。これでいっか…ってな感じで移植作業も後回しになりまくり!(^^;; だってMBASICで困る事ってほぼ無いもんねぇ…(-_-;

 

このままだといつまで経ってもBASICの移植が出来ない!(^^;;

…と思ったので、今回気合をいれてRaspberryPi Picoへの移植をしてみた!

 

移植方針

移植元となるBASICは、SMART Response XEで動いてたプログラムを使う事にする。

github.com

SMART Response XEで実際にプログラムをした事もあるので、機種特有の関数にも見覚えがあるのが有利だ。それに実際に動いてるものが手元にあるのはとても心強い!(^^)

 

BASICの命令は極力サポートするけれど、XEにあったSLEEPやTONEなど本体に依存したものは削除する方向。ムリに削除はせず当面は放置だけど!(^^;

 

キーボードによる入力は限定したものとする。

Picoにキーボードが繋げられるらしいんだけど、USBコネクタからの電源供給の兼ね合いがあり、繋げる方法が良くわからない(T^T) 仕方ないので、Arduino IDEのシリアルモニタから入力を受け付ける事にする。これはリアルタイムの入力はできなくなるという意味でもある。

INKEY$命令はおそらく期待通りの動きにならない。

 

プログラムのセーブはSDカードに行うようにする。Picoに載っているフラッシュをファイルシステムとして使う機能があるが、こちらはサポートしない(XEでは使ってるようだったので削除する方向)。

 

液晶は480x320ドットのパネル専用とするけど、多分ちょっとした変更で別のサイズのパネルで動く気がする。

 

出来ればグラフィックスをサポートしたい。してみたい!希望的!(^^)

 

移植作業!

このBASICはとってもコンパクトに作られている。

ソースファイルはbasic.cpp、host.cpp、そしてinoファイルの3つだけだ!

inoファイル  主に初期化のみ

host.cpp   機種依存の処理

basic.cpp   BASIC本体

 

こんな感じになっているようなので、おそらくhost.cppを中心に改造していけば動くんじゃないかな…と予想を立てる。

 

ここで、私が良くやっている移植方法を書いてみようと思う(^-^)

 

まずは必要と思われるファイルをコピーする。最初は足りなくても良いので、とにかく思いつくファイルをコピーしまくる。

そして空のinoファイル(setupとloopは存在する)を用意して、おもむろにビルド。

#includeのファイルが無かったり、文法的に対応出来ないものが出てくるので、必要に応じてファイルをコピーしたりして1つずつ直して行く。

 

この時点ではプログラムの内部構造などは全く理解していない(^^;;

しかも必要なさそうな箇所は条件コンパイルで強制的に外しちゃったりしているので、ビルドが通ったとしてもまーったく動かない状態だ!

特に表示系や外部ハード(SDカードなど)は作り直しになる可能性が高いので、関数名だけ残してごっそり外してしまうケースが多い。

 

移植作業中、タイマー割り込みで何かをしているコードがある事に気がついた。とりあえずはこのコードも外してしまった(^^;

 

頑張ってビルドが通るようになったら、今度はinoファイルのsetupの中身を書いていく。

オリジナルのファイルを参照しながら、必要な初期化を入れていく。一気に処理を追加してしまうと追いにくくなってしまうので、いつも1つずつ入れていってる(^^;;

基本は小心者よw

 

今回のBASICの移植作業では、初期化が終わってオープニングメッセージが表示されるところまでは、そんな難しくなかった。

f:id:PocketGriffon:20220406200941j:plain

少しプログラムコードを見たところ、画面に表示される情報はscreenBufferというバッファに入る事が分かったので、それを可視化してみる。

いきなり画面表示を組み立てていくのは簡単ではないので、まずはシリアルへ出力(^^)

 

当面はシリアルに表示も出していけばいいかと思ったが、この先のデバッグでシリアルへの出力がかなり埋まるはずなので、早めに表示系は液晶に出してしまう事にした。

CP/Mの時に作った表示系があったので、さくっと移植!

f:id:PocketGriffon:20220406202149j:plain

携帯がモロに写っちゃったね…汗

液晶側にスクリーン情報を出せるようになれば、シリアル側は雑多な情報を垂れ流しても邪魔にはならなくなる(^^

 

BASICプログラムが動かない

さて、BASICは起動したけれど……簡単なプログラムすら動かない事がすぐに判明。

 A=10

これが動かない(^^;;

なんぞ??

これじゃあBASICのプログラムどころの話じゃない(TOT)

 

少しだけコードを追ってみて気がついたのは、ポインタをキャストして値を代入している箇所があった!つまりこんな感じ。

 long *p = (long *)&mem[アドレス];

 *p = value;

これ……アドレスが奇数の場合に良くない事が起こる気がする(^^;

 

世の中には奇数アドレスからのワードアクセスを許すCPUと許さないCPUが存在する。

もしかしたらCPU側の設定とかあるのかも知れないが、私が経験したことがあるのは以下の3パターン。

 問題なくアクセス出来る(ただし遅い)

 データの上下がひっくり返る

 そもそもアクセス出来ない

そしてRaspberryPi Picoは「アクセス出来ない」ようだ。

試しにException Handlerを設定してみたところ、見事に引っかかった!

 

Exceptionを起こしたアドレスが分かれば良いんだけど、残念ながら手元にデバッガがない状態で詳しく調べるのが難しい。

 

随分前になるが、仕事でExceptionを捕まえてPCやレジスタの表示、スタックからのバックトレース情報とかその付近の逆アセンブラを…など楽しげに作っていたら、クライアントから呆れられたのは良い思い出だw

 

止まってる箇所(アドレス)は分からないが、それっぽいコードを発見してるので修正してみる事に。結局、short、long、floatで同様のコードがあり、全部で69箇所を修正した!

 

数が多かったため、1度では修正しきれず、ソース(basic.cpp)を2度3度見直す必要があった。そのおかげと言ってはなんだが、内部の構造はかなり理解出来た(^^)

f:id:PocketGriffon:20220406210645j:plain

この修正でかなりのBASICプログラムが動くようになった!

この時点ではまだセーブは出来ず、全部手入力でプログラムを打ち込んでいた(^^;

 

マンデルブロを動かしてみたい!

BASIC自体が動くようになってきたので、やはり私としてはマンデルブロ集合を動かしてみたくなる!動作速度も見てみたいが、小数点を用いたBASICプログラムが動くのかチェックする意味も兼ねている!

 

マンデルブロ集合のBASICプログラムはそこまで長くはない。

Retro PC Gallery

上記URLにあった写真にBASICプログラムが載っていたので、これを利用させて頂いた。

BASICプログラムを文字列のデータとしてcppファイルに埋めておき、キー入力処理を横取りして1文字ずつキーデータとして返してやれば、BASICでキー入力したのと同じになる。

 

いざ動かそうとしたところ、なんと移植したBASICにはCHR$命令が存在しなかった!(@_@;

あらら……ASC命令はあるのに対となるCHR$命令はないのか…

 

他の命令で代用できないかとパズルをしてみたが、MID$を使って文字列を取り出すなど効率がめちゃ下がる方法しか思い浮かばない(^^;;

先のポインタ修正でBASICの内部構造は理解出来ていたので、思い切ってCHR$命令を追加してみる事にした!

 

内部構造的には、引数がスタックに載る(良くある)構造なため、書くべきプログラムは命令の追加命令の中身、そしてその命令の実行結果として何がスタックに載ったのか、という情報くらい。全体を見渡す感じでコードを理解できていたため、あっさり追加出来てしまった(^-^)

 

よし、これでマンデルブロが動くようになるはずだ!

f:id:PocketGriffon:20220406220643j:plain

お……おぅ…(T-T)

なんか…これは…表示が気になる!(ToT)

やっぱり表示幅は切り替えられるようにしよう。

将来的にはFONT命令とかでサイズ変更出来るようにしたいけど、今は固定でサイズ変更!

f:id:PocketGriffon:20220406221139j:plain

よし、これで見慣れた画面になったね(^^)

 

f:id:PocketGriffon:20220406221625j:plain

ちなみに実行速度はこのくらい。

前回のブログでZ80エミュレータ高速化版で1分26秒(86秒)だったことを考えると、さすがにネイティブBASICは速い!(^^)

 

セーブロード、その他

BASICが安定して動くようになってきたので、そろそろセーブとロードを考える。

SMART Response XE版のソースを見る限り、SDカードを使う場合には、まずはMOUNT、その後LOAD / SAVEをするような運用となっている。

これは……なんでだろ?SDカードの活線挿抜を可能にするための仕組みかな??(-_-;

 

PicoではMOUNT / UNMOUNTせずにSDカードを使えるように修正し、DIR / SAVE / LOAD / DELETEの4つの命令を使えるようにした! よっしゃ、これで便利(^^)

 

その他、タイマー割り込みがカーソルの点滅に使われていたので、これも導入。Picoでタイマー割り込みを使ったことがなかったので、まずは調べるところから開始w

 add_repeating_timer_ms

という関数でタイマー割り込みが使える事が分かった。

この関数でタイマー割り込み時に呼び出されるCallBack(関数)を登録する。

注意点としては、CallBackの戻り値で1を返さないと次が呼び出されない。

 

この先は…

ここまでの作業で、だいぶちゃんと動くようになった!(^-^)

この先はRaspberryPi Pico用にカスタマイズ+拡張をしていく。

 

グラフィック画面を追加出来ないかなーとか考えてるんだけど…(^^)

楽しみはまだまだ続くぜー(^^)

 

ではまた次回!(^-^)ノ

Z80エミュレータを新規開発した!

f:id:PocketGriffon:20220325071612j:plain

ここんところ、ずーっと気になっていたこと……。

それは自作のZ80エミュレータの速度が遅い!…という事(^^;

 

pocketgriffon.hatenablog.com

過去のブログでも嘆いているくらいなので、結構気にしてる!(=_=;

 

この処理が重いの考え方はいろいろあると思うけど、今感じてるのは「自分が想定している以上の速度低下がある」で、それが理解出来ないレベルにまで達しているのだ(T-T)

 

私が使っているZ80エミュレータコアは、私自身が1990年前半にC言語で書いたものが原型となっていて、それをずっとメンテナンスするカタチで使っている。遅いのは紛れもなく自分自身のプログラムが悪いはずだ(ToT)

 

ここ数年で書いた6809CPUエミュレータはARM CPUに特化してカリッカリにチューニングしてあるおかげでかなりパフォーマンスが出るようになっている。それに比較する以上にZ80が遅いなぁ…とか思っていた。

 

今回は意を決してZ80エミュレータプログラムを作り直したお話(^-^)

 

CPUエミュレーション

CPUのエミュレータと聞くと身構えてしまう人がいるかも知れないが、実際にはとても単純な構造でできている。構成としては主に3つかなぁと思う。

 レジスタ

 エミュレータ内で扱えるメモリ

 ハードウェアとのやりとりをするI/O

 

レジスタ

CPU内部にはレジスタと呼ばれるとてもとても小さな変数みたいなものが存在する。これはCPUによって違うため、CPUが変わるごとにエミュレータプログラムを作る事になる。

f:id:PocketGriffon:20220325072948j:plain

Z80の場合は上の写真のような構成になっている。

これをプログラムで表現しようとする場合…

例えばAレジスタは8ビット長なので、

  uint8_t A;

なんてすれば仮想的なレジスタが定義出来る。

 

ただZ80には「8ビット長のAレジスタとFレジスタをくっつけて、16ビット長のAFレジスタとして扱うことができる」というレジスタペアと呼ばれる面倒な仕組みがある。

C言語には共用体という仕組みがあり、コレを利用すればレジスタペアは簡単に実現出来る。

構造体は理解しているけど共用体は知らない…という人は、まずここで躓く(^^;;

 

Z80で面白いなぁ…と思うのは、裏レジスタというレジスタセットがもう1つある事。

メモリよりも高速にアクセス出来るものなので、たくさんある方がプログラムのパフォーマンスが上がりやすい、とても便利なのだ。でもワリと存在自体を忘れがちで、普通にプログラミングしているとほぼ使わない。使う人と使わない人が二分するような気がする(^^)

 

エミュレータ内で扱えるメモリ

f:id:PocketGriffon:20220325093706j:plain

エミュレーションするCPUが使えるメモリを管理する必要がある。Z80の場合は64KBのメモリ空間があり、メモリマップはエミュレーションするマシンに依存する。

このため、メモリアクセス関数はマシンにあわせて作り直す構造にしておく必要がある。

 

MSXの場合はスロット切り替え、PC-8801ならばバンク切り替えなどに対応した、メモリアクセス関数を用意する必要があるだろう。

 

私は1バイト読み込み、1バイト書き込みという2つの関数を別途用意して、これらを利用してエミュレータのメモリへアクセスするようにしている。

速度は速くないがエミュレータの汎用性は保たれる。

 

ハードウェアとのやりとりをするI/O

f:id:PocketGriffon:20220325094009j:plain

Z80はI/Oポートを介して外部のハードウェアとやりとりする仕組みが備わっている。キーボードとかメモリのバンク切り替えとかFDDとか、用途はさまざまだ。

しかもマシンごとにすべて違っている(もっと言えばシリーズ別で違ってたりもする)ので、これもプログラム的には汎用性をもたせる必要がある。

 

エミュレータを作ろうと思った時、CPUのエミュレータを実装するのは簡単なのだが、ハードウェアのエミュレーションを作るのがとても大変だ!エミュレータを作る上での敷居の高さは、95%くらいがこっちの難易度だと思う(T-T)

 

利用してるZ80エミュレータ

上にも書いた通り、今まで使っていたZ80エミュレータは1990年代前半に設計したものだ。当時は68030CPUで動くワークステーション上で開発していた。この頃は「確実に動くもの」を目指していて、実行速度は特に気にしていなかった。

 

それにしても……自分が想像する以上に遅いのがとても気になっていた。プログラマは自分が書いたコードの実行速度は想像が出来る。しかしこのエミュレータはどう考えても遅い。なんでだ??(T-T)

 

そう思って、ながーい事見ていなかった自分のコードを見てみる事に…。そしたら……なんと人の手が加わっていた!しかも実行効率がより悪くなるコードが入っていて、これが想定していない速度低下の原因だと分かった(ToT) 

過去にもプログラムを6行追加されただけで速度が1/3まで落ちるという経験をした事があり、その時にはキャッシュメモリというものを理解していない人が組むとこうなるのか…と思い知らされた(^^;

 

その部分を修正する事で速度低下は回復するだろうな…とは思ったものの、30年くらい使い続けたプログラムを一新するチャンスでもあるな…と思ったので、作り直しを決断!

久しぶりにZ80エミュレータを書くことになった!(^-^)

 

エミュレータの実装

新しいエミュレータを書く上で、いくつか気にした事があった。

 

プログラムサイズをコンパクトに!

昨今ではプログラムがキャッシュメモリに入るかどうかが大きなファクターとなる事が多く、複雑なプログラムであっても全体的に見てサイズが小さい方が速くなるケースがあり得る。

 

特に今回のエミュレータは1つ1つの関数が極単純なので、出来る限りプログラムをまとめて小さくする努力をした。ちなみに6809CPUエミュレータもサイズをコンパクトにする事で、大幅な高速化に成功した経験がある。

 

汎用性を犠牲!

PC-8801のようにCPUが複数入ってるマシンがあるので、プログラム中にCPUが1つという限定をせずに動かすようにしていた。しかし構造体のメンバをアクセスするアドレッシングが足を引っ張るため、少々汚いがグローバル変数をアクセスするようにしてリンク時にアドレスを確定するようにした。

 

CPUが複数ある場合は、グローバル変数に情報をコピーした上でエミュレーションを動かすようにすれば良い。汚い実装だけどここは速度優先だ(T-T)

 

プログラム実行をRAM上で!

f:id:PocketGriffon:20220325101926j:plain

これはRaspberryPi Picoに特化した話だが、プログラムをRAMで実行するようにした。

 

デフォルトのPicoプログラムはFlash上で実行される。Flashからプログラムを読み込まれる際、どうしても時間がかかってしまうはずなので、おそらく少なからずボトルネックになっているのでは?と思ったからだ。

 

しかし実際に試してみると、実行速度はほとんど変わらない事が分かった(ToT)

ボトルネックは別にあるって事なんだと思われる。

 

今は入れてないけれども…

6809エミュレータを書いた際、likely / unlikelyを入れる事でパフォーマンスを上げる事に成功していた。RP2040のコンパイラで有効かどうか不明だが、後ほどコードが固まってきたら入れていきたい(^^)

 

現状の結果

今の時点でのパフォーマンスだけど、大体2.6倍くらいの速度アップになった!

f:id:PocketGriffon:20220325102027j:plainf:id:PocketGriffon:20220325102059j:plain

携帯のストップウォッチで測ってるので正確ではないが、大雑把な数字としてもかなりの違いとなった!

これは嬉しい!(^o^)/

何度も作ってみても毎回思うのだが、エミュレータの開発には根気が必要だ。

ずーっと画面に何も出ない状態でコードを書き続ける必要があり、かつ書くコードは単純なモノばかり。何かまとまったものが動くようになるまでひたすら頑張り続けるのだ(^^;

 

今回は慣れたZ80の実装だったので丸1日頑張ったら動くようになってきたが、一度も使ったことがないCPUだったら数日は掛かる気がする…。

今はまだ8080+αくらいの機能しか入っていないので、これをキレイにZ80まで仕上げていきたい(^^)

 

ではまた次回!(^-^)ノ

 

 

2020.03.25 追記

RAM上でプログラムを実行したけど速度が変わらなかった件。

もしかしたらプログラム自体がCPUのキャッシュに収まるサイズなのかも知れない。気になってmapファイルを見て見たが、エミュレータ全体でも32KBを超えていなかった。

なので「速くならない」のではなく「遅くなりにくい」だけなのかも知れないです!

 

もひとつ(^^)

Retro PC Gallery

↑のページにマンデルブロ集合のベンチマークがあった。

これを見ると1分26秒という速さはZ80の11〜12MHzくらいの速度になるみたい。RaspberryPi Picoを120MHzで動作させている事を鑑みると、1/10の速度はワリと優秀?

コレ以上の大幅アップはありえないのかもなーと思う。

f:id:PocketGriffon:20220325213738j:plain

↑ちなみにPicoを200MHzで動作させてみると、ほぼきっちりクロック数分だけ速度が上がった!(^-^) 120MHzで1分切るのは夢ですねー(^^;;