@"Christof Eb." said:
Hi Ada,
perhaps you might by chance be willing to provide some help for people, who want to use your Z80 and/or MC68000 emulators?
Yea, I was waiting for someone to ask...
I would need some starting point for the 68k-emulator to get it execute a program from HUB Ram. A separated 68k emulator file would be rather helpful....
Would be helpful, but doesn't exist for a reason. There are certain machine specificities that must be hardcoded in multiple places. NeoYume was started by copying out the relevant sections from MegaYume and tweaking them. I guess I could make a generic 68k RAM-only machine emulator, but a) that wouldn't be very helpful as a useful example of how to implement memory mapping b) I'm currently slightly busy wanting to tie up the *Yume projects and want to do focus on other things afterwards. So here's a bunch of advice instead. Read along in the source code.
You want to be at least semi-familiar with 68000 programming
Be well aware that there are probably a bunch of bugs (especially since an actual OS will use features a bare-metal game engine will never touch). Be ready to debug strangeities.
Start with MegaYume's version of MotoKore, it has slightly less complex memory mapping (only one ROM region instead of prog/port/bios), but you can/should also cross reference the NeoYume version.
I've been knocking loose some instructions here and there lately, make sure you're using the latest source.
The actual 68000 code is entirely within the sections labeled as "MotoKore 68000"
mk_init is the hubexec entry point for the emulator cog. There's some other gunk shoved in there that you want to trim. The important parts:
loading cog/lut code
caching ROM vectors into mk_vectorcache (you'll need to refactor everything vector-related if you want RAM vectors!!!)
setting up initial SR/SP/PC
In general: most routines must end in ret wcz. The main exception is top-level instruction implementation code.
Also: keep in mind that 68k is a big endian machine. This can trip you up.
Memory mapping for data read/write is performed in mk_setup_ea8/mk_setup_ea16/mk_setup_ea32. This must result in appropriate memory handler for the indicated width being installed in mk_readf/mk_writef. mk_eacache can be used to convey a temp value to these handlers (for my RAM handlers, this is the real hub address that backs the virtual RAM)
A memory handler may:
Freely use pa,ptrb,mk_eacache and any mk_memtmp*
Readmk_effaddr
Clobber mk_memvalue (if a write handler)
A memory read handler should not return values that are out-of-range (i.e. an 8 bit handler should not return any value with bits 8 through 31 set)
Memory mapping for program read is performed in mk_dobranch. This must result in an opword reading function being installed in mk_getopf. mk_dobranch and the current getopf have dominion over ptra
This function has to read the next word of the program into mk_opword and increment mk_virtualpc
My RAM execution isn't terribly optimized since it is much faster than external ROM, anyways. You could optimize yours with PTRA (which poses the issue that you can run past the end of the virtual RAM and execute garbo, but that shouldn't be happening, should it?)
After redoing both memory mapping codes and removing the PSRAM code, it should compile outside of MegaYume. From there you can hook up a simple serial output port or smth and write a hello world program or something.
Note that there's a SEGA-ism in mk_hub_tst_tas: The megadrive bus logic does not support the atomic TAS instruction properly. To restore the standard functionality you just comment out the if_nz ret.
IRQs are handled by self-modifying the mk_ihook* instruction slots. They are set up in mk_hub_set_sr. These must be duplicated verbatim in mk_hub_tracehook because reasons.
Speaking of, I don't think I ever properly tested trace mode, so it probably doesn't work.
@"Christof Eb." said:
Hi Ada,
perhaps you might by chance be willing to provide some help for people, who want to use your Z80 and/or MC68000 emulators?
Yea, I was waiting for someone to ask...
I would need some starting point for the 68k-emulator to get it execute a program from HUB Ram. A separated 68k emulator file would be rather helpful....
Would be helpful, but doesn't exist for a reason. There are certain machine specificities that must be hardcoded in multiple places. NeoYume was started by copying out the relevant sections from MegaYume and tweaking them. I guess I could make a generic 68k RAM-only machine emulator, but a) that wouldn't be very helpful as a useful example of how to implement memory mapping b) I'm currently slightly busy wanting to tie up the *Yume projects and want to do focus on other things afterwards. So here's a bunch of advice instead. Read along in the source code.
You want to be at least semi-familiar with 68000 programming
Be well aware that there are probably a bunch of bugs (especially since an actual OS will use features a bare-metal game engine will never touch). Be ready to debug strangeities.
Start with MegaYume's version of MotoKore, it has slightly less complex memory mapping (only one ROM region instead of prog/port/bios), but you can/should also cross reference the NeoYume version.
I've been knocking loose some instructions here and there lately, make sure you're using the latest source.
The actual 68000 code is entirely within the sections labeled as "MotoKore 68000"
mk_init is the hubexec entry point for the emulator cog. There's some other gunk shoved in there that you want to trim. The important parts:
loading cog/lut code
caching ROM vectors into mk_vectorcache (you'll need to refactor everything vector-related if you want RAM vectors!!!)
setting up initial SR/SP/PC
In general: most routines must end in ret wcz. The main exception is top-level instruction implementation code.
Also: keep in mind that 68k is a big endian machine. This can trip you up.
Memory mapping for data read/write is performed in mk_setup_ea8/mk_setup_ea16/mk_setup_ea32. This must result in appropriate memory handler for the indicated width being installed in mk_readf/mk_writef. mk_eacache can be used to convey a temp value to these handlers (for my RAM handlers, this is the real hub address that backs the virtual RAM)
A memory handler may:
Freely use pa,ptrb,mk_eacache and any mk_memtmp*
Readmk_effaddr
Clobber mk_memvalue (if a write handler)
A memory read handler should not return values that are out-of-range (i.e. an 8 bit handler should not return any value with bits 8 through 31 set)
Memory mapping for program read is performed in mk_dobranch. This must result in an opword reading function being installed in mk_getopf. mk_dobranch and the current getopf have dominion over ptra
This function has to read the next word of the program into mk_opword and increment mk_virtualpc
My RAM execution isn't terribly optimized since it is much faster than external ROM, anyways. You could optimize yours with PTRA (which poses the issue that you can run past the end of the virtual RAM and execute garbo, but that shouldn't be happening, should it?)
After redoing both memory mapping codes and removing the PSRAM code, it should compile outside of MegaYume. From there you can hook up a simple serial output port or smth and write a hello world program or something.
Note that there's a SEGA-ism in mk_hub_tst_tas: The megadrive bus logic does not support the atomic TAS instruction properly. To restore the standard functionality you just comment out the if_nz ret.
Feel free to ask further questions
Thanks for your long answer!
I totally understand, that you want to focus on new things.
I will see, if I can get an idea of how your emulator works with these informations. Hm, yes, will be not easy for me. Well, we will see, at mk_getop_rom prtra seems to point into some sort of cache, while mk_virtualpc "is the real pc". It's a starting point. :-)
@"Christof Eb." said:
Thanks for your long answer!
I totally understand, that you want to focus on new things.
I will see, if I can get an idea of how your emulator works with these informations. Hm, yes, will be not easy for me. Well, we will see, at mk_getop_rom prtra seems to point into some sort of cache, while mk_virtualpc "is the real pc". It's a starting point. :-)
Oh yeah, explaining the important registers is probably a good idea, too:
mk_virtualpc is the actual PC, as said, whereas the romque (note: I can never spell queue right, thus "que") system uses ptra to keep track of the PC in the currently cached bit of ROM
mk_d*/mk_a* are obvious. Note that they are at the very beginning of cog RAM for a reason.
mk_sr is also obvious
mk_opword usually contains the current opcode, but some instructions trash it because brr fast
mk_effaddr contains the current computed EA, but it can also be set manually (see mk_call) followed by a call to ``mk_setup_ea*`. Notably, this is the real computed EA, full 32 bits (there's actually nothing preventing one from removing the 24bit address masking and having a real 4GB virtual address space)
mk_shiftit generally contains the amount of bits above the current operand width (24 for byte, 16 for word, 0 for long). It is automatically set by the mk_setup_operand* functions (except for _move entry points) and is used such that code paths can be shared between different widths. Note that the bitshift instructions use it in a slightly different way.
mk_memvalue is the input/output for memory handlers, as mentioned above
Oh, and mk_branchdisplace is just a parameter for the mk_dobranch routine. Absolute branches are always translated to relative, since that's what it needs to figure out if it can just adjust the romque pointer or if it needs to do a full memory mapping + que refill.
Also, ignore all the commented code in mk_dobranch, it's just debugging remnants. amogus.
Final polishment for MegaYume/NeoYume V1.0 is starting.
NeoYume is pretty much there, but I really need to work on MegaYume's UI some more. It does more, but (aside from the nice icons) looks kinda terrible. Good thing it's written like actual Smile. Apparently the concept of using coordinates relative to constants instead of just hardcoding them was invented sometime this year.
@Rayman said:
@Wuerfel_21 Thanks for including instructions for the two PSRAM modules. I'm glad you all figured out a way to make them work...
Was just looking at RAMCONFIG.MD for MegaYume and it says the 96 MB board doesn't work for it. Is that still true?
Yes. I could make it work, but I just couldn't be bothered (yet). Might actually see to that.
I'm going to try to find a way to "sell" these modules. Will probably be via donation to Red Cross...
Neat.
@Rayman said:
@Wuerfel_21 Is the list of supported games for MegaYume on Github?
No, because there doesn't need to be one (99% of Megadrive games can reasonably be launched from just the ROM image, whereas NeoGeo is headache of ROM layouts and bankswitching that requires prior knowledge of in neoyume_gamedb.spin2). I can give you an impromptu one though:
Games I like to believe work 100% (got decent amount of test play):
Sonic the Hedgehog 1/2/3
Castlevania Bloodlines
Panorama Cotton (sometimes broken music if loaded after quitting another game)
Rocket Knight Adventures has slight visual glitches in levels with water (HBlank IRQ timing?)
King of the Monsters, Samurai Shodown and probably other SNK ports have sprite corruption (just play the NeoGeo versions instead)
Thunder Force 4 has music tempo issues
Any EA game has a corrupted logo
Monster World 4 is missing a text crawl in it's attract sequence (few emulators get this one right)
Xeno Crisis has minor music issues (occasional odd timbres and dropped notes).
Zero Tolerance runs too fast.
Don't work:
Super Street Fighter 2 (bankswitch)
Sonic & Knuckles (bankswitch, use Sonic 3 Complete hack instead)
Any homebrew with bankswitching, for that matter
Double Dragon 2 (????)
Everything else is in "should work, complain to me if it doesn't" territory. In particular, I hope the SRAM save implementation is robust enough. Always sucks to get a saved game corrupted.
Had to update FlexProp too. Menu is different now, but Sonic Wings 2 still doesn't work... Either my 24 MB PSRAM config is wrong, or my ROM files are wrong...
@Rayman said:
My version may be old, I'll update. Thanks.
Had to update FlexProp too. Menu is different now, but Sonic Wings 2 still doesn't work... Either my 24 MB PSRAM config is wrong, or my ROM files are wrong...
Crossed Swords does still work....
Something to with the higher banks then (with 4 bit PSRAM each line on the load screen corrosponds to one bank, FYI).
Got it working on P2 Eval board.
Previously was on my own board with P2 Edge Rev.A board.
Maybe Rev. A Edge doesn't go fast enough? Or, maybe there's some power issue...
@Rayman said:
Got it working on P2 Eval board.
Previously was on my own board with P2 Edge Rev.A board.
Maybe Rev. A Edge doesn't go fast enough? Or, maybe there's some power issue...
The known-good settings are for P2Eval only. If your board has different trace lengths you need to mess with the delay/sync values
Wired XBox One controller doesn't seem to work. Maybe it shouldn't.
There's nothing I need to change in config.spin2 for it to work, right?
I'm assuming it would be automatically detected, if it could work...
Hmm, that might be using a similar rickety autodetect scheme as the wired 8bitdo pad I have (that's also not working) - Switch protocol is some subset of the regular HID protocol (not sure on specifics) but to show up as an XInput device on PC requires a special protocol. The correct solution to this is to have some button combo to switch modes...
Here's all the things I know work with the *yume USB driver:
- Anything that works as a boot protocol / 6KRO keyboard and can send the right scancodes
- NeoGeo mini controller (hardcoded support - inexplicably doesn't work with @macca 's actual HID implementation)
- RetroBit SEGA Saturn USB Controller (hardcoded support, but also has an XInput mode (entered by holding start+B))
- Speedlink Strike FX (XInput)
- 8bitdo SNES Retro Receiver (XInput protocol inexplicably accessible on firmware update port)
Should probably work:
- RetroBit SEGA Megadrive USB Controller (hopefully same PID/Report as Saturn version - should probably buy one)
- Wired Xbox 360 controller (genuine or knockoff)
Known not to work:
- Any HID/"DInput" controller without hardcoded support (should probably refactor away from direct hardcoding to a table/bytecode approach that makes it easier to add new definitions)
- 8bitdo SN30 Pro USB (buggy firmware - have to downgrade for it to work on Windows even)
- PS3 controller (There's some leftover code to handle it's special sauce, but I don't have one to mess with, so it isn't supported by the button mapping layer)
- XBox One controller (according to Rayman - I think uses new protocol)
As previously mentioned, USB controllers are a minefield, thanks to HID being a terrible non plug-and-play thing for anything but keyboards and the XInput protocol being a de-facto standard at best.
@Rayman said:
I just added Wii Classic Controller support.
Neat. Don't have any hardware set up to test it... Might buy some extension cord to cannibalize so I can test this.
Slight nitpick: Please ZIP up the multiple files, downloading them individually is a pain. Or better yet, do it as a git PR.
Think you can have many players using this...
Yes, you can. For the emulators, you just put the second player data into the upper 16 bits of the controller long.
Also actual code criticism:
10ms wait polling period is bad(tm), because it will give you unneccessary latency fluctuations due to time alignment/beating between the VBlank interrupt and the polling. You either want to poll fast enough to where it isn't an issue or synchronize polling to the video timing (ideally such that you get done just ahead of the IRQ hitting on line 224). For SNES polling I just reduced the period to 1ms, which shouldn't be noticeable to even the most attuned to these things. TBF I'm not sure what polling interval the USB driver ends up with.
What weird knockoff controller are you using where your left stick actually reports -32/+31? Especially in diagonals. Original Wii classic controllers (or any nintendo controller from that era, really) don't do that, the housing constrains the range to an octagon within the theoretical XY plane.
D-Pad isn't mapped at all????
I guess you could have re-used the SNES_LAYOUT defines (since Wii CC basically has the same layout), but whatever.
Comments
Yea, I was waiting for someone to ask...
Would be helpful, but doesn't exist for a reason. There are certain machine specificities that must be hardcoded in multiple places. NeoYume was started by copying out the relevant sections from MegaYume and tweaking them. I guess I could make a generic 68k RAM-only machine emulator, but a) that wouldn't be very helpful as a useful example of how to implement memory mapping b) I'm currently slightly busy wanting to tie up the *Yume projects and want to do focus on other things afterwards. So here's a bunch of advice instead. Read along in the source code.
mk_init
is the hubexec entry point for the emulator cog. There's some other gunk shoved in there that you want to trim. The important parts:mk_vectorcache
(you'll need to refactor everything vector-related if you want RAM vectors!!!)In general: most routines must end in
ret wcz
. The main exception is top-level instruction implementation code.mk_setup_ea8
/mk_setup_ea16
/mk_setup_ea32
. This must result in appropriate memory handler for the indicated width being installed inmk_readf
/mk_writef
.mk_eacache
can be used to convey a temp value to these handlers (for my RAM handlers, this is the real hub address that backs the virtual RAM)A memory handler may:
pa
,ptrb
,mk_eacache
and anymk_memtmp*
mk_effaddr
mk_memvalue
(if a write handler)A memory read handler should not return values that are out-of-range (i.e. an 8 bit handler should not return any value with bits 8 through 31 set)
Memory mapping for program read is performed in
mk_dobranch
. This must result in an opword reading function being installed inmk_getopf
.mk_dobranch
and the current getopf have dominion overptra
mk_opword
and incrementmk_virtualpc
After redoing both memory mapping codes and removing the PSRAM code, it should compile outside of MegaYume. From there you can hook up a simple serial output port or smth and write a hello world program or something.
mk_hub_tst_tas
: The megadrive bus logic does not support the atomic TAS instruction properly. To restore the standard functionality you just comment out theif_nz ret
.Oh, some more:
mk_ihook*
instruction slots. They are set up inmk_hub_set_sr
. These must be duplicated verbatim inmk_hub_tracehook
because reasons.Thanks!
Thanks for your long answer!
I totally understand, that you want to focus on new things.
I will see, if I can get an idea of how your emulator works with these informations. Hm, yes, will be not easy for me. Well, we will see, at mk_getop_rom prtra seems to point into some sort of cache, while mk_virtualpc "is the real pc". It's a starting point. :-)
Oh yeah, explaining the important registers is probably a good idea, too:
mk_virtualpc
is the actual PC, as said, whereas the romque (note: I can never spell queue right, thus "que") system uses ptra to keep track of the PC in the currently cached bit of ROMmk_d*
/mk_a*
are obvious. Note that they are at the very beginning of cog RAM for a reason.mk_sr
is also obviousmk_opword
usually contains the current opcode, but some instructions trash it because brr fastmk_effaddr
contains the current computed EA, but it can also be set manually (seemk_call
) followed by a call to ``mk_setup_ea*`. Notably, this is the real computed EA, full 32 bits (there's actually nothing preventing one from removing the 24bit address masking and having a real 4GB virtual address space)mk_shiftit
generally contains the amount of bits above the current operand width (24 for byte, 16 for word, 0 for long). It is automatically set by themk_setup_operand*
functions (except for_move
entry points) and is used such that code paths can be shared between different widths. Note that the bitshift instructions use it in a slightly different way.mk_memvalue
is the input/output for memory handlers, as mentioned aboveAnd yeah, my code is pretty spaghett...
Oh, and
mk_branchdisplace
is just a parameter for themk_dobranch
routine. Absolute branches are always translated to relative, since that's what it needs to figure out if it can just adjust the romque pointer or if it needs to do a full memory mapping + que refill.Also, ignore all the commented code in
mk_dobranch
, it's just debugging remnants. amogus.Final polishment for MegaYume/NeoYume V1.0 is starting.
NeoYume is pretty much there, but I really need to work on MegaYume's UI some more. It does more, but (aside from the nice icons) looks kinda terrible. Good thing it's written like actual Smile. Apparently the concept of using coordinates relative to constants instead of just hardcoding them was invented sometime this year.
Like why did I write it like that
Decided I'm too lazy to do anything more than properly centering the UI and removing some debug nonsense. In other words, RC1 upcoming I guess.
Well yes, released v1.0-RC1 for both emulators. If this survives a decent while without any sort of bug report, I'll bump to full v1.0, i guess
https://github.com/IRQsome/MegaYume
https://github.com/IRQsome/NeoYume
@Wuerfel_21 Thanks for including instructions for the two PSRAM modules. I'm glad you all figured out a way to make them work...
Was just looking at RAMCONFIG.MD for MegaYume and it says the 96 MB board doesn't work for it. Is that still true?
I'm going to try to find a way to "sell" these modules. Will probably be via donation to Red Cross...
@Wuerfel_21 Is the list of supported games for MegaYume on Github?
Yes. I could make it work, but I just couldn't be bothered (yet). Might actually see to that.
Neat.
No, because there doesn't need to be one (99% of Megadrive games can reasonably be launched from just the ROM image, whereas NeoGeo is headache of ROM layouts and bankswitching that requires prior knowledge of in
neoyume_gamedb.spin2
). I can give you an impromptu one though:Games I like to believe work 100% (got decent amount of test play):
Working with known issues:
Don't work:
Everything else is in "should work, complain to me if it doesn't" territory. In particular, I hope the SRAM save implementation is robust enough. Always sucks to get a saved game corrupted.
@Wuerfel_21 Sonic Wings 2 not working. It shows up in yellow-green instead of white in the menu... Should that be telling me something?
If it's yellow in the menu your version is shat outta date. Used to have no sound, but it should work properly now. Fixed that weeks ago.
If no games start at all, you have to fiddle with the RAM config.
My version may be old, I'll update. Thanks.
Had to update FlexProp too. Menu is different now, but Sonic Wings 2 still doesn't work... Either my 24 MB PSRAM config is wrong, or my ROM files are wrong...
Crossed Swords does still work....
Something to with the higher banks then (with 4 bit PSRAM each line on the load screen corrosponds to one bank, FYI).
Can you post your config?
Got it working on P2 Eval board.
Previously was on my own board with P2 Edge Rev.A board.
Maybe Rev. A Edge doesn't go fast enough? Or, maybe there's some power issue...
The known-good settings are for P2Eval only. If your board has different trace lengths you need to mess with the delay/sync values
I’m assuming somebody got NeoYume to work with the 32 mb psram edge module, right?
I have to try this with parallax base and see which rev of ramless modules work…
Correct. That is how I ran it here.
No one got it to work on a non-RAM edge though. May just be unsuitable...
CLK/2 doesn't work with non-RAM Edge, external PSRAM and 300+ MHz clock. Can 16 bit at CLK/4 be suitable? This may work
Currently there's only sysclk/3 support and only in NeoYume. uncomment USE_PSRAM_SLOW
Wired XBox One controller doesn't seem to work. Maybe it shouldn't.
There's nothing I need to change in config.spin2 for it to work, right?
I'm assuming it would be automatically detected, if it could work...
Yeah, I think XBone controllers use a different protocol than the 360 one that all the generic pads use.
No luck with Terios controller for Nintendo Switch either.
Works with PC though and shows up as XBox 360 controller...
Hmm, that might be using a similar rickety autodetect scheme as the wired 8bitdo pad I have (that's also not working) - Switch protocol is some subset of the regular HID protocol (not sure on specifics) but to show up as an XInput device on PC requires a special protocol. The correct solution to this is to have some button combo to switch modes...
Here's all the things I know work with the *yume USB driver:
- Anything that works as a boot protocol / 6KRO keyboard and can send the right scancodes
- NeoGeo mini controller (hardcoded support - inexplicably doesn't work with @macca 's actual HID implementation)
- RetroBit SEGA Saturn USB Controller (hardcoded support, but also has an XInput mode (entered by holding start+B))
- Speedlink Strike FX (XInput)
- 8bitdo SNES Retro Receiver (XInput protocol inexplicably accessible on firmware update port)
Should probably work:
- RetroBit SEGA Megadrive USB Controller (hopefully same PID/Report as Saturn version - should probably buy one)
- Wired Xbox 360 controller (genuine or knockoff)
Known not to work:
- Any HID/"DInput" controller without hardcoded support (should probably refactor away from direct hardcoding to a table/bytecode approach that makes it easier to add new definitions)
- 8bitdo SN30 Pro USB (buggy firmware - have to downgrade for it to work on Windows even)
- PS3 controller (There's some leftover code to handle it's special sauce, but I don't have one to mess with, so it isn't supported by the button mapping layer)
- XBox One controller (according to Rayman - I think uses new protocol)
As previously mentioned, USB controllers are a minefield, thanks to HID being a terrible non plug-and-play thing for anything but keyboards and the XInput protocol being a de-facto standard at best.
I just added Wii Classic Controller support.
Think you can have many players using this...
Neat. Don't have any hardware set up to test it... Might buy some extension cord to cannibalize so I can test this.
Slight nitpick: Please ZIP up the multiple files, downloading them individually is a pain. Or better yet, do it as a git PR.
Yes, you can. For the emulators, you just put the second player data into the upper 16 bits of the controller long.
Also actual code criticism:
SNES_LAYOUT
defines (since Wii CC basically has the same layout), but whatever.It can be improved for sure.
I just posted as soon as playable.