たまりば

  パソコン・インターネット パソコン・インターネット  三鷹市 三鷹市

PIC16のDhrystone MIPSを測ろうとしてみた
2020年04月29日 02:23

PIC、中でも昔ながらのミッドレンジは演算性能が低いことで有名だ。
ベンチマークでどれほど低い値が出るか気になる。マイコンの性能比較に広く使われているDhrystoneは整数演算だけなので低性能なCPUでも走りそうだ。

そう思ってDhrystoneのソースコードを見てみると…
int    Arr_2_Glob [50] [50];

ミッドレンジPICに5000バイトもメモリは無い。最大でも368バイトだ。

-完-



…とここで諦めてしまうのはつまらない。
よく見てみれば2500個の要素を全て使っているわけではない。アクセスするのはわずか4箇所。これならなんとかなるのではないか。
なんとかなるというのはつまり、Dhrystoneをそのまま走らせることはできないにせよ、なるべく結果に影響の出ない範囲でコードをいじったり、もし本来のコードが走ったらどの程度の速度が出るかを推測することで、それなりに意味のある値を出せるのではないか。

というわけでやってみた。
使うコンパイラはMPLABの標準のCコンパイラ、XC8(v1.44)。無償版なのできっと最適化とかに制限があるだろうが気にしない。

まずは何はともあれコンパイルが通るようにしなければならない。適当に配列要素の数を減らそう。50×50要素を7×6要素に。
int Arr_2_Glob [7] [6]; //Arr_2_Glob [50] [50];
typedef int     Arr_2_Dim [7][6]; //[50] [50];
これが妥当かどうかは後で見ていく。
ついでにもう1つこっちも減らさないといけない。50要素を25要素に。
int Arr_1_Glob [25]; //Arr_1_Glob [50];
typedef int     Arr_1_Dim [25]; //[50];
int 50個=100バイトはあるんではないかと思うかもしれないが、コンパイラの仕様上1つのオブジェクトは1バンクに収まっていないとならない。
また総メモリ量もわりときつく、ちょっと増やすとすぐ割り付けに失敗する。

配列の読み書きのコードは変えていない。
範囲外を読み書きすることになるがそこは自分の足を撃てるC言語。何の問題もない。

他数箇所変更した。
・エラーが出るたび適当にそれっぽいヘッダファイルをinclude
よく分からないまま追加しているがまあ多い分には問題ないだろう。

・NOSTRUCTASSIGN
structassignで「can't generate code for this expression」が出たので、それ用に用意されたコードが使われるよう
#define NOSTRUCTASSIGN
したら動くようになった。
これにともないmemcpyがコードに書かれたものが使われるようになったが、ライブラリ側のものと衝突したのでコメントアウトした。

・時間計測のためのTIMEだのなんだのを削除
結果に影響の無い部分なのでバッサリと。
このへんをPICで動くように変更するのは手間なので時間計測はシミュレータで行うことにする。
PICは実行クロック数が確定的なので問題ない。

・mallocができない
のでユーザーズガイドを見ると、
Dynamic memory allocation, (heap-based allocation using malloc, etc.) is not supported on any 8-bit device.
とある。
幸いmallocが使われているのは初期化の部分、ベンチマーク本体のループの外だ。直接宣言してしまおう。
ポインタが指す先のメモリがどう用意されたものかはプログラムを走らせる上で違いはないだろう。
おそらく最適化を防ぐために持ってまわった定義をしているのだろうと思われ、そこまでの最適化はきっとされていないだろうと判断する。少なくともポインタ参照はされている。
/*
  Next_Ptr_Glob = (Rec_Pointer) malloc (sizeof (Rec_Type));
  Ptr_Glob = (Rec_Pointer) malloc (sizeof (Rec_Type));
*/
  Rec_Type temp1;
  Rec_Type temp2;
  Next_Ptr_Glob = &temp1;
  Ptr_Glob = &temp2;

なお、これで測り終えた後見つけたのだが、別の対処法として、呼び出されると事前に用意したポインタを返す疑似mallocを使っている人がいた。(後述のAVRのコード)
こちらの方がより安全そうなので真似してみたのだが、後述のポインタ幅違いがまた出てどうしてもうまくいかなかった。
諦めて直接宣言のままにした。

・よく分からないエラーが出た
 *Ptr_Ref_Par = Ptr_Glob->Ptr_Comp;
の部分で、「error: (1466) registers unavailable for code generation of this expression」というエラーが出た。
PICでレジスタってなんだろう。RAMとは違うのか。
なんだかよく分からないが2分割したらエラーが消えたのでこれでよしとする。
struct record *temp;

    temp = Ptr_Glob->Ptr_Comp;
    *Ptr_Ref_Par = temp;
…が、後にこれは必要なくなる。後述。

これで先へ進むようになり、コンパイルが通るか…と思いきや意外なことにプログラムメモリが足りない。結構食うんだなあ。
RAMがミッドレンジ最大の368バイトあるものとして手持ちからPIC16F88を選んだのだが、こいつのプログラムメモリは4kワード。
どうせシミュレータで動かすのだし最大の8kワードある石を適当に選ぶか…と思ったが、コードを見ると大量のprintfがあって容量を食っていそうだ。
状況説明のための出力は不要なので開始と終了を1文字だけ残して消す。
結果出力部分に未使用の変数が消されることを防ぐためのprintfがあるがこちらも極力切り詰める。
例えばこれを
  printf ("Int_Glob:            %d\n", Int_Glob);
  printf ("        should be:   %d\n", 5);
  printf ("Bool_Glob:           %d\n", Bool_Glob);
  printf ("        should be:   %d\n", 1);
こうする感じで。
  printf ("a:%d\n", Int_Glob);
  printf ("b:%d\n", Bool_Glob);
こうして切り詰めたら余裕で入った。3kワード弱にまでなった。
なおprintfから呼ばれるputchの実装は、意味のある出力をするものではなく、シミュレータで適宜止めて出力内容を見るための、引数が消されない最低限のコードにした。

これでビルドが通り、(内容はともかく)動作をするようになった。

出力を確かめると、配列の部分の値が違うのは想定通りだが、他に構造体のポインタ演算あたりの部分も違っている。
調べてみるとおかしいのは例の2分割で代入したところで、16bitのポインタに8bitのポインタを代入している。
この際データが不可解な変化を見せており、その影響でそのあとif文で通らないはずの方を通っている。

XC8ではポインタが8bitと16bitの2種類あるのだが、使い分けは手動ではできず、コード中の全ての代入を認識して自動で最適なものを使い分けてくれるという。
これが判定に失敗して間違った代入をしてしまっているように見える。つまりコンパイラのバグではないか。

ループ外に適当に代入文を書いてみたところ今まで8bitだったところに16bitのポインタが使われるようになり、出力が正しくなった。
そして例の2分割代入部分も元のコードでコンパイルが通るようになった。

配列部分も正しく動作しているか調べよう。
配列のアクセスの部分の数値を書き換えて配列内をアクセスするようにし、正しい結果が出るかを見る。
出た。
また、ここで時間計測し、配列アクセス部以外にかかる時間が変化していないことを確認した。
よって、不正なアドレスをアクセスすることで他の実行結果に影響を与えていることは無いと考えられる。

これで多分配列部分以外は本来のDhrystoneの想定通り動いているだろう。



さて配列の量をいじった事によりベンチマーク結果にどれほどの影響が出ているか考えてみよう。

1次元配列の方の要素数は、(2次元配列を[2][3]にしてメモリを空けた上で)[25][30][35][40][48]を試し、実行時間は一切変化しなかった。
こちらの影響は無いものとみて良いだろう。

2次元配列の要素数を[7][6]から変えて変化を見てみる。
配列の宣言の部分と、typedefの部分。
宣\def[50][50][50][6][7][50][7][6][2][2]
[7][6]58005693580056935571
[4][6]58005693580056935571
[8][3]58005693580056935571
[2][3]58005800580058005571

これを見ると、typedefの方の2次元めを変えると実行時間にやや変化が見られるのに対し、
配列の宣言の方は[2][3]でのみ値が変わるが他は要素数によらず一定である。

本来の[50][50]に近づけられないのが気がかりだが、現状の判断として、配列宣言の要素数を大きくしてもベンチマークの実行時間には影響を与えないと考えることにする。
typedefの方は折角なので本来の[50][50]にしておく。

なおこの要素数だが、変えるとコンパイルに失敗することがかなり多い。
要素数の積が48(メモリ量が96バイト)を越えるとメモリ不足でコンパイルできないのは分かるのだが、次表のようにそれ以下でも値に応じて不規則に失敗する。
[*][2][*][3][*][4][*][5][*][6][*][7][*][8][*][9]
[2][*]×××××
[3][*]××××××
[4][*]×××××
[5][*]××××
[6][*]××
[7][*]
[8][*]
[9][*]??
[10][*]??
[11][*]??
[12][*]??
(○: コンパイル成功 / ×: コンパイルエラー / ?および表範囲外: 未チェック / 空欄: 96バイト超)

この時のエラーは次のようなもので、調べるとメモリのバンクへの割り付けに失敗しているエラーで、PICにはよくあることのようだ。有料版だと出なくなることもあるらしい。
D:\P\xc8\v1.44\sources\common\lwdiv.c:30: error: (1357) fixup overflow storing 0x8E in 1 byte at 0x950/0x2 -> 0x4A8 (dist/default/debug\Dhrystone88.X.debug.obj 120/0x26)

配列の宣言の方はこれでいいとして、実際にアクセスする部分はどうか。この分をどう考えるかは難しい。

何らかの計算をしてアドレスを求め、配列の範囲外とはいえどこかしらをアクセスしてはいる。
しかし、ここで生成されているコードはどうあっても2500の全ての要素にアクセスすることはできない。
そのような不完全なコードで時間を計ってよいものか。

とはいえ2500要素5000バイト分のアドレスというものはミッドレンジではそもそも存在し得ないのだから、それを計算するコードとしてどのようなものを想定するのかの正解は無い。

考えた末、以下2点を補正することにした。

・乗算
まず、アドレスの生成部分を見ると、1バイト×1バイト=1バイトの乗算ルーチンが呼ばれている。実行時間は100サイクル程度。
これはまずい。50×50要素の配列にアクセスするには積を2バイトで持たなければならない。
試しに2つのchar型の変数を乗算してみると実行時間は値によって変わり、124や178サイクル。
    char temp01 = 8;
    char temp02 = 100;
    int temp03 = temp01*temp02;
→124
    char temp01 = 38;
    char temp02 = 100;
    int temp03 = temp01*temp02;
→178
この分を適当に補正することにする。1回あたり50サイクル、配列アクセスは4回あるので計200サイクル追加することにする。

・バンク
5000バイトのメモリは存在しないが、せめて複数バンクにわたるメモリのアドレスを算出するコードは考慮しておきたい。
1バンクあたり96バイトという中途半端な値を使うのはさすがに大変すぎる。
仮に大量のメモリを積むならもっとまともなアクセスができるようにするだろう(というか、実際にPIC18やEnhancedミッドレンジでされている)
悩んだ末、「80バイトや96バイトの容量があるバンクが十分に大量にあり、バンクごとに切りの良い64バイトだけを使う」「バンク選択にかかる時間は考えない」ことを仮定し、以下のコードを書いた。
int address = 0x1234
char addrh = address >> 6;
char addrl = address & 0x3F;
これに50サイクル掛かった。2次元配列のアクセスは4箇所あるので200サイクルを追加する。

以上より、実測値5800サイクルに2次元配列アクセスの分を想定した補正値400サイクルを加えた6200サイクルを、ミッドレンジPICにおけるDhrystone1回分の実行時間に相当する値と考える。

これをクロック数に直して24800クロック、1MHz・1秒あたりDhrystone相当値は40.3、1757で割りDMIPS/MHzの推定値は、
0.0229 DMIPSっぽい値/MHz
と得られた。



比較しよう。

16bitや32bitのマイコンの宣伝にはよく公式にDMIPSが書かれている。見ると、比較的性能の低い(インオーダー・シングルコア)のものでも1DMIPS/MHz程度はある。
CPUDMIPS/MHz情報ソース
PIC32MM 1.53http://ww1.microchip.com/downloads/en/DeviceDoc/60001324b.pdf
RL781.39https://www.renesas.com/ja-jp/products/microcontrollers-microprocessors/rl78/rl78-features.html
ARM Cortex-M00.89 (最適化条件を変えれば1.02や1.27)https://developer.arm.com/products/processors/cortex-m/cortex-m0

何十倍も違うとあまり比較にならない。
もっと性能の低い、8bitCPUの値を探そう。
探してみるとそれなりに見つかる。

http://oneweekwonder.blogspot.jp/2013/12/z80-dhrystones.html
Z80。定番だ。
3.5MHzで0.142DMIPSというので、0.04DMIPS/MHz。

http://www.homebrewcpu.com/new_stuff.htm
65(C)02とZ80。6502はファミコンで有名だ。
6502 - 1.02MHz: 37Dhrystone
Z80 - 2.5MHz: 91Dhrystone
というので、
37/1757/1.02 = 0.02DMIPS/MHz
91/1757/2.5 = 0.02DMIPS/MHz
Z80の値が上の半分だ。まあ環境やコンパイラでこの程度の誤差は出るものだろう。

http://ww2.tiki.ne.jp/~maro/AVR/project/bench1/index.html
AVR。PICとよく比較されるマイコンだ。
at90s8515・8MHzで、最速の結果で1.938MIPSとある。0.24DMIPS/MHzだ。
ただしこの人、律儀に2500バイト(?)のために読み書きに3クロックかかる外部RAMを接続している。
(*2500バイトと書いてあるのはたぶん間違いだと思う。C言語的にIntは16bit以上のはず。)
自分のやったように必要な分のメモリだけにするなら外部RAMは要らずもっと速くなる。
4クロックから3クロックに変えるだけで1.659MIPSから1.933MIPSに上がったというのだから、同じ割合で1クロックになれば2.920MIPS・0.365MIPS/MHzに上がる計算になる。

http://www.ecrostech.com/Other/Resources/Dhrystone.htm
AVRもうひとつ。
こちらは内蔵メモリに収まるよう配列要素を半分にするなどしているようだ。先述の疑似mallocはここの記述だ。
ATmega64で、0.328DMIPS/MHz。
上の0.24DMIPS/MHzより速く、推定0.365MIPS/MHzよりは遅い。
調べてみるとat90には乗算命令がなくATmegaにはあるようなので、at90より速いはずだ。してみると上の推定0.365MIPS/MHzは少々怪しいか。

これらの値と今回のPICの推定値を比較してみる。

Z80: 0.02~0.04DMIPS/MHz
Z80はゲームボーイのCPUしか扱ったことがないが、1命令のクロック数は4の倍数で、概ねメモリアクセスの回数×4クロックに一致する。これはPICと似た性能である。
本来のZ80は多少クロック数が異なり、またレジスタが多かったりループ命令があったりするが、大勢に影響は無いだろう。
クロックあたりに行う動作がPICと同程度で、命令がZ80の方が豊富であることから、DMIPS値が1倍~2倍というのはだいたい感覚と合っている。

6502: 0.02DMIPS/MHz
6502はメモリアクセスあたり1クロック+α(内部処理分)といった趣で、Z80よりクロックあたりの性能は高い印象がある。
Z80と同等か低い値というのはちょっと感覚と合わない。まあZ80も2つで2倍の差があるくらいだし、この程度は誤差なのかもしれない。

AVR: 0.24~0.365DMIPS/MHz
Z80や6502と1桁違う。PICの10.5~16倍。感覚的にちょっと多すぎな気もするが、よく考えてみよう。
まずAVRは大半の命令が1命令1クロックで動く。PICは基本的に1命令4クロックなので、命令数では2.6~4倍となる。
ミッドレンジPICとAVRのアーキテクチャを比較すると、
・PICは汎用レジスタが1つのみであり何をやるにもメモリに値を置く必要がある
・PICはプログラムメモリのページ・RAMのバンク切り替えで時間を食う
・PICには乗算命令が無い
・PICは間接アドレッシング用のレジスタが1本しか無いことやメモリのバンク分けのため、C言語に向いていない
・一方AVRは間接アドレッシング用のレジスタが16bit×3本もあり、大変C言語向きである
といったあたりの要因からして、2.6倍くらいは妥当だし4倍もまあありうるのかなと思えてくる。

Conclusion


独自の仮定のもとにそれなりにDMIPSに近いと信じる値を算出し、いくつかの8bit CPUの値と比較して感覚とそれなりに合う結果となったので満足である。



結論に問題があるとすれば、まず一番怪しいのが2次元配列アクセスの補正が正当かどうか。
そしてmalloc除去を始め各所で加えた変更が正当かどうか。
あとはPICでまともにC言語を使ったのは初めてなのでなにか手順にとんでもない間違いがあるかもしれない。
有料版で最適化をかけて別次元の速さになったりしないかも少し心配だ。(「(PRO版なら)最大で60%小さく400%速いコードができていたんだぜ」ってコンパイルするたびに言われるのだ)
コードはgithubに上げたので、何か疑問があれば色々試してみるとよいだろう。
https://github.com/Ikadzuchi/Dhrystone88.X

なお、本稿をほぼ書き終えた段階で公開用にコードを整形したところ、ループ外の不要部を削っただけなのだが、ループの実行時間は5800サイクルから5824サイクルに増えた。面倒なので文中の数値は直さない。
コードの位置が変わることによりページをまたいだりして実行時間がこの程度変化することは十分考えられる。
他にもループ外にテスト用の乗算コードを書いたら7016→6996に短縮したこともあった。(不正な場所を実行していた時なので実行時間は大きく違う)  

  • マリオ3でのキノコの突然変異の考察
    2020年03月10日 09:37

    スーパーマリオシリーズの代表的なアイテムにスーパーキノコ(や1UPキノコ)がある。

    今は左のようなデザインで定着しているが初代スーパーマリオブラザーズでは右のデザインだった。
    現在と初代のスーパーキノコ
    初めて初代のキノコを見たときは何だこりゃと思った。
    変わりすぎだろ何があった。
    …と、ずっと思っていた。

    先日マリオ2を初めてプレイして知ったのだが、まず顔が付いたのはマリオ2だったんだな。
    マリオ2のキノコ
    マリオコレクション(今風のデザイン)やマリオDXのおまけ(初代のデザイン)でしかマリオ2は見たことがなかったので知らなかった。
    スーパーマリオシリーズの様々な物に顔が付いていることについてはこのように言われている。
    雲や草には顔があるように見えるが、これは立体感を出すために書き込まれた線が、顔に見えていただけだった。
    しかし、これが「怖い」と言われたことから、次回作の『2』では、背景の雲や草にはっきりとした顔が書かれた。
    https://www26.atwiki.jp/gcmatome/pages/3410.html

    ただちょっと不思議なのは、初代の時からパッケージイラストにはマリオの手に持つスーパーキノコと思しきキノコにしっかりと顔が書かれていることだ。地面の花をも差し置いて。
    初代スーパーマリオのパッケージのキノコの顔
    スターやジュゲムの雲には初代から顔があるし、これは物には顔を描くというスタイルは初代からあったのを2で徹底したということか。

    にしても。
    スーパーマリオ1・2・3・ワールドのキノコ

    顔以外も2から3で変わりすぎじゃないか。
    …と思っていた。
    実はそうでもなかったのである。

    まず、実は上の画像には1つ嘘がある。
    マリオ1・2の斑点は橙~茶色・マリオ3の斑点は赤だと認識していたのだが(そして実際上の画像もそうなっているが)、斑点の色はどれも同じである。ついでにマリオの服や赤ノコノコの甲羅、スーパー木の葉も同じ色である。
    マリオ2・3のキノコの色
    今回キノコの色について調べていて初めて気づいた。
    スーパーファミコン以降の真っ赤になった色のイメージや、空の色やキノコの地の色の違いで誤認していたものと思われる。
    そして同じ条件で撮られた別のソフトの画像はあまり存在しないことから、気づけなかったのだろう。
    実際、先の画像はMarioWikiから取ってきたのだが、見ての通り色は一定していなかった。ファミコンの色はかくも曖昧なものである。

    次に地の色が白になった事について。これはアートスタイルの変化に対するハードの制限である。
    マリオ3ではキャラクターの絵に(大半は黒の)縁取りが付くようになった。
    これにより、縁に1色とられることとなり、残り2色でキノコを描くことになった。
    地の黄色を残すか柄(え)の白を残すかの選択になるが、1UPキノコとの統一や他のキャラクターとの兼ね合いを考えると、白にするのが自然だろう。
    ちなみにマリオ3の通常の(地上面での)スプライトのパレットは
    ・プレイヤーキャラ用(色はパワーアップなどで変わる。標準マリオはベージュ/赤茶/黒)
    ・白/赤茶/黒 (スーパーキノコ、スーパー木の葉、赤ノコノコの甲羅、パタクリボーなど)
    ・白/緑/黒 (1UPキノコ、緑ノコノコの甲羅、蔓/葉など)
    ・ベージュ/黄土/黒 (クリボー、ノコノコの顔など)
    の4種である。

    とはいえ縁取りを付けただけで突然3のデザインになるわけでもなかろう。…と思っていた。
    試しに2のキノコに縁取りだけ付けてみよう。
    ところで、縁取りの付け方には4連結と8連結がある。
    4連結と8連結
    不思議なことに、マリオ3ではほとんどのキャラクターのほとんどの部分は8連結で縁取りされているのだが、キノコは4連結である。
    キノコだけ最初に描かれたとか、デザイナーが別とかだろうか。何にせよ4連結で描くことにする。
    まずは外形をそのままにして描くのが自然だろう。
    マリオ2から3のキノコへ_1
    すると、妙に痩せた印象になってしまった。縁取りが無ければ物と背景との境が外形だと認識するのに対し、縁取りをすると縁の1pxの中央を外形と認識するからだな。
    1pxだけ広げよう。
    マリオ2から3のキノコへ_2

    さて、ファミコンにはスプライトの左右反転機能があるので、左右対称な物体は必要なタイル数が半分で済む。
    タイル数は節約したいので、左右対称のデザインにしよう。
    とりあえずそのまま左右対称にしてみると、
    マリオ2から3のキノコへ_3
    左はあからさまに2つの大きな斑点が並んでしまって不自然だ。右の方が望みがありそうだな。
    くっついてしまった上の斑点を切って、中央のスペースが不自然に空いているので、消えてしまった左のものくらいの大きさの斑点を1個置く。
    マリオ2から3のキノコへ_4

    ほぼマリオ3のキノコになってしまった!

    比較してみよう。
    マリオ2から3のキノコへ_比較
    特に上部の丸みが完全に一致しているのと、斑点の形の大部分が一致しているあたりがポイントが高い。
    縁取りを付けてからここまで制限に従って不自然な部分を直していっただけだ。
    ここまできたら後は笠を1px下に下ろし、柄を1px太らせ、斑点を自然に調整するだけ。その程度の変化には特に不思議はない。

    マリオはハードの制限から生まれたデザインというのは有名だが、キノコもそうだったのだなあ。
    次作のワールドで色数やスプライト数の制限が無くなったにも関わらず3のデザインを(地と斑点の色は反転したが)踏襲、更にこの時左右非対称になったのがその後左右対称に戻り、一番制限のきつかった3のデザインに固定されたというのがなかなか面白いところだ。  

  • 【訂正・加筆】
    2020年03月09日 00:00

    2020/03/09 まぶしいものを見た後のような残像が常に見える症状に1ヶ月後の状況を追記。
    2019/09/03 ポケットプリンタ制御でコードの一部がHTMLタグ扱いで消えていたのを修正。
    2019/07/17 ファミコンで9×9ドット文字表示(ほか)に実機動作していただいたツイートを追加、およびコードのミスを修正。
    2019/07/17 ファミコンで全画面に任意の画像(ただしモノクロ)を表示のコードのミスを修正。
    2017/04/23, 2017/05/22, 2017/07/24, 2017/11/21, 2019/06/08, 2019/06/16, 2019/06/22, 2019/07/15, 2020/02/27ゼルダの伝説ブレスオブザワイルドプレイ日記(ネタバレ有) 更新。
    2016/12/06 漢点字一覧表で、別の元データとの間で不一致があったので追記。
    2016/09/10 ネット契約を1Mbpsにしてみたの読み込み時間の記述が間違っていたので訂正。
    2016/09/04 ファミコンの縦解像度224px説の考察 左8pxを隠す機能の用途が思っていたのと違ったので追記。
    2016/06/06 Windows7でのペイントの劣化具合に新しく気づいたバグ情報を追加。
    2015/08/29 ベースラインPICの注意点で、型番の間違いを訂正、OPTION2の書き込み方法の間違いを訂正。
    2015/08/28 Windows7でのペイントの劣化具合に新しく気づいたバグ情報を追加。
    2013/09/26 5×5 ひらがなフォント5×5フォント改 / JavaScriptフォント表示機から5×5ドットフォント完成版に、思えばリンクを貼っていなかったのでリンク。
    2013/05/05 最弱のPICマイコンで電子オルガン_前編の単純ミスを1ヶ所修正。
    2013/04/27 文字コード表示機が特定の環境で動かない問題を修正、RTL文字での表示崩れを修正。
    2013/03/03 5×5ドットフォント完成版が紹介されていたので少々加筆しました。
    2012/11/18 ハロウィー?ンの正規表現に訂正・加筆があります。

    【このエントリについて】
    (2012/11/18)
    今まで、記事の内容にミスを見つけた場合はその記事だけ修正していたのですが、最新の記事はともかく古い記事にミスを見つけた場合は直しても気づかれないだろうと思って直すのが億劫になっていました。
    これではいけないと、どうするべきか考えた結果、訂正を知らせるエントリを1つ作ることにしました。
    記事を訂正した際にはこのエントリを更新して最上位に持ってくるように運用しようと思います。
    (2013/03/03)
    訂正だけに限らなかったので、エントリ名を【訂正】から【訂正・加筆】に変更しました。  
    タグ :お知らせ

  • まぶしいものを見た後のような残像が常に見える症状
    2020年02月02日 01:07

    そんな症状が出た。

    太陽や電灯などの明るい光を見たり、同じ絵を数十秒注視し続けた後、目を閉じたり白い背景に視線を移したりすると、その形がしばらく残って見える現象があると思う。
    それとちょうど同じような見え方が、目を閉じたり開けたりする度に起こる。
    しばらく目を開いて白い背景を見た後目を閉じると、丸い残像が見える。視界にそのような形が無かったにもかかわらず。
    しばらく目を閉じていてから目を開いても同様に見える。
    残像は数秒でほぼ見えなくなる。そのへんもまぶしいものを見た後の残像に似ている。
    白い背景を見ながら高速でまばたきを繰り返すとよく見える。
    普通に生活していれば気にしなければ忘れる程度の見え方でしかない。

    位置は視野の中心のそば、7時の方向。
    片目づつつぶって確かめるとどうやら症状は左目に起こっている。
    形は真円。大きさは、手を伸ばした握り拳が視野角約10°と知られているのでそれと比較すると6~7°程度か、と思っていたらまた測ると8°程度に見えたりもする。測定誤差か実際に変化しているのかは分からない。少なくとも大きな変化は無いようだ。

    ネットで情報を探すと、なかなか検索語句が難しかったが、「目 残像 残る」あたりで2件の情報を見つけた。
    右目の残像が消えない!
    怖い!右目の視界の端に、常に残像っぽい丸いのが見える!
    どちらもたぶん同じ病状で、目の奥で何か漏れ出て溜まることで目の奥が膨らみ網膜が変形したことでこの症状は出るようだ。(結論から言うと自分の診断もたぶん同じもの)
    失明などの危険なものではなく放っておいても治る程度のもののようなので、だいぶ安心。自分の症状も急に悪化したりはしていないので、休日を待って眼科に行くことに。

    眼科。
    まず機械を覗いて平野の気球を見て(たぶん屈折率か何か測られた)、別の機械で風を吹き付けられ(眼圧測定)、視力を測り(裸眼0.06、矯正1.2とか言っていた。裸眼視力が低くてショック)、
    瞳孔を開く目薬をさされて効くまでしばらく待つ。瞳孔が開いてまぶしくなる他にピント調節にも影響するようで手元の携帯にメガネを掛けた状態でピントが合わなくなった。幸い近視なのでメガネを外せばよく見える。
    事前に乗り物の運転をしないか聞かれたがなるほど確かにこの状態で運転は危険だ。自転車と迷ったが徒歩で行っていて良かった。

    瞳孔が開いたところでまた別の機械を覗いて、フラッシュを焚かれまぶしい。
    ここで診察室に入り医師の診察。机の横に設置された機械を覗いて、斜めから光を照らしながら観察、また別の小さな手持ちの道具でも光を照らしながらいろいろな方向を見るよう指示されながら観察される。

    さて診断は…飛蚊症。
    網膜とかには異常はないということで、飛蚊症の一種だろうという。いやそれは違うんじゃないかなと、普通の飛蚊症とは大きさ・形が違うとか目をつぶっても見えるとかを伝えるも、「飛蚊症にも色々あるので」といった反応。
    先入観を持って見るのもまずいだろうとここまで言わないでいたが、上記のサイトで見た「目の奥が膨らんでいる」状態を疑っている旨伝え、そういうものは無いですかねと言うと、眼底の断層画像を見せてくれたので見てみると、なんだか膨らみのようなもの見える。これは何かと尋ねたところ、やはりそれであった。事前に調べていって本当に良かった。
    思えば病名は聞いていないが、状況としては血管から漏れ出た液体が溜まっているもので悪性ではなく、やはりストレスで起こるものという説明を受けた。上記サイトのものと同じようなものだろう。
    出た薬は「カリジノゲナーゼ」と「柴苓湯」。血液の流れをどうこうするようなもののようだ。

    なお画像は貰えるか聞いたところ、写メとかならいいですよと言われたので撮ってきた。
    眼底の膨らみ
    このはっきりした膨らみに気づかないものなのだろうか…。
    少々医師の診断能力に疑問を抱かないでもないが、考えてみるとこの症状は
    ・気にしなければ気にならない程度のもの
    ・放っておいても治る
    ・致命的なことにはならない
    というもので、この症状で医者に行く人は少ないのではないか。
    症例が滅多になければ診断は難しくなる。また、誤診されても放っておいて治る病気なら気づかれない。
    そういうことでなかなか気づかないものなのかなあと思ったりもする。
    とりあえず、きちんと時間を掛けて説明して患者の話も聞いてくれた点で良い先生だと思う。

    それにしても非侵襲で正面からの光だけでこんな断面が撮れるものなんだなあ。かがくのちからってすげー。
    調べてみるとどうやらOCT(Optical Coherence Tomography; 光干渉断層計)というもののようだ。
    反射光を干渉させて反射した点の距離が分かるという原理。そんな方法があるのか。

    (2020/03/09追記)
    1ヶ月後にまた来てくれという事だったので薬を飲みつつ待った。
    その間、見える像の形が主に上側で微妙に変わったような気がしなくもなかったがほとんど変化の無いまま3週間くらい過ぎて、少々心配だったのだが1ヶ月を目前にして急に薄くなってきた。

    1ヶ月(正確には4週間)後、再び眼科に行きほぼ同じ内容の診察(平野の気球と裸眼視力が無かった)を受け、例の画像を見る。
    眼底の膨らみ2
    おお、改善している。
    またもう1種類別の画像も見せてくれた。
    網膜の厚み
    網膜の厚みを図示したもののようだ。
    左の真ん中が分かりやすいが水平に線が見えていて、これを「ニボー」といい、立っている姿勢で見ているので水が下側に溜まっているのが見えているという。
    ということは時によって微妙に形が変わって見えたのも水のたまり具合が変わったからだったのか。逆さ吊りや横になったりすればそれに応じて形が変わって見えたのかもしれない。もう試せるほどの症状が出ていないので残念だ。
    誰か同じ症状が出た人は是非試してみてほしい。そして結果を聞かせてほしい。  

  • 7セグ亜種
    2019年09月07日 07:23

    液晶やLEDによる7セグメント表示器は数字を表示するために様々なところで用いられている。

    それとともに、7セグを元に、数字以外のものを表示するためにセグメントを付け加えたものや、一部の数字のみを表示するためにセグメントを省略した特殊なセグメント配置が用いられることがある。
    そのような7セグ亜種をまとめてみた。内部構造を見たもの・表示内容から構造を推測したもの・自分で考案したものがある。
    なお以下文中でセグメントの名前を一般的なA~Gで表す。
    7セグのセグメントの名前

    7未満セグ


    7セグメントからいくつか省いたり複数のセグメントを融合したりして、10個の数字のうちの一部のみを表示するもの。また数字以外も表示するものもある。

    ・BC
    BC
    2セグメント融合の1セグメント。(以下、複数が融合したセグメントは色を付けて表す)
    1が表示できる。無表示を0と見て、最上位に0と1だけを表示する桁があるように使う。
    使われる場所は最も多いだろう。一例として、12時間制の時計の10の位や、テスターなど各種機器で使われる。
    普通は7セグメントで10進1桁を表示するので、つまり1セグメント当たり1/7桁。それに対しlog102≒0.3桁分が1セグメントで表示できるのでとてもお得だ。
    また1は幅が狭いので、以降のもののようにセグメント数を減らすだけでなくスペースも減らせる利点もある。
    よく「½桁」として勘定される。例えば普通の7セグが3桁と最上位にこれがある場合「3½桁」のように。
    「½」の意味については「1桁は(BCDで)4bit使うのに対し、1と-の2つのセグメントでは2bitなので½」という説を見つけた。
    http://act-ele.c.ooco.jp/blogroot/igarage/article/2235.html
    あまり説得力のある説には思えない。「half」や「半」がきっかり0.5を表すことばかりでもないように、単に1桁より少ないことを表しているのだと考える方が自然に思う。

    ・BC-G
    BC-G
    上記「1」に加えて「-」を表示するために中央のセグメントを加えたもの。上記2bit説によればこれのみが本来の「½桁」。
    1と「-」、およびくっついてしまうが「-1」も一応表示できる。
    またそもそも普通の7セグメントでも1桁で「-1」を表示することができる。7セグ2桁で下が-19まで表示できる温度計など存在したような、-9までしか表示できないものも存在したような、曖昧な記憶がある。
    なお、7セグとは関係ない形で「-」のセグメントを用意することもできる。
    BC-マイナス
    セグメント数は同じで、くっつかなくできるので、こちらの方が優れていると思う。

    ・ADEG-B-C
    ADEG-B-C
    一見6セグメントだが4セグメント融合したわずか3セグメント。1と2が表示できる。
    24時間制の時計の10の位にこの形が使われているのをよく見る。内部構造までは分からないが、最小で済むこの分割の可能性が高いと思う。

    ・ADG-B-C-E
    ADG-B-C-E
    こちらは4セグメント。1と2に加え3も表示できる。
    上と同じ形だがセグメントの融合具合が異なる。可能性として存在するが、実例を見たことはない。1~3というのはカレンダーの日の10の位に便利かもしれない。あとは摂氏の温度計…は40℃は普通にあり得るからちょっとつらいか。

    ・AD-他
    AD-他
    6セグメント。7のみ表示できない。
    何に使うかというと、時計の10分/10秒の位に使う。見た時は感心した。

    ・ACF-他
    ACF-他
    5セグメント。567890が表示できる。
    さらに何に使うのか疑問だと思うがちゃんと実例がある。
    FMの周波数は60MHz帯または87.5-108MHz、日本のみ76-90MHzであり、10の位が6-0に収まっているのだ。

    ・AEF-D-G
    AEF-D-G
    3セグメント。CとFが表示できる。(他にEも)
    温度計用。摂氏と華氏の表示に使う。CとFの2つのセグメントを作った方が2セグメントで済んでいい気もするが、この方が大きく表示できるのが利点か。また、常時表示のセグメントがあれば1セグメントは統合して2セグメント相当にもできる。
    図が左上3セグメントが分割されていないのは体温計で見つけた実例に合わせた。

    他に7セグメントから融合させて何か有用な配置ができないかと探すとこんなものが見つかった。
    ・ACDF-BE-G
    ACDF-BE-G
    3セグメント。5と8と0が表示できる。
    最小表示単位が5の時に使えそう。
    電気ポットで5度刻みおよび98℃を表示するものがあってもしやこれではと思ったがその後「h」も表示しているのを見たので違った。

    ・AD-CF-B-E-G
    AD-CF-B-E-G
    5セグメント。0, 2, 4, 6, 8と5(と9)が表示できるので、2刻みと5刻みの数が表示できる。

    7セグだけど普通じゃない


    ・左側が角ばっている
    8がB
    普通は8を表す表示をBとして使う。
    見かけたのは上野の科学博物館のエレベーター。エレベーターなのでもしかするとRも表示するかもしれない。最初見たとき8にしか見えなかった。
    この手の「通常の7セグで使う点灯の組み合わせを、セグメントの形をいじって別のものとして使う」ものは、普通の7セグとしての見方が邪魔をしてなかなか想定されたように見えないので、あまり好きでない。

    ・普通の7セグを1段下げて使う
    1段下
    μnpが表示できる。(他にcgijoqruも)
    コンデンサの容量表示に。
    秋月の容量計キットで見かけたのだが今探しても見つからない。販売終了だろうか。

    ・小数点付きのものを逆さに使う
    逆さ
    「℃」(や℉)として使える。マクドナルドだったかにあった機械の温度表示で見かけた。

    ・小数点付きで斜めのものを2つ点対称に並べる
    点対称
    コロンが出せる。時計キットで見かけたと思う。

    ・普通の7セグを普通に使う
    中国語の曜日が表示できる。
    中国語の曜日は月曜日~土曜日は星期一~六の数字である。
    ただ1つ日曜日のみは数字でなく星期日または星期天というが、なんと「日」は7セグで表せる。

    7セグ超


    7セグメントにいくつか追加して、全ての数字に加え他の文字(主に英字)を表示できるようにしたもの。

    ・縦線
    縦線
    +1セグメントでDMTWが表示できる。
    7セグで元から表示できるACEFGHIJLOPSUYと合わせアルファベット大文字のうち18文字が無理なく表示できる。
    いろいろなところに使えると思うが、よく見るのは曜日の1文字目である。SUN,MON,TUE,WED,THU,FRI,SATと、これが無ければ4/7が表示不能なのだ。

    ・縦線(2分割)
    縦線(2分割)
    Wikipediaに記載があったもの。全アルファベットが表示できるという。
    表示の情報は無かったので自分で頑張ってみたが少々無理がある。
    その後見つけたうみほたるさんによるフォント「紅亜鉛鉱9セグメント」がこちらだ。(pixivリンク)
    紅亜鉛鉱9セグメント
    違いがこう。
    9セグ_うみほたるさんのものとの差分
    Vの形は思いつかなかった。
    何にせよやはり少々無理がある。

    ・左上
    左上
    +1セグメントでRが表示できる。
    Rがあれば曜日の2文字目が表示できる。1文字目とは対照的に、2文字目はRだけが7セグで表示できない。
    小学校の時担任の教師が付けていた腕時計で使われていたもの。金曜日に「先生! 今日は金曜日ですが先生の時計の曜日の2文字目を見せてください!」とか言った気がする。
    しかしこれをRと読むには無理がある。あまりにも無理があったので図は左右で真ん中のくびれを変えておいた。
    Rを表示するセグメントは次の方が好みである。

    ・下に斜線\
    下に斜線\
    +1セグメントでRkQが表示できる。
    用途はまずは曜日の2文字目。昔進研ゼミのポイントを溜めて入手した腕時計に使われていた。防水だし気に入っていたのだが紛失してしまって悲しい。右下はC,Dセグメントを少し削って斜線を伸ばし絶妙に噛みあわせないとRが不格好になってしまう。どうもうまく再現できなかった。
    もう一つの用途として、トランプの表示がある。昔見たトランプの液晶ゲーム機で使われていた。1桁で数字およびkとQを表示し(AとJは元々表示できる)、左に「1」用のセグメントを追加して10を表示していた。10は「0」と表示してもよかったんではないかなと思う。なおこちらは斜線の右下を伸ばす必要はないので、図は別の形で示した。

    ・下に斜線/
    下に斜線/
    ふと思いついた小文字のaevzが表示できるセグメント。思いついた用途は曜日の2文字目と3文字目。
    下斜線で曜日
    7セグ亜種で曜日を大文字で表示しようと思うと、3文字目はTとNの両立が非常に困難であり、2文字にせざるを得ない。でも3文字のほうが読みやすいのでなんとかできないかなと考えていて思いついた。
    7セグで元から自然に表示できるbcdhijlnortuと合わせて16文字、特に母音すべてが表示できるようになるので、他にも何かと使いみちがあるかもしれない。

    ・16セグメント
    16セグ
    英字アルファベット(大文字)全26文字がほぼ自然に表示できる。対称性もよく、他にも様々な記号を表示できるポテンシャルがある。有名な形であり、この形のLEDや液晶はよく市販されている。
    敢えて問題点を探すと、BとDがやや不自然である。右側の丸みを出すことができないため、左を突き出すことで差をつけている。
    なおBの形は他にこれらも考えられるが、まあDと合わせたほうが自然だろう。
    他のB
    また、斜めのセグメントが外まで伸ばせないため、K,R,X,Yあたりで斜め線が短くなってしまうのもやや不自然である。特にXが一回り小さく見える。

    ・14セグメント
    14セグ
    実は英大文字の表示には16セグメントは要らない。上と下の横棒をそれぞれ1つにした14セグメントで上と同一の形状が出せる。実感として16セグより見かける頻度は少ない。

    ただ、英字大文字以外を考えると、16セグメントの自由度が欲しい。
    16セグメントあれば例えば、ギリシャ文字もそれなりに表示できる。
    14セグ_ギリシャ文字
    …と思ったらこれも14セグメントで足りる。
    なお、ΦとΩが微妙。

    16セグメントあればキリル文字もそれなりに表示できる。
    16セグ_キリル文字
    …まあ多少無理はあるが。
    ЙФЦЩあたりがちょっと微妙、Ёは無理だった。Ёの点はしばしば省略されるので許してほしい。
    ともかく、こちらはちゃんと16セグメント要る。ДЙЛЪЫЮで上下のセグメントの片方を使った。

    他に、記号類も16セグメントあった方が何かと作りやすいだろう。例えばBeatManiaで使われているのを見て感動した「!」だが、これも16セグメントがいる。
    16セグ_BeatManiaの「!」

    非7セグ亜種


    16セグメントを超える7セグ亜種は見た覚えがない。
    これ以上増やしても新たな文字が表示できるようになったりしないのだろう。
    「曲」の字19セグメントでカタカナと漢数字を表示するという特許があったりするが、実際に使われたものではないだろう。
    もっと増やすとそろそろセグメント数がドットマトリクスに近づいてくる。例えば5×5の25ドットでカタカナと英字大文字が難なく表示できるのだ。

    ただし、扱える文字の増加ではなく、字形を自然にするために7セグとはかけ離れた形のセグメントを使うものはわりと存在する。雑に紹介しよう。

    シチズンの電卓で使われていたという「ナチュフェイス」という37セグメントの数字。
    ナチュフェイス
    http://nanzocamera1.cocolog-nifty.com/blog/2016/02/post-fb9d.html

    カリグラフィー風の数字。
    カリグラフィー風数字
    https://www.gizmodo.jp/2014/04/script.html

    駅の発車標の大小文字表示できるなんかやたら多いセグメントの表示器。

    他、ドットマトリクス亜種と呼ぶべきものもある。
    詳細は分からないが、5×7ドットからいくつか斜めに分割して日本語の曜日を表示していたもの。こんな感じの。
    5×7ドット斜め分割_曜日

    5×7ドットに追加で濁点と半濁点を表示するセグメント
    5×7に追加で(半)濁点

    昔雑誌に載っていた時計の曜日表示部分にあった、ドットマトリクスから融合したような、英字を一応表示できそうなもの。記憶は曖昧。
    ドットマトリクス融合_時計の曜日

    <ここにカッコいい締めの言葉を入れたかったが疲れた>