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混在

Parents
  • はし さん、こんにちは。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にするとよく分からない値が格納されてしまい、加算処理が途中で終了してしまいます。

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

Reply
  • はし さん、こんにちは。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にするとよく分からない値が格納されてしまい、加算処理が途中で終了してしまいます。

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

Children
  • 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

     
    続く。

  • const __farで修飾された変数定義で__attribute__((section("セクション名")))でセクション名を変更するのは諦め、先日別スレッドで知ったリンカスクリプトの小技を使ってみました。(もっとも、そこは瑣末の部分で、本質的な部分はconstでは無くconst __farで修飾するように変更したこと、ですけれども。) そして、sum_eの値を確認すると期待した通りの0x017dfeになりました。まだ続きます。

    関連リンク

    GCCプロジェクトで変数や関数を指定のセクションに配置する方法
    japan.renesasrulz.com/cafe_rene/forums-groups/mcu-mpu/rx/f/forum5/7937/gcc

    ソース

    const unsigned short __far psum_rom = 0x1234; /* This variable is placed at the end of the ROM by linker script. */

    unsigned short _RomSum( void )
    {
        const   unsigned char   __far   *prom;
                unsigned long   count;
                unsigned long   sum_s = 0x00000L;
                unsigned long   sum_e = (unsigned long)&psum_rom; ← 期待した通りの0x017dfeになる(ちなみに強制的キャストを1つ減らせました)
                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 );
    }

     
    リンカスクリプト

        …略…
        .frodata :
        {
            . = ALIGN(2);
            *(.frodata)
            *(EXCLUDE_FILE(*/hashi_gnurl78_far.o) .frodata.*)
            _efrodata = .;
        } > ROM
        …略…
        .psum 0x017DFE: AT(0x017DFE)
        {
            */hashi_gnurl78_far.o(.frodata.*)
        } > ROM
        …略…

     
    マップファイル

    …略…
    .lowtext        0x00000000000000d8        0x0
     *(.plt)
     .plt           0x00000000000000d8        0x0 ./src/hashi_gnurl78_far.o ← 今回は中身は何もありません
     *(.lowtext)
                    0x00000000000000d8                . = ALIGN (0x2)

    …略…
    .frodata        0x00000000000004d2        0x0
                    0x00000000000004d2                . = ALIGN (0x2)
     *(.frodata)
     *(EXCLUDE_FILE(*/hashi_gnurl78_far.o) .frodata.*)
                    0x00000000000004d2                _efrodata = .
    …略…
    .psum           0x0000000000017dfe        0x2
     */hashi_gnurl78_far.o(.frodata.*)
     .frodata.psum_rom
                    0x0000000000017dfe        0x2 ./src/hashi_gnurl78_far.o ← ちなみに0x1234が入っています
                    0x0000000000017dfe                psum_rom
    …略…

     

  • ワーニングに気を付けていれば気が付けるだろうかなぁ、と考えを巡らせていたのですけれども、当方でソースを整理した以下のソースでも、const __farで修飾された変数定義をconstだけの修飾にしても(e2 studioで設定出来る限りのワーニング検出を有効にしていても)何もワーニングは出ることもなく誤動作するようになってしまいますので(PLTのスタブのアドレスが値として返されてしまってsum_eが期待した値になりませんので)、何と言うか、気を付けましょう、の精神論ぐらいしか思い浮かびませんでした、、、

    #include <stdint.h>
    const uint16_t __far psum_rom = 0x1234; /* This variable is placed at the end of the ROM by linker script. */
    uint16_t _RomSum(void);
    uint16_t _RomSum(void)
    {
        // FIXME: If one of -O2, -O3, -Os is used, the generated code calls `abort()` immediately.
        // FIXME: In case of using the immediate value in stead of `&psum_rom`, if other than -O0, -Og is
        // used, the generated code has no ES register handling. (Note: If one of -O2, -O3, -Os is used,
        // the generated code calls `abort()` immediately. Therefore `other than -O0, -Og` means only -O1.)

        const uint16_t __far *prom, *sum_s, *sum_e;
        uint16_t sum = 0;

        sum_s = 0;
        sum_e = &psum_rom; // (const uint16_t __far *)0x017DFEUL; --> No ES register handling.
        for( prom = sum_s; prom < sum_e; prom++ )
        {
            sum += *prom;
        }
        return sum;
    }

     
    続く。

  • 少し脱線しますが、最適化オプションで-O2, -O3, -Osを指定したところ、ソースのプログラムがNULL領域をアクセスすることをGNURL78がコンパイル時に検出して、ソッコーでabort()を呼び出す以下のコードが生成されましたね、、、(Linux/Windows/MacOS/その他もろもろ、といった汎用OS向けのGCCの機能として存在することは耳にしていましたが、いざ自分が遭遇すると、組み込み向けでは勘弁して欲しいなぁ、といったところですかね、、、(少なくとも0番地側にフラッシュメモリを配置するタイプのマイコンでは、、、))

      32                                .text
      34                                    .global _abort
      35                                    .section    .text._RomSum,"ax",@progbits
      36                                    .global __RomSum
      38                                __RomSum:
      44 0000 11 AF 00 00                   movw    ax, es:!0
      45 0004 FC 00 00 00                   call    !!_abort

     
    続く。

  • また少し脱線しますが、ソースを以下の通りに変更するとESレジスタを操作していないコードが生成されますね、、、

    ソース変更箇所

        sum_e = (const uint16_t __far *)0x017DFEUL;

     
    リストファイル(最適化オプションは-O1です)

      32                                .text
      34                                    .section    .text._RomSum,"ax",@progbits
      35                                    .global __RomSum
      37                                __RomSum:
      41                                    ; start of function
      42                                    ; push 2: r16
      43                                    ; uses ES register
      44 0000 61 EF                         sel rb2
      45 0002 C1                            push    ax ; r16
      47 0003 61 CF                         sel rb0
      50 0005 C9 F4 00 00                   movw    r12, #0
      51 0009 C9 F6 00 00                   movw    r14, #0
      53 000d C9 F0 00 00                   movw    r8, #0
      55                                .L2:
      57 0011 AD F4                         movw    ax, r12
      58 0013 BD F2                         movw    r10, ax
      59 0015 FA F2                         movw    hl, r10
      60 0017 AB                            movw    ax, [hl]
      61 0018 06 F0                         addw    ax, r8
      62 001a BD F0                         movw    r8, ax
      *** 001c AD F4                         movw ax, r12
      65 001e 04 02 00                      addw ax, #2
      66 0021 BD F4                         movw r12, ax
      67 0023 AD F6                         movw ax, r14
      68 0025 61 D8                         sknc
      69 0027 A1                            incw ax
      70 0028 BD F6                         movw r14,ax
      73 002a AD F6                         movw    ax, r14
      74 002c 44 01 00                       cmpw   ax, #1
      75 002f AD F4                          movw   ax, r12
      76 0031 61 F8                          sknz
      77 0033 44 FE 7D                       cmpw   ax, #32254
      78 0036 61 E8                          skz
      79 0038 EC 11 00 00                    br !!.L2
      81 003c 61 EF                         sel rb2
      82 003e C0                            pop ax ; r16
      83 003f 61 CF                         sel rb0
      84 0041 D7                            ret


    続く。