ratfor on unix/v6

ある夜、佐川さんが荷物を持ってきた。女房が通販で頼んだ物みたい。嬉々として荷物を解く女房。 何が出てきたかと言うと、ポテトチップス製造機(with 餅焼き網)

スリットが28本入った皿みたいなのと、芋をスライスする鉋みたいのが付いてた。さつまいもとか カボチャとかもチップスに出来るようだ。

早速、ままごとの始まりですよ。説明書には、500Wの電子レンジで11分。800Wで7分チンして くださいなんて書いてある。我が家にある石釜オーブン付き電子レンジは、牛乳と冷や飯の 暖めにしか使っていないから、500Wで11分なんて、どうやって設定するのやら?

面倒な事はおいらに出番が回ってくる。家中探して、取り扱い説明書を探してきた。そして、 手動設定の項目を見つけたよ。手動出力切り替えボタンを、ぽんぽん押せば、表示が代わるんだ。 そして、秒、分、10分のボタンを適当に押して、時間を設定するのね。準備が出来たら、スタート ボタンを押す。こんな使い方、初めて知ったよ。

待つ事、7分の短縮モード。焦げてる所があるかと思うと、まだ半生みたいな所もあって、 一回目は失敗。最近のレンジって、何で回転しなくなっちゃったの? 何か退化してるぞ。 2回目は、11分モードで、途中手動回転させませたよ。レンジの蓋を開けると、もうもうと 蒸気が出てきて、やけどしそうになった。排気はどうなってるん?

今度は、うまく出来たようだ。黒胡椒入りの塩を振りかけて、ビールと一緒に楽しみましたよ。 ああ、また太ってしまうぞ。

いろいろな言語(bas)

前回 unix/v6にbasicが入っている事を確かめた。どんな風に出来ているかと確かめてみると

# grep bas CATLOG
/bin/bas
/usr/source/s1/bas.s
/mnt/man/man0/basinf
/mnt/man/man1/bas.1
# cd usr/source/s1
# wc bas.s
   2113    3916 bas.s

/binの下とは、いい席を確保してるなあ。あの頃はbasicは幅を利かせていたんだ。ソースの サフィックスに.sが付いているって事は、アセンブラか、2100行ねえ。バイナリーサイズを 調べたら、8k Basicだったよ。

おいらが始めて触れたミニコンには、2種類のBasicが付いていた。4kの方は、普通のBasic。 コアメモリーが8Kあると、8kバージョンのBasicが使えた。行列演算をサポートしてて、行列の 掛け算とかやらせて、ああ、数学の通りに動いてるって、歓心したものだ。

確か東大の森口先生が書いた入門書が出てて、会社で一括購入してくれた事を覚えている。そして、 その中に問題集が付いていて、ピタゴラスの三角形を求めよとか、うさぎときつねの問題 (うさぎが増えるとそれを餌にするきつねも増え、きつねが増えすぎるとうさぎの数が減るという、平衡問題) を解けとか、無い頭を絞って、勉強したなあ。1970年頃の話だけど。

        mov     $1f,r0
        jsr     pc,print
        br      loop
1:
        <Cannot open file\n\0>; .even
print:
        mov     r0,wbuf
        jsr     pc,size
        mov     r0,wlen
        mov     prfile,r0
        sys     indir; syswrit
        rts     pc
syswrit:        sys     write; wbuf: 0; wlen: 0

一部を出してみた。あの頃のアセンブラって、文字列は、三角括弧で囲んで表して、NULL ターミネートだったのね。これぐらいなら、すらすら動作を読めるけど、さすが、2000行を 嫁と言われると辛いなあ。

いろいろな言語(as)

上でも出てきたけど、sysの正体って一体何よ? pdp-11のハンドブックをみると、それらしい のに、emtとかtrapとかあるけど、sysって言うのはDEC用語にない。はてどう調べよう。 asコマンドにアセンブラリストを出力するオプションも無いし。。

# cd /usr/source/as
# wc *
    106     198 as11.s
     70     125 as12.s
    131     246 as13.s
    231     446 as14.s
    170     284 as15.s
    291     498 as16.s
    220     382 as17.s
     65     112 as18.s
    347     669 as19.s
    258     526 as21.s
    151     308 as22.s
    165     307 as23.s
    113     237 as24.s
      4       6 as25.s
    559    1028 as26.s
    294     589 as27.s
     77     120 as28.s
    280     543 as29.s
      9      26 run
   3541    6650 total

アセンブラもアセンブラで書かれているし。。(じゃ、それをアセンブルするのは、どうする? と言う、哲学問題には、目をつぶる)調べるのやだな。で、ふと思ったね。

# cat check.s
        emt 14.
        trap 14.
        sys 14.
# as check.s
check.s
u 0001
u 0002

あら、このアセンブラは、emtもtrapも受付ないよ。そんなにDECが嫌いだったん? しゃーない、 こうなったら、人間逆アセンブラするしか。

# cat check.s
        sys 16.
        sys 15.
        sys 14.
# as check.s
# od a.out
0000000 000407 000006 000000 000000 000000 000000 000000 000000
0000020 104420 104417 104416 000000 000000 000000

16.のように、数字の最後に ドットを付けると、10進数とみなされる。何もつけないと8進数と なるのかな。オクタルダンプって、pdp11用のダンプだったんですねぇ。で、1044xxってのが 3つ並んでいるので、これは、sysですね。ハンドブックを調べると、そう、DECで言うtrap 命令でした。ついでに、XXの所は、00-377まで(すなわち256種のトラップバリエーション)指定出来ます。

trap命令の動作は、PSWとPCをスタックにセーブし、34番地の内容をPCに転送、36番地の内容をPSWに 転送するんだ。これが、システムコールの元になるハードの動きね。

ついでに、a.outのフォーマットは、最初(407)はマジック番号、text,data,bss,symbolサイズ、続いて、エントリポイント (常にゼロ)、未使用、フラグとなっている。実際のデータは、16バイト目からとなる。

マジック番号の407は、pdp11語に翻訳すると、7ワード先へ飛んでけ(PC相対のJump)と言う事で、これは、取りも なおさず、a.outのヘッダーをスキップして、データが格納されている所から実行せよと言う 事だ。昔は、こういう風に合理的だったのね。

Bill Joyも自分の生年月日を覚えてもらって 、誕生日祝いを貰いたく、ファイルシステムのマジック番号にしてる。(ちなみに彼の誕生日は、 1954年01月19日、#define FS_MAGIC 0x011954)

# size a.out
6+0+0=6 (6)
# size /bin/bas
8064+700+15254=24018 (56722)

basって、変数エリアたくさん使うのね。

いろいろな言語(sno)

pdfになったマニュアルを見ていると、いろいろな言語が列挙されている。snoとか言うのが あった、どんなものか調べてみたよ

よう分からん言語だ。awkの前身? でもなさそうだし。作者がこれに変わる言語 Icon とか いうのを作っているようだけど、これって、matzさんも注目してたよね。

# sno
Unix is long term OS.  ## <--- input from keyboard
1       eof on input   ## <--- Ctrl-d
0  f
0  s
0  end
0  start
0  define
0  return
0  freturn
3  syspit
4  syspot
2  Unix
0  is
0  long
0  term
0  OS.

いろいろな言語(ratfor)

binの下を探検してたら、rc なんていうコマンドに遭遇した。 rc ったら、普通、run commandと思ってしまうよね。 ところが、見事に予想が外れましたよ。

変な言語 ratfor用のコンパイラーですって。ratforは、合理的なFortranの略だそうです。unix/v6には既に fortran コンパイラーが搭載されています。何年式って言われたら、fortan75とでも言えばいいのでしょうか。 で、ratforは、fortaranの語彙の貧弱さを補うために開発された、ラッパーです。

このrator、どこかで聞いた事があるぞと思って調べてみたら、『ソフトウェア作法』の使用言語に なっていました。私もこの本持ってました。1987年に購入してました。懐かしくなって、パラパラみてました。

訳者(木村 泉)さんの前書きに、この本を出版するにあたり、久野靖君達には、多大な重労働を させてしまった。感謝している。なんてくだりが出ていた。以前、茗荷谷の赤ちょうちんで 久野先生から聞いた苦労話と重なったよ。先生曰く、日本語の写植をやるために、学校と メーカーの間を、山の手線を乗り継いで、MTを運んだと。それで、木村先生が、潔癖な方で 何往復もした(させられた)と。

早速動かしてみる。題材は、ports/lang/ratforにあったテストコード。

# cat test.r
integer x,y
x=1; y=2
if(x == y)
        write(6,600)
else if(x > y)
        write(6,601)
else
        write(6,602)
x=1
while(x < 10){
        if(y != 2) break
        if(y != 2) next
        write(6,603)x
        x=x+1
        }
repeat
        x=x-1
until(x == 0)
for(x=0; x < 10; x=x+1)
        write(6,604)x
600 format('Wrong, x != y')
601 format('Also wrong, x < y')
602 format('Ok!')
603 format('x = ',i2)
604 format('x = ',i2)
end

format文があったりで、Fortranの姿が見え隠れする不思議が言語です。現代なら、Javaの 文法が見え隠れする、Scalaとかに匹敵するんですかね? 時代が変わっても、人間の考える事に 進歩は無いなあ。

# rc -f test.r
test.r:
   MAIN.f:
# ls -l
total 22
-rw-rw-rw-  1 root      962 Jul 20 18:41 MAIN.f
-rwxrwxrwx  1 root     7644 Jul 20 18:41 a.out
-rw-rw-rw-  1 root     1036 Jul 20 18:41 test.o
-rw-rw-rw-  1 root      436 Jul 20 18:35 test.r

Fortan経由で、最終binaryまで一機に作ってみました。展開して出来たFortanのソースが 元ファイルの2倍になってる事からすると、生産性は2倍だったんでしょうかね? 見方に よってはDSLとかマクロとかにも見えるな。

# cat MAIN.f
       integer x,y
       x=1
       y=2
       if(.not.(x .eq. y))  goto 23001
       write(6,600)
       goto 23002
23001  continue
       if(.not.(x .gt. y))  goto 23003
       write(6,601)
       goto 23004
23003  continue
       write(6,602)
23004  continue
23002  continue
       x=1
       continue
23005  if(.not.(x .lt. 10)) goto 23006
       if(.not.(y .ne. 2))  goto 23007
       goto 23006
23007  continue
       if(.not.(y .ne. 2))  goto 23009
       goto 23005
23009  continue
       write(6,603)x
       x=x+1
       goto 23005
23006  continue
       continue
23011  continue
       x=x-1
23012  if(.not.(x .eq. 0))  goto 23011
23013  continue
       continue
       x=0
23014  if(.not.( x .lt. 10))   goto 23016
       write(6,604)x
23015   x=x+1
       goto 23014
23016  continue
600       format(13hWrong, x != y)
601       format(17hAlso wrong, x < y)
602       format(3hOk!)
603       format(4hx = ,i2)
604       format(4hx = ,i2)
       end

おーーーー。懐かしい(Fortranはよく知らないけど)Fortranだ。高校生の頃、NHK教育TVで フォートラン講座をやってて、毎週見てた記憶があるぞ。

# ./a.out
Ok!
x =  1
 :
x =  9

実行結果は面白い事は何も無し。走りましたね、ぐらいかな。それじゃつまらないので、 rcをちょいと紐解いてみますか。

/usr/source/s2/rc.cにあるのは、コンパイラードライバーでしょう。本命は、/usr/source/ratの 中でしょうね。

# cd /usr/source/rat
# ls -l
total 30
-rw-r--r--  1 bin      5164 Jan  1  1970 lex.c
-rw-r--r--  1 bin       864 Jan  1  1970 r.g
-rw-r--r--  1 bin       132 Jan  1  1970 r.h
-rw-r--r--  1 bin      4476 Jan  1  1970 r1.c
-rw-r--r--  1 bin      1612 Jan  1  1970 r2.c
-rw-r--r--  1 bin       114 Jan  1  1970 run
# cat run
yacc r.g
cc -s -O r1.c r2.c lex.c -ly -lp
cmp a.out /usr/lib/ratfor
cp a.out /usr/lib/ratfor
rm *.o a.out y.tab.c

ratforと言う、Fortranへの変換機はライブラリー扱いになるのね。それを、rc.cの中から 呼び出す仕組みか。

# cat r.g
%term   LCURL RCURL LPAR RPAR SCOL DIGITS
%term   XIF XELSE XFOR XWHILE XBREAK NEXT
%term   OLDDO NEWDO
%term   XGOK XDEFINE XINCLUDE
%term   REPEAT UNTIL
%%

statl   : statl  stat
        |
        ;
stat    : if stat       ={ outcont($1); }
        | ifelse stat   ={ outcont($1+1); }
        | while stat    ={ whilestat($1); }
        | for stat      ={ forstat($1); }
        | repeat stat UNTIL     ={ untils($1); }
        | XBREAK        ={ breakcode($1); }
        | NEXT          ={ nextcode($1); }
        | newdo stat    ={ dostat($1); }
        | OLDDO         ={ docode(0,$1); }
        | XGOK          ={ gokcode($1); }
        | SCOL
        | LCURL statl RCURL
        | label stat
        | error         ={ errcode($1); yyclearin; }
        ;
label   : DIGITS        ={ outcode($1); outcode("\t"); }
        ;
if      : XIF           ={ ifcode($1); }
        ;
ifelse  : if stat XELSE ={ outgoto($1+1); outcont($1); }
        ;
while   : XWHILE        ={ whilecode($1); }
        ;
for     : XFOR          ={ forcode($1); }
        ;
repeat  : REPEAT        ={ repcode($1); }
        ;
newdo   : NEWDO         ={ docode(1,$1); }
        ;
%%

この辺は、青木さんのc♭にも出てきたような。。今だと、前橋さんかな。嗚呼、matzさんも これの大型判と取り組んでいるのね。(ruby-1.9.Xのparse.yは、優に10000行を超えてるよ。恐ろしや!)

言語じゃないけど、(simh)

pdp11のシュミレータなんだけど、manもinfoも付属していない。なんてこったい、なんだけど 、そんなのソース嫁。読んでみたら、scp.cに、それらしいコメントが書いてあった。RMSなんて 人の名前も、よく登場してるんだけど、あの人? とにかく、メモリーの検査とレジスターの 値確認ぐらいは、出来なければね。

Simulation stopped, PC: 021630 (MOV (SP)+,177776)
sim> e 30-40
30:     000746
32:     000345
34:     000746
36:     000346
40:     000167
sim> e 746/20
746:    016767
750:    177024
752:    040620
754:    005767
756:    040602
760:    001017
762:    016767
764:    176604

最初の例は、30番地から40番地まで検査。次の例は、746番地から20byte(8進数で)までの 表示です。逆アセンブルもしてくれるといいのに。

sim> ie state
PC:     021630
R0:     140004
R1:     034272
R2:     005262
R3:     000200
R4:     000000
R5:     141724
SP:     141710
R00:    140004  ^Dsim> e psw
PSW:    030000

レジスターの値を順番に表示(CRで次)ほっておくと大量のレジスターを表示してしまうので、 途中でCtrl-dを使って表示を中止して、新たに、PSWだけ表示してみた。

後便利そうなのが、break -e address で、ブレークの設定。nobreak で、解除かな。それに 加えて、ahow breakで、BPの確認。stepで1命令づつ実行(止まった時、逆アセンブル表示) もしてくれる。 そこまで、使えるようになったら嬉しいな。

sim> break -e 746
sim> c

Breakpoint, PC: 000746 (MOV 177776,41574)
sim> break 746;

とか、言いながら、使ってしまったよ。これは、面白い。更に面白いのも有って

sim> set cpu history=100
  :  ## running unix, then Ctrl-e
sim> show cpu history=20
PC     PSW     src    dst     IR

034372 030000|000003 034372  SOB R4,34316
034316 030000|034316 005206  ADD #26,R2
034322 030000|034322 005234  CMP #7322,R2
034326 030000|               BHI 34334
034334 030000|034334 005234  CMPB #3,(R2)
034340 030000|               BNE 34372
034372 030000|000002 034372  SOB R4,34316
034316 030000|034316 005234  ADD #26,R2
034322 030000|034322 005262  CMP #7322,R2
034326 030000|               BHI 34334
034334 030000|034334 005262  CMPB #3,(R2)
034340 030000|               BNE 34372
034372 030000|000001 034372  SOB R4,34316
034374 030000|       034374  TST 41576
034400 030004|               BNE 34414
034402 030004|005262 034402  MOV R2,41576
034406 030000|034406 034406  JSR PC,21620
021620 030000|021620 141712  MOV 177776,-(SP)
021624 030000|       140004  SPL 0
021626 030000|               WAIT

これ、pdp11の実行履歴ですね。どこを回っていたか確認出来るよ。

なお、simhについては、 simhの日本語解説英文マニュアルが 参考になります。

最後に、cobolが無いのは肯けるけど、lispが入っていないのは、なして?(ぼそっ) 調べてみたら、 ここで歴史 が説明されてた。そして当たりを付けて lisp に辿りついたよ。