GP2X Blitter - The Code


GernotFrisch

Member
Joined
Jan 2, 2007
Messages
445
Here's some code how to use the blitter for filling rects, blitting from buffers to back buffer and such. Also code for color keying.

Hope this is of any help.

The source/dest addresses must be real addresses (use MMU hack)
Code:
#if defined(GP2X)
unsigned long devMem=0;
volatile unsigned long *gp2x_memreg32=NULL;
volatile unsigned short *gp2x_memreg16=NULL;
volatile unsigned long* blitter32=NULL;
#endif


#ifdef GP2X
volatile int   vsync_polarity;
#endif

#if defined(GP2X) && !defined(WANT_SDL)
extern unsigned int GetUpperRealAddress(void*);

// dummy blit to force MMSP2's blitter to flush it's cache
void gp2x_dummy_blit(void)
{
	// Blitter seems to have a 16 byte buffer, so force flushing by
	// drawing 8 16bit pixels onto themselves.
	blitter32[0x0000 >> 2] = (1 << 5) | (1 << 6);
	blitter32[0x0004 >> 2] = 0x3101000; // front screen buffer
	blitter32[0x000C>>2] = 1<<8;
	blitter32[0x0020>>2] = 0;
	blitter32[0x002C>>2] = (1 << 16) | 8; // number of bytes to copy
	blitter32[0x0030>>2] = 1<< 8 | 1<< 9 | 0xaa;
	// Make sure blitter is ready
	while (blitter32[0x0034 >> 2] & 1)
	{
		asm volatile ("nop");
		asm volatile ("nop");
		asm volatile ("nop");
		asm volatile ("nop");
	}
	asm volatile ("" ::: "memory");
	blitter32[0x0034 >> 2] = 0x0001;
}

void gp2x_clear_back_buffer(void)
{
	blitter32[0x4 >> 2] = 0x3381000;
	blitter32[0x0] = 1<<5; 											// Destination is 16 bpp
	blitter32[0x8 >> 2] = 640;										// Destination stride size in bytes
	blitter32[0x20 >> 2] = 1 << 5  | 2 << 3;					// Perform ROP with a 1bpp pattern
	blitter32[0x24 >> 2] = 0;								// Setup the foreground and background colors for the pattern to be the same
	blitter32[0x28 >> 2] = 0;								// so we don't actually have to upload a pattern image.
	blitter32[0x2c >> 2] = (240 << 16) | (320 << 0);				// Height and width to blit
	blitter32[0x30 >> 2] = (1<<8) | (1<<9 ) | 0xf0;			// Fill from top left to bottom right

	// Wait for blitter to be free, start it, and then wait for completion.
	// Throw in some nop's so we don't saturate the address bus with polling requests.
	while (blitter32[0x0034 >> 2] & 1)
	{
		asm volatile ("nop");
		asm volatile ("nop");
		asm volatile ("nop");
		asm volatile ("nop");
	}
	blitter32[0x34 >> 2] = 1;
	gp2x_dummy_blit();
}

// fill solid rectangle -workes, yahoo!
void gp2x_fillrect(int x, int y, int w, int h, unsigned short col)
{
	blitter32[0x4 >> 2] = (0x3381000 + (y*640) + (x<<1) ) & ~3;
	blitter32[0x0] = 1<<5  | ((x & 0x00000001) << 4); 											// Destination is 16 bpp
	blitter32[0x8 >> 2] = 640;										// Destination stride size in bytes
	blitter32[0xC >> 2] = 0;


	blitter32[0x20 >> 2] = 1 << 5  | 2 << 3;					// Perform ROP with a 1bpp pattern
	blitter32[0x24 >> 2] = col;								// Setup the foreground and background colors for the pattern to be the same
	blitter32[0x28 >> 2] = col;								// so we don't actually have to upload a pattern image.
	blitter32[0x2c >> 2] = (h << 16) | (w << 0);				// Height and width to blit
	blitter32[0x30 >> 2] = (1<<8) | (1<<9 ) | 0xf0;			// Fill from top left to bottom right

	// Wait for blitter to be free, start it, and then wait for completion.
	// Throw in some nop's so we don't saturate the address bus with polling requests.
	while (blitter32[0x0034 >> 2] & 1)
	{
		asm volatile ("nop");
		asm volatile ("nop");
		asm volatile ("nop");
		asm volatile ("nop");
	}
	blitter32[0x34 >> 2] = 1;
	gp2x_dummy_blit();
}


void gp2x_blit(unsigned int hardware_src, int x_from, int y_from, int src_stride_bytes, int x, int y, int w, int h)
{
	blitter32[0x4 >> 2] = (0x3381000 + (y*640) + (x<<1) ) & ~3; // dest ptr
	blitter32[0x0] = 1<<5  | ((x & 0x00000001) << 4); 			// Destination is 16 bpp
	blitter32[0x8 >> 2] = 640;									// Destination stride size in bytes

	//Set the source address
	blitter32[0x0010 >> 2] = (hardware_src +(y_from*640)+(x_from<<1) )&~3;
	//Set the pitch of source in bytes
	blitter32[0x0014 >> 2] = src_stride_bytes;
	//Do nothing with pattern
	blitter32[0x0020 >> 2] = 0;

	// Set a 16bit source, enable source and say the source is not controlled by CPU(?)
	blitter32[0x000C >> 2] = (1 << 8) | (1 << 7) | (1 << 5) | ((x_from & 0x00000001) << 4);

	// Clear the source input FIFO, positive X,Y. And do a copy ROP.
	blitter32[0x0030 >> 2] = (1 << 10) | (1 << 9) | (1 << 8) | 0xCC;

	blitter32[0x2c >> 2] = (h << 16) | (w << 0);				// Height and width to blit

	// Wait for blitter to be free, start it, and then wait for completion.
	// Throw in some nop's so we don't saturate the address bus with polling requests.
	while (blitter32[0x0034 >> 2] & 1)
	{
		asm volatile ("nop");
		asm volatile ("nop");
		asm volatile ("nop");
		asm volatile ("nop");
	}
	blitter32[0x34 >> 2] = 1;
	gp2x_dummy_blit();
}


void gp2x_blit_cookie(unsigned int hardware_src, int x_from, int y_from, int src_stride_bytes, int x, int y, int w, int h, unsigned short cookie)
{
	blitter32[0x4 >> 2] = (0x3381000 + (y*640) + (x<<1) ) & ~3; // dest ptr
	blitter32[0x0] = 1<<5  | ((x & 0x00000001) << 4); 			// Destination is 16 bpp
	blitter32[0x8 >> 2] = 640;									// Destination stride size in bytes

	//Set the source address
	blitter32[0x0010 >> 2] = (hardware_src +(y_from*640)+(x_from<<1) )&~3;
	//Set the pitch of source in bytes
	blitter32[0x0014 >> 2] = src_stride_bytes;
	//Do nothing with pattern
	blitter32[0x0020 >> 2] = 0;

	// Set a 16bit source, enable source and say the source is not controlled by CPU(?)
	blitter32[0x000C >> 2] = (1 << 8) | (1 << 7) | (1 << 5) | ((x_from & 0x00000001) << 4);

	// Clear the source input FIFO, positive X,Y. And do a copy ROP.  cookie		enable cookie
	blitter32[0x0030 >> 2] = (1 << 10) | (1 << 9) | (1 << 8) | 0xCC | (cookie<<16)|(1<<11);

	blitter32[0x2c >> 2] = (h << 16) | (w << 0);				// Height and width to blit

	// Wait for blitter to be free, start it, and then wait for completion.
	// Throw in some nop's so we don't saturate the address bus with polling requests.
	while (blitter32[0x0034 >> 2] & 1)
	{
		asm volatile ("nop");
		asm volatile ("nop");
		asm volatile ("nop");
		asm volatile ("nop");
	}
	blitter32[0x34 >> 2] = 1;
	gp2x_dummy_blit();
}
 
Don't forget that if you use the mmu hack on an area of memory >32MB that you also want to blit from, then you should ensure that the data cache is flushed first, or you may get strange results (blitter doesn't care about/use the cache).
 
Squidge posted on Feb 5 2007 at 09:10 AM said:
Don't forget that if you use the mmu hack on an area of memory >32MB that you also want to blit from, then you should ensure that the data cache is flushed first, or you may get strange results (blitter doesn't care about/use the cache).
Is this what I do with gp2x_dummy_blit()? I call this once before the program does anything, too.
 
Last edited by a moderator:
No, it's different. The dummy blit flushes the blitter's cache. The MMU hack allows the area of memory above 32MB to be cached in the 920's data cache, allowing quicker reads and writes. This also means that some data may not be actually written to memory (because it is still sitting in the cache) when the blitter comes along to read the memory (the blitter doesn't know about the data cache).

Squidge, what is the actual code to flush the data cache?
 
This is what i'm using to clear and invalidate the caches.
It first drains the writebuffer, and then it cleans the DCache before
invalidating both the ICache and the DCache.

It would ofcourse be better to write a snippet of code that could clean only the
relevant parts of the DCache.
But i use this when i'm playing around with the MMU.

Code:
void cleanAndInvalidateCaches()
{
		asm volatile(" \n"
				" mov r0, #0 \n"
				" MCR   p15, 0, r0, c7, c10, 4 @ Drain write buffer.\n"
				" @Clean DCache			\n"
				" mov   r1, #0x00		  \n"
				"Loop1%=:				  \n"
				" mov   r2, #0x00		  \n"
				"Loop2%=:				  \n"
				" mov   r3, r2, lsl#26	 \n"
				" orr   r3, r3, r1, lsl#5  \n"
				" MCR   p15, 0, r3, c7, c14, 2 \n"
				" add   r2, r2, #0x01	  \n"
				" cmp   r2, #64			\n"
				" bne   Loop2%=			\n"
				" add   r1, r1, #0x01	  \n"
				" cmp   r1, #8			 \n"
				" bne   Loop1%=			\n"

				" mov r0, #0			   \n"
				" @Invalidate I&D cache	\n"
				" mcr   p15, 0, r0, c7, c7 \n"

				:
				:
				: "r0", "r1", "r2", "r3");

}
 
Will that work in user mode? If not, then there's a SWI call you can use that will get Linux to do the same thing for you. No idea which one it is however, but it'll be > 0x9f000, and the source code is there :)
 
User mode? i doubt it, who runs his code in usermore anyway? :D
 
mithris posted on Feb 5 2007 at 12:46 PM said:
This is what i'm using to clear and invalidate the caches.
It first drains the writebuffer, and then it cleans the DCache before
invalidating both the ICache and the DCache.

When do you call that? At the beginning of your program, or before every blit?
 
Last edited by a moderator:
Back
Top