0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PanCake の !RESUP! で読み込まれるデータの仕様を調べてみた

Posted at

IchigoCake 版の PanCake には、CakeRes と呼ばれる EEPROM からリソースを取り込んで利用する !RESUP! コマンドがある。

執筆時点において、上記リンク先の !RESUP! コマンドの説明には

00 で PanCake デフォルトのリソースへ変更します。

と書かれているが、これは真っ赤な嘘である。
引数にかかわらず、!RESUP! コマンドは CakeRes からリソースを取り込む処理を行い、このコマンドでデフォルトのリソースに戻すことはできない。

しかし、これに対応する CakeRes は、今のところ販売終了した CakeRes×レトロゲームズ しか見つかっていない。
これでは、せっかくの素敵な機能が活かせないではないか。

そこで、今回は読み込まれる EEPROM のデータとリソースの関係を調べ、PanCake の !RESUP! コマンドで使える EEPROM を自作できるようにする。

これまでの調査結果

以前書いた記事
【注意喚起】IchigoCake の PC !RESUP! 00 の効果は「デフォルトのリソースへ戻す」ではない #IchigoJam - Qiita
より、CakeRes について以下のことがわかる。

  • 「CakeRes×レトロゲームズ」では、EEPROM S-24CM01CI-J8T1U4 が用いられている
    • 同 EEPROM の秋月電子通商における販売コードは 111170
    • 7ビットアドレスは、!RESUP! 00 用が 0x50、!RESUP! 01 用が 0x51
  • EEPROM の読み込みは、I2C により以下のように行われる
    1. 読み込むアドレスを、2バイトビッグエンディアンで EEPROM に送信する
    2. ストップコンディションを送信する
    3. EEPROM からデータを 256 バイト連続で読み込む
  • アドレス 0x0000 ~ 0x9fff の範囲が読み込まれる

さらに、データとリソースの関係について、以下のことが推測できる。

  • 読み込まれたデータのどこかの部分は、起動時の画面 (すなわち、PANCAKE IMAGE 00 で表示される画像?) として用いられる
  • このとき、(少なくともある程度の範囲について) 連続したバイトの列が画面上で連続して用いられる
  • 同じバイトの中では、下位ニブル (4ビット) が左側の色、上位ニブルが右側の色として用いられる

リソースの数と容量に基づく構造の仮説

PanCake では、以下のリソースが定義されている。

  • IMAGE (80×45ピクセルの画面全体に表示する画像) 8枚
  • SPRITE (8×8ピクセルの画像) 176 (0xb0) 枚
  • 組み込みMML 4個

画像は2ピクセルを1バイトで表すようなので、1枚あたり

  • IMAGE:80×45÷2=1800 バイト
  • SPRITE:8×8÷2=32 バイト

のはずである。
また、PANCAKE MUSIC SCORE で指定できるMMLが64バイトまでで、さらに音色とテンポを1バイトで指定するので、組み込みMML1個あたり合計65バイトと推測できる。

よって、リソースの容量は合わせて 1800×8+32×176+65×4=20292 (0x4f44) バイトとなる。
これは、読み込まれる 0xa000 バイトと比べて少ないようである。

一方、CakeRes×レトロゲームズ のページのアーカイブを見ると、以下のリソースが定義されていることがわかる。

  • IMAGE 16枚
  • SPRITE 240 (0xf0) 枚
  • BGM 28個
  • SE 14個

BGMとSEはいずれも同様の組み込みMMLで表されると仮定すると、リソースの容量は 1800×16+32×240+65×(28+14)=39210 (0x992a) バイトとなり、読み込まれる 0xa000 バイトに近づく。

スプライト番号 #F0#FE は自作スプライトに、#FF はスプライトを消すのに使用されるため、リソースのスプライトは最大 240 (0xf0) 個であると推測できる。
また、読み込まれる 0xa000 バイトから「CakeRes×レトロゲームズ」で定義されているリソースに用いると推測される容量の 39210 バイトを引くと 1750 バイトとなり、1800 バイトの IMAGE をもう1枚格納するには足りない。
よって、IMAGE は最大 16 枚、SPRITE は最大 240 枚であり、残りの領域が組み込みMMLに使用されると推測できる。
これらの IMAGE と SPRITE の容量を引いた、この組み込みMMLに使用できる領域のサイズは 4480 バイトである。
この 4480 を 64~70 で割ると、以下のようになる。

割る数
64 70.0
65 68.92307692307692
66 67.87878787878788
67 66.86567164179104
68 65.88235294117646
69 64.92753623188406
70 64.0

管理用データを含めて1個の組み込みMMLを70バイトで表現し、最大で64個の組み込みMMLを格納できる、と仮定すると、これはキリが良い美しい仕様である。
よって、CakeRes には以下のデータが格納されているという仮説を立てることができる。

  • IMAGE 1枚1,800バイト×16枚
  • SPRITE 1枚32バイト×240枚
  • 組み込みMML 1個70バイト×64個

なお、現時点でこれらがどのような順番で格納されているかは不明である。

画像の配置の調査

まずは、IMAGE や SPRITE として用いられるデータが EEPROM のどこに格納されているのかを調査する。

パターンの書き込み

数バイトのデータを見ることで、それがデータの最初から何バイト目かがわかるようなパターンを用いて、EEPROM のデータのどこが画像として読み込まれているかを調べる。
さらに、今回はデータを PanCake が出力する画像として読み取るので、読み取りを間違えにくいデータにしなければならない。
そこで、今回は PanCake で定義された色のうち、以下の5種類だけを使うことにした。

  • 0:黒
  • 1:白
  • 2:赤
  • 3:桃
  • a:水

バイト単位で極端にバラバラになることは無いと仮定し、以下の構造を持った画像を出力させることを狙う。

  1. 最初の1ピクセルは「水」にする
  2. それに続く7ピクセルで、このブロックの最初のピクセルを表すアドレスを2ビット論理右シフトした値を黒・白・赤・桃の4色を用いて表す

アドレスは、左のピクセルを上位、右のピクセルを下位とし、色番号をそのまま桁の数字とした4進数で表現する。

リマインダー:1バイトの中で、左のピクセルは下位ニブル、右のピクセルは上位ニブルに格納されるようである

このパターンを、前述の以前書いた記事のプログラムを少し変えた以下のプログラムで EEPROM に書き込む。
なお、今回は !RESUP! 01 用の領域 (7ビットアドレス #51) のみに書き込む。

10 ' CakeRes チョウサ パターン
20 FOR J=0 TO #9F
30 FOR K=0 TO #FF STEP 4
40 POKE #700+K,#0A+J>>2&#30,J>>4&3+J<<2&#30,J&3+K>>2&#30,K>>4&3+K<<2&#30
50 NEXT
60 [0]=J
70 IF I2CW(#51,#800,2,#700,#100) THEN PRINT " WRITE ERROR":END
80 CLT
90 IF I2CR(#51,#800,0)=0 THEN GOTO 110
100 IF TICK()>=60 THEN PRINT " TIMEOUT":END ELSE GOTO 90
110 IF J%16=15 THEN PRINT J/16;
120 NEXT
130 PRINT " DONE!"

IMAGE 用の画像データの配置の調査

このプログラムを実行して EEPROM への書き込みを行い、続いて ?"PC !RESUP! 01" を実行してリソースの書き換えを実行し、IchigoCake を再起動すると、以下の画面になった。

パターン書き込み後の起動画面

この画面から、最初の画面 (IMAGE 00 用の画像) は 0x0000 ~ 0x0707 の領域が連続して使われているらしいことがわかった。

さらに ?"PC IMAGE 01" を実行すると、以下の画面になった。

IMAGE 01 の画面

IMAGE 00 用の画像のデータの直後の領域が連続して使われているようである。

プログラム

10 FOR I=0 TO 15
20 ?"PC IMAGE ";HEX$(I,2)
30 WAIT 120
40 NEXT

を実行し、画面を順に送ると、IMAGE 02IMAGE 0F も同様に連続した領域が並んでいるらしい様子がうかがわた。
最終的には (IMAGE 0F の画面は) 以下のようになり、アドレス 0x6978~0x707f が使われていることが読み取れる。

IMAGE 0F の画面

IMAGE 用画像の領域全体のアドレスは 0x0000~0x707f となり、これは1800バイトの画像16枚分ちょうどの容量である。

SPRITE 用の画像データの配置の調査

以下のプログラムは、最初の32個のスプライトを、上から下に、1列描画したら右の列に行ってまた上から下に、と描画するものである。

10 ?"PC CLEAR 08"
20 FOR X=0 TO 7
30 FOR Y=0 TO 3
40 ?"PC STAMPS ";HEX$(X*9+1,2);" ";HEX$(Y*9+1,2);" ";HEX$(X*4+Y,2)
50 NEXT:NEXT

これを実行すると、以下の結果が得られた。

最初の32個のスプライトの描画

スプライトの画像データは、アドレス 0x7170 から連続して配置されているようである。
また、一部のスプライトでは色が抜けている (透明として扱われている) ことがわかる。
具体的には、この画面の中では、

  • 4で割って1余る番号のスプライトの、色番号 3 (桃)
  • スプライト 3 の、色番号 0 (黒)
  • スプライト 19 の、色番号 1 (白)

が透明になっているようである。
EEPROM 内の別のどこかのデータが透明色として解釈されている、という仮説を立てられそうだ。

次に、スプライトを32個ずつ描画していく、以下のプログラムを実行した。

10 FOR I=0 TO #E0 STEP #20
20 ?"PC CLEAR 08"
30 FOR X=0 TO 7
40 FOR Y=0 TO 3
50 ?"PC STAMPS ";HEX$(X*9+1,2);" ";HEX$(Y*9+1,2);" ";HEX$(I+X*4+Y,2)
60 NEXT:NEXT
70 WAIT 120
80 NEXT

すると、やはりスプライトのデータも連続していそうな様子がみられ、最後の画面は以下のようになった。

最後の32個のスプライトの描画結果

最後の16個は自作スプライト用の領域であり、リソースとは別にデフォルト値が格納されているため、リソースの書き換えの影響を受けない、と考えられる。
リソースのスプライトの最後 #EF のデータは、アドレス 0x8f50~0x8f6f のようである。
リソースのスプライトの領域全体では、0x7170~0x8f6f がちょうど32バイトのスプライト240個分のサイズになっており、ここに順にスプライトのデータが格納されると推測できる。

スプライトの透明色情報の配置の調査

ここまでの調査により、EEPROM の

  • アドレス 0x0000~0x707f に、IMAGE の画像データ
  • アドレス 0x7170~0x8f6f に、スプライト240個の画像データ

が格納されているらしいことがわかった。
さて、この隙間のアドレス 0x7080~0x716f は、ちょうど240個ある。
ということは、ここにスプライトの透明色情報が保存されているという仮説を立てることができる。

これを検証するため、スプライトのどの色が透明になるかを調査したい。
そこで、IMAGE 00 の画像データとスプライトの画像データを以下のように書き換える。

  • IMAGE 00:白と灰色の市松模様 (これを「透明」と解釈する人も少なくないだろう)
  • スプライト:全スプライトを、左上から右下に向かって16色が2×2ピクセルずつ並んだ画像

これにより、各スプライトのどの色が透明になったかがわかるはずである。

以下が、このデータの書き込みを行うプログラムである。

10 ' CakeRes トウメイ チョウサ
20 FOR J=0 TO #07
30 FOR K=0 TO #FF
40 POKE #700+K,#1F+(#F1-#1F)*((J<<8+K)%80<40)
50 NEXT
60 [0]=J
70 IF I2CW(#51,#800,2,#700,#100+(#08-#100)*(J=#07)) THEN PRINT "WRITE ERROR":END
80 CLT
90 IF I2CR(#51,#800,0)=0 THEN GOTO 110
100 IF TICK()>=60 THEN PRINT " TIMEOUT":END ELSE GOTO 90
110 NEXT
120 FOR I=0 TO #1F
130 POKE #700+I,#11*(I%4)+#44*(I/8)
140 NEXT
150 FOR J=#0170 TO #1F60 STEP #10
160 POKE#800,J>>8+#70,J
170 IF I2CW(#51,#800,2,#710-J%#20,#10) THEN PRINT "WRITE ERROR":END
180 CLT
190 IF I2CR(#51,#800,0)=0 THEN GOTO 210
200 IF TICK()>=60 THEN PRINT " TIMEOUT":END ELSE GOTO 190
210 NEXT
220 PRINT "DONE!"

EEPROM の仕様で、256 バイトのページの境界を超える書き込みは1回ではできないようなので、スプライトのデータは境界を超えないように16バイトずつ書き込みを行う。

このプログラムによる EEPROM の書き込みを行い、続いて !RESUP! によるリソースの書き込みを行ったあと、IchigoCake を再起動し、先ほどのスプライト表示プログラムを少し変えた以下のプログラムを実行する。

10 FOR I=0 TO #E0 STEP #20
20 ?"PC IMAGE 00"
30 ?"PC LINE 01 2B ";HEX$(I/#20+1,2);" 2B 00"
40 FOR X=0 TO 7
50 FOR Y=0 TO 3
60 ?"PC STAMPS ";HEX$(X*9+1,2);" ";HEX$(Y*9+1,2);" ";HEX$(I+X*4+Y,2)
70 NEXT:NEXT
80 WAIT 120
90 NEXT

変更点は、以下の2点である。

  • CLEAR ではなく IMAGE で画面をクリアするようにした
  • 今何枚目の画面かを表すバーを追加した

実行すると、以下のような画面になった。(例として最初の画面のみを提示)

スプライトの透明色の調査用プログラムの実行結果

実行結果から、以下のことが読み取れた。

  • 4 で割って 1 余るスプライト番号では、常に色番号 3 (桃) が透明になる
  • スプライト番号 #80#BF では、4 で割って 2 余るスプライト番号で色番号 1 (白) が透明になる
  • スプライト番号 #03#43#83#C3 では、色番号 0 (黒) が透明になる
  • スプライト番号 #13#53#93#D3 では、色番号 1 (白) が透明になる
  • スプライト番号 #23#63#A3#E3 では、色番号 2 (赤) が透明になる
  • スプライト番号 #33#73#B3 では、色番号 3 (桃) が透明になる

次に、パターンを EEPROM に書き込むプログラムを改造し、透明色情報が保存されているという仮説を立てた領域のパターンを出力させてみた。

プログラム
10 FOR I=#7080 TO #716F STEP #10
20 J=I>>8:L=I&#FF:?HEX$(I,4);
30 FOR K=L TO L+#F STEP 4
40 ?" ";HEX$(#0A+J>>2&#30,2);" ";HEX$(J>>4&3+J<<2&#30,2);" ";HEX$(J&3+K>>2&#30,2);" ";HEX$(K>>4&3+K<<2&#30,2);
50 NEXT
60 ?""
70 NEXT
実行結果
7080 1A 03 20 00 1A 03 20 10 1A 03 20 20 1A 03 20 30
7090 1A 03 20 01 1A 03 20 11 1A 03 20 21 1A 03 20 31
70A0 1A 03 20 02 1A 03 20 12 1A 03 20 22 1A 03 20 32
70B0 1A 03 20 03 1A 03 20 13 1A 03 20 23 1A 03 20 33
70C0 1A 03 30 00 1A 03 30 10 1A 03 30 20 1A 03 30 30
70D0 1A 03 30 01 1A 03 30 11 1A 03 30 21 1A 03 30 31
70E0 1A 03 30 02 1A 03 30 12 1A 03 30 22 1A 03 30 32
70F0 1A 03 30 03 1A 03 30 13 1A 03 30 23 1A 03 30 33
7100 1A 03 01 00 1A 03 01 10 1A 03 01 20 1A 03 01 30
7110 1A 03 01 01 1A 03 01 11 1A 03 01 21 1A 03 01 31
7120 1A 03 01 02 1A 03 01 12 1A 03 01 22 1A 03 01 32
7130 1A 03 01 03 1A 03 01 13 1A 03 01 23 1A 03 01 33
7140 1A 03 11 00 1A 03 11 10 1A 03 11 20 1A 03 11 30
7150 1A 03 11 01 1A 03 11 11 1A 03 11 21 1A 03 11 31
7160 1A 03 11 02 1A 03 11 12 1A 03 11 22 1A 03 11 32

すると、仮に以下の法則で透明になる色が決まっているとすれば、この結果の説明がつくことがわかる。(仮説)

  • アドレス 0x7080 + スプライト番号 のバイトの値が 0x00~0x0f の場合は、そのバイトの値の色が透明になる
  • 同バイトの値がそれ以外 (0x10~0xff) の場合は、どの色も透明にならない

まず、以下のプログラムでこの領域にランダムで 0x00~0x0f を書き込み、スプライトがそれに従って透明になるかをみる。

10 FOR I=#7080 TO #716F STEP #10
20 ?HEX$(I-#7080,2);
30 FOR J=0 TO #F:R=RND(16):?" ";DEC$(R,2);:POKE#700+J,R:NEXT
40 POKE#800,I>>8,I
50 IF I2CW(#51,#800,2,#700,#10) THEN PRINT "WRITE ERROR":END
60 CLT
70 IF I2CR(#51,#800,0)=0 THEN GOTO 90
80 IF TICK()>=60 THEN PRINT " TIMEOUT":END ELSE GOTO 70
90 ?""
100 NEXT
110 PRINT "DONE!"

全部チェックするのは面倒なので最初と最後のページだけチェックしたところ、全て生成した乱数と透明になった色が一致していた。

次に、以下のプログラムでこの領域に 0x10~0xff を書き込み、スプライトが透明にならないかをみる。

10 FOR I=#7080 TO #716F STEP #10
20 FOR J=0 TO #F:POKE#700+J,I-#7070+R:NEXT
30 POKE#800,I>>8,I
40 IF I2CW(#51,#800,2,#700,#10) THEN PRINT "WRITE ERROR":END
50 CLT
60 IF I2CR(#51,#800,0)=0 THEN GOTO 80
70 IF TICK()>=60 THEN PRINT " TIMEOUT":END ELSE GOTO 60
80 NEXT
90 PRINT "DONE!"

その結果、透明になるスプライトは無かった。

よって、この仮説が正しそうだと推測できる。

まとめ

調査の結果、PanCake で用いられる画像のデータは、CakeRes に以下のように格納されているらしいことがわかった。

  • アドレス「0x0000 + 1800×IMAGE番号」からの1800バイトに、それぞれの IMAGE 用の画像データ
  • アドレス「0x7080 + スプライト番号」の1バイトに、それぞれのスプライトの透明色
  • アドレス「0x7170 + 32×スプライト番号」からの32バイトに、それぞれのスプライトの画像データ

画像データは、以下の構造になっているようである。

  • 上の行から下の行の順に格納する
  • 行内では、左のピクセルから右のピクセルの順に格納する
  • 隣り合う2ピクセルを1バイトに格納する
  • バイト内では、左のピクセルを下位ニブルに、右のピクセルを上位ニブルに格納する

組み込みMMLの配置の調査

調査の結果、画像のデータは EEPROM のアドレス 0x0000~0x8f6f に格納されているらしいことがわかった。
よって、組み込みMMLは残りのアドレス 0x8f70~0x9fff のどこかに格納されているはずである。
具体的にどこに格納されているのか、調べていこう。

使える容量の確認

調査の結果、「リソースの数と容量に基づく構造の仮説」では考慮しなったスプライトの透明色情報が格納されていることがわかった。
そのため、その分組み込みMMLに使える容量が減ることになる。
具体的には、組み込みMMLに使えるのは 4240 バイトである。
これを64~70で割ると、商は以下のようになる。

割る数
64 66.25
65 65.23076923076923
66 64.24242424242425
67 63.28358208955224
68 62.35294117647059
69 61.44927536231884
70 60.57142857142857

もし組み込みMMLの数の上限が 64 個だとすると、1個あたりの容量は 66 バイトである。
ただし、「CakeRes×レトロゲームズ」で定義されているBGMとSEは #00#30 (途中欠番あり) の合わせて 42 個であり、組み込みMMLの数の上限は 64 個より少ないかもしれない。

組み込みMMLのアドレスの調査

画像の調査を行ったのと同様に、PanCake の出力からアドレスが特定できるようなデータを EEPROM に書き込み、どこのデータが読み込まれているかの調査を行った。

MUSIC SCORE コマンドの資料によると、この命令で使用するバイナリデータは以下の形式のようである。

  • テンポ・音色:上位ニブルにテンポ (0~F)、下位ニブルに音色 (0~3) を格納した1バイトで表す
  • 音 (MML):上位ニブルに音階 (0~7)、下位ニブルに具体的な音 (0~B) を格納した1バイトで表す

組み込みMMLでも同じ形式と仮定し、「テンポ・音色」と「音」に共通して有効なデータのみを用いたい。
また、MMLの最大有効長は64バイトのようなので、それ以下の長さで位置を識別できるようにしたい。

そこで、今回は以下の形式の16バイトを一組としたパターンを用いる。

  • #50 4バイト:区切り識別用のヘッダとして用いる
  • 組内の最初のバイトのアドレスデータ 13バイト
    • 上位から下位のビットに向かって順に格納する
    • ビットが 1 なら #60 または #620 なら #40 または #42 とする
    • 下位ニブルは、同じバイトが続いても区切りがわかるよう、交互に用いる
    • 16バイト一組なので、アドレスの下位4ビットは省略する

以下のプログラムで、このパターンのデータを EEPROM の組み込みMMLを格納すると思われる領域に書き込む。

10 ' CakeRes MML チョウサ
20 FOR J=#8F TO #9F
30 FOR K=0 TO #FF STEP 16
40 POKE #700+K,#50,#50,#50,#50
50 A=J<<4+K>>4
60 FOR I=0 TO 11:POKE #704+K+I,#40+#20*(A>>(11-I)&1)+2*(I%2):NEXT
70 NEXT
80 POKE#800,J,#70*(J=#8F)
90 IF I2CW(#51,#800,2,#700+#70*(J=#8F),#100-#70*(J=#8F)) THEN PRINT "WRITE ERROR":END
100 CLT
110 IF I2CR(#51,#800,0)=0 THEN GOTO 130
120 IF TICK()>=60 THEN PRINT " TIMEOUT":END ELSE GOTO 110
130 NEXT
140 PRINT "DONE!"

EEPROM の書き換え後、!RESUP! によるリソースの書き込みを行い、IchigoCake を再起動すると音が流れる。
これを PC MUSIC PLAY 00 で止めたあと、PC MUSIC PLAY 01 00 でチャンネル 0 の音を再生した。
これを録音し、Audacity で増幅してスペクトログラム表示すると、以下のようになった。

実行結果のスペクトログラム表示

音を聞いた様子と比べると、1000 Hz 付近の棒が明るいビットが 1 (高い音)、暗いビットが 0 (低い音) のようである。
最初のかたまりが表しているビット列は 100011110111 であり、本来4バイトのはずのヘッダが3バイト分しか再生されていないことも考えると、チャンネル 0 のデフォルトの音楽を表す組み込みMMLはアドレス 0x8f71 から格納されているらしいことがわかる。
同様に調査を行うと、各チャンネルのデフォルトの音楽は、以下のアドレスから格納されているようだった。

チャンネル アドレス
0 0x8f71
1 0x8fb3
2 0x8ff5
3 0x9037

この中の隣り合うチャンネルのアドレスの差は、いずれも 66 である。
よって、各チャンネルのデフォルトの音楽を表す組み込みMMLのデータは、66 バイトごとに格納されていそうであることがわかる。
1 個のデータを 66 バイトで表す場合、4240 バイトでは最大で 64 個のデータを格納できる。

さらに、PC MUSIC LOAD を用いていくつかの組み込みMMLをロードし、PC MUSIC PLAY で再生したものを同様に調査すると、以下のアドレスから格納されていることがわかった。

組み込みMML番号 アドレス
#00 0x8f71
#10 0x9391
#20 0x97b1
#30 0x9bd1
#3E 0x9f6d
#3F 0x9faf
#40 0x9ff2
#41 -

#40 を再生すると、他の組み込みMMLよりも短い地点で再生が打ち切られた。
また、「66バイトおきに格納される」という法則も崩れている。
#41 を再生すると、音は流れなかった。
よって、これらは有効な指定ではなく、有効な組み込みMMLは #00#3F の64個らしいことがわかった。

#00#3F のうち、今回試した組み込みMML番号については、全てアドレスは組み込みMML番号 #00 のアドレスに、組み込みMML番号に66を掛けたものを足した値となっている。
よって、その他の組み込みMML番号についても、組み込みMMLのデータは 66 バイトずつ順番に格納されていると推測できる。

テンポと音色のアドレスの調査

組み込みMMLは 66 バイトごとに格納され、組み込みMMLの最大の長さは 64 バイトらしいので、組み込みMMLのデータの前か後にテンポと音色のデータが格納されていると推測できる。
そこで、どこに格納されているかを調べるため、EEPROM の 256 バイトのページ境界をまたがない組み込みMML 0 と 1 を用いて検証を行った。

以下のプログラムは、組み込みMML 0 と 1 にだんだん音階が上がっていく同じデータを書き込み、さらに

  • 組み込みMML 0 の開始地点の1バイト前に #30
  • 組み込みMML 0 の開始地点の64バイト後に #71
  • 組み込みMML 1 の開始地点の1バイト前に #B2
  • 組み込みMML 1 の開始地点の64バイト後に #F3

を書き込む。

10 ' CakeRes MML チョウサ 2
20 FOR J=0 TO 1
30 FOR K=0 TO 65:POKE #700+K,#FF:NEXT
40 POKE#700,#30+#82*J:POKE #700+65,#71+#82*J
50 FOR K=0 TO #B:POKE #701+K,#40+K:NEXT
60 POKE#800,#8F,#70+66*J
70 IF I2CW(#51,#800,2,#700,66) THEN PRINT "WRITE ERROR":END
80 CLT
90 IF I2CR(#51,#800,0)=0 THEN GOTO 110
100 IF TICK()>=60 THEN PRINT " TIMEOUT":END ELSE GOTO 90
110 NEXT
120 PRINT "DONE!"

これにより EEPROM を書き換え、!RESUP! によるリソースの書き込みを行った後、チャンネル 0 と 1 のデフォルトで流れる音声を調べた。
その結果、チャンネル 0 (組み込みMML 0) では #30 に相当するテンポと音色で、チャンネル 1 (組み込みMML 1) では #B2 に相当するテンポと音色で再生されているようだった。

よって、それぞれの組み込みMMLのテンポと音色のデータは、それぞれの組み込みMMLの直前に1バイトで格納されていると考えられる。

言い換えれば、それぞれの組み込みMMLのデータは、

  • アドレス 0x8f70 から 66 バイトずつ格納される
  • 最初に1バイトでテンポと音色の情報を格納し、その次のバイトから楽譜の情報を格納する

と考えられる。

組み込みMMLの最後のバイトが楽譜に反映されるかの実験

MUSIC SCORE コマンドの資料では、「組み込みMMLは64バイトまで有効」とされている。
一方、ここまでの調査でわかった CakeRes のデータの構造では、組み込みMML 1個あたり 66 バイトを使用し、1バイトでテンポと音色の情報を格納するので、残り65バイトある。
この最後のバイトも組み込みMMLに反映されるかを実験する。

以下のプログラムは、データの65バイト目 (楽譜の64バイト目) に #20 を書き込み、さらに66バイト目に

  • 組み込みMML 0 には、#4E (ノイズを再生する)
  • 組み込みMML 1 には、#FF (終了)

を書き込む。

10 ' CakeRes MML ラストバイト ジッケン
20 FOR J=0 TO 1
30 FOR K=0 TO 65:POKE #700+K,#FF:NEXT
40 POKE#700,#40
50 FOR K=0 TO #63:POKE #701+K,#40+K%2*2:NEXT
60 POKE #700+63,#4E,#20,#4E+(#FF-#4E)*J
70 POKE#800,#8F,#70+66*J
80 IF I2CW(#51,#800,2,#700,66) THEN PRINT "WRITE ERROR":END
90 CLT
100 IF I2CR(#51,#800,0)=0 THEN GOTO 120
110 IF TICK()>=60 THEN PRINT " TIMEOUT":END ELSE GOTO 100
120 NEXT
130 PRINT "DONE!"

これにより EEPROM を書き換え、!RESUP! によるリソースの書き込みを行った後、チャンネル 0 と 1 のデフォルトで流れる音声を調べた。
すると、どちらも演奏の最後は #20 相当の音が流れ続け、最後のバイトの情報は反映されていないようであることがわかった。

万全を期すため、上記プログラムの60行目を

60 POKE #700+62,#4E,#20,#4E+(#FF-#4E)*J,#FF

に書き換え、特徴的なバイトを楽譜の64バイト目に置いてみた。
すると、チャンネル 0 は最後にノイズが流れ続け、チャンネル 1 は最後に再生が止まった。
よって、楽譜の64バイト目までは反映されることを確かめることができた。

まとめ

IchigoCake 版 PanCake の !RESUP! コマンドでは、以下の仕様の EEPROM からリソースを取り込むらしいことがわかった。

!RESUP! コマンドでは必ず EEPROM からリソースを取り込み、デフォルトのリソースに戻すことはできない。
!RESUP! コマンドの引数の違いは、EEPROM にアクセスする際に指定するデバイスアドレスの違いとして反映されるのみのようである。

EEPROM との通信

EEPROM とは、I2C プロトコルで通信を行う。
!RESUP! 00 では7ビットアドレス 0x50 が、!RESUP! 01 では7ビットアドレス 0x51 が用いられる。
データの呼び出しを行う際は、まずI2Cのライトコマンドでデータのアドレスを2バイトビッグエンディアンで指定 (すなわち、先に上位バイトを、後に下位バイトを送信する) し、その後I2Cのリードコマンドでそのアドレスから始まるバイト列を読み込む。

EEPROM の中身

!RESUP! で読み出される EEPROM の中身は、以下の構造になっている。

データのアドレス 中身 1要素のバイト数 要素数
0x0000 ~ 0x707f IMAGE 用画像 1800 16
0x7080 ~ 0x716f SPRITE 用画像の透明色 1 240
0x7170 ~ 0x8f6f SPRITE 用画像 32 240
0x8f70 ~ 0x9fef 組み込みMML 66 64

それぞれの領域において、各要素が 0 番から番号の昇順に格納されている。

画像のデータは、以下の形式で格納されている。(IMAGE・SPRITE 共通)

  • 上の行から下の行の順で格納する
  • 同じ行内では、左のピクセルから右のピクセルの順で格納する
  • 隣接する2ピクセルの色情報を1バイトに格納する
  • バイト内では、左のピクセルの色を下位ニブルに、右のピクセルの色を上位ニブルに格納する

SPRITE の透明色は、1バイトで表す。
0x00~0x0f はその色を透明にすることを表し、0x10~0xff はどの色も透明にしないことを表す。

組み込みMMLのデータは、以下の形式で表す。
テンポ・音色・楽譜の形式は、MUSIC SCORE のバイナリコマンドで用いるものと同じようである。

オフセット バイト数 内容
0 1 テンポ・音色
1 64 楽譜
65 1 未解明 (パディング?)

おわりに

読み出されるアドレスを特定するためのパターンデータを書き込んだ EEPROM を PanCake の !RESUP! で読み込ませ、出力される画像や音を分析することで、データを自作できるよう構造の解明を行った。
これで、あとは絵や音楽を作るスキルさえあれば、理論上は PanCake 用のリソース切り替え用 EEPROM のデータを自作できそうだ。

なお、販売終了前の「CakeRes×レトロゲームズ」の紹介ページによれば、CakeRes を使用した作成物の公開については

  • 個人の作成物は、指定の権利表記をすることで公開できる (法人・個人事業主については未定義)
  • 「WEB上以外での公開」「コンテストへの応募」「商用利用」は禁止

という制限を受けるようである。
この制限が、CakeRes の仕様に合わせてサードパーティーが勝手に作成した EEPROM のデータ及び回路・基板にも適用されるか (≒サードパーティーが勝手に作成した EEPROM のデータ及び回路・基板であっても、仕様が合っていれば CakeRes とみなされるか) は不明である。

プロジェクト終了に伴い公開が不可となったのは、CakeRes 全般ではなく、「CakeRes×レトロゲームズ」を使用した作成物についてのみのようである。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?