Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/mips/common/syscalls/syscalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ SOFTWARE.

struct JmpBuf;

#ifndef PS1_PC_PORT
static __attribute__((always_inline)) int enterCriticalSection() {
register int n asm("a0") = 1;
register int r asm("v0");
Expand Down Expand Up @@ -598,3 +599,4 @@ static __attribute__((always_inline)) int syscall_getDeviceStatus() {
__asm__ volatile("" : "=r"(n) : "r"(n));
return ((int (*)())0xc0)();
}
#endif // #ifdef PS1_PC_PORT
2 changes: 2 additions & 0 deletions src/mips/psyqo/advancedpad.hh
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,10 @@ class AdvancedPad {
};

void busyLoop(unsigned delay) {
#ifndef PS1_PC_PORT
unsigned cycles = 0;
while (++cycles < delay) asm("");
#endif
};

void configurePort(uint8_t port);
Expand Down
2 changes: 2 additions & 0 deletions src/mips/psyqo/fragment-concept.hh
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@ struct has_explicit_copy_constructor<

template <typename Frag>
concept Fragment = requires(Frag frag) {
#ifndef PS1_PC_PORT
{ (alignof(Frag) & 3) == 0 };
{ (sizeof(Frag) & 3) == 0 };
{ (sizeof(frag.head)) == 4 };
{ ((offsetof(Frag, head)) & 3) == 0 };
#endif
// Can't seem to make this work with variadic templated constructors
// { has_explicit_copy_constructor<Frag>() } -> std::convertible_to<std::true_type>;
{ frag.getActualFragmentSize() } -> std::convertible_to<size_t>;
Expand Down
55 changes: 45 additions & 10 deletions src/mips/psyqo/fragments.hh
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,50 @@ namespace Fragments {
* @tparam T The primitive type.
*/

struct ChainEntryPS1;
struct ChainEntryPC;

#ifdef PS1_PC_PORT
using ChainEntry = ChainEntryPC;
#else
using ChainEntry = ChainEntryPS1;
#endif

struct ChainEntryPC {
ChainEntry* next;
unsigned size;
void setEndMarker() {
next = nullptr;
size = 0;
}
void set(ChainEntry* next_, unsigned size_) {
next = next_;
size = size_;
}
};

struct ChainEntryPS1 {
uintptr_t head;
void setEndMarker() {
head = 0xffffff;
}
void set(ChainEntryPS1* next, unsigned size) {
head = (size << 24) | (next->head & 0xffffff);
}
};

template <Primitive Prim>
struct SimpleFragment {
struct SimpleFragment : ChainEntry {
constexpr size_t maxSize() const { return 1; }
template <typename... Args>
SimpleFragment(Args &&...args) : primitive(eastl::forward<Args>(args)...) {
#ifndef PS1_PC_PORT
static_assert(sizeof(*this) == (sizeof(uint32_t) + sizeof(Prim)), "Spurious padding in simple fragment");
#endif
}
explicit SimpleFragment(const SimpleFragment &) = default;
typedef Prim FragmentBaseType;
constexpr size_t getActualFragmentSize() const { return sizeof(Prim) / sizeof(uint32_t); }
uint32_t head;
Prim primitive;
};

Expand All @@ -89,18 +122,19 @@ struct SimpleFragment {
*/

template <Primitive Prim, size_t N>
struct FixedFragment {
struct FixedFragment : ChainEntry {
constexpr size_t maxSize() const { return N; }
FixedFragment() {
static_assert(sizeof(*this) == (sizeof(unsigned) + sizeof(uint32_t) + sizeof(Prim) * N),
#ifndef PS1_PC_PORT
static_assert(sizeof(*this) == (sizeof(unsigned) + sizeof(uintptr_t) + sizeof(Prim) * N),
"Spurious padding in fixed fragment");
#endif
}
explicit FixedFragment(const FixedFragment &) = default;
typedef Prim FragmentBaseType;
size_t getActualFragmentSize() const { return (sizeof(Prim) * count) / sizeof(uint32_t); }
unsigned count = N;
uint32_t head;
eastl::array<Prim, N> primitives;
unsigned count = N;
};

/**
Expand All @@ -118,19 +152,20 @@ struct FixedFragment {
*/

template <Primitive P, Primitive Prim, size_t N>
struct FixedFragmentWithPrologue {
struct FixedFragmentWithPrologue : ChainEntry {
constexpr size_t maxSize() const { return N; }
FixedFragmentWithPrologue() {
static_assert(sizeof(*this) == (sizeof(unsigned) + sizeof(uint32_t) + sizeof(P) + sizeof(Prim) * N),
#ifndef PS1_PC_PORT
static_assert(sizeof(*this) == (sizeof(unsigned) + sizeof(uintptr_t) + sizeof(P) + sizeof(Prim) * N),
"Spurious padding in fixed fragment");
#endif
}
explicit FixedFragmentWithPrologue(const FixedFragmentWithPrologue &) = default;
typedef Prim FragmentBaseType;
size_t getActualFragmentSize() const { return (sizeof(P) + sizeof(Prim) * count) / sizeof(uint32_t); }
unsigned count = N;
uint32_t head;
P prologue;
eastl::array<Prim, N> primitives;
unsigned count = N;
};

} // namespace Fragments
Expand Down
21 changes: 11 additions & 10 deletions src/mips/psyqo/gpu.hh
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ class GPU {
*/
template <Fragment Frag>
void sendFragment(const Frag &fragment) {
sendFragment(&fragment.head + 1, fragment.getActualFragmentSize());
sendFragment(reinterpret_cast<const uint32_t *>(&fragment.head + 1), fragment.getActualFragmentSize());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Find all .head references in gpu.hh templates that lack PS1_PC_PORT guards.

rg -n '\.head' src/mips/psyqo/gpu.hh | grep -v 'm_chainHead\|m_chainTail\|chainHead\|chainTail\|head ='

Repository: grumpycoders/pcsx-redux

Length of output: 603


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## gpu.hh outline\n'
ast-grep outline src/mips/psyqo/gpu.hh --view expanded || true

printf '\n## fragment-concept.hh outline\n'
ast-grep outline src/mips/psyqo/fragment-concept.hh --view expanded || true

printf '\n## ordering-table.hh outline\n'
ast-grep outline src/mips/psyqo/ordering-table.hh --view expanded || true

printf '\n## gpu.hh relevant ranges\n'
sed -n '200,380p' src/mips/psyqo/gpu.hh | cat -n

printf '\n## fragment-concept.hh relevant ranges\n'
sed -n '1,140p' src/mips/psyqo/fragment-concept.hh | cat -n

printf '\n## ordering-table.hh relevant ranges\n'
sed -n '1,220p' src/mips/psyqo/ordering-table.hh | cat -n

Repository: grumpycoders/pcsx-redux

Length of output: 20849


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## fragments.hh outline\n'
ast-grep outline src/mips/psyqo/fragments.hh --view expanded || true

printf '\n## fragments.hh relevant ranges\n'
sed -n '1,260p' src/mips/psyqo/fragments.hh | cat -n

printf '\n## search for ChainEntryPC / ChainEntryPS1\n'
rg -n 'ChainEntryPC|ChainEntryPS1|struct ChainEntry|class ChainEntry|using ChainEntry' src/mips/psyqo

Repository: grumpycoders/pcsx-redux

Length of output: 8340


Guard GPU fragment/chain templates for PS1_PC_PORT
sendFragment, chain(Frag&), chain(Frag1*, Frag2*), and chain(OrderingTable&) still dereference .head unconditionally. Fragments::ChainEntryPC has no head, so these templates won’t instantiate for PC-port fragments even though the Fragment concept already drops that requirement. Mirror the #ifdef PS1_PC_PORT split used in ordering-table.hh::insert(), or route the PC path through the ChainEntry abstraction.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mips/psyqo/gpu.hh` at line 231, Guard the GPU fragment and chain
templates, including sendFragment, chain(Frag&), chain(Frag1*, Frag2*), and
chain(OrderingTable&), so PS1_PC_PORT builds never access .head on
Fragments::ChainEntryPC. Mirror the conditional path used by
ordering-table.hh::insert(), or use the ChainEntry abstraction for PC-port
fragments while preserving the existing non-PC behavior.

}

/**
Expand All @@ -243,7 +243,8 @@ class GPU {
template <Fragment Frag>
void sendFragment(const Frag &fragment, eastl::function<void()> &&callback,
DMA::DmaCallback dmaCallback = DMA::FROM_MAIN_LOOP) {
sendFragment(&fragment.head + 1, fragment.getActualFragmentSize(), eastl::move(callback), dmaCallback);
sendFragment(reinterpret_cast<const uint32_t *>(&fragment.head + 1), fragment.getActualFragmentSize(),
eastl::move(callback), dmaCallback);
}

/**
Expand Down Expand Up @@ -354,8 +355,8 @@ class GPU {
*/
template <size_t N, Safe safety = Safe::Yes>
void chain(OrderingTable<N, safety> &table) {
chain(&table.m_table[N], &table.m_table[0], 0);
scheduleOTC(&table.m_table[N], N + 1);
chain(&table.m_table[N].head, &table.m_table[0].head, 0);
scheduleOTC(&table.m_table[N].head, N + 1);
}

/**
Expand Down Expand Up @@ -535,8 +536,8 @@ class GPU {
DMA::DmaCallback dmaCallback);
void scheduleNormalDMA(uintptr_t data, size_t count);
void scheduleChainedDMA(uintptr_t head);
void chain(uint32_t *first, uint32_t *last, size_t count);
void scheduleOTC(uint32_t *start, uint32_t count);
void chain(uintptr_t *first, uintptr_t *last, size_t count);
void scheduleOTC(uintptr_t *start, uint32_t count);
void checkOTCAndTriggerCallback();
void prepareForTakeover();

Expand All @@ -548,8 +549,8 @@ class GPU {
uint32_t m_frameCount = 0;
uint32_t m_previousFrameCount = 0;
unsigned m_parity = 0;
uint32_t *m_chainHead = nullptr;
uint32_t *m_chainTail = nullptr;
uintptr_t *m_chainHead = nullptr;
uintptr_t *m_chainTail = nullptr;
size_t m_chainTailCount = 0;
enum { CHAIN_IDLE, CHAIN_TRANSFERRING, CHAIN_TRANSFERRED } m_chainStatus = CHAIN_IDLE;
struct Timer {
Expand All @@ -562,11 +563,11 @@ class GPU {
};
eastl::fixed_list<Timer, 32> m_timers;
struct ScheduledOTC {
uint32_t *start;
uintptr_t *start;
uint32_t count;
};
eastl::fixed_list<ScheduledOTC, 32> m_OTCs[2];
uint32_t *m_chainNext = nullptr;
uintptr_t *m_chainNext = nullptr;

uint16_t m_lastHSyncCounter = 0;
bool m_interlaced = false;
Expand Down
Loading
Loading