all repos — mgba @ 003db6019c1f901060980aa7016be141b46dade6

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 <errno.h>
  6#include <fcntl.h>
  7#include <string.h>
  8#include <sys/mman.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		munmap(savedata->data, SIZE_CART_SRAM);
 36		break;
 37	case SAVEDATA_FLASH512:
 38		munmap(savedata->data, SIZE_CART_FLASH512);
 39		break;
 40	case SAVEDATA_FLASH1M:
 41		munmap(savedata->data, SIZE_CART_FLASH1M);
 42		break;
 43	case SAVEDATA_EEPROM:
 44		munmap(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	int flags = MAP_SHARED;
 66	if (savedata->fd < 0) {
 67		GBALog(0, GBA_LOG_ERROR, "Cannot open savedata file %s (errno: %d)", savedata->filename, errno);
 68		end = 0;
 69		flags |= MAP_ANON;
 70	} else {
 71		end = lseek(savedata->fd, 0, SEEK_END);
 72		if (end < SIZE_CART_FLASH512) {
 73			ftruncate(savedata->fd, SIZE_CART_FLASH1M);
 74		}
 75	}
 76	// mmap enough so that we can expand the file if we need to
 77	savedata->data = mmap(0, SIZE_CART_FLASH1M, PROT_READ | PROT_WRITE, flags, savedata->fd, 0);
 78	savedata->currentBank = savedata->data;
 79	if (end < SIZE_CART_FLASH512) {
 80		memset(&savedata->data[end], 0xFF, SIZE_CART_FLASH512 - end);
 81	}
 82}
 83
 84void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
 85	if (savedata->type == SAVEDATA_NONE) {
 86		savedata->type = SAVEDATA_EEPROM;
 87	} else {
 88		GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
 89		return;
 90	}
 91	savedata->fd = open(savedata->filename, O_RDWR | O_CREAT, 0666);
 92	off_t end;
 93	int flags = MAP_SHARED;
 94	if (savedata->fd < 0) {
 95		GBALog(0, GBA_LOG_ERROR, "Cannot open savedata file %s (errno: %d)", savedata->filename, errno);
 96		end = 0;
 97		flags |= MAP_ANON;
 98	} else {
 99		end = lseek(savedata->fd, 0, SEEK_END);
100		if (end < SIZE_CART_EEPROM) {
101			ftruncate(savedata->fd, SIZE_CART_EEPROM);
102		}
103	}
104	savedata->data = mmap(0, SIZE_CART_EEPROM, PROT_READ | PROT_WRITE, flags, savedata->fd, 0);
105	if (end < SIZE_CART_EEPROM) {
106		memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM - end);
107	}
108}
109
110void GBASavedataInitSRAM(struct GBASavedata* savedata) {
111	if (savedata->type == SAVEDATA_NONE) {
112		savedata->type = SAVEDATA_SRAM;
113	} else {
114		GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
115		return;
116	}
117	savedata->fd = open(savedata->filename, O_RDWR | O_CREAT, 0666);
118	off_t end;
119	int flags = MAP_SHARED;
120	if (savedata->fd < 0) {
121		GBALog(0, GBA_LOG_ERROR, "Cannot open savedata file %s (errno: %d)", savedata->filename, errno);
122		end = 0;
123		flags |= MAP_ANON;
124	} else {
125		end = lseek(savedata->fd, 0, SEEK_END);
126		if (end < SIZE_CART_SRAM) {
127			ftruncate(savedata->fd, SIZE_CART_SRAM);
128		}
129	}
130	savedata->data = mmap(0, SIZE_CART_SRAM, PROT_READ | PROT_WRITE, flags, savedata->fd, 0);
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		ftruncate(savedata->fd, 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}