これまでの簡単なプログラムはリセットベクタFFFF0からいきなり書いて電源ONで直接実行されるようにしていましたがリセットベクタからだとメモリの終端まであと16バイトしかありません。
それより長い、複雑な処理をするにはリセットベクタにはジャンプ命令のみ置いて
プログラム本体の先頭(ふつうはROMの先頭番地)へ制御を移すわけですが、
ここではROMに書くデータが一目で眺められるようにROMの最後の256バイトに収まるよう飛び先を$FFF00番地としました
アセンブラではこう記述しました。
リセットベクタのオフセットFFF0番地からオフセットFF00番地へのジャンプ。
相対ジャンプ命令(ディスプレートメント16ビット)が生成されて、
実際のコードは 「E9/0D/FF」 (FF0D=243バイト戻り)となりました。
ところがこの単純なコードが全く動作しません。
おそらくmainじゃないアドレスに飛んで暴走してしてしまうのですがわけがわかりません。ROMに正しいアドレスが与えられてないのだと思いハードも確認しましたが問題なさそう。
これで1日悩みました。
ま、わかってしまえば当たり前なんですがセグメントの呪いですね
セグメントがあることはわかっていますがリセット時のアドレス$FFFF0というと無意識に
コードセグメント CS:F000
インストラクションポインタ IP:FFF0
で物理アドレスFFFF0が生成されると思い込んでました。
これであれば上のコードは正しくジャンプできますが、実際リセット時のレジスタの初期値はマニュアルに明記されていて
コードセグメント CS:FFFF
インストラクションポインタ IP:0000
が正解です。
この状態で3バイトのコードをフェッチしてから相対ジャンプFF0Dを実行してしまうと
CS:FFFF
IP: FF10(0003+FF0D)
-----------
10FF00
・・・とぐるっと一周回ってSRAMのアドレスにジャンプすることになります
これを防ぐにはどんなに至近距離であろうがFARジャンプ(セグメント間ジャンプ)を使ってコードセグメントCSとインストラクションポインタIPを同時にセットしなければならない、ということです。