#include <stdio.h>
#include <stdint.h>
#include "arm.h"
#include "mem.h"
#include "video.h"
#include "gba.h"

unsigned long lastbiosop;
//int output;
unsigned char sram[65536];
int ins;
int cycles;
void initmem()
{
        int c;
        rom=malloc(16*1024*1024);
        ram=malloc(32*1024);
        ram2=malloc(256*1024);
        vram=malloc(128*1024);
        bios=malloc(16*1024);
        romb=(unsigned char *)rom;
        ramb=(unsigned char *)ram;
        ram2b=(unsigned char *)ram2;
        vramb=(unsigned char *)vram;
        biosb=(unsigned char *)bios;
        romw=(unsigned short *)rom;
        ramw=(unsigned short *)ram;
        ram2w=(unsigned short *)ram2;
        vramw=(unsigned short *)vram;
        biosw=(unsigned short *)bios;
        for (c=0;c<256;c++) readfast[c]=readfast2b[c]=readfast2w[c]=readfast2l[c]=writefast[c]=0;
        /*BIOS*/
//        readfast[0]=1;
//        readfast2b[0]=readfast2w[0]=readfast2l[0]=biosw;
//        readfastm[0]=0x3FFF;
        /*Internal RAM*/
        readfast[3]=1;//writefast[3]=1;
        readfast2b[3]=readfast2w[3]=readfast2l[3]=ramw;
        readfastm[3]=0x7FFF;
        /*External RAM*/
//        readfast[2]=writefast[2]=1;
//        readfast2b[2]=readfast2w[2]=readfast2l[2]=ram2w;
//        readfastm[2]=0x3FFFF;
        /*VRAM*/
//        readfast[6]=writefast[6]=1;
//        readfast2b[6]=readfast2w[6]=readfast2l[6]=vramw;
//        readfastm[6]=0x1FFFF;
        memset(ram,0,32*1024);
        memset(ram2,0,256*1024);
        memset(vram,0,128*1024);
}

int loadbios()
{
        FILE *f;
        f=fopen("bios.rom","rb");
        if (!f) return 1;
        fread(bios,16*1024,1,f);
        fclose(f);
        return 0;
}

void loadrom()
{
        int c,chk;
        FILE *f=fopen(romname,"rb");
//        FILE *f=fopen("payback.gba","rb");
        fread(rom,16*1024*1024,1,f);
        fclose(f);
        
/*        f=fopen("logo","rb");
        fread(romb+4,0x9C,1,f);
        fclose(f);
        romb[0xB0]=0x30;
        romb[0xB1]=0x31;
        romb[0xB2]=0x96;
        for (c=0xB3;c<0xBD;c++) romb[c]=0;
        chk=0;
        for (c=0xA0;c<0xC0;c++) chk=chk-romb[c];
        chk=(chk-0x19)&0xFF;
        romb[0xBD]=chk;*/
}

int isvalidforfastread(int a)
{
        return 0;
}
int isvalidforfastwrite(int a)
{
        return 0;
}

unsigned long *getpccache(unsigned long pc)
{
        unsigned long *t;
//        printf("PCcache %08X\n",pc);
        switch (pc&0x0F000000)
        {
                case 0x00000000: /*BIOS*/
                t=&bios[((long)(pc&0x3000)-(long)(pc&~0xFFF))>>2];
                wait32=wait16=1;
                return t;
                case 0x02000000: /*External RAM*/
                t=&ram2[((long)(pc&0x3F000)-(long)(pc&~0xFFF))>>2];
                wait16=3;
                wait32=6;
                return t;
                case 0x03000000: /*Internal RAM*/
                t=&ram[((long)(pc&0x7000)-(long)(pc&~0xFFF))>>2];
                wait32=wait16=1;
                return t;
                case 0x05000000: /*Palette - this is not a joke!*/
                t=&pal[((long)(pc&0x000)-(long)(pc&~0xFFF))>>2];
                wait16=1;
                wait32=2;
                return t;
                case 0x08000000: /*ROM*/
                t=&rom[((long)(pc&0xFFF000)-(long)(pc&~0xFFF))>>2];
                wait16=romwait16[0];
                wait32=romwait32[0];
                return t;
        }
        printf("Bad getpccache %08X\n",pc);
        dumpregs();
        exit(-1);
}

unsigned long readmemfl(unsigned long a)
{
        unsigned short temp;
        switch (a&0x0F000000)
        {
                case 0:
                if (a<32) return 0;
//                cycles--;
                if (PC>0x3FFF) return lastbiosop;
                return bios[(a&0x3FFC)>>2];
                case 0x02000000: /*External RAM*/
                if (!indma) cycles-=5;
                return ram2[(a&0x3FFFC)>>2];
                case 0x03000000: /*Internal RAM*/
//                cycles--;
                return ram[(a&0x7FFC)>>2];
                case 0x04000000: /*IO*/
                temp=readio(a);
                return temp|(readio(a+2)<<16);
                case 0x05000000: /*Palette RAM*/
                return pal[(a>>1)&511]|(pal[((a+2)>>1)&511]<<16);
                case 0x06000000: /*VRAM*/
                return vram[(a&0x1FFFC)>>2];
                case 0x07000000: /*VRAM*/
                temp=oam[(a>>1)&511];
                return temp|(oam[((a+2)>>1)&511]<<16);
                case 0x08000000: /*ROM*/
                case 0x09000000: /*ROM*/
                if (!indma) cycles-=(romwait32[0]-1);
                return rom[(a&0xFFFFFC)>>2];
                case 0x0A000000: /*ROM*/
                case 0x0B000000: /*ROM*/
                if (!indma) cycles-=(romwait32[1]-1);
                return rom[(a&0xFFFFFC)>>2];
                case 0x0C000000: /*ROM*/
//                case 0x0D000000: /*ROM*/
//                if (!indma) cycles-=7;
                if (!indma) cycles-=(romwait32[2]-1);
                return rom[(a&0xFFFFFC)>>2];
        }
        return 0;
        printf("Bad read %08X\n",a);
        dumpregs();
        exit(-1);
}

unsigned short readmemfw(unsigned long a)
{
//        if (output) rpclog("readmemw %08X\n",a);
        switch (a&0x0F000000)
        {
                case 0x00000000:
                if (a<32) return 0;
//                cycles--;
                if (PC>0x3FFF) return lastbiosop;
                return biosw[(a&0x3FFE)>>1];
                case 0x02000000: /*External RAM*/
                if (!indma) cycles-=2;
                return ram2w[(a&0x3FFFE)>>1];
                case 0x03000000: /*Internal RAM*/
//                cycles--;
                return ramw[(a&0x7FFE)>>1];
                case 0x04000000: /*IO*/
                return readio(a);
                case 0x05000000: /*Palette RAM*/
                return pal[(a>>1)&511];
                case 0x06000000: /*VRAM*/
                return vramw[(a&0x1FFFE)>>1];
                case 0x07000000: /*VRAM*/
                return oam[(a>>1)&511];
                case 0x08000000: /*ROM*/
                case 0x09000000: /*ROM*/
                if (!indma) cycles-=(romwait16[0]-1);
                return romw[(a&0xFFFFFE)>>1];
                case 0x0A000000: /*ROM*/
                case 0x0B000000: /*ROM*/
                if (!indma) cycles-=(romwait16[1]-1);
                return romw[(a&0xFFFFFE)>>1];
                case 0x0C000000: /*ROM*/
//                case 0x0D000000: /*ROM*/
                if (!indma) cycles-=(romwait16[2]-1);
                return romw[(a&0xFFFFFE)>>1];
                case 0xD000000:
                if (eeprompresent) return readeeprom();
                return 0;
                printf("Read EEPROM %08X\n",a);
                return 0;
        }
        return 0;
        printf("Bad readw %08X\n",a);
        dumpregs();
        exit(-1);
}

unsigned char readmemfb(unsigned long a)
{
        switch (a&0x0F000000)
        {
                case 0x00000000:
                if (a<32) return 0;
//                cycles--;
                if (PC>0x3FFF) return lastbiosop;
                return biosb[a&0x3FFF];
                case 0x02000000: /*External RAM*/
                if (!indma) cycles-=2;
                return ram2b[a&0x3FFFF];
                case 0x03000000: /*Internal RAM*/
//                cycles--;
                return ramb[a&0x7FFF];
                case 0x04000000: /*IO*/
                if (a&1) return readio(a)>>8;
                return readio(a);
                case 0x05000000:
                if (a&1) return pal[(a>>1)&511]>>8;
                return pal[(a>>1)&511];
                case 0x06000000: /*VRAM*/
                return vramb[a&0x1FFFF];
                case 0x08000000: /*ROM*/
                case 0x09000000: /*ROM*/
                if (!indma) cycles-=(romwait16[0]-1);
                return romb[a&0xFFFFFF];
                case 0x0A000000: /*ROM*/
                case 0x0B000000: /*ROM*/
                if (!indma) cycles-=(romwait16[1]-1);
                return romb[a&0xFFFFFF];
                case 0x0C000000: /*ROM*/
//                case 0x0D000000: /*ROM*/
                if (!indma) cycles-=(romwait16[2]-1);
                return romb[a&0xFFFFFF];
                case 0x0E000000: /*SRAM*/
                if (flashpresent)
                {
//                        printf("Read FLASH %08X\n",a);
                        return readflash(a);
                }
                if (a<0xE020000) return readsram(a);
                return 0xFF;
        }
        return 0;
        printf("Bad readb %08X\n",a);
        dumpregs();
        exit(-1);
}

void writememfl(unsigned long a, unsigned long v)
{
////        if (((a)&0xF0003FC)==0x400000C) printf("Write BG3L %08X %07X\n",a,v,PC);
//        if (((a)&0xF007FFC)==0x03001048) rpclog("Write %08XL %08X %07X\n",v,PC);
//        if (((a)&0xF007FFC)==0x03005068) rpclog("Write %08XL %08X %07X\n",0x5068,v,PC);
//        if (a==0x3007A0C) printf("Write 7A0C %08X at %08X %i\n",v,PC,ins);
/*        if (((a)&0xF007FFC)==0x30007B8)
        {
                printf("Write 30007B8L %08X %07X\n",v,PC);
                if (output)
                {
                        dumpregs();
                        exit(-1);
                }
        }*/
        switch (a&0x0F000000)
        {
                case 0: return;
                case 0x02000000: /*WRAM*/
                if (!indma) cycles-=5;
                ram2[(a&0x3FFFC)>>2]=v;
                return;
                case 0x03000000: /*Internal RAM*/
//                cycles--;
//                if ((a&0x7FFC)==0x7FFC) printf("Write 7FFC %08X %08X\n",v,PC);
                ram[(a&0x7FFC)>>2]=v;
                return;
                case 0x04000000: /*IO*/ /*16-bit, so split up the transfers*/
                writeio(a,v&0xFFFF);
                writeio(a+2,v>>16);
                return;
                case 0x05000000: /*Palette*/
                pal[(a>>1)&511]=v&0xFFFF;
                pal[((a+2)>>1)&511]=v>>16;
                palchange=1;
                return;
                case 0x06000000: /*VRAM*/
                //30064AC
//                if ((a&0x1FFFC)>=0x11000 && (a&0x1FFFC)<0x11800) printf("Write VRAMl %08X %05X %07X  %08X %08X\n",v,a,PC,armregs[0],armregs[1]);
//                if ((a&0x1FFFC)==0x11350) printf("Write VRAMl %08X %05X %07X\n",v,a,PC);
//                if (!(a&0x1FFFC)) printf("Write VRAMl %08X %05X %07X\n",v,a,PC);
//                printf("Write VRAMl %08X %05X %07X\n",v,a,PC);
                vram[(a&0x1FFFC)>>2]=v;
                return;
                case 0x07000000: /*Sprites*/
                oam[(a>>1)&511]=v&0xFFFF;
                oam[((a+2)>>1)&511]=v>>16;
                return;
        }
//        printf("Bad write %08X %08X\n",a,v);
//        dumpregs();
//        exit(-1);
}

void writememfw(unsigned long a, unsigned short v)
{
//        if (((a)&0xF007FFC)==0x03001048) rpclog("Write %08XW %04X %07X\n",a,v,PC);
//        if (((a)&0xF007FFE)==0x03005068) rpclog("Write %08XW %08X %07X\n",0x5068,v,PC);
//        if (((a)&0xF0003FE)==0x400000E) printf("Write BG3W %04X %07X\n",a,v,PC);
/*        if (((a)&0xF03FFFE)==0x202EFFC && v==0x6823)
        {
                printf("Write 202EFFCW %04X %07X %i\n",v,PC,ins);
                if (!(lcd.ctrl&7)) output=1;
                if (output)
                {
                        rpclog("Write 202EFFCW %04X %07X %i\n",v,PC,ins);
                        rpclog("OUTPUT ON!!\n");
                }
        }
        if (((a)&0xF03FFFE)==0x202EFFE && output) rpclog("Write 202EFFEW %04X %07X %i\n",v,PC,ins);
        if (((a)&0xF03FFFE)==0x202F000 && output) rpclog("Write 202F000W %04X %07X %i\n",v,PC,ins);
        if (((a)&0xF03FFFE)==0x202F002 && output) rpclog("Write 202F002W %04X %07X %i\n",v,PC,ins);
        if (((a)&0xF03FFFE)==0x202F002 && output) output=0;*/
//        if (output) rpclog("Write %08X %04X %i\n",a,v,output);
//        if (((a)&0xF007FFC)==0x30007C0) printf("Write 30007C0W %04X %07X\n",v,PC);
        switch (a&0x0F000000)
        {
                case 0: return;
                case 0x02000000: /*External RAM*/
                if (!indma) cycles-=2;
                ram2w[(a&0x3FFFE)>>1]=v;
                return;
                case 0x03000000: /*Internal RAM*/
//                cycles--;
                ramw[(a&0x7FFE)>>1]=v;
                return;
                case 0x04000000: /*IO*/ /*16-bit, so split up the transfers*/
                writeio(a,v&0xFFFF);
                return;
                case 0x05000000: /*Palette*/
//                printf("Write palette %i %04X %08X\n",(a>>1)&511,v,armregs[15]);
                pal[(a>>1)&511]=v&0xFFFF;
                palchange=1;
                return;
                case 0x06000000: /*VRAM*/
  //              if ((a&0x1FFFE)==0x11000) printf("Write VRAMw %04X %05X %07X\n",v,a,PC);
//                if ((a&0x1FFFE)==0x11350) printf("Write VRAMw %04X %05X %07X\n",v,a,PC);
//                printf("Write VRAMw %04X %05X %07X\n",v,a,PC);
                vramw[(a&0x1FFFE)>>1]=v;
                return;
                case 0x07000000: /*Sprites*/
                oam[(a>>1)&511]=v&0xFFFF;
                return;
                case 0x08000000: /*ROM*/
                case 0x09000000: /*ROM*/
                case 0x0A000000: /*ROM*/
                case 0x0B000000: /*ROM*/
                case 0x0C000000: /*ROM*/
//                case 0x0D000000: /*ROM*/
                return;
                case 0xD000000:
                        if (eeprompresent) writeeeprom(v);
//                        printf("Write EEPROM %08X %04X\n",a,v);
                        return;
        }
//        printf("Bad writew %08X %08X\n",a,v);
//        dumpregs();
//        exit(-1);
}

void writememfb(unsigned long a, unsigned char v)
{
//        if (((a)&0xF007FFC)==0x03001048) rpclog("Write %08XB %02X %07X\n",a,v,PC);
//        if (((a)&0xF0003FE)==0x400000E) printf("Write BG3B %08X %02X %07X\n",a,v,PC);
//        if (((a)&0xF03FFFC)==0x202EFFC) printf("Write 202EFFCB %02X %07X\n",v,PC);
        switch (a&0x0F000000)
        {
                case 0: return;
                case 0x02000000: /*External RAM*/
                if (!indma) cycles-=2;
                ram2b[a&0x3FFFF]=v;
                return;
                case 0x03000000: /*Internal RAM*/
//                cycles--;
                ramb[a&0x7FFF]=v;
                return;
                case 0x04000000: /*IO*/ /*16-bit, so split up the transfers*/
                if (a==0x4000301) writehaltcnt(v);
//                printf("BYTE WRITE %06X %02X\n",a,v);
                if (a&1) writeio(a,(v<<8)|(readio(a)&0xFF));
                else     writeio(a,v|(readio(a)&0xFF00));
                return;
                case 0x05000000: /*Palette*/
//                printf("Write palette %i %04X %08X\n",(a>>1)&511,v,armregs[15]);
                pal[(a>>1)&511]=v|(v<<8);
                palchange=1;
                return;
                case 0x06000000: /*VRAM*/
//                if (!(a&0x1FFFE)) printf("Write VRAMb %02X %05X %07X\n",v,a,PC);
                if (!(a&0x10000)) vramw[(a&0x1FFFE)>>1]=v|(v<<8);
                return;
                case 0x0E000000: /*SRAM*/
                if (flashpresent)
                {
//                        printf("Write FLASH %08X %02X\n",a,v);
                        writeflash(a,v);
                        return;
                }
                if (a<0xE020000) writesram(a,v);
                return;
        }
//        printf("Bad writeb %08X %02X\n",a,v);
//        dumpregs();
//        exit(-1);
}
