all repos — mgba @ aaaafb90a57df651c9bf3ce04afc3057755a4b98

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			savedata->command = FLASH_COMMAND_NONE;
141			break;
142		default:
143			if (address == FLASH_BASE_HI && value == FLASH_COMMAND_START) {
144				savedata->flashState = FLASH_STATE_START;
145			} else {
146				GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
147			}
148			break;
149		}
150		break;
151	case FLASH_STATE_START:
152		if (address == FLASH_BASE_LO && value == FLASH_COMMAND_CONTINUE) {
153			savedata->flashState = FLASH_STATE_CONTINUE;
154		} else {
155			GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
156			savedata->flashState = FLASH_STATE_RAW;
157		}
158		break;
159	case FLASH_STATE_CONTINUE:
160		savedata->flashState = FLASH_STATE_RAW;
161		if (address == FLASH_BASE_HI) {
162			switch (savedata->command) {
163			case FLASH_COMMAND_NONE:
164				switch (value) {
165				case FLASH_COMMAND_ERASE:
166				case FLASH_COMMAND_ID:
167				case FLASH_COMMAND_PROGRAM:
168				case FLASH_COMMAND_SWITCH_BANK:
169					savedata->command = value;
170					break;
171				default:
172					GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash operation: %#02x", value);
173					break;
174				}
175				break;
176			case FLASH_COMMAND_ERASE:
177				switch (value) {
178				case FLASH_COMMAND_ERASE_CHIP:
179					_flashErase(savedata);
180					break;
181				default:
182					GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
183					break;
184				}
185				savedata->command = FLASH_COMMAND_NONE;
186				break;
187			case FLASH_COMMAND_ID:
188				if (value == FLASH_COMMAND_TERMINATE) {
189					savedata->command = FLASH_COMMAND_NONE;
190				}
191				break;
192			default:
193				GBALog(0, GBA_LOG_ERROR, "Flash entered bad state: %#02x", savedata->command);
194				savedata->command = FLASH_COMMAND_NONE;
195				break;
196			}
197		} else if (savedata->command == FLASH_COMMAND_ERASE) {
198			if (value == FLASH_COMMAND_ERASE_SECTOR) {
199				_flashEraseSector(savedata, address);
200				savedata->command = FLASH_COMMAND_NONE;
201			} else {
202				GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
203			}
204		}
205		break;
206	}
207}
208
209void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32_t writeSize) {
210	switch (savedata->command) {
211	// Read header
212	case EEPROM_COMMAND_NULL:
213	default:
214		savedata->command = value & 0x1;
215		break;
216	case EEPROM_COMMAND_PENDING:
217		savedata->command <<= 1;
218		savedata->command |= value & 0x1;
219		if (savedata->command == EEPROM_COMMAND_WRITE) {
220			savedata->addressBits = writeSize - 64 - 2;
221			savedata->writeAddress = 0;
222		} else {
223			savedata->addressBits = writeSize - 2;
224			savedata->readAddress = 0;
225		}
226		break;
227	// Do commands
228	case EEPROM_COMMAND_WRITE:
229		// Write
230		if (writeSize > 65) {
231			savedata->writeAddress <<= 1;
232			savedata->writeAddress |= (value & 0x1) << 6;
233		} else if (writeSize == 1) {
234			savedata->command = EEPROM_COMMAND_NULL;
235			savedata->writePending = 1;
236		} else {
237			uint8_t current = savedata->data[savedata->writeAddress >> 3];
238			current &= ~(1 << (0x7 - (savedata->writeAddress & 0x7)));
239			current |= (value & 0x1) << (0x7 - (savedata->writeAddress & 0x7));
240			savedata->data[savedata->writeAddress >> 3] = current;
241			++savedata->writeAddress;
242		}
243		break;
244	case EEPROM_COMMAND_READ_PENDING:
245		// Read
246		if (writeSize > 1) {
247			savedata->readAddress <<= 1;
248			if (value & 0x1) {
249				savedata->readAddress |= 0x40;
250			}
251		} else {
252			savedata->readBitsRemaining = 68;
253			savedata->command = EEPROM_COMMAND_READ;
254		}
255		break;
256	}
257}
258
259uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) {
260	if (savedata->command != EEPROM_COMMAND_READ) {
261		return 1;
262	}
263	--savedata->readBitsRemaining;
264	if (savedata->readBitsRemaining < 64) {
265		int step = 63 - savedata->readBitsRemaining;
266		uint8_t data = savedata->data[(savedata->readAddress + step) >> 3] >> (0x7 - (step & 0x7));
267		if (!savedata->readBitsRemaining) {
268			savedata->command = EEPROM_COMMAND_NULL;
269		}
270		return data & 0x1;
271	}
272	return 0;
273}
274
275void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
276	savedata->currentBank = &savedata->data[bank << 16];
277	if (bank > 0) {
278		savedata->type = SAVEDATA_FLASH1M;
279		ftruncate(savedata->fd, SIZE_CART_FLASH1M);
280	}
281}
282
283void _flashErase(struct GBASavedata* savedata) {
284	size_t size = 0x10000;
285	if (savedata->type == SAVEDATA_FLASH1M) {
286		size = 0x20000;
287	}
288	memset(savedata->data, 0xFF, size);
289}
290
291void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart) {
292	size_t size = 0x1000;
293	if (savedata->type == SAVEDATA_FLASH1M) {
294		GBALog(0, GBA_LOG_DEBUG, "Performing unknown sector-size erase at %#04x", sectorStart);
295	}
296	memset(&savedata->currentBank[sectorStart & ~(size - 1)], 0xFF, size);
297}