aburi6800のブログ

コンピュータのプログラミング、ゲームに関するニッチな情報を書いていくブログです。

【MSX】ubuntu20.04でopenMSX+debuggerの環境を構築する

WindowsにはblueMSXというデバッガ機能を内蔵した強力なエミュレータがあるが、Linux(ubuntu)には用意されていない。
そこで、openMSXに用意されているdebuggerを使って、デバッグ環境を構築してみる。
なお、openMSXはリポジトリからのインストールも可能だが、今回はgithubに公開されているソースからビルドする。

openMSXのインストール

githubからcloneする。

$ git clone https://github.com/openMSX/openMSX.git

configureする。

$ cd openMSX
$ ./configure

すると以下の出力がされる。

Using Python: python3
Probing target system...
Creating derived/x86_64-linux-opt/config/probed_defs.mk...
Creating derived/x86_64-linux-opt/config/systemfuncs.hh...

Found libraries:
  ALSA:             version 1.2.2
  GLEW:             no
  libogg:           no
  libpng:           version 1.6.37
  libtheora:        no
  libvorbis:        no
  OpenGL:           version 4.6
  SDL2:             version 2.0.10
  SDL2_ttf:         no
  Tcl:              version 8.6.10
  zlib:             version 1.2.11

Components overview:
  Emulation core:   no
  GL renderer:      no
  Laserdisc:        no
  ALSA MIDI:        yes

Customisable options:
  Install to        /opt/openMSX
  (you can edit these in build/custom.mk)

Please install missing libraries and headers and rerun "configure".

If the detected libraries differ from what you think is installed on this system, please check the log file: derived/x86_64-linux-opt/config/probe.log

このうち、Found Librariesのところでnoと出ているのが不足しているライブラリになる。
追加でインストールする。

$ sudo apt install libglew-dev
$ sudo apt install libogg-dev
$ sudo apt install libtheora-dev
$ sudo apt install libvorbis-dev
$ sudo apt install libsdl2-ttf-dev

make、install。

$ sudo make
$ sudo make install

起動する。
BIOSを適切に配置して設定すると実機と同様にBASICが使えるが、素の状態ではC-BIOSという互換BIOSが起動する。

$ openmsx &

f:id:aburi6800:20210831223109p:plain
C-BIOSでのopenMSXの起動

debuggerのインストール

OpenMSX debuggerにはqt5が必要なのでインストールする。

$ sudo apt install qtbase5-dev qttools5-dev-tools qt5-default

githubからcloneする。

$ git clone https://github.com/openMSX/debugger.git

configureはないので、そのままmake。

$ cd debugger
$ make

するとエラーが出る。

Compiling DebuggerForm.cpp...
src/DebuggerForm.cpp: In member function ‘void DebuggerForm::createForm()’:
src/DebuggerForm.cpp:624:45: error: ‘SkipEmptyParts’ is not a member of ‘Qt’
  624 |   QStringList s = list.at(i).split(" ", Qt::SkipEmptyParts);
      |                                             ^~~~~~~~~~~~~~
src/DebuggerForm.cpp: In member function ‘void DebuggerForm::setDebuggables(const QString&)’:
src/DebuggerForm.cpp:1454:38: error: ‘SkipEmptyParts’ is not a member of ‘Qt’
 1454 |  QStringList l = list.split(" ", Qt::SkipEmptyParts);
      |                                      ^~~~~~~~~~~~~~
make: *** [build/main.mk:326: derived/obj/DebuggerForm.o] Error 1

表示されたとおり、Qt::SkipEmptyPartsqtにないよ、と言われている。
調べてみると現在のバージョンでは廃止になっているようで、代わりにQString::SplitBehavior::SkipEmptyPartsを使う模様。
エラーに出ているsrc/DebuggerForm/cppの624行目と1454行目の該当箇所を書き換えて、改めてmake。

2021/9/9 追記
z88dkの.mapを読んでdisassemblerウィンドウにラベルを表示するパッチを適用する。
以下からzipを取得して展開。
[openmsx-debugger で z88dk の -m 形式の .map symbol を読ませる hack (github.com)]
中にある0001-add-z88dk-symbol-read-hack.patchopenmsx-debuggerディレクトリに配置し、以下コマンドを実行。
$ patch -p1 < 0001-add-z88dk-symbol-read-hack.patch
@h1romas4さん、ありがとうございました!

無事にコンパイルできたら、openmsxと同じ場所にコピーしておく。(make installは用意されていない)

$ sudo cp -p derived/bin/openmsx-debugger /usr/local/bin

起動してみる。

$ openmsx &
$ openmsx-debugger &

起動したデバッガーウィンドウのメニューから[System]-[Connect]を選ぶか、[Ctrl]+[C]を押す。

f:id:aburi6800:20210831223214p:plain
openMSXへの接続
メモリやレジスタなどの情報が表示されれば、ひとまず成功。
f:id:aburi6800:20210831223245p:plain
openMSXへの接続後の状態

補足

この記事の内容は、ubuntu20.04、及びWindows10+WSL2のubuntu20.04で実施済です。
また、記事執筆時点(2021/8/31)でのソース及びライブラリの状態での結果ですので、今後はエラーが解消していたり、新しいエラーが発生する可能性があります。
この記事を参考に作業される場合は、上記をご理解の上、行ってください。

【MSX】GALAXY ZONE

これまでのMSX-BASICの研究結果をもとに、ちょっとしたゲームを作ってみました。
以下のURLで遊べます。
webmsx.org

遊び方

  • しばらく待って、タイトルが出たらSPACE or Aボタンでスタート。

    f:id:aburi6800:20210815133037p:plain
    タイトル画面

  • カーソルキー or コントローラの左右で移動、スペースキーで前進して、ミサイルを動かします。

  • ステロイドにぶつからないように、画面上部のマザーシップを破壊してください。

    f:id:aburi6800:20210815133215p:plain
    ゲーム画面

  • 以下のいずれかの条件でミスになります。

    1. ステロイドに当たった
    2. 燃料がなくなった
    3. マザーシップに当てられなかった
  • 3回ミスするとゲームオーバーです。

ソースなど

githubでもソースとディスクイメージを公開しています。
詳細については、こちらを参照してください。
github.com

ではでは。

【MSX】MSX-BASICの研究(2) PRINTとVPOKEの性能比較

この記事について

MSXでキャラクタを画面表示する際には、PRINTを使うよりはVPOKEを使う方が速いのだが、ではどれくらい速度差があるのだろうか?
具体的に数値として知らなかったので、調べてみました。

テスト方法

今回のテスト方法は、以下の方針としています。

  • WebMSXで実施。
  • 一般的に使用される、SCREEN 1、WIDTH 32、DEFINT A-Zで初期化した状態で実行する。
  • 文字表示を1,000回繰り返し、経過時間を測る。(TIMEを使用)

1文字表示編

パターン1:PRINT(セミコロンなし)

プログラムリスト

10 SCREEN 1:WIDTH 32:DEFINT A-Z
20 TI=TIME
30 FOR I=0 TO 999:LOCATE 0,0:PRINT "A":NEXT
40 LOCATE 0,5:PRINT "TIME="; TIME-TI

結果

f:id:aburi6800:20210811013101p:plain
パターン1の実行結果

パターン2:PRINT(セミコロンあり)

プログラムリスト

10 SCREEN 1:WIDTH 32:DEFINT A-Z
20 TI=TIME
30 FOR I=0 TO 999:LOCATE 0,0:PRINT "A";:NEXT
40 LOCATE 0,5:PRINT "TIME="; TIME-TI

結果

f:id:aburi6800:20210811013357p:plain
パターン2の実行結果

パターン3:VPOKE

プログラムリスト

10 SCREEN 1:WIDTH 32:DEFINT A-Z
20 TI=TIME
30 FOR I=0 TO 999:VPOKE &H1800,&H41:NEXT
40 LOCATE 0,5:PRINT "TIME="; TIME-TI

結果

f:id:aburi6800:20210811013510p:plain
パターン3の実行結果

比較結果

パターン 概要 処理時間
1 PRINT(セミコロンなし) 355
2 PRINT(セミコロンあり) 305
3 VPOKE 116

VPOKEPRINTの2倍以上速いという結果になりました。
また、PRINTも、セミコロンをつけることで若干高速化されています。

2×2キャラクタ表示編

パターン4:PRINT(セミコロンなし)

プログラムリスト

10 SCREEN 1:WIDTH 32:DEFINT A-Z
20 TI=TIME
30 FOR I=0 TO 999:LOCATE 0,0:PRINT "マイ":LOCATE 0,1:PRINT "コン":NEXT
40 LOCATE 0,5:PRINT "TIME="; TIME-TI

結果

f:id:aburi6800:20210811013748p:plain
パターン4の実行結果

パターン5:PRINT(セミコロンあり)

プログラムリスト

10 SCREEN 1:WIDTH 32:DEFINT A-Z
20 TI=TIME
30 FOR I=0 TO 999:LOCATE 0,0:PRINT "マイ";:LOCATE 0,1:PRINT "コン";:NEXT
40 LOCATE 0,5:PRINT "TIME="; TIME-TI

結果

f:id:aburi6800:20210811013907p:plain
パターン5の実行結果

パターン6:VPOKE

プログラムリスト

10 SCREEN 1:WIDTH 32:DEFINT A-Z
20 TI=TIME
30 FOR I=0 TO 999:VPOKE&H1800,&HCF:VPOKE&H1801,&HB2:VPOKE&H1820,&HBA:VPOKE&H1821,&HDD:NEXT
40 LOCATE 0,5:PRINT "TIME="; TIME-TI

結果

f:id:aburi6800:20210811014019p:plain
パターン6の実行結果

比較結果

パターン 概要 処理時間
4 PRINT(セミコロンなし) 733
5 PRINT(セミコロンあり) 633
6 VPOKE 362

1文字表示の時と比べての処理時間は、PRINTは約2倍強ですが、VPOKEは約3倍強ほどになっています。
これは、プログラムリストの通り、キャラクタの数だけVPOKEする(=4回実行する)必要があるためですが、それを差し引いても、PRINTよりは速いですね。
通常、ゲームで表示するパターンは2×2キャラクタ(16×16ドット)が多いと思われるため、なるべくVPOKEを使うことで高速化が見込めると思います。

余談

なお、VPOKEする値を10進数で書いて計測もしてみたのでが、16進数の時とほとんど変わりませんでした。
MSXのBASICプログラムでは、16進数、10進数のどちらで書いても良いと思います。


※2021/8/11追記: エスケープシーケンスで、1回のPRINT文で2×2キャラクタを表示した場合の処理速度を計測してみました。

パターン7:PRINT(エスケープシーケンス使用)

プログラムリスト

10 SCREEN 1:WIDTH 32
20 TI=TIME
30 FOR I=0 TO 999:LOCATE 0,0:PRINT "マイ"+CHR$(29)+CHR$(29)+CHR$(31)+"コン";:NEXT
40 LOCATE 0,5:PRINT "TIME="; TIME-TI

CHR$(29)がカーソル左移動、CHR$(31)がカーソル下移動になります。

処理結果

f:id:aburi6800:20210811091619p:plain
パターン7の処理結果

結果、961とPRINT2回より遅いという結果になりました。
MZとかだと速かった気がしたんですけど、MSXだと結構遅かったのですね…。


関連記事

【MSX】MSX-BASICの研究(1) 条件分岐ロジック - aburi6800のブログ

【MSX】MSX-BASICの研究(1) 条件分岐ロジック

この記事について

MSX-BASICでプログラムを書いていて思い出すのが、昔ベーマガでDr.Dが「論理式を使うのじゃ」と口を酸っぱくして言っていたこと。 確かに論理式を使うとIF文は無くなるが、しかし、論理式は本当に速いのだろうか?
というのも、ネットでBASICの高速化テクニックを探していたところ、ON〜GOTO〜を使うほうが速い、という記事を見つけたためで、本当はどれが速いのだろうか?と考えた次第。
それじゃあ実際に比較してみようか、というのがこの記事の趣旨になります。

今の時代にどれだけの人が必要としている情報なのかわかりませんが(汗

※(2021/8/9)パターン4として、移動量を配列変数に定義した場合をテストしてみました。
記事の最後に追記しています。

テスト方法

今回のテスト方法は、以下の方針としています。

  • WebMSXで実施。
  • 一般的に使用される、SCREEN 1、WIDTH 32、DEFINT A-Zで初期化した状態で実行する。
  • ロジックは、実際の実装を考慮して4方向の判定処理とする。
  • カウントダウン終了から画面右端までの移動時間を測る。

パターン1

素直に入力値をIF文でひとつひとつ判定していくコードです。

プログラムリスト

100 ' *******************************
110 '   CHARACTER MOVE LOGIC TEST 1
120 ' *******************************
130 ' *** COMMON INITIALIZE ***
140 SCREEN 1:WIDTH 32:DEFINT A-Z
150 LOCATE 0,0:FOR I=3 TO 1 STEP-1:PRINT I;:FOR J=0 TO 999:NEXT J,I:PRINT " START"
160 X=0:Y=10:TM=TIME
200 ' *** LOGIC ***
210 S=STICK(0):VX=0:VY=0
220 IF S=1 THEN VY=-1:GOTO 260 
230 IF S=3 THEN VX=1:GOTO 260
240 IF S=5 THEN VY=1:GOTO 260
250 IF S=7 THEN VY=-1
260 LOCATE X,Y:PRINT " ";:X=X+VX:Y=Y+VY:LOCATE X,Y:PRINT "A";
270 IF X<32 GOTO 200
300 ' *** RESULT *** 
310 LOCATE 0,1:PRINT "TIME="; TIME-TM
320 END

実行結果

f:id:aburi6800:20210808125512p:plain
パターン1の実行結果

パターン2

ON〜GOTO〜で分岐させるコードです。

プログラムリスト

100 ' *******************************
110 '   CHARACTER MOVE LOGIC TEST 2
120 ' *******************************
130 ' *** COMMON INITIALIZE ***
140 SCREEN 1:WIDTH 32:DEFINT A-Z
150 LOCATE 0,0:FOR I=3 TO 1 STEP-1:PRINT I;:FOR J=0 TO 999:NEXT J,I:PRINT " START"
160 X=0:Y=10:TM=TIME
200 ' *** LOGIC ***
210 S=STICK(0)
220 ON S+1 GOTO 270,230,270,240,270,250,270,260,270
230 VX=0:VY=-1:GOTO 270
240 VX=1:VY=0:GOTO 270
250 VX=0:VY=1:GOTO 270
260 VX=-1:VY=0:GOTO 270
270 LOCATE X,Y:PRINT " ";:X=X+VX:Y=Y+VY:LOCATE X,Y:PRINT "A";
280 IF X<32 GOTO 200
300 ' *** RESULT *** 
310 LOCATE 0,1:PRINT "TIME="; TIME-TM
320 END

実行結果

f:id:aburi6800:20210808125735p:plain
パターン2の実行結果

パターン3

論理式を使ったコードです。

プログラムリスト

100 ' *******************************
110 '   CHARACTER MOVE LOGIC TEST 3
120 ' *******************************
130 ' *** COMMON INITIALIZE ***
140 SCREEN 1:WIDTH 32:DEFINT A-Z
150 LOCATE 0,0:FOR I=3 TO 1 STEP-1:PRINT I;:FOR J=0 TO 999:NEXT J,I:PRINT " START"
160 X=0:Y=10:TM=TIME
200 ' *** LOGIC ***
210 S=STICK(0)
220 VX=(S=7)-(S=3)
230 VY=(S=1)-(S=5)
240 LOCATE X,Y:PRINT " ";:X=X+VX:Y=Y+VY:LOCATE X,Y:PRINT "A";
250 IF X<32 GOTO 200
300 ' *** COMMON RESULT *** 
310 LOCATE 0,1:PRINT "TIME="; TIME-TM
320 END

実行結果

f:id:aburi6800:20210808125608p:plain
パターン3の実行結果

比較結果

結果は以下のようになりました。

パターン 処理方法 処理時間
1 IF文のみ 56
2 ON〜GOTO〜 51
3 論理式 59

総括

ON〜GOTO〜で処理するのが一番速いですね。実際にテストしていても、体感できるくらいに違いました。
また、パターン3の論理式がパターン1のIF文のみより遅かったという結果は、ちょっと意外でした。
しかし、パターン1のプログラムを見ていただけるとわかりますが、IF文で判定とは言え、条件に合致したときに他のIF文に入らないようにGOTOで飛ばしていますので、速くなっていたのでしょう。
論理式ではすべての値を評価する必要があるので、全てのIF文を実行しているのと同じことになっているのだと考えます。

実際、パターン1で各IF文のGOTOを書かなかった場合、処理時間は64と最も遅い結果になっていました.。

プログラムで条件分岐書く時は、ON〜GOTO〜で書けるように変数の値も考えると良いですね。

また気になる事があったら、検証してみたいと思います。


※2021/8/9 追記

パターン4

入力に対する各方向への移動量を配列変数に定義したコードです。

プログラムリスト

100 ' *******************************
110 '   CHARACTER MOVE LOGIC TEST 4
120 ' *******************************
130 ' *** COMMON INITIALIZE ***
140 SCREEN 1:WIDTH 32:DEFINT A-Z
142 DIM VX(9),VY(9):FOR I=0 TO 8:READ VX(I),VY(I):NEXT I
144 DATA 0,0,0,-1,0,0,1,0,0,0,0,1,0,0,-1,0,0,0
150 LOCATE 0,0:FOR I=3 TO 1 STEP-1:PRINT I;:FOR J=0 TO 999:NEXT J,I:PRINT " START"
160 X=0:Y=10:TM=TIME
200 ' *** LOGIC ***
210 LOCATE X,Y:PRINT " ";:S=STICK(0):X=X+VX(S):Y=Y+VY(S):LOCATE X,Y:PRINT "A";
220 IF X<32 GOTO 200
300 ' *** COMMON RESULT *** 
310 LOCATE 0,1:PRINT "TIME="; TIME-TM
320 END

実行結果

f:id:aburi6800:20210809132336p:plain
パターン4の実行結果

比較結果

パターン4を含むと、以下の結果になりました。

パターン 処理方法 処理時間
1 IF文のみ 56
2 ON〜GOTO〜 51
3 論理式 59
4 配列変数 43

配列変数が抜き出て速いですね。
パターン1や2にあるGOTOが無いのでこの結果になったのだと考えます。
BASICはインタプリタ言語なので、命令数が1つ増えただけでも処理速度に影響するというのは認識ありましたが、実際に結果を見ると相当違いますね。

なお、このテストでは、座標の上限値/下限値を1方向しか判定していないので、実際にゲームに組み込んだ場合の性能とは異なります。
あくまで指標として捉えていただけると幸いです。

【MSX】GitHubに置いた.dskファイルをWebMSXで実行する方法

今日書いたこちらの記事の補足です。

aburi6800.hatenablog.com

最初、WebMSXでGitHubに置いた.dskファイルを読めなかったので、フリーのWebサーバに.dskファイルを置いていたのですが、URLパラメタの指定方法を見直したらGitHubの.dskファイルでも実行できましたので、書いておきます。

具体的には、以下のように指定します。

https://webmsx.org/?MACHINE=MSX1J&DISKA_URL=<対象の.dskファイルのURL>&FAST_BOOT

「DISKA_URL」パラメタで与えるのがミソです。
なお、最後の「FAST_BOOT」は気持ち起動が速くなるようなので付けていますが、任意です。(この指定の仕方でいいのか自信がない…)

そして、この「DISKA_URL」に与える「対象の.dskファイルのURL」ですが、GitHubで対象の.dskファイルのページにある「Download」ボタンのURLになります。
このボタンを右クリックした「URLをコピー」することで取得したURLを指定してください。
f:id:aburi6800:20210802001812p:plain

他、多数のオプションを指定できますが、詳細は以下のGitHubリポジトリのドキュメントを参照してください。

github.com

ちなみに、使用する.dskファイルがあるリポジトリは、publicにしておいてくださいね。

ではでは。

【MSX】BLOCK BREAKER

唐突ですが、MSX-BASICの練習で、ブロック崩しゲームを作ってみました。
(ボールの挙動が若干怪しいのと、パドルの移動にイライラしますが…)

以下のURLで遊べます。
(※GitHubの.dskをWebMSXに直接渡せたので、URLを変更しました)

webmsx.org

しばらく待って、タイトルが出たらSPACE or Aボタンでスタート。

f:id:aburi6800:20210801174251p:plain
タイトル画面

カーソルキー or コントローラの左右でパドルを動かしてボールを落とさないようにして、ブロックを壊してください。

f:id:aburi6800:20210801174325p:plain
ゲーム画面

ボールを3回落とすとゲームオーバーです。

f:id:aburi6800:20210801174354p:plain
ゲームオーバー画面

githubでもソースとディスクイメージ、某雑誌風のドキュメントを公開しています。 詳細については、こちらのドキュメントを参照してください。

github.com

このプログラムを元にアセンブラ化していきたいと思っていますが、小数点の計算をどうするかな…

ではでは。

【git】GithubにSSH接続する

Githubリポジトリへのアクセスは、主にhttpsSSHがある。
一度cloneするだけであればhttps接続で問題ないが、GitHubではSSH接続が推奨されており、アクセスするたびにユーザー名とアクセストークンを入力することも不要になるため、普段から利用するリポジトリへのアクセスはSSHが望ましい。
このSSHの設定方法については既に多数の記事が存在するが、ようやく自分の中で手法が固まったので、まとめ直したものを記載する。

0. SSHキー生成の準備

基本的に、1つの端末に1つ作成するイメージで良いと思う。
Windowsの場合は自分のホームディレクトリに.sshフォルダを作成後、スタートメニューからGit Bashを起動する。
Linuxの場合は、ホームディレクトリでそのまま作業を行う。

以下のコマンド実行例については、WindowsのGit bashを使ったものになります。

1. SSHキー生成コマンド

以下のコマンドを実行する。

$ ssh-keygen -t rsa -C <メールアドレス>

作成先のディレクトリの確認。
デフォルトで問題ないため、そのままEnterを押す。

Enter file in which to save the key (/c/Users/hitoshi/.ssh/id_rsa):

作成先に既に作成されている場合は以下のメッセージが表示される。
上書きして良いならyを入力する。

/c/Users/aburi/.ssh/id_rsa already exists.
Overwrite (y/n)? 

続いてパスフェーズの入力を求められる。
何も入力せずEnterを入力する。

Enter passphrase (empty for no passphrase):
Enter same passphrase again:

パスフェーズを設定するとVS Codeからのpush/pull操作が上手くいきませんでした。
回避方法があると思われますが、個人利用であれば未設定で構わないと思います。

以下のメッセージが表示され、公開鍵が生成される。

Your identification has been saved in /c/Users/hitoshi/.ssh/id_rsa.
Your public key has been saved in /c/Users/hitoshi/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:<暗号化文字列>
The key's randomart image is:
+---[RSA 2048]----+
|    +o*o         |
|   . *.=.        |
|    O o          |
|   + = =         |
| .. * o S        |
|+..+ + = +       |
|==o . + =        |
|#... ..o..       |
|@O   ... E       |
+----[SHA256]-----+

後半のランダムアートの内容は、生成するごとに変わります。

2. 作成結果確認

.sshディレクトリにid_rsa.pubが存在していれば成功。

$ ll
total 6
-rw-r--r-- 1 hitosh i 197121 1671 2月   3 01:51 id_rsa
-rw-r--r-- 1 hitoshi 197121  401 2月   3 01:51 id_rsa.pub
-rw-r--r-- 1 hitoshi 197121  405 2月   3 01:35 known_hosts

3. Githubに公開鍵を登録

生成した公開鍵の内容をコピーする。
id_rsa.pubテキストエディタで開き中身をコピーするか、以下でコンソールに出力したものをコピーする。

$ cat ~/.ssh/id_rsa.pub

Git Bashでは以下コマンドでファイルの内容をクリップボードにコピーできるため、楽で確実です。

$ clip < ~/.ssh/id_rsa.pub

githubのアカウントの「Settings」ページを開き、「SSH and GPG keys」を選択、「SSH keys」の右側にある「New SSH key」ボタンを押す。
名前は適当に、「<端末名>@<OS名>」のようにすると良いかもしれない。
f:id:aburi6800:20210725121553p:plain

4. GithubとのSSH接続確認

接続の確認は以下で行う。

$ ssh -T git@github.com

成功すると以下のメッセージが表示される。

Hi <ユーザー名>! You've successfully authenticated, but GitHub does not provide shell access.

5. トラブルシューティング

失敗した場合は、以下を確認する。
1. そもそもネットワークに接続されているか。wifiや有線LANの接続を確認する。
2. Githubに登録したSSH公開鍵の内容。一度削除して登録しなおしてみる。
3. ssh公開鍵の作成内容。暗号化方式が誤っていないかなど。一度.sshディレクトリ内のファイルを削除し、再度作り直してみる。