all repos — mgba @ 377d8e60a73ff9b24f460aea4d42e828a677da05

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