diff --git a/Makefile b/Makefile
index 08baa96..8dcfe03 100644
--- a/Makefile
+++ b/Makefile
@@ -20,11 +20,12 @@ SHARPMZ_SRC = $(wildcard ./support/sharpmz/*.cpp)
ARCHIE_SRC = $(wildcard ./support/archie/*.cpp)
ST_SRC = $(wildcard ./support/st/*.cpp)
X86_SRC = $(wildcard ./support/x86/*.cpp)
+SNES_SRC = $(wildcard ./support/snes/*.cpp)
-VPATH = ./:./support/minimig:./support/sharpmz:./support/archie:./support/st:./support/x86
+VPATH = ./:./support/minimig:./support/sharpmz:./support/archie:./support/st:./support/x86:./support/snes
-OBJ = $(SRC:.c=.o) $(SRC2:.cpp=.o) $(MINIMIG_SRC:.cpp=.o) $(SHARPMZ_SRC:.cpp=.o) $(ARCHIE_SRC:.cpp=.o) $(ST_SRC:.cpp=.o) $(X86_SRC:.cpp=.o)
-DEP = $(SRC:.c=.d) $(SRC2:.cpp=.d) $(MINIMIG_SRC:.cpp=.d) $(SHARPMZ_SRC:.cpp=.d) $(ARCHIE_SRC:.cpp=.d) $(ST_SRC:.cpp=.d) $(X86_SRC:.cpp=.d)
+OBJ = $(SRC:.c=.o) $(SRC2:.cpp=.o) $(MINIMIG_SRC:.cpp=.o) $(SHARPMZ_SRC:.cpp=.o) $(ARCHIE_SRC:.cpp=.o) $(ST_SRC:.cpp=.o) $(X86_SRC:.cpp=.o) $(SNES_SRC:.cpp=.o)
+DEP = $(SRC:.c=.d) $(SRC2:.cpp=.d) $(MINIMIG_SRC:.cpp=.d) $(SHARPMZ_SRC:.cpp=.d) $(ARCHIE_SRC:.cpp=.d) $(ST_SRC:.cpp=.d) $(X86_SRC:.cpp=.d) $(SNES_SRC:.cpp=.d)
CFLAGS = $(DFLAGS) -c -O3 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE -DVDATE=\"`date +"%y%m%d"`\"
LFLAGS = -lc -lstdc++ -lrt
diff --git a/MiSTer.vcxproj b/MiSTer.vcxproj
index df594ec..941417f 100644
--- a/MiSTer.vcxproj
+++ b/MiSTer.vcxproj
@@ -63,6 +63,7 @@
+
@@ -99,6 +100,7 @@
+
diff --git a/MiSTer.vcxproj.filters b/MiSTer.vcxproj.filters
index 09646dd..3b13493 100644
--- a/MiSTer.vcxproj.filters
+++ b/MiSTer.vcxproj.filters
@@ -91,6 +91,9 @@
Source Files
+
+ Source Files
+
@@ -195,5 +198,8 @@
Header Files
+
+ Header Files
+
\ No newline at end of file
diff --git a/support.h b/support.h
index a0373ad..73ce1d9 100644
--- a/support.h
+++ b/support.h
@@ -15,4 +15,7 @@
#include "support/st/st_tos.h"
// X86 support
-#include "support/x86/x86.h"
\ No newline at end of file
+#include "support/x86/x86.h"
+
+// SNES support
+#include "support/snes/snes.h"
diff --git a/support/snes/snes.cpp b/support/snes/snes.cpp
new file mode 100644
index 0000000..0f897e8
--- /dev/null
+++ b/support/snes/snes.cpp
@@ -0,0 +1,160 @@
+
+#include
+#include
+#include
+#include
+
+#include "../../file_io.h"
+
+static uint8_t hdr[512];
+
+enum HeaderField {
+ CartName = 0x00,
+ Mapper = 0x15,
+ RomType = 0x16,
+ RomSize = 0x17,
+ RamSize = 0x18,
+ CartRegion = 0x19,
+ Company = 0x1a,
+ Version = 0x1b,
+ Complement = 0x1c, //inverse checksum
+ Checksum = 0x1e,
+ ResetVector = 0x3c,
+};
+
+static uint32_t score_header(const uint8_t *data, uint32_t size, uint32_t addr)
+{
+ if (size < addr + 64) return 0; //image too small to contain header at this location?
+ int score = 0;
+
+ uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8);
+ uint16_t checksum = data[addr + Checksum] | (data[addr + Checksum + 1] << 8);
+ uint16_t complement = data[addr + Complement] | (data[addr + Complement + 1] << 8);
+
+ uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset
+ uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit
+
+ //$00:[0000-7fff] contains uninitialized RAM and MMIO.
+ //reset vector must point to ROM at $00:[8000-ffff] to be considered valid.
+ if (resetvector < 0x8000) return 0;
+
+ //some images duplicate the header in multiple locations, and others have completely
+ //invalid header information that cannot be relied upon.
+ //below code will analyze the first opcode executed at the specified reset vector to
+ //determine the probability that this is the correct header.
+
+ //most likely opcodes
+ if (resetop == 0x78 //sei
+ || resetop == 0x18 //clc (clc; xce)
+ || resetop == 0x38 //sec (sec; xce)
+ || resetop == 0x9c //stz $nnnn (stz $4200)
+ || resetop == 0x4c //jmp $nnnn
+ || resetop == 0x5c //jml $nnnnnn
+ ) score += 8;
+
+ //plausible opcodes
+ if (resetop == 0xc2 //rep #$nn
+ || resetop == 0xe2 //sep #$nn
+ || resetop == 0xad //lda $nnnn
+ || resetop == 0xae //ldx $nnnn
+ || resetop == 0xac //ldy $nnnn
+ || resetop == 0xaf //lda $nnnnnn
+ || resetop == 0xa9 //lda #$nn
+ || resetop == 0xa2 //ldx #$nn
+ || resetop == 0xa0 //ldy #$nn
+ || resetop == 0x20 //jsr $nnnn
+ || resetop == 0x22 //jsl $nnnnnn
+ ) score += 4;
+
+ //implausible opcodes
+ if (resetop == 0x40 //rti
+ || resetop == 0x60 //rts
+ || resetop == 0x6b //rtl
+ || resetop == 0xcd //cmp $nnnn
+ || resetop == 0xec //cpx $nnnn
+ || resetop == 0xcc //cpy $nnnn
+ ) score -= 4;
+
+ //least likely opcodes
+ if (resetop == 0x00 //brk #$nn
+ || resetop == 0x02 //cop #$nn
+ || resetop == 0xdb //stp
+ || resetop == 0x42 //wdm
+ || resetop == 0xff //sbc $nnnnnn,x
+ ) score -= 8;
+
+ //at times, both the header and reset vector's first opcode will match ...
+ //fallback and rely on info validity in these cases to determine more likely header.
+
+ //a valid checksum is the biggest indicator of a valid header.
+ if ((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4;
+
+ if (addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM
+ if (addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM
+ if (addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually SDD1
+ if (addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM
+
+ if (data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header
+ if (data[addr + RomType] < 0x08) score++;
+ if (data[addr + RomSize] < 0x10) score++;
+ if (data[addr + RamSize] < 0x08) score++;
+ if (data[addr + CartRegion] < 14) score++;
+
+ if (score < 0) score = 0;
+ return score;
+}
+
+static uint32_t find_header(const uint8_t *data, uint32_t size)
+{
+ uint32_t score_lo = score_header(data, size, 0x007fc0);
+ uint32_t score_hi = score_header(data, size, 0x00ffc0);
+ uint32_t score_ex = score_header(data, size, 0x40ffc0);
+ if (score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits
+
+ if (score_lo >= score_hi && score_lo >= score_ex)
+ {
+ return score_lo ? 0x007fc0 : 0;
+ }
+ else if (score_hi >= score_ex)
+ {
+ return score_hi ? 0x00ffc0 : 0;
+ }
+
+ return score_ex ? 0x40ffc0 : 0;
+}
+
+uint8_t* snes_get_header(fileTYPE *f)
+{
+ memset(hdr, 0, sizeof(hdr));
+ uint8_t *buf = (uint8_t*)malloc(f->size);
+ if (buf)
+ {
+ FileSeekLBA(f, 0);
+ if (FileReadAdv(f, buf, f->size))
+ {
+ uint32_t addr = find_header(buf, f->size);
+ if (addr)
+ {
+ uint8_t romsz = buf[addr + RomSize];
+ uint8_t ramsz = buf[addr + RamSize];
+ if (romsz >= 0x10) romsz = 0;
+ if (ramsz >= 0x08) ramsz = 0;
+ hdr[0] = (ramsz << 4) | romsz;
+
+ hdr[1] = (addr == 0x00ffc0) ? 1 : (addr == 0x40ffc0) ? 5 : 0; //LHRom type
+
+ hdr[2] = buf[addr + RomType];
+
+ if ((buf[addr + CartRegion] >= 0x02 && buf[addr + CartRegion] <= 0x0C) || buf[addr + CartRegion] == 0x11)
+ { //PAL Regions
+ hdr[3] |= 1;
+ }
+
+ *(uint32_t*)(&hdr[4]) = addr;
+ }
+ }
+ FileSeekLBA(f, 0);
+ free(buf);
+ }
+ return hdr;
+}
diff --git a/support/snes/snes.h b/support/snes/snes.h
new file mode 100644
index 0000000..8651d4e
--- /dev/null
+++ b/support/snes/snes.h
@@ -0,0 +1,6 @@
+#ifndef SNES_H
+#define SNES_H
+
+uint8_t* snes_get_header(fileTYPE *f);
+
+#endif
diff --git a/user_io.cpp b/user_io.cpp
index 11c2eea..161ca63 100644
--- a/user_io.cpp
+++ b/user_io.cpp
@@ -152,6 +152,13 @@ char is_x86_core()
return (is_x86_type == 1);
}
+static int is_snes_type = 0;
+char is_snes_core()
+{
+ if (!is_snes_type) is_snes_type = strcasecmp(core_name, "SNES") ? 2 : 1;
+ return (is_snes_type == 1);
+}
+
char is_cpc_core()
{
return !strcasecmp(core_name, "amstrad");
@@ -1260,6 +1267,17 @@ int user_io_file_tx(const char* name, unsigned char index, char opensave, char m
}
else
{
+ if (is_snes_core())
+ {
+ printf("Load SNES ROM.\n");
+ uint8_t* buf = snes_get_header(&f);
+ hexdump(buf, 16, 0);
+ EnableFpga();
+ spi8(UIO_FILE_TX_DAT);
+ spi_write(buf, 512, fio_size);
+ DisableFpga();
+ }
+
while (bytes2send)
{
printf(".");
diff --git a/user_io.h b/user_io.h
index e1a6d1a..c31e69a 100644
--- a/user_io.h
+++ b/user_io.h
@@ -196,6 +196,7 @@ char *user_io_get_core_name();
const char *user_io_get_core_name_ex();
char is_menu_core();
char is_x86_core();
+char is_snes_core();
char has_menu();
int user_io_get_kbdemu();