Calling GTE Tool 160 from C

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

Runtime Environment

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.

Standard

Running lcov on a GitHub Linux Runner

For Wordle GS, I’ve been trying to maintain decent unit test coverage of the core game logic. My approach has been to leverage Cpputest, gcov, & lcov both locally as well as on the Ubuntu GitHub Runner based CI builds. To simplify checking coverage on PRs, I added lcov-reporter-action to the mix. There was just one problem – the summary numbers in the comments left were clearly bogus. With overall coverage showing as NaN% (with a +/- change of NaN% to boot) and the totals for all the other metrics showing as 100% (despite the clear presence of uncovered lines), this wasn’t as usable as I’d hoped.

Continue reading
Standard

Wumpus for PicoSystem v0.0.2 Released

In celebration of Halloween (given the original Wumpus’s place in survival horror game history), the v0.0.2 release of Wumpus for PicoSystem is now available. The GitHub release contains both the source code and the compiled binary UF2 file. This version adds randomized placement of the player and hazards as well as counters to keep track of how many times you’ve gotten the Wumpus… and how many times the Wumpus has gotten you!

The key pieces of infrastructure implemented:

  • Random number generation using the ROSC
  • Bitmap image used in splash screen
  • Moved from structs and function pointers to classes for states

Standard

Compiling for the PicoSystem

I recently purchased a PicoSystem by Pimoroni with the intention of immediately diving in and programming a game; but, I have to admit I got distracted by how much fun Super Square Bros. is to play right out of the box. The overall system feels great as a small portable in the style of the 80s portable LCD based game systems. I’m noticing that the form factor of the system is a bit small for me from a purely functional perspective; but, again, I really like the overall aesthetic and the case is absolutely stellar.

Snake example running on the PicoSystem

As much fun as I’m having playing the shipped game, I decided the weekend was definitely the time to get a small hello world up and running. I’m using Vagrant in order to keep my dev environment consistent and source controlled. Starting from the guide on Pimoroni, I ran into this issue:

CMake Error at examples/snake/CMakeLists.txt:10 (find_package):
  By not providing "FindPICOSYSTEM.cmake" in CMAKE_MODULE_PATH this project
  has asked CMake to find a package configuration file provided by
  "PICOSYSTEM", but CMake did not find one.

  Could not find a package configuration file provided by "PICOSYSTEM" with
  any of the following names:

    PICOSYSTEMConfig.cmake
    picosystem-config.cmake

  Add the installation prefix of "PICOSYSTEM" to CMAKE_PREFIX_PATH or set
  "PICOSYSTEM_DIR" to a directory containing one of the above files.  If
  "PICOSYSTEM" provides a separate development package or SDK, be sure it has
  been installed.


-- Configuring incomplete, errors occurred!

Which has a workaround listed in issue 4 on the PicoSystem SDK project of providing the path to the PicoSystem SDK via PICOSYSTEM_DIR like so:

git clone https://github.com/pimoroni/picosystem.git ~/picosystem
mkdir ~/picosystem/build
cd picosystem/build
cmake -DPICOSYSTEM_DIR:PATH=~/picosystem ..
make

The next error I hit was:

    default: CMake Error at CMakeLists.txt:2 (project):
    default:   No CMAKE_CXX_COMPILER could be found.
    default: 
    default:   Tell CMake where to find the compiler by setting either the environment
    default:   variable "CXX" or the CMake cache entry CMAKE_CXX_COMPILER to the full path
    default:   to the compiler, or to the compiler name if it is in the PATH.
    default: 
    default: -- Configuring incomplete, errors occurred!

This was addressed by revisiting the initial package installs and adding build-essential like so:

    apt-get -y install build-essential \
                       cmake \
                       gcc-arm-none-eabi \
                       libnewlib-arm-none-eabi \
                       libstdc++-arm-none-eabi-newlib

At this point, the snake example built and I was able to transfer it to my PicoSystem in DFU mode. I’m including the current working version of my Vagrantfile below in case it is useful to others:

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/focal64"

  config.vm.provision "shell", inline: <<-SHELL
    apt-get update
    apt-get upgrade -y

    # pico requirements
    apt-get -y install build-essential \
                       cmake \
                       gcc-arm-none-eabi \
                       libnewlib-arm-none-eabi \
                       libstdc++-arm-none-eabi-newlib
  SHELL

  config.vm.provision "shell", privileged: false, env: {"PICO_SDK_PATH" => "~vagrant/pico-sdk"}, inline: <<-SHELL
    # pico sdk
    git clone https://github.com/raspberrypi/pico-sdk.git ~/pico-sdk
    echo 'export PICO_SDK_PATH="~vagrant/pico-sdk"' >> ~/.bashrc
    cd ~/pico-sdk && \
      git submodule update --init

    # pico system
    git clone https://github.com/pimoroni/picosystem.git ~/picosystem
    mkdir ~/picosystem/build

    cd ~/picosystem/build && \
      cmake -DPICOSYSTEM_DIR:PATH=~/picosystem .. && \
      make

  SHELL
end
Standard

Generating Towns for Townscaper in Processing

I’ve been playing Oskar StÃ¥lberg’s Townscaper recently and enjoying seeing how the algorithms react to changes. While looking for information on how garden paths are calculated, I stumbled across Chris Love’s article on the Townscaper file format. Using that as a starting point, I decided to try building up town maps in processing using Perlin noise to determine height, color, and distance above waterline.

My first attempt iterated the map while incrementing a 1D vector in Perlin noise space to determine these attributes. This gave a nice wavy pattern; but, wasn’t quite what I was trying for.

Continue reading
Standard

Classic Computer, Modern IDE

Years ago, I spent a week in the wilderness of Utah.  The night sky was breathtaking; the days were spent in canyons mostly untouched by humans.  It was an amazing experience that I will always be grateful for.  However, by the end of the week, I was happy to return to modern conveniences.

Programming the IIGS on the IIGS feels like experiencing those canyons in Utah and, overall, very authentic. I’ve relived my early attempts in all their glory and frustration. Knowing the convenience of modern development environments, the second part has been bothering me more this time. I am accustomed to syntax highlighting, large screens, fast compile cycles, and all the other conveniences. Thankfully, there’s a solution for this:

With those three packages installed on my Mac, I can develop for the IIGS in Xcode and test in emulation.  This was a very straightforward installation process with one exception.  I did struggle a bit on Step 5 – Install ProFUSE. After some head scratching and overthinking the problem, I realized that from GitLab, I could simply select Repository -> Tags and then filter using the term profuse and a pkg file was provided.

With everything up and running, the function I wrote for the first lesson is formatted and highlighted as I would expect:

Additionally, compile/test cycles are dramatically shortened with this setup. Overall, I feel like this is a reasonable compromise between staying 100% authentic to the original experience and having modern conveniences.

Standard