2015年1月27日火曜日

Christopher StracheyのGPM

このブログでGPMのことを書いたのは2013年12月だから1年以上前になる. 英国の計算機保存団体の発行するComputer Resurrectionの昨年の夏号にStrachey's General Purpose Macrogeneratorという記事を見付けたのを切っ掛けに元の論文にあった例題以外のマクロを書こうと思いたった.

かなりたくさんのマクロを書いたので, GPMの楽しさを紹介しようとして今年のプログラミング・シンポジウムでそれに関する話をした.

これとそれに続く何回かのブログでは, それらのマクロについて書きたい.

まず簡単におさらいをしておく.

"$def,a,<b~1d>;" はマクロ "a" が "<b~1d>" であると定義する. "$a,c;" でマクロ "a" を実引数 "c" をもって呼び出すと, マクロ本体の第1引数 "~1" に "c" が代入されて "bcd" が返る. "~0" は第0引数を表わすから, マクロ "a'" を "$def,a',<~0b~1d>;" と定義し "$a',c;" と呼び出すと "a'bcd" が返る.

マクロ呼出しは "$マクロ名, 実引数1, 実引数2, ... ;" の形で, マクロ名も実引数もまず評価される, つまりマクロ呼出しがあれば実行し, 引数"~n" があれば置き換える. それ以外はそのまま. 評価を避けたい時は"<" と ">" で囲むが評価が済むと両端の "<" と ">" がなくなる.

上の "a" の定義があった時, "$a,$a,c;;" と呼び出すと, 第1引数 "$a,c;" がまず評価されて "bcd" になり, それで "$a,bcd;" と呼び出すから "bbcdd" が返る.

マクロ呼出しの実引数の並びの中に局所定義を書くことが出来る. 定義されてから呼び出すから, "$a,c,$def,a,<b~1d>;;" のように書くと, "bcd" が返る. 局所定義は呼出しが終わると消える.

同じマクロ名の定義はスタック状に記憶され, 最新のものを使う. 定義が消えるともとの定義が見えるようになる.

"$def,a,b;" と定義しておき, "$a,$def,a,c;,$def,a,d;;" と呼び出すと, "a" の最後の定義が "$def,a,d;" だから "d" が返り, それが済むと最初の定義がスタックの上に来るから, ここで "$a;" と呼び出すと "b" が返る.

この機能を使うと条件式が書ける.

"(if (eq? α β) γ δ)" は"$α,$def,α,δ;,$def,β,γ;;" と書く. αとβが同じ時は, 2つの局所定義の後のもαになるからそちらを使い, γになり, 違う時は, 前の局所定義を使い, δになる.

これだけ知っていれば, GPMのマクロを書くことが出来る.

基本演算

GPMには算術演算もないから, その辺から始めなければならない. 最初は引数に1を足すマクロ "1+". 定義と使用例は次のとおり.
$def,1+,<$1,2,3,4,5,6,7,8,9,10,$def,1,<~>~1;;>;
$1+,0; => 1
$1+,4; => 5
$1+,9; => 10
"$1+,4;" の場合, マクロ "1+" の本体 "$1,2,3,4,5,6,7,8,9,10,$def,1,<~>~1;;" が評価される. つまりマクロ "1" を実引数 "2,3,4,5,6,7,8,9,10" で呼び出す. その"1"の局所定義は後にあり, "~1" に "4" が代入されて "$def,1,~4;" になっている. 従って第4引数 "5" が返る.

$def,1-,<$-1,0,1,2,3,4,5,6,7,8,$def,-1,<~>~1;;>;
$1-,0; => -1
$1-,9; => 8
"1-"も上のマクロ "1+" と殆ど同じ. 引数が"~"の後の1文字なので, ここで使う整数は0から9までということになる.

$def,+,<$~1,
 $def,~1,<$1+,$+,$1-,>~1<;,>~2<;;>;,
 $def,0,~2;;>;
$+,0,3; => 3
$+,3,5; => 8
マクロ "+" は条件式になっている. 最後の行 "def,0,~2;" は, 第1引数が "0" なら第2引数が答, そうでないなら, 第1引数から1を引いたものと第2引数と足してそれに1を足すと再帰的に定義する.

Scheme流に書けば
(λ (x y) (if (= x 0) y (1+ (+ (1- x) y))))

$def,-,<$~2,
 $def,~2,<$-,$1-,>~1<;,$1-,>~2<;;>;,
 $def,0,~1;;>;
$-,3,0; => 3
$-,6,4; => 2
第2引数による条件式で, 0なら第1引数が答. そうでないなら第1引数から1を引いたものから第2引数から1を引くと再帰的に定義する.

(λ (x y) (if (= y 0) x (- (1- x) (1- y))))

$def,*,<$~1,
  $def,~1,<$+,$*,$1-,>~1<;,>~2<;,>~2<;>;
  $def,0,0;;>;
$*,0,5; => 0
$*,1,4; => 4
$*,2,3; => 6
(λ (x y) (if (= x 0) 0 (+ (* (1- x) y) y)))

$def,lt,<$~1,
  $def,~1,<$p,>~1<,>~2<,$def,p,$lt,$1-,>~1<;,>~2<;;;>;
  $def,-1,t;$def,~2,f;;>;
$lt,-1,-1; => f
$lt,-1,0;  => t
$lt,0,-1;  => f
ltつまり<は, この世界の下が-1までであることを利用している.

(λ(x y) (cond ((= x y) f) ((= x -1) t) (else (lt (1- x) y))))


これを使って剰余を書く.
$def,r,<$$lt,~1,~2;,
 $def,t,~1;,$def,f,<$r,$-,>~1<,>~2<;,>~2<;>;;>;
$r,9,5; => 4
(λ (x y) (if (< x y) x (r (- x y) y)))

基本演算ばかりでは面白くないので, 応用として情報科学標準問題のHanoiの塔をやってみよう.

Hanoiの塔

$def,1-,<$-1,0,1,2,3,4,5,6,7,8,
 $def,-1,<~>~1;;>;
$def,hanoi,<$~4,
 $def,~4,<$hanoi,>~1<,>~3<,>~2<,$1-,>~4<;;
  +>~1<->~3<+
  $hanoi,>~2<,>~1<,>~3<,$1-,>~4<;;>;
 $def,0,<>~1<->~3<>;;>;
$hanoi,a,b,c,0; => a-c
$hanoi,a,b,c,1; => a-b+a-c+b-c
$hanoi,a,b,c,2; 
=> a-c+a-b+c-b+a-c+b-a+b-c+a-c
$hanoi,a,b,c,n;はn枚の円板をaからcへbを中継点として移動する手続きで, nが0なら$def,0,にあるように ~1-~3とする. -は引き算ではなく, 引数の間に-を書くことである. そうでないなら, $hanoi,~1,~3,~2,$1-,~4;; をやり +~1-~3+とし, $hanoi,~2,~1,~3,$1-,~4;; をやると定義してある.

結果は上のようだ. 次のブログへ続く.

2015年1月12日月曜日

Piの近似値

Martin GardnerのPiの話を読んでいたら, 下のような図があり, おやっと思った.


円の半径を1とすると, FGの長さが16/113になるというのだ. 本当かと思い, 計算してみた.

図はこのように描いてある. ACGは半径1の円の1/4である.

BはCAのCから7/8の点, DはBGのGから1/2の点である.

DからACと平行にDEを引き, CG上の点をEとする. DからBEと平行にDFを引き, CG上の点をFとする.

BGの長さを計算すると BG2=(7/8)2+12=(49+64)/82 =113/82

BCGとDEGは相似だから EG/CG=DG/BG ∴ EG=CG*DG/BG

BEGとDFGは相似だから FG/EG=DG/BG ∴ FG=EG*DG/BG

この2つから FG=CG*DG/BG*DG/BG

CG=1だから FG=DG2/BG2=(1/4)/113/82=16/113

もう何だか分った人も多いだろうが, この値に3を足すと(3*113+16)/113=355/113で円周率の近似値になるのである. つまりFGは円周率の小数部分であった.

2014年12月15日月曜日

微分解析機

このブログの8月25日のにフロントラッシュの話を書き, 遊星歯車の話にもなった. また遊星歯車が話題だ.

微分解析機の加算器は差分歯車や遊星歯車を使うといわれているが, 理科大の微分解析機の加算器は見慣れない形をしていた. どうなっているのか.

下の図も一種の遊星歯車である. 左が軸方向から見た図. 同径, 同歯数の歯車6枚があり, Bの2枚とCの2枚は遊星キャリアに乗っている.

中央にあるAとDは, キャリアと同じ軸に乗っているが, A,D,キャリアは軸には固定されていない. 歯車による束縛条件はあるが, その下で自由に回転できる.

この図を左方から見たのが右の図で, 遊星歯車のBとCは見ての通り, 深さ方向にずれて配置されている. つまりAはBと左図の緑の点で接し, BとCは黒い点で接し, CはDと橙色の点で接している.


まずキャリアを固定し, Aを反時計方向に回転する. するとBは時計方向に, Cは反時計方向に回転する. ゆえにDは時計方向に回転するわけだ. 歯車の歯数が同じだから, 回転の角度も同じである.

隣同士の歯車は逆に回る. 中間歯車が1個なら同じ方向に回る. AとDは間に歯車が2個あるから, 逆に回る.

回転角を記述するのに, 反時計回りを正として, 歯車Aの回転角をφ, Dのをψ, キャリアのをθと表すことにする. そこでこれを

θ=0の時φ=-ψ

と書いておく.

キャリアがAと一緒に回転すると, この図がこのまま回転するから, DもAと同様に回転する.

φ=θ=αの時ψ=α

と書いておく.

すこし様子が分って來たが, もう少し図を示すと, 下図の0は上の図のA, 右上のB, 右下のCとDを書いたものだ. AとDが重さなっているから, A,B,CとB,C,Dと図を分割してある. 緑色の点はA,Bの接点, 黒の点はB,Cの接点, 橙色の点はC,Dの接点で, これも上の図と同様. 歯車やキャリアが回転すると, これらの点は付いて一緒に回る.


一段下って1の図はAを60度回転したものである(φ=60°.) Dは固定してある(ψ=0°.) すなわちDの橙色の点は0の図と同じ位置にある. するとキャリアは30度回転する(θ=30°.) キャリアの向きはAやDから見たBとCの接点の方向である.

図2はφ=120°, ψ=0° θ=60°の時だ.

3では今度はDを逆回転してみた. φ=120°, ψ=-60° θ=30°

4ではDを更に逆回転した. φ=120°, ψ=-120° θ=0°

つまり, この遊星歯車では φ+ψ=2θ となっている.

私の2010年1月4日のブログでは, 太陽歯車の半径r, 回転角φ, 内歯車の半径R, 回転角ψ, キャリアの回転角θとすると,

θ=φ*r/(R+r)+ψ*R/(R+r)

とした. 今回の歯車は遊星歯車を2段にし, 内歯車を太陽歯車と同様にした. だからr=Rとなり, それから上の式が導ける.

さて下は東京理科大の微分解析機の加算器の写真である. ベイとベイを繋ぐバスボックス内にある3本(手前からX,Y,Zとする)のスタブシャフトに設置してある.



中央のYに乗っているのが, 上述の遊星歯車である. 手前のXの歯車と噛んでいる平歯車と一緒になっている真鍮のがAである. その左, Aと噛んでいる手前と向こうのがB, Aからは離れているが, Bと半分の幅で噛んでいるのがC, Cの奥にDがあるのだが, それは見えていない. DはY軸に固定されている.

真鍮の歯車群の左にあるのがキャリアで, 2:1で向こう側のZ軸の平歯車と噛んでいる. φ+ψ=2θだったので, θを2θにしているわけだ.

これを図にしたのが次だ. 円内下端の数は歯数である. 軸が破線で描kてあるのは, 固定していないことを示す.



これまで何回ものブログで微分解析機の殆どの仕掛けが分ったように思う.

2014年12月12日金曜日

クリスマスの歌

今年もクリスマスが近付いた. 最近の私にそういう機会はないが, 以前はクリスマスキャロルに誘われて聞きにいったりした.

そこで歌われる歌の中に, On the first day of Christmas,... で始まる歌がある. 出典によって文句や順番が多少違ったりするが, 大体はこうだ.

On the 1st day of Christmas,
my true love sent to me:
a Partridge in a Pear Tree.

On the 2nd day of Christmas,
my true love sent to me:
2 Turtle Doves
and a Partridge in a Pear Tree.

クリスマスの1日目, 私の恋人は
ナシの木の1羽のヤマウズラをくれた.

クリスマスの2日目, 私の恋人は
2羽のヤマバトと
ナシの木の1羽のヤマウズラをくれた.
つまり
クリスマスのn日目, 私の恋人は
n個のなにか, 
n-1個のなにか,
...
2羽のヤマバトと
ナシの木の1羽のヤマウズラをくれた.
と12日目まで続く.

結構規則的なので, 記号処理プログラムで生成するのが簡単であり, その昔Snobol 4の手引き書に出ていたのを覚えている. さらにこの歌詞をAlgol 68での階乗のプログラムにしたという話もあるが(The Most Contrived Factorial Program, Allgol Bulletin 42 (1978)), それはまたいつか書くこともあろう.

私自身はエディタのTecoで書いたことがあった. (bit Vo. 14, No. 10, 1982) 最近のことだが, GPMでも書いてみた. いろいろ苦労したが, 超絶技巧な技を利用. それについてもまたいつか書きたい.

TecoとかGPMのようなプリミティブなプログラム言語とは別に, Schemeで書くとどうなるか. やはり超簡単の単であった. 以下の通り.
(define a '
("12 Drummers Drumming\n"
 "11 Pipers Piping\n"
 "10 Lords a Leaping\n"
 "9 Ladies Dancing\n"
 "8 Maids a Milking\n"
 "7 Swans a Swimming\n"
 "6 Geese a Laying\n"
 "5 Golden Rings\n"
 "4 Colly Birds\n"
 "3 French Hens\n"
 "2 Turtle Doves\nand "
 "a Partridge in a Pear Tree.\n"))
(define b '
("12th" "11th" "10th" "9th" "8th" "7th" 
 "6th" "5th" "4th" "3rd" "2nd" "1st"))
(do ((i 11 (- i 1))) ((< i 0))
(display (string-append
"\nOn the " (list-ref b i) 
" day of Christmas,\nmy true love sent to me:\n"
(apply string-append (list-tail a i)))))
ところで私は何となく贈りものの数を1+2+...+12=78だと思っていたが, ヤマウズラは12日間贈り, 2羽のヤマバトは11日間贈るので, 総計は1*12+2*11+3*10+...+12*1だったらしい. 丁度途中の6*7+7*6のところで折り返すから,1*12+2*11+3*10+4*9+5*8+6*7まで計算し2倍する.

4*9+5*8+6*7は簡単かもしれない.


赤の枠が5*8, 緑の枠が4*9. 青の枠が6*7である. 青の斜線部分を左に1だけ移動し, 赤枠内に移動すると, 緑の斜線部分と合せて赤枠の面積マイナス1になり, 緑の残り部分と青の残り部分も赤枠-1になるから, 5*8*3-2=118だ.

1*12+2*11+3*10も同様に2*11*3-2=64. 全体は(118+64)*2=364だ. 1年の日数に1日足りないのか.

2014年12月2日火曜日

EDSACのプログラム技法

前回のブログではy' を計算するサブルーチンにジャンプするところまで述べた.

今回はそのサブルーチンの中身の説明から始めよう.



0行目と1行目はlinkageで主ルーチンにもどる命令を作り, 22行目に入れる.

2行目は8Dにあるt' をアキュムレータに取り出す. 3行目のEはアキュムレータが正ならジャンプする条件ジャンプ命令なので, t' が正なら6行目(6番地)へ行く. 負の場合は8Dを2回引いて正にし, 6行目で合流した時には絶対値になっている.

平方根をとるサブルーチンS2は, 被開平数を4Dで渡すのでT4D. そしてS2へジャンプ. 結果は4Dにある.

9行目からはt' の3乗を計算する. まずH8Dで8Dにあるt' を乗数レジスタに置く. V8Dで2乗をつくり, TDで0D番地へ置く. VDで3乗が出来る.

13行目のRDはアキュムレータの1ビット右シフトである. EDSACのシフト命令のシフト数は, 命令の一番右のビットの位置で決まる. だからRDなら1ビット右シフト. R1Fなら2ビット, R2Fなら3ビット, R4Fなら4ビットである. 番地部が2nならn+2ビットシフトする. 命令語の上の方のビットは影響しないから, 1ビットシフトは番地部が奇数ならなんでもよい.

14行目からは5倍の作業だ. まずUDで0D番地に入れる. UはTと同様な格納命令だが, アキュムレータをクリアしない. L1Fで左へ2ビットシフトで4倍する. それに0Dにあった元の数を足して5倍を実現する. それを一旦0Dへ退避し, 4Dにあった平方根をとりだし, R512Fで(512は2^9なので)右へ11ビットシフトし, 2-11倍する. それに5.2-1t' 3を加え, y' を8Dに置いて主ルーチンに戻る. T8Dをやったので, アキュムレータは0になり, 0は正なので, 正ジャンプのE命令で戻ってきた.

以後の作業には前回のブログのプログラムを見てほしい.

18行目. y' を乗算レジスタに入れる. 19行目. アキュムレータに取り出す.

次はyが400を超えるときはtoo largeと出力する仕様なので, y' を400.2^-13と比較する.

アドレスのところが2^-15だから, P400*2^2Fと比較すればよく, 5Mのような定数になる. ただ5MにはP1600Dとあるから, 400.125*2^-13と比較している. ちょうど400の時はtoo largeにしたくないというつもりだろうか.

21行目のGは負ジャンプ. 400を超えた場合は22番地で999.2^-13を乗算レジスタに入れる. これも定数はアドレス部に999の4倍が書いてある. (too largeと出力する代わりに999で代用する.)

ところで, これは正の方向に400を超ているを見ているだけで, -400より小さいときは別になにもしないようだ. 不思議な仕様である. 他の例を見ても正の場合しか考慮していない.

続いて213=8192を掛けるのだが, 全体を小数にする必要があるので, 十進の小数の4桁目に小数点が來るように, 213/10000を掛ける.

乗算VnDは, 乗算レジスタにある数とnDにある数を掛けてアキュムレータに足すから, TDでアミュムレータをクリアしておく. そして2MDにある数を掛ける. しかし2MDのようにコードレターを2つ書くことは, EDSACのイニシアルオーダーでは出来なかったので, Dの代わりにπを書いていた.

2Mと3Mにあるのが, 213/10000である. なぜそうかというと

0.819210= 0.110100011011011100010111010110001110001000011001011012

これをEDSACの短語に対応させてみると, EDSACでは偶数語が右, それ+1の奇数語が左なので



のようになる. この図で見ると2Mの語の最後はDだが, プログラムではFなのはこの辺で切り捨てたのであろうか.

掛け算が済んだので, 0Dに入れ, 小数の印刷ルーチンP14を呼ぶ.

このP14は28行目にあるプログラムパラメタで, レイアウトできる.

例えば小数0.123456789を
のように出力するには, スペースの位置の下の数と, 最後の桁の上の数を足したものがパラメタの値であり, 28行目は3072+32=3104となっている.

この後はt を取り出すアドレスと, カウンタi の変更が残っている.

29行目は14行目の命令を40D, 38D, ...と減らすもので, A14θ, S 9M, T 14θ で変更する. 32行目からは, A 7M, S 8M, U 7Mとカウンタを減らし, 35行目で, アキュムレータに正の数が残っていれば, 3番地へ戻る. 負になったらジャンプせず, 36行目のZ命令で停止する.

論文にあった出力はこうだ.
10  0000 04472
 9  0000 03162
 8  0999 00000
 7 -0044 85586
 6  0047 75414
 5  0999 00000
 4  0062 35157
 3  0999 00000
 2 -1077 55051
 1  0999 00000
    0018 09974
最下行 0 がプリントされていない. 論文には印刷ルーチンの虫だろうと書いてある. そうだろうか.

私がSchemeで計算したのは次だ.
(define (tpk t)
(+ (sqrt (abs t)) (* 5 (expt t 3))))

(define data 
'(1.5 8 -6 9.5 2.3 9.9 2.1 -2.1 6 0.001 -0.002))

(map tpk data)
=>
(18.09974487139159 2562.828427124746 -1077.5505102572167
 4289.957207001485 62.35157508881029 4854.641426544511
 47.75413767461895 -44.85586232538106 1082.4494897427833
 .03162278160168379 .044721319549995794)
という次第でこのプログラムは解明出来た.

いまから思うと大変な手間であったが, 私がパラメトロン計算機PC-1でプログラマー人生を始めた頃はまったくこの通りであった.

2014年11月30日日曜日

EDSACのプログラム技法

英国の計算機歴史学者Martin Campbell-KellyがProgramming the EDSAC: Early Programming Activity at the University of Cambridgeという長い長い論文をIEEE Annals of the History of Computing Vol.2 No.1 January 1980に書いている.

この最後の方にInfluence on programming for other machinesという章があり, 東大のTACのことも述べているが, E. Wada, who was responsible for programming, designed a new set of initial orders based on those of Wheeler, but incorporating floating addresses and some other small refinements.と書かれているのを知った.

今回のブログの目的はその論文にあるEDSACのプログラムの例を説明することである. MartinはEDSACの例にTPKプログラムを使った. なんの変哲もない問題だが, アセンブリ械語のプログラムとしては長さ的に適当かも知れない.

11個のデータを読み込み, 配列aの0から10に置き, t =a[t ]として,

y =√|t|+5×t 3 (*)

を計算, 印刷するものである.

t が<1なら, 最初の項は平方根だからtより小さくなるから無視し, 次の項は3乗なのでこれが結果の大きさに利く. t が±10位ならyは±5000程度のt3の曲線を考えればよい.

EDSACの頃の計算機は, 扱う値を±1以下としていたから, tが10程度までなら1/16にし, yも1/213=1/8192として計算する.

つまり *の式にy=213y', t=24t'を入れ, y' =2-11t' +5×2^-1t'3 を計算し, 後で213倍する. t=1とすると, t'=2^-4. その平方根は2^-2. 2^-11を掛けると2^-13. またtの3乗は2^-12. 5.2^-1を掛けて5.2^-13. 両方を足して2^13を掛けると6になる.

EDSACに用意してあるライブラリのサブルーチンもみなこの範囲と思って働く.
さてMartinの書いた主プログラムはこうだ.



上から数行目の 0 |A θ と書いてある行を0番地としたアドレスにはθを最後に付ける. 下の方 M 0|P 4 D の行を0番地とするときは, Mを付ける. そのMは38θであること, set M-parameterの行で設定する.

0と1行目はWheelerリンケージで, 56絶対番地のR1へサブルーチンジャンプする. 2行目 T20Dはサブルーチンに20Dからデータを格納せよとの指定である.

データはテープに15+8+6-95+23+99+21+21-6+0001+0002-Fとパンチしてあり, Fが來るまで0.15, 0.8, -0.6, ...のように20番地, 22番地, 24番地, ...,40番地に読み込まれる. そして3番地に戻ってくる.

O 10M, O 11Mは10MにCarriage return(復帰), 11MにLine feed(改行)のコードがあって, 出力装置を改行する. こういう機能鍵にはアルファベットが書いてないので, パンチの都合上ギリシア文字が割り当てられている.

4,5番地にT Dが2度あるが, 最初のはアキュムレータを0にするもの, 次はその0を0番地の長語に格納するものだ. そして7M番地にある10を0番地の短語に設定し, 9,10番地でP7へサブルーチンジャンプする. EDSACの短語は, EDSACのプログラム技法のブログの2012年9月22日の命令語の図にあるように, L/Sビットのところが整数の1の桁であり, アドレスはその上に1ビットずれているから, P5Fと書くと10になり, 8MのようにPD, つまりアドレス部が0でL/Sビットがあると, 整数の1になる. (ファンクション部のPは0である.)

11番地へ戻るとO 12Mでスペースを2文字出力する. 次はtを16で割りt'にするのだが, もともと読み込むデータが1/10になっているとしたので, (15は0.15と読まれたが, 1.5のつもりであった.) 10/16を掛けることになる.

EDSACの乗算は一方の数を乗算レジスタへ置き, ある番地にある数と掛けて積をアキュムレータに置くので, H 4Mで10/16を乗算レジスタに置く. 4Mを見ると J F である. F はL/Sビットが0のことだが, Jはなにかというと, 前のブログでEDSACの文字コードを見るとJは01010である. 左端の0の次に小数点があるから, これが10/16なのである.

14番地のV40Dで40番地にある長語のデータと乗算が出来た. それを8Dに置いてサブルーチンへ行き, y' を計算する. y' も8Dに置いてある.

まだまだ続くが, 今回はここまでにしよう.

2014年9月24日水曜日

割込み

私が2年ほど前にツィートした割込みの話がいまでも時々リツィートされているようだ.

今日はその割込み(英語ではinterrupt)の昔話しを書いてみたい.

私がいた研究室, つまり東大物理学科の高橋研究室では, 院生の後藤さんが発明したパラメトロンを使って計算機を自作した. 1958年のことである. Parametron Computer No.1ということで, 通称はPC-1という.

当時計算機とかプログラミングでは, 英国ケンブリッジ大学のEDSACの本があったので, 計算機を作ろうとすると, EDSACが手本であった. PC-1ではEDSACのアーキテクチャを十分検討し, ほとんどそのまま採用したものもあれば, 我々の哲学に従って変更したものもある.

入出力命令も変更した部類であった.

EDSACの入力命令 I n は, 5単位紙テープの1列を, n番地の右端に読み込む; 出力命令 O n は, n番地の左端の5ビットを書き出す. しかしこれは不便ではないかと我々は考えた. 読み込んだ文字コードを調べるには, これを演算装置, アキュムレータに取り出さなければならい. また文字コードをアキュムレータで作ってから書き出すこともあろう. そういうわけで, PC-1で は, 入出力はアキュムレータを経由することとした. さらに入力はアキュムレータの左端に入れることにした. そうするとサインビットの判定も楽だからだ.

もちろん, 入力命令コードは i, 出力は o である. ではこれらの命令の番地部は何に使うか. 計算機の処理速度に対して, 入出力は遲いから, 計算機は次々と入力や出力を続ける際, 待たされることになるだろう. そこで入出力命令を実行しようとした時, 相手の機器の準備が出来ていなければ, 別の計算に移れるように, 命令の番地部へとぶようにした. つまり条件ジャンプ命令にしたのである.

入出力をしながら, 同時に計算も出来るということで, 聖徳太子は十人の訴えを同時に聞いたと伝えられるから, この機能には聖徳太子というあだ名がついた.

しかし, 実際問題として, こういうプログラムを書くのは結構面倒である. 従って普通には命令は自分の番地にジャンプする, つまりダイナミックループをするように書かれていた.

下がPC-1が最初に実行したプログラム(neglect erase)である. 当然だが二進法で紙テープに穿孔してある.

0    i 0;
1    b 6;
2    zl 0;
3    b 6;
4    o 4;
5    jl 0;
6    111111000...0;

0番地のi命令の番地部は0, 4番地のo命令の番地部は4になっている. このプログラムは紙テープを読みそのまま出力テープに複写するが, 抹消のコード(6単位がすべて1, つまり全穿孔)は複写しない. 6番地には左端6ビットに1が入っており, 1番地のb命令でビット毎のxorをとり, 結果が0ならzl 0;で0番地に戻る. そうでなければ3番地で元に戻し, 4番地で出力, 5番地で0にジャンプバックする.

入出力と演算の同時実行のプログラムがなぜ面倒かというと, 入出力機器の準備ができていなくて演算をしていると, 今度は準備が出来たかどうかを絶えず見にいかなければならず, それはまた煩わしいのである.

例えていえば, なるべく早く食事したいが, 食堂がまだ準備中なので本を読んでいるとする. しかし食堂が開いたかどうか, 絶えず注意していると, 本を読むのも途切れ途切れで能率が悪い. 能率よく本を読みしかもすばやく食事にありつく対処法は食堂の準備が出来た時, 食堂の方から知らせてくれることである.

というわけで, 入出力機器, といっても出力のテレタイプの方だけだが, 準備ができたらプログラムに知らせる機能をつけることにした.

どうやってプログラムに知らせるか, プログラムの実行中の情報をどう保存するかを次に考えることになった.

PC-1の記憶装置は512語であったので, 510番地に次に実行する命令の番地をいれ, 511番地にジャンプすることになった. 割込む機器は1台であったが, 複数台になったとき, 割込みが続けておきると, 帰り番地の情報が失われる恐れがあるので, 割込みと同時に, その後の割込みを禁止する. この禁止はプログラムで解除する.

PC-1にはプログラムでセット, リセットできるフリップフロップが2個あった. 2014年3月13日の私のブログ「計算機による音楽演奏」で使ったのもこのフリップフロップである. その一つを割込み禁止に使った.

では割込み処理の説明に移ろう.

なにか長い計算をしていて, 途中時々数値を出力しているとする. 出力サブルーチンはその数値を十進法に変換し, そのそれぞれの数字や記号をテレタイプのコードに変換してテレタイプに送りだすのが通常の処理である.

それに対し, 割込みではコードに変換するまでは同じだが, それを1文字ずつリングバッファに入れるのである.

下の図が当時の割込み処理の流れ図である.



出力サブルーチンは, コード変換がすむと, 流れ図の左上からこの処理に入ってくる. まず割込みを禁止し, 帰り番地を510番地に設定する. 次に出力すべきテレタイプのコードをリングバッファに入れ, 入り口の指標indexを1増やす.

それから中央の線へ行き, 出口の指標exdexの指す一番古いコードをアキュムレータに取り出し, o 命令を実行する. 出力機器の準備が出来ていれば出力でき, yesの口から出る. 反対に準備がまだならnoへ出, バッファが満杯かどうかを見る. 満杯なら仕方ないから, 先程の古いコードを取り出すところへ戻り, 機器の準備を待つ. 満杯でなければ, 次のコードを入れられるから, 下へ抜けてもとのプログラムへ戻る.

1文字出力出来た場合は, 中央の線の一番上へ戻り, 出口の指標exdexを1増やす. バッファが空なら戻る. 空でなければ一応出力してみようと 試みる.

割込み処理から戻るときには, 割込み禁止を解除しておく.

一方割込みが掛かって処理ルーチンに来るときは右上から入る. この時は帰り番地の設定は済み, 割込み禁止も掛かっているが, なにかの実行中で飛んできたので, アキュムレータや他のレジスタ類を退避してから 出力のループに入る.

簡単だがこれはいちおうマルチプログラミングである. この実験をやったのは1959年の夏ころと思う. ちょうどその頃, パリでUNESCO主催のコンピュータの会議があり, Christopher Stracheyが割込み, マルチプログラミング, TSSの提案をしていた. 我々はその割込みを先取りしているので大いに気持ちがよかった.