- 追加された行はこのように表示されます。
- 削除された行は
このように表示されます。
{{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<MemPtr>( m_pCall68KFunc( m_pEmulState,
PceNativeTrapNo( sysTrapMemPtrNew ),
&size, 4 | kPceNativeWantA0) );
return p;
}
Err PalmOSAPI::MemPtrFree( MemPtr p ) {
p = reinterpret_cast<MemPtr>( 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}}