plan9fox/sys/src/games/c64/vic.c
2015-02-07 18:03:17 +01:00

367 lines
7.3 KiB
C

#include <u.h>
#include <libc.h>
#include <thread.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];
u8int pic[400*300*3*3];
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;
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;
for(i = 0; i < n; i++){
c = p >> 56;
p <<= 8;
q[4 * i] = cb[c];
q[4 * i + 1] = cg[c];
q[4 * i + 2] = cr[c];
}
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;
}