all repos — mgba @ dd1f1bc79e71700a279acd4c43effb235e6aa6ce

mGBA Game Boy Advance Emulator

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

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