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;
--
⑨