ekeeke / Genesis-Plus-GX

An enhanced port of Genesis Plus - accurate & portable Sega 8/16 bit emulator
Other
698 stars 199 forks source link

Feature Request - Sega CD PRG & Word RAM Cheats #445

Closed BooBerry closed 1 year ago

BooBerry commented 2 years ago

Greetings!

I'd like to request support for PRG/Word RAM cheats for Sega CD games. While FF-style cheats that use Genesis RAM already do work (e.g. Sonic CD) there is a growing number of Sega CD games out there with PRG/Word RAM cheats available but you currently can't use them in Genesis Plus GX.

Examples of Sega CD games with PRG/Word RAM cheats available:

https://gamehacking.org/game/85288 https://gamehacking.org/game/85396 https://gamehacking.org/game/85289 https://gamehacking.org/game/85251 https://gamehacking.org/game/85405 https://gamehacking.org/game/85262 https://gamehacking.org/game/85416

And the list goes on. GameHacking.org's Sega CD section is a good resource of Sega CD cheats with new ones being added as time goes by.

Thoughts?

ekeeke commented 2 years ago

This is feasible but cheat support is implemented by the frontend so it's not really a core feature and this needs to be added by frontend developers.

Currently, only the standalone gc/wii application and the libretro port have cheat support but they share more or less the same code I think (see https://github.com/ekeeke/Genesis-Plus-GX/blob/master/gx/gui/cheats.c#L447 for the gc/wii standalone port and https://github.com/ekeeke/Genesis-Plus-GX/blob/master/libretro/libretro.c#L2220 for the libretro port).

Adding support to RAM cheats located outside work RAM (FFxxxx) would require modifications to apply_cheats, clear_cheats and RAMCheatUpdate functions in these two files.

No sure however how this RAM cheats work though, do they use the main-cpu (genesis side) address and so only are able to patch currently visible prg-ram bank ? what about word-ram that can also switched into two banks, one visible on each side ?

birdybro commented 2 years ago

I could be wrong, but I think this is the MiSTer core's update that gave it this capability:

https://github.com/MiSTer-devel/MegaCD_MiSTer/commit/cbb288238a3538948d9608cc99cf6ce85e663b2e

If that helps. I'm not directly knowledgeable on how this works however.

ekeeke commented 2 years ago

According to commit log, it does not seem related to sega CD RAM patch support but thanks anyway, looking in other emulator sourcecode (even in VHDL) that is known to support Sega CD RAM patches could still be helpful to figure the code format.

From the cheat codes linked above, I kinda figured that any code with address field between 0x000000 and 0x07ffff corresponds to 512KB PRG-RAM address in sub-cpu memory map while anything between 0x200000 and 0x23ffff corresponds to Word-RAM address in main-cpu memory map but I am not sure how it works in 1M mode when the Word-RAM is splitted in two 128KB banks, each one being mapped in only one of the two cpu address space simultaneously: does the address correspond to the bank mapped on main-cpu side (like the code address seems to indicate) or to the one mapped on sub-cpu side (like for prg-ram patches) ? or does the address always correspond to an offset in 256KB (joined) Word-RAM ? Unfortunately, I could not find any documentation about the code format or how these codes were found... like with any niche emulation feature I guess.

BooBerry commented 2 years ago

As far as I know the only ways to use PRG and Word RAM cheats for Sega CD games right now is through BizHawk or MAME or using an app like ArtMoney/Cheat Engine.

Support for PRG and Word RAM cheats is supposed to be coming to the MegaSD flash cart at some time in the future. The developer (neodev) told me that he had basically completely support for it, but it hasn't been tested as of yet.

I'll see if I can ask him how he's intending on supporting Word RAM cheats.

Personally, I care more about PRG RAM cheats as there's a lot more of those available, I just included Word RAM with the request since a few of those exist like for Lunar.

EDIT: There's a couple topics on GameHacking.org's forums, which seems to be the source for a lot of the RAM cheats for the Sega CD.

https://gamehacking.org/vb/forum/video-game-hacking-and-development/retro-hacking/206165-my-sega-cd-codes-for-the-bizhawk-emulator

https://gamehacking.org/vb/forum/video-game-hacking-and-development/hacker-threads/5332-lee4-s-sega-cd-par-ram-emucheat-codes

birdybro commented 2 years ago

Looking at it again the cheat engine has had that support since it was added @ekeeke so the relevant places to look would be cheatcodes.sv, mcd.vhd. And this commit that added cheat support probably. The cheatcodes module is a generic parameterized module originally made for the NES core which is now used in other cores, so maybe just look at the parameters and then look at mcd.vhd to see how the module is instantiated.

Here's the instantiation in the Genesis side of things (in SystemVerilog):

CODES #(.ADDR_WIDTH(24), .DATA_WIDTH(16)) codes (
    .clk(MCLK),
    .reset(GG_RESET),
    .enable(~GG_EN),
    .addr_in({M68K_A[23:1], 1'b0}),
    .data_in(MBUS_DI),
    .code(GG_CODE),
    .available(GG_AVAILABLE),
    .genie_ovr(genie_ovr),
    .genie_data(genie_data)
);

Versus the Sega CD side of things (in VHDL):

gg : CODES
    generic map(
        ADDR_WIDTH  => 24,
        DATA_WIDTH  => 16
    )
    port map(
        clk         => CLK,
        reset       => GG_RESET,
        enable      => not GG_EN,
        addr_in     => S68K_A & '0',
        data_in     => S68K_DI,
        code        => GG_CODE,
        available   => GG_AVAILABLE,
        genie_ovr   => GENIE_OVR,
        genie_data  => GENIE_DATA
    );

In our top-level emu module for the core this is how we define the registers for the codes, it's thoroughly commented:

///////////////////////////////////////////////////
// Code loading for WIDE IO (16 bit)
reg [128:0] gg_code;
wire        gg_available = gg_available1 | gg_available2;

// Code layout:
// {clock bit, code flags,     32'b address, 32'b compare, 32'b replace}
//  128        127:96          95:64         63:32         31:0
// Integer values are in BIG endian byte order, so it up to the loader
// or generator of the code to re-arrange them correctly.

always_ff @(posedge clk_sys) begin
    gg_code[128] <= 0;

    if (code_download & ioctl_wr) begin
        case (ioctl_addr[3:0])
            0:  gg_code[111:96]  <= ioctl_data; // Flags Bottom Word
            2:  gg_code[127:112] <= ioctl_data; // Flags Top Word
            4:  gg_code[79:64]   <= ioctl_data; // Address Bottom Word
            6:  gg_code[95:80]   <= ioctl_data; // Address Top Word
            8:  gg_code[47:32]   <= ioctl_data; // Compare Bottom Word
            10: gg_code[63:48]   <= ioctl_data; // Compare top Word
            12: gg_code[15:0]    <= ioctl_data; // Replace Bottom Word
            14: begin
                 gg_code[31:16]   <= ioctl_data; // Replace Top Word
                 gg_code[128]     <= 1;      // Clock it in
            end
        endcase
    end
end

Hopefully this isn't one of those situations where it's not that easy to compare and translate back into C. Good luck!

ekeeke commented 2 years ago

Thanks but it is indeed a situation where you cannot translate hdl back to C, this code is only how the cheat codes are wired from interface to cheat module. Also, looking into it, the codes used in Mister seems to be in a different format with additional informations (namely flags and original data value to compare with) comparing to usual xxxxxx:yyyy codes so it cannot really be used as reference.

EDIT: it seems the 'address' field in Mister cheat codes is simply the absolute address as seen from sub-cpu so it can technically patch any address in sub-cpu memory map (not just ram) by connecting the cheat module on sub-cpu address/data bus and the same is likely done for main-cpu in genesis module with another instance of cheat module. So basically, if they are straightly converted to mister format (without changing the address value):

I am not sure if this is the intended behavior for these patches on RAM that is shareable by genesis and scd (as this means on mister, only the data read by one of the cpu is patched, not the RAM content itself) but this is how Mister cheat module works it seems.

ekeeke commented 1 year ago

Added in https://github.com/ekeeke/Genesis-Plus-GX/commit/98b5c603557f0ce3bbf347c2f75c1412288ec66c

I only tested the Wii port, with one game having PRG-RAM cheat codes (Earthworm Jim CD) and one having Word-RAM cheat codes (Lunar Eternal Blue) to verify both types of codes were working. I did not tested with Retroarch but considering the code changes are identical, it should work the same.

BooBerry commented 1 year ago

Okay, I finally got to test this. In my tests I used RetroArch 1.12.0 (tried both 32-bit and 64-bit versions) on Windows 11 using the libretro core available in this repository's builds folder for 32-bit and from libretro's buildbot for 64-bit with Earthworm Jim Special Edition.

This is the contents of the cheat .cht file that I used.

cheats = 5 

cheat0_desc = "Invincibility"
cheat0_code = "02F554:0010"
cheat0_enable = false

cheat1_desc = "Infinite Health"
cheat1_code = "02F423:0039+02F424:0039+02F425:0039"
cheat1_enable = false

cheat2_desc = "Infinite Lives"
cheat2_code = "02A2BC:0039"
cheat2_enable = false

cheat3_desc = "Infinite Ammo"
cheat3_code = "02F41A:0039+02F41B:0039+02F41C:0039+02F41D:0039"
cheat3_enable = false

cheat4_desc = "Infinite Continues"
cheat4_code = "02A2C4:0009"
cheat4_enable = false

And unfortunately none of the cheats work in-game. In addition there seems to be a 'side effect', enabling multi-line cheats (e.g. I tested a 14 code Game Genie cheat for Invincibility in Mortal Kombat that previously worked fine) in Genesis games are now causing the GPGX core to immediately crash it seems. Will have to investigate this more when I have more time but it's definitely these cheat changes causing that.

ekeeke commented 1 year ago

Indeed, I was a bit quick when I backported the code changes from gamecube/wii port to libretro.c and actually broke cheat support in libretro port (RAM patches being disabled entirely and ROM cheats potentially ending up accessing some undefined address in memory, thus causing application crash).

This should now be fixed with latest commit (https://github.com/ekeeke/Genesis-Plus-GX/commit/90bb356a173dc1010d6694bdb470c176efd9c03b)