code: plan9front

Download patch

ref: f881d4f67254359ef348376e092c779993f57ed6
parent: 2259c436be76e22801766f8cd1a056f5b4430139
author: Jacob Moody <moody@posixcafe.org>
date: Sat Feb 24 09:35:33 EST 2024

games/gba: add rtc GPIO implementation

--- a/sys/man/1/nintendo
+++ b/sys/man/1/nintendo
@@ -28,6 +28,9 @@
 .B -s
 .I savetype
 ] [
+.B -g
+.I gpiotype
+] [
 .B -x
 .I scale
 ]
@@ -127,6 +130,10 @@
 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:
--- a/sys/src/games/gba/dat.h
+++ b/sys/src/games/gba/dat.h
@@ -9,7 +9,7 @@
 extern u16int reg[];
 extern uchar *rom, *back;
 extern int nrom, nback, backup;
-extern int flashid;
+extern int flashid, gpiogame;
 
 extern int hblank, ppuy;
 
--- a/sys/src/games/gba/fns.h
+++ b/sys/src/games/gba/fns.h
@@ -26,3 +26,10 @@
 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);
--- a/sys/src/games/gba/gba.c
+++ b/sys/src/games/gba/gba.c
@@ -255,7 +255,7 @@
 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 @@
 	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 @@
 
 	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);
--- /dev/null
+++ b/sys/src/games/gba/gpio.c
@@ -1,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;
+}
--- a/sys/src/games/gba/mem.c
+++ b/sys/src/games/gba/mem.c
@@ -263,7 +263,21 @@
 		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 @@
 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 @@
 		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)
--- a/sys/src/games/gba/mkfile
+++ b/sys/src/games/gba/mkfile
@@ -11,6 +11,7 @@
 	apu.$O\
 	state.$O\
 	eui.$O\
+	gpio.$O\
 
 HFILES=dat.h fns.h