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}