flycast/core/hw/aica/aica.cpp

293 lines
5.6 KiB
C++

#include "aica.h"
#include "aica_if.h"
#include "aica_mem.h"
#include "sgc_if.h"
#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"
#define SH4_IRQ_BIT (1 << (holly_SPU_IRQ & 31))
CommonData_struct* CommonData;
DSPData_struct* DSPData;
InterruptInfo* MCIEB;
InterruptInfo* MCIPD;
InterruptInfo* MCIRE;
InterruptInfo* SCIEB;
InterruptInfo* SCIPD;
InterruptInfo* SCIRE;
std::deque<u8> midiSendBuffer;
//Interrupts
//arm side
static u32 GetL(u32 which)
{
if (which > 7)
which = 7; //higher bits share bit 7
u32 bit = 1 << which;
u32 rv = 0;
if (CommonData->SCILV0 & bit)
rv = 1;
if (CommonData->SCILV1 & bit)
rv |= 2;
if (CommonData->SCILV2 & bit)
rv |= 4;
return rv;
}
static 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
static 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) != 0)
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)
{
aicaarm::run(32);
return AICA_TICK;
}
//Mainloop
void libAICA_TimeStep()
{
for (int i=0;i<3;i++)
timers[i].StepTimer(1);
SCIPD->SAMPLE_DONE = 1;
MCIPD->SAMPLE_DONE = 1;
AICA_Sample();
//Make sure sh4/arm interrupt system is up to date :)
update_arm_interrupts();
UpdateSh4Ints();
}
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, (u32)0);
}
}
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<u32>(raddr);
}
else
{
// wave mem to regs
for (u32 i = 0; i < len; i++, waddr += 4, raddr += 4)
WriteMem_aica_reg(raddr, *(u32*)&aica_ram[waddr]);
}
}
CommonData->DEXE = 0;
MCIPD->DMA_END = 1;
UpdateSh4Ints();
SCIPD->DMA_END = 1;
update_arm_interrupts();
}
//Memory i/o
template<typename T>
void WriteAicaReg(u32 reg, T data)
{
constexpr size_t sz = sizeof(T);
switch (reg)
{
case SCIPD_addr:
verify(sz!=1);
// other bits are read-only
if (data & (1<<5))
{
SCIPD->SCPU=1;
update_arm_interrupts();
}
break;
case SCIRE_addr:
verify(sz != 1);
SCIPD->full &= ~data /*& SCIEB->full)*/; //is the & SCIEB->full needed ? doesn't seem like it
update_arm_interrupts();
break;
case MCIPD_addr:
verify(sz != 1);
// other bits are read-only
if (data & (1 << 5))
{
MCIPD->SCPU = 1;
UpdateSh4Ints();
aicaarm::avoidRaceCondition();
}
break;
case MCIRE_addr:
verify(sz != 1);
MCIPD->full &= ~data;
UpdateSh4Ints();
break;
case TIMER_A:
WriteMemArr(aica_reg, reg, data);
timers[0].RegisterWrite();
break;
case TIMER_B:
WriteMemArr(aica_reg, reg, data);
timers[1].RegisterWrite();
break;
case TIMER_C:
WriteMemArr(aica_reg, reg, data);
timers[2].RegisterWrite();
break;
// DEXE, DDIR, DLG
case 0x288C:
WriteMemArr(aica_reg, reg, data);
AicaInternalDMA();
break;
default:
WriteMemArr(aica_reg, reg, data);
break;
}
}
template void WriteAicaReg<>(u32 reg, u8 data);
template void WriteAicaReg<>(u32 reg, u16 data);
template void WriteAicaReg<>(u32 reg, u32 data);
void aica_midiSend(u8 data)
{
midiSendBuffer.push_back(data);
SCIPD->MIDI_IN = 1;
update_arm_interrupts();
MCIPD->MIDI_IN = 1;
UpdateSh4Ints();
}
//misc :p
s32 libAICA_Init()
{
init_mem();
aica_Init();
static_assert(sizeof(*CommonData) == 0x508, "Invalid CommonData size");
static_assert(sizeof(*DSPData) == 0x15C8, "Invalid DSPData size");
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);
return 0;
}
void libAICA_Reset(bool hard)
{
if (hard)
{
init_mem();
sgc_Init();
sh4_sched_request(aica_schid, AICA_TICK);
}
for (u32 i = 0; i < 3; i++)
timers[i].Init(aica_reg, i);
aica_Reset(hard);
}
void libAICA_Term()
{
sgc_Term();
term_mem();
sh4_sched_unregister(aica_schid);
aica_schid = -1;
}