mirror of https://github.com/PCSX2/pcsx2.git
Implement thread listing for IOP
Abstracts away threads behind a common interface for both EE and IOP
This commit is contained in:
parent
7198c6b8c6
commit
9420615317
|
@ -107,18 +107,15 @@ QVariant StackModel::headerData(int section, Qt::Orientation orientation, int ro
|
|||
|
||||
void StackModel::refreshData()
|
||||
{
|
||||
if (m_cpu.getCpuType() == BREAKPOINT_IOP)
|
||||
return;
|
||||
|
||||
// Hopefully in the near future we can get a stack frame for
|
||||
// each thread
|
||||
beginResetModel();
|
||||
for (const auto& thread : getEEThreads())
|
||||
for (const auto& thread : m_cpu.GetThreadList())
|
||||
{
|
||||
if (thread.data.status == THS_RUN)
|
||||
if (thread->Status() == ThreadStatus::THS_RUN)
|
||||
{
|
||||
m_stackFrames = MipsStackWalk::Walk(&m_cpu, m_cpu.getPC(), m_cpu.getRegister(0, 31), m_cpu.getRegister(0, 29),
|
||||
thread.data.entry_init, thread.data.stack);
|
||||
thread->EntryPoint(), thread->StackTop());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,10 +27,7 @@ ThreadModel::ThreadModel(DebugInterface& cpu, QObject* parent)
|
|||
|
||||
int ThreadModel::rowCount(const QModelIndex&) const
|
||||
{
|
||||
if (m_cpu.getCpuType() == BREAKPOINT_EE)
|
||||
return getEEThreads().size();
|
||||
else
|
||||
return 0;
|
||||
return m_cpu.GetThreadList().size();
|
||||
}
|
||||
|
||||
int ThreadModel::columnCount(const QModelIndex&) const
|
||||
|
@ -40,66 +37,65 @@ int ThreadModel::columnCount(const QModelIndex&) const
|
|||
|
||||
QVariant ThreadModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
const auto threads = m_cpu.GetThreadList();
|
||||
auto* const thread = threads.at(index.row()).get();
|
||||
|
||||
if (role == Qt::DisplayRole)
|
||||
{
|
||||
const auto thread = getEEThreads().at(index.row());
|
||||
|
||||
switch (index.column())
|
||||
{
|
||||
case ThreadModel::ID:
|
||||
return thread.tid;
|
||||
return thread->TID();
|
||||
case ThreadModel::PC:
|
||||
{
|
||||
if (thread.data.status == THS_RUN)
|
||||
if (thread->Status() == ThreadStatus::THS_RUN)
|
||||
return QtUtils::FilledQStringFromValue(m_cpu.getPC(), 16);
|
||||
else
|
||||
return QtUtils::FilledQStringFromValue(thread.data.entry, 16);
|
||||
|
||||
return QtUtils::FilledQStringFromValue(thread->PC(), 16);
|
||||
}
|
||||
case ThreadModel::ENTRY:
|
||||
return QtUtils::FilledQStringFromValue(thread.data.entry_init, 16);
|
||||
return QtUtils::FilledQStringFromValue(thread->EntryPoint(), 16);
|
||||
case ThreadModel::PRIORITY:
|
||||
return QString::number(thread.data.currentPriority);
|
||||
return QString::number(thread->Priority());
|
||||
case ThreadModel::STATE:
|
||||
{
|
||||
const auto& state = ThreadStateStrings.find(thread.data.status);
|
||||
const auto& state = ThreadStateStrings.find(thread->Status());
|
||||
if (state != ThreadStateStrings.end())
|
||||
return state->second;
|
||||
else
|
||||
return tr("INVALID");
|
||||
|
||||
return tr("INVALID");
|
||||
}
|
||||
case ThreadModel::WAIT_TYPE:
|
||||
{
|
||||
const auto& waitType = ThreadWaitStrings.find(thread.data.waitType);
|
||||
const auto& waitType = ThreadWaitStrings.find(thread->Wait());
|
||||
if (waitType != ThreadWaitStrings.end())
|
||||
return waitType->second;
|
||||
else
|
||||
return tr("INVALID");
|
||||
|
||||
return tr("INVALID");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (role == Qt::UserRole)
|
||||
{
|
||||
const auto thread = getEEThreads().at(index.row());
|
||||
|
||||
switch (index.column())
|
||||
{
|
||||
case ThreadModel::ID:
|
||||
return thread.tid;
|
||||
return thread->TID();
|
||||
case ThreadModel::PC:
|
||||
{
|
||||
if (thread.data.status == THS_RUN)
|
||||
if (thread->Status() == ThreadStatus::THS_RUN)
|
||||
return m_cpu.getPC();
|
||||
else
|
||||
return thread.data.entry;
|
||||
|
||||
return thread->PC();
|
||||
}
|
||||
case ThreadModel::ENTRY:
|
||||
return thread.data.entry_init;
|
||||
return thread->EntryPoint();
|
||||
case ThreadModel::PRIORITY:
|
||||
return thread.data.currentPriority;
|
||||
return thread->Priority();
|
||||
case ThreadModel::STATE:
|
||||
return thread.data.status;
|
||||
return static_cast<u32>(thread->Status());
|
||||
case ThreadModel::WAIT_TYPE:
|
||||
return thread.data.waitType;
|
||||
return static_cast<u32>(thread->Wait());
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
|
|
|
@ -48,19 +48,27 @@ public:
|
|||
void refreshData();
|
||||
|
||||
private:
|
||||
const std::map<int, QString> ThreadStateStrings{
|
||||
{THS_BAD, tr("BAD")},
|
||||
{THS_RUN, tr("RUN")},
|
||||
{THS_READY, tr("READY")},
|
||||
{THS_WAIT, tr("WAIT")},
|
||||
{THS_SUSPEND, tr("SUSPEND")},
|
||||
{THS_WAIT_SUSPEND, tr("WAIT SUSPEND")},
|
||||
{THS_DORMANT, tr("DORMANT")}};
|
||||
const std::map<ThreadStatus, QString> ThreadStateStrings{
|
||||
{ThreadStatus::THS_BAD, tr("BAD")},
|
||||
{ThreadStatus::THS_RUN, tr("RUN")},
|
||||
{ThreadStatus::THS_READY, tr("READY")},
|
||||
{ThreadStatus::THS_WAIT, tr("WAIT")},
|
||||
{ThreadStatus::THS_SUSPEND, tr("SUSPEND")},
|
||||
{ThreadStatus::THS_WAIT_SUSPEND, tr("WAIT SUSPEND")},
|
||||
{ThreadStatus::THS_DORMANT, tr("DORMANT")},
|
||||
};
|
||||
|
||||
const std::map<int, QString> ThreadWaitStrings{
|
||||
{WAIT_NONE, tr("NONE")},
|
||||
{WAIT_WAKEUP_REQ, tr("WAKEUP REQUEST")},
|
||||
{WAIT_SEMA, tr("SEMAPHORE")}};
|
||||
const std::map<WaitState, QString> ThreadWaitStrings{
|
||||
{WaitState::NONE, tr("NONE")},
|
||||
{WaitState::WAKEUP_REQ, tr("WAKEUP REQUEST")},
|
||||
{WaitState::SEMA, tr("SEMAPHORE")},
|
||||
{WaitState::SLEEP, tr("SLEEP")},
|
||||
{WaitState::DELAY, tr("DELAY")},
|
||||
{WaitState::EVENTFLAG, tr("EVENTFLAG")},
|
||||
{WaitState::MBOX, tr("MBOX")},
|
||||
{WaitState::VPOOL, tr("VPOOL")},
|
||||
{WaitState::FIXPOOL, tr("FIXPOOL")},
|
||||
};
|
||||
|
||||
DebugInterface& m_cpu;
|
||||
};
|
||||
|
|
|
@ -15,29 +15,70 @@
|
|||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "BiosDebugData.h"
|
||||
#include "IopMem.h"
|
||||
#include "Memory.h"
|
||||
|
||||
std::vector<EEThread> getEEThreads()
|
||||
{
|
||||
std::vector<EEThread> threads;
|
||||
|
||||
if (CurrentBiosInformation.threadListAddr <= 0)
|
||||
std::vector<std::unique_ptr<BiosThread>> getEEThreads()
|
||||
{
|
||||
std::vector<std::unique_ptr<BiosThread>> threads;
|
||||
|
||||
if (CurrentBiosInformation.eeThreadListAddr <= 0)
|
||||
return threads;
|
||||
|
||||
const u32 start = CurrentBiosInformation.threadListAddr & 0x3fffff;
|
||||
const u32 start = CurrentBiosInformation.eeThreadListAddr & 0x3fffff;
|
||||
|
||||
for (int tid = 0; tid < 256; tid++)
|
||||
{
|
||||
EEThread thread;
|
||||
|
||||
EEInternalThread* internal = static_cast<EEInternalThread*>(PSM(start + tid * sizeof(EEInternalThread)));
|
||||
if (internal->status != THS_BAD)
|
||||
if (internal->status != (int)ThreadStatus::THS_BAD)
|
||||
{
|
||||
thread.tid = tid;
|
||||
thread.data = *internal;
|
||||
threads.push_back(thread);
|
||||
auto thread = std::make_unique<EEThread>(tid, *internal);
|
||||
threads.push_back(std::move(thread));
|
||||
}
|
||||
}
|
||||
|
||||
return threads;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<BiosThread>> getIOPThreads()
|
||||
{
|
||||
std::vector<std::unique_ptr<BiosThread>> threads;
|
||||
|
||||
if (CurrentBiosInformation.iopThreadListAddr <= 0)
|
||||
return threads;
|
||||
|
||||
u32 item = iopMemRead32(CurrentBiosInformation.iopThreadListAddr);
|
||||
|
||||
while (item != 0)
|
||||
{
|
||||
IOPInternalThread data{};
|
||||
|
||||
u16 tag = iopMemRead16(item + 0x8);
|
||||
if (tag != 0x7f01)
|
||||
{
|
||||
// something went wrong
|
||||
return {};
|
||||
}
|
||||
|
||||
data.stackTop = iopMemRead32(item + 0x3c);
|
||||
data.status = iopMemRead8(item + 0xc);
|
||||
data.tid = iopMemRead16(item + 0xa);
|
||||
data.entrypoint = iopMemRead32(item + 0x38);
|
||||
data.waitstate = iopMemRead16(item + 0xe);
|
||||
data.initPriority = iopMemRead16(item + 0x2e);
|
||||
|
||||
data.SavedSP = iopMemRead32(item + 0x10);
|
||||
|
||||
data.PC = iopMemRead32(data.SavedSP + 0x8c);
|
||||
|
||||
auto thread = std::make_unique<IOPThread>(data);
|
||||
threads.push_back(std::move(thread));
|
||||
|
||||
item = iopMemRead32(item + 0x24);
|
||||
}
|
||||
|
||||
|
||||
return threads;
|
||||
}
|
||||
|
|
|
@ -15,9 +15,24 @@
|
|||
|
||||
#pragma once
|
||||
#include "common/Pcsx2Types.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "ps2/BiosTools.h"
|
||||
|
||||
struct EEInternalThread { // internal struct
|
||||
enum class ThreadStatus
|
||||
{
|
||||
THS_BAD = 0x00,
|
||||
THS_RUN = 0x01,
|
||||
THS_READY = 0x02,
|
||||
THS_WAIT = 0x04,
|
||||
THS_SUSPEND = 0x08,
|
||||
THS_WAIT_SUSPEND = 0x0C,
|
||||
THS_DORMANT = 0x10,
|
||||
};
|
||||
|
||||
|
||||
struct EEInternalThread
|
||||
{ // internal struct
|
||||
u32 prev;
|
||||
u32 next;
|
||||
int status;
|
||||
|
@ -40,26 +55,139 @@ struct EEInternalThread { // internal struct
|
|||
u32 heap_base;
|
||||
};
|
||||
|
||||
enum {
|
||||
THS_BAD = 0x00,
|
||||
THS_RUN = 0x01,
|
||||
THS_READY = 0x02,
|
||||
THS_WAIT = 0x04,
|
||||
THS_SUSPEND = 0x08,
|
||||
THS_WAIT_SUSPEND = 0x0C,
|
||||
THS_DORMANT = 0x10,
|
||||
};
|
||||
|
||||
enum {
|
||||
WAIT_NONE = 0,
|
||||
WAIT_WAKEUP_REQ = 1,
|
||||
WAIT_SEMA = 2,
|
||||
};
|
||||
|
||||
struct EEThread
|
||||
// Not the full struct, just what we care about
|
||||
struct IOPInternalThread
|
||||
{
|
||||
int tid;
|
||||
u32 tid;
|
||||
u32 PC;
|
||||
u32 stackTop;
|
||||
u32 SavedSP;
|
||||
u32 status;
|
||||
u32 entrypoint;
|
||||
u32 waitstate;
|
||||
u32 initPriority;
|
||||
};
|
||||
|
||||
enum class IOPWaitStatus
|
||||
{
|
||||
TSW_SLEEP = 1,
|
||||
TSW_DELAY = 2,
|
||||
TSW_SEMA = 3,
|
||||
TSW_EVENTFLAG = 4,
|
||||
TSW_MBX = 5,
|
||||
TSW_VPL = 6,
|
||||
TSW_FPL = 7,
|
||||
};
|
||||
|
||||
enum class EEWaitStatus
|
||||
{
|
||||
WAIT_NONE = 0,
|
||||
WAIT_WAKEUP_REQ = 1,
|
||||
WAIT_SEMA = 2,
|
||||
};
|
||||
|
||||
enum class WaitState
|
||||
{
|
||||
NONE,
|
||||
WAKEUP_REQ,
|
||||
SEMA,
|
||||
SLEEP,
|
||||
DELAY,
|
||||
EVENTFLAG,
|
||||
MBOX,
|
||||
VPOOL,
|
||||
FIXPOOL,
|
||||
};
|
||||
|
||||
class BiosThread
|
||||
{
|
||||
public:
|
||||
virtual ~BiosThread() = default;
|
||||
[[nodiscard]] virtual u32 TID() const = 0;
|
||||
[[nodiscard]] virtual u32 PC() const = 0;
|
||||
[[nodiscard]] virtual ThreadStatus Status() const = 0;
|
||||
[[nodiscard]] virtual WaitState Wait() const = 0;
|
||||
[[nodiscard]] virtual u32 EntryPoint() const = 0;
|
||||
[[nodiscard]] virtual u32 StackTop() const = 0;
|
||||
[[nodiscard]] virtual u32 Priority() const = 0;
|
||||
};
|
||||
|
||||
class EEThread : public BiosThread
|
||||
{
|
||||
public:
|
||||
EEThread(int tid, EEInternalThread th)
|
||||
: tid(tid)
|
||||
, data(th)
|
||||
{
|
||||
}
|
||||
~EEThread() override = default;
|
||||
|
||||
[[nodiscard]] u32 TID() const override { return tid; };
|
||||
[[nodiscard]] u32 PC() const override { return data.entry; };
|
||||
[[nodiscard]] ThreadStatus Status() const override { return static_cast<ThreadStatus>(data.status); };
|
||||
[[nodiscard]] WaitState Wait() const override
|
||||
{
|
||||
auto wait = static_cast<EEWaitStatus>(data.waitType);
|
||||
switch (wait)
|
||||
{
|
||||
case EEWaitStatus::WAIT_NONE:
|
||||
return WaitState::NONE;
|
||||
case EEWaitStatus::WAIT_WAKEUP_REQ:
|
||||
return WaitState::WAKEUP_REQ;
|
||||
case EEWaitStatus::WAIT_SEMA:
|
||||
return WaitState::SEMA;
|
||||
}
|
||||
};
|
||||
[[nodiscard]] u32 EntryPoint() const override { return data.entry_init; };
|
||||
[[nodiscard]] u32 StackTop() const override { return data.stack; };
|
||||
[[nodiscard]] u32 Priority() const override { return data.currentPriority; };
|
||||
|
||||
private:
|
||||
u32 tid;
|
||||
EEInternalThread data;
|
||||
};
|
||||
|
||||
std::vector<EEThread> getEEThreads();
|
||||
class IOPThread : public BiosThread
|
||||
{
|
||||
public:
|
||||
IOPThread(IOPInternalThread th)
|
||||
: data(th)
|
||||
{
|
||||
}
|
||||
~IOPThread() override = default;
|
||||
|
||||
[[nodiscard]] u32 TID() const override { return data.tid; };
|
||||
[[nodiscard]] u32 PC() const override { return data.PC; };
|
||||
[[nodiscard]] ThreadStatus Status() const override { return static_cast<ThreadStatus>(data.status); };
|
||||
[[nodiscard]] WaitState Wait() const override
|
||||
{
|
||||
auto wait = static_cast<IOPWaitStatus>(data.waitstate);
|
||||
switch (wait)
|
||||
{
|
||||
case IOPWaitStatus::TSW_DELAY:
|
||||
return WaitState::DELAY;
|
||||
case IOPWaitStatus::TSW_EVENTFLAG:
|
||||
return WaitState::EVENTFLAG;
|
||||
case IOPWaitStatus::TSW_SLEEP:
|
||||
return WaitState::SLEEP;
|
||||
case IOPWaitStatus::TSW_SEMA:
|
||||
return WaitState::SEMA;
|
||||
case IOPWaitStatus::TSW_MBX:
|
||||
return WaitState::MBOX;
|
||||
case IOPWaitStatus::TSW_VPL:
|
||||
return WaitState::VPOOL;
|
||||
case IOPWaitStatus::TSW_FPL:
|
||||
return WaitState::FIXPOOL;
|
||||
}
|
||||
return WaitState::NONE;
|
||||
};
|
||||
[[nodiscard]] u32 EntryPoint() const override { return data.entrypoint; };
|
||||
[[nodiscard]] u32 StackTop() const override { return data.stackTop; };
|
||||
[[nodiscard]] u32 Priority() const override { return data.initPriority; };
|
||||
|
||||
private:
|
||||
IOPInternalThread data;
|
||||
};
|
||||
|
||||
std::vector<std::unique_ptr<BiosThread>> getIOPThreads();
|
||||
std::vector<std::unique_ptr<BiosThread>> getEEThreads();
|
||||
|
|
|
@ -728,6 +728,12 @@ SymbolMap& R5900DebugInterface::GetSymbolMap() const
|
|||
return R5900SymbolMap;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<BiosThread>> R5900DebugInterface::GetThreadList() const
|
||||
{
|
||||
return getEEThreads();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// R3000DebugInterface
|
||||
//
|
||||
|
@ -1000,3 +1006,8 @@ SymbolMap& R3000DebugInterface::GetSymbolMap() const
|
|||
{
|
||||
return R3000SymbolMap;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<BiosThread>> R3000DebugInterface::GetThreadList() const
|
||||
{
|
||||
return getIOPThreads();
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
*/
|
||||
|
||||
#pragma once
|
||||
#include "DebugTools/BiosDebugData.h"
|
||||
#include "MemoryTypes.h"
|
||||
#include "ExpressionParser.h"
|
||||
#include "SymbolMap.h"
|
||||
|
@ -84,6 +85,7 @@ public:
|
|||
virtual u32 getCycles() = 0;
|
||||
virtual BreakPointCpu getCpuType() = 0;
|
||||
[[nodiscard]] virtual SymbolMap& GetSymbolMap() const = 0;
|
||||
[[nodiscard]] virtual std::vector<std::unique_ptr<BiosThread>> GetThreadList() const = 0;
|
||||
|
||||
bool initExpression(const char* exp, PostfixExpression& dest);
|
||||
bool parseExpression(PostfixExpression& exp, u64& dest);
|
||||
|
@ -130,6 +132,7 @@ public:
|
|||
void setPc(u32 newPc) override;
|
||||
void setRegister(int cat, int num, u128 newValue) override;
|
||||
[[nodiscard]] SymbolMap& GetSymbolMap() const override;
|
||||
[[nodiscard]] std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
|
||||
|
||||
std::string disasm(u32 address, bool simplify) override;
|
||||
bool isValidAddress(u32 address) override;
|
||||
|
@ -168,6 +171,7 @@ public:
|
|||
void setPc(u32 newPc) override;
|
||||
void setRegister(int cat, int num, u128 newValue) override;
|
||||
[[nodiscard]] SymbolMap& GetSymbolMap() const override;
|
||||
[[nodiscard]] std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
|
||||
|
||||
std::string disasm(u32 address, bool simplify) override;
|
||||
bool isValidAddress(u32 address) override;
|
||||
|
|
|
@ -20,8 +20,12 @@
|
|||
#include "R5900.h" // for g_GameStarted
|
||||
#include "IopBios.h"
|
||||
#include "IopMem.h"
|
||||
#include "iR3000A.h"
|
||||
#include "ps2/BiosTools.h"
|
||||
#include "DebugTools/SymbolMap.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <fmt/format.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include "common/FileSystem.h"
|
||||
|
@ -983,6 +987,37 @@ namespace R3000A
|
|||
|
||||
namespace loadcore
|
||||
{
|
||||
|
||||
// Gets the thread list ptr from thbase
|
||||
u32 GetThreadList(u32 a0reg, u32 version)
|
||||
{
|
||||
// Function 3 returns the main thread manager struct
|
||||
u32 function = iopMemRead32(a0reg + 0x20);
|
||||
|
||||
// read the lui
|
||||
u32 thstruct = (iopMemRead32(function) & 0xFFFF) << 16;
|
||||
thstruct |= iopMemRead32(function + 4) & 0xFFFF;
|
||||
|
||||
u32 list = thstruct + 0x42c;
|
||||
|
||||
if (version > 0x101)
|
||||
list = thstruct + 0x430;
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
int RegisterLibraryEntries_HLE()
|
||||
{
|
||||
const std::string modname = iopMemReadString(a0 + 12);
|
||||
if (modname == "thbase")
|
||||
{
|
||||
const u32 version = iopMemRead32(a0 + 8);
|
||||
CurrentBiosInformation.iopThreadListAddr = GetThreadList(a0, version);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RegisterLibraryEntries_DEBUG()
|
||||
{
|
||||
const std::string modname = iopMemReadString(a0 + 12);
|
||||
|
@ -1093,6 +1128,11 @@ namespace R3000A
|
|||
EXPORT_H( 14, Kprintf)
|
||||
END_MODULE
|
||||
|
||||
// For grabbing the thread list from thbase
|
||||
MODULE(loadcore)
|
||||
EXPORT_H( 6, RegisterLibraryEntries)
|
||||
END_MODULE
|
||||
|
||||
// Special case with ioman and iomanX
|
||||
// They are mostly compatible excluding stat structures
|
||||
if(libname == "ioman" || libname == "iomanx")
|
||||
|
|
|
@ -1012,7 +1012,7 @@ void SYSCALL()
|
|||
case Syscall::StartThread:
|
||||
case Syscall::ChangeThreadPriority:
|
||||
{
|
||||
if (CurrentBiosInformation.threadListAddr == 0)
|
||||
if (CurrentBiosInformation.eeThreadListAddr == 0)
|
||||
{
|
||||
u32 offset = 0x0;
|
||||
// Suprisingly not that slow :)
|
||||
|
@ -1030,16 +1030,16 @@ void SYSCALL()
|
|||
// We've found the instruction pattern!
|
||||
// We (well, I) know that the thread address is always 0x8001 + the immediate of the 6th instruction from here
|
||||
const u32 op = memRead32(0x80000000 + offset + (sizeof(u32) * 6));
|
||||
CurrentBiosInformation.threadListAddr = 0x80010000 + static_cast<u16>(op) - 8; // Subtract 8 because the address here is offset by 8.
|
||||
DevCon.WriteLn("BIOS: Successfully found the instruction pattern. Assuming the thread list is here: %0x", CurrentBiosInformation.threadListAddr);
|
||||
CurrentBiosInformation.eeThreadListAddr = 0x80010000 + static_cast<u16>(op) - 8; // Subtract 8 because the address here is offset by 8.
|
||||
DevCon.WriteLn("BIOS: Successfully found the instruction pattern. Assuming the thread list is here: %0x", CurrentBiosInformation.eeThreadListAddr);
|
||||
break;
|
||||
}
|
||||
offset += 4;
|
||||
}
|
||||
if (!CurrentBiosInformation.threadListAddr)
|
||||
if (!CurrentBiosInformation.eeThreadListAddr)
|
||||
{
|
||||
// We couldn't find the address
|
||||
CurrentBiosInformation.threadListAddr = -1;
|
||||
CurrentBiosInformation.eeThreadListAddr = -1;
|
||||
// If you're here because a user has reported this message, this means that the instruction pattern is not present on their bios, or it is aligned weirdly.
|
||||
Console.Warning("BIOS Warning: Unable to get a thread list offset. The debugger thread and stack frame views will not be functional.");
|
||||
}
|
||||
|
|
|
@ -311,7 +311,7 @@ bool LoadBIOS()
|
|||
if (EmuConfig.CurrentIRX.length() > 3)
|
||||
LoadIrx(EmuConfig.CurrentIRX, &eeMem->ROM[0x3C0000], sizeof(eeMem->ROM) - 0x3C0000);
|
||||
|
||||
CurrentBiosInformation.threadListAddr = 0;
|
||||
CurrentBiosInformation.eeThreadListAddr = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,9 @@ const u32 ThreadListInstructions[3] =
|
|||
|
||||
struct BiosDebugInformation
|
||||
{
|
||||
u32 threadListAddr;
|
||||
u32 eeThreadListAddr;
|
||||
u32 iopThreadListAddr;
|
||||
u32 iopModListAddr;
|
||||
};
|
||||
|
||||
extern BiosDebugInformation CurrentBiosInformation;
|
||||
|
|
Loading…
Reference in New Issue