Programming for 8-bit computers can be quite fun, CC65 is a C compiler for 8-bit 6502-based computers. Along with CC65 is a library called TGI or Tiny Graphics Interface. This library makes it easier to draw graphics on the screen, and also allows for use of vector fonts.
I decided to have a go at drawing graphics on the Commodore 64 using this library.
The Commodore 64 has a native maximum resolution of 320x200 with a 16 color palette.
Here's a color table:
# Name RGB Value (Hex) CC65 Color Definition TGI Color Definition
--------------------------------------------------------------------------------
0 Black #000000 COLOR_BLACK TGI_COLOR_BLACK
1 White #FFFFFF COLOR_WHITE TGI_COLOR_WHITE
2 Red #9F4E44 COLOR_RED TGI_COLOR_RED
3 Cyan #6ABFC6 COLOR_CYAN TGI_COLOR_CYAN
4 Purple #A057A3 COLOR_PURPLE TGI_COLOR_PURPLE
5 Green #5CAB5E COLOR_GREEN TGI_COLOR_GREEN
6 Blue #50459B COLOR_BLUE TGI_COLOR_BLUE
7 Yellow #C9D487 COLOR_YELLOW TGI_COLOR_YELLOW
8 Orange #A1683C COLOR_ORANGE TGI_COLOR_ORANGE
9 Brown #6D5412 COLOR_BROWN TGI_COLOR_BROWN
10 Light Red #CB7E75 COLOR_LIGHTRED TGI_COLOR_LIGHTRED
11 Dark Gray #626262 COLOR_GRAY1 TGI_COLOR_GRAY1
12 Mid Gray #898989 COLOR_GRAY2 TGI_COLOR_GRAY2
13 Light Green #9AE29B COLOR_LIGHTGREEN TGI_COLOR_LIGHTGREEN
14 Light Blue #887ECB COLOR_LIGHTBLUE TGI_COLOR_LIGHTBLUE
15 Light Gray #ADADAD COLOR_GRAY3 TGI_COLOR_GRAY3
The TGI graphics library (tgi.h
) has functions for various things, like drawing lines and shapes. For now I'm interested in
the following:
tgi_install(const void *driver); // Install a driver that's been linked at compile time
tgi_init(); // Initialize the driver
tgi_getmaxx(); // Returns the max value on the x-axis of the pixel grid
tgi_getmaxy(); // Returns the max value on the y-axis of the pixel grid
tgi_getaspectratio(); // Returns the aspect ratio of the display
tgi_setpalette(const unsigned char* palette); // Sets the color palette to the values set in an array
tgi_clear(); // Clears the screen
tgi_setdrawpage(unsigned char page); // Sets the page (buffer) which drawing will happen on
tgi_setviewpage(unsigned char page); // Sets the page (buffer) which is currently being viewed
tgi_setcolor(unsigned char color); // Set the color to be used for drawing
tgi_clear(); // Clears the screen
tgi_uninstall(); // Uninstalls and unloads the loaded driver
A function that's part of conio.h
used for setting the color of the border:
bordercolor(unsigned char color);
The tgi_stat_stddrv.s
driver only supports displaying two colors out of the 16 color palette at once.
The graphics driver needs to be compiled, tgi_stat_stddrv.s
from the cc65 source tree libsrc/c64/tgi_stat_stddrv.s
can be
compiled by running:
$ ca65 tgi_stat_stddrv.s
Now, some headers need to be included in your program. For the Commodore 64 the ca64.h
header is needed. The tgi.h
header is
the header for the graphics library, it also needs to be included. conio.h
(console I/O) needs to be included because many
things will be used from there. The cc65.h
header contains functions specific to cc65.
#include <cc65.h>
#include <conio.h>
#include <tgi.h>
#include <c64.h>
Next, some variables should be defined:
static unsigned max_x;
static unsigned max_y;
static unsigned aspect_ratio;
unsigned char border;
static const unsigned char palette[2] = {
/* The TGI COLOR definitions for the 2 colors you want to use */
};
Then the driver and library need to be installed and initialized:
tgi_install(tgi_static_stddrv);
tgi_init();
Now it's time to set the maximum value on the x and y axis of the pixel grid, as well as the aspect ratio and border color:
max_x = tgi_getmaxx();
max_y = tgi_getmaxy();
aspect_ratio = tgi_getaspectratio();
border = bordercolor(/* CC65 color definition */);
Now time to set the pallette, draw color, clear the screen, and set the drawpage:
tgi_setpalette(palette);
tgi_setcolor(/* TGI color definition */);
tgi_setdrawpage(0); // Sets it to the 0th draw page
Now we can start drawing pixels! There are some functions for doing this:
tgi_line(int x1, int y1, int x2, int y2); // Draws a line on the set coordinates
tgi_lineto(int x2, int y2); // Draws a line from the current position of the cursor to a specified endpoint
tgi_outtext(const char* s); // Outputs text at the current position of the cursor
tgi_outtextxy(int x, int y, const char* s); // Outputs text at the given coordinates
tgi_setpixel(int x, int y); // Plot a pixel at the given coordinates
There are also functions for drawing shapes, those are more complicated and won't be covered in this post which I'm focusing on the basics.
After drawing some pixels the viewpage needs to be set, which will display what you've drawn on the drawpage: tgi_setviewpage(0);
After you're done with the tgi library, you need to invoke tgi_uninstall();
at the end of your program, and set the border color:
tgi_uninstall();
(void) bordercolor(Border);
I will show some examples below of basic programs I've written to draw some 2d and 3d objects, as well as screenshots:
#include <stdio.h>
#include <cc65.h>
#include <conio.h>
#include <tgi.h>
#include <c64.h>
int main(void) {
static unsigned max_x;
static unsigned max_y;
static unsigned aspect_ratio;
unsigned char border;
static const unsigned char palette[2] = {
TGI_COLOR_BLACK,
TGI_COLOR_WHITE,
};
tgi_install(tgi_static_stddrv);
tgi_init();
max_x = tgi_getmaxx();
max_y = tgi_getmaxy();
aspect_ratio = tgi_getaspectratio();
border = bordercolor(COLOR_BLACK);
tgi_setpalette(palette);
tgi_setcolor(TGI_COLOR_WHITE);
tgi_clear();
tgi_setdrawpage(0);
tgi_line(100, 150, 150, 50);
tgi_line(200, 150, 150, 50);
tgi_line(100, 150, 125, 175);
tgi_line(125, 175, 200, 150);
tgi_line(125, 175, 150, 50);
tgi_line(100, 150, 165, 125);
tgi_line(165, 125, 200, 150);
tgi_line(165, 125, 150, 50);
tgi_setviewpage(0);
cgetc();
tgi_uninstall();
(void) bordercolor(border);
return 0;
}
#include <stdio.h>
#include <conio.h>
#include <cc65.h>
#include <tgi.h>
#include <c64.h>
static unsigned max_x;
static unsigned max_y;
static unsigned aspect_ratio;
int main(void) {
unsigned char border;
static const unsigned char palette[2] = {
TGI_COLOR_BLACK,
TGI_COLOR_WHITE,
};
tgi_install(tgi_static_stddrv);
tgi_init();
max_x = tgi_getmaxx();
max_y = tgi_getmaxy();
aspect_ratio = tgi_getaspectratio();
border = bordercolor(COLOR_BLACK);
tgi_setpalette(palette);
tgi_setcolor(TGI_COLOR_WHITE);
tgi_clear();
tgi_setdrawpage(0);
tgi_line(100, 150, 100, 50);
tgi_line(100, 150, 200, 150);
tgi_line(200, 150, 200, 50);
tgi_line(100, 50, 200, 50);
tgi_line(100, 50, 125, 25);
tgi_line(125, 25, 225, 25);
tgi_line(200, 50, 225, 25);
tgi_line(225, 25, 225, 125);
tgi_line(225, 125, 200, 150);
tgi_line(100, 150, 125, 125);
tgi_line(125, 125, 125, 25);
tgi_line(125, 125, 225, 125);
tgi_setviewpage(0);
cgetc();
tgi_uninstall();
(void) bordercolor(border);
return 0;
}
#include <stdio.h>
#include <conio.h>
#include <cc65.h>
#include <tgi.h>
#include <c64.h>
static unsigned max_x;
static unsigned max_y;
static unsigned aspect_ratio;
int main(void) {
unsigned char border;
static const unsigned char palette[2] = {
TGI_COLOR_BLACK,
TGI_COLOR_WHITE,
};
tgi_install(tgi_static_stddrv);
tgi_init();
max_x = tgi_getmaxx();
max_y = tgi_getmaxy();
aspect_ratio = tgi_getaspectratio();
border = bordercolor(COLOR_BLACK);
tgi_setpalette(palette);
tgi_setcolor(TGI_COLOR_WHITE);
tgi_clear();
tgi_setdrawpage(0);
tgi_line(100, 100, 100, 75);
tgi_line(100, 150, 100, 125);
tgi_line(150, 150, 150, 125);
tgi_line(150, 100, 150, 75);
tgi_line(200, 150, 200, 125);
tgi_line(200, 100, 200, 75);
tgi_line(100, 100, 150, 125);
tgi_line(150, 100, 200, 125);
tgi_line(100, 150, 150, 200);
tgi_line(150, 200, 200, 150);
tgi_line(100, 75, 150, 25);
tgi_line(200, 75, 150, 25);
tgi_line(100, 125, 125, 112);
tgi_line(200, 100, 175, 112);
tgi_setviewpage(0);
cgetc();
tgi_uninstall();
(void) bordercolor(border);
return 0;
}