ORANGE pico では、配列で宣言した最大の添字より大きい添字にアクセスしようとすると Subscript out of range というエラーになるが、0未満の添字にはエラーにならずにアクセスできる。
ただし、-30000
など、小さすぎる添字にアクセスするとリセットがかかってしまう。
そこで、リセットがかかる前の0未満の添字にアクセスし、何があるのかを観察してみた。
データの取得
今回は、以下のプログラムで0未満の添字にアクセスした。
ORANGE pico の配列には16ビットの整数が入ることがわかっているので、配列から読みだした値を十六進数4桁で出力することにした。
リセット前にきちんとデータを出力させるため、出力後に pause
を入れた。
また、配列へのアクセスを行う前に、特徴的な値が入った変数をいくつか用意した。
10 a=&HDEADBEEF
20 b!=123.456
30 dim array(0)
40 i=0
50 print format$("%04x",array(i) & &HFFFF);
60 pause 10
70 i=i-1
80 goto 50
これを、uart
コマンドで結果をシリアル出力するように設定した後、流し込んで実行を行った。
以下の画面は、実行直前の状態である。
実行を開始して約5分経つと、リセットがかかった。
そこで、シリアル通信で出力されたデータ (十六進数) をバイト列に変換し、さらに順番を反転させた。
From Hex, Reverse - CyberChef
プログラムでは配列の添字0に近いデータから遠いデータの順で出力している。
したがって、順番を反転したデータは配列の添字0に遠いデータから近いデータ、すなわちリセットがかかる直前の位置から配列の添字0の位置の順になっているはずである。
得られたデータ
まずは得られたデータの先頭、すなわちリセットがかかる直前の位置を見てみる。
何らかのデータがあるが、意味はよくわからない。
しばらくゼロが連続した後、また何らかのデータが出てきた。
意味はよくわからない。
少し進むと、大量の3が並んだデータが出てきた。
たまに0が混ざっている。
大量の3の後には、少しの0が並んでいた。
またゼロが連続し、しばらく進むと、文字列や変数名などのデータがポツポツある領域があった。
この領域は、このあたりまで続いた。
またしばらくゼロが連続し、今度は FF FF がポツポツある領域があった。
並び方には規則性があるかもしれない。
少し進むと、入力したプログラムの文字列があった。
画面のデータ(VRAM)かと思ったが、それにしては最初に出力された部分が無い。
UARTで入力された内容かもしれない。
少し進むと、先ほどの文字列の次のバイトの位置 0x8A01
がリトルエンディアンで格納されていた。
その次の 0xA000
と組み合わせると、ORANGE pico で使用されているマイコン PIC32MX 系の KSEG1 の RAM のアドレスになるかもしれない。
さらに進むと、謎のデータがあった。
なんとなく昇順になっている気がする。
さらに進むと、変数名と代入しているデータがあった。
変数 a
に代入した &HDEADBEEF
はビッグエンディアンで、b!
に代入した 123.456
はリトルエンディアンのfloat32で格納されているようである。
よく見ると、文字列 %04x
などのデータもあり、このデータは入力したプログラムを表している可能性が考えられる。
またしばらくゼロが連続した後、謎のデータがあった。
00 A0
が並んでおり、何らかのアドレスかもしれない。
そこから少し進むと、標準関数やコマンドの名前が並んでいる部分があった。
この位置に近いファイルオフセットと 00 A0
の組み合わせが多数見られ、線形リストなどの木構造になっている可能性が考えられる。
今回得られたデータの最後の部分、すなわち配列の添字0の要素の直前の部分である。
変数 a
や b!
のデータがどちらもリトルエンディアンで格納されており、その後に変数名も見られる。
さらにこの付近のファイルオフセットと 00 A0
の組み合わせも見られ、これまた線形リストなどの木構造になっている可能性が考えられる。
考察
今回は、配列の要素を4桁の十六進数で出力した。
そしてそのデータを反転した結果、関数・コマンド・変数の名前が通常の順番で出てきた。
これは、ORANGE pico では数値はリトルエンディアンで処理されているため、4桁の十六進数で出力すると後のバイト→先のバイトの順で出力され、反転するとメモリ上の順番になるからであると考えられる。
得られたデータにはパッと見では意味がよくわからない部分もあった一方で、
- UARTから入力されたデータ
- プログラムのデータ
- コマンドなどの名前が格納された構文解析などにかかわる木
- 変数の名前やデータが格納された木
の可能性がある部分も見つかった。
今回は読み込みのみを行ったが、これらの部分にうまく書き込みを行うことで、面白い現象を起こすことができる可能性もあるかもしれない。
追加実験:メモリー配列とキャラクターパターンを探す
特徴的な変数を配置するかわりに、メモリー配列に特徴的なパターンを置いた。
さらに、これを cpoke
によりキャラクターパターンとしても格納した。
DE AD BE EF
が4個連続していればメモリー配列、2個連続していればキャラクターパターンと推測できる。
さらに、i
が十分小さくなってからのみ pause
を実行するようにすることで、効率の向上を行った。
10 mptr 8000
20 mdata &HDE,&HAD,&HBE,&HEF,&HDE,&HAD,&HBE,&HEF,&HDE,&HAD,&HBE,&HEF,&HDE,&HAD,&HBE,&HEF
30 cpoke 0,-1
40 dim array(0)
50 i=0
60 print format$("%04x",array(i) & &HFFFF);
70 if i < -25000 then pause 10
80 i=i-1
90 goto 60
同様にデータを取得した結果、0x2308
から DE AD BE EF
4個の連続が見つかった。
メモリー配列は、ここから8000を引いた 0x03c8
から格納されると推測できる。
DE AD BE EF
2個だけの連続は見つからなかった。
キャラクターパターンは今回取得した範囲には格納されていないか、自分の想定していない形式で格納されている可能性が考えられる。
追記
自分が以前書いた記事
【リ】ORANGE pico をリセットするコード|みけCAT
において、「gget
コマンドで取得する幅を増やしていくと、画面上の文字が上から消えていく」という現象を報告していたことを思い出した。
ということは、キャラクターパターンは文字ごとにまとまっておらず、各文字の最初の行→各文字の次の行→…というデータ配置になっているはずである。
(もし、文字ごとにまとまっているならば、すべての文字が一部消えるのではなく、消える文字と消えない文字に分かれるはずである)
さらに、幅328で文字が消え始めたという報告があり、このときのデータサイズは $328 \times 328 \div 8 = 13448$ バイトである。
これをメモリー配列の開始アドレス 0x03c8
に加えると、0x3850
付近にキャラクターパターンのデータがありそうであることがわかる。
これを踏まえてデータを見返すと、0x3780
にデータ DE
が、0x3880
にデータ AD
があった。
その後も256バイトごとに DE AD BE EF
のパターンがみられたので、ここがキャラクターパターンのデータと考えてよいだろう。