all repos — mgba @ ca959c640a5fe148bd43befc7ce7e6c41348b374

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