極最小限のデバッグの為にも、まずはSCIFによる一文字出力をやらなければな らない。そこで0xffe80000をレジスタに格納し、これをベースとしてSCIFの各 レジスタを操作しようとした。当時ハマっていたソースそのものは現在残って いないが、つまりはこういう事だったと思う。
_scif_init: … mov.l scif_base_addr,r1 … mov.w r0,@(4,r0) ! SCSCR2(0xffe80008番地)に値を… … scif_base_addr: .long 0xffe80000 |
これがいつまでたっても思った通りの結果が得られない。どうーしてなのだ。
しばらくお手上げだった。いくばくかの時が流れ、 思い出したのがMarcus氏 のサンプル。それを読んで顎然とした。なんと、sh-hitachi-elf-asのディス プレースメント付きレジスタ間接の記述は、日立のマニュアルに載っているの と違っていたのだ!
日立の@(disp,Rn) | ||||||||
|
gasの@(disp,Rn) | ||||||||
|
運が悪かったのは、dispに指定した値がgasがアクセスサイズに対して許すも のだったことだ。もしこれが5なんて与えていたら、
Error: misaligned offset |
とエラーが出て、流石の私もすぐに気付いただろう。
確かに一々アクセスサイズでアドレスの差を割って指定しなければならないより は、自然な記述が出来るという考え方もあろう。しかし…純正ニーモニックを無 視するのは…その上、一見しただけでは分からないのでは…。いや、悪いのは 私なんだが…。
で、pacctx.s(当時のhospac.s)には割り込みの禁止/許可のサービスコール、 hospac_dis_int/hospac_ena_intもあるので、それらもサラサラっと書いたの だ。まずはこんな風に。
! ----------------------------------------------- ! 割り込み禁止 ! void hospac_dis_int(void) ! ----------------------------------------------- .text .align 5 _hospac_dis_int: stc sr,r0 mov.l .bl_set_mask,r1 or r1,r0 rts ldc r0,sr ! ----------------------------------------------- ! 割り込み許可 ! void hospac_ena_int(void) ! ----------------------------------------------- .text .align 5 _hospac_ena_int: stc sr,r0 mov.l .bl_clr_mask,r1 and r1,r0 rts ldc r0,sr .align 4 .bl_set_mask: .long 0x10000000 .bl_clr_mask: .long 0xefffffff |
まあ気持ちは分かって貰えると思う。「rtsの直後のldc r0,srがスロット不当 やんけ」という突っ込みについては、ここでは放っておいて欲しい。
とにかくこれでtestを動かそうとした訳だ。だが、何故かmknl_loc_sysで突 然死してしまう。勿論スロット不当なのだが、実はその時はそれに気付かない。 というか、そもそも考えずにmknl_lock_sysの実体が
#define mknl_loc_sys() \ do { if ( !(mknl_ctx_stat & TSS_DINT) ) { hospac_dis_int(); } } while (0) |
であることをmknl.hから読み、「??なんでこんなんが即死の原因になるんじゃ?? まーえーわ、まだ割り込みも実装してないし、dis/enaにはしばらく空のコー ルでいてもらお」とばかりに、以下のように書き換えたのだった。
! ----------------------------------------------- ! 割り込み禁止 ! void hospac_dis_int(void) ! ----------------------------------------------- .text .align 5 _hospac_dis_int: ; stc sr,r0 ; mov.l .bl_set_mask,r1 ; or r1,r0 rts ; ldc r0,sr nop ! ----------------------------------------------- ! 割り込み許可 ! void hospac_ena_int(void) ! ----------------------------------------------- .text .align 5 _hospac_ena_int: ; stc sr,r0 ; mov.l .bl_clr_mask,r1 ; and r1,r0 rts ; ldc r0,sr nop .align 4 .bl_set_mask: .long 0x10000000 .bl_clr_mask: .long 0xefffffff |
そう。とにかくrts nopな関数にした*つもり*だったのだ。
ところがいくらやっても即死状態は続いた。
ホトホト嫌気がさしてきた頃、漫然とtest.srecからsh-hitachi-elf-objdump で逆アセンブルしたリストを眺めていると…hospac_dis/ena_intが、rts nop な関数にはなっていなかった!
hospac.o: file format elf32-sh Disassembly of section .text: 00000000 <_hospac_dis_int>: 0: 00 02 stc sr,r0 2: d1 07 mov.l 20 <.bl_set_mask>,r1 ! 0x10000000 4: 20 1b or r1,r0 6: 00 0b rts 8: 40 0e ldc r0,sr a: 00 09 nop |
そうなのだ。sh-hitachi-elf-asのコメントは「!」以降であって「;」ではな いのだ!で、何故か「;」があっても何も文句を言わないのだだ!
h8300-hms-asでは「;」がコメントだったので、ついつい…。こんな事でどれ だけ時間を無駄にしたことか…。
「例外発生→SR,PC,r15をそれぞれSSR,SPC,SGRに保存→例外コードを(例外事 象|割り込み事象)レジスタにセット→特権モードかつレジスタバンク1かつ割 り込み禁止→VBR+オフセット(割り込みは0x600、TLBミスは0x400、一般例外 0x100)の*アドレスに*ジャンプ」
という流れで進む。多重割り込みを考慮する場合に一手間(SRのimaskとか)増 えたり、VBRがベクタテーブルへのベースアドレスであるアーキテクチャとは ちょっと前処理が変わるとかいう事があるのだが、それについては 「割り込みを捕まえろ」に譲る。
ここで述べたいのはどんな馬鹿なことにはまっていたか、である。
その頃既に、なんとかかんとか取り敢えずは動き始めたHOS V4ではあったが、 私は常に原因不明のリセットに悩まされていた。僅か一行の関数呼出を加えた り削ったり、リンクの順序を変えただけで、即死したり何事もなく動いたりし ているように思えた。
何なのだ。
そこで私は考えた。「そうだ!例外を捕らえれば、即死の原因が分かる!」 これは今にして思うとズレた思考なのだが…。
そこで簡単な例外処理を書いた。例外コードをSCIFを通じて表示し、無限ルー プに入るだけのものである。
さて、それではこの例外処理がちゃんと動く事を確認せねばない。その為には、 自ら特定の例外を起こし、それを正しく捕らえて表示できれば良いだろう。 手頃で確実な例外…trapaだ。例外を起こしたいポイントにasm("trapa #2"); と書いた。
小さなテストプログラムは完成し、いつものようにDreamcastに転送…即死。
なんの表示もない。
何故なんだ。何でtrapaも捕まえられないんだ!
「SRのBLが立っている時に例外が発生すると、 CPUはリセット状態になる」これを知るまでに浪費した時間って…。
何でそんなエラーがここで出るんだ。この前、ちゃんと動いていた時、お前は 何の問題もなく働いていた筈じゃないか!
次第にあっちをいぢり、こっちをいぢりした殆んど差のない作業ディレクトリ が増殖していった。そこで何度目かのmakeを実行した時である。リンクに失敗 したのだ。
sh-hitachi-elf-gcc -Wl,-Ttext=0x8c010000 -nostartfiles -nostdlib -L../../lib/sh4/obj -o test startup.o pacctx.o pacexep_asm.o pacexep_c.o scif.o hossh4.o kernel_cfg.o test.o -lgcc -lhosv4 test.o: In function `Task3': test.o(.text+0x95c): undefined reference to `__sdivsi3_i4' collect2: ld returned 1 exit status *** Error code 1 Stop in /usr/home/m-arai/work/HOSV4/b4020116/sample/sh4. |
あれ?何だってこんなものがundefined referenceになるのだ?そんなの libgcc.aに入っているに決まっているじゃないか…-lgccしてるのにな…。
mapファイルを見たりしている間に、それに気付いた…。全く当然なことだが、 libgccは-m2/-m3e/-m4/-m4-single/-m4-single-onlyそれぞれ別に存在する。 勿論、リトルエンディアン指定(-ml)に応じてさらにml版も。
このMakefileのオブジェクトリンクには…ターゲットの指定がスカっと抜けて いたのだ。これが一体如何なる効果をもたらしていたかというと…「ターゲッ トもエンディアンも違うlibgccをリンク」
エラーが発生するのも当たり前ではないか。
そこで調べてみると、増殖した作業ディレクトリのMakefileの中には、ちゃん とオブジェクトリンクに関してもターゲットアーキテクチャの指定があるもの も含まれているではないか。だから時には(つまり作業ディレクトリによって は)うまく行ったり行かなかったりしていたのか…。
意識せずに二つのMakefileを使ってしまっていた。それにしても何でリンク時 に文句を言ってくれない…いや、悪いのは私か…。
私の馬鹿馬鹿馬鹿…。
もう…何も言わない…これを…。
void foo1( int val1, int val2); void foo2( int val1, ...); void foo( void) { foo1( 1, 2); foo2( 1, 2, 3); } |
「-S -Os」すると、
_foo: mov.l r14,@-r15 mov.l .L3,r1 ! .L3: .long _foo1 mov #1,r4 sts.l pr,@-r15 mov #2,r5 jsr @r1 mov r15,r14 mov.l .L4,r1 ! .L4: .long _foo2 mov #1,r4 mov #2,r5 jsr @r1 mov #3,r6 mov r14,r15 lds.l @r15+,pr rts mov.l @r15+,r14 |
そして*日立製コンパイラと互換性をもたせる* 「-mhitachi」オプションを加えると、
_foo: mov.l r14,@-r15 mov.l .L3,r1 ! .L3: .long _foo1 mov #1,r4 sts.l pr,@-r15 mov #2,r5 jsr @r1 mov r15,r14 add #-12,r15 mov #1,r1 mov.l r1,@r15 mov #2,r1 mov.l r1,@(4,r15) mov #3,r1 mov.l r1,@(8,r15) mov.l .L4,r1 ! .L4: .long _foo2 jsr @r1 nop mov r14,r15 lds.l @r15+,pr rts mov.l @r15+,r14 |
可変長の引数の渡し方は「日立コンパイラに合わせて」変わる訳だ…。(他に 日立コンパイラでは、関数呼出においてmacレジスタも保存するらしい)
なんでリンクして出来たブツを逆アセンブルして見なかったのだろう…なぜ手 で-S付きコンパイルするとき「-mhitachi」を落してしまったのだろう…。
私の馬鹿馬鹿馬鹿馬鹿…。