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}