Table of Contents
Getting started with the GameTank C SDK
To start a new project you can use the template feature on github to create a new repository based on a snapshot of gametank_sdk.
Along with the SDK itself you'll need to install some other tools:
cc65
cc65 is an open source toolchain for cross-compiling C to 6502-based platforms. It includes a C compiler, an assembler, and a linker. It supports multiple variants of the 6502, including the 65C02 and the W65C02S used by the GameTank.
This should be built from source on a recent commit, or can be downloaded as a snapshot build for Windows. The last official release is currently quite dated, so you'll have trouble building the SDK if you install it with apt on Linux or use the “Releases” tab on github instead of the latest snapshot.
NodeJS
Node is used for the sprite and music conversion scripts as well as the build config generator. Version 20 or newer should be used.
Zopfli
Zopfli is the compression tool used by the SDK for sprites. It can be installed with apt or built from source. We'll be using its Deflate mode that corresponds to the Inflate algorithm provided as a library in cc65.
Make (and a few other Unix tools)
If you already installed any tools for compiling C then make is probably also installed. This command will handle the numerous other commands you'd need to convert your collection of code files and art assets into a GameTank ROM file.
Some other GNU tools such as find
or sh
will be required to build.
Emulator
Finally you'll need the GameTank Emulator to test your games on your PC.
First Build
A clean copy of the SDK will include the following folders:
- assets
- lib
- scripts
- src
src
will be the home for your C code, and src/gt holds the library code provided to help with tasks like drawing images, reading controllers, or playing sounds.
assets
is where you'll place files such as bitmaps and midis to be picked up by conversion scripts.
scripts
is where the conversion and build config scripts live.
lib
contains prebuilt helper binaries and doesn't need to be interacted with that much.
You'll also find, among the files in the project root, project.json
which contains project-level customization options such as the game name and enabled modules. You can leave this as-is for now.
To check that dependencies are installed and the SDK was pulled cleanly, build the starter code by running make
.
Quite a bit is going on under the hood in the Makefile, but the end result is that the code is compiled into a .gtr (for “GameTank Rom”) inside the bin
folder. By default the rom name is game.gtr
and it can be opened in the GameTank Emulator.
You should see an orange square bouncing around on a gray background. This behavior is defined in main.c, which is the starting point of your program as far as C code is concerned.
Let's take a look at this file!
#include "gt/gametank.h" #include "gt/gfx/draw_queue.h" char box_x = 30, box_y = 20; char dx = 1, dy = 1; int main () { init_graphics(); while (1) { // Run forever queue_clear_screen(3); queue_draw_box(box_x, box_y, 8, 8, 92); queue_clear_border(0); box_x += dx; box_y += dy; if(box_x == 1) { dx = 1; } else if(box_x == 119) { dx = -1; } if(box_y == 8) { dy = 1; } else if(box_y == 112) { dy = -1; } await_draw_queue(); await_vsync(1); flip_pages(); } return (0); // We should never get here! }
init_graphics()
takes care of some setup used for other drawing functions, and should be called pretty early on.
The GameTank has a double buffer, and typically you'll be drawing on one frame while the other is being presented to the television. flip_buffer()
toggles both the render target and the output buffer. In the beginning it's used to clear the screen on both buffers, and then is used on every frame right after the beginning of the video sync period.
A notable feature of the SDK is a queued drawing API. The GameTank's blitter gives it the ability to continue executing code while pixels are being copied into the framebuffer. Making the most of this ability requires performing as much CPU computation as we can during a blit operation instead of simply waiting for a blit to finish. Eventually though there is nothing else to do but wait, and await_draw_queue()
allows us to wait not just for the current draw but for all drawings we've requested that are still pending.
queue_draw_box
draws a box with a solid color at a given location. queue_clear_screen
and queue_clear_border
are essentially shortcuts for calling queue_draw_box
on the full screen or the screen edges. This is part of the queued drawing API, so if it is called multiple times consecutively the first box will begin drawing while the sucessive calls are added to a list to be automatically drawn when the previous box finishes.
await_vsync
waits for a number of Vertical Sync signals, and is used for synchronizing with the television picture. await_vsync(1)
doesn't wait for an entire 1/60s frame, but instead waits until the beginning of the next vertical sync period. This is when the electron beam will be approaching the top-left corner of the monitor, and soon begin to emit the next full frame.
Testing on hardware
See the Cartridge Flasher article for how to put your game onto a cartridge. The SDK generates a “game.gtr” file to use with the emulator and several “game.gtr.bankXX” files to use with the flasher.
./GTFO.exe -p COM3 ~/repos/fiend/bin/game.gtr.bank*