all repos — mgba @ 7978a57128e32aa78245216fc0a173642685de1c

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
 11void GBASavedataInit(struct GBASavedata* savedata, const char* filename) {
 12	savedata->type = SAVEDATA_NONE;
 13	savedata->data = 0;
 14	savedata->fd = -1;
 15	savedata->filename = filename;
 16}
 17
 18void GBASavedataDeinit(struct GBASavedata* savedata) {
 19	switch (savedata->type) {
 20	case SAVEDATA_SRAM:
 21		munmap(savedata->data, SIZE_CART_SRAM);
 22		break;
 23	case SAVEDATA_FLASH512:
 24		munmap(savedata->data, SIZE_CART_FLASH512);
 25		break;
 26	case SAVEDATA_FLASH1M:
 27		munmap(savedata->data, SIZE_CART_FLASH1M);
 28		break;
 29	case SAVEDATA_EEPROM:
 30		munmap(savedata->data, SIZE_CART_EEPROM);
 31		break;
 32	default:
 33		break;
 34	}
 35	close(savedata->fd);
 36	savedata->type = SAVEDATA_NONE;
 37}
 38
 39void GBASavedataInitFlash(struct GBASavedata* savedata) {
 40	savedata->type = SAVEDATA_FLASH512;
 41	savedata->fd = open(savedata->filename, O_RDWR | O_CREAT, 0666);
 42	off_t end;
 43	int flags = MAP_SHARED;
 44	if (savedata->fd < 0) {
 45		GBALog(GBA_LOG_WARN, "Cannot open savedata file %s (errno: %d)", savedata->filename, errno);
 46		end = 0;
 47		flags |= MAP_ANON;
 48	} else {
 49		end = lseek(savedata->fd, 0, SEEK_END);
 50		if (end < SIZE_CART_FLASH512) {
 51			ftruncate(savedata->fd, SIZE_CART_FLASH1M);
 52		}
 53	}
 54	// mmap enough so that we can expand the file if we need to
 55	savedata->data = mmap(0, SIZE_CART_FLASH1M, PROT_READ | PROT_WRITE, flags, savedata->fd, 0);
 56	if (end < SIZE_CART_FLASH512) {
 57		memset(&savedata->data[end], 0xFF, SIZE_CART_FLASH512 - end);
 58	}
 59}
 60
 61void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
 62	savedata->type = SAVEDATA_EEPROM;
 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(GBA_LOG_WARN, "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_EEPROM) {
 73			ftruncate(savedata->fd, SIZE_CART_EEPROM);
 74		}
 75	}
 76	savedata->data = mmap(0, SIZE_CART_EEPROM, PROT_READ | PROT_WRITE, flags, savedata->fd, 0);
 77	if (end < SIZE_CART_EEPROM) {
 78		memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM - end);
 79	}
 80}
 81
 82void GBASavedataInitSRAM(struct GBASavedata* savedata) {
 83	savedata->type = SAVEDATA_SRAM;
 84	savedata->fd = open(savedata->filename, O_RDWR | O_CREAT, 0666);
 85	off_t end;
 86	int flags = MAP_SHARED;
 87	if (savedata->fd < 0) {
 88		GBALog(GBA_LOG_WARN, "Cannot open savedata file %s (errno: %d)", savedata->filename, errno);
 89		end = 0;
 90		flags |= MAP_ANON;
 91	} else {
 92		end = lseek(savedata->fd, 0, SEEK_END);
 93		if (end < SIZE_CART_SRAM) {
 94			ftruncate(savedata->fd, SIZE_CART_SRAM);
 95		}
 96	}
 97	savedata->data = mmap(0, SIZE_CART_SRAM, PROT_READ | PROT_WRITE, flags, savedata->fd, 0);
 98	if (end < SIZE_CART_SRAM) {
 99		memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM - end);
100	}
101}
102
103
104void GBASavedataWriteFlash(struct GBASavedata* savedata, uint8_t value) {
105	(void)(savedata);
106	(void)(value);
107	GBALog(GBA_LOG_STUB, "Flash memory unimplemented");
108}
109
110void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32_t writeSize) {
111	switch (savedata->command) {
112	// Read header
113	case EEPROM_COMMAND_NULL:
114	default:
115		savedata->command = value & 0x1;
116		break;
117	case EEPROM_COMMAND_PENDING:
118		savedata->command <<= 1;
119		savedata->command |= value & 0x1;
120		if (savedata->command == EEPROM_COMMAND_WRITE) {
121			savedata->addressBits = writeSize - 64 - 2;
122			savedata->writeAddress = 0;
123		} else {
124			savedata->addressBits = writeSize - 2;
125			savedata->readAddress = 0;
126		}
127		break;
128	// Do commands
129	case EEPROM_COMMAND_WRITE:
130		// Write
131		if (writeSize > 65) {
132			savedata->writeAddress <<= 1;
133			savedata->writeAddress |= (value & 0x1) << 6;
134		} else if (writeSize == 1) {
135			savedata->command = EEPROM_COMMAND_NULL;
136			savedata->writePending = 1;
137		} else {
138			uint8_t current = savedata->data[savedata->writeAddress >> 3];
139			current &= ~(1 << (0x7 - (savedata->writeAddress & 0x7)));
140			current |= (value & 0x1) << (0x7 - (savedata->writeAddress & 0x7));
141			savedata->data[savedata->writeAddress >> 3] = current;
142			++savedata->writeAddress;
143		}
144		break;
145	case EEPROM_COMMAND_READ_PENDING:
146		// Read
147		if (writeSize > 1) {
148			savedata->readAddress <<= 1;
149			if (value & 0x1) {
150				savedata->readAddress |= 0x40;
151			}
152		} else {
153			savedata->readBitsRemaining = 68;
154			savedata->command = EEPROM_COMMAND_READ;
155		}
156		break;
157	}
158}
159
160uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) {
161	if (savedata->command != EEPROM_COMMAND_READ) {
162		return 1;
163	}
164	--savedata->readBitsRemaining;
165	if (savedata->readBitsRemaining < 64) {
166		int step = 63 - savedata->readBitsRemaining;
167		uint8_t data = savedata->data[(savedata->readAddress + step) >> 3] >> (0x7 - (step & 0x7));
168		if (!savedata->readBitsRemaining) {
169			savedata->command = EEPROM_COMMAND_NULL;
170		}
171		return data & 0x1;
172	}
173	return 0;
174}