Jump to content
IGNORED

cc7800, a C compiler dedicated to the Atari 7800


bsteux

Recommended Posts

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.

 

gameover.png

shmup.png

shmup_enemies.png

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!

  • Like 1
Link to comment
Share on other sites

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.

Nyttkuva2023-10-09201104.thumb.png.3bd0d9f5e57f32303d8fafa99f82ba76.png

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 !

  • Like 1
Link to comment
Share on other sites

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.

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.

  • Like 1
Link to comment
Share on other sites

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

  • Like 3
Link to comment
Share on other sites

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.

 

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 ?

  • Like 1
Link to comment
Share on other sites

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

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

  • Like 1
Link to comment
Share on other sites

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.

 

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

  • Like 4
Link to comment
Share on other sites

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 :

bombjack_tiles.png.9d4ceb00a9856296569c90cc55438d9e.png

And then, with the Tiled editor, we reconstitute the whole sphinx :

Screenshotfrom2023-10-1609-10-11.thumb.png.3f9226bcc26d14b21e541d9847c936d6.png

 

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 :

Screenshotfrom2023-10-1609-09-24.thumb.png.dcf2bd0776652418e82729a7a971375f.png

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 :

Screenshotfrom2023-10-1609-00-29.thumb.png.3327c66b3afe6eba22d6944e3186ff78.png

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

bombjack_sprites.yaml bombjack_tiles.tsx bombjack_sphinx.tmx bombjack_tiles.yaml example_bombjack1.a78

  • Like 6
Link to comment
Share on other sites

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 by KrunchyTC
Link to comment
Share on other sites

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.

  • Like 1
Link to comment
Share on other sites

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

 

  • Like 1
Link to comment
Share on other sites

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.

  • Like 2
Link to comment
Share on other sites

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.

bombjack_sprites.png

bombjack_sprites.yaml example_bombjack.a78

  • Like 7
Link to comment
Share on other sites

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

 

 

 

  • Like 4
  • Thanks 1
Link to comment
Share on other sites

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

RType_background.png

RType_tiles.png

RType_tiles.tsx RType_level1.tmx RType_tiles.yaml example_horizontal_scrolling.a78 cc7800-0.2.16-x86_64.msi

  • Like 13
Link to comment
Share on other sites

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

 

  • Like 3
Link to comment
Share on other sites

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.

 

7800Dobkeratopsgraphics.PNG.41961da9e6d6c6cd65bb17dc46fe8f99.PNG

 

 

7800Dobkeratopspalette.PNG.03857a034d29e7fea9bf789dda15dbc9.PNG

 

The Dobkeratops screen:

 

  • Like 3
Link to comment
Share on other sites

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!

  • Like 3
Link to comment
Share on other sites

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.

Nyttkuva2023-11-06180230.thumb.png.f2984033ce680d67a30396fce737fef4.png

  • Like 2
Link to comment
Share on other sites

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.

Nyttkuva2023-11-06180230.thumb.png.f2984033ce680d67a30396fce737fef4.png

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.

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