9front - general discussion about 9front
 help / color / mirror / Atom feed
* Re: [9front] proposal: move common code from md, nes, snes, gb, gba, c64 to a libemu
@ 2018-05-12 19:49 qwx
  0 siblings, 0 replies; 7+ messages in thread
From: qwx @ 2018-05-12 19:49 UTC (permalink / raw)
  To: 9front

Solid copy, I'll deal with these issues as soon as I can tomorrow.

Thanks again,

qwx


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [9front] proposal: move common code from md, nes, snes, gb, gba, c64 to a libemu
@ 2018-05-13  5:04 qwx
  0 siblings, 0 replies; 7+ messages in thread
From: qwx @ 2018-05-13  5:04 UTC (permalink / raw)
  To: 9front

Libemu has been removed as a library, and moved instead as a common file
compiled against the emulators.

For anyone that has built it, please rm /$objtype/lib/libemu.a.

Sorry for the confusion,

qwx


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [9front] proposal: move common code from md, nes, snes, gb, gba, c64 to a libemu
@ 2018-05-12 18:19 cinap_lenrek
  0 siblings, 0 replies; 7+ messages in thread
From: cinap_lenrek @ 2018-05-12 18:19 UTC (permalink / raw)
  To: 9front

what about /sys/include/ape/u.h? should probably also get the signed
types. but check that collides with anything.

--
cinap


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [9front] proposal: move common code from md, nes, snes, gb, gba, c64 to a libemu
@ 2018-05-12 18:00 cinap_lenrek
  0 siblings, 0 replies; 7+ messages in thread
From: cinap_lenrek @ 2018-05-12 18:00 UTC (permalink / raw)
  To: 9front

do we really need this as a library in /sys/src? this stuff is a single
.c file that could as well be just compiled from some other directory
from the emulators. just look how jpg(1) does it. also the name is stupid.

--
cinap


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [9front] proposal: move common code from md, nes, snes, gb, gba, c64 to a libemu
@ 2018-05-11 20:18 qwx
  0 siblings, 0 replies; 7+ messages in thread
From: qwx @ 2018-05-11 20:18 UTC (permalink / raw)
  To: 9front

[-- Attachment #1: Type: text/plain, Size: 275 bytes --]

Hello,

Attached is an amended version of libemu.

Changes over last:
- fullspeed emulation toggled with ` instead of space
- signed typedefs assumed in /$objtype/include/u.h,
  see previous patch sent
- all used keys are now registered with regkey() on startup

Thanks,

qwx

[-- Attachment #2: mail.diff --]
[-- Type: text/plain, Size: 65202 bytes --]

diff -r abfb40232967 sys/man/1/nintendo
--- a/sys/man/1/nintendo	Fri May 11 16:16:37 2018 +0200
+++ b/sys/man/1/nintendo	Fri May 11 22:11:38 2018 +0200
@@ -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:
diff -r abfb40232967 sys/man/1/sega
--- a/sys/man/1/sega	Fri May 11 16:16:37 2018 +0200
+++ b/sys/man/1/sega	Fri May 11 22:11:38 2018 +0200
@@ -4,14 +4,14 @@
 .SH SYNOPSIS
 .B games/md
 [
-.B -23a
+.B -a
 ]
 .I romfile
 .SH DESCRIPTION
 .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
diff -r abfb40232967 sys/man/2/emu
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/man/2/emu	Fri May 11 22:11:38 2018 +0200
@@ -0,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.
diff -r abfb40232967 sys/src/games/c64/c64.c
--- a/sys/src/games/c64/c64.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/c64/c64.c	Fri May 11 22:11:38 2018 +0200
@@ -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);
 }
diff -r abfb40232967 sys/src/games/c64/cpu.c
--- a/sys/src/games/c64/cpu.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/c64/cpu.c	Fri May 11 22:11:38 2018 +0200
@@ -1,5 +1,6 @@
 #include <u.h>
 #include <libc.h>
+#include <emu.h>
 #include "dat.h"
 #include "fns.h"
 
@@ -289,8 +290,6 @@
 	rP |= FLAGI;
 }
 
-int trace;
-
 void
 step(void)
 {
diff -r abfb40232967 sys/src/games/c64/dat.h
--- a/sys/src/games/c64/dat.h	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/c64/dat.h	Fri May 11 22:11:38 2018 +0200
@@ -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,
diff -r abfb40232967 sys/src/games/c64/mem.c
--- a/sys/src/games/c64/mem.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/c64/mem.c	Fri May 11 22:11:38 2018 +0200
@@ -1,6 +1,7 @@
 #include <u.h>
 #include <libc.h>
 #include <thread.h>
+#include <emu.h>
 #include "dat.h"
 #include "fns.h"
 
diff -r abfb40232967 sys/src/games/c64/vic.c
--- a/sys/src/games/c64/vic.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/c64/vic.c	Fri May 11 22:11:38 2018 +0200
@@ -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;
 }
diff -r abfb40232967 sys/src/games/gb/apu.c
--- a/sys/src/games/gb/apu.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/gb/apu.c	Fri May 11 22:11:38 2018 +0200
@@ -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)
diff -r abfb40232967 sys/src/games/gb/cpu.c
--- a/sys/src/games/gb/cpu.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/gb/cpu.c	Fri May 11 22:11:38 2018 +0200
@@ -1,6 +1,7 @@
 #include <u.h>
 #include <libc.h>
 #include <thread.h>
+#include <emu.h>
 #include "dat.h"
 #include "fns.h"
 
diff -r abfb40232967 sys/src/games/gb/dat.h
--- a/sys/src/games/gb/dat.h	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/gb/dat.h	Fri May 11 22:11:38 2018 +0200
@@ -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
 };
 
diff -r abfb40232967 sys/src/games/gb/gb.c
--- a/sys/src/games/gb/gb.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/gb/gb.c	Fri May 11 22:11:38 2018 +0200
@@ -2,34 +2,28 @@
 #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
@@ -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();
diff -r abfb40232967 sys/src/games/gb/mem.c
--- a/sys/src/games/gb/mem.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/gb/mem.c	Fri May 11 22:11:38 2018 +0200
@@ -1,6 +1,7 @@
 #include <u.h>
 #include <libc.h>
 #include <thread.h>
+#include <emu.h>
 #include "dat.h"
 #include "fns.h"
 
diff -r abfb40232967 sys/src/games/gb/ppu.c
--- a/sys/src/games/gb/ppu.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/gb/ppu.c	Fri May 11 22:11:38 2018 +0200
@@ -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;
+		}
 	}
 }
 
diff -r abfb40232967 sys/src/games/gba/apu.c
--- a/sys/src/games/gba/apu.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/gba/apu.c	Fri May 11 22:11:38 2018 +0200
@@ -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)
diff -r abfb40232967 sys/src/games/gba/cpu.c
--- a/sys/src/games/gba/cpu.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/gba/cpu.c	Fri May 11 22:11:38 2018 +0200
@@ -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),
diff -r abfb40232967 sys/src/games/gba/dat.h
--- a/sys/src/games/gba/dat.h	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/gba/dat.h	Fri May 11 22:11:38 2018 +0200
@@ -1,9 +1,4 @@
-typedef char s8int;
-typedef short s16int;
-typedef long s32int;
-typedef vlong s64int;
-
-extern int cpuhalt, trace, keys;
+extern int cpuhalt;
 
 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;
diff -r abfb40232967 sys/src/games/gba/gba.c
--- a/sys/src/games/gba/gba.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/gba/gba.c	Fri May 11 22:11:38 2018 +0200
@@ -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();
diff -r abfb40232967 sys/src/games/gba/mem.c
--- a/sys/src/games/gba/mem.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/gba/mem.c	Fri May 11 22:11:38 2018 +0200
@@ -1,6 +1,7 @@
 #include <u.h>
 #include <libc.h>
 #include <thread.h>
+#include <emu.h>
 #include "dat.h"
 #include "fns.h"
 
diff -r abfb40232967 sys/src/games/gba/ppu.c
--- a/sys/src/games/gba/ppu.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/gba/ppu.c	Fri May 11 22:11:38 2018 +0200
@@ -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)
diff -r abfb40232967 sys/src/games/md/cpu.c
--- a/sys/src/games/md/cpu.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/md/cpu.c	Fri May 11 22:11:38 2018 +0200
@@ -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
diff -r abfb40232967 sys/src/games/md/dat.h
--- a/sys/src/games/md/dat.h	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/md/dat.h	Fri May 11 22:11:38 2018 +0200
@@ -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 {
diff -r abfb40232967 sys/src/games/md/md.c
--- a/sys/src/games/md/md.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/md/md.c	Fri May 11 22:11:38 2018 +0200
@@ -3,29 +3,18 @@
 #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;
 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);
 }
diff -r abfb40232967 sys/src/games/md/mem.c
--- a/sys/src/games/md/mem.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/md/mem.c	Fri May 11 22:11:38 2018 +0200
@@ -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;
diff -r abfb40232967 sys/src/games/md/vdp.c
--- a/sys/src/games/md/vdp.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/md/vdp.c	Fri May 11 22:11:38 2018 +0200
@@ -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;
 	}
 }
 
diff -r abfb40232967 sys/src/games/md/ym.c
--- a/sys/src/games/md/ym.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/md/ym.c	Fri May 11 22:11:38 2018 +0200
@@ -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)
diff -r abfb40232967 sys/src/games/nes/apu.c
--- a/sys/src/games/nes/apu.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/nes/apu.c	Fri May 11 22:11:38 2018 +0200
@@ -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)
diff -r abfb40232967 sys/src/games/nes/dat.h
--- a/sys/src/games/nes/dat.h	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/nes/dat.h	Fri May 11 22:11:38 2018 +0200
@@ -6,14 +6,14 @@
 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;
 
 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,
diff -r abfb40232967 sys/src/games/nes/mem.c
--- a/sys/src/games/nes/mem.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/nes/mem.c	Fri May 11 22:11:38 2018 +0200
@@ -2,6 +2,7 @@
 #include <libc.h>
 #include <thread.h>
 #include <draw.h>
+#include <emu.h>
 #include "dat.h"
 #include "fns.h"
 
diff -r abfb40232967 sys/src/games/nes/nes.c
--- a/sys/src/games/nes/nes.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/nes/nes.c	Fri May 11 22:11:38 2018 +0200
@@ -2,22 +2,17 @@
 #include <libc.h>
 #include <draw.h>
 #include <thread.h>
-#include <mouse.h>
 #include <keyboard.h>
+#include <emu.h>
 #include "dat.h"
 #include "fns.h"
 
 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;
 			}
 		}
diff -r abfb40232967 sys/src/games/nes/ppu.c
--- a/sys/src/games/nes/ppu.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/nes/ppu.c	Fri May 11 22:11:38 2018 +0200
@@ -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,32 +49,31 @@
 	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;
 	}
 }
 
 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
diff -r abfb40232967 sys/src/games/snes/cpu.c
--- a/sys/src/games/snes/cpu.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/snes/cpu.c	Fri May 11 22:11:38 2018 +0200
@@ -1,6 +1,7 @@
 #include <u.h>
 #include <libc.h>
 #include <thread.h>
+#include <emu.h>
 #include "dat.h"
 #include "fns.h"
 
@@ -640,8 +641,6 @@
 	rP = 0x35;
 }
 
-int trace;
-
 int
 cpustep(void)
 {
diff -r abfb40232967 sys/src/games/snes/dat.h
--- a/sys/src/games/snes/dat.h	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/snes/dat.h	Fri May 11 22:11:38 2018 +0200
@@ -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,
diff -r abfb40232967 sys/src/games/snes/dsp.c
--- a/sys/src/games/snes/dsp.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/snes/dsp.c	Fri May 11 22:11:38 2018 +0200
@@ -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)
diff -r abfb40232967 sys/src/games/snes/mem.c
--- a/sys/src/games/snes/mem.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/snes/mem.c	Fri May 11 22:11:38 2018 +0200
@@ -1,6 +1,7 @@
 #include <u.h>
 #include <libc.h>
 #include <thread.h>
+#include <emu.h>
 #include "dat.h"
 #include "fns.h"
 
diff -r abfb40232967 sys/src/games/snes/ppu.c
--- a/sys/src/games/snes/ppu.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/snes/ppu.c	Fri May 11 22:11:38 2018 +0200
@@ -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;
 	}
 }
 
diff -r abfb40232967 sys/src/games/snes/snes.c
--- a/sys/src/games/snes/snes.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/snes/snes.c	Fri May 11 22:11:38 2018 +0200
@@ -4,21 +4,16 @@
 #include <draw.h>
 #include <keyboard.h>
 #include <mouse.h>
-#include <ctype.h>
+#include <emu.h>
 #include "dat.h"
 #include "fns.h"
 
 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,18 +207,43 @@
 				msgclock = 0;
 			}
 		}
-		if(profile && perfclock <= 0){
-			perfclock = FREQ;
-			timing();
-		}
 	}
 }
 
 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
diff -r abfb40232967 sys/src/games/snes/spc.c
--- a/sys/src/games/snes/spc.c	Fri May 11 16:16:37 2018 +0200
+++ b/sys/src/games/snes/spc.c	Fri May 11 22:11:38 2018 +0200
@@ -1,6 +1,7 @@
 #include <u.h>
 #include <libc.h>
 #include <thread.h>
+#include <emu.h>
 #include "dat.h"
 #include "fns.h"
 
diff -r abfb40232967 sys/src/libemu/emu.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/libemu/emu.c	Fri May 11 22:11:38 2018 +0200
@@ -0,0 +1,315 @@
+#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();
+}
diff -r abfb40232967 sys/src/libemu/mkfile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/libemu/mkfile	Fri May 11 22:11:38 2018 +0200
@@ -0,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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [9front] proposal: move common code from md, nes, snes, gb, gba, c64 to a libemu
@ 2018-05-11 11:08 qwx
  0 siblings, 0 replies; 7+ messages in thread
From: qwx @ 2018-05-11 11:08 UTC (permalink / raw)
  To: 9front

> > - (?) added toggling fullspeed emulation when audio is enabled
> by dropping samples?
> 
> i don't like that this is enabled with space.
> i would prefer some more remote key, like `.
> 
> personally i think this is a misfeature. in other emulators i always find 
> myself using it too much.

I agree, ` is better.  I'd like to keep this just because it lets me
blaze through some extremely annoying unskippable sequences in many
games (for convenience rather than a hack).  Final verdict?


> > - (?) dynamic rescaling from x1 to x16 for larger screens;
> >  removed -2, -3
> i think i've stated before i don't like this very much.
> but i'll accept it begrudgingly, i think :).

Thank you :)


> > - emalloc, as often reproduced in the emulators;
> >  might be better off in libc or nowhere
> moving emalloc to libc has been discussed many times and vetoed every
> time.
> not sure what you mean by 'nowhere'.

I recall the debates;  I just wasn't sure if it was worth placing in the
library anyway, or to just leave it in the emulators that need it.  I'm
leaning towards just removing it from libemu, it's a bit extraneous.


> > - the signed typedefs might be better placed in /$objtype/include/u.h
> yes i would like that.
> we've postponed doing this too many times already.

So, can we?  I don't remember why it was never done.  What objections
were there?


> > - clumsy; input bit vectors are semi-arbitrary and adaptation in some
> >  emulators is fairly bad; worse examples: snes and md
> > - regkeyfn is not very useful as is
> i would like this to be fixed.
> why not just register all keys?
> something like
>  	regkey("left", Kleft, 1);
> to register it as bit 1 in the input bitvector.

True, this makes more sense.  I'll do it.


> > - can we merge 2600 even if it's buggy?
> yes.

I will then do it after all is said and done for libemu.


Thanks!

qwx


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [9front] proposal: move common code from md, nes, snes, gb, gba, c64 to a libemu
  2018-05-11  7:39 qwx
@ 2018-05-11  9:57 ` Julius Schmidt
  0 siblings, 0 replies; 7+ messages in thread
From: Julius Schmidt @ 2018-05-11  9:57 UTC (permalink / raw)
  To: 9front

it's basically sound.

some thoughts:

> - (?) added toggling fullspeed emulation when audio is enabled
by dropping samples?

i don't like that this is enabled with space.
i would prefer some more remote key, like `.

personally i think this is a misfeature. in other emulators i always find 
myself using it too much.

> - (?) dynamic rescaling from x1 to x16 for larger screens;
>  removed -2, -3
i think i've stated before i don't like this very much.
but i'll accept it begrudgingly, i think :).

> - profiling: now toggled at runtime, removed -T
good

> - fix for speed up after pausing emulation with audio turned off
reliable control of emulator speed might need control theory...

> - emalloc, as often reproduced in the emulators;
>  might be better off in libc or nowhere
moving emalloc to libc has been discussed many times and vetoed every
time.
not sure what you mean by 'nowhere'.

> - the signed typedefs might be better placed in /$objtype/include/u.h
yes i would like that.
we've postponed doing this too many times already.

> - another hacky library
who cares.

> - clumsy; input bit vectors are semi-arbitrary and adaptation in some
>  emulators is fairly bad; worse examples: snes and md
> - regkeyfn is not very useful as is
i would like this to be fixed.
why not just register all keys?
something like
 	regkey("left", Kleft, 1);
to register it as bit 1 in the input bitvector.

> - snes hires and md intla are ignored when scaling
that seems like it might be too confusing with automatic scaling.

> - maybe user programs should never care about scaling, and the library
>  should instead do some turbooptimized duff's device on screen flush
not sure.

> - can we merge 2600 even if it's buggy?
yes.


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2018-05-13  5:04 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-12 19:49 [9front] proposal: move common code from md, nes, snes, gb, gba, c64 to a libemu qwx
  -- strict thread matches above, loose matches on Subject: below --
2018-05-13  5:04 qwx
2018-05-12 18:19 cinap_lenrek
2018-05-12 18:00 cinap_lenrek
2018-05-11 20:18 qwx
2018-05-11 11:08 qwx
2018-05-11  7:39 qwx
2018-05-11  9:57 ` [9front] " Julius Schmidt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).