Blitter Help


fishybawb

Hired Geek
Joined
Jul 22, 2005
Messages
1,115
Age
44
Location
York, UK
Website
Visit site
I'm working on a game demo that could really use a bit of a speed increase, so I need some help understanding how to use the hardware blitter... If I have a pointer to a buffer of 320 * 240 bytes, how would I blit that to the screen using hardware?

I had a look at the SquidgeSNES code, which makes reference to gp2x_blitter, but to be honest it left me a bit mystified as to how it actually works :blink:

Thanks!
 
This is a very basic couple of functions that should do what you want. I have cut them out and simplified them from my codebase.

Code:
#define WAIT_BLITTER_IDLE     { do {} while (gp2x_2dregs[MESGSTATUS] & MESGSTATUS_BUSY); }
#define SERIALIZE             __asm__ __volatile__ ("" : : :"memory")
#define FRAC16(x)             (((x)&0x1)<<4)

volatile unsigned long *gp2x_2dregs;
static u32 blitcount = 0;

void initblitter(void)
{
  gp2x_2dregs = (unsigned long *)mmap(0, 0x100, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], 0xe0020000);

  // Enable all video and sound devices
  gp2x_memregs[0x090a>>1] = 0xffff;

  // Enable FASTIO for the hardware blitter
  gp2x_memregs[0x0904>>1] |= (1<<10);
}

void blit(u16 *src, u16 width, u16 height, u16 x, u16 y)
{
  WAIT_BLITTER_IDLE;

  u16 *dest = &framebuffer[x + (y * 320)]);

  gp2x_2dregs[MESGDSTCTRL] = MESGDSTCTRL_DSTBPP_16 | FRAC16(x);
  gp2x_2dregs[MESGDSTADDR] = (unsigned long)dest & ~3;
  gp2x_2dregs[MESGDSTSTRIDE] = 320;

  gp2x_2dregs[MESGSRCCTRL] = MESGSRCCTRL_SRCBPP_16 | MESGSRCCTRL_SRCENB | MESGSRCCTRL_INVIDEO;
  gp2x_2dregs[MESGSRCADDR] = (unsigned long)src & ~3;
  gp2x_2dregs[MESGSRCSTRIDE] = width * 2;

  gp2x_2dregs[MESGPATCTRL] = 0;

  gp2x_2dregs[MESGSIZE] = (height << MESGSIZE_HEIGHT) | width;
  gp2x_2dregs[MESGCTRL] = MESGCTRL_FFCLR | MESGCTRL_YDIR_POS | MESGCTRL_XDIR_POS | SRCCOPY;

  SERIALIZE;

  // Writing to the status register triggers the blit
  gp2x_2dregs[MESGSTATUS] = MESGSTATUS_BUSY;

  ++blitcount;
}

void clearbuffer(void)
{
  WAIT_BLITTER_IDLE;

  u16 *dest = framebuffer;

  gp2x_2dregs[MESGDSTCTRL] = MESGDSTCTRL_DSTBPP_16;
  gp2x_2dregs[MESGDSTADDR] = (unsigned long)dest & ~3;
  gp2x_2dregs[MESGDSTSTRIDE] = 320;
  gp2x_2dregs[MESGSIZE] = (240 << MESGSIZE_HEIGHT) | 320;
  gp2x_2dregs[MESGCTRL] = MESGCTRL_FFCLR | MESGCTRL_YDIR_POS | MESGCTRL_XDIR_POS | BLACKNESS;

  SERIALIZE;

  // Writing to the status register triggers the blit
  gp2x_2dregs[MESGSTATUS] = MESGSTATUS_BUSY;
  ++blitcount;
}

/*
  A dummy blit is required to flush out the last few pixels from the FIFO.
  This method copies the first eight pixels of the destination onto themselves.
*/
void blitdummy(void)
{
  // Only do the dummy blit if a hardware blit was performed this frame
  if (blitcount)
  {
    blitcount = 0;

    WAIT_BLITTER_IDLE;

    u16 *dest = framebuffer;

    gp2x_2dregs[MESGDSTCTRL] = MESGDSTCTRL_DSTENB | MESGDSTCTRL_DSTBPP_16;
    gp2x_2dregs[MESGDSTADDR] = (unsigned long)dest;
    gp2x_2dregs[MESGSRCCTRL] = MESGSRCCTRL_INVIDEO;
    gp2x_2dregs[MESGPATCTRL] = 0;
    gp2x_2dregs[MESGSIZE] = (8 << MESGSIZE_HEIGHT) | 1;
    gp2x_2dregs[MESGCTRL] = MESGCTRL_FFCLR | MESGCTRL_YDIR_POS | MESGCTRL_XDIR_POS | DSTCOPY;

    SERIALIZE;

    // Writing to the status register triggers the blit
    gp2x_2dregs[MESGSTATUS] = MESGSTATUS_BUSY;
  }
}

Just before you flip the framebuffers, you must call blitdummy() to flush out the blitter FIFO.
 
Can you post all of the #defines please? It's missing a bunch of constants (MESGDSTCTRL etc). I figured out some of them from various sources, but I'm bound to screw it up :)
 
typo slygamer? MESGDSTSTRIDE should be 640 for 16bit.

Fishybawb: if you're using 8-bit colours you'll need to use FRAC8 inplace of FRAC16
#define FRAC8(x) (((x)&0x3)<<3)
And if you're not blitting from the very left pixel of the src bitmap you'll need a FRAC?(src_x_offset) ORd into MESGSRCCTRL.
They're needed as the blitter always reads/writes 32bits

/*
* Blitter registers
*/

#define MESGDSTCTRL 0x0000>>2
#define MESG_DSTENB (1<<6)
#define MESG_BSTBPP 0x60
#define MESG_DSTBPP_8 (0<<5)
#define MESG_DSTBPP_16 (1<<5)

#define MESGDSTADDR 0x0004>>2
#define MESGDSTSTRIDE 0x0008>>2
#define MESGSRCCTRL 0x000c>>2
#define MESG_INVIDEO (1<<8)
#define MESG_SRCENB (1<<7)
#define MESG_SRCBPP 0x60
#define MESG_SRCBPP_8 (0<<5)
#define MESG_SRCBPP_16 (1<<5)
#define MESG_SRCBPP_1 (1<<6)

#define MESGSRCADDR 0x0010>>2
#define MESGSRCSTRIDE 0x0014>>2
#define MESGSRCFORCOLOR 0x0018>>2
#define MESGSRCBACKCOLOR 0x001c>>2
#define MESGPATCTRL 0x0020>>2
#define MESG_PATMONO (1<<6)
#define MESG_PATENB (1<<5)
#define MESG_PATBPP 0x18
#define MESG_PATBPP_8 (0<<3)
#define MESG_PATBPP_16 (1<<3)
#define MESG_PATBPP_1 (1<<4)
#define MESG_YOFFSET 0x07

#define MESGFORCOLOR 0x0024>>2
#define MESGBACKCOLOR 0x0028>>2

#define MESGSIZE 0x002c>>2
#define MESG_HEIGHT 16
#define MESG_WIDTH 0

#define MESGCTRL 0x0030>>2
#define MESG_TRANSPCOLOR 16
#define MESG_TRANSPEN (1<<11)
#define MESG_FFCLR (1<<10)
#define MESG_YDIR (1<<9)
#define MESG_YDIR_NEG (0<<9)
#define MESG_YDIR_POS (1<<9)
#define MESG_XDIR (1<<8)
#define MESG_XDIR_NEG (0<<8)
#define MESG_XDIR_POS (1<<8)
#define MESG_ROP 0xff

#define MESGSTATUS 0x0034>>2
#define MESG_BUSY (1<<0)

#define MESGFIFOSTATUS 0x0038>>2
#define MESG_FULL (1<<31)
#define MESG_REMAIN 0x1f

#define MESGFIFO 0x003c>>2
#define MESGPAT 0x0080>>2

/*
* Basic ROPs
*/
#define MESG_ROP_DSTCOPY 0xAA
#define MESG_ROP_SRCCOPY 0xCC
#define MESG_ROP_PAT 0xF0
 
Hmmm, the blitter starts at 0xe0024000, but slygamer has it starting at 0xe0020000, so either slygamer is using different #define's than paeryn, or I've spotted another typo :)
 
Squidge posted on Mar 14 2006 at 09:01 AM said:
Hmmm, the blitter starts at 0xe0024000, but slygamer has it starting at 0xe0020000, so either slygamer is using different #define's than paeryn, or I've spotted another typo :)

Are you sure? My defines are the same as paeryn's and I've always used E002 0000 as the base, as does the MMSP2 databook. E002 4xxx is not referenced in the databook anywhere.
 
Last edited by a moderator:
I have tried to implement this in MAME. With 8bpp:

Code:
void gp2x_blit_hw(void *src, unsigned short src_width, unsigned short src_x, void *dest, unsigned short dest_width, unsigned short dest_x, unsigned short width, unsigned short height)
{
  WAIT_BLITTER_IDLE;

  gp2x_2dregs[MESGDSTCTRL] = MESG_DSTBPP_8 | FRAC8(dest_x);
  gp2x_2dregs[MESGDSTADDR] = (unsigned long)dest & ~3;
  gp2x_2dregs[MESGDSTSTRIDE] = dest_width;

  gp2x_2dregs[MESGSRCCTRL] = MESG_SRCBPP_8 | MESG_SRCENB | MESG_INVIDEO | FRAC8(src_x);
  gp2x_2dregs[MESGSRCADDR] = (unsigned long)src & ~3;
  gp2x_2dregs[MESGSRCSTRIDE] = src_width;

  gp2x_2dregs[MESGPATCTRL] = 0;

  gp2x_2dregs[MESGSIZE] = (height << MESG_HEIGHT) | width;
  gp2x_2dregs[MESGCTRL] = MESG_FFCLR | MESG_YDIR_POS | MESG_XDIR_POS | MESG_ROP_SRCCOPY;

  SERIALIZE;

  gp2x_2dregs[MESGSTATUS] = MESG_BUSY;

  ++blitcount;
}

But it doesn't run, only black screen :(
 
Is it locking up, or just not blitting?
If it's locking up then make sure both the FastIO (0x904 bit 10) & blitter clock (0x90a bit 2) are enabled.
If it's just not displaying, make sure you pass the physical addresses to the blitter, not the virtual ones. Memory allocated by malloc() can't be used with the blitter - it has to be in the upper 32MB which you need to mmap() and manage yourself.
 
slygamer posted on Mar 14 2006 at 12:15 AM said:
Squidge posted on Mar 14 2006 at 09:01 AM said:
Hmmm, the blitter starts at 0xe0024000, but slygamer has it starting at 0xe0020000, so either slygamer is using different #define's than paeryn, or I've spotted another typo :)

Are you sure? My defines are the same as paeryn's and I've always used E002 0000 as the base, as does the MMSP2 databook. E002 4xxx is not referenced in the databook anywhere.

That is most odd. In that case my data book is different than the one you and paeryn have!

2d.JPG


Franxis: Since the blitter is simply a memory copying engine, try using the 16-bit mode instead of 8-bit and compensate according. Also, like paeryn says, source and destination must be physical addresses, as the blitter has no access to the MMU.
 
Last edited by a moderator:
Awesome, this is the kind of stuff I wanted to implement in my 2D graphics library, rather than it simply being a wrapper for SDL.

Umm, where'd you get the reference guide to the hardware? I haven't really spent any time looking, but I'd love to grab a copy of that...
 
Mudi: There's a version of SDL that uses the hardware blitter for several things. Unfortunately, I don't know where it hosted.

However, both links to it and links to the MMSP2 docs can be found by searching this board. I don't know if the links to the MMSP2 docs are still valid, but I don't see why they shouldn't be.
 
Squidge posted on Mar 14 2006 at 07:17 PM said:
slygamer posted on Mar 14 2006 at 12:15 AM said:
Squidge posted on Mar 14 2006 at 09:01 AM said:
Hmmm, the blitter starts at 0xe0024000, but slygamer has it starting at 0xe0020000, so either slygamer is using different #define's than paeryn, or I've spotted another typo :)

Are you sure? My defines are the same as paeryn's and I've always used E002 0000 as the base, as does the MMSP2 databook. E002 4xxx is not referenced in the databook anywhere.

That is most odd. In that case my data book is different than the one you and paeryn have!

Very odd indeed. My databook is the one that was originally leaked and is linked from the wiki (db.pdf).

Does using those addresses work? I guess it must because you use them. I don't have my GP2X setup for development at the moment, so I cannot try it out, but all other code I have seen uses E002 0000.
 
Last edited by a moderator:
What revision is the databook you have Squidge? The version I have is the same as sly (0.95)
mmsp2.png

I've just run a quick test, it seems the blitter registers are shadowed for 0xe002 x000, I tried with 0000, 2000, 4000, 8000 and they all worked
 
1.00 here. I don't have the bandwidth to host permanently, so I'll pm you and slygamer it, and hope you can host it somewhere for other people.
 
Well yeah, I have the HW-blitting SDL, I just wanted to implement my own blitter with hardware blitting enabled, if it was possible to do so. Statically linking SDL makes programs so freaking big...
 
Back
Top