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?

More than 1 year has passed since last update.

ORANGE picoAdvent Calendar 2023

Day 2

ここまで見つかっている ORANGE pico の罠まとめ

Posted at

ORANGE pico は高速なBASICを実行できるが、罠 (危険な挙動や、非直感的な挙動など) も多いようである。
ここでは、これまでに自分が見つけた ORANGE pico の罠を紹介する。
対称のバージョンは Ver 1.06 (ver() = 106) である。

普通のコマンドを実行しただけでリセットされることがある

実行時の効果として「リセット」が明示されていないコマンドを実行することにより、リセットがかかることがある。
たとえば、

? rnd(0)

を実行すると、リセットがかかる。
発見したその他のリセットコマンドについては、以前に書いた記事を参照してほしい。

【リ】ORANGE pico をリセットするコード|みけCAT

予期せずリセットがかかると、パソコンなどと連携せずに単体で開発している場合は開発中のプログラムの消失に繋がり、危険である。

行が長すぎると勝手に文字が消える

たとえば、260文字の

1 a=5678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890

を入力してみる。

260文字入力した状態

この状態でEnterキーを押す。

Enterキーを押した結果

すると、警告もエラーも出ず、256文字目が勝手に消えてしまった。
このように入力したプログラムを勝手に改変する仕様は、気付かないと意図しないプログラムを実行する原因となり、罠であるといえるだろう。

ドキュメントに反し、デフォルトではUARTに出力されない

公式のコマンド一覧の uart コマンドの説明には、以下のように書かれている。

モードのbit0(LSB)が1のときはコンソール出力されたものを同時にUARTに
送信します。モードのbit1が1のときは、UARTからの入力もキー入力とみなします。モード
は起動時に3となっています。

これを読むと、起動時のモードではコンソール出力されたものをUARTにも送信してくれそうに思える。
しかし、実際に操作してみると、起動時にはUARTには送信されない。

以下の検証画像では、Tera Term のローカルエコーを有効にしてある。

起動後、UART経由で print 1 を実行し、1OK が出力されている。
しかし、UARTには 1OK の出力が反映されていない。

ORANGE pico Tera Term
起動後操作を行った画面 (ORANGE pico) 起動後操作を行った画面 (Tera Term)

続いて、一旦リセットし、uart 1,3,115200 を実行してから print 1 を実行してみた。
すると、UARTにも 1OK が出力された。
さらに uart 1,2,115200 を実行すると、起動直後と同様に print の結果がUARTに出力されなくなった。

ORANGE pico Tera Term
uartコマンドを使用 (ORANGE pico) uartコマンドを使用 (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 を用いると、変数の値を変化させながら処理を繰り返すことができる。

for~nextによる繰り返し

しかし、

10 for i=1 to 5:print i:next

などのように for~next を1行に書くと、繰り返しが行われず、最初の1回しか実行されないようである。

1行で書いたfor~nextが実行されない

詳しくみると、

10 for i=1 to 5:print i
20 next

などのように for と繰り返したいコマンドを同じ行に書くとコマンドが1回しか実行されず、

10 for i=1 to 5
20 print i:next

などのように繰り返したいコマンドと next を同じ行に書いた場合は繰り返しが行われるようである。

for~nextの分割と繰り返し

繰り返しが1行で書けないため、特にインタラクティブモードで for~next による繰り返しが使えず、不便である。

gosubの後のコマンドが実行されない

以下のプログラムを実行してみる。

10 print 1:gosub 40:print 2
20 print 3
30 end
40 return

すると、1と3だけが出力され、2は出力されなかった。

gosubの実験

すなわち、gosubと同じ行のgosubより後のコマンドが実行されていないことが読み取れる。

これによる害が大きくなる場面の例として、if文の処理の中でサブルーチンを使いたい場面が考えられる。

10 if 1 then print 1:gosub 40:print 3
20 print 4
30 end
40 print 2:return

if文の中でもgosubの後は実行されない

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

tickms()の値がおかしい

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バスを活用することが可能である。
アダプターの制作例は、以前書いた以下の記事を参照してほしい。

ORANGE pico とOLEDモジュールで「大石泉すき」~大石泉生誕祭2023~|みけCAT

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?