]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/games/gb/mem.c
games/gb: when the LCD is turned off, reset ppuy and ppustate to 0, fixes bug in...
[plan9front.git] / sys / src / games / gb / mem.c
index f033a8ad5f97a2afe7b03898dd8ab96b94e4ea50..9098dbe18aeb21e515afac280ca4a7fe44d64d5a 100644 (file)
 #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;
 }