ORANGE pico のEEPROMをダンプし、保存形式を調べる

[ORANGE pico](http://www.picosoft.co.jp/orange/) には標準でプログラムやメモリー配列のデータを保存できるEEPROMが搭載されている。
今回は、そのEEPROMの中身をダンプし、どのようなデータが保存されているかを調べてみた。

# メモリー配列のアドレスの本当の最大値

EEPROMをみてみる前に、EEPROMでも扱えるメモリー配列についてちょっと調べてみる。
公式のコマンド一覧には、`mpoke` コマンド、`mset` コマンド、`mpeek` 関数の説明に、アドレスの範囲が 0～8007 であるかのようなことが書かれている。
しかし、これは**嘘である**。
コマンド一覧だけを見ても、`cpeek` コマンドの説明に

> パターン指定が1000～1133の場合は16×16ピクセルのパターンをメモリー配列の8000～8031に読み込みます

と書かれており、8007を超えるアドレスが存在することがわかる。
実際に試してみると、10031までは `mpoke` や `mpeek` による読み書きができることがわかった。

![メモリー配列の10031までは読み書きができ、10032はエラーになっている](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/3f8215b4-aa57-bf27-05b3-54e496e98d28.png)

# EEPROMの状態を確認する

記憶が正しければ、この時点でEEPROMは購入時の状態のままのはずである。
`i2c` コマンドで有効なアドレスを確認し、`files` コマンドでファイル一覧を見てみた。
その結果、アドレス `&H50` が有効 (すなわち、これがEEPROMのアドレス) であり、ファイルは全て無効であった。

![i2cとfilesの結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/aec059b0-8ef8-4da7-2651-86178159a3d8.png)

# EEPROMをダンプする

EEPROMの内容をダンプするため、以下のプログラムを用意した。
このプログラムは、EEPROM (24LC512) 全体を読み取り、[CyberChef](https://gchq.github.io/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` コマンドで保存し、その様子を[ロジックアナライザ](https://www.switch-science.com/products/7642)で観察すると、以下のようになった。

![saveを実行する様子](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/35ded57c-0311-36b8-8386-c155acd2d9c9.png)

通信のかたまりの一つを拡大すると、以下のようになっていた。

![saveを実行する様子 (拡大)](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/a49d56c3-0eea-e9f2-9580-eaa3fdd8974d.png)

このかたまりでは、EEPROMにデータを書き込む指示を出している。
その後、データの書き込みが完了したかをポーリングなどで確認することはなく、通信終了後約5msの間隔をあけて次の通信を開始している。

# ライセンス

今回のプログラムは、[CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/deed.ja) でライセンスする。

# まとめ

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` コマンドの罠は**意図せぬデータの破壊**に繋がる可能性があり、**危険**であるといえる。