アセンブラで円周率を求める
2007年4月30日
16ビット長の変数で円周率が 3.1416 まで求められることが分かったので、アセンブラで計算してみた。インテル 80386 系は、レジスタが32ビット。16ビットどうしの乗算を行っても、結果が32ビット内に収まるので、比較的容易にアセンブラで書ける。
このコードで、3.1416 をはじきだすことが出来る。
ここでは、乗算命令に mul を用いているが、これを加算命令のみで実行できるように改変する必要がある。
最後に除算で円周率を求める部分は、比較的容易なコードで実現できることが分かった。
上記のコードから、アセンブラの『mul』命令を除いてしまうと、次のようなコードになった。
ここまでのシミュレーションで分かったことは、以下のとおり。
1)フラグは、ゼロフラグとキャリーフラグの二つでよい。
2)初期の段階でスタックを実装することが、望ましい。
レジスタの数を減らして、メモリとのアクセスを充実させるような設計(モトローラ型)の方が、CPUの設計としてはやりやすいかもしれない。当初の計画では、メモリを使う頻度を減らすインテル型のCPUにする予定であった(メモリをフリップフロップで作成するのに骨が折れるため)が、このあたりの仕様について再考すべし。
#include <stdlib.h> #include <stdio.h> #include <conio.h> double sub(int step); int main (){ unsigned int step=65536; for(unsigned int i=0;i<(0x1fffffff-step);i+=step){ double pai=sub((int)step); printf( "%d %f\n", i, pai); if (kbhit()) return 0; } while (!kbhit()); return 0; } double sub(int step){ int i; double pai; static unsigned int num,inside; unsigned int r1,r2; _asm mov ecx,step; label05: //get 16 bit random values (note that max value of rand() is 32767) _asm push ecx; r1=(rand() & 0x7fff)*2 + (rand() & 1); r2=(rand() & 0x7fff)*2 + (rand() & 1); _asm pop ecx; //Determine if inside circle _asm mov eax,r1; _asm mul eax; _asm mov ebx,eax; _asm mov eax,r2; _asm mul eax; _asm add eax,ebx; _asm jc label10; _asm mov eax,inside; _asm inc eax; _asm mov inside,eax; label10: _asm mov eax,num; _asm inc eax; _asm mov num,eax; _asm loop label05; // Calculate the pai //pai=4*(double)inside/(double)num; unsigned int res=0; _asm mov eax,inside; _asm shl eax,2; _asm mov edx,num; _asm mov ebx,res; _asm mov ecx,6; label20: _asm cmp eax,edx; _asm jb label30; _asm sub eax,edx; _asm inc ebx; _asm jmp label20; label30: _asm push edx; _asm mov edx,0ah; _asm mul edx; _asm pop edx; _asm shl ebx,4; _asm loop label20; _asm mov res,ebx; // Determine pai from the res pai=0; for (i=0;i<6;i++){ res=res/16; pai=pai/10+(double)(res%16); } return pai; }
このコードで、3.1416 をはじきだすことが出来る。
ここでは、乗算命令に mul を用いているが、これを加算命令のみで実行できるように改変する必要がある。
最後に除算で円周率を求める部分は、比較的容易なコードで実現できることが分かった。
上記のコードから、アセンブラの『mul』命令を除いてしまうと、次のようなコードになった。
#include <stdlib.h> #include <stdio.h> #include <conio.h> double sub(int step); int main (){ unsigned int step=65536; for(unsigned int i=0;i<(0x1fffffff-step);i+=step){ double pai=sub((int)step); printf( "%d %f\n", i, pai); if (kbhit()) return 0; } while (!kbhit()); return 0; } double sub(int step){ int i; double pai; static unsigned int num,inside; _asm mov ecx,step; label05: //Determine if inside circle _asm call label_rand;//_asm mov eax,rand(); //_asm mul eax; _asm mov edx,eax; _asm call label_mul; // _asm mul edx _asm mov ebx,eax; _asm call label_rand;//_asm mov eax,rand(); _asm mov edx,eax; _asm call label_mul; // _asm mul edx _asm add eax,ebx; _asm jc label10; _asm mov eax,inside; _asm inc eax; _asm mov inside,eax; label10: _asm mov eax,num; _asm inc eax; _asm mov num,eax; _asm loop label05; // Calculate the pai //pai=4*(double)inside/(double)num; unsigned int res=0; _asm mov eax,inside; _asm shl eax,2; _asm mov edx,num; _asm mov ebx,res; _asm mov ecx,7; label20: _asm cmp eax,edx; _asm jb label30; _asm sub eax,edx; _asm inc ebx; _asm jmp label20; label30: // eax=eax*10 _asm push edx; _asm shl eax,1; _asm mov edx,eax; _asm shl eax,2; _asm add eax,edx; _asm pop edx; _asm shl ebx,4; _asm loop label20; _asm mov res,ebx; // Determine pai from the res (ebx) pai=0; for (i=0;i<7;i++){ res=res/16; pai=pai/10+(double)(res%16); } return pai; /* rand() */ label_rand: _asm push ebx; _asm push ecx; _asm push edx; i=(rand() & 0x7fff)*2 + (rand() & 1); _asm pop edx; _asm pop ecx; _asm pop ebx; _asm mov eax,i; _asm ret; /* eax=eax*edx */ label_mul: _asm push ebx; _asm push ecx; _asm mov ebx,eax; _asm xor eax,eax; _asm mov ecx,010h; label_mul_1: _asm shr ebx,1; _asm jnc label_mul_2; _asm add eax,edx; label_mul_2: _asm shl edx,1; _asm loop label_mul_1; _asm pop ecx; _asm pop ebx; _asm ret; }
ここまでのシミュレーションで分かったことは、以下のとおり。
1)フラグは、ゼロフラグとキャリーフラグの二つでよい。
2)初期の段階でスタックを実装することが、望ましい。
レジスタの数を減らして、メモリとのアクセスを充実させるような設計(モトローラ型)の方が、CPUの設計としてはやりやすいかもしれない。当初の計画では、メモリを使う頻度を減らすインテル型のCPUにする予定であった(メモリをフリップフロップで作成するのに骨が折れるため)が、このあたりの仕様について再考すべし。