Mar 97 Tips
Volume Number: 13 (1997)
Issue Number: 3
Column Tag: Tips & Tidbits
Tips & Tidbits
By Steve Sisak
Whenever you directly or indirectly modify code, you need to make sure the processor caches are synchronized in order to avoid confusing the processor and/or emulator. This includes not only self-modifying code, but also creating routine descriptors and loading code resources by hand.
If you move 68k code with BlockMove on any system (68k or PowerPC) the caches are flushed automatically (assuming blocks greater than 12 bytes). Use BlockMoveData for blocks containing no code, since they do not require the cache flushing overhead.
Note that on a PowerPC, BlockMove does not flush PPC caches at all. The only reason BlockMove flushes 68k code is for backward compatibility with software written before the '040s. It is unfortunate that a frequently-used routine like BlockMove should have to know anything about instruction caches. On the bright side, most developers do not need to copy PPC code around, since fragment preparation occurs within CFM.
On a PPC machine, calls to the 68k cache flushing routines (including FlushCodeCacheRange and indirectly via BlockMove) do everything necessary to maintain 68k instruction cache coherency. On the original 68k emulator, this meant nothing needed to be done since there was no cached code. On the DR Emulator and Speed Emulator, these calls force the emulator to "throw out" any recompiled code associated with the specified code range.
I know the different cache flushing routines are confusing and not very well documented. Here are some simple rules:
If you are dealing with 68k instructions, use BlockMove or FlushCodeCacheRange. They both specify a range of memory and therefore do not require the newer recompiling 68k emulators to perform a complete cache flush (a very time-intensive operation that can degrade performance significantly.)
If you are dealing with PPC code, you probably don't need to worry about the issue because your code was probably directly or indirectly prepared by CFM. However, if you do generate PPC code on the fly, you should call MakeDataExecutable on the range. Note that this does more than just flushing the cache. It does everything (including flushing the processor's prefetch queue) necessary to deal with newly-generated code.
Hope this helps clear things up. If you want more details, check out the tech note I wrote while I was still at Apple on the DR Emulator.
Eric Traut
Connectix Corp.
(Almost) Painless Mixing of C and Pascal Strings
It's fair to say that most of us have found ourselves in this situation: "The API wants C-style strings and all I have are these Pascsal strings!". The typical solution is to use utilities like PtoCStr and CtoPStr to shuffle the bytes around. Consider the following example where sprintf is being used to create a menu title.
void DescribeFile (Str255 title, Str255 fileName, Str255 volumeName,
unsigned long size)
{
// Temporarily convert fileName and volumeName to C strings
PtoCStr (fileName);
PtoCStr (volumeName);
// Use sprintf to describe the file eg. "File foo on bar (120.5K)"
sprintf ((char*) title, "File %s on %s (%.2fK)",
(char*) fileName,
(char*) volumeName,
(double) size / 1024.0);
// Convert title, fileName and volumeName to Pascal strings
// back to Pascal strings
CtoPStr ((char*) title);
CtoPStr ((char*) fileName);
CtoPStr ((char*) volumeName);
}
As you know there's at least one unused byte at the end of the Pascal string, and the string is not a literal, you can take a shortcut by making the Pascal strings "hybrid strings". A "hybrid string" is a null-terminated pascal string. Observe:
void DescribeFile (Str255 title, Str255 fileName, Str255 volumeName,unsigned
long size)
{
// Convert fileName and volumeName to hybrid strings
fileName [fileName [0] + 1] = \0';
volumeName [volumeName [0] + 1] = \0';
// Use sprintf to describe the file eg. "File foo on bar (120.5K)"
// Note that we skip the first byte of the hybrid strings
sprintf ((char*) &title [1], "File %s on %s (%.2fK)",
(char*) &fileName [1],
(char*) &volumeName [1],
(double) size / 1024.0);
// title' is not yet a hybrid string, it has an undefined length byte
title [0] = strlen ((char*) &title [1]);
}
Remember, you can't always get away with this. In the above we know that title, fileName and volumeName are non-const strings with room for 255 characters. Since fileName' and volumeName' will never be more than 63 and 27 bytes respectively, we're sure there is room to add the length byte to the end. Similarly, title' won't break 130 bytes.
Damon Cokenias
<http://www.netgate.net/~cokenias>