Mar 96 Tips
Volume Number: | | 12
|
Issue Number: | | 3
|
Column Tag: | | Tips & Tidbits
|
Tips & Tidbits
By Steve Sisak, Contributing Editor
Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.
Calling PowerPC Code
From 68K Code
There is a lot of information out there about calling 68K code from PowerPC code, but there is very little about calling PowerPC code from 68K code.
Here is a nice method for calling PowerPC code from 68K code. I am using CodeWarrior 7. This is a bit lengthy so hang in there:
There are three steps required to get things up and running. Lets look at a sample scenario:
You have a 68K application that you just cant convert to PowerPC code (I dont know why you cant convert it, but just bear with me), but you can take some of the time-critical code and convert it to a PowerPC shared library and call it from the 68K code.
Suppose you have two functions that perform data compression:
long CompressData( unsigned char *inBuffer,
unsigned char *outBuffer,
unsigned long inBufferSize );
long UncompressData(unsigned char *inBuffer,
unsigned char *outBuffer,
unsigned long inBufferSize );
Step 1 - Create a PPC shared library containing your functions.
We are going to export these functions using the #pragma export directive. This is set in the PPC Prefs area of Codewarrior preferences.
the shared library
// Prototypes
#pragma export on
#ifdef __cplusplus
extern "C" {
#endif
long CompressData( unsigned char *inBuffer,
unsigned char *outBuffer,
unsigned long inBufferSize );
long UncompressData( unsigned char *inBuffer,
unsigned char *outBuffer,
unsigned long inBufferSize );
#ifdef __cplusplus
}
#endif
#pragma export off
// CompressData
long CompressData( unsigned char *inBuffer, unsigned char
*outBuffer, unsigned long inBufferSize )
{
... compress data code
}
// UncompressData
long UncompressData( unsigned char *inBuffer, unsigned char
*outBuffer, unsigned long inBufferSize )
{
... uncompress data code
}
Set the project type to a shared library and build the project. We now have a PowerPC shared library that we will use in Step 2.
Step 2 - Create a resource-based PEF Fragment that references our shared library.
We create a header file that defines some mixed mode information.
the header file
#ifdef __cplusplus
extern "C" {
#endif
typedef long (*CompressDataProcPtr)( unsigned char *inBuffer,
unsigned char *outBuffer, unsigned long inBufferSize );
typedef long (*UncompressDataProcPtr)( unsigned char *inBuffer,
unsigned char *outBuffer, unsigned long inBufferSize);
#if GENERATINGCFM || USESROUTINEDESCRIPTORS
typedef UniversalProcPtr CompressDataUPP;
typedef UniversalProcPtr UncompressDataUPP;
enum {
uppCompressDataProcInfo = kCStackBased
| RESULT_SIZE( SIZE_CODE( sizeof( long ) ) )
| STACK_ROUTINE_PARAMETER( 1, SIZE_CODE(
sizeof( unsigned char *) ) )
| STACK_ROUTINE_PARAMETER( 2, SIZE_CODE(
sizeof( unsigned char *) ) ) | STACK_ROUTINE_PARAMETER( 3, SIZE_CODE(
sizeof( long ) ) )
};
enum {
uppUncompressDataProcInfo = kCStackBased
| RESULT_SIZE( SIZE_CODE( sizeof( long ) ) )
| STACK_ROUTINE_PARAMETER( 1, SIZE_CODE(
sizeof( unsigned char *) ) )
| STACK_ROUTINE_PARAMETER( 2, SIZE_CODE(
sizeof( unsigned char *) ) )
| STACK_ROUTINE_PARAMETER( 3, SIZE_CODE(
sizeof( long ) ) )
};
#else
typedef CompressDataProcPtr CompressDataUPP;
typedef UncompressDataProcPtr UncompressDataUPP;
#endif
//============================================================
// Extern Globals
//
#ifndef powerc
extern CompressDataUPP myCompressDataUPP;
extern UncompressDataUPP myUncompressDataUPP;
#endif
//============================================================
// Macros
//
#if GENERATINGPOWERPC || defined(powerc) || defined (__powerc)
#define myCompressData( a, b, c ) CompressData( a, b, c )
#define myUncompressData( a, b, c )UncompressData( a, b, c )
#else
#define myCompressData( a, b, c ) \
(*(CompressDataUPP)myCompressDataUPP( a, b, c )
#define myUncompressData( a, b, c ) \
(*(CompressDataUPP)myUncompressDataUPP( a, b, c )
#endif
//============================================================
// Prototypes
//
long CompressData( unsigned char *inBuffer, unsigned char
*outBuffer, unsigned long inBufferSize );
long UncompressData( unsigned char *inBuffer, unsigned char
*outBuffer, unsigned long inBufferSize );
#ifdef __cplusplus
}
#endif
Now we define the source file:
the source file
#include "the header file"
#ifdef __cplusplus
extern "C" {
#endif
#if GENERATINGPOWERPC || defined(powerc) || defined (__powerc)
#pragma options align=mac68k
#endif
#ifdef __CFM68K__
#pragma lib_export on
#endif
RoutineDescriptorCompressDataRD =
BUILD_ROUTINE_DESCRIPTOR( uppCompressDataProcInfo,
CompressData );
RoutineDescriptorUncompressDataRD =
BUILD_ROUTINE_DESCRIPTOR( uppUncompressDataProcInfo,
UncompressData );
#ifdef __CFM68K__
#pragma lib_export off
#endif
#if GENERATINGPOWERPC || defined(powerc) || defined (__powerc)
#pragma options align=reset
#endif
#ifdef __cplusplus
}
#endif
Add the shared library we built in Step 1 to this project.
Set the project type to shared library and build the project. We now have a PowerPC shared library that we want to convert to a resource. (Note: we dont export this as a PowerPC code resource with no header, since we would get link errors for an undefined main entry.)
Open the file up in a resource editor and create a new resource (I use a type of 'PEF ', but any will do). Move the PEF code from the data fork into this resource. (If your resource editor will not do this, then it is a simple matter of creating a small application that will do this for you; this excercise is left for the reader.)
We now have a resource file that we will add to our 68K project.
Step 3 - Add 68K support code to your project
Your 68K project needs to have a bit of support code to get the code resource and load it in as a code fragment. Add to your code:
#include "the header file"
We need to declare some globals for the universal procs that will store the address of the routine descriptors. Add to your code:
#ifndef powerc
CompressDataUPP myCompressDataUPP;
UncompressDataUPPmyUncompressDataUPP;
#endif
The following function takes care of checking for the existence of the CFM and Mixed Mode managers. We pass it a pointer to a CFragConnectionID variable and a Handle variable, which we will use later on for clean up. Add to your code:
#ifndef powerc
// SetupPPCNativeCode
OSErr SetupPPCNativeCode( CFragConnectionID *connID,
Handle *PEFHandle,
ResType codeFragmentType,
short codeFragmentID )
{
OSErr theErr = noErr;
long templong;
Str255 failedFragName;
// get PEF container resource from our resource fork
*PEFHandle = ::Get1Resource( codeFragmentType, codeFragmentID );
theErr = ::ResError();
if( !(*PEFHandle) )
{
if( theErr )
return( theErr );
else
return( ::MemError() );
}
// Check for Mixed Mode Manager and Code Fragment Manager (CFM)
Boolean hasMixedMode = !Gestalt( gestaltMixedModeAttr,
&templong );
Boolean hasCFM = !Gestalt( gestaltCFMAttr, &templong );
theErr = Gestalt( gestaltSysArchitecture, &templong );
if( ( theErr ) ||
( templong == gestalt68k ) ||
( !hasCFM ) ||
( !hasMixedMode ) )
{
::ReleaseResource( *PEFHandle );
return( -1 );
}
DetachResource( *PEFHandle );
MoveHHi( *PEFHandle );
HLock( *PEFHandle );
// Assume this is PowerPC code, so it must be prepared
theErr = ::GetMemFragment( (Ptr)**PEFHandle, 0, 0,
kNewCFragCopy, connID, 0, failedFragName );
if( theErr )
{
DisposeHandle( *PEFHandle );
return( theErr );
}
CFragSymbolClassmyClass;
if( !theErr )
theErr = ::FindSymbol( *connID,
(ConstStr255Param)"\pCompressDataRD",
(Ptr*)&myCompressDataUPP,
&myClass );
if( !theErr )
theErr = ::FindSymbol( *connID,
(ConstStr255Param)"\pUncompressDataRD",
(Ptr*)&myUncompressDataUPP,
&myClass );
if( theErr )
{
DisposeHandle( *PEFHandle );
return( theErr );
}
return( noErr );
}
The following function takes the connection id and handle returned from SetupPPCNativeCode, frees the connection and disposes of the handle. Add to your code:
// TearDownPPCNativeCode
void TearDownPPCNativeCode( CFragConnectionID connID, Handle PEFHandle
)
{
::CloseConnection( &connID );
if( PEFHandle )
::DisposeHandle( PEFHandle );
}
#endif
Build your 68K project as you normally would and be sure to include the resource file we created in Step 2 and ensure the the shared library we created in Step 1 is either in the Extensions folder of the System Folder or in the same directory as the application.
Here is what you would do the call the PowerPC code from 68K:
long PowerPC_Compress( unsigned char *inBuffer, unsigned char
*outBuffer, unsigned long inBufferSize )
{
#ifndef powerc
// For efficiency you would probably put this call in initialization code.
// sConnID and sPEFHandle are static globals.
OSErr err = SetupPPCNativeCode( &sConnID, &sPEFHandle,
'PEF ', 128 );
if( err )
return( 0 ); // We couldnt compress anything so return 0
#endif
long result = 0;
result = myCompressData( inBuffer, outBuffer, inBufferSize );
#ifndef powerc
// For efficiency you would probably put this call in termination code.
TearDownPPCNativeCode( sConnID, sPEFHandle );
#endif
}
long PowerPC_Uncompress( unsigned char *inBuffer,
unsigned char *outBuffer, unsigned long inBufferSize )
{
#ifndef powerc
// For efficiency you would probably put this call in initialization code.
// sConnID and sPEFHandle are static globals.
OSErr err = SetupPPCNativeCode( &sConnID, &sPEFHandle,
'PEF ', 128 );
if( err )
return( 0 ); // We couldnt compress anything so return 0
#endif
long result = 0;
result = myUncompressData( inBuffer, outBuffer, inBufferSize );
#ifndef powerc
// For efficiency you would probably put this call in termination code.
TearDownPPCNativeCode( sConnID, sPEFHandle );
#endif
}
Thats all there is to it. Enjoy and happy coding.
- Chris Rudolph
[BuildRoutineDescriptor() doesnt work right with CFM68k; use NewRoutineDescriptor() instead. BuildRoutineDescriptor() generates code so you need to call MakeDataExecutable() after it. - sgs]