ORANGE pico は高速なBASICを実行できるが、罠 (危険な挙動や、非直感的な挙動など) も多いようである。
ここでは、これまでに自分が見つけた ORANGE pico の罠を紹介する。
対称のバージョンは Ver 1.06 (ver() = 106) である。
普通のコマンドを実行しただけでリセットされることがある
実行時の効果として「リセット」が明示されていないコマンドを実行することにより、リセットがかかることがある。
たとえば、
? rnd(0)
を実行すると、リセットがかかる。
発見したその他のリセットコマンドについては、以前に書いた記事を参照してほしい。
【リ】ORANGE pico をリセットするコード|みけCAT
予期せずリセットがかかると、パソコンなどと連携せずに単体で開発している場合は開発中のプログラムの消失に繋がり、危険である。
行が長すぎると勝手に文字が消える
たとえば、260文字の
1 a=5678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
を入力してみる。
この状態でEnterキーを押す。
すると、警告もエラーも出ず、256文字目が勝手に消えてしまった。
このように入力したプログラムを勝手に改変する仕様は、気付かないと意図しないプログラムを実行する原因となり、罠であるといえるだろう。
ドキュメントに反し、デフォルトではUARTに出力されない
公式のコマンド一覧の uart
コマンドの説明には、以下のように書かれている。
モードのbit0(LSB)が1のときはコンソール出力されたものを同時にUARTに
送信します。モードのbit1が1のときは、UARTからの入力もキー入力とみなします。モード
は起動時に3となっています。
これを読むと、起動時のモードではコンソール出力されたものをUARTにも送信してくれそうに思える。
しかし、実際に操作してみると、起動時にはUARTには送信されない。
以下の検証画像では、Tera Term のローカルエコーを有効にしてある。
起動後、UART経由で print 1
を実行し、1
と OK
が出力されている。
しかし、UARTには 1
と OK
の出力が反映されていない。
ORANGE pico | Tera Term |
---|---|
続いて、一旦リセットし、uart 1,3,115200
を実行してから print 1
を実行してみた。
すると、UARTにも 1
や OK
が出力された。
さらに uart 1,2,115200
を実行すると、起動直後と同様に print
の結果がUARTに出力されなくなった。
ORANGE pico | Tera Term |
---|---|
このことから、コマンド一覧の「モードは起動時に3となっています。」というのは嘘であり、実際の起動時のモードは2であると考えられる。
ドキュメントに嘘が書かれている上、実際にはデフォルトではUARTには出力がされないというのは、初見殺しであるという印象が強い。
とはいえ、起動時に何も考えず uart 1,3,115200
を実行しておけばよいので、わかってしまえば面倒なだけで害は少ないだろう。
変数の直後に != 演算子は使えない
以下のプログラムを実行してみる。
=
は左辺と右辺を比較して等しい場合に1を返す演算子であり、!=
は左辺と右辺を比較して等しくない場合に1を返す演算子である。
10 a=10
20 if a=10 then print "a=10"
30 if a!=20 then print "a!=20"
すると、!=
を使用した行で Type mismatch というエラーが起きてしまった。
これは、a!=20
が「変数a
と数値20
が等しくないかの判定」ではなく、「浮動小数点型の変数a!
と数値20
が等しいかの判定」であると解釈されるためであると考えられる。
!=
演算子のかわりに <>
演算子を用いるか、変数と !=
演算子の間に空白を入れることで、変数と数値が等しくないかの判定が可能である。
10 a=10
20 if a<>20 then print "a<>20"
30 if a != 20 then print "a != 20"
for~next を1行で書くと動かない
10 print 1:print 2
などのように :
を挟んで繋げることで、複数のコマンドを1行に書いて実行することができる。
また、
10 for i=1 to 5
20 print i
30 next
などのように for~next を用いると、変数の値を変化させながら処理を繰り返すことができる。
しかし、
10 for i=1 to 5:print i:next
などのように for~next を1行に書くと、繰り返しが行われず、最初の1回しか実行されないようである。
詳しくみると、
10 for i=1 to 5:print i
20 next
などのように for
と繰り返したいコマンドを同じ行に書くとコマンドが1回しか実行されず、
10 for i=1 to 5
20 print i:next
などのように繰り返したいコマンドと next
を同じ行に書いた場合は繰り返しが行われるようである。
繰り返しが1行で書けないため、特にインタラクティブモードで for~next による繰り返しが使えず、不便である。
gosubの後のコマンドが実行されない
以下のプログラムを実行してみる。
10 print 1:gosub 40:print 2
20 print 3
30 end
40 return
すると、1と3だけが出力され、2は出力されなかった。
すなわち、gosubと同じ行のgosubより後のコマンドが実行されていないことが読み取れる。
これによる害が大きくなる場面の例として、if文の処理の中でサブルーチンを使いたい場面が考えられる。
10 if 1 then print 1:gosub 40:print 3
20 print 4
30 end
40 print 2:return
if文の中でも、同様にgosubの後のコマンドが実行されない。
複数行に分けてgotoなどを用いて制御することは可能ではあるが、複雑さが増し、行番号などの間違いの原因となるだろう。
if文のthen節でreturnを使うと謎のSyntax error表示が出る
以下のプログラムを実行する。
10 gosub 40
20 print "yes"
30 end
40 if 1 then return else goto 40
すると、謎の「Syntax error in 40」という出力がされる。
エラーになっているわけではないようで、実行は継続され、20行目の「yes」も出力されている。
繰り返し処理で実行すると、謎の表示も毎回出るようである。
10 for i=1 to 3
20 gosub 60
30 print i
40 next
50 end
60 if 1 then return else goto 60
論理を反転させ、else節でreturnを用いることで、回避が可能なようである。
10 gosub 40
20 print "yes"
30 end
40 if !1 then goto 40 else return
配列に格納した整数が16ビットに切り詰められる
ORANGE pico では、32ビットの整数が使用可能である。
しかし、整数を配列に格納すると、16ビットに切り詰められてしまうようである。
たとえば、以下のプログラムを実行すると、-31072
と出力された。
これは、配列に格納した数 100000
を16ビットに切り詰め、さらにトップビットを符号ビットとして2の補数で解釈した値である。
10 dim a(0)
20 a(0)=100000
30 print a(0)
今のところ、配列を使わない以外の回避方法は見つかっていない。
>>演算子の説明が嘘
公式の >>
演算子の説明には、「論理右シフト」と書かれている。
しかし、これは嘘であり、実際の挙動は算術右シフトである。
以下のプログラムを実行すると、-25
が出力された。
したがって、符号ビットの値が維持されており、論理右シフトではなく算術右シフトであることがわかる。
10 a = -100
20 print a >> 2
シフトの幅が固定の場合は、その幅に応じてビットマスク (AND演算) をかけることで、論理右シフトを表現できる。
シフトの幅が可変の場合でも、それに応じてビットマスクを計算すればよい。
pause、beep、play の実行中は tickms() のカウントが止まる
以下のプログラムを実行してみる。
10 clt:cltms
20 print "tick()=";tick();", tickms()=";tickms()
30 pause 1000:print "tick()=";tick();", tickms()=";tickms()
40 beep 21,1000:print "tick()=";tick();", tickms()=";tickms()
50 play "A2":print "tick()=";tick();", tickms()=";tickms()
以下の実行結果が得られた。
tick()=0, tickms()=0
tick()=62, tickms()=37
tick()=123, tickms()=69
tick()=184, tickms()=105
1/60秒単位のタイマーである tick()
は正しそうな値を返しているが、ミリ秒単位のタイマーである tickms()
は pause、beep、playの実行中の時間を除いた値を返しているようで、計算が合わない。
pause や beep であれば指定した時間をオフセットとして加えることである程度は補正できると考えられるが、play にかかる時間を別に計算して加えるのはかかる手間が大きそうである。
type S には I2C2バス用の端子が無い
ORANGE pico type A では、I2C2バス用の端子 (SDA2、SCL2) が用意されている。
type C や type E では、明示的な端子は用意されていないが、マイコンの各ピンをそのまま引き出す端子が用意されており、ここからI2C2バスの信号を取ることができる。
しかし、type S では、明示的なSDA2・SCL2端子もマイコンの各ピンをそのまま引き出す端子も用意されておらず、I2C2バスはEEPROMに接続されているのみである。
とはいえ、EEPROMには接続されているので、ソケットからEEPROMを取り外してアダプターを接続することで、type S でもI2C2バスを活用することが可能である。
アダプターの制作例は、以前書いた以下の記事を参照してほしい。