今回やったこと
IchigoJam で、アナログ入力の値に応じて画面内の白いピクセルの数を変化させてみた。
※IchigoJamはjig.jpの登録商標です。
プログラム
DRAW を用いるので、1.4 系以降専用である。
また、動作速度の関係で、IchigoJam R 以上で実行することを推奨する。
OneFiveCrowd で実行する
10 ' チョウコウ
20 LET[0],1793,1664,12215,-8176,1417,#A019,12215,-8179,#8593,16389,#D583,16389,1331,693,1427,32000,#8593,17269,25907,693,#8082,12546,8712,530,23177,17224,8635,265,12631,#B500,18328,17928,#BD00
30 CLS:C=0:P=1:E=ANA()<700
40 WAIT1:IFEN=ANA():N=N+(N>511)ELSEN=C+((C<1024ANDBTN(UP))-(C>0ANDBTN(DOWN)))*16
50 IFN<CGOTO100ELSEIFC=NGOTO40
60 FORI=1TO3
70 DRAW(P-1)%64,(P-1)/64
80 P=USR(#80C,P):IFP>3072GOTO80
90 NEXT:C=C+1:GOTO50
100 FORI=1TO3
110 P=USR(#804,P):IFP>3072GOTO110
120 DRAW(P-1)%64,(P-1)/64,0
130 NEXT:C=C-1:GOTO50
マシン語部分のソースコード
ORG #800
DATAW 1793,1664
ORG #804
IF M0 GOTO @INV_M0
MODE RV32C
R11 += 2
GOTO @CALC_RV32C
ORG #80C
IF M0 GOTO @CALC_M0
@CALC_RV32C
R11 = R11 + #400
R11 = [R11 + #400]W
R10 = R10 * R11
R11 = R0 + 2000
R11 = R11 + 1079
R10 = R10 % R11
RET
MODE M0
@INV_M0
R1 += 2
@CALC_M0
R2 = 8
R2 = R2 << 8
R1 = [R1 + R2]W
R0 *= R1
R1 = 187
R1 = R1 << 4
R1 += 87
PUSH {LR}
GOSUB R3
R0 = R1
POP {PC}
操作方法
起動時 (プログラム実行開始時) の BTN ピンの入力電圧により、操作方法が変化する。
アナログ入力操作
BTN ピンの入力電圧が十分低い状態 (アナログ入力デバイスを接続して低い電圧を入力する、ボタンを押すなど) で起動すると、アナログ入力操作モードになる。
BTN ピンへのアナログ入力の値に応じて、画面内の白いピクセルの数が変化する。
白いピクセルは電圧が低いほど少なく、電圧が高いほど多くなる。
アナログ入力デバイスを接続していても、起動時の入力電圧が高いと、キーボード操作モードになることがある。
キーボード操作
BTN ピンの入力電圧が十分高い状態 (アナログ入力デバイスを接続しない、ボタンを押さないなど) で起動すると、キーボード操作モードになる。
上 (↑) キーで、画面内の白いピクセルの数を増やす。
下 (↓) キーで、画面内の白いピクセルの数を減らす。
実行の様子
解説
理論
IchigoJam のグラフィック用画面の解像度は64×48ピクセルであり、全部で3,072ピクセル存在する。
(VIDEO1 モードで、SPIディスプレイを使用していない場合)
これらのピクセルを、ランダムに見える順番で1個ずつ白く塗っていきたい。
しかし、IchigoJam で正式に公開されているメモリで、3,072個のピクセルのランダムな順番の情報を格納するのは難しい。
今回は、単に画面を白く塗っていくだけではなく、入力の値が減った場合は黒く戻していかなければならないため、効率よく処理するためにはやはり順番の情報が欲しい。
そこで、適当な数の累乗を素数で割った余りを用いて点を打つ位置を決めることにした。
素数と「適当な数」をうまく選ぶと、累乗に1以上割る数(素数)未満の整数がランダムっぽい順番で全て出現する。
しかも、「割る数(素数)-2」乗すると、「もとの数に掛けて素数で割った余りを求めると1になる数」が得られ、この数を用いることで「前の数」を得ることができる。
これを用いることで、順番を全て覚えておかなくても、ランダムっぽい順番で点を描いたり消したりすることが可能になる。
今回は
- 素数:3079 (3072 以上の最小の素数)
- 適当な数:1793
- 戻すための数:1664
を使用した。
実装
10 ' チョウコウ
20 LET[0],1793,1664,12215,-8176,1417,#A019,12215,-8179,#8593,16389,#D583,16389,1331,693,1427,32000,#8593,17269,25907,693,#8082,12546,8712,530,23177,17224,8635,265,12631,#B500,18328,17928,#BD00
今回は、16ビットに近い数の掛け算や割り算を扱うことになり、これはBASICだけでは難しい。
そこで、マシン語を用いてこの掛け算や割り算 (すなわち、「次の数」や「前の数」を求める処理) を行う。
掛ける数 (1793 および 1664) は配列の最初にそのまま書いておき、マシン語の処理内でこれを拾う。
割る数 (3079) は、マシン語のコード内に埋め込んだ。
どっちの掛ける数を使うかは、マシン語のコードの実行を開始する位置で分ける。
#804 番地を呼び出すと、引数として渡されたRAMのアドレスをずらす処理が実行され、配列の2番目の値 (1664) が読み込まれ、「前の数」を求める処理となる。
#80C 番地を呼び出すと、この「ずらす処理」は実行されず、配列の最初の値 (1793) が読み込まれ、「次の数」を求める処理となる。
30 CLS:C=0:P=1:E=ANA()<700
画面と状態の初期化を行う。
以下の変数を初期化している。
-
C:今の画面が対応しているアナログ入力値 -
P:次に点を打つ位置を表す値 -
E:操作方法のモード
40 WAIT1:IFEN=ANA():N=N+(N>511)ELSEN=C+((C<1024ANDBTN(UP))-(C>0ANDBTN(DOWN)))*16
シミュレータで実行した際のCPUの負荷を減らすため、WAIT1 を挟む。
続いて、入力を取得する。
アナログ入力操作モード (E が真) の場合は、ANA() で BTN ピンのアナログ入力を読む。
アナログ入力値は 0~1023 であるが、1024 にならないと画面が全部埋まらないので、適当な位置で調整する。
キーボード操作モード (E が偽) の場合は、値が既に上限や下限になっていないかをチェックしつつ、押されているキーと現在の値に基づいて次の値を決める。
50 IFN<CGOTO100ELSEIFC=NGOTO40
新しい入力値 N が前回の描画時の値 C より小さい場合は、画面上の点を減らす処理に飛ぶ。
新しい入力値が前回の描画時の値と同じ場合は、入力を受け取る処理に飛ぶ。
そうでない場合 (新しい入力値が前回の描画時の値より大きい場合) は、この次に配置された画面上の点を増やす処理の実行に移る。
60 FORI=1TO3
70 DRAW(P-1)%64,(P-1)/64
80 P=USR(#80C,P):IFP>3072GOTO80
90 NEXT:C=C+1:GOTO50
描画時の値 C が入力値 N より小さいときに実行する処理である。
値の差 1 につき、画面に 3 個の点を増やす。
点の位置を表す P は 0 にはならないので、値から 1 を引いてから画面上の位置に変換する。
また、画面外となる 3073 以上の値が出てきた場合は、スキップしてさらに次の値を用いる。
最後に、C に 1 を足し、再び C と N の大小関係のチェックに戻る。
100 FORI=1TO3
110 P=USR(#804,P):IFP>3072GOTO110
120 DRAW(P-1)%64,(P-1)/64,0
130 NEXT:C=C-1:GOTO50
描画時の値 C が入力値 N より大きいときに実行する処理である。
同様に、画面上の点を 3 個減らし、C から 1 を引く。
点を増やす際は「点を打ってから、次の値を求める」順番だったが、点を減らす際は逆の「前の値を求めてから、点を消す」順番となる。
おわりに
IchigoJam で、アナログ入力値に応じて画面上の白い点の数を変化させることができた。
機種などにより、入力デバイスを端まで操作しても、アナログ入力値が端の値 (0 や 1023) にならないことがある。
そのため、端まで操作しても点が残ってしまうことがある。
端に近い値になったら端の値に読み替えてしまう処理を入れることで、これを改善し、より美しく見えるかもしれない。
なお、これを実際に調光照明として使えるかどうかは、使用するディスプレイの性能などにもよるだろう。