all repos — mgba @ de39f23a1bb4b2cb7a005e16bdb8820d47c2702e

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	close(savedata->fd);
 50	savedata->type = SAVEDATA_NONE;
 51}
 52
 53void GBASavedataInitFlash(struct GBASavedata* savedata) {
 54	if (savedata->type == SAVEDATA_NONE) {
 55		savedata->type = SAVEDATA_FLASH512;
 56	}
 57	if (savedata->type != SAVEDATA_FLASH512 && savedata->type != SAVEDATA_FLASH1M) {
 58		GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
 59		return;
 60	}
 61	savedata->fd = open(savedata->filename, O_RDWR | O_CREAT, 0666);
 62	off_t end;
 63	int flags = MAP_SHARED;
 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		flags |= MAP_ANON;
 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	}
 74	// mmap enough so that we can expand the file if we need to
 75	savedata->data = mmap(0, SIZE_CART_FLASH1M, PROT_READ | PROT_WRITE, flags, savedata->fd, 0);
 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	int flags = MAP_SHARED;
 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		flags |= MAP_ANON;
 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	}
102	savedata->data = mmap(0, SIZE_CART_EEPROM, PROT_READ | PROT_WRITE, flags, savedata->fd, 0);
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	int flags = MAP_SHARED;
118	if (savedata->fd < 0) {
119		GBALog(0, GBA_LOG_ERROR, "Cannot open savedata file %s (errno: %d)", savedata->filename, errno);
120		end = 0;
121		flags |= MAP_ANON;
122	} else {
123		end = lseek(savedata->fd, 0, SEEK_END);
124		if (end < SIZE_CART_SRAM) {
125			ftruncate(savedata->fd, SIZE_CART_SRAM);
126		}
127	}
128	savedata->data = mmap(0, SIZE_CART_SRAM, PROT_READ | PROT_WRITE, flags, savedata->fd, 0);
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}