2016/02/14

setjmp()とlongjmp()のUNIX V6への移植

UNIX V6にはsetjmp()とlongjmp()が実装されていないので、どこからか探してきて移植しなければなりません。調べてみたらUNIX V7には入っていることが分かりました。
setjmp.hの実現方法を見ると、UNIX V7のC言語にはtypedef宣言が使えるようになっているようです。しかも「#include <setjmp.h>」のようなシステム・インクルードが出来るようにもなっていたようです。しかしUNIX V6では両者とも実現されていないので別の対応を考えなければなりません。setjmp.hでは「typedef int jmp_buf[3]」と定義され、レジスタの退避領域を確保しているので「jmp_buf env;」のような箇所は「int env[3];」のようにすれば良いでしょう。

setjmp.sにあるlongjmp()の実装では、最初にcsvというルーチンが、最後にcretというルーチンが呼ばれます。これは「C register save and restore」というものでUNIX V6にも存在します。しかしUNIX V6の実装とUNIX V7の実装は多少異なります。setjmp.sはUNIX V7で実現されているので、UNIX V6に移植するために何か変更する必要があるのか検討しておかなければなりません。

cretの方は、UNIX V6版とUNIX V7版とでレジスタの使い方が若干違いますが、同じものだと考えてよいと思います。csvの方は、UNIX V6版とUNIX V7版とでは、このルーチンからの戻り方の実装だけが異なります。

そもそもPDP-11では、サブルーチンの呼び出しはjsr命令で、そこから戻るにはrts命令を使うことが想定されています。ところがcsvではそうではなく、UNIX V6版では「tst -(sp); jmp (r0)」、UNIX V7版では「jsr pc,(r0)」となっています。実現方法が違いますが、実行される結果は等価であると考えて良いと思います。

以上より、UNIX V7のsetjmp.sをUNIX V6に持ってくれば、そのままsetjmp()とlongjmp()として利用できるようです。簡単なプログラムをを作ってみましたが、ちゃんと動作しているようです。

setjmp.sのロジックを追っていたら、longjmp()の処理として以下のような箇所がありました。
    mov    6(r5),r0
    bne    1f
    mov    $1,r0
1:
コメントが入っていないので何を意図しているのか分かりませんでしたが、BSD 2.11版のsetjmp.sにある「if (val == 0) r0 = 1」というコメントを見ると何がしたかったかが分かりました。

longjmp(a,v)を呼び出すと、vという戻り値でsetjmp()から戻ってきたような動作をします。setjmp(a)が呼ばれると戻り値として0を返す仕様になっているため、longjmp()でvに0が指定されると、setjmp()から戻ってきたのか、longjmp()の結果として戻ってきたのかが区別できなくなってしまうため、もしvに0が指定されていた場合は、強制的に1に置き換えてしまうというロジックになっているようです。

この考え方はわかりますが、その代わりにvに0を指定しても1を指定しても、setjmp()からは1が戻ってくるという副作用が生じています。longjmp()はそういうものだと思うしかないでしょう。

0 件のコメント:

コメントを投稿