+bsteux Posted June 15, 2023 Author Share Posted June 15, 2023 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... 1 Quote Link to comment Share on other sites More sharing options...
KrunchyTC Posted June 15, 2023 Share Posted June 15, 2023 Tough to do in 160B mode I mean. But if there is a solution that doesn't sacrifice quality too much. Quote Link to comment Share on other sites More sharing options...
Eagle Posted June 15, 2023 Share Posted June 15, 2023 Mixed 160A and 160B mode in Direct Mode JS7800 - emulator link 160A&160B mode scroll 1 Quote Link to comment Share on other sites More sharing options...
Defender_2600 Posted June 15, 2023 Share Posted June 15, 2023 (edited) full 160B 4bpp full 160B 4bpp, up to 5 (160B) sprites in the same horizontal area (this has been tested) : full 320B full 320B Edited June 15, 2023 by Defender_2600 1 Quote Link to comment Share on other sites More sharing options...
KrunchyTC Posted June 16, 2023 Share Posted June 16, 2023 Awesome stuff! Quote Link to comment Share on other sites More sharing options...
+bsteux Posted June 17, 2023 Author Share Posted June 17, 2023 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. 3 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted June 19, 2023 Author Share Posted June 19, 2023 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... example_sfx.mp4 cc7800-0.2.7-x86_64.msi example_sfx.a78 8 Quote Link to comment Share on other sites More sharing options...
KrunchyTC Posted June 20, 2023 Share Posted June 20, 2023 (edited) 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 June 20, 2023 by KrunchyTC Quote Link to comment Share on other sites More sharing options...
+bsteux Posted June 27, 2023 Author Share Posted June 27, 2023 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); } example_rmtplayer.mp4 cc7800-0.2.8-x86_64.msi example_rmtplayer.a78 4 1 Quote Link to comment Share on other sites More sharing options...
RevEng Posted June 27, 2023 Share Posted June 27, 2023 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. 1 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted June 27, 2023 Author Share Posted June 27, 2023 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. 1 Quote Link to comment Share on other sites More sharing options...
+batari Posted June 28, 2023 Share Posted June 28, 2023 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. 1 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted June 28, 2023 Author Share Posted June 28, 2023 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. Quote Link to comment Share on other sites More sharing options...
+bsteux Posted June 28, 2023 Author Share Posted June 28, 2023 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... 2 Quote Link to comment Share on other sites More sharing options...
RevEng Posted June 28, 2023 Share Posted June 28, 2023 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. 2 Quote Link to comment Share on other sites More sharing options...
RevEng Posted June 29, 2023 Share Posted June 29, 2023 Tested out, and the fix for 7800rtm2asm seems to be working great. I've pushed it to the master repo. 2 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted June 29, 2023 Author Share Posted June 29, 2023 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. 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. 6 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted July 9, 2023 Author Share Posted July 9, 2023 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 ! sparse_tiling.mp4 cc7800-0.2.9-x86_64.msi sparse_tiling.a78 sparse.tmx test.tsx tiles.yaml 10 Quote Link to comment Share on other sites More sharing options...
SlidellMan Posted July 10, 2023 Share Posted July 10, 2023 I wonder if this would be perfect for shmups, Run-&-guns, and RPGs? Quote Link to comment Share on other sites More sharing options...
+bsteux Posted July 10, 2023 Author Share Posted July 10, 2023 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). 1 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted July 17, 2023 Author Share Posted July 17, 2023 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 ! sparse_tiling_hires.mp4 highres_tiles.tsx highres_sparse.tmx highres_tiles.yaml cc7800-0.2.10-x86_64.msi 9 1 Quote Link to comment Share on other sites More sharing options...
KrunchyTC Posted August 4, 2023 Share Posted August 4, 2023 On 6/15/2023 at 5:25 PM, Defender_2600 said: full 160B 4bpp full 160B 4bpp, up to 5 (160B) sprites in the same horizontal area (this has been tested) : full 320B full 320B was there any particular reason that R&V and pacman 40th used 320B instead of 320C? Quote Link to comment Share on other sites More sharing options...
PacManPlus Posted August 4, 2023 Share Posted August 4, 2023 Regarding PMC40th, 320C didn't look good. @Defender_2600 showed me some examples that used 320C and I didn't like the way they looked, and opted for the 'modified' 320B graphics. 2 Quote Link to comment Share on other sites More sharing options...
KrunchyTC Posted August 4, 2023 Share Posted August 4, 2023 (edited) 11 minutes ago, PacManPlus said: Regarding PMC40th, 320C didn't look good. @Defender_2600 showed me some examples that used 320C and I didn't like the way they looked, and opted for the 'modified' 320B graphics. I see. I guess different modes better suit certain games. Edited August 4, 2023 by KrunchyTC Quote Link to comment Share on other sites More sharing options...
Defender_2600 Posted August 4, 2023 Share Posted August 4, 2023 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. 2 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.