Atari Lynx programming tutorial series:
- Part 2: Development environment for Windows
- Part 3: Analyzing Hello World
- Part 4: Creating a project
In the previous parts we looked at the skeleton of a Atari Lynx game and the project you need to compile the ROM binary image, that will eventually go onto a cartridge and into your favorite Lynx game handheld for playing.
Overview of TGI
The Tiny Graphics Interface (TGI in short) is a library that is part of the CC65 suite. It exposes cross-machine functionality for graphics on your device, ranging from Apple II, through Commodore 64 to Atari Lynx.
The TGI is agnostic of the actual device, meaning that it doesn’t matter which device you are programming for. The API is always the same. However, some devices do not offer all of the functionality, but just a subset. This is true for the Atari Lynx as well: it has a subset, not all of the functions available. Still, there is a whole lot to discover and use. Read on.
A clever design makes it possible to have one set of functions that work the same for totally different hardware. There is a separation of the generic core of TGI from the specific implementation for a particular machine. This implementation is called the driver for TGI. For the Lynx the driver is located inside the lynx-160-102-16.tgi file. The filename is derived from the 160×102 resolution and 16 color palette. The other machines supported by CC65 that have a similar separate driver are C64, C128, Atmos, Apple 2, Apple 2e and Geos.
The complete set of functionality is as follows:
Lifecycle |
Typography |
|
tgi_load_driver tgi_unload tgi_install tgi_uninstall tgi_init tgi_done |
tgi_load_vectorfont tgi_install_vectorfont tgi_free_vectorfont tgi_settextdir tgi_settextscale tgi_settextstyle tgi_gettextwidth tgi_gettextheight tgi_outtext tgi_outtextxy |
|
Screen management |
Colors |
Drawing |
tgi_clear tgi_setdrawpage tgi_setviewpage tgi_getpagecount tgi_getxres tgi_getmaxx tgi_getyres tgi_getmaxy tgi_getaspectratio tgi_setaspectratio |
tgi_getcolorcount tgi_getmaxcolor tgi_setcolor tgi_getcolor tgi_setpalette tgi_getpalette tgi_getdefpalette |
tgi_getpixel tgi_setpixel tgi_gotoxy tgi_line tgi_lineto tgi_circle tgi_ellipse tgi_arc tgi_pieslice tgi_bar |
Miscellaneous |
Lynx specific |
|
tgi_error tgi_geterrormsg tgi_ioctl |
tgi_sprite tgi_flip tgi_setbgcolor tgi_setframerate tgi_busy tgi_updatedisplay tgi_setcollisiondetection |
I have highlighted the functions that are not available for the Atari Lynx in bold. The items in italic indicate that the particular function does not have any effect.
We are not going to discuss each and every function right now. That would be too much. Besides, this is a tutorial and not a TGI library programmer’s reference. Instead I will cover the essential functions and selectively pick a couple of example that illustrate the use of similar functions.
Lifecycle of the TGI driver
We already encountered the loading of the driver in the Hello World example. Let’s dive a little deeper and see what the fragment below actually does.
extern char lynxtgi[];
void initialize() {
tgi_install(&lynxtgi); tgi_init(); CLI();
// ...
}
The function call to tgi_install will install the Lynx specific TGI driver. The tgi driver file is actually a o65 object file meant for loadable drivers. Because the Lynx does not have the ability to load a driver dynamically it needs to be linked statically when the game is built. That is why the make file has a rule for creating an object file from the tgi driver file:
lynx-160-102-16.o: $(CP) $(CC65_INC)\..\tgi\$*.tgi . $(CO) --code-label _lynxtgi $*.tgi $(AS) -t lynx -o $@ $(AFLAGS) $*.s $(RM) $*.tgi $(RM) $*.s
This rule copies the tgi file from the TFI directory inside the CC65 include folder. It then runs the CO65.exe tool to convert the driver’s “o65 object file back into the native object file format used by the cc65 tool chain”.
The tgi_init function calls into the initialization routine of the driver. Where the loading and installation of the driver is performed only once, the initialization can be called more than a single time. We will look into the Lynx-specific implementation of the initialization later on, when we start using interrupts and sprites. For now it is enough to know that there is some code that does important stuff involving a hookup of an interrupt for vertical blanks.
The final line is a wrapper call to the assembly CLI instruction that will clear the Interrupt Disable flag of the processor. After that the vertical blank interrupt from the TGI driver (and all other interrupts) will fire every 60th part of a second.
After loading the driver, the lynx_tgi character array will contain the value “tgi” if the load was successful. Assuming that the driver is loaded and available, it is time to check out what we can do with it.
Typography: working with text
The TGI library has two types of typography support: vector and bitmap fonts. The Lynx only has support for bitmaps and a single system supplied bitmap font. It is part of and inside the TGI driver. Look at the source code of the CC65 in the libsrc/lynx/lynx-160-102-16.s source file at the very bottom. It shows the data for the bitmap font.
Having said that, it is no surprise that the vector related functions are not available for the Lynx. Also, the scale, style and direction (vertical or horizontal) of the font cannot be changed. This leaves just a couple of functions: outtext and outtextxy to output text.
Let’s do some examples:
void show_screen() { char text[20];
tgi_outtextxy(10, 0, "Resolution:"); itoa(tgi_getxres(), text, 10); tgi_outtextxy(10, 10, text); itoa(tgi_getyres(), text, 10); tgi_outtextxy(40, 10, text); tgi_outtextxy(10, 20, "Max coor (x,y):"); tgi_gotoxy(10, 30); tgi_outtext("(0x"); itoa(tgi_getmaxx(), text, 16); tgi_outtext(text); tgi_outtext(",0x"); itoa(tgi_getmaxy(), text, 16); tgi_outtext(text); tgi_outtext(")"); tgi_updatedisplay(); }
In the Hello World sample we already saw the tgi_outtextxy function. The first half of this code uses it to put text at a specific place. I have used two other tgi functions that have almost nothing to do with typography: tgi_getxres and tgi_getyres. These functions return the x and y resolution of the screen. For the Lynx this is 160 by 102 as per the hardware specs.
The code also has an itoa call that converts an integer value to a string value based on the radix. The radix is the
The second half of the code shows how you can use tgi_gotoxy to move the current position to a particular point (x,y). Using tgi_outtext after that will place the text at that position and advance the current coordinate to the end of the text. This allows you to compose text on the screen without too much string manipulation and coordinate management.
Here’s something a little more advanced:
void show_screen() { const char* msg; char i; tgi_clear(); tgi_setcolor(COLOR_WHITE); for (i = 0; i < 11; i++) { msg = tgi_geterrormsg(i); tgi_setbgcolor(i + 1); tgi_outtextxy(0, i * 9, msg); }
There is now a loop from 0 through 10 that reads error messages for the current index of the loop. It also sets the background color to 1 through 11 with the tgi_setbgcolor function and then outputs the messages on a new line. Since the fonts are 8 bits high and the line-height is set to 9 (the multiplication) there is a single pixel high gap between the pieces of text.
The tgi_setbgcolor is Lynx specific and not available in the core TGI functino set.
Next time
In following parts we will look at more of the TGI functions that will help showing graphics on the Lynx screen. We will cover the drawing functions and sprites. Stay tuned.
Pingback: Programming tutorial: Part 6–Colors | Diary of an Atari Lynx developer
Pingback: Programming tutorial: Part 7–The basics of sprites | Diary of an Atari Lynx developer
Pingback: Programming tutorial: Part 8–Changing appearances | Diary of an Atari Lynx developer
Pingback: Programming tutorial: Part 9–Advanced sprites | Diary of an Atari Lynx developer
Pingback: Programming tutorial: Part 10–Collisions | Diary of an Atari Lynx developer
just a question: if we want to write text in other character size besides 8×8, like 4×6 or 4×5 (seeing that 8×8 characters looks too large on a Lynx display), we must create a new procedure for that, which draws each character pixel by pixel?
Sorry for the late reply.
Actually, a font is drawn by sprites, where every character has its own sprite data. Karri Kaksonen recently posted examples and source code on how to do it: http://www.atariage.com/forums/topic/205861-do-we-need-a-relative-font-tgi-driver-also/page__hl__+atari%20+font#entry2663801
I might do a tutorial episode on creating custom fonts, as I also created two new fonts. It’s not hard. The tough part is designing the font itself. Once you get that done, it is just a matter of tweaking the sample code of Karri and you should be good to go.