#include <u.h>
#include <libc.h>
#include <thread.h>
-#include <draw.h>
+#include "../eui.h"
#include "dat.h"
#include "fns.h"
-uchar mem[65536];
-int rombank, rambank, ramen, battery, ramrom;
-extern int savefd;
+u8int *rom;
+u8int *romb, *vramb, *wramb, *eramb;
+u8int wram[32768], vram[16384], oam[256], reg[256];
+u8int *back;
+u8int palm[128];
+u32int pal[64];
+int nrom, nback, nbackbank;
+u32int divclock;
+int prish;
+MBC3Timer timer, timerl;
+s8int dma;
+u32int white;
+u32int moncols[4];
+
+Var memvars[] = {ARR(wram), ARR(vram), ARR(oam), ARR(reg), ARR(palm), VAR(clock), VAR(divclock), VAR(mode), VAR(dma), {nil, 0, 0}};
u8int
-memread(u16int p)
+regread(u8int a)
+{
+ u8int v;
+
+ switch(a){
+ case JOYP:
+ v = reg[a] & 0xff;
+ if((reg[a] & 0x10) == 0)
+ v &= 0xf0 | ~keys;
+ if((reg[a] & 0x20) == 0)
+ v &= 0xf0 | ~(keys >> 4);
+ return v;
+ case DIV:
+ return reg[DIV] + (clock - divclock >> 7 - ((mode & TURBO) != 0));
+ case TIMA:
+ return timread();
+ case STAT:
+ return reg[a] & 0xf8 | (reg[LYC] == ppuy) << 2 | ppustate;
+ case LY:
+ return ppuy;
+ case BCPD:
+ return palm[reg[BCPS] & 0x3f];
+ case OCPD:
+ return palm[0x40 | reg[OCPS] & 0x3f];
+ case NR13: case NR23: case NR31: case NR33: case NR41:
+ return 0xff;
+ case NR14: case NR24: case NR34: case NR44:
+ return reg[a] | 0xbf;
+ case NR52:
+ return apustatus;
+ case NR11: case NR21:
+ return reg[a] | 0x3f;
+ case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
+ case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
+ return waveread(a & 0xf);
+ default:
+ return reg[a];
+ }
+}
+
+void
+colcol(int i, u16int v)
+{
+ union { u8int c[4]; u32int l; } c;
+
+ c.c[0] = v >> 7 & 0xf8;
+ c.c[1] = v >> 2 & 0xf8;
+ c.c[2] = v << 3;
+ c.c[3] = 0;
+ pal[i] = c.l;
+}
+
+void
+regwrite(u8int a, u8int v)
{
- extern int keys;
-
- if((p & 0xFF80) == 0xFF00)
- switch(p){
- case 0xFF00:
- if((mem[0xFF00] & (1<<5)) == 0)
- return (mem[0xFF00] & 0xF0) | ~(keys >> 4);
- if((mem[0xFF00] & (1<<6)) == 0)
- return (mem[0xFF00] & 0xF0) | ~(keys & 0x0F);
- return (mem[0xFF00] & 0xF0) | 0x0F;
+ extern Event evhblank;
+ int i;
+ u8int u;
+
+ switch(a){
+ case JOYP: v |= 0xcf; break;
+ case SC: v |= 0x7c; break;
+ case DIV:
+ divclock = clock;
+ v = 0;
+ break;
+ case TIMA:
+ reg[a] = v;
+ timerset();
+ return;
+ case TAC:
+ v |= 0xf8;
+ timertac(v, 0);
+ break;
+ case STAT:
+ v |= 0x80;
+ if((v & IRQLYC) != 0 && ppuy == reg[LYC])
+ reg[IF] |= IRQLCDS;
+ break;
+ case LYC:
+ if((reg[STAT] & IRQLYC) != 0 && ppuy == v)
+ reg[IF] |= IRQLCDS;
+ break;
+ case LCDC:
+ ppusync();
+ if((~v & reg[a] & LCDEN) != 0){
+ ppuy = 0;
+ ppustate = 0;
+ delevent(&evhblank);
}
- if(!ramen && ((p & 0xE000) == 0xA000))
- return 0xFF;
- return mem[p];
+ if((v & ~reg[a] & LCDEN) != 0)
+ addevent(&evhblank, 456*2);
+ break;
+ case SCY: case SCX: case WY: case WX:
+ ppusync();
+ break;
+ case VBK:
+ if((mode & COL) == 0)
+ goto ff;
+ vramb = vram + ((v & 1) << 13);
+ v |= 0xfe;
+ break;
+ case SVBK:
+ if((mode & COL) == 0)
+ goto ff;
+ v &= 7;
+ wramb = wram + (v + (v - 1 >> 3 & 1) << 12);
+ v |= 0xf8;
+ break;
+ case BGP:
+ case OBP0:
+ case OBP1:
+ if((mode & COL) != 0)
+ break;
+ ppusync();
+ i = a - BGP << 2;
+ pal[i] = moncols[~v & 3];
+ pal[i+1] = moncols[~v >> 2 & 3];
+ pal[i+2] = moncols[~v >> 4 & 3];
+ pal[i+3] = moncols[~v >> 6 & 3];
+ break;
+ case DMA:
+ for(i = 0; i < 160; i++)
+ oam[i] = memread(v << 8 | i);
+ break;
+ case BCPS: v |= 0x40; break;
+ case OCPS: v |= 0x40; break;
+ case BCPD:
+ if((mode & COL) == 0)
+ break;
+ ppusync();
+ u = reg[BCPS] & 0x3f;
+ palm[u] = v;
+ colcol(u / 2, palm[u & 0xfe] | palm[u | 1] << 8);
+ if((reg[BCPS] & 0x80) != 0)
+ reg[BCPS] = reg[BCPS] + 1 - (u + 1 & 0x40);
+ break;
+ case OCPD:
+ if((mode & COL) == 0)
+ break;
+ ppusync();
+ u = 0x40 | reg[OCPS] & 0x3f;
+ palm[u] = v;
+ colcol(u / 2, palm[u & 0xfe] | palm[u | 1] << 8);
+ if((reg[OCPS] & 0x80) != 0)
+ reg[OCPS] = reg[OCPS] + 1 - ((reg[OCPS] & 0x3f) + 1 & 0x40);
+ break;
+ case IF: v |= 0xe0; break;
+ case IE: v &= 0x1f; break;
+ case KEY1: v |= 0x7e; break;
+ case HDMAC:
+ if((mode & COL) == 0)
+ goto ff;
+ dma = (v & 0x80) != 0 ? -1 : 1;
+ break;
+ case NR10: v |= 0x80; goto snd;
+ case NR14: case NR24: v |= 0x38; goto snd;
+ case NR30: v |= 0x7f; goto snd;
+ case NR32: v |= 0x9f; goto snd;
+ case NR41: v |= 0xc0; goto snd;
+ case NR44: v |= 0x3f; goto snd;
+ case NR52: v |= 0x70; goto snd;
+ case NR11: case NR12: case NR13:
+ case NR21: case NR22: case NR23:
+ case NR31: case NR33: case NR34:
+ case NR42: case NR43:
+ case NR50: case NR51:
+ snd:
+ sndwrite(a, v);
+ return;
+ case SB:
+ case TMA:
+ break;
+ case HDMASL: case HDMASH: case HDMADL: case HDMADH: case RP:
+ if((mode & COL) == 0)
+ goto ff;
+ break;
+ case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
+ case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
+ wavewrite(a & 0xf, v);
+ return;
+ default:
+ if(a >= 0x80)
+ break;
+ ff:
+ v = 0xff;
+ }
+ reg[a] = v;
}
static void
-ramswitch(int state, int bank)
+nope(int p)
{
- if(ramen){
- memcpy(ram + 8192 * rambank, mem + 0xA000, 8192);
- if(battery && savefd > 0){
- seek(savefd, rambank * 8192, 0);
- write(savefd, ram + 8192 * rambank, 8192);
+ print("unimplemented mapper function %d (mapper %d)\n", p, mbc);
+}
+
+static int
+mbc0(int a, int)
+{
+ if(a < 0)
+ switch(a){
+ case INIT:
+ if(nback != 0)
+ eramb = back;
+ return 0;
+ case SAVE:
+ case RSTR:
+ return 0;
+ case READ:
+ return -1;
+ default:
+ nope(a);
}
- ramen = 0;
- }
- rambank = bank;
- if(state){
- if(bank >= rambanks)
- sysfatal("invalid RAM bank %d selected (pc = %.4x)", bank, curpc);
- memcpy(mem + 0xA000, ram + 8192 * rambank, 8192);
- ramen = 1;
- }
+ return 0;
}
-void
-flushram(void)
+static int
+mbc1(int a, int v)
{
+ static u8int ramen, b0, b1, romram;
+ static Var mbc1vars[] = {VAR(ramen), VAR(b0), VAR(b1), VAR(romram), {nil, 0, 0}};
+ u16int b;
+
+ if(a < 0)
+ switch(a){
+ case INIT:
+ return 0;
+ case SAVE:
+ putvars(mbc1vars);
+ break;
+ case RSTR:
+ getvars(mbc1vars);
+ break;
+ case READ:
+ return -1;
+ default:
+ nope(a);
+ }
+ switch(a >> 13){
+ case 0: ramen = (v & 0xf) == 0xa; break;
+ case 1: v &= 0x1f; b0 = v != 0 ? v : 1; break;
+ case 2: b1 = v & 3; b1 %= nbackbank; break;
+ case 3: romram = v & 1; break;
+ }
+ b = b0;
+ if(!romram)
+ b |= b1 << 5;
+ b %= nrom >> 14;
+ romb = rom + (b << 14);
if(ramen)
- ramswitch(ramen, rambank);
+ if(romram)
+ eramb = back + (b1 << 13);
+ else
+ eramb = back;
+ else
+ eramb = nil;
+ return 0;
}
-static void
-romswitch(int bank)
+static int
+mbc2(int a, int v)
{
- if(bank >= rombanks)
- sysfatal("invalid ROM bank %d selected (pc = %.4x)", bank, curpc);
- rombank = bank;
- memcpy(mem + 0x4000, cart + 0x4000 * bank, 0x4000);
+ static int ramen, b;
+ static Var mbc2vars[] = {VAR(ramen), VAR(b), {nil, 0, 0}};
+
+ if(a < 0)
+ switch(a){
+ case INIT:
+ return 0;
+ case SAVE:
+ putvars(mbc2vars);
+ return 0;
+ case RSTR:
+ getvars(mbc2vars);
+ romb = rom + (b << 14);
+ return 0;
+ case READ:
+ if(ramen)
+ return back[a & 0x1ff];
+ return 0xff;
+ default:
+ nope(a);
+ }
+ if((a & 0xc100) == 0)
+ ramen = (v & 0x0f) == 0x0a;
+ else if((a & 0xc100) == 0x100){
+ b = v & 0x0f;
+ if(b == 0)
+ b++;
+ b %= nrom >> 14;
+ romb = rom + (b << 14);
+ }else if((a >> 12) == 0xa && ramen)
+ back[a & 0x1ff] = v | 0xf0;
+ return 0;
}
void
-memwrite(u16int p, u8int v)
+timerforward(MBC3Timer *t)
{
- if(p < 0x8000){
- switch(mbc){
- case 0:
- return;
- case 1:
- case 2:
- switch(p >> 13){
- case 0:
- if((v & 0x0F) == 0x0A)
- ramswitch(1, rambank);
- else
- ramswitch(0, rambank);
- return;
- case 1:
- v &= 0x1F;
- if(v == 0)
- v++;
- romswitch((rombank & 0xE0) | v);
- return;
- case 2:
- if(ramrom)
- ramswitch(ramen, v & 3);
- else
- romswitch(((v & 3) << 5) | (rombank & 0x1F));
- return;
- case 3:
- ramrom = v;
- return;
- }
- return;
- case 3:
- switch(p >> 13){
- case 0:
- if((v & 0x0F) == 0x0A)
- ramswitch(1, rambank);
- else
- ramswitch(0, rambank);
- return;
- case 1:
- v &= 0x7F;
- if(v == 0)
- v++;
- romswitch(v);
- return;
- case 2:
- if(v < 4)
- ramswitch(ramen, v);
- return;
- }
- return;
- case 5:
- switch(p >> 12){
- case 0: case 1:
- if((v & 0x0F) == 0x0A)
- ramswitch(1, rambank);
- else
- ramswitch(0, rambank);
- return;
- case 2:
- romswitch((rombank & 0x100) | v);
- return;
- case 3:
- romswitch((((int)v & 1) << 8) | (rombank & 0xFF));
- return;
- case 4:
- ramswitch(ramen, v & 15);
- return;
-
+ vlong n, nd;
+ int x;
+
+ n = nsec();
+ nd = n - t->ns;
+ if(nd < 0)
+ return;
+ if((t->dh & 0x40) != 0){
+ t->ns = n;
+ return;
+ }
+ t->ns = n - nd % BILLION;
+ x = t->sec + t->min * 60 + t->hr * 3600 + t->dl * 86400 + t->dh * (256 * 86400);
+ x += nd / BILLION;
+ t->sec = x % 60;
+ x /= 60;
+ t->min = x % 60;
+ x /= 60;
+ t->hr = x % 24;
+ x /= 24;
+ t->dl = x & 0xff;
+ x >>= 8;
+ t->dh = t->dh & 0xfe | x & 1;
+ if(x >= 2) t->dh |= 0x80;
+}
+
+static int
+mbc3(int a, int v)
+{
+ static u8int ramen, b0, b1, latch;
+ static Var mbc3vars[] = {VAR(ramen), VAR(b0), VAR(b1), VAR(latch),
+ VAR(timer.ns), VAR(timer.sec), VAR(timer.min), VAR(timer.hr), VAR(timer.dl), VAR(timer.dh),
+ VAR(timerl.sec), VAR(timerl.min), VAR(timerl.hr), VAR(timerl.dl), VAR(timerl.dh), {nil, 0, 0}};
+
+ if(a < 0)
+ switch(a){
+ case INIT:
+ return 0;
+ case SAVE:
+ putvars(mbc3vars);
+ return 0;
+ case RSTR:
+ getvars(mbc3vars);
+ romb = rom + (b0 << 14);
+ break;
+ case READ:
+ if(!ramen)
+ return -1;
+ switch(b1){
+ case 8: return timerl.sec;
+ case 9: return timerl.min;
+ case 10: return timerl.hr;
+ case 11: return timerl.dl;
+ case 12: return timerl.dh;
}
- return;
+ return -1;
default:
- sysfatal("mbc %d unimplemented", mbc);
+ nope(a);
+ }
+ switch(a >> 13){
+ case 0: ramen = (v & 0xf) == 0xa; break;
+ case 1:
+ v &= 0x7f;
+ b0 = v != 0 ? v : 1;
+ b0 %= nrom >> 14;
+ romb = rom + (b0 << 14);
+ return 0;
+ case 2: b1 = v & 15; break;
+ case 3:
+ if(latch == 0 && v == 1){
+ timerl = timer;
+ timerforward(&timerl);
}
+ latch = v;
+ break;
+ case 0xa:
+ if(!ramen)
+ return 0;
+ switch(b1){
+ case 8: timerforward(&timer); timer.sec = v; break;
+ case 9: timerforward(&timer); timer.min = v; break;
+ case 10: timerforward(&timer); timer.hr = v; break;
+ case 11: timerforward(&timer); timer.dl = v; break;
+ case 12: timerforward(&timer); timer.dh = v; break;
+ }
+ return 0;
}
- if((p & 0xFF80) == 0xFF00)
- switch(p){
- case 0xFF04:
- v = 0;
- break;
- case 0xFF07:
- timer = (v & 4) != 0;
- switch(v & 3){
- case 0:
- timerfreq = 1024;
- break;
- case 1:
- timerfreq = 16;
- break;
- case 2:
- timerfreq = 64;
- break;
- default:
- timerfreq = 256;
- }
- break;
- case 0xFF26:
- v = (v & 0xF0) | (mem[p] & 0x0F);
- case 0xFF41:
- v &= ~7;
- v |= mem[p] & 7;
- break;
- case 0xFF46:
- memcpy(mem + 0xFE00, mem + (((int)v) << 8), 0xA0);
+ eramb = ramen && b1 < nbackbank ? back + (b1 << 13) : nil;
+ return 0;
+}
+
+static int
+mbc5(int a, int v)
+{
+ static u8int ramen, b1;
+ static u16int b0;
+ static Var mbc5vars[] = {VAR(ramen), VAR(b0), VAR(b1), {nil, 0, 0}};
+
+ if(a < 0)
+ switch(a){
+ case INIT:
+ return 0;
+ case SAVE:
+ putvars(mbc5vars);
+ return 0;
+ case RSTR:
+ getvars(mbc5vars);
break;
+ case READ:
+ return -1;
+ default:
+ nope(a);
}
- mem[p] = v;
+ switch(a >> 12){
+ case 0: case 1: ramen = (v & 0xf) == 0xa; break;
+ case 2: b0 = b0 & 0x100 | v; break;
+ case 3: b0 = b0 & 0xff | v << 8 & 0x100; break;
+ case 4: b1 = v & 0xff; b1 %= nbackbank; break;
+ }
+ b0 %= nrom >> 14;
+ romb = rom + (b0 << 14);
+ eramb = ramen ? back + (b1 << 13) : nil;
+ return 0;
+
+}
+
+int (*mappers[7])(int, int) = {mbc0, mbc1, mbc2, mbc3, mbc0, mbc5, mbc0};
+int (*mapper)(int, int);
+
+u8int
+memread(u16int a)
+{
+ switch(a >> 12){
+ case 0: case 1: case 2: case 3:
+ return rom[a];
+ case 4: case 5: case 6: case 7:
+ return romb[a - 0x4000];
+ case 8: case 9:
+ return vramb[a - 0x8000];
+ case 10: case 11:
+ if(eramb != nil)
+ return eramb[a - 0xa000];
+ return mapper(READ, a);
+ case 12: case 14:
+ return wram[a & 0xfff];
+ case 15:
+ if(a >= 0xff00)
+ return regread(a);
+ else if(a >= 0xfe00)
+ return oam[a - 0xfe00];
+ case 13:
+ return wramb[a & 0xfff];
+ }
+ return 0xff;
+}
+
+void
+memwrite(u16int a, u8int v)
+{
+ switch(a >> 12){
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ mapper(a, v);
+ return;
+ case 8: case 9:
+ vramb[a - 0x8000] = v;
+ return;
+ case 10: case 11:
+ if(eramb != nil)
+ eramb[a - 0xa000] = v;
+ else
+ mapper(a, v);
+ writeback();
+ return;
+ case 12: case 14:
+ wram[a & 0xfff] = v;
+ return;
+ case 15:
+ if(a >= 0xff00){
+ regwrite(a, v);
+ return;
+ }else if(a >= 0xfe00){
+ oam[a - 0xfe00] = v;
+ return;
+ }
+ case 13:
+ wramb[a & 0xfff] = v;
+ }
+}
+
+void
+meminit(void)
+{
+ union { u8int c[4]; u32int l; } c;
+
+ c.c[0] = c.c[1] = c.c[2] = 0;
+ c.c[3] = 1;
+ for(; c.l != 1; prish++)
+ c.l >>= 1;
+
+ c.c[0] = c.c[1] = c.c[2] = 0xff;
+ c.c[3] = 0;
+ white = c.l;
+
+ romb = rom + 0x4000;
+ wramb = wram + 0x1000;
+ vramb = vram;
+ mapper = mappers[mbc];
+ mapper(INIT, 0);
+
+ reg[LCDC] = 0x91;
+ reg[VBK] = 0xfe;
+ reg[SVBK] = 0xf8;
+ reg[IF] = 0xe0;
+}
+
+void
+memload(void)
+{
+ int i;
+ u8int v;
+
+ if((mode & COL) != 0){
+ for(i = 0; i < 64; i++)
+ colcol(i, palm[2*i] | palm[2*i+1] << 8);
+ vramb = vram + ((reg[VBK] & 1) << 13);
+ wramb = wram + (reg[SVBK] + (reg[SVBK] - 1 >> 3 & 1) << 12);
+ }else{
+ v = reg[BGP];
+ pal[0] = moncols[~v & 3];
+ pal[1] = moncols[~v >> 2 & 3];
+ pal[2] = moncols[~v >> 4 & 3];
+ pal[3] = moncols[~v >> 6 & 3];
+ v = reg[OBP0];
+ pal[4] = moncols[~v & 3];
+ pal[5] = moncols[~v >> 2 & 3];
+ pal[6] = moncols[~v >> 4 & 3];
+ pal[7] = moncols[~v >> 6 & 3];
+ v = reg[OBP1];
+ pal[8] = moncols[~v & 3];
+ pal[9] = moncols[~v >> 2 & 3];
+ pal[10] = moncols[~v >> 4 & 3];
+ pal[11] = moncols[~v >> 6 & 3];
+ }
+
+}
+
+int
+dmastep(void)
+{
+ int i;
+ u16int sa, da;
+
+ sa = (reg[HDMASL] | reg[HDMASH] << 8) & 0xfff0;
+ da = (reg[HDMADL] | reg[HDMADH] << 8) & 0x1ff0 | 0x8000;
+ for(i = 0; i < 16; i++)
+ memwrite(da++, memread(sa++));
+ reg[HDMASL] += 16;
+ if((reg[HDMASL] & 0xf0) == 0)
+ reg[HDMASH]++;
+ reg[HDMADL] += 16;
+ if((reg[HDMADL] & 0xf0) == 0)
+ reg[HDMADH]++;
+ if((reg[HDMAC] & 0x7f) == 0)
+ dma = 0;
+ else{
+ reg[HDMAC]--;
+ if((reg[HDMAC] & 0x80) != 0)
+ dma = 1;
+ }
+ return 64;
}