flycast/core/hw/aica/aica.cpp

286 lines
5.4 KiB
C++
Raw Normal View History

2013-12-19 17:10:14 +00:00
#include "aica.h"
2019-04-08 20:09:22 +00:00
#include "aica_if.h"
2013-12-19 17:10:14 +00:00
#include "aica_mem.h"
2020-03-28 16:58:01 +00:00
#include "sgc_if.h"
2013-12-19 17:10:14 +00:00
#include "hw/holly/holly_intc.h"
#include "hw/holly/sb.h"
#include "hw/sh4/sh4_sched.h"
#include "hw/arm7/arm7.h"
#include "hw/arm7/arm_mem.h"
2013-12-19 17:10:14 +00:00
#define SH4_IRQ_BIT (1 << (holly_SPU_IRQ & 31))
2013-12-19 17:10:14 +00:00
CommonData_struct* CommonData;
DSPData_struct* DSPData;
InterruptInfo* MCIEB;
InterruptInfo* MCIPD;
InterruptInfo* MCIRE;
InterruptInfo* SCIEB;
InterruptInfo* SCIPD;
InterruptInfo* SCIRE;
//Interrupts
//arm side
static u32 GetL(u32 which)
2013-12-19 17:10:14 +00:00
{
2019-08-30 21:35:10 +00:00
if (which > 7)
which = 7; //higher bits share bit 7
2013-12-19 17:10:14 +00:00
2019-08-30 21:35:10 +00:00
u32 bit = 1 << which;
u32 rv = 0;
2013-12-19 17:10:14 +00:00
if (CommonData->SCILV0 & bit)
2019-08-30 21:35:10 +00:00
rv = 1;
2013-12-19 17:10:14 +00:00
if (CommonData->SCILV1 & bit)
2019-08-30 21:35:10 +00:00
rv |= 2;
2013-12-19 17:10:14 +00:00
if (CommonData->SCILV2 & bit)
2019-08-30 21:35:10 +00:00
rv |= 4;
2013-12-19 17:10:14 +00:00
return rv;
}
static void update_arm_interrupts()
2013-12-19 17:10:14 +00:00
{
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
2013-12-19 17:10:14 +00:00
}
}
libARM_InterruptChange(p_ints,Lval);
}
//sh4 side
static void UpdateSh4Ints()
2013-12-19 17:10:14 +00:00
{
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 :)
2013-12-19 17:10:14 +00:00
asic_RaiseInterrupt(holly_SPU_IRQ);
}
else
{
if ((SB_ISTEXT & SH4_IRQ_BIT) != 0)
2013-12-19 17:10:14 +00:00
asic_CancelInterrupt(holly_SPU_IRQ);
}
}
AicaTimer timers[3];
int aica_schid = -1;
const int AICA_TICK = 145125; // 44.1 KHz / 32
static int AicaUpdate(int tag, int c, int j)
2013-12-19 17:10:14 +00:00
{
2021-01-29 10:39:00 +00:00
aicaarm::run(32);
if (!settings.aica.NoBatch)
AICA_Sample32();
return AICA_TICK;
2013-12-19 17:10:14 +00:00
}
//Mainloop
2013-12-19 17:10:14 +00:00
void libAICA_TimeStep()
{
for (int i=0;i<3;i++)
timers[i].StepTimer(1);
2020-06-15 19:42:47 +00:00
SCIPD->SAMPLE_DONE = 1;
MCIPD->SAMPLE_DONE = 1;
2013-12-19 17:10:14 +00:00
if (settings.aica.NoBatch)
2013-12-19 17:10:14 +00:00
AICA_Sample();
//Make sure sh4/arm interrupt system is up to date :)
update_arm_interrupts();
UpdateSh4Ints();
}
2020-06-15 19:42:47 +00:00
static void AicaInternalDMA()
{
if (!CommonData->DEXE)
return;
// Start dma
DEBUG_LOG(AICA, "AICA internal DMA: DGATE %d DDIR %d DLG %x", CommonData->DGATE, CommonData->DDIR, CommonData->DLG);
if (CommonData->DGATE)
{
// Clear memory/registers
if (CommonData->DDIR)
{
// to wave mem
u32 addr = ((CommonData->DMEA_hi << 16) | (CommonData->DMEA_lo << 2)) & ARAM_MASK;
u32 len = std::min(CommonData->DLG, ARAM_SIZE - addr);
memset(&aica_ram.data[addr], 0, len * 4);
}
else
{
// to regs
u32 addr = CommonData->DRGA << 2;
for (u32 i = 0; i < CommonData->DLG; i++, addr += 4)
WriteMem_aica_reg(addr, 0, 4);
}
}
else
{
// Data xfer
u32 waddr = ((CommonData->DMEA_hi << 16) | (CommonData->DMEA_lo << 2)) & ARAM_MASK;
u32 raddr = CommonData->DRGA << 2;
u32 len = std::min(CommonData->DLG, ARAM_SIZE - waddr);
if (CommonData->DDIR)
{
// reg to wave mem
for (u32 i = 0; i < len; i++, waddr += 4, raddr += 4)
*(u32*)&aica_ram[waddr] = ReadMem_aica_reg(raddr, 4);
}
else
{
// wave mem to regs
for (u32 i = 0; i < len; i++, waddr += 4, raddr += 4)
WriteMem_aica_reg(raddr, *(u32*)&aica_ram[waddr], 4);
}
}
CommonData->DEXE = 0;
MCIPD->DMA_END = 1;
UpdateSh4Ints();
SCIPD->DMA_END = 1;
update_arm_interrupts();
}
2013-12-19 17:10:14 +00:00
//Memory i/o
template<u32 sz>
void WriteAicaReg(u32 reg,u32 data)
{
switch (reg)
{
case SCIPD_addr:
verify(sz!=1);
// other bits are read-only
2013-12-19 17:10:14 +00:00
if (data & (1<<5))
{
SCIPD->SCPU=1;
update_arm_interrupts();
}
break;
2013-12-19 17:10:14 +00:00
case SCIRE_addr:
verify(sz != 1);
SCIPD->full &= ~data /*& SCIEB->full)*/; //is the & SCIEB->full needed ? doesn't seem like it
update_arm_interrupts();
2013-12-19 17:10:14 +00:00
break;
case MCIPD_addr:
verify(sz != 1);
// other bits are read-only
if (data & (1 << 5))
2013-12-19 17:10:14 +00:00
{
MCIPD->SCPU = 1;
2013-12-19 17:10:14 +00:00
UpdateSh4Ints();
aicaarm::avoidRaceCondition();
2013-12-19 17:10:14 +00:00
}
break;
2013-12-19 17:10:14 +00:00
case MCIRE_addr:
verify(sz != 1);
MCIPD->full &= ~data;
UpdateSh4Ints();
2013-12-19 17:10:14 +00:00
break;
case TIMER_A:
WriteMemArr<sz>(aica_reg, reg, data);
2013-12-19 17:10:14 +00:00
timers[0].RegisterWrite();
break;
case TIMER_B:
WriteMemArr<sz>(aica_reg, reg, data);
2013-12-19 17:10:14 +00:00
timers[1].RegisterWrite();
break;
case TIMER_C:
WriteMemArr<sz>(aica_reg, reg, data);
2013-12-19 17:10:14 +00:00
timers[2].RegisterWrite();
break;
2020-06-15 19:42:47 +00:00
// DEXE, DDIR, DLG
case 0x288C:
WriteMemArr<sz>(aica_reg, reg, data);
2020-06-15 19:42:47 +00:00
AicaInternalDMA();
break;
2013-12-19 17:10:14 +00:00
default:
WriteMemArr<sz>(aica_reg, reg, data);
2013-12-19 17:10:14 +00:00
break;
}
}
template void WriteAicaReg<1>(u32 reg,u32 data);
template void WriteAicaReg<2>(u32 reg,u32 data);
//misc :p
s32 libAICA_Init()
{
init_mem();
2019-04-08 20:09:22 +00:00
aica_Init();
2013-12-19 17:10:14 +00:00
static_assert(sizeof(*CommonData) == 0x508, "Invalid CommonData size");
static_assert(sizeof(*DSPData) == 0x15C8, "Invalid DSPData size");
2013-12-19 17:10:14 +00:00
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();
if (aica_schid == -1)
{
aica_schid = sh4_sched_register(0, &AicaUpdate);
sh4_sched_request(aica_schid, AICA_TICK);
}
2013-12-19 17:10:14 +00:00
return 0;
2013-12-19 17:10:14 +00:00
}
2019-07-10 15:25:11 +00:00
void libAICA_Reset(bool hard)
2013-12-19 17:10:14 +00:00
{
2019-07-10 15:25:11 +00:00
if (hard)
{
2019-04-08 20:09:22 +00:00
init_mem();
sgc_Init();
}
for (u32 i = 0; i < 3; i++)
2019-07-10 15:25:11 +00:00
timers[i].Init(aica_reg, i);
aica_Reset(hard);
2013-12-19 17:10:14 +00:00
}
void libAICA_Term()
{
sgc_Term();
2019-07-08 16:10:43 +00:00
term_mem();
2013-12-19 17:10:14 +00:00
}