2013/07/18

エンディアン変換とバイトアクセス

最近、Open Design Computer Project で mist32 プロセッサのシミュレーターを作っています。I/O のエミュレーションにもある程度対応してます。
https://github.com/techno/mist32-simulator

そこで気づいたことをひとつ書こうかなと。あたりまえのことなのですが。

ビッグエンディアンとリトルエンディアン

ご存知の通り、この世のコンピューターは内部的にエンディアンやバイトオーダーと言われるような、1ワードのバイト列をどのようにメモリ上に配置するか、という手法が大きく分けると2つあります。

Data: 0x1234ABCD

                0  1  2  3
Big Endian:    12 34 AB CD
Little Endian: CD AB 34 12

上記が、ビッグエンディアンとリトルエンディアンの概要です。ビッグエンディアンの方が人間には自然に見えますが、コンピューター的にはそうでない面もあり、一長一短です。一般的に使われている x86 はリトルエンディアンを使っています。

ファイルシステムや、ネットワークプロトコルなどによって、エンディアンは普通は規定されています。(FAT はリトルエンディアン、ネットワークはビッグエンディアン) しかしながら、ELF のようなメモリイメージのようなフォーマットは、そう簡単に行きません。

mist32 プロセッサはビッグエンディアンです。今回のシミュレータは ELF フォーマットを読み取ります。なので、シミュレーションする際にメモリ上にデータを配置する際に、mist32 で扱うビッグエンディアンのデータや命令列をそのまま入れてもうまく行かず、エンディアンを変換してあげないとワード単位では正しく読み取れません。

エンディアン変換は、htobe32(), htole32() などの関数を使うと行うことができます。(man endian 参照) その他にも gcc であれば、__builtin_bswap32() で強制的にエンディアン変換を行えます。これらの関数は、x86 であれば普通 bswap 命令にインライン展開されて、自力のコードで論理演算するより高速に処理できます。


バイトアクセスとエンディアン

ワード単位での読み取りが、エンディアンが違うと結果が違うことがわかったのですが、バイト単位の読み取りではどうでしょう?

もう一度さかのぼって、上記の例を見てみてください。 ビッグエンディアンであれば 0 バイト目は "12" ですが、リトルエンディアンでは "CD" になっています。リトルエンディアンでも、バイトアクセスのアドレスはビッグエンディアンと変わりません。つまり、上に振ってある数字のようにアクセスされます。

ここで、"12" のバイトデータが欲しいとします。しかしながら、エンディアン変換してしまっては、ワード単位では同じ内容に変換できたとしても、バイトアクセスは同じアドレスで同じデータは取得できないことになります。エンディアン変換をする際にはとても重要なことで、これを覚えておく必要があります。

基本的には、エンディアンが違うデータをやり取りする場合には、バイトアクセスをする際にアドレス変換をしてあげる必要があります。(ワードアクセス時は必要ありません)

Big  -> Little
0 00 -> 3 11
1 01 -> 2 10
2 10 -> 1 01
3 11 -> 0 00

このような変換を行う必要があるのですが、難しい処理など必要なく、基本的に XOR 演算だけで処理できます。 バイトアクセスであれば 0x3 を XOR することで、アドレス変換できます。ハーフワード(16bit) のアドレスであれば 0x2 を XOR すれば良いです。

エンディアンの違いって吸収するのがなかなか大変ですね。

0 件のコメント: