Palm Programmer's Laboratory

トップ 差分 一覧 ソース 検索 ヘルプ RSS ログイン

【C/C++】 ARMlet での Palm OS API の呼出し方

[開発情報]

概要

 ここでは、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 を呼び出すコードが簡単に解説されています。

 

注意事項

 気をつけて書いているつもりではありますが、このページの記述には誤りがあるかもしれません。お気付きの方はご指摘のほどよろしくお願いします。

 

コメント

※ コメントスパムが来ますので、コメント用フォームを使用不可としました。コメントを記入したい方は、申し訳ありませんが直接入力するか、あるいは編集してコメント用フォームを有効にして入力願います。