all repos — mgba @ 830511472a3d3e9854b94216fc4e74e35d8d3081

mGBA Game Boy Advance Emulator

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

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