Jump to content

Atari Lynx programming tutorial


Recommended Posts

Hello Everyone,

I took the liberty of opening a new topic on the tutorial series. The main reason (besides my big ego) is that the notifications on new parts and potential discussion is now scattered throughout the Atari Lynx and Programming forum and multiple topics. Also, it gives a single, easy to find location for the source code that goes with the tutorials.

So, for your convenience, here is the list of tutorial parts:

Let me know if you find things unclear, wrong, have suggestions for topics, see room for improvement or anything else.
I hope you will find it useful and take up the programming challenge. You can take my word for it, or that of Karri, ninjabba, Matashen, sage, GadgetUK, vince, obschan, TailChao, Sebastian, Wookie, Shawn Jefferson, toyamigo, or any of the other developers: it is a lot of fun.


I've added the sources, tools and documentation for the CC65 2.13.9 SVN 5944 which is a known stable build. Remove the .txt extension for the sources archive.






Edited by LX.NET
  • Like 7
Link to comment
Share on other sites

At a cursory glance this looks like a wonderful bunch of tutorials covering C, cc65 and Lynx development all in one. I can't see any reason anyone wouldn't want to start here for console development!


I especially appreciate having a section devoted to setting up your development environment. For those used to higher level languages with integrated IDEs this seems to be a big hurdle for beginners.

  • Like 1
Link to comment
Share on other sites

Makefiles can also be used like scripts. Remember to use tab, not spaces.


    $(MAKE) sprites
    $(MAKE) sounds
    $(MAKE) game

    sprpck ...

    whatever ....

    cl65 -t lynx -o game.lnx ....


To build just the sprites type "make sprites"

To build it all type "make"

  • Like 1
Link to comment
Share on other sites

Some examples which I use:

(as always: You aren't permitted to upload this kind of file)


# makefile to create a complete Atari Lynx cart using the
# the BLL kit
# for savety
SHELL = /bin/sh
    $(error PROJECT_NAME not set!)
    $(error LYNX_BASEDIR not set!)
ifndef LYNX_BIN
    $(error LYNX_BIN not set!)
export BLL_ROOT
# misc binaries
RM=rm -f
# lynx binaries
# (lyxass, lynxer, ...)
# lynx binaries																																							   
# (newcc, ...)																																								
# Libraries and programm parameters
export CC65INCLUDE
export CC65LIB
RUNTIME=-r runtime.run
BMPFLAGS= -p1 -t6
.SUFFIXES: .o .c .cc .obj .m56 .asm .lyx .lnx
.SECONDARY: .o .lyx .obj
.PHONY: all install clean echo
#		  And now the rules...		  #
# start pic insert.o needs special treatment
#insert.o : insert.asm
#	   $(LYXASS) -v -d -o insert.o $<
#  Normal assembler file
%.o : %.asm
    echo "BLL_ROOT $(BLL_ROOT)"
    $(LYXASS) -v $<
# quiet useless
%.spr : %.bmp
    $(SPRPCK) $(BMPFLAGS) $<
%.m65 : %.c
    $(NEWCC) $(CFLAGS) $<
%.m65 : %.cc
    $(NEWCC) $(CFLAGS) $<
%.obj : %.m65
    $(XOPT) -v $<
    $(RASM) -v $<
%.puc : %.o
    $(PUCRUNCH) -c0 $< $@
%.puc : %.bin
    $(PUCRUNCH) -d -c0 $< $@
# simple lynx game, single file unpacked, lynxer-ng is used
#%.lnx : %.o
#	   $(LYNXER) -v -l $<
%.lnx : %.o
    $(LYNXDIR) $<
# convert lyx to lnx, simple 256*1024b
#%.lnx : %.lyx
#	   $(MAKE_LNX) $< -b0 256K
# well here the mak file content must go
#%.lnx : %.mak %.o
#	   $(MAKE_LNX) -l $<
#%.lyx : %.mak %.o
#	   $(MAKE_LNX) $<
# simple lynx game, single file unpacked, lynxer/-ng is used
# if lyx is really needed
#%.lyx : %.o
#	   $(LYNXER) -v $<
%.lyx : %.o
    $(LYNXDIR) $<
# now this depends on what your want as result(s).
ifeq ((suffix $(PROJECT_NAME)),)
all:    $(PROJECT_NAME) ;
    -$(RM) *.o
    -$(RM) *.m65
    -$(RM) *.obj
    -$(RM) *.lyx
    -$(RM) *.lnx
    @echo "Listing envs:"
    @echo "BLL_ROOT=$(BLL_ROOT)"
    @echo "LYXASS=$(LYXASS)"
    @echo "LYNXER=$(LYNXER)"
    @echo "LYNXDIR=$(LYNXDIR)"
    @echo "SPRPCK=$(SPRPCK)"
    @echo "MAKE_LNX=$(MAKE_LNX)"
    @echo "NEWCC=$(NEWCC)"
    @echo "XOPT=$(XOPT)"
    @echo "RASM=$(RASM)"
    @echo "LIBR65=$(LIBR65)"
    @echo "LINK65=$(LINK65)"
    @echo "BIN2OBJ=$(BIN2OBJ)"
    @echo "BIN2INC=$(BIN2INC)"
    @echo "... thats all!"



include $(LYNX_BASEDIR)/Makefile.base
include convspr1.make
include convspr2.make
include convspr3.make
LINKOPT= -r ./runtime.run
%.obj : %.m65
    $(XOPT) $<
    $(RASM) $<
soundbs.obj : soundbs.c macros.h
#	   $(RM) kisteexp.lyx
#	   $(RM) kisteexp.lnx
STUFF=file_bs3.obj lynx_puc.obj soundbs.obj
optspr.olb: option_ok.bmp
    $(SPRPCK) -i160102 -S006006 -t6 -p0 option_ok.bmp ok.obj
    $(SPRPCK) -i160102 -S006006 -o006000 -t6 -p0  option_ok.bmp no.obj
    $(SPRPCK) -i160102 -S008008 -o012000 -t6 -p0 option_ok.bmp sel.obj
    $(LIBR65) a optspr.olb ok.obj no.obj sel.obj
stuff.olb: $(STUFF)
    $(LIBR65) a stuff.olb $(STUFF)
### explode1.obj
#kiste.c: kiste53.c filenr.h
#	   $(CP) -v $< $@
scanib.c: scanib03.c filenr.h
    $(CP) -v $< $@
kmenu.c: kmenu04.c filenr.h
    $(CP) -v $< $@
kgame.c: kgame05.c filenr.h
    $(CP) -v $< $@
#kiste.obj: kiste.c
#	   $(RM) kiste.m65
#	   $(NEWCC65) kiste.c
#	   $(XOPT) kiste.m65
#	   $(RM) kiste.obj
#	   $(RA65) kiste.m65
kgame.o: stuff.olb kgame.obj  loclib/lynx.olb  loclib/c.olb loclib/runtime.run
    $(LINK65) -v -s1300 -r loclib/runtime.run -o kgame.o stuff.olb kgame.obj loclib/lynx.olb
kmenu.o: stuff.olb kmenu.obj  loclib/lynx.olb  loclib/c.olb optspr.olb loclib/runtime.run
    $(LINK65) -v -s1300 -r loclib/runtime.run -o kmenu.o stuff.olb kmenu.obj loclib/lynx.olb
scanib.o: stuff.olb scanib.obj  loclib/lynx.olb  loclib/c.olb loclib/runtime.run
    $(LINK65) -v -s1300 -r loclib/runtime.run -o scanib.o stuff.olb scanib.obj loclib/lynx.olb
## loclib/c.olb
#### war 1300
# BLL serial loader screen
bllload.o : bllload.asm
    $(LYXASS) -v $<
#  Intro
kontis.bin: kon_eu.spr kon_as.spr kon_na.spr kon_af.spr kon_au.spr kon_sa.spr kon_ar.spr kon_an.spr 
    cat $^ >> $@
menu.bin: mw.p16 mhbl.pal mw.spr
    cat $^ >> $@
score.bin: flash/score18.p16 flash/score18.spr
    cat $^ >> $@
option.bin: flash/option.p16 flash/option.spr flash/opt_con.spr flash/opt_res.spr flash/opt_men.spr
    cat $^ >> $@
    sprpck -t2 -i160102 optneu.sps optionneu1.spr
    sprpck -u -t2 -i160102 optneu.sps optionneu2.spr
optionneu1.dtt: optionneu1.p16 optionneu1.spr
    cat $^ >> $@
optionneu2.dtt: optionneu1.p16 optionneu2.spr
    cat $^ >> $@
intro.o : intro.asm
    $(LYXASS) -v $<
intro.asm : intro13.asm
    $(CP) -v $< $@
#  Boot Logo
logo3.spr: logo3.bmp
    $(SPRPCK) -v -s4 -t6 logo3.bmp
insert.o: insert.asm logo3.spr
# Now the ROM image
%.puc : %.dtt
    $(PUCRUNCH) -d -c0 $< $@
#%.spp : %.spr
#	   $(PUCRUNCH) -d -c0 $< $@
#ANIMS = ani_p/scani_01.spp ani_p/scani_02.spp ani_p/scani_03.spp ani_p/scani_04.spp ani_p/scani_05.spp ani_p/scani_06.spp ani_p/scani_07.spp \
#   ani_p/scani_08.spp  ani_p/scani_09.spp ani_p/scani_10.spp ani_p/scani_11.spp ani_p/scani_12.spp ani_p/scani_13.spp ani_p/scani_14.spp \
#   ani_p/scani_15.spp ani_p/scani_16.spp ani_p/scani_17.spp ani_p/scani_18.spp ani_p/scani_19.spp scani.dat
#scani.puc: $(ANIMS)
#	   cat $(ANIMS) > scani.puc
SETS = setp/set_0.puc setp/set_0s.puc setp/set_0m.puc setp/set_31.puc setp/set_31s.puc setp/set_31m.puc setp/set_32.puc setp/set_32s.puc setp/set_32m.puc \
 setp/set_33.puc setp/set_33s.puc setp/set_33m.puc setp/set_34.puc setp/set_34s.puc setp/set_34m.puc setp/set_12.puc setp/set_12s.puc setp/set_12m.puc \
 setp/set_13.puc setp/set_13s.puc setp/set_13m.puc setp/set_14.puc setp/set_14s.puc setp/set_14m.puc setp/set_15.puc setp/set_15s.puc setp/set_15m.puc \
 setp/set_16.puc setp/set_16s.puc setp/set_16m.puc setp/set_17.puc setp/set_17s.puc setp/set_17m.puc setp/set_18.puc setp/set_18s.puc setp/set_18m.puc \
 setp/set_19.puc setp/set_19s.puc setp/set_19m.puc setp/set_20.puc setp/set_20s.puc setp/set_20m.puc

LEVELS = level_p/00_null.puc level_p/05_five.puc level_p/16_sixteen.puc level_p/notused.puc \
 level_p/01_one.puc level_p/06_six.puc level_p/20_twenty.puc level_p/sym2_1.puc \
 level_p/02_two.puc level_p/07_seven.puc level_p/30_thirty.puc level_p/sym2_2.puc \
 level_p/03_three_1.puc level_p/08_eight.puc level_p/40_fourty.puc level_p/sym2_3.puc \
 level_p/03_three_2.puc level_p/09_nine.puc level_p/asym.puc level_p/sym4_1.puc \
 level_p/03_three_3.puc level_p/10_ten.puc level_p/bigger.puc level_p/sym4_2.puc \
 level_p/04_four.puc level_p/14_fourteen.puc level_p/doubles.puc
kisteexp.lnx: kisteexp.mak insert.o menu_lex.o intro.puc bllload.puc kmenu.puc kgame.puc \
		    optionneu1.puc optionneu2.puc option.puc menu.puc kontis.puc chip.puc scanib.puc score.puc scani.dat \
		    $(LEVELS) $(SETS) kgame.m65 kmenu.m65 neuebilder/rochen/rochen.p2dat
    lynxdir kisteexp.mak
pusharoundtheworld: kisteexp.lnx kisteexp.lyx
option_g3r.dat: option_g3r.bmp
    $(SPRPCK) -u -s4 -t6 option_g3r.bmp
    cat option_g3r.p16 option_g3r.spr > option_g3r.dat

Link to comment
Share on other sites

It is a fantastic tutorial, I am really fired up on how buzzing this community is at the moment and with all the stuff LX.NET and Karri have done recently regards tutorials and improving CC65 I can see lots of new games coming to the Lynx. Keep up the great work, these tutorials really should be in a book or something - the quality is excellent.

Link to comment
Share on other sites

Do you plan to explain how to develop (espacially configuring makefiles) without Visual Studio ?


I don't know what you use but I wouldnt mind getting Code Blocks working with CC65. For the moment I've got Visual Studio working and a tonne of other bits and pieces on the go but if someone doesnt get Code Blocks working I might take a look sometime in 2013. You can get the free version of Visual Studio I think so its not a none starter.

Link to comment
Share on other sites

I can see why you might not have access to Visual Studio if you are using an other os than Windows. For the doubters out there: I could do a video on VS. I'll also do a part on non-VS like asked by sage, but I am still uncertain of the priority. I'd rather get some other topics covered first, such as interrupts, timers, controls and sound.

Link to comment
Share on other sites

The sound tutorial is tricky. There are many candidates out there. A tutorial should consider a way to do simple sounds in a controlled way.


Something like


unsigned char pong_sound[] = {
 some bytes


On the other hand I would also like to have some intuitive way to output a note from C-code directly like:


unsigned char tune[] = {


Or something like this. You can of course use Chipper as a sequencer and edit the whole tune on a PC. But it would be nice to be able to create and output simple stuff manually.




Link to comment
Share on other sites

I can see why you might not have access to Visual Studio if you are using an other os than Windows. For the doubters out there: I could do a video on VS. I'll also do a part on non-VS like asked by sage, but I am still uncertain of the priority. I'd rather get some other topics covered first, such as interrupts, timers, controls and sound.


What about Eclipse? Wouldn't that cover a large amount of operating systems with one dev environment?

Link to comment
Share on other sites

Eclipse would be cool, I can use the Mac then =)


Edit: That said, with the instructions already provided for Visual Studio I should be able to setup Code Blocks or Eclipse. I would focus on the other things youve mentioned - interrupts and sound etc.

Edited by GadgetUK
Link to comment
Share on other sites


I'm looking for advice or perhaps something else. I'm new to Lynx development and Lynx in general. I once talked a bit to Sage and he encouraged me to use LyxAss rather than cc65 (especially that new cc65). I totally agreed then and agree now, that it's a pain in the ass that the new cc65 is incompatible with the old version - I mean the libraries and all that stuff required to do some serious coding. On the other hand, however, LyxAss is really poorly documented and some functions don't seem to work as expected (or maybe as I expect them to work). That slows down the coding and kills all the fun of it.

So, my question is: what would you say? I know that cc65 is the "gold standard" here, but what if you had to write down the pros and cons of both LyxAss and cc65?

Looking forward to a discussion :-)


Link to comment
Share on other sites

I do not get your point: the BLL kit contains quiet a lot of code-examples.

Like Bastians 3d qube, times examples. actually everything which pops up the ROM sites as lynx "homebrew" haha

lynx_asm/apfel_2a.asm lynx_asm/demo0006.asm lynx_asm/irq_test.asm

lynx_asm/boing.asm lynx_asm/disass.asm lynx_asm/play_smp.asm

lynx_asm/brk.asm lynx_asm/drawtest.asm lynx_asm/raw.asm

lynx_asm/check_ee.asm lynx_asm/fonttst2.asm

lynx_asm/cir_tunn.asm lynx_asm/i2c_test.asm

  • Like 1
Link to comment
Share on other sites

The biggest difference between lyxass and ca65 is the syntax of labels and keywords. I have been thinking to add an automatic converter to ca65 to understand lyxass sources. Then you could use ca65 for bll sources directly.


The good thing with cc65 is that you don't have to use the provided startup code. It can also use the techniques provided by bll.

Link to comment
Share on other sites

CC65 is great, you can start quickly without much Lynx knowledge and while you are progressing in understanding the hardware and the compiler you can check CC65 output and fine tune, correct what you think could be better done with inline asm or even directly ca65 assembly source.

And the project is alive, cc65 team constantly improves it, and karri continuously update the lynx library when new ideas, suggestions come.

Edited by obschan
  • Like 2
Link to comment
Share on other sites

  • 8 months later...

Hello everyone,


I cannot believe it has been more than half a year since the last episode.

Anyway, for your reading comfort and education: part 12 discusses the memory management of the Lynx.


Proofreading, corrections and suggestions are very welcome.




Edited by LX.NET
Link to comment
Share on other sites

That update with part 12 is awesome, I've just learnt a fair bit from that - probably obvious things to other people but I wasn't familiar with how the internal ROM was mapped and I never realised up until this point how to toggle the memory mapping in order to access RAM, Suzi or Mikie etc.


Great work =)

  • Like 1
Link to comment
Share on other sites

  • 3 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...