all repos — mgba @ 1baa6fefbb49bea6c72cdffeda1f5f3ce50a1779

mGBA Game Boy Advance Emulator

src/gba/vfame.c (view raw)

  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
 11static 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};
 16static 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};
 21static 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};
 26static 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
 32static const int8_t MODE_CHANGE_START_SEQUENCE[5] = { 0x99, 0x02, 0x05, 0x02, 0x03 };
 33static 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
 36static const char INIT_SEQUENCE[16] = { 0xB4, 0x00, 0x9F, 0xE5, 0x99, 0x10, 0xA0, 0xE3, 0x00, 0x10, 0xC0, 0xE5, 0xAC, 0x00, 0x9F, 0xE5 };
 37
 38static bool _isInMirroredArea(uint32_t address, size_t romSize);
 39static uint32_t _getPatternValue(uint32_t addr);
 40static uint32_t _patternRightShift2(uint32_t addr);
 41static int8_t _modifySramValue(enum GBAVFameCartType type, uint8_t value, int mode);
 42static uint32_t _modifySramAddress(enum GBAVFameCartType type, uint32_t address, int mode);
 43static int _reorderBits(uint32_t value, const uint8_t* reordering, int reorderLength);
 44
 45void GBAVFameInit(struct GBAVFameCart* cart) {
 46	cart->cartType = VFAME_NO;
 47	cart->sramMode = -1;
 48	cart->romMode = -1;
 49	cart->acceptingModeChange = false;
 50}
 51
 52void 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
 79uint32_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
 90static 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...
106uint32_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
124static 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
207static 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
215void 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
253static 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
264static 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
280static 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}