Try my Java SNES emulator? :)

Announce new emulators, discuss which games run best under each emulator, and much much more.

Moderator: General Mods

spiller
JSNES Developer
JSNES Developer
Posts: 43
Joined: Sun Mar 15, 2009 11:09 pm
Location: Ireland

Post by spiller »

creaothceann wrote:Why PHP, though?
It's unfussy and quick to code, and has good string & array manipulation. I suppose I use it like someone who knows Perl would use Perl.
byuu

Post by byuu »

Explain this. I don't follow but I really want to! By the way something I tried very early on was to split the complex banks into smaller objects so instead of using if/elses to dissect the address ranges it would use some of the upper bits of the 16-bit bank offset as an index into an array of more function pointer things. It turned out slower, though.
That was the gist of it, but I'll go into more detail.

Okay, so the smallest block of memory is the GSU1, it spans 00-3f|80-bf:3000-32ff. 768 bytes, and since that's not divisible cleanly, we end up with 256.

So the net result is we need a page table, 64k entries. I know, that's a lot of memory, 256KB on a 32-bit system.

So now you would do:
Page &p = page[addr >> 8];
uint8 read = p.read(addr + p.offset);
p.write(addr + p.offset, write);

MMIO tends to map individual registers, but they always appear in 00-3f|80-bf:2000-5fff, so I special case those. I set their page[] entries to a special MMIO class. This class has 16K entries and performs one last indirection.

uint8 read = mmio[addr & 0x3fff].read(addr);
mmio[addr & 0x3fff].write(addr, write);

We want the full 24-bit address passed through to the final read/write, as the BS-X mapper treats an access to 00:5000 differently than to 01:5000, etc.

Now whenever you load a ROM, you initialize this table. You would have a class to represent ROM, and it would inherit from whatever type the page[] table is.

So say you map 01:8000-80ff in LoROM, you would have a call to:
uint8 ROM::read(unsigned addr) {
//addr = 0x018000 + offset
return rom_data[addr];
}

offset in this case would have been set in the page table during ROM load. Since we really want 0x8000, offset would be -0x010000; thus 0x018000 + -0x010000 == 0x8000.

And we sit back and laugh at the idiots who say Java is purer because it doesn't have pointer arithmetic =)

99% of games do not have any memory mapping capabilities, the only ones that do:
BS-X town cartridge + Satellaview downloaded games
S-DD1 games (2 total)
SPC7110 games (effectively 3 total)

And even then, it's not like they do nothing but remap constantly. Still, make your remapper fast. Or if it really bothers you, you can always make a special class that does the MMC functions for you, eg:

uint8 SDD1::read(unsigned addr) {
addr = transform_addr_per_MMC_settings(addr);
return ROM[addr];
}

Also, you're getting close to multiple inheritance here, but if you can make your CPU+PPU inheritable by the MMIO[] base type, you can map the objects directly into your tables. This bypasses your need for a dispatcher that shunts one range to the PPU, one to the APU, and one to the CPU.

Anyway, it may be a bit slower, I don't know. You know me, I've always valued code readability and maintainability over speed. And it allowed me to implement XML-based memory mapping in a day, which would seem to be impossible with your model :(
Those op files are compiled with a rather big and uglyish PHP parser, but there are two big advantages:
(1) reduced code duplication
(2) I can directly compare, for each CPU, running the opcodes with a switch block versus arrays of function pointers, and also ways of handling the states of the M/X/E CPU flag bits, because it takes seconds to switch between different ways of generating the code.
We definitely have our differences in approach, especially with regards to language, but it is always fun seeing the similarities.

I did the exact same thing with a custom parser:

Code: Select all

bcc(0x90, !regs.p.c),
bcs(0xb0, regs.p.c),
bne(0xd0, !regs.p.z),
beq(0xf0, regs.p.z),
bpl(0x10, !regs.p.n),
bmi(0x30, regs.p.n),
bvc(0x50, !regs.p.v),
bvs(0x70, regs.p.v) {
  if(!$1) last_cycle();
  rd.l = op_readpc();
  if($1) {
    aa.w = regs.pc.d + (int8)rd.l;
    regs.pc.w = aa.w;
  } else {
    end;
  }
  op_io_cond6(aa.w);
  last_cycle();
  op_io();
}
And it was one of the things I mentioned right from the beginning, I had a version that built a switch table, and a version that built a jump table. end; becomes break; or return; as appropriate.

What I've discovered in regards to that is that speed is the same. The jump table is more eloquent from an optimization standpoint (it won't overwhelm the compiler with a 50,000-line function), but the switch table compiles faster (doesn't have to make 256*~1-5 functions) and produces a slightly smaller binary.

I've never noticed a difference in speed. The switch table just becomes a jump table after compilation anyway.

I go with a jump table now because I generate the code using C++ templates now. I guess I just really hated having to invoke a pre-processor prior to compilation, though you could automate that with fancy Makefile rules, too. Eh, whatever.

Really, I would say if Java has it, run a profiler on your code. I made the mistake earlier on of considering optimizations just like this, switch vs function pointer, 16-bit read optimizations, NZ flag optimizations, etc. It's focusing on stuff that really doesn't matter.

If you really want to gain massive speed benefits, the most important parts are PPU rendering and S-CPU interrupt testing.
spiller
JSNES Developer
JSNES Developer
Posts: 43
Joined: Sun Mar 15, 2009 11:09 pm
Location: Ireland

Post by spiller »

byuu wrote:So now you would do:
Page &p = page[addr >> 8];
uint8 read = p.read(addr + p.offset);
p.write(addr + p.offset, write);
8-bit page division is something I've tried, but it was within the 16-bit bank division system. So that's two levels of objects, and it wasn't worth it. I suppose I could try redoing the CPU to use 8-bit pages everywhere, but 16-bit banks align naturally with most of the the address wrapping logic of the 65816, and its PB & DB registers.

I've not done any of the BS-X, S-DD1, or SPC7110 things, so I might have to rethink the memory mapping logic as I do those, I dunno.
Still, make your remapper fast.
I've discovered this with the NES. Some games flip segments in and out rapidly, so I break the PRG ROM and CHR ROM arrays into small chunks that can be mapped in and out of byte[][] arrays with simple pointer assignment instead of copying memory. During normal execution then, there aren't even any offsets to add. (It's a sorry substitute for real pointer arithmetic though.)
if you can make your CPU+PPU inheritable by the MMIO[] base type, you can map the objects directly into your tables. This bypasses your need for a dispatcher that shunts one range to the PPU, one to the APU, and one to the CPU.
Elegant!, though I don't think there's any real advantage to it.
If you really want to gain massive speed benefits, the most important parts are PPU rendering and S-CPU interrupt testing.
Oh the PPU, definitely, but since I only aim for opcode-level accuracy on most of the rest of the system, interrupts are happily trivial.
The switch table just becomes a jump table after compilation anyway.
Java has a bytecode instruction for switch jump tables, but the VM doesn't use it to actually implement a jump table once compiling to native code. So manually coding a function pointer table makes a *massive* difference.
grinvader
ZSNES Shake Shake Prinny
Posts: 5632
Joined: Wed Jul 28, 2004 4:15 pm
Location: PAL50, dood !

Post by grinvader »

byuu wrote:Okay, so the smallest block of memory is the GSU1, it spans 00-3f|80-bf:3000-32ff. 768 bytes, and since that's not divisible cleanly, we end up with 256.
For reference, if you drop the "i know what's on the other side already" approach and go for a hardware approach, the smallest block is still 256, with 00-3F(7):2100-21FF being bound to address bus B.
皆黙って俺について来い!!

Code: Select all

<jmr> bsnes has the most accurate wiki page but it takes forever to load (or something)
Pantheon: Gideon Zhi | CaitSith2 | Nach | kode54
byuu

Post by byuu »

Well, technically B breaks down further.

2100-213f = PPU
2140-217f = APU
2180-2183 = CPU
2184+ = BS-X base unit, Exertainment Bike, and if my memory of a forum post years ago is correct, the Ultra-16; and maybe the SFC Box too.

Of course, a purer method would be to multicast the access to all chips on the B bus and let them decide whether or not they want to respond, heh.
BlackSheep
New Member
Posts: 2
Joined: Wed Aug 15, 2012 6:05 pm

Re: Try my Java SNES emulator? :)

Post by BlackSheep »

Hi there,

Do you intend on releasing the source code?

Or would you like to work something out together?

I would love to hack an Android version... !!

I think we could hack games like Pac-Man 2 to run amazingly on the touch screen. =)

Cheers*
BlackSheep
New Member
Posts: 2
Joined: Wed Aug 15, 2012 6:05 pm

Re: Try my Java SNES emulator? :)

Post by BlackSheep »

This is a noob post... please don't kill me. :p

I got an email saying there was a reply to this thread... I don't see any. Please send me a PM, if it was something meant for me. Thx.
Johan_H
Starzinger Addict
Posts: 998
Joined: Tue Aug 17, 2004 1:14 pm
Location: Sweden
Contact:

Re: Try my Java SNES emulator? :)

Post by Johan_H »

Probably a spam post that got deleted.


Robot edit: you guessed correctly.
anim0y
New Member
Posts: 2
Joined: Sun Sep 16, 2012 8:04 am
Location: Philippines

Re: Try my Java SNES emulator? :)

Post by anim0y »

the emulator doesn't seemed to work on my phone (SE k610i).
here are the things that I did.
first, I downloaded the two versions (the two *.zip files) and I used my phone's file manager to see what is inside. Apparently, It was just a bunch *.class files so I just renamed it to *.jar. then, I installed it. but it says "Operation Failed" so I checked the MANIFEST.MF and I changed it (actually, I changed it with the manifest from other apps that I have.). And it installed. but I cant open it...
It kept on saying "Invalid Application".
can someone tell me what's wrong?

it is actually the first time i had encounter such error on an emulator...
other emulator worked just fine on my phone (gb,gbc,nes,commodore64 emulators).


haha.. sorry for the wrong grammar...
paulguy
Zealot
Posts: 1076
Joined: Sat Jul 02, 2005 2:01 am
Contact:

Re: Try my Java SNES emulator? :)

Post by paulguy »

It probably needs a full, fairly up to date JRE. I imagine phones use something reasonably old and stripped down. If you do get it to run on a phone, though, I could imagine like single digit FPS.
Maybe these people were born without that part of their brain that lets you try different things to see if they work better. --Retsupurae
anim0y
New Member
Posts: 2
Joined: Sun Sep 16, 2012 8:04 am
Location: Philippines

Re: Try my Java SNES emulator? :)

Post by anim0y »

incompatibility eh?...
you've got some point there. I just wanted to try it to my old SE phone to see if the emulator works. I dont want to break my android by playing games like super mario world. haha...
thanks man...
Post Reply