285 lines
4.3 KiB
C++
285 lines
4.3 KiB
C++
#include "aica.h"
|
|
#include "sgc_if.h"
|
|
#include "aica_mem.h"
|
|
#include <math.h>
|
|
#include "hw/holly/holly_intc.h"
|
|
#include "hw/holly/sb.h"
|
|
|
|
#define SH4_IRQ_BIT (1<<(holly_SPU_IRQ&255))
|
|
|
|
CommonData_struct* CommonData;
|
|
DSPData_struct* DSPData;
|
|
InterruptInfo* MCIEB;
|
|
InterruptInfo* MCIPD;
|
|
InterruptInfo* MCIRE;
|
|
InterruptInfo* SCIEB;
|
|
InterruptInfo* SCIPD;
|
|
InterruptInfo* SCIRE;
|
|
|
|
//Interrupts
|
|
//arm side
|
|
u32 GetL(u32 witch)
|
|
{
|
|
if (witch>7)
|
|
witch=7; //higher bits share bit 7
|
|
|
|
u32 bit=1<<witch;
|
|
u32 rv=0;
|
|
|
|
if (CommonData->SCILV0 & bit)
|
|
rv=1;
|
|
|
|
if (CommonData->SCILV1 & bit)
|
|
rv|=2;
|
|
|
|
if (CommonData->SCILV2 & bit)
|
|
rv|=4;
|
|
|
|
return rv;
|
|
}
|
|
void update_arm_interrupts()
|
|
{
|
|
u32 p_ints=SCIEB->full & SCIPD->full;
|
|
|
|
u32 Lval=0;
|
|
if (p_ints)
|
|
{
|
|
u32 bit_value=1;//first bit
|
|
//scan all interrupts , lo to hi bit.I assume low bit ints have higher priority over others
|
|
for (u32 i=0;i<11;i++)
|
|
{
|
|
if (p_ints & bit_value)
|
|
{
|
|
//for the first one , Set the L reg & exit
|
|
Lval=GetL(i);
|
|
break;
|
|
}
|
|
bit_value<<=1; //next bit
|
|
}
|
|
}
|
|
|
|
libARM_InterruptChange(p_ints,Lval);
|
|
}
|
|
|
|
//sh4 side
|
|
void UpdateSh4Ints()
|
|
{
|
|
u32 p_ints = MCIEB->full & MCIPD->full;
|
|
if (p_ints)
|
|
{
|
|
if ((SB_ISTEXT & SH4_IRQ_BIT )==0)
|
|
{
|
|
//if no interrupt is already pending then raise one :)
|
|
asic_RaiseInterrupt(holly_SPU_IRQ);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (SB_ISTEXT&SH4_IRQ_BIT)
|
|
{
|
|
asic_CancelInterrupt(holly_SPU_IRQ);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
////
|
|
//Timers :)
|
|
struct AicaTimerData
|
|
{
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
u32 count:8;
|
|
u32 md:3;
|
|
u32 nil:5;
|
|
u32 pad:16;
|
|
};
|
|
u32 data;
|
|
};
|
|
};
|
|
class AicaTimer
|
|
{
|
|
public:
|
|
AicaTimerData* data;
|
|
s32 c_step;
|
|
u32 m_step;
|
|
u32 id;
|
|
void Init(u8* regbase,u32 timer)
|
|
{
|
|
data=(AicaTimerData*)®base[0x2890 + timer*4];
|
|
id=timer;
|
|
m_step=1<<(data->md);
|
|
c_step=m_step;
|
|
}
|
|
void StepTimer(u32 samples)
|
|
{
|
|
do
|
|
{
|
|
c_step--;
|
|
if (c_step==0)
|
|
{
|
|
c_step=m_step;
|
|
data->count++;
|
|
if (data->count==0)
|
|
{
|
|
if (id==0)
|
|
{
|
|
SCIPD->TimerA=1;
|
|
MCIPD->TimerA=1;
|
|
}
|
|
else if (id==1)
|
|
{
|
|
SCIPD->TimerB=1;
|
|
MCIPD->TimerB=1;
|
|
}
|
|
else
|
|
{
|
|
SCIPD->TimerC=1;
|
|
MCIPD->TimerC=1;
|
|
}
|
|
}
|
|
}
|
|
} while(--samples);
|
|
}
|
|
|
|
void RegisterWrite()
|
|
{
|
|
u32 n_step=1<<(data->md);
|
|
if (n_step!=m_step)
|
|
{
|
|
m_step=n_step;
|
|
c_step=m_step;
|
|
}
|
|
}
|
|
};
|
|
|
|
AicaTimer timers[3];
|
|
//Mainloop
|
|
void libAICA_Update(u32 Samples)
|
|
{
|
|
AICA_Sample32();
|
|
}
|
|
|
|
void libAICA_TimeStep()
|
|
{
|
|
for (int i=0;i<3;i++)
|
|
timers[i].StepTimer(1);
|
|
|
|
SCIPD->SAMPLE_DONE=1;
|
|
|
|
if (settings.aica.NoBatch)
|
|
AICA_Sample();
|
|
|
|
//Make sure sh4/arm interrupt system is up to date :)
|
|
update_arm_interrupts();
|
|
UpdateSh4Ints();
|
|
}
|
|
|
|
//Memory i/o
|
|
template<u32 sz>
|
|
void WriteAicaReg(u32 reg,u32 data)
|
|
{
|
|
switch (reg)
|
|
{
|
|
case SCIPD_addr:
|
|
verify(sz!=1);
|
|
if (data & (1<<5))
|
|
{
|
|
SCIPD->SCPU=1;
|
|
update_arm_interrupts();
|
|
}
|
|
//Read only
|
|
return;
|
|
|
|
case SCIRE_addr:
|
|
{
|
|
verify(sz!=1);
|
|
SCIPD->full&=~(data /*& SCIEB->full*/ ); //is the & SCIEB->full needed ? doesn't seem like it
|
|
data=0;//Write only
|
|
update_arm_interrupts();
|
|
}
|
|
break;
|
|
|
|
case MCIPD_addr:
|
|
if (data & (1<<5))
|
|
{
|
|
verify(sz!=1);
|
|
MCIPD->SCPU=1;
|
|
UpdateSh4Ints();
|
|
}
|
|
//Read only
|
|
return;
|
|
|
|
case MCIRE_addr:
|
|
{
|
|
verify(sz!=1);
|
|
MCIPD->full&=~data;
|
|
UpdateSh4Ints();
|
|
//Write only
|
|
}
|
|
break;
|
|
|
|
case TIMER_A:
|
|
WriteMemArr(aica_reg,reg,data,sz);
|
|
timers[0].RegisterWrite();
|
|
break;
|
|
|
|
case TIMER_B:
|
|
WriteMemArr(aica_reg,reg,data,sz);
|
|
timers[1].RegisterWrite();
|
|
break;
|
|
|
|
case TIMER_C:
|
|
WriteMemArr(aica_reg,reg,data,sz);
|
|
timers[2].RegisterWrite();
|
|
break;
|
|
|
|
default:
|
|
WriteMemArr(aica_reg,reg,data,sz);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
template void WriteAicaReg<1>(u32 reg,u32 data);
|
|
template void WriteAicaReg<2>(u32 reg,u32 data);
|
|
|
|
//misc :p
|
|
s32 libAICA_Init()
|
|
{
|
|
init_mem();
|
|
|
|
verify(sizeof(*CommonData)==0x508);
|
|
verify(sizeof(*DSPData)==0x15C8);
|
|
|
|
CommonData=(CommonData_struct*)&aica_reg[0x2800];
|
|
DSPData=(DSPData_struct*)&aica_reg[0x3000];
|
|
//slave cpu (arm7)
|
|
|
|
SCIEB=(InterruptInfo*)&aica_reg[0x289C];
|
|
SCIPD=(InterruptInfo*)&aica_reg[0x289C+4];
|
|
SCIRE=(InterruptInfo*)&aica_reg[0x289C+8];
|
|
//Main cpu (sh4)
|
|
MCIEB=(InterruptInfo*)&aica_reg[0x28B4];
|
|
MCIPD=(InterruptInfo*)&aica_reg[0x28B4+4];
|
|
MCIRE=(InterruptInfo*)&aica_reg[0x28B4+8];
|
|
|
|
sgc_Init();
|
|
for (int i=0;i<3;i++)
|
|
timers[i].Init(aica_reg,i);
|
|
|
|
return rv_ok;
|
|
}
|
|
|
|
void libAICA_Reset(bool m)
|
|
{
|
|
}
|
|
|
|
void libAICA_Term()
|
|
{
|
|
sgc_Term();
|
|
}
|