GNURL78やLLVM-RL78でのover 64KB ROMのchecksumを計算するcodeを考えてみるスレッド

こんにちは。NoMaYです。

最近、以下のスレッドに関わったのですが、暫く時間が経った後で思い返してみると、farキーワードの起源の8086のマイクロソフトコンパイラでの仕様とか、CC-RL及びそれに準ずるLLVM-RL78での仕様とか、それらを鑑みると、投稿した以下のコードでの64KB境界を跨いだfarポインタのインクリメントやポインタ比較は素朴に期待したようには動かないのが本来の仕様である可能性が高い筈、と思い直しました。そこで、コードを考え直してみることにしました。

続く。

C言語からのアセンブラ関数呼び出しについて
community-ja.renesas.com/cafe_rene/forums-groups/beginners/f/002-2095199602/9532/c

上のスレッドに投稿した、素朴に期待したようには動かないのが本来の仕様である可能性が高い、そんなコード(現状はGNURL78でOKだが):

#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 __attribute__((noinline, optimize("no-isolate-erroneous-paths-dereference"))) _RomSum(void)
{
    // FIXME: In case of using the immediate value in stead of `&psum_rom`, if other than -O0 is used,
    // the generated code has no ES register handling. It seems to be a bug of GNURL78 (ex. 4.9.2.202201).

    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;
}

 

Parents
  • こんにちは。NoMaYです。

    昔話ですけれども、near/farのキーワードの起源となった8086のマイクロソフトコンパイラ(ボーランドコンパイラもだったような(両方使っていましたが記憶が曖昧です))には、near/farの他にfarの上位互換のようなhugeというキーワードがありました。farとhugeの違いは以下の英語版Wikipediaにも書かれているように、ポインタ演算時に、farは下位16ビットから上位側へ桁上がりや桁借りが行われない、のに対し、hugeは下位16ビットから上位側へ桁上がりや桁借りが行われる、という違いがありました。

    en.wikipedia.org/wiki/Intel_8086#Segmentation

    Some compilers also support huge pointers, which are like far pointers except that pointer arithmetic on a huge pointer treats it as a linear 20-bit pointer, while pointer arithmetic on a far pointer wraps around within its 16-bit offset without touching the segment part of the address.


    他方、その頃、私はGCCを使っていなかったので、GCCではどういう仕様になっていたのか、というか8086向けGCCはもともと存在しなかったような(曖昧な記憶では80386向けDOSエクステンダ上で動作するDJGPP(DJ Delorie氏が開発)というGCC以前にはx86向けのものは存在しなかったような)気もしますが、私はそのあたりのことが分かりません。

    そして、GCCのドキュメントには、現在のx86向けのGCCに関してはnear/far/hugeというキーワードは存在しないようでした。

    なお、GCCのドキュメントには、M32CやRL78に関して__farキーワードの記載があるものの、例えばRL78では以下の通りとなっていて、ポインタ演算時に下位16ビットから上位側へ桁上がりや桁借りが行われるのかどうか不明です。

    gcc.gnu.org/onlinedocs/gcc/Named-Address-Spaces.html#RL78-Named-Address-Spaces

    6.17.4 RL78 Named Address Spaces

    On the RL78 target, variables qualified with __far are accessed with 32-bit pointers (20-bit addresses) rather than the default 16-bit addresses. Non-far variables are assumed to appear in the topmost 64KiB of the address space.


    さらに、GCC for Renesas RL78 4.9.2.202201のリリースノートにも以下の記載があるだけで、ポインタ演算時に下位16ビットから上位側へ桁上がりや桁借りが行われるのかどうか不明です。

    インストールフォルダ¥release_notes.pdf

    KNOWN ISSUES IN GCC 4.9.2.202201-GNURL78

    This section describes the known issues in the GCC 4.9.2.202201-GNURL78 release.

      1. ES is used without being initialized.

        Workaround:

        In order to initialize ES, the address should be stored in a far pointer before usage. So instead of:

        ((volatile reg __far*)0x000FFF).bit._1 = 0;

        the code will be:

        volatile reg __far *address0 = 0x000FFF;

        (*address0).bit._1 = 0;

      …略…


    ちなみに、CC-RLは以下の通りとなっていまして、ポインタ演算時に、__farに関しては下位16ビットから上位側へ桁上がりや桁借りが行われない、ことが明記されています。そして、LLVM-RL78はCC-RLの仕様を完コピすることを目標に開発が行われていますので、CC-RLと同じ仕様でなければならない、ということになります。

    tool-support.renesas.com/autoupdate/support/onlinehelp/ja-JP/csp/V8.08.00/CS+.chm/Compiler-CCRL.chm/Output/ccrl04c0206y0003.html

    - ポインタ演算

     - farポインタの加算は下位2バイトで行ないます。上位は変化しません。

     - farポインタの減算は下位2バイトで行ないます。上位は変化しません。

     - nearポインタとfarポインタ間の減算は,右項の型を左項の型にキャストしてから減算します。

     - nearポインタと整数の演算結果は,nearポインタとします。


    - ポインタ比較

     - オプション-strict_std指定時は,ポインタと整数間で直接比較できません。しかし,オプション-strict_std非指定時は,警告を出して直接比較を可能とします。
    警告時の比較は,整数型をポインタ型に合わせて比較します。整数型からポインタ型への変換は,キャストの項目に書いた規則に従います。

     - オプション-strict_std指定時は,変数ポインタと関数ポインタで直接比較できません。しかし,オプション-strict_std非指定時は,警告を出して直接比較を可能とします。
    警告時の比較は,次のように機能します。

     -farポインタの等値演算(==,!=)は,下位3バイトで行ないます。最上位の1バイトは演算結果に影響しません。

     -farポインタの関係演算は,下位2バイトのみで比較します。上位の2バイトは演算結果に影響しません。
    上位バイトを含めて比較する場合は,unsigned longにキャストしてから比較する必要があります。


    続く。

    [関連リンク]

    Google検索: GCC OR GNURL78 variable far
    www.google.com/search?q=GCC+OR+GNURL78+variable+far

    全ROMチェックサム計算プログラム ← CC-RLに関するスレッドです[訂正]すみません、CA78K0Rみたいな気がします
    community-ja.renesas.com/cafe_rene/forums-groups/mcu-mpu/rl78/f/forum18/1315/rom/6084#6084

    CS+のメモリ表示についての質問 ← CC-RLに関するスレッドです
    community-ja.renesas.com/cafe_rene/forums-groups/beginners/f/002-2095199602/7056/cs/37779#37779

    [余談]

    DJ Delorie氏はRedHat社でGNURL78の開発もされていたようです。

Reply
  • こんにちは。NoMaYです。

    昔話ですけれども、near/farのキーワードの起源となった8086のマイクロソフトコンパイラ(ボーランドコンパイラもだったような(両方使っていましたが記憶が曖昧です))には、near/farの他にfarの上位互換のようなhugeというキーワードがありました。farとhugeの違いは以下の英語版Wikipediaにも書かれているように、ポインタ演算時に、farは下位16ビットから上位側へ桁上がりや桁借りが行われない、のに対し、hugeは下位16ビットから上位側へ桁上がりや桁借りが行われる、という違いがありました。

    en.wikipedia.org/wiki/Intel_8086#Segmentation

    Some compilers also support huge pointers, which are like far pointers except that pointer arithmetic on a huge pointer treats it as a linear 20-bit pointer, while pointer arithmetic on a far pointer wraps around within its 16-bit offset without touching the segment part of the address.


    他方、その頃、私はGCCを使っていなかったので、GCCではどういう仕様になっていたのか、というか8086向けGCCはもともと存在しなかったような(曖昧な記憶では80386向けDOSエクステンダ上で動作するDJGPP(DJ Delorie氏が開発)というGCC以前にはx86向けのものは存在しなかったような)気もしますが、私はそのあたりのことが分かりません。

    そして、GCCのドキュメントには、現在のx86向けのGCCに関してはnear/far/hugeというキーワードは存在しないようでした。

    なお、GCCのドキュメントには、M32CやRL78に関して__farキーワードの記載があるものの、例えばRL78では以下の通りとなっていて、ポインタ演算時に下位16ビットから上位側へ桁上がりや桁借りが行われるのかどうか不明です。

    gcc.gnu.org/onlinedocs/gcc/Named-Address-Spaces.html#RL78-Named-Address-Spaces

    6.17.4 RL78 Named Address Spaces

    On the RL78 target, variables qualified with __far are accessed with 32-bit pointers (20-bit addresses) rather than the default 16-bit addresses. Non-far variables are assumed to appear in the topmost 64KiB of the address space.


    さらに、GCC for Renesas RL78 4.9.2.202201のリリースノートにも以下の記載があるだけで、ポインタ演算時に下位16ビットから上位側へ桁上がりや桁借りが行われるのかどうか不明です。

    インストールフォルダ¥release_notes.pdf

    KNOWN ISSUES IN GCC 4.9.2.202201-GNURL78

    This section describes the known issues in the GCC 4.9.2.202201-GNURL78 release.

      1. ES is used without being initialized.

        Workaround:

        In order to initialize ES, the address should be stored in a far pointer before usage. So instead of:

        ((volatile reg __far*)0x000FFF).bit._1 = 0;

        the code will be:

        volatile reg __far *address0 = 0x000FFF;

        (*address0).bit._1 = 0;

      …略…


    ちなみに、CC-RLは以下の通りとなっていまして、ポインタ演算時に、__farに関しては下位16ビットから上位側へ桁上がりや桁借りが行われない、ことが明記されています。そして、LLVM-RL78はCC-RLの仕様を完コピすることを目標に開発が行われていますので、CC-RLと同じ仕様でなければならない、ということになります。

    tool-support.renesas.com/autoupdate/support/onlinehelp/ja-JP/csp/V8.08.00/CS+.chm/Compiler-CCRL.chm/Output/ccrl04c0206y0003.html

    - ポインタ演算

     - farポインタの加算は下位2バイトで行ないます。上位は変化しません。

     - farポインタの減算は下位2バイトで行ないます。上位は変化しません。

     - nearポインタとfarポインタ間の減算は,右項の型を左項の型にキャストしてから減算します。

     - nearポインタと整数の演算結果は,nearポインタとします。


    - ポインタ比較

     - オプション-strict_std指定時は,ポインタと整数間で直接比較できません。しかし,オプション-strict_std非指定時は,警告を出して直接比較を可能とします。
    警告時の比較は,整数型をポインタ型に合わせて比較します。整数型からポインタ型への変換は,キャストの項目に書いた規則に従います。

     - オプション-strict_std指定時は,変数ポインタと関数ポインタで直接比較できません。しかし,オプション-strict_std非指定時は,警告を出して直接比較を可能とします。
    警告時の比較は,次のように機能します。

     -farポインタの等値演算(==,!=)は,下位3バイトで行ないます。最上位の1バイトは演算結果に影響しません。

     -farポインタの関係演算は,下位2バイトのみで比較します。上位の2バイトは演算結果に影響しません。
    上位バイトを含めて比較する場合は,unsigned longにキャストしてから比較する必要があります。


    続く。

    [関連リンク]

    Google検索: GCC OR GNURL78 variable far
    www.google.com/search?q=GCC+OR+GNURL78+variable+far

    全ROMチェックサム計算プログラム ← CC-RLに関するスレッドです[訂正]すみません、CA78K0Rみたいな気がします
    community-ja.renesas.com/cafe_rene/forums-groups/mcu-mpu/rl78/f/forum18/1315/rom/6084#6084

    CS+のメモリ表示についての質問 ← CC-RLに関するスレッドです
    community-ja.renesas.com/cafe_rene/forums-groups/beginners/f/002-2095199602/7056/cs/37779#37779

    [余談]

    DJ Delorie氏はRedHat社でGNURL78の開発もされていたようです。

Children
No Data