|
|
Technote 1127
In Search of... ...Missing Links*
(*注“スタートレック”とは関係ありません。)
問題: 欠落ルーチン
クラシック 68K の API のいくつかは PowerPC および CFM-68K
の InterfaceLib に含まれていません。そうしたルーチンを CFM
コードから呼び出すと、プログラムのリンク時に「シンボルが解決できない」というエラーが発生します。
解決策:
グルーを書く
この TECHNOTE
で提供するのは一時的な解決策で、プログラムにインクルードできるグルーコードの作成方法を説明します。欠落した
API それぞれについて 1
つずつグルールーチンを書く必要があります。各ルーチンは 68K A
トラップのアドレスを決定し、適切なパラメータを指定して、CallUniversalProc
か CallOSTrapUniversalProc を呼び出します。将来欠落している
API を含む新しい InterfaceLib
ファイルがリリースされたら、グルーファイルなしでプロジェクトをビルドし直すことができます。
簡単なサンプル
(非常に簡単な 4 例)
注意: 次の 4 つの API は欠落 API ではなく、ちゃんと
InterfaceLib
に入っています。簡単な例を示す目的で取り上げました。
<MacWindows.h> には
InitWindows、SelectWindow、FrontWindow、CheckUpdate
が次のように定義されています。
extern pascal void InitWindows(void)
ONEWORDINLINE(0xA912);
extern pascal void SelectWindow(WindowPtr's theWindow)
ONEWORDINLINE(0xA91F);
extern pascal WindowPtr FrontWindow(void)
ONEWORDINLINE(0xA924);
extern pascal Boolean CheckUpdate(EventRecord *theEvent)
ONEWORDINLINE(0xA911);
|
InitWindows の場合、次のように A
トラップのアドレスと Pascal 型の ProcInfo を指定して
CallUniversalProc を呼び出します。
pascal void InitWindows(void)
// ONEWORDINLINE(0xA912);
{
CallUniversalProc(GetToolboxTrapAddress(0xA912),
kPascalStackBased);
}
|
それほどむずかしくありませんでしたね。GetToolboxTrapAddress
の 0xA912 がどこから来ているかわからない方のために、それは
ONEWORDINLINE マクロにあります。<Traps.h>
の中から探すこともできます。さあ、次はパラメータを渡す例です。
extern pascal void SelectWindow(WindowPtr theWindow)
// ONEWORDINLINE(0xA91F);
{
CallUniversalProc(GetToolboxTrapAddress(0xA91F),
kPascalStackBased |
STACK_ROUTINE_PARAMETER(1,SIZE_CODE(sizeof(WindowPtr))),
theWindow // the parameter
);
}
|
何もむずかしいことはありません。procinfo に
STACK_ROUTINE_PARAMETER
マクロを加え、パラメータを追加しました。次は値を返す例です。
extern pascal WindowPtr FrontWindow(void)
// ONEWORDINLINE(0xA924);
{
return (WindowPtr) CallUniversalProc(
GetToolboxTrapAddress(0xA924),
kPascalStackBased |
RESULT_SIZE(SIZE_CODE(sizeof(WindowPtr)))
);
}
|
これは RESULT_SIZE
マクロを使って、スタック上の結果のサイズを定義します。CallUniversalProc
が返す long を WindowPtr
に型変換しなくてはならないことに注意してください。さて、ここまで学習したことを組み合わせてみましょう。次は、パラメータが
1 個で、値を返す API です。
extern pascal Boolean CheckUpdate(EventRecord *theEvent)
// ONEWORDINLINE(0xA911);
{
return (Boolean) CallUniversalProc(
GetToolboxTrapAddress(0xA911),
kPascalStackBased |
RESULT_SIZE(SIZE_CODE(sizeof(Boolean))) |
STACK_ROUTINE_PARAMETER(1,SIZE_CODE(sizeof(EventRecord*)))
);
}
|
レジスタベースの
A トラップ
オペレーティングシステムへの A
トラップはパラメータをレジスタで渡します。アップルは最近
PBXGetVolInfo (PBX の「X」に注目) の A
トラップを導入しました。これは PBGetVolInfo
のように動作しますが、新たに大容量ボリューム情報を返すことができるようになりました。CFM
からは次のようにして呼び出すことができます。
// 警告: このコードは使わないように - 後続のセクションを読んでください。
//#pragma parameter __D0 PBXGetVolInfoSync(__A0)
pascal OSErr PBXGetVolInfoSync(XVolumeParamPtr paramBlock)
// TWOWORDINLINE(0x7012, 0xA060);
{
return (OSErr) CallOSTrapUniversalProc(
(UniversalProcPtr) GetOSTrapAddress(0xA060),
kRegisterBased |
RESULT_SIZE(SIZE_CODE(sizeof(OSErr))) |
REGISTER_RESULT_LOCATION(kRegisterD0) |
REGISTER_ROUTINE_PARAMETER(1, kRegisterD0,
kTwoByteCode) | // selector
REGISTER_ROUTINE_PARAMETER(2, kRegisterA0,
SIZE_CODE(sizeof(XVolumeParamPtr*))),
0x0012, // selector
paramBlock); // parameter(s)
}
// 警告: このコードは使わないように - 後続のセクションを読んでください。
|
最初に注目してほしいのは、CallUniversalProc
ではなく、CallOSTrapUniversalProc
を使用していることです。CallOSTrapUniversalProc 関数は、OS
の A
トラップを実行する標準的な呼び出し方法にしたがって、指定のユニバーサルプロシージャポインタに対応するルーチンを実行します。A1、A2、D1、D2
のレジスタはルーチンが実行される前に保存され、実行完了後に復元されます。さらに、トラップワード内のフラグビットが設定されているため、レジスタ
A0 も保存復元されます。
セレクタ (0x0012)
はどのように決定したのでしょうか。MacsBug で TWOWORDINLINE
の 16 進コードをディスアセンブルすると、次のようになります。
MacsBug> dh 7012 A060
MOVEQ #$12,D0
_FSDispatch
|
誤解しないでください。API と xWORDINLINE の情報は
Universal Interface から直接コピーしています。この場合は
pragma
パラメータを見ると、どのレジスタがどこで使われているかがわかります
(OSErr は DO に、XVolumeParamPtr パラメータは
A0)。先の場合のように、xWORDINLINE
マクロをディスアセンブルすると、セレクタ情報と A
トラップが得られます。ProcInfo に kD0DispatchedRegisterBased
の呼び出し規則はないので、DO
セレクタも他のレジスタベースのパラメータと同じように処理しなければなりません。
GetToolboxTrapAddress の代わりに GetOSTrapAddress
を使っていることに注目してください。これによって別の問題が発生します。OS
の A トラップには、A トラップのビット 9 とビット 10
でフラグを渡すものがあるのです。例えば、次の NewPrt
を見てください。
_NewPtr = 0xA11E
_NewPtrSys = 0xA51E
_NewPtrClear= 0xA31E
_NewPtrSysClear = 0xA71E
|
つまり、ビット 9 は 'Sys' ビットで、ビット 10 は
'Clear' ビットであることがわかります。これら 4 つの A
トラップは、単独のディスパッチャーを通じて処理され、フラグビットでどのルーチンを呼び出すかが決まります。ご存じですか。PBXGetVolInfoSync
(0xA060) の A トラップも同様です。
_FSDispatch = 0xA060
_HFSDispatch = 0xA260
_FSDispatchAsync = 0xA460
_HFSDispatchAsync = 0xA660
|
Trap Manager の詳細については、『Inside Macintosh:
Operating Systems Utilities』を参照してください。
それでは、A トラップディスパッチャーは、どの A
トラップを呼び出すのかをどうして判断するのでしょうか。A
トラップは D1 レジスタで渡します。ですから、上記の
PBXGetVolInfoSync のグルーを修正するには、D1 レジスタで A
トラップを渡すことを Mixed Mode Manager
に伝える別のパラメータを追加する必要があります。
注意:
ここでは順序がたいへん重要です。CFM グルーが CFM
コードでパッチされたトラップを呼び出していて、ProcInfo
パラメータの順序が同じでなかったら、そのパッチは元のパラメータの順番で呼び出されます。正規の順序は、セレクタ
(必要な場合)、D1 (A
トラップ)、高級言語のプロトタイプに現れる順序で並んだパラメータです。
pascal OSErr PBXGetVolInfoSync(XVolumeParamPtr paramBlock)
// TWOWORDINLINE(0x7012, 0xA060);
{
return (OSErr) CallOSTrapUniversalProc (
(UniversalProcPtr) GetOSTrapAddress(0xA060),
kRegisterBased |
RESULT_SIZE(SIZE_CODE(sizeof(OSErr))) |
REGISTER_RESULT_LOCATION(kRegisterD0) |
REGISTER_ROUTINE_PARAMETER(1, kRegisterD0,
kTwoByteCode) | // selector
REGISTER_ROUTINE_PARAMETER(2, kRegisterD1,
kTwoByteCode) | // A-Trap
REGISTER_ROUTINE_PARAMETER(3, kRegisterA0,
SIZE_CODE(sizeof(XVolumeParamPtr*))),
0x0012, // selector
0xA060, // A-Trap
paramBlock); // parameter(s)
}
|
レジスタでディスパッチされる
A トラップ
レジスタでディスパッチされる A
トラップでは、セレクタをレジスタ D0 または D1
のいずれかで渡す必要があります。次に示すのは、最近まで
CFM-68K の InterfaceLib から欠落していた AppleGuide API
です。
pascal AGErr AGGeneral(AGRefNum refNum, AGEvent theEvent)
// TWOWORDINLINE(0x700D, 0xAA6E);
{
return (AGErr) CallUniversalProc(GetToolboxTrapAddress(0xAA6E),
kD0DispatchedPascalStackBased |
RESULT_SIZE(SIZE_CODE(sizeof(AGErr))) |
DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE(kTwoByteCode) |
DISPATCHED_STACK_ROUTINE_PARAMETER(1,
SIZE_CODE(sizeof(refNum))) |
DISPATCHED_STACK_ROUTINE_PARAMETER(2,
SIZE_CODE(sizeof(theEvent))),
0x000D, // selector -> D0
refNum,theEvent // the parameters
);
}
|
このコードで最初に注目してほしいのはセレクタ
(0x0012) です。TWOWORDINLINE マクロから MacsBug
で値を取得する、次の方法を覚えておいてください。
MacsBug> dh 700D AA6E
MOVEQ #$0D,D0
_AGGeneral
|
ここから、この API が D0
でディスパッチされることが類推できます。さて、DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE
に使用する正しい値は、どのようにして調べたらよいのでしょうか。もう一度、次のようにして
MacsBug で、_AppleGuideDispatch の A
トラップを通じてディスパッチされる API
のリストを取得することができます。
MacsBug> api AA6E
。 AA6E _AppleGuideDispatch
DO.W=0001 AGOpen
DO.W=0002 AGOpenWithSearch
DO.W=0003 AGOpenWithSequence
<...>
|
この結果から、セレクタは実際にはワードベースだということがわかります。ですから、DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE
には kTwoByteCode という値を指定します。
スタックでディスパッチされる
A トラップ
スタックでディスパッチされる A
トラップには、スタックでセレクタを渡す必要があります。TEGetPoint
は InterfaceLib
に含まれていますが、ここではこれを例として借りて、注意点を説明します。
// 警告: このコードは使わないように - 後続の段落を読んでください。
static Point TEGetPoint(short offset,TEHandle hTE)
{
// THREEWORDINLINE(0x3F3C, 0x0008, 0xA83D);
// MOVE.W #$0008,-(A7)
// _TEDispatch
// _TEGetPoint is A83D (_TEDispatch) when A7^.W=0008
// A7^.W=0008 のとき _TEGetPoint は A83D (_TEDispatch)
return (Point) CallUniversalProc(
GetToolboxTrapAddress(_TEDispatch), // ディスパッチャーのアドレス
kStackDispatchedPascalStackBased | // proc info
RESULT_SIZE(SIZE_CODE(sizeof(Point))) |
DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE(kTwoByteCode) |
DISPATCHED_STACK_ROUTINE_PARAMETER(1,
SIZE_CODE(sizeof(short))) |
DISPATCHED_STACK_ROUTINE_PARAMETER(2,
SIZE_CODE(sizeof(TEHandle))),
0x0008, // selector -> stack
offset, hTE // parameter(s)
);
}
// 警告: このコードは使わないように - 後続の段落を読んでください。
|
今度は、セレクタはスタック上のワードとして渡されます。おわかりですか。だいたいわかりますね。これは引っかけの質問で、むずかしいところまで飛ばしてしまおうとする人に注意を喚起するためです。TEGetPoint
は Point
を返します。問題はありません。それは型変換すればよいのです。やってみてください。おっと、ヒューストン、問題が起こった。コンパイラが「エラー:
明示的な型変換が不正 - 'long' から 'struct
Point'」と文句を言っています。これを回避するには、結果をローカル変数に保管し、そのアドレスを
Point
へのポインタに型変換しなくてはなりません。そして、次のように、ポインタから値を取り出して正確な結果を得ます。
static Point TEGetPoint(short offset,TEHandle hTE)
{
// THREEWORDINLINE(0x3F3C, 0x0008, 0xA83D);
// MOVE.W #$0008,-(A7)
// _TEDispatch
// _TEGetPoint is A83D (_TEDispatch) when A7^.W=0008
// A7^.W=0008 のとき _TEGetPoint は A83D (_TEDispatch)
long private_results = CallUniversalProc(
GetToolboxTrapAddress(_TEDispatch), // ディスパッチャーのアドレス
kStackDispatchedPascalStackBased | // proc info
RESULT_SIZE(SIZE_CODE(sizeof(Point))) |
DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE(kTwoByteCode) |
DISPATCHED_STACK_ROUTINE_PARAMETER(1,
SIZE_CODE(sizeof(short))) |
DISPATCHED_STACK_ROUTINE_PARAMETER(2,
SIZE_CODE(sizeof(TEHandle))),
0x0008, // selector
offset, hTE // parameter(s)
);
return *(Point*) &private_results;
}
|
構造体のサイズが 4
バイトの場合はこれですべてうまくいきます。しかし、4
バイトより小さい場合はどうなるのでしょう。次のサンプルを試してください。
typedef struct CryptoCharsRecord {
char findChar;
char replaceChar;
} CryptoCharsRec, *CryptoCharsPtr, **CryptoCharsHdl;
|
CallUniversalProc から返されるこの構造体を long
の結果に保存する場合、次のようになります (Mac OS
は上位バイトを先に、下位バイトを後に保存することを思い出してください)。
+---------------------------+
Low Mem | 0 | <-- private_result のアドレス
+---------------------------+
| 1 |
+---------------------------+
| 2 | findChar
+---------------------------+
High Mem| 3 | replaceChar
+---------------------------+
|
この場合、単純な型変換もポインタから取り出した値の型変換もうまくいきません。やりかたは、結果のアドレスに上位ワードへのオフセットを加えたものから値を取り出します。
pascal CryptoCharsRec GetEncodeKey(char* pThePassword)
{
long private_results = CallUniversalProc(
GetToolboxTrapAddress(0xAxxx),
kPascalStackBased |
RESULT_SIZE(SIZE_CODE(sizeof(Boolean))) |
STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(EventRecord*)))
theEvent // the parameter
);
return *(((CryptoCharsRec*)&private_result) + 1);
}
|
一見して、オフセットが 2
でなければならないと思うかもしれませんが、この場合はバイトのオフセットでなく、2
バイトの構造体のオフセットだということを思い出してください。C
言語はこのことを認識しており、1 ではなく 2
を加算します。こうしたオフセットの計算は非常に間違いやすいので、変数または変数型が与えられた場合にオフセットを計算するマクロをここに紹介しておきます。
#define RESULT_OFFSET(type) \
((sizeof(type) == 1) ? 3 : ((sizeof(type) == 2) ? 1 : 0))
|
何もむずかしいことはありません。構造体のサイズが 1
だったら、上位バイトへのオフセット、3 (バイト)
になります。構造体のサイズが 2
だったら、上位ワードへのオフセット、1 (ワード)
になります。このいずれでもなければオフセットはゼロです。これは次のように使用します。
pascal CryptoCharsRec Get_Encode_Key(char* pThePassword)
{
long private_results = CallUniversalProc(
GetToolboxTrapAddress(0xA911),
kPascalStackBased |
RESULT_SIZE(SIZE_CODE(sizeof(Boolean))) |
STACK_ROUTINE_PARAMETER(1,
SIZE_CODE(sizeof(EventRecord*)))
theEvent // the parameter
);
return *(((CryptoCharsRec*)&private_result) +
RESULT_OFFSET(CryptoCharsRec));
}
|
おわかりですか。では、結果が 4
バイトよりも大きかったらどうなるでしょうか。この場合、CallUniversalProc
を使用してルーチンを呼び出すことはできません。私が見た
(マシンが生成した) グルーコードには、次のように、結果が
CallUniversalProc
の処理可能なサイズよりも大きいかどうかテストするものがありました。
#ifdef applec
#if sizeof(OSErr) > 4
#error "Result types larger than 4 bytes are not supported."
#endif
#endif
|
実際のサンプル
実際に InterfaceLib から欠落している API
をいくつか見てみましょう。まず、Cursor Device Manager の API
です。
pascal OSErr CursorDeviceMoveTo(CursorDevicePtr ourDevice, long absX, long absY)
{
// TWOWORDINLINE(0x7001, 0xAADB);
// MOVEQ #$01,D0 | 7001
// _CursorDeviceDispatch | AADB
return (OSErr) CallUniversalProc(
(UniversalProcPtr)GetToolboxTrapAddress(0xAADB),
kD0DispatchedPascalStackBased |
RESULT_SIZE(SIZE_CODE(sizeof(OSErr))) |
DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE(kFourByteCode) |
DISPATCHED_STACK_ROUTINE_PARAMETER(1,
SIZE_CODE(sizeof(ourDevice))) |
DISPATCHED_STACK_ROUTINE_PARAMETER(2,
SIZE_CODE(sizeof(absX))) |
DISPATCHED_STACK_ROUTINE_PARAMETER(3,
SIZE_CODE(sizeof(absY))),
0x00000001, // selector
ourDevice, absX, absY); // parameter(s)
}
|
次は OSUtils.h の欠落 API です。
static pascal OSErr FlushCodeCacheRange(void *address, unsigned long count)
{
// TWOWORDINLINE(0x7009, 0xA098);
// MOVEQ #$09,D0| 7009
// _HWPriv | A098
return (OSErr) CallOSTrapUniversalProc(
GetOSTrapAddress(_HWPriv),
kRegisterBased
| RESULT_SIZE (SIZE_CODE (sizeof (OSErr)))
| REGISTER_RESULT_LOCATION (kRegisterD0)
| REGISTER_ROUTINE_PARAMETER(1,kRegisterA0,
SIZE_CODE(sizeof(address)))
| REGISTER_ROUTINE_PARAMETER(2,kRegisterA1,
SIZE_CODE(sizeof(count)))
address,count); // parameter(s)
}
|
次の API は InterfaceLib にもヘッダにもありません
(ここまで読んでくださった方へのお礼の意味をこめて)。
pascal ComponentResult TVSetFrequency(TVTunerComponent ci, long frequency)
//FIVEWORDINLINE(0x2F3C, 0x04, kSelectTVSetFrequency, 0x7000, 0xA82A);
// MOVE.L #$00040001,-(A7)
// MOVEQ #$00,D0
// _ComponentDispatch
{
return (ComponentResult) CallUniversalProc(
(UniversalProcPtr) GetToolboxTrapAddress(0xA82A),
kD0DispatchedPascalStackBased |
RESULT_SIZE(SIZE_CODE(sizeof(ComponentResult))) |
DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE(kTwoByteCode) |
DISPATCHED_STACK_ROUTINE_PARAMETER( 1,
SIZE_CODE(sizeof(long))) |
DISPATCHED_STACK_ROUTINE_PARAMETER( 2,
SIZE_CODE(sizeof(TVTunerComponent))) |
DISPATCHED_STACK_ROUTINE_PARAMETER( 3,
SIZE_CODE(sizeof(long))),
0x0000, // D0 selector
0x00040001, // Stack dispatched selector
ci, frequency); // parameter(s)
}
|
完全なサンプル:
HFS+
HFS+用の eXtended DiskInit
ルーチンの完全なグルーファイルを次に掲載します。
/*
File: DiskInit.Glue.c
Copyright: ゥ 1984-1997 by Apple Computer, Inc.
All rights reserved.
*/
#include <DiskInit.h>
#include <MixedMode.h>
static UniversalProcPtr gPack2TrapUPP = kUnresolvedCFragSymbolAddress;
static UniversalProcPtr gUnimplementedUPP = kUnresolvedCFragSymbolAddress;
pascal OSErr DIXFormat(short drvNum, Boolean fmtFlag,
unsigned long fmtArg, unsigned long *actSize)
// THREEWORDINLINE(0x700C, 0x3F00, 0xA9E9);
// MOVEQ #$0C,D0 | 700C
// MOVE.W D0,-(A7) | 3F00
// _Pack2 | A9E9
{
long private_result = unimpErr; // 未実装の A トラップを仮定
if ((Ptr) gUnimplementedUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gUnimplementedUPP = GetToolboxTrapAddress(_Unimplemented);
if ((Ptr) gPack2TrapUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gPack2TrapUPP = GetToolboxTrapAddress(0xA9E9);
if ((Ptr) gPack2TrapUPP != (Ptr) gUnimplementedUPP)
{
private_result = CallUniversalProc(gPack2TrapUPP,
kStackDispatchedPascalStackBased |
RESULT_SIZE(SIZE_CODE(sizeof(OSErr))) |
DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE(kTwoByteCode) |
DISPATCHED_STACK_ROUTINE_PARAMETER(1,
SIZE_CODE(sizeof(short))) |
DISPATCHED_STACK_ROUTINE_PARAMETER(2,
SIZE_CODE(sizeof(Boolean))) |
DISPATCHED_STACK_ROUTINE_PARAMETER(3,
SIZE_CODE(sizeof(unsigned long))) |
DISPATCHED_STACK_ROUTINE_PARAMETER(4,
SIZE_CODE(sizeof(unsigned long))),
0x000C, // selector
drvNum, fmtFlag, fmtArg, actSize); // parameter(s)
}
return (OSErr) private_result;
}
pascal OSErr DIXZero(short drvNum, ConstStr255Param volName,
short fsid, short mediaStatus, short volTypeSelector,
unsigned long volSize, void *extendedInfoPtr)
// THREEWORDINLINE(0x700E, 0x3F00, 0xA9E9);
// MOVEQ #$0E,D0 | 700E
// MOVE.W D0,-(A7) | 3F00
// _Pack2 | A9E9
{
long private_result = unimpErr; // 未実装の A トラップを仮定
if ((Ptr) gUnimplementedUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gUnimplementedUPP = GetToolboxTrapAddress(_Unimplemented);
if ((Ptr) gPack2TrapUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gPack2TrapUPP = GetToolboxTrapAddress(0xA9E9);
if ((Ptr) gPack2TrapUPP != (Ptr) gUnimplementedUPP)
{
private_result = CallUniversalProc(gPack2TrapUPP,
kStackDispatchedPascalStackBased |
RESULT_SIZE(SIZE_CODE(sizeof(OSErr))) |
DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE(kTwoByteCode) |
DISPATCHED_STACK_ROUTINE_PARAMETER(1,
SIZE_CODE(sizeof(short))) |
DISPATCHED_STACK_ROUTINE_PARAMETER(2,
SIZE_CODE(sizeof(ConstStr255Param))) |
DISPATCHED_STACK_ROUTINE_PARAMETER(3,
SIZE_CODE(sizeof(short))) |
DISPATCHED_STACK_ROUTINE_PARAMETER(4,
SIZE_CODE(sizeof(short))) |
DISPATCHED_STACK_ROUTINE_PARAMETER(5,
SIZE_CODE(sizeof(short))) |
DISPATCHED_STACK_ROUTINE_PARAMETER(6,
SIZE_CODE(sizeof(unsigned long))) |
DISPATCHED_STACK_ROUTINE_PARAMETER(7,
SIZE_CODE(sizeof(void *))),
0x000E, // selector
drvNum, volName, fsid, mediaStatus, // parameter(s)
volTypeSelector, volSize, extendedInfoPtr);
}
return (OSErr) private_result;
}
pascal OSErr DIReformat(short drvNum, short fsid,
ConstStr255Param volName, ConstStr255Param msgText)
// THREEWORDINLINE(0x7010, 0x3F00, 0xA9E9);
// MOVEQ #$10,D0 | 7010
// MOVE.W D0,-(A7) | 3F00
// _Pack2 | A9E9
{
long private_result = unimpErr; // 未実装の A トラップを仮定
if ((Ptr) gUnimplementedUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gUnimplementedUPP = GetToolboxTrapAddress(_Unimplemented);
if ((Ptr) gPack2TrapUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gPack2TrapUPP = GetToolboxTrapAddress(0xA9E9);
if ((Ptr) gPack2TrapUPP != (Ptr) gUnimplementedUPP)
{
private_result = CallUniversalProc(gPack2TrapUPP,
kStackDispatchedPascalStackBased |
RESULT_SIZE(SIZE_CODE(sizeof(OSErr))) |
DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE(kTwoByteCode) |
DISPATCHED_STACK_ROUTINE_PARAMETER(1,
SIZE_CODE(sizeof(short))) |
DISPATCHED_STACK_ROUTINE_PARAMETER(2,
SIZE_CODE(sizeof(short))) |
DISPATCHED_STACK_ROUTINE_PARAMETER(3,
SIZE_CODE(sizeof(ConstStr255Param))) |
DISPATCHED_STACK_ROUTINE_PARAMETER(4,
SIZE_CODE(sizeof(ConstStr255Param))),
0x0010, // selector
drvNum, fsid, volName, msgText); // parameter(s)
}
return (OSErr) private_result;
}
|
完全なサンプル:
ControlStrip
/*
File: ControlStrip.Glue.c
Copyright: ゥ 1984-1997 by Apple Computer, Inc.
All rights reserved.
*/
#include <MixedMode.h>
#include <ControlStrip.h>
static UniversalProcPtr gControlStripTrapUPP = kUnresolvedCFragSymbolAddress;
static UniversalProcPtr gUnimplementedUPP = kUnresolvedCFragSymbolAddress;
#define _ControlStripDispatch 0xAAF2
pascal Boolean SBIsControlStripVisible(void)
// TWOWORDINLINE(0x7000, 0xAAF2);
// MOVEQ #$00,D0
// _ControlStripDispatch
{
long private_result = 0L;
if ((Ptr) gUnimplementedUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gUnimplementedUPP =
GetToolboxTrapAddress(_Unimplemented);
if ((Ptr) gControlStripTrapUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gControlStripTrapUPP =
GetToolboxTrapAddress(_ControlStripDispatch);
if ((Ptr) gControlStripTrapUPP != (Ptr) gUnimplementedUPP)
{
private_result = CallUniversalProc(gControlStripTrapUPP,
kD0DispatchedPascalStackBased |
DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE(kTwoByteCode) |
RESULT_SIZE(SIZE_CODE(sizeof(Boolean))),
0x0000); // selector
}
return (Boolean) private_result;
}
pascal void SBShowHideControlStrip(Boolean showIt)
// THREEWORDINLINE(0x303C, 0x0101, 0xAAF2);
// MOVE.W #$0101,D0
// _ControlStripDispatch
{
if ((Ptr) gUnimplementedUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gUnimplementedUPP = GetToolboxTrapAddress(_Unimplemented);
if ((Ptr) gControlStripTrapUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gControlStripTrapUPP = GetToolboxTrapAddress(_ControlStripDispatch);
if ((Ptr) gControlStripTrapUPP != (Ptr) gUnimplementedUPP)
{
CallUniversalProc(gControlStripTrapUPP,
kD0DispatchedPascalStackBased |
DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE(kTwoByteCode) |
DISPATCHED_STACK_ROUTINE_PARAMETER(1,
SIZE_CODE(sizeof(Boolean))),
0x0101, // selector
showIt); // parameter(s)
}
}
|
完全なサンプル:
Power Manager
/*
File: Power.Glue.c
Copyright: ゥ 1984-1997 by Apple Computer, Inc.
All rights reserved.
*/
#include <MixedMode.h>
#include <Power.h>
static UniversalProcPtr gPowerTrapUPP = kUnresolvedCFragSymbolAddress;
static UniversalProcPtr gUnimplementedUPP = kUnresolvedCFragSymbolAddress;
pascal Boolean HardDiskPowered(void)
// TWOWORDINLINE(0x7006, 0xA09E);
// MOVEQ #$06,D0 | 7006 ; Move selector
// _PowerMgr | A09E ; for _HardDiskPowered
{
long private_result = 0L;
if ((Ptr) gUnimplementedUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gUnimplementedUPP = GetToolboxTrapAddress(_Unimplemented);
if ((Ptr) gPowerTrapUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gPowerTrapUPP = GetToolboxTrapAddress(0xA09E);
if ((Ptr) gPowerTrapUPP != (Ptr) gUnimplementedUPP)
{
private_result = CallUniversalProc(gPowerTrapUPP,
kD0DispatchedPascalStackBased |
DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE(kTwoByteCode) |
RESULT_SIZE(SIZE_CODE(sizeof(Boolean))),
0x0006); // selector
}
return (OSErr) private_result;
}
pascal void SpinDownHardDisk(void)
// TWOWORDINLINE(0x7007, 0xA09E);
// MOVEQ #$07,D0 | 7006 ; Move selector
// _PowerMgr | A09E ; for _SpinDownHardDisk
{
if ((Ptr) gUnimplementedUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gUnimplementedUPP = GetToolboxTrapAddress(_Unimplemented);
if ((Ptr) gPowerTrapUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gPowerTrapUPP = GetToolboxTrapAddress(0xA09E);
if ((Ptr) gPowerTrapUPP != (Ptr) gUnimplementedUPP)
{
CallUniversalProc(gPowerTrapUPP,
kD0DispatchedPascalStackBased |
DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE(kTwoByteCode),
0x0007); // selector
}
}
pascal Boolean IsSpindownDisabled(void)
// TWOWORDINLINE(0x7008, 0xA09E);
// MOVEQ #$08,D0 ; Move selector
// _PowerMgr ; for _IsSpindownDisabled
{
long private_result = 0L;
if ((Ptr) gUnimplementedUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gUnimplementedUPP = GetToolboxTrapAddress(_Unimplemented);
if ((Ptr) gPowerTrapUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gPowerTrapUPP = GetToolboxTrapAddress(0xA09E);
if ((Ptr) gPowerTrapUPP != (Ptr) gUnimplementedUPP)
{
private_result = CallUniversalProc(gPowerTrapUPP,
kD0DispatchedPascalStackBased |
DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE(kTwoByteCode) |
RESULT_SIZE(SIZE_CODE(sizeof(Boolean))),
0x0008); // selector
}
return (OSErr) private_result;
}
// これら 2 つの API は A トラップで D0 の上位ワードに論理値を入れて渡す
// という変則的なものです。下位ワードにはセレクタが入ります。
// (変ですね)
pascal void SetSpindownDisable(Boolean setDisable)
// FOURWORDINLINE(0x4840, 0x303C, 0x0009, 0xA09E);
// SWAP D0 ; Move setDisable to high-Word of D0
// MOVE.W #$0009,D0 ; Move selector to low-Word of D0
// _PowerMgr ; for _SetSpindownDisable
{
if ((Ptr) gUnimplementedUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gUnimplementedUPP = GetToolboxTrapAddress(_Unimplemented);
if ((Ptr) gPowerTrapUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gPowerTrapUPP = GetToolboxTrapAddress(0xA09E);
if ((Ptr) gPowerTrapUPP != (Ptr) gUnimplementedUPP)
CallUniversalProc(gPowerTrapUPP,
kD0DispatchedPascalStackBased |
DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE(kTwoByteCode),
(setDisable << 16) | 0x0009); // selector
}
pascal void AutoSleepControl(Boolean enableSleep)
// FOURWORDINLINE(0x4840, 0x303C, 0x000D, 0xA09E);
// SWAP D0 ; Move setDisable to high-Word of D0
// MOVE.W #$000D,D0 ; Move selector to low-Word of D0
// _PowerMgr ; for _AutoSleepControl
{
if ((Ptr) gUnimplementedUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gUnimplementedUPP = GetToolboxTrapAddress(_Unimplemented);
if ((Ptr) gPowerTrapUPP == (Ptr) kUnresolvedCFragSymbolAddress)
gPowerTrapUPP = GetToolboxTrapAddress(0xA09E);
if ((Ptr) gPowerTrapUPP != (Ptr) gUnimplementedUPP)
CallUniversalProc(gPowerTrapUPP,
kD0DispatchedPascalStackBased |
DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE(kTwoByteCode),
(enableSleep << 16) | 0x000D); // selector
}
|
要約
これで、InterfaceLib
から欠落しているルーチンすべてのグルーを書く方法を紹介しましたから、もう言い訳はできませんね。今こそ、すばらしい
CFM アプリケーションを書くべきときです。
参考文献
- 『Inside Macintosh: PowerPC System Software』の第 2
章「Mixed Mode Manager」
- Joe Zobkiw 著『A Fragment of Your Imagination』
(ISBN:0-201-48358)
|