/****************************************************************************1* Genesis Plus2* Game Genie Hardware emulation3*4* Copyright (C) 2009-2011 Eke-Eke (Genesis Plus GX)5*6* Based on documentation from Charles McDonald7* (http://cgfm2.emuviews.com/txt/genie.txt)8*9* Redistribution and use of this code or any derivative works are permitted10* provided that the following conditions are met:11*12* - Redistributions may not be sold, nor may they be used in a commercial13* product or activity.14*15* - Redistributions that are modified from the original source must include the16* complete source code, including the source code for all components used by a17* binary built from the modified sources. However, as a special exception, the18* source code distributed need not include anything that is normally distributed19* (in either source or binary form) with the major components (compiler, kernel,20* and so on) of the operating system on which the executable runs, unless that21* component itself accompanies the executable.22*23* - Redistributions must reproduce the above copyright notice, this list of24* conditions and the following disclaimer in the documentation and/or other25* materials provided with the distribution.26*27* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"28* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE29* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE30* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE31* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR32* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF33* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS34* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN35* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)36* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE37* POSSIBILITY OF SUCH DAMAGE.38*39****************************************************************************************/4041#include "shared.h"4243struct44{45uint8 enabled;46uint8 *rom;47uint16 regs[0x20];48uint16 old[6];49uint16 data[6];50uint32 addr[6];51} ggenie;5253static unsigned int ggenie_read_byte(unsigned int address);54static unsigned int ggenie_read_word(unsigned int address);55static void ggenie_write_byte(unsigned int address, unsigned int data);56static void ggenie_write_word(unsigned int address, unsigned int data);57static void ggenie_write_regs(unsigned int offset, unsigned int data);5859void ggenie_init(void)60{61int i;62FILE *f;6364memset(&ggenie,0,sizeof(ggenie));6566/* Store Game Genie ROM (32k) above cartridge ROM + SRAM area */67if (cart.romsize > 0x810000) return;68ggenie.rom = cart.rom + 0x810000;6970/* Open Game Genie ROM file */71f = fopen(GG_ROM,"rb");72if (f == NULL) return;7374/* Load ROM */75for (i=0; i<0x8000; i+=0x1000)76{77fread(ggenie.rom + i, 0x1000, 1, f);78}7980/* Close ROM file */81fclose(f);8283#ifdef LSB_FIRST84for (i=0; i<0x8000; i+=2)85{86/* Byteswap ROM */87uint8 temp = ggenie.rom[i];88ggenie.rom[i] = ggenie.rom[i+1];89ggenie.rom[i+1] = temp;90}91#endif9293/* $0000-$7fff mirrored into $8000-$ffff */94memcpy(ggenie.rom + 0x8000, ggenie.rom, 0x8000);9596/* set flag */97ggenie.enabled = 1;98}99100void ggenie_shutdown(void)101{102if (ggenie.enabled)103{104ggenie_switch(0);105ggenie.enabled = 0;106}107}108109void ggenie_reset(int hard)110{111if (ggenie.enabled)112{113if (hard)114{115/* clear codes */116ggenie_switch(0);117118/* reset internal state */119memset(ggenie.regs,0,sizeof(ggenie.regs));120memset(ggenie.old,0,sizeof(ggenie.old));121memset(ggenie.data,0,sizeof(ggenie.data));122memset(ggenie.addr,0,sizeof(ggenie.addr));123}124125/* Game Genie ROM is mapped at $000000-$007fff */126m68k.memory_map[0].base = ggenie.rom;127128/* Internal registers are mapped at $000000-$00001f */129m68k.memory_map[0].write8 = ggenie_write_byte;130m68k.memory_map[0].write16 = ggenie_write_word;131132/* Disable registers reads */133m68k.memory_map[0].read16 = NULL;134}135}136137void ggenie_switch(int enable)138{139int i;140if (enable)141{142/* enable cheats */143for (i=0; i<6; i++)144{145/* patch is enabled ? */146if (ggenie.regs[0] & (1 << i))147{148/* save old value and patch ROM if enabled */149ggenie.old[i] = *(uint16 *)(cart.rom + ggenie.addr[i]);150*(uint16 *)(cart.rom + ggenie.addr[i]) = ggenie.data[i];151}152}153}154else155{156/* disable cheats in reversed order in case the same address is used by multiple patches */157for (i=5; i>=0; i--)158{159/* patch is enabled ? */160if (ggenie.regs[0] & (1 << i))161{162/* restore original ROM value */163*(uint16 *)(cart.rom + ggenie.addr[i]) = ggenie.old[i];164}165}166}167}168169static unsigned int ggenie_read_byte(unsigned int address)170{171unsigned int data = ggenie.regs[(address >> 1) & 0x1f];172return ((address & 1) ? (data & 0xff) : ((data >> 8) & 0xff));173}174175static unsigned int ggenie_read_word(unsigned int address)176{177return ggenie.regs[(address >> 1) & 0x1f];178}179180static void ggenie_write_byte(unsigned int address, unsigned int data)181{182/* Register offset */183uint8 offset = (address >> 1) & 0x1f;184185/* /LWR and /UWR are used to decode writes */186if (address & 1)187{188data = (ggenie.regs[offset] & 0xff00) | (data & 0xff);189}190else191{192data = (ggenie.regs[offset] & 0x00ff) | ((data & 0xff) << 8);193}194195/* Update internal register */196ggenie_write_regs(offset,data);197}198199static void ggenie_write_word(unsigned int address, unsigned int data)200{201/* Register offset */202uint8 offset = (address >> 1) & 0x1f;203204/* Write internal register (full WORD) */205ggenie_write_regs(offset,data);206}207208static void ggenie_write_regs(unsigned int offset, unsigned int data)209{210/* update internal register */211ggenie.regs[offset] = data;212213/* Mode Register */214if (offset == 0)215{216/* MODE bit */217if (data & 0x400)218{219/* $0000-$7ffff reads mapped to Cartridge ROM */220m68k.memory_map[0].base = cart.rom;221m68k.memory_map[0].read8 = NULL;222m68k.memory_map[0].read16 = NULL;223}224else225{226/* $0000-$7ffff reads mapped to Game Genie ROM */227m68k.memory_map[0].base = ggenie.rom;228m68k.memory_map[0].read8 = NULL;229m68k.memory_map[0].read16 = NULL;230231/* READ_ENABLE bit */232if (data & 0x200)233{234/* $0000-$7ffff reads mapped to Game Genie Registers */235/* code doing this should execute in RAM so we don't need to modify base address */236m68k.memory_map[0].read8 = ggenie_read_byte;237m68k.memory_map[0].read16 = ggenie_read_word;238}239}240241/* LOCK bit */242if (data & 0x100)243{244/* decode patch address (ROM area only)*/245/* note: Charles's doc is wrong, first register holds bits 23-16 of patch address */246ggenie.addr[0] = ((ggenie.regs[2] & 0x3f) << 16) | ggenie.regs[3];247ggenie.addr[1] = ((ggenie.regs[5] & 0x3f) << 16) | ggenie.regs[6];248ggenie.addr[2] = ((ggenie.regs[8] & 0x3f) << 16) | ggenie.regs[9];249ggenie.addr[3] = ((ggenie.regs[11] & 0x3f) << 16) | ggenie.regs[12];250ggenie.addr[4] = ((ggenie.regs[14] & 0x3f) << 16) | ggenie.regs[15];251ggenie.addr[5] = ((ggenie.regs[17] & 0x3f) << 16) | ggenie.regs[18];252253/* decode patch data */254ggenie.data[0] = ggenie.regs[4];255ggenie.data[1] = ggenie.regs[7];256ggenie.data[2] = ggenie.regs[10];257ggenie.data[3] = ggenie.regs[13];258ggenie.data[4] = ggenie.regs[16];259ggenie.data[5] = ggenie.regs[19];260261/* disable internal registers */262m68k.memory_map[0].write8 = m68k_unused_8_w;263m68k.memory_map[0].write16 = m68k_unused_16_w;264265/* patch ROM when GG program exits (LOCK bit set) */266/* this is done here to handle patched program reads faster & more easily */267/* on real HW, address decoding would be done on each reads */268ggenie_switch(1);269}270else271{272m68k.memory_map[0].write8 = ggenie_write_byte;273m68k.memory_map[0].write16 = ggenie_write_word;274}275}276277/* RESET register */278else if (offset == 1)279{280ggenie.regs[1] |= 1;281}282}283284285