ref: 1195ca910c393e542d6aa23035fa75719af1107e
parent: 70c7a9eb07516843da083b6231d55a07d28b9350
author: qwx <devnull@localhost>
date: Sat May 12 15:20:53 EDT 2018
add libemu move redundant code from emulators to a common library
--- a/sys/man/1/nintendo
+++ b/sys/man/1/nintendo
@@ -4,7 +4,7 @@
.SH SYNOPSIS
.B games/gb
[
-.B -23acdT
+.B -acdT
]
[
.B -C
@@ -14,7 +14,7 @@
.br
.B games/gba
[
-.B -23aT
+.B -aT
] [
.B -b
.I biosfile
@@ -26,13 +26,13 @@
.br
.B games/nes
[
-.B -23aos
+.B -aos
]
.I romfile
.br
.B games/snes
[
-.B -23ahmsT
+.B -ahmsT
]
.I romfile
.SH DESCRIPTION
@@ -43,7 +43,7 @@
.I snes
are emulators for the Nintendo Game Boy and Game Boy Color (GB and GBC), Nintendo Game Boy Advance (GBA), Nintendo Entertainment System (NES), and Super Nintendo Entertainment System (SNES).
They execute the romfile given as an argument.
-The \fBz\fR, \fBx\fR, \fBa\fR, \fBs\fR, return and shift keys correspond to B, A, Y, X, Start and Select, respectively.
+The \fBz\fR, \fBx\fR, \fBa\fR, \fBs\fR, \fBq\fR, \fBw\fRreturn and shift keys correspond to B, A, Y, X, L1, L2, Start and Select, respectively.
Other keys:
.TP
F5
@@ -60,14 +60,8 @@
.PP
Command line options:
.TP
-.B -2 -3
-Scale the screen by the given factor.
-.TP
.B -a
Enable audio output.
-.TP
-.B -T
-Display percentage of how fast the emulator is running relative to a real console.
.PP
.B gb
options:
--- a/sys/man/1/sega
+++ b/sys/man/1/sega
@@ -4,7 +4,7 @@
.SH SYNOPSIS
.B games/md
[
-.B -23a
+.B -a
]
.I romfile
.SH DESCRIPTION
@@ -11,7 +11,7 @@
.I Md
is an emulator for the Sega Megadrive/Genesis.
It executes the romfile given as an argument.
-The \fBz\fR, \fBx\fR, \fBc\fR, return and shift keys correspond to A, B, C, Start and Select, respectively.
+The \fBz\fR, \fBx\fR, \fBa\fR, return and shift keys correspond to A, B, C, Start and Select, respectively.
Other keys:
.TP
Esc
@@ -22,9 +22,6 @@
.PP
Command line options:
.TP
-.B -2 -3
-Scale the screen by the given factor.
-.TP
.B -a
Enable audio output.
.SH SOURCE
@@ -33,4 +30,4 @@
Probably!
.SH HISTORY
.I Md
-first appeared in 9front (May, 2014).
+first appeared in 9front (November, 2014).
--- /dev/null
+++ b/sys/man/2/emu
@@ -1,0 +1,137 @@
+.TH EMU 2
+.SH NAME
+initemu, regkeyfn, flushmouse, flushscreen, flushaudio, screenwipe \- graphical emulator-like software scaffolding
+.SH SYNOPSIS
+.nf
+.ft L
+#include <u.h>
+#include <libc.h>
+#include <emu.h>
+.PP
+.ta +\w'\fLvoid fP'u
+.B
+void flushmouse(int discard);
+.PP
+.B
+void flushscreen(void);
+.PP
+.B
+void flushaudio(int (*fn)(void));
+.PP
+.B
+void regkeyfn(Rune r, void (*fn)(void));
+.PP
+.B
+void regkey(char *joyk, Rune r, int k);
+.PP
+.B
+void initemu(int dx, int dy, int bpp, ulong chan,
+.B
+ int dokey, void(*kproc)(void*));
+.SH DESCRIPTION
+.I Libemu
+implements common user interfaces for programs controlled
+with a joypad or a limited number of keys.
+.PP
+.B initemu
+initializes the display for the given internal screen size
+.B dx
+by
+.B dy
+and
+.B bpp
+bit depth.
+.B Chan
+is an
+.B Image
+pixel format descriptor to be used for an internal framebuffer (see
+.IR draw (2)).
+.PP
+If
+.B dokey
+is true,
+a keyboard process is started which sets a 64-bit wide bit vector for input keys.
+.PP
+Keys are set via
+.B regkey.
+Pressing the key corresponding to the
+.B r
+rune, or writing
+.B joyk
+to standard in will
+.L OR
+.B k
+with the key bit vector.
+.PP
+.B Regkeyfn
+registers an additional rune and a callback for the keyboard process.
+.PP
+Normally, a joypad process is also started, and parses standard input for key presses.
+If
+.B dokey
+is false, only the joypad process will be started.
+If
+.B kproc
+is a valid function pointer,
+it will be used for keyboard processing instead of the library-provided one,
+and no joypad process will be started.
+.PP
+.IP
+.EX
+.ta 6n
+uchar *pic;
+.EE
+.PP
+Once
+.B initemu
+is called, a framebuffer of the specifized size is allocated,
+and may be accessed via
+.BR pic .
+.L Libemu
+scales the framebuffer to fit the greatest multiple of the framebuffer's
+width in the window.
+The scaling is horizontal only and needs to be taken into account for drawing
+within the program.
+.PP
+Typically, mouse event handling is followed by drawing the final image from the
+internal framebuffer render and writing a constant amount of audio samples,
+thereby synchronizing the program's framerate to the audio writes.
+.IP
+.EX
+.ta 6n
+Mouse m;
+extern Mousectl *mc;
+
+flushmouse(0);
+while(nbrecv(mc->c, &m) > 0){
+ ...
+}
+flushscreen();
+flushaudio(audioout);
+.EE
+.PP
+Besides window resizing, mouse events are discarded by default.
+If
+.B discard
+is false
+.B flushmouse
+will let the user program handle mouse events prior to flushing the screen (see
+.BR event (2)).
+.PP
+.B Flushscreen
+handles re-scaling and re-allocating the buffers used, as well as drawing to
+the screen, either directly, or by duplicating pre-scaled scanlines.
+.SH SOURCE
+.B /sys/src/libemu
+.SH "SEE ALSO"
+.IR draw (2),
+.IR event (2)
+.SH BUGS
+The semantics for
+.B initemu
+input selection are confusing.
+.PP
+A greater effort should be made to simplify automatic scaling for user programs.
+.SH HISTORY
+.I Libemu
+first appeared in 9front in May, 2018.
--- a/sys/src/games/c64/c64.c
+++ b/sys/src/games/c64/c64.c
@@ -4,18 +4,15 @@
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
char *bindir = "/sys/lib/c64";
-Image *tmp, *bg, *red;
-Rectangle picr, progr;
-Mousectl *mc;
-QLock pauselock;
-int paused, scale;
+Image *red;
+Rectangle progr;
u8int *rom;
int nrom;
-u64int keys;
u16int joys;
uchar *tape, tapever, tapeplay;
ulong tapelen;
@@ -26,7 +23,8 @@
{
static int cur;
int w;
-
+
+ extern Image *bg;
if(b == 0 || a == 0){
if(cur != 0){
draw(screen, progr, bg, nil, ZP);
@@ -212,21 +210,6 @@
}
static void
-screeninit(void)
-{
- Point p, q;
-
- p = divpt(addpt(screen->r.min, screen->r.max), 2);
- picr = (Rectangle){subpt(p, Pt(picw/2*scale, pich/2*scale)), addpt(p, Pt(picw/2*scale, pich/2*scale))};
- p.y += pich*scale*3/4;
- q = Pt(Dx(screen->r) * 2/5, 8);
- progr = (Rectangle){subpt(p, q), addpt(p, q)};
- freeimage(tmp);
- tmp = allocimage(display, Rect(0, 0, picw*scale, scale > 1 ? 1 : pich), XRGB32, 1, 0);
- draw(screen, screen->r, bg, nil, ZP);
-}
-
-static void
usage(void)
{
fprint(2, "usage: %s [ -23a ] [ rom ]\n", argv0);
@@ -236,17 +219,9 @@
void
threadmain(int argc, char **argv)
{
- scale = 1;
-
memreset();
ARGBEGIN {
- case '2':
- scale = 2;
- break;
- case '3':
- scale = 3;
- break;
case 'c':
loadcart(EARGF(usage()));
break;
@@ -272,16 +247,8 @@
loadsys("crom.bin", crom, 4096);
vicreset();
-
- if(initdraw(nil, nil, nil) < 0)
- sysfatal("initdraw: %r");
- mc = initmouse(nil, screen);
- if(mc == nil)
- sysfatal("initmouse: %r");
- bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
+ initemu(picw, pich, 4, XRGB32, 1, keyproc);
red = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xFF0000FF);
- screeninit();
- proccreate(keyproc, nil, mainstacksize);
nmien = IRQRESTORE;
pc = memread(0xFFFC) | memread(0xFFFD) << 8;
@@ -309,7 +276,8 @@
static Menu m = {
items, nil, 0
};
-
+
+ extern Mousectl *mc;
switch(menuhit(3, mc, &m, nil)){
case JOY:
joymode = (joymode + 1) % 3;
@@ -332,52 +300,11 @@
void
flush(void)
{
- extern u8int pic[];
-// vlong new, diff;
-// static vlong old, delta;
-
- if(nbrecvul(mc->resizec) > 0){
- if(getwindow(display, Refnone) < 0)
- sysfatal("resize failed: %r");
- screeninit();
- }
+ extern Mousectl *mc;
+ flushmouse(0);
while(nbrecv(mc->c, &mc->Mouse) > 0)
if((mc->buttons & 4) != 0)
menu();
- if(scale == 1){
- loadimage(tmp, tmp->r, pic, picw*pich*4);
- draw(screen, picr, tmp, nil, ZP);
- }else{
- Rectangle r;
- uchar *s;
- int w;
-
- s = pic;
- r = picr;
- w = picw*4*scale;
- while(r.min.y < picr.max.y){
- loadimage(tmp, tmp->r, s, w);
- s += w;
- r.max.y = r.min.y+scale;
- draw(screen, r, tmp, nil, ZP);
- r.min.y = r.max.y;
- }
- }
- flushimage(display, 1);
-/*
- if(audioout() < 0){
- new = nsec();
- diff = 0;
- if(old != 0){
- diff = BILLION/60 - (new - old) - delta;
- if(diff >= MILLION)
- sleep(diff/MILLION);
- }
- old = nsec();
- if(diff != 0){
- diff = (old - new) - (diff / MILLION) * MILLION;
- delta += (diff - delta) / 100;
- }
- }
-*/
+ flushscreen();
+ flushaudio(nil);
}
--- a/sys/src/games/c64/cpu.c
+++ b/sys/src/games/c64/cpu.c
@@ -1,5 +1,6 @@
#include <u.h>
#include <libc.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
@@ -288,8 +289,6 @@
pc |= memread(0xFFFB | (!nmi << 2)) << 8;
rP |= FLAGI;
}
-
-int trace;
void
step(void)
--- a/sys/src/games/c64/dat.h
+++ b/sys/src/games/c64/dat.h
@@ -1,10 +1,8 @@
-typedef char s8int;
-
extern u8int reg[47], crom[4096], krom[8192], brom[8192], cram[1024], cart[16384];
extern u16int pc, curpc;
extern u8int rP;
-extern int nrdy, irq, nmi, irqen, nmien, trace;
+extern int nrdy, irq, nmi, irqen, nmien;
extern u8int pla;
@@ -12,9 +10,8 @@
extern ulong tapelen;
extern u16int ppux, ppuy, picw, pich;
-extern u64int keys;
extern u16int joys;
-extern int scale, region;
+extern int region;
enum {
FLAGC = 1<<0,
@@ -81,8 +78,6 @@
};
enum {
- BILLION = 1000*1000*1000,
- MILLION = 1000*1000,
HZ = 3579545,
RATE = 44100,
SAMPDIV = HZ / 3 / RATE,
--- a/sys/src/games/c64/mem.c
+++ b/sys/src/games/c64/mem.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
--- a/sys/src/games/c64/vic.c
+++ b/sys/src/games/c64/vic.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
@@ -9,7 +10,6 @@
u16int vc, vcbase, vmli;
u8int badln, rc, displ, fract, visreg, hbord, vbord, rbord0, lbord0;
u16int chrp[40];
-u8int pic[420*263*4*3];
u64int pxs, npxs, npxs0, opxs;
u8int fg;
@@ -92,24 +92,40 @@
void
pixeldraw(u64int p, int n)
{
- int i, j;
+ int i;
+ union { u8int c[4]; u32int l; } u;
static u8int cr[] = {0, 255, 136, 170, 204, 0, 0, 238, 221, 102, 255, 51, 119, 170, 0, 187};
static u8int cg[] = {0, 255, 0, 255, 68, 204, 0, 238, 136, 68, 119, 51, 119, 255, 136, 187};
static u8int cb[] = {0, 255, 0, 238, 204, 85, 170, 119, 85, 0, 119, 51, 119, 102, 255, 187};
- u8int *q, c;
-
- q = pic + picidx * 4 * scale;
+ u8int c;
+ u32int *q;
+
+ q = (u32int *)pic + picidx * scale;
for(i = 0; i < n; i++){
c = p >> 56;
p <<= 8;
-
- j = scale;
- do{
- *q++ = cb[c];
- *q++ = cg[c];
- *q++ = cr[c];
- q++;
- }while(--j);
+ u.c[0] = cb[c];
+ u.c[1] = cg[c];
+ u.c[2] = cr[c];
+ u.c[3] = 0;
+ switch(scale){
+ case 16: *q++ = u.l;
+ case 15: *q++ = u.l;
+ case 14: *q++ = u.l;
+ case 13: *q++ = u.l;
+ case 12: *q++ = u.l;
+ case 11: *q++ = u.l;
+ case 10: *q++ = u.l;
+ case 9: *q++ = u.l;
+ case 8: *q++ = u.l;
+ case 7: *q++ = u.l;
+ case 6: *q++ = u.l;
+ case 5: *q++ = u.l;
+ case 4: *q++ = u.l;
+ case 3: *q++ = u.l;
+ case 2: *q++ = u.l;
+ default: *q++ = u.l;
+ }
}
picidx += n;
}
--- a/sys/src/games/gb/apu.c
+++ b/sys/src/games/gb/apu.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
@@ -475,7 +476,7 @@
if(sbufp == sbuf)
return 0;
cl = clock;
- rc = write(fd, sbuf, (sbufp - sbuf) * 2);
+ rc = warp10 ? (sbufp - sbuf) * 2 : write(fd, sbuf, (sbufp - sbuf) * 2);
if(rc > 0)
sbufp -= (rc+1)/2;
if(sbufp < sbuf)
--- a/sys/src/games/gb/cpu.c
+++ b/sys/src/games/gb/cpu.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
--- a/sys/src/games/gb/dat.h
+++ b/sys/src/games/gb/dat.h
@@ -1,10 +1,6 @@
-typedef char s8int;
-typedef short s16int;
-typedef long s32int;
typedef struct Event Event;
typedef struct MBC3Timer MBC3Timer;
-extern int trace;
extern u16int curpc;
extern uchar *rom, *back, reg[256], oam[256];
@@ -24,7 +20,6 @@
extern u8int mode;
extern u8int mbc, feat;
-extern int keys, scale;
enum {
JOYP = 0x00,
@@ -129,8 +124,6 @@
TIMERSIZ = 18,
PICW = 160,
PICH = 144,
- MILLION = 1000000,
- BILLION = 1000000000,
FREQ = 1<<23
};
--- a/sys/src/games/gb/gb.c
+++ b/sys/src/games/gb/gb.c
@@ -2,35 +2,29 @@
#include <libc.h>
#include <thread.h>
#include <draw.h>
-#include <mouse.h>
#include <keyboard.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
int cpuhalt;
-int scale, profile;
-Rectangle picr;
-Image *bg, *tmp;
-Mousectl *mc;
-int keys, paused, framestep, backup;
-QLock pauselock;
+int backup;
int savefd = -1, saveframes;
ulong clock;
-int savereq, loadreq;
u8int mbc, feat, mode;
extern MBC3Timer timer, timerl;
-void *
-emalloc(ulong sz)
+extern double TAU;
+void
+tauup(void)
{
- void *v;
-
- v = malloc(sz);
- if(v == nil)
- sysfatal("malloc: %r");
- setmalloctag(v, getcallerpc(&sz));
- return v;
+ TAU += 5000;
}
+void
+taudn(void)
+{
+ TAU -= 5000;
+}
void
writeback(void)
@@ -204,174 +198,14 @@
}
void
-screeninit(void)
-{
- Point p;
-
- p = divpt(addpt(screen->r.min, screen->r.max), 2);
- picr = (Rectangle){subpt(p, Pt(scale * PICW/2, scale * PICH/2)), addpt(p, Pt(scale * PICW/2, scale * PICH/2))};
- freeimage(tmp);
- tmp = allocimage(display, Rect(0, 0, scale * PICW, scale > 1 ? 1 : scale * PICH), XRGB32, scale > 1, 0);
- draw(screen, screen->r, bg, nil, ZP);
-}
-
-void
-keyproc(void *)
-{
- int fd, n, k;
- static char buf[256];
- char *s;
- Rune r;
- extern double TAU;
-
- fd = open("/dev/kbd", OREAD);
- if(fd < 0)
- sysfatal("open: %r");
- for(;;){
- if(buf[0] != 0){
- n = strlen(buf)+1;
- memmove(buf, buf+n, sizeof(buf)-n);
- }
- if(buf[0] == 0){
- n = read(fd, buf, sizeof(buf)-1);
- if(n <= 0)
- sysfatal("read /dev/kbd: %r");
- buf[n-1] = 0;
- buf[n] = 0;
- }
- if(buf[0] == 'c'){
- if(utfrune(buf, KF|5))
- savereq = 1;
- if(utfrune(buf, KF|6))
- loadreq = 1;
- if(utfrune(buf, Kdel)){
- close(fd);
- threadexitsall(nil);
- }
- if(utfrune(buf, 't'))
- trace = !trace;
- if(utfrune(buf, KF|9))
- TAU += 5000;
- if(utfrune(buf, KF|10))
- TAU -= 5000;
- }
- if(buf[0] != 'k' && buf[0] != 'K')
- continue;
- s = buf + 1;
- k = 0;
- while(*s != 0){
- s += chartorune(&r, s);
- switch(r){
- case Kdel: close(fd); threadexitsall(nil);
- case 'z': k |= 1<<5; break;
- case 'x': k |= 1<<4; break;
- case Kshift: k |= 1<<6; break;
- case 10: k |= 1<<7; break;
- case Kup: k |= 1<<2; break;
- case Kdown: k |= 1<<3; break;
- case Kleft: k |= 1<<1; break;
- case Kright: k |= 1<<0; break;
- case Kesc:
- if(paused)
- qunlock(&pauselock);
- else
- qlock(&pauselock);
- paused = !paused;
- break;
- case KF|1:
- if(paused){
- qunlock(&pauselock);
- paused=0;
- }
- framestep = !framestep;
- break;
- }
- }
- k &= ~(k << 1 & 0x0a | k >> 1 & 0x05);
- keys = k;
- }
-
-}
-
-void
-timing(void)
-{
- static int fcount;
- static vlong old;
- static char buf[32];
- vlong new;
-
- if(++fcount == 60)
- fcount = 0;
- else
- return;
- new = nsec();
- if(new != old)
- sprint(buf, "%6.2f%%", 1e11 / (new - old));
- else
- buf[0] = 0;
- draw(screen, rectaddpt(Rect(10, 10, 200, 30), screen->r.min), bg, nil, ZP);
- string(screen, addpt(screen->r.min, Pt(10, 10)), display->black, ZP, display->defaultfont, buf);
- old = nsec();
-}
-
-void
flush(void)
{
extern uchar pic[];
- Mouse m;
static vlong old, delta;
- vlong new, diff;
- if(nbrecvul(mc->resizec) > 0){
- if(getwindow(display, Refnone) < 0)
- sysfatal("resize failed: %r");
- screeninit();
- }
- while(nbrecv(mc->c, &m) > 0)
- ;
- if(scale == 1){
- loadimage(tmp, tmp->r, pic, PICW*PICH*4);
- draw(screen, picr, tmp, nil, ZP);
- } else {
- Rectangle r;
- uchar *s;
- int w;
-
- s = pic;
- r = picr;
- w = PICW*4*scale;
- while(r.min.y < picr.max.y){
- loadimage(tmp, tmp->r, s, w);
- s += w;
- r.max.y = r.min.y+scale;
- draw(screen, r, tmp, nil, ZP);
- r.min.y = r.max.y;
- }
- }
- flushimage(display, 1);
- if(profile)
- timing();
- if(audioout() < 0){
- new = nsec();
- diff = 0;
- if(old != 0){
- diff = BILLION/60 - (new - old) - delta;
- if(diff >= MILLION)
- sleep(diff/MILLION);
- }
- old = nsec();
- if(diff != 0){
- diff = (old - new) - (diff / MILLION) * MILLION;
- delta += (diff - delta) / 100;
- }
- }
- if(framestep){
- paused = 1;
- qlock(&pauselock);
- framestep = 0;
- }
-
+ flushmouse(1);
+ flushscreen();
+ flushaudio(audioout);
if(saveframes > 0 && --saveframes == 0)
flushback();
if(savereq){
@@ -430,20 +264,10 @@
int t;
colinit();
- scale = 1;
ARGBEGIN {
- case '2':
- scale = 2;
- break;
- case '3':
- scale = 3;
- break;
case 'a':
audioinit();
break;
- case 'T':
- profile++;
- break;
case 'c':
mode |= CGB;
break;
@@ -460,15 +284,17 @@
usage();
loadrom(argv[0]);
-
- if(initdraw(nil, nil, nil) < 0)
- sysfatal("initdraw: %r");
- mc = initmouse(nil, screen);
- if(mc == nil)
- sysfatal("initmouse: %r");
- proccreate(keyproc, nil, mainstacksize);
- bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
- screeninit();
+ initemu(PICW, PICH, 4, XRGB32, 1, nil);
+ regkey("b", 'z', 1<<5);
+ regkey("a", 'x', 1<<4);
+ regkey("control", Kshift, 1<<6);
+ regkey("start", '\n', 1<<7);
+ regkey("up", Kup, 1<<2);
+ regkey("down", Kdown, 1<<3);
+ regkey("left", Kleft, 1<<1);
+ regkey("right", Kright, 1<<0);
+ regkeyfn(KF|9, tauup);
+ regkeyfn(KF|10, taudn);
eventinit();
meminit();
--- a/sys/src/games/gb/mem.c
+++ b/sys/src/games/gb/mem.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
--- a/sys/src/games/gb/ppu.c
+++ b/sys/src/games/gb/ppu.c
@@ -1,11 +1,11 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
u8int ppustate, ppuy;
-u32int pic[PICW*PICH*3];
ulong hblclock, rendclock;
jmp_buf mainjmp, renderjmp;
static int cyc, done, ppux, ppux0;
@@ -63,7 +63,7 @@
}
ppux = 0;
ppux0 = 0;
- picp = pic + ppuy * PICW * scale;
+ picp = (u32int*)pic + ppuy * PICW * scale;
y = ppuy + reg[SCY] << 1 & 14;
ta = 0x1800 | reg[LCDC] << 7 & 0x400 | ppuy + reg[SCY] << 2 & 0x3e0 | reg[SCX] >> 3;
x = -(reg[SCX] & 7);
@@ -197,7 +197,7 @@
int x, x1;
u16int chr;
- picp = pic + ppuy * PICW * scale;
+ picp = (u32int*)pic + ppuy * PICW * scale;
for(q = spr; q < sprm; q++){
if(q->x <= ppux0 || q->x >= ppux + 8)
continue;
@@ -254,18 +254,31 @@
lineexpand(void)
{
u32int *picp, *p, *q, l;
- int i, s;
+ int i;
- s = scale;
- picp = pic + ppuy * PICW * s;
+ picp = (u32int*)pic + ppuy * PICW * scale;
p = picp + PICW;
q = picp + PICW * scale;
for(i = PICW; --i >= 0; ){
l = *--p;
- *--q = l;
- *--q = l;
- if(scale == 3)
- *--q = l;
+ switch(scale){
+ case 16: *--q = l;
+ case 15: *--q = l;
+ case 14: *--q = l;
+ case 13: *--q = l;
+ case 12: *--q = l;
+ case 11: *--q = l;
+ case 10: *--q = l;
+ case 9: *--q = l;
+ case 8: *--q = l;
+ case 7: *--q = l;
+ case 6: *--q = l;
+ case 5: *--q = l;
+ case 4: *--q = l;
+ case 3: *--q = l;
+ case 2: *--q = l;
+ case 1: *--q = l;
+ }
}
}
--- a/sys/src/games/gba/apu.c
+++ b/sys/src/games/gba/apu.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
@@ -350,7 +351,7 @@
void
audioinit(void)
{
- fd = open("/dev/audio", OWRITE);
+ fd = open("/dev/audio", OWRITE);
if(fd < 0)
sysfatal("open: %r");
sbufp = sbuf;
@@ -370,7 +371,7 @@
if(sbufp == sbuf)
return 0;
cl = clock;
- rc = write(fd, sbuf, (sbufp - sbuf) * 2);
+ rc = warp10 ? (sbufp - sbuf) * 2 : write(fd, sbuf, (sbufp - sbuf) * 2);
if(rc > 0)
sbufp -= (rc+1)/2;
if(sbufp < sbuf)
--- a/sys/src/games/gba/cpu.c
+++ b/sys/src/games/gba/cpu.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
@@ -34,7 +35,7 @@
int irq;
u32int instr0, instr1, pipel = -1;
-int cyc, trace;
+int cyc;
Var cpuvars[] = {
ARR(r), VAR(cpsr), VAR(spsr), ARR(saver), VAR(irq),
--- a/sys/src/games/gba/dat.h
+++ b/sys/src/games/gba/dat.h
@@ -1,10 +1,5 @@
-typedef char s8int;
-typedef short s16int;
-typedef long s32int;
-typedef vlong s64int;
+extern int cpuhalt;
-extern int cpuhalt, trace, keys;
-
extern u32int curpc;
extern int irq;
@@ -18,7 +13,6 @@
extern int hblank, ppuy;
extern int clock;
-extern int scale;
typedef struct Event Event;
struct Event {
@@ -141,8 +135,6 @@
KB = 1024,
BACKTYPELEN = 64,
HZ = 16777216,
- MILLION = 1000000,
- BILLION = 1000000000,
};
typedef struct Var Var;
--- a/sys/src/games/gba/gba.c
+++ b/sys/src/games/gba/gba.c
@@ -2,36 +2,19 @@
#include <libc.h>
#include <thread.h>
#include <draw.h>
-#include <mouse.h>
#include <keyboard.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
int cpuhalt;
-int scale, profile;
-Rectangle picr;
-Image *bg, *tmp;
-Mousectl *mc;
-int keys, paused, framestep, backup;
-QLock pauselock;
+Image *tmp;
+int backup;
int savefd, saveframes;
int clock;
-int savereq, loadreq;
char *biosfile = "/sys/games/lib/gbabios.bin";
-void *
-emalloc(ulong sz)
-{
- void *v;
-
- v = malloc(sz);
- if(v == nil)
- sysfatal("malloc: %r");
- setmalloctag(v, getcallerpc(&sz));
- return v;
-}
-
void
writeback(void)
{
@@ -212,172 +195,14 @@
}
void
-screeninit(void)
-{
- Point p;
-
- p = divpt(addpt(screen->r.min, screen->r.max), 2);
- picr = (Rectangle){subpt(p, Pt(scale * 120, scale * 80)), addpt(p, Pt(scale * 120, scale * 80))};
- freeimage(tmp);
- tmp = allocimage(display, Rect(0, 0, scale * 240, scale > 1 ? 1 : scale * 160), CHAN4(CIgnore, 1, CBlue, 5, CGreen, 5, CRed, 5), scale > 1, 0);
- draw(screen, screen->r, bg, nil, ZP);
-}
-
-void
-keyproc(void *)
-{
- int fd, n, k;
- static char buf[256];
- char *s;
- Rune r;
-
- fd = open("/dev/kbd", OREAD);
- if(fd < 0)
- sysfatal("open: %r");
- for(;;){
- if(buf[0] != 0){
- n = strlen(buf)+1;
- memmove(buf, buf+n, sizeof(buf)-n);
- }
- if(buf[0] == 0){
- n = read(fd, buf, sizeof(buf)-1);
- if(n <= 0)
- sysfatal("read /dev/kbd: %r");
- buf[n-1] = 0;
- buf[n] = 0;
- }
- if(buf[0] == 'c'){
- if(utfrune(buf, KF|5))
- savereq = 1;
- if(utfrune(buf, KF|6))
- loadreq = 1;
- if(utfrune(buf, Kdel)){
- close(fd);
- threadexitsall(nil);
- }
- if(utfrune(buf, 't'))
- trace = !trace;
- }
- if(buf[0] != 'k' && buf[0] != 'K')
- continue;
- s = buf + 1;
- k = 0;
- while(*s != 0){
- s += chartorune(&r, s);
- switch(r){
- case Kdel: close(fd); threadexitsall(nil);
- case 'z': k |= 1<<1; break;
- case 'x': k |= 1<<0; break;
- case 'a': k |= 1<<9; break;
- case 's': k |= 1<<8; break;
- case Kshift: k |= 1<<2; break;
- case 10: k |= 1<<3; break;
- case Kup: k |= 1<<6; break;
- case Kdown: k |= 1<<7; break;
- case Kleft: k |= 1<<5; break;
- case Kright: k |= 1<<4; break;
- case Kesc:
- if(paused)
- qunlock(&pauselock);
- else
- qlock(&pauselock);
- paused = !paused;
- break;
- case KF|1:
- if(paused){
- qunlock(&pauselock);
- paused=0;
- }
- framestep = !framestep;
- break;
- }
- }
- k &= ~(k << 1 & 0xa0 | k >> 1 & 0x50);
- keys = k;
- }
-
-}
-
-void
-timing(void)
-{
- static int fcount;
- static vlong old;
- static char buf[32];
- vlong new;
-
- if(++fcount == 60)
- fcount = 0;
- else
- return;
- new = nsec();
- if(new != old)
- sprint(buf, "%6.2f%%", 1e11 / (new - old));
- else
- buf[0] = 0;
- draw(screen, rectaddpt(Rect(10, 10, 200, 30), screen->r.min), bg, nil, ZP);
- string(screen, addpt(screen->r.min, Pt(10, 10)), display->black, ZP, display->defaultfont, buf);
- old = nsec();
-}
-
-void
flush(void)
{
- extern uchar pic[];
- Mouse m;
int x;
- static vlong old, delta;
- vlong new, diff;
- if(nbrecvul(mc->resizec) > 0){
- if(getwindow(display, Refnone) < 0)
- sysfatal("resize failed: %r");
- screeninit();
- }
- while(nbrecv(mc->c, &m) > 0)
- ;
- if(scale == 1){
- loadimage(tmp, tmp->r, pic, 240*160*2);
- draw(screen, picr, tmp, nil, ZP);
- } else {
- Rectangle r;
- uchar *s;
- int w;
+ flushmouse(1);
+ flushscreen();
+ flushaudio(audioout);
- s = pic;
- r = picr;
- w = 240*2*scale;
- while(r.min.y < picr.max.y){
- loadimage(tmp, tmp->r, s, w);
- s += w;
- r.max.y = r.min.y+scale;
- draw(screen, r, tmp, nil, ZP);
- r.min.y = r.max.y;
- }
- }
- flushimage(display, 1);
- if(profile)
- timing();
- if(audioout() < 0){
- new = nsec();
- diff = 0;
- if(old != 0){
- diff = BILLION/60 - (new - old) - delta;
- if(diff >= MILLION)
- sleep(diff/MILLION);
- }
- old = nsec();
- if(diff != 0){
- diff = (old - new) - (diff / MILLION) * MILLION;
- delta += (diff - delta) / 100;
- }
- }
- if(framestep){
- paused = 1;
- qlock(&pauselock);
- framestep = 0;
- }
-
if(saveframes > 0 && --saveframes == 0)
flushback();
@@ -395,7 +220,7 @@
void
usage(void)
{
- fprint(2, "usage: %s [-23aT] [-s savetype] [-b biosfile] rom\n", argv0);
+ fprint(2, "usage: %s [-aT] [-s savetype] [-b biosfile] rom\n", argv0);
exits("usage");
}
@@ -405,14 +230,7 @@
char *s;
int t;
- scale = 1;
ARGBEGIN {
- case '2':
- scale = 2;
- break;
- case '3':
- scale = 3;
- break;
case 'a':
audioinit();
break;
@@ -425,9 +243,6 @@
case 'b':
biosfile = strdup(EARGF(usage()));
break;
- case 'T':
- profile++;
- break;
default:
usage();
} ARGEND;
@@ -436,16 +251,17 @@
loadbios();
loadrom(argv[0]);
-
- if(initdraw(nil, nil, nil) < 0)
- sysfatal("initdraw: %r");
- mc = initmouse(nil, screen);
- if(mc == nil)
- sysfatal("initmouse: %r");
- proccreate(keyproc, nil, mainstacksize);
- bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
- screeninit();
-
+ 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);
+ regkey("l1", 'a', 1<<9);
+ regkey("r1", 's', 1<<8);
+ regkey("control", Kshift, 1<<2);
+ regkey("start", '\n', 1<<3);
+ regkey("up", Kup, 1<<6);
+ regkey("down", Kdown, 1<<7);
+ regkey("left", Kleft, 1<<5);
+ regkey("right", Kright, 1<<4);
eventinit();
memreset();
reset();
--- a/sys/src/games/gba/mem.c
+++ b/sys/src/games/gba/mem.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
--- a/sys/src/games/gba/ppu.c
+++ b/sys/src/games/gba/ppu.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
@@ -11,7 +12,6 @@
u32int pixcol[480];
u8int pixpri[480];
u8int pixwin[240];
-uchar pic[240*160*3*2];
int objalpha;
typedef struct bg bg;
@@ -643,40 +643,6 @@
}
void
-linecopy(void)
-{
- u32int *p;
- uchar *q;
- u16int *r;
- u16int v;
- union { u16int w; u8int b[2]; } u;
- int n;
-
- p = pixcol;
- q = pic + ppuy * 240 * 2 * scale;
- r = (u16int*)q;
- n = 240;
- while(n--){
- v = *p++;
- if(scale == 1){
- *q++ = v;
- *q++ = v >> 8;
- continue;
- }
- u.b[0] = v;
- u.b[1] = v >> 8;
- if(scale == 2){
- *r++ = u.w;
- *r++ = u.w;
- }else{
- *r++ = u.w;
- *r++ = u.w;
- *r++ = u.w;
- }
- }
-}
-
-void
syncppu(int x1)
{
int i;
@@ -725,6 +691,43 @@
}
void
+linecopy(u32int *p, int y)
+{
+ uchar *q;
+ u16int *r;
+ u16int v;
+ union { u16int w; u8int b[2]; } u;
+ int n;
+
+ q = pic + y * 240 * 2 * scale;
+ r = (u16int*)q;
+ n = 240;
+ while(n--){
+ v = *p++;
+ u.b[0] = v;
+ u.b[1] = v >> 8;
+ switch(scale){
+ case 16: *r++ = u.w;
+ case 15: *r++ = u.w;
+ case 14: *r++ = u.w;
+ case 13: *r++ = u.w;
+ case 12: *r++ = u.w;
+ case 11: *r++ = u.w;
+ case 10: *r++ = u.w;
+ case 9: *r++ = u.w;
+ case 8: *r++ = u.w;
+ case 7: *r++ = u.w;
+ case 6: *r++ = u.w;
+ case 5: *r++ = u.w;
+ case 4: *r++ = u.w;
+ case 3: *r++ = u.w;
+ case 2: *r++ = u.w;
+ default: *r++ = u.w;
+ }
+ }
+}
+
+void
hblanktick(void *)
{
extern Event evhblank;
@@ -755,7 +758,7 @@
}else{
syncppu(240);
if(ppuy < 160)
- linecopy();
+ linecopy(pixcol, ppuy);
addevent(&evhblank, 68*4);
hblank = 1;
if((stat & IRQHBLEN) != 0)
--- a/sys/src/games/md/cpu.c
+++ b/sys/src/games/md/cpu.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
@@ -19,7 +20,7 @@
u32int irqla[8];
u16int rS;
static u32int op;
-int trace, tim;
+int tim;
#define ra (r+8)
static void
--- a/sys/src/games/md/dat.h
+++ b/sys/src/games/md/dat.h
@@ -1,9 +1,4 @@
-typedef signed char s8int;
-typedef signed short s16int;
-typedef signed long s32int;
-
extern u32int curpc, irq;
-extern int trace, debug;
extern u8int reg[32];
extern u8int dma;
@@ -18,8 +13,6 @@
extern u32int sramctl, sram0, sram1;
extern int savefd, saveclock;
-extern int keys, scale;
-
extern u16int vram[32768], vsram[40];
extern u32int cramc[64];
extern u16int vdpstat;
@@ -82,8 +75,6 @@
RATE = 44100,
SAMPDIV = FREQ / RATE,
SAVEFREQ = FREQ / 4,
- MILLION = 1000 * 1000,
- BILLION = 1000 * 1000 * 1000,
};
enum {
--- a/sys/src/games/md/md.c
+++ b/sys/src/games/md/md.c
@@ -3,12 +3,10 @@
#include <thread.h>
#include <draw.h>
#include <keyboard.h>
-#include <mouse.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
-int debug;
-
u16int *prg;
int nprg;
u8int *sram;
@@ -15,17 +13,8 @@
u32int sramctl, nsram, sram0, sram1;
int savefd = -1;
-int keys;
-
int dmaclock, vdpclock, z80clock, audioclock, ymclock, saveclock;
-int scale, paused;
-QLock pauselock;
-Mousectl *mc;
-Channel *flushc;
-Rectangle picr;
-Image *tmp, *bg;
-
void
flushram(void)
{
@@ -114,146 +103,14 @@
}
void
-screeninit(void)
-{
- Point p;
-
- originwindow(screen, Pt(0, 0), screen->r.min);
- p = divpt(addpt(screen->r.min, screen->r.max), 2);
- picr = (Rectangle){subpt(p, Pt(scale * 160, scale * 112)), addpt(p, Pt(scale * 160, scale * 112))};
- if(tmp != nil) freeimage(tmp);
- tmp = allocimage(display, Rect(0, 0, scale * 320, scale > 1 ? 1 : scale * 224), XRGB32, scale > 1, 0);
- if(bg != nil) freeimage(bg);
- bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
- draw(screen, screen->r, bg, nil, ZP);
-}
-
-void
-screenproc(void *)
-{
- extern u8int pic[320*224*4*4];
- extern int intla;
- Rectangle r;
- uchar *s;
- int w, h;
-
- enum { AMOUSE, ARESIZE, AFLUSH, AEND };
- Alt a[AEND+1] = {
- { mc->c, nil, CHANRCV },
- { mc->resizec, nil, CHANRCV },
- { flushc, nil, CHANRCV },
- { nil, nil, CHANEND }
- };
-
- for(;;){
- switch(alt(a)){
- case ARESIZE:
- if(getwindow(display, Refnone) < 0)
- sysfatal("resize failed: %r");
- screeninit();
- /* wet floor */
- case AFLUSH:
- if(scale == 1){
- loadimage(tmp, tmp->r, pic, 320*224*4);
- draw(screen, picr, tmp, nil, ZP);
- }else{
- s = pic;
- r = picr;
- w = 320*4*scale;
- h = scale;
- if(intla && (h & 1) == 0)
- h >>= 1;
- while(r.min.y < picr.max.y){
- loadimage(tmp, tmp->r, s, w);
- s += w;
- r.max.y = r.min.y+h;
- draw(screen, r, tmp, nil, ZP);
- r.min.y = r.max.y;
- }
- }
- flushimage(display, 1);
- break;
- }
- }
-}
-
-void
-keyproc(void *)
-{
- int fd, n, k;
- static char buf[256];
- char *s;
- Rune r;
-
- fd = open("/dev/kbd", OREAD);
- if(fd < 0)
- sysfatal("open: %r");
- for(;;){
- if(buf[0] != 0){
- n = strlen(buf)+1;
- memmove(buf, buf+n, sizeof(buf)-n);
- }
- if(buf[0] == 0){
- n = read(fd, buf, sizeof(buf)-1);
- if(n <= 0)
- sysfatal("read /dev/kbd: %r");
- buf[n-1] = 0;
- buf[n] = 0;
- }
- if(buf[0] == 'c'){
- if(utfrune(buf, Kdel)){
- close(fd);
- threadexitsall(nil);
- }
- if(utfrune(buf, 't'))
- trace = !trace;
- }
- if(buf[0] != 'k' && buf[0] != 'K')
- continue;
- s = buf + 1;
- k = 0xc00;
- while(*s != 0){
- s += chartorune(&r, s);
- if(r >= '0' && r <= '9') debug = r - '0';
- switch(r){
- case Kdel: close(fd); threadexitsall(nil);
- case 'c': k |= 0x0020; break;
- case 'x': k |= 0x0010; break;
- case 'z': k |= 0x1000; break;
- case 10: k |= 0x2000; break;
- case Kup: k |= 0x0101; break;
- case Kdown: k |= 0x0202; break;
- case Kleft: k |= 0x0004; break;
- case Kright: k |= 0x0008; break;
- case Kesc:
- if(paused)
- qunlock(&pauselock);
- else
- qlock(&pauselock);
- paused = !paused;
- break;
- }
- }
- keys = ~k;
- }
-}
-
-void
threadmain(int argc, char **argv)
{
int t;
- scale = 1;
ARGBEGIN{
case 'a':
initaudio();
break;
- case '2':
- scale = 2;
- break;
- case '3':
- scale = 3;
- break;
default:
;
} ARGEND;
@@ -263,15 +120,15 @@
threadexitsall("usage");
}
loadrom(*argv);
- if(initdraw(nil, nil, argv0) < 0)
- sysfatal("initdraw: %r");
- flushc = chancreate(sizeof(ulong), 1);
- mc = initmouse(nil, screen);
- if(mc == nil)
- sysfatal("initmouse: %r");
- screeninit();
- proccreate(keyproc, nil, 8192);
- proccreate(screenproc, nil, 8192);
+ initemu(320, 224, 4, XRGB32, 1, nil);
+ regkey("a", 'c', 1<<5);
+ regkey("b", 'x', 1<<4);
+ regkey("y", 'z', 1<<12);
+ regkey("start", '\n', 1<<13);
+ regkey("up", Kup, 0x101);
+ regkey("down", Kdown, 0x202);
+ regkey("left", Kleft, 1<<2);
+ regkey("right", Kright, 1<<3);
cpureset();
vdpmode();
ymreset();
@@ -320,22 +177,7 @@
void
flush(void)
{
- static vlong old, delta;
- vlong new, diff;
-
- sendul(flushc, 1); /* flush screen */
- if(audioout() < 0){
- new = nsec();
- diff = 0;
- if(old != 0){
- diff = BILLION/60 - (new - old) - delta;
- if(diff >= MILLION)
- sleep(diff/MILLION);
- }
- old = nsec();
- if(diff != 0){
- diff = (old - new) - (diff / MILLION) * MILLION;
- delta += (diff - delta) / 100;
- }
- }
+ flushmouse(1);
+ flushscreen();
+ flushaudio(audioout);
}
--- a/sys/src/games/md/mem.c
+++ b/sys/src/games/md/mem.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
@@ -31,7 +32,7 @@
switch(a | 1){
case 0x0001: return 0xa0;
case 0x0003:
- v = keys;
+ v = ~(keys & 0xffff);
if((ctl[0] & 0x40) == 0)
v >>= 8;
return ctl[0] & 0xc0 | v & 0x3f;
--- a/sys/src/games/md/vdp.c
+++ b/sys/src/games/md/vdp.c
@@ -1,10 +1,10 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
-u8int pic[320*224*4*4];
u16int vdpstat = 0x3400;
int vdpx, vdpy, vdpyy, frame, intla;
u16int hctr;
@@ -29,32 +29,31 @@
static void
pixeldraw(int x, int y, int v)
{
- u8int *p;
- u32int *q;
- union { u32int w; u8int b[4]; } u;
+ u32int *p;
+ union { u32int l; u8int b[4]; } u;
- if(scale == 1){
- p = pic + (x + y * 320) * 4;
- p[0] = v >> 16;
- p[1] = v >> 8;
- p[2] = v;
- return;
- }
+ p = (u32int *)pic + (x + y * 320) * scale;
u.b[0] = v >> 16;
u.b[1] = v >> 8;
u.b[2] = v;
u.b[3] = 0;
- if(scale == 2){
- if(intla)
- y = y << 1 | frame;
- q = (u32int*)pic + (x + y * 320) * 2;
- q[0] = u.w;
- q[1] = u.w;
- }else{
- q = (u32int*)pic + (x + y * 320) * 3;
- q[0] = u.w;
- q[1] = u.w;
- q[2] = u.w;
+ switch(scale){
+ case 16: *p++ = u.l;
+ case 15: *p++ = u.l;
+ case 14: *p++ = u.l;
+ case 13: *p++ = u.l;
+ case 12: *p++ = u.l;
+ case 11: *p++ = u.l;
+ case 10: *p++ = u.l;
+ case 9: *p++ = u.l;
+ case 8: *p++ = u.l;
+ case 7: *p++ = u.l;
+ case 6: *p++ = u.l;
+ case 5: *p++ = u.l;
+ case 4: *p++ = u.l;
+ case 3: *p++ = u.l;
+ case 2: *p++ = u.l; /* intla ignored */
+ default: *p = u.l;
}
}
--- a/sys/src/games/md/ym.c
+++ b/sys/src/games/md/ym.c
@@ -1,11 +1,10 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
-extern int debug;
-
u8int ym[512];
enum {
MODE = 0x27,
@@ -436,7 +435,7 @@
return -1;
if(sbufp == sbuf)
return 0;
- rc = write(fd, sbuf, (sbufp - sbuf) * 2);
+ rc = warp10 ? (sbufp - sbuf) * 2 : write(fd, sbuf, (sbufp - sbuf) * 2);
if(rc > 0)
sbufp -= (rc+1)/2;
if(sbufp < sbuf)
--- a/sys/src/games/nes/apu.c
+++ b/sys/src/games/nes/apu.c
@@ -2,6 +2,7 @@
#include <libc.h>
#include <thread.h>
#include <draw.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
@@ -279,7 +280,7 @@
return -1;
if(sbufp == sbuf)
return 0;
- rc = write(fd, sbuf, (sbufp - sbuf) * 2);
+ rc = warp10 ? (sbufp - sbuf) * 2 : write(fd, sbuf, (sbufp - sbuf) * 2);
if(rc > 0)
sbufp -= (rc+1)/2;
if(sbufp < sbuf)
--- a/sys/src/games/nes/dat.h
+++ b/sys/src/games/nes/dat.h
@@ -6,7 +6,7 @@
extern u8int ppusx, vrambuf;
extern int mirr, ppux, ppuy, odd, vramlatch, keylatch, keylatch2;
-extern int map, scale, mmc3hack, oflag;
+extern int map, mmc3hack, oflag;
extern uchar *prg, *chr;
extern int nprg, nchr, map, chrram;
@@ -13,7 +13,7 @@
extern u8int apuseq, apuctr[13];
extern u16int dmcaddr, dmccnt;
-extern int keys, keys2, clock, ppuclock, apuclock, dmcclock, dmcfreq, saveclock, paused;
+extern int clock, ppuclock, apuclock, dmcclock, dmcfreq, saveclock;
extern void (*mapper[])(int, u8int);
@@ -78,8 +78,6 @@
enum {
FREQ = 21477272,
- MILLION = 1000000,
- BILLION = 1000000000,
APUDIV = 89490,
RATE = 44100,
SAMPDIV = FREQ / RATE,
--- a/sys/src/games/nes/mem.c
+++ b/sys/src/games/nes/mem.c
@@ -2,6 +2,7 @@
#include <libc.h>
#include <thread.h>
#include <draw.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
--- a/sys/src/games/nes/nes.c
+++ b/sys/src/games/nes/nes.c
@@ -2,8 +2,8 @@
#include <libc.h>
#include <draw.h>
#include <thread.h>
-#include <mouse.h>
#include <keyboard.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
@@ -10,14 +10,9 @@
extern uchar ppuram[16384];
int nprg, nchr, map, chrram;
uchar *prg, *chr;
-int scale;
-Rectangle picr;
-Image *tmp, *bg;
int clock, ppuclock, apuclock, dmcclock, dmcfreq, sampclock, msgclock, saveclock;
-Mousectl *mc;
-int keys, keys2, paused, savereq, loadreq, oflag, savefd = -1;
+int oflag, savefd = -1;
int mirr;
-QLock pauselock;
void
message(char *fmt, ...)
@@ -122,139 +117,18 @@
mapper[map](INIT, 0);
}
-extern int trace;
-
void
-joyproc(void *)
-{
- char *s, *down[9];
- static char buf[64];
- int n, k, j;
-
- j = 1;
- for(;;){
- n = read(0, buf, sizeof(buf) - 1);
- if(n <= 0)
- sysfatal("read: %r");
- buf[n] = 0;
- n = getfields(buf, down, nelem(down), 1, " ");
- k = 0;
- for(n--; n >= 0; n--){
- s = down[n];
- if(strcmp(s, "joy1") == 0)
- j = 1;
- else if(strcmp(s, "joy2") == 0)
- j = 2;
- else if(strcmp(s, "a") == 0)
- k |= 1<<0;
- else if(strcmp(s, "b") == 0)
- k |= 1<<1;
- else if(strcmp(s, "control") == 0)
- k |= 1<<2;
- else if(strcmp(s, "start") == 0)
- k |= 1<<3;
- else if(strcmp(s, "up") == 0)
- k |= 1<<4;
- else if(strcmp(s, "down") == 0)
- k |= 1<<5;
- else if(strcmp(s, "left") == 0)
- k |= 1<<6;
- else if(strcmp(s, "right") == 0)
- k |= 1<<7;
- }
- if(j == 2)
- keys2 = k;
- else
- keys = k;
- }
-}
-
-void
-keyproc(void *)
-{
- int fd, n, k;
- static char buf[256];
- char *s;
- Rune r;
-
- fd = open("/dev/kbd", OREAD);
- if(fd < 0)
- sysfatal("open: %r");
- for(;;){
- if(buf[0] != 0){
- n = strlen(buf)+1;
- memmove(buf, buf+n, sizeof(buf)-n);
- }
- if(buf[0] == 0){
- n = read(fd, buf, sizeof(buf)-1);
- if(n <= 0)
- sysfatal("read /dev/kbd: %r");
- buf[n-1] = 0;
- buf[n] = 0;
- }
- if(buf[0] == 'c'){
- if(utfrune(buf, Kdel)){
- close(fd);
- threadexitsall(nil);
- }
- if(utfrune(buf, KF|5))
- savereq = 1;
- if(utfrune(buf, KF|6))
- loadreq = 1;
- if(utfrune(buf, 't'))
- trace ^= 1;
- }
- if(buf[0] != 'k' && buf[0] != 'K')
- continue;
- s = buf + 1;
- k = 0;
- while(*s != 0){
- s += chartorune(&r, s);
- switch(r){
- case Kdel: close(fd); threadexitsall(nil);
- case 'x': k |= 1<<0; break;
- case 'z': k |= 1<<1; break;
- case Kshift: k |= 1<<2; break;
- case 10: k |= 1<<3; break;
- case Kup: k |= 1<<4; break;
- case Kdown: k |= 1<<5; break;
- case Kleft: k |= 1<<6; break;
- case Kright: k |= 1<<7; break;
- case Kesc:
- if(paused)
- qunlock(&pauselock);
- else
- qlock(&pauselock);
- paused = !paused;
- break;
- }
- }
- keys = k;
- }
-}
-
-void
threadmain(int argc, char **argv)
{
- int t, h, sflag;
- Point p;
+ int t, sflag;
- scale = 1;
- h = 240;
sflag = 0;
ARGBEGIN {
case 'a':
initaudio();
break;
- case '2':
- scale = 2;
- break;
- case '3':
- scale = 3;
- break;
case 'o':
oflag = 1;
- h -= 16;
break;
case 's':
sflag = 1;
@@ -269,20 +143,16 @@
threadexitsall("usage");
}
loadrom(argv[0], sflag);
- if(initdraw(nil, nil, nil) < 0)
- sysfatal("initdraw: %r");
- mc = initmouse(nil, screen);
- if(mc == nil)
- sysfatal("initmouse: %r");
- proccreate(joyproc, nil, 8192);
- proccreate(keyproc, nil, 8192);
- originwindow(screen, Pt(0, 0), screen->r.min);
- p = divpt(addpt(screen->r.min, screen->r.max), 2);
- picr = (Rectangle){subpt(p, Pt(scale * 128, scale * h/2)), addpt(p, Pt(scale * 128, scale * h/2))};
- tmp = allocimage(display, Rect(0, 0, scale * 256, scale * h), XRGB32, 0, 0);
- bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
- draw(screen, screen->r, bg, nil, ZP);
-
+ initemu(256, 240 - oflag * 16, 4, XRGB32, 1, nil);
+ regkey("b", 'z', 1<<1);
+ regkey("a", 'x', 1<<0);
+ regkey("control", Kshift, 1<<2);
+ regkey("start", '\n', 1<<3);
+ regkey("up", Kup, 1<<4);
+ regkey("down", Kdown, 1<<5);
+ regkey("left", Kleft, 1<<6);
+ regkey("right", Kright, 1<<7);
+
pc = memread(0xFFFC) | memread(0xFFFD) << 8;
rP = FLAGI;
dmcfreq = 12 * 428;
@@ -324,7 +194,8 @@
if(msgclock > 0){
msgclock -= t;
if(msgclock <= 0){
- draw(screen, screen->r, bg, nil, ZP);
+ extern Image *bg;
+ draw(screen, screen->r, bg, nil, ZP);
msgclock = 0;
}
}
--- a/sys/src/games/nes/ppu.c
+++ b/sys/src/games/nes/ppu.c
@@ -3,18 +3,17 @@
#include <thread.h>
#include <draw.h>
#include <mouse.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
int ppuy, ppux, odd;
-uchar pic[256*240*4*9];
static void
pixel(int x, int y, int val, int back)
{
- int Y;
union { u8int c[4]; u32int l; } u;
- u32int *p, l;
+ u32int *p;
static u8int palred[64] = {
0x7C, 0x00, 0x00, 0x44, 0x94, 0xA8, 0xA8, 0x88,
0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -50,25 +49,24 @@
u.c[1] = palgreen[val];
u.c[2] = palred[val];
u.c[3] = back ? 0 : 0xFF;
- l = u.l;
- if(scale == 3){
- p = ((u32int*)pic) + y * 3 * 3 * 256 + 3 * x;
- for(Y = 0; Y < 3; Y++){
- *p++ = l;
- *p++ = l;
- *p = l;
- p += 3 * 256 - 2;
- }
- }else if(scale == 2){
- p = ((u32int*)pic) + y * 2 * 2 * 256 + 2 * x;
- *p++ = l;
- *p = l;
- p += 2 * 256 - 1;
- *p++ = l;
- *p = l;
- }else{
- p = ((u32int*)pic) + y * 256 + x;
- *p = l;
+ p = (u32int *)pic + y * 256 * scale + x * scale;
+ switch(scale){
+ case 16: *p++ = u.l;
+ case 15: *p++ = u.l;
+ case 14: *p++ = u.l;
+ case 13: *p++ = u.l;
+ case 12: *p++ = u.l;
+ case 11: *p++ = u.l;
+ case 10: *p++ = u.l;
+ case 9: *p++ = u.l;
+ case 8: *p++ = u.l;
+ case 7: *p++ = u.l;
+ case 6: *p++ = u.l;
+ case 5: *p++ = u.l;
+ case 4: *p++ = u.l;
+ case 3: *p++ = u.l;
+ case 2: *p++ = u.l;
+ default: *p = u.l;
}
}
@@ -75,7 +73,7 @@
static int
iscolor(int x, int y)
{
- return pic[y * scale * scale * 256 * 4 + x * scale * 4 + 3] != 0;
+ return pic[(scale * 4) * (y * 256 + x) + 3] != 0;
}
static int
@@ -252,52 +250,9 @@
static void
flush(void)
{
- extern Rectangle picr;
- extern Image *tmp, *bg;
- extern Mousectl *mc;
- static vlong old, delta;
- vlong new, diff;
- Mouse m;
- Point p;
- int h;
-
- h = 240;
- if(oflag)
- h -= 16;
- while(nbrecv(mc->c, &m) > 0)
- ;
- if(nbrecvul(mc->resizec) > 0){
- if(getwindow(display, Refnone) < 0)
- sysfatal("resize failed: %r");
- p = divpt(addpt(screen->r.min, screen->r.max), 2);
- picr = (Rectangle){subpt(p, Pt(scale * 128, scale * h/2)), addpt(p, Pt(scale * 128, scale * h/2))};
- if(bg->chan != screen->chan){
- freeimage(bg);
- bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
- }
- draw(screen, screen->r, bg, nil, ZP);
- }
- if(screen->chan != tmp->chan || !rectinrect(picr, screen->r)){
- loadimage(tmp, tmp->r, pic + oflag*8*256*4*scale*scale, 256*h*4*scale*scale);
- draw(screen, picr, tmp, nil, ZP);
- }else
- loadimage(screen, picr, pic + oflag*8*256*4*scale*scale, 256*h*4*scale*scale);
- flushimage(display, 1);
- memset(pic, sizeof pic, 0);
- if(audioout() < 0){
- new = nsec();
- diff = 0;
- if(old != 0){
- diff = BILLION/60 - (new - old) - delta;
- if(diff >= MILLION)
- sleep(diff/MILLION);
- }
- old = nsec();
- if(diff != 0){
- diff = (old - new) - (diff / MILLION) * MILLION;
- delta += (diff - delta) / 100;
- }
- }
+ flushmouse(1);
+ flushscreen();
+ flushaudio(audioout);
}
void
--- a/sys/src/games/snes/cpu.c
+++ b/sys/src/games/snes/cpu.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
@@ -639,8 +640,6 @@
rS = 0x100;
rP = 0x35;
}
-
-int trace;
int
cpustep(void)
--- a/sys/src/games/snes/dat.h
+++ b/sys/src/games/snes/dat.h
@@ -1,14 +1,10 @@
-typedef signed char s8int;
-typedef signed short s16int;
-
extern u8int rP, dma, nmi, irq, emu, wai;
extern u16int rA, rX, rY, rS, rD, pc;
extern u32int rPB, rDB, curpc, hdma;
-extern int trace;
extern uchar *prg, *sram;
extern int nprg, nsram, hirom;
-extern u32int keys, keylatch, lastkeys;
+extern u32int keylatch, lastkeys;
extern u8int reg[32768], mem[131072], spcmem[65536], vram[65536], oam[544];
extern u16int cgram[256], vramlatch;
extern u8int mdr, mdr1, mdr2;
@@ -25,7 +21,7 @@
extern u16int dspcounter, noise;
extern int ppuclock, spcclock, dspclock, stimerclock, cpupause;
-extern int battery, saveclock, scale, mouse;
+extern int battery, saveclock, mouse;
enum {
FLAGC = 1<<0,
--- a/sys/src/games/snes/dsp.c
+++ b/sys/src/games/snes/dsp.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
@@ -77,7 +78,7 @@
return -1;
if(sbufp == sbuf)
return 0;
- rc = write(fd, sbuf, (sbufp - sbuf) * 2);
+ rc = warp10 ? (sbufp - sbuf) * 2 : write(fd, sbuf, (sbufp - sbuf) * 2);
if(rc > 0)
sbufp -= (rc+1)/2;
if(sbufp < sbuf)
--- a/sys/src/games/snes/mem.c
+++ b/sys/src/games/snes/mem.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
--- a/sys/src/games/snes/ppu.c
+++ b/sys/src/games/snes/ppu.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
@@ -8,7 +9,6 @@
static u8int mode, bright, pixelpri[2], hires;
static u32int pixelcol[2];
u16int vtime = 0x1ff, htime = 0x1ff, subcolor;
-uchar pic[256*239*2*3];
u16int hofs[5], vofs[5];
s16int m7[6];
@@ -40,30 +40,31 @@
static void
pixeldraw(int x, int y, u16int v, int s)
{
- uchar *p;
- u16int *q;
+ u16int *p;
union { u16int w; u8int b[2]; } u;
if(bright != 0xf && s >= 0)
v = darken(v);
- if(scale == 1){
- p = pic + (x + y * 256) * 2;
- p[0] = v;
- p[1] = v >> 8;
- return;
- }
+ p = (u16int *)pic + (x + y * 256) * scale;
u.b[0] = v;
u.b[1] = v >> 8;
- if(scale == 2){
- q = (u16int*)pic + (x + y * 256) * 2;
- if(s < 1)
- q[0] = u.w;
- q[1] = u.w;
- }else{
- q = (u16int*)pic + (x + y * 256) * 3;
- q[0] = u.w;
- q[1] = u.w;
- q[2] = u.w;
+ switch(scale){
+ case 16: *p++ = u.w;
+ case 15: *p++ = u.w;
+ case 14: *p++ = u.w;
+ case 13: *p++ = u.w;
+ case 12: *p++ = u.w;
+ case 11: *p++ = u.w;
+ case 10: *p++ = u.w;
+ case 9: *p++ = u.w;
+ case 8: *p++ = u.w;
+ case 7: *p++ = u.w;
+ case 6: *p++ = u.w;
+ case 5: *p++ = u.w;
+ case 4: *p++ = u.w;
+ case 3: *p++ = u.w;
+ case 2: if(s < 1) *p++ = u.w;
+ default: *p = u.w;
}
}
--- a/sys/src/games/snes/snes.c
+++ b/sys/src/games/snes/snes.c
@@ -4,7 +4,7 @@
#include <draw.h>
#include <keyboard.h>
#include <mouse.h>
-#include <ctype.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
@@ -11,14 +11,9 @@
uchar *prg, *sram;
int nprg, nsram, hirom, battery;
-int ppuclock, spcclock, dspclock, stimerclock, saveclock, msgclock, paused, perfclock, cpupause;
-Mousectl *mc;
-Channel *flushc, *msgc;
-QLock pauselock;
-u32int keys;
-int savefd, scale, profile, mouse, loadreq, savereq;
-Rectangle picr;
-Image *tmp, *bg;
+int ppuclock, spcclock, dspclock, stimerclock, saveclock, msgclock, cpupause;
+Channel *msgc;
+int savefd, mouse;
void
flushram(void)
@@ -114,187 +109,13 @@
}
void
-keyproc(void *)
-{
- int fd, n, k;
- static char buf[256];
- char *s;
- Rune r;
-
- fd = open("/dev/kbd", OREAD);
- if(fd < 0)
- sysfatal("open: %r");
- for(;;){
- if(buf[0] != 0){
- n = strlen(buf)+1;
- memmove(buf, buf+n, sizeof(buf)-n);
- }
- if(buf[0] == 0){
- n = read(fd, buf, sizeof(buf)-1);
- if(n <= 0)
- sysfatal("read /dev/kbd: %r");
- buf[n-1] = 0;
- buf[n] = 0;
- }
- if(buf[0] == 'c'){
- if(utfrune(buf, KF|5))
- savereq = 1;
- if(utfrune(buf, KF|6))
- loadreq = 1;
- if(utfrune(buf, Kdel)){
- close(fd);
- threadexitsall(nil);
- }
- if(utfrune(buf, 't'))
- trace = !trace;
- }
- if(buf[0] != 'k' && buf[0] != 'K')
- continue;
- s = buf + 1;
- k = 0xffff;
- while(*s != 0){
- s += chartorune(&r, s);
- switch(r){
- case Kdel: close(fd); threadexitsall(nil);
- case 'z': k |= 1<<31; break;
- case 'x': k |= 1<<23; break;
- case 'a': k |= 1<<30; break;
- case 's': k |= 1<<22; break;
- case 'q': k |= 1<<21; break;
- case 'w': k |= 1<<20; break;
- case Kshift: k |= 1<<29; break;
- case 10: k |= 1<<28; break;
- case Kup: k |= 1<<27; break;
- case Kdown: k |= 1<<26; break;
- case Kleft: k |= 1<<25; break;
- case Kright: k |= 1<<24; break;
- case Kesc:
- if(paused)
- qunlock(&pauselock);
- else
- qlock(&pauselock);
- paused = !paused;
- break;
- }
- }
- if(!mouse)
- keys = k;
- }
-}
-
-void
-screeninit(void)
-{
- Point p;
-
- p = divpt(addpt(screen->r.min, screen->r.max), 2);
- picr = (Rectangle){subpt(p, Pt(scale * 128, scale * 112)), addpt(p, Pt(scale * 128, scale * 127))};
- if(tmp != nil) freeimage(tmp);
- tmp = allocimage(display, Rect(0, 0, scale * 256, scale > 1 ? 1 : scale * 239), RGB15, scale > 1, 0);
- if(bg != nil) freeimage(bg);
- bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
- draw(screen, screen->r, bg, nil, ZP);
-}
-
-void
-screenproc(void *)
-{
- extern uchar pic[256*239*2*3];
- char *s;
- Mouse m;
- Point p;
-
- enum { AMOUSE, ARESIZE, AFLUSH, AMSG, AEND };
- Alt a[AEND+1] = {
- { mc->c, &m, CHANRCV },
- { mc->resizec, nil, CHANRCV },
- { flushc, nil, CHANRCV },
- { msgc, &s, CHANRCV },
- { nil, nil, CHANEND }
- };
-
- for(;;){
- switch(alt(a)){
- case AMOUSE:
- if(mouse && ptinrect(m.xy, picr)){
- p = subpt(m.xy, picr.min);
- p.x /= scale;
- p.y /= scale;
- keys = keys & 0xff3f0000 | p.x | p.y << 8;
- if((m.buttons & 1) != 0)
- keys |= 1<<22;
- if((m.buttons & 4) != 0)
- keys |= 1<<23;
- if((m.buttons & 2) != 0)
- lastkeys = keys;
- }
- break;
- case ARESIZE:
- if(getwindow(display, Refnone) < 0)
- sysfatal("resize failed: %r");
- screeninit();
- /* wet floor */
- case AFLUSH:
- if(scale == 1){
- loadimage(tmp, tmp->r, pic, 256*239*2);
- draw(screen, picr, tmp, nil, ZP);
- } else {
- Rectangle r;
- uchar *s;
- int w;
-
- s = pic;
- r = picr;
- w = 256*2*scale;
- while(r.min.y < picr.max.y){
- loadimage(tmp, tmp->r, s, w);
- s += w;
- r.max.y = r.min.y+scale;
- draw(screen, r, tmp, nil, ZP);
- r.min.y = r.max.y;
- }
- }
- flushimage(display, 1);
- break;
- case AMSG:
- draw(screen, rectaddpt(Rect(10, 10, 200, 30), screen->r.min), bg, nil, ZP);
- if(s != nil){
- string(screen, addpt(screen->r.min, Pt(10, 10)), display->black, ZP,
- display->defaultfont, s);
- free(s);
- }
- break;
- }
- }
-}
-
-void
-timing(void)
-{
- static vlong old;
- vlong new;
-
- new = nsec();
- if(new != old)
- message("%6.2f%%", 1e11 / (new - old));
- old = nsec();
-}
-
-void
threadmain(int argc, char **argv)
{
int t;
extern u16int pc;
- scale = 1;
hirom = -1;
ARGBEGIN {
- case '2':
- scale = 2;
- break;
- case '3':
- scale = 3;
- break;
case 'a':
audioinit();
break;
@@ -308,9 +129,6 @@
case 'h':
hirom++;
break;
- case 'T':
- profile++;
- break;
default:
goto usage;
} ARGEND;
@@ -321,16 +139,20 @@
threadexitsall("usage");
}
loadrom(argv[0]);
- if(initdraw(nil, nil, argv0) < 0)
- sysfatal("initdraw: %r");
- flushc = chancreate(sizeof(ulong), 1);
- msgc = chancreate(sizeof(char*), 0);
- mc = initmouse(nil, screen);
- if(mc == nil)
- sysfatal("initmouse: %r");
- screeninit();
- proccreate(keyproc, 0, 8192);
- proccreate(screenproc, 0, 8192);
+ initemu(256, 239, 2, RGB15, !mouse, nil);
+ regkey("b", 'z', 1<<31);
+ regkey("a", 'x', 1<<23);
+ regkey("y", 'a', 1<<30);
+ regkey("x", 's', 1<<22);
+ regkey("l1", 'q', 1<<21);
+ regkey("r1", 'w', 1<<20);
+ regkey("control", Kshift, 1<<29);
+ regkey("start", '\n', 1<<28);
+ regkey("up", Kup, 1<<27);
+ regkey("down", Kdown, 1<<26);
+ regkey("left", Kleft, 1<<25);
+ regkey("right", Kright, 1<<24);
+ msgc = chancreate(sizeof(char*), 1);
loadbat(argv[0]);
cpureset();
memreset();
@@ -358,7 +180,6 @@
stimerclock += t;
ppuclock += t;
dspclock += t;
- perfclock -= t;
while(ppuclock >= 4){
ppustep();
@@ -386,10 +207,6 @@
msgclock = 0;
}
}
- if(profile && perfclock <= 0){
- perfclock = FREQ;
- timing();
- }
}
}
@@ -396,8 +213,37 @@
void
flush(void)
{
- sendul(flushc, 1); /* flush screen */
- audioout();
+ char *s;
+ Mouse m;
+ Point p;
+
+ extern Rectangle picr;
+ extern Mousectl *mc;
+ flushmouse(!mouse);
+ while(nbrecv(mc->c, &m) > 0){
+ if(ptinrect(m.xy, picr)){
+ p = subpt(m.xy, picr.min);
+ p.x /= scale;
+ p.y /= scale;
+ keys = keys & 0xff3f0000 | p.x | p.y << 8;
+ if((m.buttons & 1) != 0)
+ keys |= 1<<22;
+ if((m.buttons & 4) != 0)
+ keys |= 1<<23;
+ if((m.buttons & 2) != 0)
+ lastkeys = keys;
+ }
+ }
+ flushscreen();
+ while(nbrecv(msgc, &s) > 0){
+ if(s != nil){
+ string(screen, addpt(screen->r.min, Pt(10, 10)), display->black, ZP,
+ display->defaultfont, s);
+ free(s);
+ flushimage(display, 1);
+ }
+ }
+ flushaudio(audioout);
}
void
--- a/sys/src/games/snes/spc.c
+++ b/sys/src/games/snes/spc.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <emu.h>
#include "dat.h"
#include "fns.h"
--- /dev/null
+++ b/sys/src/libemu/emu.c
@@ -1,0 +1,316 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <keyboard.h>
+#include <mouse.h>
+#include <emu.h>
+
+typedef struct Kfn Kfn;
+
+u64int keys, keys2;
+int trace, paused;
+int savereq, loadreq;
+QLock pauselock;
+int scale, warp10;
+uchar *pic;
+Rectangle picr;
+Mousectl *mc;
+Image *bg;
+
+static int profile, framestep;
+static int vwdx, vwdy, vwbpp;
+static ulong vwchan;
+static Image *fb;
+
+struct Kfn{
+ Rune r;
+ int k;
+ char joyk[16];
+ void(*fn)(void);
+ Kfn *n;
+};
+Kfn kfn, kkn;
+
+void *
+emalloc(ulong sz)
+{
+ void *v;
+
+ v = mallocz(sz, 1);
+ if(v == nil)
+ sysfatal("malloc: %r");
+ setmalloctag(v, getcallerpc(&sz));
+ return v;
+}
+
+static void
+joyproc(void *)
+{
+ char *s, *down[9];
+ static char buf[64];
+ int n, k, j;
+ Kfn *kp;
+
+ j = 1;
+
+ for(;;){
+ n = read(0, buf, sizeof(buf) - 1);
+ if(n <= 0)
+ sysfatal("read: %r");
+ buf[n] = 0;
+ n = getfields(buf, down, nelem(down), 1, " ");
+ k = 0;
+ for(n--; n >= 0; n--){
+ s = down[n];
+ if(strcmp(s, "joy1") == 0)
+ j = 1;
+ else if(strcmp(s, "joy2") == 0)
+ j = 2;
+ for(kp=kkn.n; kp!=nil; kp=kp->n){
+ if(strcmp(kp->joyk, s) == 0)
+ k |= kp->k;
+ }
+ }
+ if(j == 2)
+ keys2 = k;
+ else
+ keys = k;
+ }
+}
+
+static void
+keyproc(void *)
+{
+ int fd, n, k;
+ static char buf[256];
+ char *s;
+ Rune r;
+ Kfn *kp;
+
+ fd = open("/dev/kbd", OREAD);
+ if(fd < 0)
+ sysfatal("open: %r");
+ for(;;){
+ if(buf[0] != 0){
+ n = strlen(buf)+1;
+ memmove(buf, buf+n, sizeof(buf)-n);
+ }
+ if(buf[0] == 0){
+ n = read(fd, buf, sizeof(buf)-1);
+ if(n <= 0)
+ sysfatal("read /dev/kbd: %r");
+ buf[n-1] = 0;
+ buf[n] = 0;
+ }
+ if(buf[0] == 'c'){
+ if(utfrune(buf, Kdel)){
+ close(fd);
+ threadexitsall(nil);
+ }
+ if(utfrune(buf, KF|5))
+ savereq = 1;
+ if(utfrune(buf, KF|6))
+ loadreq = 1;
+ if(utfrune(buf, KF|12))
+ profile ^= 1;
+ if(utfrune(buf, 't'))
+ trace = !trace;
+ for(kp=kfn.n; kp!=nil; kp=kp->n){
+ if(utfrune(buf, kp->r))
+ kp->fn();
+ }
+ }
+ if(buf[0] != 'k' && buf[0] != 'K')
+ continue;
+ s = buf + 1;
+ k = 0;
+ while(*s != 0){
+ s += chartorune(&r, s);
+ switch(r){
+ case Kdel: close(fd); threadexitsall(nil);
+ case Kesc:
+ if(paused)
+ qunlock(&pauselock);
+ else
+ qlock(&pauselock);
+ paused = !paused;
+ break;
+ case KF|1:
+ if(paused){
+ qunlock(&pauselock);
+ paused=0;
+ }
+ framestep = !framestep;
+ break;
+ case '`':
+ warp10 = !warp10;
+ break;
+ }
+ for(kp=kkn.n; kp!=nil; kp=kp->n){
+ if(utfrune(buf, kp->r))
+ k |= kp->k;
+ }
+ }
+ k &= ~(k << 1 & 0xa0 | k >> 1 & 0x50);
+ keys = k;
+ }
+}
+
+static void
+timing(void)
+{
+ static int fcount;
+ static vlong old;
+ static char buf[32];
+ vlong new;
+
+ if(++fcount == 60)
+ fcount = 0;
+ else
+ return;
+ new = nsec();
+ if(new != old)
+ sprint(buf, "%6.2f%%", 1e11 / (new - old));
+ else
+ buf[0] = 0;
+ draw(screen, rectaddpt(Rect(10, 10, vwdx-40, 30), screen->r.min), bg, nil, ZP);
+ string(screen, addpt(screen->r.min, Pt(10, 10)), display->black, ZP, display->defaultfont, buf);
+ old = nsec();
+}
+
+static void
+screeninit(void)
+{
+ Point p;
+
+ scale = Dx(screen->r) / vwdx;
+ if(scale <= 0)
+ scale = 1;
+ else if(scale > 16)
+ scale = 16;
+ p = divpt(addpt(screen->r.min, screen->r.max), 2);
+ picr = Rpt(subpt(p, Pt(scale * vwdx/2, scale * vwdy/2)),
+ addpt(p, Pt(scale * vwdx/2, scale * vwdy/2)));
+ freeimage(fb);
+ fb = allocimage(display, Rect(0, 0, scale * vwdx, scale > 1 ? 1 : scale * vwdy),
+ vwchan, scale > 1, 0);
+ free(pic);
+ pic = emalloc(vwdx * vwdy * vwbpp * scale);
+ draw(screen, screen->r, bg, nil, ZP);
+}
+
+void
+flushmouse(int discard)
+{
+ Mouse m;
+
+ if(nbrecvul(mc->resizec) > 0){
+ if(getwindow(display, Refnone) < 0)
+ sysfatal("resize failed: %r");
+ screeninit();
+ }
+ if(discard)
+ while(nbrecv(mc->c, &m) > 0)
+ ;
+}
+
+void
+flushscreen(void)
+{
+ flushmouse(1);
+ if(scale == 1){
+ loadimage(fb, fb->r, pic, vwdx * vwdy * vwbpp);
+ draw(screen, picr, fb, nil, ZP);
+ } else {
+ Rectangle r;
+ uchar *s;
+ int w;
+
+ s = pic;
+ r = picr;
+ w = vwdx * vwbpp * scale;
+ while(r.min.y < picr.max.y){
+ loadimage(fb, fb->r, s, w);
+ s += w;
+ r.max.y = r.min.y+scale;
+ draw(screen, r, fb, nil, ZP);
+ r.min.y = r.max.y;
+ }
+ }
+ flushimage(display, 1);
+ if(profile)
+ timing();
+}
+
+void
+flushaudio(int (*audioout)(void))
+{
+ static vlong old, delta;
+ vlong new, diff;
+
+ if(audioout == nil || audioout() < 0 && !warp10){
+ new = nsec();
+ diff = 0;
+ if(old != 0){
+ diff = BILLION/60 - (new - old) - delta;
+ if(diff >= MILLION)
+ sleep(diff/MILLION);
+ }
+ old = nsec();
+ if(diff > 0){
+ diff = (old - new) - (diff / MILLION) * MILLION;
+ delta += (diff - delta) / 100;
+ }
+ }
+ if(framestep){
+ paused = 1;
+ qlock(&pauselock);
+ framestep = 0;
+ }
+}
+
+void
+regkeyfn(Rune r, void (*fn)(void))
+{
+ Kfn *kp;
+
+ for(kp=&kfn; kp->n!=nil; kp=kp->n)
+ ;
+ kp->n = emalloc(sizeof *kp);
+ kp->n->r = r;
+ kp->n->fn = fn;
+}
+
+void
+regkey(char *joyk, Rune r, int k)
+{
+ Kfn *kp;
+
+ for(kp=&kkn; kp->n!=nil; kp=kp->n)
+ ;
+ kp->n = emalloc(sizeof *kp);
+ strncpy(kp->n->joyk, joyk, sizeof(kp->n->joyk)-1);
+ kp->n->r = r;
+ kp->n->k = k;
+}
+
+void
+initemu(int dx, int dy, int bpp, ulong chan, int dokey, void(*kproc)(void*))
+{
+ vwdx = dx;
+ vwdy = dy;
+ vwchan = chan;
+ vwbpp = bpp;
+ if(initdraw(nil, nil, nil) < 0)
+ sysfatal("initdraw: %r");
+ mc = initmouse(nil, screen);
+ if(mc == nil)
+ sysfatal("initmouse: %r");
+ if(dokey)
+ proccreate(kproc != nil ? kproc : keyproc, nil, mainstacksize);
+ if(kproc == nil)
+ proccreate(joyproc, nil, mainstacksize*2);
+ bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
+ screeninit();
+}
--- /dev/null
+++ b/sys/src/libemu/mkfile
@@ -1,0 +1,20 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libemu.a
+
+OFILES=\
+ emu.$O\
+
+HFILES=\
+ /sys/include/draw.h\
+ /sys/include/emu.h\
+ /sys/include/mouse.h\
+ /sys/include/keyboard.h
+
+UPDATE=\
+ mkfile\
+ $HFILES\
+ ${OFILES:%.$O=%.c}\
+ ${LIB:/$objtype/%=/386/%}\
+
+</sys/src/cmd/mksyslib