Programming tutorial: Part 4–Creating a project

Atari Lynx programming tutorial series:

In the previous three parts we looked at how to get started with a development environment. We also took a look at an Hello World example. But, how was the Hello World code put together by the environment? In this part we will create a skeleton project for Visual Studio and use the NMake utility to create rom image of the game we will be making.

This part focuses somewhat on the use of the NMake utility instead of make.exe. It is also meant as an example of using NMake. If you are using a different development environment than Visual Studio, this part may still be relevant in some areas. Although the make tool NMake is part of Visual Studio, its syntax is very similar to that of makefile files for the make.exe utility from Unix. Most of the concepts still hold for make.exe.

Preparing a project for Atari Lynx games

Refer to part 2 – Development environment to see what you have to do to get Visual C++ 2010 Express all set up. If you’re spoiled like me, you might just have a paid version of Visual Studio 2010, which should include Visual C++.

Start Visual Studio and create a new project by selecting File, New > Project from the menu.

image

Choose the Makefile project and pick a name for your project. I have named it TutorialPart4.

image

A wizard will start that allows you to enter lots of information. Simply press Finish, as you can set this up in another dialog (with some more space for editing).

At the Solution Explorer, you will see the new Starter project. Right-click it and choose Properties. First, go to the VC++ Directories node and remove all directories listed there. Fill the Executables and Include directories with $(PATH); and the CC65 include folder respectively.

image

Next, go to the Configuration Properties, NMake at the left tree and fill the three command-lines with the following:

nmake.exe /f tutorial.mak BUILD=Debug all
nmake.exe /f tutorial.mak BUILD=Debug all
nmake.exe /f tutorial.mak BUILD=Debug clean

This means that we will have a tutorial.mak NMAKE file for our compiler to use. There will be two configurations for building, rebuilding (the same for now) and cleaning.

Set the Output to tutorial-part4.lnx. That is the name of the file that we will be producing. The dialog should resemble this:

image

Next, add new text files “tutorial.mak” and “lynxcc65.mak” to the project. You will also need to include a tutorial.c file in the root of the project. The result looks like this:

image

A generic make file for CC65 Lynx projects

There is a reason to have two make files. There are some generic rules that apply to each game for the Lynx. These are captured inside the lynxcc65.mak file. The game specific make instructions can be kept separate from this. In particular, the tutorial.mak file includes the generic make file. The latter is reused over projects and contain the vanilla rules. Let’s create that one first, shall we?

Open the lynxcc65.mak file and copy the following text in it:

CC65="C:\Program Files (x86)\CC65"
CC65_BIN=$(CC65)\bin
CC65_INC=$(CC65)\include
CC65_ASMINC=$(CC65)\asminc
CC65_TOOLS=$(CC65)\wbin
 
BUILDDIR=$(MAKEDIR)\$(BUILD)
ODIR=$(MAKEDIR)\obj
.SUFFIXES : .c .s .o .asm .bmp .pal .spr
.SOURCE : 
# Compiling for Atari Lynx system SYS=lynx # Names of tools CO=co65 CC=cc65 AS=ca65 AR=ar65 CL=cl65 SPRPCK=sprpck CP=copy RM=rm ECHO=echo TOUCH=touch CODE_SEGMENT=CODE DATA_SEGMENT=DATA RODATA_SEGMENT=RODATA BSS_SEGMENT=BSS SEGMENTS=--code-name $(CODE_SEGMENT) \ --rodata-name $(RODATA_SEGMENT) \ --bss-name $(BSS_SEGMENT) \ --data-name $(DATA_SEGMENT) # Flag for assembler AFLAGS= # Flags for C-code compiler CFLAGS=-I . -t $(SYS) --add-source -O -Or -Cl -Os # Rule for making a *.o file out of a *.s file .s.o: $(AS) -t $(SYS) -I $(CC65_ASMINC) -o $@ $(AFLAGS) $< # Rule for making a *.o file out of a *.c file .c.o: $(CC) $(SEGMENTS) $(CFLAGS) $< $(AS) -o $@ $(AFLAGS) $(*).s lynx-stdjoy.o: $(CP) $(CC65_INC)\..\joy\$*.joy . $(CO) --code-label _lynxjoy $*.joy $(AS) -t lynx -o $@ $(AFLAGS) $*.s $(RM) $*.joy $(RM) $*.s 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 # Rule for making a *.o file out of a *.bmp file .bmp.o: $(SPRPCK) -t6 -p2 $< $(ECHO) .global _$(*B) > $*.s $(ECHO) .segment "$(RODATA_SEGMENT)" >> $*.s $(ECHO) _$(*B): .incbin "$*.spr" >> $*.s $(AS) -t lynx -o $@ $(AFLAGS) $*.s $(RM) $*.s $(RM) $*.pal $(RM) $*.spr

Explaining the contents of the make file

You might wonder what it all means. And that’s a good thing, because you will need to know. In tutorial parts much later than this one, we will revisit this. Time to take a short look at the individual elements.

CC65="C:\Program Files (x86)\CC65"
CC65_BIN=$(CC65)\bin
CC65_INC=$(CC65)\include
CC65_ASMINC=$(CC65)\asminc
CC65_TOOLS=$(CC65)\wbin
BUILDDIR=$(MAKEDIR)\$(BUILD) 
ODIR=$(MAKEDIR)\obj

These first entries are synonyms for directory names. CC65 is the base directory where the CC65 suite is located. The BIN folder has the compiler executables, INC the include files, ASMINC the include files for assembler code and TOOLS contains the Unix tools that we will use.

Next, there are some more synonyms. BUILDDIR is a place for the current build artifacts. ODIR is the folder where intermediate build files like object files can be stored.

.SOURCE :  .SUFFIXES : .c .s .o .asm .bmp .pal .spr 

These two definitions are specific for the NMake tool. .SOURCE is not used right now, but indicates subdirectories where source code might be located. .SUFFIXES lists the extensions of files that while be recognized. As you can see from the list we have indicated that we know about C files, assembler files with .s and .asm extensions and bitmap files.

CO=co65
CC=cc65
AS=ca65
AR=ar65
CL=cl65
SPRPCK=sprpck
CP=copy
RM=rm
ECHO=echo
TOUCH=touch 

Next are shorthand names for the various tools in the compiler suite and the Unix tools folder we will or might be using. The first five are the compilers and linker. SPRPCK is the sprite utility from the BLL toolkit (it is not a part of the CC65 suite). copy is the DOS command for copying files. De tools rm, echo and touch are Unix tools for Windows that remove files, echo output to a text file and touch a file so the timestamp is bumped to the current time.

CODE_SEGMENT=CODE
DATA_SEGMENT=DATA
RODATA_SEGMENT=RODATA
BSS_SEGMENT=BSS
SEGMENTS=--code-name $(CODE_SEGMENT) \
	--rodata-name $(RODATA_SEGMENT) \
	--bss-name $(BSS_SEGMENT) \
	--data-name $(DATA_SEGMENT)

The segment definitions are needed for the compiler, assembler and linker to know about the special segments in the binary. These definitions are the default names for the segments. In a simple compilation such as the one the defaults will be enough. For more complex games structure (e.g. with a file structure inside the binary like the original cartridges had) separate segment definitions will be necessary. In those cases the default can be overridden.

The SEGMENTS definition is a convenience label for the compiler that holds all four individual segments that the compiler knows about (code, read-only data, bss and data).

# Flag for assembler
AFLAGS=   # Flags for C-code compiler
CFLAGS=-I . -t $(SYS) --add-source -O -Or -Cl -Os 

The two flag definitions are made to capture all assembler and compiler switches that are used. It keeps them separate from the actual rules we will see later. This way you can easily see and change the flags.

There are no assembler flags for now. The compiler flags are:

  • -I .
    The include directory for header files is the current directory (where the mak file is located)
  • -t $(SYS)
    The target for the compiler is the system defined in SYS, which is “lynx” in our case.
  • –add-source
    Include the source code as comment inside of the generated assembler files. This will help understand what the original C source code was for the assembler instructions that were generated by the compiler.
  • -O –Or –Os
    Switches for optimization. In particular and in order: optimize the code, enable register variables and inline some known functions.
  • -Cl
    Make local variables static

The next section defines the various rules that are used by the NMake tool to figure out how to build the desired output files. We want to object files from assembler code files with the assembler AS65. Sometimes the assembler code is already available in a file, e.g. when the code cannot be written in C.

# Rule for making a *.o file out of a *.s file
.s.o:
  $(AS) -t $(SYS) -I $(CC65_ASMINC) -o $@ $(AFLAGS) $< 

This rule defines how a .o file can be created from a .s file. You need the AS65 assembler targeting SYS (our lynx) with an include folder set to the assembler include location specified at the beginning. Then comes the interesting part: the –o switch indicates the output file name. It is set to $@ which is short for the target of the rule. For example, if this rule is applied to a file interrupt.s, $@ will be equal to interrupt.o, because the rule specifies how a interrupt.o is created from interrupt.s. The assembler flags are also included. Finally the input (or source) file is provided by $<, a notation for the input file of this rule. Sticking to the previous example, this would be interrupt.s.

The rule will give this result for a interrupt.s file:

ca65 -t lynx -I "C:\Program Files (x86)\CC65"\asminc 
-o interrupt.o interrupt.s

However, most of the times we have c source code that needs the compiler CC65 to generate the assembler code first, after which the assembler code can be assembled.

# Rule for making a *.o file out of a *.c file
.c.o:
	$(CC) $(SEGMENTS) $(CFLAGS) $<
	$(AS) -o $@ $(AFLAGS) $(*).s 

This rule shows a two step process:

  1. Run the CC65 compiler
  2. Use the output and run it through the assembler.

The compiler is run with the segments and flags we saw earlier. The compiler takes the original source file as input. As another example, let’s assume this is tutorial.c.

The output of the rule is tutorial.o, which is what the assembler uses as –o output switch with $@. The assembler flags are applied  and the input in this case is the file that was generated by the compiler. This would be tutorial.s. But, there is no symbol that has this intermediate file name. That means we need to construct is ourselves from the input file without file extension with shorthand $(*).

The final result of this rule applied to a tutorial.c file will be this:

cc65 --code-name CODE --rodata-name RODATA 
--bss-name BSS --data-name DATA -I . -t lynx
--add-source -O -Or -Cl -Os tutorial.c ca65 -o tutorial.o  tutorial.s

Last but not least there are three additional rules that create two specific files for the TGI graphics library and joystick support: lynx-160-102-16.o and lynx-stdjoy.o respectively. We will go into details on those rules in a future part of the tutorial.

A game specific make file

The tutorial.mak file has the game specific entries. Edit the file and include the following:

!INCLUDE <lynxcc65.mak>
 
target = tutorial-part4.lnx
objects = lynx-160-102-16.o lynx-stdjoy.o tutorial.o
 
$(target) : $(objects)
	$(CL) -t $(SYS) -o $@ $(objects) lynx.lib
	$(CP) $@ .\$(BUILD)\$@
 
all: $(target)
	
clean:
	$(RM) -f *.tgi
	$(RM) -f *.s
	$(RM) -f *.joy
	$(RM) -f *.o
	$(RM) -f *.lnx

The very first line includes the generic make file we created earlier. This means we can use all of the definitions and rules we declared in there.

The target and objects variables are for the output of the project and the object files that are required to build it. The target is tutorial.lnx ROM image file. The objects for the image are tutorial.o and two library files for the TGI graphics library and joystick.

There is one important rule in the tutorial.mak file. It is how to create the target. As you can see it defines that in order to build the target it requires the objects from above. The target is built in one actual step. It runs the linker CL65 linker tool from the CC65 suite to link all of the objects together with the lynx.lib static library.

After the target as been created we perform an additional step that is not strictly necessary: we copy the target to the build folder for our current configuration. Because we did not change this, the configuration will be Debug.

image

The project configuration allows you to create separate builds for Debug and Release (or other configurations that you might define, such as “Emulator” or “Hardware”). The configuration is available as a variable in the NMake tool.

Finally, there are two make targets defined: all and clean. The first builds all files, and the latter cleans the intermediate files. The intermediate files are not part of the project itself. Whenever you want to get rid of these, simply right-click on the project and select Clean or choose Clean from the Build menu.

Clicking the Show All Files button at the top of the Solution Explorer reveals the contents of the project and Debug folder:

image

Check the original commands again that you entered when you created the project. You will see that the targets all and clean are connected to the respective commands of Build and Rebuild (for the all target) and Clean.

It’s recommended that you right-click the tutorial-part4.lnx file and choose Include In Project from the context menu. It will include the LNX file to the project. If you use a source code repository it will also get checked in. But, equally important, it will allow you to quickly run your game ROM by double clicking the LNX file. This does require an association of .lnx files with Handy to work correctly. You can create this by double-clicking the LNX file from Windows Explorer and choose Handy as the default program.

Almost there

Before you can give the project a spin we need just a single extra file: tutorial.c. Create this file in the project and copy this into it:

#include <6502.h>
#include <lynx.h>
#include <tgi.h>
 
extern char lynxtgi[];
 
void show_screen()
{
  tgi_clear();
  tgi_setcolor(COLOR_WHITE);
  tgi_outtextxy(30, 48, "Hello, World!");
  tgi_updatedisplay();
}
 
void initialize()
{
  tgi_install(&lynxtgi);
  tgi_init();
  CLI();
  while (tgi_busy());
  tgi_setbgcolor(COLOR_BLACK);  tgi_setpalette(tgi_getdefpalette());
  tgi_clear();
}
 
void main(void)  {	
  initialize();
  while (1)
  {
    if (!tgi_busy())
    {
      show_screen();
    }
  };
}

You may recognize this as the (minified) source code for the Hello World sample from part 3. When you build the project by pressing Ctrl+Shift+B or from the Build menu. The output in the Output window should resemble the one below.

image

Building the project (again)

You can build the project again and you should see simpler output, when the target of the project (tutorial.lnx) was created successfully on a previous build. The NMake tool will check dependencies that may have changed in the meantime.

image

Try editing the tutorial.c file with a dummy edit (such a a newline in an insignificant place) and build again.

Try cleaning the project as well.

image

More information can be found at the MSDN website.

Next time

In the next part we will take a closer look at how the various files, such as C source files, assembler .s files, we have talked about are related and how they are part of the build process to create the final .lnx rom image. Stay tuned.

This entry was posted in Tutorial. Bookmark the permalink.

11 Responses to Programming tutorial: Part 4–Creating a project

  1. Pingback: Programming tutorial: Part 5–Exploring TGI | Diary of an Atari Lynx developer

  2. Pingback: Programming tutorial: Part 6–Colors | Diary of an Atari Lynx developer

  3. Thank you so much for this guide. It got me up and running with CC65 in Visual Studio 11.
    One note: I had to put the arg -f behind the $(RM) to make clean work.

    clean:
    $(RM) -f *.o $(RM) -f *.xex

  4. alexthissen says:

    Hi Richard,
    It certainly took me a long while to read your comment and reply. Sorry about that.
    Thanks for the comment. I will have a look at the -f and will change the post accordingly.
    I plan to write some more parts pretty soon. I have gotten a lot of other work out of the way. Keep posting your feedback.

  5. Pingback: Programming tutorial: Part 7–The basics of sprites | Diary of an Atari Lynx developer

  6. Pingback: Programming tutorial: Part 8–Changing appearances | Diary of an Atari Lynx developer

  7. Pingback: Programming tutorial: Part 9–Advanced sprites | Diary of an Atari Lynx developer

  8. Pingback: Programming tutorial: Part 10–Collisions | Diary of an Atari Lynx developer

  9. Pingback: Programming tutorial: Part 11–Pens and more collisions | Diary of an Atari Lynx developer

  10. nitrofurano says:

    i’m a Gedit and command-line user on Linux, and i really don’t enjoy IDEs at all! what can you recommend instead? thanks!

  11. Johney says:

    I am not a programmer and if i want to be an programmer that will take a dekage of 2 before i finaly know how to program a system, but even then it will take me another 20 years to make/port a game to this system.
    Regardless of the capability’s of this amezing system, i was thinking about a yoshi’s island port ,since it become famouse for it’s sprite scaling & some polygons etc,,,,the lynx becomes an ideal system for this game since it can do sprite scaling & polygons etc,,,

Leave a reply to nitrofurano Cancel reply