void CEXIChannel::DoState(PointerWrap &p) { p.DoPOD(m_Status); p.Do(m_DMAMemoryAddress); p.Do(m_DMALength); p.Do(m_Control); p.Do(m_ImmData); for (int d = 0; d < NUM_DEVICES; ++d) { IEXIDevice* pDevice = m_pDevices[d].get(); TEXIDevices type = pDevice->m_deviceType; p.Do(type); IEXIDevice* pSaveDevice = (type == pDevice->m_deviceType) ? pDevice : EXIDevice_Create(type, m_ChannelId); pSaveDevice->DoState(p); if(pSaveDevice != pDevice) { // if we had to create a temporary device, discard it if we're not loading. // also, if no movie is active, we'll assume the user wants to keep their current devices // instead of the ones they had when the savestate was created, // unless the device is NONE (since ChangeDevice sets that temporarily). if(p.GetMode() != PointerWrap::MODE_READ) { delete pSaveDevice; } else { AddDevice(pSaveDevice, d, false); } } } }
void CEXIChannel::RegisterMMIO(MMIO::Mapping* mmio, u32 base) { // Warning: the base is not aligned on a page boundary here. We can't use | // to select a register address, instead we need to use +. mmio->Register(base + EXI_STATUS, MMIO::ComplexRead<u32>([this](u32) { // check if external device is present // pretty sure it is memcard only, not entirely sure if (m_ChannelId == 2) { m_Status.EXT = 0; } else { m_Status.EXT = GetDevice(1)->IsPresent() ? 1 : 0; } return m_Status.Hex; }), MMIO::ComplexWrite<u32>([this](u32, u32 val) { UEXI_STATUS newStatus(val); m_Status.EXIINTMASK = newStatus.EXIINTMASK; if (newStatus.EXIINT) m_Status.EXIINT = 0; m_Status.TCINTMASK = newStatus.TCINTMASK; if (newStatus.TCINT) m_Status.TCINT = 0; m_Status.CLK = newStatus.CLK; if (m_ChannelId == 0 || m_ChannelId == 1) { m_Status.EXTINTMASK = newStatus.EXTINTMASK; if (newStatus.EXTINT) m_Status.EXTINT = 0; } if (m_ChannelId == 0) m_Status.ROMDIS = newStatus.ROMDIS; IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT ^ newStatus.CHIP_SELECT); m_Status.CHIP_SELECT = newStatus.CHIP_SELECT; if (pDevice != nullptr) pDevice->SetCS(m_Status.CHIP_SELECT); ExpansionInterface::UpdateInterrupts(); })); mmio->Register(base + EXI_DMAADDR, MMIO::DirectRead<u32>(&m_DMAMemoryAddress), MMIO::DirectWrite<u32>(&m_DMAMemoryAddress)); mmio->Register(base + EXI_DMALENGTH, MMIO::DirectRead<u32>(&m_DMALength), MMIO::DirectWrite<u32>(&m_DMALength)); mmio->Register(base + EXI_DMACONTROL, MMIO::DirectRead<u32>(&m_Control.Hex), MMIO::ComplexWrite<u32>([this](u32, u32 val) { m_Control.Hex = val; if (m_Control.TSTART) { IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT); if (pDevice == nullptr) return; if (m_Control.DMA == 0) { // immediate data switch (m_Control.RW) { case EXI_READ: m_ImmData = pDevice->ImmRead(m_Control.TLEN + 1); break; case EXI_WRITE: pDevice->ImmWrite(m_ImmData, m_Control.TLEN + 1); break; case EXI_READWRITE: pDevice->ImmReadWrite(m_ImmData, m_Control.TLEN + 1); break; default: _dbg_assert_msg_(EXPANSIONINTERFACE, 0, "EXI Imm: Unknown transfer type %i", m_Control.RW); } } else { // DMA switch (m_Control.RW) { case EXI_READ: pDevice->DMARead(m_DMAMemoryAddress, m_DMALength); break; case EXI_WRITE: pDevice->DMAWrite(m_DMAMemoryAddress, m_DMALength); break; default: _dbg_assert_msg_(EXPANSIONINTERFACE, 0, "EXI DMA: Unknown transfer type %i", m_Control.RW); } } m_Control.TSTART = 0; // Check if device needs specific timing, otherwise just complete transfer // immediately if (!pDevice->UseDelayedTransferCompletion()) SendTransferComplete(); } })); mmio->Register(base + EXI_IMMDATA, MMIO::DirectRead<u32>(&m_ImmData), MMIO::DirectWrite<u32>(&m_ImmData)); }
void CEXIChannel::Write32(const u32 _iValue, const u32 _iRegister) { DEBUG_LOG(EXPANSIONINTERFACE, "(w32) 0x%08x channel: %i register: %s", _iValue, m_ChannelId, Debug_GetRegisterName(_iRegister)); switch (_iRegister) { case EXI_STATUS: { UEXI_STATUS newStatus(_iValue); m_Status.EXIINTMASK = newStatus.EXIINTMASK; if (newStatus.EXIINT) m_Status.EXIINT = 0; m_Status.TCINTMASK = newStatus.TCINTMASK; if (newStatus.TCINT) m_Status.TCINT = 0; m_Status.CLK = newStatus.CLK; if (m_ChannelId == 0 || m_ChannelId == 1) { m_Status.EXTINTMASK = newStatus.EXTINTMASK; if (newStatus.EXTINT) m_Status.EXTINT = 0; } if (m_ChannelId == 0) m_Status.ROMDIS = newStatus.ROMDIS; IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT ^ newStatus.CHIP_SELECT); m_Status.CHIP_SELECT = newStatus.CHIP_SELECT; if (pDevice != NULL) pDevice->SetCS(m_Status.CHIP_SELECT); CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0); } break; case EXI_DMAADDR: INFO_LOG(EXPANSIONINTERFACE, "Wrote DMAAddr, channel %i", m_ChannelId); m_DMAMemoryAddress = _iValue; break; case EXI_DMALENGTH: INFO_LOG(EXPANSIONINTERFACE, "Wrote DMALength, channel %i", m_ChannelId); m_DMALength = _iValue; break; case EXI_DMACONTROL: INFO_LOG(EXPANSIONINTERFACE, "Wrote DMAControl, channel %i", m_ChannelId); m_Control.Hex = _iValue; if (m_Control.TSTART) { IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT); if (pDevice == NULL) return; if (m_Control.DMA == 0) { // immediate data switch (m_Control.RW) { case EXI_READ: m_ImmData = pDevice->ImmRead(m_Control.TLEN + 1); break; case EXI_WRITE: pDevice->ImmWrite(m_ImmData, m_Control.TLEN + 1); break; case EXI_READWRITE: pDevice->ImmReadWrite(m_ImmData, m_Control.TLEN + 1); break; default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI Imm: Unknown transfer type %i", m_Control.RW); } m_Control.TSTART = 0; } else { // DMA switch (m_Control.RW) { case EXI_READ: pDevice->DMARead (m_DMAMemoryAddress, m_DMALength); break; case EXI_WRITE: pDevice->DMAWrite(m_DMAMemoryAddress, m_DMALength); break; default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI DMA: Unknown transfer type %i", m_Control.RW); } m_Control.TSTART = 0; } if(!m_Control.TSTART) // completed ! { m_Status.TCINT = 1; CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0); } } break; case EXI_IMMDATA: INFO_LOG(EXPANSIONINTERFACE, "Wrote IMMData, channel %i", m_ChannelId); m_ImmData = _iValue; break; } }