For fun, I decided to see if I could write something graphical for the older Mac OS. Looking through my options, it seemed like writing direct Macintosh Toolbox code would take too long to get up to speed, so I searched a bit more and found that there is an older PPC Mac OS port of the fantastic SDL library which I’ve got a lot of experience in.

The trick with SDL, especially on obscure platforms, is getting the toolchain to work, often with sparse documentation. Here’s how I got something (very simple) building on my Power Macintosh 8600/200 running Mac OS 9.1 (an OS that’s only 16 years old!)

Install toolchain

I installed CodeWarrior Pro 4. You can do this with MPW as well, but I decided not to, since I also wanted to learn how CodeWarrior worked.

I selected the following options. I’d have done a full install but the machine only has a 2GB hard drive and most of it was filled with other stuff:

  • C compilers for 68k, Fat, PPC
  • C++ compilers for same
  • “Find Library”

At this point, you should download and extract the SDL release. I used 1.2.13, available from the SDL releases directory. For reasons you can probably guess, there hasn’t been a recent release of SDL 1.x or any release for SDL 2.x for the classic Mac OS. The file I got was SDL-1.2.13-PPC.sea.bin. There is no library source code in this package.

Set up the project

In CodeWarrior, I created a new project file and chose a C/C++ MacOS PPC target (as far as I know, this release of SDL does not support 68k, so a 68k or Fat target is unnecessary).

Similar to how Visual Studio handles it, a project file in CodeWarrior has a ton of extra compiler options that are defined per-project. The next step is to navigate to the project preferences.

I did the following things:

  1. Point CodeWarrior’s paths to the include directory of the SDL install that you just unpacked
  2. Set the preferred and minimum heap sizes of the application to 4096 kB. I didn’t do this initially, and ended up with a problem where SDL_SetVideoMode silently failed, probably because the default ~300kB heap size wasn’t enough room to have a buffer. On the classic Mac OS, applications pre-request their heaps through this mechanism and (usually) do not have a way to dynamically request more memory at runtime. You can read more about it on the Wikipedia page, but if you’re doing this, chances are you originally owned an older Mac and have already experienced the pain of manually tweaking application memory sizes to get programs to behave.
  3. Close the project preferences, saving them.
  4. Add the SDL and SDLmain.PPC.LIB files to the Mac Libraries section of the CodeWarrior project. You can find them in the SDL stuff you just unpacked.
  5. In Finder, copy the SDL library file to the folder that contains the project file. Otherwise, the application you are building can’t find it on startup and won’t start. You can also place this file somewhere global, but for the sake of keeping my machine ‘pure’ I didn’t do so.
  6. Add the SDL.rsrc resource file to the Resources section of the CodeWarrior project window. This provides resources such as dialogue boxes, icons, menus, etc that the SDL application you are building will need.

Write a simple SDL C program

If you have experience with SDL, you should be able to write this pretty easily.

Something like this should get you started:

int SDL_main(int argc, char** argv[]) { // note SDL_main, not main!
  SDL_Surface* screen;
  Uint32 orange;
  Uint32* keys;
  SDL_Event event;

  if(SDL_Init(SDL_INIT_EVERYTHING) < 0) {
    ExitToShell(); return 1;
  }

  screen = SDL_SetVideoMode(512, 342, 0, SDL_ANYFORMAT | SDL_DOUBLEBUF);
  if(!screen) {
    // didn't initialize - maybe out of memory? (see above)
    ExitToShell(); return 2;
  }
  orange = SDL_MapRGB(screen->format, 255, 125, 0);
  SDL_WM_SetCaption("Hello SDL!");

  while(true) {
    SDL_FillRect(screen, NULL, orange);

    while(SDL_PollEvent(&event)) {
      // This event doesn't seem to work (see below)
      // but you need to poll events, otherwise the app will lock up
      // the machine.
      if(event.type == SDL_QUIT) { break; }
    }

    keycodes = SDL_GetKeyState(NULL);
    if(keycodes[SDLK_ESCAPE]) {
      break;
    }

    SDL_Flip(screen);
    SDL_Delay(0);
  }

  return 0;
}

You should be able to run the program now, with or without the debugger. I noticed some gotchas that I don’t yet know how to solve (see below).

Gotchas

Crashes on “unknown instruction” error (Type 3) on program exit

I’m not sure what’s causing this. Looking at it in the CodeWarrior debugger, it seems like it is trying to execute invalid/unknown opcodes in some post-exit “file close” code. I’ll look at this further, since I’ve had this lock up my machine while experimenting before.

SDLK_UNKNOWN is the only virtual key code returned

When handling keydown events you usually write something like this:

while(SDL_PollEvent(&event)) {
  if(event.type == SDL_KEYDOWN) {
    if(event.key.keysym.sym == SDLK_ESCAPE) {
      // bail out!
      ShouldQuit = true;
    }
    ... // other keys
  }
  ... // other event types
}

For whatever reason, on this distribution of SDL, or perhaps just on my machine, keysym.sym always is set to SDLK_UNKNOWN, no matter what key is hit. As a workaround, you can see that I’ve used SDL_GetKeyState, which does seem to have the correct mapping from virtual keys to real keys set up for the keys that are hit.

keysym.scancode seems to be set properly, but you may not want to be using direct physical scancodes instead of virtual keys.

I will look into this further, since it’s pretty annoying.

Still to come

Missing lots of toolbox knowledge

I don’t know how to create an alert dialogue box (for instance, to tell the user that SDL could not be initialized, or the screen surface could not be created before quitting). Based on other code I’ve seen on the Internet and in Inside Macintosh, something like this should work:

ParamText("\pOh no, something is wrong!", "\p", "\p", "\p");
StopAlert(1001, NULL); // 1001 is the ID of the DLOG that SDL.rsrc contains

Nothing appears to happen at all, and the application proceeds as if the call was never made. Such a passive-aggressive action is usually how the Macintosh Toolbox expresses displeasure, so I am guessing that I am calling it incorrectly.

It would also be nice to have a custom menu and have the ‘close’ button on the window send the quit event to the app, to make it work more like an actual Mac application. I might come back to this and write some more on “Mac-ifying” an SDL game in the future.