/*
    Hardware addressing
    ===================

    Written by LDChen
    Modified by LDChen
*/

#include "fpse.h"

#ifdef MSB_FIRST
#define   HW8(a)  hwarea[4*((a)/4)+3-(a)%4]
#define   HW16(a) (*(UINT16*)&hwarea[4*((a)/4)+2-(a)%4])
#define   HW32(a) (*(UINT32*)&hwarea[a])
#else
#define   HW8(a)  hwarea[a]
#define   HW16(a) (*(UINT16*)&hwarea[a])
#define   HW32(a) (*(UINT32*)&hwarea[a])
#endif

#define NOPRINTPC   1

typedef struct {
     UINT32 addr;
     char  *name;
} NAM;

/*
     1f801000
     1f801004
     1f801008
     1f80100c
     1f801010
     1f80101c
     1f802041  ???
*/
static NAM hwnames[] = {
     {0x1f801014,"spu_delay"},
     {0x1f801018,"dv5_delay"},
     {0x1f801020,"com_delay"},

     {0x1f801040,"sio0_data"},
     {0x1f801044,"sio0_status"},
        {0x1f801048,"sio0_mode"},
     {0x1f80104a,"sio0_control"},
     {0x1f80104e,"sio0_baud"},

     {0x1f801050,"sio1_data"},
     {0x1f801054,"sio1_status"},
        {0x1F801058,"sio1_mode"},
     {0x1f80105a,"sio1_control"},
     {0x1f80105e,"sio1_baud"},

     {0x1f801060,"ram_size"},

     {0x1f801070,"int_reg"},
     {0x1f801074,"int_mask"},
     
     {0x1f801080,"mdec_dma0_madr"},
     {0x1f801084,"mdec_dma0_bcr"},
     {0x1f801088,"mdec_dma0_chcr"},
     
     {0x1f801090,"mdec_dma1_madr"},
     {0x1f801094,"mdec_dma1_bcr"},
     {0x1f801098,"mdec_dma1_chcr"},
     
     {0x1f8010a0,"gpu_dma_madr"},
     {0x1f8010a4,"gpu_dma_bcr"},
     {0x1f8010a8,"gpu_dma_chcr"},
     
     {0x1f8010b0,"cd_dma_madr"},
     {0x1f8010b4,"cd_dma_bcr"},
     {0x1f8010b8,"cd_dma_chcr"},
     
     {0x1f8010c0,"spu_dma_madr"},
     {0x1f8010c4,"spu_dma_bcr"},
     {0x1f8010c8,"spu_dma_chcr"},
     
     {0x1f8010d0,"dma5_madr"},
     {0x1f8010d4,"dma5_bcr"},
     {0x1f8010d8,"dma5_chcr"},
     
     {0x1f8010e0,"dma6_madr"},
     {0x1f8010e4,"dma6_bcr"},
     {0x1f8010e8,"dma6_chcr"},
     
     {0x1f8010f0,"dma_pcr"},
     {0x1f8010f4,"dma_icr"},

     {0x1f801100,"t0_count"},
     {0x1f801104,"t0_mode"},
     {0x1f801108,"t0_target"},

     {0x1f801110,"t1_count"},
     {0x1f801114,"t1_mode"},
     {0x1f801118,"t1_target"},

     {0x1f801120,"t2_count"},
     {0x1f801124,"t2_mode"},
     {0x1f801128,"t2_target"},

     {0x1f801800,"cdrom_reg0"},
     {0x1f801801,"cdrom_reg1"},
     {0x1f801802,"cdrom_reg2"},
     {0x1f801803,"cdrom_reg3"},

     {0x1f801810,"gpu_reg0"},
     {0x1f801814,"gpu_reg1"},

     {0x1f801820,"mdec_reg0"},
     {0x1f801824,"mdec_reg1"},

     {0x1f801d80,"spu_mvol_l"},
     {0x1f801d82,"spu_mvol_r"},
     {0x1f801d84,"spu_reverb_l"},
     {0x1f801d86,"spu_reverb_r"},
     {0x1f801d88,"spu_key_on_1"},
     {0x1f801d8a,"spu_key_on_2"},
     {0x1f801d8c,"spu_key_off_1"},
     {0x1f801d8e,"spu_key_off_2"},
     {0x1f801d90,"spu_key_modefm_1"},
     {0x1f801d92,"spu_key_modefm_2"},
     {0x1f801d94,"spu_key_modenoise_2"},
     {0x1f801d96,"spu_key_modenoise_2"},
     {0x1f801d98,"spu_key_modereverb_1"},
     {0x1f801d9a,"spu_key_modereverb_2"},
     {0x1f801d9c,"spu_key_channelactive_1"},
     {0x1f801d9e,"spu_key_channelactive_2"},

     {0x1f801da6,"spu_sbaddr"},
     {0x1f801da8,"spu_data"},
     {0x1f801daa,"spu_reg0"},
     {0x1f801dac,"spu_reg1"},
     {0x1f801dae,"spu_status"},

     {0x1f801db0,"spu_cdvol_l"},
     {0x1f801db2,"spu_cdvol_r"},
     {0x1f801db4,"spu_extvol_l"},
     {0x1f801db6,"spu_extvol_r"},

     {0x1f801dc0,"spu_reverbconfig"},
     {0x1f801dfc,"spu_factor_l"},
     {0x1f801dfe,"spu_factor_r"},

     {0x1f802030,"int_2000"},
     {0x1f802040,"dip_switches"},

     /* extension */
     {0x1f000000,"parrom_status"},
     {0x1f000001,"parrom_type"},
     {0x1f005555,"parrom_reg0"},
     {0x1f002aaa,"parrom_reg1"},
     {0x1f060000,"par_in"}, /* byte */
     {0x1f060008,"par_out"}, /* byte */
     {0x1f020010,"par_status"}, /* bit 0: 1= busy */
     {0x1f020018,"par_switch"}, /* bit 0: 1= on */

     {0,""}
};

static char *sregname[] = {
     "vol_l","vol_r","freq","sbadr","ar","adsr2","adsrvol","repeat"
};

char* hwname(unsigned long addr)
{
     NAM *p;
     static char buf[16];
     for(p=hwnames; p->addr && p->addr!=addr; p++) ;
     if (p->addr) return p->name;
     if (addr>=0x1f801c00 && addr<0x1f801c00+16*24) {
          sprintf(buf,"voice%d_%s",(int)((addr-0x1f801c00)/16),sregname[(addr&15)/2]);
     } else  sprintf(buf,"%08x",(int)addr);
     return buf;
}

#define   spu_reg0  HW16(0x1daa)
#define   t0_count  HW16(0x1100)
#define   t0_mode   HW32(0x1104)
#define   t0_target HW16(0x1108)
#define   t1_count  HW16(0x1110)
#define   t1_mode   HW32(0x1114)
#define   t1_target HW16(0x1118)
#define   t2_count  HW16(0x1120)
#define   t2_mode   HW32(0x1124)
#define   t2_target HW16(0x1128)
#define   int_reg   HW16(0x1070)
#define   int_mask  HW16(0x1074)
#define   dma_pcr   HW32(0x10f0)
#define   dma_icr   HW32(0x10f4)

UINT8 hwarea[0x8000];

/*
     counter mode

     base = 0x48
     0x10 割りこみ許可?
     0x01 ゲート条件有効
     0x100     0=clock/8 1=clock
*/

#define INTERRUPT(no) reg |= (1<<no)

#define UPPER   0xFFFF

static UINT32 base_count;
static UINT32 base_count2;
static UINT32 t0_limit=UPPER;
static UINT32 t1_limit=UPPER;
static UINT32 t2_limit=UPPER;

int Irq_Pulse = 0;

INT32  VSync_Register = 0;
INT32  Event_Register = 0;
INT32  Event_List[16];
UINT32 Event_Mask[16];

int (*EventCallBack[16])();
int (*VSyncCallBack[16])();

#define MAINCLOCK   33868800L

#define   PER  64

#define HSYNC   (MAINCLOCK/15734)
#define VSYNC   (MAINCLOCK/60)
#define ESYNC   13

static int treg=0;

static int dec1_count=HSYNC;

int update_hw(void)
{
    int reg = 0;
    int tmp;

    base_count+=PER;

    if (base_count>=VSYNC) {
        base_count=0;

        PRINTF("VSync\n");
        win_update();
        INTERRUPT(INT_VSync);
    }

    tmp = t0_count + ((t0_mode & 0x100)?PER:PER/8);
    if (tmp>=t0_limit && t0_count<t0_limit) {
        tmp = 0;
// printf("t0 == limit\n");
        if (t0_mode & 0x50) INTERRUPT(INT_CNT0);
    }
    t0_count=tmp;

    if ((t1_mode & 0x100)==0) {
        tmp = t1_count + PER;

        if (tmp>=t1_limit && t1_count<t1_limit) {
            tmp = 0;
// printf("t1 == limit\n");
            if (t1_mode & 0x50) INTERRUPT(INT_CNT1);
        }
        t1_count=tmp;
    } else
    if ((dec1_count-=PER) <= 0) {
        if (++t1_count == t1_limit) {
            t1_count = 0;
// printf("t1 == limit\n");
            if (t1_mode & 0x50) INTERRUPT(INT_CNT1);
        }
        dec1_count = HSYNC;
    }

    if ((t2_mode & 1) == 0)
    {
        tmp = t2_count + ((t2_mode & 0x200)?PER/8:PER);
        if (tmp>=t2_limit && t2_count<t2_limit) {
            tmp = 0;
// printf("t2 == limit\n");
            if (t2_mode & 0x50) INTERRUPT(INT_CNT2);
        }
        t2_count=tmp;
    }

    if (++base_count2>=ESYNC) {
        base_count2=0;
        if (VSync_Register < 0)
        {
            int v = 1;
//            PRINTF("Entering VSYNC EVENT\n");
            for (tmp=0;tmp<16;tmp++)
            {
                if ((VSync_Register & v) && --Event_List[tmp] <= 0)
                {
                    PRINTF("Found %d^ ASync\n",tmp);
                    treg |= v;
                    VSync_Register ^= v;
                    if ( (VSyncCallBack[tmp]) )
                    {
                        if (!VSyncCallBack[tmp]()) treg &= ~v;
                    }
                }
                v <<= 1;
            }
            if ((VSync_Register & 0x7FFFFFFF) == 0) 
                VSync_Register = 0;
        }
    }

    if (dma_icr&0x7f000000) { INTERRUPT(INT_DMA); }

    if (Event_Register < 0)
    {
        int x,y;

//printf("Async event enabled: %08x\n",Event_Register);
            for(x=0;x<16;x++)
            {
                y = Event_Mask[x];
//printf("Event_List[%d]=%d\n",x,Event_List[x]);
                if ((Event_Register & y) && Event_List[x] <= 0)
                {
                    treg |= y;
                    Event_Register &= ~y;
                    if ( (EventCallBack[x]) )
                    {
                        if (!EventCallBack[x]()) treg &= ~y;
                    }
                }
            }
            if (!(Event_Register & 0x7FFFFFFF))
                Event_Register = 0;
    }

    reg |= Irq_Pulse;
    HW32(0x1070) |= reg;
    Irq_Pulse = tmp = 0;
    if ((HW32(0x1074) & HW32(0x1070)))
        tmp = 1;

    HW32(0x1070) |= treg;
    if ( (treg) && (HW32(0x1074) & treg) )
        tmp = 1;

    treg=0;

    return tmp;
}

int update_counter(void)
{
    return update_hw();
}

/* OT clear */
void dma6_exec(UINT32 adr,UINT32 bcr,UINT32 chcr)
{
    if (chcr!=0x11000002) {
        PRINTF("dma6 unknown %x\n",(int)chcr);
        return;
    }

    if (!bcr) return;

    adr &= 0xFFFFFF;

    CompileFlush(adr-bcr*4,adr);

    while (--bcr) {
        *(UINT32*)&ram[adr & 0x1FFFFF] = SWAP32((adr-4)&0xffffff);
        adr-=4;
    }

    *(UINT32*)&ram[adr & 0x1FFFFF] = SWAP32(0xffffff);
}

int hw_init(void)
{
    int x;

#if AUTOSPEED
    time_t t1;
    UINT32 clk1 = rdtsc();

    t1 = clock() + CLOCKS_PER_SEC;
    while (clock() < t1);
    oldcnt = rdtsc();
    cpuspeed = (oldcnt - clk1);
    printf("CPU speed: %d MHz\n",(int)(cpuspeed/1000000));
    vsync = cpuspeed / 60; // -> 550000
    hsync = vsync / 250;
#endif

    memset(hwarea,0,0x8000);

// Init event masks
    for (x=0;x<16;x++) {
        Event_Mask[x] = 1<<x;
        EventCallBack[x] = NULL;
        VSyncCallBack[x] = NULL;
    }

// Start initialization
    mdec_init();
    if (sio_init() != FPSE_OK) return FPSE_ERR;
    cd_initvar();
    if (win_init() != FPSE_OK) return FPSE_ERR;

    return FPSE_OK;
}

void hw_close(void)
{
    win_term();
}

static UINT32 ParReply = 0x57;

UINT8 hw_romread(UINT32 adr)
{
    int ret;

    switch (adr) {
    case 0x1F020018:
    case 0x1F020010:
        ret = 1; break;
    case 0x1F060000: 
        if (ParReply & 0xF0) ret = ParReply;
        else {
            if (ParReply < 4) ret = extrom[3-ParReply];
                         else ret = 0;
            ParReply++;
        }
        break;
    default:
        if (adr > 0x1F000000 &&
            adr < 0x1F020000) ret = *(UINT32 *)&extrom[adr & 0x1FFFF];
        else ret = 0xFFFFFFFF; // Out of address
        break;
    }

    PRINTF("read rom extension %s,%02x\n",hwname(adr),ret);
    return ret;
}

void hw_romwrite(UINT32 adr, UINT32 data)
{
    PRINTF("write rom extension %s,%02x\n",hwname(adr),(int)data);

    data &= 0xFF;

    switch (adr) {
    case 0x1F060008:
        switch (data) {
        case 0x52: ParReply = 0x42; break;
        case 0x57: if (actionreplay) ParReply = 0x58;
                                else ParReply = 0x55;
                   break;
        case 0x55:
        case 0x58: ParReply = 0; break;
        default: break;
        }
        break;
    }
}

UINT8 hw_read8(UINT32 adr)
{
     int ret;
     switch(adr){
     case 0x1f801040: ret = sio_readdata8(&Sio0); break;
     case 0x1f801050: ret = sio_readdata8(&Sio1); break;
     case 0x1f801800: ret = cd0_read(); break;
     case 0x1f801801: ret = cd1_read(); break;
     case 0x1f801802: ret = cd2_read(); break;
     case 0x1f801803: ret = cd3_read(); break;
     default:
          ret = HW8(adr-0x1f800000); break;
     }
#if NOPRINTPC
     printpc();
#endif
     PRINTF("read byte %s,%02x\n",hwname(adr),ret);
     return ret;
}

UINT16 hw_read16(UINT32 adr)
{
     int ret;

     switch(adr){
     case 0x1f801070:
        if (Event_Register < 0)
        {
            int x,y;

//            ret = Event_RootCount;
//            Event_RootCount++;
            for(x=0;x<16;x++)
            {
                y = Event_Mask[x];
                if (Event_Register & y)
//                    if (ret >= Event_List[x])
                    if (--Event_List[x] <= 0)
                    {
                        treg |= y;
                        Event_Register &= ~y;
                    }
                if (!(Event_Register & 0x7FFFFFFF))
                    Event_Register = 0;
            }
        }
        ret= HW16(0x1070)|treg; break;

/* SIO0 - Controllers & MemCards */
     case 0x1f801040: ret = sio_readdata16(&Sio0); break;
     case 0x1f801044: ret = Sio0.Status; break;
        case 0x1F801048: ret = Sio0.Mode; break;
     case 0x1f80104a: ret = Sio0.Ctrl.Control16; break;
     case 0x1f80104e: ret = Sio0.Baud; break;
/* SIO1 - Serial port */
     case 0x1f801050: ret = sio_readdata16(&Sio1); break;
     case 0x1f801054: ret = Sio1.Status; break;
        case 0x1F801058: ret = Sio1.Mode; break;
     case 0x1f80105a: ret = Sio1.Ctrl.Control16; break;
     case 0x1f80105e: ret = Sio1.Baud; break;
/*
     case 0x1f801daa:
     case 0x1f801dae:
          return HW16(adr-0x1f800000);
*/
     default:
          if (adr>=0x1f801c00 && adr<0x1f801e00)
               ret = SPU_Read(adr);
                else ret = HW16(adr & 0x7FFF);
                break;
        }
#if NOPRINTPC
     printpc();
#endif
     PRINTF("read short %s,%04x\n",hwname(adr),ret);
     return ret;
}

UINT32 hw_read32(UINT32 adr)
{
     UINT32 ret;

     switch(adr) {
     case 0x1f801810: ret = GP0_Read(); break;
     case 0x1f801814: ret = GP1_Read(); break;
/* SIO0 */
     case 0x1f801040: ret = sio_readdata32(&Sio0); break;
/* SIO1 */
     case 0x1f801050: ret = sio_readdata32(&Sio1); break;

     case 0x1f801070:
        if (Event_Register < 0)
        {
            int x,y;

//            ret = Event_RootCount;
//            Event_RootCount++;
//printf("Async event enabled: %08x\n",Event_Register);
            for(x=0;x<16;x++)
            {
//printf("Event_List[%d]=%d\n",x,Event_List[x]);
                y = Event_Mask[x];
                if (Event_Register & y)
//                    if (ret >= Event_List[x])
                    if (--Event_List[x] <= 0)
                    {
                        treg |= y;
                        Event_Register &= ~y;
                    }
                if (!(Event_Register & 0x7FFFFFFF))
                    Event_Register = 0;
            }
        }
        ret= HW32(0x1070)|treg; break;
     case 0x1f801820: ret = mdec0_read(); break;
     case 0x1f801824: ret = mdec1_read(); break;
#if 0
     case 0x1f801088:
     case 0x1f801098:
     case 0x1f8010a8:
     case 0x1f8010b8:
     case 0x1f8010c8:
     case 0x1f8010d8:
     case 0x1f8010e8:
          // dma_chcr
          // busyでなくなったら read時に 0x01000000 が変わる ?
//        ret = 0;
//        break;
     case 0x1f8010f4:
          // dma status
//        ret = 0;
//        break;
#endif
     default:
          ret = HW32(adr-0x1f800000);
          break;
     }
#if NOPRINTPC
     printpc();
#endif
     PRINTF("read long %s,%08x\n",hwname(adr),(int)ret);
     return ret;
}

void hw_write8(UINT32 adr,UINT32 data)
{
#if NOPRINTPC
     printpc();
#endif
     PRINTF("write byte %s,%02x\n",hwname(adr),(int)data);

     switch(adr){
     case 0x1f801040: sio_writedata8(&Sio0,data); break;
     case 0x1f801050: sio_writedata8(&Sio1,data); break;
     case 0x1f801800: cd0_write(data); break;
     case 0x1f801801: cd1_write(data); break;
     case 0x1f801802: cd2_write(data); break;
     case 0x1f801803: cd3_write(data); break;
     default:
          HW8(adr-0x1f800000)=data;
          break;
     }
}

void hw_write16(UINT32 adr,UINT32 data)
{
#if NOPRINTPC
     printpc();
#endif
     PRINTF("write short %s,%04x\n",hwname(adr),(int)data);

     switch(adr) {
     case 0x1f801040: sio_writedata16(&Sio0,data); break;
     case 0x1f801048: sio_mode_write(&Sio0,data); break;
     case 0x1f80104a: sio_control_write(&Sio0,data); break;
     case 0x1f80104e: sio_baud_write(&Sio0,data); break;

     case 0x1f801050: sio_writedata16(&Sio1,data); break;
     case 0x1f801058: sio_mode_write(&Sio1,data); break;
     case 0x1f80105a: sio_control_write(&Sio1,data); break;
     case 0x1f80105e: sio_baud_write(&Sio1,data); break;

     case 0x1f801070:
          /* interrupt clear? */
                HW16(0x1070) &= (data & HW16(0x1074));
                treg &= (data & HW16(0x1074));
          break;
        case 0x1F801104:
                HW16(0x1104) = data;
                if ((data & 0x08)==0 || !t0_target) t0_limit = UPPER;
                                               else t0_limit = t0_target;
                break;
        case 0x1F801108:
                HW16(0x1108) = data & 0xFFFF;
                if ((t0_mode & 0x08)==0 || !t0_target) t0_limit = UPPER;
                                                  else t0_limit = t0_target;
                break;
        case 0x1F801114:
                HW16(0x1114) = data;
                if ((data & 0x08)==0 || !t1_target) t1_limit = UPPER;
                                               else t1_limit = t1_target;
                break;
        case 0x1F801118:
                HW16(0x1118) = data & 0xFFFF;
                if ((t1_mode & 0x08)==0 || !t1_target) t1_limit = UPPER;
                                                  else t1_limit = t1_target;
                break;
        case 0x1F801124:
                HW16(0x1124) = data;
                if ((data & 0x08)==0 || !t2_target) t2_limit = UPPER;
                                               else t2_limit = t2_target;
                break;
        case 0x1F801128:
                HW16(0x1128) = data & 0xFFFF;
                if ((t2_mode & 0x08)==0 || !t2_target) t2_limit = UPPER;
                                                  else t2_limit = t2_target;
                break;

     default:
          if (adr>=0x1f801c00 && adr<0x1f801e00) {
               SPU_Write(adr,data);
               break;
          }
          HW16(adr-0x1f800000)=data;
          break;
     }
}

void hw_write32(UINT32 adr,UINT32 data)
{
#if NOPRINTPC
     printpc();
#endif
     PRINTF("write long %s,%08x\n",hwname(adr),(int)data);

     switch(adr) {
     case 0x1f801040: sio_writedata32(&Sio0,data); break;
     case 0x1f801050: sio_writedata32(&Sio1,data); break;

     case 0x1f801070:
          /* 何をストアしても interrupt clear? */
                HW32(0x1070) &= (data & HW32(0x1074));
                treg &= (data & HW32(0x1074));
          break;
     case 0x1f801810: GP0_Write(data); return;
     case 0x1f801814: GP1_Write(data); return;

#define   DMA_ENABLE(n)  (dma_pcr&(8<<(n*4)))
#define   DMA_INTERRUPT(n)    if (dma_icr&(1<<(16+n))) dma_icr |= 0x80000000|(1<<(24+n));

     case 0x1f801088: /* MDECin */
          if (DMA_ENABLE(0)) {
               dma0_exec(HW32(0x1080),HW32(0x1084),data);
               HW32(0x1088) = data&~0x01000000;
               DMA_INTERRUPT(0);
          }
          break;
     case 0x1f801098: /* MDECout */
          if (DMA_ENABLE(1)) {
               dma1_exec(HW32(0x1090),HW32(0x1094),data);
               HW32(0x1098) = data&~0x01000000;
               DMA_INTERRUPT(1);
          }
          break;
     case 0x1f8010a8: /* GPU DMA */
          if (DMA_ENABLE(2)) {
//                        printf("GPUDMA %08x %08x %08x\n",
//                                HW32(0x10a0),HW32(0x10a4),data);
               GPU_DmaExec(HW32(0x10a0),HW32(0x10a4),data);
               HW32(0x10A8) = data&~0x01000000;
               DMA_INTERRUPT(2);
          }
          break;
     case 0x1f8010b8: /* CDROM DMA */
          if (DMA_ENABLE(3)) {
               dma3_exec(HW32(0x10b0),HW32(0x10b4),data);
               HW32(0x10B8) = data&~0x01000000;
               DMA_INTERRUPT(3);
          }
          break;
     case 0x1f8010c8: /* SPU DMA */
          if (DMA_ENABLE(4)) {
               SPU_DmaExec(HW32(0x10c0),HW32(0x10c4),data);
               HW32(0x10C8) = data&~0x01000000;
               DMA_INTERRUPT(4);
          }
          break;
#if 0
     case 0x1f8010d8: /* PIO DMA */
          if (DMA_ENABLE(5)) {
               dma5_exec(HW32(0x10D0),HW32(0x10D4),data);
               HW32(0x10D8) = data&~0x01000000;
               DMA_INTERRUPT(5);
          }
          break;
#endif
     case 0x1f8010e8: /* OT clear DMA */
          if (DMA_ENABLE(6)) {
               dma6_exec(HW32(0x10E0),HW32(0x10E4),data);
               HW32(0x10E8) = data&~0x01000000;
               DMA_INTERRUPT(6);
          }
          break;
     case 0x1f8010f4:
                HW32(0x10F4) = data & 0xFFFFFF;
          break;

        case 0x1F801104:
                HW32(0x1104) = data;
                if (data & 0x08) t0_limit = t0_target;
                            else t0_limit = 0xFFFF;
                break;
        case 0x1F801108:
                HW32(0x1108) = data & 0xFFFF;
                if (t0_mode & 0x08) t0_limit = t0_target;
                               else t0_limit = 0xFFFF;
                break;
        case 0x1F801114:
                HW32(0x1114) = data;
                if (data & 0x08) t1_limit = t1_target;
                            else t1_limit = 0xFFFF;
                break;
        case 0x1F801118:
                HW32(0x1118) = data & 0xFFFF;
                if (t1_mode & 0x08) t1_limit = t1_target;
                               else t1_limit = 0xFFFF;
                break;
        case 0x1F801124:
                HW32(0x1124) = data;
                if (data & 0x08) t2_limit = t2_target;
                            else t2_limit = 0xFFFF;
                break;
        case 0x1F801128:
                HW32(0x1128) = data & 0xFFFF;
                if (t2_mode & 0x08) t2_limit = t0_target;
                               else t2_limit = 0xFFFF;
                break;

     case 0x1f801820: mdec0_write(data); break;
     case 0x1f801824: mdec1_write(data); break;
     default:
          HW32(adr-0x1f800000)=data;
          break;
     }
}