高度な検索
Developer Connection
Member Login ログイン | ご入会 ADC連絡先

Technote 1127

In Search of...
...Missing Links*

(*注“スタートレック”とは関係ありません。)


目次

問題: 欠落ルーチン

解決策: グルーを書く

簡単なサンプル

レジスタベースの A トラップ

レジスタでディスパッチされる
A トラップ

スタックでディスパッチされる
A トラップ

実際のサンプル

完全なサンプル: HFS+

完全なサンプル: ControlStrip

完全なサンプル: Power Manager

要約

「失われたリンク」はいたる所に存在します。CFM アプリケーションを書いていて、FlushCodeCacheRange を呼び出すとき、コントロールストリップを表示するとき、隠すとき、Cursor Device Managerを使うとき、同じ問題に悩まされます。それが「失われたリンク」です。これらの API (Application Programming Interfaces) はまだ InterfaceLib に実装されていません。それではどうしたらよいのでしょうか。この TECHNOTE には「失われたリンク」の修復方法が記載されています。これは、クラシック 68K アプリケーションから CFM ルーチンを呼び出す方法について書かれた「TECHNOTE 1077」の続編です。この TECHNOTE では、クラシック 68K の A トラップルーチンを呼び出す CFM グルーコードの書き方を説明します。

この TECHNOTE は、クラシック 68K の API の中で、現在 InterfaceLib から欠落している API を使用するデベロッパを対象に書かれています。

警告: これは上級者向けのトピックで、API が欠落しているために開発を続行できないデベロッパが使うものです。アップルは、この TECHNOTE のリリース後も、失われたリンクを見つけて InterfaceLib に追加する努力を続けていきます。


問題: 欠落ルーチン

クラシック 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)