2017-03-08 16:38 — asano
カテゴリー:
80系(8080,Z80,8085)で真のリロケータブルコード(PIC)を書くことはほぼ不可能です。
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)と呼ばれたデバッガです。
自分は一旦0100Hにロードされますが、ここにはデバッグ対象のコードをロードする必要があります。それで自分をアドレスの上位(システムにより異なります)に逃がすようになっていて、この時この書き換えのテクニックを使っていたようです。
CP/M-80のシステム自身もMOVCPMでアドレス変更ができますが、これは確か上記マップの手法だったと思いますね。実行時ではないので、簡易リンケージエディタといった方がよいかもしれません。
コメントを追加