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用にカスタマイズ+拡張をしていく。

 

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

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

 

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