Fix double INTRQ assertion on certain occasions involving DMA transfers and ATAPI commands

Still doesn't fix broken DMA transfer for the READ DVD STRUCTURE
This commit is contained in:
StrikerX3 2018-12-09 19:14:21 -02:00
parent bd501ac359
commit 84f6dd48bc
13 changed files with 70 additions and 11 deletions

View file

@ -158,6 +158,7 @@ void ATAChannel::ReadData(uint32_t *value, uint8_t size) {
// Read data for the command and clear it if finished
m_currentCommand->ReadData((uint8_t*)value, size);
if (m_currentCommand->IsFinished()) {
log_spew("ATAChannel::ReadData: Finished processing command for channel %d\n", m_channel);
delete m_currentCommand;
m_currentCommand = nullptr;
}
@ -181,6 +182,7 @@ void ATAChannel::WriteData(uint32_t value, uint8_t size) {
// Write data for the command and clear it if finished
m_currentCommand->WriteData((uint8_t*)&value, size);
if (m_currentCommand->IsFinished()) {
//log_spew("ATAChannel::WriteData: Finished processing command for channel %d\n", m_channel);
delete m_currentCommand;
m_currentCommand = nullptr;
}
@ -220,6 +222,7 @@ void ATAChannel::WriteCommand(uint8_t value) {
{
m_currentCommand->Execute();
if (m_currentCommand->IsFinished()) {
//log_spew("ATAChannel::WriteCommand: Finished processing command 0x%x for channel %d, device %d\n", cmd, m_channel, devIndex);
delete m_currentCommand;
m_currentCommand = nullptr;
}
@ -237,6 +240,7 @@ DMATransferResult ATAChannel::ReadDMA(uint8_t *dstBuffer, uint32_t readLen) {
// Read data for the command and clear it if finished
m_currentCommand->ReadData(dstBuffer, readLen);
if (m_currentCommand->IsFinished()) {
log_spew("ATAChannel::ReadDMA: Finished processing command for channel %d\n", m_channel);
delete m_currentCommand;
m_currentCommand = nullptr;
return DMATransferEnd;
@ -256,6 +260,7 @@ DMATransferResult ATAChannel::WriteDMA(uint8_t *srcBuffer, uint32_t writeLen) {
// Write data for the command and clear it if finished
m_currentCommand->WriteData(srcBuffer, writeLen);
if (m_currentCommand->IsFinished()) {
log_spew("ATAChannel::WriteDMA: Finished processing command for channel %d\n", m_channel);
delete m_currentCommand;
m_currentCommand = nullptr;
return DMATransferEnd;
@ -266,7 +271,11 @@ DMATransferResult ATAChannel::WriteDMA(uint8_t *srcBuffer, uint32_t writeLen) {
void ATAChannel::SetInterrupt(bool asserted) {
if (asserted != m_interrupt && m_regs.AreInterruptsEnabled()) {
log_spew("ATAChannel::SetInterrupt: %s interrupt for channel %d\n", (asserted ? "asserting" : "negating"), m_channel);
m_interrupt = asserted;
for (auto it = m_intrHooks.begin(); it != m_intrHooks.end(); it++) {
(*it)->OnChange(asserted);
}
m_irqHandler.HandleIRQ(m_irqNum, m_interrupt);
}
}

View file

@ -15,7 +15,7 @@
#include "vixen/cpu.h"
#include "../basic/irq.h"
#include "../basic/interrupt_trigger.h"
#include "../basic/interrupt.h"
#include "ata_device.h"
#include "ata_common.h"
@ -81,6 +81,7 @@ public:
bool AreInterruptsEnabled() { return m_regs.AreInterruptsEnabled(); }
InterruptTrigger& GetInterruptTrigger() { return m_intrTrigger; }
void RegisterInterruptHook(InterruptHook *hook) { m_intrHooks.push_back(hook); }
private:
friend class ATA;
@ -121,6 +122,8 @@ private:
IRQHandler& m_irqHandler;
uint8_t m_irqNum;
std::vector<InterruptHook *> m_intrHooks;
// ----- Command port operations ------------------------------------------
void ReadData(uint32_t *value, uint8_t size);

View file

@ -14,7 +14,7 @@
#include <cstdint>
#include "../ata/ata_defs.h"
#include "../basic/interrupt_trigger.h"
#include "../basic/interrupt.h"
#include "ata_common.h"
#include "drvs/ata_device_driver.h"
#include "drvs/drv_null.h"

View file

@ -138,7 +138,7 @@ void DMAProtocolCommand::ReadData(uint8_t *value, uint32_t size) {
if (m_currentByte >= m_endingByte) {
FinishTransfer();
m_regs.status &= ~(StBusy | StDataRequest);
// INTRQ will be asserted in BMIDEChannel::RunWorker()
m_interrupt.Assert();
}
}
@ -189,7 +189,7 @@ void DMAProtocolCommand::WriteData(uint8_t *value, uint32_t size) {
if (m_currentByte >= m_endingByte) {
FinishTransfer();
m_regs.status &= ~(StBusy | StDataRequest);
// INTRQ will be asserted in BMIDEChannel::RunWorker()
m_interrupt.Assert();
}
}

View file

@ -45,6 +45,8 @@ PacketProtocolCommand::PacketProtocolCommand(ATADevice& device)
: IATACommand(device)
, m_packetCmdBuffer(nullptr)
, m_packetDataBuffer(nullptr)
, m_packetDataPos(0)
, m_packetDataSize(0)
{
}

View file

@ -145,6 +145,8 @@ bool ImageDVDDriveATADeviceDriver::ProcessATAPIPacketDataRead(PacketInformation&
if (!m_transfer) {
m_currentByte = lba * kDVDSectorSize;
m_lastByte = m_currentByte + transferLength * kDVDSectorSize;
m_transfer = true;
log_spew("ImageDVDDriveATADeviceDriver::ProcessATAPIPacketDataRead: Starting transfer: 0x%llx to 0x%llx\n", m_currentByte, m_lastByte);
}
// TODO: maybe handle caching? Could improve performance if accessing real media on supported drives
@ -160,6 +162,7 @@ bool ImageDVDDriveATADeviceDriver::ProcessATAPIPacketDataRead(PacketInformation&
// Update position
m_currentByte += *packetDataSize;
if (m_currentByte >= m_lastByte || *packetDataSize < readLen) {
log_spew("ImageDVDDriveATADeviceDriver::ProcessATAPIPacketDataRead: Transfer finished\n");
m_transfer = false;
}
}

View file

@ -9,5 +9,10 @@ public:
virtual void Negate() = 0;
};
class InterruptHook {
public:
virtual void OnChange(bool asserted) = 0;
};
}
}

View file

@ -10,6 +10,8 @@
#include "vixen/thread.h"
namespace vixen {
namespace hw {
namespace bmide {
using namespace hw::bmide;
using namespace hw::ata;
@ -81,3 +83,5 @@ void BMIDEDevice::PCIIOWrite(int barIndex, uint32_t port, uint32_t value, uint8_
}
}
}
}

View file

@ -17,6 +17,8 @@
#include "vixen/hw/ata/ata.h"
namespace vixen {
namespace hw {
namespace bmide {
class BMIDEDevice : public PCIDevice {
public:
@ -35,3 +37,5 @@ private:
};
}
}
}

View file

@ -10,6 +10,8 @@
#include "vixen/thread.h"
namespace vixen {
namespace hw {
namespace bmide {
using namespace hw;
using namespace hw::bmide;
@ -20,7 +22,9 @@ BMIDEChannel::BMIDEChannel(Channel channel, ATAChannel& ataChannel, uint8_t *ram
, m_ataChannel(ataChannel)
, m_ram(ram)
, m_ramSize(ramSize)
, m_intrHook(IntrHook(*this))
{
ataChannel.RegisterInterruptHook(&m_intrHook);
m_worker_running = true;
m_job_running = false;
m_job_cancel = false;
@ -243,11 +247,6 @@ void BMIDEChannel::RunWorker() {
// Set Interrupt flag if the ATA device triggered an interrupt
if (result == DMATransferEnd) {
if (m_ataChannel.AreInterruptsEnabled()) {
m_status |= StInterrupt;
m_ataChannel.GetInterruptTrigger().Assert();
//log_spew("BM IDE channel %d: Interrupt asserted\n", m_channel);
}
//log_spew("BM IDE channel %d: Transfer ended\n", m_channel);
m_job_running = false;
}
@ -269,3 +268,5 @@ void BMIDEChannel::RunWorker() {
}
}
}
}

View file

@ -15,6 +15,8 @@
#include "vixen/hw/ata/ata.h"
namespace vixen {
namespace hw {
namespace bmide {
class BMIDEChannel {
public:
@ -61,6 +63,32 @@ private:
void StartWork();
void StopWork();
// ----- Interrupt hook ---------------------------------------------------
class IntrHook : public hw::InterruptHook {
private:
IntrHook(BMIDEChannel& channel)
: m_channel(channel)
{
}
void OnChange(bool asserted) override {
if (asserted) {
m_channel.m_status |= StInterrupt;
log_spew("BM IDE channel %d: Interrupt asserted\n", m_channel.m_channel);
}
}
BMIDEChannel& m_channel;
friend class BMIDEChannel;
};
friend class IntrHook;
IntrHook m_intrHook;
};
}
}
}

View file

@ -462,7 +462,7 @@ EmulatorStatus Xbox::InitHardware() {
m_NVAPU = new NVAPUDevice();
m_AC97 = new AC97Device();
m_PCIBridge = new PCIBridgeDevice();
m_BMIDE = new BMIDEDevice(m_ram, m_ramSize, *m_ATA);
m_BMIDE = new hw::bmide::BMIDEDevice(m_ram, m_ramSize, *m_ATA);
m_AGPBridge = new AGPBridgeDevice();
m_NV2A = new NV2ADevice(m_ram, m_ramSize, *m_i8259);

View file

@ -141,7 +141,7 @@ protected:
NVAPUDevice *m_NVAPU;
AC97Device *m_AC97;
PCIBridgeDevice *m_PCIBridge;
BMIDEDevice *m_BMIDE;
hw::bmide::BMIDEDevice *m_BMIDE;
AGPBridgeDevice *m_AGPBridge;
NV2ADevice *m_NV2A;