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