KrunchyTC Posted October 9, 2023 Share Posted October 9, 2023 4 hours ago, bsteux said: Have you played Atari today ? Here is a 60 fps shmup for the Atari 7800 (well, not really, it's just the beginning of it, but well... it runs at full speed !). Nothing really new this week, as this is just an integration of the previous developments in order to check that everything fits in... In the end, it's a rather complete basis for developing a shmup : the code manages the FIFOs for bullets, enemies, explosions, the collisions, the music (Pokey & TIA for sound effects) and the scrolling background. And the gameover (I'm proud of this one with the big skull). It just lacks the enemy spawning (I just spawn a single boss) and the level design (it's YOUR work as game developers). The result is that all the stuff takes approximately 70% of CPU available (in NTSC and 60fps), so there is still room for completing the game. On the other hand, it terms of ROM space, it's already very tight. Indeed, this is the first time I'm using cc7800 to make a true supergame (128KB ROM with bankswitching). The memory layout is the following (each bank is 16kB) : - Bank0 (or default bank) is the one that is fixed at 0xC000 => 0xFFF (in the ROM layout, it's indeed the last one. cc7800 manages this for you). It contains most of the code (including RMTPlayer for the Pokey Music) and 4kb of sprites that will be used throughout the game (spaceship, gameover, the bullets, digits for scoring...). In the end, Bank0 is almost full (there is still ~2kb of ROM available, 4kb if you remove POKEY RTMPlayer support). - Bank1 to Bank6 contain the level definitions and are mapped from 0x8000 to 0xbffff on demand (when you jump into a banked function, there is automatic bankswitching). I've only included the definition for 1 level, but the bank is almost full (4kb for the tiles definition, 4kb for the sparse tileset definition (the actual layout of the background), 4kb for the enemy sprites (here only 1 160B big boss sprite), 2.7Kb for the Pokey music and 1.3kb for the enemy management code). There is no room anymore for the spawning of enemies. Solution ? Reduce the music size - nice music but too fat - and reduce the tileset definition by reusing parts of the layout. - Bank7 contains the initialization code (palettes definitions, multisprite library init, etc), which helps to reduce bank0 usage. A splash screen would perfectly fit there... In attachment, you will find : - The latest version of cc7800, with several bankswitching related bug corrected. - 2 tested ROMS : . one with RAM at 0x4000 (with a vertical scrolling in immediate mode), no pokey and a lot of CPU available . the other with Pokey at 0x4000 (with music, but background drawing in indirect mode). Less CPU available, but not that bad indeed. - The C source code of the Shmup game (let's call it Power Shmup 7800) - The yaml and png to be used with sprites7800 and tiles7800 to generate the graphics code. In order to compile the full thing, type in cc7800 directory (from the github repository) : cc7800 -g -v -Iheaders -Iexamples -DMULTISPRITE_USE_VIDEO_MEMORY examples/example_shmup.c to get the the immediate mode vertical scrolling, and cc7800 -g -v -Iheaders -Iexamples -DPOKEY_MUSIC examples/example_shmup.c to get the one with some Music. The 2 options are not compatible yet, since both of the them use the 0x4000 precious memory bank (Concerto doesn't support 0x450 Pokey at the moment). (the -g flag generates the .asm and .lst files, the -v generates a verbose output with the full memory layout. Very very instructive. You need to understand this if you want to go further). You can also compile with -DDEBUG flag to see the CPU available on screen. Next time ? The use of sparse tiling to draw the background of a single screen game. example_shmup.mp4 cc7800-0.2.15-x86_64.msi 1.59 MB · 1 download example_shmup_pokey.a78 128.13 kB · 1 download example_shmup_vmem.a78 128.13 kB · 1 download shmup.yaml 3.62 kB · 1 download example_shmup.c 23.71 kB · 1 download Thats awesome! Hope this evolves into a full game. Need more shmups! 1 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted October 11, 2023 Author Share Posted October 11, 2023 On 10/9/2023 at 7:12 PM, karri said: Pretty amazing tool. Installing everything, compiling examples. And everything just works! I will later test it on the real 7800. The screenshot is from a7800. I will share apartment with two French developers @Fadest, @LordKraken at eJagfest two weeks from now. I am sure we will discuss cc7800 and try it out. Thanks for sharing it. Please tell me if you can run it on the Dragonfly (I guess you'll have to use 7800sign on the binary). If your french Atari friends are from the Paris area, tell them to get in touch ! 1 Quote Link to comment Share on other sites More sharing options...
+karri Posted October 11, 2023 Share Posted October 11, 2023 38 minutes ago, bsteux said: Please tell me if you can run it on the Dragonfly (I guess you'll have to use 7800sign on the binary). If your french Atari friends are from the Paris area, tell them to get in touch ! It runs on Dragonfly without signing it. The same is true on a PAL 7800. It does not check the signature. VID_20231011_163324.mp4 I did notice some strange graphic garbage on screen a few times. As you can see on the video. PS. I have been thinking about writing an improved version of one of my previous games (Shaken, not stirred) on cc7800. I love the speed you have been able to get out of this console. Of course the entire game needs to be written from scratch and the visuals would be very different. I hope it is ok with you. 1 Quote Link to comment Share on other sites More sharing options...
+Karl G Posted October 11, 2023 Share Posted October 11, 2023 On 10/9/2023 at 9:36 AM, bsteux said: Have you played Atari today ? Here is a 60 fps shmup for the Atari 7800 (well, not really, it's just the beginning of it, but well... it runs at full speed !). Okay; I'm afraid you're going to have to take a break from your compiler, and finish making this game. Sorry; I don't make the rules. 3 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted October 11, 2023 Author Share Posted October 11, 2023 59 minutes ago, karri said: It runs on Dragonfly without signing it. The same is true on a PAL 7800. It does not check the signature. VID_20231011_163324.mp4 79.78 MB · 0 downloads I did notice some strange graphic garbage on screen a few times. As you can see on the video. PS. I have been thinking about writing an improved version of one of my previous games (Shaken, not stirred) on cc7800. I love the speed you have been able to get out of this console. Of course the entire game needs to be written from scratch and the visuals would be very different. I hope it is ok with you. Ah yes. It's from the video memory version I guess no ? I probably don't reset correctly the video memory filling after a gameover when I restart the scrolling. I'll check that. Ok course you are welcome to make any game based on this code. It's the goal of it all ! Good to know that it's working on the Dragonfly without signing. Have you tried the pokey version ? 1 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted October 11, 2023 Author Share Posted October 11, 2023 9 minutes ago, Karl G said: Okay; I'm afraid you're going to have to take a break from your compiler, and finish making this game. Sorry; I don't make the rules. Indeed, my plan is to make a one screen example (bombjack like), and then an horizontal scrolling example (R-Type like), and then a bitmap based example (with 3d projection like The Sentinel), and then go back on this to try to finish a game. Let's see. Maybe will I add spawning and additional weapons to this example first to complete it... 1 Quote Link to comment Share on other sites More sharing options...
+karri Posted October 11, 2023 Share Posted October 11, 2023 1 hour ago, bsteux said: Have you tried the pokey version ? Yes. I taped it using the front camera in order to use the joystick also. VID_20231011_183550.mp4 3 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted October 13, 2023 Author Share Posted October 13, 2023 On 10/11/2023 at 3:39 PM, karri said: It runs on Dragonfly without signing it. The same is true on a PAL 7800. It does not check the signature. VID_20231011_163324.mp4 79.78 MB · 0 downloads I did notice some strange graphic garbage on screen a few times. As you can see on the video. PS. I have been thinking about writing an improved version of one of my previous games (Shaken, not stirred) on cc7800. I love the speed you have been able to get out of this console. Of course the entire game needs to be written from scratch and the visuals would be very different. I hope it is ok with you. Oops! I completely forgot to clear the screen after a game over, which generates a lot of glitches. Here is a fixed version. example_shmup_pokey.a78 example_shmup_vmem.a78 example_shmup.c 4 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted October 16, 2023 Author Share Posted October 16, 2023 Now, let's bombjack a little bit. Why bombjack ? Because it's a nice little example of one-screen game, and it's particularly difficult to adapt to the Atari 7800 : it requires to display a nice background with a lot of sprites overlaid on it - 24 bombs + 24 enemies, so lots of CPU/DMA fight. And as it also requires to be colorful, we'll have to deal with the 160A/B modes. It's also a game that was crappy on most 8-bit platform back in the eighties, especially on the NES which had a "mighty Bombjack" far from the arcade game, with crappy backgrounds ! So let's do better. As a first step, here is an example of how to use sparse tiling (again) to display the famous sphinx and the first level (or arrangement of platforms). In the previous examples, we used sparse tiling for scrolling. But we can also use it for a static screen. We'll introduce a new function: multisprite_sparse_tiling. As in the arcade game, the background will be 14x14 tiles big, i.e. 112x224 fat pixels (224x224 in the arcade game, which had a fabulous 256x256 display). As we need to free as much CPU as possible for the sprites, all the tiles will be in 160A, i.e. 3 colors + transparency. Using a screenshot of the arcade game and bit of GIMP, we build the tiles as follow, reducing the palette to 5 different colors and removing duplicated tiles : And then, with the Tiled editor, we reconstitute the whole sphinx : Using sprites7800 and tiles7800, we build the C code for the graphics. But how can we build a 5 colors background using only 160A tiles ? OK. 1 is black so it's the background. So how 4 colors and not 3 per tile ? By using the "background" feature of tiles7800. For instance, the top of the pyramid is defined in bombjack_tiles.yaml as : - name: sky top: 0 left: 0 height: 16 width: 16 holeydma: false palette: sky palette_number: 4 - name: pyramid_top1 top: 0 left: 48 height: 16 width: 16 holeydma: false background: sky palette: sphinx palette_number: 5 Note that pyramid_top1 tells that tiles7800 should use the sky tile as a background tile. Tiles7800 will then overlay a pyramid_top1 tile over a sky tile, mixing the colors. We are using overlaid 160A tiles. This doesn't cost so much, because the sky tileset using the blue palette is not interrupted. There is just an additional tile that will be overlaid on it. We then define another sparse tiling (very sparse in that case) for the platform : Note the the sphinx tiles use 146 out of 256 tiles (for a 4KB memory bank), i.e. 2.5KB + plus 500 bytes for the sparse tiling arrays = 3KB. Without tile compression, the sphinx would take 14x14x2 bytes = 392 tiles, i.e. 6.2KB of ROM. So here, using sparse tiling, the gain is 50% AND we can use more colors and make a nicer background. To finish the example, we just have to write a little C code to draw everything together. Here is the source code for the example : #include "prosystem.h" #include "multisprite.h" // Generated by sprites7800 bombjack_tiles.yaml #include "example_bombjack_tiles.c" // Generated by sprites7800 bombjack_sprites.yaml #include "example_bombjack_sprites.c" // Generated by tiles7800 --sparse bombjack_tiles.yaml --varname tileset_sphinx bombjack_sphinx.tmx #include "example_bombjack_sphinx.c" // Generated by tiles7800 --sparse bombjack_tiles.yaml --varname tilemap_arrangement_A bombjack_arrangement_A.tmx #include "example_bombjack_arrangement_A.c" const char topborder[15] = {6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8}; const char topborder_dl[] = { topborder, 0x60, topborder >> 8, (3 << 5) | (-15 & 0x1f), 0, 0, 0}; const char bottomborder[15] = {10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12}; const char bottomborder_dl[] = { bottomborder, 0x60, bottomborder >> 8, (3 << 5) | (-15 & 0x1f), 0, 0, 0}; // Arrangements - x, y of first bomb, nb bombs, direction (0 to 3). const char arrangement_A[] = { 6 + 6 * 8, 3 * 16 - 8, 4, 0, 4 + 13 * 8, 8 + 4 * 16, 4, 3, 4, 5 * 16, 4, 3, 4 + 2 * 8, 0, 3, 0, 4 + 11 * 8, 11 * 16, 3, 2, 5 * 8, 13 * 16, 3, 2, 4 + 13 * 8, 0, 3, 2}; const char arrangements_nb_series[] = { 7 }; const char *arrangements[] = { arrangement_A }; const signed char arrangement_dx[4] = { 12, 0, -12, 0 }; const signed char arrangement_dy[4] = { 0, -24, 0, 24 }; void display_arrangement(char a) { char *arrangement = arrangements[X = a]; char i, j, nb_series = arrangements_nb_series[X]; for (Y = 0, i = 0; i != nb_series; i++) { char x = arrangement[Y++]; char y = arrangement[Y++]; char nb_bombs = arrangement[Y++]; char direction = arrangement[Y++]; _save_y = Y; for (j = 0; j != nb_bombs; j++) { multisprite_display_sprite_fast(x, y, bomb, 2, 2); x += arrangement_dx[X = direction]; y += arrangement_dy[X]; } Y = _save_y; } } void main() { char y; multisprite_init(); multisprite_set_charbase(platform); // Top border in overscan if (_ms_pal_detected) { X = 4; } else { X = 1; } _ms_b0_dll[X] = topborder_dl >> 8; _ms_b1_dll[X] = topborder_dl >> 8; _ms_b0_dll[++X] = topborder_dl; _ms_b1_dll[X] = topborder_dl; // Bottom border in overscan X += (_MS_DLL_ARRAY_SIZE - 1) * 3 + 1; _ms_b0_dll[X] = 0x08; // 9 lines _ms_b1_dll[X] = 0x08; // 9 lines _ms_b0_dll[++X] = bottomborder_dl >> 8; _ms_b1_dll[X] = bottomborder_dl >> 8; _ms_b0_dll[++X] = bottomborder_dl; _ms_b1_dll[X] = bottomborder_dl; // Adjust the bottom of screen to fit the screen size X += 4; if (_ms_pal_detected) { _ms_b0_dll[X] = 0x07; // 8 lines _ms_b1_dll[X] = 0x07; // 8 lines } else { _ms_b0_dll[X] = 0x00; // 1 line _ms_b1_dll[X] = 0x00; // 1 line } // Left and right borders for (y = 0; y < 224; y += 16) { multisprite_display_sprite_fast(0, y, border, 1, 3); multisprite_display_sprite_fast(116, y, border, 1, 3); } // Display the sphinx and platforms multisprite_sparse_tiling(tilemap_sphinx_data_ptrs, 0, 4, 14); multisprite_sparse_tiling(tilemap_arrangement_A_data_ptrs, 0, 4, 14); multisprite_save(); *P1C2 = multisprite_color(0x3c); // Rose // Bomb palette *P2C1 = multisprite_color(0x34); // Red *P2C2 = multisprite_color(0x1c); // Yellow *P2C3 = 0x0f; // Fire palette *P3C1 = multisprite_color(0x24); // Red *P3C2 = multisprite_color(0x28); // Orange *P3C3 = multisprite_color(0x1c); // Yellow // Blue palette *P4C1 = multisprite_color(0x84); // Dark blue *P4C2 = multisprite_color(0x87); // Light blue *P4C3 = multisprite_color(0xac); // Turquoise // Sphinx palette *P5C1 = multisprite_color(0x12); // Red *P5C2 = multisprite_color(0x15); // Orange *P5C3 = multisprite_color(0x18); // Yellow // Main loop do { // Display the first arrangement display_arrangement(0); multisprite_flip(); } while(1); } Note the use of the new function multisprite_sparse_tiling, which is used twice (one for the bakground, one for the platforms). We also had to draw top and bottom borders in overscan, since the display in multisprite.h is 224 pixels high (14 * 16 pixels), and we NEED a 240 pixels high display. We have to display two 8 pixels high borders on top and on bottom of it. For this, we directly modify the DLLs (Display List Lists), using the _ms_b0_dll and _ms_b1_dll arrays (see the multisprite_init function code in multisprite.h to better understand the standard cc7800 screen DLLs layout). Here, we have to check that we are in PAL or NTSC, because the number of lines in overscan are different in the 2 TV modes, and we have to modify b0 and b1 (for buffer0 and buffer1), the 2 DLLs that are used for double buffering. Here the resulting screenshot for the whole scenery, which looks quite good for a 160A display : Note the white strip that is generated by the -DDEBUG option, which shows that 1/4 of the available CPU is used by displaying the bombs arrangement every frame (24 sprites to display). It's huge, since with the background AND all the sprites, DMA access will severely limit the CPU available for the game logic. We'll have to find a solution for that. We NEED to run at 60fps for bombjack. Next week, I'll introduce the bombjack character. We'll see how to check collisions with the platforms (sprite vs tiles collisions), and how to manage the bombs display so that they don't take so much CPU. bombjack_sprites.yaml bombjack_tiles.tsx bombjack_sphinx.tmx bombjack_tiles.yaml example_bombjack1.a78 6 Quote Link to comment Share on other sites More sharing options...
KrunchyTC Posted October 19, 2023 Share Posted October 19, 2023 (edited) On 6/15/2023 at 5:25 PM, Defender_2600 said: full 160B 4bpp, up to 5 (160B) sprites in the same horizontal area (this has been tested) : I still have questions. Would this zelda demo still be possible with game logic present? Also, the Bomb Jack example above, could the back ground be done in 160B? if not, what is the difference between the two besides color? Edited October 19, 2023 by KrunchyTC Quote Link to comment Share on other sites More sharing options...
Trebor Posted October 19, 2023 Share Posted October 19, 2023 43 minutes ago, KrunchyTC said: I still have questions. Would this zelda demo still be possible with game logic present? Also, the Bomb Jack example above, could the back ground be done in 160B? if not, what is the difference between the two besides color? Yes, the Zelda demo would still be possible with game logic present. With an even more detailed perspective and response than the first link, the answer is still yes. Respecting the Bomb Jack example above, the difference between 160B and 160A besides color is resource usage. Less resource utilization for the background, platforms, bombs, means more resources available for the player and enemy sprites. 1 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted October 19, 2023 Author Share Posted October 19, 2023 For bombjack, maybe it would be possible to use a 160B background in immediate mode (using a lot of ROM), but it certainly makes things more complicated. I haven't made the computations, but with Bombjack you always have the situation where you have to draw many sprites on the same line (at least 3 bombs + bombjack + enemies + drawing things on the right of the screen (score, etc)). The game logic is not simple : all the enemies are moving at each step (so you have to redraw all the sprites at every frame), and there can be many enemies at the same time, since the game is spawning, spawning, spawning... so you need some CPU left and it must run at 60fps. So better a 160A background with nice palette switching... The result can be next to 160B if well managed. For Zelda, this is a different case : first, you seldom have 5 sprites in a row at the same time, and second, you don't care if it runs at 60fps or not, so you have much, much more CPU available for your logic. Not the same kind of games... 1 Quote Link to comment Share on other sites More sharing options...
Eagle Posted October 19, 2023 Share Posted October 19, 2023 BombJack XL/XE source code https://github.com/shanti77atari/gacek and old cart version BombJake http://sources.pigwa.net/files/gry/BombJake_Cartridge.zip my version of BombJack is using 160B for background and sprites. The background is a bitmap and I draw/erase bombs on the screen. 1 Quote Link to comment Share on other sites More sharing options...
KrunchyTC Posted October 19, 2023 Share Posted October 19, 2023 Thats cool, I like the different approaches to Bomb Jack Quote Link to comment Share on other sites More sharing options...
+bsteux Posted October 20, 2023 Author Share Posted October 20, 2023 15 hours ago, Eagle said: BombJack XL/XE source code https://github.com/shanti77atari/gacek and old cart version BombJake http://sources.pigwa.net/files/gry/BombJake_Cartridge.zip my version of BombJack is using 160B for background and sprites. The background is a bitmap and I draw/erase bombs on the screen. Thank you for the info provided, very useful. The commented C64 code of Bombjack is quite impressive ! What a job it was to hand-code all this in ASM ! It's also true that it's a very good idea to draw/erase bombs inside a bitmap. I haven't developed a bitmap.h header yet with bit-blitting for cc7800, but it's in my plan, so maybe will I revisit this later. At the moment, the bombjack code here is only a way to show how to use sparse tiling and sprites to make a game with cc7800. It's just a tutorial. It's not meant to be the ideal implementation, but at least a simple one, easy to understand and light. 2 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted October 23, 2023 Author Share Posted October 23, 2023 Hi, So here's the sequel to the previous example: bombjack with the bombjack character. I've used the sprites provided by @TIX, which I scaled down to 16 pixels high (with mediocre success) and colorized to 160B. Well, it would need rework, but it's not the main goai. I think I mixed up jumping and dancing sprites... This example shows: - How a rather complicated character state machine can be implemented in C - The use of the new function multisprite_sparse_tiling_collision to detect collisions between bombjack and the platforms. - The use of 16 bits arithmetics (bombjack_ypos is a 16 bit "unsigned short" variable, due to the vertical acceleration that is applied to the character when jumping), and in particular the use of >>8 and <<8 cc7800 optimizations to go from 16 bits to 8 bits and vice versa. - The use of a new feature call overlay. In cc7800 multisprite.h, as in 7800basic, there is the notion of background with, the "multisprite_save" function. I've added another layer, called "overlay", that can also be saved for the next frame. This overlay contains the bombs. It's not static as a background, but it's not as moving as foreground sprites. It's in between, and we don't want to spend time redrawing them every frame, like a background. This is why I've introduced 2 new functions: multisprite_clear_overlay() and multisprite_save_overlay(), which are used when bombjack grabs a bomb. Note that the bombs redraw must be called twice (update_overlay=2 in the code), because multisprite.h is always using double buffering (it's not an option). Note that this feature allows to reduce significantly the use of cpu (down to 10% approx), since we don't have to redraw the bombs every frame. Here is the example source code (example_bombjack2.c in the cc7800 github repository) : #include "prosystem.h" #define MULTISPRITE_OVERLAY #include "multisprite.h" #include "joystick.h" #include "string.h" // Generated by sprites7800 bombjack_tiles.yaml #include "example_bombjack_tiles.c" // Generated by sprites7800 bombjack_sprites.yaml #include "example_bombjack_sprites.c" // Generated by tiles7800 --sparse bombjack_tiles.yaml --varname tilemap_arrangement_A bombjack_arrangement_A.tmx #include "example_bombjack_arrangement_A.c" // Generated by tiles7800 --sparse bombjack_tiles.yaml --varname tilemap_sphinx bombjack_sphinx.tmx #include "example_bombjack_sphinx.c" const char topborder[15] = {26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28}; const char topborder_dl[] = { topborder, 0x60, topborder >> 8, (3 << 5) | (-15 & 0x1f), 0, 0, 0}; const char bottomborder[15] = {30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32}; const char bottomborder_dl[] = { bottomborder, 0x60, bottomborder >> 8, (3 << 5) | (-15 & 0x1f), 0, 0, 0}; #define BOMBS_RIGHT 0 #define BOMBS_UP 1 #define BOMBS_LEFT 2 #define BOMBS_DOWN 3 // Arrangements - x, y of first bomb, nb bombs, direction (0 to 3). const char arrangement_A[] = { 6 + 6 * 8, 3 * 16 - 8, 4, BOMBS_RIGHT, 4 + 13 * 8, 8 + 4 * 16, 4, BOMBS_DOWN, 4, 5 * 16, 4, BOMBS_DOWN, 4 + 2 * 8, 0, 3, BOMBS_RIGHT, 4 + 11 * 8, 11 * 16, 3, BOMBS_LEFT, 5 * 8, 13 * 16, 3, BOMBS_LEFT, 4 + 13 * 8, 0, 3, BOMBS_LEFT}; const char arrangements_nb_series[] = { 7 }; const char *arrangements[] = { arrangement_A }; const signed char arrangement_dx[4] = { 12, 0, -12, 0 }; const signed char arrangement_dy[4] = { 0, -24, 0, 24 }; ramchip char bomb_disposed[24]; ramchip char lighted_bomb; ramchip char explosion_color1, explosion_color2, explosion_color3; ramchip int score; ramchip char update_score; ramchip char display_score_str[5]; #define SPRITES_NB_MAX 24 #define SPRITE_TWO_HUNDRED 0 ramchip char sprite_xpos[SPRITES_NB_MAX], sprite_ypos[SPRITES_NB_MAX], sprite_type[SPRITES_NB_MAX], sprite_state[SPRITES_NB_MAX], sprite_counter1[SPRITES_NB_MAX], sprite_counter2[SPRITES_NB_MAX]; ramchip char sprite_first, sprite_last; void destroy_sprite() { sprite_type[X] = -1; // Removed do { X++; if (X == SPRITES_NB_MAX) X = 0; } while (X != sprite_last && sprite_type[X] == -1); sprite_first = X; } void draw_sprites() { char i, x, y; for (i = sprite_first; i != sprite_last; i++) { if (i == SPRITES_NB_MAX) { i = 0; if (sprite_last == 0) break; } X = i; if (sprite_type[X] != -1) { if (sprite_type[X] == SPRITE_TWO_HUNDRED) { sprite_counter1[X]--; if (sprite_counter1[X] == 0) { destroy_sprite(); } else { x = sprite_xpos[X]; y = sprite_ypos[X]; multisprite_display_sprite_ex(x, y, twohundred, 3, 2, 0); } } } } } void display_score_update() { char display_score_ascii[6]; itoa(score, display_score_ascii, 10); Y = strlen(display_score_ascii); for (X = 0; X != 5 - Y; X++) { display_score_str[X] = 0; // '0' } X = 4; do { display_score_str[X--] = ((display_score_ascii[--Y] - '0') << 1); } while (Y); } void display_arrangement(char a) { char *arrangement = arrangements[X = a]; char i, j, k, nb_series = arrangements_nb_series[X]; for (Y = 0, k = 0, i = 0; i != nb_series; i++) { char x = arrangement[Y++]; char y = arrangement[Y++]; char nb_bombs = arrangement[Y++]; char direction = arrangement[Y++]; _save_y = Y; for (j = 0; j != nb_bombs; k++, j++) { if (!bomb_disposed[X = k]) { multisprite_display_sprite_fast(x, y, bomb, 2, 2); if (k == lighted_bomb) { multisprite_display_sprite_fast(x, y, explosion, 2, 7); } } x += arrangement_dx[X = direction]; y += arrangement_dy[X]; } Y = _save_y; } } // All the bombjack states #define BOMBJACK_LEFT 64 #define BOMBJACK_RIGHT 128 #define BOMBJACK_STILL 1 #define BOMBJACK_STILL_LEFT (BOMBJACK_STILL | BOMBJACK_LEFT) #define BOMBJACK_STILL_RIGHT (BOMBJACK_STILL | BOMBJACK_RIGHT) #define BOMBJACK_JUMPING 2 #define BOMBJACK_JUMPING_LEFT (BOMBJACK_JUMPING | BOMBJACK_LEFT) #define BOMBJACK_JUMPING_RIGHT (BOMBJACK_JUMPING | BOMBJACK_RIGHT) #define BOMBJACK_WALKING 4 #define BOMBJACK_WALKING_LEFT (BOMBJACK_WALKING | BOMBJACK_LEFT) #define BOMBJACK_WALKING_RIGHT (BOMBJACK_WALKING | BOMBJACK_RIGHT) #define BOMBJACK_FALLING 8 #define BOMBJACK_FALLING_LEFT (BOMBJACK_FALLING | BOMBJACK_LEFT) #define BOMBJACK_FALLING_RIGHT (BOMBJACK_FALLING | BOMBJACK_RIGHT) #define BORDER_WIDTH 4 #define PLAYFIELD_HEIGHT 224 #define PLAYFIELD_WIDTH 112 #define SPRITE_HEIGHT 16 #define SPRITE_WIDTH 12 #define PLATFORM_HEIGHT 8 #define GRAVITY 20 ramchip char bombjack_xpos, bombjack_state, bombjack_counter; ramchip char button_pressed, current_arrangement; // ypos and yspeed are 16-bits to allow for fine vertical movement ramchip unsigned short bombjack_yspeed, bombjack_ypos; ramchip char update_overlay; void bombjack_move_left() { char x, top, collision; bombjack_xpos--; if (bombjack_xpos < 3) bombjack_xpos = 3; else { // Test if we bump into a platform with the head x = (bombjack_xpos - BORDER_WIDTH + 1) >> 3; top = (bombjack_ypos >> 8) >> 4; collision = multisprite_sparse_tiling_collision(top, x, x); if (collision != -1 && ((bombjack_ypos >> 8) & 0x0f) < PLATFORM_HEIGHT - 1) { bombjack_xpos++; return; } // Test if we bump into a platform with the foot top = ((bombjack_ypos >> 8) + SPRITE_HEIGHT - 1) >> 4; collision = multisprite_sparse_tiling_collision(top, x, x); if (collision != -1) { bombjack_xpos++; } } } void bombjack_move_right() { char x, top, collision; bombjack_xpos++; if (bombjack_xpos >= PLAYFIELD_WIDTH - SPRITE_WIDTH + 6) bombjack_xpos = PLAYFIELD_WIDTH - SPRITE_WIDTH + 5; else { // Test if we bump into a platform with the head x = (bombjack_xpos - BORDER_WIDTH + SPRITE_WIDTH - 3) >> 3; top = (bombjack_ypos >> 8) >> 4; collision = multisprite_sparse_tiling_collision(top, x, x); if (collision != -1 && ((bombjack_ypos >> 8) & 0x0f) < PLATFORM_HEIGHT - 1) { bombjack_xpos--; return; } // Test if we bump into a platform with the foot top = ((bombjack_ypos >> 8) + SPRITE_HEIGHT - 1) >> 4; collision = multisprite_sparse_tiling_collision(top, x, x); if (collision != -1) { bombjack_xpos--; } } } void bombjack_dispose_bombs(char a) { char *arrangement = arrangements[X = a]; char x2 = bombjack_xpos + 2; char y2 = (bombjack_ypos >> 8) + 1; char i, j, k, nb_series = arrangements_nb_series[X]; for (Y = 0, k = 0, i = 0; i != nb_series; i++) { char x = arrangement[Y++] + 1; char y = arrangement[Y++] + 6; char nb_bombs = arrangement[Y++]; char direction = arrangement[Y++]; for (j = 0; j != nb_bombs; k++, j++) { if (!bomb_disposed[X = k]) { multisprite_compute_box_collision(x, y, 5, 9, x2, y2, 7, 14); if (multisprite_collision_detected) { char add, find_next_lighted_bomb = 0; bomb_disposed[X] = 1; if (X == lighted_bomb) { find_next_lighted_bomb = 1; add = 200; // Display 200 points X = sprite_last++; if (sprite_last == SPRITES_NB_MAX) sprite_last = 0; if (sprite_last != sprite_first) { sprite_xpos[X] = x - 3; sprite_ypos[X] = y - 6; sprite_counter1[X] = 30; // Stay for half a second } else sprite_last = X; } else { add = 100; if (lighted_bomb == -1) { lighted_bomb = X; find_next_lighted_bomb = 1; } } score += add; update_score = 1; // Find next lighted_bomb if (find_next_lighted_bomb) { lighted_bomb++; while (lighted_bomb < 24 && bomb_disposed[X = lighted_bomb]) { lighted_bomb++; } if (lighted_bomb == 24) { lighted_bomb = 0; while (lighted_bomb < 24 && bomb_disposed[X = lighted_bomb]) { lighted_bomb++; } if (lighted_bomb == 24) lighted_bomb = -1; } } update_overlay = 2; // In order to trigger the update of the overlay (bombs display) i = nb_series - 1; // To get out of upper loop break; } } x += arrangement_dx[X = direction]; y += arrangement_dy[X]; } } } void bombjack() { char *gfx, y; char left, right, top, collision; if (bombjack_state & BOMBJACK_FALLING) { if (bombjack_state & BOMBJACK_LEFT) { gfx = bombjack_falling_left; bombjack_move_left(); } else if (bombjack_state & BOMBJACK_RIGHT) { gfx = bombjack_falling_right; bombjack_move_right(); } else { if (bombjack_yspeed == 0) { gfx = bombjack_jumping; } else { gfx = bombjack_falling; } } bombjack_yspeed += GRAVITY; bombjack_ypos += bombjack_yspeed; // Does it land on bottom of the screen ? if (bombjack_ypos >> 8 >= PLAYFIELD_HEIGHT - 16) { bombjack_ypos = (PLAYFIELD_HEIGHT - 16) << 8; bombjack_state = BOMBJACK_STILL; } else { // Does it land on a platform ? left = (bombjack_xpos + (4 - BORDER_WIDTH)) >> 3; right = (bombjack_xpos + (7 - BORDER_WIDTH)) >> 3; top = ((bombjack_ypos >> 8) >> 4) + 1; collision = multisprite_sparse_tiling_collision(top, left, right); if (collision != -1) { y = (top - 1) << 4; bombjack_ypos = y << 8; bombjack_state = BOMBJACK_STILL; } } } else if (bombjack_state & BOMBJACK_JUMPING) { if (bombjack_state & BOMBJACK_LEFT) { gfx = bombjack_jumping_left; bombjack_move_left(); } else if (bombjack_state & BOMBJACK_RIGHT) { gfx = bombjack_jumping_right; bombjack_move_right(); } else gfx = bombjack_jumping; if (bombjack_yspeed < GRAVITY) { bombjack_yspeed = 0; bombjack_state = BOMBJACK_FALLING; } else { bombjack_yspeed -= GRAVITY; // Does it bump into the ceiling ? if (bombjack_ypos < bombjack_yspeed) { bombjack_ypos = 0; bombjack_yspeed = 0; bombjack_state = BOMBJACK_FALLING; } else { bombjack_ypos -= bombjack_yspeed; // Does it bump into a platform ? left = (bombjack_xpos + (4 - BORDER_WIDTH)) >> 3; right = (bombjack_xpos + (7 - BORDER_WIDTH)) >> 3; top = (((bombjack_ypos >> 8) + 7 ) >> 4); collision = multisprite_sparse_tiling_collision(top, left, right); if (collision != -1) { y = (top << 4) + PLATFORM_HEIGHT; // Head below the platform bombjack_ypos = y << 8; // Turn to 16 bits bombjack_state = BOMBJACK_FALLING; bombjack_yspeed = 0; } } } } else { bombjack_yspeed = 0; } if (bombjack_state & BOMBJACK_STILL) { if (bombjack_state == BOMBJACK_STILL_LEFT) { gfx = bombjack_still_left; } else if (bombjack_state == BOMBJACK_STILL_RIGHT) { gfx = bombjack_still_right; } else { gfx = bombjack_still; } } else if (bombjack_state & BOMBJACK_WALKING) { bombjack_counter++; if (bombjack_counter == 8) bombjack_counter = 0; y = bombjack_counter >> 1; // Walking animation if (bombjack_state & BOMBJACK_LEFT) { if (y == 0 || y == 2) { gfx = bombjack_walking_left1; } else if (y == 1) { gfx = bombjack_walking_left2; } else { gfx = bombjack_walking_left3; } bombjack_move_left(); } else { if (y == 0 || y == 2) { gfx = bombjack_walking_right1; } else if (y == 1) { gfx = bombjack_walking_right2; } else { gfx = bombjack_walking_right3; } bombjack_move_right(); } if (bombjack_ypos >> 8 != PLAYFIELD_HEIGHT - 16) { // Does it fall from a platform ? left = (bombjack_xpos + (4 - 4)) >> 3; right = (bombjack_xpos + (7 - 4)) >> 3; top = ((bombjack_ypos >> 8) + 17) >> 4; collision = multisprite_sparse_tiling_collision(top, left, right); if (collision == -1) { bombjack_state = BOMBJACK_FALLING | (bombjack_state & 0xc0); } } } y = bombjack_ypos >> 8; multisprite_display_sprite_ex(bombjack_xpos, y, gfx, 6, 0, 1); bombjack_dispose_bombs(current_arrangement); } void game_init() { for (X = 23; X >= 0; X--) bomb_disposed[X] = 0; bombjack_xpos = (PLAYFIELD_WIDTH - SPRITE_WIDTH) / 2 + BORDER_WIDTH; bombjack_ypos = (PLAYFIELD_WIDTH - 8) << 8; bombjack_yspeed = 0; bombjack_state = BOMBJACK_FALLING; current_arrangement = 0; lighted_bomb = -1; } void joystick_input() { char left_or_right; joystick_update(); if (joystick[0] & JOYSTICK_BUTTON1) { if (!button_pressed) { button_pressed = 1; if (bombjack_state & BOMBJACK_STILL) left_or_right = 0; else left_or_right = bombjack_state & 0xc0; if ((bombjack_state & BOMBJACK_FALLING) || (bombjack_state & BOMBJACK_JUMPING)) { bombjack_state = BOMBJACK_FALLING | left_or_right; bombjack_yspeed = 0; } else { bombjack_state = BOMBJACK_JUMPING | left_or_right; if (!left_or_right && (joystick[0] & JOYSTICK_UP)) { bombjack_yspeed = 1500; } else { bombjack_yspeed = 1000; } } return; }; } else button_pressed = 0; if (joystick[0] & JOYSTICK_LEFT) { if (bombjack_state & BOMBJACK_STILL) { bombjack_state = BOMBJACK_WALKING_LEFT; bombjack_counter = 0; } else if (bombjack_state & BOMBJACK_FALLING) { bombjack_state = BOMBJACK_FALLING_LEFT; } else if (bombjack_state & BOMBJACK_JUMPING) { bombjack_state = BOMBJACK_JUMPING_LEFT; } else if (bombjack_state != BOMBJACK_WALKING_LEFT) bombjack_state = BOMBJACK_STILL; } else if (joystick[0] & JOYSTICK_RIGHT) { if (bombjack_state & BOMBJACK_STILL) { bombjack_state = BOMBJACK_WALKING_RIGHT; bombjack_counter = 0; } else if (bombjack_state & BOMBJACK_FALLING) { bombjack_state = BOMBJACK_FALLING_RIGHT; } else if (bombjack_state & BOMBJACK_JUMPING) { bombjack_state = BOMBJACK_JUMPING_RIGHT; } else if (bombjack_state != BOMBJACK_WALKING_RIGHT) bombjack_state = BOMBJACK_STILL; } else { if (bombjack_state & BOMBJACK_WALKING) { bombjack_state = BOMBJACK_STILL | (bombjack_state & 0xc0); } else { bombjack_state &= ~(BOMBJACK_LEFT | BOMBJACK_RIGHT); } } } void display_init() { char y; multisprite_init(); multisprite_set_charbase(digits); joystick_init(); // Top border in overscan if (_ms_pal_detected) { X = 4; } else { X = 1; } _ms_b0_dll[X] = topborder_dl >> 8; _ms_b1_dll[X] = topborder_dl >> 8; _ms_b0_dll[++X] = topborder_dl; _ms_b1_dll[X] = topborder_dl; // Bottom border in overscan X += (_MS_DLL_ARRAY_SIZE - 1) * 3 + 1; _ms_b0_dll[X] = 0x08; // 9 lines _ms_b1_dll[X] = 0x08; // 9 lines _ms_b0_dll[++X] = bottomborder_dl >> 8; _ms_b1_dll[X] = bottomborder_dl >> 8; _ms_b0_dll[++X] = bottomborder_dl; _ms_b1_dll[X] = bottomborder_dl; // Adjust the bottom of screen to fit the screen size X += 4; if (_ms_pal_detected) { _ms_b0_dll[X] = 0x07; // 8 lines _ms_b1_dll[X] = 0x07; // 8 lines } else { _ms_b0_dll[X] = 0x00; // 1 line _ms_b1_dll[X] = 0x00; // 1 line } // Display the sphinx and platforms multisprite_sparse_tiling(tilemap_sphinx_data_ptrs, 0, 4, 14); multisprite_sparse_tiling(tilemap_arrangement_A_data_ptrs, 0, 4, 14); // Left and right borders for (y = 0; y < PLAYFIELD_HEIGHT; y += 16) { multisprite_display_sprite_fast(0, y, border, 1, 3); multisprite_display_sprite_fast(116, y, border, 1, 3); } multisprite_display_sprite_fast(121, 0, sideone, 8, 0); // Score display score = 0; update_score = 0; display_score_update(); multisprite_display_tiles(120, 1, display_score_str, 5, 3); // Save the background multisprite_save(); update_overlay = 2; // Enemies palette *P0C1 = 0x04; // Dark gray *P0C2 = 0x08; // Medium gray *P0C3 = 0x34; // Red // Bombjack palette *P1C1 = multisprite_color(0x87); // Light blue *P1C2 = multisprite_color(0x3c); // Rose *P1C3 = 0x00; // Black // Bomb palette *P2C1 = multisprite_color(0x34); // Red *P2C2 = multisprite_color(0x1c); // Yellow *P2C3 = 0x0f; // White // Platform palette *P3C1 = multisprite_color(0x24); // Red *P3C2 = multisprite_color(0x28); // Orange *P3C3 = multisprite_color(0x1c); // Yellow // Blue palette *P4C1 = multisprite_color(0x84); // Dark blue // Sphinx palette *P5C1 = multisprite_color(0x12); // Red *P5C2 = multisprite_color(0x15); // Orange *P5C3 = multisprite_color(0x18); // Yellow // Explosion palette explosion_color1 = multisprite_color(0x24); // Red explosion_color2 = multisprite_color(0x28); // Orange explosion_color3 = multisprite_color(0x1c); // Yellow button_pressed = 0; } void explosion_palette_roll() { char roll = explosion_color3; explosion_color3 = explosion_color2; explosion_color2 = explosion_color1; explosion_color1 = roll; *P7C1 = explosion_color1; *P7C2 = explosion_color2; *P7C3 = explosion_color3; } void main() { game_init(); display_init(); // Main loop do { // Display overlay if (update_overlay != 0) { update_overlay--; multisprite_clear_overlay(); display_arrangement(current_arrangement); multisprite_save_overlay(); } joystick_input(); bombjack(); explosion_palette_roll(); draw_sprites(); if (update_score) { display_score_update(); update_score = 0; } multisprite_flip(); } while(1); } The result is quite funny and not far from the actual arcade game dynamics. I'll stop there with bombjack at the moment (maybe will I go back to this when the cc7800 project will have reached the end), and go for R-Type. Next week, I'll try to show how to use sparse tiling to make a 60fps horizontal scrolling shooter. example_bombjack.mp4 bombjack_sprites.yaml example_bombjack.a78 7 Quote Link to comment Share on other sites More sharing options...
TIX Posted October 23, 2023 Share Posted October 23, 2023 49 minutes ago, bsteux said: . So here's the sequel to the previous example: bombjack with the bombjack character. I've used the sprites provided by @TIX, which I scaled down to 16 pixels high (with mediocre success) and colorized to 160B. Well, it would need rework, but it's not the main goai. I think I mixed up jumping and dancing sprites... . Hey @bsteux, looking good man ! I'll be happy to help flesh it out some more if you are planning to move forward to a full port. PM me 4 1 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted October 30, 2023 Author Share Posted October 30, 2023 Hi, Here is another example of using sparse tiling. I've added a new routine that enables horizontal scrolling (sparse_tiling_scroll) based on a possibly infinite level width (here for example, I've designed with tiled a RType-like level which is 13x512 tiles wide and that takes 2.5KB of ROM). Note that this is not completely satisfactory : the CPU load is not evenly balanced among frames, so it should still be enhanced to be really usable. I'll do that for the next time, as well as add a few shuttles and lasers... Here is the example, as always in C, not using a single line of ASM, no RAM and no DLI : #include "string.h" #define HORIZONTAL_SCROLLING #define _MS_BOTTOM_SCROLLING_ZONE 1 #include "sparse_tiling.h" #include "joystick.h" // Generated from sprites7800 RType_tiles.yaml #include "example_RType_tiles.c" // Generated from tiles7800 --sparse RType_tiles.yaml --varname tilemap_level1 RType_level1.tmx #include "example_RType_level1.c" char scroll_background_counter; void scroll_background() { char c; signed char pos1, pos2, pos3, pos4; pos1 = -scroll_background_counter; pos2 = pos1 + 80; pos3 = pos1 - 8; if (pos3 < -16) pos3 += 16; pos4 = pos3 + 80; if (_ms_buffer) { X = _MS_DLL_ARRAY_SIZE + 1; scroll_background_counter++; if (scroll_background_counter == 16) scroll_background_counter = 0; } else X = 1; _ms_tmpptr = _ms_dls[X]; for (c = 0; c != 3; c++) { // Modify bytes 4 and 8 of the DLL entries (x position of background sprites= _ms_tmpptr[Y = 4] = pos1; _ms_tmpptr[Y = 8] = pos2; _ms_tmpptr = _ms_dls[++X]; _ms_tmpptr[Y = 4] = pos1; _ms_tmpptr[Y = 8] = pos2; _ms_tmpptr = _ms_dls[++X]; _ms_tmpptr[Y = 4] = pos3; _ms_tmpptr[Y = 8] = pos4; _ms_tmpptr = _ms_dls[++X]; _ms_tmpptr[Y = 4] = pos3; _ms_tmpptr[Y = 8] = pos4; _ms_tmpptr = _ms_dls[++X]; } } ramchip int score; ramchip char update_score; ramchip char display_score_str[5]; void display_score_update() { char display_score_ascii[6]; itoa(score, display_score_ascii, 10); Y = strlen(display_score_ascii); for (X = 0; X != 5 - Y; X++) { display_score_str[X] = 0; // '0' } X = 4; do { display_score_str[X--] = ((display_score_ascii[--Y] - '0') << 1); } while (Y); } void main() { char button_pressed = 0; scroll_background_counter = 0; joystick_init(); multisprite_init(); sparse_tiling_init(tilemap_level1_data_ptrs); multisprite_set_charbase(digits); // Green (background) color *P3C1 = multisprite_color(0xd0); *P3C3 = multisprite_color(0xd1); *P3C2 = multisprite_color(0xd2); // Beige palette *P4C1 = multisprite_color(0x12); *P4C2 = multisprite_color(0x14); *P4C3 = multisprite_color(0x16); // Blue palette *P5C1 = multisprite_color(0x84); // Dark blue *P5C2 = multisprite_color(0x87); // Light blue *P5C3 = multisprite_color(0xac); // Turquoise // Rose palette *P6C1 = multisprite_color(0x34); // Dark Rose *P6C2 = multisprite_color(0x38); // Rose *P6C3 = multisprite_color(0x3c); // Light Rose // Grey palette *P7C1 = 0x04; // Dark gray *P7C2 = 0x08; // Medium gray *P7C3 = 0x0c; // Dark gray multisprite_display_tiles(0, 13, display_score_str, 5, 5); // Background display char c, y = 0; for (c = 0; c != 3; c++) { y += 16; multisprite_display_sprite_ex(0, y, background_level1, 20, 3, 0); multisprite_display_sprite_fast(80, y, background_level1, 24, 3); y += 16; multisprite_display_sprite_ex(0, y, background_level1_1, 20, 3, 0); multisprite_display_sprite_fast(80, y, background_level1_1, 24, 3); y += 16; multisprite_display_sprite_ex(-8, y, background_level1, 20, 3, 0); multisprite_display_sprite_fast(72, y, background_level1, 24, 3); y += 16; multisprite_display_sprite_ex(-8, y, background_level1_1, 20, 3, 0); multisprite_display_sprite_fast(72, y, background_level1_1, 24, 3); } // Save it multisprite_save(); sparse_tiling_display(); multisprite_save_overlay(); multisprite_flip(); sparse_tiling_display(); multisprite_save_overlay(); do { scroll_background(); sparse_tiling_scroll(2); char x = _tiling_xpos[0]; score = x; display_score_update(); multisprite_flip(); } while (1); } I know, it's in 160A (aka poor man's graphics mode), and the routine is still not compatible with 160B (and in that case in order to be tractable, it'd need to use immediate mode), but at least it's simple and economical. It leaves a lot of CPU (it will be better when balanced) to be used for sprites and uses a bunch of colors (5 palettes here). In attachment, a video with a7800 (not on real hardware : I'm in vacations and haven't brought my Atari 7800 along with me...), all the tiles definitions and the latest win32 installer for cc7800. Maybe nothing next week, as I'm far away from my atari ! But the spaceships are coming, beware... example_horizontal_scrolling.mp4 RType_tiles.tsx RType_level1.tmx RType_tiles.yaml example_horizontal_scrolling.a78 cc7800-0.2.16-x86_64.msi 13 Quote Link to comment Share on other sites More sharing options...
KrunchyTC Posted October 31, 2023 Share Posted October 31, 2023 Still looks cool 1 Quote Link to comment Share on other sites More sharing options...
Defender_2600 Posted October 31, 2023 Share Posted October 31, 2023 12 hours ago, bsteux said: But the spaceships are coming, beware... If you need some help with R-Type's graphics, let me know. 5 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted October 31, 2023 Author Share Posted October 31, 2023 2 hours ago, Defender_2600 said: If you need some help with R-Type's graphics, let me know. Oh yes, I definitely need some help with the graphics... Whaou, your big boss looks so cool. I was thinking to implement exactly this one in my example (never played R-Type in my life, the only thing I know of R-Type is this iconic boss...)... Can I use it for the example ? It should fit perfectly in 160B... 3 Quote Link to comment Share on other sites More sharing options...
Defender_2600 Posted November 1, 2023 Share Posted November 1, 2023 On 10/31/2023 at 11:00 AM, bsteux said: I was thinking to implement exactly this one in my example (never played R-Type in my life, the only thing I know of R-Type is this iconic boss...)... Can I use it for the example ? Sure! On 10/31/2023 at 11:00 AM, bsteux said: It should fit perfectly in 160B... The Dobkeratops screen is almost without background graphics, so it can be done in 160B, leaving the animation of his tail in 160A. I attach the graphics and the palette (11 colors), I still have to draw the four eyes because they have to use the other palette, sharing some colors with the spaceship. I hope you can use 160B mode for the player's spaceship. I'll get you some more graphics in the next few days. It would also be nice to have some music from @Synthpopalooza. The Dobkeratops screen: 3 Quote Link to comment Share on other sites More sharing options...
+saxmeister Posted November 1, 2023 Share Posted November 1, 2023 I absolutely love the way the devs handled this. Since the Dobkeratops is so graphically intense, they faded the background to black and then removed all of the tiles that were there. What a genius way to hide limitations and turn it into a great special effect and set the mood. @Defender_2600 your adaptation of the arcade artwork is great! @bsteux your C compiler and the code you have been not only demonstrating, but sharing for all to see is an impressive and generous display. Thank you! 3 Quote Link to comment Share on other sites More sharing options...
+karri Posted November 6, 2023 Share Posted November 6, 2023 Just a small note about the compiler. I really like the verbose output of the compilation result. You can easily confirm where your ram variables go and how much space they get. So far I have had to add some extra routines and split complex lines into simpler code. But I like the very clear error statemants so you know where the limitations are. I am not really creating anything remarkable right now. Just re-writing my adventure engine to generate suitable code for the cc7800. 2 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted November 7, 2023 Author Share Posted November 7, 2023 19 hours ago, karri said: Just a small note about the compiler. I really like the verbose output of the compilation result. You can easily confirm where your ram variables go and how much space they get. So far I have had to add some extra routines and split complex lines into simpler code. But I like the very clear error statemants so you know where the limitations are. I am not really creating anything remarkable right now. Just re-writing my adventure engine to generate suitable code for the cc7800. One small remark for all the people who start coding with cc7800 : please use 'int' only when necessary, and use 'char' whenever possible (like with cc65). Succession of operations of 'int' (16 bits) have not been implemented in cc7800 and must be cut into several lines using intermediate results (it's not done automatically by cc7800. It will just tell you it's too complex). Shifting is also not implemented for 16 bits variables. Please also note that 'char' is unsigned by default (like cc65, but unlike gcc). Good luck with your adventure engine. It looks cool. I liked these when I was young. 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.