{{category 開発情報}} {{category 陰郎の書いた記事,nolink}} !!概要  ここでは、Palm OS 5 での ARMlet 呼び出しにおいて、ARMlet 側から Palm OS API を呼び出す方法について簡単に解説します。 !!説明  まず、ARMlet 呼び出しについて簡単におさらいしておきます。68K コードから ARMlet を呼び出すための API 関数である PceNativeCall( ) は以下のようなものでした。 UInt32 PceNativeCall( NativeFuncType *nativeFuncP, void *userDataP );  この PceNativeCall 関数、第1パラメータとして(コードリソースなどから取得した)ARMlet 関数のポインタを、第2パラメータとしてユーザ定義の構造体(など)のポインタを指定することになっています。そして、ARMlet のエントリポイントである NativeFuncType は PceNativeCall.h にて以下のように typedefine されています。 typedef unsigned long NativeFuncType( const void *emulStateP, void *userData68KP, Call68KFuncType *call68KFuncP );  この NativeFuncType に適合する関数を ARMlet 内で作成して呼び出すことになるわけですが、ここで第2パラメータである userData68KP に 68K コードからユーザ定義の構造体(など)のポインタが渡されることになるわけですね。そして、このポインタ自体は endian の変換を Palm OS 側でやってくれるわけですが、このポインタが指しているデータそのものについては Palm OS は何も知らないため、自前で変換をしてあげなければならない、ということでした。おさらい終了。  では、この ARMlet 関数の内部から Palm OS API を呼び出すにはどうすればよいか。Palm OS SDK に付属のドキュメント「Palm OS 5 ARM Programming.pdf」に解説されているのですが、ここで ARMlet 関数のパラメータとして渡される call68KFuncP を使用することになります。このポインタ、型は Call68KFuncType となっていますが、この関数もまた PceNativeCall.h にて typedefine されています。 typedef unsigned long Call68KFuncType( const void *emulStateP, unsigned long trapOrFunction, const void *argsOnStackP, unsigned long argsSizeAndwantA0 );  call68KFuncP を経由してこの Call68KFuncType を呼び出す場合、第1パラメータの emulStateP には ARMlet 関数の第1パラメータに渡されたモノをそのまま渡します。次の trapOrFunction には、CoreTraps.h から望みの API の core trap number を拾って PceNativeTrapNo マクロにかけたものを渡します。その次の argsOnStackP がクセモノなのですが、パラメータとして渡す値を格納した変数のアドレスを渡します。パラメータが複数ある場合は、スタック上で隣接するように(すなわち、ローカル変数ならば並べて宣言)します。そして、最後のパラメータである argsSizeAndwantA0 でパラメータ全体のサイズを指定します。呼び出す Palm OS API の復帰値がポインタ型である場合、最後のパラメータの値と kPceNativeWantA0 で OR をとります。  Palm OS SDK のサンプルに含まれている armlet-oscall.c から適当に抜き出してきたコードを以下に示します。この ARMlet 関数では、単純に 10 バイトのメモリを MemPtrNew で確保して 68K コードに返しています。そうそう、Palm OS API に渡すパラメータは、endian をひっくり返さなければならないようです。 unsigned long MyNativeOSCall( const void* emulStateP, void* userData68KP, Call68KFuncType* call68KFuncP ) { unsigned long size = 10; unsigned char* pBuffer = NULL; size = EndianSwap32( size ); pBuffer = (unsigned char*)( call68KFuncP( emulStateP, PceNativeTrapNo( sysTrapMemPtrNew ), &size, 4 | kPceNativeWantA0 ) ); return (unsigned long)pBuffer; }    基本的には以上です。最後に、筆者が ARMlet で使用している(現時点では使おうとしている)カプセル化クラスを簡単に紹介します。以下 C++ になりますがご容赦下さい。以下のようなクラスを用意し、emulStateP と call68KFuncP をメンバとして持たせます。そして、メンバ関数として Palm OS API を使用する分だけ宣言します。 class PalmOSAPI { private: const void* m_pEmulState; Call68KFuncType* m_pCall68KFunc; public: PalmOSAPI( const void* pEnumState, Call68KFuncType* pCall68KFunc ); public: MemPtr MemPtrNew( UInt32 size ); Err MemPtrFree( MemPtr p ); };  これらのメンバに対して、以下のような実装を用意します。コンストラクタは面倒なので省略します。 MemPtr PalmOSAPI::MemPtrNew( UInt32 size ) { size = ByteSwap32( size ); MemPtr p = reinterpret_cast( m_pCall68KFunc( m_pEmulState, PceNativeTrapNo( sysTrapMemPtrNew ), &size, 4 | kPceNativeWantA0) ); return p; } Err PalmOSAPI::MemPtrFree( MemPtr p ) { p = reinterpret_cast( ByteSwap32( p ) ); Err ret = m_pCall68KFunc( m_pEmulState, PceNativeTrapNo( sysTrapMemChunkFree ), &p, 4 ); return ret; }  こうしておけば、先ほどの 10 バイトのメモリを確保するコードは以下のように書けるようになります。 unsigned long MyNativeOSCall( const void* emulStateP, void* userData68KP, Call68KFuncType* call68KFuncP ) { PalmOSAPI api( emlStateP, call68KFuncP ); MemPtr pBuffer = api.MemPtrNew( 10 ); return (unsigned long)pBuffer; }   !!参考情報  Palm OS SDK のドキュメントに含まれている「Palm OS 5 ARM Programming.pdf」にて、ARMlet プログラミングについて解説されています。また、Palm OS SDK に含まれている PceNativeCall.h や CoreTraps.h に眼を通しておきましょう。さらに、Palm OS SDK のサンプルに含まれている armlet-oscall.c では ARMlet から Palm OS API を呼び出すコードが簡単に解説されています。   !!注意事項  気をつけて書いているつもりではありますが、このページの記述には誤りがあるかもしれません。お気付きの方はご指摘のほどよろしくお願いします。   !!コメント ※ コメントスパムが来ますので、コメント用フォームを使用不可としました。コメントを記入したい方は、申し訳ありませんが直接入力するか、あるいは編集してコメント用フォームを有効にして入力願います。 //{{comment multi}}