Jump to content
damosan

cc65: A simple PM and EOR example.

Recommended Posts

When I started with cc65 last year I spend a lot of time looking at the code others had written so I  hope this might be helpful to someone.  Somewhere.

 

My goal was to create a "game" that would EOR bitmaps and drive a few player/missiles and this does just that - using nothing but C.  I've tried to optimize a few things here and there - namely I pre-compute the shifted bitmap for the player's fighter.  It helped tremendously even though it was a bit tedious.  My graphics 8 stuff has a routine to shift the bitmaps at init-time.

 

So, the "game" - you're flying a fighter trying to shoot down two bad guys.  Once you kill them (in about 8 - 15 seconds) the game ends.  Simple.  :)  It can def. stand to be improved or cleaned up in spots but, hey, it's an example for someone to take, tweak, and improve.

 

Attached you'll find the source code and the linker config file.

gr7game2.c atari.cfg

gr7game2.xex

Edited by damosan
Added XEX
  • Like 4

Share this post


Link to post
Share on other sites
40 minutes ago, damosan said:

When I started with cc65 last year I spend a lot of time looking at the code others had written so I  hope this might be helpful to someone.  Somewhere.

 

My goal was to create a "game" that would EOR bitmaps and drive a few player/missiles and this does just that - using nothing but C.  I've tried to optimize a few things here and there - namely I pre-compute the shifted bitmap for the player's fighter.  It helped tremendously even though it was a bit tedious.  My graphics 8 stuff has a routine to shift the bitmaps at init-time.

 

So, the "game" - you're flying a fighter trying to shoot down two bad guys.  Once you kill them (in about 8 - 15 seconds) the game ends.  Simple.  :)  It can def. stand to be improved or cleaned up in spots but, hey, it's an example for someone to take, tweak, and improve.

 

Attached you'll find the source code and the linker config file.

gr7game2.c 18.65 kB · 2 downloads atari.cfg 2.68 kB · 1 download

Thanks for this!  I've never looked at any C code on this machine but so many people are doing stuff, I really need to check into it.  This should be a great example.

Share this post


Link to post
Share on other sites

Cool. It's worth noting that _atarios.h, which is referenced by atari.h, has all of the OS memory locations in a nice convenient structure.

 

Same for gtia.h, antic.h, and pokey.h. So you don't have to resort to a poke macro.

 

Also, as far as using memory for things like PMBASE etc, I typically pick an aligned area of memory and memcpy the data from a RODATA segment into the target area. 

 

As for modifying the linker files, seriously, whatever, sometimes you need to.

 

-Thom

Share this post


Link to post
Share on other sites

Hey,

as  @tschak909 mentioned, since you're including <atari.h> , you don't need all the defines below. for example, instead of defining "NMIEN" as you did:

Quote

#define NMIEN  *((byte *) 0xd40e)

you can use this:

Quote

ANTIC.nmien

just search the following files (they appear in the include directory of CC65 library) to see what can you use instead of these defines:

Quote

 

_atarios.h

_antic.h

_gtia.h

_pia.h

 

some other things,

instead of using this:

Quote

  if( EOK != posix_memalign( ptr, 1024, 2048))
    exit(0);
 

you can just set the segments in the config file OR use a #define for a specific memory address you know follow the 1K, 2K boundary rules.

you define int variables which are pretty wastefull in CC65 just for memory allocations and addresses

 

for example PMBASE could be easily defined as:

Quote

#define PMBASE_MEM 7000

and then just use the PMBASE when needed, for example:

Quote

ANTIC.pmbase = PMBASE_MEM >> 8; 

easy as that.

 

EDIT: one thing i forgot, you seem to use "memmove" , if the memory to copy is not overlapping it is recommended to use memcpy instead.

 

general comment, if you'd like, try to separate code into other headers and c files, makes it easier to read and maintain rather than 1 big main c file.

 

 

Edited by Yaron Nir

Share this post


Link to post
Share on other sites

also, if you can attach here the command line you were using to compile the 2 files and the xex executable that was produced 

Share this post


Link to post
Share on other sites
4 hours ago, tschak909 said:

As for modifying the linker files, seriously, whatever, sometimes you need to.

I agree - it's just another thing you have to figure out when you're starting with the environment.  So I decided to skip that issue all together for this one.

Share this post


Link to post
Share on other sites
1 hour ago, Yaron Nir said:

also, if you can attach here the command line you were using to compile the 2 files and the xex executable that was produced 

It's in the C source file.

Share this post


Link to post
Share on other sites
9 hours ago, Yaron Nir said:

Hey,

as  @tschak909 mentioned, since you're including <atari.h> , ...

 

Using the ANTIC (etc.) structure or creating defines results in the same generated code - it's preference really.

 

The linker file is my preferred way to chop up memory in advance (for fonts, etc.) but what is the define you're talking about wrt specifying memory locations for data?  Or are you just defining an arbitrary location in memory for your stuff? 

 

As far as memmove() it's being used to move players in the pm memory area so there is source/dest overlap once the program is running.

 

Share this post


Link to post
Share on other sites

I often use higher STARTADDRESS e.g. $6000 and use memory locations from $2000 up for data that require aligned memory (PMG, screen memory, dlist) or comes from other assembler (RMT music player compiled by MADS).

Share this post


Link to post
Share on other sites
3 hours ago, ilmenit said:

I often use higher STARTADDRESS e.g. $6000 and use memory locations from $2000 up for data that require aligned memory (PMG, screen memory, dlist) or comes from other assembler (RMT music player compiled by MADS).

That's what I love about this platform - there's about 100 ways to do almost anything.

  • Like 1

Share this post


Link to post
Share on other sites
30 minutes ago, explorer said:

The flipper variable value is used?

It was in an earlier cut when I used it as a flag to do certain things during a vertical blank e.g. in this pass I'm going to update the enemies and in the next pass through the vertical blank handler I'll update the player positions.  It worked well enough.

 

I removed all of that when I converted it to use exclusive ors.

 

 

Share this post


Link to post
Share on other sites

with my version of CC65 it gives a lot of errors

mainly arround the usage of the "b" for the binary representation of the shifted sprites.... so i've changed all your binary to decimal and it compiled properly and run.

 

i've noticed something in the code.

you wait for 2 frames inside the main loop. that is not good practice.

this also what slows your animation and causes it to be jerky (the spaceships flicker).

if i comment out the second wait (wait_for_jiffy) the enemies moves smooth, but own main spaceship is not working well.

that has something to do with the order of the drawing of your own spaceship.

also there are wastful members within the routines, where the guidelines of cc65 recommend to use global vars.

 

other than that, good work 

Share this post


Link to post
Share on other sites
1 hour ago, Yaron Nir said:

with my version of CC65 it gives a lot of errors

mainly arround the usage of the "b" for the binary representation of the shifted sprites.... so i've changed all your binary to decimal and it compiled properly and run.

 

i've noticed something in the code.

you wait for 2 frames inside the main loop. that is not good practice.

this also what slows your animation and causes it to be jerky (the spaceships flicker).

if i comment out the second wait (wait_for_jiffy) the enemies moves smooth, but own main spaceship is not working well.

that has something to do with the order of the drawing of your own spaceship.

also there are wastful members within the routines, where the guidelines of cc65 recommend to use global vars.

 

other than that, good work 

If you get cc65 from github and build it yourself they've added the 0b00000000 thing.  It makes for cleaner code IMHO but to each their own.

 

I went back and forth in the main loop - if you remove the pauses you'll get solid objects but as the objects move down the screen they start to exhibit weird visuals.  It has something to do with the timing of the physical screen update and the Atari itself.  Dunno. 

 

If you look at the build string you'll notice that I use "--static-locals" - this has the effect of making locally declared variables "globals" - or more efficiently accessed.  Using "--static-locals" is better than making *everything* global IMHO - it helps to keep your namespace uncluttered.

 

Try to make it better and flicker free - I'd be interested in seeing your results.  hit_detect() is a mess.  The drawing routines can probably be sped up some.  I have some assembly code that'd speed things right up but I wanted to keep everything straight C.

 

I've toyed with drawing and updating the player sprite in a vblank.  Good practice or no...if it works it works.

 

D.

 

 

  • Like 1

Share this post


Link to post
Share on other sites

For making it flicker free I recommend to use double buffering. You need two screens and swap where you are writing to (so two tables gfx_indexes in your case), then swap the the screen memory when new screen is ready.

Share this post


Link to post
Share on other sites
9 hours ago, ilmenit said:

For making it flicker free I recommend to use double buffering. You need two screens and swap where you are writing to (so two tables gfx_indexes in your case), then swap the the screen memory when new screen is ready.

I started with the double buffering but the eor'ing sequence made my nose bleed.  I kept thinking I'd have to store the position of where the ship was two screens ago so I could EOR the old ship away, update the new coords, and then plot the new position.  Is there a code example that does this well?  I can study it and then update the code so it runs better.

 

D.

Share this post


Link to post
Share on other sites
20 hours ago, damosan said:

If you get cc65 from github and build it yourself they've added the 0b00000000 thing.  It makes for cleaner code IMHO but to each their own.

yes, using the 0b prefix is much easier especially with pre-shifted software sprites. i wonder why latest version (not from git and not building yourself) doesn't support it.

 

20 hours ago, damosan said:

Try to make it better and flicker free - I'd be interested in seeing your results.

I have created a CC65 tutorial for software sprites using pre-shifted software sprites. here is a link to that tutorial:

 

let me know if that helped you

 

Share this post


Link to post
Share on other sites

some code optimization,

instead of this code:

Quote

  POKE( pmbase + PMLOC3 + 0, 0 );
  POKE( pmbase + PMLOC3 + 1, 0 );
  POKE( pmbase + PMLOC3 + 2, 8 );
  POKE( pmbase + PMLOC3 + 3, 24 );
  POKE( pmbase + PMLOC3 + 4, 124 );
  POKE( pmbase + PMLOC3 + 5, 124 );
  POKE( pmbase + PMLOC3 + 6, 24 );
  POKE( pmbase + PMLOC3 + 7, 8 );
  POKE( pmbase + PMLOC3 + 8, 0 );
  POKE( pmbase + PMLOC3 + 9, 0 );
  

(your code contained the binary representation of the data), you can created an array with the values: 

Quote

unsigned char arr[] = { 0,0,8,24,124,124,24,8,0,0};

and use memcpy in one operation:

Quote

memcpy((void*)pmbase+PMLOC3+0,(void*)arr,9);

 

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, Yaron Nir said:

yes, using the 0b prefix is much easier especially with pre-shifted software sprites. i wonder why latest version (not from git and not building yourself) doesn't support it.

 

let me know if that helped you

 

I'm generally of the opinion that one should try to build their tools first...before downloading binaries.  Public binaries tend to lag behind the development tree quite a bit.

 

No.  The code already uses pre-shifted bitmaps.  The basic rule of "don't compute when you can pre-compute" applies here.

 

Tweaking the PM setup stuff is easy but doesn't really gain much at the end of the day.

 

The part that needs work is that main game loop.  Converting it back to double buffering with two display lists would take about 15 minutes (basically call _graphics( 7 + 16 ) twice storing relevant pointers).  I'd be interested in a true double buffering GR.7 config using EOR to update the bitmaps on each screen.  That'd be useful and remove the flickering - probably getting at least 30fps if not more with no flickering.  Trying to figure that part out hurt my head so the flickering version got posted.

 

Share this post


Link to post
Share on other sites

I have not seen your code, but the basic algorithm should be something like:

 

loop:
	- sync to some screen line
	- show screen buffer 1
	work on screen buffer 2:
		- erase previous sprites in buffer 2 (needs a list of the positions and sprite types previously drawn in buffer 2)
		- update real sprite positions (and animations, creation, destruction..)
		- draw sprites in buffer 2 (save positions/types in list for buffer 2)

	- sync to some screen line
	- show screen buffer 2
	work on screen buffer 1:
		- erase previous sprites in buffer 1 (needs a list of the positions and sprite types previously drawn in buffer 1)
		- update real sprite positions (and animations, creation, destruction..)
		- draw sprites in buffer 1 (save positions/types in list for buffer 1)

jump loop:

 

  • Like 1

Share this post


Link to post
Share on other sites
15 hours ago, NRV said:

I have not seen your code, but the basic algorithm should be something like:

 

That's the magic right there....  I'll be updating the code to take it back to double buffering and repost.

Share this post


Link to post
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.

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