ref: a25b7e46e00d7bb9769c1f48339322bd4a95bc7e
parent: aebec5673978f3b4cf6e84d7578a3bd3b5916421
author: qwx <qwx@sciops.net>
date: Mon Feb 24 13:15:41 EST 2025
add eui: experimental fixes and scaling
--- /dev/null
+++ b/sys/src/games/2600/tia.c
@@ -1,0 +1,195 @@
+#include <u.h>
+#include <libc.h>
+#include "../eui.h"
+#include "dat.h"
+#include "fns.h"
+
+int ppux=1, ppuy;
+int col, pri;
+u8int p0x, p1x, m0x, m1x, blx;
+u16int coll;
+u8int disp;
+int p0difc;
+int bwmod = 1<<3;
+
+enum {
+ SRCPF,
+ SRCP0,
+ SRCP1,
+ SRCM0,
+ SRCM1,
+ SRCBL,
+};
+
+static void
+pixeldraw(u8int v)
+{
+ u32int c;
+ union { u32int l; u8int c[4]; } u;
+ u32int *p;
+ static u32int col[] = {
+ 0x000000, 0x404040, 0x6C6C6C, 0x909090, 0xB0B0B0, 0xC8C8C8, 0xDCDCDC, 0xECECEC,
+ 0x444400, 0x646410, 0x848424, 0xA0A034, 0xB8B840, 0xD0D050, 0xE8E85C, 0xFCFC68,
+ 0x702800, 0x844414, 0x985C28, 0xAC783C, 0xBC8C4C, 0xCCA05C, 0xDCB468, 0xECC878,
+ 0x841800, 0x983418, 0xAC5030, 0xC06848, 0xD0805C, 0xE09470, 0xECA880, 0xFCBC94,
+ 0x880000, 0x9C2020, 0xB03C3C, 0xC05858, 0xD07070, 0xE08888, 0xECA0A0, 0xFCB4B4,
+ 0x78005C, 0x8C2074, 0xA03C88, 0xB0589C, 0xC070B0, 0xD084C0, 0xDC9CD0, 0xECB0E0,
+ 0x480078, 0x602090, 0x783CA4, 0x8C58B8, 0xA070CC, 0xB484DC, 0xC49CEC, 0xD4B0FC,
+ 0x140084, 0x302098, 0x4C3CAC, 0x6858C0, 0x7C70D0, 0x9488E0, 0xA8A0EC, 0xBCB4FC,
+ 0x000088, 0x1C209C, 0x3840B0, 0x505CC0, 0x6874D0, 0x7C8CE0, 0x90A4EC, 0xA4B8FC,
+ 0x00187C, 0x1C3890, 0x3854A8, 0x5070BC, 0x6888CC, 0x7C9CDC, 0x90B4EC, 0xA4C8FC,
+ 0x002C5C, 0x1C4C78, 0x386890, 0x5084AC, 0x689CC0, 0x7CB4D4, 0x90CCE8, 0xA4E0FC,
+ 0x003C2C, 0x1C5C48, 0x387C64, 0x509C80, 0x68B494, 0x7CD0AC, 0x90E4C0, 0xA4FCD4,
+ 0x003C00, 0x205C20, 0x407C40, 0x5C9C5C, 0x74B474, 0x8CD08C, 0xA4E4A4, 0xB8FCB8,
+ 0x143800, 0x345C1C, 0x507C38, 0x6C9850, 0x84B468, 0x9CCC7C, 0xB4E490, 0xC8FCA4,
+ 0x2C3000, 0x4C501C, 0x687034, 0x848C4C, 0x9CA864, 0xB4C078, 0xCCD488, 0xE0EC9C,
+ 0x442800, 0x644818, 0x846830, 0xA08444, 0xB89C58, 0xD0B46C, 0xE8CC7C, 0xFCE08C,
+ };
+
+ c = col[v >> 1];
+ u.c[0] = c;
+ u.c[1] = c >> 8;
+ u.c[2] = c >> 16;
+ u.c[3] = 0xff;
+ p = (u32int *)pic + ppuy * PICW + ppux * 2;
+ p[0] = p[1] = u.l;
+}
+
+static void
+pixel(u8int v, int p, int s)
+{
+ if(p > pri){
+ col = v;
+ pri = p;
+ }
+ disp |= 1<<s;
+}
+
+static void
+playfield(void)
+{
+ int x, p;
+ u8int c;
+
+ x = ppux / 4;
+ if(x >= 20)
+ if((reg[CTRLPF] & 1) != 0)
+ x = 39 - x;
+ else
+ x = x - 20;
+ if(x < 4){
+ if((reg[PF0] & 0x10<<x) == 0)
+ return;
+ }else if(x < 12){
+ if((reg[PF1] & 0x800>>x) == 0)
+ return;
+ }else
+ if((reg[PF2] & 1<<x-12) == 0)
+ return;
+ if((reg[CTRLPF] & 6) == 2)
+ if(ppux < 80){
+ c = reg[COLUP0];
+ p = 3;
+ }else{
+ c = reg[COLUP1];
+ p = 2;
+ }
+ else{
+ c = reg[COLUPF];
+ p = (reg[CTRLPF] & 4) + 1;
+ }
+ pixel(c, p, SRCPF);
+}
+
+static void
+player(int n)
+{
+ u8int c;
+ int x;
+
+ c = reg[GRP0 + n];
+ x = ppux - (n ? p1x : p0x);
+ if(x < 0)
+ return;
+ switch(reg[NUSIZ0 + n] & 7){
+ default: if(x >= 8) return; break;
+ case 1: if(x >= 8 && (x < 16 || x >= 24)) return; break;
+ case 2: if(x >= 8 && (x < 32 || x >= 40)) return; break;
+ case 3: if(x >= 40 || ((x & 15) >= 8)) return; break;
+ case 4: if(x >= 8 && (x < 64 || x >= 72)) return; break;
+ case 5: if(x >= 16) return; x >>= 1; break;
+ case 6: if(x >= 72 || ((x & 31) >= 8)) return; break;
+ case 7: if(x >= 32) return; x >>= 2; break;
+ }
+ x &= 7;
+ if((reg[REFP0 + n] & 8) == 0)
+ x ^= 7;
+ if((c & 1<<x) == 0)
+ return;
+ c = reg[COLUP0 + n];
+ pixel(c, 3 - n, SRCP0 + n);
+}
+
+static void
+missile(int n)
+{
+ int x;
+
+ x = ppux - (n ? m1x : m0x);
+ if((reg[RESMP0 + n] & 2) != 0){
+ if(n)
+ m1x = p1x;
+ else
+ m0x = p0x;
+ return;
+ }
+ if(x < 0 || x >= 1<<(reg[NUSIZ0] >> 4 & 3) || (reg[ENAM0 + n] & 2) == 0)
+ return;
+ pixel(reg[COLUP0 + n], 3 - n, SRCM0 + n);
+}
+
+static void
+ball(void)
+{
+ int x;
+
+ x = ppux - blx;
+ if(x < 0 || x >= 1<<(reg[CTRLPF] >> 4 & 3) || (reg[ENABL] & 2) == 0)
+ return;
+ pixel(reg[COLUPF], (reg[CTRLPF] & 4) + 1, SRCBL);
+}
+
+void
+tiastep(void)
+{
+ static u16int colltab[64] = {
+ 0x0000, 0x0000, 0x0000, 0x0020, 0x0000, 0x0080, 0x8000, 0x80a0,
+ 0x0000, 0x0200, 0x0001, 0x0221, 0x0002, 0x0282, 0x8003, 0x82a3,
+ 0x0000, 0x0800, 0x0008, 0x0828, 0x0004, 0x0884, 0x800c, 0x88ac,
+ 0x4000, 0x4a00, 0x4009, 0x4a29, 0x4006, 0x4a86, 0xc00f, 0xcaaf,
+ 0x0000, 0x2000, 0x0010, 0x2030, 0x0040, 0x20c0, 0x8050, 0xa0f0,
+ 0x0100, 0x2300, 0x0111, 0x2331, 0x0142, 0x23c2, 0x8153, 0xa3f3,
+ 0x0400, 0x2c00, 0x0418, 0x2c38, 0x0444, 0x2cc4, 0x845c, 0xacfc,
+ 0x4500, 0x6f00, 0x4519, 0x6f39, 0x4546, 0x6fc6, 0xc55f, 0xefff,
+ };
+
+ if(ppuy < PICH && ppux < 160){
+ col = reg[COLUBK];
+ pri = 0;
+ disp = 0;
+ playfield();
+ player(0);
+ player(1);
+ missile(0);
+ missile(1);
+ ball();
+ coll |= colltab[disp];
+ pixeldraw(col);
+ }
+ if(ppux == 160)
+ nrdy = 0;
+ if(++ppux == 228){
+ ppuy++;
+ ppux = 0;
+ }
+}
--- /dev/null
+++ b/sys/src/games/c64/vic.c
@@ -1,0 +1,369 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "../eui.h"
+#include "dat.h"
+#include "fns.h"
+
+int region, picidx;
+u16int ppux, ppuy, lastx, wrapx, maxy, lvis, rvis, uvis, dvis, picw, pich, lbord, rbord, ubord, dbord, spr0;
+u16int vc, vcbase, vmli;
+u8int badln, rc, displ, fract, visreg, hbord, vbord, rbord0, lbord0;
+u16int chrp[40];
+u64int pxs, npxs, npxs0, opxs;
+u8int fg;
+
+typedef struct spr spr;
+enum {
+ SPRFDMA = 1,
+ SPRFYEX = 2,
+ SPRFDISP = 4,
+};
+struct spr {
+ u8int flags;
+ u32int data;
+ u8int mc, mcbase, dp;
+ u16int x;
+} sp[8];
+
+void
+bordset(void)
+{
+ int r, c;
+
+ r = (reg[CTRL1] & RSEL) != 0;
+ c = (reg[CTRL2] & CSEL) != 0;
+ lbord = c ? 0x14 : 0x1c;
+ lbord0 = c ? 0xf0 : 0xe0;
+ rbord = c ? 0x154 : 0x14c;
+ rbord0 = c ? 0x0f : 0x3f;
+ ubord = r ? 0x33 : 0x37;
+ dbord = r ? 0xfb : 0xf7;
+ if((reg[CTRL1] & DEN) == 0)
+ ubord = -1;
+}
+
+void
+vicreset(void)
+{
+ switch(region){
+ case NTSC0:
+ lastx = 0x1fc;
+ wrapx = 0x19c;
+ maxy = 262;
+ picw = 412;
+ pich = 234;
+ spr0 = 0x16c;
+ lvis = 0x1e4;
+ rvis = 0x184;
+ uvis = 41;
+ dvis = 13;
+ break;
+ case NTSC:
+ lastx = 0x1fc;
+ wrapx = 0x19c;
+ maxy = 263;
+ picw = 419;
+ pich = 235;
+ spr0 = 0x174;
+ lvis = 0x1e4;
+ rvis = 0x18c;
+ uvis = 41;
+ dvis = 13;
+ break;
+ case PAL:
+ lastx = 0x1f4;
+ wrapx = 0x194;
+ maxy = 312;
+ picw = 404;
+ pich = 284;
+ spr0 = 0x164;
+ lvis = 0x1dc;
+ rvis = 0x17c;
+ uvis = 16;
+ dvis = 300;
+ break;
+ }
+ ppux = 4;
+ ppuy = 0;
+ bordset();
+}
+
+void
+pixeldraw(u64int p, int n)
+{
+ 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 c;
+ u32int *q;
+
+ q = (u32int *)pic + picidx;
+ for(i = 0; i < n; i++){
+ c = p >> 56;
+ p <<= 8;
+ u.c[0] = cb[c];
+ u.c[1] = cg[c];
+ u.c[2] = cr[c];
+ u.c[3] = 0;
+ *q++ = u.l;
+ }
+ picidx += n;
+}
+
+void
+pixels(u8int d, u16int c)
+{
+ u8int c0, c1, c2, n;
+ int i;
+
+ npxs0 = npxs;
+ npxs = 0;
+ switch((reg[CTRL1] & (ECM|BMM) | reg[CTRL2] & MCM) >> 4){
+ case 0:
+ c0 = c >> 8;
+ normal:
+ fg = d;
+ for(i = 0; i < 8; i++){
+ npxs = npxs << 8 | ((d & 0x80) != 0 ? c0 : reg[BG0]);
+ d <<= 1;
+ }
+ break;
+ case 1:
+ c0 = c >> 8 & 7;
+ if((c & 0x800) == 0)
+ goto normal;
+ fg = d & 0xaa | d >> 1 & 0x55;
+ for(i = 0; i < 8; i += 2){
+ n = d >> 6;
+ npxs = npxs << 16 | (n == 3 ? c0 : reg[BG0 + n]) * 0x101;
+ d <<= 2;
+ }
+ break;
+ case 2:
+ c0 = c & 15;
+ c1 = c >> 4 & 15;
+ fg = d;
+ for(i = 0; i < 8; i++){
+ npxs = npxs << 8 | ((d & 0x80) != 0 ? c1 : c0);
+ d <<= 1;
+ }
+ break;
+ case 3:
+ c0 = c & 15;
+ c1 = c >> 4 & 15;
+ c2 = c >> 8;
+ fg = d & 0xaa | d >> 1 & 0x55;
+ for(i = 0; i < 8; i += 2){
+ n = d >> 6;
+ switch(n){
+ default: n = reg[BG0]; break;
+ case 1: n = c1; break;
+ case 2: n = c0; break;
+ case 3: n = c2;
+ }
+ npxs = npxs << 16 | n * 0x101;
+ d <<= 2;
+ }
+ break;
+ case 4:
+ c0 = c >> 8;
+ fg = d;
+ for(i = 0; i < 8; i++){
+ npxs = npxs << 8 | ((d & 0x80) != 0 ? c0 : reg[BG0 + (d >> 6 & 3)]);
+ d <<= 1;
+ }
+ break;
+ default:
+ fg = 0;
+ break;
+ }
+}
+
+void
+bgpixels(void)
+{
+ int i, j, x;
+ u8int h;
+ u8int spract, s, o;
+
+ h = hbord;
+ opxs = 0;
+ for(i = 0; i < 8; i++){
+ if((reg[CTRL2] + 4 & 7) == i)
+ pxs = i >= 4 ? npxs : npxs0;
+ o = pxs >> 56;
+ pxs <<= 8;
+
+ x = ppux + i;
+ spract = 0;
+ for(j = 0; j < 8; j++)
+ if((sp[j].flags & SPRFDISP) != 0 && (u16int)(x-sp[j].x) < 48 && (sp[j].data & ((reg[SPRMC]&1<<j)?3:2)<<22) != 0)
+ spract |= 1<<j;
+ if((spract & spract - 1) != 0 && vbord == 0){
+ reg[SPRSPR] |= spract;
+ irq |= IRQSPRCOLL;
+ }
+ if(fg != 0 && spract != 0 && vbord == 0){
+ reg[SPRBG] |= spract;
+ irq |= IRQBGCOLL;
+ }
+ s = spract & ~(reg[SPRDP] & ((s8int)fg) >> 7);
+ if(s != 0)
+ for(j = 0; j < 8; j++)
+ if((s & 1<<j) != 0){
+ if((reg[SPRMC] & 1<<j) != 0)
+ switch(sp[j].data >> 22 & 3){
+ case 1: o = reg[SPRMC0]; break;
+ case 2: o = reg[SPRCOL+j]; break;
+ case 3: o = reg[SPRMC1]; break;
+ }
+ else
+ o = reg[SPRCOL+j];
+ break;
+ }
+ if((h & 0x80) != 0)
+ o = reg[EC];
+ opxs = opxs << 8 | o;
+ h <<= 1;
+ fg <<= 1;
+ for(j = 0; j < 8; j++)
+ if((u16int)(x-sp[j].x) < 48 && ((reg[SPRXE] & 1<<j) == 0 || (x-sp[j].x & 1) != 0))
+ if((reg[SPRMC] & 1<<j) != 0){
+ if((x-sp[j].x & 1) != 0)
+ sp[j].data <<= 2;
+ }else
+ sp[j].data <<= 1;
+ }
+}
+
+void
+vicstep(void)
+{
+ u16int gaddr;
+ int i;
+
+ if(ppuy == 0x30 && (reg[CTRL1] & DEN) != 0)
+ fract = 1;
+ badln = ((ppuy ^ reg[CTRL1]) & 7) == 0 && fract;
+ hbord = ((s8int)(hbord << 7)) >> 7;
+ if(ppux == rbord && hbord == 0)
+ hbord = rbord0;
+ else if(ppux == lbord){
+ if(ppuy == dbord)
+ vbord = 1;
+ if(ppuy == ubord)
+ vbord = 0;
+ if(!vbord)
+ hbord = lbord0;
+ }
+ if(badln)
+ displ = 1;
+ if(ppux == 4){
+ vc = vcbase;
+ vmli = 0;
+ if(badln)
+ rc = 0;
+ }
+ if(ppux == 12)
+ for(i = 0; i < 8; i++){
+ if((sp[i].flags & SPRFDISP) == 0 || (reg[SPRYE] & 1<<i) != 0 && (sp[i].flags & SPRFYEX) == 0)
+ continue;
+ sp[i].mcbase += 3;
+ if(sp[i].mcbase == 63)
+ sp[i].flags &= ~(SPRFDMA|SPRFDISP);
+ }
+ if(ppux >= 0x14 && ppux <= 0x14c){
+ if((reg[CTRL1] & BMM) != 0)
+ gaddr = (reg[MEMP] & 0x08) << 10 | vc << 3 | rc;
+ else
+ gaddr = (reg[MEMP] & 0x0e) << 10 | (chrp[vmli] & 0xff) << 3 | rc;
+ if(!displ)
+ gaddr = 0x3fff;
+ if((reg[CTRL1] & ECM) != 0)
+ gaddr &= ~0x600;
+ pixels(vmemread(gaddr) & -displ, chrp[vmli] & -displ);
+ vmli++;
+ vc = vc + 1 & 0x3ff;
+ }
+ if(visreg && (ppux >= lvis || ppux < rvis)){
+ bgpixels();
+ pixeldraw(opxs, ppux == lvis ? region == NTSC ? 3 : 4 : 8);
+ }
+ if(ppux == 0x14c){
+ for(i = 0; i < 8; i++){
+ if((reg[SPRYE] & 1<<i) != 0)
+ sp[i].flags ^= SPRFYEX;
+ if((reg[SPREN] & 1<<i) == 0 || reg[2*i+1] != (u8int)ppuy)
+ continue;
+ sp[i].flags |= SPRFDMA;
+ sp[i].mcbase = 0;
+ if((reg[SPRYE] & 1<<i) != 0)
+ sp[i].flags &= ~SPRFYEX;
+ }
+ }else if(ppux == 0x154)
+ npxs = reg[BG0] * 0x0101010101010101ULL;
+ if(badln){
+ if(ppux == lastx - 8)
+ nrdy = 1;
+ else if(ppux >= 0xc && ppux <= 0x144)
+ chrp[vmli] = vmemread(vc | (reg[MEMP] & 0xf0) << 6) | cram[vc] << 8;
+ else if(ppux == 0x154)
+ nrdy = 0;
+ }
+ if(ppux == 0x164){
+ if(displ && rc == 7){
+ displ = badln;
+ vcbase = vc;
+ }
+ if(displ)
+ rc = rc + 1 & 7;
+ for(i = 0; i < 8; i++){
+ sp[i].mc = sp[i].mcbase;
+ if((sp[i].flags & SPRFDMA) != 0 && reg[2*i+1] == (u8int)ppuy)
+ sp[i].flags |= SPRFDISP;
+ }
+ }
+ if((u16int)(ppux - spr0) < 128){
+ i = ppux - spr0 >> 4;
+ nrdy = (sp[i].flags & SPRFDMA) != 0;
+ if((ppux & 8) == 0){
+ sp[i].dp = vmemread((reg[MEMP] & 0xf0) << 6 | 0x3f8 | i);
+ sp[i].x = nrdy ? reg[2 * i] | reg[MSBX] << 8 - i & 0x100 : -1;
+ if(nrdy)
+ sp[i].data = vmemread(sp[i].dp << 6 | sp[i].mc++) << 16;
+ }else if(nrdy){
+ sp[i].data = sp[i].data & 0xff00ff | vmemread(sp[i].dp << 6 | sp[i].mc++) << 8;
+ sp[i].data = sp[i].data & 0xffff00 | vmemread(sp[i].dp << 6 | sp[i].mc++);
+ }
+ }else if(ppux - spr0 == 128)
+ nrdy = 0;
+ if(ppux == wrapx){
+ ppuy++;
+ if(ppuy == maxy){
+ flush();
+ ppuy = 0;
+ vcbase = 0;
+ }
+ if((ppuy & 0xff) == reg[RASTER] && ((ppuy ^ reg[CTRL1] << 1) & 0x100) == 0)
+ irq |= IRQRASTER;
+ if(ppuy == dbord)
+ vbord = 1;
+ else if(ppuy == ubord)
+ vbord = 0;
+ else if(ppuy == dvis)
+ visreg = 0;
+ if(ppuy == uvis){
+ picidx = 0;
+ visreg = 1;
+ }
+ if(ppuy == 0xf7)
+ fract = 0;
+ }
+ if(ppux == lastx)
+ ppux = 4;
+ else
+ ppux += 8;
+}
--- /dev/null
+++ b/sys/src/games/eui.c
@@ -1,0 +1,479 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <keyboard.h>
+#include <mouse.h>
+#include "eui.h"
+
+typedef struct Kfn Kfn;
+
+u64int keys, keys2;
+Channel *keychan;
+int trace, paused;
+int savereq, loadreq;
+QLock pauselock;
+int scale, fixscale, warp10;
+uchar *pic;
+Rectangle picr;
+Mousectl *mc;
+Image *bg;
+
+static int profile, framestep;
+static int vwdx, vwdy, vwbpp;
+static ulong vwchan;
+static Image *fb;
+static Channel *conv, *sync[2];
+static uchar *screenconv[2], *backfb;
+static int screenconvi;
+
+struct Kfn{
+ Rune r;
+ int k;
+ char joyk[16];
+ void(*fn)(void);
+ Kfn *n;
+};
+static Kfn kfn, kkn;
+static int ax0, ax1;
+
+void *
+emalloc(ulong sz)
+{
+ void *v;
+
+ v = mallocz(sz, 1);
+ if(v == nil)
+ sysfatal("malloc: %r");
+ setmalloctag(v, getcallerpc(&sz));
+ return v;
+}
+
+Image *
+eallocimage(Rectangle r, ulong chan, int repl, ulong col)
+{
+ Image *i;
+
+ if((i = allocimage(display, r, chan, repl, col)) == nil)
+ sysfatal("allocimage: %r");
+ return i;
+}
+
+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;
+ if(keychan != nil)
+ nbsendul(keychan, 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(kp->r == r){
+ k |= kp->k;
+ break;
+ }
+ }
+ if((k & ax0) == ax0)
+ k &= ~ax0;
+ if((k & ax1) == ax1)
+ k &= ~ax1;
+ keys = k;
+ if(keychan != nil)
+ nbsendul(keychan, 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;
+
+ send(sync[0], nil);
+ if(!fixscale){
+ scale = Dx(screen->r) / vwdx;
+ if(Dy(screen->r) / vwdy < scale)
+ scale = Dy(screen->r) / vwdy;
+ }
+ 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 : vwdy),
+ vwchan, scale > 1, 0);
+ free(backfb);
+ if(scale > 1)
+ backfb = emalloc(vwdx * vwbpp * scale);
+ else
+ backfb = nil;
+ draw(screen, screen->r, bg, nil, ZP);
+ recv(sync[1], nil);
+}
+
+void
+flushmouse(int discard)
+{
+ Mouse m;
+
+ if(nbrecvul(mc->resizec) > 0){
+ send(sync[0], nil);
+ if(getwindow(display, Refnone) < 0)
+ sysfatal("resize failed: %r");
+ recv(sync[1], nil);
+ screeninit();
+ }
+ if(discard)
+ while(nbrecv(mc->c, &m) > 0)
+ ;
+}
+
+static void
+screenproc(void*)
+{
+ uchar *p;
+ enum { Draw, Sync1, Sync2 };
+ Alt alts[] = {
+ [Draw] {.c = conv, .v = &p, .op = CHANRCV},
+ [Sync1] {.c = sync[0], .op = CHANRCV},
+ [Sync2] {.c = sync[1], .op = CHANNOP},
+ {.op = CHANEND},
+ };
+
+ for(;;) switch(alt(alts)){
+ case Draw:
+ if(scale == 1){
+ loadimage(fb, fb->r, p, vwdx * vwdy * vwbpp);
+ draw(screen, picr, fb, nil, ZP);
+ } else {
+ Rectangle r;
+ int w, x;
+ uchar *bp;
+
+ r = picr;
+ w = vwdx * vwbpp * scale;
+ while(r.min.y < picr.max.y){
+ x = 0;
+ bp = backfb;
+ while(x < vwdx){
+ switch(vwbpp){
+ case 4: {
+ u32int *d = (u32int *)bp;
+ u32int s = *(u32int *)p;
+ switch(scale){
+ case 16: *d++ = s;
+ case 15: *d++ = s;
+ case 14: *d++ = s;
+ case 13: *d++ = s;
+ case 12: *d++ = s;
+ case 11: *d++ = s;
+ case 10: *d++ = s;
+ case 9: *d++ = s;
+ case 8: *d++ = s;
+ case 7: *d++ = s;
+ case 6: *d++ = s;
+ case 5: *d++ = s;
+ case 4: *d++ = s;
+ case 3: *d++ = s;
+ case 2: *d++ = s;
+ default: *d++ = s;
+ }
+ bp = (uchar *)d;
+ break;
+ } case 2: {
+ u16int *d = (u16int *)bp;
+ u16int s = *(u16int *)p;
+ switch(scale){
+ case 16: *d++ = s;
+ case 15: *d++ = s;
+ case 14: *d++ = s;
+ case 13: *d++ = s;
+ case 12: *d++ = s;
+ case 11: *d++ = s;
+ case 10: *d++ = s;
+ case 9: *d++ = s;
+ case 8: *d++ = s;
+ case 7: *d++ = s;
+ case 6: *d++ = s;
+ case 5: *d++ = s;
+ case 4: *d++ = s;
+ case 3: *d++ = s;
+ case 2: *d++ = s;
+ default: *d++ = s;
+ }
+ bp = (uchar *)d;
+ break;
+ } case 1: {
+ uchar *d = bp;
+ uchar s = *p;
+ switch(scale){
+ case 16: *d++ = s;
+ case 15: *d++ = s;
+ case 14: *d++ = s;
+ case 13: *d++ = s;
+ case 12: *d++ = s;
+ case 11: *d++ = s;
+ case 10: *d++ = s;
+ case 9: *d++ = s;
+ case 8: *d++ = s;
+ case 7: *d++ = s;
+ case 6: *d++ = s;
+ case 5: *d++ = s;
+ case 4: *d++ = s;
+ case 3: *d++ = s;
+ case 2: *d++ = s;
+ default: *d++ = s;
+ }
+ bp = (uchar *)d;
+ break;
+ } default: {
+ int n;
+ uchar *s, *e, *d = bp;
+ for(n=scale; n>0; n--){
+ s = p;
+ e = d + vwbpp;
+ while(d < e)
+ *d++ = *s++;
+ }
+ bp = (uchar *)d;
+ break;
+ }}
+ p += vwbpp;
+ x++;
+ }
+ loadimage(fb, fb->r, backfb, w);
+ r.max.y = r.min.y+scale;
+ draw(screen, r, fb, nil, ZP);
+ r.min.y = r.max.y;
+ }
+ }
+ flushimage(display, 1);
+ break;
+ case Sync1:
+ alts[Draw].op = CHANNOP;
+ alts[Sync1].op = CHANNOP;
+ alts[Sync2].op = CHANSND;
+ break;
+ case Sync2:
+ alts[Draw].op = CHANRCV;
+ alts[Sync1].op = CHANRCV;
+ alts[Sync2].op = CHANNOP;
+ break;
+ }
+}
+
+void
+flushscreen(void)
+{
+ memmove(screenconv[screenconvi], pic, vwdx * vwdy * vwbpp);
+ if(sendp(conv, screenconv[screenconvi]) > 0)
+ screenconvi = (screenconvi + 1) % 2;
+ 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);
+ if(strcmp(joyk, "up") == 0 || strcmp(joyk, "down") == 0)
+ ax0 |= k;
+ if(strcmp(joyk, "left") == 0 || strcmp(joyk, "right") == 0)
+ ax1 |= k;
+ 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);
+ bg = eallocimage(Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
+ scale = fixscale;
+ conv = chancreate(sizeof(uchar*), 0);
+ sync[0] = chancreate(1, 0);
+ sync[1] = chancreate(1, 0);
+ proccreate(screenproc, nil, mainstacksize);
+ pic = emalloc(vwdx * vwdy * vwbpp);
+ screenconv[0] = emalloc(vwdx * vwdy * vwbpp);
+ screenconv[1] = emalloc(vwdx * vwdy * vwbpp);
+ screeninit();
+}
--- /dev/null
+++ b/sys/src/games/eui.h
@@ -1,0 +1,19 @@
+enum{
+ MILLION = 1000000,
+ BILLION = 1000000000,
+};
+
+extern u64int keys, keys2;
+extern int trace, paused;
+extern int savereq, loadreq;
+extern QLock pauselock;
+extern int scale, fixscale, warp10;
+extern uchar *pic;
+
+void* emalloc(ulong);
+void flushmouse(int);
+void flushscreen(void);
+void flushaudio(int(*)(void));
+void regkeyfn(Rune, void(*)(void));
+void regkey(char*, Rune, int);
+void initemu(int, int, int, ulong, int, void(*)(void*));
--- /dev/null
+++ b/sys/src/games/gb/ppu.c
@@ -1,0 +1,326 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "../eui.h"
+#include "dat.h"
+#include "fns.h"
+
+u8int ppustate, ppuy, ppuw;
+ulong hblclock, rendclock;
+jmp_buf mainjmp, renderjmp;
+static int cyc, done, ppux, ppux0;
+extern int prish;
+extern u32int white;
+
+Var ppuvars[] = {VAR(ppustate), VAR(ppuy), VAR(hblclock), VAR(rendclock), {nil, 0, 0}};
+
+#define ryield() {if(setjmp(renderjmp) == 0) longjmp(mainjmp, 1);}
+#define myield() {if(setjmp(mainjmp) == 0) longjmp(renderjmp, 1);}
+
+typedef struct sprite sprite;
+struct sprite {
+ u8int dy, x, t;
+ u8int fetched, pal;
+ u16int chr;
+};
+enum {
+ SPRPRI = 0x80,
+ SPRYFL = 0x40,
+ SPRXFL = 0x20,
+ SPRPAL = 0x10,
+ SPRBANK = 0x08,
+
+ TILCOL0 = 0x01,
+ TILPRI = 0x02,
+ TILSPR = 0x04,
+};
+sprite spr[10], *sprm;
+
+void
+ppurender(void)
+{
+ int x, y, i, n, m, win;
+ u16int ta, ca, chr;
+ u8int tile, attr, pali;
+ u32int sr[8], *picp;
+ #define eat(nc) if(cyc <= nc){for(i = 0; i < nc; i++) if(--cyc == 0) ryield();} else cyc -= nc;
+
+ ryield();
+
+ attr = 0;
+ for(;;){
+ eat(6*2);
+ m = 168 + (reg[SCX] & 7);
+ win = 0;
+ if((reg[LCDC] & WINEN) != 0 && ppuy >= reg[WY] && reg[WX] <= 166)
+ if(reg[WX] == 0)
+ m = 7;
+ else if(reg[WX] == 166)
+ m = 0;
+ else{
+ m = reg[WX] + (reg[SCX] & 7) + 1;
+ win = -1;
+ }
+ ppux = 0;
+ ppux0 = 0;
+ picp = (u32int*)pic + ppuy * PICW;
+ y = ppuy + reg[SCY] << 1 & 14;
+ ta = 0x1800 | reg[LCDC] << 7 & 0x400 | ppuy + reg[SCY] << 2 & 0x3e0 | reg[SCX] >> 3;
+ x = -(reg[SCX] & 7);
+ restart:
+ do{
+ tile = vram[ta];
+ if((mode & COL) != 0)
+ attr = vram[8192 + ta];
+ if((reg[LCDC] & BGTILE) != 0)
+ ca = (tile << 4) + y;
+ else
+ ca = 0x1000 + ((s32int)(tile << 24) >> 20) + y;
+ if((attr & 0x40) != 0)
+ ca ^= 14;
+ ca |= (attr & 8) << 10;
+ chr = vram[ca] << 8 | vram[ca+1];
+ pali = attr << 2 & 0x1c;
+ if((attr & 0x20) == 0)
+ for(i = 0; i < 8; i++){
+ sr[i] = pal[pali | chr >> 15 | chr >> 6 & 2] | ((chr & 0x8080) == 0) << prish;
+ chr <<= 1;
+ }
+ else
+ for(i = 0; i < 8; i++){
+ sr[i] = pal[pali | chr << 1 & 2 | chr >> 8 & 1] | ((chr & 0x0101) == 0) << prish;
+ chr >>= 1;
+ }
+ if((attr & 0x80) != 0)
+ for(i = 0; i < 8; i++)
+ sr[i] |= 2 << prish;
+ if((reg[LCDC] & BGEN) == 0 && (mode & COL) == 0 && ((mode & CGB) != 0 || win == 0))
+ for(i = 0; i < 8; i++)
+ sr[i] = white;
+ if(cyc <= 2*8){
+ for(i = 0; i < 2*8; i++)
+ if(--cyc == 0)
+ ryield();
+ y = ppuy + reg[SCY] << 1 & 14;
+ ta = 0x1800 | reg[LCDC] << 7 & 0x400 | ppuy + reg[SCY] << 2 & 0x3e0 | ta & 0x1f;
+ }else
+ cyc -= 2*8;
+ m -= 8;
+ n = m < 8 ? m : 8;
+ if((ta & 0x1f) == 0x1f)
+ ta &= ~0x1f;
+ else
+ ta++;
+ for(i = 0; i < n; i++, x++)
+ if(x >= 0)
+ picp[x] = sr[i];
+ ppux = x;
+ }while(m > 8);
+ if(win == -1){
+ win = 1;
+ ta = 0x1800 | reg[LCDC] << 4 & 0x400 | ppuw - reg[WY] << 2 & 0x3e0;
+ y = ppuw - reg[WY] << 1 & 14;
+ cyc += 2;
+ m = 175 - reg[WX];
+ goto restart;
+ }
+ done = 1;
+ ryield();
+ }
+}
+
+void
+oamsearch(void)
+{
+ u8int *p;
+ sprite *q;
+ int y0, sy;
+ u8int t, tn;
+ u8int *chrp;
+
+ y0 = ppuy + 16;
+ sy = (reg[LCDC] & SPR16) != 0 ? 16 : 8;
+ sprm = spr;
+ if((reg[LCDC] & SPREN) == 0)
+ return;
+ for(p = oam; p < oam + 160; p += 4){
+ if((u8int)(y0 - p[0]) >= sy)
+ continue;
+ if((mode & COL) == 0){
+ for(q = spr; q < sprm; q++)
+ if(q->x > p[1]){
+ if(sprm != spr + 10){
+ memmove(q + 1, q, (sprm - q) * sizeof(sprite));
+ sprm++;
+ }else
+ memmove(q + 1, q, (sprm - 1 - q) * sizeof(sprite));
+ goto found;
+ }
+ if(q == spr + 10)
+ continue;
+ sprm++;
+ found:;
+ }else
+ q = sprm++;
+ q->dy = y0 - p[0];
+ q->x = p[1];
+ q->t = t = p[3];
+ if((t & SPRYFL) != 0)
+ q->dy ^= sy - 1;
+ tn = p[2];
+ if(sy == 16)
+ tn = tn & ~1 | q->dy >> 3;
+ chrp = vram + (tn << 4 | q->dy << 1 & 14);
+ if((mode & COL) != 0){
+ chrp += t << 10 & 0x2000;
+ q->pal = 0x20 | t << 2 & 0x1c;
+ }else
+ q->pal = 4 + (t >> 2 & 4);
+ q->chr = chrp[0] << 8 | chrp[1];
+ if(p[1] < 8)
+ if((t & SPRXFL) != 0)
+ q->chr >>= 8 - p[1];
+ else
+ q->chr <<= 8 - p[1];
+ q->fetched = 0;
+ if((mode & COL) != 0 && sprm == spr + 10)
+ break;
+ }
+}
+
+void
+sprites(void)
+{
+ sprite *q;
+ u8int attr;
+ u32int *picp;
+ int x, x1;
+ u16int chr;
+
+ picp = (u32int*)pic + ppuy * PICW;
+ for(q = spr; q < sprm; q++){
+ if(q->x <= ppux0 || q->x >= ppux + 8)
+ continue;
+ x = q->x - 8;
+ if(x < ppux0) x = ppux0;
+ x1 = q->x;
+ if(x1 > ppux) x1 = ppux;
+ for(; x < x1; x++){
+ attr = picp[x] >> prish;
+ chr = q->chr;
+ if((chr & ((q->t & SPRXFL) != 0 ? 0x0101 : 0x8080)) != 0 && (attr & TILSPR) == 0 &&
+ ((mode & COL) != 0 && (reg[LCDC] & BGPRI) == 0 ||
+ (attr & TILCOL0) != 0 ||
+ (attr & TILPRI) == 0 && (q->t & SPRPRI) == 0))
+ if((q->t & SPRXFL) == 0)
+ picp[x] = pal[q->pal | chr >> 15 | chr >> 6 & 2] | TILSPR << prish;
+ else
+ picp[x] = pal[q->pal | chr << 1 & 2 | chr >> 8 & 1] | TILSPR << prish;
+ if((q->t & SPRXFL) != 0)
+ q->chr >>= 1;
+ else
+ q->chr <<= 1;
+ }
+ }
+ ppux0 = ppux;
+}
+
+void
+ppusync(void)
+{
+ if(ppustate != 3)
+ return;
+ cyc = clock - rendclock;
+ if(cyc != 0)
+ myield();
+ sprites();
+ rendclock = clock;
+}
+
+int
+linelen(void)
+{
+ int t;
+
+ t = 174 + (reg[SCX] & 7);
+ if((reg[LCDC] & WINEN) != 0 && ppuy >= reg[WY] && reg[WX] < 166)
+ if(reg[WX] == 0)
+ t += 7;
+ else
+ t += 6;
+ return t*2;
+}
+
+void
+hblanktick(void *)
+{
+ extern Event evhblank;
+ int t;
+
+ switch(ppustate){
+ case 0:
+ hblclock = clock + evhblank.time;
+ if(reg[WX] <= 166 && reg[WY] <= 143)
+ ppuw++;
+ if(++ppuy == 144){
+ ppustate = 1;
+ if((reg[STAT] & IRQM1) != 0)
+ reg[IF] |= IRQLCDS;
+ addevent(&evhblank, 456*2);
+ reg[IF] |= IRQVBL;
+ flush();
+ }else{
+ ppustate = 2;
+ if((reg[STAT] & IRQM2) != 0)
+ reg[IF] |= IRQLCDS;
+ addevent(&evhblank, 80*2);
+ }
+ if((reg[STAT] & IRQLYC) != 0 && ppuy == reg[LYC])
+ reg[IF] |= IRQLCDS;
+ break;
+ case 1:
+ hblclock = clock + evhblank.time;
+ if(reg[WX] <= 166 && reg[WY] <= 143)
+ ppuw++;
+ if(++ppuy == 154){
+ ppuy = 0;
+ ppuw = 0;
+ ppustate = 2;
+ if((reg[STAT] & IRQM2) != 0)
+ reg[IF] |= IRQLCDS;
+ addevent(&evhblank, 80*2);
+ }else
+ addevent(&evhblank, 456*2);
+ if((reg[STAT] & IRQLYC) != 0 && ppuy == reg[LYC])
+ reg[IF] |= IRQLCDS;
+ break;
+ case 2:
+ oamsearch();
+ rendclock = clock + evhblank.time;
+ ppustate = 3;
+ addevent(&evhblank, linelen());
+ break;
+ case 3:
+ ppusync();
+ if(!done) print("not done?!\n");
+ done = 0;
+ ppustate = 0;
+ if((reg[STAT] & IRQM0) != 0)
+ reg[IF] |= IRQLCDS;
+ t = hblclock + 456 * 2 - clock;
+ addevent(&evhblank, t < 0 ? 456 * 2 : t);
+ if(dma & DMAHBLANK)
+ dma |= DMAREADY;
+ break;
+ }
+}
+
+void
+ppuinit(void)
+{
+ static char ppustack[4096];
+
+ renderjmp[JMPBUFPC] = (uintptr)ppurender;
+ renderjmp[JMPBUFSP] = (uintptr)(ppustack + sizeof(ppustack) - 64);
+ myield();
+}
--- /dev/null
+++ b/sys/src/games/gba/apu.c
@@ -1,0 +1,390 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "../eui.h"
+#include "dat.h"
+#include "fns.h"
+
+Event evsamp;
+int srate, sratediv;
+s16int sbuf[2*4000], *sbufp, bias;
+enum {
+ Freq = 44100,
+};
+static int stime;
+s8int snddma[2];
+static int fd;
+
+u16int envctr, envrel, envmod;
+u8int sweepen, sweepctr;
+u16int sweepfreq;
+typedef struct chan chan;
+struct chan {
+ u8int n, ectr;
+ u16int len;
+ u16int *env, *freq;
+ u16int fctr, fthr;
+ u32int finc;
+ u8int vol;
+};
+u8int wave[64], wpos, wbank;
+u16int lfsr;
+
+chan sndch[4] = {
+ {
+ .n = 0,
+ .env = reg + 0x62/2,
+ .freq = reg + 0x64/2,
+ },
+ {
+ .n = 1,
+ .env = reg + 0x68/2,
+ .freq = reg + 0x6c/2,
+ },
+ {
+ .n = 2,
+ },
+ {
+ .n = 3,
+ .env = reg + 0x78/2,
+ .freq = reg + 0x7c/2,
+ }
+};
+
+Var apuvars[] = {
+ ARR(snddma), VAR(envctr), VAR(envrel), VAR(envmod), VAR(sweepen),
+ VAR(sweepctr), VAR(sweepfreq), ARR(wave), VAR(wpos), VAR(wbank), VAR(lfsr),
+ VAR(sndch[0].ectr), VAR(sndch[0].len), VAR(sndch[0].fctr), VAR(sndch[0].fthr), VAR(sndch[0].finc), VAR(sndch[0].vol),
+ VAR(sndch[1].ectr), VAR(sndch[1].len), VAR(sndch[1].fctr), VAR(sndch[1].fthr), VAR(sndch[1].finc), VAR(sndch[1].vol),
+ VAR(sndch[2].ectr), VAR(sndch[2].len), VAR(sndch[2].fctr), VAR(sndch[2].fthr), VAR(sndch[2].finc), VAR(sndch[2].vol),
+ VAR(sndch[3].ectr), VAR(sndch[3].len), VAR(sndch[3].fctr), VAR(sndch[3].fthr), VAR(sndch[3].finc), VAR(sndch[3].vol),
+ {nil, 0, 0},
+};
+
+void
+rate(int i, u16int v)
+{
+ switch(i){
+ case 0: case 1:
+ sndch[i].finc = 131072ULL * 65536 / (srate * (2048 - (v & 0x7ff)));
+ break;
+ case 2:
+ sndch[2].finc = 2097152ULL * 65536 / (srate * (2048 - (v & 0x7ff)));
+ break;
+ case 3:
+ sndch[3].finc = 524288ULL * 65536 / srate;
+ if((v & 7) != 0)
+ sndch[3].finc /= v & 7;
+ else
+ sndch[3].finc <<= 1;
+ sndch[3].finc >>= (v >> 4 & 15) + 1;
+ }
+}
+
+void
+env(chan *c)
+{
+ if((envmod & 1) == 0 && c->len > 0 && (*c->freq & 1<<14) != 0)
+ --c->len;
+ if(c->len == 0){
+ c->vol = 0;
+ return;
+ }
+ if((envmod & 7) != 7 || c->ectr == 0 || --c->ectr != 0)
+ return;
+ c->ectr = *c->env >> 8 & 7;
+ if((*c->env & 1<<11) != 0){
+ if(c->vol < 15)
+ c->vol++;
+ }else
+ if(c->vol > 0)
+ c->vol--;
+}
+
+s8int
+wavesamp(void)
+{
+ s8int x;
+ u16int vol, cnt;
+ int v;
+
+ sndch[2].fctr = v = sndch[2].fctr + sndch[2].finc;
+ if(sndch[2].len == 0 || (reg[0x70/2] & 1<<7) == 0)
+ return 0;
+ vol = reg[0x72/2];
+ cnt = reg[0x70/2];
+ for(;;){
+ x = wave[wbank ^ wpos];
+ v -= 0x10000;
+ if(v < 0)
+ break;
+ wpos++;
+ if((cnt & 1<<5) != 0)
+ wpos &= 63;
+ else
+ wpos &= 31;
+ }
+ if((vol & 1<<15) != 0)
+ x = (x >> 2) + (x >> 3);
+ else if((vol & 3<<14) == 0)
+ x = 0;
+ else
+ x = x >> (vol >> 14 & 3);
+ return x;
+}
+
+s8int
+lfsrsamp(void)
+{
+ int v;
+ u16int l;
+
+ sndch[3].fctr = v = sndch[3].fctr + sndch[3].finc;
+ for(;;){
+ l = lfsr;
+ v -= 0x10000;
+ if(v < 0)
+ break;
+ lfsr >>= 1;
+ if(((l ^ lfsr) & 1) != 0)
+ if((reg[0x7c/2] & 1<<3) != 0)
+ lfsr |= 0x40;
+ else
+ lfsr |= 0x4000;
+ }
+ if((l & 1) != 0)
+ return -sndch[3].vol;
+ else
+ return sndch[3].vol;
+}
+
+void
+sweep(int wb)
+{
+ u16int fr;
+ int d;
+ u16int cnt;
+
+ cnt = reg[0x60/2];
+ d = sweepfreq >> (cnt & 7);
+ if((cnt & 1<<3) != 0)
+ d = -d;
+ fr = sweepfreq + d;
+ if(fr > 2047){
+ sndch[0].len = 0;
+ sndch[0].vol = 0;
+ sweepen = 0;
+ }else if(wb){
+ sweepfreq = fr;
+ reg[0x64/2] = reg[0x64/2] & 0xfc00 | fr;
+ rate(0, fr);
+ sweep(0);
+ }
+}
+
+void
+sndstart(chan *c, u16int v)
+{
+ u16int cnt;
+
+ c->vol = *c->env >> 12;
+ c->ectr = *c->env >> 8 & 7;
+ if(c->len == 0)
+ c->len = 64;
+ if(c == sndch){
+ cnt = reg[0x60/2];
+ sweepen = (cnt & 0x07) != 0 && (cnt & 0x70) != 0;
+ sweepctr = cnt >> 4 & 7;
+ sweepfreq = v & 0x7ff;
+ if((cnt & 0x07) != 0)
+ sweep(0);
+ }
+}
+
+void
+sampletick(void *)
+{
+ u16int cntl, cnth;
+ s16int ch[6];
+ s16int s[2];
+ int i;
+
+ addevent(&evsamp, sratediv);
+
+ if(--envctr == 0){
+ envctr = envrel;
+ env(&sndch[0]);
+ env(&sndch[1]);
+ if((envmod & 1) == 0 && sndch[2].len > 0 && (reg[0x74/2] & 1<<14) != 0)
+ sndch[2].len--;
+ env(&sndch[3]);
+ if((envmod & 3) == 2 && sweepen && --sweepctr == 0){
+ sweepctr = reg[0x60/2] >> 4 & 7;
+ sweep(1);
+ }
+ envmod++;
+ }
+
+ sndch[0].fctr += sndch[0].finc;
+ if(sndch[0].fctr >= sndch[0].fthr)
+ ch[0] = sndch[0].vol;
+ else
+ ch[0] = -sndch[0].vol;
+ sndch[1].fctr += sndch[1].finc;
+ if(sndch[1].fctr >= sndch[1].fthr)
+ ch[1] = sndch[1].vol;
+ else
+ ch[1] = -sndch[1].vol;
+ ch[2] = wavesamp();
+ ch[3] = lfsrsamp();
+
+ cntl = reg[SOUNDCNTL];
+ cnth = reg[SOUNDCNTH];
+ for(i = 0; i < 4; i++)
+ ch[i] = ch[i] >> (cnth & 3);
+ ch[5] = snddma[0] << 1 + (cnth >> 2 & 1);
+ ch[6] = snddma[1] << 1 + (cnth >> 3 & 1);
+
+ s[0] = 0;
+ s[1] = 0;
+ for(i = 0; i < 4; i++){
+ if((cntl & 1<<8<<i) != 0)
+ s[1] += ch[i] * (1 + (cntl & 7));
+ if((cntl & 1<<12<<i) != 0)
+ s[0] += ch[i] * (1 + (cntl >> 4 & 7));
+ }
+ for(i = 5; i < 6; i++){
+ if((cnth & 1<<3<<i) != 0)
+ s[1] += ch[i];
+ if((cnth & 1<<4<<i) != 0)
+ s[0] += ch[i];
+ }
+ s[0] += bias;
+ s[1] += bias;
+ if(s[0] < -0x200) s[0] = -0x200;
+ else if(s[0] > 0x1ff) s[0] = 0x1ff;
+ if(s[1] < -0x200) s[1] = -0x200;
+ else if(s[1] > 0x1ff) s[1] = 0x1ff;
+
+ stime -= Freq;
+ while(stime < 0){
+ if(sbufp < sbuf + nelem(sbuf)){
+ sbufp[0] = s[0] << 6;
+ sbufp[1] = s[1] << 6;
+ sbufp += 2;
+ }
+ stime += srate;
+ }
+}
+
+
+void
+sndwrite(u16int a, u16int v)
+{
+ int sh, p, i;
+ static u16int thr[4] = {0x2000, 0x4000, 0x8000, 0xC000};
+
+ switch(a){
+ case 0x62:
+ sndch[0].fthr = thr[v >> 6 & 3];
+ sndch[0].len = 64 - (v & 63);
+ break;
+ case 0x64:
+ rate(0, v);
+ if((v & 1<<15) != 0)
+ sndstart(&sndch[0], v);
+ break;
+ case 0x68:
+ sndch[1].fthr = thr[v >> 6 & 3];
+ break;
+ case 0x6c:
+ rate(1, v);
+ if((v & 1<<15) != 0)
+ sndstart(&sndch[1], v);
+ break;
+ case 0x70:
+ wbank = v >> 1 & 32;
+ break;
+ case 0x72:
+ sndch[2].len = 256 - (v & 0xff);
+ break;
+ case 0x74:
+ rate(2, v);
+ if((v & 1<<15) != 0 && sndch[2].len == 0)
+ sndch[2].len = 256;
+ break;
+ case 0x7c:
+ rate(3, v);
+ if((v & 1<<15) != 0){
+ if((v & 1<<3) != 0)
+ lfsr = 0x7f;
+ else
+ lfsr = 0x7fff;
+ sndstart(&sndch[3], v);
+ }
+ break;
+ case SOUNDBIAS*2:
+ sh = 9 - (v >> 14 & 3);
+ if(sratediv != 1<<sh){
+ srate = 1 << 24 - sh;
+ sratediv = 1 << sh;
+ envrel = srate / 512;
+ rate(0, reg[0x64/2]);
+ rate(1, reg[0x6c/2]);
+ rate(2, reg[0x74/2]);
+ rate(3, reg[0x7c/2]);
+ }
+ bias = (v & 0x3ff) - 0x200;
+ break;
+ case 0x90: case 0x92: case 0x94: case 0x96:
+ case 0x98: case 0x9a: case 0x9c: case 0x9e:
+ p = ~reg[0x70/2] >> 1 & 32;
+ for(i = a - 0x90; i < a - 0x90 + 2; i++){
+ wave[(wpos + 2 * i) & 31 + p] = v >> 4 & 0xf;
+ wave[(wpos + 2 * i + 1) & 31 + p] = v & 0xf;
+ v >>= 8;
+ }
+ break;
+ }
+}
+
+void
+audioinit(void)
+{
+ fd = open("/dev/audio", OWRITE);
+ if(fd < 0)
+ sysfatal("open: %r");
+ sbufp = sbuf;
+ sndwrite(SOUNDBIAS*2, 0x200);
+ evsamp.f = sampletick;
+ addevent(&evsamp, sratediv);
+}
+
+int
+audioout(void)
+{
+ int rc;
+ static int cl;
+ static vlong t0;
+
+ if(t0 == 0)
+ t0 = nsec();
+ if(sbufp == nil)
+ return -1;
+ if(sbufp == sbuf)
+ return 0;
+ cl = clock;
+ vlong t = nsec();
+ rc = warp10 ? (sbufp - sbuf) * 2 : write(fd, sbuf, (sbufp - sbuf) * 2);
+ vlong t2 = nsec();
+ double Δ = (t2 - t) / 1000000.0;
+ double τ = (t2 - t0) / 1000000.0;
+ double τ0 = rc * 1000.0 / (44100.0 * 4);
+ fprint(2, "%d %f %f\n", rc, Δ, τ-τ0);
+ t0 = t2;
+ if(rc > 0)
+ sbufp -= (rc+1)/2;
+ if(sbufp < sbuf)
+ sbufp = sbuf;
+ return 0;
+}
--- /dev/null
+++ b/sys/src/games/gba/ppu.c
@@ -1,0 +1,781 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "../eui.h"
+#include "dat.h"
+#include "fns.h"
+
+int hblank, ppuy;
+u8int bldy, blda, bldb;
+u32int hblclock;
+int ppux0;
+u32int pixcol[480];
+u8int pixpri[480];
+u8int pixwin[240];
+int objalpha;
+
+typedef struct bg bg;
+struct bg {
+ uchar n;
+ s32int rpx0, rpy0, rpx1, rpy1, rpx, rpy;
+ u16int tx, ty;
+ u8int tnx, tny;
+
+ u8int mosaic, mctr, lasti;
+ u32int curc;
+ u8int curpri;
+};
+static bg bgst[4] = {{.n = 0}, {.n = 1}, {.n = 2}, {.n = 3}};
+
+Var ppuvars[] = {
+ VAR(hblank), VAR(ppuy), VAR(hblclock),
+ VAR(bldy), VAR(blda), VAR(bldb), VAR(objalpha),
+ VAR(bgst[2].rpx0), VAR(bgst[2].rpy0), VAR(bgst[3].rpx0), VAR(bgst[3].rpy0),
+ {nil, 0, 0},
+};
+
+typedef struct sprite sprite;
+struct sprite {
+ uchar w, wb, h;
+ s16int x;
+ uchar ysh;
+
+ uchar *base;
+ u16int *pal;
+ u16int inc;
+
+ u32int t0;
+ u16int t1;
+ uchar depth;
+
+ s32int rx, ry;
+ s16int dx, dy;
+
+ u8int mctr, mcol;
+};
+static sprite sprt[128], *sp = sprt;
+enum {
+ SPRROT = 1<<8,
+ SPRDIS = 1<<9,
+ SPRDOUB = 1<<9,
+ SPRMOSA = 1<<12,
+ SPR8 = 1<<13,
+ SPRWIDE = 1<<14,
+ SPRTALL = 1<<15,
+ SPRHFLIP = 1<<28,
+ SPRVFLIP = 1<<29,
+ SPRSIZE0 = 1<<30,
+ SPRSIZE1 = 1<<31,
+
+ NOWIN = 0,
+ OBJWIN = 1,
+ WIN2 = 2,
+ WIN1 = 4,
+
+ OBJALPHA = 1<<16,
+ SRCOBJ = 4<<17,
+ SRCBACK = 5<<17,
+
+ VACANT = 0x10,
+ BACKDROP = 8,
+};
+#define SRCBG(n) ((n)<<17)
+
+void
+sprinit(void)
+{
+ u16int *p, *pp;
+ u16int cnt, t1;
+ u32int t0;
+ int budg;
+ uchar ws, h, hb, d, dy, s;
+ static uchar wss[16] = {3, 4, 5, 6, 4, 5, 5, 6, 3, 3, 4, 5};
+ static uchar hss[16] = {3, 4, 5, 6, 3, 3, 4, 5, 4, 5, 5, 6};
+
+ sp = sprt;
+ cnt = reg[DISPCNT];
+ budg = (cnt & HBLFREE) != 0 ? 954 : 1210;
+ for(p = oam; p < oam + 512; p += 4){
+ t0 = p[0];
+ if((t0 & (SPRROT|SPRDIS)) == SPRDIS)
+ continue;
+ t0 |= p[1] << 16;
+ s = t0 >> 30 & 3 | t0 >> 12 & 12;
+ hb = h = 1 << hss[s];
+ dy = ppuy - (u8int) t0;
+ if((t0 & (SPRROT|SPRDOUB)) == (SPRROT|SPRDOUB))
+ hb <<= 1;
+ if(dy >= hb || (u8int)t0 + hb > 256 && ppuy + 256 - (u8int)t0 >= hb)
+ continue;
+ if((t0 & SPRMOSA) != 0){
+ dy = dy - dy % ((reg[MOSAIC] >> 12 & 15) + 1);
+ sp->mctr = 0;
+ }
+ sp->x = (s32int)(t0 << 7) >> 23;
+ sp->t0 = t0;
+ ws = wss[s];
+ sp->wb = sp->w = 1<<ws;
+ sp->h = h;
+ sp->t1 = t1 = p[2];
+ sp->base = vram + 0x10000 + ((t1 & 0x3ff) << 5);
+ d = (t0 & SPR8) != 0;
+ sp->ysh = (cnt & OBJNOMAT) != 0 ? 2 + d + ws : 10;
+ if((t0 & SPRROT) != 0){
+ if((t0 & SPRDOUB) != 0)
+ sp->wb <<= 1;
+ budg -= 10 + sp->w*2;
+ pp = oam + 3 + (t0 >> 21 & 0x1f0);
+ sp->dx = pp[0];
+ sp->dy = pp[8];
+ sp->rx = (dy - hb/2) * (s16int) pp[4] + (sp->w << 7) - sp->dx * sp->wb/2;
+ sp->ry = (dy - hb/2) * (s16int)pp[12] + (sp->h << 7) - sp->dy * sp->wb/2;
+ if(sp->x < 0){
+ sp->rx -= sp->x * sp->dx;
+ sp->ry -= sp->x * sp->dy;
+ }
+ }else{
+ budg -= sp->w;
+ if((t0 & SPRVFLIP) != 0)
+ dy = h - 1 - dy;
+ sp->base += (dy & 7) << 2 + d;
+ sp->base += dy >> 3 << sp->ysh;
+ if((t0 & SPRHFLIP) != 0)
+ sp->base += sp->w - 7 << 2 + d;
+ sp->inc = (1 << 5 + d) - (1 << 2 + d);
+ if(sp->x < 0)
+ if((t0 & SPRHFLIP) != 0){
+ sp->base -= ((-sp->x & 7) >> 1 - d) + (-sp->x >> 3 << 5 + d);
+ if((t0 & SPR8) == 0 && (sp->x & 1) != 0)
+ sp->base--;
+ }else
+ sp->base += ((-sp->x & 7) >> 1 - d) + (-sp->x >> 3 << 5 + d);
+ }
+ if((t0 & SPR8) != 0)
+ sp->pal = pram + 0x100;
+ else
+ sp->pal = pram + 0x100 + (t1 >> 8 & 0xf0);
+ if(budg < 0)
+ break;
+ sp++;
+ }
+}
+
+void
+spr(int x1)
+{
+ int x0, i, dx, sx0, sx1;
+ u8int pri, v, d, *b;
+ u16int x, y;
+ u32int c, t0;
+ sprite *s;
+
+ x0 = ppux0;
+ for(s = sprt; s < sp; s++){
+ if(s->x >= x1 || s->x + s->wb <= x0)
+ continue;
+ t0 = s->t0;
+ pri = s->t1 >> 10 & 3;
+ sx0 = s->x >= x0 ? s->x : x0;
+ sx1 = s->x + s->wb;
+ if(x1 < sx1)
+ sx1 = x1;
+ dx = sx0 - s->x;
+ for(i = sx0; i < sx1; i++, dx++){
+ if((t0 & SPRROT) != 0){
+ d = (t0 & SPR8) != 0;
+ x = s->rx >> 8;
+ y = s->ry >> 8;
+ s->rx += s->dx;
+ s->ry += s->dy;
+ if(x < s->w && y < s->h){
+ b = s->base;
+ b += (y & 7) << 2 + d;
+ b += y >> 3 << s->ysh;
+ b += (x & 7) >> 1 - d;
+ b += x >> 3 << 5 + d;
+ v = *b;
+ if(!d)
+ if((x & 1) != 0)
+ v >>= 4;
+ else
+ v &= 0xf;
+ }else
+ v = 0;
+ }else if((t0 & SPRHFLIP) != 0){
+ if((t0 & SPR8) != 0)
+ v = *--s->base;
+ else if((dx & 1) != 0)
+ v = *s->base & 0x0f;
+ else
+ v = *--s->base >> 4;
+ if((dx & 7) == 7)
+ s->base -= s->inc;
+ }else{
+ v = *s->base;
+ if((t0 & SPR8) != 0)
+ s->base++;
+ else if((dx & 1) != 0){
+ v >>= 4;
+ s->base++;
+ }else
+ v &= 0xf;
+ if((dx & 7) == 7)
+ s->base += s->inc;
+ }
+ if((t0 & SPRMOSA) != 0)
+ if(s->mctr == 0){
+ s->mctr = reg[MOSAIC] >> 8 & 15;
+ s->mcol = v;
+ }else{
+ --s->mctr;
+ v = s->mcol;
+ }
+ if(v != 0){
+ c = s->pal[v] | SRCOBJ;
+ switch(t0 >> 10 & 3){
+ case 1:
+ c |= OBJALPHA;
+ objalpha++;
+ case 0:
+ if(pri < pixpri[i]){
+ pixcol[i] = c;
+ pixpri[i] = pri;
+ }
+ break;
+ case 2:
+ if((reg[DISPCNT] & 1<<15) != 0)
+ pixwin[i] |= OBJWIN;
+ break;
+ }
+ }
+ }
+ }
+}
+
+void
+bgpixel(bg *b, int i, u32int c, int pri)
+{
+ u8int *p;
+ u32int *q;
+ int j;
+
+ if(b != nil){
+ c |= SRCBG(b->n);
+ if(b->mosaic){
+ for(j = (u8int)(b->lasti+1); j <= i; j++){
+ if(b->mctr == 0){
+ if(j == i){
+ b->curc = c;
+ b->curpri = pri;
+ }else
+ b->curpri = VACANT;
+ b->mctr = reg[MOSAIC] & 15;
+ }else
+ b->mctr--;
+ if(b->curpri != VACANT && (pixwin[j] & 1<<b->n) == 0)
+ bgpixel(nil, j, b->curc, b->curpri);
+ }
+ b->lasti = i;
+ return;
+ }
+ }
+ p = pixpri + i;
+ q = pixcol + i;
+ if(pri < p[0]){
+ p[240] = p[0];
+ p[0] = pri;
+ q[240] = q[0];
+ q[0] = c;
+ }else if(pri < p[240]){
+ p[240] = pri;
+ q[240] = c;
+ }
+}
+
+
+
+void
+bginit(bg *b, int scal, int)
+{
+ u16int x, y;
+ u16int *rr;
+ int msz;
+
+ b->mosaic = (reg[BG0CNT + b->n] & BGMOSAIC) != 0;
+ if(b->mosaic){
+ b->mctr = 0;
+ b->lasti = -1;
+ }
+ if(scal){
+ rr = reg + (b->n - 2 << 3);
+ if(ppuy == 0){
+ b->rpx0 = (s32int)(rr[BG2XL] | rr[BG2XH] << 16) << 4 >> 4;
+ b->rpy0 = (s32int)(rr[BG2YL] | rr[BG2YH] << 16) << 4 >> 4;
+ }
+ if(!b->mosaic || ppuy % ((reg[MOSAIC] >> 4 & 15) + 1) == 0){
+ b->rpx1 = b->rpx0;
+ b->rpy1 = b->rpy0;
+ }
+ b->rpx = b->rpx1;
+ b->rpy = b->rpy1;
+ b->rpx0 += (s16int)rr[BG2PB];
+ b->rpy0 += (s16int)rr[BG2PD];
+ }else{
+ rr = reg + (b->n << 1);
+ x = rr[BG0HOFS] & 0x1ff;
+ y = ppuy;
+ if(b->mosaic){
+ msz = (reg[MOSAIC] >> 4 & 15) + 1;
+ y = y - y % msz;
+ }
+ y += (rr[BG0VOFS] & 0x1ff);
+ b->tx = x >> 3;
+ b->ty = y >> 3;
+ b->tnx = x & 7;
+ b->tny = y & 7;
+ }
+}
+
+void
+bgsinit(void)
+{
+ switch(reg[DISPCNT] & 7){
+ case 0:
+ bginit(&bgst[0], 0, 0);
+ bginit(&bgst[1], 0, 0);
+ bginit(&bgst[2], 0, 0);
+ bginit(&bgst[3], 0, 0);
+ break;
+ case 1:
+ bginit(&bgst[0], 0, 0);
+ bginit(&bgst[1], 0, 0);
+ bginit(&bgst[2], 1, 0);
+ break;
+ case 2:
+ bginit(&bgst[2], 1, 0);
+ bginit(&bgst[3], 1, 0);
+ break;
+ case 3:
+ case 4:
+ case 5:
+ bginit(&bgst[2], 1, 1);
+ break;
+ }
+}
+
+void
+bitbg(bg *b, int x1)
+{
+ u8int *base, *p, pri, d;
+ u16int cnt, *rr, sx, sy;
+ int i, v;
+
+ cnt = reg[DISPCNT];
+ if((cnt & 1<<8 + b->n) == 0)
+ return;
+ rr = reg + (b->n - 2 << 3);
+ if((cnt & 7) != 5){
+ sx = 240 << 8;
+ sy = 160 << 8;
+ d = (cnt & 7) == 3;
+ }else{
+ sx = 160 << 8;
+ sy = 128 << 8;
+ d = 1;
+ }
+ base = vram;
+ if((cnt & FRAME) != 0 && (cnt & 7) != 3)
+ base += 0xa000;
+ pri = reg[BG0CNT + b->n] & 3;
+ for(i = ppux0; i < x1; i++){
+ if(((pixwin[i] & 1<<b->n) == 0 || b->mosaic) && (u32int)b->rpx < sx && (u32int)b->rpy < sy){
+ if(d){
+ p = base + 2 * (b->rpx >> 8) + 480 * (b->rpy >> 8);
+ v = p[0] | p[1] << 8;
+ }else{
+ v = base[(b->rpx >> 8) + 240 * (b->rpy >> 8)];
+ if(v != 0)
+ v = pram[v];
+ else
+ v = -1;
+ }
+ if(v >= 0)
+ bgpixel(b, i, v, pri);
+
+ }
+ b->rpx += (s16int) rr[BG2PA];
+ b->rpy += (s16int) rr[BG2PC];
+ }
+}
+
+void
+txtbg(bg *b, int x1)
+{
+ u8int y, v, d, *cp;
+ u16int bgcnt, ta0, ta, tx, ty, t, *pal;
+ u32int ca;
+ int i, x, mx;
+
+ if((reg[DISPCNT] & 1<<8 + b->n) == 0)
+ return;
+ bgcnt = reg[BG0CNT + b->n];
+ d = bgcnt >> 7 & 1;
+ tx = b->tx;
+ ty = b->ty;
+ ta0 = (bgcnt << 3 & 0xf800) + ((ty & 0x1f) << 6);
+ switch(bgcnt >> 14){
+ case 2: ta0 += ty << 6 & 0x800; break;
+ case 3: ta0 += ty << 7 & 0x1000; break;
+ }
+ x = ppux0;
+ i = b->tnx;
+ for(; x < x1; tx++, i = 0){
+ ta = ta0 + ((tx & 0x1f) << 1);
+ if((bgcnt & 1<<14) != 0)
+ ta += tx << 6 & 0x800;
+ t = vram[ta] | vram[ta+1] << 8;
+ if(d)
+ pal = pram;
+ else
+ pal = pram + (t >> 8 & 0xf0);
+ ca = (bgcnt << 12 & 0xc000) + ((t & 0x3ff) << 5+d);
+ if(ca >= 0x10000)
+ continue;
+ y = b->tny;
+ if((t & 1<<11) != 0)
+ y ^= 7;
+ ca += y << 2+d;
+ cp = vram + ca;
+ for(; i < 8; i++, x++){
+ if(x >= x1)
+ goto out;
+ if((pixwin[x] & 1<<b->n) != 0 && !b->mosaic)
+ continue;
+ mx = i;
+ if((t & 1<<10) != 0)
+ mx ^= 7;
+ v = cp[mx >> 1-d];
+ if(!d)
+ if((mx & 1) != 0)
+ v >>= 4;
+ else
+ v &= 0xf;
+ if(v != 0)
+ bgpixel(b, x, pal[v], bgcnt & 3);
+ }
+ }
+out:
+ b->tx = tx;
+ b->tnx = i;
+}
+
+void
+rotbg(bg *b, int x1)
+{
+ uchar *p, v;
+ u16int bgcnt, *rr, ta;
+ int i, row, sz, x, y;
+
+ rr = reg + (b->n - 2 << 3);
+ if((reg[DISPCNT] & 1<<8 + b->n) == 0)
+ return;
+ bgcnt = reg[BG0CNT + b->n];
+ row = (bgcnt >> 14) + 4;
+ sz = 1 << 3 + row;
+ for(i = ppux0; i < x1; i++){
+ x = b->rpx >> 8;
+ y = b->rpy >> 8;
+ b->rpx += (s16int) rr[BG2PA];
+ b->rpy += (s16int) rr[BG2PC];
+ if((pixwin[i] & 1<<b->n) != 0 && !b->mosaic)
+ continue;
+ if((bgcnt & DISPWRAP) != 0){
+ x &= sz - 1;
+ y &= sz - 1;
+ }else if((uint)x >= sz || (uint)y >= sz)
+ continue;
+ ta = (bgcnt << 3 & 0xf800) + ((y >> 3) << row) + (x >> 3);
+ p = vram + (bgcnt << 12 & 0xc000) + (vram[ta] << 6);
+ p += (x & 7) + ((y & 7) << 3);
+ if((v = *p) != 0)
+ bgpixel(b, i, pram[v], bgcnt & 3);
+
+ }
+}
+
+void
+windows(int x1)
+{
+ static u8int wintab[8] = {2, 3, 1, 1, 0, 0, 0, 0};
+ int i, sx0, sx1;
+ u16int v, h;
+ u16int cnt;
+
+ cnt = reg[DISPCNT];
+ if((cnt >> 13) != 0){
+ if((cnt & 1<<13) != 0){
+ v = reg[WIN0V];
+ h = reg[WIN0H];
+ if(ppuy < (u8int)v && ppuy >= v >> 8){
+ sx1 = (u8int)h;
+ sx0 = h >> 8;
+ if(sx0 < ppux0)
+ sx0 = ppux0;
+ if(sx1 > x1)
+ sx1 = x1;
+ for(i = sx0; i < sx1; i++)
+ pixwin[i] |= WIN1;
+ }
+ }
+ if((cnt & 1<<14) != 0){
+ v = reg[WIN1V];
+ h = reg[WIN1H];
+ if(ppuy < (u8int)v && ppuy >= v >> 8){
+ sx1 = (u8int)h;
+ sx0 = h >> 8;
+ if(sx0 < ppux0)
+ sx0 = ppux0;
+ if(sx1 > x1)
+ sx1 = x1;
+ for(i = sx0; i < sx1; i++)
+ pixwin[i] |= WIN2;
+ }
+ }
+ for(i = ppux0; i < x1; i++){
+ v = wintab[pixwin[i]];
+ h = reg[WININ + (v & 2) / 2];
+ if((v & 1) != 0)
+ h >>= 8;
+ pixwin[i] = ~h;
+ }
+ }
+ for(i = ppux0; i < x1; i++)
+ if(pixpri[i] == VACANT || (pixwin[i] & 1<<4) != 0){
+ pixcol[i] = pram[0] | SRCBACK;
+ pixpri[i] = BACKDROP;
+ }else{
+ pixcol[i+240] = pram[0] | SRCBACK;
+ pixpri[i+240] = BACKDROP;
+ }
+ objalpha = 0;
+}
+
+u16int
+mix(u16int c1, u16int c2)
+{
+ u16int r, g, b, eva, evb;
+
+ eva = blda;
+ evb = bldb;
+ b = ((c1 & 0x7c00) * eva + (c2 & 0x7c00) * evb) >> 4;
+ g = ((c1 & 0x03e0) * eva + (c2 & 0x03e0) * evb) >> 4;
+ r = ((c1 & 0x001f) * eva + (c2 & 0x001f) * evb) >> 4;
+ if(b > 0x7c00) b = 0x7c00;
+ if(g > 0x03e0) g = 0x03e0;
+ if(r > 0x001f) r = 0x001f;
+ return b & 0x7c00 | g & 0x03e0 | r;
+}
+
+u16int
+brighten(u16int c1)
+{
+ u16int r, g, b, y;
+
+ y = bldy;
+ b = c1 & 0x7c00;
+ b = b + (0x7c00 - b) * y / 16;
+ g = c1 & 0x03e0;
+ g = g + (0x03e0 - g) * y / 16;
+ r = c1 & 0x001f;
+ r = r + (0x001f - r) * y / 16;
+ if(b > 0x7c00) b = 0x7c00;
+ if(g > 0x03e0) g = 0x03e0;
+ if(r > 0x001f) r = 0x001f;
+ return b & 0x7c00 | g & 0x03e0 | r;
+}
+
+u16int
+darken(u16int c1)
+{
+ u16int r, g, b, y;
+
+ y = 16 - bldy;
+ b = c1 & 0x7c00;
+ b = b * y / 16;
+ g = c1 & 0x03e0;
+ g = g * y / 16;
+ r = c1 & 0x001f;
+ r = r * y / 16;
+ return b & 0x7c00 | g & 0x03e0 | r;
+}
+
+void
+colormath(int x1)
+{
+ u16int bldcnt;
+ u32int *p;
+ int i;
+
+ bldcnt = reg[BLDCNT];
+ if((bldcnt & 3<<6) == 0 && objalpha == 0)
+ return;
+ p = pixcol + ppux0;
+ for(i = ppux0; i < x1; i++, p++){
+ if((*p & OBJALPHA) != 0)
+ goto alpha;
+ if((pixwin[i] & 1<<5) != 0 || (bldcnt & 1<<(*p >> 17)) == 0)
+ continue;
+ switch(bldcnt >> 6 & 3){
+ case 1:
+ alpha:
+ if((bldcnt & 1<<8+(p[240] >> 17)) == 0)
+ continue;
+ *p = mix(*p, p[240]);
+ break;
+ case 2:
+ *p = brighten(*p);
+ break;
+ case 3:
+ *p = darken(*p);
+ break;
+ }
+ }
+}
+
+void
+syncppu(int x1)
+{
+ int i;
+ u16int cnt;
+
+ if(hblank || ppuy >= 160)
+ return;
+ if(x1 >= 240)
+ x1 = 240;
+ else if(x1 <= ppux0)
+ return;
+ cnt = reg[DISPCNT];
+ if((cnt & FBLANK) != 0){
+ for(i = ppux0; i < x1; i++)
+ pixcol[i] = 0xffff;
+ ppux0 = x1;
+ return;
+ }
+
+ if((cnt & 1<<12) != 0)
+ spr(x1);
+ windows(x1);
+ switch(cnt & 7){
+ case 0:
+ txtbg(&bgst[0], x1);
+ txtbg(&bgst[1], x1);
+ txtbg(&bgst[2], x1);
+ txtbg(&bgst[3], x1);
+ break;
+ case 1:
+ txtbg(&bgst[0], x1);
+ txtbg(&bgst[1], x1);
+ rotbg(&bgst[2], x1);
+ break;
+ case 2:
+ rotbg(&bgst[2], x1);
+ rotbg(&bgst[3], x1);
+ break;
+ case 3:
+ case 4:
+ case 5:
+ bitbg(&bgst[2], x1);
+ }
+ colormath(x1);
+ ppux0 = x1;
+}
+
+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;
+ r = (u16int*)q;
+ n = 240;
+ while(n--){
+ v = *p++;
+ u.b[0] = v;
+ u.b[1] = v >> 8;
+ *r++ = u.w;
+ }
+}
+
+void
+hblanktick(void *)
+{
+ extern Event evhblank;
+ u16int stat;
+
+ stat = reg[DISPSTAT];
+ if(hblank){
+ hblclock = clock + evhblank.time;
+ addevent(&evhblank, 240*4);
+ hblank = 0;
+ ppux0 = 0;
+ memset(pixpri, VACANT, sizeof(pixpri));
+ memset(pixwin, 0, 240);
+ if(++ppuy >= 228){
+ ppuy = 0;
+ flush();
+ }
+ if(ppuy < 160){
+ sprinit();
+ bgsinit();
+ }else if(ppuy == 160){
+ dmastart(DMAVBL);
+ if((stat & IRQVBLEN) != 0)
+ setif(IRQVBL);
+ }
+ if((stat & IRQVCTREN) != 0 && ppuy == stat >> 8)
+ setif(IRQVCTR);
+ }else{
+ syncppu(240);
+ if(ppuy < 160)
+ linecopy(pixcol, ppuy);
+ addevent(&evhblank, 68*4);
+ hblank = 1;
+ if((stat & IRQHBLEN) != 0)
+ setif(IRQHBL);
+ if(ppuy < 160)
+ dmastart(DMAHBL);
+ }
+}
+
+void
+ppuwrite(u16int a, u16int v)
+{
+ syncppu((clock - hblclock) / 4);
+ switch(a){
+ case BLDALPHA*2:
+ blda = v & 0x1f;
+ if(blda > 16)
+ blda = 16;
+ bldb = v >> 8 & 0x1f;
+ if(bldb > 16)
+ bldb = 16;
+ break;
+ case BLDY*2:
+ bldy = v & 0x1f;
+ if(bldy > 16)
+ bldy = 16;
+ break;
+ case BG2XL*2: bgst[2].rpx0 = bgst[2].rpx0 & 0xffff0000 | v; break;
+ case BG2XH*2: bgst[2].rpx0 = bgst[2].rpx0 & 0xffff | (s32int)(v << 20) >> 4; break;
+ case BG2YL*2: bgst[2].rpy0 = bgst[2].rpy0 & 0xffff0000 | v; break;
+ case BG2YH*2: bgst[2].rpy0 = bgst[2].rpy0 & 0xffff | (s32int)(v << 20) >> 4; break;
+ case BG3XL*2: bgst[3].rpx0 = bgst[3].rpx0 & 0xffff0000 | v; break;
+ case BG3XH*2: bgst[3].rpx0 = bgst[3].rpx0 & 0xffff | (s32int)(v << 20) >> 4; break;
+ case BG3YL*2: bgst[3].rpy0 = bgst[3].rpy0 & 0xffff0000 | v; break;
+ case BG3YH*2: bgst[3].rpy0 = bgst[3].rpy0 & 0xffff | (s32int)(v << 20) >> 4; break;
+ }
+}
--- /dev/null
+++ b/sys/src/games/md/vdp.c
@@ -1,0 +1,378 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "../eui.h"
+#include "dat.h"
+#include "fns.h"
+
+u16int vdpstat = 0x3400;
+int vdpx, vdpy, vdpyy, frame, intla;
+u16int hctr;
+static int xmax, xdisp;
+static int sx, snx, col, pri, lum;
+enum { DARK, NORM, BRIGHT };
+enum { ymax = 262, yvbl = 234 };
+
+void
+vdpmode(void)
+{
+ if((reg[MODE4] & WIDE) != 0){
+ xmax = 406;
+ xdisp = 320;
+ }else{
+ xdisp = 256;
+ xmax = 342;
+ }
+ intla = (reg[MODE4] & 6) == 6;
+}
+
+static void
+pixeldraw(int x, int y, int v)
+{
+ u32int *p;
+ union { u32int l; u8int b[4]; } u;
+
+ u.b[0] = v >> 16;
+ u.b[1] = v >> 8;
+ u.b[2] = v;
+ u.b[3] = 0;
+ *((u32int *)pic + (x + y * 320)) = u.l;
+}
+
+static u32int
+shade(u32int v, int l)
+{
+ if(l == 1)
+ return v;
+ if(l == 2)
+ return v << 1 & 0xefefef;
+ return v >> 1 & 0xf7f7f7;
+}
+
+static void
+pixel(int v, int p)
+{
+ if(p >= pri){
+ col = v;
+ pri = p;
+ }
+}
+
+struct pctxt {
+ u8int ws, w, hs, h;
+ u16int tx, ty;
+ u8int tnx, tny;
+ u16int t;
+ u32int c;
+} pctxt[3];
+int lwin, rwin;
+
+static void
+tile(struct pctxt *p)
+{
+ u16int a;
+ int y;
+
+ switch(p - pctxt){
+ default: a = (reg[PANT] & 0x38) << 9; break;
+ case 1: a = (reg[PBNT] & 7) << 12; break;
+ case 2: a = (reg[PWNT] & 0x3e) << 9; break;
+ }
+ a += p->ty << p->ws;
+ a += p->tx;
+ p->t = vram[a];
+ y = p->tny;
+ if(intla){
+ if((p->t & 0x1000) != 0)
+ y = 15 - y;
+ a = (p->t & 0x7ff) << 5 | y << 1;
+ }else{
+ if((p->t & 0x1000) != 0)
+ y = 7 - y;
+ a = (p->t & 0x7ff) << 4 | y << 1;
+ }
+ p->c = vram[a] << 16 | vram[a+1];
+}
+
+static void
+planeinit(void)
+{
+ static int szs[] = {5, 6, 6, 7};
+ int v, a, i;
+ struct pctxt *p;
+
+ pctxt[0].hs = pctxt[1].hs = szs[reg[PLSIZ] >> 4 & 3];
+ pctxt[0].ws = pctxt[1].ws = szs[reg[PLSIZ] & 3];
+ pctxt[2].ws = (reg[MODE4] & WIDE) != 0 ? 6 : 5;
+ pctxt[2].hs = 5;
+ for(i = 0; i <= 2; i++){
+ pctxt[i].h = 1<<pctxt[i].hs;
+ pctxt[i].w = 1<<pctxt[i].ws;
+ }
+ a = reg[HORSCR] << 9 & 0x7fff;
+ switch(reg[MODE3] & 3){
+ case 1: a += vdpy << 1 & 0xe; break;
+ case 2: a += vdpy << 1 & 0xff0; break;
+ case 3: a += vdpy << 1 & 0xffe; break;
+ }
+ for(i = 0; i < 2; i++){
+ p = pctxt + i;
+ v = -(vram[a + i] & 0x3ff);
+ p->tnx = v & 7;
+ p->tx = v >> 3 & pctxt[i].w - 1;
+ if(intla){
+ v = vsram[i] + vdpyy;
+ p->tny = v & 15;
+ p->ty = v >> 4 & pctxt[i].h - 1;
+ }else{
+ v = vsram[i] + vdpy;
+ p->tny = v & 7;
+ p->ty = v >> 3 & pctxt[i].h - 1;
+ }
+ tile(p);
+ if(p->tnx != 0)
+ if((p->t & 0x800) != 0)
+ p->c >>= p->tnx << 2;
+ else
+ p->c <<= p->tnx << 2;
+ }
+ sx = 0;
+ snx = 0;
+ v = reg[WINV] << 3 & 0xf8;
+ if((reg[WINV] & 0x80) != 0 ? vdpy < v : vdpy >= v){
+ lwin = 0;
+ rwin = reg[WINH] << 4 & 0x1f0;
+ if((reg[WINH] & 0x80) != 0){
+ lwin = rwin;
+ rwin = 320;
+ }
+ }else{
+ lwin = 0;
+ rwin = 320;
+ }
+ if(rwin > lwin){
+ p = pctxt + 2;
+ p->tx = lwin >> 3 & pctxt[2].w - 1;
+ p->tnx = lwin & 7;
+ p->tny = vdpy & 7;
+ p->ty = vdpy >> 3 & pctxt[2].h - 1;
+ tile(p);
+ }
+}
+
+static void
+plane(int n, int vis)
+{
+ struct pctxt *p;
+ u8int v, pr;
+
+ p = pctxt + n;
+ if((p->t & 0x800) != 0){
+ v = p->c & 15;
+ p->c >>= 4;
+ }else{
+ v = p->c >> 28;
+ p->c <<= 4;
+ }
+ if(vis != 0){
+ if(v != 0){
+ v |= p->t >> 9 & 48;
+ pr = 2 - (n & 1) + (p->t >> 13 & 4);
+ pixel(v, pr);
+ }
+ lum |= p->t >> 15;
+ }
+ if(++p->tnx == 8){
+ p->tnx = 0;
+ if(++p->tx == p->w)
+ p->tx = 0;
+ tile(pctxt + n);
+ }
+}
+
+static void
+planes(void)
+{
+ int i, w;
+ u16int v;
+
+ if((reg[MODE3] & 4) != 0 && ++snx == 16){
+ snx = 0;
+ sx++;
+ for(i = 0; i < 2; i++){
+ v = vsram[sx + i] + vdpy;
+ pctxt[i].tny = v & 7;
+ pctxt[i].ty = v >> 3 & pctxt[i].h - 1;
+ }
+ }
+ w = vdpx < rwin && vdpx >= lwin;
+ plane(0, !w);
+ plane(1, 1);
+ if(w)
+ plane(2, 1);
+}
+
+static struct sprite {
+ u16int y, x;
+ u8int w, h;
+ u16int t;
+ u32int c[4];
+} spr[21], *lsp;
+
+static void
+spritesinit(void)
+{
+ u16int t, *p, dy, c;
+ u32int v;
+ int i, ns, np, nt;
+ struct sprite *q;
+
+ t = (reg[SPRTAB] << 8 & 0x7f00);
+ p = vram + t;
+ q = spr;
+ ns = (reg[MODE4] & WIDE) != 0 ? 20 : 16;
+ np = 0;
+ nt = 0;
+ do{
+ if(intla){
+ q->y = (p[0] & 0x3ff) - 256;
+ q->h = (p[1] >> 8 & 3) + 1 << 4;
+ dy = vdpyy - q->y;
+ }else{
+ q->y = (p[0] & 0x3ff) - 128;
+ q->h = (p[1] >> 8 & 3) + 1 << 3;
+ dy = vdpy - q->y;
+ }
+ if(dy >= q->h)
+ continue;
+ q->t = p[2];
+ if((q->t & 0x1000) != 0)
+ dy = q->h + ~dy;
+ q->x = (p[3] & 0x3ff) - 128;
+ if(q->x == 0xff80)
+ break;
+ q->w = (p[1] >> 10 & 3) + 1 << 3;
+ c = ((q->t & 0x7ff) << 4+intla) + (dy << 1);
+ for(i = 0; i < q->w >> 3 && np < xdisp; i++){
+ v = vram[c] << 16 | vram[(u16int)(c+1)];
+ c += q->h << 1;
+ if((q->t & 0x800) != 0)
+ q->c[(q->w >> 3) - 1 - i] = v;
+ else
+ q->c[i] = v;
+ np += 8;
+ }
+ if((u16int)-q->x < q->w){
+ i = -(s16int)q->x;
+ if((q->t & 0x800) != 0)
+ q->c[i>>3] >>= (i & 7) << 2;
+ else
+ q->c[i>>3] <<= (i & 7) << 2;
+ }
+ if(++q == spr + ns || np >= xdisp){
+ vdpstat |= STATOVR;
+ break;
+ }
+ }while(p = vram + (u16int)(t + ((p[1] & 0x7f) << 2)), p - vram != t && ++nt < 80);
+ lsp = q;
+}
+
+static void
+sprites(void)
+{
+ struct sprite *p;
+ u16int dx;
+ int v, col, set;
+ u32int *c;
+
+ set = 0;
+ col = 0;
+ for(p = spr; p < lsp; p++){
+ dx = vdpx - p->x;
+ if(dx >= p->w)
+ continue;
+ c = p->c + (dx >> 3);
+ if((p->t & 0x800) != 0){
+ v = *c & 15;
+ *c >>= 4;
+ }else{
+ v = *c >> 28;
+ *c <<= 4;
+ }
+ if(v != 0)
+ if(set != 0)
+ vdpstat |= STATCOLL;
+ else{
+ set = 1 | p->t & 0x8000;
+ col = p->t >> 9 & 48 | v;
+ }
+ }
+ if(set)
+ if((reg[MODE4] & SHI) != 0)
+ if((col & 0xfe) == 0x3e)
+ lum = col & 1;
+ else{
+ pixel(col, set >> 13 | 2);
+ if((col & 0xf) == 0xe)
+ lum = 1;
+ else
+ lum |= set >> 15;
+ }
+ else
+ pixel(col, set >> 13 | 2);
+}
+
+void
+vdpstep(void)
+{
+ u32int v;
+
+ if(vdpx == 0){
+ planeinit();
+ spritesinit();
+ }
+ if(vdpx < 320 && vdpy < 224)
+ if(vdpx < xdisp){
+ col = reg[BGCOL] & 0x3f;
+ pri = 0;
+ lum = 0;
+ planes();
+ sprites();
+ if((reg[MODE2] & 0x40) != 0 && (vdpx >= 8 || (reg[MODE1] & 0x20) == 0)){
+ v = cramc[col];
+ if((reg[MODE4] & SHI) != 0)
+ v = shade(v, lum);
+ pixeldraw(vdpx, vdpy, v);
+ }else
+ pixeldraw(vdpx, vdpy, 0);
+ }else
+ pixeldraw(vdpx, vdpy, 0xcccccc);
+ if(++vdpx >= xmax){
+ z80irq = 0;
+ vdpx = 0;
+ if(++vdpy >= ymax){
+ vdpy = 0;
+ irq &= ~INTVBL;
+ vdpstat ^= STATFR;
+ vdpstat &= ~(STATINT | STATVBL | STATOVR | STATCOLL);
+ flush();
+ }
+ if(intla)
+ vdpyy = vdpy << 1 | frame;
+ if(vdpy == 0 || vdpy >= 225)
+ hctr = reg[HORCTR];
+ else
+ if(hctr-- == 0){
+ if((reg[MODE1] & IE1) != 0)
+ irq |= INTHOR;
+ hctr = reg[HORCTR];
+ }
+ if(vdpy == yvbl){
+ vdpstat |= STATVBL | STATINT;
+ frame ^= 1;
+ z80irq = 1;
+ if((reg[MODE2] & IE0) != 0)
+ irq |= INTVBL;
+ }
+ }
+}
--- /dev/null
+++ b/sys/src/games/nes/ppu.c
@@ -1,0 +1,280 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <mouse.h>
+#include "../eui.h"
+#include "dat.h"
+#include "fns.h"
+
+int ppuy, ppux, odd;
+
+static void
+pixel(int x, int y, int val, int back)
+{
+ union { u8int c[4]; u32int l; } u;
+ static u8int palred[64] = {
+ 0x7C, 0x00, 0x00, 0x44, 0x94, 0xA8, 0xA8, 0x88,
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xBC, 0x00, 0x00, 0x68, 0xD8, 0xE4, 0xF8, 0xE4,
+ 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xF8, 0x3C, 0x68, 0x98, 0xF8, 0xF8, 0xF8, 0xFC,
+ 0xF8, 0xB8, 0x58, 0x58, 0x00, 0x78, 0x00, 0x00,
+ 0xFC, 0xA4, 0xB8, 0xD8, 0xF8, 0xF8, 0xF0, 0xFC,
+ 0xF8, 0xD8, 0xB8, 0xB8, 0x00, 0xF8, 0x00, 0x00,
+ };
+ static u8int palgreen[64] = {
+ 0x7C, 0x00, 0x00, 0x28, 0x00, 0x00, 0x10, 0x14,
+ 0x30, 0x78, 0x68, 0x58, 0x40, 0x00, 0x00, 0x00,
+ 0xBC, 0x78, 0x58, 0x44, 0x00, 0x00, 0x38, 0x5C,
+ 0x7C, 0xB8, 0xA8, 0xA8, 0x88, 0x00, 0x00, 0x00,
+ 0xF8, 0xBC, 0x88, 0x78, 0x78, 0x58, 0x78, 0xA0,
+ 0xB8, 0xF8, 0xD8, 0xF8, 0xE8, 0x78, 0x00, 0x00,
+ 0xFC, 0xE4, 0xB8, 0xB8, 0xB8, 0xA4, 0xD0, 0xE0,
+ 0xD8, 0xF8, 0xF8, 0xF8, 0xFC, 0xD8, 0x00, 0x00,
+ };
+ static u8int palblue[64] = {
+ 0x7C, 0xFC, 0xBC, 0xBC, 0x84, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0xBC, 0xF8, 0xF8, 0xFC, 0xCC, 0x58, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x44, 0x88, 0x00, 0x00, 0x00,
+ 0xF8, 0xFC, 0xFC, 0xF8, 0xF8, 0x98, 0x58, 0x44,
+ 0x00, 0x18, 0x54, 0x98, 0xD8, 0x78, 0x00, 0x00,
+ 0xFC, 0xFC, 0xF8, 0xF8, 0xF8, 0xC0, 0xB0, 0xA8,
+ 0x78, 0x78, 0xB8, 0xD8, 0xFC, 0xF8, 0x00, 0x00,
+ };
+
+ u.c[0] = palblue[val];
+ u.c[1] = palgreen[val];
+ u.c[2] = palred[val];
+ u.c[3] = back ? 0 : 0xFF;
+ *((u32int *)pic + y * 256 + x) = u.l;
+}
+
+static int
+iscolor(int x, int y)
+{
+ return pic[4 * (y * 256 + x) + 3] != 0;
+}
+
+static int
+pal(int c, int a, int spr)
+{
+ if(c == 0)
+ return ppuread(0x3F00);
+ return ppuread(0x3F00 | ((a&3)<<2) | (c & 3) | (spr << 4));
+}
+
+static void
+incppuy(void)
+{
+ int y;
+
+ if((ppuv & 0x7000) != 0x7000){
+ ppuv += 0x1000;
+ return;
+ }
+ y = (ppuv >> 5) & 31;
+ if(y++ == 29){
+ y = 0;
+ ppuv ^= 0x800;
+ }
+ ppuv = (ppuv & 0x0C1F) | ((y & 31) << 5);
+}
+
+static void
+drawbg(void)
+{
+ static int t;
+ u8int c, a;
+ static u8int nr1, nr2, na;
+ static u16int r1, r2, a1, a2;
+
+ if(ppux >= 2 && ppux <= 257 || ppux >= 322 && ppux <= 337){
+ c = (r1 >> (15-ppusx)) & 1 | (r2 >> (14-ppusx)) & 2;
+ if(ppuy < 240 && ppux <= 257){
+ a = (a1 >> (15-ppusx)) & 1 | (a2 >> (14-ppusx)) & 2;
+ pixel(ppux-2, ppuy, pal(c, a, 0), c == 0);
+ }
+ r1 <<= 1;
+ r2 <<= 1;
+ a1 <<= 1;
+ a2 <<= 1;
+ }
+ if(ppux >= 256 && ppux <= 320){
+ if(ppux == 256)
+ incppuy();
+ if(ppux == 257)
+ ppuv = (ppuv & 0x7BE0) | (pput & 0x041F);
+ return;
+ }
+ switch(ppux & 7){
+ case 0:
+ if(ppux != 0){
+ if((ppuv & 0x1f) == 0x1f){
+ ppuv &= ~0x1f;
+ ppuv ^= 0x400;
+ }else
+ ppuv++;
+ }
+ break;
+ case 1:
+ t = ppuread(0x2000 | ppuv & 0x0FFF);
+ if(ppux != 1){
+ r1 |= nr1;
+ r2 |= nr2;
+ if(na & 1)
+ a1 |= 0xff;
+ if(na & 2)
+ a2 |= 0xff;
+ }
+ break;
+ case 3:
+ na = ppuread(0x23C0 | ppuv & 0x0C00 | ((ppuv & 0x0380) >> 4) | ((ppuv & 0x001C) >> 2));
+ if((ppuv & 0x0002) != 0) na >>= 2;
+ if((ppuv & 0x0040) != 0) na >>= 4;
+ break;
+ case 5:
+ nr1 = ppuread(((mem[PPUCTRL] & BGTABLE) << 8) | t << 4 | ppuv >> 12);
+ break;
+ case 7:
+ nr2 = ppuread(((mem[PPUCTRL] & BGTABLE) << 8) | t << 4 | ppuv >> 12 | 8);
+ break;
+ }
+}
+
+static void
+drawsprites(int show)
+{
+ uchar *p;
+ int big, dx, dy, i, x, cc, pri;
+ u8int r1, r2, c;
+ static int n, m, nz, s0, t0;
+ static struct { u8int x, a; u16int t; } s[8], *sp;
+ static struct { u8int x, a, r1, r2; } t[8];
+
+ big = (mem[PPUCTRL] & BIGSPRITE) != 0;
+ if(ppux == 65){
+ s0 = 0;
+ for(p = oam, sp = s, n = 0; p < oam + sizeof(oam); p += 4){
+ if((dy = p[0]) >= 0xEF)
+ continue;
+ dy = ppuy - dy;
+ if(dy < 0 || dy >= (big ? 16 : 8))
+ continue;
+ if(p == oam)
+ s0 = 1;
+ sp->t = p[1];
+ sp->a = p[2];
+ sp->x = p[3];
+ if((sp->a & (1<<7)) != 0)
+ dy = (big ? 15 : 7) - dy;
+ if(big){
+ sp->t |= (sp->t & 1) << 8;
+ if(dy >= 8){
+ sp->t |= 1;
+ dy -= 8;
+ }else
+ sp->t &= 0x1fe;
+ }else
+ sp->t |= (mem[PPUCTRL] & SPRTABLE) << 5;
+ sp->t = sp->t << 4 | dy;
+ sp++;
+ if(++n == 8)
+ break;
+ }
+ }
+ if(ppux >= 2 && ppux <= 257 && m > 0){
+ x = ppux - 2;
+ dx = x - t[0].x;
+ if(t0 && dx >= 0 && dx < 8 && ppux != 257){
+ if((nz & 1) != 0 && iscolor(x, ppuy) && show)
+ mem[PPUSTATUS] |= SPRITE0HIT;
+ nz >>= 1;
+ }
+ cc = -1;
+ pri = 0;
+ for(i = m - 1; i >= 0; i--){
+ dx = x - t[i].x;
+ if(dx < 0 || dx > 7)
+ continue;
+ c = (t[i].r1 & 1) | (t[i].r2 & 1) << 1;
+ if(c != 0){
+ cc = pal(c, t[i].a & 3, 1);
+ pri = (t[i].a & (1<<5)) == 0;
+ }
+ t[i].r1 >>= 1;
+ t[i].r2 >>= 1;
+ }
+ if(cc != -1 && show && (pri || !iscolor(x, ppuy)))
+ pixel(x, ppuy, cc, 0);
+ }
+ if(ppux == 257){
+ for(i = 0; i < n; i++){
+ r1 = ppuread(s[i].t);
+ r2 = ppuread(s[i].t | 8);
+ if((s[i].a & (1<<6)) == 0){
+ r1 = ((r1 * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
+ r2 = ((r2 * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
+ }
+ t[i].x = s[i].x;
+ t[i].a = s[i].a;
+ t[i].r1 = r1;
+ t[i].r2 = r2;
+ }
+ m = n;
+ nz = t[0].r1 | t[0].r2;
+ t0 = s0;
+ }
+}
+
+static void
+flush(void)
+{
+ flushmouse(1);
+ flushscreen();
+ flushaudio(audioout);
+}
+
+void
+ppustep(void)
+{
+ extern int nmi;
+ int mask;
+
+ if(ppuy < 240 || ppuy == 261){
+ mask = mem[PPUMASK];
+ if((mask & BGDISP) != 0)
+ drawbg();
+ if((((mask & BGDISP) == 0 && ppux <= 257 || ppux < 10 && (mask & BG8DISP) == 0) && ppux >= 2) && ppuy != 261)
+ pixel(ppux - 2, ppuy, ppuread(0x3F00), 1);
+ if((mask & SPRITEDISP) != 0 && ppuy != 261)
+ drawsprites(ppux >= 10 || (mask & SPRITE8DISP) != 0);
+ if(ppux == 240 && (mask & SPRITEDISP) != 0)
+ mapper[map](SCAN, 0);
+ if(ppuy == 261){
+ if(ppux == 1)
+ mem[PPUSTATUS] &= ~(PPUVBLANK|SPRITE0HIT);
+ else if(ppux >= 280 && ppux <= 304 && (mask & BGDISP) != 0)
+ ppuv = (pput & 0x7BE0) | (ppuv & 0x041F);
+ }
+ }else if(ppuy == 241){
+ if(ppux == 1){
+ mem[PPUSTATUS] |= PPUVBLANK;
+ if((mem[PPUCTRL] & PPUNMI) != 0)
+ nmi = 2;
+ flush();
+ }
+ }
+ ppux++;
+ if(ppux > 340){
+ ppux = 0;
+ ppuy++;
+ if(ppuy > 261){
+ ppuy = 0;
+ if(odd && (mem[PPUMASK] & (BGDISP | SPRITEDISP)) != 0)
+ ppux++;
+ odd ^= 1;
+ }
+ }
+}
--- /dev/null
+++ b/sys/src/games/snes/ppu.c
@@ -1,0 +1,936 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "../eui.h"
+#include "dat.h"
+#include "fns.h"
+
+int ppux, ppuy, rx;
+static u8int mode, bright, pixelpri[2], hires;
+static u32int pixelcol[2];
+u16int vtime = 0x1ff, htime = 0x1ff, subcolor;
+u16int hofs[5], vofs[5];
+s16int m7[6];
+
+enum {
+ M7A,
+ M7B,
+ M7C,
+ M7D,
+ M7X,
+ M7Y
+};
+
+enum { OBJ = 4, COL = 5, OBJNC = 8 };
+
+static u16int
+darken(u16int v)
+{
+ u8int r, g, b;
+
+ r = (v >> 10) & 0x1f;
+ g = (v >> 5) & 0x1f;
+ b = v & 0x1f;
+ r = r * bright / 15;
+ g = g * bright / 15;
+ b = b * bright / 15;
+ return r << 10 | g << 5 | b;
+}
+
+static void
+pixeldraw(int x, int y, u16int v, int s)
+{
+ union { u16int w; u8int b[2]; } u;
+
+ if(bright != 0xf && s >= 0)
+ v = darken(v);
+ u.b[0] = v;
+ u.b[1] = v >> 8;
+ *((u16int *)pic + (x + y * 256)) = u.w; /* FIXME: some weird shit in the diff */
+}
+
+static int
+window(int n)
+{
+ int a, w1, w2;
+
+ a = reg[0x2123 + (n >> 1)];
+ if((n & 1) != 0)
+ a >>= 4;
+ if((a & (WIN1|WIN2)) == 0)
+ return 0;
+ w1 = rx >= reg[0x2126] && rx <= reg[0x2127];
+ w2 = rx >= reg[0x2128] && rx <= reg[0x2129];
+ if((a & INVW1) != 0)
+ w1 = !w1;
+ if((a & INVW2) != 0)
+ w2 = !w2;
+ if((a & (WIN1|WIN2)) != (WIN1|WIN2))
+ return (a & WIN1) != 0 ? w1 : w2;
+ a = reg[0x212a + (n >> 2)] >> ((n & 3) << 1);
+ switch(a & 3){
+ case 1: return w1 & w2;
+ case 2: return w1 ^ w2;
+ case 3: return w1 ^ w2 ^ 1;
+ }
+ return w1 | w2;
+}
+
+static void
+npixel(int n, int v, int pri)
+{
+ int a;
+
+ a = 1<<n;
+ if((reg[TM] & a) != 0 && pri > pixelpri[0] && ((reg[TMW] & a) == 0 || !window(n))){
+ pixelcol[0] = v;
+ pixelpri[0] = pri;
+ }
+ if((reg[TS] & a) != 0 && pri > pixelpri[1] && ((reg[TSW] & a) == 0 || !window(n))){
+ pixelcol[1] = v;
+ pixelpri[1] = pri;
+ }
+}
+
+static void
+spixel(int n, int v, int pri)
+{
+ int a;
+
+ a = 1<<n;
+ if((reg[TS] & a) != 0 && pri > pixelpri[1] && ((reg[TSW] & a) == 0 || !window(n))){
+ pixelcol[1] = v;
+ pixelpri[1] = pri;
+ }
+}
+
+static void
+mpixel(int n, int v, int pri)
+{
+ int a;
+
+ a = 1<<n;
+ if((reg[TM] & a) != 0 && pri > pixelpri[0] && ((reg[TMW] & a) == 0 || !window(n))){
+ pixelcol[0] = v;
+ pixelpri[0] = pri;
+ }
+}
+
+static void (*pixel)(int, int, int) = npixel;
+
+static u16int
+tile(int n, int tx, int ty)
+{
+ int a;
+ u16int ta;
+ u16int t;
+
+ a = reg[0x2107 + n];
+ ta = ((a & ~3) << 9) + ((tx & 0x1f) << 1) + ((ty & 0x1f) << 6);
+ if((a & 1) != 0)
+ ta += (tx & 0x20) << 6;
+ if((a & 2) != 0)
+ ta += (ty & 0x20) << (6 + (a & 1));
+ t = vram[ta++];
+ return t | vram[ta] << 8;
+}
+
+static void
+chr(int n, int nb, int w, int h, u16int t, int x, int y, u32int c[])
+{
+ u16int a;
+
+ if(w == 16)
+ t += (x >> 3 ^ t >> 14) & 1;
+ if(h == 16){
+ if(y >= 8){
+ t += (~t >> 11) & 16;
+ y -= 8;
+ }else
+ t += (t >> 11) & 16;
+ }
+ if((t & 0x8000) != 0)
+ y = 7 - y;
+ a = reg[0x210b + (n >> 1)];
+ if((n & 1) != 0)
+ a >>= 4;
+ else
+ a &= 0xf;
+ a = (a << 13) + (t & 0x3ff) * 8 * nb + y * 2;
+ c[0] = vram[a++];
+ c[0] |= vram[a] << 8;
+ if(nb != 2){
+ a += 15;
+ c[0] |= vram[a++] << 16;
+ c[0] |= vram[a] << 24;
+ if(nb == 8){
+ a += 15;
+ c[1] = vram[a++];
+ c[1] |= vram[a] << 8;
+ a += 15;
+ c[1] |= vram[a++] << 16;
+ c[1] |= vram[a] << 24;
+ }
+ }
+}
+
+static int
+palette(int n, int p)
+{
+ switch(mode){
+ case 0:
+ return p << 2 | n << 5;
+ case 1:
+ if(n >= 2)
+ return p << 2;
+ case 2:
+ case 6:
+ return p << 4;
+ case 5:
+ if(n == 0)
+ return p << 4;
+ return p << 2;
+ case 3:
+ if(n != 0)
+ return p << 4;
+ case 4:
+ if(n != 0)
+ return p << 2;
+ if((reg[CGWSEL] & DIRCOL) != 0)
+ return 0x10000;
+ }
+ return 0;
+}
+
+static void
+shift(u32int *c, int nb, int n, int d)
+{
+ if(d){
+ c[0] >>= n;
+ if(nb == 8)
+ c[1] >>= n;
+ }else{
+ c[0] <<= n;
+ if(nb == 8)
+ c[1] <<= n;
+ }
+}
+
+static u8int
+bgpixel(u32int *c, int nb, int d)
+{
+ u8int v;
+
+ if(d){
+ v = c[0] & 1 | c[0] >> 7 & 2;
+ if(nb != 2){
+ v |= c[0] >> 14 & 4 | c[0] >> 21 & 8;
+ if(nb == 8){
+ v |= c[1] << 4 & 16 | c[1] >> 3 & 32 | c[1] >> 10 & 64 | c[1] >> 17 & 128;
+ c[1] >>= 1;
+ }
+ }
+ c[0] >>= 1;
+ }else{
+ v = c[0] >> 7 & 1 | c[0] >> 14 & 2;
+ if(nb != 2){
+ v |= c[0] >> 21 & 4 | c[0] >> 28 & 8;
+ if(nb == 8){
+ v |= c[1] >> 3 & 16 | c[1] >> 10 & 32 | c[1] >> 17 & 64 | c[1] >> 24 & 128;
+ c[1] <<= 1;
+ }
+ }
+ c[0] <<= 1;
+ }
+ return v;
+}
+
+static struct bgctxt {
+ u8int w, h, wsh, hsh, nb, pri[2];
+ u16int tx, ty, tnx, tny;
+ u16int t;
+ u32int c[2];
+ int pal;
+ u8int msz, mv, mx;
+ u16int otx, oty, otny;
+} bgctxts[4];
+
+static void
+bginit(int n, int nb, int prilo, int prihi)
+{
+ struct bgctxt *p;
+ int sx, sy;
+
+ p = bgctxts + n;
+ p->hsh = ((reg[BGMODE] & (1<<(4+n))) != 0) ? 4 : 3;
+ p->wsh = mode >= 5 ? 4 : p->hsh;
+ p->h = 1<<p->hsh;
+ p->w = 1<<p->wsh;
+ p->nb = nb;
+ p->pri[0] = prilo;
+ p->pri[1] = prihi;
+ sx = hofs[n];
+ sy = vofs[n] + ppuy;
+ if(reg[MOSAIC] != 0 && (reg[MOSAIC] & (1<<n)) != 0){
+ p->msz = (reg[MOSAIC] >> 4) + 1;
+ if(p->msz != 1){
+ sx -= p->mx = sx % p->msz;
+ sy -= sy % p->msz;
+ }
+ }else
+ p->msz = 1;
+ if(mode >= 5)
+ sx <<= 1;
+redo:
+ p->tx = sx >> p->wsh;
+ p->tnx = sx & (p->w - 1);
+ p->ty = sy >> p->hsh;
+ p->tny = sy & (p->h - 1);
+ p->t = tile(n, p->tx, p->ty);
+ chr(n, nb, p->w, p->h, p->t, p->tnx, p->tny, p->c);
+ p->pal = palette(n, p->t >> 10 & 7);
+ if((p->tnx & 7) != 0)
+ shift(p->c, nb, p->tnx & 7, p->t & 0x4000);
+ if(p->msz != 1 && p->mx != 0 && sx % p->msz == 0){
+ p->mv = bgpixel(p->c, nb, p->t & 0x4000);
+ if(p->tnx + p->mx >= 8){
+ sx += p->mx;
+ goto redo;
+ }else if(p->mx > 1)
+ shift(p->c, nb, p->mx - 1, p->t & 0x4000);
+ }
+
+}
+
+static void
+bg(int n)
+{
+ struct bgctxt *p;
+ u8int v;
+
+ p = bgctxts + n;
+ v = bgpixel(p->c, p->nb, p->t & 0x4000);
+ if(p->msz != 1)
+ if(p->mx++ == 0)
+ p->mv = v;
+ else{
+ if(p->mx == p->msz)
+ p->mx = 0;
+ v = p->mv;
+ }
+ if(v != 0)
+ pixel(n, p->pal + v, p->pri[(p->t & 0x2000) != 0]);
+ if(++p->tnx == p->w){
+ p->tx++;
+ p->tnx = 0;
+ p->t = tile(n, p->tx, p->ty);
+ p->pal = palette(n, p->t >> 10 & 7);
+ }
+ if((p->tnx & 7) == 0)
+ chr(n, p->nb, p->w, p->h, p->t, p->tnx, p->tny, p->c);
+}
+
+static void
+optinit(void)
+{
+ struct bgctxt *p;
+
+ p = bgctxts + 2;
+ p->hsh = (reg[BGMODE] & (1<<6)) != 0 ? 4 : 3;
+ p->wsh = mode >= 5 ? 4 : p->hsh;
+ p->w = 1<<p->wsh;
+ p->h = 1<<p->hsh;
+ p->tnx = hofs[2] & (p->w - 1);
+ p->tx = hofs[2] >> p->wsh;
+ bgctxts[0].otx = bgctxts[1].otx = 0xffff;
+ bgctxts[0].oty = bgctxts[0].ty;
+ bgctxts[0].otny = bgctxts[0].tny;
+ bgctxts[1].oty = bgctxts[1].ty;
+ bgctxts[1].otny = bgctxts[1].tny;
+}
+
+static void
+opt(void)
+{
+ struct bgctxt *p;
+ u16int hval, vval;
+ int sx, sy;
+
+ p = bgctxts + 2;
+ if(++p->tnx == p->w){
+ if(mode == 4){
+ hval = tile(2, p->tx, vofs[2] >> p->hsh);
+ if((hval & 0x8000) != 0){
+ vval = hval;
+ hval = 0;
+ }else
+ vval = 0;
+ }else{
+ hval = tile(2, p->tx, vofs[2] >> p->hsh);
+ vval = tile(2, p->tx, (vofs[2]+8) >> p->hsh);
+ }
+ sx = (rx & ~7) + (hval & 0x1ff8);
+ sy = ppuy + (vval & 0x1fff);
+ if((vval & 0x2000) != 0){
+ bgctxts[0].oty = sy >> bgctxts[0].hsh;
+ bgctxts[0].otny = sy & (bgctxts[0].h - 1);
+ }else{
+ bgctxts[0].oty = bgctxts[0].ty;
+ bgctxts[0].otny = bgctxts[0].tny;
+ }
+ if((vval & 0x4000) != 0){
+ bgctxts[1].oty = sy >> bgctxts[1].hsh;
+ bgctxts[1].otny = sy & (bgctxts[1].h - 1);
+ }else{
+ bgctxts[1].oty = bgctxts[1].ty;
+ bgctxts[1].otny = bgctxts[1].tny;
+ }
+ if((hval & 0x2000) != 0)
+ bgctxts[0].otx = sx >> bgctxts[0].wsh;
+ else
+ bgctxts[0].otx = 0xffff;
+ if((hval & 0x4000) != 0)
+ bgctxts[1].otx = sx >> bgctxts[1].wsh;
+ else
+ bgctxts[1].otx = 0xffff;
+ p->tnx = 0;
+ p->tx++;
+ }
+}
+
+static void
+bgopt(int n)
+{
+ struct bgctxt *p;
+ u8int v;
+
+ p = bgctxts + n;
+ v = bgpixel(p->c, p->nb, p->t & 0x4000);
+ if(p->msz != 1)
+ if(p->mx++ == 0)
+ p->mv = v;
+ else{
+ if(p->mx == p->msz)
+ p->mx = 0;
+ v = p->mv;
+ }
+ if(v != 0)
+ pixel(n, p->pal + v, p->pri[(p->t & 0x2000) != 0]);
+ if(++p->tnx == p->w){
+ p->tx++;
+ p->tnx = 0;
+ }
+ if((p->tnx & 7) == 0){
+ p->t = tile(n, p->otx == 0xffff ? p->tx : p->otx, p->oty);
+ p->pal = palette(n, p->t >> 10 & 7);
+ chr(n, p->nb, p->w, p->h, p->t, p->tnx, p->otny, p->c);
+ }
+}
+
+struct bg7ctxt {
+ int x, y, x0, y0;
+ u8int msz, mx, mv;
+} b7[2];
+
+void
+calc7(void)
+{
+ s16int t;
+
+ if((reg[0x2105] & 7) != 7)
+ return;
+ t = hofs[4] - m7[M7X];
+ if((t & 0x2000) != 0)
+ t |= ~0x3ff;
+ else
+ t &= 0x3ff;
+ b7->x0 = (t * m7[M7A]) & ~63;
+ b7->y0 = (t * m7[M7C]) & ~63;
+ t = vofs[4] - m7[M7Y];
+ if((t & 0x2000) != 0)
+ t |= ~0x3ff;
+ else
+ t &= 0x3ff;
+ b7->x0 += (t * m7[M7B]) & ~63;
+ b7->y0 += (t * m7[M7D]) & ~63;
+ b7->x0 += m7[M7X] << 8;
+ b7->y0 += m7[M7Y] << 8;
+}
+
+static void
+bg7init(int n)
+{
+ u8int m, y;
+ struct bg7ctxt *p;
+
+ p = b7 + n;
+ m = reg[M7SEL];
+ y = ppuy;
+ p->msz = 1;
+ if((reg[MOSAIC] & 1) != 0){
+ p->msz = (reg[MOSAIC] >> 4) + 1;
+ if(p->msz != 1)
+ y -= y % p->msz;
+ }
+ if(n == 1)
+ if((reg[MOSAIC] & 2) != 0)
+ p->msz = (reg[MOSAIC] >> 4) + 1;
+ else
+ p->msz = 1;
+ if((m & 2) != 0)
+ y = 255 - y;
+ p->x = b7->x0 + ((m7[M7B] * y) & ~63);
+ p->y = b7->y0 + ((m7[M7D] * y) & ~63);
+ if((m & 1) != 0){
+ p->x += 255 * m7[M7A];
+ p->y += 255 * m7[M7C];
+ }
+}
+
+static void
+bg7(int n)
+{
+ u16int x, y;
+ u8int m, v, t;
+ struct bg7ctxt *p;
+
+ p = b7 + n;
+ m = reg[M7SEL];
+ x = p->x >> 8;
+ y = p->y >> 8;
+ if((m & 0x80) == 0){
+ x &= 1023;
+ y &= 1023;
+ }else if(x > 1023 || y > 1023){
+ if((m & 0x40) != 0){
+ t = 0;
+ goto lookup;
+ }
+ v = 0;
+ goto end;
+ }
+ t = vram[x >> 2 & 0xfe | y << 5 & 0x7f00];
+lookup:
+ v = vram[t << 7 | y << 4 & 0x70 | x << 1 & 0x0e | 1];
+end:
+ if(p->msz != 1){
+ if(p->mx == 0)
+ p->mv = v;
+ else
+ v = p->mv;
+ if(++p->mx == p->msz)
+ p->mx = 0;
+ }
+ if(n == 1){
+ if((v & 0x7f) != 0)
+ if((v & 0x80) != 0)
+ pixel(1, v & 0x7f, 0x71);
+ else
+ pixel(1, v, 0x11);
+ }else if(v != 0)
+ pixel(0, v, 0x40);
+ if((m & 1) != 0){
+ p->x -= m7[M7A];
+ p->y -= m7[M7C];
+ }else{
+ p->x += m7[M7A];
+ p->y += m7[M7C];
+ }
+}
+
+static void
+bgsinit(void)
+{
+ static int bitch[8];
+
+ pixel = npixel;
+ switch(mode){
+ case 0:
+ bginit(0, 2, 0x80, 0xb0);
+ bginit(1, 2, 0x71, 0xa1);
+ bginit(2, 2, 0x22, 0x52);
+ bginit(3, 2, 0x13, 0x43);
+ break;
+ case 1:
+ bginit(0, 4, 0x80, 0xb0);
+ bginit(1, 4, 0x71, 0xa1);
+ bginit(2, 2, 0x12, (reg[BGMODE] & 8) != 0 ? 0xd2 : 0x42);
+ break;
+ case 2:
+ bginit(0, 4, 0x40, 0xa0);
+ bginit(1, 4, 0x11, 0x71);
+ optinit();
+ break;
+ case 3:
+ bginit(0, 8, 0x40, 0xa0);
+ bginit(1, 4, 0x11, 0x71);
+ break;
+ case 4:
+ bginit(0, 8, 0x40, 0xa0);
+ bginit(1, 2, 0x11, 0x71);
+ optinit();
+ break;
+ case 5:
+ bginit(0, 4, 0x40, 0xa0);
+ bginit(1, 2, 0x11, 0x71);
+ break;
+ case 6:
+ bginit(0, 4, 0x40, 0xa0);
+ optinit();
+ break;
+ case 7:
+ bg7init(0);
+ if((reg[SETINI] & EXTBG) != 0)
+ bg7init(1);
+ break;
+ default:
+ bgctxts[0].w = bgctxts[1].w = 0;
+ if(bitch[mode]++ == 0)
+ print("bg mode %d not implemented\n", mode);
+ }
+}
+
+static void
+bgs(void)
+{
+ switch(mode){
+ case 0:
+ bg(0);
+ bg(1);
+ bg(2);
+ bg(3);
+ break;
+ case 1:
+ bg(0);
+ bg(1);
+ bg(2);
+ break;
+ case 2:
+ case 4:
+ opt();
+ bgopt(0);
+ bgopt(1);
+ break;
+ case 3:
+ bg(0);
+ bg(1);
+ break;
+ case 5:
+ pixel = spixel;
+ bg(0);
+ bg(1);
+ pixel = mpixel;
+ bg(0);
+ bg(1);
+ pixel = npixel;
+ break;
+ case 6:
+ opt();
+ pixel = spixel;
+ bgopt(0);
+ pixel = mpixel;
+ bgopt(0);
+ pixel = npixel;
+ break;
+ case 7:
+ bg7(0);
+ if((reg[SETINI] & EXTBG) != 0)
+ bg7(1);
+ break;
+ }
+}
+
+static void
+sprites(void)
+{
+ static struct {
+ short x;
+ u8int y, i, c, sx, sy;
+ u16int t0, t1;
+ } s[32], *sp;
+ static struct {
+ short x;
+ u8int sx, i, c, pal, pri;
+ u32int *ch;
+ } t[32], *tp;
+ static u32int ch[34];
+ static u8int *p, q;
+ static int n, m;
+ static int *sz;
+ static int szs[] = {
+ 8, 8, 16, 16, 8, 8, 32, 32,
+ 8, 8, 64, 64, 16, 16, 32, 32,
+ 16, 16, 64, 64, 32, 32, 64, 64,
+ 16, 32, 32, 64, 16, 32, 32, 32
+ };
+ static u16int base[2];
+ u8int dy, v, col, pri0, pri1, prio;
+ u16int a;
+ u32int w, *cp;
+ int i, nt, dx;
+
+ if(rx == 0){
+ n = 0;
+ sp = s;
+ sz = szs + ((reg[OBSEL] & 0xe0) >> 3);
+ base[0] = (reg[OBSEL] & 0x07) << 14;
+ base[1] = base[0] + (((reg[OBSEL] & 0x18) + 8) << 10);
+ }
+ if((rx & 1) == 0){
+ p = oam + 2 * rx;
+ if(p[1] == 0xf0)
+ goto nope;
+ q = (oam[512 + (rx >> 3)] >> (rx & 6)) & 3;
+ dy = ppuy - p[1];
+ sp->sx = sz[q & 2];
+ sp->sy = sz[(q & 2) + 1];
+ if(dy >= sp->sy)
+ goto nope;
+ sp->x = p[0];
+ if((q & 1) != 0)
+ sp->x |= 0xff00;
+ if(sp->x <= -(short)sp->sx && sp->x != -256)
+ goto nope;
+ if(n == 32){
+ reg[0x213e] |= 0x40;
+ goto nope;
+ }
+ sp->i = rx >> 1;
+ sp->y = p[1];
+ sp->c = p[3];
+ sp->t0 = p[2] << 5;
+ sp->t1 = base[sp->c & 1];
+ sp++;
+ n++;
+ }
+nope:
+ if(ppuy != 0){
+ col = 0;
+ pri0 = 0;
+ pri1 = 128;
+ if((reg[OAMADDH] & 0x80) != 0)
+ prio = oamaddr >> 2;
+ else
+ prio = 0;
+ for(i = 0, tp = t; i < m; i++, tp++){
+ dx = rx - tp->x;
+ if(dx < 0 || dx >= tp->sx)
+ continue;
+ w = *tp->ch;
+ if((tp->c & 0x40) != 0){
+ v = w & 1 | w >> 7 & 2 | w >> 14 & 4 | w >> 21 & 8;
+ *tp->ch = w >> 1;
+ }else{
+ v = w >> 7 & 1 | w >> 14 & 2 | w >> 21 & 4 | w >> 28 & 8;
+ *tp->ch = w << 1;
+ }
+ if((dx & 7) == 7)
+ tp->ch++;
+ nt = (tp->i - prio) & 0x7f;
+ if(v != 0 && nt < pri1){
+ col = tp->pal + v;
+ pri0 = tp->pri;
+ pri1 = nt;
+ }
+ }
+ if(col > 0)
+ pixel(OBJ, col, pri0);
+ }
+ if(rx == 255){
+ cp = ch;
+ m = n;
+ for(sp = s + n - 1, tp = t + n - 1; sp >= s; sp--, tp--){
+ tp->x = sp->x;
+ tp->sx = 0;
+ tp->c = sp->c;
+ tp->pal = 0x80 | sp->c << 3 & 0x70;
+ tp->pri = 3 * (0x10 + (sp->c & 0x30));
+ if((tp->c & 8) != 0)
+ tp->pri |= OBJ;
+ else
+ tp->pri |= OBJNC;
+ tp->ch = cp;
+ tp->i = sp->i;
+ nt = sp->sx >> 3;
+ dy = ppuy - sp->y;
+ if((sp->c & 0x80) != 0)
+ dy = sp->sy - 1 - dy;
+ a = sp->t0 | (dy & 7) << 1;
+ if(dy >= 8)
+ a += (dy & ~7) << 6;
+ if(sp->x < 0 && (i = (-sp->x >> 3)) != 0){
+ if((sp->c & 0x40) != 0)
+ a -= i << 5;
+ else
+ a += i << 5;
+ nt -= i;
+ tp->sx += i << 3;
+ }
+ if((sp->c & 0x40) != 0){
+ a += sp->sx * 4;
+ for(i = 0; i < nt; i++){
+ if(cp < ch + nelem(ch)){
+ w = vram[sp->t1 | (a -= 16) & 0x1fff] << 16;
+ w |= vram[sp->t1 | (a + 1) & 0x1fff] << 24;
+ w |= vram[sp->t1 | (a -= 16) & 0x1fff] << 0;
+ w |= vram[sp->t1 | (a + 1) & 0x1fff] << 8;
+ *cp++ = w;
+ tp->sx += 8;
+ }else
+ reg[0x213e] |= 0x80;
+ }
+ }else
+ for(i = 0; i < nt; i++){
+ if(cp < ch + nelem(ch)){
+ w = vram[sp->t1 | a & 0x1fff];
+ w |= vram[sp->t1 | ++a & 0x1fff] << 8;
+ w |= vram[sp->t1 | (a += 15) & 0x1fff] << 16;
+ w |= vram[sp->t1 | ++a & 0x1fff] << 24;
+ *cp++ = w;
+ tp->sx += 8;
+ a += 15;
+ }else
+ reg[0x213e] |= 0x80;
+ }
+ if(sp->x < 0 && (i = (-sp->x) & 7) != 0)
+ if((sp->c & 0x40) != 0)
+ *tp->ch >>= i;
+ else
+ *tp->ch <<= i;
+ }
+ }
+}
+
+static u16int
+colormath(int n)
+{
+ u16int v, w, r, g, b;
+ u8int m, m2, div;
+ int cw;
+
+ m = reg[CGWSEL];
+ m2 = reg[CGADSUB];
+ cw = -1;
+ switch(m >> 6){
+ default: v = 1; break;
+ case 1: v = cw = window(COL); break;
+ case 2: v = !(cw = window(COL)); break;
+ case 3: v = 0; break;
+ }
+ if(v){
+ if((pixelcol[n] & 0x10000) != 0)
+ v = pixelcol[n];
+ else
+ v = cgram[pixelcol[n] & 0xff];
+ }
+ if((m2 & (1 << (pixelpri[n] & 0xf))) == 0)
+ return v;
+ switch((m >> 4) & 3){
+ case 0: break;
+ case 1: if(cw < 0) cw = window(COL); if(!cw) return v; break;
+ case 2: if(cw < 0) cw = window(COL); if(cw) return v; break;
+ default: return v;
+ }
+ div = (m2 & 0x40) != 0;
+ if((m & 2) != 0){
+ if((pixelcol[1-n] & 0x10000) != 0)
+ w = pixelcol[1-n];
+ else
+ w = cgram[pixelcol[1-n] & 0xff];
+ div = div && (pixelpri[1-n] & 0xf) != COL;
+ }else
+ w = subcolor;
+ if((m2 & 0x80) != 0){
+ r = (v & 0x7c00) - (w & 0x7c00);
+ g = (v & 0x03e0) - (w & 0x03e0);
+ b = (v & 0x001f) - (w & 0x001f);
+ if(r > 0x7c00) r = 0;
+ if(g > 0x03e0) g = 0;
+ if(b > 0x001f) b = 0;
+ if(div){
+ r = (r >> 1) & 0xfc00;
+ g = (g >> 1) & 0xffe0;
+ b >>= 1;
+ }
+ return r | g | b;
+ }else{
+ r = (v & 0x7c00) + (w & 0x7c00);
+ g = (v & 0x03e0) + (w & 0x03e0);
+ b = (v & 0x001f) + (w & 0x001f);
+ if(div){
+ r = (r >> 1) & 0xfc00;
+ g = (g >> 1) & 0xffe0;
+ b >>= 1;
+ }
+ if(r > 0x7c00) r = 0x7c00;
+ if(g > 0x03e0) g = 0x03e0;
+ if(b > 0x001f) b = 0x001f;
+ return r | g | b;
+ }
+}
+
+void
+ppustep(void)
+{
+ int yvbl;
+
+ mode = reg[BGMODE] & 7;
+ bright = reg[INIDISP] & 0xf;
+ hires = mode >= 5 && mode < 7 || (reg[SETINI] & 8) != 0;
+ yvbl = (reg[SETINI] & OVERSCAN) != 0 ? 0xf0 : 0xe1;
+
+ if(ppux >= XLEFT && ppux <= XRIGHT && ppuy < 0xf0){
+ rx = ppux - XLEFT;
+ if(ppuy < yvbl && (reg[INIDISP] & 0x80) == 0){
+ pixelcol[0] = 0;
+ pixelpri[0] = COL;
+ pixelcol[1] = 0x10000 | subcolor;
+ pixelpri[1] = COL;
+ bgs();
+ sprites();
+ if(ppuy != 0)
+ if(hires){
+ pixeldraw(rx, ppuy - 1, colormath(1), 0);
+ pixeldraw(rx, ppuy - 1, colormath(0), 1);
+ }else
+ pixeldraw(rx, ppuy - 1, colormath(0), 0);
+ }else if(ppuy != 0)
+ pixeldraw(rx, ppuy - 1, ppuy >= yvbl ? 0x6739 : 0, -1);
+ }
+
+ if(ppux == 134)
+ cpupause = 1;
+ if(ppux == 0x116 && ppuy <= yvbl)
+ hdma |= reg[0x420c];
+ if((reg[NMITIMEN] & HCNTIRQ) != 0 && htime+4 == ppux && ((reg[NMITIMEN] & VCNTIRQ) == 0 || vtime == ppuy))
+ irq |= IRQPPU;
+ if(++ppux >= 340){
+ ppux = 0;
+ if(++ppuy >= 262){
+ ppuy = 0;
+ reg[RDNMI] &= ~VBLANK;
+ reg[0x213e] = 1;
+ reg[0x213f] ^= 0x80;
+ hdma = reg[0x420c]<<8;
+ flush();
+ }
+ if(ppuy < yvbl)
+ bgsinit();
+ if(ppuy == yvbl){
+ reg[RDNMI] |= VBLANK;
+ if((reg[NMITIMEN] & VBLANK) != 0)
+ nmi = 2;
+ if((reg[INIDISP] & 0x80) == 0)
+ oamaddr = reg[0x2102] << 1 | (reg[0x2103] & 1) << 9;
+ if((reg[NMITIMEN] & AUTOJOY) != 0){
+ memwrite(0x4016, 1);
+ memwrite(0x4016, 0);
+ reg[0x4218] = keylatch >> 16;
+ reg[0x4219] = keylatch >> 24;
+ keylatch = keylatch << 16 | 0xffff;
+ }
+ }
+ if((reg[NMITIMEN] & (HCNTIRQ|VCNTIRQ)) == VCNTIRQ && vtime == ppuy)
+ irq |= IRQPPU;
+ }
+}