all repos — mgba @ f62ccde49d484c65b212ae86299f4720c9dc836d

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