all repos — mgba @ a0505ee7d77261d656a1193c00326bb5f3b8d4af

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