Jump to content
IGNORED

cc7800, a C compiler dedicated to the Atari 7800


bsteux

Recommended Posts

13 hours ago, KrunchyTC said:

Interesting. So bidirectional scrolling is always going to be tough to do. This is a GPU/CPU cycle limited task, rather than RAM limited?

It's definitely not RAM limited. With a 16k RAM bank on a 7800, you can use a 128x128 map, which is quite big (a screen is 20 tiles wide and 14 tiles high). And bigger if you're using ROM... It's really not tough to do, as MARIA was designed to do it rather easily, but you have to accept to tile in 160A/320A mode, limit the size of the display, or use background in some areas to spare some CPU/DMA...

  • Like 1
Link to comment
Share on other sites

Indeed, in direct mode, you have much more bandwidth, because 160B background filling requires only 10 * 3 + 81 * 3 = 273 DMA cycles, which leaves 129 cycles per line to display overlaying sprites. A 8 pixels wide sprite display should consumes 10 + 4 * 3 = 22 cycles, so one should be able to display 5 such sprites on the same line. At the moment my cc7800 lib only supports 160A display and was focusing on "tiling" or "character" mode background scrolling / tiling. OK. I understand that I must support more advanced display modes (especially 320C looks promising), which shouldn't be an issue now that everything is in place. I'll work on that for the next version of cc7800.

 

  • Like 3
Link to comment
Share on other sites

Hi,

This week's progress : the integration of TIA sound effects in cc7800 (using a new header: sfx.h). Two functions are defined : sfx_schedule and sfx_play, which mush be called every frame. I've integrated the whole library of TIA sounds from 7800basic (117 sounds!), courtesy of @RevEng (I've used a new tool in Tools7800: basic2cc7800, which grabs the 7800basic arrays and convert them to C arrays...). Here is the example (examples/example_sfx.c) :

 

#include "conio.h"
#include "joystick.h"
#include "stdlib.h"
#include "sfx.h"

// SFX converted from 7800basic (soundtest.bas example file) with basic2cc7800 (from tools7800) :
// All sounds are copyright Mike Saarna (msaarna), aka RevEng, and I guess most of them have been generated using his
// tool sound2tia

const char *array_name[117] = {
	"sfx-salvolasershot", "sfx-spaceinvshoot", "sfx-berzerkrobotdeath", "sfx-echo1", "sfx-echo2", "sfx-jumpman", "sfx-cavalry", "sfx-alientrill1",
	"sfx-alientrill2", "sfx-pitfalljump", "sfx-advpickup", "sfx-advdrop", "sfx-advbite", "sfx-advdragonslain", "sfx-bling", "sfx-dropmedium",
...
	"sfx-bigboom", "sfx-thud", "sfx-bump", "sfx-shouty", "sfx-quack"
};

const char sfx_salvolasershot[72] = {
	0x10, 0x18, 0x01, 0x04, 0x08, 0x08, 0x05, 0x08, 0x07, 0x04, 0x08, 0x07, 0x05, 0x08, 0x07, 0x06,
	0x08, 0x07, 0x07, 0x08, 0x06, 0x06, 0x08, 0x06, 0x07, 0x08, 0x06, 0x08, 0x08, 0x06, 0x09, 0x08,
...
};

...
  
const char sfx_quack[42] = {
	0x10, 0x10, 0x00, 0x15, 0x06, 0x08, 0x15, 0x06, 0x09, 0x15, 0x06, 0x0a, 0x14, 0x06, 0x0b, 0x14,
	0x06, 0x0c, 0x14, 0x06, 0x0d, 0x14, 0x06, 0x0e, 0x13, 0x06, 0x0f, 0x13, 0x06, 0x0f, 0x13, 0x06,
	0x0f, 0x13, 0x06, 0x0f, 0x13, 0x06, 0x0f, 0x00, 0x00, 0x00
};

const char *array_data[117] = {
	sfx_salvolasershot, sfx_spaceinvshoot, sfx_berzerkrobotdeath, sfx_echo1, sfx_echo2, sfx_jumpman, sfx_cavalry, sfx_alientrill1,
	sfx_alientrill2, sfx_pitfalljump, sfx_advpickup, sfx_advdrop, sfx_advbite, sfx_advdragonslain, sfx_bling, sfx_dropmedium,
	sfx_electrobump, sfx_explosion, sfx_humanoid, sfx_transporter, sfx_twinkle, sfx_electroswitch, sfx_nonobounce, sfx_70stvcomputer,
	sfx_alienlife, sfx_chirp, sfx_plonk, sfx_spawn, sfx_maser, sfx_rubbermallet, sfx_alienkitty, sfx_electropunch,
	sfx_drip, sfx_ribbit, sfx_wolfwhistle, sfx_cabwhistle, sfx_jumpo, sfx_pulsecannon, sfx_spring, sfx_buzzbomb,
	sfx_bassbump, sfx_hophop, sfx_distressed, sfx_ouch, sfx_laserrecoil, sfx_electrosplosion, sfx_hophip, sfx_hophipquick,
	sfx_bassbump2, sfx_pickupprize, sfx_distressed2, sfx_pewpew, sfx_denied, sfx_teleported, sfx_alienklaxon, sfx_crystalchimes,
	sfx_oneup, sfx_babywah, sfx_gotthecoin, sfx_babyribbit, sfx_squeek, sfx_whoa, sfx_gotthering, sfx_yahoo,
	sfx_warcry, sfx_downthepipe, sfx_powerup, sfx_falling, sfx_eek, sfx_uhoh, sfx_anotherup, sfx_bubbleup,
	sfx_jump1, sfx_plainlaser, sfx_aliencoo, sfx_simplebuzz, sfx_jump2, sfx_jump3, sfx_dunno, sfx_snore,
	sfx_uncovered, sfx_doorpound, sfx_distressed3, sfx_eek2, sfx_rubberhammer, sfx_alienbuzz, sfx_anotherjumpman, sfx_anotherjumpdies,
	sfx_longgongsilver, sfx_strum, sfx_dropped, sfx_alienaggressor, sfx_electroswitch2, sfx_gooditem, sfx_babyribbithop, sfx_distressed4,
	sfx_hahaha, sfx_yeah, sfx_arfarf, sfx_activate, sfx_hahaha2, sfx_wilhelm, sfx_poof1, sfx_poof2,
	sfx_dragit, sfx_roarcheep, sfx_roarroar, sfx_deeproar, sfx_echobang, sfx_tom, sfx_clopclop, sfx_museboom,
	sfx_bigboom, sfx_thud, sfx_bump, sfx_shouty, sfx_quack
};

char page, choice, index;
char *name;
ramchip char i, j, string[10];
ramchip char prevjoy;

#define MAXPAGES ((sizeof(array_data) / 2 / 20) + 1)

void display_page()
{
    clrscr();
    textcolor(0);
    gotoxy(0, 0);
    itoa(page, string, 10);
    cputs(string);
    putch('/');
    itoa(MAXPAGES, string, 10);
    cputs(string);
    textcolor(2);
    gotoxy(6, 0);
    cputs("SFX Examples");
    textcolor(7);
    for (i = 0, X = 1; X < page; X++) {
        i += 20;
    }
    for (j = 0; j < 20; j++) {
        Y = i + j;
        if (Y < sizeof(array_data) / 2) {
            name = array_name[Y];
            gotoxy(2, j + 2);
            cputs(name);
        }
    }
    
    textcolor(3); gotoxy(0, choice + 2); putch('>');
}

void main()
{
    page = 1;
    choice = 0;
    index = 0;
    prevjoy = 0;

    joystick_init();
    display_page();

    do {
        joystick_update();
        if (joystick[0] != prevjoy) {
            if (joystick[0] & JOYSTICK_UP) {
                if (choice > 0) {
                    textcolor(3); gotoxy(0, choice + 2); putch(' ');
                    choice--; index--;
                    gotoxy(0, choice + 2); putch('>');
                }
            } else if (joystick[0] & JOYSTICK_DOWN) {
                if (choice < 19 && index < sizeof(array_data) / 2 - 1) {
                    textcolor(3); gotoxy(0, choice + 2); putch(' ');
                    choice++; index++;
                    gotoxy(0, choice + 2); putch('>');
                }
            } else if (joystick[0] & JOYSTICK_RIGHT) {
                if (page < MAXPAGES) {
                    page++; index += 20;
                    display_page();
                }
            } else if (joystick[0] & JOYSTICK_LEFT) {
                if (page > 1) {
                    page--; index -= 20;
                    display_page();
                }
            } else if (joystick[0] & JOYSTICK_BUTTON1) {
                if (index < sizeof(array_data) / 2) {
                    sfx_schedule(array_data[index]);
                }
            }
        }
        prevjoy = joystick[0];
        
        // Wait for VBLANK
        if (*MSTAT & 0x80) { // If in VBLANK
            sfx_play();
            if (multisprite_pal_frame_skip())
                sfx_play(); // Advance twice every 5 frames (to cope with 60Hz instead of 50Hz)
            while (*MSTAT & 0x80); // Wait for end of VBLANK
        }
    } while (1);
}

 

and the result is in attachment...

To follow on my agenda :

- The integration of Pokey music through Raster Media Tracker player integration (maybe too ambitious, let's see)

- The support of 160B, 320B and 320C graphics in sprites7800 (from tools7800, my collection of 7800 utilities (in Rust!))

- The support of sparse and mixed mode tiling

- The support of bitmaps (i.e. direct mode display)

- The support of scrolling of bitmaps... if my free time allows it...

- Any other suggestion ?

Whooo... Let's see how far I can go on this... let's do it while I still have fun doing this...

 

 

 

cc7800-0.2.7-x86_64.msi example_sfx.a78

  • Like 8
Link to comment
Share on other sites

On 6/14/2023 at 3:07 AM, bsteux said:

Definitely no, and indeed the Maria chip is very strong at handling bidirectionnal scrolling (in contrary to the NES VPU which requires very specific programming OR special hardware like metroid if the field is bigger than 2 screens wide). BUT unfortunately, a fully tiled background is practically only usable in 160A mode (160 pixels wide and 4 colors only) if you want to keep a little CPU time and/or DMA bandwidth to display some sprites over your field... (roughly, it takes 10 + 3 + 9 * 21 = 202 cycles / 402 available in 160A, and 10 + 3 + 9 * 41 = 382 cycles out of 402 in 160B or 320B/C).

A solution would be partially tiled background, a solution I'll try to develop a library for in the future...

Because I am dumb, it took me a while to actually comprehend what you said here 😆 now I get it. This problem sounds like it would probably be an issue on any platform of the era.

Edited by KrunchyTC
Link to comment
Share on other sites

Hi,

Here are the latest news on cc7800 : the Raster Music Tracker (https://github.com/VinsCool/RASTER-Music-Tracker) integration, using the famous Pokey chip.

You can find below the `examples/example_rmtracker.c` source file, as well as the result running on real hardware.

A few notes about this example :

- This code is based on the RMT player code ported to DASM by @Eckhard Stolberg (Are you still around?). The code is old but seems capable of playing recent RMT files, as least some in the github repository of @VinsCool. Maybe @Vinscool could tell us how to update it to the latest RMT version ? Very cool Music Tracker indeed, and kept up to date : it's running perfectly on my laptop. Amazing tracker indeed.

- I've taken a RMT track by pure random (maybe guided by the "Alien3" name...) and converted it into ASM using the 7800rmt2asm.c utility found in the 7800Basic repository (I guess this is a code by @RevEng). By the way, most of the development effort on my side was on the integration of DASM code in cc7800, which is now quite straightforward.

- I've slightly modified the produced ASM file (renamed .RMTSTART to RMTSTART, and added an optionnal "; codesize:" tag for cc7800 to figure out what is the compiled size of this assembly code.

- I've written a simple player shown below, and tested it in a7800: perfect result.

- Tried on the real Atari 7800 with my brand new Concerto cart with soldered Hokey chip... and nothing... No sound. No crash but no sound, except a vaguely faded rhythm (maybe is this just some noise related to processor activity).

- OK. Read a little bit about the problems with the Hokey chips, and figured out that I should move the Pokey chip to address 0x4000 instead of 0x450, and it worked...

Conclusion : RMTTracker is a very nice way of composing and playing (quality) music on the Atari 7800, BUT apparently (if I didn't make a mistake) there are still some issues with the Concerto firmware when Pokey is located at 0x450 (which is very desirable if you want to use extra RAM at 0x4000). So at the moment, it's Pokey OR extra RAM. Any news about that on the Concerto side @batari ?

 

 

#include "prosystem.h"
#include "rmtplayer.h"
#include "examples/RMT_Alien3.asm"
#include "conio.h"

char i, j, k, l;

void main()
{
    clrscr();
    gotoxy(6, 0);
    textcolor(2);
    cputs("Raster Music Tracker Player");

    for (i = 0; i != 4; i++) {
        gotoxy(14 + (i << 2), 19);
        putch('1' + i);
    }

    asm("LDX #<RMTSTART"); // Give pointer to RMT music
    asm("LDY #>RMTSTART");
    load(0); // Song line in A register 
    asm("JSR rmt_init");

    textcolor(1);

    i = 0;

    do {
        // Display the volume for each channel
        k = 14 + (i << 2);
        l = 17 - (trackn_audc[Y = i] & 0x0f);
        for (j = 2; j < l; j++) {
            gotoxy(k, j);
            putch(' ');
        }
        for (; j < 18; j++) {
            gotoxy(k, j);
            putch(127);
        }
        i++;
        if (i == 4) i = 0;

        *BACKGRND = 0x00;
        while (*MSTAT & 0x80); // Wait for end of VBLANK
        while (!(*MSTAT & 0x80)); // Wait for VBLANK
        *BACKGRND = 0x0f;
        asm("JSR rmt_play");
    } while(1);
}

 

cc7800-0.2.8-x86_64.msi example_rmtplayer.a78

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

1 hour ago, bsteux said:

- This code is based on the RMT player code ported to DASM by @Eckhard Stolberg (Are you still around?). The code is old but seems capable of playing recent RMT files, as least some in the github repository of @VinsCool. Maybe @Vinscool could tell us how to update it to the latest RMT version ? Very cool Music Tracker indeed, and kept up to date : it's running perfectly on my laptop. Amazing tracker indeed.

- I've taken a RMT track by pure random (maybe guided by the "Alien3" name...) and converted it into ASM using the 7800rmt2asm.c utility found in the 7800Basic repository (I guess this is a code by @RevEng). By the way, most of the development effort on my side was on the integration of DASM code in cc7800, which is now quite straightforward.

Heads-up that there's some kind of relocation bug for a good chunk of RMTs (say 1/3) where they don't loop or end correctly if they're not located at $4000. I've been trying to track down this bug, which is why I haven't released the RMT tracker integration in 7800basic yet.

  • Like 1
Link to comment
Share on other sites

58 minutes ago, RevEng said:

Heads-up that there's some kind of relocation bug for a good chunk of RMTs (say 1/3) where they don't loop or end correctly if they're not located at $4000. I've been trying to track down this bug, which is why I haven't released the RMT tracker integration in 7800basic yet.

Thanks for the info. I'll have a look at this issue on my side.

  • Like 1
Link to comment
Share on other sites

14 hours ago, bsteux said:

Conclusion : RMTTracker is a very nice way of composing and playing (quality) music on the Atari 7800, BUT apparently (if I didn't make a mistake) there are still some issues with the Concerto firmware when Pokey is located at 0x450 (which is very desirable if you want to use extra RAM at 0x4000). So at the moment, it's Pokey OR extra RAM. Any news about that on the Concerto side @batari ?

The CEM devices fix any issues with POKEY at $0450. The CEM devices with the smallest feature set (CEM #0) will be essentially free to Concerto owners who want one (just pay shipping.)

 

Regardless, I will continue to improve firmware for the best possible functioning for those who don't get a CEM #0 for whatever reason.

  • Like 1
Link to comment
Share on other sites

12 hours ago, batari said:

The CEM devices fix any issues with POKEY at $0450. The CEM devices with the smallest feature set (CEM #0) will be essentially free to Concerto owners who want one (just pay shipping.)

 

Regardless, I will continue to improve firmware for the best possible functioning for those who don't get a CEM #0 for whatever reason.

Ah, OK, CEM stands for "Concerto Enhancement Module". Looks cool. Put me on your list when it's ready to ship.

Link to comment
Share on other sites

On 6/27/2023 at 3:29 PM, RevEng said:

Heads-up that there's some kind of relocation bug for a good chunk of RMTs (say 1/3) where they don't loop or end correctly if they're not located at $4000. I've been trying to track down this bug, which is why I haven't released the RMT tracker integration in 7800basic yet.

I think I've found at least one small issue, which may be the one you're referring to. Indeed, the Goto looping in the RMT song list is actually a 0xfe followed by a dummy byte + 2 absolute address bytes. In your 7800rmt2asm.c file, you're looking for a 0xfe followed by 0x00, which is not always the case for a goto (for instance, in my favorite Alien3 song, the end looping goto was 0xfe 0x02, which was then not relocated by your program and miserably failed at the end of music - which I hadn't even noticed, oops). If you change line 249 of 7800rmt2asm.c to

		if( (buffer[t+rmtstart]==0xFE) /*&& (buffer[t+1+rmtstart]==0x00)*/)

the music loops at the end, and everything's fine. Tell me if this helps...

  • Like 2
Link to comment
Share on other sites

1 hour ago, bsteux said:

Tell me if this helps...

That seems to be a great lead - thanks!

 

The $FE,$00 flag I took from this format description and this comment in the latest RMT driver. The ported RMT driver code (and the latest RMT driver code) doesn't seem to do anything with the byte after $FE, so I'm guessing maybe that byte can vary, for some unknown reason.

  • Like 2
Link to comment
Share on other sites

9 hours ago, RevEng said:

Tested out, and the fix for 7800rtm2asm seems to be working great. I've pushed it to the master repo. :thumbsup:

That's very nice. RMT is a really very good Pokey tracker (MIDI compatible!) for the 7800 platform. I've shown it to my 17-old daughter (who is composing on Abletone Live), and she was amazed.

  • Like 6
Link to comment
Share on other sites

  • 2 weeks later...

Hi,

Here is the latest new thingy in cc7800: sparse tiling. What the #^% is that ?

- You want to make some nice Mario Bros like levels ? Draw them with Tiled, the famous tiles editor (see sparse.tmx and test.tsx below).

- You provide a description of your tiles (tiles.yaml) and generate the C code for your tiles with sprites7800 (from https://github.com/steux/tools7800)

- From your tiled file, you generate the C code for your Maris Bros like level, using tiles7800 and the new --sparse option (from https://github.com/steux/tools7800 again):

tiles7800 --sparse tiles.yaml sparse.tmx

 

You write a simple C code around your generates data code, using the brand new sparse_tiling.h header provided in cc7800 (examples/example_sparse_tiling.c) :

#include "stdlib.h"
#include "string.h"
#include "prosystem.h"
#include "joystick.h"
#define _MS_TOP_SCROLLING_ZONE 1

// Code generated by tiles7800 --sparse
const char tilemap_0_0[6] = {0, 0, 0, 0, 0, 0};
const char tilemap_0_data[] = {5, 0, tilemap_0_0, 0x60, tilemap_0_0 >> 8, (1 << 5) | ((-6) & 0x1f), 33, 0x7f, 0xff};
const char tilemap_1_0[5] = {0, 0, 0, 0, 0};
const char tilemap_1_data[] = {4, 0, tilemap_1_0, 0x60, tilemap_1_0 >> 8, (1 << 5) | ((-5) & 0x1f), 29, 0x7f, 0xff};
const char tilemap_2_0[4] = {0, 0, 0, 0};
const char tilemap_2_data[] = {3, 0, tilemap_2_0, 0x60, tilemap_2_0 >> 8, (1 << 5) | ((-4) & 0x1f), 24, 0x7f, 0xff};
const char tilemap_3_0[3] = {0, 0, 0};
const char tilemap_3_1[1] = {22};
...
#define TILING_HEIGHT 32
#define TILING_WIDTH 32
#include "sparse_tiling.h"

// Code generated by sprites7800
reversed scattered(16,14) char tiles[224] = {
	0x50, 0x50, 0x00, 0x00, 0xaa, 0xbf, 0xfe, 0xfe, 0xab, 0xea, 0xcf, 0xcf, 0x2a, 0xa8, 0x50, 0x50,
	0x13, 0xc4, 0xd6, 0xff, 0xee, 0xee, 0xaf, 0xfa, 0x3c, 0x3c, 0x20, 0x08, 0x50, 0x50, 0x14, 0xc4,
	0xff, 0xff, 0xde, 0xde, 0xaa, 0xfa, 0xf3, 0xf3, 0x20, 0x08, 0x50, 0x50, 0x12, 0x30, 0x3f, 0xfc,
	0xde, 0xde, 0xa6, 0xfe, 0xc3, 0xc3, 0x20, 0x08, 0x05, 0x05, 0x12, 0x3c, 0x2f, 0xfc, 0xee, 0xee,
...
    0xbd, 0xe0, 0xff, 0xf0, 0xff, 0xf0, 0xbf, 0xe0, 0x03, 0xc0, 0xbf, 0xe0, 0xbf, 0xe0, 0x0f, 0x00, 0xbf, 0xe0, 0xbf, 0xe0,
	0x3f, 0xc0, 0xff, 0xf0, 0xff, 0xf0, 0x3f, 0xc0, 0x03, 0xc0, 0x3f, 0xc0, 0x3f, 0xc0, 0x0f, 0x00, 0x3f, 0xc0, 0x3f, 0xc0,
	0x2a, 0x80, 0xaa, 0xa0, 0xaa, 0xa0, 0x2a, 0x80, 0x02, 0x80, 0x2a, 0x80, 0x2a, 0x80, 0x0a, 0x00, 0x2a, 0x80, 0x2a, 0x80,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

ramchip int score;
ramchip char display_score_str[5];
ramchip char display_score_ascii[6];

ramchip int x, y;

void display_score_update()
{
    itoa(score, display_score_ascii, 10);
    Y = strlen(display_score_ascii); 
    for (X = 0; X != 5 - Y; X++) {
        display_score_str[X] = DIGITS_INDEX; // '0'
    }
    X = 4;
    do {
        display_score_str[X--] = DIGITS_INDEX + ((display_score_ascii[--Y] - '0') << 1);
    } while (Y);
}

void main()
{
    score = 0;
    x = 0; y = 0;

    multisprite_init();
    multisprite_set_charbase(tiles);
    tiling_init(tilemap_data);
    joystick_init();
    multisprite_display_tiles(0, 0, display_score_str, 5, 0);
    multisprite_save();
    

    *P0C1 = multisprite_color(0x1c); // Setup Palette 0: Yellow
    *P0C2 = multisprite_color(0xc5); // Green
    *P0C3 = multisprite_color(0xcd); // Light green
   
    *P1C1 = 0x0f; // White
    *P1C2 = multisprite_color(0x9D);
    *P1C3 = multisprite_color(0x97);
    
    *P2C1 = multisprite_color(0x92);
    *P2C2 = multisprite_color(0x97);
    *P2C3 = multisprite_color(0x9D);

    // Rainbow
    *P4C1 = multisprite_color(0x4A);
    *P4C2 = multisprite_color(0x5A);
    *P4C3 = multisprite_color(0x8A);

    *P5C1 = multisprite_color(0xDA);
    *P5C2 = multisprite_color(0x1A);
    *P5C3 = multisprite_color(0x2A);
    
    *P6C1 = multisprite_color(0x3A);
    *P6C2 = 0x0f;

    tiling_goto(0, 0);

    // Main loop
    do {
        multisprite_flip();
        joystick_update();
        if (joystick[0] & JOYSTICK_LEFT) x--; 
        else if (joystick[0] & JOYSTICK_RIGHT) x++;
        if (joystick[0] & JOYSTICK_UP) y--; 
        else if (joystick[0] & JOYSTICK_DOWN) y++;
        tiling_goto(x, y);
        score++;
        display_score_update();
    } while(1);
}

 

And the tiling_goto function does all the magic : displaying your tilesets at the given pixel precise position.

So what are the advantages of this display method ?

- All the data are in ROM, no need for RAM (in contrary to bitmap display).

- Every time there are holes (sparsity) in the tileset, some CPU is freed for something else than display.

- You can mix at will 160A and 160B tiles, and change palette on the fly (160A : economy of DMA, CPU and ROM - 3 colors only, 160B : for colored items (12 colors) -> See the rainbow in my tileset). Say Hello to 25 color backgrounds !

- ROM usage is very low : tiles information gets "compressed"

- Double buffering is included : if you need some CPU for your game, turn to 30fps instead of 60fps.

At the moment, the full thingy is not optimized yet : I display the whole screen every frame, while it would'nt be very complicated to keep the Display Lists while continuously scrolling... Well, I'll do this later. Anyway, the provided routine is fast enough to display at 60fps without any issue (it consumes ~1.5 display lines of CPU per displayed tileset, whatever is the size of it)?

Next step: provide a 320A/320C example using sparse tiling... That's not complicated, but I'm definitely not an artist...

See you !

tiles.png

tiled_sparse_tiling.png

cc7800-0.2.9-x86_64.msi sparse_tiling.a78 sparse.tmx test.tsx tiles.yaml

  • Like 10
Link to comment
Share on other sites

5 hours ago, SlidellMan said:

I wonder if this would be perfect for shmups, Run-&-guns, and RPGs?

Sparse tiling is a good solution for RPGs and platform games. For vertical only schmups, the VERTICAL_SCROLLING feature of multisprite.h is more efficient, since you only need to provide programmatically the new arriving data when requested by the scrolling algorithm (and thus the level can be of infinite vertical size). With VERTICAL_SCROLLING, you can feed the display with any kind of data : tiles, bitmap data, and they can be sparse if you want to spare some CPU... It's the right method to make a vertical Schump or Run&Gun. See examples/example_vertical_scrolling.c. I'll update it to include 160B graphics (it's only 160A at the moment).

  • Thanks 1
Link to comment
Share on other sites

Hi,

Here is a new example showing sparse tiling with 320A/C mixed mode (headers/multisprite.h now supports MODE_320AC and MODE320BD options). For economy of DMA and CPU, some tiles are displayed in 320A (one color), while some others (here the colored cube) are in 4 colors + BG (320C mode), yielding a "Rikki & Vikki" look. Note that tiles7800 and sprites7800 (from tools7800 github repository) now support 320C mode tiles and sprites, and has a new "alias" feature, allowing the same tiles to be used with different colors (you can use them with different colors in Tiled, but they will be present only once in ROM). 

Here is the example with the generated code from tiles7800 and sprites7800 :

#include "stdlib.h"
#include "string.h"
#include "prosystem.h"
#include "joystick.h"

#define MODE_320AC

// Generated from tiles7800 --sparse highres_tiles.yaml highres_sparse.tmx 
const char tilemap_0_data[] = {96, 0xff};
const char tilemap_1_data[] = {96, 0xff};
const char tilemap_2_data[] = {96, 0xff};
const char tilemap_3_data[] = {96, 0xff};
const char tilemap_4_data[] = {96, 0xff};
const char tilemap_5_0[5] = {16, 12, 6, 12, 8};
const char tilemap_5_data[] = {6, 2, tilemap_5_0, 0x60, tilemap_5_0 >> 8, (1 << 5) | ((-5) & 0x1f), 29, 96, 0xff};
...
const char tilemap_31_data[] = {96, 0xff};
const char tilemap_data[64] = {tilemap_0_data & 0xff, tilemap_0_data >> 8, tilemap_1_data & 0xff, tilemap_1_data >> 8, tilemap_2_data & 0xff, tilemap_2_data >> 8, tilemap_3_data & 0xff, tilemap_3_data >> 8, tilemap_4_data & 0xff, tilemap_4_data >> 8, tilemap_5_data & 0xff, tilemap_5_data >> 8, tilemap_6_data & 0xff, tilemap_6_data >> 8, tilemap_7_data & 0xff, tilemap_7_data >> 8, tilemap_8_data & 0xff, tilemap_8_data >> 8, tilemap_9_data & 0xff, tilemap_9_data >> 8, tilemap_10_data & 0xff, tilemap_10_data >> 8, tilemap_11_data & 0xff, tilemap_11_data >> 8, tilemap_12_data & 0xff, tilemap_12_data >> 8, tilemap_13_data & 0xff, tilemap_13_data >> 8, tilemap_14_data & 0xff, tilemap_14_data >> 8, tilemap_15_data & 0xff, tilemap_15_data >> 8, tilemap_16_data & 0xff, tilemap_16_data >> 8, tilemap_17_data & 0xff, tilemap_17_data >> 8, tilemap_18_data & 0xff, tilemap_18_data >> 8, tilemap_19_data & 0xff, tilemap_19_data >> 8, tilemap_20_data & 0xff, tilemap_20_data >> 8, tilemap_21_data & 0xff, tilemap_21_data >> 8, tilemap_22_data & 0xff, tilemap_22_data >> 8, tilemap_23_data & 0xff, tilemap_23_data >> 8, tilemap_24_data & 0xff, tilemap_24_data >> 8, tilemap_25_data & 0xff, tilemap_25_data >> 8, tilemap_26_data & 0xff, tilemap_26_data >> 8, tilemap_27_data & 0xff, tilemap_27_data >> 8, tilemap_28_data & 0xff, tilemap_28_data >> 8, tilemap_29_data & 0xff, tilemap_29_data >> 8, tilemap_30_data & 0xff, tilemap_30_data >> 8, tilemap_31_data & 0xff, tilemap_31_data >> 8};

#define TILING_HEIGHT 32
#define TILING_WIDTH 32
#include "sparse_tiling.h"

// Generated from sprites7800 highres_tiles.yaml
reversed scattered(16,24) char blue_tubes[384] = {
	0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfc,
	0x30, 0x00, 0x00, 0x18, 0x7f, 0xfe, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x00, 0xc0,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0xb6, 0x00, 0x00, 0xda, 0x7f, 0xfe, 0x3f, 0xfc,
	0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfb, 0xfe, 0xdf, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xdf, 0xfc,
...
  	0x1c, 0x1c, 0x01, 0x60, 0x70, 0x0e, 0x1c, 0x1c, 0x01, 0x80, 0x6f, 0xf6, 0x18, 0x18, 0x01, 0x60,
	0x5f, 0xfa, 0x08, 0x08, 0x01, 0x80, 0x3f, 0xfc, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
reversed scattered(16,4) char colored_cube[64] = {
	0x00, 0x00, 0x00, 0x00, 0x30, 0xf0, 0xf0, 0xc0, 0x50, 0xf0, 0xf0, 0xa2, 0x60, 0xf0, 0xf0, 0x6a,
	0x70, 0x00, 0x00, 0xea, 0x70, 0x7f, 0xef, 0xea, 0x70, 0x75, 0xe5, 0xea, 0x70, 0x7f, 0xef, 0xea,
	0x70, 0x75, 0xe5, 0xea, 0x70, 0x7f, 0xef, 0xea, 0x70, 0x75, 0xe5, 0xea, 0x70, 0x00, 0x00, 0xea,
	0x60, 0xfa, 0xfa, 0x6a, 0x52, 0xfa, 0xfa, 0xaa, 0x32, 0xfa, 0xfa, 0xc8, 0x00, 0x00, 0x00, 0x00
};

ramchip int x, y;

void main()
{
    x = 0; y = 0;

    multisprite_init();
    multisprite_set_charbase(blue_tubes);
    tiling_init(tilemap_data);
    joystick_init();
    
    tiling_goto(x, y);
    *P0C2 = 0x0f; // White
    *P1C2 = multisprite_color(0x9a); // Blue
    *P2C2 = multisprite_color(0x3a); // Orange
    *P3C2 = multisprite_color(0x33); // Red

    // Main loop
    do {
        multisprite_flip();
        joystick_update();
        if (joystick[0] & JOYSTICK_LEFT) x--; 
        else if (joystick[0] & JOYSTICK_RIGHT) x++;
        if (joystick[0] & JOYSTICK_UP) y--; 
        else if (joystick[0] & JOYSTICK_DOWN) y++;
        tiling_goto(x, y);
    } while(1);
}

 

Note that the latest 0.2.10 version of cc7800 is now based on cc6502 v1.0, which has most of its limitations alleviated : you can now use functions with parameters and local variables, without any performance hit (parameters and local variables are automatically "optimally" allocated in zeropage based on call tree during link phase).

I'm preparing an example based on 320B/D mixed mode (very likely the one use in "Rikky & Vikki", based on the screenshots I've seen), which should finalize the support of all the 7800 graphics modes in Sprite7800 and Tiles7800. I'll then release a win32 binary version of these tools. By now, you still need Rust & Cargo to compile the binaries from the github repository (not complicated, but not immediate either. A good exercise by the way. Get Rusty.).

Have a nice day !

 

 

 

 

tiled_sparse_tiling_hires.png

highres_tiles.png

highres_tiles.tsx highres_sparse.tmx highres_tiles.yaml cc7800-0.2.10-x86_64.msi

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

  • 3 weeks later...
12 hours ago, KrunchyTC said:

was there any particular reason that R&V and pacman 40th used 320B instead of 320C?

 

Although 320B has 2 fewer colors than 320C, with 320B you can use 4 colors (out of 6 total) without any pixel placement restrictions (or all 6 colors without restrictions but in this case you have to give up transparency), instead with 320C all 8 colors have restrictions on pixel placement, in fact each pair of pixels must have the same color or a color + background color.

 

So they are different modes with different strengths that must be evaluated for the graphics you want to create.

  • Like 2
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...