One of the KansasFest 2022 presentations that particularly stood out to me was Lucas Scharenbroich’s Generic Tile Engine for the Apple IIgs (GTE). In his presentation, Lucas demonstrated a number of awesome capabilities in GTE and how it handles the details of working with the quirks of Apple IIgs graphics so you can focus on writing game logic code. I’m excited to try using this to write a game; but, I’m significantly more comfortable programming in C than in assembly. This is a capture of my first successful attempt in doing that in case it helps others get started.
Dev Environment
- Xcode 12.4 on macOS Catalina 10.15.7
- Apple2GSBuildPipeline v3.0 build system by Jeremy Rand
- Golden Gate r2.0.6 compatibility layer by Kelvin Sherlock
- ORCA/C compiler and libraries by The Byte Works
- ORCA/C 2.2.0 B6 update by Stephen Heumann
Runtime Environment
- System 6.0.1 as included in Apple2GSBuildPipeline
- GTE / Tool 160 Beta 1 (KansasFest Edition) by Lucas Scharenbroich
Nuance – I did modify the 2mg
image for System 6.0.1 in my project so it has Tool 160 already installed in the tools folder.
The Code
The core of the code is in a main.c
file which starts up the tools, waits in a loop for the option key to be pressed, and then shuts down the tools and exits. The details of the functions and the mask of values for GTEReadControl
were taken from the GTE Toolbox documentation.
Nuance – it did take me a little bit to realize I needed A0
for the XX
in the toolbox calls. Finding the macro file was the thing that helped me realize 160
decimal -> A0
hexadecimal.
[Updated 2022-Jul-28: The below code originally incorrectly passed address 0
to GTEStartUp
for the dPageAddr
parameter. Thank you to Lucas Scharenbroich for noticing and for the updated code which correctly allocates 2 consecutive pages of bank 0 memory and passes that instead.]
#include <locator.h>
#include <memory.h>
#include <misctool.h>
#include <types.h>
#include "main.h"
#define TOOLFAIL(string) \
if (toolerror()) SysFailMgr(toolerror(), "\p" string "\n\r Error Code -> $");
extern pascal void GTEStartUp(Word dPageAddr, Word capFlags, Word userID) inline(0x02A0, dispatcher);
extern pascal void GTEShutDown(void) inline(0x03A0, dispatcher);
extern pascal Word GTEReadControl(void) inline(0x09A0, dispatcher);
int main (void) {
Ref toolStartupRef;
Word controlMask;
unsigned int userId;
Handle dpHndl;
Word dpWord;
userId = MMStartUp();
TOOLFAIL("Unable to start memory manager");
TLStartUp();
TOOLFAIL("Unable to start tool locator");
toolStartupRef = StartUpTools(userId, refIsResource, rez_tools);
TOOLFAIL("Unable to start tools");
dpHndl = NewHandle(0x0200, userId, 0x4015, 0);
if (dpHndl == NULL) {
TOOLFAIL("Unable to allocate bank 0 memory");
}
dpWord = (Word)(*dpHndl);
if ((dpWord & 0x00FF) != 0x0000) {
TOOLFAIL("Allocated bank 0 memory is not aligned");
}
GTEStartUp(dpWord, 0x0000, userId);
TOOLFAIL("Unable to start GTE");
do {
controlMask = GTEReadControl();
} while ((controlMask & 0x0100) == 0x00);
GTEShutDown();
TOOLFAIL("Unable to shutdown GTE");
ShutDownTools(refIsHandle, toolStartupRef);
TOOLFAIL("Unable to shutdown tools");
TLShutDown();
TOOLFAIL("Unable to shutdown tool locator");
MMShutDown(userId);
TOOLFAIL("Unable to shutdown memory manager");
}
The main.h
file really just sets up the constants for the tool list resource:
#ifndef _GUARD_PROJECTGTETest1_FILEmain_
#define _GUARD_PROJECTGTETest1_FILEmain_
#define rez_tools 1
#endif /* defined(_GUARD_PROJECTGTETest1_FILEmain_) */
The main.rez
file is just a tool list resource:
#include "types.rez"
#include "main.h"
resource rToolStartup (rez_tools) {
mode320,
{
3, $0302, /* Misc Tool */
160, $0100 /* GTE */
}
};
While this is a relatively small step, it did prove out to me that I can call into the tool from C. I’m looking forward to trying more of the API in the near future.