Click here to Skip to main content
15,884,298 members
Articles / Programming Languages / C

Exploring Retro68 GCC-based SDK for 68K Macintosh Computers

Rate me:
Please Sign up or sign in to vote.
4.00/5 (1 vote)
7 May 2023CPOL13 min read 2.1K  
Retro68 GCC-based SDK for 68K Mac
In this post, we explore Retro68 GCC-based SDK for the 68K Macintosh computer.

UPDATE: The 68k development environment with Retro68 together with CodeLite and pce/macplus emulator running on Ubuntu is now ready for download as a VirtualBox image. See my latest post for details.


Being a fan of vintage Apple computers, I have always been looking for a modern way to build apps for 68k Mac directly from laptop without having to resort to ancient tools like Macintosh Programmer’s Workshop or Think C. Luckily, my dream is realized when I came across Retro68, a GCC-based 68k cross-compiler that seems to be feature-rich and easy to use. I spent a few weeks exploring Retro68 various features and this article will share my findings.

What is Retro68 All About?

Developed by Wolfgang Thaller, Retro68 allows you to compile your existing 68k C code, with only minor modifications using GCC from your modern OS and run the generated binary files on your 68k Mac. After compiling the code, Retro68 will create a MacBinary image (.bin), a disk image (.dsk) as well as a Mac application package with resource fork (.APPL) containing the compiled executable which can then be copied over to your favorite 68k machine for testing.

Retro68 is written in C and can be compiled for various platforms such as Linux, Mac OS and even Windows (with the help of cygwin), although Mac-compatible APPL application packages will only be generated if Retro68 is running on a Mac from a HFS/HFS+ compatible volume. The following sections will describe the steps needed to compile Retro68 on Ubuntu 16, my favourite Linux distro.

Compiling Retro68 from Source

First, perform a git clone of the Retro68 github repository. Next, copy the CInludes and RIncludes folders from your Macintosh Programmer’s Workshop (MPW) installation to the main Retro68 folder. If you do not have a copy of MPW, you can download the header files for MPW v3.5 here. After that, install the packages required by Retro68 using the following:

Bash
sudo apt-get install cmake libgmp-dev libmpfr-dev 
     libmpc-dev libboost-all-dev bison autoconf automake texinfo

Verify that all packages have been installed successfully. In particular, the autoconf package is critical. If it is missing, compiling Retro68 will fail towards the end of the build process (which may take between 30-60 mins even on a fast computer). The output may also display some cryptic error messages that could confuse you for a while.

Next, create a folder named Retro68-build in the parent directory where the Retro68 SDK is downloaded. Also inside the parent directory, create a start_build.sh script with the following content:

Bash
rm -rf Retro68-build
cd Retro68-build
sh ../Retro68/build-toolchain.sh

Take note that this bash script will remove any previous build output before starting the build process. Now execute the script and be prepared to wait between 30 to 60 minutes for the build to complete. If the process is successful, there should be no error messages at the end and you will find a few sample applications under Retro68-build/build-target/Samples:

  1. Dialog: a simple application showing a Dialog with various buttons
  2. HelloWorld: a console-based Hello World application
  3. Raytracer: demonstrate the use of various graphics funtions

In my experience, the Raytracer application may fail to build due to some missing header files. As this problem does not affect the functionality of Retro68, you can work around the issue by replacing the content of the Raytracer folder with the HelloWorld application and editing CMakeLists.txt to keep the same target name, e.g. ‘Raytracer':

add_application(Raytracer
	hello.c
	CONSOLE
   )

UPDATE: This issue has been fixed in the latest release of Retro68 (dated 12 April 2017) and the RayTracer sample application should build properly.

With this modification, Retro68 should finish building with no issues. You can now try the sample apps by copying the MacBinary files (.bin) file to your favourite machine, and extract them to show the original executable files. The following is the screenshot of the sample Dialog application running on pce/macplus, a 68K Macintosh emulator:

Screenshot from 2017-04-11 15-35-37

The following is the HelloWorld sample console application, also running on pce/macplus. The app was built using libRetroConsole, a library by Retro68 that supports basic console functionality. There was no support for developing console application in the original Macintosh design as apps were supposed to be GUI-based.

Screenshot from 2017-04-11 15-38-30

Retro68 also generates a 1.44MB HFS disk image (.DSK) that can be written to a 1.44MB floppy disk (for example by using dd) to facilitate copying of the compiled application. On older Macs that do not support 1.44MB floppies, you can use the MacBinary output (.bin) for file transfer instead.

Code Structure

Despite being based on GCC, a modern 32-bit compiler, C codes meant for vintage 68k development tools such as MPW or Think C should compile just fine under Retro68, with some modifications. Take a look at the Dialog sample application, which contains just 3 files:

  • dialog.r: the text resource file
  • dialog.c: the main application file
  • CMakeLists.txt: build rules to compile the application

The CMakeLists.txt file contains just the following:

cmake_minimum_required(VERSION 2.8)

add_application(Dialog
	dialog.c
	dialog.r
	)

Adding more .c files to the application requires modifying the make file to include these new files. Obviously, it is not necessary to include the header files (.h) in the make file, however.

To build the application using the CMakeLists.txt file, run the following bash script:

Bash
rm -rf build
mkdir build
cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=
      ./Retro68/Retro68-build/toolchain/m68k-apple-macos/cmake/retro68.toolchain.cmake
make

The output will then be created in the build folder.

For those who are new to 68k programming, the Dialog.r file is a text resource file which defines various GUI elements using a C-like syntax:

resource 'DLOG' (128) {
	{ 50, 100, 240, 420 },
	dBoxProc,
	visible,
	noGoAway,
	0,
	128,
	"",
	centerMainScreen
};

Retro68 uses Rez, an implementation of Apple’s Rez resource compiler which reads the .r files and compiles them to binary resource files whose names look like ‘dialog.r.rsrc’. These resource files will then be bundled together with the binary output from GCC to create the final Mac application.

With this design, most of the sample codes from MPW or even Think C should compile fine under Retro68 after changing CMakeLists.txt to include all required source files. Take note of the changes in various method names between MPW 3.1 and MPW 3.5, which may result in the need to modify parts of the code. For example, DisposHandle is renamed DisposeHandle and AddResMenu is renamed AppendResMenu. Use the following code snippet to assist you with the changes:

C
#define ResetAlrtStage ResetAlertStage
#define SetDItem SetDialogItem
#define GetDItem GetDialogItem
#define GetItem GetMenuItemText
#define DisposHandle DisposeHandle
#define AddResMenu AppendResMenu
#define DisposPtr DisposePtr
#define ResrvMem ReserveMem

The text resource format has also been changed, resulting in cryptic error messages (for example, incorrect number of parameters) when using an older resource file. To fix this error, check the various resource header files in the RIncludes folder as well the sample applications from MPW 3.5 and find out the expected format.

Code Segmentation

The original MPW compiler used 16-bit offsets and made uses of #pragma segment to organize the codes into different segments, the names of which need to be configured into the linker phase, to be loaded/unloaded dynamically at run-time as and when the respective code is needed. This results in complicated code and causes the generated application to contain multiple segments under its CODE resources, each of which smaller than 32KB. The following is the CODE resource of Eudora 1.3.1 when displayed under ResEdit:

Screenshot from 2017-04-11 16-07-32

Retro68, on the other hand, uses GCC which has 32-bit absolute addresses and does not need to stick faithfully to that part of the 68K Mac architecture. It simply places most of the code into a single segment and uses some custom code to make all those addresses point to the right places once the code is loaded. The Retro68 code responsible for this can be found in libretro/relocate.c.

The following is the CODE resource of the Dialog application compiled using Retro68:

Screenshot from 2017-04-12 17-01-55

There are only two segments, and most of the code can simply be put in CODE 1. The MPW architecture of segmenting via #pragma segment is no longer needed, and will simply be ignored by GCC.

On a side note, if your CODE resource has an extra segment with ID = 256, like in the following screenshot, your 68k Mac OS installation is most likely infected with the nVIR virus and you should use a virus scanner software such as Mcafee Virus Scan 101 to clean up the infection. In my case, the virus could have come from one of my downloads on MacGUI. Applications created using Retro68 should only have two code segments having 0 and 1 as their IDs.

Screenshot from 2017-04-11 16-12-48

Here comes a trap for new 68K programmers. I was happily using Retro68 to build my application, adding more and more code, when suddenly, the following error message appeared immediately after launching the application:

Screenshot from 2017-04-11 16-18-19

The error message read, ‘The application has unexpectedly quit (out of application memory)”, under System 6.0.8 and System 7.5.5. It changed to, “The application has unexpectedly quit, because an error of type 15 occurred”, under System 7.6.1:

Screenshot from 2017-04-11 16-19-45

Error of type 15 is “Segment Loader Error” according to this, which perhaps indicated the same thing, out of memory while loading the application. Clearly, memory was not an issue, so what had gone wrong?

The root cause was simply some hard-coded resource values which declared the memory requirements of the application. These values are used by Finder to allocate the amount of memory required by the application. In my case, my Sample.r text resource file had a section with the following code:

C
resource 'SIZE' (-1) {
...
kPrefSize * 1024,
kMinSize * 1024
}

where kPrefSize and kMinSize were declared in the header file as follows:

C
#define kMinSize 23
#define kPrefSize 35

These values mean that the application needs a minimum of 23KB, and prefers at least 35KB of memory to work properly. When more codes are added without these values being changed, the app will no longer work properly because Finder won’t allocate enough memory for it. Setting the above values to at least 100KB each fixed the issues for me.

Macintosh 512K Support

After testing the generated binaries on my Macintosh SE running System 6.0.8, I tried to run the compiled app on my Macintosh 512K with Mac OS 3.2 (Finder 5.3) and received the message “A system error has occurred” with ID = 10 indicating that the app attempted to access a non-existent ROM routine or something like that. After some debugging, it seemed that the error happened even if the app only contained a single return; in the main() method, so the problem must have been somewhere inside the bundled codes that were called even before execution reached the main() method. But where exactly?

After some troubleshooting with the author, who was very helpful and provided me with a lot of good information, the issue seemed to be with method Retro68Relocate() in relocate.c. In particular, there are calls made to StripAddress and SysEnvirons, which are not supported on older ROMs:

C
#define RETRO68_GET_DISPLACEMENT_STRIP(DISPLACEMENT) \
	_RETRO68_GET_DISPLACEMENT(DISPLACEMENT, StripAddress)
...
RETRO68_GET_DISPLACEMENT_STRIP(displacement);
...
SysEnvRec env;
env.processor = 0;
SysEnvirons(0, &env);
if(env.processor >= env68040)
{
    FlushCodeCache();
}

Method StripAddress is used to convert GCC 32-bit offsets to their 24-bit equivalents whereas method SysEnvirons is used to check if the code cache needs to be flushed on a 68040. None of these two methods are supported on the 64K ROM used by the Macintosh 512K. Being new to 68K programming, it took me almost a week to find a solution, that is to read the word value at the ROM85 memory position (0x028E) to see whether the ROM is 64K or 128K, and only call these two methods if the ROM is 128K. On 64K ROM, the following can be used as an alternative to StripAddress, by taking the last 24 bit of the 32-bit address:

C
#define StripAddress(x) ((Ptr) ((unsigned long)(x) & 0x00FFFFFF))

During the debugging process, in order to recompile only libretro with the modified files without having to compile the entire Retro68 SDK, I used the following script:

Bash
make -C ./Retro68/Retro68-build/build-target
rm ./Retro68/Retro68-build/toolchain/m68k-apple-macos/lib/libretrocrt.a
cp ./Retro68/Retro68-build/build-target/libretro/libretrocrt.a 
    /Retro68/Retro68-build/toolchain/m68k-apple-macos/lib

The script forces a rebuild of the sample applications, which will then rebuild libretro and other related libraries. After that, the output file libretrocrt.a is copied to the correct folder expected by the m68k-apple-macos toolchain. This is needed otherwise the build will continue to use the old libretrocrt.a library file for building.

I submitted the above code change to the author, who integrated it to the latest release of Retro68 (dated 12 April 2017). With this, apps compiled using Retro68 should work fine on the Macintosh 512K (or even the Macintosh 128K), provided that they do not make any other function calls not compatible with old ROMs.

An issue which remains is that console apps built using Retro68 will not work on the 512K. The compiled app size is bigger than the 800K floppy disk (almost 912K due to the added library size), and even with the HD20, it will fail to run (not enough memory). According to the author, the culprit is libstdc++ huge implementation of locales, which get pulled in all the time in new versions of GCC, even though those codes are never really used. Unfortunately, fixing this problem seems to be non-trivial for now.

MPW Object File Compatibilities

In one of my experiments, I received a linker error the moment I started to use the SerReset function (found in Serial.h) to initialize the serial port. A linker error also happens with OpenDeskAcc used in one of the MPW sample applications. This means that these functions are declared in the header files but not in the object files used during the link process, resulting in the linker errors. This is when I checked the original MPW disks and saw a lot of object files in the libraries folder, among which the Interface.o file contained the SerReset function which I was using. These libraries also contained a lot of other methods commonly used by other applications. Clearly, Retro68 did not use any of these libraries. So how could most of the sample applications compile just fine?

I contacted the author and the answer provided was that he cannot reuse any of the MPW .o files because the format is different from GCC standards. Instead, he simply rewrites some of these methods in libretro/glue.c. For example, the OpenDriver method is rewritten using PBOpenSync with the following code:

C
pascal OSErr OpenDriver(ConstStr255Param name, short *drvrRefNum)
{
	ParamBlockRec pb;
	OSErr err;
	memset(&pb, 0, sizeof(pb));

	pb.ioParam.ioNamePtr = (StringPtr)name;

	err = PBOpenSync(&pb);
	*drvrRefNum = pb.ioParam.ioRefNum;
	return err;
}

Some other functions such as StripAddress do not need to be rewritten in glue.c, since they invoke the ROM functions directly:

C
EXTERN_API(Ptr) StripAddress(void * theAddress)
                             ONEWORDINLINE(0xA055);

Unfortunately, with this implementation, many important API methods (e.g., File Manager, Device Manager or Quickdraw calls that are not declared as ONEWORDINLINE or TWOWORDINLINE) will not work unless glue.c is modified to include those methods, or the MPW .o library files are converted to a format accepted by GCC and included in the linker phase. For now, attempting to use any of the unsupported methods will result in a linker error. This is a known issue of Retro68, with no solutions yet.

Conclusion

Despite the limitations, I still like Retro68 as I can use it on my laptop running Ubuntu 16 to compile 68k Mac apps. Also, Retro68 together with CodeLite and pce/macplus, after some custom configurations, make a very good 68k development environment:

Screenshot from 2017-04-11 17-17-43

With just a few basic bash scripts to compile the project using Retro68 and run it in pce/macplus, I can comfortably edit my 68k codes using CodeLite. Even auto-completion is supported as well! For those who are interested, I will be preparing a VirtualBox Ubuntu image with Retro68, CodeLite, PCE/macplus and other necessary components allowing you to develop 68k apps the modern way. Stay tuned!

See Also

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Writer
Singapore Singapore
Since 2008, ToughDev has been publishing technical sharing articles on a wide range of topics from software development to electronics design. Our interests include, but are not limited to, Android/iOS programming, VoIP products, embedded design using Arduino/PIC microcontrollers, reverse-engineering, retro-computing, and many others. We also perform product reviews, both on new and vintage products, and share our findings with the community. In addition, our team also develops customized software/hardware solutions highly adapted to suit your needs. Contact us for more information.

Comments and Discussions

 
-- There are no messages in this forum --