シリーズ「コンピュータの CPU のしくみ」 第6回

回路が「繰り返す」とは?

足し算しか、できない

ここまでの 5 回を、短く振り返っておこう。

手順を 0 と 1 のパターンにしてメモリに並べ、上 4 桁をデコーダに通して入口を開き、「読む拍」と「こなす拍」を交互に回した(第1〜3回)。 番地のカウンタに命令の下 4 桁を書き込めば飛べる(第4回)。 引き算のあとのランプ C は「引く前の数が、その数以上」を覚えていて、そのランプで飛ぶかどうかを選べた(第5回)。

前回の終わりに、こう書いた。

飛ぶとランプなら飛ぶを組み合わせると、同じ命令を何度も通り、決めたところで抜ける道が作れる。 けれど、この回路にできる計算は、足すと引くだけだ。

足し算しかできない回路に、掛け算をさせられないか。

掛け算の回路を新しく作る、という手もある。 けれど、この回はゲートを 1 つも足さない。 前回と同じ回路のまま、メモリの中のパターンだけで、掛け算をさせてみる。

足し算を、繰り返す

3 × 5 は、3 を 5 回足すことだ。

0 から始めて、3、6、9、12、15。 5 回足したら、やめる。

人間なら、指を折って数える。 回路には指が無いので、数えるのも、メモリに置いた数でやる。

メモリに置くものは、2 つ。

  • 答え … 0 から始めて、3 ずつ増えていく
  • 残りの回数 … 5 から始めて、1 回足すたびに 1 ずつ減らす

残りの回数が 0 になったら、やめればいい。

では、「0 になった」は、どうやって知るか。 この回路が見られるのは、ランプ C だけだ。

ここで前回の引き算が効いてくる。 残りの回数から 1 を引くと、ランプ C は「引く前の回数が、1 以上」のときに点く。 つまり、まだ足す回数が残っていれば点き、もう 0 だったら消える。

ランプが点いていれば、続ける。消えていれば、止まる。 数えることも、やめどきを知ることも、前回までの命令でできる。

ただ、順番には気をつけたい。 「数えてから足す」と「足してから数える」。どちらでもよさそうに見える。

下の図で、回数 N を押して比べてみてほしい。掛けられる数は 3 にしてある。

回数 N(掛けられる数は 3)

数えてから足す(この回のプログラム)

  1. 残り 5・点く
  2. +3 → 3
  3. 残り 4・点く
  4. +3 → 6
  5. 残り 3・点く
  6. +3 → 9
  7. 残り 2・点く
  8. +3 → 12
  9. 残り 1・点く
  10. +3 → 15
  11. 残り 0・消える
  12. 止まる

足した回数 5 回 → 9 番地は 15(3 × 5 = 15)合う

足してから数える

  1. +3 → 3
  2. 残り 5・点く
  3. +3 → 6
  4. 残り 4・点く
  5. +3 → 9
  6. 残り 3・点く
  7. +3 → 12
  8. 残り 2・点く
  9. +3 → 15
  10. 残り 1・点く
  11. +3 → 2
  12. 残り 0・消える
  13. 止まる

足した回数 6 回 → 9 番地は 2(3 × 6 = 18 の下 4 桁で 2)1 回多い

緑の札が、回数から 1 を引いてランプ C が点いたところ、黄色の札が答えに 3 を足したところ。回数が 0 になる回も、引く前の回数は 1 なのでランプは点く。足してから数えるほうは、そのランプを見て、もう 1 周してしまう。

足してから数えるほうは、いつも 1 回多く足す。 3 × 5 なら、3 を 6 回足して 18。4 桁に収まらず、残るのは 2 だ。 回数を 0 にしても、見る前に 1 回足してしまうので、答えは 3 になる。

原因は、ランプの意味にある。 最後の 1 回、残りの回数を 1 から 0 にするときも、引く前は 1 なので、ランプは点く。 足してから数えるほうは、そのランプを見て、もう 1 周してしまう。

だから、先に数えて、足すのはそのあとにする。 残りの回数から 1 を引いてみて、ランプが点いたら(まだ残っていたら)、1 回足す。

プログラムを、読む

この順番で書いたのが、下のプログラムだ。13 語ある。

番地中身読み方
00001 1011持ってくる 11 番地
10100 1100引く 12 番地
20110 0100ランプなら飛ぶ 4 番地
31111 0000止まる
40011 1011書き込む 11 番地
50001 1001持ってくる 9 番地
60010 1010足す 10 番地
70011 1001書き込む 9 番地
80101 0000飛ぶ 0 番地
90000 0000答えを置く場所
100000 0011数 3(掛けられる数)
110000 0101数 5(残りの回数)
120000 0001数 1(回数から引く 1)

上から、4 つのまとまりに分けて読める。

  • 0〜3 番地:回数を確かめる。 残りの回数を持ってきて、1 を引く。ランプ C が点いていれば 4 番地へ飛ぶ。消えていれば、そのまま 3 番地の止まるに進む
  • 4 番地:回数を書き戻す。 1 減らした回数を、11 番地に書き込む
  • 5〜7 番地:答えに足す。 答えを持ってきて、3 を足し、9 番地に書き込む
  • 8 番地:戻る。 0 番地へ飛んで、また回数を確かめる

計算するたびに、持ってきて、計算して、書き戻している。 A の箱は 1 つしかないからだ。 回数を減らすときも、答えに足すときも、A の箱を使う。だから、使い終わったらメモリに返しておく。

12 番地の 1 は、「1 を引く」ためだけに置いた数だ。 この回路の引くは、いつも「その番地の数」を引く。1 を引きたければ、1 をどこかの番地に置いておくしかない。

もう 1 つ、細かいところ。 答えが 15 を超えると、6 番地の足すでランプ C が点く(4 桁からあふれた)。 けれど、そのランプを見る 2 番地の前に、1 番地の引くが、ランプ C を「引く前の回数が 1 以上か」で上書きする。 だから、あふれのランプを、続けるかどうかと取り違えることはない。

押して、確かめる

繰り返す シミュレータ

「自動で進める」を押して、9 番地の答えと 11 番地の残りの回数がどう変わるかを見てください

拍の状態読む拍クロックの門開次の番地を選ぶ+1+1番地のカウンタ0000 = 0命令の置き場0000 0000デコーダ0011000101000010111101010110番地を選ぶ0000 = 0メモリ(16 語)0001 1011メモリ → A の箱閉反転加算器 → A の箱閉A の箱0000 = 0加算器1011 = 11+1ランプ C0クロック0

回路は前回とまったく同じ(1900 ゲート)。変わったのは、メモリの中のパターンだけ。「次の番地を選ぶ」の大きな文字が、いま通しているほう

速さ
拍
0
こなした命令
0

はじめは読む拍。番地のカウンタは 0。次の番地を選ぶ箱は、いつもは「+1」を通している。クロックを入れると、0 番地の語が命令の置き場に届く。

読んだ番地の順:まだ読んでいない

拍ごとの記録

拍段階番地起きたこと
まだクロックを入れていない
番地中身読み方(人間のメモ)
0000(0)持ってくる 11 番地
0001(1)引く 12 番地
0010(2)ランプなら飛ぶ 4 番地
0011(3)止まる
0100(4)書き込む 11 番地
0101(5)持ってくる 9 番地
0110(6)足す 10 番地
0111(7)書き込む 9 番地
1000(8)飛ぶ 0 番地
1001(9)0000 0000数 0(答えを置く場所)
1010(10)0000数 3(掛けられる数)
1011(11)0000数 5(残りの回数)
1100(12)0000 0001数 1(回数から引く 1)
1101(13)0000 0000
1110(14)0000 0000
1111(15)0000 0000

青い行は、読む拍で使う番地(番地のカウンタ)。琥珀色の行は、こなす拍で使う番地(命令の下 4 桁)。0〜8 番地は 8 桁すべてを、10・11 番地(掛けられる数と回数)は下 4 桁を、押して替えられる(替えると、はじめからやり直す)

図は、前回とまったく同じだ。ゲートは 1 つも増えていない。 変わったのは、下の表のパターンだけだ。

「クロックを入れる」で何拍か進めたら、「自動で進める」を押してほしい。 88 拍あるので、速さは「速く」にすると見やすい。

0 番地から 8 番地までを通って、0 番地に戻る。これで 1 周、8 命令・16 拍だ。 1 周するたびに、11 番地の残りの回数が 1 減り、9 番地の答えが 3 増える。

周終わった拍11 番地(残りの回数)9 番地(答え)
はじめ050
11643
23236
34829
464112
580015

5 周したあと、もう一度 0 番地から。 残りの回数 0 から 1 を引くと、ランプ C は消える。 2 番地のランプなら飛ぶは飛ばず、3 番地の止まるを読んで、88 拍で止まる。 9 番地は 15。3 × 5 の答えだ。

止まったとき、A の箱には 1111(15)が残っている。 これは答えではない。最後に 0 − 1 を計算した残りだ。0〜15 の読み方では引ききれず、1111 になった(前回の 3 − 7 と同じ)。 3 × 5 では、たまたま答えと同じ 15 になる。答えは、あくまで 9 番地だ。

表の 10 番地と 11 番地は、押して替えられる。 10 番地を 0101(5)、11 番地を 0011(3)にして、5 × 3 を試してほしい。答えは同じ 15 なのに、56 拍で止まる。

答えが 15 を超える組では、9 番地には下 4 桁だけが残る。 たとえば 4 × 5 = 20 なら、残るのは 4。16 × 16 の 256 通りのうち、4 桁に収まるのは 76 通りだ。 どの組でも、9 番地には X × N の下 4 桁が入り、必ず止まる。

何拍、かかるか

5 × 3 のほうが速かったのは、なぜか。

1 周は、いつも 8 命令だ。足す数が 3 でも 5 でも、1 周の長さは変わらない。 変わるのは、何周するか。それを決めるのは、残りの回数 N だけだ。

そして最後に、回数を確かめて止まるまでの 4 命令(0・1・2・3 番地)がある。

こなす命令の数は、8N + 4。 1 命令は 2 拍なので、拍はその 2 倍になる。

N = 0:8 拍0N = 1:24 拍1N = 2:40 拍2N = 3:56 拍3N = 4:72 拍4N = 5:88 拍588N = 6:104 拍6N = 7:120 拍7N = 8:136 拍8N = 9:152 拍9N = 10:168 拍10N = 11:184 拍11N = 12:200 拍12N = 13:216 拍13N = 14:232 拍14N = 15:248 拍15

横は回数 N、縦は止まるまでの拍の数

N = 5:こなす命令は 8 × 5 + 4 = 44 個、拍はその 2 倍の 88。回数が 1 増えるごとに、16 拍ずつ伸びる。掛けられる数をいくつにしても、拍の数は変わらない。

3 × 5 は N = 5 で 44 命令・88 拍。5 × 3 は N = 3 で 28 命令・56 拍。 N が 1 増えるたびに、16 拍ずつ伸びる。いちばん長いのは N = 15 で、248 拍だ。 1 回も足さない N = 0 でも、回数を確かめるのに 8 拍かかる。

掛け算の答えは、時間をかけないと出てこない。 しかも、掛ける回数が大きいほど、時間がかかる。

回路で解くなら

掛け算をするだけなら、別のやり方もある。

『計算のしくみ』の第6回では、掛け算の回路を作った。 2 進法の掛け算の筆算を、AND と加算器でそのまま組んだものだ。 部分積を一度に足すアレイ方式と、加算器 1 つで 1 本ずつ足すシフト加算方式を、こう比べていた。 そこに、この回のプログラムを 1 列足してみる。

アレイ方式シフト加算方式この CPU のプログラム
AND16 個4 個0 個(足していない)
4 桁加算器3 つ1 つ1 つ(もとからある)
覚えておく場所要らない要るメモリの 4 語(もとからある)
かかる手順1 回(通り抜けるだけ)4 回8N + 4 命令(最大 248 拍)

アレイ方式は、部品を並べて、一度に答える。 この CPU は、何も並べない。もとからある加算器 1 つに、同じ足し算を何度も通す。そのかわり、いちばん時間がかかる。

この箱のゲートは、1900 個ある。 けれど、掛け算のために足したゲートは、0 個だ。 足したのはメモリの中の 13 語、それも、もとからある 128 桁のどこかを 0 にするか 1 にするかを決めただけだ。

1900 個の内訳と、掛け算の回路を足した場合を並べてみよう。

合わせて 1900 個

  • メモリの 128 桁を覚える1408
  • メモリの語を選ぶ・書く172
  • ほかの覚える箱(19 桁)209
  • 加算器と反転の箱24
  • そのほか(デコーダ・入口・選ぶ箱・クロック)87
この回の掛け算のために足したゲートは 0 個。プログラムの 13 語は、もとからある「メモリの 128 桁」の中に置いただけだ。そのかわり、3 × 5 に 88 拍かかる。

1900 個のうち 1408 個は、メモリの 128 桁を覚えているフリップフロップだ。 掛け算のプログラムは、その中に住んでいる。

計算の第6回は、2 節で「足し算を繰り返せばいいのでは」と考えて、すぐにやめていた。 32 桁なら最大で約 43 億回かかる、というのが理由だった。 この CPU がしているのは、まさにそのやめたやり方だ。 4 桁なら最大 15 回で済むので、248 拍で終わる。けれど、桁が増えれば、待ち時間はどこまでも伸びる。

4 桁の CPU 4004 にも、掛け算の命令は無かった。45 種類の命令のどれも、掛け算ではない。 掛け算をしたければ、足し算などの命令を並べたプログラムを書くことになる。 そのかわり、繰り返しのための命令はあった。ISZ という命令で、レジスタに 1 を足し、0 になっていなければ飛ぶ。回数を数えて、飛ぶかどうかを決めるところまでを、1 つの命令でこなしていた。 のちの CPU、たとえば 8086 には、掛け算の命令がある。

部品か、時間か

同じ掛け算を、2 通りに解いた。

  • 部品を増やして解く。 AND と加算器を並べて、一度に通り抜ける
  • 手順を繰り返して解く。 もとからある加算器に、同じ足し算を何度も通す

アレイ方式の回路は、速い。けれど、掛け算しかできない。 この CPU は、遅い。けれど、掛け算のために何も作り足していない。同じ 1900 個のゲートが、配線を 1 本も変えずに、3 + 5 も、前回の「大きいほう」も、掛け算もこなす。

部品を増やして解くか、手順を繰り返して解くか。CPU は、部品を増やすかわりに時間を使う。

『記憶のしくみ』を読んだ人は、第6回の「同じ加算器を、何度も」を思い出してほしい。足す数を 3 にしてクロックを 5 回入れると、3、6、9、12、15 と、3 × 5 の答えが出た。あのとき「覚える回路を手に入れたことで、回路は時間を使えるようになった」と書いた。ただし、5 回で止めていたのは、クロックを押していた人間だった。いまは、回数を数えるのも、止めるのも、メモリの中のプログラムがやっている。

手順を繰り返すために要ったのは、3 つだけだ。

  • 戻る(飛ぶ)
  • やめどきを知る(引いて、ランプなら飛ぶ)
  • 途中の数を覚えておく(メモリ)

どれも、前回までにそろっていた。この回で足したのは、パターンだけだ。

表を、まとめて

掛け算はできた。 では、次はこんなことを考えてみる。

3 つ、4 つの数の合計を取りたい。 たとえば、12・13・14・15 番地に並べた 4 つの数だ。

いまの命令で書くなら、こうなる。

  • 足す 12 番地
  • 足す 13 番地
  • 足す 14 番地
  • 足す 15 番地

足す命令を、数の数だけ並べる。 4 つなら 4 命令で済む。けれど、メモリは 16 語しかない。数が増えると、命令を置く場所が足りなくなる。

掛け算のときのように、1 つの「足す」を、何度も通したい。 ただ、掛け算では、毎回同じ 10 番地の 3 を足せばよかった。 今度は、1 周するたびに、足す番地を 12、13、14、15 と1 つずつずらしたい。

ところが、「足す 12 番地」の 12 は、命令の下 4 桁に書き込まれたパターンだ。 プログラムが動いているあいだ、ずっと 12 のままだ。

命令の中の番地を、1 つずつずらすことはできないか。

次回は、命令そのものを、数として眺めてみる。

参考文献