// license:BSD-3-Clause // copyright-holders:ElSemi, Deunan Knute, R. Belmont // thanks-to: kingshriek /* Sega/Yamaha AICA emulation Confirmed Part numbers: SEGA 315-6232 G21003 (Later) SEGA 315-6119 FQ1003 (Earlier) This is effectively a 64-voice SCSP, with the following differences: - No FM mode - A third sample format (ADPCM) has been added - Some minor other tweeks (no EGHOLD, slighly more capable DSP) TODO: - Timebases are based on 44100KHz case? */ #include "emu.h" #include "aica.h" #include static constexpr s32 clip16(int x) { return std::min(32767, std::max(-32768, x)); } static constexpr s32 clip18(int x) { return std::min(131071, std::max(-131072, x)); } #define SHIFT 12 #define FIX(v) ((u32)((float)(1 << SHIFT) * (v))) #define EG_SHIFT 16 #define LFO_SHIFT 8 #define LFIX(v) ((u32)((float)(1 << LFO_SHIFT) * (v))) //Convert DB to multiply amplitude #define DB(v) LFIX(powf(10.0f, v / 20.0f)) //Convert cents to step increment #define CENTS(v) LFIX(powf(2.0f, v / 1200.0f)) /* AICA features 64 programmable slots that can generate PCM and ADPCM (from ROM/RAM) sound */ //SLOT PARAMETERS #define KEYONEX(slot) ((slot->udata.data[0x0] >> 0x0) & 0x8000) #define KEYONB(slot) ((slot->udata.data[0x0] >> 0x0) & 0x4000) #define SSCTL(slot) ((slot->udata.data[0x0] >> 0xA) & 0x0001) #define LPCTL(slot) ((slot->udata.data[0x0] >> 0x9) & 0x0001) #define PCMS(slot) ((slot->udata.data[0x0] >> 0x7) & 0x0003) #define SA(slot) (((slot->udata.data[0x0] & 0x7F) << 16) | (slot->udata.data[0x4 / 2])) #define LSA(slot) (slot->udata.data[0x8 / 2]) #define LEA(slot) (slot->udata.data[0xc / 2]) #define D2R(slot) ((slot->udata.data[0x10 / 2] >> 0xB) & 0x001F) #define D1R(slot) ((slot->udata.data[0x10 / 2] >> 0x6) & 0x001F) #define AR(slot) ((slot->udata.data[0x10 / 2] >> 0x0) & 0x001F) #define LPSLNK(slot) ((slot->udata.data[0x14 / 2] >> 0x0) & 0x4000) #define KRS(slot) ((slot->udata.data[0x14 / 2] >> 0xA) & 0x000F) #define DL(slot) ((slot->udata.data[0x14 / 2] >> 0x5) & 0x001F) #define RR(slot) ((slot->udata.data[0x14 / 2] >> 0x0) & 0x001F) #define TL(slot) ((slot->udata.data[0x28 / 2] >> 0x8) & 0x00FF) #define OCT(slot) ((slot->udata.data[0x18 / 2] >> 0xB) & 0x000F) #define FNS(slot) ((slot->udata.data[0x18 / 2] >> 0x0) & 0x03FF) #define LFORE(slot) ((slot->udata.data[0x1c / 2] >> 0x0) & 0x8000) #define LFOF(slot) ((slot->udata.data[0x1c / 2] >> 0xA) & 0x001F) #define PLFOWS(slot) ((slot->udata.data[0x1c / 2] >> 0x8) & 0x0003) #define PLFOS(slot) ((slot->udata.data[0x1c / 2] >> 0x5) & 0x0007) #define ALFOWS(slot) ((slot->udata.data[0x1c / 2] >> 0x3) & 0x0003) #define ALFOS(slot) ((slot->udata.data[0x1c / 2] >> 0x0) & 0x0007) #define ISEL(slot) ((slot->udata.data[0x20 / 2] >> 0x0) & 0x000F) #define IMXL(slot) ((slot->udata.data[0x20 / 2] >> 0x4) & 0x000F) #define DISDL(slot) ((slot->udata.data[0x24 / 2] >> 0x8) & 0x000F) #define DIPAN(slot) (MONO() ? 0 : ((slot->udata.data[0x24 / 2] >> 0x0) & 0x001F)) #define EFSDL(slot) ((m_EFSPAN[slot * 4] >> 8) & 0x000f) #define EFPAN(slot) (MONO() ? 0 : ((m_EFSPAN[slot * 4] >> 0) & 0x001f)) //Unimplemented #define Q(slot) ((slot->udata.data[0x28 / 2] >> 0x0) & 0x001F) // (0.75 × register value - 3) #define FLV0(slot) ((slot->udata.data[0x2c / 2] >> 0x0) & 0x1FFF) #define FLV1(slot) ((slot->udata.data[0x30 / 2] >> 0x0) & 0x1FFF) #define FLV2(slot) ((slot->udata.data[0x34 / 2] >> 0x0) & 0x1FFF) #define FLV3(slot) ((slot->udata.data[0x38 / 2] >> 0x0) & 0x1FFF) #define FLV4(slot) ((slot->udata.data[0x3c / 2] >> 0x0) & 0x1FFF) #define FAR(slot) ((slot->udata.data[0x40 / 2] >> 0x8) & 0x001F) #define FD1R(slot) ((slot->udata.data[0x40 / 2] >> 0x0) & 0x001F) #define FD2R(slot) ((slot->udata.data[0x44 / 2] >> 0x8) & 0x001F) #define FRR(slot) ((slot->udata.data[0x44 / 2] >> 0x0) & 0x001F) //Envelope times in ms static constexpr double ARTimes[64] = {100000/*infinity*/,100000/*infinity*/,8100.0,6900.0,6000.0,4800.0,4000.0,3400.0,3000.0,2400.0,2000.0,1700.0,1500.0, 1200.0,1000.0,860.0,760.0,600.0,500.0,430.0,380.0,300.0,250.0,220.0,190.0,150.0,130.0,110.0,95.0, 76.0,63.0,55.0,47.0,38.0,31.0,27.0,24.0,19.0,15.0,13.0,12.0,9.4,7.9,6.8,6.0,4.7,3.8,3.4,3.0,2.4, 2.0,1.8,1.6,1.3,1.1,0.93,0.85,0.65,0.53,0.44,0.40,0.35,0.0,0.0}; static constexpr double DRTimes[64] = {100000/*infinity*/,100000/*infinity*/,118200.0,101300.0,88600.0,70900.0,59100.0,50700.0,44300.0,35500.0,29600.0,25300.0,22200.0,17700.0, 14800.0,12700.0,11100.0,8900.0,7400.0,6300.0,5500.0,4400.0,3700.0,3200.0,2800.0,2200.0,1800.0,1600.0,1400.0,1100.0, 920.0,790.0,690.0,550.0,460.0,390.0,340.0,270.0,230.0,200.0,170.0,140.0,110.0,98.0,85.0,68.0,57.0,49.0,43.0,34.0, 28.0,25.0,22.0,18.0,14.0,12.0,11.0,8.5,7.1,6.1,5.4,4.3,3.6,3.1}; #define MONO(aica) ((m_udata.data[0] >> 0x0) & 0x8000) #define MEM8MB(aica) ((m_udata.data[0] >> 0x0) & 0x0200) #define DAC18B(aica) ((m_udata.data[0] >> 0x0) & 0x0100) #define MVOL(aica) ((m_udata.data[0] >> 0x0) & 0x000F) #define RBL(aica) ((m_udata.data[2] >> 0xD) & 0x0003) #define RBP(aica) ((m_udata.data[2] >> 0x0) & 0x0fff) #define MOFULL(aica) ((m_udata.data[4] >> 0x0) & 0x1000) #define MOEMPTY(aica) ((m_udata.data[4] >> 0x0) & 0x0800) #define MIOVF(aica) ((m_udata.data[4] >> 0x0) & 0x0400) #define MIFULL(aica) ((m_udata.data[4] >> 0x0) & 0x0200) #define MIEMPTY(aica) ((m_udata.data[4] >> 0x0) & 0x0100) #define AFSEL(aica) ((m_udata.data[0xc / 2] >> 0x0) & 0x4000) #define MSLC(aica) ((m_udata.data[0xc / 2] >> 0x8) & 0x3F) #define SCILV0(aica) ((m_udata.data[0xa8 / 2] >> 0x0) & 0xff) #define SCILV1(aica) ((m_udata.data[0xac / 2] >> 0x0) & 0xff) #define SCILV2(aica) ((m_udata.data[0xb0 / 2] >> 0x0) & 0xff) #define MCIEB(aica) ((m_udata.data[0xb4 / 2] >> 0x0) & 0xff) #define MCIPD(aica) ((m_udata.data[0xb8 / 2] >> 0x0) & 0xff) #define MCIRE(aica) ((m_udata.data[0xbc / 2] >> 0x0) & 0xff) #define SCIEX0 0 #define SCIEX1 1 #define SCIEX2 2 #define SCIMID 3 #define SCIDMA 4 #define SCIIRQ 5 #define SCITMA 6 #define SCITMB 7 static constexpr float SDLT[16] = {-1000000.0,-42.0,-39.0,-36.0,-33.0,-30.0,-27.0,-24.0,-21.0,-18.0,-15.0,-12.0,-9.0,-6.0,-3.0,0.0}; u8 aica_device::DecodeSCI(u8 irq) { u8 SCI = 0; u8 v = (SCILV0((AICA)) & (1 << irq)) ? 1 : 0; SCI |= v; v = (SCILV1((AICA)) & (1 << irq)) ? 1 : 0; SCI |= v << 1; v = (SCILV2((AICA)) & (1 << irq)) ? 1 : 0; SCI |= v << 2; return SCI; } void aica_device::ResetInterrupts() { #if 0 u32 reset = m_udata.data[0xa4 / 2]; if (reset & 0x40) m_irq_cb(-m_IrqTimA); if (reset & 0x180) m_irq_cb(-m_IrqTimBC); #endif } void aica_device::CheckPendingIRQ() { u32 pend = m_udata.data[0xa0 / 2]; u32 en = m_udata.data[0x9c / 2]; if (m_MidiW != m_MidiR) { m_IRQL = m_IrqMidi; m_irq_cb(1); return; } if (!pend) return; if (pend & 0x40) if (en & 0x40) { m_IRQL = m_IrqTimA; m_irq_cb(1); return; } if (pend & 0x80) if (en & 0x80) { m_IRQL = m_IrqTimBC; m_irq_cb(1); return; } if (pend & 0x100) if (en & 0x100) { m_IRQL = m_IrqTimBC; m_irq_cb(1); return; } } void aica_device::CheckPendingIRQ_SH4() { if (m_mcipd & m_mcieb) m_main_irq_cb(1); if ((m_mcipd & m_mcieb) == 0) m_main_irq_cb(0); } TIMER_CALLBACK_MEMBER( aica_device::timerA_cb ) { m_TimCnt[0] = 0xFFFF; m_udata.data[0xa0 / 2] |= 0x40; m_mcipd |= 0x40; m_udata.data[0x90 / 2] &= 0xff00; m_udata.data[0x90 / 2] |= m_TimCnt[0] >> 8; CheckPendingIRQ(); CheckPendingIRQ_SH4(); } TIMER_CALLBACK_MEMBER( aica_device::timerB_cb ) { m_TimCnt[1] = 0xFFFF; m_udata.data[0xa0 / 2] |= 0x80; m_mcipd |= 0x80; m_udata.data[0x94 / 2] &= 0xff00; m_udata.data[0x94 / 2] |= m_TimCnt[1] >> 8; CheckPendingIRQ(); CheckPendingIRQ_SH4(); } TIMER_CALLBACK_MEMBER( aica_device::timerC_cb ) { m_TimCnt[2] = 0xFFFF; m_udata.data[0xa0 / 2] |= 0x100; m_mcipd |= 0x100; m_udata.data[0x98 / 2] &= 0xff00; m_udata.data[0x98 / 2] |= m_TimCnt[2] >> 8; CheckPendingIRQ(); CheckPendingIRQ_SH4(); } int aica_device::Get_AR(int base,int R) { int Rate = base + (R << 1); if (Rate > 63) Rate = 63; if (Rate < 0) Rate = 0; return m_ARTABLE[Rate]; } int aica_device::Get_DR(int base,int R) { int Rate = base + (R << 1); if (Rate > 63) Rate = 63; if (Rate < 0) Rate = 0; return m_DRTABLE[Rate]; } int aica_device::Get_RR(int base,int R) { int Rate = base + (R << 1); if (Rate > 63) Rate = 63; if (Rate < 0) Rate = 0; return m_DRTABLE[Rate]; } void aica_device::Compute_EG(AICA_SLOT *slot) { int octave = (OCT(slot) ^ 8) - 8; int rate; if (KRS(slot) != 0xf) rate = octave + 2 * KRS(slot) + ((FNS(slot) >> 9)&1); else rate = 0; //rate = ((FNS(slot) >> 9)&1); slot->EG.volume = 0x17f << EG_SHIFT; slot->EG.AR = Get_AR(rate, AR(slot)); slot->EG.D1R = Get_DR(rate, D1R(slot)); slot->EG.D2R = Get_DR(rate, D2R(slot)); slot->EG.RR = Get_RR(rate, RR(slot)); slot->EG.RR = Get_RR(rate, RR(slot)); slot->EG.DL = 0x1f - DL(slot); } int aica_device::EG_Update(AICA_SLOT *slot) { switch (slot->EG.state) { case AICA_ATTACK: slot->EG.volume += slot->EG.AR; if (slot->EG.volume >= (0x3ff << EG_SHIFT)) { if (!LPSLNK(slot) && slot->EG.D1R) { slot->EG.state = AICA_DECAY1; if (slot->EG.D1R >= (1024 << EG_SHIFT) && slot->EG.D2R) //Skip DECAY1, go directly to DECAY2 slot->EG.state = AICA_DECAY2; } slot->EG.volume = 0x3ff << EG_SHIFT; } break; case AICA_DECAY1: slot->EG.volume -= slot->EG.D1R; if (slot->EG.volume <= 0) slot->EG.volume = 0; if (slot->EG.volume >> (EG_SHIFT + 5) <= slot->EG.DL) slot->EG.state = AICA_DECAY2; break; case AICA_DECAY2: if (D2R(slot) == 0) return (slot->EG.volume >> EG_SHIFT) << (SHIFT - 10); slot->EG.volume -= slot->EG.D2R; if (slot->EG.volume <= 0) slot->EG.volume = 0; break; case AICA_RELEASE: slot->EG.volume -= slot->EG.RR; if (slot->EG.volume <= 0) { slot->EG.volume = 0; StopSlot(slot, 0); // slot->EG.volume = 0x17f << EG_SHIFT; // slot->EG.state = AICA_ATTACK; } break; default: return 1 << SHIFT; } return (slot->EG.volume >> EG_SHIFT) << (SHIFT - 10); } u32 aica_device::Step(AICA_SLOT *slot) { int octave = (OCT(slot) ^ 8) - 8 + SHIFT - 10; u32 Fn = FNS(slot) + 0x400; if (octave >= 0) Fn <<= octave; else Fn >>= -octave; return Fn; } void aica_device::Compute_LFO(AICA_SLOT *slot) { if (PLFOS(slot) != 0) LFO_ComputeStep(&(slot->PLFO), LFOF(slot), PLFOWS(slot), PLFOS(slot), 0); if (ALFOS(slot) != 0) LFO_ComputeStep(&(slot->ALFO), LFOF(slot), ALFOWS(slot), ALFOS(slot), 1); } #define ADPCMSHIFT 8 static constexpr int ADFIX(float f) { return int(f * float(1 << ADPCMSHIFT)); } static constexpr int TableQuant[8] = {ADFIX(0.8984375),ADFIX(0.8984375),ADFIX(0.8984375),ADFIX(0.8984375),ADFIX(1.19921875),ADFIX(1.59765625),ADFIX(2.0),ADFIX(2.3984375)}; static constexpr int quant_mul[16] = { 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15}; void aica_device::InitADPCM(int *PrevSignal, int *PrevQuant) { *PrevSignal = 0; *PrevQuant = 0x7f; } s16 aica_device::DecodeADPCM(int *PrevSignal, u8 Delta, int *PrevQuant) { int x = (*PrevQuant * quant_mul[Delta & 7]) / 8; if (x > 0x7FFF) x = 0x7FFF; if (Delta & 8) x = -x; x += *PrevSignal; #if 0 // older implementation int x = *PrevQuant * quant_mul [Delta & 15]; x = *PrevSignal + ((int)(x + ((u32)x >> 29)) >> 3); #endif *PrevSignal = clip16(x); *PrevQuant = (*PrevQuant * TableQuant[Delta & 7]) >> ADPCMSHIFT; *PrevQuant = (*PrevQuant < 0x7f) ? 0x7f : ((*PrevQuant > 0x6000) ? 0x6000 : *PrevQuant); return *PrevSignal; } void aica_device::StartSlot(AICA_SLOT *slot) { slot->active = 1; slot->Backwards = 0; slot->cur_addr = 0; slot->nxt_addr = 1 << SHIFT; slot->prv_addr = -1; slot->step = Step(slot); Compute_EG(slot); slot->EG.state = AICA_ATTACK; slot->EG.volume = 0x17f << EG_SHIFT; Compute_LFO(slot); if (PCMS(slot) >= 2) { slot->curstep = 0; slot->adbase = SA(slot); InitADPCM(&(slot->cur_sample), &(slot->cur_quant)); InitADPCM(&(slot->cur_lpsample), &(slot->cur_lpquant)); // on real hardware this creates undefined behavior. if (LSA(slot) > LEA(slot)) { slot->udata.data[0xc / 2] = 0xffff; } } } void aica_device::StopSlot(AICA_SLOT *slot,int keyoff) { if (keyoff /*&& slot->EG.state!=AICA_RELEASE*/) { slot->EG.state = AICA_RELEASE; } else { slot->active = 0; slot->lpend = 1; } slot->udata.data[0] &= ~0x4000; } void aica_device::Init() { int i; m_IrqTimA = m_IrqTimBC = m_IrqMidi = 0; m_MidiR = m_MidiW = 0; m_MidiOutR = m_MidiOutW = 0; m_DSP.space = m_data; m_DSP.cache = m_cache; m_timerA = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(aica_device::timerA_cb), this)); m_timerB = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(aica_device::timerB_cb), this)); m_timerC = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(aica_device::timerC_cb), this)); for (i = 0; i < 0x400; ++i) { float envDB = ((float)(3 * (i - 0x3ff))) / 32.0f; float scale = (float)(1 << SHIFT); m_EG_TABLE[i] = (s32)(powf(10.0f, envDB / 20.0f) * scale); } for (i = 0; i < 0x20000; ++i) { int iTL = (i >> 0x0) & 0xff; int iPAN = (i >> 0x8) & 0x1f; int iSDL = (i >> 0xD) & 0x0F; float SegaDB = 0; float fSDL; float PAN; float LPAN,RPAN; if (iTL & 0x01) SegaDB -= 0.4f; if (iTL & 0x02) SegaDB -= 0.8f; if (iTL & 0x04) SegaDB -= 1.5f; if (iTL & 0x08) SegaDB -= 3.0f; if (iTL & 0x10) SegaDB -= 6.0f; if (iTL & 0x20) SegaDB -= 12.0f; if (iTL & 0x40) SegaDB -= 24.0f; if (iTL & 0x80) SegaDB -= 48.0f; float TL = powf(10.0f, SegaDB / 20.0f); SegaDB = 0; if (iPAN & 0x1) SegaDB -= 3.0f; if (iPAN & 0x2) SegaDB -= 6.0f; if (iPAN & 0x4) SegaDB -= 12.0f; if (iPAN & 0x8) SegaDB -= 24.0f; if ((iPAN & 0xf) == 0xf) PAN = 0.0; else PAN = powf(10.0f, SegaDB / 20.0f); if (iPAN < 0x10) { LPAN = PAN; RPAN = 1.0; } else { RPAN = PAN; LPAN = 1.0; } if (iSDL) fSDL = powf(10.0f, (SDLT[iSDL]) / 20.0f); else fSDL = 0.0; m_LPANTABLE[i] = FIX((4.0f * LPAN * TL * fSDL)); m_RPANTABLE[i] = FIX((4.0f * RPAN * TL * fSDL)); } m_ARTABLE[0] = m_DRTABLE[0] = 0; //Infinite time m_ARTABLE[1] = m_DRTABLE[1] = 0; //Infinite time for (i=2; i < 64; ++i) { double step,scale; double t = ARTimes[i]; //In ms if (t != 0.0) { step = (1023 * 1000.0) / (44100.0 * t); scale = (double)(1 << EG_SHIFT); m_ARTABLE[i] = (int)(step * scale); } else m_ARTABLE[i] = 1024 << EG_SHIFT; t = DRTimes[i]; //In ms step = (1023 * 1000.0) / (44100.0 * t); scale = (double)(1 << EG_SHIFT); m_DRTABLE[i] = (int)(step * scale); } ClockChange(); // make sure all the slots are off for (i = 0; i < 64; ++i) { m_Slots[i].slot = i; m_Slots[i].active = 0; m_Slots[i].EG.state = AICA_RELEASE; m_Slots[i].lpend = 1; } LFO_Init(); // no "pend" m_udata.data[0xa0 / 2] = 0; //AICA[1].udata.data[0x20 / 2] = 0; m_TimCnt[0] = 0xffff; m_TimCnt[1] = 0xffff; m_TimCnt[2] = 0xffff; } void aica_device::ClockChange() { m_rate = ((double)clock()) / 512.0; } void aica_device::UpdateSlotReg(int s,int r) { AICA_SLOT *slot = m_Slots + s; switch (r & 0x7f) { case 0: case 1: if (KEYONEX(slot)) { for (int sl = 0; sl < 64; ++sl) { AICA_SLOT *s2 = m_Slots + sl; { if (KEYONB(s2) && s2->EG.state == AICA_RELEASE/*&& !s2->active*/) { s2->lpend = 0; StartSlot(s2); #if 0 printf("StartSlot[%02X]: SSCTL %01X SA %06X LSA %04X LEA %04X PCMS %01X LPCTL %01X\n",sl,SSCTL(s2),SA(s2),LSA(s2),LEA(s2),PCMS(s2),LPCTL(s2)); printf(" AR %02X D1R %02X D2R %02X RR %02X DL %02X KRS %01X LPSLNK %01X\n",AR(s2),D1R(s2),D2R(s2),RR(s2),DL(s2),KRS(s2),LPSLNK(s2) >> 14); printf(" TL %02X OCT %01X FNS %03X\n",TL(s2),OCT(s2),FNS(s2)); printf(" LFORE %01X LFOF %02X ALFOWS %01X ALFOS %01X PLFOWS %01X PLFOS %01X\n",LFORE(s2),LFOF(s2),ALFOWS(s2),ALFOS(s2),PLFOWS(s2),PLFOS(s2)); printf(" IMXL %01X ISEL %01X DISDL %01X DIPAN %02X\n",IMXL(s2),ISEL(s2),DISDL(s2),DIPAN(s2)); printf("\n"); fflush(stdout); #endif } if (!KEYONB(s2) /*&& s2->active*/) { StopSlot(s2,1); } } } slot->udata.data[0] &= ~0x8000; } break; case 0x18: case 0x19: slot->step = Step(slot); break; case 0x14: case 0x15: slot->EG.RR = Get_RR(0, RR(slot)); slot->EG.DL = 0x1f - DL(slot); break; case 0x1c: case 0x1d: Compute_LFO(slot); break; case 0x24: // printf("[%02d]: %x to DISDL/DIPAN (PC=%x)\n", s, slot->udata.data[0x24 / 2], arm7_get_register(15)); break; } } void aica_device::UpdateReg(int reg) { switch (reg & 0xff) { case 0x4: case 0x5: { m_DSP.RBL = (8 * 1024) << RBL(); // 8 / 16 / 32 / 64 kwords m_DSP.RBP = RBP(); } break; case 0x8: case 0x9: midi_in(m_udata.data[0x8 / 2] & 0xff); break; //case 0x0c: //case 0x0d: // printf("%04x\n",m_udata.data[0xc / 2]); // break; case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: break; case 0x80: case 0x81: m_dma.dmea = ((m_udata.data[0x80 / 2] & 0xfe00) << 7) | (m_dma.dmea & 0xfffc); /* TODO: $TSCD - MRWINH regs */ break; case 0x84: case 0x85: m_dma.dmea = (m_udata.data[0x84 / 2] & 0xfffc) | (m_dma.dmea & 0x7f0000); break; case 0x88: case 0x89: m_dma.drga = (m_udata.data[0x88 / 2] & 0x7ffc); m_dma.dgate = (m_udata.data[0x88 / 2] & 0x8000) >> 15; break; case 0x8c: case 0x8d: m_dma.dlg = (m_udata.data[0x8c / 2] & 0x7ffc); m_dma.ddir = (m_udata.data[0x8c / 2] & 0x8000) >> 15; if (m_udata.data[0x8c / 2] & 1) // dexe exec_dma(); break; case 0x90: case 0x91: if (!m_irq_cb.isnull()) { u32 time; m_TimPris[0] = 1 << ((m_udata.data[0x90 / 2] >> 8) & 0x7); m_TimCnt[0] = (m_udata.data[0x90 / 2] & 0xff) << 8; if ((m_udata.data[0x90 / 2] & 0xff) != 255) { time = (clock() / m_TimPris[0]) / (255 - (m_udata.data[0x90 / 2] & 0xff)); if (time) { m_timerA->adjust(attotime::from_ticks(512, time)); } } } break; case 0x94: case 0x95: if (!m_irq_cb.isnull()) { u32 time; m_TimPris[1] = 1 << ((m_udata.data[0x94 / 2] >> 8) & 0x7); m_TimCnt[1] = (m_udata.data[0x94 / 2] & 0xff) << 8; if ((m_udata.data[0x94 / 2] & 0xff) != 255) { time = (clock() / m_TimPris[1]) / (255 - (m_udata.data[0x94 / 2] & 0xff)); if (time) { m_timerB->adjust(attotime::from_ticks(512, time)); } } } break; case 0x98: case 0x99: if (!m_irq_cb.isnull()) { u32 time; m_TimPris[2] = 1 << ((m_udata.data[0x98 / 2] >> 8) & 0x7); m_TimCnt[2] = (m_udata.data[0x98 / 2] & 0xff) << 8; if ((m_udata.data[0x98 / 2] & 0xff) != 255) { time = (clock() / m_TimPris[2]) / (255 - (m_udata.data[0x98 / 2] & 0xff)); if (time) { m_timerC->adjust(attotime::from_ticks(512, time)); } } } break; case 0x9c: //SCIEB case 0x9d: if (m_udata.data[0x9c / 2] & 0x631) popmessage("AICA: SCIEB enabled %04x, contact MAME/MESSdev",m_udata.data[0x9c / 2]); break; case 0xa4: //SCIRE case 0xa5: if (!m_irq_cb.isnull()) { m_udata.data[0xa0 / 2] &= ~m_udata.data[0xa4 / 2]; ResetInterrupts(); // behavior from real hardware (SCSP, assumed to carry over): if you SCIRE a timer that's expired, // it'll immediately pop up again if (m_TimCnt[0] >= 0xff00) { m_udata.data[0xa0 / 2] |= 0x40; } if (m_TimCnt[1] >= 0xff00) { m_udata.data[0xa0 / 2] |= 0x80; } if (m_TimCnt[2] >= 0xff00) { m_udata.data[0xa0 / 2] |= 0x100; } } break; case 0xa8: case 0xa9: case 0xac: case 0xad: case 0xb0: case 0xb1: if (!m_irq_cb.isnull()) { m_IrqTimA = DecodeSCI(SCITMA); m_IrqTimBC = DecodeSCI(SCITMB); m_IrqMidi = DecodeSCI(SCIMID); } break; case 0xb4: //MCIEB case 0xb5: if (m_udata.data[0xb4 / 2] & 0x7df) popmessage("AICA: MCIEB enabled %04x, contact MAME/MESSdev",m_udata.data[0xb4 / 2]); m_mcieb = m_udata.data[0xb4 / 2]; CheckPendingIRQ_SH4(); break; case 0xb8: case 0xb9: if (m_udata.data[0xb8 / 2] & 0x20) m_mcipd |= 0x20; CheckPendingIRQ_SH4(); break; case 0xbc: case 0xbd: m_mcipd &= ~m_udata.data[0xbc / 2]; CheckPendingIRQ_SH4(); break; } } void aica_device::UpdateSlotRegR(int slot,int reg) { } void aica_device::UpdateRegR(int reg) { switch (reg & 0xff) { case 8: case 9: { u16 v=m_udata.data[0x8 / 2]; v &= 0xff00; v |= m_MidiStack[m_MidiR]; m_irq_cb(0); // cancel the IRQ if (m_MidiR != m_MidiW) { ++m_MidiR; m_MidiR &= 15; } m_udata.data[0x8 / 2] = v; } break; case 0x10: // LP check case 0x11: { int slotnum = MSLC(); AICA_SLOT *slot = m_Slots + slotnum; u16 LP; if (!(AFSEL())) { LP = slot->lpend ? 0x8000 : 0x0000; slot->lpend = 0; u16 SGC = (slot->EG.state << 13) & 0x6000; int EG = slot->active ? slot->EG.volume : 0; EG >>= (EG_SHIFT - 13); EG = 0x1FFF - EG; if (EG < 0) EG = 0; m_udata.data[0x10 / 2] = (EG & 0x1FF8) | SGC | LP; } else { LP = slot->lpend ? 0x8000 : 0x0000; m_udata.data[0x10 / 2] = LP; } } break; case 0x14: // CA (slot address) case 0x15: { //m_stream->update(); int slotnum = MSLC(); AICA_SLOT *slot = m_Slots + slotnum; u32 CA; if (PCMS(slot) == 0) // 16-bit samples { CA = (slot->cur_addr >> (SHIFT - 1)) & ~1; } else // 8-bit PCM and 4-bit ADPCM { CA = (slot->cur_addr >> SHIFT); } //printf("%08x %08x\n",CA,slot->cur_addr & ~1); m_udata.data[0x14 / 2] = CA; } break; case 0xb8: case 0xb9: m_udata.data[0xb8 / 2] = m_mcipd; break; } } void aica_device::w16(u32 addr,u16 val) { addr &= 0xffff; if (addr < 0x2000) { int slot=addr / 0x80; addr &= 0x7f; // printf("%x to slot %d offset %x\n", val, slot, addr); *((u16 *)(m_Slots[slot].udata.datab + (addr))) = val; UpdateSlotReg(slot, addr & 0x7f); } else if (addr < 0x2800) { if (addr <= 0x2044) { // printf("%x to EFSxx slot %d (addr %x)\n", val, (addr - 0x2000)/4, addr & 0x7f); m_EFSPAN[addr & 0x7f] = val; } } else if (addr < 0x3000) { if (addr < 0x28be) { // printf("%x to AICA global @ %x\n", val, addr & 0xff); *((u16 *)(m_udata.datab+((addr & 0xff)))) = val; UpdateReg(addr & 0xff); } else if (addr == 0x2d00) { m_IRQL = val; popmessage("AICA: write to IRQL?"); } else if (addr == 0x2d04) { m_IRQR = val; if (val & 1) { m_irq_cb(0); } if (val & 0x100) popmessage("AICA: SH-4 write protection enabled!"); if (val & 0xfefe) popmessage("AICA: IRQR %04x!",val); } } else { //DSP if (addr < 0x3200) //COEF *((u16 *)(m_DSP.COEF+(addr - 0x3000) / 2)) = val; else if (addr < 0x3300) *((u16 *)(m_DSP.MADRS+(addr - 0x3200) / 2)) = val; else if (addr < 0x3400) popmessage("AICADSP write to undocumented reg %04x -> %04x", addr, val); else if (addr < 0x3c00) { *((u16 *)(m_DSP.MPRO+(addr - 0x3400) / 2)) = val; if (addr == 0x3bfe) { m_DSP.start(); } } else if (addr < 0x4000) { popmessage("AICADSP write to undocumented reg %04x -> %04x",addr,val); } else if (addr < 0x4400) { if (addr & 4) m_DSP.TEMP[(addr >> 3) & 0x7f] = (m_DSP.TEMP[(addr >> 3) & 0x7f] & 0xffff0000) | (val & 0xffff); else m_DSP.TEMP[(addr >> 3) & 0x7f] = (m_DSP.TEMP[(addr >> 3) & 0x7f] & 0xffff) | (val << 16); } else if (addr < 0x4500) { if (addr & 4) m_DSP.MEMS[(addr >> 3) & 0x1f] = (m_DSP.MEMS[(addr >> 3) & 0x1f] & 0xffff0000) | (val & 0xffff); else m_DSP.MEMS[(addr >> 3) & 0x1f] = (m_DSP.MEMS[(addr >> 3) & 0x1f] & 0xffff) | (val << 16); } else if (addr < 0x4580) { if (addr & 4) m_DSP.MIXS[(addr >> 3) & 0xf] = (m_DSP.MIXS[(addr >> 3) & 0xf] & 0xffff0000) | (val & 0xffff); else m_DSP.MIXS[(addr >> 3) & 0xf] = (m_DSP.MIXS[(addr >> 3) & 0xf] & 0xffff) | (val << 16); } else if (addr < 0x45c0) *((u16 *)(m_DSP.EFREG+(addr - 0x4580)/4)) = val; //else if (addr < 0x45c8) // *((u16 *)(m_DSP.EXTS+(addr - 0x45c0) / 2)) = val; // Read only } } u16 aica_device::r16(u32 addr) { u16 v = 0; addr &= 0xffff; if (addr < 0x2000) { int slot=addr / 0x80; addr &= 0x7f; UpdateSlotRegR(slot,addr & 0x7f); v=*((u16 *)(m_Slots[slot].udata.datab+(addr))); } else if (addr < 0x3000) { if (addr <= 0x2044) { v = m_EFSPAN[addr & 0x7f]; } else if (addr < 0x2800) popmessage("AICA read undocumented reg %04x", addr); else if (addr < 0x28be) { UpdateRegR(addr & 0xff); v= *((u16 *)(m_udata.datab+((addr & 0xff)))); if ((addr & 0xfffe) == 0x2810) m_udata.data[0x10 / 2] &= 0x7FFF; // reset LP on read } else if (addr == 0x2d00) { return m_IRQL; } else if (addr == 0x2d04) { //popmessage("AICA: read to IRQR?"); return m_IRQR; } } else { if (addr < 0x3200) //COEF v= *((u16 *)(m_DSP.COEF+(addr - 0x3000) / 2)); else if (addr < 0x3300) v= *((u16 *)(m_DSP.MADRS+(addr - 0x3200) / 2)); else if (addr < 0x3400) popmessage("AICADSP read undocumented reg %04x", addr); else if (addr < 0x3c00) v= *((u16 *)(m_DSP.MPRO+(addr - 0x3400) / 2)); else if (addr < 0x4000) popmessage("AICADSP read undocumented reg %04x",addr); else if (addr < 0x4400) { if (addr & 4) v= m_DSP.TEMP[(addr >> 3) & 0x7f] & 0xffff; else v= m_DSP.TEMP[(addr >> 3) & 0x7f] >> 16; } else if (addr < 0x4500) { if (addr & 4) v= m_DSP.MEMS[(addr >> 3) & 0x1f] & 0xffff; else v= m_DSP.MEMS[(addr >> 3) & 0x1f] >> 16; } else if (addr < 0x4580) { if (addr & 4) v= m_DSP.MIXS[(addr >> 3) & 0xf] & 0xffff; else v= m_DSP.MIXS[(addr >> 3) & 0xf] >> 16; } else if (addr < 0x45c0) v = *((u16 *)(m_DSP.EFREG+(addr - 0x4580)/4)); else if (addr < 0x45c8) v = *((u16 *)(m_DSP.EXTS+(addr - 0x45c0) / 2)); } return v; } #ifdef UNUSED_FUNCTION void aica_device::TimersAddTicks(int ticks) { if (m_TimCnt[0] <= 0xff00) { m_TimCnt[0] += ticks << (8-((m_udata.data[0x18 / 2] >> 8) & 0x7)); if (m_TimCnt[0] > 0xFF00) { m_TimCnt[0] = 0xFFFF; m_udata.data[0xa0 / 2] |= 0x40; } m_udata.data[0x90 / 2] &= 0xff00; m_udata.data[0x90 / 2] |= m_TimCnt[0] >> 8; } if (m_TimCnt[1] <= 0xff00) { m_TimCnt[1] += ticks << (8-((m_udata.data[0x1a / 2] >> 8) & 0x7)); if (m_TimCnt[1] > 0xFF00) { m_TimCnt[1] = 0xFFFF; m_udata.data[0xa0 / 2] |= 0x80; } m_udata.data[0x94 / 2] &= 0xff00; m_udata.data[0x94 / 2] |= m_TimCnt[1] >> 8; } if (m_TimCnt[2] <= 0xff00) { m_TimCnt[2] += ticks << (8-((m_udata.data[0x1c / 2] >> 8) & 0x7)); if (m_TimCnt[2] > 0xFF00) { m_TimCnt[2] = 0xFFFF; m_udata.data[0xa0 / 2] |= 0x100; } m_udata.data[0x98 / 2] &= 0xff00; m_udata.data[0x98 / 2] |= m_TimCnt[2] >> 8; } } #endif s32 aica_device::UpdateSlot(AICA_SLOT *slot) { s32 sample; int step = slot->step; u32 addr1, addr2, addr_select; // current and next sample addresses u32 *addr[2] = {&addr1, &addr2}; // used for linear interpolation u32 *slot_addr[2] = {&(slot->cur_addr), &(slot->nxt_addr)}; // u32 chanlea = LEA(slot); if (SSCTL(slot) != 0) //no FM or noise yet return 0; if (PCMS(slot) == 3) // Red Dog music relies on this chanlea = (chanlea + 3) & ~3; if (PLFOS(slot) != 0) { step = step * PLFO_Step(&(slot->PLFO)); step >>= SHIFT; } if (PCMS(slot) == 1) { addr1 = slot->cur_addr >> SHIFT; addr2 = slot->nxt_addr >> SHIFT; } else if (PCMS(slot) == 0) { addr1 = (slot->cur_addr >> (SHIFT - 1)) & ~1; addr2 = (slot->nxt_addr >> (SHIFT - 1)) & ~1; } else { addr1 = slot->cur_addr >> SHIFT; addr2 = slot->nxt_addr >> SHIFT; } if (PCMS(slot) == 1) // 8-bit signed { s8 p1 = m_cache->read_byte(SA(slot) + addr1); s8 p2 = m_cache->read_byte(SA(slot) + addr2); s32 s; s32 fpart=slot->cur_addr & ((1 << SHIFT) - 1); s = (int)(p1 << 8) * ((1 << SHIFT) - fpart) + (int)(p2 << 8) * fpart; sample = (s >> SHIFT); } else if (PCMS(slot) == 0) //16 bit signed { s16 p1 = m_cache->read_word(SA(slot) + addr1); s16 p2 = m_cache->read_word(SA(slot) + addr2); s32 s; s32 fpart = slot->cur_addr & ((1 << SHIFT) - 1); s = (int)(p1) * ((1 << SHIFT) - fpart) + (int)(p2) * fpart; sample = (s >> SHIFT); } else // 4-bit ADPCM { u32 base = slot->adbase; int cur_sample; //current ADPCM sample int nxt_sample; //next ADPCM sample s32 fpart=slot->cur_addr&((1 << SHIFT)-1); u32 steps_to_go = addr1 > addr2 ? chanlea : addr2, curstep = slot->curstep; cur_sample = slot->cur_sample; // may already contains current decoded sample // seek to the interpolation sample while (curstep < steps_to_go) { int shift1 = 4 & (curstep << 2); u8 delta1 = (m_cache->read_byte(base) >> shift1) & 0xf; DecodeADPCM(&(slot->cur_sample), delta1, &(slot->cur_quant)); if (!(++curstep & 1)) base++; if (curstep == addr1) cur_sample = slot->cur_sample; if (curstep == LSA(slot)) { slot->cur_lpsample = slot->cur_sample; slot->cur_lpquant = slot->cur_quant; } } nxt_sample = slot->cur_sample; slot->adbase = base; slot->curstep = curstep; s32 s = (int)cur_sample * ((1 << SHIFT) - fpart) + (int)nxt_sample * fpart; sample = (s >> SHIFT); } slot->prv_addr = slot->cur_addr; slot->cur_addr += step; slot->nxt_addr = slot->cur_addr + (1 << SHIFT); addr1 = slot->cur_addr >> SHIFT; addr2 = slot->nxt_addr >> SHIFT; if (addr1 >= LSA(slot)) { if (LPSLNK(slot) && slot->EG.state == AICA_ATTACK && slot->EG.D1R) slot->EG.state = AICA_DECAY1; } for (addr_select = 0; addr_select < 2; addr_select++) { s32 rem_addr; switch (LPCTL(slot)) { case 0: //no loop if (*addr[addr_select] >= LSA(slot) && *addr[addr_select] >= chanlea) { StopSlot(slot,0); } break; // TODO: causes an hang in Border Down/Metal Slug 6/Karous etc. // for mslug6 culprit RAM address is 0x13880 ARM side (a flag that should be zeroed somehow) case 1: //normal loop if (*addr[addr_select] >= chanlea) { slot->lpend = 1; rem_addr = *slot_addr[addr_select] - (chanlea << SHIFT); *slot_addr[addr_select] = (LSA(slot) << SHIFT) + rem_addr; if (PCMS(slot)>=2 && addr_select == 0) { // restore the state @ LSA - the sampler will naturally walk to (LSA + remainder) slot->adbase = SA(slot) + (LSA(slot) / 2); slot->curstep = LSA(slot); if (PCMS(slot) == 2) { slot->cur_sample = slot->cur_lpsample; slot->cur_quant = slot->cur_lpquant; } // printf("Looping: slot_addr %x LSA %x LEA %x step %x base %x\n", *slot_addr[addr_select] >> SHIFT, LSA(slot), LEA(slot), slot->curstep, slot->adbase); } } break; } } if (ALFOS(slot) != 0) { sample = sample * ALFO_Step(&(slot->ALFO)); sample >>= SHIFT; } if (slot->EG.state == AICA_ATTACK) sample = (sample*EG_Update(slot)) >> SHIFT; else sample = (sample * m_EG_TABLE[EG_Update(slot) >> (SHIFT - 10)]) >> SHIFT; return sample; } void aica_device::DoMasterSamples(int nsamples) { stream_sample_t *exts[2]; int i; stream_sample_t *bufr = m_bufferr; stream_sample_t *bufl = m_bufferl; exts[0] = m_exts0; exts[1] = m_exts1; for (int s = 0; s < nsamples; ++s) { s32 smpl = 0, smpr = 0; // mix slots' direct output for (int sl = 0; sl < 64; ++sl) { AICA_SLOT *slot = m_Slots + sl; if (m_Slots[sl].active) { s32 sample = UpdateSlot(slot); u32 Enc = ((TL(slot)) << 0x0) | ((IMXL(slot)) << 0xd); m_DSP.setsample((sample * m_LPANTABLE[Enc]) >> (SHIFT - 2), ISEL(slot), IMXL(slot)); Enc = ((TL(slot)) << 0x0) | ((DIPAN(slot)) << 0x8) | ((DISDL(slot)) << 0xd); { smpl += (sample * m_LPANTABLE[Enc]) >> SHIFT; smpr += (sample * m_RPANTABLE[Enc]) >> SHIFT; } } } // process the DSP m_DSP.step(); // mix DSP output for (i = 0; i < 16; ++i) { if (EFSDL(i)) { u32 Enc = ((EFPAN(i)) << 0x8) | ((EFSDL(i)) << 0xd); smpl += (m_DSP.EFREG[i] * m_LPANTABLE[Enc]) >> SHIFT; smpr += (m_DSP.EFREG[i] * m_RPANTABLE[Enc]) >> SHIFT; } } // mix EXTS output for (i = 0; i < 2; ++i) { if (EFSDL(i + 16)) // 16,17 for EXTS { m_DSP.EXTS[i] = exts[i][s]; u32 Enc = ((EFPAN(i + 16)) << 0x8) | ((EFSDL(i + 16)) << 0xd); smpl += (m_DSP.EXTS[i] * m_LPANTABLE[Enc]) >> SHIFT; smpr += (m_DSP.EXTS[i] * m_RPANTABLE[Enc]) >> SHIFT; } } if (DAC18B()) { smpl = clip18(smpl >> 1); smpr = clip18(smpr >> 1); } else { smpl = clip16(smpl >> 3); smpr = clip16(smpr >> 3); } *bufl++ = (smpl * m_LPANTABLE[MVOL() << 0xd]) >> SHIFT; *bufr++ = (smpr * m_LPANTABLE[MVOL() << 0xd]) >> SHIFT; } } /* TODO: this needs to be timer-ized */ void aica_device::exec_dma() { static u16 tmp_dma[4]; int i; printf("AICA: DMA transfer START\n" "DMEA: %08x DRGA: %08x DLG: %04x\n" "DGATE: %d DDIR: %d\n",m_dma.dmea,m_dma.drga,m_dma.dlg,m_dma.dgate,m_dma.ddir); /* Copy the dma values in a temp storage for resuming later */ /* (DMA *can't* overwrite its parameters). */ if (!(m_dma.ddir)) { for (i = 0; i < 4; i++) tmp_dma[i] = m_udata.data[(0x80 + (i * 4)) / 2]; } /* note: we don't use space.read_word / write_word because it can happen that SH-4 enables the DMA instead of ARM like in DCLP tester. */ /* TODO: don't know if params auto-updates, I guess not ... */ if (m_dma.ddir) { if (m_dma.dgate) { for (i = 0; i < m_dma.dlg; i+=2) { m_data->write_word(m_dma.dmea, 0); m_dma.dmea += 2; } } else { for (i = 0; i < m_dma.dlg; i+=2) { u16 tmp; tmp = r16(m_dma.drga); m_data->write_word(m_dma.dmea, tmp); m_dma.dmea += 4; m_dma.drga += 4; } } } else { if (m_dma.dgate) { for (i = 0; i < m_dma.dlg; i+=2) { w16(m_dma.drga, 0); m_dma.drga += 4; } } else { for (i = 0; i < m_dma.dlg; i+=2) { u16 tmp = m_cache->read_word(m_dma.dmea); w16(m_dma.drga, tmp); m_dma.dmea += 4; m_dma.drga += 4; } } } /*Resume the values*/ if (!(m_dma.ddir)) { for (i = 0; i < 4; i++) m_udata.data[(0x80+(i*4)) / 2] = tmp_dma[i]; } /* Job done, clear DEXE */ m_udata.data[0x8c / 2] &= ~1; /* request a dma end irq */ m_mcipd |= 0x10; CheckPendingIRQ_SH4(); } #ifdef UNUSED_FUNCTION int aica_device::IRQCB(void *param) { CheckPendingIRQ(param); return -1; } #endif //------------------------------------------------- // sound_stream_update - handle a stream update //------------------------------------------------- void aica_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) { m_bufferl = outputs[0]; m_bufferr = outputs[1]; m_exts0 = inputs[0]; m_exts1 = inputs[1]; DoMasterSamples(samples); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void aica_device::device_start() { m_data = &space(0); // Find our direct access m_cache = space().cache<1, 0, ENDIANNESS_LITTLE>(); // init the emulation Init(); // set up the IRQ callbacks m_irq_cb.resolve_safe(); m_main_irq_cb.resolve_safe(); m_stream = machine().sound().stream_alloc(*this, 2, 2, (int)m_rate); // save state save_item(NAME(m_udata.data)); save_item(NAME(m_IRQL)); save_item(NAME(m_IRQR)); save_item(NAME(m_EFSPAN)); for (int slot = 0; slot < 64; slot++) { save_item(NAME(m_Slots[slot].udata.data), slot); save_item(NAME(m_Slots[slot].active), slot); save_item(NAME(m_Slots[slot].prv_addr), slot); save_item(NAME(m_Slots[slot].cur_addr), slot); save_item(NAME(m_Slots[slot].nxt_addr), slot); save_item(NAME(m_Slots[slot].step), slot); save_item(NAME(m_Slots[slot].Backwards), slot); save_item(NAME(m_Slots[slot].EG.volume), slot); save_item(NAME(m_Slots[slot].EG.step), slot); save_item(NAME(m_Slots[slot].EG.AR), slot); save_item(NAME(m_Slots[slot].EG.D1R), slot); save_item(NAME(m_Slots[slot].EG.D2R), slot); save_item(NAME(m_Slots[slot].EG.RR), slot); save_item(NAME(m_Slots[slot].EG.DL), slot); save_item(NAME(m_Slots[slot].PLFO.phase), slot); save_item(NAME(m_Slots[slot].PLFO.phase_step), slot); save_item(NAME(m_Slots[slot].ALFO.phase), slot); save_item(NAME(m_Slots[slot].ALFO.phase_step), slot); save_item(NAME(m_Slots[slot].slot), slot); save_item(NAME(m_Slots[slot].cur_sample), slot); save_item(NAME(m_Slots[slot].cur_quant), slot); save_item(NAME(m_Slots[slot].curstep), slot); save_item(NAME(m_Slots[slot].cur_lpquant), slot); save_item(NAME(m_Slots[slot].cur_lpsample), slot); save_item(NAME(m_Slots[slot].cur_lpstep), slot); save_item(NAME(m_Slots[slot].adbase), slot); save_item(NAME(m_Slots[slot].lpend), slot); } save_item(NAME(m_IrqTimA)); save_item(NAME(m_IrqTimBC)); save_item(NAME(m_IrqMidi)); save_item(NAME(m_MidiOutW)); save_item(NAME(m_MidiOutR)); save_item(NAME(m_MidiStack)); save_item(NAME(m_MidiW)); save_item(NAME(m_MidiR)); save_item(NAME(m_TimPris)); save_item(NAME(m_TimCnt)); save_item(NAME(m_mcieb)); save_item(NAME(m_mcipd)); save_item(NAME(m_dma.dmea)); save_item(NAME(m_dma.drga)); save_item(NAME(m_dma.dlg)); save_item(NAME(m_dma.dgate)); save_item(NAME(m_dma.ddir)); save_item(NAME(m_DSP.RBP)); save_item(NAME(m_DSP.RBL)); save_item(NAME(m_DSP.COEF)); save_item(NAME(m_DSP.MADRS)); save_item(NAME(m_DSP.MPRO)); save_item(NAME(m_DSP.TEMP)); save_item(NAME(m_DSP.MEMS)); save_item(NAME(m_DSP.DEC)); save_item(NAME(m_DSP.MIXS)); save_item(NAME(m_DSP.EXTS)); save_item(NAME(m_DSP.EFREG)); save_item(NAME(m_DSP.Stopped)); save_item(NAME(m_DSP.LastStep)); } //------------------------------------------------- // device_post_load - called after loading a saved state //------------------------------------------------- void aica_device::device_post_load() { for (int slot = 0; slot < 64; slot++) Compute_LFO(&m_Slots[slot]); } //------------------------------------------------- // device_clock_changed - called if the clock // changes //------------------------------------------------- void aica_device::device_clock_changed() { ClockChange(); m_stream->set_sample_rate((int)m_rate); } //------------------------------------------------- // memory_space_config - return a description of // any address spaces owned by this device //------------------------------------------------- device_memory_interface::space_config_vector aica_device::memory_space_config() const { return space_config_vector{ std::make_pair(0, &m_data_config) }; } u16 aica_device::read(offs_t offset) { return r16(offset * 2); } void aica_device::write(offs_t offset, u16 data, u16 mem_mask) { u16 tmp = r16(offset * 2); COMBINE_DATA(&tmp); w16(offset * 2, tmp); } void aica_device::midi_in(u8 data) { m_MidiStack[m_MidiW++] = data; m_MidiW &= 15; } u8 aica_device::midi_out_r() { u8 val = m_MidiStack[m_MidiR++]; m_MidiR &= 7; return val; } DEFINE_DEVICE_TYPE(AICA, aica_device, "aica", "Yamaha AICA") aica_device::aica_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, AICA, tag, owner, clock) , device_sound_interface(mconfig, *this) , device_memory_interface(mconfig, *this) , m_data_config("data", ENDIANNESS_LITTLE, 16, 23) // 16 bit data bus confirmed , m_rate(44100.0) , m_irq_cb(*this) , m_main_irq_cb(*this) , m_IRQL(0) , m_IRQR(0) , m_IrqTimA(0) , m_IrqTimBC(0) , m_IrqMidi(0) , m_MidiOutW(0) , m_MidiOutR(0) , m_MidiW(0) , m_MidiR(0) , m_mcieb(0) , m_mcipd(0) , m_bufferl(nullptr) , m_bufferr(nullptr) , m_exts0(nullptr) , m_exts1(nullptr) { memset(&m_udata.data, 0, sizeof(m_udata.data)); std::fill(std::begin(m_EFSPAN), std::end(m_EFSPAN), 0); memset(m_Slots, 0, sizeof(m_Slots)); std::fill(std::begin(m_MidiStack), std::end(m_MidiStack), 0); std::fill(std::begin(m_LPANTABLE), std::end(m_LPANTABLE), 0); std::fill(std::begin(m_TimPris), std::end(m_TimPris), 0); std::fill(std::begin(m_TimCnt), std::end(m_TimCnt), 0); memset(&m_dma, 0, sizeof(m_dma)); std::fill(std::begin(m_ARTABLE), std::end(m_ARTABLE), 0); std::fill(std::begin(m_DRTABLE), std::end(m_DRTABLE), 0); memset(&m_DSP, 0, sizeof(m_DSP)); std::fill(std::begin(m_EG_TABLE), std::end(m_EG_TABLE), 0); std::fill(std::begin(m_PLFO_TRI), std::end(m_PLFO_TRI), 0); std::fill(std::begin(m_PLFO_SQR), std::end(m_PLFO_SQR), 0); std::fill(std::begin(m_PLFO_SAW), std::end(m_PLFO_SAW), 0); std::fill(std::begin(m_PLFO_NOI), std::end(m_PLFO_NOI), 0); std::fill(std::begin(m_ALFO_TRI), std::end(m_ALFO_TRI), 0); std::fill(std::begin(m_ALFO_SQR), std::end(m_ALFO_SQR), 0); std::fill(std::begin(m_ALFO_SAW), std::end(m_ALFO_SAW), 0); std::fill(std::begin(m_ALFO_NOI), std::end(m_ALFO_NOI), 0); memset(m_PSCALES, 0, sizeof(m_PSCALES)); memset(m_ASCALES, 0, sizeof(m_ASCALES)); } static const float LFOFreq[32] = {0.17f,0.19f,0.23f,0.27f,0.34f,0.39f,0.45f,0.55f,0.68f,0.78f,0.92f,1.10f,1.39f,1.60f,1.87f,2.27f, 2.87f,3.31f,3.92f,4.79f,6.15f,7.18f,8.60f,10.8f,14.4f,17.2f,21.5f,28.7f,43.1f,57.4f,86.1f,172.3f}; static const float ASCALE[8] = {0.0f,0.4f,0.8f,1.5f,3.0f,6.0f,12.0f,24.0f}; static const float PSCALE[8] = {0.0f,7.0f,13.5f,27.0f,55.0f,112.0f,230.0f,494.0f}; void aica_device::LFO_Init() { int i, s; for (i = 0; i < 256; ++i) { int a, p; // float TL; //Saw a = 255 - i; if (i < 128) p = i; else p = i - 256; m_ALFO_SAW[i] = a; m_PLFO_SAW[i] = p; //Square if (i < 128) { a = 255; p = 127; } else { a = 0; p = -128; } m_ALFO_SQR[i] = a; m_PLFO_SQR[i] = p; //Tri if (i < 128) a = 255 - (i * 2); else a = (i * 2) - 256; if (i < 64) p = i * 2; else if (i < 128) p = 255 - i * 2; else if (i < 192) p = 256 - i * 2; else p = i * 2 - 511; m_ALFO_TRI[i] = a; m_PLFO_TRI[i] = p; //noise //a = lfo_noise[i]; a = machine().rand() & 0xff; p = 128 - a; m_ALFO_NOI[i] = a; m_PLFO_NOI[i] = p; } for (s = 0; s < 8; ++s) { float limit = PSCALE[s]; for (i = -128; i < 128; ++i) { m_PSCALES[s][i+128] = CENTS(((limit * (float) i) / 128.0f)); } limit = -ASCALE[s]; for (i = 0; i < 256; ++i) { m_ASCALES[s][i] = DB(((limit * (float) i) / 256.0f)); } } } s32 aica_device::PLFO_Step(AICA_LFO_t *LFO) { int p; LFO->phase += LFO->phase_step; #if LFO_SHIFT!=8 LFO->phase &= (1 << (LFO_SHIFT + 8)) - 1; #endif p = LFO->table[LFO->phase >> LFO_SHIFT]; p = LFO->scale[p + 128]; return p << (SHIFT - LFO_SHIFT); } s32 aica_device::ALFO_Step(AICA_LFO_t *LFO) { int p; LFO->phase += LFO->phase_step; #if LFO_SHIFT!=8 LFO->phase &= (1 << (LFO_SHIFT + 8)) - 1; #endif p = LFO->table[LFO->phase >> LFO_SHIFT]; p = LFO->scale[p]; return p << (SHIFT - LFO_SHIFT); } void aica_device::LFO_ComputeStep(AICA_LFO_t *LFO,u32 LFOF,u32 LFOWS,u32 LFOS,int ALFO) { float step = (float) LFOFreq[LFOF]*256.0f/44100.0f; LFO->phase_step = (u32)((float)(1 << LFO_SHIFT)*step); if (ALFO) { switch (LFOWS) { case 0: LFO->table = m_ALFO_SAW; break; case 1: LFO->table = m_ALFO_SQR; break; case 2: LFO->table = m_ALFO_TRI; break; case 3: LFO->table = m_ALFO_NOI; break; default: printf("Unknown ALFO %d\n", LFOWS); } LFO->scale = m_ASCALES[LFOS]; } else { switch (LFOWS) { case 0: LFO->table = m_PLFO_SAW; break; case 1: LFO->table = m_PLFO_SQR; break; case 2: LFO->table = m_PLFO_TRI; break; case 3: LFO->table = m_PLFO_NOI; break; default: printf("Unknown PLFO %d\n", LFOWS); } LFO->scale = m_PSCALES[LFOS]; } }