forked from sanni/cartreader
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SV.ino
683 lines (591 loc) · 18.7 KB
/
SV.ino
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
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
//******************************************
// SNES Satellaview 8M Memory pack code by tamanegi_taro
// Revision 1.0.0 October 22nd 2018
// Added BSX Sram, copied from skamans enhanced sketch //sanni
//******************************************
#include "options.h"
#ifdef enable_SV
/******************************************
Satellaview 8M Memory Pack
******************************************/
/******************************************
Prototype Declarations
*****************************************/
/* Hoping that sanni will use this progressbar function */
extern void draw_progressbar(uint32_t processedsize, uint32_t totalsize);
//void svMenu();
void readROM_SV();
//void setup_SV();
void writeROM_SV (void);
void eraseCheck_SV(void);
void supplyCheck_SV(void);
void writeCheck_SV(void);
void detectCheck_SV(void);
void eraseAll_SV(void);
/******************************************
Variables
*****************************************/
//No global variables
/******************************************
Menu
*****************************************/
// SV flash menu items
static const char svFlashMenuItem1[] PROGMEM = "Read Memory Pack";
static const char svFlashMenuItem2[] PROGMEM = "Write Memory Pack";
static const char svFlashMenuItem3[] PROGMEM = "Read BS-X Sram";
static const char svFlashMenuItem4[] PROGMEM = "Write BS-X Sram";
static const char svFlashMenuItem5[] PROGMEM = "Back";
static const char* const menuOptionsSVFlash[] PROGMEM = {svFlashMenuItem1, svFlashMenuItem2, svFlashMenuItem3, svFlashMenuItem4, svFlashMenuItem5};
void svMenu() {
// create menu with title and 3 options to choose from
unsigned char mainMenu;
// Copy menuOptions out of progmem
convertPgm(menuOptionsSVFlash, 5);
mainMenu = question_box(F("Satellaview 8M Memory"), menuOptions, 5, 0);
// wait for user choice to come back from the question box menu
switch (mainMenu)
{
// Read memory pack
case 0:
// Change working dir to root
sd.chdir("/");
readROM_SV();
break;
// Write memory pack
case 1:
// Change working dir to root
sd.chdir("/");
writeROM_SV();
break;
// Read BS-X Sram
case 2:
// Change working dir to root
sd.chdir("/");
readSRAM_SV();
break;
// Write BS-X Sram
case 3:
// Change working dir to root
sd.chdir("/");
writeSRAM_SV();
unsigned long wrErrors;
wrErrors = verifySRAM_SV();
if (wrErrors == 0) {
println_Msg(F("Verified OK"));
display_Update();
}
else {
print_Msg(F("Error: "));
print_Msg(wrErrors);
println_Msg(F(" bytes "));
print_Error(F("did not verify."), false);
}
wait();
break;
// Reset
case 4:
resetArduino();
break;
}
}
/******************************************
Setup
*****************************************/
void setup_SV() {
// 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 clockgen_installed
else {
display_Clear();
print_Error(F("Clock Generator not found"), true);
}
#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_SV(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_SV(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;
}
/******************************************
SatellaView BS-X Sram functions
*****************************************/
void readSRAM_SV () {
// set control
controlIn_SNES();
// Get name, add extension and convert to char array for sd lib
strcpy(fileName, "BSX.srm");
// create a new folder for the save file
EEPROM_readAnything(0, foldern);
sprintf(folder, "SNES/SAVE/BSX/%d", foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
// write new folder number back to eeprom
foldern = foldern + 1;
EEPROM_writeAnything(0, foldern);
//open file on sd card
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
print_Error(F("SD Error"), true);
}
int sramBanks = 0;
readBank_SV(0x10, 0); // Preconfigure to fix corrupt 1st byte
// Sram size
long lastByte = (long(sramSize) * 0x80);
//startBank = 0x10; endBank = 0x17; CS low
for (byte BSBank = 0x10; BSBank < 0x18; BSBank++) {
//startAddr = 0x5000
for (long currByte = 0x5000; currByte < 0x6000; currByte += 512) {
for (unsigned long c = 0; c < 512; c++) {
sdBuffer[c] = readBank_SV(BSBank, currByte + c);
}
myFile.write(sdBuffer, 512);
}
}
// Close the file:
myFile.close();
// Signal end of process
display_Clear();
print_Msg(F("Saved to "));
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
wait();
}
void writeSRAM_SV() {
filePath[0] = '\0';
sd.chdir("/");
fileBrowser(F("Select srm file"));
// Create filepath
sprintf(filePath, "%s/%s", filePath, fileName);
//clear the screen
display_Clear();
//open file on sd card
if (myFile.open(filePath, O_READ)) {
// Set pins to output
dataOut();
// Set RST RD WR to High and CS to Low
controlOut_SNES();
long lastByte = (long(sramSize) * 0x80);
println_Msg(F("Writing sram..."));
display_Update();
// Write to sram bank
for (byte currBank = 0x10; currBank < 0x18; currBank++) {
//startAddr = 0x5000
for (long currByte = 0x5000; currByte < 0x6000; currByte += 512) {
myFile.read(sdBuffer, 512);
for (unsigned long c = 0; c < 512; c++) {
//startBank = 0x10; CS low
writeBank_SV(currBank, currByte + c, sdBuffer[c]);
}
}
draw_progressbar(((currBank - 0x10) * 0x1000), 32768);
}
// Finish progressbar
draw_progressbar(32768, 32768);
delay(100);
// Set pins to input
dataIn();
// Close the file:
myFile.close();
println_Msg("");
println_Msg(F("SRAM writing finished"));
display_Update();
}
else {
print_Error(F("File doesnt exist"), false);
}
}
// Check if the SRAM was written without any error
unsigned long verifySRAM_SV() {
//open file on sd card
if (myFile.open(filePath, O_READ)) {
// Variable for errors
writeErrors = 0;
// Set control
controlIn_SNES();
int sramBanks = 0;
// Sram size
long lastByte = (long(sramSize) * 0x80);
//startBank = 0x10; endBank = 0x17; CS low
for (byte BSBank = 0x10; BSBank < 0x18; BSBank++) {
//startAddr = 0x5000
for (long currByte = 0x5000; currByte < 0x6000; currByte += 512) {
//fill sdBuffer
myFile.read(sdBuffer, 512);
for (unsigned long c = 0; c < 512; c++) {
if ((readBank_SV(BSBank, currByte + c)) != sdBuffer[c]) {
writeErrors++;
}
}
}
}
// Close the file:
myFile.close();
return writeErrors;
}
else {
print_Error(F("Can't open file"), false);
}
}
/******************************************
SatellaView 8M Memory Pack functions
*****************************************/
// Read memory pack to SD card
void readROM_SV() {
// Set control
dataIn();
controlIn_SNES();
// Get name, add extension and convert to char array for sd lib
strcpy(fileName, "MEMPACK.bs");
// create a new folder for the save file
EEPROM_readAnything(0, foldern);
sprintf(folder, "SNES/ROM/%s/%d", "MEMPACK", foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
//clear the screen
display_Clear();
print_Msg(F("Saving to "));
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
// write new folder number back to eeprom
foldern = foldern + 1;
EEPROM_writeAnything(0, foldern);
//open file on sd card
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
print_Error(F("Can't create file on SD"), true);
}
// Read Banks
for (int currBank = 0x40; currBank < 0x50; currBank++) {
// Dump the bytes to SD 512B at a time
for (long currByte = 0; currByte < 65536; currByte += 512) {
draw_progressbar((currBank - 0x40) * 0x10000 + currByte, 0x100000);
for (int c = 0; c < 512; c++) {
sdBuffer[c] = readBank_SV(currBank, currByte + c);
}
myFile.write(sdBuffer, 512);
}
}
draw_progressbar(0x100000, 0x100000); //Finish drawing progress bar
// Close the file:
myFile.close();
println_Msg(F("Read pack completed"));
display_Update();
wait();
}
void writeROM_SV (void) {
// Get Checksum as string to make sure that BS-X cart is inserted
dataIn();
controlIn_SNES();
sprintf(checksumStr, "%02X%02X", readBank_SV(0, 65503), readBank_SV(0, 65502));
//if CRC is not 8B86, BS-X cart is not inserted. Display error and reset
if (strcmp("8B86", checksumStr) != 0)
{
display_Clear();
print_Error(F("Error: Must use BS-X cart"), true);
}
//Display file Browser and wait user to select a file. Size must be 1MB.
filePath[0] = '\0';
sd.chdir("/");
fileBrowser(F("Select BS 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 != 0x100000) {
println_Msg(F("File must be 1MB"));
display_Update();
myFile.close();
wait();
return;
}
//Disable 8M memory pack write protection
dataOut();
controlOut_SNES();
writeBank_SV(0x0C, 0x5000, 0x80); //Modify write enable register
writeBank_SV(0x0E, 0x5000, 0x80); //Commit register modification
//Erase memory pack
println_Msg(F("Erasing pack..."));
display_Update();
eraseAll_SV();
//Blank check
//Set pins to input
dataIn();
controlIn_SNES();
println_Msg(F("Blank check..."));
display_Update();
for (int currBank = 0xC0; currBank < 0xD0; currBank++) {
draw_progressbar(((currBank - 0xC0) * 0x10000), 0x100000);
for (long currByte = 0; currByte < 65536; currByte++) {
if (0xFF != readBank_SV(currBank, currByte))
{
println_Msg(F(""));
println_Msg(F("Erase failed"));
display_Update();
myFile.close();
wait();
return;
}
}
}
draw_progressbar(0x100000, 0x100000);
//Write memory pack
dataOut();
controlOut_SNES();
println_Msg(F("Writing pack..."));
display_Update();
for (int currBank = 0xC0; currBank < 0xD0; currBank++) {
draw_progressbar(((currBank - 0xC0) * 0x10000), 0x100000);
for (long currByte = 0; currByte < 65536; currByte++) {
writeBank_SV(0xC0, 0x0000, 0x10); //Program Byte
writeBank_SV(currBank, currByte, myFile.read());
writeBank_SV(0xC0, 0x0000, 0x70); //Status Mode
writeCheck_SV();
}
}
writeBank_SV(0xC0, 0x0000, 0x70); //Status Mode
writeCheck_SV();
writeBank_SV(0xC0, 0x0000, 0xFF); //Terminate write
draw_progressbar(0x100000, 0x100000);
//Verify
dataIn(); //Set pins to input
controlIn_SNES();
myFile.seekSet(0); // Go back to file beginning
println_Msg(F("Verifying..."));
display_Update();
for (int currBank = 0xC0; currBank < 0xD0; currBank++) {
draw_progressbar(((currBank - 0xC0) * 0x10000), 0x100000);
for (long currByte = 0; currByte < 65536; currByte++) {
if (myFile.read() != readBank_SV(currBank, currByte))
{
println_Msg(F(""));
println_Msg(F("Verify failed"));
display_Update();
myFile.close();
wait();
return;
}
}
}
// Close the file:
myFile.close();
draw_progressbar(0x100000, 0x100000);
println_Msg(F("Finished successfully"));
display_Update();
wait();
}
else {
print_Error(F("File doesn't exist"), false);
}
}
void eraseCheck_SV(void) {
byte ret;
dataIn();
controlIn_SNES();
// Read register
ret = readBank_SV(0xC0, 0x0004);
// CE or OE must be toggled with each subsequent status read or the
// completion of a program or erase operation will not be evident.
while ((ret & 0x80) == 0x00) { //Wait until X.bit7 = 1
controlOut_SNES();
// Switch CS(PH3) High
PORTH |= (1 << 3);
// Leave CE high for at least 60ns
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
controlIn_SNES();
// Leave CE low for at least 50ns
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
// Read register
ret = readBank_SV(0xC0, 0x0004);
}
// Switch to write
dataOut();
controlOut_SNES();
}
void supplyCheck_SV(void) {
byte ret;
dataIn();
controlIn_SNES();
// Read register
ret = readBank_SV(0xC0, 0x0004);
// CE or OE must be toggled with each subsequent status read or the
// completion of a program or erase operation will not be evident.
while ((ret & 0x08) == 0x08) { //Wait until X.bit3 = 0
controlOut_SNES();
// Switch CS(PH3) High
PORTH |= (1 << 3);
// Leave CE high for at least 60ns
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
controlIn_SNES();
// Leave CE low for at least 50ns
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
// Read register
ret = readBank_SV(0xC0, 0x0004);
}
// Switch to write
dataOut();
controlOut_SNES();
}
void writeCheck_SV(void) {
byte ret;
dataIn();
controlIn_SNES();
// Read register
ret = readBank_SV(0xC0, 0x0000);
// CE or OE must be toggled with each subsequent status read or the
// completion of a program or erase operation will not be evident.
while ((ret & 0x80) == 0x00) {
controlOut_SNES();
// Switch CS(PH3) High
PORTH |= (1 << 3);
// Leave CE high for at least 60ns
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
controlIn_SNES();
// Leave CE low for at least 50ns
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
// Read register
ret = readBank_SV(0xC0, 0x0000);
}
// Switch to write
dataOut();
controlOut_SNES();
}
void detectCheck_SV(void) {
int i = 0;
byte ret;
dataIn();
controlIn_SNES();
// Read register
ret = readBank_SV(0xC0, 0x0002);
// CE or OE must be toggled with each subsequent status read or the
// completion of a program or erase operation will not be evident.
while ((ret & 0x80) == 0x00) {
i++;
if ( i > 10000)
{
//timeout
break;
}
controlOut_SNES();
// Switch CS(PH3) High
PORTH |= (1 << 3);
// Leave CE high for at least 60ns
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
controlIn_SNES();
// Leave CE low for at least 50ns
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
// Read register
ret = readBank_SV(0xC0, 0x0002);
}
// Switch to write
dataOut();
controlOut_SNES();
}
void eraseAll_SV(void)
{
dataOut();
controlOut_SNES();
writeBank_SV(0xC0, 0x0000, 0x50); //Clear Status Registers
writeBank_SV(0xC0, 0x0000, 0x71); //Status Mode
supplyCheck_SV();
writeBank_SV(0xC0, 0x0000, 0xA7); //Chip Erase
writeBank_SV(0xC0, 0x0000, 0xD0); //Confirm
writeBank_SV(0xC0, 0x0000, 0x71); //Status Mode
eraseCheck_SV();
writeBank_SV(0xC0, 0x0000, 0xFF); //Teriminate
}
#endif
//******************************************
// End of File
//******************************************