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

//int output;
uint32_t opcode,opcode2,opcode3;

int hblank=0;

#define VBLINT 1
#define HBLINT 2
#define VCMINT 4
#define TIMER0INT 0x08
#define TIMER1INT 0x10
#define TIMER2INT 0x20
#define TIMER3INT 0x40

int waitclks[4]={5,4,3,9};

void writehaltcnt(unsigned char v)
{
        haltit=1;
}

unsigned long getbase()
{
        if (!(lcd.ctrl&0x10)) return 0;
        return 0xA000;
}

void updateints()
{
        if (gba.ifr&gba.ie && gba.ime) armirq=1;
        else                           armirq=0;
}

int testint()
{
        return (gba.ifr&gba.ie && gba.ime);
}

void gbaint(int num)
{
        gba.ifr|=num;
//        printf("GBA int %04X %04X %04X %i\n",num,gba.ifr,gba.ie,gba.ime);
        updateints();
}

void clearhblank()
{
        hblank=0;
}

void clockhblank()
{
        hblank=1;
        drawline(lcd.line);
        lcd.line++;
        if (lcd.line>=228) lcd.line=0;
        if (lcd.line==160 && lcd.dispstat&8)
        {
                gbaint(VBLINT);
//                printf("Start video\n");
//                drawvideo();
//                printf("End video\n");
        }
        if (lcd.line==(lcd.dispstat>>8) && lcd.dispstat&0x20)
        {
                gbaint(VCMINT);
        }
        if (lcd.dispstat&0x10)
        {
                gbaint(HBLINT);
//                printf("HBLINT %i\n",lcd.line);
        }
}

int prescalar[4]={1,64,256,1024};
int timerints[4]={TIMER0INT,TIMER1INT,TIMER2INT,TIMER3INT};
void clocktimers()
{
        int c;
        int preoverflow=0;
        for (c=0;c<4;c++)
        {
                if (timers.ctrl[c]&0x80)
                {
                        if (!(timers.ctrl[c]&4)) timers.count[c]-=613;//1226;
                        else if (preoverflow)    timers.dat[c]++;
                        preoverflow=0;
                        if (timers.ctrl[c]&4)
                        {
                                timers.dat[c]&=0xFFFF;
//                                printf("Timers %04X %04X\n",timers.count[c],timers.latch[c]);
                                if (timers.dat[c]==0)
                                {
                                        timers.dat[c]+=timers.latch[c];
                                        if (timers.ctrl[c]&0x40)
                                        {
                                                gbaint(timerints[c]);
                                        }
                                        preoverflow=1;
                                        switch (c)
                                        {
                                                case 0:
                                                if (!(sound.ctrlh&0x0400)) clocksounda();
                                                if (!(sound.ctrlh&0x4000)) clocksoundb();
                                                break;
                                                case 1:
                                                if (sound.ctrlh&0x0400) clocksounda();
                                                if (sound.ctrlh&0x4000) clocksoundb();
                                                break;
                                        }
                                }
                        }
                        else
                        {
                                while (timers.count[c]<=0)//(timers.latch[c]*prescalar[timers.ctrl[c]&3]))
                                {
//                                        if (!c) printf("Timer 0 overflow! %04X %04X\n",gba.ie,lcd.dispstat);
                                        timers.count[c]+=((0x10000-timers.latch[c])*prescalar[timers.ctrl[c]&3]);
//                                        timers.count[c]-=timers.latch[c]*prescalar[timers.ctrl[c]&3];
                                        if (timers.ctrl[c]&0x40)
                                        {
                                                gbaint(timerints[c]);
                                        }
                                        preoverflow=1;
                                        switch (c)
                                        {
                                                case 0:
                                                if (!(sound.ctrlh&0x0400)) clocksounda();
                                                if (!(sound.ctrlh&0x4000)) clocksoundb();
                                                break;
                                                case 1:
                                                if (sound.ctrlh&0x0400) clocksounda();
                                                if (sound.ctrlh&0x4000) clocksoundb();
                                                break;
                                        }
                                }
                                timers.dat[c]=0xFFFF-(timers.count[c]/prescalar[timers.ctrl[c]&3]);
                        }
                }
        }
}

void startdma(int c)
{
        uint32_t temp,olddst;
        if (dma[c].ctrl&0x3000)
        {
//                printf("Bad DMA%i start mode! %04X  src %08X dest %08X len %08X\n",c,dma[c].ctrl,dma[c].src,dma[c].dst,dma[c].len);
//                dma[c].ctrl&=~0x8000;
                return;
//                dumpregs();
//                exit(-1);
        }
        indma=1;
        dmasize=dma[c].len;
//        printf("DMA%i! %08X %08X %04X %04X\n",c,dma[c].src,dma[c].dst,dma[c].len,dma[c].ctrl);
        olddst=dma[c].dst;
        do
        {
                if (dma[c].ctrl&0x400) /*32 bit*/
                {
                        temp=readmeml(dma[c].src);
                        writememl(dma[c].dst,temp);
//                        printf("DMA read %08X write %08X\n",dma[c].src,dma[c].dst);
                }
                else /*16 bit*/
                {
                        temp=readmemw(dma[c].src);
                        writememw(dma[c].dst,temp);
//                        if ((dma[c].dst&0xF000000)==0x5000000) printf("Palette write %08X %08X %04X\n",dma[c].src,dma[c].dst,temp);
                }
                switch ((dma[c].ctrl>>7)&3) /*src address mode*/
                {
                        case 0: dma[c].src+=(dma[c].ctrl&0x400)?4:2; break;
                        case 1: dma[c].src-=(dma[c].ctrl&0x400)?4:2; break;
                }
                switch ((dma[c].ctrl>>5)&3) /*dst address mode*/
                {
                        case 0: case 3: dma[c].dst+=(dma[c].ctrl&0x400)?4:2; break;
                        case 1: dma[c].dst-=(dma[c].ctrl&0x400)?4:2; break;
//                        case 3: error("dst DMA addr mode 3\n"); dumpregs(); exit(-1);
                }
                dma[c].len--;
                if (c!=3) dma[c].len&=0x3FFF;
        } while (dma[c].len);
        indma=0;
        if (((dma[c].ctrl>>5)&3)==3) dma[c].dst=olddst;
        dma[c].ctrl&=~0x8000;
        if (dma[c].ctrl&0x4000)
        {
                error("DMA IRQ expected! %04X\n",dma[c].ctrl);
                dumpregs();
                exit(-1);
        }
//        printf("DMA over!\n");
}

void sounddma(int c)
{
        int d;
        unsigned long temp;
        if ((dma[c].ctrl&0x3000)!=0x3000) return;
        for (d=0;d<16;d+=4)
        {
                temp=readmeml(dma[c].src);
                writememw(dma[c].dst,temp&0xFFFF);
                writememw(dma[c].dst+2,temp>>16);
                dma[c].src+=4;
        }
        if (dma[c].ctrl&0x4000)
        {
                gbaint(0x100<<c);
/*                error("DMA IRQ expected! %04X\n",dma[c].ctrl);
                dumpregs();
                exit(-1);*/
        }
}

void hblankdma()
{
        int c;
        uint32_t temp;
        uint32_t olddst;
        if (lcd.line>=160) return;
        indma=1;
        for (c=0;c<4;c++)
        {
                if ((dma[c].ctrl&0x3000)==0x2000 && dma[c].ctrl&0x8000)
                {
                        olddst=dma[c].dst;
                        do
                        {
                                if (dma[c].ctrl&0x400) /*32 bit*/
                                {
//                                        printf("%i HBLANK - Write %08X %08X\n",c,dma[c].dst,temp);
                                        temp=readmeml(dma[c].src);
                                        writememl(dma[c].dst,temp);
//                                        if (dma[c].dst==0x4000028) printf("Writingl BG2X from %08X\n",dma[c].src);
                                }
                                else /*16 bit*/
                                {
//                                        printf("%i HBLANK - Write %08X %04X\n",c,dma[c].dst,temp);
                                        temp=readmemw(dma[c].src);
                                        writememw(dma[c].dst,temp);
//                                        if (dma[c].dst==0x4000028) printf("Writingw BG2X from %08X\n",dma[c].src);
                                }
                                switch ((dma[c].ctrl>>7)&3) /*src address mode*/
                                {
                                        case 0: dma[c].src+=(dma[c].ctrl&0x400)?4:2; break;
                                        case 1: dma[c].src-=(dma[c].ctrl&0x400)?4:2; break;
                                }
                                switch ((dma[c].ctrl>>5)&3) /*dst address mode*/
                                {
                                        case 0: case 3: dma[c].dst+=(dma[c].ctrl&0x400)?4:2; break;
                                        case 1: dma[c].dst-=(dma[c].ctrl&0x400)?4:2; break;
                                }
                                dma[c].len--;
//                                if (c!=3) dma[c].len&=0x3FFF;
                        } while (dma[c].len);
                        dma[c].len=dma[c].lenl;
                        if (((dma[c].ctrl>>5)&3)==3) dma[c].dst=olddst;
                }
        }
        indma=0;
}

unsigned short wind40,wind42,wind44,wind46;
void writeio(unsigned long a, unsigned short v)
{
//        if ((a&0xFF8)==0x40) printf("Write IO %08X %04X %08X %i\n",a,v,PC,indma);
        switch (a&0x7FE)
        {
                case 0: /*LCD control*/
//                if ((v&7)!=(lcd.ctrl&7)) printf("LCD mode now %i\n",v&7);
                lcd.ctrl=v;
//                printf("DISPCTRL %04X\n",v);
                break;
                case 2: /*Green swap*/
                break;
                case 4: /*DISPSTAT*/
                lcd.dispstat=v;
//                printf("DISPSTAT now %04X\n",v);
                break;
                case 6: break;
                case 0x8: case 0xA: case 0xC: case 0xE:
//                if ((((a-8)>>1)&3)==2) printf("BG2CTRL %04X\n",v);
                lcd.bgctrl[((a-8)>>1)&3]=v;
                break;
                case 0x10: case 0x14: case 0x18: case 0x1C:
                lcd.xscroll[((a-0x10)>>2)&3]=v&511;
//                printf("Xscroll %i %i %08X\n",((a-0x10)>>2)&3,v,a);
                break;
                case 0x12: case 0x16: case 0x1A: case 0x1E:
                lcd.yscroll[((a-0x12)>>2)&3]=v&511;
//                printf("Yscroll %i %i %08X\n",((a-0x12)>>2)&3,v,a);
                break;
                case 0x20: lcd.dx[0]=v;  if (v&0x8000) lcd.dx[0]|=0xFFFF0000;  break;
                case 0x22: lcd.dmx[0]=v; if (v&0x8000) lcd.dmx[0]|=0xFFFF0000; break;
                case 0x24: lcd.dy[0]=v;  if (v&0x8000) lcd.dy[0]|=0xFFFF0000;  break;
                case 0x26: lcd.dmy[0]=v; if (v&0x8000) lcd.dmy[0]|=0xFFFF0000; break;
                case 0x28: lcd.bg2x=(lcd.bg2x&0xFFF0000)|v; rotozoom.bg2x=lcd.bg2x; /*printf("Write BG2X %08X\n",PC); */break;
                case 0x2A: if (v&0x800) v|=0xF000; lcd.bg2x=(lcd.bg2x&0xFFFF)|(v<<16); rotozoom.bg2x=lcd.bg2x; break;
                case 0x2C: lcd.bg2y=(lcd.bg2y&0xFFF0000)|v; rotozoom.bg2y=lcd.bg2y; break;
                case 0x2E: if (v&0x800) v|=0xF000; lcd.bg2y=(lcd.bg2y&0xFFFF)|(v<<16); rotozoom.bg2y=lcd.bg2y; break;

                case 0x30: lcd.dx[1]=v;  if (v&0x8000) lcd.dx[1]|=0xFFFF0000;  break;
                case 0x32: lcd.dmx[1]=v; if (v&0x8000) lcd.dmx[1]|=0xFFFF0000; break;
                case 0x34: lcd.dy[1]=v;  if (v&0x8000) lcd.dy[1]|=0xFFFF0000;  break;
                case 0x36: lcd.dmy[1]=v; if (v&0x8000) lcd.dmy[1]|=0xFFFF0000; break;
                case 0x38: lcd.bg3x=(lcd.bg3x&0xFFF0000)|v; rotozoom.bg3x=lcd.bg3x; break;
                case 0x3A: lcd.bg3x=(lcd.bg3x&0xFFFF)|((v<<16)&0xFFF0000); rotozoom.bg3x=lcd.bg3x; break;
                case 0x3C: lcd.bg3y=(lcd.bg3y&0xFFF0000)|v; rotozoom.bg3y=lcd.bg3y; break;
                case 0x3E: lcd.bg3y=(lcd.bg3y&0xFFFF)|((v<<16)&0xFFF0000); rotozoom.bg3y=lcd.bg3y; break;

                case 0x40:
                windows.x2[0]=v&0xFF;
                windows.x1[0]=v>>8;
                if (windows.x1[0]>windows.x2[0]) windows.x2[0]=240;
                wind40=v;
                break;
                case 0x42:
                windows.x2[1]=v&0xFF;
                windows.x1[1]=v>>8;
                if (windows.x1[1]>windows.x2[1]) windows.x2[1]=240;
                wind42=v;
                break;
                case 0x44:
                windows.y2[0]=v&0xFF;
                windows.y1[0]=v>>8;
                if (windows.y1[0]>windows.y2[0]) windows.y2[0]=240;
                wind44=v;
                break;
                case 0x46:
                windows.y2[1]=v&0xFF;
                windows.y1[1]=v>>8;
                if (windows.y1[1]>windows.y2[1]) windows.y2[1]=240;
                wind46=v;
                break;
                case 0x48: lcd.winin=v; break;
                case 0x4A: lcd.winout=v; break;
                case 0x4C: lcd.mosaic=v; break;
                
                case 0x50: gba.blend=v; break;
                case 0x52: gba.blalpha=v; gba.bl1=v&0x1F; gba.bl2=(v>>8)&0x1F; break;
                case 0x54: /*printf("Write 54 %04X\n",v); */gba.bright=v; if (v>15) gba.br=16; else gba.br=v; break;

                case 0x60: case 0x62: case 0x64: case 0x66:
                case 0x68: case 0x6A: case 0x6C: case 0x6E:
                case 0x70: case 0x72: case 0x74: case 0x76:
                case 0x78: case 0x7A: case 0x7C: case 0x7E:
                sound.ctrls[((a-0x60)>>1)&0xF]=v;
                case 0x90: case 0x92: case 0x94: case 0x96:
                case 0x98: case 0x9A: case 0x9C: case 0x9E:
                writesound(a,v);
                break;

                case 0x80: sound.ctrll=v; break;
                case 0x82:
                sound.ctrlh=v;
                if (v&0x0800) resetsounda();
                if (v&0x8000) resetsoundb();
                break;
                case 0x84: sound.ctrlx=v; break;
                case 0x88:
                sound.bias=v;
                break;
                case 0xA0: case 0xA2:
                writesounda(v&0xFF);
                writesounda(v>>8);
                break;
                case 0xA4: case 0xA6:
                writesoundb(v&0xFF);
                writesoundb(v>>8);
                break;
                case 0xB0: /*printf("DMASRCL %04X\n",v);*/ dma[0].srcl=(dma[0].srcl&0xFFFF0000)|v; break;
                case 0xB2: /*printf("DMASRCH %04X\n",v);*/ dma[0].srcl=(dma[0].srcl&0xFFFF)|(v<<16); break;
                case 0xB4: /*printf("DMADSTL %04X\n",v);*/ dma[0].dstl=(dma[0].dstl&0xFFFF0000)|v; break;
                case 0xB6: /*printf("DMADSTH %04X\n",v);*/ dma[0].dstl=(dma[0].dstl&0xFFFF)|(v<<16); break;
                case 0xB8: /*printf("DMALEN %04X\n",v);*/ dma[0].lenl=v; break;
                case 0xBA:
//                        printf("Write DMA0 CTRL %04X %04X %08X %08X %08X\n",v,dma[0].ctrl,PC,armregs[0],armregs[2]);
                if (v&0x8000 && !(dma[0].ctrl&0x8000))
                {
                        dma[0].src=dma[0].srcl;
                        dma[0].dst=dma[0].dstl;
                        dma[0].len=dma[0].lenl;
                }
                dma[0].ctrl=v;
                if (v&0x8000) startdma(0);
                break;

                case 0xBC: dma[1].srcl=(dma[1].srcl&0xFFFF0000)|v; break;
                case 0xBE: dma[1].srcl=(dma[1].srcl&0xFFFF)|(v<<16); break;
                case 0xC0: dma[1].dstl=(dma[1].dstl&0xFFFF0000)|v; break;
                case 0xC2: dma[1].dstl=(dma[1].dstl&0xFFFF)|(v<<16); break;
                case 0xC4: dma[1].lenl=v; break;
                case 0xC6:
                if (v&0x8000 && !(dma[1].ctrl&0x8000))
                {
                        dma[1].src=dma[1].srcl;
                        dma[1].dst=dma[1].dstl;
                        dma[1].len=dma[1].lenl;
                }
                dma[1].ctrl=v;
                if (v&0x8000) startdma(1);
                break;

                case 0xC8: dma[2].srcl=(dma[2].srcl&0xFFFF0000)|v; break;
                case 0xCA: dma[2].srcl=(dma[2].srcl&0xFFFF)|(v<<16); break;
                case 0xCC: dma[2].dstl=(dma[2].dstl&0xFFFF0000)|v; break;
                case 0xCE: dma[2].dstl=(dma[2].dstl&0xFFFF)|(v<<16); break;
                case 0xD0: dma[2].lenl=v; break;
                case 0xD2:
                if (v&0x8000 && !(dma[2].ctrl&0x8000))
                {
                        dma[2].src=dma[2].srcl;
                        dma[2].dst=dma[2].dstl;
                        dma[2].len=dma[2].lenl;
                }
                dma[2].ctrl=v;
                if (v&0x8000) startdma(2);
                break;
                
                case 0xD4: dma[3].srcl=(dma[3].srcl&0xFFFF0000)|v; break;
                case 0xD6: dma[3].srcl=(dma[3].srcl&0xFFFF)|(v<<16); break;
                case 0xD8: dma[3].dstl=(dma[3].dstl&0xFFFF0000)|v; break;
                case 0xDA: dma[3].dstl=(dma[3].dstl&0xFFFF)|(v<<16); break;
                case 0xDC: dma[3].lenl=v; /*rpclog("DMALEN3 %03X\n",dma[3].lenl);*/ break;
                case 0xDE:
                if (v&0x8000 && !(dma[3].ctrl&0x8000))
                {
                        dma[3].src=dma[3].srcl;
                        dma[3].dst=dma[3].dstl;
                        dma[3].len=dma[3].lenl;
                }
                dma[3].ctrl=v;
                if (v&0x8000)
                {
//                        rpclog("Start DMA src %08X dst %08X len %04X\n",dma[3].src,dma[3].dst,dma[3].len);
                        startdma(3);
                }
                break;
                
                case 0x100: timers.latch[0]=v; break;
                case 0x104: timers.latch[1]=v; break;
                case 0x108: timers.latch[2]=v; /*printf("Timer2latch %04X\n",v); */break;
                case 0x10C: timers.latch[3]=v; break;
                case 0x102:
                if (v&0x80 && !(timers.ctrl[0]&0x80)) timers.count[0]=0;//timers.latch[0]*prescalar[v&3];
                timers.ctrl[0]=v;
                break;
                case 0x106:
                if (v&0x80 && !(timers.ctrl[1]&0x80)) timers.count[1]=0;//timers.latch[1]*prescalar[v&3];
                timers.ctrl[1]=v;
                break;
                case 0x10A:
                if (v&0x80 && !(timers.ctrl[2]&0x80)) timers.count[2]=0;//timers.latch[2]*prescalar[v&3];
                timers.ctrl[2]=v;
//                printf("Timer2crtl %04X\n",v);
                break;
                case 0x10E:
                if (v&0x80 && !(timers.ctrl[3]&0x80)) timers.count[3]=0;//timers.latch[3]*prescalar[v&3];
                timers.ctrl[3]=v;
                break;
                
                case 0x132:
                gba.keycnt=v;
//                printf("KEYCNT %04X %i %i\n",v,eeprompresent,srampresent);
                break;
                case 0x134:
                gba.rcnt=v;
                break;
                
                case 0x200:
                gba.ie=v;
                updateints();
//                printf("Interrupt enable %04X %08X %08X\n",v,armregs[15],opcode);
/*                if (v==0x2000 && armregs[15]==0x300357C)
                {
                        output=1;
                        printf("OUTPUT 1!!\n");
                }*/
//                if (armregs[15]==0x8001170) output=1;
/*                if (output)
                {
                        dumpregs();
                        exit(-1);
                }*/
                break;
                case 0x202:
                gba.ifr&=~v;
                updateints();
                break;
                case 0x204:
                gba.waitcnt=v&0x7FFF;
//                printf("WAITCNT %04X\n",v);
                romwait16[0]=waitclks[(v>>2)&3];
                romwait16[1]=waitclks[(v>>5)&3];
                romwait16[2]=waitclks[(v>>8)&3];
                romwait32[0]=romwait16[0]+((v&0x10)?1:2);
                romwait32[1]=romwait16[1]+((v&0x80)?1:4);
                romwait32[2]=romwait16[2]+((v&0x400)?1:8);
//                printf("Waitstates - %i,%i %i,%i %i,%i\n",romwait16[0],romwait32[0],romwait16[1],romwait32[1],romwait16[2],romwait32[2]);
                break;
                case 0x208:
                gba.ime=v&1;
                updateints();
                break;
                case 0x20A: break;
                case 0x300: gba.bootflag=v&1; /*printf("Write %08X %04X\n",a,v); */break;
                case 0x800: //printf("MEMCTRL %04X\n",v);
                break;

                default:
                        return;
                error("Bad IO write %08X\n",a);
                dumpregs();
                exit(-1);
        }
}

unsigned short readio(unsigned long a)
{
        unsigned short temp=0;
//        if (a!=0x4000006) printf("Read IO %08X %07X  %04X %04X  %04X %04X %04X %04X\n",a,PC,timers.count[2],timers.count[3],timers.latch[0],timers.latch[1],timers.latch[2],timers.latch[3]);
        switch (a&0x7FE)
        {
                case 0: /*LCD control*/
                return lcd.ctrl;
                case 2: return 0;
                case 4: /*DISPSTAT*/
                if (lcd.line>=160) temp|=1;
                if (lcd.line==(lcd.dispstat>>8)) temp|=4;
                if (hblank) temp|=2;
                return temp|(lcd.dispstat&0xFF38);
                case 6: /*VCOUNT*/
//                printf("Read line %08X %02X\n",PC,lcd.line);
                return lcd.line;
                case 0x8: case 0xA: case 0xC: case 0xE:
                return lcd.bgctrl[((a-8)>>1)&3];
                case 0x50: return gba.blend;
                case 0x52: return gba.blalpha;
                case 0x54: return 0;//gba.bright;
                case 0x60: case 0x62: case 0x64: case 0x66:
                case 0x68: case 0x6A: case 0x6C: case 0x6E:
                case 0x70: case 0x72: case 0x74: case 0x76:
                case 0x78: case 0x7A: case 0x7C: case 0x7E:
                return sound.ctrls[((a-0x60)>>1)&0xF];
                case 0x80: return sound.ctrll;
                case 0x82: return sound.ctrlh;
                case 0x84: return sound.ctrlx;
                case 0x88: return sound.bias;
//                case 0xB8: return 0;
                case 0xBA: return dma[0].ctrl;
//                case 0xC4: return 0;
                case 0xC6: return dma[1].ctrl;
//                case 0xD0: return 0;
                case 0xD2: return dma[2].ctrl;
//                case 0xDC: return 0;
                case 0xDE: return dma[3].ctrl;
                case 0x100: return timers.dat[0];
//                case 0x100: return timers.count[0]/prescalar[timers.ctrl[0]&3];
                case 0x102: return timers.ctrl[0];
                case 0x104: return timers.dat[1];
//                if (timers.ctrl[1]&4) return timers.count[1];
//                return timers.count[1]/prescalar[timers.ctrl[1]&3];
                case 0x106: return timers.ctrl[1];
                case 0x108: return timers.dat[2];
//                if (timers.ctrl[2]&4) return timers.count[2];
//                return timers.count[2]/prescalar[timers.ctrl[2]&3];
                case 0x10A: return timers.ctrl[2];
                case 0x10C: return timers.dat[3];
//                if (timers.ctrl[3]&4) return timers.count[3];
//                return timers.count[3]/prescalar[timers.ctrl[3]&3];
                case 0x10E: return timers.ctrl[3];
                case 0x120: return 0; /*SIODATA32*/
                case 0x122: case 0x124: case 0x126: return 0; /*SIOMULTIx*/
                case 0x128: return 0; /*SIOCNT*/
                case 0x12A: return 0; /*SIODATA*/
                case 0x130: /*KEYSTAT*/
                return readkeystat();
                case 0x132:
                return gba.keycnt;
                case 0x134: return gba.rcnt;
                case 0x140: case 0x154: case 0x158: return 0;
                case 0x150: case 0x152: return 0;
                case 0x1F8: return 0;
                case 0x200:
//                printf("IE %04X %08X\n",gba.ie,armregs[15]);
                return gba.ie;
                case 0x202:
//                printf("IFR %04X %08X\n",gba.ifr,armregs[15]);
                return gba.ifr;
                case 0x204:
                return gba.waitcnt;
                case 0x206: return 0;
                case 0x208:
                return gba.ime;
                case 0x20A: return 0;
                case 0x300: return 0;//gba.bootflag;
                
                case 0x10: case 0x12: case 0x14: case 0x16:
                case 0x18: case 0x1A: case 0x1C: case 0x1E:
                case 0x20: case 0x22: case 0x24: case 0x26:
                case 0x28: case 0x2A: case 0x2C: case 0x2E:
                case 0x30: case 0x32: case 0x34: case 0x36:
                case 0x38: case 0x3A: case 0x3C: case 0x3E:
                        case 0x56:
                        return 0;
                case 0x40: return wind40;
                case 0x42: return wind42;
                case 0x44: return wind44;
                case 0x46: return wind46;
                case 0x48: return lcd.winin;
                case 0x4A: return lcd.winout;

                case 0x4C: case 0x4E: return 0;
                
                case 0xB0: case 0xB2: case 0xB4: case 0xB6: case 0xB8: return 0;
                case 0xBC: case 0xBE: case 0xC0: case 0xC2: case 0xC4: return 0;
                case 0xC8: case 0xCA: case 0xCC: case 0xCE: case 0xD0: return 0;
                case 0xD4: case 0xD6: case 0xD8: case 0xDA: case 0xDC: return 0;
                
                default:
                if (a&0x400) return 0;
                return 0;
                dumpregs();
                error("Bad IO read %08X\n",a);
                exit(-1);
        }
}
