Bubble Bobble (W.I.P.)

Use this forum to share and discuss Uzebox games and demos.
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Bubble Bobble (W.I.P.)

Post by D3thAdd3r »

nicksen782 wrote: Sat Sep 09, 2017 9:01 pm So, streaming from SPI RAM is more likely? You would still need to create the ASM state machine for that?
streaming with SPI ram is quite easy. When I have a bit of time for development I will do the demo which should more or less be directly usable in any game. Definitely no asm state machine will be required for that, only Jubatian's SPI ram asm library called from C code.
nicksen782 wrote: Sat Sep 09, 2017 9:01 pm Perhaps something like "SPI RAM not detected. Music will not be available" or something.
It seems like a good idea if you go that route, so at least it is playable on anyone's hardware.
Artcfox wrote: Sun Sep 10, 2017 3:00 am I don't think it is as unusable as you think it is.
Ok I will leave that alone then. I guess the state machine does not really have to be in asm either, and it is not terribly more work to do it in asm than C anyways. It could be done, and perhaps it wouldn't even take too long to develop.
nicksen782 wrote: Sat Sep 09, 2017 7:34 pm I've attached a graphic to demonstrate. There are 5 frames and 5 ram tile sets.
That animation is pretty cool, I would definitely like to see that in an intro or something in your games!
nicksen782 wrote: Sat Sep 09, 2017 9:01 pm At this point I've been able to "stream" graphical animations from SD. I love the concept but it really only applies to "non-action" screens where whatever is happening is fully scripted or something.
I ran into some issues right away on AE with reports that the intro animation of the Uzebox logo would animate more slowly and take a long time with certain cards. So I changed some details to make sure it wouldn't go way too fast or way too slow on any card, but it definitely does run at different speeds on different cards which sucks; also it limits what you can do having to keep that in mind. One advantage of the SPI ram is that you don't have to worry about that and so can push things a bit further perhaps into real time interactivity for some things.

Edit @ nicksen - PM me your address and I will make sure you get something to use SPI ram right away.
User avatar
uze6666
Site Admin
Posts: 4801
Joined: Tue Aug 12, 2008 9:13 pm
Location: Montreal, Canada
Contact:

Re: Bubble Bobble (W.I.P.)

Post by uze6666 »

Kits are on the way!!!
User avatar
nicksen782
Posts: 714
Joined: Wed Feb 01, 2012 8:23 pm
Location: Detroit, United States
Contact:

Re: Bubble Bobble (W.I.P.)

Post by nicksen782 »

Thanks, Uze!

I have 50 levels done (tiles and tile maps.) I also have all the level data. This would be the time before "Hurry" appears and Skel, bubble speed, etc. Additionally, bad guy types and positions.

I'm basing my game off of the NES version but the data that I'm using (actually had to write something to scrape the data from the website) is based on the arcade version. Much of it is in fact similar but there were some changes. I'll need to adjust for those.

I just couldn't find a complete list of level stats anywhere. That website was all that I could find.

... Still working on getting enemy/player projectiles. I'm presently toying with my sprite flickering functions.

Next demo update should be in November.
User avatar
nicksen782
Posts: 714
Joined: Wed Feb 01, 2012 8:23 pm
Location: Detroit, United States
Contact:

Re: Bubble Bobble (W.I.P.)

Post by nicksen782 »

Next demo is coming soon!

I've converted my level data/level tile/ram tile and font tile SD data stuff to be read from SPI RAM instead. For SD I now only have a simple loader than loads the data into SPI RAM. After that it is all SPI RAM. Is it faster? I dunno. Seems to be the same speed on the online emulator.

I did save all my code for the SD version. Actually, that code was pretty good and it seems a waste to lose it.

Additionally, I have a lives counter and the player will die if touched. If no lives remain then it is game over. You know, just like a normal game.

I still intend to use the SD card for writing. I'm not sure what to save. I was thinking that having the last level stored would be nice. I still want to do a password system but an easier continue system may be better.

So, now that I have SPI RAM all figured out I am really really looking forward to doing some streaming music with it! My goal is for the next demo to have music.

I have two SPIRAM modules. Neither works. The screen just hangs. I'll need to investigate this further later.
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Bubble Bobble (W.I.P.)

Post by D3thAdd3r »

Once a sector is "spooled up" the next 512 bytes read should be almost as fast as SPI ram. If not too many sectors are crossed, it might not appear visually slower.

Weird what is going on with the modules. Have you tried flashing Alec's original Akuma demo to test with?

Looking forward to the demo! Let me know if you have music questions.
User avatar
Jubatian
Posts: 1561
Joined: Thu Oct 01, 2015 9:44 pm
Location: Hungary
Contact:

Re: Bubble Bobble (W.I.P.)

Post by Jubatian »

Do you mean actual SPI RAM hardware? There is a problem on the V1.3.1 PCB and maybe other SPI RAM solutions that the HOLD pin isn't tied high. It is very easy to fix, though! viewtopic.php?f=4&t=2445 (if you have a soldering iron, that's just a few seconds, and it's all right).

I guess you might also want this SPI RAM video mode then: viewtopic.php?f=3&t=9383&start=10 I won't start building it now since first I want to do the bootloader at least, but later maybe!
User avatar
uze6666
Site Admin
Posts: 4801
Joined: Tue Aug 12, 2008 9:13 pm
Location: Montreal, Canada
Contact:

Re: Bubble Bobble (W.I.P.)

Post by uze6666 »

Jubatian wrote: Sun Oct 01, 2017 8:45 am Do you mean actual SPI RAM hardware? There is a problem on the V1.3.1 PCB and maybe other SPI RAM solutions that the HOLD pin isn't tied high. It is very easy to fix, though! viewtopic.php?f=4&t=2445 (if you have a soldering iron, that's just a few seconds, and it's all right).

I guess you might also want this SPI RAM video mode then: viewtopic.php?f=3&t=9383&start=10 I won't start building it now since first I want to do the bootloader at least, but later maybe!
Arrgh damnit, I just sent another batch of 1.3.1 to be manufactured and forgot I fixed that!! :x
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Bubble Bobble (W.I.P.)

Post by D3thAdd3r »

uze6666 wrote: Sat Oct 28, 2017 2:55 am Arrgh damnit, I just sent another batch of 1.3.1 to be manufactured and forgot I fixed that!! :x
Maybe just an addendum to the build guide, as even it is a very clean in place fix anyway.

@nicksen782 - It is cool you are jumping right to the bleeding edge with this and willing to try some new/experimental stuff with the streaming music. As we discussed, and in general probably what most would want, you are looking to do more than just music with the SPI Ram(adding the requirement to a game is a big decision). This example I think is not exactly what you are looking for with the in place graphics stuff, but it shows what is basically the only thing you have to do to keep the streaming player happy...just end the sequential read, do all your other stuff, then put it's sequential read with the offset it had, before feeding the buffer. Naturally the whole point of the buffer is so that you can do things like that in whatever way you need and still have the music run. BTW the static is an artifact of the way I hack...I mean mix..the PCM data into the stream for this cheesy example, and is not a flaw with the SPI Ram reading concept itself. Hit B to fire a railgun :twisted:

Edit - I am working on a tool that might help on the graphics stuff as I am in need of that right now also. It basically takes a list of files either C array or binary, then writes them as binary to an SD image and creates a directory of pointers to that data wherever you want. How are you getting your graphics data into the SD image currently, perhaps another tool is not necessary to create?
Attachments
SPIRamMusicDemo.zip
(114.15 KiB) Downloaded 623 times
User avatar
nicksen782
Posts: 714
Joined: Wed Feb 01, 2012 8:23 pm
Location: Detroit, United States
Contact:

Re: Bubble Bobble (W.I.P.)

Post by nicksen782 »

I think you are thinking of the tool that I call C2BIN. The name says it all. So, it pretty much just writes C arrays to a binary file.

I don't do any fancy string parsing (such as with INC2IMG.) You run a shell script that compiles the program each time which outputs the binary files and removes the compiled program since you should have a fresh one each time.

For C2BIN I use data structures and looping to store all the values that I need. I would be happy to share the one that I am using for Bubble Bobble. A key difference between the way that you do music and I do graphics is how they are found. You have a sort of file table at the beginning. I use a list of #defines which are offsets to the proper part of the binary file to read from. Obviously your method has an advantage of portability if other people use the same method. In fact, I expect to adapt to your method a bit. I'll be putting the music in the beginning of the SPIRAM and then my graphics data after that. I'll still do my graphics the way that I am but the music I would use your method for. You read from the beginning of SPIRAM for that file table.

Example C2BIN setup:

c2bin_runit.sh (compiles c2bin.c and copies the new files to their destination.)

Code: Select all

#!/bin/bash
DESTDIR="/home/ubuntu/workspace//non-web/Uzebox/BubbleBobble2017/"
gcc c2bin.c -std=gnu99 && ./a.out
rm a.out
# cp -f *.bin $DESTDIR/output/ | true
cp -f *.BIN $DESTDIR/output/
cp -f *.def $DESTDIR/src/assets
cp -f ../PROGMEM/*.inc $DESTDIR/src/assets
Snips of c2bin.c (since it can be rather large.)

Code: Select all

// Ram tile Maps (only)
#include "C2BIN_RTMAP_tatio2.inc"
#include "C2BIN_RTMAP_info1.inc"
#include "C2BIN_RTMAP_NICKSEN782LOGO1.inc"
#include "C2BIN_RTMAP_gfBubbleF1.inc"
#include "C2BIN_RTMAP_gfBubbleF2.inc"
#include "C2BIN_RTMAP_gfBubbleF3.inc"
#include "C2BIN_RTMAP_gfBubbleF4.inc"
#include "C2BIN_RTMAP_dinos.inc"
#include "C2BIN_RTMAP_endingPlayersF1.inc"
#include "C2BIN_RTMAP_endingPlayersF2.inc"
#include "C2BIN_RTMAP_human.inc"
#include "C2BIN_RTMAP_hugs.inc"
#include "C2BIN_RTMAP_NICKSEN782LOGO2.inc"
#include "C2BIN_RTMAP_NICKSEN782LOGO3.inc"
#include "C2BIN_RTMAP_NICKSEN782LOGO4.inc"
#include "C2BIN_RTMAP_NICKSEN782LOGO5.inc"

void bb_createFile_ramtilemaps  (FILE * defs, FILE * dest){
	fprintf(defs, "\n\n// C2BIN Offsets Definitions File: bubbleBobble_ramtile_maps\n");

	// Write the offset to the defs file.
	// Write the number of tiles.
	// Write the tileset.
	// Write the tilemap.

	struct maplist_ {
	 const char name[30] ;
	 const char * tileset;
	 const char * tilemap;
	 unsigned long tileset_size;
	 unsigned long tilemap_size;
	};

	struct maplist_ maplist[] = {
		{ .name = "RTMAP_tatio2         " , .tileset = C2BIN_RTMAP_tatio2         , .tilemap = RTMAP_tatio2         , .tileset_size = sizeof(C2BIN_RTMAP_tatio2)           , .tilemap_size = sizeof(RTMAP_tatio2)          },
		{ .name = "RTMAP_info1          " , .tileset = C2BIN_RTMAP_info1          , .tilemap = RTMAP_info1          , .tileset_size = sizeof(C2BIN_RTMAP_info1)           , .tilemap_size = sizeof(RTMAP_info1)           },
		{ .name = "RTMAP_gfBubbleF1     " , .tileset = C2BIN_RTMAP_gfBubbleF1     , .tilemap = RTMAP_gfBubbleF1     , .tileset_size = sizeof(C2BIN_RTMAP_gfBubbleF1)      , .tilemap_size = sizeof(RTMAP_gfBubbleF1)      },
		{ .name = "RTMAP_gfBubbleF2     " , .tileset = C2BIN_RTMAP_gfBubbleF2     , .tilemap = RTMAP_gfBubbleF2     , .tileset_size = sizeof(C2BIN_RTMAP_gfBubbleF2)      , .tilemap_size = sizeof(RTMAP_gfBubbleF2)      },
		{ .name = "RTMAP_gfBubbleF3     " , .tileset = C2BIN_RTMAP_gfBubbleF3     , .tilemap = RTMAP_gfBubbleF3     , .tileset_size = sizeof(C2BIN_RTMAP_gfBubbleF3)      , .tilemap_size = sizeof(RTMAP_gfBubbleF3)      },
		{ .name = "RTMAP_gfBubbleF4     " , .tileset = C2BIN_RTMAP_gfBubbleF4     , .tilemap = RTMAP_gfBubbleF4     , .tileset_size = sizeof(C2BIN_RTMAP_gfBubbleF4)      , .tilemap_size = sizeof(RTMAP_gfBubbleF4)      },
		{ .name = "RTMAP_dinos          " , .tileset = C2BIN_RTMAP_dinos          , .tilemap = RTMAP_dinos          , .tileset_size = sizeof(C2BIN_RTMAP_dinos)           , .tilemap_size = sizeof(RTMAP_dinos)           },
		{ .name = "RTMAP_endingPlayersF1" , .tileset = C2BIN_RTMAP_endingPlayersF1, .tilemap = RTMAP_endingPlayersF1, .tileset_size = sizeof(C2BIN_RTMAP_endingPlayersF1) , .tilemap_size = sizeof(RTMAP_endingPlayersF1) },
		{ .name = "RTMAP_endingPlayersF2" , .tileset = C2BIN_RTMAP_endingPlayersF2, .tilemap = RTMAP_endingPlayersF2, .tileset_size = sizeof(C2BIN_RTMAP_endingPlayersF2) , .tilemap_size = sizeof(RTMAP_endingPlayersF2) },
		{ .name = "RTMAP_human          " , .tileset = C2BIN_RTMAP_human          , .tilemap = RTMAP_human          , .tileset_size = sizeof(C2BIN_RTMAP_human)           , .tilemap_size = sizeof(RTMAP_human)           },
		{ .name = "RTMAP_hugs           " , .tileset = C2BIN_RTMAP_hugs           , .tilemap = RTMAP_hugs           , .tileset_size = sizeof(C2BIN_RTMAP_hugs)            , .tilemap_size = sizeof(RTMAP_hugs)            },
		{ .name = "RTMAP_NICKSEN782LOGO1" , .tileset = C2BIN_RTMAP_NICKSEN782LOGO1, .tilemap = RTMAP_NICKSEN782LOGO1, .tileset_size = sizeof(C2BIN_RTMAP_NICKSEN782LOGO1) , .tilemap_size = sizeof(RTMAP_NICKSEN782LOGO1) },
		{ .name = "RTMAP_NICKSEN782LOGO5" , .tileset = C2BIN_RTMAP_NICKSEN782LOGO5, .tilemap = RTMAP_NICKSEN782LOGO5, .tileset_size = sizeof(C2BIN_RTMAP_NICKSEN782LOGO5) , .tilemap_size = sizeof(RTMAP_NICKSEN782LOGO5) },
		{ .name = "RTMAP_NICKSEN782LOGO4" , .tileset = C2BIN_RTMAP_NICKSEN782LOGO4, .tilemap = RTMAP_NICKSEN782LOGO4, .tileset_size = sizeof(C2BIN_RTMAP_NICKSEN782LOGO4) , .tilemap_size = sizeof(RTMAP_NICKSEN782LOGO4) },
		{ .name = "RTMAP_NICKSEN782LOGO3" , .tileset = C2BIN_RTMAP_NICKSEN782LOGO3, .tilemap = RTMAP_NICKSEN782LOGO3, .tileset_size = sizeof(C2BIN_RTMAP_NICKSEN782LOGO3) , .tilemap_size = sizeof(RTMAP_NICKSEN782LOGO3) },
		{ .name = "RTMAP_NICKSEN782LOGO2" , .tileset = C2BIN_RTMAP_NICKSEN782LOGO2, .tilemap = RTMAP_NICKSEN782LOGO2, .tileset_size = sizeof(C2BIN_RTMAP_NICKSEN782LOGO2) , .tilemap_size = sizeof(RTMAP_NICKSEN782LOGO2) },
	};

	unsigned int maplist_size = sizeof(maplist) / sizeof(maplist[0]);
	unsigned long thisRecordSize=0;
	unsigned int banksize = 0xFF00;

	for(unsigned char i=0; i<maplist_size; i+=1){
		// Defs line.
		fprintf(defs, "#define %s %lu    \t// %d/%d -- Uniques: %lu -- Map bytes: %lu, -- Dims: %d by %d \n", maplist[i].name, ftell(dest), i+1, maplist_size, maplist[i].tileset_size/64, maplist[i].tilemap_size,  maplist[i].tilemap[0],  maplist[i].tilemap[1] );

		// Number of tiles.
		fputc ( maplist[i].tileset_size/64, dest );

		// Tileset.
		fwrite(maplist[i].tileset, sizeof(const char), maplist[i].tileset_size, dest);

		// Tilemap.
		fwrite(maplist[i].tilemap, sizeof(const char), maplist[i].tilemap_size, dest);
	}

}

int main(){
	// Files:
	// Open the output files.
	F_SPIRAM        = fopen (FNAME_SPIRAM       , "wb+"); // File 7: spiramSR.bin : FNAME_SPIRAM:        Holds the combined SD data as one file.
	F_defineSPIRAM  = fopen (FNAME_defineSPIRAM , "wb+"); // File 8: defineSR.def : FNAME_defineSPIRAM:  Holds defines for SPI RAM usage.

	// Padding for the SD and SPI RAM files.
	unsigned int padBytes=0;

	// Write the SPIRAM file.
	fprintf(F_defineSPIRAM, "// C2BIN DEFINITIONS: SPI RAM (%s) \n\n", FNAME_defineSPIRAM);

	unsigned long offset_gamesave = ftell(F_SPIRAM);
	bb_createFile_gamesave      ( F_defineSPIRAM, F_SPIRAM );

	unsigned long offset_leveltiles = ftell(F_SPIRAM);
	bb_createFile_levelTiles    ( F_defineSPIRAM, F_SPIRAM );

	unsigned long offset_leveldata = ftell(F_SPIRAM);
	bb_createFile_levelData     ( F_defineSPIRAM, F_SPIRAM );

	unsigned long offset_flashtilemaps = ftell(F_SPIRAM);
	bb_createFile_flashtilemaps ( F_defineSPIRAM, F_SPIRAM );

	unsigned long offset_ramtilemaps = ftell(F_SPIRAM);
	bb_createFile_ramtilemaps   ( F_defineSPIRAM, F_SPIRAM );

	unsigned long offset_music = ftell(F_SPIRAM);
	bb_createFile_music   ( F_defineSPIRAM, F_SPIRAM );

	// Seek to the end of each of the opened files.
	fseek(F_defineSPIRAM,  0L, SEEK_END);

	fseek(F_SPIRAM,        0L, SEEK_END);
	padBytes = ftell(F_SPIRAM)        % 512 ;
	
	// Pad out bytes until the next 512 byte offset. 
	// for(unsigned int i=0; i<padBytes; i++){ fputc ( 'X', F_SPIRAM        ); }

	// Write the section offsets to the end of the define file.
	fprintf(F_defineSPIRAM, "\n\n");
	fprintf(F_defineSPIRAM, "// ***** SECTION OFFSETS *****\n");
	fprintf(F_defineSPIRAM, "#define offset_gamesave      %lu \n", offset_gamesave );
	fprintf(F_defineSPIRAM, "#define offset_leveltiles    %lu \n", offset_leveltiles );
	fprintf(F_defineSPIRAM, "#define offset_leveldata     %lu \n", offset_leveldata );
	fprintf(F_defineSPIRAM, "#define offset_flashtilemaps %lu \n", offset_flashtilemaps );
	fprintf(F_defineSPIRAM, "#define offset_ramtilemaps   %lu \n", offset_ramtilemaps );
	fprintf(F_defineSPIRAM, "#define offset_music         %lu \n", offset_music );
	fprintf(F_defineSPIRAM, "#define offset_endofspiram   %lu \n", ftell(F_SPIRAM) );
// midisong1

	// Output some summary data to the console.
	printf("\n");
	printf("// ***** C2BIN END *****\n");
	printf("// ** Compile Date: %s \n", __DATE__);
	printf("// ** Compile Time: %s \n", __TIME__);
	printf("// -----------------------------------------------\n" );
	// printf("// Definition file size: SD offsets    : (%s) : (%lu bytes)\n", FNAME_defineSD, ftell(F_defineSD) );
	printf("// Definition file size: SPIRAM offsets: (%s) : (%lu bytes)\n", FNAME_SPIRAM, ftell(F_defineSPIRAM) );
	printf("// -----------------------------------------------\n" );
	printf("// File size for SPI RAM:\n" );
	printf("//  File 1 : (%s) : (%lu bytes)\n", FNAME_SPIRAM, ftell(F_SPIRAM) );
	printf("// -----------------------------------------------\n" );
	printf("// SPIRAM Section Offsets\n" );
	printf("//  offset_gamesave      : %lu \n", offset_gamesave );
	printf("//  offset_leveltiles    : %lu \n", offset_leveltiles );
	printf("//  offset_leveldata     : %lu \n", offset_leveldata );
	printf("//  offset_flashtilemaps : %lu \n", offset_flashtilemaps );
	printf("//  offset_ramtilemaps   : %lu \n", offset_ramtilemaps );
	printf("//  offset_ramtilemaps   : %lu \n", offset_ramtilemaps );
	printf("//  offset_music         : %lu \n", offset_music );
	printf("//  END OF FILE PAD BYTES: %d \n",  padBytes );
	printf("// -----------------------------------------------\n" );

	// Close the output files.
	fclose(F_defineSPIRAM);
	fclose(F_SPIRAM);

	// Final console output string. (Do not change. Used as a token to detect C2BIN completion.)
	printf("// C 2 Bin - DONE!\n");

}
Example code for writing a ram tile map to the screen.

Code: Select all

// Draw a ram tile map.
void SD_RT_MAP_DrawMap(unsigned char originX, unsigned char originY, unsigned long mapOffset){
	// Given offset, x/y position.
	// To keep things simple... only draw 1 ramtile map at a time for the same screen.
	uint8_t mapwidth=0;
	uint8_t mapheight=0;
	uint8_t numUniques=0;
	uint8_t * ram_tiles_ptr;
	uint8_t * thisVramRow;
	uint8_t bank  = 0;
	uint16_t addr = 0;
	ram_tiles_ptr = &ram_tiles[0*64];

	// Queue the offset position in the correct file.
	unsigned long position=mapOffset;

	// First byte: Get the number of unique tiles.
	numUniques = SpiRamReadU8( (position)>>16, (position) );
	position++;

	// Prevent the game from crashing if the image has more tiles than RAM_TILES_COUNT would allow.
	if(numUniques>RAM_TILES_COUNT){ numUniques=RAM_TILES_COUNT; }

	SetUserRamTilesCount(numUniques);

	// Get the ram tiles.
	// Read numUniques * 64 bytes into current ram_tiles address. (8*8 is 64)
	SpiRamReadInto( (position)>>16, (position), ram_tiles_ptr, (numUniques*64));
	position+=(uint32_t)(numUniques*64);

	// Get the map data.
	// Second and third bytes: Get the width and height.
	mapwidth  = SpiRamReadU8( (position)>>16, (position) );position++;
	mapheight = SpiRamReadU8( (position)>>16, (position) );position++;

	// Draw width*height bytes from the SD to vram at the specified location.
	for(uint8_t y=0; y<mapheight; y++){
		// Set the values for bank and addr.
		bank = (uint8_t)  ((((uint32_t)(position))) >> 16);
		addr = (uint16_t) ((uint32_t) ((position)));

		// Set the value for the vram pointer.
		thisVramRow = &vram[ ( (y+originY) * VRAM_TILES_H ) + originX ] ;

		// Read the SPIRAM data into vram.
		SpiRamReadInto(bank, addr, thisVramRow, mapwidth);

		// Adjust the position.
		position+=mapwidth;
	}
}
That's how I've been creating my binary file. Just put a new entry into the data structure for that type of data and then loop through. Keep track of offsets along the way and use them to make a defines file. And at the end provide a bunch of information since I like to see it even through the offsets are kinda automatic.

When reading something like a tilemap from SPIRAM I do much like how I did with SD. Open the file (not really needed with SPIRAM), go to the beginning offset and read data.
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Bubble Bobble (W.I.P.)

Post by D3thAdd3r »

That seems pretty good. For .raw PCM files(no header, binary) perhaps I will make a dead simple tool to facilitate that.

Thinking on the sprites, as I believe you mentioned the flicker was pretty bad and sometimes there is no substitute for more ram tiles, maybe the SPI vram mode Jubatian already has some example code could help. If you are doing graphics in SPI Ram anyway, then with that mode you could potentially load all levels in 1 shot at the beginning, and just scroll down as needed. I would expect 10+ ram tiles too which is the bigger item for this, though that little detail the mode does not yet exist of course.
Post Reply