You are here

80系でリロケータブルを実現する


カテゴリー:

80系(8080,Z80,8085)で真のリロケータブルコード(PIC)を書くことはほぼ不可能です。

これらのプロセッサが現役だった頃、リロケータブルなコードといえば現在のPIC (Position Independent Code)の事を指していました。好きなアドレスにロードすればそのまま(書き換えたりせずに)実行できるコードです。
MC6809では普通に可能だったので、6809ファンの友人によく馬鹿にされたものです。

PICであるためには何が必要で80系では何が欠けているのか考えて見ましょう。必要なのはコード内のアドレスを正しく求め、参照できることです。

一番簡単なのはCPUがPC相対アドレッシングを持っていること、内部参照を相対アドレッシングだけで書くことができればそれはPICになります。でも8080,8085にはそのような命令はまったくありません。Z80には近距離のブランチのみ可能(JR, DJNZ)になりましたが、CALL命令やデータの参照はPC相対にはできません。6809ではこれが自由にできたのでPICは現実的でしたし、OS-9のようにそれを前提にしているシステムもありました。

もう一つは自分のアドレスを何らかの方法で知り、ソフトウェアで参照先のアドレス計算をし、参照する方法です。

まず自分のアドレスを知るのは(若干の副作用がありますが)以下のようなコードで可能です。


   1:	0000  3EC9    	    LD      A,0C9H              ; RET
   2:	0002  3240FF  	    LD      (0FF40H),A
   3:	0005  F3      	    DI
   4:	0006  CD40FF  	    CALL    0FF40H
   5:	0009  21FEFF  	    LD      HL,-2
   6:	000C  39      	    ADD     HL,SP
   7:	000D  F9      	    LD      SP,HL
   8:	000E  FB      	    EI
   9:	000F  E1      	    POP     HL

副作用というのはメモリのどこかに1バイト壊していいアドレスが固定で必要だということです。
1,2行目でそのアドレスにRET命令を書き込みます。
3行目で割り込みを禁止しているのはスタックをいじるのでその間割り込みでスタックを使われないようにするためです。
4行目で先ほど書き込んだアドレスをCALLします。このときに戻りアドレス(この例では0009H)がスタックに積まれ、CALL先にはRET命令があるのですぐに戻ってきます。
5~7行目ではスタックポインタを操作してRET命令で取り出された戻りアドレスを再度取り出せるように調整します。
9行目でCALL命令の次のアドレスが入っているというわけです。

メモリのどこかを壊さずにRET命令が偶然書かれているところを探してCALLすれば良いのではと考えた方、残念ながら不可能です。CALL (HL)のような命令は無いからです。
普段関数テーブル等で使う、戻り番地をPUSHしておいてJP (HL)などの方法はその戻り番地が欲しくてやっていることなので、鶏と卵の関係なので駄目です。
CALL命令のアドレス部分を書き換えるのも同じ理由で不可です(「書き換えたりせずに」にも反します)。

参照先のアドレス計算はやれば可能です。数少ないレジスタを自分のアドレスを保存しておくために占有されますのでやり繰りが苦しくなるとは思いますが。

あとは参照するだけですが、これも面倒ですですが可能です。データ参照は可能、ジャンプもJP (HL)でできます。CALLは戻り番地を計算してPUSHしておいてからJP (HL)すれば良いので可能です。

でもこれ実際にやるとなるとかなり大変だと思います。下手をすると本来の目的のコードよりPIC化のためのコードのほうが大きくなったりしかねません。とても現実的なものではありません。

仕方が無いので「書き換えたりせずに」という条件を外すことにします。デメリットはMMUなどを使って同じメモリを異なるアドレスにマップするような使い方ができなくなることですが、当時80系でそんなシステムを組むことは考えにくいです。

先頭で自分のアドレスを知り(上記のような方法で)、JP, CALL, LD HL,xxxxHといった内部参照のアドレスを書き換えてしまいます。書き換えるべき箇所は何らかの形で持つ必要がありますが、よくあったのはアドレスが書かれている場所を示す(ビット)マップを持つタイプですね。コードが1kBだとすると128バイト(1kビット)のデータが後ろについています。書き換えるべきアドレスのリストを付けても良いです。普通256バイト単位で十分なのでアドレスの上位バイトのみ書き換えます。

CP/M-80ではこのような面倒を避けるため実行プログラムは必ず0100H番地にロードすることになっていましたが、困ったのはDDT (Dynamic Debugging Tool)と呼ばれたデバッガです。

デバッグ(虫取り)のプログラムなので殺虫剤のDDT (Dichloro-Diphenyl-Trichloroethane)にちなんだネーミングです。

自分は一旦0100Hにロードされますが、ここにはデバッグ対象のコードをロードする必要があります。それで自分をアドレスの上位(システムにより異なります)に逃がすようになっていて、この時この書き換えのテクニックを使っていたようです。

CP/M-80のシステム自身もMOVCPMでアドレス変更ができますが、これは確か上記マップの手法だったと思いますね。実行時ではないので、簡易リンケージエディタといった方がよいかもしれません。

ここに挙げたコード例は記憶を頼りに書いてクロスアセンブルしてリスティングファイルから抜粋しました。実行確認はしていないので間違いが含まれているかもしれません。お気付きの方は「ご意見・ご要望」にてご指摘いただけると助かります。

Add new comment

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
※ コメントは原則公開です。個別のご相談などは「ご意見・ご要望」からお願いします。