ORANGE pico には標準でプログラムやメモリー配列のデータを保存できるEEPROMが搭載されている。
今回は、そのEEPROMの中身をダンプし、どのようなデータが保存されているかを調べてみた。
メモリー配列のアドレスの本当の最大値
EEPROMをみてみる前に、EEPROMでも扱えるメモリー配列についてちょっと調べてみる。
公式のコマンド一覧には、mpoke
コマンド、mset
コマンド、mpeek
関数の説明に、アドレスの範囲が 0~8007 であるかのようなことが書かれている。
しかし、これは嘘である。
コマンド一覧だけを見ても、cpeek
コマンドの説明に
パターン指定が1000~1133の場合は16×16ピクセルのパターンをメモリー配列の8000~8031に読み込みます
と書かれており、8007を超えるアドレスが存在することがわかる。
実際に試してみると、10031までは mpoke
や mpeek
による読み書きができることがわかった。
EEPROMの状態を確認する
記憶が正しければ、この時点でEEPROMは購入時の状態のままのはずである。
i2c
コマンドで有効なアドレスを確認し、files
コマンドでファイル一覧を見てみた。
その結果、アドレス &H50
が有効 (すなわち、これがEEPROMのアドレス) であり、ファイルは全て無効であった。
EEPROMをダンプする
EEPROMの内容をダンプするため、以下のプログラムを用意した。
このプログラムは、EEPROM (24LC512) 全体を読み取り、CyberChef の To Hexdump の形式でUARTにダンプを出力する。
通常、ORANGE pico がUARTに出力する改行はCRであるが、このプログラムはLFを出力するため、必要に応じて端末の設定を切り替えて使用することを推奨する。
10 ch=2:deviceaddr=&H50:size=&H10000:sizeperrow=&H10
20 for i=0 to size-1 step sizeperrow
30 b=(i>>8) & &HFF:if b<>0 then addrhigh$=chr$(b) else addrhigh$="\x00"
40 b=i & &HFF:if b<>0 then addrlow$=chr$(b) else addrlow$="\x00"
50 res=i2cw(ch,deviceaddr,addrhigh$+addrlow$,0)
60 if res<>0 then print "write error: ";res:end
70 res=i2cr(ch,deviceaddr,sizeperrow,1)
80 if res<>0 then print "read error: ";res:end
90 uartput 1,format$("%08x ",i)
100 for j=0 to sizeperrow-1
110 uartput 1,format$(" %02x",mpeek(j))
120 next
130 uartput 1," |"
140 for j=0 to sizeperrow-1
150 c=mpeek(j)
160 if c<&H20 || &H7F<=c then c=asc(".")
170 uartput 1,chr$(c)
180 next
190 uartput 1,"|"+chr$(&H0A)
200 next
初期状態でダンプを行った結果、ほとんどの部分は ff であったが、0x0000 付近および 0x8000 付近に ff でない以下のデータを発見した。
これらのデータの意味は未解明である。
00000000 06 09 21 10 00 0e 44 97 03 19 29 02 49 00 11 09 |..!...D...).I...|
00000010 04 19 10 09 06 09 25 15 e8 0e 72 97 03 49 60 03 |......%...r..I`.|
00000020 06 17 21 09 23 15 21 09 06 09 25 19 38 0e 72 97 |..!.#.!...%.8.r.|
00000030 03 49 60 03 06 17 21 09 23 15 21 09 06 09 26 00 |.I`...!.#.!...&.|
00000040 02 0e 72 97 03 49 60 03 06 17 21 09 23 15 21 09 |..r..I`...!.#.!.|
00000050 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
00007ff0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
00008000 06 09 25 15 00 50 01 46 01 00 36 15 00 00 95 68 |..%..P.F..6....h|
00008010 00 00 00 00 00 00 70 61 00 00 50 00 00 00 12 00 |......pa..P.....|
00008020 00 00 35 00 00 00 00 00 00 00 03 00 00 00 45 14 |..5...........E.|
00008030 00 00 64 01 00 00 07 07 00 00 00 00 00 00 74 05 |..d...........t.|
00008040 00 00 05 01 00 00 45 00 00 00 56 00 00 00 00 00 |......E...V.....|
00008050 00 00 04 00 00 00 ff ff ff ff ff ff ff ff ff ff |................|
00008060 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
00008070 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
00008080 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
00008090 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
000080a0 ff ff ff ff ff ff ff ff ff ff ff ff 73 01 11 00 |............s...|
000080b0 f8 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |.P..............|
000080c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000080d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000080e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000080f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00008100 00 00 06 09 21 09 42 50 23 27 01 00 36 15 00 00 |....!.BP#'..6...|
00008110 36 54 00 00 00 00 00 00 51 57 00 00 35 00 00 00 |6T......QW..5...|
00008120 12 00 00 00 20 00 00 00 00 00 00 00 03 00 00 00 |.... ...........|
00008130 37 13 00 00 64 01 00 00 48 06 00 00 00 00 00 00 |7...d...H.......|
00008140 25 05 00 00 63 00 00 00 45 00 00 00 14 00 00 00 |%...c...E.......|
00008150 00 00 00 00 04 00 00 00 ff ff ff ff ff ff ff ff |................|
00008160 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
EEPROMにデータを書き込んでみる
以下のコマンドで、EEPROMにデータを書き込んでみた。
メモリー配列の内容はリセット時のまま、new
を実行する前のプログラムの内容は上記のEEPROMをダンプするプログラムである。
mwrite
はメモリー配列の内容を書き込むコマンド、save
はプログラムを書き込むコマンドである。
mwrite 0
mwrite 1,"file"
mwrite 2,"filename0123456789"
mwrite 3,"file"
save 4
save 5,""
save 6,"name12345678"
new
save 7,"name1234"
その結果、files
コマンドの出力は以下のようになった。
また、save
の実行時、「677 Bytes written」のようにプログラムデータのサイズが出力された。
0:[D]
1:[D]:file
2:[D]:filename
3:[D]:file
4:[P]
5:[P]
6:[P]:name1234
7:[P]:name1234
このことから、以下のことがわかる。
- ファイル名は、0文字でもよい
- ファイル名は、長くても8文字で切られる
- 同じEEPROM内に同じファイル名の別のデータがあってもよい
さらに、mread "name1234"
や load ""
を実行すると、「Bad File」が出力された。
これはファイル属性が合っていないものを読もうとした際のエラーである。
load "name1234"
を実行すると、677バイトが読み込まれた。
mread ""
を実行すると、問題なく読み込みが行われた。
mread "FILE"
や mread "filenameaaa"
を実行すると、「File not found」が出力された。
これらのことから、さらに以下のことが推測できる。
- ファイル名が0文字であっても、ファイル名を指定した読み込みの対象にできる
- 同じファイル名のデータがある場合、ファイル名を指定して読み込もうとすると、該当する一番最初のデータを読み込もうとする。このとき属性によるフィルタリングは行わない
- ファイル名の大文字と小文字は区別される
- 読み込み時のファイル名は8文字を超えても切られず、先頭だけ一致してもマッチしない
EEPROMをダンプすると、それぞれのファイル領域の先頭部分は以下のようになっていた。
00000000 70 69 63 6f 00 01 00 00 00 00 00 00 00 00 00 00 |pico............|
00000010 00 6a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |.j..............|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00002000 70 69 63 6f 00 01 66 69 6c 65 00 00 00 00 00 00 |pico..file......|
00002010 00 6a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |.j..............|
00002020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00002030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00002040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00004000 70 69 63 6f 00 01 66 69 6c 65 6e 61 6d 65 00 00 |pico..filename..|
00004010 00 6a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |.j..............|
00004020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00004030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00004040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00006000 70 69 63 6f 00 01 66 69 6c 65 00 00 00 00 00 00 |pico..file......|
00006010 00 6a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |.j..............|
00006020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00006030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00006040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00008000 70 69 63 6f 00 00 00 00 00 00 00 00 00 00 00 00 |pico............|
00008010 00 6a 00 00 a5 02 3f 00 0a 95 01 02 63 68 ad 98 |.j....?.....ch..|
00008020 02 9b 01 0a 64 65 76 69 63 65 61 64 64 72 ad b5 |....deviceaddr..|
00008030 00 00 00 50 9b 01 04 73 69 7a 65 ad b5 00 01 00 |...P...size.....|
00008040 00 9b 01 0a 73 69 7a 65 70 65 72 72 6f 77 ad b5 |....sizeperrow..|
0000a000 70 69 63 6f 00 00 00 00 00 00 00 00 00 00 00 00 |pico............|
0000a010 00 6a 00 00 a5 02 3f 00 0a 95 01 02 63 68 ad 98 |.j....?.....ch..|
0000a020 02 9b 01 0a 64 65 76 69 63 65 61 64 64 72 ad b5 |....deviceaddr..|
0000a030 00 00 00 50 9b 01 04 73 69 7a 65 ad b5 00 01 00 |...P...size.....|
0000a040 00 9b 01 0a 73 69 7a 65 70 65 72 72 6f 77 ad b5 |....sizeperrow..|
0000c000 70 69 63 6f 00 00 6e 61 6d 65 31 32 33 34 00 00 |pico..name1234..|
0000c010 00 6a 00 00 a5 02 3f 00 0a 95 01 02 63 68 ad 98 |.j....?.....ch..|
0000c020 02 9b 01 0a 64 65 76 69 63 65 61 64 64 72 ad b5 |....deviceaddr..|
0000c030 00 00 00 50 9b 01 04 73 69 7a 65 ad b5 00 01 00 |...P...size.....|
0000c040 00 9b 01 0a 73 69 7a 65 70 65 72 72 6f 77 ad b5 |....sizeperrow..|
0000e000 70 69 63 6f 00 00 6e 61 6d 65 31 32 33 34 00 00 |pico..name1234..|
0000e010 00 6a 00 00 00 00 63 9e 00 06 00 b4 95 0f 00 16 |.j....c.........|
0000e020 00 be 95 4e 95 98 01 9a 99 01 7c a0 57 9d b5 00 |...N......|.W...|
0000e030 00 00 0a 9e 00 06 00 c8 95 0f 00 95 92 95 01 01 |................|
0000e040 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
さらに、
mpoke 0, &H55
mpoke 1, &HAA
を実行した後 mwrite 0
でメモリー配列を保存し、ダンプを行うと、先頭部分は以下のようになった。
00000000 70 69 63 6f 00 01 00 00 00 00 00 00 00 00 00 00 |pico............|
00000010 00 6a 00 00 00 00 55 aa 00 00 00 00 00 00 00 00 |.j....U.........|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
このことから、この部分の構造は以下のようになっていると推測できる。
また、ファイル名を省略した際は0文字のファイル名 ""
を指定したのと同じ状態になるようである。
オフセット | サイズ | 内容 |
---|---|---|
0x00 | 5 | マジックナンバー 70 69 63 6f 00 ("pico") |
0x05 | 1 | ファイル種別 0:プログラム、1:メモリー配列 |
0x06 | 8 | ファイル名 左詰め、余った部分は00で埋める |
0x0E | 6 | 未解明 00 00 00 6a 00 00 |
0x14 | 2 | プログラムサイズ(リトルエンディアン) メモリー配列では0 |
0x16 | 可変 | プログラムまたはメモリー配列のデータ |
保存されたプログラムは、以下のようになっていた。
00008000 70 69 63 6f 00 00 00 00 00 00 00 00 00 00 00 00 |pico............|
00008010 00 6a 00 00 a5 02 3f 00 0a 95 01 02 63 68 ad 98 |.j....?.....ch..|
00008020 02 9b 01 0a 64 65 76 69 63 65 61 64 64 72 ad b5 |....deviceaddr..|
00008030 00 00 00 50 9b 01 04 73 69 7a 65 ad b5 00 01 00 |...P...size.....|
00008040 00 9b 01 0a 73 69 7a 65 70 65 72 72 6f 77 ad b5 |....sizeperrow..|
00008050 00 00 00 10 00 28 00 14 95 0c 95 01 01 69 ad 98 |.....(.......i..|
00008060 00 95 93 95 01 04 73 69 7a 65 9f 98 01 95 94 95 |......size......|
00008070 01 0a 73 69 7a 65 70 65 72 72 6f 77 00 4c 00 1e |..sizeperrow.L..|
00008080 95 01 01 62 ad 9d 01 01 69 a7 98 08 9e 95 a8 95 |...b....i.......|
00008090 b5 00 00 00 ff 9b 02 95 01 01 62 ae 98 00 95 92 |..........b.....|
000080a0 95 01 09 61 64 64 72 68 69 67 68 24 ad 57 9d 01 |...addrhigh$.W..|
000080b0 01 62 9e 95 15 95 01 09 61 64 64 72 68 69 67 68 |.b......addrhigh|
000080c0 24 ad 99 04 5c 78 30 30 00 45 00 28 95 01 01 62 |$...\x00.E.(...b|
000080d0 ad 01 01 69 95 a8 95 b5 00 00 00 ff 9b 02 95 01 |...i............|
000080e0 01 62 ae 98 00 95 92 95 01 08 61 64 64 72 6c 6f |.b........addrlo|
000080f0 77 24 ad 57 9d 01 01 62 9e 95 15 95 01 08 61 64 |w$.W...b......ad|
00008100 64 72 6c 6f 77 24 ad 99 04 5c 78 30 30 00 39 00 |drlow$...\x00.9.|
00008110 32 95 01 03 72 65 73 ad 88 9d 01 02 63 68 9a 01 |2...res.....ch..|
00008120 0a 64 65 76 69 63 65 61 64 64 72 9a 01 09 61 64 |.deviceaddr...ad|
00008130 64 72 68 69 67 68 24 a0 01 08 61 64 64 72 6c 6f |drhigh$...addrlo|
00008140 77 24 9a 98 00 9e 00 2b 00 3c 95 02 95 01 03 72 |w$.....+.<.....r|
00008150 65 73 ae 98 00 95 92 95 04 95 99 0d 77 72 69 74 |es..........writ|
00008160 65 20 65 72 72 6f 72 3a 20 9c 01 03 72 65 73 9b |e error: ...res.|
00008170 12 00 2f 00 46 95 01 03 72 65 73 ad 89 9d 01 02 |../.F...res.....|
00008180 63 68 9a 01 0a 64 65 76 69 63 65 61 64 64 72 9a |ch...deviceaddr.|
00008190 01 0a 73 69 7a 65 70 65 72 72 6f 77 9a 98 01 9e |..sizeperrow....|
000081a0 00 2a 00 50 95 02 95 01 03 72 65 73 ae 98 00 95 |.*.P.....res....|
000081b0 92 95 04 95 99 0c 72 65 61 64 20 65 72 72 6f 72 |......read error|
000081c0 3a 20 9c 01 03 72 65 73 9b 12 00 18 00 5a 95 4e |: ...res.....Z.N|
000081d0 95 98 01 9a 87 9d 99 05 25 30 38 78 20 9a 01 01 |........%08x ...|
000081e0 69 9e 00 1f 00 64 95 0c 95 01 01 6a ad 98 00 95 |i....d.....j....|
000081f0 93 95 01 0a 73 69 7a 65 70 65 72 72 6f 77 9f 98 |....sizeperrow..|
00008200 01 00 1b 00 6e 95 4e 95 98 01 9a 87 9d 99 05 20 |....n.N........ |
00008210 25 30 32 78 9a 6d 9d 01 01 6a 9e 9e 00 06 00 78 |%02x.m...j.....x|
00008220 95 0f 00 0f 00 82 95 4e 95 98 01 9a 99 03 20 20 |.......N...... |
00008230 7c 00 1f 00 8c 95 0c 95 01 01 6a ad 98 00 95 93 ||.........j.....|
00008240 95 01 0a 73 69 7a 65 70 65 72 72 6f 77 9f 98 01 |...sizeperrow...|
00008250 00 0f 00 96 95 01 01 63 ad 6d 9d 01 01 6a 9e 00 |.......c.m...j..|
00008260 29 00 a0 95 02 95 01 01 63 b1 b5 00 00 00 20 95 |).......c..... .|
00008270 ac 95 b5 00 00 00 7f b2 01 01 63 95 92 95 01 01 |..........c.....|
00008280 63 ad 63 9d 99 01 2e 9e 00 10 00 aa 95 4e 95 98 |c.c..........N..|
00008290 01 9a 57 9d 01 01 63 9e 00 06 00 b4 95 0f 00 16 |..W...c.........|
000082a0 00 be 95 4e 95 98 01 9a 99 01 7c a0 57 9d b5 00 |...N......|.W...|
000082b0 00 00 0a 9e 00 06 00 c8 95 0f 00 95 92 95 01 01 |................|
000082c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
(中略:全て00)
00008770 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00008780 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
文字列や変数名はそのまま格納されているが、それ以外の部分は何らかのエンコードを行って格納されているようである。
さらに、空のプログラムを保存した際にみられた 63 9e 00 06 ~ 92 95 01 01 のデータが、このプログラムデータの最後にも現れており、プログラムとは別に何らかの情報を保存していると考えられる。
さらに、このデータの後にしばらく00の連続が書き込まれていた。
空のプログラムを保存した際にはこのような00の連続は見られなかったし、保存領域全体を埋めるというわけでもないようである。
この長さの法則は未解明である。
この00の連続は、mwrite
コマンドにより次のページに書き込まれたメモリー配列のデータであると推測できる。
メモリー配列の保存範囲を確かめる
メモリー配列の領域0~10031いっぱいに、オフセットを2バイト、リトルエンディアンで書き込む、以下のプログラムを用意し、実行した。
10 size=10032
20 for i=0 to size-1 step 2
30 mpoke i,i
40 mpoke i+1,i>>8
50 next
この結果を、mwrite 0,"offset"
で保存した。
すると、ダンプ結果は以下のようになった。
00000000 70 69 63 6f 00 01 6f 66 66 73 65 74 00 00 00 00 |pico..offset....|
00000010 00 6a 00 00 00 00 00 00 02 00 04 00 06 00 08 00 |.j..............|
00000020 0a 00 0c 00 0e 00 10 00 12 00 14 00 16 00 18 00 |................|
00000030 1a 00 1c 00 1e 00 20 00 22 00 24 00 26 00 28 00 |...... .".$.&.(.|
00000040 2a 00 2c 00 2e 00 30 00 32 00 34 00 36 00 38 00 |*.,...0.2.4.6.8.|
00000050 3a 00 3c 00 3e 00 40 00 42 00 44 00 46 00 48 00 |:.<.>.@.B.D.F.H.|
00000060 4a 00 4c 00 4e 00 50 00 52 00 54 00 56 00 58 00 |J.L.N.P.R.T.V.X.|
00000070 5a 00 5c 00 5e 00 60 00 62 00 64 00 66 00 68 00 |Z.\.^.`.b.d.f.h.|
(中略)
00002700 ea 26 ec 26 ee 26 f0 26 f2 26 f4 26 f6 26 f8 26 |.&.&.&.&.&.&.&.&|
00002710 fa 26 fc 26 fe 26 00 27 02 27 04 27 06 27 08 27 |.&.&.&.'.'.'.'.'|
00002720 0a 27 0c 27 0e 27 10 27 12 27 14 27 16 27 18 27 |.'.'.'.'.'.'.'.'|
00002730 1a 27 1c 27 1e 27 20 27 22 27 24 27 26 27 28 27 |.'.'.' '"'$'&'('|
00002740 2a 27 2c 27 2e 27 f0 26 f2 26 f4 26 f6 26 f8 26 |*','.'.&.&.&.&.&|
00002750 fa 26 fc 26 fe 26 00 27 02 27 04 27 06 27 08 27 |.&.&.&.'.'.'.'.'|
00002760 0a 27 0c 27 0e 27 10 27 12 27 14 27 16 27 18 27 |.'.'.'.'.'.'.'.'|
00002770 1a 27 1c 27 1e 27 20 27 22 27 24 27 26 27 28 27 |.'.'.' '"'$'&'('|
00002780 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
なんと、データが保存領域の境界である 0x2000 を超え、隣の保存領域にまで書き込まれた。
しかも、最後の部分を見ると 0x2728 すなわち 10024 までしか書き込まれておらず、最後の6バイトは保存されないという中途半端な仕様になっていそうに見える。
files
コマンドの出力結果は
0:[D]:offset
1:[X]
2:[D]:filename
3:[D]:file
4:[P]
5:[P]
6:[P]:name1234
7:[P]:name1234
となり、隣の保存領域のデータが破壊されていることがわかる。
公式のコマンド一覧の mwrite
の説明には
メモリー配列の内容をEEPROMの指定したページに書き込みます。
と書かれており、うっかりすると指定したページにしか書き込まないと解釈してしまうだろう。
これも新しく発見した罠であるといえる。
一旦電源を切ることでリセットし、以下のプログラムを実行した。
10 for i=10020 to 10031
20 print "mpeek(";i;")=";mpeek(i)
30 next
以下の結果が得られ、メモリー配列は0に初期化されていそうであることがわかる。
mpeek(10020)=0
mpeek(10021)=0
mpeek(10022)=0
mpeek(10023)=0
mpeek(10024)=0
mpeek(10025)=0
mpeek(10026)=0
mpeek(10027)=0
mpeek(10028)=0
mpeek(10029)=0
mpeek(10030)=0
mpeek(10031)=0
mread 0
を実行し、再びプログラムを実行すると、以下の結果が得られた。
mpeek(10020)=36
mpeek(10021)=39
mpeek(10022)=38
mpeek(10023)=39
mpeek(10024)=40
mpeek(10025)=39
mpeek(10026)=42
mpeek(10027)=39
mpeek(10028)=44
mpeek(10029)=39
mpeek(10030)=46
mpeek(10031)=39
なんと、ダンプ結果ではみられなかったように思えた 10026~10031 についても値が復元されている。
もう一度ダンプ結果をよく見てみると、最後の6バイトは 0x2740 からきちんと保存されており、0x2746~0x277f のデータは 0x2706~0x273f のデータを繰り返していることがわかった。
冗長性確保?フォーマットの確認?とにかく、メモリー配列は最後まで保存され、読み込みもできることがわかった。
EEPROMのデータを消去してみる
前述のオフセットの情報を mwrite 0,"offset"
で書き込んだ状態から、erase 0
を実行し、データを消去してみた。
すると、ダンプ結果は以下のようになった。
00000000 58 58 58 58 00 01 00 00 00 00 00 00 00 00 00 00 |XXXX............|
00000010 00 6a 00 00 00 00 00 27 02 27 04 27 06 27 08 27 |.j.....'.'.'.'.'|
00000020 0a 27 0c 27 0e 27 10 27 12 27 14 27 16 27 18 27 |.'.'.'.'.'.'.'.'|
00000030 1a 27 1c 27 1e 27 20 27 22 27 24 27 26 27 28 27 |.'.'.' '"'$'&'('|
00000040 2a 00 2c 00 2e 00 30 00 32 00 34 00 36 00 38 00 |*.,...0.2.4.6.8.|
00000050 3a 00 3c 00 3e 00 40 00 42 00 44 00 46 00 48 00 |:.<.>.@.B.D.F.H.|
00000060 4a 00 4c 00 4e 00 50 00 52 00 54 00 56 00 58 00 |J.L.N.P.R.T.V.X.|
00000070 5a 00 5c 00 5e 00 60 00 62 00 64 00 66 00 68 00 |Z.\.^.`.b.d.f.h.|
00000080 6a 00 6c 00 6e 00 70 00 72 00 74 00 76 00 78 00 |j.l.n.p.r.t.v.x.|
00000090 7a 00 7c 00 7e 00 80 00 82 00 84 00 86 00 88 00 |z.|.~...........|
000000a0 8a 00 8c 00 8e 00 90 00 92 00 94 00 96 00 98 00 |................|
000000b0 9a 00 9c 00 9e 00 a0 00 a2 00 a4 00 a6 00 a8 00 |................|
000000c0 aa 00 ac 00 ae 00 b0 00 b2 00 b4 00 b6 00 b8 00 |................|
000000d0 ba 00 bc 00 be 00 c0 00 c2 00 c4 00 c6 00 c8 00 |................|
000000e0 ca 00 cc 00 ce 00 d0 00 d2 00 d4 00 d6 00 d8 00 |................|
000000f0 da 00 dc 00 de 00 e0 00 e2 00 e4 00 e6 00 e8 00 |................|
00000100 ea 00 ec 00 ee 00 f0 00 f2 00 f4 00 f6 00 f8 00 |................|
ここから、以下のことが読み取れる。
- ヘッダの
pico
はXXXX
で潰されている。 - ファイル名は0埋めで消去されている。
- ファイル形式の
01
および未解明の6a
は残っている。 - データ領域の先頭に、最後の冗長部分のデータの一部 0x2756~0x277f が書き込まれている。
- データ領域の残りは変更されていない。
すなわち、erase
コマンドはEEPROMに「データが保存されていない」という情報を書き込み、データの最初の部分を最後に近い部分で上書きするが、他の大部分のデータには手を加えない (消去しない) ということである。
「データを消去する」コマンドだと思って使用し、「消去」後のEEPROMの使い回しや譲渡を行うと、残っているデータが読み取られ、予期せぬ結果が生じるという罠かもしれない。
続いて、erase 6
コマンドでプログラムを消去し、消去後の状態をダンプして確認してみる。
結果は以下のようになった。
0000c000 58 58 58 58 00 01 00 00 00 00 00 00 00 00 00 00 |XXXX............|
0000c010 00 6a 00 00 00 00 63 9e 00 06 00 b4 95 0f 00 16 |.j....c.........|
0000c020 00 be 95 4e 95 98 01 9a 99 01 7c a0 57 9d b5 00 |...N......|.W...|
0000c030 00 00 0a 9e 00 06 00 c8 95 0f 00 95 92 95 01 01 |................|
0000c040 00 9b 01 0a 73 69 7a 65 70 65 72 72 6f 77 ad b5 |....sizeperrow..|
0000c050 00 00 00 10 00 28 00 14 95 0c 95 01 01 69 ad 98 |.....(.......i..|
0000c060 00 95 93 95 01 04 73 69 7a 65 9f 98 01 95 94 95 |......size......|
0000c070 01 0a 73 69 7a 65 70 65 72 72 6f 77 00 4c 00 1e |..sizeperrow.L..|
0000c080 95 01 01 62 ad 9d 01 01 69 a7 98 08 9e 95 a8 95 |...b....i.......|
0000c090 b5 00 00 00 ff 9b 02 95 01 01 62 ae 98 00 95 92 |..........b.....|
0000c0a0 95 01 09 61 64 64 72 68 69 67 68 24 ad 57 9d 01 |...addrhigh$.W..|
0000c0b0 01 62 9e 95 15 95 01 09 61 64 64 72 68 69 67 68 |.b......addrhigh|
0000c0c0 24 ad 99 04 5c 78 30 30 00 45 00 28 95 01 01 62 |$...\x00.E.(...b|
0000c0d0 ad 01 01 69 95 a8 95 b5 00 00 00 ff 9b 02 95 01 |...i............|
0000c0e0 01 62 ae 98 00 95 92 95 01 08 61 64 64 72 6c 6f |.b........addrlo|
0000c0f0 77 24 ad 57 9d 01 01 62 9e 95 15 95 01 08 61 64 |w$.W...b......ad|
0000c100 64 72 6c 6f 77 24 ad 99 04 5c 78 30 30 00 39 00 |drlow$...\x00.9.|
0000c110 32 95 01 03 72 65 73 ad 88 9d 01 02 63 68 9a 01 |2...res.....ch..|
0000c120 0a 64 65 76 69 63 65 61 64 64 72 9a 01 09 61 64 |.deviceaddr...ad|
0000c130 64 72 68 69 67 68 24 a0 01 08 61 64 64 72 6c 6f |drhigh$...addrlo|
0000c140 77 24 9a 98 00 9e 00 2b 00 3c 95 02 95 01 03 72 |w$.....+.<.....r|
0000c150 65 73 ae 98 00 95 92 95 04 95 99 0d 77 72 69 74 |es..........writ|
0000c160 65 20 65 72 72 6f 72 3a 20 9c 01 03 72 65 73 9b |e error: ...res.|
0000c170 12 00 2f 00 46 95 01 03 72 65 73 ad 89 9d 01 02 |../.F...res.....|
0000c180 63 68 9a 01 0a 64 65 76 69 63 65 61 64 64 72 9a |ch...deviceaddr.|
0000c190 01 0a 73 69 7a 65 70 65 72 72 6f 77 9a 98 01 9e |..sizeperrow....|
0000c1a0 00 2a 00 50 95 02 95 01 03 72 65 73 ae 98 00 95 |.*.P.....res....|
0000c1b0 92 95 04 95 99 0c 72 65 61 64 20 65 72 72 6f 72 |......read error|
0000c1c0 3a 20 9c 01 03 72 65 73 9b 12 00 18 00 5a 95 4e |: ...res.....Z.N|
0000c1d0 95 98 01 9a 87 9d 99 05 25 30 38 78 20 9a 01 01 |........%08x ...|
0000c1e0 69 9e 00 1f 00 64 95 0c 95 01 01 6a ad 98 00 95 |i....d.....j....|
0000c1f0 93 95 01 0a 73 69 7a 65 70 65 72 72 6f 77 9f 98 |....sizeperrow..|
0000c200 01 00 1b 00 6e 95 4e 95 98 01 9a 87 9d 99 05 20 |....n.N........ |
0000c210 25 30 32 78 9a 6d 9d 01 01 6a 9e 9e 00 06 00 78 |%02x.m...j.....x|
0000c220 95 0f 00 0f 00 82 95 4e 95 98 01 9a 99 03 20 20 |.......N...... |
0000c230 7c 00 1f 00 8c 95 0c 95 01 01 6a ad 98 00 95 93 ||.........j.....|
0000c240 95 01 0a 73 69 7a 65 70 65 72 72 6f 77 9f 98 01 |...sizeperrow...|
0000c250 00 0f 00 96 95 01 01 63 ad 6d 9d 01 01 6a 9e 00 |.......c.m...j..|
0000c260 29 00 a0 95 02 95 01 01 63 b1 b5 00 00 00 20 95 |).......c..... .|
0000c270 ac 95 b5 00 00 00 7f b2 01 01 63 95 92 95 01 01 |..........c.....|
0000c280 63 ad 63 9d 99 01 2e 9e 00 10 00 aa 95 4e 95 98 |c.c..........N..|
0000c290 01 9a 57 9d 01 01 63 9e 00 06 00 b4 95 0f 00 16 |..W...c.........|
0000c2a0 00 be 95 4e 95 98 01 9a 99 01 7c a0 57 9d b5 00 |...N......|.W...|
0000c2b0 00 00 0a 9e 00 06 00 c8 95 0f 00 95 92 95 01 01 |................|
0000c2c0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
同様にヘッダが潰され、ファイル名が消去され、データの冒頭が最後の部分で上書きされている。
ところで、ロジックアナライザでみると、erase
コマンドはEEPROMの読み込みを行わず、書き込みのみを行っていることがわかった。
すなわち、データの冒頭に上書きする「最後の部分」は実際のデータの最後の部分を読み取っているわけではないといえる。
となると、このデータはどこから来ているのだろうか?
リセット直後に erase 0
を実行してみると、データは00の連続が書き込まれた。
00000000 58 58 58 58 00 01 00 00 00 00 00 00 00 00 00 00 |XXXX............|
00000010 00 6a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |.j..............|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
ダンププログラムを実行しても、書き込まれるデータは変わらず00の連続だった。
files
コマンドを実行すると、書き込まれるデータが 63 9e
から始まるデータに変化した。
00000000 58 58 58 58 00 01 00 00 00 00 00 00 00 00 00 00 |XXXX............|
00000010 00 6a 00 00 00 00 63 9e 00 06 00 b4 95 0f 00 16 |.j....c.........|
00000020 00 be 95 4e 95 98 01 9a 99 01 7c a0 57 9d b5 00 |...N......|.W...|
00000030 00 00 0a 9e 00 06 00 c8 95 0f 00 95 92 95 01 01 |................|
mread 2
を実行してメモリー配列に00の連続を読み込むと、書き込まれるデータはまた00の連続に変化した。
(このとき、実際は保存領域3のヘッダも読み込まれ、厳密には00の連続ではないだろう)
先ほどのメモリー配列にオフセットを書き込むプログラムを実行しても、書き込まれるデータは00の連続のままであった。
さらに mwrite 5
を実行すると、書き込まれるデータは 00 27
から始まるオフセットのデータに変化した。
00000000 58 58 58 58 00 01 00 00 00 00 00 00 00 00 00 00 |XXXX............|
00000010 00 6a 00 00 00 00 00 27 02 27 04 27 06 27 08 27 |.j.....'.'.'.'.'|
00000020 0a 27 0c 27 0e 27 10 27 12 27 14 27 16 27 18 27 |.'.'.'.'.'.'.'.'|
00000030 1a 27 1c 27 1e 27 20 27 22 27 24 27 26 27 28 27 |.'.'.' '"'$'&'('|
save 6
を実行してEEPROMのダンプを行うプログラムを保存すると、書き込まれるデータは再び 63 9e
から始まるデータに変化した。
リセットを行い、mread 5
を実行すると、書き込まれるデータは 00 27
から始まるオフセットのデータに変化した。
もう一度リセットを行い、load 6
を実行すると、書き込まれるデータは 63 9e
から始まるデータに変化した。
以上の実験から、書き込まれるデータはリセット時は00の連続であり、少なくとも以下のコマンドによって設定されると推測できる。
コマンド | 設定内容 |
---|---|
files |
未解明のデータ 63 9e ... 01 01
|
load |
未解明のデータ 63 9e ... 01 01
|
save |
未解明のデータ 63 9e ... 01 01
|
mread |
読み込んだメモリー配列のアドレス9984~10025のデータ |
mwrite |
メモリー配列のアドレス9984~10025のデータ |
プログラムを保存する様子を見てみる
EEPROMをダンプするプログラムを save
コマンドで保存し、その様子をロジックアナライザで観察すると、以下のようになった。
通信のかたまりの一つを拡大すると、以下のようになっていた。
このかたまりでは、EEPROMにデータを書き込む指示を出している。
その後、データの書き込みが完了したかをポーリングなどで確認することはなく、通信終了後約5msの間隔をあけて次の通信を開始している。
ライセンス
今回のプログラムは、CC0 1.0 でライセンスする。
まとめ
ORANGE pico は、EEPROMにプログラムやメモリー配列を保存する際、以下のヘッダーをつける。
オフセット | サイズ | 内容 |
---|---|---|
0x00 | 5 | マジックナンバー 70 69 63 6f 00 ("pico") |
0x05 | 1 | ファイル種別 0:プログラム、1:メモリー配列 |
0x06 | 8 | ファイル名 左詰め、余った部分は00で埋める |
0x0E | 6 | 未解明 00 00 00 6a 00 00 |
0x14 | 2 | プログラムサイズ(リトルエンディアン) メモリー配列では0 |
EEPROMへの書き込み時は、書き込み完了を確認せず、通信 (書き込み指示) 終了後約5ms間隔で次の通信を開始する。
さらに、新たに以下の罠が発見された。
- 公式のコマンド一覧にはメモリー配列の最大のアドレスが8007であるかのような記述が見られるが、これは嘘で、実際の最大は10031である。
- EEPROMへの保存時に長いファイル名を指定すると、警告やエラーが出ずに無断で8文字に切り詰められる。
-
mwrite
コマンドは、指定したページだけでなく、その次のページにもデータを上書きする。 -
erase
コマンドは、指定したページのデータを完全には消去せず、先頭の64バイトを上書きして無効化するだけである。
特に mwrite
コマンドの罠は意図せぬデータの破壊に繋がる可能性があり、危険であるといえる。