今回は前回の経験を生かしつつ、論理演算・シフト演算・符号なし除算余り算について学びましょう。
本題の前に、Console.WriteLineに用意されているメソッドをいくつか紹介しておきます。
int16、int8などはそのまま出力できないので注意です。
論理演算は、引数を一つだけとるnotと、引数を2つとるand・or・xorの4つがあります。今回はunsigned int32で行ってみましょう。
.assembly hina{} .method public static void main(){ .entrypoint .maxstack 2 ldc.i4 0xFEDCBA98 //1111 1110 1101 1100 1011 1010 1001 1000 ldc.i4 0x12345678 //0001 0010 0011 0100 0101 0110 0111 1000 and call void [mscorlib]System.Console::WriteLine(unsigned int32) //0x12141218 0001 0010 0001 0100 0001 0010 0001 1000 ldc.i4 0xFEDCBA98 //1111 1110 1101 1100 1011 1010 1001 1000 ldc.i4 0x12345678 //0001 0010 0011 0100 0101 0110 0111 1000 or call void [mscorlib]System.Console::WriteLine(unsigned int32) //0xFEFCFEF8 1111 1110 1111 1100 1111 1110 1111 1000 ldc.i4 0xFEDCBA98 //1111 1110 1101 1100 1011 1010 1001 1000 ldc.i4 0x12345678 //0001 0010 0011 0100 0101 0110 0111 1000 xor call void [mscorlib]System.Console::WriteLine(unsigned int32) //0xECE8ECE0 1110 1100 1110 1000 1110 1100 1110 0000 ret }
それぞれ、2つの引数をとります。「and」はビットごとの論理積、「or」はビットごとの論理和、「xor」はビットごとの排他的論理和になります。
実行結果は下のようになります。
シフト演算は、算術シフト演算と、論理シフト演算があります。この2つはさらに右シフトと、左シフトの2つに分けられます。論理シフト演算はシフトして空いた場所に0を詰めます。算術シフト演算の左シフトは論理シフトと同じく0を詰めるだけです。右シフト演算は論理シフトと違い、一番左のビットで空いたところを埋めます。
つまり、左シフトはshl(shift integer left)一つの命令しかありません。そして右シフトは論理演算としてshr(shift integer right)、算術としてshr.un(shift integer right, unsigned)が用意されています。
ちなみ、最初にプッシュされた方がシフトされる側、後にプッシュされた方がシフトする側の数になります。
.assembly hina{} .method public static void main(){ .entrypoint .maxstack 2 ldc.i4 1 ldc.i4 5 shl //321 call void [mscorlib]System.Console::WriteLine( int32) ldc.i4 -1024 // 0xFFFFFC00 1111 1111 1111 1111 1111 1100 0000 0000 ldc.i4 5 shr //-32 0xFFFFFFE0 1111 1111 1111 1111 1111 1111 1110 0000 call void [mscorlib]System.Console::WriteLine( int32) ldc.i4 -1024 ldc.i4 5 shr.un //134217696 0x7FFFFE0 0000 0111 1111 1111 1111 1111 1110 0000 call void [mscorlib]System.Console::WriteLine( int32) ret }
「shl.un」という命令は存在しないので注意しましょう。
「add,sub,mul」と違い割り算では符号のあるなしが大きく関わります(addとsubは符号有り無しどちらでも演算方法が変わらず、mulは符号で意味が違うようになる時にはオーバーフローしてしまうため)。そのため、div.unとrem.unという命令が用意されています。
.assembly hina{} .method public static void main(){ .entrypoint .maxstack 2 ldc.i4 -2000000000 ldc.i4 2 div call void [mscorlib]System.Console::WriteLine(int32) ldc.i4 -2000000000 ldc.i4 2 div call void [mscorlib]System.Console::WriteLine(unsigned int32) ldc.i4 -2000000000 ldc.i4 2 div.un call void [mscorlib]System.Console::WriteLine(int32) ldc.i4 -2000000000 ldc.i4 2 div.un call void [mscorlib]System.Console::WriteLine(unsigned int32) ret }
実行結果は下のようになります。
remは省略です。
場所を占領するのでint64は利用しませんでした。試しに利用してみるのも良いと思います。