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