games/gba: add rtc GPIO implementation

This commit is contained in:
Jacob Moody 2024-02-24 14:35:33 +00:00
parent 2259c436be
commit f881d4f672
7 changed files with 235 additions and 5 deletions

View file

@ -28,6 +28,9 @@ gb, gba, nes, snes \- emulators
.B -s
.I savetype
] [
.B -g
.I gpiotype
] [
.B -x
.I scale
]
@ -127,6 +130,10 @@ Some roms require a specific flash device id which may need to be set manually f
Valid formats and corresponding ids are:
flash512 (SST), flash512mx (Macronix 64K), flash512pan (Panasonic), flash512atm (Atmel),
flash1024 (Macronix 128K), flash1024san (Sanyo).
.TP
.B -g
GPIO hardware used by the original game. The only valid type currently is rtc (real time clock).
By default, the emulator attempts to automatically detect the GPIO hardware using the game code found in the rom header.
.PP
.B nes
options:

View file

@ -9,7 +9,7 @@ extern u16int pram[], oam[];
extern u16int reg[];
extern uchar *rom, *back;
extern int nrom, nback, backup;
extern int flashid;
extern int flashid, gpiogame;
extern int hblank, ppuy;

View file

@ -26,3 +26,10 @@ void sndwrite(u16int, u16int);
void loadstate(char *);
void savestate(char *);
void cpuload(void);
void gpioident(void);
void gpiowdata(u32int);
void gpiowdir(u32int);
void gpiowcontrol(u32int);
u32int gpiordata(void);
u32int gpiordir(void);
u32int gpiorcontrol(void);

View file

@ -255,7 +255,7 @@ flush(void)
void
usage(void)
{
fprint(2, "usage: %s [-a] [-s savetype] [-b biosfile] [-x scale] rom\n", argv0);
fprint(2, "usage: %s [-a] [-s savetype] [-b biosfile] [-x scale] [-g gpiotype] rom\n", argv0);
exits("usage");
}
@ -281,6 +281,11 @@ threadmain(int argc, char **argv)
case 'x':
fixscale = strtol(EARGF(usage()), nil, 0);
break;
case 'g':
s = EARGF(usage());
if(strcmp(s, "rtc") == 0)
gpiogame = 1;
break;
default:
usage();
} ARGEND;
@ -289,6 +294,7 @@ threadmain(int argc, char **argv)
loadbios();
loadrom(argv[0]);
gpioident();
initemu(240, 160, 2, CHAN4(CIgnore, 1, CBlue, 5, CGreen, 5, CRed, 5), 1, nil);
regkey("b", 'z', 1<<1);
regkey("a", 'x', 1<<0);

173
sys/src/games/gba/gpio.c Normal file
View file

@ -0,0 +1,173 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
#include "../eui.h"
#include "dat.h"
#include "fns.h"
int gpiogame;
static int gpioen;
static int rtcclk;
static uchar rtcdata, rtcout;
static int rtccount;
static uchar pinstate[3] = { 1, 1, 1 };
enum {
RCLK=1<<0,
RSIO=1<<1,
RCS=1<<2,
PCLK=0,
PSIO=1,
PCS=2,
};
void
gpioident(void)
{
char code[4];
memcpy(code, rom+0xAC, 4);
/* Pokemon E/S/R. RTC only */
if(memcmp(code, "BPEE", 4) == 0
|| memcmp(code, "AXPE", 4) == 0
|| memcmp(code, "AXVE", 4) == 0)
gpiogame = 1;
}
#define BCD(x) (x/10 * 16 + x%10)
static void
getdate(uchar out[7])
{
static Tzone *zone;
static Tm date;
if(zone == nil)
zone = tzload("local");
tmnow(&date, zone);
date.year -= 100;
date.mon++;
out[0] = BCD(date.year);
out[1] = BCD(date.mon);
out[2] = BCD(date.mday);
out[3] = BCD(date.wday);
out[4] = BCD(date.hour);
out[5] = BCD(date.min);
out[6] = BCD(date.sec);
}
// https://html.alldatasheet.com/html-pdf/80559/SII/S-3511/45/1/S-3511.html
static void
rtcstate(void)
{
enum { CMD, STATUS, DATETIME };
static int state = CMD;
enum { WR=0, RD=1 };
static int dir = WR;
static int datetimepos = 0;
static uchar date[7];
uchar cmd;
switch(state){
case CMD:
dir = rtcdata&1;
if((rtcdata&0xF0) != 0x60){
print("wrong rtc state: %X\n", rtcdata);
return;
}
cmd = (rtcdata&0xF)>>1;
switch(cmd){
case 0: /* reset */
break;
case 1: /* status */
if(dir == RD)
rtcdata = 0b01000000;
state = STATUS;
break;
case 2: /* year → seconds */
case 3: /* hour → seconds */
getdate(date);
datetimepos = (cmd-2)<<2;
if(dir == RD)
rtcdata = date[datetimepos];
datetimepos++;
state = DATETIME;
break;
default:
print("unsupported rtc cmd: %d %d\n", dir, cmd);
rtcdata = 0;
break;
}
break;
case STATUS:
state = CMD;
if(dir == WR)
rtcdata = 0;
break;
case DATETIME:
if(dir == RD)
rtcdata = date[datetimepos];
else
rtcdata = 0;
if(++datetimepos == 8){
datetimepos = 0;
state = CMD;
}
break;
}
}
void
gpiowdata(u32int v)
{
if((v&RCLK) == 0 && rtcclk == 1){
if(pinstate[PSIO] == 1){
rtcdata |= ((v&RSIO)>>1)<<(7-rtccount);
} else {
rtcout = rtcdata&1;
rtcdata >>= 1;
}
rtccount++;
}
if(rtccount == 8){
rtcstate();
rtccount = 0;
}
rtcclk = v&RCLK;
}
u32int
gpiordata(void)
{
return rtcout<<1;
}
void
gpiowdir(u32int v)
{
pinstate[PCLK] = v&1;
pinstate[PSIO] = (v&2)>>1;
pinstate[PCS] = (v&4)>>2;
}
u32int
gpiordir(void)
{
return (pinstate[PCS]<<2) | (pinstate[PSIO]<<1) | (u32int)pinstate[PCLK];
}
void
gpiowcontrol(u32int v)
{
gpioen = v;
}
u32int
gpiorcontrol(void)
{
return gpioen;
}

View file

@ -263,7 +263,21 @@ memread(u32int a, int n, int seq)
b = a & sizeof(oam) - 1;
cyc++;
return ar16read(oam + b/2, b & 1, n);
case 8: case 9: case 10: case 11: case 12: case 13:
case 8:
if(!gpiogame)
goto Rom;
b = a & 0xffffff;
switch(b){
case 0xC4:
return gpiordata();
case 0xC6:
return gpiordir();
case 0xC8:
return gpiorcontrol();
}
/* fallthrough */
case 9: case 10: case 11: case 12: case 13:
Rom:
b = a & 0x1ffffff;
cyc += waitst[(a >> 25) - 4 | seq << 2 | (n > 2) << 3];
if(b >= nrom){
@ -290,7 +304,7 @@ memread(u32int a, int n, int seq)
void
memwrite(u32int a, u32int v, int n)
{
u32int b;
u32int b, t;
assert((a & n-1) == 0);
switch(a >> 24){
@ -342,7 +356,29 @@ memwrite(u32int a, u32int v, int n)
if(n != 1)
ar16write(oam + b/2, b & 1, v, n);
return;
case 8: case 9: case 10: case 11: case 12: case 13:
case 8:
if(!gpiogame)
goto Rom;
b = a & 0xffffff;
switch(b){
case 0xC4:
t = v&0xFFFF;
gpiowdata(t);
if(n <= 2)
return;
v>>=16;
case 0xC6:
t = v&0xFFFF;
gpiowdir(t);
return;
case 0xC8:
t = v&0xFFFF;
gpiowcontrol(t);
return;
}
/* fallthrough */
case 9: case 10: case 11: case 12: case 13:
Rom:
if(backup == EEPROM){
b = a & 0x01ffffff;
if(b >= eepstart)

View file

@ -11,6 +11,7 @@ OFILES=\
apu.$O\
state.$O\
eui.$O\
gpio.$O\
HFILES=dat.h fns.h