DSPHLE MailHandler: Synchronize reads and interrupts

This change is meant to solve the following problem: how to translate the
following snippet to DSPHLE:
    SendInterruptAndWaitRead(MAIL_A);
    SendAndWaitRead(MAIL_B);

    SendInterruptAndWaitRead(MAIL_C);

This should cause the following actions on the CPU side:
    ---> Woken up by interrupt
    Reads MAIL_A
    Reads MAIL_B
    <--- Exits interrupt handler

    ---> Woken up by interrupt
    Reads MAIL_C
    <---

But with the current DSPHLE mail support, the following would happen because
the "AndWaitRead" part is not supported:
    ---> Woken up by interrupt
    Reads MAIL_A
    Reads MAIL_B
    <--- Exits interrupt handler

    [Never gets the second interrupt since it was triggered at the same time as
    the first one! Misses MAIL_C.]

This changes fixes the issue by storing two values in the mail queue on the DSP
side: the value of the mail itself, and whether a read of that mail should
trigger a DSP interrupt. If nothing is in the queue yet and an interrupt is
requested, just trigger the interrupt. In the present example, the queue will
look like this:
    Mail value       Interrupt requested
     MAIL_A                  No           <-- Interrupt was triggered when
                                              pushing the mail to the queue.
     MAIL_B                  Yes
     MAIL_C                  No

When the CPU will read MAIL_B, this will cause MailHandler to trigger the
interrupt, which will be handled by the CPU when coming back from the exception
handler. MAIL_C is then successfully read.
This commit is contained in:
Pierre Bourdon 2014-11-20 05:02:09 +01:00
parent 8f3302419b
commit a770cc0778
2 changed files with 39 additions and 26 deletions

View File

@ -3,6 +3,7 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "Common/ChunkFile.h" #include "Common/ChunkFile.h"
#include "Core/HW/DSP.h"
#include "Core/HW/DSPHLE/MailHandler.h" #include "Core/HW/DSPHLE/MailHandler.h"
CMailHandler::CMailHandler() CMailHandler::CMailHandler()
@ -14,9 +15,20 @@ CMailHandler::~CMailHandler()
Clear(); Clear();
} }
void CMailHandler::PushMail(u32 _Mail) void CMailHandler::PushMail(u32 _Mail, bool interrupt)
{ {
m_Mails.push(_Mail); if (interrupt)
{
if (m_Mails.empty())
{
DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP);
}
else
{
m_Mails.front().second = true;
}
}
m_Mails.emplace(_Mail, false);
DEBUG_LOG(DSP_MAIL, "DSP writes 0x%08x", _Mail); DEBUG_LOG(DSP_MAIL, "DSP writes 0x%08x", _Mail);
} }
@ -25,7 +37,7 @@ u16 CMailHandler::ReadDSPMailboxHigh()
// check if we have a mail for the core // check if we have a mail for the core
if (!m_Mails.empty()) if (!m_Mails.empty())
{ {
u16 result = (m_Mails.front() >> 16) & 0xFFFF; u16 result = (m_Mails.front().first >> 16) & 0xFFFF;
return result; return result;
} }
return 0x00; return 0x00;
@ -36,8 +48,15 @@ u16 CMailHandler::ReadDSPMailboxLow()
// check if we have a mail for the core // check if we have a mail for the core
if (!m_Mails.empty()) if (!m_Mails.empty())
{ {
u16 result = m_Mails.front() & 0xFFFF; u16 result = m_Mails.front().first & 0xFFFF;
bool generate_interrupt = m_Mails.front().second;
m_Mails.pop(); m_Mails.pop();
if (generate_interrupt)
{
DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP);
}
return result; return result;
} }
return 0x00; return 0x00;
@ -59,7 +78,7 @@ void CMailHandler::Halt(bool _Halt)
if (_Halt) if (_Halt)
{ {
Clear(); Clear();
m_Mails.push(0x80544348); PushMail(0x80544348);
} }
} }
@ -73,21 +92,25 @@ void CMailHandler::DoState(PointerWrap &p)
for (int i = 0; i < sz; i++) for (int i = 0; i < sz; i++)
{ {
u32 mail = 0; u32 mail = 0;
bool interrupt = false;
p.Do(mail); p.Do(mail);
m_Mails.push(mail); p.Do(interrupt);
m_Mails.emplace(mail, interrupt);
} }
} }
else // WRITE and MEASURE else // WRITE and MEASURE
{ {
std::queue<u32> temp; std::queue<std::pair<u32, bool>> temp;
int sz = (int)m_Mails.size(); int sz = (int)m_Mails.size();
p.Do(sz); p.Do(sz);
for (int i = 0; i < sz; i++) for (int i = 0; i < sz; i++)
{ {
u32 value = m_Mails.front(); u32 value = m_Mails.front().first;
bool interrupt = m_Mails.front().second;
m_Mails.pop(); m_Mails.pop();
p.Do(value); p.Do(value);
temp.push(value); p.Do(interrupt);
temp.emplace(value, interrupt);
} }
if (!m_Mails.empty()) if (!m_Mails.empty())
PanicAlert("CMailHandler::DoState - WTF?"); PanicAlert("CMailHandler::DoState - WTF?");
@ -95,9 +118,10 @@ void CMailHandler::DoState(PointerWrap &p)
// Restore queue. // Restore queue.
for (int i = 0; i < sz; i++) for (int i = 0; i < sz; i++)
{ {
u32 value = temp.front(); u32 value = temp.front().first;
bool interrupt = temp.front().second;
temp.pop(); temp.pop();
m_Mails.push(value); m_Mails.emplace(value, interrupt);
} }
} }
} }

View File

@ -5,6 +5,8 @@
#pragma once #pragma once
#include <queue> #include <queue>
#include <utility>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
class PointerWrap; class PointerWrap;
@ -15,7 +17,7 @@ public:
CMailHandler(); CMailHandler();
~CMailHandler(); ~CMailHandler();
void PushMail(u32 _Mail); void PushMail(u32 _Mail, bool interrupt = false);
void Clear(); void Clear();
void Halt(bool _Halt); void Halt(bool _Halt);
void DoState(PointerWrap &p); void DoState(PointerWrap &p);
@ -24,20 +26,7 @@ public:
u16 ReadDSPMailboxHigh(); u16 ReadDSPMailboxHigh();
u16 ReadDSPMailboxLow(); u16 ReadDSPMailboxLow();
u32 GetNextMail() const
{
if (!m_Mails.empty())
{
return m_Mails.front();
}
else
{
// WARN_LOG(DSPHLE, "GetNextMail: No mails");
return 0;
}
}
private: private:
// mail handler // mail handler
std::queue<u32> m_Mails; std::queue<std::pair<u32, bool>> m_Mails;
}; };