all repos — mgba @ 73425e80b51ffbb7f0553a7fa59455ad38890c7d

mGBA Game Boy Advance Emulator

src/gba/gba-savedata.c (view raw)

  1#include "gba-savedata.h"
  2
  3#include "gba.h"
  4
  5#include "util/memory.h"
  6#include "util/vfs.h"
  7
  8#include <errno.h>
  9#include <fcntl.h>
 10
 11static void _flashSwitchBank(struct GBASavedata* savedata, int bank);
 12static void _flashErase(struct GBASavedata* savedata);
 13static void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart);
 14
 15void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
 16	savedata->type = SAVEDATA_NONE;
 17	savedata->data = 0;
 18	savedata->command = EEPROM_COMMAND_NULL;
 19	savedata->flashState = FLASH_STATE_RAW;
 20	savedata->vf = vf;
 21}
 22
 23void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type) {
 24	if (savedata->type != SAVEDATA_NONE) {
 25		GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
 26		return;
 27	}
 28	savedata->type = type;
 29}
 30
 31void GBASavedataDeinit(struct GBASavedata* savedata) {
 32	if (savedata->vf) {
 33		switch (savedata->type) {
 34		case SAVEDATA_SRAM:
 35			savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_SRAM);
 36			break;
 37		case SAVEDATA_FLASH512:
 38			savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_FLASH512);
 39			break;
 40		case SAVEDATA_FLASH1M:
 41			savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_FLASH1M);
 42			break;
 43		case SAVEDATA_EEPROM:
 44			savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_EEPROM);
 45			break;
 46		case SAVEDATA_NONE:
 47			break;
 48		}
 49		savedata->vf = 0;
 50	} else {
 51		switch (savedata->type) {
 52		case SAVEDATA_SRAM:
 53			mappedMemoryFree(savedata->data, SIZE_CART_SRAM);
 54			break;
 55		case SAVEDATA_FLASH512:
 56			mappedMemoryFree(savedata->data, SIZE_CART_FLASH512);
 57			break;
 58		case SAVEDATA_FLASH1M:
 59			mappedMemoryFree(savedata->data, SIZE_CART_FLASH1M);
 60			break;
 61		case SAVEDATA_EEPROM:
 62			mappedMemoryFree(savedata->data, SIZE_CART_EEPROM);
 63			break;
 64		case SAVEDATA_NONE:
 65			break;
 66		}
 67	}
 68	savedata->type = SAVEDATA_NONE;
 69}
 70
 71void GBASavedataInitFlash(struct GBASavedata* savedata) {
 72	if (savedata->type == SAVEDATA_NONE) {
 73		savedata->type = SAVEDATA_FLASH512;
 74	}
 75	if (savedata->type != SAVEDATA_FLASH512 && savedata->type != SAVEDATA_FLASH1M) {
 76		GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
 77		return;
 78	}
 79	off_t end;
 80	if (!savedata->vf) {
 81		end = 0;
 82		savedata->data = anonymousMemoryMap(SIZE_CART_FLASH1M);
 83	} else {
 84		end = savedata->vf->seek(savedata->vf, 0, SEEK_END);
 85		if (end < SIZE_CART_FLASH512) {
 86			savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
 87		}
 88		savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, MEMORY_WRITE);
 89	}
 90
 91	savedata->currentBank = savedata->data;
 92	if (end < SIZE_CART_FLASH512) {
 93		memset(&savedata->data[end], 0xFF, SIZE_CART_FLASH512 - end);
 94	}
 95}
 96
 97void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
 98	if (savedata->type == SAVEDATA_NONE) {
 99		savedata->type = SAVEDATA_EEPROM;
100	} else {
101		GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
102		return;
103	}
104	off_t end;
105	if (!savedata->vf) {
106		end = 0;
107		savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM);
108	} else {
109		end = savedata->vf->seek(savedata->vf, 0, SEEK_END);
110		if (end < SIZE_CART_EEPROM) {
111			savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM);
112		}
113		savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, MEMORY_WRITE);
114	}
115	if (end < SIZE_CART_EEPROM) {
116		memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM - end);
117	}
118}
119
120void GBASavedataInitSRAM(struct GBASavedata* savedata) {
121	if (savedata->type == SAVEDATA_NONE) {
122		savedata->type = SAVEDATA_SRAM;
123	} else {
124		GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
125		return;
126	}
127	off_t end;
128	if (!savedata->vf) {
129		end = 0;
130		savedata->data = anonymousMemoryMap(SIZE_CART_SRAM);
131	} else {
132		end = savedata->vf->seek(savedata->vf, 0, SEEK_END);
133		if (end < SIZE_CART_SRAM) {
134			savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM);
135		}
136		savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM, MEMORY_WRITE);
137	}
138
139	if (end < SIZE_CART_SRAM) {
140		memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM - end);
141	}
142}
143
144uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address) {
145	if (savedata->command == FLASH_COMMAND_ID) {
146		if (savedata->type == SAVEDATA_FLASH512) {
147			if (address < 2) {
148				return FLASH_MFG_PANASONIC >> (address * 8);
149			}
150		} else if (savedata->type == SAVEDATA_FLASH1M) {
151			if (address < 2) {
152				return FLASH_MFG_SANYO >> (address * 8);
153			}
154		}
155	}
156	return savedata->currentBank[address];
157}
158
159void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8_t value) {
160	switch (savedata->flashState) {
161	case FLASH_STATE_RAW:
162		switch (savedata->command) {
163		case FLASH_COMMAND_PROGRAM:
164			savedata->currentBank[address] = value;
165			savedata->command = FLASH_COMMAND_NONE;
166			break;
167		case FLASH_COMMAND_SWITCH_BANK:
168			if (address == 0 && value < 2) {
169				_flashSwitchBank(savedata, value);
170			} else {
171				GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash bank switch");
172				savedata->command = FLASH_COMMAND_NONE;
173			}
174			savedata->command = FLASH_COMMAND_NONE;
175			break;
176		default:
177			if (address == FLASH_BASE_HI && value == FLASH_COMMAND_START) {
178				savedata->flashState = FLASH_STATE_START;
179			} else {
180				GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
181			}
182			break;
183		}
184		break;
185	case FLASH_STATE_START:
186		if (address == FLASH_BASE_LO && value == FLASH_COMMAND_CONTINUE) {
187			savedata->flashState = FLASH_STATE_CONTINUE;
188		} else {
189			GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
190			savedata->flashState = FLASH_STATE_RAW;
191		}
192		break;
193	case FLASH_STATE_CONTINUE:
194		savedata->flashState = FLASH_STATE_RAW;
195		if (address == FLASH_BASE_HI) {
196			switch (savedata->command) {
197			case FLASH_COMMAND_NONE:
198				switch (value) {
199				case FLASH_COMMAND_ERASE:
200				case FLASH_COMMAND_ID:
201				case FLASH_COMMAND_PROGRAM:
202				case FLASH_COMMAND_SWITCH_BANK:
203					savedata->command = value;
204					break;
205				default:
206					GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash operation: %#02x", value);
207					break;
208				}
209				break;
210			case FLASH_COMMAND_ERASE:
211				switch (value) {
212				case FLASH_COMMAND_ERASE_CHIP:
213					_flashErase(savedata);
214					break;
215				default:
216					GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
217					break;
218				}
219				savedata->command = FLASH_COMMAND_NONE;
220				break;
221			case FLASH_COMMAND_ID:
222				if (value == FLASH_COMMAND_TERMINATE) {
223					savedata->command = FLASH_COMMAND_NONE;
224				}
225				break;
226			default:
227				GBALog(0, GBA_LOG_ERROR, "Flash entered bad state: %#02x", savedata->command);
228				savedata->command = FLASH_COMMAND_NONE;
229				break;
230			}
231		} else if (savedata->command == FLASH_COMMAND_ERASE) {
232			if (value == FLASH_COMMAND_ERASE_SECTOR) {
233				_flashEraseSector(savedata, address);
234				savedata->command = FLASH_COMMAND_NONE;
235			} else {
236				GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
237			}
238		}
239		break;
240	}
241}
242
243void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32_t writeSize) {
244	switch (savedata->command) {
245	// Read header
246	case EEPROM_COMMAND_NULL:
247	default:
248		savedata->command = value & 0x1;
249		break;
250	case EEPROM_COMMAND_PENDING:
251		savedata->command <<= 1;
252		savedata->command |= value & 0x1;
253		if (savedata->command == EEPROM_COMMAND_WRITE) {
254			savedata->addressBits = writeSize - 64 - 2;
255			savedata->writeAddress = 0;
256		} else {
257			savedata->addressBits = writeSize - 2;
258			savedata->readAddress = 0;
259		}
260		break;
261	// Do commands
262	case EEPROM_COMMAND_WRITE:
263		// Write
264		if (writeSize > 65) {
265			savedata->writeAddress <<= 1;
266			savedata->writeAddress |= (value & 0x1) << 6;
267		} else if (writeSize == 1) {
268			savedata->command = EEPROM_COMMAND_NULL;
269			savedata->writePending = 1;
270		} else {
271			uint8_t current = savedata->data[savedata->writeAddress >> 3];
272			current &= ~(1 << (0x7 - (savedata->writeAddress & 0x7)));
273			current |= (value & 0x1) << (0x7 - (savedata->writeAddress & 0x7));
274			savedata->data[savedata->writeAddress >> 3] = current;
275			++savedata->writeAddress;
276		}
277		break;
278	case EEPROM_COMMAND_READ_PENDING:
279		// Read
280		if (writeSize > 1) {
281			savedata->readAddress <<= 1;
282			if (value & 0x1) {
283				savedata->readAddress |= 0x40;
284			}
285		} else {
286			savedata->readBitsRemaining = 68;
287			savedata->command = EEPROM_COMMAND_READ;
288		}
289		break;
290	}
291}
292
293uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) {
294	if (savedata->command != EEPROM_COMMAND_READ) {
295		return 1;
296	}
297	--savedata->readBitsRemaining;
298	if (savedata->readBitsRemaining < 64) {
299		int step = 63 - savedata->readBitsRemaining;
300		uint8_t data = savedata->data[(savedata->readAddress + step) >> 3] >> (0x7 - (step & 0x7));
301		if (!savedata->readBitsRemaining) {
302			savedata->command = EEPROM_COMMAND_NULL;
303		}
304		return data & 0x1;
305	}
306	return 0;
307}
308
309void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
310	savedata->currentBank = &savedata->data[bank << 16];
311	if (bank > 0) {
312		savedata->type = SAVEDATA_FLASH1M;
313		savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
314	}
315}
316
317void _flashErase(struct GBASavedata* savedata) {
318	size_t size = 0x10000;
319	if (savedata->type == SAVEDATA_FLASH1M) {
320		size = 0x20000;
321	}
322	memset(savedata->data, 0xFF, size);
323}
324
325void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart) {
326	size_t size = 0x1000;
327	if (savedata->type == SAVEDATA_FLASH1M) {
328		GBALog(0, GBA_LOG_DEBUG, "Performing unknown sector-size erase at %#04x", sectorStart);
329	}
330	memset(&savedata->currentBank[sectorStart & ~(size - 1)], 0xFF, size);
331}