damosan Posted January 19, 2020 Share Posted January 19, 2020 (edited) 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 January 22, 2020 by damosan Added XEX 5 Quote Link to comment Share on other sites More sharing options...
+Stephen Posted January 19, 2020 Share Posted January 19, 2020 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. Quote Link to comment Share on other sites More sharing options...
tschak909 Posted January 20, 2020 Share Posted January 20, 2020 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 Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted January 21, 2020 Share Posted January 21, 2020 (edited) 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 January 21, 2020 by Yaron Nir Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted January 21, 2020 Share Posted January 21, 2020 also, if you can attach here the command line you were using to compile the 2 files and the xex executable that was produced Quote Link to comment Share on other sites More sharing options...
damosan Posted January 21, 2020 Author Share Posted January 21, 2020 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. Quote Link to comment Share on other sites More sharing options...
damosan Posted January 21, 2020 Author Share Posted January 21, 2020 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. Quote Link to comment Share on other sites More sharing options...
damosan Posted January 21, 2020 Author Share Posted January 21, 2020 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. Quote Link to comment Share on other sites More sharing options...
ilmenit Posted January 21, 2020 Share Posted January 21, 2020 Add a compiled version, please ? Quote Link to comment Share on other sites More sharing options...
ilmenit Posted January 21, 2020 Share Posted January 21, 2020 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). Quote Link to comment Share on other sites More sharing options...
damosan Posted January 21, 2020 Author Share Posted January 21, 2020 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. 1 Quote Link to comment Share on other sites More sharing options...
explorer Posted January 24, 2020 Share Posted January 24, 2020 The flipper variable value is used? Quote Link to comment Share on other sites More sharing options...
damosan Posted January 24, 2020 Author Share Posted January 24, 2020 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. Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted February 3, 2020 Share Posted February 3, 2020 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 Quote Link to comment Share on other sites More sharing options...
damosan Posted February 3, 2020 Author Share Posted February 3, 2020 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. 1 Quote Link to comment Share on other sites More sharing options...
ilmenit Posted February 4, 2020 Share Posted February 4, 2020 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. Quote Link to comment Share on other sites More sharing options...
damosan Posted February 4, 2020 Author Share Posted February 4, 2020 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. Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted February 4, 2020 Share Posted February 4, 2020 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 Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted February 4, 2020 Share Posted February 4, 2020 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); 1 Quote Link to comment Share on other sites More sharing options...
damosan Posted February 4, 2020 Author Share Posted February 4, 2020 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. Quote Link to comment Share on other sites More sharing options...
NRV Posted February 4, 2020 Share Posted February 4, 2020 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: 1 Quote Link to comment Share on other sites More sharing options...
damosan Posted February 5, 2020 Author Share Posted February 5, 2020 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. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.