大砲の飛ばし方(前編)-計算機と機械語とアセンブラ-
『はじめに』はほぼ余談です。まじめに勉強したい方は飛ばすことをおすすめします
前提は、正しいと認めてくれないことには話が進まない内容です。無条件に認めてあげてください(なぜ正しいかはのちの章の内容となります)
はじめに
第一章、迷いました。
pythonのような応用言語をいきなりおしえてもそれは私の憎む文科省と同じことをしているわけで、だからといい基礎の基礎から教えようとなればアーキテクチャ→回路→トランジスタ→量子力学→世界の真理…となるわけです
私は、何を学ぶにしてもそれを学ぶ目的と、なぜそれが発達したかを重視します
非西洋圏の人間は大学生ですら知らないことが多いですが、近代自然科学は本来キリストの信仰のために17世紀ヨーロッパで発達しました。日本人は、運動の三法則にしろ、メンデルの遺伝の法則にしろ、諸現象はある法則があってそれに従って世の中が成り立っている世界観に抵抗がない人が多いですが、僕は子供のころからずっと理科の教科書に出てくる『法則』が存在することに疑問をいだいていました。ニュートンやガリレオがその疑問を抱かず『法則』の存在を疑いもせず研究をつづけたのは、その『法則』で世界を支配するとされる存在、キリスト教を信じていたからなのです(古代ギリシアなどにも科学はありましたが、近代科学の直接の源流とは言えないでしょう)
近代自然科学は神を信仰するため、なのでキリスト教国家であるフランスなどが科学アカデミーとかに金をぶっこんで発展したわけです。ロマンチックですよね、しかしコンピューター科学にも『アメリカが敵国に大砲の玉をぶち込む』という近代科学より美しい源流を持っています
というわけで前置きが長くなりましたが、この章では大砲を敵にぶち当てるための軸となる最低限必要なコンピューター最低限必要な計算機の動作と、そのプログラム方法(アセンブラ)を解説します。その後の章で、それより抽象度の低い回路と、抽象度の高いオペレーティングシステムやプログラムを解説するものとします
大砲を当てるには(もうちょっと詳しく)
かつてニュートンは言いました
「なんか、世の中の物体は動き方だいたいわかった気がする」
いろいろ調べてみるとどうやら、このど派手髪型のフランス人の言ってることは本当だったようで、それを計算するための微分積分とともに世の中の物体の動き方(以下運動)が解明されていきました
大砲を敵に当てるにはどうすればいいでしょう。物理の素養のある人であれば
「弾道は二次方程式に従うからそれに合わせて打てばいいよ」
と考えるでしょう。無理です
どうやらこの世には重力だけでなく、地球が回っていることによりかかる謎の力(コリオリの力)、空気抵抗、気流などがあり、それを考えた計算しなければいけませんらしいです
ニュートンのおかげで計算自体はわかるのですが、その計算量は人類の手には及ばないものでした。そこでアメリカはコンピューターの理論に目をつけ大量のお金を投資しました。そうして完成したものが以下で説明するもの(より低レベルなもの)です
前提
and、or、not、Dフリップフロップなどを組み合わせて後述するcometⅡが作れる。なぜ作れるかはのちの章で説明
- 定義:and回路
*1左の図でx1とx2を高い電位*+の電気がいっぱいあること にするとyが高い電位になり、それ以外では低い電位*ーの電気がry となる。なお次の図のように略記し、高電位を1、低電位を0とすると次の表で表せるなお、xは入力といい電位の操作は電源の+やーの部分につなげればいい、yは出力といわれており、ダイオードなどをつないで出力を確認したり、ほかの回路の入力につないだりして使う*無理やり出力同士をつないだり、電源につなげたりするとショートする、回路の実験ではショートが多発し実験室中が焦げ臭くなる
- 定義:or回路 andと同様に
- 定義:not回路 同様に
- 定義:Dフリップフロップ(簡易版) 下図でCLK(クロック)が0から1になった瞬間にQ(出力)の電位がD(入力)と同じになる。その瞬間以外ではQは電位を同じ値で維持し続ける
計算機cometⅡ
cometⅡとは、日本情報処理学会?が考案した計算機のモデルであり、現実には存在しないが、需要がないだけで作ろうとおもえば作れる(たぶん)。現実の計算機システムを簡易化したものだがこれを学べば十分現実のものについても理解できる。なお実際に弾道計算にアメリカ陸軍が用いたENIACの行った計算もこれを使えば再現できる(たぶん)
なおcometⅡのシミュレーターはCASLシミュレータ (CASL II 対応)様のサイトで公開されていて、いつでもプログラミングできます
また、cometⅡ内のデータはすべて0と1で管理されます。これは計算機器での信号や情報がすべて電位が高いか低いかで管理されることに由来します
cometⅡのアーキテクチャ
0~65535番と名前(番地、アドレスという)を付けられたメモリという命令とデータを記憶する場所が65536個ある。また命令とデータは0か1の信号16個で管理され、これを以後16bitという(現代のパソコンなんかは64個で管理され64bitパソコンなんて言い方もしますね)
16bitを記憶するためのレジスタが13個ある(ただしフラグ系のみ1bit)
- メモリに格納した命令の内、何番を実行しようとしているかをcometⅡがメモしておくためのポインタレジスタ(略称PR)
- あるタイミング(後述)で使うスタックレジスタ(SR)
- 条件分岐(後述)のために、前回の命令結果によって値が変化する3つのフラグレジスタ、ゼロフラグ(ZF)サインフラグ(SF)オーバーフローフラグ(OF)
- 一時的な記録に使うジェネラルレジスタ(GR0...GR7)がある
また入出力装置(キーボードと文字を表示するディスプレイ)も考えます
cometⅡの動作
まず、基本的な使い方ですが、あらかじめメモリに命令とデータを入れて置き、実行します。なおレジスタははじめすべて0000000000000000か0、スタックポインタのみ1111111111111111です
実行中にキーボードで何か打ったり、画面に表示されている文字を見て大砲の玉を飛ばす角度なんかの計算結果を取得します*現代のコンピューターは出力に音や画像を出力するのでcometⅡがかなり簡易的な作りであることがわかるでしょう?
動作は以下の通りです
- ポインタレジスタ(PR)に格納してある値(*データの示す16bitの信号を2進法ととらえ10進法に変換した数字のこと、例えば0000000000000001なら『1』を表し、0000000000000010なら『2』を表す、詳しくはググれ)を見る
- その値の番地のメモリのデータを見る。そこの16bitのデータを命令ととらえ実行する。命令の実行とはレジスタやメモリのデータを移すものであったりデータの値を足し算引き算するものであったりポインタレジスタの値を変更するもの(jump命令)であったりする、詳しくは後述*なお命令が32bitのものもあり、その場合はその次の番地のアドレスも同時に読み込む(以下2語命令という)
- ポインタレジスタ(PR)が次の命令の番地を指すように、PRの値を1増やし、再び1.ステップに戻る*ただし2.ステップで2語命令の場合は2増やし、jump命令(ポインタレジスタを直接いじる命令)を行った場合は増やさない
- もし2.ステップで実行した命令がRET命令(return)かつスタックポインタが1111111111111111なら動作を停止する*なおRET命令のデータは0100000100000000である。スタックポインタについては意味がわからないだろうがいずれわかる
要は0番のメモリから、1番、2番...って感じで順番に読み込んで実行するってことですね
大砲を飛ばす下準備
当然、みなさんが知りたいのは上のcometⅡの動き方ではなく、上のような動き方をした上でのミサイルや大砲の弾道計算ですよね
その準備としてまずは、7番地と8番地にある値を足し算して0番地に格納するだけのプログラムを見てみましょう。なお、今後データはめんどくさいので16bitを16進法4桁で記すものとし、今後何進法かを数字の横に明記するものとします*例えば0000000000000010(2)なら『0002(16)』、1111000011001001(2)なら『F0B9(16)』わからないなら『16進法』でググりましょう
アドレス データ(16進法)
0 | 1000(16)
1 | 0007(16)
2 | 2000(16)
3 | 0008(16)
4 | 1100(16)
5 | 0000(16)
6 | 8100(16)
7 | 000C(16)
8 | 000D(16)
上のデータをどーんとメモリに格納するわけです。*このようにメモリにぶち込む命令を考える作業をプログラミングといいます なお7,8番地には000C(16)と000D(16)が入っています。10進法で12と13です
各行解説すると
1000 0007 まずはここから命令を実行します。これは2語命令ですので0番地と1番地を同時に読み取った結果LD(ロード)これは7番地のデータをGR0に格納せよという意味ですのでGR0に000Cが入ります。命令を実行したのでアドレスポインタを進めて次は2番地を見ます
2000 0008 これは2語命令ADDA(算術和算)で、GR0の値と8番地の値を足し算せよとのことです。12+13で25、16進法で0019(16)がGR0に格納されます
1100 0000 2語命令ST(ストア)、GR0の値がメモリの0番地に格納されます。0番地にGR0の値が格納され、足し算の値をメモリに出力できました
8100 RET命令(return)、つまり動作を終了
000C 000D データを格納してある7,8番地目です
というように上から順番に命令を読んでいって足し算を実行するわけです
「それだけかよおい!」と思った人もいるかもしれません、しかしよく考えてみてください。足し算引き算はすべての基本の演算なわけです
- 掛け算はひっ算により足し算に帰着します。割り算も似たようなものです
- 積分は小さい値を何度も足し算することに帰着します
- 微分は引き算した後に割り算するのが基本的な思想です
- 高級なことを知っている方であれば関数解析に出てくる演算子も無限の行列(matrix)です、つまり足し算しまくれば近似できます
要は現代の科学の問題に対するほぼすべてのオペレーション(演算)がそろっているわけです。しかもこれを人間では考えられない速度で実行するわけです。ミスをしまくるポンコツホモサピエンスのあなたとどちらが優秀かは明白です
ただしこの時点では大きな問題があります。
上のようなプログラムで10万回足し算するには上のデータ格納部分と命令部分を50万行ほど書かなければいけません。しかし安心してください、これを何とかする命令があります
この続きは3章に回すこととして2章では実際どのようにand回路やDフリップフロップ回路で計算機(ここではcometⅡ)が作られていくかを考えます。抽象レベルの低い話題に興味のない方は2章は飛ばしてもらっても構いません
アセンブラに興味のある方へ
上でやった機械語プログラミング(直接メモリに数字を入れるプログラミング)はしんどいので、実際にはアセンブラ言語というものを使うと、上で示したプログラムは
SUM START
LD GR0,7
ADDA GR0,8
ST GR0,0
RET
DATA1 DC 12
DATA2 DC 13
END
と書けます。例えばLD GR0,7の行は0,1番地に格納されていた1000 0007を表します
実際に上のコード(プログラムの文字列のこと)をこのサイトのソースの部分にぶち込んでアセンブルボタンを押すと機械語翻訳した値をメモリに格納してくれます(このサイトでその様子が見れます。さらになんと実行もできます)
アセンブラ言語(cometⅡのアセンブラ言語のことはcasl2という)については
C/C++/C#/Java/BasicプログラマのためのCASL II 入門講座
様方のサイトを参照し勉強するのが良いでしょう