GP2X Squidgesnes_0.37 Code Comments

I think the best way, after reading this interesting thread, would be to work on the core in a thread just like this, I think it would provide the best optimised code as the work went on with everyone chipping in ideas.
I think that instead of trying to write an emulator at full speed, we instead have an emulator with several "modes", and use a specific mode for each game. For example, the snes allows you to change many things during each hblank, meaning that updating the screen is both difficult to code for and very computationally intensive. Some games don't actually use these features (or don't look that bad without them), so for those we could ignore them and run the game faster as a result.

So, keep the current graphics code and call it "Maximum compatibility" and create other faster versions that are verified to work with certain games.

Would be much easier than trying to optimise the code and still retail full compatibility.

I think something much more productive would be to forget writting a new emulator, leave that to the snes9x team, and instead write "patchsets" focusing on the main bottleneck area's of the code. That would mean we could create a generic patch with the ARM ASM memcpy(), fixed-floating point operations, drawing functions, MMU hack! :) etc... and then a more specific patch for say the ASM Sound Core, GP2x Menu system, etc..

So then (in an ideal world) you would just have to add the patch's to any new snes9x code releases. et volia, a new gp2x snes update. And would enable us to focus more on the GP2x ASM and speed improvements.

Notaz seemed to get about a 30% speed increase according to his tests after he added his ASM SPC700 core over the original C core. This looks to me to be the best move for a speed boost, however Notaz's GP2X seems to have been hopelessly delayed in the postal system...
:) hello,

Been poking around squidgesnes again.. I think that minimal.c does all of the hardcore drawing/sound/joystick/etc for the gp2x?

Would this work?

Current Code:

void gp2x_blitter_rect15(gp2x_rect *r)


  int x, y; unsigned short *data=(unsigned short *)r->data, *offset=&gp2x_video_RGB[0].screen16[r->x+r->y*320];

  x=r->w, y=r->h; if(r->solid)

				  while(y--) { while(x--) *offset++=*data++; offset+=320-(x=r->w); }


				  while(y--) { while(x--) { if(*data) *offset=*data; offset++, data++; }

							   offset+=320-(x=r->w); }


void gp2x_blitter_rect15(gp2x_rect *r) {
  // TODO - Maybe some clip checking??
  // Does making two variables, woffset, and xblock work better than calculating them in the loop?

  unsigned short *data=(unsigned short *)r->data, *offset=&gp2x_video_RGB[0].screen16[r->x+r->y*320];
  unsigned int x = r->w, y=r->h, woffset = 320 - x, xblock = sizeof(unsigned short) * x; 

  // if its solid, Do the loop...
  if (r->solid) while(y--) { // this could be...... while ((r->solid) && (y--)) {  ?
	//old method.. while(x--) *offset++=*data++; 
	//Scanline method... TODO - use ASM memcpy.
	memcpy(offset,data, xblock);
	offset += woffset; 

  // not solid, do this. If it WAS solid, Y would == 0 and not run =) This is to remove a branch.

  while(y--) { 
	while(x--) { 
	  if(*data) *offset=*data; 
	  ++offset, ++data; 

	offset += woffset; 


Another great optimisation, but unfortunately for a function that isn't actually called in SquidgeSNES...
Squidge posted on Aug 10 2006 at 11:07 PM said:
Another great optimisation, but unfortunately for a function that isn't actually called in SquidgeSNES...

Damn =/ Ok.. I might continue with that anyway as I think its a public lib. Someone might like 'em =)

Here's a few more...

line 1369: minimal.c

  inline void gp2x_dualcore_pause() { gp2x_memregs[0x0904>>1] &= 0xFFFE; }
  inline void gp2x_dualcore_unpause() { gp2x_memregs[0x0904>>1] |= 1; }

  void gp2x_dualcore_pause(int yes) { if(yes) gp2x_memregs[0x0904>>1] &= 0xFFFE; else gp2x_memregs[0x0904>>1] |= 1; }

line 1430: minimal.c




line 1871: minimal.c

  //If statements can be faster if case's are not linear.
  if (buffer[c] == '\b') {
	g->x = x; g->y = y;
  } else if (buffer[c] == '\n') {
	g->y += g->h;
	//previous code ran "default" here too.. is it needed?
  } else if (buffer[c] == '\r') {
	g->x = x;
  } else {
	gp2x_printfchar(g, (unsigned char)buffer[c]);
	g->x += g->w;

line 175: minimal.c

#define UNSATURATE asm volatile ("nop"); \
		asm volatile ("nop"); \
		asm volatile ("nop"); \
		asm volatile ("nop"); 


inline void gp2x_video_waitvsync(void)


  while(gp2x_memregs[0x1182>>1]&(1<<4)) { UNSATURATE };


/* Function: gp2x_video_waithsync

   This function halts the program until an horizontal sync is done.

   See also:

   <gp2x_video_waitvsync> */

inline void gp2x_video_waithsync(void)


  while(gp2x_memregs[0x1182>>1]&(1<<5)) { UNSATURATE };


OK, So How does squidgesnes draw to the fb?

Last edited by a moderator:
The original version uses the hardware blitter to draw to the frame buffer.

Some hacked versions write to the buffer directly, as the hardware blitter causes some gp2x's to crash.
Hi, i have a memcpy testing suite with arm-optimized memcpy() routines.

I would be curious to see numbers for the gp2x are. Also i'm curious about effects of the MMU hack, if anyone can show best-case results for that.

Nokia 770 arm926ej-s:
memcpy() memory bandwidth (align=0, size=4096): 118.92MB/s
__aeabi_memcpy() memory bandwidth (align=0, size=4096): 117.65MB/s
arm9_memcpy() memory bandwidth (align=0, size=4096): 128.60MB/s
arm11_memcpy() memory bandwidth (align=0, size=4096): 120.21MB/s
memcpy_libc() memory bandwidth (align=0, size=4096): 118.92MB/s

Nokia 800 arm1136j-s:
memcpy() memory bandwidth (align=0, size=4096): 335.13MB/s
__aeabi_memcpy() memory bandwidth (align=0, size=4096): 345.60MB/s
arm9_memcpy() memory bandwidth (align=0, size=4096): 283.57MB/s
arm11_memcpy() memory bandwidth (align=0, size=4096): 335.13MB/s
memcpy_libc() memory bandwidth (align=0, size=4096): 345.60MB/s

Note those are best-case figures (more or less). Different sizes give lower numbers.