まずは己を疑え

何とはなしにハマってしまった事柄を、自戎の念を込めてここに記録する。

  1. ディスプレースメントに…
  2. コメントに…
  3. 例外処理って…
  4. 二つのMakefile
  5. -mhitachi

2002.02.16 記 新井
2002.03.04 追記

ディスプレースメントに…

これはまだHOS V4の移植作業に入る準備段階のことである。

極最小限のデバッグの為にも、まずは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)
アクセスサイズ実効アドレスの計算式
バイトRn + disp → EA
ワードRn + disp×2 → EA
ロングワードRn + disp×4 → EA

gasの@(disp,Rn)
アクセスサイズ実効アドレスの計算式
バイトRn + disp → EA
ワードRn + disp → EA(但しdispは2n)
ロングワードRn + disp → EA(但しdispは4n)

運が悪かったのは、dispに指定した値がgasがアクセスサイズに対して許すも のだったことだ。もしこれが5なんて与えていたら、

Error: misaligned offset

とエラーが出て、流石の私もすぐに気付いただろう。

確かに一々アクセスサイズでアドレスの差を割って指定しなければならないより は、自然な記述が出来るという考え方もあろう。しかし…純正ニーモニックを無 視するのは…その上、一見しただけでは分からないのでは…。いや、悪いのは 私なんだが…。


コメントに…

サクっとhospac_swi_ctxやhospac_cre_ctx_asmを書き、適当にMakefileを書き 上げたらホイ出来上がり。そんな風に思っていた。

で、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では「;」がコメントだったので、ついつい…。こんな事でどれ だけ時間を無駄にしたことか…。


例外処理って…

SH4/SH3の例外処理(割り込み、TLBミス、それら以外の一般例外)は、

「例外発生→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はリセット状態になる」これを知るまでに浪費した時間って…。


二つのMakefile

なんとか例外が捕らえられるようになり、ある程度即死を避けられるようになっ た私は、しかし、問題の根本原因にたどり着くことが出来ないでいた。

何でそんなエラーがここで出るんだ。この前、ちゃんと動いていた時、お前は 何の問題もなく働いていた筈じゃないか!

次第にあっちをいぢり、こっちをいぢりした殆んど差のない作業ディレクトリ が増殖していった。そこで何度目かの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を使ってしまっていた。それにしても何でリンク時 に文句を言ってくれない…いや、悪いのは私か…。

私の馬鹿馬鹿馬鹿…。


-mhitachi

↑と書いてからそう経たぬ日。漫然とMakefileを使い回す危険を嫌という程学 んだ筈の私…。しかし、やっていた。「-mhitachi」である。

もう…何も言わない…これを…。
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」を落してしまったのだろう…。

私の馬鹿馬鹿馬鹿馬鹿…。

この項終り