Programming in C traditionally starts with outputting “Hello World” to the text console/terminal command-line. In the previous C tutorial we looked at that code but didn’t examine it, so let’s do that now, and expand to include input from the user so we can start making useful code.
Coding a Retro Game: Learn to Code in C
Learn to code in C and create your own retro games with this free multi-part C programming tutorial series.
We will also look at the essentials of compiling our C code so that we can actually run it and see the results. First, however, we need to set up our C programming tools and development environment!
Installing CC65, the C Compiler for 6502 8-Bit Retro Computers
For retro 8-bit systems built around the 6502 processor, the go-to C compiler is CC65.
c65 has C and runtime library support for many of the old 6502 machines, including
- Commodore:
- VIC 20
- C16/Plus/4
- C64 and 128
- PET series
- Apple ][
- Atari 8-bit home computers and 2600/5200 consoles.
- PC-Engine/ TurboGrafx-16
- Nintendo Entertainment System (NES) console.
- Oric Atmos
Installing CC65 on Windows
Download the CC65 Windows binary snapshot and extract it to your machine.

Installing CC65 on MacOS
On Mac you can install CC65 using Homebrew. Open a terminal and enter
brew install cc65
Installing CC65 on Raspberry Pi / Linux

On Debian-based Linux you can install via the Deb package manager. Open a terminal and enter either
sudo apt-get install cc65
or sudo apt install cc65
as appropriate.
Installing CC65 from Source
Installing on Linux and Mac otherwise involves downloading the source code from the Github repository and then compiling it, which means you need to ensure you have a modern compiler installed.
Installing VBCC, the C Cross-Compiler for Atari ST and Amiga

For 16-bit platforms, vbcc (Volker Barthelmann’s C Compiler) is a popular, if difficult to get started with, choice.
Unlike CC65, there only seems to be a Windows binary of VBCC available, though it does compile and run on Linux and MacOS. Even with the binary, you also need some extra things like config files. This repo has a full setup for Amiga targets.
We will return to VBCC later in the series.
z88dk Spectrum/Amstrad/MSX C Compiler

z88dk is the C compiler for a huge number of z80 targets, both with the z88dk classic and new libraries.
If you want to code in C for the Sinclair ZX Spectrum family (including the Next), Amstrad CPC 464 etc, CP/M, MSX, Sega Mastersystem, this is the tool for you.
Originally whatever I did with this compiler, nothing would work. Now I return to it and everything seems fine. My only guess if it doesn’t work for you either, nuke any and all competing z80 compilers and environment variables. I am so glad it works for me now because it is an amazing achievement, the best Z80 compiler and library combination that I have found.

To pair with z88dk, grab a copy of Retro Virtual Machine for cross-platform Amstrad and Spectrum emulation. Many Speccy fans enjoy the Fuse emulator (Mac binary is here) for Spectrum C code testing, but you are going to want ZEsarUX if you want exhaustive Amstrad CPC464, Spectrum support AND the ZX Spectrum Next.
Compilation takes the form zcc +zx -vn -o hello test.c -lndos -create-app
(we will dive deeper into z80 C programming later in the series).
Installing z88dk for Linux or Unix
There is a Snap for Linux here.
Next easiest would be to use Docker and the Docker image here.
Otherwise you will need to download the source, extract and compile it, which will require you to ensure you have a modern C compiler (see following section).
Installing z88dk on Windows
Download the latest z88dk Windows release and extract it.
z88dk can build .tap files for Spectrum. To make Amstrad-compatible disks from your compiled C code you will also want a copy of CPCDiskXP.
Installing z88dk for MacOS
On Mac, z88dk just needs downloading, extracting and a shell environment variable creating:
curl -O http://nightly.z88dk.org/z88dk-osx-latest.zip
unzip z88dk-osx-latest.zip
After this you will have a directory called z88dk. Go into the directory and enter pwd
so you can copy the full path to z88dk.
You will need to edit the .profile
or environment for your shell, in my case zsh but you might be using bash, etc. To discover which your default is you can always echo "$SHELL"

As I am using zsh I need to edit ~/.zshenv
:

Next time you start a new shell you can test if this worked by using echo $ZCCCFG
z88dk can build .tap files for Spectrum. To make Amstrad-compatible disks from your compiled C code you will also want iDSK or run a copy of CPCDiskXP under Wine/Crossover.
Installing C Compilers for Modern Desktop Operating Systems
The most popular, and free, C compilers are Clang/LLVM and GCC. In the Microsoft universe, Visual C++ dominates.
You might find that your system already has one of these C compilers built-in. Drop to the command-line terminal and enter cc -v
and see what you get back. CC tends to point to the system default.

It could be you have GCC (GNU C compiler), especially on a Unix-like environment such as Linux, BSD, etc. You can find that with which gcc
On Mac the free Xcode from Apple is available to download. It’s big if you install everything, but it’s the go-to for Mac. You don’t have to use the Xcode editor, there are tons of options as you will see below.
For Windows, obviously Microsoft would be the choice of many. Their subscription-based Visual Studio suite is a massive, comprehensive, fully-featured environment that provides everything, with free community options too.
To get the Windows version of the GCC tools to match the rest of us, there is MingW. After installing the binary package, you will need to run pacman -S --needed base-devel mingw-w64-x86_64-toolchain

After closing the window then relaunching Mysys2 you should be able to enter CC -v
to test everything worked.


Set Up C Editor / IDE

To program in C you just need a text editor. There are editors and there are Integrated Development Environments that include a lot more in the way of features and benefits.
That means on an ancient PDP-11 we can use the command-line editor, Ed. There are still programmers who stick to Ed, Vi, Vim et al. If you are just starting out, I wouldn’t be so hardcore.
Raspberry Pi OS comes with a nice little IDE called Geany right out of the box.

On other modern systems we have a wealth of tools, with code completion and colors and everything! (To the Vim fans, yes I know there are syntax highlighting etc in Vim by using syntax on
, shhh)
One of my favourites is Microsoft Visual Studio Code, which is multi-platform and free.
Atom is really cool too, and also cross-platform.
Many people swear by Sublime text but I prefer the free options above.
The Structure of a C Program
So now we get to actually writing a C program.
C programs have a structure that will become second-nature to you but often feels a little strange to begin with.
C Headers and Include Files
At the start we have #include
which tells the compiler to include external code, either code you have written into another text file in the current directory, or in the case of <>
to look in where header files or libraries are stored in the system.
This is one of the most powerful features of the C language and many others. The actual language of C is small, all of the really fancy stuff you can do with it is stored in these extra files. Also, keeping different things in different, separate but included files, helps to organize your projects so you don’t have to keep scrolling up and down one huge wall of text.
I have highlighted line 2 to show a problem that we already have. Conio.h only exists on some systems! On a Mac or Linux computer you will get a compiler error.
This is a downside of these library files, they are often but not always platform-specific, so selecting and testing is important.
Int Main()
Next up we have our Main function. After compilation, when you run your program this is where the program will start executing your instructions.
Arduino coders might be a little confused right now. With Arduino, which is actually C/C++ as mentioned before, the Arduino IDE hides Main from you, and instead you have two boilerplate functions, Setup()
and Loop()
.
The int
means this function returns an integer number. Functions that don’t return anything have void
functionName
instead, but we will see further down that this Main does need to return a number.
C Data Types and Variables
Now we see int x
. This is a variable declaration. We are saying we want an integer number called x.
A variable is a named box to store data in. We can put things in the box, examine it, take it out, copy it, do math with it, display the contents, and destroy it.
C has a standard set of variable data types, char, int, float and double, along with signed, unsigned, etc – here are some of the highlights:
Name | Description | Bits |
---|---|---|
char | A byte of data, char refers to a character of text, such as the letter A. | 8 |
signed char | A byte but represents a number in the [−127, +127] range. | 8 |
int / signed int | Integer containing a number in the [−32,767, +32,767] range. | 16 |
unsigned int | Integer that contains a number in the [0, 65,535] range. | 16 |
long | Contains a number in the [−2,147,483,647, +2,147,483,647] range. | 32 |
unsigned long | Capable of containing [0, 4,294,967,295] | 32 |
float | Single-precision floating-point number. | |
double | Double-precision floating-point number |
If you include stdbool.h
then you also get a bool type which represents a true or false value, ie. a boolean or bit value.
Comments in C Programming
A very good habit to get into with any programming is to add comments to your code. They are like notes to yourself or a future programmer to explain what is going on.
Some people argue that they are unnecessary because well-written code should be easily understood, but I suggest you get into the habit anyway because if you are anything like me you will forget how your code works.

Originally C comments were only of the /* my comment */
variety, where anything between the start and end is a comment and excluded from the final compiled version of the code, but C++ also introduced // comment
as an option for short comments. Most C compilers will accept the slash style now.
PrintF()
To output some text we use PrintF
. This is a function that comes with Stdio.H
Function is another way of saying subroutine or command. It’s a named group of functionality, that might accept parameters and might return a value as well as doing something useful. As well as the functions that come packaged in pre-made libraries, we can make our own.
In this case we use PrintF to output Hello World\n
– the \n is a special symbol that tells the output to add a new line so that anything added afterwards appears below the original text.
function ‘getch’ is invalid – Ahh 💩 Conio.H doesn’t exist on Mac and Linux!
Next we want to wait for the user to confirm they have seen the message with some input, before closing. There is a problem, though.
Remember I said Conio.h only exists on some systems? Our program will fail to compile on Mac and Linux because we are trying to use getch()
and we don’t have that.
For now we can replace the platform-specific function with getchar()
which has the disadvantage of waiting for the user to hit enter as well as input a key, but will at least allow us to move ahead.
Enter the following in the text editor of your choice, and call it something like hello_1.c – remember where you saved it, then drop to a command-line terminal.
#include <stdio.h>
int main()
{
/* Variable to store input */
char x;
/* Print Hello */
printf("Hello World\n");
/* Wait for keypress */
x = getchar();
printf("%c\n",x);
return(0);
}

Go into where you saved your hello_1.c and enter
cc -o hello hello_1.c
and press return.
Hello
is the name of the program filename to output, and hello_1.c
is our C source code input file.
If there were no problems it will return back without any output.
Now you should be able to run your hello
program and see Hello World, it will wait for you to enter a single keypress followed by enter/return. Whatever you entered should also be echoed to the screen.
Compiling for C64 and other Retro Computers
CC65 allows you to compile C code for many, many retro computers. You specify which target you want to compile for using the -t
parameter like so:
cl65 -o hello_c64.prg hello_c64.c -t c64
Even more convenient, CC65 does support conio.h
so we can do this:
#include <stdio.h>
#include <conio.h>
int main()
{
/* Variable to store keypress */
int x;
/* Clear Screen */
clrscr();
bgcolor(0);
bordercolor(5);
textcolor(4);
/* Print Hello World */
gotoxy(10,10);
printf("Hello World\n");
/* Wait for keypress */
x = getchar();
return(0);
}

After compiling, simply drag and drop the hello_c64.prg
onto your Vice64 window to launch it. Not all emulated systems are so forgiving with how they need the software packaging, so we will return to more 8-bit retro computer targets later.
Accepting String Input in C
What if you want more than one keypress, like for example, a full sentence?
Collections of characters are called strings.
In C a string is an array, with a null at the end (‘\0’).
You might recall from the Amiga Programming Tutorial that if a variable is like a box for values to be put into, arrays are like a shelf of those boxes:


We need to specify how big we want the container to be, so we put the length in between [ ] brackets in line 6:
#include <stdio.h>
int main()
{
/* Variable to store input */
char name[255];
/* Print Hello */
printf("Hello World\n");
/* Wait for name */
printf("What's your name?\n");
scanf("%s",name);
printf("Hello %s\n\n", name);
return(0);
}
To accept the input we now use ScanF()
instead of Getchar
. This new function allows us to specify that we are expecting a string by using the %s
symbol as our parameter. We can then output that string back to the user.
Next Up
In this tutorial we got our C code to compile, and did some simple input and output. Next we need to solve that problem we discovered where only some platforms have a conio.h
before we can have nicely formatted, interactive games!