問題
BREW では、標準的な浮動小数点演算が使えません。浮動小数点演算は BREW ヘルパ関数に置き換えなくてはいけません。
また、double 型だけのサポートで、float 型は使えません。数学関数もほとんどサポートされていません。
原因
BREW 用に SoftFP 演算を用いたコンパイルをすると、ARM コンパイラは浮動小数点演算をすべてコンパイラ組込み関数に置き換えます。
組込み関数は内部で様々なライブラリをリンクし、そのなかにはスタティック変数を使います。しかし、BREW ではスタティック変数は使えません。
また、BREW アプリには main() 関数がないので、標準ライブラリの初期化コードが自動的に呼び出されません。
目標
次のような浮動小数点演算コードが記述できるようにする。
float a = 1.0; float b = 2.0; float c; int i; c = sin(a) + cos(b); i = c * 100.05;
解決策
ARM コンパイラは、浮動小数点演算をARM コンパイラ付属の rt_fp.h に定義される ARM コンパイラ組込み関数に置き換えます。
/* * Single-precision arithmetic routines. */ extern __softfp float _fadd(float, float); extern __softfp float _fsub(float, float); extern __softfp float _fmul(float, float); extern __softfp float _fdiv(float, float); ...etc.
上記組込み関数を使うために前提となる _fp_init() は、アプリ起動時に呼び出される AEEMod_Load() で呼び出されるようにします。
/* * Call this before using any fplib routines, if you're trying to * use fplib on the bare metal. */ extern void _fp_init(void);
_fp_init() を呼び出すように変更したアプリをコンパイルしリンクするとさまざまな組込み関数がリンクされます。 その中の以下の 3 つの関数が、スタティック変数や SWI 命令を使うため、 BREW アプリは浮動小数点演算が使えません。
<rt_misc.h> /* * Redefine this to replace the library's entire signal handling * mechanism in the most efficient possible way. The default * implementation of this is what calls __raise (above). */ void __rt_raise(int /*sig*/, int /*type*/); <errno.h> extern __pure volatile int *__rt_errno_addr(void); <rt_fp.h> /* * This returns a pointer to the FP status word, when it's stored * in memory. */ extern unsigned *__rt_fp_status_addr(void);
この問題の解決策には、C 言語による方法とアセンブラのよる方法があります。( C 言語を使う方法が推奨されます。 )
上記関数のうちの 2 つは、組込み関数が内部で使う変数プールのアドレスを返すことを要求します。変数プールを何処に配置するかで実装の方法が異なります。
C 言語による __rt_raise 関数のリターゲット
C 言語の場合、変数プールを AEEApplet 構造体の一部に確保して、そのアドレスを返却するように関数を実装します。
関数が呼ばれたとき、AEEApplet 構造体のアドレスを取得するために、GETAPPINSTANCE() ヘルパ関数を呼び出します。
#if defined __arm #include <rt_misc.h > #include <errno.h > #include <rt_fp.h > #endif #if defined __arm #pragma import(__use_no_semihosting_swi) #endif typedef struct SoftFPApplet { AEEApplet aee; #if defined __arm signed int volatile _errno; unsigned int _fpstatus; #endif } SoftFPApplet; #if defined __arm void __rt_raise(signed int signal, signed int type) { return; } signed int volatile* __rt_errno_addr(void) { SoftFPApplet* ap = (SoftFPApplet*)GETAPPINSTANCE(); return(&ap->_errno); } unsigned int* __rt_fp_status_addr(void) { SoftFPApplet* ap = (SoftFPApplet*)GETAPPINSTANCE(); return(&ap->_fpstatus); } #endif int AEEClsCreateInstance(AEECLSID ClsId,IShell * pIShell,IModule * po,void ** ppObj) { *ppObj = NULL; if (ClsId == AEECLSID_SOFTFP) { if (AEEApplet_New(sizeof(SoftFPApplet), ClsId, pIShell, po, (IApplet**)ppObj, (AEEHANDLER)SoftFP_HandleEvent, NULL) == TRUE) { #if defined __arm _fp_init(); #endif // Add your code here ..... return(AEE_SUCCESS); } } return(EFAILED); }
アセンブラによる __rt_raise 関数のリターゲット
アセンブラの場合、変数プールはアプリモジュールの一部をスタティック変数領域として使います。 このとき、BREW アプリが ROPI ( Read Only Position Independent ) であることを無視します。 そのため、実際にはリードオンリーメモリなのに書き込まれるかもしれません。 従って、この方法は携帯電話によっては使えない可能性があります。
AREA ||i.__rt_raise||, CODE, READONLY EXPORT __rt_raise __rt_raise BX lr AREA ||i.__rt_errno_addr||, CODE, READONLY EXPORT __rt_errno_addr __rt_errno_addr LDR r0, table_errno_addr ADD r0, pc, r0 BX lr table_errno_addr DCD errno_addr - {PC} AREA ||i.__rt_fp_status_addr||, CODE, READONLY EXPORT __rt_fp_status_addr __rt_fp_status_addr LDR r0, table_status_addr ADD r0, pc, r0 BX lr table_status_addr DCD status_addr - {PC} AREA ||.constdata||, DATA, READONLY errno_addr DCD 0x00000000 status_addr DCD 0x00000000 END
以上のテクニックで、BREW で標準的な浮動小数点演算が利用可能になります。
SophiaFramework では、デフォルトで標準的な浮動小数点演算が利用可能です。