Page 1 of 1

PCX/tile display problem

Posted: Wed Apr 11, 2012 5:32 pm
by icedaddy
Hi

I am trying to read a pcx file and then display it as a tiled background. It works fine on emulators (DeSmuME & No$gba) but the display is corrupted on my DSL and DSi. This should be simple but I am surely doing something stupid.

The image is 128x128 pixels, 256 col. I am trying to display it in the top left corner of the text background (Mode 0, layer 0). I use the gfx and palette data from the pcx file but I create my own map in code (a simple incrementing tile counter in the area I want the image). On the real DS hardware the last tile is missing plus some (between around 5 to 10 for the DSL and DSi respectively) at the start.

My test code is here, why does this not work on real hardware but is good on emulators?

Code: Select all

/* PCX display test */

#include <nds.h>
#include <filesystem.h>

#include <stdio.h>

int main(int argc, char **argv) {

	// setup video
	videoSetMode(MODE_0_2D);
	vramSetBankA(VRAM_A_MAIN_BG_0x06000000);
	vramSetBankE(VRAM_E_BG_EXT_PALETTE);

	// init nitro
	nitroFSInit();

	// init background for our pcx
	s16 bg = bgInit(0, BgType_Text8bpp, BgSize_T_256x256, 0, 1);

	// load the PCX file from filesystem (the file length is hardcoded here as this is a test)
	u16 pcxSize = 7944;
	FILE* file = fopen("yoshi.pcx", "rb");
	u8* pcxData = (u8*)malloc(pcxSize);
	fread(pcxData, pcxSize, 1, file);
	fclose(file);
	
	// 'load' the PCX
	sImage image;
	loadPCX(pcxData, &image);
	
	// make it into tiles
	imageTileData(&image);
	
	// copy tiles to vram (skip 1st tile as this is the background tile)
	dmaCopy(image.image.data16, bgGetGfxPtr(bg)+32, image.height*image.width);
	// copy palette to vram
	dmaCopy(image.palette, BG_PALETTE, 512);
	
	// create a map to show the image at TLC od screen
	u16 mapSize = 2048;
	u16* mapData = (u16*)malloc(mapSize);
	memset(mapData, 0, mapSize);
	u16 i, j;
	for(j=0; j<16; j++) {
		for(i=0; i< 16; i++) {
			mapData[(j*32)+i] = (j*16)+i+1;
		}
	}
	
	// copy map to vram
	dmaCopy(mapData, bgGetMapPtr(bg), mapSize);


	while(true);

	return 0;
}

Re: PCX/tile display problem

Posted: Wed Apr 11, 2012 6:52 pm
by mtheall
Well this is (probably) easy. You forgot to flush the cache.

Code: Select all

   // copy map to vram
   DC_FlushRange(mapData, mapSize);
   dmaCopy(mapData, bgGetMapPtr(bg), mapSize);
When you dmaCopy, you need to make sure to flush the cache, because it will only copy the uncached data.

Re: PCX/tile display problem

Posted: Wed Apr 11, 2012 6:58 pm
by mtheall
Also, you might want to

Code: Select all

   while(true)
      swiWaitForVBlank();
It will save you some battery life :)

Re: PCX/tile display problem

Posted: Wed Apr 11, 2012 7:00 pm
by mtheall
And, you have two malloc's and no free's. Just sayin'

Re: PCX/tile display problem

Posted: Wed Apr 11, 2012 7:23 pm
by icedaddy
That's it, thanks a lot mtheall.

So should I call DC_FlushRange before all and any dmaCopy call?

And how about DC_InvalidateRange, should this be called after each dmaCopy also?

As for the mallocs and swiWaitForVBlank, you are quite right also but this was just dirty proof of concept code.

Re: PCX/tile display problem

Posted: Wed Apr 11, 2012 11:32 pm
by mtheall
If you WRITE to memory with CPU and then try to DMA it to somewhere else, then you use DC_Flush, because you need to FLUSH what's in the cache to main memory.
If you DMA something, and then try to READ that memory with CPU, then you use DC_Invalidate, because the DMA updated memory, but not the cache, so the cache is INVALID.

That is to say, the CPU will try to access cache first, and if there is a hit, it will use the data in the cache. If it is a miss, it will fetch it from main memory. DMA does not have access to the cache, it operates only on main memory. It also does not have access to DTCM, so you cannot DMA stack <-> main memory.

Typical examples:
CPU write to a buffer, then DMA to VRAM = you need to flush
DMA from VRAM to a buffer, then read from CPU = you need to invalidate

For future reference, if you get junk when using dmaCopy, try memcpy instead. If it fixes the problem, the you need to flush/invalidate the cache, depending on the situation. Also note that in some cases, memcpy can be faster than dmaCopy, due to the caching mechanism, but memcpy should always work (assuming you using 16-bit aligned addresses when you involve VRAM, you cannot make 8-bit writes to VRAM).

Re: PCX/tile display problem

Posted: Wed Apr 11, 2012 11:34 pm
by mtheall
Also note that there are the DC_FlushAll and a DC_InvalidateAll functions. They will flush/invalidate the ENTIRE data cache. Sometimes this will be faster, especially if you're going to end up flushing the whole cache anyway (e.g. a large copy loop).

Re: PCX/tile display problem

Posted: Thu Apr 12, 2012 9:43 pm
by icedaddy
Thanks a lot mtheall, it makes perfect sense.