2026-03-07

mboot

UNIX V6のセットアップをおこなうドキュメント「SETTING UP UNIX - Sixth Edition」によると、配布テープは「The tape contains 12100 512-byte records followed by a single file mark」だそうです。さらにオフセット100から4000レコードが「バイナリ」、その後の4000レコードが「ソース」、その後の4000レコードが「ドキュメント」との事です。全体として12,100レコードということは、オフセット0から100レコードには、テープを読みこみディスクに書き出すユーティリティが格納されていることになります。

 

ドキュメントの「Making a Disk From Tape」では、TU10コードにより、テープのオフセット0から数レコードを読んでメモリの000000番地以降に展開しています。ここで何を読み込んだのか不明ですし、どのような処理が行われた結果としてプロンプト「=」が出るのか、霧の中です。もっとも、この過程がわからなくても、UNIX V6のディスクを作ることはできます。しかも、わかったところでUNIX V6の本体の理解が進むわけでもありません。そうであったとしても、何故この処理を理解しようとするのかと言えば、今後UNIX V6を学んでいくための準備練習として最適ではないかと考えるからです。

 

TU10コードを実行した後からプロンプト「=」が出るまでの間を理解するため、Googleを検索したり、Geminiに相談したりしましたが、確たる情報は得られませんでした。そうならば、発想を変えて、TU10コードが実行された後のメモリがどうなっているかを調べてみようと思い至りました。「Installing UNIX v6 (PDP-11) on SIMH」で使われている「Unix-v6-Ken-Wellsch.tap」を使用してTU10コードを実行してみました。その後simhのPDP11エミュレータで「e -m 000000-001000」することで、1レコード分を逆アセンブルしてみました。その内容を確認してみると「mboot」であることが確認できました。

 

UNIX V6のソースを確認すると「source/mdec/run」には、次のような箇所があります。

as tpboot.s tty.s tm.s

strip a.out

ls -l a.out

cp a.out /usr/mdec/mboot 


ソースには「source/mdec/tpboot.s」、「tty.s」や「tm.s」もありますから、逆アセンブルした結果を全て確認したら、まさに「tpboot.s」、「tty.s」、「tm.s」そのものでした。これらをアセンブルしたものが「mboot」というバイナリで、これはa.out形式です。サイズは440バイトだったので、1レコードに収まります。ここから考えても、TU10コードでは数レコードを読み込もうとしていますが、1レコードを読み込むだけで十分であることが分かります。

 

次はmbootの処理を追いかけてみようと思います。 

 

 

 

 

UNIX V6の配布テープを読み込む処理

UNIX V6を通してOSについて深く学ぼうと考えています。simhのPDP-11エミュレータを使用しようと考えています。このようなアプローチは、以前よりオンラインでも書籍でも多くの情報があります。それらは、ほとんどの場合において、UNIX V6がディスク上にインストールされていることを前提としています。しかし「SETTING UP UNIX - Sixth Edition」を読むと、前段階として「Making a Disk From Tape」が必要です。これらはOSの挙動とは関係ありませんが、PDP-11の動作を学ぶには格好の素材ではないかと思います。そういうつもりで、この処理を詳しく調べてみることにしました。

 

まず最初におこなうのは、TU10コードを(本物のPDP-11/40なら、フロントパネルから)入力し、テープから「何か」を読み込むことです。ドキュメントにはバイナリコードが記載されていますが、理解しやすくするため逆アセンブルしておきます。これが100000番地から格納されることになります。

100000: MOV #172526,R0

100004: MOV R0,-(R0)

100006: MOV #060003,-(R0)

100012: BR 100012 

 

100000番地で、R0に定数「172526」を代入しています。これは100004番地の処理をするための、準備と言えます。100004番地では、代入先が「-(R0)」となっていますから、まずR0をデクリメントし、そのR0が指しているアドレスに何かを(ここではR0の内容)を代入しています。R0は172526だったのですから、デクリメントすると172524になります。これはTM11のMTBRCレジスタです。代入される値もR0ですから「172524」です。

 

ドキュメント「TM11 DECmagtape system」(DEC-11-HTMAA-D-D)によると、「4.2.3 Byte Record Counter (MTBRC)」で説明されていますが、転送量の2の補数を指定します。ここでは「172524」を指定していますが、これは転送量を2の補数にしたものなので、本当の転送量は、逆算して、「2732」となります。読み取り操作では、ワード数で指定するようです。テープは512バイトを1レコードとしていますが、2732ワードならば、10レコード以上あるし、しかも中途半端です。何故中途半端な値を指定するのかは不明ですが、推測するに、フロントパネルから入力するロジックを短くしたかったのでしょう。おそらく本当に必要なのは1レコードだったと思われますが、それを忠実にロジックに落とすと、ちょっとだけ長くなるので、それを避けたかったのでしょう。今日であれば気にするほどのことではないと思いますが、当時の時代背景を踏まえると、重大問題だったのだと思います。

 

100006番地では、さらにR0をデクリメントしていますから、172522であるMTCに「060003」を代入しています。これもドキュメントの「4.2.2 Command Register (MTC)」より、「DEN8」が「1」で「DEN5」も「1」なので「800bpiの9チャンネルテープ」であり、「Function」が「001」なので「Read」となり、「GO」が「1」なので実行開始されます。

 

最後に無限ループに入りますが、現実のPDP-11/40ならば、操作する人が目視していれば、テープ装置の読取り終了が判断できるのでしょう。 

 

このようにしてテープから読み込まれた内容(おそらく1レコードで十分なはず)が000000番地から書き込まれています。ドキュメントでは、手順4として「Halt and restart the CPU at 0.」と書かれているので、読み込まれたプログラムに制御が移ります。 

PDP-11のJSR命令の挙動と時代背景

PDP-11のJSR命令は、サブルーチンを呼び出すために使われます。x86におけるCALL命令のようなものです。x86に限らず、Z80でも6800でも、同様の命令が存在します。使うには、x86なら「CALL サブルーチン」のようにします。戻り番地はスタックに積まれているので、サブルーチンでは「RET」するだけです。

 

ところがPDP-11では「JSR レジスタ, サブルーチン」のようにしています。このとき戻り番地がレジスタに入りますが、レジスタをR7(=PC) とするなら、戻り番地はスタックに積まれます。要するにx86や6800などと同じ挙動です。サブルーチンを呼び出す命令で、なぜレジスタが出てくるのか分からず、Geminiに相談してみました。Geminiの応答は、自信たっぷりに返事をしますが、必ずしも正しいとは限らないので、話半分に聞いておく必要がありますが、それなりに納得できました。

 

Geminiが言っていたのは、PDP-11が登場した頃の時代背景が理由として考えられるそうです。その要点は、つぎのようなものでした。

  1. 当時は、アーキテクチャとしてスタックが今日のように一般的ではなかった。
  2. サブルーチン呼出し命令の直後に引数を置いておくテクニックが存在した。
  3. コードとデータの分離が不完全で、職人技的技法としてコードを書き換えることもあった。 

 

x86などでは、サブルーチンに与える引数は、レジスタに持つことが多く、少なくともCALL命令の直後に置いておくことはありません。しかも、8080の頃ならいざ知らず、80386以降のプロテクションモードの時代では、実行中にコードを書き換えることはなく、そんなことをしたら保護違反になってしまいます。

 

手元に、共立出版が1978年に発行した『マイクロコンピュータのプログラミング』という本があります。この中で「プログラム技法」(中西正和)では、「2.6 命令の中に命令を作る」として、今日では考えられないテクニックが紹介されています。ここで書かれているのは、バイナリ列「01 FE FF」 があったとすると、先頭から見れは「LXI B,0FFFEH」だが、2バイト目から見れば「CPI 0FFH」と見做せるし、3バイト目から見れば「RST 7」と見做せるというものです。まさに職人技です。そうかもしれないけど、そこまでするかという感じです。このようなテクニックがもてはやされるのは、メモリも少なく、(今日からすると)CPUも遅いという時代背景があるためだと思います。

 

PDP-11のJSR命令における不思議なレジスタの存在も、当時のプログラミング事情として「インライン引数」というものが存在したという時代背景が根底にあるようです。このような事情を知らないと、PDP-11のJSR命令の挙動が理解できない事を学びました。

洗濯時間はこれほど短かったのか

先月、自宅で使用している洗濯機がエラーを出しました。「ピーピーピー」という警告音が鳴った後、操作パネルのLEDが全て点滅していました。その時は、再スタートしたところ、その後は問題ありませんでした。ところが、後日洗濯機を使おうとした際にも、エラーが出てることが多くなりました。もしかしたら故障したのだろうか、買い替えるべきなのかと悩みました。

 

そもそも使用している洗濯機は、製造元が三洋電機です。購入時期は覚えていませんが、20年以上前だと思います。買い替えても良いとは思いますが、自宅で他にも故障が相次いでおり、出費がかさんでいるので、できれば買換えは避けたいところです。

 

何が悪いかわかりませんが、少なくともエラーを出す前から注水の勢いが弱いとは思っていました。水道栓と洗濯機の間が狭くて、ホースが捻じ曲がって配置されています。もしかすると水の通りが悪いのではないかと当たりをつけました。ホースを取り外し、スムースに水が供給されるように、配線を変えてみました。

 

変更した結果、力強く給水されるのが確認され、それ以来エラーも出ていません。これが原因だったのかもしれません。それだけではなく、洗濯時間も短くなりました。以前は2時間くらいかかっていましたが、洗濯量にもよりますが、今は30分ほどで終わります。あまりにも早いので、途中でエラーで止まっているんじゃないかと不安になるくらいです。