1 /* Copyright (c) 2016 taizou
2  *
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include <mgba/internal/gba/vfame.h>
7 
8 #include <mgba/internal/gba/gba.h>
9 #include <mgba/internal/gba/memory.h>
10 
11 static const uint8_t ADDRESS_REORDERING[4][16] = {
12 	{ 15, 14, 9, 1, 8, 10, 7, 3, 5, 11, 4, 0, 13, 12, 2, 6 },
13 	{ 15, 7, 13, 5, 11, 6, 0, 9, 12, 2, 10, 14, 3, 1, 8, 4 },
14 	{ 15, 0, 3, 12, 2, 4, 14, 13, 1, 8, 6, 7, 9, 5, 11, 10 }
15 };
16 static const uint8_t ADDRESS_REORDERING_GEORGE[4][16] = {
17 	{ 15, 7, 13, 1, 11, 10, 14, 9, 12, 2, 4, 0, 3, 5, 8, 6 },
18 	{ 15, 14, 3, 12, 8, 4, 0, 13, 5, 11, 6, 7, 9, 1, 2, 10 },
19 	{ 15, 0, 9, 5, 2, 6, 7, 3, 1, 8, 10, 14, 13, 12, 11, 4 }
20 };
21 static const uint8_t VALUE_REORDERING[4][16] = {
22 	{ 5, 4, 3, 2, 1, 0, 7, 6 },
23 	{ 3, 2, 1, 0, 7, 6, 5, 4 },
24 	{ 1, 0, 7, 6, 5, 4, 3, 2 }
25 };
26 static const uint8_t VALUE_REORDERING_GEORGE[4][16] = {
27 	{ 3, 0, 7, 2, 1, 4, 5, 6 },
28 	{ 1, 4, 3, 0, 5, 6, 7, 2 },
29 	{ 5, 2, 1, 6, 7, 0, 3, 4 }
30 };
31 
32 static const int8_t MODE_CHANGE_START_SEQUENCE[5] = { 0x99, 0x02, 0x05, 0x02, 0x03 };
33 static const int8_t MODE_CHANGE_END_SEQUENCE[5] = { 0x99, 0x03, 0x62, 0x02, 0x56 };
34 
35 // A portion of the initialisation routine that gets copied into RAM - Always seems to be present at 0x15C in VFame game ROM
36 static const char INIT_SEQUENCE[16] = { 0xB4, 0x00, 0x9F, 0xE5, 0x99, 0x10, 0xA0, 0xE3, 0x00, 0x10, 0xC0, 0xE5, 0xAC, 0x00, 0x9F, 0xE5 };
37 
38 static bool _isInMirroredArea(uint32_t address, size_t romSize);
39 static uint32_t _getPatternValue(uint32_t addr);
40 static uint32_t _patternRightShift2(uint32_t addr);
41 static int8_t _modifySramValue(enum GBAVFameCartType type, uint8_t value, int mode);
42 static uint32_t _modifySramAddress(enum GBAVFameCartType type, uint32_t address, int mode);
43 static int _reorderBits(uint32_t value, const uint8_t* reordering, int reorderLength);
44 
GBAVFameInit(struct GBAVFameCart * cart)45 void GBAVFameInit(struct GBAVFameCart* cart) {
46 	cart->cartType = VFAME_NO;
47 	cart->sramMode = -1;
48 	cart->romMode = -1;
49 	cart->acceptingModeChange = false;
50 }
51 
GBAVFameDetect(struct GBAVFameCart * cart,uint32_t * rom,size_t romSize)52 void GBAVFameDetect(struct GBAVFameCart* cart, uint32_t* rom, size_t romSize) {
53 	cart->cartType = VFAME_NO;
54 
55 	// The initialisation code is also present & run in the dumps of Digimon Ruby & Sapphire from hacked/deprotected reprint carts,
56 	// which would break if run in "proper" VFame mode so we need to exclude those..
57 	if (romSize == 0x2000000) { // the deprotected dumps are 32MB but no real VF games are this size
58 		return;
59 	}
60 
61 	// Most games have the same init sequence in the same place
62 	// but LOTR/Mo Jie Qi Bing doesn't, probably because it's based on the Kiki KaiKai engine, so just detect based on its title
63 	if (memcmp(INIT_SEQUENCE, &rom[0x57], sizeof(INIT_SEQUENCE)) == 0 || memcmp("\0LORD\0WORD\0\0AKIJ", &((struct GBACartridge*) rom)->title, 16) == 0) {
64 		cart->cartType = VFAME_STANDARD;
65 		mLOG(GBA_MEM, INFO, "Vast Fame game detected");
66 	}
67 
68 	// This game additionally operates with a different set of SRAM modes
69 	// Its initialisation seems to be identical so the difference must be in the cart HW itself
70 	// Other undumped games may have similar differences
71 	if (memcmp("George Sango", &((struct GBACartridge*) rom)->title, 12) == 0) {
72 		cart->cartType = VFAME_GEORGE;
73 		mLOG(GBA_MEM, INFO, "George mode");
74 	}
75 }
76 
77 // This is not currently being used but would be called on ROM reads
78 // Emulates mirroring used by real VF carts, but no games seem to rely on this behaviour
GBAVFameModifyRomAddress(struct GBAVFameCart * cart,uint32_t address,size_t romSize)79 uint32_t GBAVFameModifyRomAddress(struct GBAVFameCart* cart, uint32_t address, size_t romSize) {
80 	if (cart->romMode == -1 && (address & 0x01000000) == 0) {
81 		// When ROM mode is uninitialised, it just mirrors the first 0x80000 bytes
82 		// All known games set the ROM mode to 00 which enables full range of reads, it's currently unknown what other values do
83 		address &= 0x7FFFF;
84 	} else if (_isInMirroredArea(address, romSize)) {
85 		address -= 0x800000;
86 	}
87 	return address;
88 }
89 
_isInMirroredArea(uint32_t address,size_t romSize)90 static bool _isInMirroredArea(uint32_t address, size_t romSize) {
91 	address &= 0x01FFFFFF;
92 	// For some reason known 4m games e.g. Zook, Sango repeat the game at 800000 but the 8m Digimon R. does not
93 	if (romSize != 0x400000) {
94 		return false;
95 	}
96 	if (address < 0x800000) {
97 		return false;
98 	}
99 	if (address >= 0x800000 + romSize) {
100 		return false;
101 	}
102 	return true;
103 }
104 
105 // Looks like only 16-bit reads are done by games but others are possible...
GBAVFameGetPatternValue(uint32_t address,int bits)106 uint32_t GBAVFameGetPatternValue(uint32_t address, int bits) {
107 	switch (bits) {
108 	case 8:
109 		if (address & 1) {
110 			return _getPatternValue(address) & 0xFF;
111 		} else {
112 			return (_getPatternValue(address) & 0xFF00) >> 8;
113 		}
114 	case 16:
115 		return _getPatternValue(address);
116 	case 32:
117 		return (_getPatternValue(address) << 2) + _getPatternValue(address + 2);
118 	}
119 	return 0;
120 }
121 
122 // when you read from a ROM location outside the actual ROM data or its mirror, it returns a value based on some 16-bit transformation of the address
123 // which the game relies on to run
_getPatternValue(uint32_t addr)124 static uint32_t _getPatternValue(uint32_t addr) {
125 	addr &= 0x1FFFFF;
126 	uint32_t value = 0;
127 	switch (addr & 0x1F0000) {
128 	case 0x000000:
129 	case 0x010000:
130 		value = (addr >> 1) & 0xFFFF;
131 		break;
132 	case 0x020000:
133 		value = addr & 0xFFFF;
134 		break;
135 	case 0x030000:
136 		value = (addr & 0xFFFF) + 1;
137 		break;
138 	case 0x040000:
139 		value = 0xFFFF - (addr & 0xFFFF);
140 		break;
141 	case 0x050000:
142 		value = (0xFFFF - (addr & 0xFFFF)) - 1;
143 		break;
144 	case 0x060000:
145 		value = (addr & 0xFFFF) ^ 0xAAAA;
146 		break;
147 	case 0x070000:
148 		value = ((addr & 0xFFFF) ^ 0xAAAA) + 1;
149 		break;
150 	case 0x080000:
151 		value = (addr & 0xFFFF) ^ 0x5555;
152 		break;
153 	case 0x090000:
154 		value = ((addr & 0xFFFF) ^ 0x5555) - 1;
155 		break;
156 	case 0x0A0000:
157 	case 0x0B0000:
158 		value = _patternRightShift2(addr);
159 		break;
160 	case 0x0C0000:
161 	case 0x0D0000:
162 		value = 0xFFFF - _patternRightShift2(addr);
163 		break;
164 	case 0x0E0000:
165 	case 0x0F0000:
166 		value = _patternRightShift2(addr) ^ 0xAAAA;
167 		break;
168 	case 0x100000:
169 	case 0x110000:
170 		value = _patternRightShift2(addr) ^ 0x5555;
171 		break;
172 	case 0x120000:
173 		value = 0xFFFF - ((addr & 0xFFFF) >> 1);
174 		break;
175 	case 0x130000:
176 		value = 0xFFFF - ((addr & 0xFFFF) >> 1) - 0x8000;
177 		break;
178 	case 0x140000:
179 	case 0x150000:
180 		value = ((addr >> 1) & 0xFFFF) ^ 0xAAAA;
181 		break;
182 	case 0x160000:
183 	case 0x170000:
184 		value = ((addr >> 1) & 0xFFFF) ^ 0x5555;
185 		break;
186 	case 0x180000:
187 	case 0x190000:
188 		value = ((addr >> 1) & 0xFFFF) ^ 0xF0F0;
189 		break;
190 	case 0x1A0000:
191 	case 0x1B0000:
192 		value = ((addr >> 1) & 0xFFFF) ^ 0x0F0F;
193 		break;
194 	case 0x1C0000:
195 	case 0x1D0000:
196 		value = ((addr >> 1) & 0xFFFF) ^ 0xFF00;
197 		break;
198 	case 0x1E0000:
199 	case 0x1F0000:
200 		value = ((addr >> 1) & 0xFFFF) ^ 0x00FF;
201 		break;
202 	}
203 
204 	return value & 0xFFFF;
205 }
206 
_patternRightShift2(uint32_t addr)207 static uint32_t _patternRightShift2(uint32_t addr) {
208 	uint32_t value = addr & 0xFFFF;
209 	value >>= 2;
210 	value += (addr & 3) == 2 ? 0x8000 : 0;
211 	value += (addr & 0x10000) ? 0x4000 : 0;
212 	return value;
213 }
214 
GBAVFameSramWrite(struct GBAVFameCart * cart,uint32_t address,uint8_t value,uint8_t * sramData)215 void GBAVFameSramWrite(struct GBAVFameCart* cart, uint32_t address, uint8_t value, uint8_t* sramData) {
216 	address &= 0x00FFFFFF;
217 	// A certain sequence of writes to SRAM FFF8->FFFC can enable or disable "mode change" mode
218 	// Currently unknown if these writes have to be sequential, or what happens if you write different values, if anything
219 	if (address >= 0xFFF8 && address <= 0xFFFC) {
220 		cart->writeSequence[address - 0xFFF8] = value;
221 		if (address == 0xFFFC) {
222 			if (memcmp(MODE_CHANGE_START_SEQUENCE, cart->writeSequence, sizeof(MODE_CHANGE_START_SEQUENCE)) == 0) {
223 				cart->acceptingModeChange = true;
224 			}
225 			if (memcmp(MODE_CHANGE_END_SEQUENCE, cart->writeSequence, sizeof(MODE_CHANGE_END_SEQUENCE)) == 0) {
226 				cart->acceptingModeChange = false;
227 			}
228 		}
229 	}
230 
231 	// If we are in "mode change mode" we can change either SRAM or ROM modes
232 	// Currently unknown if other SRAM writes in this mode should have any effect
233 	if (cart->acceptingModeChange) {
234 		if (address == 0xFFFE) {
235 			cart->sramMode = value;
236 		} else if (address == 0xFFFD) {
237 			cart->romMode = value;
238 		}
239 	}
240 
241 	if (cart->sramMode == -1) {
242 		// when SRAM mode is uninitialised you can't write to it
243 		return;
244 	}
245 
246 	// if mode has been set - the address and value of the SRAM write will be modified
247 	address = _modifySramAddress(cart->cartType, address, cart->sramMode);
248 	value = _modifySramValue(cart->cartType, value, cart->sramMode);
249 	address &= (SIZE_CART_SRAM - 1);
250 	sramData[address] = value;
251 }
252 
_modifySramAddress(enum GBAVFameCartType type,uint32_t address,int mode)253 static uint32_t _modifySramAddress(enum GBAVFameCartType type, uint32_t address, int mode) {
254 	mode &= 0x3;
255 	if (mode == 0) {
256 		return address;
257 	} else if (type == VFAME_GEORGE) {
258 		return _reorderBits(address, ADDRESS_REORDERING_GEORGE[mode - 1], 16);
259 	} else {
260 		return _reorderBits(address, ADDRESS_REORDERING[mode - 1], 16);
261 	}
262 }
263 
_modifySramValue(enum GBAVFameCartType type,uint8_t value,int mode)264 static int8_t _modifySramValue(enum GBAVFameCartType type, uint8_t value, int mode) {
265 	int reorderType = (mode & 0xF) >> 2;
266 	if (reorderType != 0) {
267 		if (type == VFAME_GEORGE) {
268 			value = _reorderBits(value, VALUE_REORDERING_GEORGE[reorderType - 1], 8);
269 		} else {
270 			value = _reorderBits(value, VALUE_REORDERING[reorderType - 1], 8);
271 		}
272 	}
273 	if (mode & 0x80) {
274 		value ^= 0xAA;
275 	}
276 	return value;
277 }
278 
279 // Reorder bits in a byte according to the reordering given
_reorderBits(uint32_t value,const uint8_t * reordering,int reorderLength)280 static int _reorderBits(uint32_t value, const uint8_t* reordering, int reorderLength) {
281 	uint32_t retval = value;
282 
283 	int x;
284 	for (x = reorderLength; x > 0; x--) {
285 		uint8_t reorderPlace = reordering[reorderLength - x]; // get the reorder position
286 
287 		uint32_t mask = 1 << reorderPlace; // move the bit to the position we want
288 		uint32_t val = value & mask; // AND it with the original value
289 		val >>= reorderPlace; // move the bit back, so we have the correct 0 or 1
290 
291 		unsigned destinationPlace = x - 1;
292 
293 		uint32_t newMask = 1 << destinationPlace;
294 		if (val == 1) {
295 			retval |= newMask;
296 		} else {
297 			retval &= ~newMask;
298 		}
299 	}
300 
301 	return retval;
302 }
303