YouTube で大流行した Nyan Cat を MSX-BASIC で作ってみました。
……はい。MSX-BASIC です。マシン語とか C 言語とかではなく。
BASIC コンパイラも使わない、普通の BASIC です。
CPU は Z80 です。(R800 モードでも正常に動作します)
画像部分は 2014 年 4 月までには作ってあったので、
そこから BASIC で音楽付になるまで(2024年12月)
10 年半も要してしまいました……
シャープポケコン PC-G850 シリーズ・IchigoJam BASIC 版もあります。
データファイルもあるので、ディスクイメージで実行しています。
MSX-BASIC で、とは記載していますが……BASIC プログラムはこれがすべてです。
100 COLOR 15,1,1
110 SCREEN 3
120 DEFINT A-Z
130 POKE &HFAFC,PEEK(&HFAFC) OR 8
140 FOR I=0 TO 15
150 READ R,G,B
160 COLOR=(I,R,G,B)
170 NEXT I
180 BLOAD"NYAN1.DAT",S
190 BLOAD"NYAN2.DAT",S
200 BLOAD"NYANYANY.DAT",S
210 PLAY"T144L16V11"
220 PLAY"O5EFGR16R8EF"
230 PLAY"GO6CDEDO5BO6CR16"
240 PLAY"O5GR16EFGR16R8"
250 PLAY"O6DO5BO6CDFEFG"
260 IF PLAY(0)=-1 GOTO 260
270 P=&HA000
280 V=0
290 ON INTERVAL=4 GOSUB 430
300 INTERVAL ON
310 SOUND 8,11
320 SOUND 9,8
330 SOUND 10,8
340 TIME=0
350 FOR S=0 TO 5
360 D=VPEEK(P)
370 SOUND S,D
380 P=P+1
390 NEXT S
400 IF P=&HA600 THEN P=&HA000
410 IF TIME<6 GOTO 410
420 GOTO 340
430 VDP(4)=8+V
440 V=(V+1) MOD 12
450 RETURN
460 DATA 0,0,0, 0,0,0, 0,1,2, 6,1,0
470 DATA 7,4,1, 7,6,1, 2,6,1, 1,4,7
480 DATA 3,1,7, 7,5,4, 6,1,6, 7,4,7
490 DATA 0,0,7, 6,4,4, 4,4,4, 7,7,7
BASIC プログラムには画像データがなく、音楽も PLAY がちょっとしかありません。
DATA で持っているのは COLOR 0~15 のパレットだけです。
100 COLOR 15,1,1
110 SCREEN 3
120 DEFINT A-Z
130 POKE &HFAFC,PEEK(&HFAFC) OR 8
140 FOR I=0 TO 15
150 READ R,G,B
160 COLOR=(I,R,G,B)
170 NEXT I
180 BLOAD"NYAN1.DAT",S
190 BLOAD"NYAN2.DAT",S
200 BLOAD"NYANYANY.DAT",S
460 DATA 0,0,0, 0,0,0, 0,1,2, 6,1,0
470 DATA 7,4,1, 7,6,1, 2,6,1, 1,4,7
480 DATA 3,1,7, 7,5,4, 6,1,6, 7,4,7
490 DATA 0,0,7, 6,4,4, 4,4,4, 7,7,7
SCREEN 3 で、オリジナルに似せるためにパレットを使っていますが、
それだけではなくて、ポイントなのが 130 の POKE。
SCREEN 3 だと MSX1 時代の VDP TMS9918 のアドレス合わせて
0~&H3FFF になっているのですが、
これをなくして &H4000 以降も使えるようにしています。
この広げた VRAM に画像データと音楽データを BLOAD,S で入れています。
画像データが NYAN1.DAT と NYAN2.DAT に分かれているのは、
&H4000 毎の考慮で分割してあります。(&H4000 で SCREEN 3 の 8 枚分)
NYANYANY.DAT は音声データです。
MSX2 VRAM 128K が主流となり、販売もフロッピーディスクが多くなった頃は、
4 ページ使用できる SCREEN 5 を用いて、表示用 1 ページ以外の 3 ページに
各データをフロッピーディスクのファイルから VRAM へ読み込んで、
転送するテクニックが用いられていました。
280 V=0
430 VDP(4)=8+V
440 V=(V+1) MOD 12
450 RETURN
VDP 4 番の変更でパターンジェネレータテーブル、
いわゆる SCREEN 3 の画面を表示するアドレスを変えています。
VDP(4)=8 で &H4000、9 で &H4800、……と &H800 毎に指定できます。
これを用いて &H4000~&H9FFF に 12 枚の SCREEN 3 画像を入れて、
順番にパラパラアニメ状態で表示させています。
画像表示がこれだけの処理なので、BASIC でも素早く切り替えられます。
MSX2 には SET PAGE がありますが、そんな感じに使えるわけです。
440 V=V+1:IF V=12 THEN V=0
440 は上記と同じ処理です。MOD は割り算のあまり。
12÷12=1 あまり 0 で 11 の次は 0 になります。
290 ON INTERVAL=4 GOSUB 430
300 INTERVAL ON
ON INTERVAL GOSUB で割り込みを入れて処理しています。
210 PLAY"T144L16V11"
220 PLAY"O5EFGR16R8EF"
230 PLAY"GO6CDEDO5BO6CR16"
240 PLAY"O5GR16EFGR16R8"
250 PLAY"O6DO5BO6CDFEFG"
260 IF PLAY(0)=-1 GOTO 260
前奏は画面が黒い状態なので、 PLAY で再生できています。
再生が終わるまで待っているのが 220 の IF PLAY(●) です。
PLAY(1)~PLAY(3) でチャンネル毎での確認もできます。
270 P=&HA000
310 SOUND 8,11
320 SOUND 9,8
330 SOUND 10,8
340 TIME=0
350 FOR S=0 TO 5
360 D=VPEEK(P)
370 SOUND S,D
380 P=P+1
390 NEXT S
400 IF P=&HA600 THEN P=&HA000
410 IF TIME<6 GOTO 410
420 GOTO 340
テンポのはやい曲で画像切替も短時間なので
PLAY では MSX turbo R でも ウェイトが発生してしまい、
ON INTERVAL GOSUB でもうまく動いてくれませんでした。
そこで、音楽を SOUND で行うようにして、
処理を軽減させるために SOUND の値で音声データを扱い、
VRAM に入れて、常時タイミングで読み出して SOUND を変える事で
音の周波数を変えて音色を出す、という方法をとっています。
音楽データは VRAM の &HA000~&HA5FF となっています。
幸い Nyan Cat の音楽で採用されている Nyanyanyanyanyanyanya! は
最短でも 16 分音符・休符になっていて、その間隔でデータを入れていて、
BASIC で処理しきれるよう、SOUND 0~5 の値を直接入れてあります。
ループで行っているのは VPEEK で読み込んで SOUND に入れてるだけです。
テンポ調整は 340~410 の TIME です。
メインルーチンの音楽再生を TIME でタイミングをあわせていて、
画像も ON INTERVAL GOSUB でタイミングをあわせているので、
CPU の異なる Z80 でも R800 でも正常に動作するようにしてあります。
| \ | O1 | O2 | O3 | O4 | O5 | O6 | O7 | O8 |
| C | D5D | 6AF | 357 | 1AC | 0D6 | 06B | 035 | 01B |
| C# | C9C | 64E | 327 | 194 | 0CA | 065 | 032 | 019 |
| D | BE7 | 5F4 | 2FA | 17D | 0BE | 05F | 030 | 018 |
| D# | B3C | 59E | 2CF | 168 | 084 | 05A | 02D | 016 |
| E | A9B | 54E | 2A7 | 153 | 0AA | 055 | 02A | 015 |
| F | A02 | 501 | 281 | 140 | 0A0 | 050 | 028 | 014 |
| F# | 973 | 4BA | 25D | 12E | 097 | 04C | 026 | 013 |
| G | 8EB | 476 | 23B | 11D | 08F | 047 | 024 | 012 |
| G# | 86B | 436 | 21B | 10D | 087 | 043 | 022 | 011 |
| A | 7F2 | 3F9 | 1FD | 0FE | 07F | 040 | 020 | 010 |
| A# | 780 | 3C0 | 1E0 | 0F0 | 078 | 03C | 01E | 00F |
| B | 714 | 38A | 1C5 | 0E3 | 071 | 039 | 01C | 00E |
例えば O4C は 1AC となっています。16 進数(&H~)の値です。
SOUND 0 に下位 2 文字なので AC、SOUND 1 に上位 1 文字なので 1 を入れます。
SOUND 0,&HAC
SOUND 1,&H1
SOUND 8,11
実際にタイレクトモードで上を入れて試してみて下さい。
SOUND 8 が音量です。PLAY"V1104C" の音が出続けます。
このまま SOUND 0 と SOUND 1 の値を上の表を元に入れてみましょう。
SOUND 8,0 または CTRL+G で BEEP 音を出して止められます。
同様にチャンネル 2 が SOUND2・3 で音程、SOUND 9 で音量、
チャンネル 3 が SOUND 4・5 で音程、SOUND 10 で音量となっています。
なお、SOUND 8~10 を 16 にした場合、
SOUND 11~13 で設定したエンベローブが有効になります。
ただし チャンネル 1 だと SOUND 0 と SOUND 1 で周波数を設定するのですが、SOUND 0 設定から SOUND 1 を設定する間に時間が空くので
これにより異音が発生してしまっています。
しかし命令を増やすと Z80 では何だかの影響があり、BASIC の限界です。
機械語・マシン語や C 言語などを用いて PSG で音を出す場合、
SOUND と同じ値で設定を行うので、この辺の仕組みがわかれば
機械語・マシン語や C 言語でも音を入れる事ができるでしょう。
SOUND のノイズについては 1 分タイマー風船 で使用しているので
そちらのページで触れています。
z88dk のモノクロスプライトを用いた Nyan Cat です。
画像のみですが、Z80 のプラットフォームで動作し、
MSX では SCREEN 3 でちょうど良く表示できます。
ただし、MSX によるモノクロスプライトの線画は
VDP データへの変換処理が入るので遅いです。
