Palm Programmer's Laboratory
Palm OS Programmer's Companion Volume I/14-5
14-5 ARM コードからの Palm OS API の呼び出し
Palm OS Garnet では、ネイティブ ARM コードは 68K の世界にコールバックすることができます。これは Palm OS 関数または開発者が提供するコールバック関数です。単一のエントリポイントである Call68KFuncType が、開発者提供の 68K 関数とトラップを介した OS 関数の両方の呼び出しのためのメカニズムを提供します。この関数は PceNativeCall.h で以下のように宣言されています。
typedef unsigned long Call68KFuncType(const void *emulStateP, unsigned long trapOrFunction, const void *argsOnStackP, unsigned long argsSizeAndwantA0)
この関数のパラメータは以下のように定義されます。
- → emulStateP
- PACE エミュレーションステートを指すポインタ。PACE から ARM 関数に渡されたポインタを指定します。
- → trapOrFunction
- トラップナンバーと kPceNativeTrapNoMask の論理積、またはコールする関数のポインタ。kPceNativeTrapNoMask よりも小さな値は全てトラップナンバーとみなされます。
- → argsOnStackP
- 関数コールに先立って 68K 側のスタックにコピーされるメモリブロックのネイティブ(リトルエンディアン)ポインタ。このメモリには通常呼出される 68K 関数の引数が含まれます。Call68KFuncType は復帰前に 68K スタックからこれらの値をポップします。
- → argsSizeAndwantA0
- argsOnStackP から 68K エミュレータスタックにコピーされるバイト数(リトルエンディアンフォーマット)。関数またはトラップが結果を 68K のレジスタ A0 で返す(すなわち結果がポインタ型の)場合、このバイト数と kPceNativeWantA0 の論理和をとる必要があります。
68K 関数からの(D0 または A0 レジスタに渡される)復帰値は、argsSizeAndwantA0 に従ってこの関数の結果として返されます。この復帰値はネイティブ(リトルエンディアン)形式で返されます。
パラメータをバイトスワップしたりアライメントを正しく揃えるには相応の労力が必要になるため、ARM コードが一連のオペレーティングシステム関数を頻繁にコールする必要がある場合、オペレーティングシステム関数をコールする小さな68Kコールバック関数を用意し、その 68K 関数をコールする方が簡単かもしれません。
トラップのコール
リスト 14.2 に、トラップナンバーを使用して ARM ネイティブコードからオペレーティングシステム関数をコールする方法を示します。このサンプルでは MemPtrNew をコールして 10 バイトのメモリブロックをアロケートし、初期化してからそのポインタをARM関数の結果として返却しています。
リスト 14.2 ARM コードからの Palm OS 関数コール
/* This armlet makes a call (through PACE) to MemPtrNew to allocate a buffer. * The arguments to the OS function (here just size) must be on the stack, and * must be in big-endian format. */ #include "PceNativeCall.h" #include "endianutils.h" // byte-swapping macros // from CoreTraps.h #define sysTrapMemPtrNew 0xA013 // we need this in order to call into MemPtrNew // prototype for our OS call convenience function void *PalmOS_MemPtrNew (const void *emulStateP, Call68KFuncType *call68KFuncP, unsigned long sizeLE); // This is the main entry point into the armlet. It's the first function in // the file so we can calculate its address easily. unsigned long NativeFunction (const void *emulStateP, void *userData68KP, Call68KFuncType *call68KFuncP) { unsigned char *bufferP; int i; // allocate 10 bytes of memory using a convenience function bufferP = (unsigned char*)PalmOS_MemPtrNew(emulStateP, call68KFuncP, 10); // Do something with the bytes in the buffer for (i = 0; i < 9; i++) bufferP[i] = i+'A'; // write in "ABCDEFGHI" bufferP[9] = 0; // terminate the string return (unsigned long)bufferP; } // Convenience function for calling MemPtrNew within ARM code void *PalmOS_MemPtrNew(const void *emulStateP, Call68KFuncType *call68KFuncP, unsigned long sizeLE) { // First, declare the argument(s) that will be passed to the OS call. // In this case, we're calling MemPtrNew, so we need a size argument. // Because this code is compiled by an ARM compiler (little endian), // and MemPtrNew expects its argument to be big endian, swap it. unsigned long sizeBE = ByteSwap32(sizeLE); // Call the trap. Note that because MemPtrNew returns a pointer, the byte // count (the last parameter) must be “OR”d” with kPceNativeWantA0. return ((void *)((call68KFuncP)(emulStateP, PceNativeTrapNo(sysTrapMemPtrNew), &sizeBE, 4 | kPceNativeWantA0))); }
関数ポインタを使用した関数呼び出し
リスト 14.3 のコード抜粋は、ARM ネイティブコードに 68K コールバック関数を渡す方法を示しています。また、リスト 14.4 は ARM コードから 68K 関数をコールする方法を示しています。このARMコードは最終的には先程と同じ処理(MemPtrNewのコール)を行ないますが、この例ではアロケートの実行は 68K 側のコールバック関数が行ないます。ここで、コールバック関数のポインタはデータとして構造体に埋め込まれて ARM コードに渡されることに注意して下さい。
68K側では以下の内容が実装されます。
リスト 14.3 ARM コードからの PACE アプリケーションコール(68K側)
typedef struct MyParamsTag { void *myAllocateFunctionP; UInt32 anotherValue; } MyParamsType; // function to allocate 10 bytes void *MyAllocateFunction() { return MemPtrNew(10); } // code to call the native function, defined in the next listing MyParamsType myParams; MemHandle armChunkH; void *myNativeFuncP; Byte *result; armChunkH = DmGetResource('armc', 0); myNativeFuncP = MemHandleLock(armChunkH); myParams.myAllocateFunctionP = &MyAllocateFunction; result = (Byte *)PceNativeCall(myNativeFuncP, &myParams);
ARM ファイルでは、以下のコードがコールバック関数のポインタを受け取り、10バイトのメモリブロックをアロケートするために68K関数を使用します。
リスト 14.4 ARM コードからの PACE アプリケーションコール(ARM側)
typedef struct MyParamsTag { void *myAllocateFunctionP; unsigned long anotherValue; } MyParamsType; unsigned long MyNativeFunc(const void *emulStateP, void *userData68KP, Call68KFuncType *call68KFunc) { unsigned char *buffer68K; // array of Byte unsigned char i; // Byte void *my68KFuncP; // get the function pointer out of the passed parameter block my68KfuncP = ByteSwap4(userData68KP->myAllocateFunctionP); // invoke the callback function to allocate 10 bytes buffer68K = (void *)((call68KFunc)(emulStateP, my68KFuncP, &size, 4 | kPceNativeWantA0)); // do something with the bytes in the buffer for (i = 10; i > 0; i--) buffer68K[i] = i; return (unsigned long)buffer68K; }