Beta Pocketsnes Hack


Hitnrun

Member
Joined
Mar 1, 2008
Messages
427
Well, I got the Wiz PocketSNES source code and wanted to see if I was able to make it run on the Pandora.

It uses Rlyeh's mini-lib for gp2x, so first I tried to see it I could do a SDL "wrapper" for it.
This lib writes directly to the gp2x memory, so out of laziness I just tried to copy the gp2x memory to a SDL surface's pixels, and it worked :)

Then I mapped the gp2x buttons to SDL, disabled the sound and did a SDL_SoftStretch, and Mario ran fullscreen!

Well as I said, this is only a hack, it is slow and laggy (~20fps), sound does not work (because it seemed to be too different from SDL, but I am not too good at sound programming), there's lot of debugging comments on the source, well, I am releasing this so if anyone wants to hack on it, let's have some fun!

This is NOT an official port, I have not contacted the original author either, this is only a for-fun hack.

For coders, there's a Makefile.pnd, that uses the mini-lib simulation in pandora_sdk.h/pandora_sdk.c, and a "pandora" directory with the pnd-making script.

Enough talking. Here are the goods:

http://www.rangelreale.com/pandora/pocketsnes_hack_0.1.pnd

http://www.rangelreale.com/pandora/PocketSNES_v650_pandora_src-0.1.tar.gz

EDIT: To exit, press 1 + 2.
 
craigix said:
I'm not at home right now but does the ASM core work on this?

Well, I'm not an expert in ASM, but the compilation does compile all the asm (.S) files that are compiled for the gp2x/wiz, so I assume it is using asm in its routines.

Unscaled, it seems to be running pretty smooth, I would say that about 45fps. But using software scaling slows down it quite a bit, like to about ~15fps.

I must also add that sound as not disabled, only the playing part was commented, so the sound processing is still being done, only not output.
 
Last edited by a moderator:
Hitnrun said:
craigix said:
I'm not at home right now but does the ASM core work on this?

Well, I'm not an expert in ASM, but the compilation does compile all the asm (.S) files that are compiled for the gp2x/wiz, so I assume it is using asm in its routines.

Unscaled, it seems to be running pretty smooth, I would say that about 45fps. But using software scaling slows down it quite a bit, like to about ~15fps.

I must also add that sound as not disabled, only the playing part was commented, so the sound processing is still being done, only not output.

How did you scale it? I think you can scale using the hardware... I think notaz does that with op_fbrun...
 
Last edited:
EvilDragon said:
How did you scale it? I think you can scale using the hardware... I think notaz does that with op_fbrun...

Still scaling with SDL_SoftStretch, I must learn this fbrun thing someday now.
 
Last edited by a moderator:
Hitnrun said:
EvilDragon said:
How did you scale it? I think you can scale using the hardware... I think notaz does that with op_fbrun...

Still scaling with SDL_SoftStretch, I must learn this fbrun thing someday now.

Well, the code is available here. Not much in there, hope it helps :)

BTW: I'm pretty sure the PocketSNES we have here runs with ASM core... as I doubt there's any other core included in the sourcecode anymore :)
 
Last edited:
op_runfbapp and SDL don't mix at the moment. Then again, if SDL is only being used to get something on the screen it's not too tricky to toss it aside and write to the frame buffer directly. Then you can use the ofbset/fbset/op_runfbapp combination to get hardware scaling.
 
SteveM said:
op_runfbapp and SDL don't mix at the moment. Then again, if SDL is only being used to get something on the screen it's not too tricky to toss it aside and write to the frame buffer directly. Then you can use the ofbset/fbset/op_runfbapp combination to get hardware scaling.

PocketSNES uses the rlyeh lib, so it writes directly to the framebuffer, but it is programmed to the gp2x fb, this must be very different from the Pandora, I presume.

What we need is a Pandora "wrapper" of the rlyeh minilib, this would bring a lot of the more optimized programs.
 
Last edited by a moderator:
Hitnrun said:
PocketSNES uses the rlyeh lib, so it writes directly to the framebuffer, but it is programmed to the gp2x fb, this must be very different from the Pandora, I presume.
I should think it's very similar. If you've managed to fake it by writing to an SDL surface's pixels, you can just as easily write to a memory mapped frame buffer; you just get a pointer to a memory location and scribble away.

(Edit: Here's an example but you'll want to use /dev/fb1 for Pandora.)

What we need is a Pandora "wrapper" of the rlyeh minilib, this would bring a lot of the more optimized programs.
Or is it something that could be ported to the Pandora?
 
Last edited by a moderator:
SteveM said:
Hitnrun said:
PocketSNES uses the rlyeh lib, so it writes directly to the framebuffer, but it is programmed to the gp2x fb, this must be very different from the Pandora, I presume.
I should think it's very similar. If you've managed to fake it by writing to an SDL surface's pixels, you can just as easily write to a memory mapped frame buffer; you just get a pointer to a memory location and scribble away.

(Edit: Here's an example but you'll want to use /dev/fb1 for Pandora.)

What we need is a Pandora "wrapper" of the rlyeh minilib, this would bring a lot of the more optimized programs.
Or is it something that could be ported to the Pandora?

Hmm I'm not too used to accessing a fb direcly, here is the initialization section of the gp2x sdk, see if you think the code is reusable with the Pandora fb (I've cut some non-important parts):

Code:
void gp_initGraphics(unsigned short bpp, int flip, int applyMmuHack)
{

#ifdef DEBUG
		sprintf(buf, "Initing buffer\r\n");
		printf(buf);
#endif 
		gp2x_dev[0] = open("/dev/fb0", O_RDWR);
		gp2x_dev[1] = open("/dev/fb1", O_RDWR);
		gp2x_dev[2] = open("/dev/mem", O_RDWR);
		
#ifdef DEBUG
		sprintf(buf, "Devices opened\r\n");
		printf(buf);
		sprintf(buf, "/dev/fb0: %x \r\n", gp2x_dev[0]);
		printf(buf);
		sprintf(buf, "/dev/fb1: %x \r\n", gp2x_dev[1]);
		printf(buf);
		sprintf(buf, "/dev/mem: %x \r\n", gp2x_dev[2]);
		printf(buf);
#endif 		

		gp2x_memregs=(unsigned short *)mmap(0, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], 0xc0000000);
		gp2x_memregl=(unsigned long *)gp2x_memregs;
		if (!gp2x_blitter) gp2x_blitter=(unsigned long  *)mmap(0, 0x100, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], 0xe0020000);
  
		if (!framebuffer_mmap[0]) framebuffer_mmap[0]=(void *)mmap(0, fb_size, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], (gp2x_physvram[0]=0x04000000-(0x26000*4) )); 
		if (!framebuffer_mmap[1]) framebuffer_mmap[1]=(void *)mmap(0, fb_size, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], (gp2x_physvram[1]=0x04000000-(0x26000*3) )); 
		if (!framebuffer_mmap[2]) framebuffer_mmap[2]=(void *)mmap(0, fb_size, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], (gp2x_physvram[2]=0x04000000-(0x26000*2) )); 
		if (!framebuffer_mmap[3]) framebuffer_mmap[3]=(void *)mmap(0, fb_size, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], (gp2x_physvram[3]=0x04000000-(0x26000*1) )); 

		// offset externally visible buffers by 8
		// this allows DrMD to not worry about clipping
		framebuffer16[0]=framebuffer_mmap[0]+8;
		framebuffer16[1]=framebuffer_mmap[1]+8;
		framebuffer16[2]=framebuffer_mmap[2]+8;
		framebuffer16[3]=framebuffer_mmap[3]+8;
		//ofset physical buffer as well
		gp2x_physvram[0]+=16;
		gp2x_physvram[1]+=16;
		gp2x_physvram[2]+=16;
		gp2x_physvram[3]+=16;
		// clear all the framebuffers to black
		// otherwise you get crap displayed on the screen the first time
		// you start 
		framebuffer8[0]=(unsigned char*)framebuffer16[0];
		framebuffer8[1]=(unsigned char*)framebuffer16[1];
		framebuffer8[2]=(unsigned char*)framebuffer16[2];
		framebuffer8[3]=(unsigned char*)framebuffer16[3];

		// Clear the frame buffers
		memset(framebuffer16[0],0,320*240*2);
		memset(framebuffer16[1],0,320*240*2);
		memset(framebuffer16[2],0,320*240*2);
		memset(framebuffer16[3],0,320*240*2);

	// Set graphics mode
	gp2x_memregs[0x28DA>>1]=(((bpp+1)/8)<<9)|0xAB; /*8/15/16/24bpp...*/
	gp2x_memregs[0x290C>>1]=320*((bpp+1)/8);
}

And the flipping page code:

Code:
void gp_setFramebuffer(int flip, int sync)
{
	CurrentFrameBuffer=flip;
	unsigned int address=(unsigned int)gp2x_physvram[flip];
	unsigned short x=0;

	gp2x_memregs[0x290E>>1]=(unsigned short)(address & 0xffff);
	gp2x_memregs[0x2910>>1]=(unsigned short)(address >> 16);
	gp2x_memregs[0x2912>>1]=(unsigned short)(address & 0xffff);
	gp2x_memregs[0x2914>>1]=(unsigned short)(address >> 16);
}
 
Last edited by a moderator:
Hitnrun said:
Hmm I'm not too used to accessing a fb direcly, here is the initialization section of the gp2x sdk, see if you think the code is reusable with the Pandora fb (I've cut some non-important parts):
Probably easier to rewrite it, by the looks of things. I can have a look at doing a Pandora version this evening if you like. Is this code in the tarball you posted or do I need to get it somewhere else?
 
Last edited by a moderator:
And my hacked code that worked (but is very ugly!):

Code:
void gp_initGraphics(unsigned short bpp, int flip, int applyMmuHack)
{
#ifdef DEBUG
		sprintf(buf, "Initing buffer\r\n");
		printf(buf);
#endif 
		if (!framebuffer_mmap[0]) framebuffer_mmap[0]=(void *)malloc(fb_size);
		if (!framebuffer_mmap[1]) framebuffer_mmap[1]=(void *)malloc(fb_size);
		if (!framebuffer_mmap[2]) framebuffer_mmap[2]=(void *)malloc(fb_size);
		if (!framebuffer_mmap[3]) framebuffer_mmap[3]=(void *)malloc(fb_size);

		// offset externally visible buffers by 8
		// this allows DrMD to not worry about clipping
		framebuffer16[0]=framebuffer_mmap[0]+8;
		framebuffer16[1]=framebuffer_mmap[1]+8;
		framebuffer16[2]=framebuffer_mmap[2]+8;
		framebuffer16[3]=framebuffer_mmap[3]+8;
		// clear all the framebuffers to black
		// otherwise you get crap displayed on the screen the first time
		// you start 
		framebuffer8[0]=(unsigned char*)framebuffer16[0];
		framebuffer8[1]=(unsigned char*)framebuffer16[1];
		framebuffer8[2]=(unsigned char*)framebuffer16[2];
		framebuffer8[3]=(unsigned char*)framebuffer16[3];

		// Clear the frame buffers
		memset(framebuffer16[0],0,320*240*2);
		memset(framebuffer16[1],0,320*240*2);
		memset(framebuffer16[2],0,320*240*2);
		memset(framebuffer16[3],0,320*240*2);
	

	
    if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 ) {
	fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
	exit(1);
    }
    atexit(SDL_Quit);

	if (screen == NULL)
{
	screen = SDL_SetVideoMode(800, 480, bpp, SDL_SWSURFACE|SDL_FULLSCREEN);
    if ( screen == NULL ) {
        fprintf(stderr, "Unable to set 320x240 video: %s\n", SDL_GetError());
        exit(1);
    }

		sgame = SDL_CreateRGBSurface(SDL_HWSURFACE, 320, 240, screen->format->BitsPerPixel,
		                    screen->format->Rmask,
		                    screen->format->Gmask,
		                    screen->format->Bmask, 0);
	    if ( sgame == NULL ) {
		fprintf(stderr, "Unable to create 320x240 surface: %s\n", SDL_GetError());
		exit(1);
	    }

	for (x=0; x<4; x++)
	{
		game[x] = SDL_CreateRGBSurfaceFrom(framebuffer16[x], 320, 240, screen->format->BitsPerPixel,
				    //320 * (screen->format->BitsPerPixel / 8),
				    sgame->pitch,
		                    screen->format->Rmask,
		                    screen->format->Gmask,
		                    screen->format->Bmask, 0);
	    if ( game[x] == NULL ) {
		fprintf(stderr, "Unable to create 320x240 surface: %s\n", SDL_GetError());
		exit(1);
	    }
	}
	SDL_ShowCursor(SDL_DISABLE);
}
	
}

And flip (the first commented section also works, but is somewhat slower).

Code:
void gp_setFramebuffer(int flip, int sync)
{
	CurrentFrameBuffer=flip;

	//SDL_LockSurface( sgame );
	//memcpy(sgame->pixels, framebuffer16[flip], fb_size);
	//SDL_UnlockSurface( sgame );
	//SDL_BlitSurface(sgame, NULL, screen, NULL);
	//SDL_SoftStretch( sgame, NULL, screen, NULL );

	SDL_SoftStretch( game[CurrentFrameBuffer], NULL, screen, NULL );
	//SDL_BlitSurface(game[CurrentFrameBuffer], NULL, screen, NULL);

	SDL_Flip(screen);
}
 
Well, didn't manage to finish it off, but here's a patch for direct frame buffer rendering. Input uses the kernel interface, and V-sync is in there somewhere. Still some issues with flickering (probably doing the steps of the flip in the wrong order or something) and there's a double free on exit to track down, but it gets about 45-60 FPS at 500MHz, depending on the game.

http://www.cosam.org/computers/pandora/PocketSNES_v650_SteveM.patch

I ran it like this:

Code:
#!/bin/bash

FBDEV=/dev/fb1

ofbset -fb $FBDEV -pos 80 0 -size 640 480 -mem 153600 -en 1
fbset -fb $FBDEV -g 320 240 320 960 16

op_runfbapp ./pocketsnes

ofbset -fb $FBDEV -pos 0 0 -size 0 0 -mem 0 -en 0
 
Fixed the flickering, patch updated. Also fixed control layout (now matches real SNES controller) and saving of default ROM directory should work too.

PND based on Hitnrun's PXML can be had here.

Now we just need someone to fix the sound... ;-)
 
SteveM said:
Fixed the flickering, patch updated. Also fixed control layout (now matches real SNES controller) and saving of default ROM directory should work too.

PND based on Hitnrun's PXML can be had here.

Now we just need someone to fix the sound... ;-)

Nice! Will take a look soon!

Now we can look for minilib-based gp2x software to port :)
 
Last edited by a moderator:
Just tested it, something is still wrong... it's still pretty slow.
It should run fullspeed with 500MHz, but it goes down to 40 fps.

Also, the background in the menu is moving very slowly. With GINGE and PocketSNES, the menu background runs way too fast (emulation doesn't work though), so something is still there that slows it down.

BTW: You should also map the menu to some other key... can't find VOL+/- :D

But good work so far :)
 
EvilDragon said:
Just tested it, something is still wrong... it's still pretty slow.
It should run fullspeed with 500MHz, but it goes down to 40 fps.
All we got rid of here was the SDL_SoftStretch so it'll run about the same as the non-scaled SDL build (although I say this without having tried the SDL one, so if it's significantly slower do let me know). I was also expecting much better performance, although it runs quite nicely at 700MHz.

Also, the background in the menu is moving very slowly. With GINGE and PocketSNES, the menu background runs way too fast (emulation doesn't work though), so something is still there that slows it down.
Not sure what's going on there but it could be something to do with vsync. I think the menu waits for vsync at every flip, but during emulation it might not do so for every frame.

BTW: You should also map the menu to some other key... can't find VOL+/- :D
Yeah, we'll map it to the "any" key ;-p
 
Last edited by a moderator:
Back
Top