Jump to content
IGNORED

Atari Lynx programming tutorial


LX.NET

Recommended Posts

Nice. For sprites there is a replacement for sprpck called sp65. It has only built-in support for pcx format files as this format required no extra libraries.

 

You can read in bmp files in Gimp and save them as pcx.

 

The syntax of sp65 has 3 parts:

 

sp65 input format output

 

example:

# Rule for making a *.o file out of a *.bmp file
%.o : %.pcx
        $(SP) -r $< -c lynx-sprite,mode=packed -w $*.c,ident=$*
        $(CC) $(CFLAGS) -o $*.s $*.c
        $(AS) -o $@ $(AFLAGS) $(*).s
        $(RM) $*.c
        $(RM) $*.s

or in clear text

sp65 -r sprite.pcx -c lynx-sprite,mode=packed -w sprite.c,ident=sprite

This would create a C-file and give the identifier "sprite" to the bitmap.

 

You can also define anchor points

-c lynx-sprite,mode=packed,ax=10,ay=10

Or even use only a small area of the bitmap.

--slice x,y,w,h

The sp65 also supports a new sprite mode "shaped" in addition to "literal" and "packed". WIth the "shaped" you can use all 16 colours for the sprite and its edges to left and right can be cut with an edge colour.

 

And for converting graphics to the "standard" colours the easiest thing is to do it in Gimp. A Gimp Lynx palette is included in the post here

 

My basic workflow is to

- read in a sprite

- image->rgb conversion

- open Lynx palette, open Colors window

- edit image until everything is in Lynx colors

- image->indexed, choose custom Lynx palette

- save as pcx

Edited by karri
Link to comment
Share on other sites

After few hours of testing, I've a problem with the sprite.

Everything seems fine expect I have an error with segment. I add a sprite (robot.pcx) to intro directory and of course didn't change anything else of the pong3 code.

 

I modify the makefile and the objlist for adding robot.o, and launch a make. I have an error with the segment INTRO_DATA

make[2]: entrant dans le répertoire « /home/sam/lynx/pong3/intro »
cc65 --code-name INTRO_CODE \
	--rodata-name INTRO_RODATA \
	--bss-name INTRO_BSS \
	--data-name INTRO_DATA \
	-I . -t lynx --add-source -O -Or -Cl -Os -o  intro.s intro.c
ca65 -o intro.o  intro.s
rm -f intro.s
sp65 -r robot.pcx -c lynx-sprite,mode=packed,ax=0,ay=0 -w robot.c,ident=robot
cc65 --code-name INTRO_CODE \
	--rodata-name INTRO_RODATA \
	--bss-name INTRO_BSS \
	--data-name INTRO_DATA \
	-I . -t lynx --add-source -O -Or -Cl -Os -o robot.s robot.c
ca65 -o robot.o  robot.s

cl65 -t lynx -C lynx-coll.cfg -o pong.lnx -m pong.map exehdr.o directory.o @../intro/objlist @../music/objlist @../play/objlist @../main/objlist lynx.lib
ld65: Error: Missing memory area assignment for segment `INTRO_DATA'
make[2]: *** [pong.lnx] Erreur 1

But, I verify in my file robot.s, i write into the INTRO_RODATA

	.fopt		compiler,"cc65 v 2.13.9"
	.setcpu		"65C02"
	.smart		on
	.autoimport	on
	.case		on
	.debuginfo	off
	.importzp	sp, sreg, regsave, regbank
	.importzp	tmp1, tmp2, tmp3, tmp4, ptr1, ptr2, ptr3, ptr4
	.macpack	longbranch
	.export		_robot

.segment	"INTRO_RODATA"

_robot:
	.byte	$05
	.byte	$08
...
...

So I check the value in the lynx-coll.cfg (remove the #)

INTRO_DATA:     load = INTRO,    type = rw,  define = yes;

The compiler give me a good lnx file without error but I can't see my sprite on intro screen:

#include <lynx.h>
#include <tgi.h>
#include <6502.h> 
#include <joystick.h>
#include <stdlib.h>

extern void tgi_arialxy(int x, int y, char *msg);

extern char robot[];

SCB_REHV_PAL sprrobot = 
{
  BPP_4 | TYPE_NORMAL|PACKED,	
  REHV, 0x01,
  0,
  robot,
  20, 30, 0x0200, 0x0200,
  { 0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF }
};


void intro()
{
    while (tgi_busy()) {
    }
    tgi_clear();

    tgi_arialxy(7, 30, "  Welcome to a nice game");
    tgi_arialxy(7, 42, "        of Rygar!");
    tgi_arialxy(7, 66, "  Written by Karri in 2013.");

	tgi_sprite(&sprrobot);


    tgi_updatedisplay();
    while (!joy_read(JOY_1))
        ;
}
Link to comment
Share on other sites

If you add a new segment like INTRO_DATA (it contains initialisation values for variables at startup) you must also remember to add the bytes to the cart.

 

In this case adding sprrobot caused the values we need to copy into sprrobot structure to require a DATA segment. You could also just have defined a structure and copy in the code by assignments. Then the code would go into CODE.

 

So in your cart/directory.s

.import         __INTRO_CODE_SIZE__,__INTRO_DATA_SIZE__,__INTRO_RODATA_SIZE__

and later when you write the directory entry on cart

len3=__INTRO_CODE_SIZE__+__INTRO_DATA_SIZE__+__INTRO_RODATA_SIZE__

So, the things you need to store in the cart are CODE, DATA, RODATA. The segment BSS is just empty space (heap) needed at runtime.

Edited by karri
Link to comment
Share on other sites

Thanks again, it's working now. I see my robot on screen. :-D

Although I do not understand why he wants me to add Intro_Data as my image segment is in intro_RoData.

 

 

But anyway, I look Inside the the file .cfg and the two segments : pong_start and intro_star begins both at 0x200. So when I load Pong, I write on top of the intro data.

 

I have test with loading a background. I take the bg.pcx file from the sample pegsolitaire. I verify the size after compilation and it's the same as pegsolitaire

bg.o:
    INTRO_RODATA      Offs=000076  Size=0014A0  Align=00001  Fill=0000

But when I compil, I have this error message :

sp65 -r bg.pcx -c lynx-sprite,mode=packed,ax=0,ay=0 -w bg.c,ident=bg
cc65 --code-name INTRO_CODE \
	--rodata-name INTRO_RODATA \
	--bss-name INTRO_BSS \
	--data-name INTRO_DATA \
	-I . -t lynx --add-source -O -Or -Cl -Os -o bg.s bg.c
ca65 -o bg.o  bg.s
#rm -f bg.c
#rm -f bg.s
make[1]: quittant le répertoire « /home/sam/lynx/pong3/intro »

...
...

cl65 -t lynx -C lynx-coll.cfg -o pong.lnx -m pong.map exehdr.o directory.o @../intro/objlist @../music/objlist @../play/objlist @../main/objlist lynx.lib
ld65: Warning: lynx-coll.cfg(24): Memory area overflow in `INTRO', segment `INTRO_RODATA' (1927 bytes)
ld65: Error: Cannot generate output due to memory area overflow
make[1]: *** [pong.lnx] Erreur 1
make[1]: quittant le répertoire « /home/sam/lynx/pong3/cart »
make: *** [all] Erreur 2

One day, I'll understand how the segments works. :_(

Link to comment
Share on other sites

You also have to allocate 1927 more bytes to the INTROSIZE. Perhaps you should move the music higher. At least POPCORNSTART needs to be 1927 bytes later in the build to make more space for your code.

SYMBOLS {
    __STACKSIZE__: type = weak, value = $0800; # 2k stack
    __STARTOFDIRECTORY__: type = weak, value = $00CB; # start just after loader
    __BLOCKSIZE__: type = weak, value = 1024; # cart block size
    __BOOTLDR__:   type = import;
    __MEMEND__: type = weak, value = $9E58 - __STACKSIZE__;
    __PONGSTART__: type = weak, value = $0200;
    __INTROSTART__: type = weak, value = $0200;
    __POPCORNSTART__: type = weak, value = $1000;
    __RAMSTART__: type = weak, value = $5000;
    __PONGSIZE__: type = weak, value = __POPCORNSTART__ - __PONGSTART__;
    __INTROSIZE__: type = weak, value = __POPCORNSTART__ - __INTROSTART__;
    __POPCORNSIZE__: type = weak, value = __RAMSTART__ - __POPCORNSTART__;
    __RAMSIZE__: type = weak, value = __MEMEND__ - __RAMSTART__;
}

You could try with

__POPCORNSTART__: type = weak, value = $1000 + 1927;

The file here is cart/lynx-coll.cfg. Tweaking segment sizes is a pain. But it is hard to fit everything into 64k. The collision detection used a whole screen buffer. So in this build there is 2 screen buffers for double buffered display and an extra collision screen. Most people just do not use collision detection because of this.

Edited by karri
Link to comment
Share on other sites

I found out it's easier than it looks (mind you it's really picky with the syntax), Karri already did all the difficult stuff for us.

 

The short answer is that for every segment you create (say you copy the play folder and edit it to something else of your own), you just imitate/copy all the stuff you see for one segment in all the files and edit it to fit your new segment. So you add all your objects to all the make files and config files and add the new stuff to directory.s. Then you can also chek how much space your stuff takes in the map file. Also I think every segment always has all four data types INTRO_CODE \ INTRO_RODATA \ INTRO_BSS \ INTRO_DATA

 

Really good info here, but you've probably seen this already: https://atarilynxdeveloper.wordpress.com/2014/09/10/programming-tutorial-part-18files/

 

P.S. Damn that popcorn version in pong is good! :thumbsup:

Edited by Turbo Laser Lynx
Link to comment
Share on other sites

pong.zip

I added my code source if needed.

 

I'm a big loser. I read very seriously the tutorial 18 but I still have a problem.

I started doing what you told me : segment by segment and I test only the introduction.

 

I remove all stuff I don't need for intro, only left cart, intro and main.

As you can see on picture. The top with the original pong3 code and the bottom with my code and some garbage. Same data/picture

 

screen12.png

 

I changed directory.s and lynx_coll.cfg. I only left what I need.

 


.include "lynx.inc"
.import __STARTOFDIRECTORY__
.import __RAM_START__
.import __CODE_SIZE__,__DATA_SIZE__,__RODATA_SIZE__
.import __STARTUP_SIZE__,__INIT_SIZE__
.import __BLOCKSIZE__
.import __INTRO_CODE_LOAD__
.import __INTRO_CODE_SIZE__,__INTRO_DATA_SIZE__,__INTRO_RODATA_SIZE__

; ------------------------------------------------------------------------
; Lynx directory
.segment "DIRECTORY"

__DIRECTORY_START__:
off0=__STARTOFDIRECTORY__+(__DIRECTORY_END__-__DIRECTORY_START__)

; Entry 0 - first executable
block0=off0/__BLOCKSIZE__
len0=__STARTUP_SIZE__+__INIT_SIZE__+__CODE_SIZE__+__DATA_SIZE__+__RODATA_SIZE__
.byte <block0
.word off0 & (__BLOCKSIZE__ - 1)
.byte $88
.word __RAM_START__
.word len0
off1=off0+len0

; Entry 1 - intro
block1=off1/__BLOCKSIZE__
len1=__INTRO_CODE_SIZE__+__INTRO_DATA_SIZE__+__INTRO_RODATA_SIZE__
.byte <block1
.word off1 & (__BLOCKSIZE__ - 1)
.byte $88
.word __INTRO_CODE_LOAD__
.word len1
;off2=off1+len1

__DIRECTORY_END__:

SYMBOLS {
    __STACKSIZE__: type = weak, value = $0800; # 2k stack
    __STARTOFDIRECTORY__: type = weak, value = $00CB; # start just after loader
    __BLOCKSIZE__: type = weak, value = 1024; # cart block size
    __BOOTLDR__:   type = import;
    __MEMEND__: type = weak, value = $9E58 - __STACKSIZE__;
    __INTROSTART__: type = weak, value = $0200;
    __RAMSTART__: type = weak, value = $5000;
    __INTROSIZE__: type = weak, value = __RAMSTART__-__INTROSTART__;
    __RAMSIZE__: type = weak, value = __MEMEND__ - __RAMSTART__;
}
MEMORY {
    ZP:     file = "", define = yes, start = $0000, size = $0100;
    HEADER: file = %O,               start = $0000, size = $0040;
    BOOT:   file = %O,               start = $0200, size = __STARTOFDIRECTORY__;
    DIR:    file = %O,               start = $0000, size = 4*8;
    RAM:    file = %O, define = yes, start = __RAMSTART__, size = __RAMSIZE__;
    INTRO:   file = %O, define = yes, start = __INTROSTART__, size = __INTROSIZE__;
}
SEGMENTS {
    EXEHDR:   load = HEADER, type = ro;
    BOOTLDR:  load = BOOT,   type = ro;
    DIRECTORY:load = DIR,    type = ro;
    STARTUP:  load = RAM,    type = ro,  define = yes;
    LOWCODE:  load = RAM,    type = ro,                optional = yes;
    INIT:     load = RAM,    type = ro,  define = yes, optional = yes;
    CODE:     load = RAM,    type = ro,  define = yes;
    RODATA:   load = RAM,    type = ro,  define = yes;
    DATA:     load = RAM,    type = rw,  define = yes;
    BSS:      load = RAM,    type = bss, define = yes;
    ZEROPAGE: load = ZP,     type = zp;
    EXTZP:    load = ZP,     type = zp,                optional = yes;
    APPZP:    load = ZP,     type = zp,                optional = yes;
    INTRO_CODE:     load = INTRO,    type = ro,  define = yes;
    INTRO_RODATA:   load = INTRO,    type = ro,  define = yes;
    INTRO_DATA:     load = INTRO,    type = rw,  define = yes;
    #INTRO_BSS:      load = INTRO,    type = bss, define = yes;
}

In the pong.map


Segment list:
-------------
Name                   Start     End    Size  Align
----------------------------------------------------
DIRECTORY             000000  00000F  000010  00001
EXEHDR                000000  00003F  000040  00001
ZEROPAGE              000000  000019  00001A  00001
EXTZP                 00001A  000032  000019  00001
BOOTLDR               000200  0002CA  0000CB  00001
INTRO_CODE            000200  000228  000029  00001
INTRO_RODATA          000229  000257  00002F  00001
INTRO_DATA            000258  00026E  000017  00001
STARTUP               005000  00507C  00007D  00001
INIT                  00507D  0050AB  00002F  00001
CODE                  0050AC  005DCA  000D1F  00001
RODATA                005DCB  005F02  000138  00001
DATA                  005F03  005FED  0000EB  00001
BSS                   005FEE  0060E3  0000F6  00001
Edited by tonma
Link to comment
Share on other sites

The error is in loading the segment.

 

You cannot load file number 3

#define MAIN_FILENR 0
#define INTRO_FILENR 3

lynx_load(INTRO_FILENR);

when you only define files 0 and 1,

 

There is no sanity check built in to the cart library. It happily loads in garbage from some random place of the cart and jumps there. = crash

 

Btw. It is only possible to create 4096 files on a Lynx cart. File numbers 0..4095. This is a hard limitation in the driver.

Edited by karri
Link to comment
Share on other sites

I also saw someone create macro for simplifying the creation of the directory.

.macro entry old_off, old_len, new_off, new_block, new_len, new_size, new_addr
new_off=old_off+old_len
new_block=new_off/__BLOCKSIZE__
new_len=new_size
    .byte    <new_block
    .word    new_off & (__BLOCKSIZE__ - 1)
    .byte    $88
    .word    new_addr
    .word    new_len
.endmacro

; The entry 0 is the main exe
entry __STARTOFDIRECTORY__,(__DIRECTORY_END__-__DIRECTORY_START__), off0, block0, len0,__STARTUP_SIZE__+__INIT_SIZE__+__CODE_SIZE__+__DATA_SIZE__+__RODATA_SIZE__,__RAM_START__

; The entry 1 is the entertainment module that we run at startup
entry off0, len0, off1, block1, len1,__INTRO_CODE_SIZE__+__INTRO_RODATA_SIZE__+__INTRO_DATA_SIZE__, __INTRO_CODE_LOAD__

; The entry 2 is the 1st game level we want to run.
entry off1, len1, off2, block2, len2, __PEGSOLITAIRE_CODE_SIZE__+__PEGSOLITAIRE_RODATA_SIZE__+__PEGSOLITAIRE_DATA_SIZE__, __PEGSOLITAIRE_CODE_LOAD__

; The entry 3 should come here. It could be the 2nd level to run or data...

__DIRECTORY_END__:

This makes it a bit easier to keep up with new files in the directory.

Link to comment
Share on other sites

The error is in loading the segment.

 

You cannot load file number 3

#define MAIN_FILENR 0
#define INTRO_FILENR 3

lynx_load(INTRO_FILENR);

when you only define files 0 and 1,

 

There is no sanity check built in to the cart library. It happily loads in garbage from some random place of the cart and jumps there. = crash

 

Btw. It is only possible to create 4096 files on a Lynx cart. File numbers 0..4095. This is a hard limitation in the driver.

 

Thanks a lot. 4096 is not so bad. :-D

Link to comment
Share on other sites

I did tests this week-end on collisions, and after problems with "pens" that changed the colors of the sprites, it works.

 

lynx0010.png

 

On the other hand, I have a tearing effect when I move the sprites.

Can someone test my rom, I feel it must come from the emulator ? (or from my eyes maybe). Because I always have 60 fps and the VBL is on by default in the code.

 

For moving sprites, it's only integer : one by one pixels, or two pixels, etc ... ?

 

lynx001.zip

Link to comment
Share on other sites

  • 1 month later...

I'm quite late to this topic and hoping this is in the right place. I'm attempting to write Atari Lynx games but am having problems starting. First off, my coding experience is limited to the likes of visual basic, apple basic,st basic, ect. So as of now I'm working with some C code and would like to know the basics as looking at "the diary of a lynx developer" left me with too many questions. Whats the best text editor for writing lynx programs? Whats a good compiler? After attempting to go through the "hello world" example I couldn't make it work let alone convert it to a proper lnx file and run it on Handy. So I ask the wise Lynx programmers here to please give me guidance on this issue. Maybe soon I can share some simple hombrew with the AA community.

Link to comment
Share on other sites

Hello! Tutorial is simply amazing, so i give it a try. For a while i wanted to try out creating a game, this helped me chose Lynx system (and i registered on AtariAge forums :)) Got some experience in coding and a lot experience in graphics. Thanks again for your hard work!

Link to comment
Share on other sites

The compiler code has advanced a little since the last Windows version. I wonder if it would make sense to maintain a set of download binaries at bitbucket.org/atarilynx ?

 

Right now setting up the tools is simple for any Linux system or Raspberry Pi. But Windows may still be a problem...

Link to comment
Share on other sites

I manged to set up development environment for Windows :) Already got to part 8. Managed to understand how colors work on sprite, now trying to fix some bugs on edges which appear on every bitmap i try to attach to project (Left side of picture moves to right side)

gallery_49654_1819_72411.jpg
I'm really excited about this. Hopefully can learn a bit and join some bigger project in future. For now on i will try some basics out there - writing novel game (with pictures) on Lynx. btw. how about sounds in Lynx? is it hard to made something for it?
Edited by hikachi
  • Like 1
Link to comment
Share on other sites

I would recommend using chipper for sounds. It is a bit hard to get started with it though.

 

The idea is to first load an instrument (string, hi-hat, beep)

 

Then add a track (0 1 2 3).

 

You can then fill in the notes using your keyboard like a piano. (Z = lower "C", S = "C#", X = "D" etc.)

 

Once you like the playback you can export the tune for the new CC65 compiler.

 

The Lynx has 4 simultaneous sound channels. These sound channels operate in parallell with the CPU. So sound created with chipper is cheap.

 

It may be a good idea to save one sound channel for game sounds and use only 3 channels for background music.

Link to comment
Share on other sites

@Vidiot1981

 

As with most programming projects it seems setting things up is always harder than the actual coding! The tools you can use have always been somewhat scattered on the Lynx, we started compiling a list earlier this fall:

http://atariage.com/forums/topic/256753-atari-lynx-programming-tools-and-tutorials-wip/

 

It seems that at the moment people using 'CC65' (third in the toolchain list) are most active on this forum, but people have different setups for it, and all three toolchains are indeed alive. For example personally I have a silly setup so that I code in notepad++ do graphics in gimp and music in chipper, all on windows but then I transfer the files to my raspberry pi and compile it there, because I couldn't get cc65 set up on windows either.

 

LX tutorial is quite technical if you're more of a tinkerer like me but it contains very much essential information. I've always hacked game-examples that are available where the difficult stuff is already coded and you can just make the whole structure fit your own game. There's some nice game-templates that you could start out with, 'shaken', 'peg-solitaire' and 'pong'.

 

C basics are as easy as any programming language. You wouldn't have to master it unless your convinced that you want to do everything from scratch.

  • Like 1
Link to comment
Share on other sites

Thank you for the information.I completely agree that getting all the pieces together has so far been the most difficult part. When I follow the link one of the first tools is BLL but the link to download it doesn't go anywhere. It would be fantastic if everything was located in one spot with updated links. Does anyone currently have an Atari Lynx programming environment set up on a Windows 7 64 bit computer? And if so perhaps lend some pointers on how to get up and running on one?

Link to comment
Share on other sites

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.

Guest
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.

Loading...
  • Recently Browsing   0 members

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