C言語からのアセンブラ関数呼び出しについて

いつもお世話になっております。

far領域(00000H~017DFFH)のRomデータを取得して加算するプログラムをC言語で作成したのですが、010000H以上のアクセスができませんでした。逆アセンブリをみたところ、ESレジスタの記述が見つからず、そこで、データ取得処理をアセンブラで記述してみました。

アセンブラ関数は、

16ビットアドレスのfar領域アクセス用関数(ES=0x00)と、20ビットアドレスのfar領域アクセス用関数(ES=0x01)を

2つ用意して、共に引数をunsigned short アドレスとして、戻り値をunsigned charでデータとしました。

Cソースファイルからアセンブラ関数を呼び出して、指定アドレスのデータを取得したいのですが、取得出来ません。

ステップ実行で確認すると、アセンブラ関数では指定のアドレスデータを取得できて、戻り値としてAレジスタにセットしてリターンしているのですが、C側に戻ると取得データは格納されておらず、逆アセンブリを見てみると、別のアドレスを指していました。

C言語からアセンブラ関数を呼び出す際の注意点などありましたら、教えていただきたく、宜しくお願いいたします。

【環境】

 マイコン  :RL78/G13(S2コア)

 開発環境  :e2studio ver 2022-04 (22.4.0)

 コンパイラ :GCC for Renesas RL78 ver 4.9.2.202201  最適化 -o1

 プロジェクト:C++、C混在

  • はし さん、こんにちは。NoMaYと申します。

    本件は、双方の話の行き違いを減らす為に、まずソースが見たいです。発端の話も含めて、以下のソースを見せて頂けませんか?(単に投稿に貼り付けるだけですとインデントがおかしくなって後の手間が増えますので、出来ればソースをzipファイルに固めたものをリプライに添付して頂けると有難いです。)

    (1) 以下のCソース(プロジェクトの全てのCソースということではありません)

    「far領域(00000H~017DFFH)のRomデータを取得して加算するプログラムをC言語で作成したのですが、010000H以上のアクセスができませんでした。」

    (2) 以下の呼び出される側のASMソースと呼び出す側のCソース(プロジェクトの全てのCソース/ASMソースということではありません)

    「16ビットアドレスのfar領域アクセス用関数(ES=0x00)と、20ビットアドレスのfar領域アクセス用関数(ES=0x01)を2つ用意して、共に引数をunsigned short アドレスとして、戻り値をunsigned charでデータとしました。」



    なお、咄嗟には、(2)に関して以下のように書かれている点で、Aレジスタに入れるのはCC-RLもしくLLVM-RL78でのルールだった筈で、GNURL78では違うルールだった筈で、そこからおかしくなっているように感じました。追ってデバッグして確認しますけれども。

    > 戻り値としてAレジスタにセットしてリターン

  • はし さん、こんにちは。NoMaYです。

    > Aレジスタに入れるのはCC-RLもしくLLVM-RL78でのルールだった筈で、GNURL78では違うルールだった筈で、

    ルールは以下のファイルに記載されています。これを読むと、やはり違いますね。(戻り値の返し方も、引数の渡し方も。)

    インストールフォルダ¥Doc¥RL78-ABI.html


    …略…

    Arguments to functions are always passed on the stack, with the first argument in the parameter list pushed last. Note: this is the common "args on stack" layout most compilers use. 16-bit and larger types shall be 16-bit aligned. If an 8-bit type is passed between two 16-bit types, the 8-bit type shall be padded towards the LSB (i.e. towards address zero, so it ends up on an even address too).

    Return values shall be returned in R8..Rn for as many registers as are needed to store the value. That lets us store 8, 16, 32, and 64 bit values in the same place, without using up all the "real" registers (AX, BC, etc) that might be needed to compute the return value.

    …略…


    [追記]

    かふぇルネでは、相変わらず、半角 64 が *** の伏字になってしまいますので、全角 64 に変えてあります。

  • NoMaYさま

    ご回答いただきありがとうございます。

    プログラムを整理していたら時間がかかってしまいました。

    すぐに返答できず、申し訳ございません。

    Cソースを添付いたします。

    サム値を格納する領域をセクションを切って、const変数をそこに配置するようにしております。

    そのconst変数のアドレスを用いて、00000H~const変数アドレスまで、ROMデータを取得して加算を考えております。

    セクションの開始アドレスを017DFEHに設定したいのですが、00FFFEHまでしか正しく動きません。

    const変数のアドレスを格納する変数をunsigned shortとしていることが原因だと思うのですが、unsigned longにするとよく分からない値が格納されてしまい、加算処理が途中で終了してしまいます。

    昨日問い合わせ内容では、ESの記述が無く~と記載したのですが、今回プログラムを整理して確認したところ、ESの記述はちゃんとありました。申し訳ございません。

    ES=0x1としたかったのですが、上記のカウント処理が上手く行かないことから、アセンブラに挑戦してみよう(カウンタで0x10000超えたらES=0x1セット)と考えて、質問いたしました。

    もし、Cソースの方で解決策がありましたら、ご教授いただければ幸いです。

    (質問と異なってしまいますが、、、)

    アセンブラの件、ありがとうございました。

    インストールフォルダ内にあることを失念しておりました(一生懸命ルネサスのホームページなどで検索しておりました)。

    Aレジスタではないですね。

    こちらも検討を進めたいと思います。

    お手数をおかけしますが、宜しくお願いいたします。

    1_C言語.zip

  • はし さん、こんにちは。NoMaYです。

    > const変数のアドレスを格納する変数をunsigned shortとしていることが原因だと思うのですが、

    まさに、その通りだと私も思います。

        …略…
        unsigned long   count;
        unsigned short  sum_s = 0x00000L;
        unsigned short  sum_e = (void*)&psum_rom; ← sum_eはunsigned short!
        …略…

     

        …略…
        for ( count = sum_s ; count < sum_e ; count += 2 ) ← countがunsigned longでも、sum_eはunsigned shortですから。
        …略…

     


    > unsigned longにするとよく分からない値が格納されてしまい、加算処理が途中で終了してしまいます。

    思うに、ソースを整理する前の最初のソースが動作しなかったので、何とかしようと頑張っているうちに迷走?気味になっているような、そんな気がするのですが、、、すみません、最初のソースはもっと素朴なコードではなかったのでしょうかね?それとも、最初のものも頂いたソースと大差無い(といっても主観的なものですが)ものでしたか?もしも頂いたものとは違って素朴なものだったのでしたら、出来れば、そちらも見せて頂きたいのですが、、、

  • NoMaYさま

    ご確認いただきありがとうございます。

    ソース整理では、色々と関係ないものを排除したので、処理自体は変更していないです(ESレジスタについては再確認はしました)。

    ただ、この処理にはベースのソフトがあります。

    こちらは、バイト単位の加算処理で、prom自体をインクリメントしております。

    また、セクションも番地指定をせずに、プログラムの最後尾にサム値を格納するものでした。

    (たまたまプログラムの容量が小さかったので、16bitアドレス内で加算出来ればOKだったようです)

    今回は、ワード単位の加算&プログラムの全領域をサムの対象とする必要があり、処理を変更し、デバッグをしておりました。

    一応、ベースソフトを添付いたします(ただ、こちらも010000H以降の加算は不可です。)。

    宜しくお願いいたします。

    2_C言語(ベース).zip

  • はし さん、こんにちは。NoMaYです。

    それぞれソースをどうもありがとうございました。ベースのソースを見て(当方の話でしかないのですけれども)合点が行きました。また、頂いたソースを弄りながらビルドして何が起きているのかも見えて来たような気がします。高度に専門的な話になるのですが、GNURL78のPLTと呼ばれる機構にまつわる話のような気がしてきました。

    細かい話は後にした方が良いかも、とも思いつつ、、、

    実はGNURL78には関数farポインタが存在しません。(変数farポインタは存在します。) 関数farポインタが存在しないとなると0x10000以上に配置された関数を関数ポインタで呼び出すことが出来ないのではないか?という話になりますが、もともとGCCに備わっていたPLT(Procedure Link Table)という機構を流用/適用して、0x10000以上に配置された関数のアドレスが参照されると、0x10000未満の領域にスタブが自動的に作られて、そのスタブのアドレスが値として返されるような仕組みになっています。スタブといっても大したものではなくて、0x10000以上に配置された関数の本体の先頭へジャンプする分岐命令があるだけのものなのですが、スタブのアドレスは必ず0x10000未満になっていて、必ず関数nearポインタに格納可能なものになっているのです。

    問題は、どうも、その機構が以下の変数定義に対しても発動してしまっているようなのです、、、

    const unsigned short psum_rom __attribute__ ((section (".psum"))) = 0x1234;

     
    手元では、マップファイル上には以下のようにpsum_romに対してPLTに関するセクションが自動的に作られていました。

    .lowtext        0x00000000000000d8        0x4
     *(.plt)
     .plt           0x00000000000000d8        0x4 ./src/hashi_gnurl78_far.o ← ファイル名は気にしないで下さい
                    0x00000000000000da                psum_rom.plt
     *(.lowtext)
                    0x00000000000000dc                . = ALIGN (0x2)

     
    推測ですが、このアドレスが以下の文面の「よく分からない値」の正体ではないかと思われます。(なお、今回、リンカスクリプトは頂かなかったので当方の手元のものとはアドレスが微妙に違うかも知れません。)

    > const変数のアドレスを格納する変数をunsigned shortとしていることが原因だと思うのですが、unsigned longにするとよく分からない値が格納されてしまい、加算処理が途中で終了してしまいます。

    対策(および少し当方の頭を冷やして再度の原因見直し)について、この後で考えてみようと思います。

  • はし さん、こんにちは。NoMaYです。

    しばらく五月雨式にリプライしますけれども(一気に解に辿り着けなさそうなので)、しばらく静観していて下さい、、、



    > const変数のアドレスを格納する変数をunsigned shortとしていることが原因だと思うのですが、unsigned longにするとよく分からない値が格納されてしまい、加算処理が途中で終了してしまいます。

    推測ですが、上の赤文字の話はコンパイル時のワーニングを無視していた?ことも関係すると思いますが、ワーニングを取って以下のようなソースにしても、それでもsum_eの値は意図したものになりません。続く。

    ソース

    const unsigned short psum_rom   __attribute__ ((section (".psum"))) = 0x1234;

    unsigned short _RomSum( void )
    {
        const   unsigned char   __far   *prom;
                unsigned long   count;
                unsigned long   sum_s = 0x00000L;
                unsigned long   sum_e = (unsigned long)(const unsigned char __far *)&psum_rom; ← 0xf00d8になってしまう
                unsigned char   rom_h, rom_l;
                unsigned short  sum = 0;

        // 1Word単位で加算
        prom = (const unsigned char __far *)sum_s;
        for ( count = sum_s ; count < sum_e ; count += 2 )
        {
            rom_h = *prom;
            ++prom;
            rom_l = *prom;
            ++prom;

            sum += (unsigned short)(((unsigned short)rom_h << 8) | rom_l);
        }

        return( sum );
    }

     
    リンカスクリプト

        …略…
        .psum 0x017DFE: AT(0x017DFE)
        {
            *(.psum)
        } > ROM
        …略…

     
    マップファイル

    …略…
    .lowtext        0x00000000000000d8        0x4
     *(.plt)
     .plt           0x00000000000000d8        0x4 ./src/hashi_gnurl78_far.o
                    0x00000000000000da                psum_rom.plt
     *(.lowtext)
                    0x00000000000000dc                . = ALIGN (0x2)
    …略…
    .psum           0x0000000000017dfe        0x2
     *(.psum)
     .psum          0x0000000000017dfe        0x2 ./src/hashi_gnurl78_far.o
                    0x0000000000017dfe                psum_rom
    …略…

     

  • ただ、よくよく考えてみるとGNURL78のnear/farの世界では、以下の変数定義は__farが抜けているのでは?というのがあります、、、

    何か変?

    const unsigned short psum_rom   __attribute__ ((section (".psum"))) = 0x1234;

     
    上よりは望ましい気がする

    const unsigned short __far psum_rom __attribute__ ((section (".psum"))) = 0x1234;


    ただし、コンパイル時のリストファイルを見ると、なんとセクション名が意図したものではない、、、

     169                                    .global _psum_rom
     170                                    .section    .froda,"a",@progbits
     171                                    .balign 2
     174                                _psum_rom:
     175 0000 34 12                         .short  4660

     
    続く。

  • 対症療法探しの最中に気付いた、思わず笑ってしまったセクション名の不具合、ですが、なんで a がダブるのさ、、、

    ソース

    const unsigned short __far psum_rom    __attribute__ ((section (".frodata2.psum"))) = 0x1234;

     
    リストファイル(なんで a がダブるのさ、、、)

     169                                    .global _psum_rom
     170                                    .section    .frodaata2.psum,"a",@progbits
     171                                    .balign 2
     174                                _psum_rom:
     175 0000 34 12                         .short  4660

      
    続く。

  • 対症療法探しの最中に気付いた、また別のセクション名の不具合(だと思うのだけれども)、ですが、なんで f が先頭に勝手に付くのさ、、、

    ソース(セクション名の先頭に f は付けていないのだけれども)

    const unsigned short __far psum_rom    __attribute__ ((section (".rodata2.psum"))) = 0x1234;

     
    リストファイル(なんで f が先頭に勝手に付くのさ、、、)

     169                                    .global _psum_rom
     170                                    .section    .frodata2.psum,"a",@progbits
     171                                    .balign 2
     174                                _psum_rom:
     175 0000 34 12                         .short  4660

     
    続く。