git: 9front

Download patch

ref: c913f5ef68f7ac907cbc60047b806ecb0d0e2ede
parent: d695590dcbf447bee5b1a5c31ead95bc6412621c
author: Jacob Moody <moody@posixcafe.org>
date: Sat Mar 14 18:14:25 EDT 2026

games/md: add support for SEGA mapper

Only used officially by super street fighter 2, but also sees
use in homebrew.

--- a/sys/src/games/md/dat.h
+++ b/sys/src/games/md/dat.h
@@ -8,7 +8,7 @@
 
 extern u16int ram[32768];
 extern u16int *prg;
-extern int nprg;
+extern u32int prgend;
 extern u8int *sram;
 extern u32int sramctl, sram0, sram1;
 extern int savefd, saveclock;
--- a/sys/src/games/md/md.c
+++ b/sys/src/games/md/md.c
@@ -8,7 +8,7 @@
 #include "fns.h"
 
 u16int *prg;
-int nprg;
+u32int prgend;
 u8int *sram;
 u32int sramctl, nsram, sram0, sram1;
 int savefd = -1;
@@ -39,6 +39,16 @@
 		readn(savefd, sram, nsram);
 }
 
+struct {
+	char a[16];
+	int n;
+} systems[] = {
+	"SEGA MEGA DRIVE", 15,
+	"SEGA GENESIS", 12,
+	/* homebrew */
+	"SEGA SSF", 8
+};
+
 static void
 loadrom(char *file)
 {
@@ -45,27 +55,38 @@
 	static uchar hdr[512], buf[4096];
 	u32int v;
 	u16int *p;
-	int fd, rc, i;
-	
+	int fd, rc, i, n;
+	vlong r;
+
 	fd = open(file, OREAD);
 	if(fd < 0)
 		sysfatal("open: %r");
 	if(readn(fd, hdr, 512) < 512)
 		sysfatal("read: %r");
-	if(memcmp(hdr + 0x100, "SEGA MEGA DRIVE ", 16) != 0 && memcmp(hdr + 0x100, "SEGA GENESIS    ", 16) != 0)
-		sysfatal("invalid rom");
+	for(i = 0; i < nelem(systems); i++)
+		if(memcmp(hdr + 0x100, systems[i].a, systems[i].n) == 0)
+			break;
+	if(i == nelem(systems))
+		sysfatal("invalid rom system type: %.*s", 16, (char*)(hdr + 0x100));
+
 	v = hdr[0x1a0] << 24 | hdr[0x1a1] << 16 | hdr[0x1a2] << 8 | hdr[0x1a3];
 	if(v != 0)
-		sysfatal("rom starts at nonzero address");
+		sysfatal("rom starts at nonzero address: %ux", v);
 	v = hdr[0x1a4] << 24 | hdr[0x1a5] << 16 | hdr[0x1a6] << 8 | hdr[0x1a7];
-	nprg = v = v+2 & ~1;
-	if(nprg == 0)
+	prgend = v+2 & ~1;
+	if(prgend == 0)
 		sysfatal("invalid rom");
-	p = prg = malloc(v);
+	r = seek(fd, -1, 2);
+	if(r < 0)
+		sysfatal("rom seek: %r");
+	if(r > 32*1024*1024)
+		sysfatal("rom above max size of 32M");
+	n = r+2 & ~1;
+	p = prg = malloc(n);
 	if(prg == nil)
 		sysfatal("malloc: %r");
 	seek(fd, 0, 0);
-	while(v != 0){
+	for(v = n; v != 0;){
 		rc = readn(fd, buf, sizeof buf);
 		if(rc == 0)
 			break;
--- a/sys/src/games/md/mem.c
+++ b/sys/src/games/md/mem.c
@@ -21,6 +21,10 @@
 u8int z80bus = RESET;
 u16int z80bank;
 
+int ssfmapper;
+/* virtual → physical 512K page */
+u16int ssfpage[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+
 //#define vramdebug(a, s, a1, a2, a3) if((a & ~1) == 0xe7a0) print(s, a1, a2, a3);
 #define vramdebug(a, s, a1, a2, a3)
 
@@ -50,6 +54,8 @@
 void
 regwrite(u16int a, u16int v)
 {
+	int p;
+
 	switch(a | 1){
 	case 0x0003: case 0x0005: case 0x0007:
 	case 0x0009: case 0x000b: case 0x000d:
@@ -71,7 +77,11 @@
 		else
 			sramctl &= ~SRAMEN;
 		return;
-	case 0x30f3: case 0x30f5: case 0x30f7: case 0x30f9: case 0x30fb:
+	case 0x30f3: case 0x30f5: case 0x30f7: case 0x30f9: case 0x30fb: case 0x30fd: case 0x30ff:
+		ssfmapper = 1;
+		p = ((a|1) - 0x30f1) / 2;
+		assert(p < 8);
+		ssfpage[p] = v;
 		return;
 	}
 	fprint(2, "write to 0xa1%.4x (pc=%#.6ux)", a, curpc);
@@ -117,13 +127,14 @@
 u16int
 memread(u32int a)
 {
-	u16int v;
+	u16int v, p;
+	u32int a2;
 
 	switch(a >> 21 & 7){
 	case 0: case 1:
 		if(a < sram0 || a > sram1)
 			goto rom;
-		if((sramctl & SRAMEN) == 0 && nprg > 2*1024*1024)
+		if((sramctl & SRAMEN) == 0 && prgend > 2*1024*1024)
 			goto rom;
 		switch(sramctl & ADDRMASK){
 		case ADDREVEN: return sram[(a - sram0) >> 1] << 8;
@@ -131,7 +142,14 @@
 		case ADDRBOTH: return sram[a - sram0] << 8 | sram[a - sram0 + 1];
 		}
 	rom:
-		return prg[(a % nprg) / 2];
+		if(ssfmapper){
+			a2 = a % prgend;
+			p = a2 / (512*1024);
+			a2 = a2 % (512*1024);
+			a2 += ssfpage[p] * 512*1024;
+			return prg[a2 / 2];
+		} else
+			return prg[(a % prgend) / 2];
 	case 5:
 		switch(a >> 16 & 0xff){
 		case 0xa0:
@@ -209,7 +227,7 @@
 	case 0: case 1:
 		if(a < sram0 || a > sram1)
 			goto invalid;
-		if((sramctl & SRAMEN) == 0 && nprg > 2*1024*1024)
+		if((sramctl & SRAMEN) == 0 && prgend > 2*1024*1024)
 			goto invalid;
 		switch(sramctl & ADDRMASK){
 		case ADDREVEN: sram[(a - sram0) >> 1] = v >> 8; break;
--