diff options
author | Jacob Moody <moody@posixcafe.org> | 2023-02-24 06:53:14 +0000 |
---|---|---|
committer | Jacob Moody <moody@posixcafe.org> | 2023-02-24 06:53:14 +0000 |
commit | e6c6217b35c319127f0200fdb28ec86e1b774a4f (patch) | |
tree | dc045471e6611f5b75e953f19b74fe07d709b640 /sys/src/games | |
parent | f5eaa9b867e46ce946734026ac4e38640a477197 (diff) |
games/gb: crude serial port emulation
Timing is not as good as it needs to be,
but servicable in more forgiving scenarios.
Clock drift between two paired systems sits
around 8 - 32 cycles when tested locally.
Diffstat (limited to 'sys/src/games')
-rw-r--r-- | sys/src/games/gb/dat.h | 2 | ||||
-rw-r--r-- | sys/src/games/gb/ev.c | 3 | ||||
-rw-r--r-- | sys/src/games/gb/fns.h | 2 | ||||
-rw-r--r-- | sys/src/games/gb/gb.c | 13 | ||||
-rw-r--r-- | sys/src/games/gb/mem.c | 6 | ||||
-rw-r--r-- | sys/src/games/gb/mkfile | 1 | ||||
-rw-r--r-- | sys/src/games/gb/serial.c | 142 |
7 files changed, 166 insertions, 3 deletions
diff --git a/sys/src/games/gb/dat.h b/sys/src/games/gb/dat.h index e6bc54453..ce97d9031 100644 --- a/sys/src/games/gb/dat.h +++ b/sys/src/games/gb/dat.h @@ -151,6 +151,6 @@ struct Var { }; #define VAR(a) {&a, sizeof(a), 1} #define ARR(a) {a, sizeof(*a), nelem(a)} -enum { NEVENT = 8 }; +enum { NEVENT = 9 }; extern int (*mapper)(int, int); extern u32int moncols[4]; diff --git a/sys/src/games/gb/ev.c b/sys/src/games/gb/ev.c index 2fdafaf97..abbecf34f 100644 --- a/sys/src/games/gb/ev.c +++ b/sys/src/games/gb/ev.c @@ -6,7 +6,8 @@ Event evhblank, evtimer, evenv; extern Event evsamp, chev[4]; -Event *events[NEVENT] = {&evhblank, &evtimer, &evenv, &evsamp, &chev[0], &chev[1], &chev[2], &chev[3]}; +extern Event evse; +Event *events[NEVENT] = {&evhblank, &evtimer, &evenv, &evsamp, &chev[0], &chev[1], &chev[2], &chev[3], &evse}; Event *elist; static int timshtab[4] = {10, 4, 6, 8}, timsh; ulong timclock; diff --git a/sys/src/games/gb/fns.h b/sys/src/games/gb/fns.h index be9b24960..6d2639176 100644 --- a/sys/src/games/gb/fns.h +++ b/sys/src/games/gb/fns.h @@ -26,3 +26,5 @@ void sndwrite(u8int, u8int); u8int waveread(u8int); void wavewrite(u8int, u8int); u8int timread(void); +void serialwrite(void); +void serialinit(int, char *); diff --git a/sys/src/games/gb/gb.c b/sys/src/games/gb/gb.c index 0c1119bb1..31981bd82 100644 --- a/sys/src/games/gb/gb.c +++ b/sys/src/games/gb/gb.c @@ -262,6 +262,8 @@ void threadmain(int argc, char **argv) { int t; + int lis = -1; + char *addr = nil; colinit(); ARGBEGIN { @@ -280,12 +282,23 @@ threadmain(int argc, char **argv) case 'x': fixscale = strtol(EARGF(usage()), nil, 0); break; + case 'l': + lis = 1; + addr = EARGF(usage()); + break; + case 'i': + lis = 0; + addr = EARGF(usage()); + break; default: usage(); } ARGEND; if(argc < 1) usage(); + if(lis == 0 || lis == 1) + serialinit(lis, addr); + loadrom(argv[0]); initemu(PICW, PICH, 4, XRGB32, 1, nil); regkey("b", 'z', 1<<5); diff --git a/sys/src/games/gb/mem.c b/sys/src/games/gb/mem.c index 165445870..5b07b6a10 100644 --- a/sys/src/games/gb/mem.c +++ b/sys/src/games/gb/mem.c @@ -83,7 +83,11 @@ regwrite(u8int a, u8int v) switch(a){ case JOYP: v |= 0xcf; break; - case SC: v |= 0x7c; break; + case SC: + if((reg[a] & 0x80) == 0 && (v & 0x80)) + serialwrite(); + reg[a] = v; + return; case DIV: divclock = clock; v = 0; diff --git a/sys/src/games/gb/mkfile b/sys/src/games/gb/mkfile index 80f1d0110..93cf613b0 100644 --- a/sys/src/games/gb/mkfile +++ b/sys/src/games/gb/mkfile @@ -11,6 +11,7 @@ OFILES=\ state.$O\ apu.$O\ eui.$O\ + serial.$O\ HFILES=dat.h fns.h diff --git a/sys/src/games/gb/serial.c b/sys/src/games/gb/serial.c new file mode 100644 index 000000000..b752f8f2b --- /dev/null +++ b/sys/src/games/gb/serial.c @@ -0,0 +1,142 @@ +#include <u.h> +#include <libc.h> +#include <thread.h> +#include "../eui.h" +#include "dat.h" +#include "fns.h" + +static int infd = -1; +static int outfd = -1; +static int leader = 0; + +static Channel *inc = nil; +static Channel *outc = nil; + +typedef struct Msg Msg; +struct Msg { + u8int c; + u8int b; + u32int t; +}; + +enum{ + Cex = 1, + Csync = 2, + Cgone = 0xFF, +}; + +static Msg mine = {Cgone}; +static Msg theirs = {Cgone}; +Event evse; + +static void +writer(void *) +{ + Msg m; + uchar buf[1+1+4]; + + while(recv(outc, &m) == 1){ + buf[0] = m.c; + buf[1] = m.b; + buf[2] = m.t>>24; + buf[3] = m.t>>16; + buf[4] = m.t>>8; + buf[5] = m.t; + if(write(outfd, buf, sizeof buf) != sizeof buf) + break; + } + + chanclose(outc); + threadexits(nil); +} + +static void +reader(void *) +{ + Msg m; + uchar buf[1+1+4]; + + while(readn(infd, buf, sizeof buf) == sizeof buf){ + m.c = buf[0]; + m.b = buf[1]; + m.t = ((u32int)buf[2]<<24) | ((u32int)buf[3]<<16) | ((u32int)buf[4]<<8) | (u32int)buf[5]; + send(inc, &m); + } + + chanclose(inc); + threadexits(nil); +} + +void +serialwrite(void) +{ + if(outc == nil) + return; + mine = (Msg){Cex, reg[SB], clock}; +} + +void +serialtick(void *) +{ + Msg m, t; + + if(leader){ + m = (Msg){mine.c == Cgone ? Csync : mine.c, mine.b, clock}; + if(send(outc, &m) != 1) + return; + if(recv(inc, &t) != 1) + return; + theirs = t; + } else { + if(recv(inc, &t) != 1) + return; + theirs = t; + m = (Msg){theirs.c, reg[SB], clock}; + if(send(outc, &m) != 1) + return; + } + + addevent(&evse, FREQ / 64); + assert(theirs.c != Cgone); + if(theirs.c == Csync) + return; + + reg[SB] = theirs.b; + reg[SC] &= ~0x80; + if(leader) + reg[SC] |= 0x1; + else + reg[SC] &= ~0x1; + reg[IF] |= 0x8; +} + +void +serialinit(int dolis, char *addr) +{ + int acfd, lcfd; + char adir[40], ldir[40]; + + evse.f = serialtick; + addevent(&evse, FREQ / 64); + + if(dolis){ + acfd = announce(addr, adir); + if(acfd < 0) + sysfatal("failed to announce: %r"); + lcfd = listen(adir, ldir); + if(lcfd < 0) + sysfatal("failed to listen: %r"); + infd = outfd = accept(lcfd, ldir); + leader = 1; + } else { + infd = outfd = dial(addr, nil, nil, nil); + } + + if(infd < 0 || outfd < 0) + sysfatal("failed to establish link: %r"); + + inc = chancreate(sizeof(Msg), 0); + outc = chancreate(sizeof(Msg), 0); + proccreate(reader, nil, mainstacksize); + proccreate(writer, nil, mainstacksize); +} |