Developing vintage 68K Macintosh apps with CodeLite IDE, Retro68 and pce-macplus emulator
UPDATE (22 Aug 2022): A reader by the name of Henlin has adapted my build scripts to work with VSCode and shared his development workflow here. VSCode is much better than CodeLite as it’s more well-maintained and has superior support for auto completion (or IntelliSense in Microsoft’s terms). Using Retro68 and my build scripts, he was able to develop his own libraries, CoprocessorJS and Nuklear, which other Retro68 developers can use to develop their own GUI apps. Thanks Henlin for sharing his knowledge with us.
In my previous post I described how Retro68 can be used to compile 68K Macintosh apps from your modern Linux distribution and also explored how Retro68 together with CodeLite and pce/macplus emulator can form a very good 68k development environment. In this post, I will provide the details of how such a development environment can be created based on Ubuntu, a common Linux distribution. For those who do not have the time to set up everything from scratch, towards the end of the post I will also provide links to download a VirtualBox image with Retro68, CodeLite, pce/macplus and other necessary components in a single Ubuntu installation.
Pre-requisites
Before we start, you should already have Retro68, pce/macplus and optionally Basilisk II installed on your preferred Linux distribution, for example by using sudo apt-get install basilisk2 and sudo apt-get install codelite. Although any version of CodeLite will work for our purposes, if you have the time, try to install CodeLite from the latest source code as some older versions of CodeLite might have autocompletion issues which will manifest when used with the Retro68 SDK.
Preparing the build environment
As our objective is to be able to compile and run Retro68 apps within CodeLite, preferably with some debugging support, the first task is to write bash scripts to automate various tasks. For our purposes, we only need the following scripts (refer to the download links at the end of the article for the complete source code):
- build.sh: build the specified project
- build_and_run.sh: build the specified project and runs in in the specified emulator. This script will call build.sh and once done, start the selected Mac OS emulator.
- clean.sh: remove all build output for the selected project.
Building Retro68 apps as well as starting the pce/macplus emulator from command line is trivial. However, there is a challenge of copying the compiled executable to the selected emulator, to be started by the user once the emulator has finished booting. Fortunately, Retro68 produces build output in both BinHex (.BIN) or 800k disk image (.DSK). You can simply rename the .DSK output to .image and mounted it as a 800k floppy drive with pce/macplus. Alternatively, you can use hformat, hmount and hcopy, part of the hfsutils package, to create a HFS disk image with the .BIN file as well as any other input files from your application, and mount it with pce/macplus. The HFS image will appear as a Hard Disk 20 disk drive and can be read on the Macintosh Plus and later with the stock ROM, or on a Macintosh 128K/512K with the Macintosh Plus ROM.
Our first CodeLite 68k project is now ready to be created. We will use CodeLite’s C++ template and add different project configuration for different types of Mac OS emulators:
For each project configuration, we will configure our bash scripts under Workspace > Open Active Project Settings. In General settings, configure the script to build and run the project with our selected emulator:
The parameters for our build_and_run.sh script indicates the project to be built and the emulator to run it on, which need to be specified correctly in program arguments. The working directory also needs to be set correctly. Remember to tick “This program is a GUI application” otherwise the emulator won’t start. Under Customize > Custom Build, specify the command to build and run the project:
Under Code Completion, specify the path to the Macintosh Programmer’s Workshop C headers, in particular the CInludes and RIncludes folders, for autocomplete to work:
These folders will also need to be specified under Global Settings > Additional Include Paths:
Also add these folders to Settings > Code Completion > CTags > Search Paths:
As MPW uses .r files that are written in a language similar to C, for dialog resources, we should also add *.r in the list of C++ file extensions. You can do this in Settings > Colours and Fonts > Customize > C++:
Of course you might also want to tweak autocompletion and other editor settings to suit your needs. Repeat the above steps for each of the project configuration and remember to change the command line parameters to indicate the Mac OS emulator to run the project on. Once done, choose Workspace > Parse Workspace and restart CodeLite. You will notice that method signatures can now be detected and autocomplete can work:
With this setup, common CodeLite tasks such as build, run and clean should now work properly. The following screenshot shows CodeLite running the MenuSample example on System 6.0.8 under pce/macplus:
The MenuSample app can be started from the emulated Test Apps disk:
To get pce/macplus to boot from the existing hard disk with System software and not from the HFS disk we created with just the compiled code, it is important to set a value of around 3-5 seconds for the insert_delay parameter in pce configuration. Otherwise, the emulator will attempt to boot from this HFS disk and fail since our HFS disk is obviously not bootable.
Adding debug support
There is some support for debugging in pce/macplus, but its integrated debugger can only work at assembly language level and will not suit our purposes. And although Retro68 produces GDB debug symbols, making use of these symbols to debug our 68K apps via pce/macplus will not be trivial due to the emulation layer involved. The only simple way I can think of is to write debug messages to the Macintosh serial port, and configure redirection of serial output to a text file on disk. This can be done using the following configuration entry:
serial { port = 1 drive = "stdio:file=serial.out" }
The above configuration will redirect all serial output to a file named serial.out on disk. Writing to the serial port from pce can then be done with the following code:
#include <Serial.h> #include <Devices.h> OSErr writeSerialPort(short refNum, const char* str) { #define MODEM_PORT_OUT "\p.AOut" #define MODEM_PORT_IN "\p.AIn" #define PRINTER_PORT_OUT "\p.BOut" #define PRINTER_PORT_IN "\p.BIn" const char* nameStr = ""; switch (refNum) { case aoutRefNum: nameStr = MODEM_PORT_OUT; break; case boutRefNum: nameStr = PRINTER_PORT_OUT; break; // input device not valid for writing data /* case ainRefNum: nameStr = MODEM_PORT_IN; break; case binRefNum: nameStr = MODEM_PORT_IN; break; */ default: return -1; } short serialPort = 0; OSErr err = MacOpenDriver(nameStr, &serialPort); if (err < 0) return err; CntrlParam cb; cb.ioCRefNum = serialPort; cb.csCode = 8; cb.csParam[0] = stop10 | noParity | data8 | baud9600; err = PBControl ((ParmBlkPtr) & cb, 0); if (err < 0) return err; IOParam pb2; pb2.ioRefNum = serialPort; char str2[255]; sprintf(str2, "%s\n", str); pb2.ioBuffer = (Ptr) str2; pb2.ioReqCount = strlen(str2); err = PBWrite((ParmBlkPtr)& pb2, 0); if (err < 0) return err; err = MacCloseDriver(serialPort); return err; } writeSerialPort(aoutRefNum, "Hello World"); // write to Modem Port writeSerialPort(boutRefNum, "Hello World"); // write to Printer Port
There are usually two serial ports on vintage Macintosh computers, modem port and printer port. On our pce/macplus emulators, preferably you should reserve the modem port for networking and use the printer port to output debug messages. CodeLite can also be set to ‘tail’ the serial output file to monitor debug messages:
CodeLite, Retro68 and pce/macplus as a VirtualBox image
You can download a VirtualBox image with Retro68, CodeLite, pce/macplus and other necessary components in a single Ubuntu installation here. The 10GB ZIP file has been split into 10 parts, which can be joined using a tool such as FFSJ. The username is macdev and password is macdev. Retro68 is installed in the Documents folder, together with the HelloWorld, MenuSample and Dialog examples adapted for use with CodeLite. Basilisk 2 emulator is also installed with System 7.6 disk image. I have also included original MPW 3.1 and MPW 3.5 header files as well a copy of Inside Macintosh, which is a must-read for anyone serious about 68k Macintosh programming.
All pce/macplus instances installed in the Mac OS Emulators folders are ready for use with CodeLite. The System 6.0.8 instance also contains MacTCP and other useful network utilities. By using the included Fetch, you can also transfer files between the System 6.0.8 instance and the Ubuntu host, which has vsftpd installed. To allow Internet access from the TUN interface, I have also enabled IP masquerade on the LAN interface for TUN traffic with the following command:
iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o enp0s3 -j MASQUERADE
The scripts mentioned in the article can be found in the Retro68kApps folder. There is also a create_tun.sh script to create a tunnel that can be used with pce (after updating the interface name and IP address to reflect your network configuration). If TUN doesn’t work for you, you can also use start_ppd.sh and start_tty0tty.sh to emulate a PPPD server instead.
When running on VirtualBox, sometimes you will not be able to open Settings > Displays to change the resolution. If this happens, you can use something like xrandr -s 1366×768 to set the preferred resolution. VirtualBox mouse integration should be disabled under Input > Mouse Integration menu, otherwise the mouse will move erratically inside the Mac OS emulators.
The following YouTube video, captured using VirtualBox, demonstrates how everything we set up can work together to form a custom 68k development environment:
For those who do not want to download the entire VirtualBox image, a ZIP file with just the CodeLite projects, bash scripts and a soft copy of Inside Macintosh, can be downloaded here.
See also
Exploring Retro68 GCC-based SDK for 68K Macintosh computers
PCE/macplus, the ultimate 68K Macintosh emulator
This is pretty amazing stuff. I’m making heavy use of it, while trying to work out how to get an old Macintosh 128k to measure beer keg fullness in our office bar. It’s amazing how many rough edges there are in PCE, and it seems you’ve found – and documented workarounds for – 99% of the ones I’ve run into, so far. Thanks!
Once I work out how to make the mouse go, that is. It’s being initialized (in macplus.c:959), and I see a mouse pointer on the screen when it boots into the System 3.2 HD I have. But it never moves
John
Try to disable VirtualBox mouse and keyboard integration which is known to cause the exact same issue you’re having. If you still have problems, change the mouse emulation type to PS2 – default is USB mouse which PCE doesn’t like.
This was nothing to do with virtualbox; I was running everything on my fedora laptop.
Actually, I managed to find the bugs in PCE that caused the problem, and sent in a patch. But in case anyone else has these problems…
diff -ru pce-20181220-pure/src/arch/macplus/macplus.c pce-20181220-simple/src/arch/macplus/macplus.c
— pce-20181220-pure/src/arch/macplus/macplus.c 2018-12-20 14:37:29.000000000 +0000
+++ pce-20181220-simple/src/arch/macplus/macplus.c 2019-06-15 21:50:39.353681174 +0100
@@ -284,7 +284,7 @@
static
void mac_check_mouse (macplus_t *sim)
{
– if (sim->adb != NULL) {
+ if (sim->adb == NULL) {
return;
}
@@ -349,7 +349,6 @@
if (sim->adb_mouse != NULL) {
adb_mouse_move (sim->adb_mouse, but, dx, dy);
– return;
}
if ((sim->mouse_button ^ but) & ~but & 2) {
Thanks for sharing the patch
Thanks a lot for the write up!
I am having a tough time setting this up on my machine unfortunately. I followed the instructions very closely, but I can not get Codelite to recognize methods. It recognizes types from the Retro68 libs (such as Rect and EventRecord) and can be followed to the declaration, but a method like InitGraf() is not suggested when typed and cannot be followed to the declaration.
Do you have any tips on what I might try next? I desperately want to get this working
Hi,
Which version of CodeLite are you using? Some old versions have auto-completion issues so try to use the latest version. Also which MPW headers are you using? The above setup was tested with MPW 3.5 headers.
Locate the header file which contains the method that you are interested in, e.g. InitGraf(). Are all methods not suggested by CodeLite, or only some of them? Are most types suggested by autocomplete? If you can identify which methods are suggested and which ones are not, try to see what is so special about these methods. In my experience, CodeLite is confused by things like ONEWORDINLINE and will ignore many declarations following it. See if things improve after you add these keywords to your CodeLite autocompletion ignore list.
Try to start with a brand new project, slowly add things in and see if autocomplete works at the beginning and if so, at which point it fails. Of note, the resource files (.r) might confuse autocomplete, so you might not want to add it to the project for a start. The setup still works without adding the .r files to the project, just that you will have to edit the file outside of CodeLite.
If all fails, you can download the virtual machine I provided and refer to the sample setup.
Let me know how it goes.