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