Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/waterbox/gpgx/core/cart_hw/ggenie.c
2 views
1
/****************************************************************************
2
* Genesis Plus
3
* Game Genie Hardware emulation
4
*
5
* Copyright (C) 2009-2011 Eke-Eke (Genesis Plus GX)
6
*
7
* Based on documentation from Charles McDonald
8
* (http://cgfm2.emuviews.com/txt/genie.txt)
9
*
10
* Redistribution and use of this code or any derivative works are permitted
11
* provided that the following conditions are met:
12
*
13
* - Redistributions may not be sold, nor may they be used in a commercial
14
* product or activity.
15
*
16
* - Redistributions that are modified from the original source must include the
17
* complete source code, including the source code for all components used by a
18
* binary built from the modified sources. However, as a special exception, the
19
* source code distributed need not include anything that is normally distributed
20
* (in either source or binary form) with the major components (compiler, kernel,
21
* and so on) of the operating system on which the executable runs, unless that
22
* component itself accompanies the executable.
23
*
24
* - Redistributions must reproduce the above copyright notice, this list of
25
* conditions and the following disclaimer in the documentation and/or other
26
* materials provided with the distribution.
27
*
28
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
29
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
32
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38
* POSSIBILITY OF SUCH DAMAGE.
39
*
40
****************************************************************************************/
41
42
#include "shared.h"
43
44
struct
45
{
46
uint8 enabled;
47
uint8 *rom;
48
uint16 regs[0x20];
49
uint16 old[6];
50
uint16 data[6];
51
uint32 addr[6];
52
} ggenie;
53
54
static unsigned int ggenie_read_byte(unsigned int address);
55
static unsigned int ggenie_read_word(unsigned int address);
56
static void ggenie_write_byte(unsigned int address, unsigned int data);
57
static void ggenie_write_word(unsigned int address, unsigned int data);
58
static void ggenie_write_regs(unsigned int offset, unsigned int data);
59
60
void ggenie_init(void)
61
{
62
int i;
63
FILE *f;
64
65
memset(&ggenie,0,sizeof(ggenie));
66
67
/* Store Game Genie ROM (32k) above cartridge ROM + SRAM area */
68
if (cart.romsize > 0x810000) return;
69
ggenie.rom = cart.rom + 0x810000;
70
71
/* Open Game Genie ROM file */
72
f = fopen(GG_ROM,"rb");
73
if (f == NULL) return;
74
75
/* Load ROM */
76
for (i=0; i<0x8000; i+=0x1000)
77
{
78
fread(ggenie.rom + i, 0x1000, 1, f);
79
}
80
81
/* Close ROM file */
82
fclose(f);
83
84
#ifdef LSB_FIRST
85
for (i=0; i<0x8000; i+=2)
86
{
87
/* Byteswap ROM */
88
uint8 temp = ggenie.rom[i];
89
ggenie.rom[i] = ggenie.rom[i+1];
90
ggenie.rom[i+1] = temp;
91
}
92
#endif
93
94
/* $0000-$7fff mirrored into $8000-$ffff */
95
memcpy(ggenie.rom + 0x8000, ggenie.rom, 0x8000);
96
97
/* set flag */
98
ggenie.enabled = 1;
99
}
100
101
void ggenie_shutdown(void)
102
{
103
if (ggenie.enabled)
104
{
105
ggenie_switch(0);
106
ggenie.enabled = 0;
107
}
108
}
109
110
void ggenie_reset(int hard)
111
{
112
if (ggenie.enabled)
113
{
114
if (hard)
115
{
116
/* clear codes */
117
ggenie_switch(0);
118
119
/* reset internal state */
120
memset(ggenie.regs,0,sizeof(ggenie.regs));
121
memset(ggenie.old,0,sizeof(ggenie.old));
122
memset(ggenie.data,0,sizeof(ggenie.data));
123
memset(ggenie.addr,0,sizeof(ggenie.addr));
124
}
125
126
/* Game Genie ROM is mapped at $000000-$007fff */
127
m68k.memory_map[0].base = ggenie.rom;
128
129
/* Internal registers are mapped at $000000-$00001f */
130
m68k.memory_map[0].write8 = ggenie_write_byte;
131
m68k.memory_map[0].write16 = ggenie_write_word;
132
133
/* Disable registers reads */
134
m68k.memory_map[0].read16 = NULL;
135
}
136
}
137
138
void ggenie_switch(int enable)
139
{
140
int i;
141
if (enable)
142
{
143
/* enable cheats */
144
for (i=0; i<6; i++)
145
{
146
/* patch is enabled ? */
147
if (ggenie.regs[0] & (1 << i))
148
{
149
/* save old value and patch ROM if enabled */
150
ggenie.old[i] = *(uint16 *)(cart.rom + ggenie.addr[i]);
151
*(uint16 *)(cart.rom + ggenie.addr[i]) = ggenie.data[i];
152
}
153
}
154
}
155
else
156
{
157
/* disable cheats in reversed order in case the same address is used by multiple patches */
158
for (i=5; i>=0; i--)
159
{
160
/* patch is enabled ? */
161
if (ggenie.regs[0] & (1 << i))
162
{
163
/* restore original ROM value */
164
*(uint16 *)(cart.rom + ggenie.addr[i]) = ggenie.old[i];
165
}
166
}
167
}
168
}
169
170
static unsigned int ggenie_read_byte(unsigned int address)
171
{
172
unsigned int data = ggenie.regs[(address >> 1) & 0x1f];
173
return ((address & 1) ? (data & 0xff) : ((data >> 8) & 0xff));
174
}
175
176
static unsigned int ggenie_read_word(unsigned int address)
177
{
178
return ggenie.regs[(address >> 1) & 0x1f];
179
}
180
181
static void ggenie_write_byte(unsigned int address, unsigned int data)
182
{
183
/* Register offset */
184
uint8 offset = (address >> 1) & 0x1f;
185
186
/* /LWR and /UWR are used to decode writes */
187
if (address & 1)
188
{
189
data = (ggenie.regs[offset] & 0xff00) | (data & 0xff);
190
}
191
else
192
{
193
data = (ggenie.regs[offset] & 0x00ff) | ((data & 0xff) << 8);
194
}
195
196
/* Update internal register */
197
ggenie_write_regs(offset,data);
198
}
199
200
static void ggenie_write_word(unsigned int address, unsigned int data)
201
{
202
/* Register offset */
203
uint8 offset = (address >> 1) & 0x1f;
204
205
/* Write internal register (full WORD) */
206
ggenie_write_regs(offset,data);
207
}
208
209
static void ggenie_write_regs(unsigned int offset, unsigned int data)
210
{
211
/* update internal register */
212
ggenie.regs[offset] = data;
213
214
/* Mode Register */
215
if (offset == 0)
216
{
217
/* MODE bit */
218
if (data & 0x400)
219
{
220
/* $0000-$7ffff reads mapped to Cartridge ROM */
221
m68k.memory_map[0].base = cart.rom;
222
m68k.memory_map[0].read8 = NULL;
223
m68k.memory_map[0].read16 = NULL;
224
}
225
else
226
{
227
/* $0000-$7ffff reads mapped to Game Genie ROM */
228
m68k.memory_map[0].base = ggenie.rom;
229
m68k.memory_map[0].read8 = NULL;
230
m68k.memory_map[0].read16 = NULL;
231
232
/* READ_ENABLE bit */
233
if (data & 0x200)
234
{
235
/* $0000-$7ffff reads mapped to Game Genie Registers */
236
/* code doing this should execute in RAM so we don't need to modify base address */
237
m68k.memory_map[0].read8 = ggenie_read_byte;
238
m68k.memory_map[0].read16 = ggenie_read_word;
239
}
240
}
241
242
/* LOCK bit */
243
if (data & 0x100)
244
{
245
/* decode patch address (ROM area only)*/
246
/* note: Charles's doc is wrong, first register holds bits 23-16 of patch address */
247
ggenie.addr[0] = ((ggenie.regs[2] & 0x3f) << 16) | ggenie.regs[3];
248
ggenie.addr[1] = ((ggenie.regs[5] & 0x3f) << 16) | ggenie.regs[6];
249
ggenie.addr[2] = ((ggenie.regs[8] & 0x3f) << 16) | ggenie.regs[9];
250
ggenie.addr[3] = ((ggenie.regs[11] & 0x3f) << 16) | ggenie.regs[12];
251
ggenie.addr[4] = ((ggenie.regs[14] & 0x3f) << 16) | ggenie.regs[15];
252
ggenie.addr[5] = ((ggenie.regs[17] & 0x3f) << 16) | ggenie.regs[18];
253
254
/* decode patch data */
255
ggenie.data[0] = ggenie.regs[4];
256
ggenie.data[1] = ggenie.regs[7];
257
ggenie.data[2] = ggenie.regs[10];
258
ggenie.data[3] = ggenie.regs[13];
259
ggenie.data[4] = ggenie.regs[16];
260
ggenie.data[5] = ggenie.regs[19];
261
262
/* disable internal registers */
263
m68k.memory_map[0].write8 = m68k_unused_8_w;
264
m68k.memory_map[0].write16 = m68k_unused_16_w;
265
266
/* patch ROM when GG program exits (LOCK bit set) */
267
/* this is done here to handle patched program reads faster & more easily */
268
/* on real HW, address decoding would be done on each reads */
269
ggenie_switch(1);
270
}
271
else
272
{
273
m68k.memory_map[0].write8 = ggenie_write_byte;
274
m68k.memory_map[0].write16 = ggenie_write_word;
275
}
276
}
277
278
/* RESET register */
279
else if (offset == 1)
280
{
281
ggenie.regs[1] |= 1;
282
}
283
}
284
285