論理シミュレーションで使うメモリのデータを作って、それを使ってシミュレーションをしてみると期待値と合わない。
なにが起きてるのかと調べたらMSBとLSBが逆転していた。
どうもメモリ内部のビット順と、論理的なビット順が逆になっているようで。
というわけでメモリのデータを作る前に、ビット順を反転させる処理を足せばいいんですね。
論理シミュレーションに食わせるデータはPerlで書いていたのだが、ここにこういうコードを足した。
$a=($a&0x0F)<<4|($a&0xF0)>>4;
$a=($a&0x33)<<2|($a&0xCC)>>2;
$a=($a&0x55)<<1|($a&0xAA)>>1;
これで8ビット分の反転になる。
1行目で前4ビットと後4ビットを入れ替え、2行目で4ビット内で前2ビットと後2ビットを入れ替え、3行目で偶数ビットと奇数ビットを入れ替える。
0行目: 12345678
1行目: 56781234
2行目: 78563412
3行目: 87654321
結果として、MSBとLSBを反転することが出来る。
ビット順の入れ替えをやることはそんなに多くなさそうだが、
ビックエンディアンとリトルエンディアンでデータを行き来するときに、バイト順を入れ替えると言う処理はやる機会も多いかも。
書き方はいろいろあるんだが、上と同じように書けばこんなんかね。
a=(a&0x0000FFFF)<<16|(a&0xFFFF0000)>>16;
a=(a&0x00FF00FF)<< 8|(a&0xFF00FF00)>> 8;
こういう書き方って一般的なのかな? サンプルではあんまり見ないけど。
上のコードと組み合わせると、32ビットの反転は下記の通り書ける。
a=(a&0x0000FFFF)<<16|(a&0xFFFF0000)>>16;
a=(a&0x00FF00FF)<< 8|(a&0xFF00FF00)>> 8;
a=(a&0x0F0F0F0F)<< 4|(a&0xF0F0F0F0)>> 4;
a=(a&0x33333333)<< 2|(a&0xCCCCCCCC)>> 2;
a=(a&0x55555555)<< 1|(a&0xAAAAAAAA)>> 1;
プログラムで書くとなんとなくめんどくさいが、得られるものはただ単にビット順を入れ替えたもの。
回路で言えば結線を変えるだけ。計算とも言えない操作だ。
というわけで、CPUはバイト順・ビット順を入れ替える専用の命令を持ってることが普通だ。
ARMだと REV命令 でバイト順を反転でき、RBIT命令でビット順を反転できる。x86だと BSWAP命令 でバイト順を反転できる。
パフォーマンス重視ならそういう専用の命令を使えた方が便利だよね。
ただ、プログラムの中でちょっと組み込むのにアセンブラ呼び出したりするのは割に合わないしね。
上のような記述法の出番はそこそこありそう。
自分は上のように書いたのだけど、後で別の目的で同じような処理をしているコードがあって、それを見たら、
a=(a&0x80)>>7|(a&0x40)>>5|(a&0x20)>>3|(a&0x10)>>1|
(a&0x08)<<1|(a&0x04)<<3|(a&0x02)<<5|(a&0x01)<<7;
安直だなぁと思ったけど、やりたいことってそういうことだよね。
どちらもパフォーマンスはどうでもいい用途なので、別にどういう書き方でもいいんだが、
単純に考えれば、最初に僕が書いた書き方の方がかなり効率はよさそう。4ビット単位に拡張したりできるメリットもあるし。