1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
|
//******************************************
// SNES Game Processor RAM Cassette code by LuigiBlood
// Revision 1.0.0 February 2024
//******************************************
#ifdef ENABLE_GPC
/******************************************
Game Processor RAM Cassette
******************************************/
/******************************************
Prototype Declarations
*****************************************/
/* Hoping that sanni will use this progressbar function */
extern void draw_progressbar(uint32_t processedsize, uint32_t totalsize);
//void gpcMenu();
void readRAM_GPC();
//void setup_GPC();
void writeRAM_GPC(void);
/******************************************
Variables
*****************************************/
//No global variables
/******************************************
Menu
*****************************************/
// GPC flash menu items
static const char gpcFlashMenuItem1[] PROGMEM = "Read RAM";
static const char gpcFlashMenuItem2[] PROGMEM = "Write RAM";
static const char* const menuOptionsGPCFlash[] PROGMEM = { gpcFlashMenuItem1, gpcFlashMenuItem2, FSTRING_RESET };
void gpcMenu() {
// create menu with title and 3 options to choose from
unsigned char mainMenu;
// Copy menuOptions out of progmem
convertPgm(menuOptionsGPCFlash, 3);
mainMenu = question_box(F("Game Processor RAM"), menuOptions, 3, 0);
// wait for user choice to come back from the question box menu
switch (mainMenu) {
// Read ram
case 0:
// Change working dir to root
sd.chdir("/");
readRAM_GPC();
break;
// Write ram
case 1:
// Change working dir to root
sd.chdir("/");
writeRAM_GPC();
unsigned long wrErrors;
wrErrors = verifyRAM_GPC();
if (wrErrors == 0) {
println_Msg(F("Verified OK"));
display_Update();
} else {
print_STR(error_STR, 0);
print_Msg(wrErrors);
print_STR(_bytes_STR, 1);
print_Error(did_not_verify_STR);
}
wait();
break;
// Reset
case 2:
resetArduino();
break;
}
}
/******************************************
Setup
*****************************************/
void setup_GPC() {
// Request 5V
setVoltage(VOLTS_SET_5V);
// Set cicrstPin(PG1) to Output
DDRG |= (1 << 1);
// Output a high signal until we're ready to start
PORTG |= (1 << 1);
// Set cichstPin(PG0) to Input
DDRG &= ~(1 << 0);
// Adafruit Clock Generator
i2c_found = clockgen.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
if (i2c_found) {
clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLB);
clockgen.set_freq(2147727200ULL, SI5351_CLK0);
clockgen.set_freq(307200000ULL, SI5351_CLK2);
clockgen.output_enable(SI5351_CLK0, 1);
clockgen.output_enable(SI5351_CLK1, 0);
clockgen.output_enable(SI5351_CLK2, 1);
}
#ifdef ENABLE_CLOCKGEN
else {
display_Clear();
print_FatalError(F("Clock Generator not found"));
}
#endif
// Set Address Pins to Output
//A0-A7
DDRF = 0xFF;
//A8-A15
DDRK = 0xFF;
//BA0-BA7
DDRL = 0xFF;
//PA0-PA7
DDRA = 0xFF;
// Set Control Pins to Output RST(PH0) CS(PH3) WR(PH5) RD(PH6)
DDRH |= (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6);
// Switch RST(PH0) and WR(PH5) to HIGH
PORTH |= (1 << 0) | (1 << 5);
// Switch CS(PH3) and RD(PH6) to LOW
PORTH &= ~((1 << 3) | (1 << 6));
// Set Refresh(PE5) to Output
DDRE |= (1 << 5);
// Switch Refresh(PE5) to LOW (needed for SA-1)
PORTE &= ~(1 << 5);
// Set CPU Clock(PH1) to Output
DDRH |= (1 << 1);
//PORTH &= ~(1 << 1);
// Set IRQ(PH4) to Input
DDRH &= ~(1 << 4);
// Activate Internal Pullup Resistors
//PORTH |= (1 << 4);
// Set expand(PG5) to output
DDRG |= (1 << 5);
// Output High
PORTG |= (1 << 5);
// Set Data Pins (D0-D7) to Input
DDRC = 0x00;
// Enable Internal Pullups
//PORTC = 0xFF;
// Unused pins
// Set wram(PE4) to Output
DDRE |= (1 << 4);
//PORTE &= ~(1 << 4);
// Set pawr(PJ1) to Output
DDRJ |= (1 << 1);
//PORTJ &= ~(1 << 1);
// Set pard(PJ0) to Output
DDRJ |= (1 << 0);
//PORTJ &= ~(1 << 0);
// Start CIC by outputting a low signal to cicrstPin(PG1)
PORTG &= ~(1 << 1);
// Wait for CIC reset
delay(1000);
}
/******************************************
Low level functions
*****************************************/
// Write one byte of data to a location specified by bank and address, 00:0000
void writeBank_GPC(byte myBank, word myAddress, byte myData) {
PORTL = myBank;
PORTF = myAddress & 0xFF;
PORTK = (myAddress >> 8) & 0xFF;
PORTC = myData;
// Arduino running at 16Mhz -> one nop = 62.5ns
// Wait till output is stable
__asm__("nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t");
// Switch WR(PH5) to LOW
PORTH &= ~(1 << 5);
// Leave WR low for at least 60ns
__asm__("nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t");
// Switch WR(PH5) to HIGH
PORTH |= (1 << 5);
// Leave WR high for at least 50ns
__asm__("nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t");
}
// Read one byte of data from a location specified by bank and address, 00:0000
byte readBank_GPC(byte myBank, word myAddress) {
PORTL = myBank;
PORTF = myAddress & 0xFF;
PORTK = (myAddress >> 8) & 0xFF;
// Arduino running at 16Mhz -> one nop = 62.5ns -> 1000ns total
__asm__("nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t");
// Read
byte tempByte = PINC;
return tempByte;
}
/******************************************
Game Processor RAM Cassette functions
*****************************************/
// Read RAM cassette to SD card
void readRAM_GPC() {
// Set control
dataIn();
controlIn_SNES();
// Get name, add extension and convert to char array for sd lib
createFolderAndOpenFile("SNES", "ROM", "GPC4M", "sfc");
// Read Banks
for (int currBank = 0xC0; currBank < 0xC8; currBank++) {
// Dump the bytes to SD 512B at a time
for (long currByte = 0; currByte < 65536; currByte += 512) {
draw_progressbar((currBank - 0xC0) * 0x10000 + currByte, 0x80000);
for (int c = 0; c < 512; c++) {
sdBuffer[c] = readBank_GPC(currBank, currByte + c);
}
myFile.write(sdBuffer, 512);
}
}
draw_progressbar(0x80000, 0x80000); //Finish drawing progress bar
// Close the file:
myFile.close();
println_Msg(F("Read ram completed"));
display_Update();
wait();
}
void writeRAM_GPC(void) {
//Display file Browser and wait user to select a file. Size must be 512KB.
filePath[0] = '\0';
sd.chdir("/");
fileBrowser(F("Select SFC file"));
// Create filepath
sprintf(filePath, "%s/%s", filePath, fileName);
display_Clear();
//open file on sd card
if (myFile.open(filePath, O_READ)) {
fileSize = myFile.fileSize();
if (fileSize != 0x80000) {
println_Msg(F("File must be 512KB"));
display_Update();
myFile.close();
wait();
return;
}
//Disable ram cassette write protection
dataOut();
controlOut_SNES();
for (int countProtect = 0; countProtect < 15; countProtect++) {
writeBank_GPC(0x20, 0x6000, 0x00);
}
//Write ram
dataOut();
controlOut_SNES();
println_Msg(F("Writing ram..."));
display_Update();
for (int currBank = 0xC0; currBank < 0xC8; currBank++) {
//startAddr = 0x0000
for (long currByte = 0x0000; currByte < 0x10000; currByte += 512) {
myFile.read(sdBuffer, 512);
for (unsigned long c = 0; c < 512; c++) {
//startBank = 0x10; CS low
writeBank_GPC(currBank, currByte + c, sdBuffer[c]);
}
}
draw_progressbar(((currBank - 0xC0) * 0x10000), 0x80000);
}
//reenable write protection
dataIn();
controlIn_SNES();
byte keepByte = readBank_GPC(0x20, 0x6000);
delay(100);
dataOut();
controlOut_SNES();
writeBank_GPC(0x20, 0x6000, keepByte);
draw_progressbar(0x80000, 0x80000);
delay(100);
// Set pins to input
dataIn();
// Close the file:
myFile.close();
println_Msg("");
println_Msg(F("RAM writing finished"));
display_Update();
} else {
print_Error(FS(FSTRING_FILE_DOESNT_EXIST));
}
}
// Check if the RAM was written without any error
unsigned long verifyRAM_GPC() {
//open file on sd card
if (myFile.open(filePath, O_READ)) {
// Variable for errors
writeErrors = 0;
// Set control
controlIn_SNES();
//startBank = 0xC0; endBank = 0xC7; CS low
for (byte currBank = 0xC0; currBank < 0xC8; currBank++) {
//startAddr = 0x0000
for (long currByte = 0x0000; currByte < 0x10000; currByte += 512) {
//fill sdBuffer
myFile.read(sdBuffer, 512);
for (unsigned long c = 0; c < 512; c++) {
if ((readBank_GPC(currBank, currByte + c)) != sdBuffer[c]) {
writeErrors++;
}
}
}
}
// Close the file:
myFile.close();
return writeErrors;
} else {
print_Error(open_file_STR);
return 1;
}
}
#endif
//******************************************
// End of File
//******************************************
|