From 2d31dd13bced728bab0e841e708a29f13d1ef227 Mon Sep 17 00:00:00 2001 From: Nicolas 'Pixel' Noble Date: Wed, 8 Jul 2026 13:31:01 -0700 Subject: [PATCH 1/2] psyqo: gate non-portable assembly behind PS1_PC_PORT The GTE kernels/registers, the BIOS syscall wrappers, the cop0 helpers in kernel.hh, and the AdvancedPad busy loop are thin wrappers around MIPS instructions that only assemble for the target. Guard them behind PS1_PC_PORT so psyqo can be built on the host as part of a PC port: with the macro defined those paths compile out (the GTE register readers return 0), and on a normal PS1 build the macro is undefined and codegen is unchanged. Co-authored-by: Elias Daler Signed-off-by: Nicolas 'Pixel' Noble --- src/mips/common/syscalls/syscalls.h | 2 + src/mips/psyqo/advancedpad.hh | 2 + src/mips/psyqo/gte-kernels.hh | 112 +++++++++++++++++++++++----- src/mips/psyqo/gte-registers.hh | 24 ++++++ src/mips/psyqo/kernel.hh | 22 +++++- 5 files changed, 142 insertions(+), 20 deletions(-) diff --git a/src/mips/common/syscalls/syscalls.h b/src/mips/common/syscalls/syscalls.h index f2dc1ebc8..fac71dc8a 100644 --- a/src/mips/common/syscalls/syscalls.h +++ b/src/mips/common/syscalls/syscalls.h @@ -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"); @@ -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 diff --git a/src/mips/psyqo/advancedpad.hh b/src/mips/psyqo/advancedpad.hh index 29212ae7b..d443e3810 100644 --- a/src/mips/psyqo/advancedpad.hh +++ b/src/mips/psyqo/advancedpad.hh @@ -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); diff --git a/src/mips/psyqo/gte-kernels.hh b/src/mips/psyqo/gte-kernels.hh index 95eaee1c5..16662baba 100644 --- a/src/mips/psyqo/gte-kernels.hh +++ b/src/mips/psyqo/gte-kernels.hh @@ -55,49 +55,75 @@ enum LM : unsigned { Unlimited, Limited }; // RTPS - Perspective Transformation (single) // pers(([rt]·[v0]) >> 12 + [tr]) -> sxy2 // 14 cycles -static inline void rtps() { asm volatile("cop2 0x0180001"); } +static inline void rtps() { +#ifndef PS1_PC_PORT + asm volatile("cop2 0x0180001"); +#endif +} // RTPT - Perspective Transformation (triple) // pers(([rt]·[v0]) >> 12 + [tr]) -> sxy0 // pers(([rt]·[v1]) >> 12 + [tr]) -> sxy1 // pers(([rt]·[v2]) >> 12 + [tr]) -> sxy2 // 22 cycles -static inline void rtpt() { asm volatile("cop2 0x0280030"); } +static inline void rtpt() { +#ifndef PS1_PC_PORT + asm volatile("cop2 0x0280030"); +#endif +} // Depth Queuing // DCPL - Depth Cue Color light // (1 - dp)·[rgb·sv] + dp·[fc] -> rgb, lv, sv // 8 cycles -static inline void dpcl() { asm volatile("cop2 0x0680029"); } +static inline void dpcl() { +#ifndef PS1_PC_PORT + asm volatile("cop2 0x0680029"); +#endif +} // DPCS - Depth Cueing (single) // (1 - dp)·[rgb] + dp·[fc] -> rgb, lv, sv // 8 cycles -static inline void dpcs() { asm volatile("cop2 0x0780010"); } +static inline void dpcs() { +#ifndef PS1_PC_PORT + asm volatile("cop2 0x0780010"); +#endif +} // DPCT - Depth Cueing (triple) // (1 - dp)·[rgb0] + dp·[fc] -> rgb0, lv, sv // (1 - dp)·[rgb1] + dp·[fc] -> rgb1, lv, sv // (1 - dp)·[rgb2] + dp·[fc] -> rgb2, lv, sv // 17 cycles -static inline void dpct() { asm volatile("cop2 0x0f8002a"); } +static inline void dpct() { +#ifndef PS1_PC_PORT + asm volatile("cop2 0x0f8002a"); +#endif +} // INTPL - Interpolation of a vector and far color // (1 - dp)·[sv] + dp·[fc] -> rgb2, lv, sv // 8 cycles -static inline void intpl() { asm volatile("cop2 0x0980011"); } +static inline void intpl() { +#ifndef PS1_PC_PORT + asm volatile("cop2 0x0980011"); +#endif +} // Termwise Vector Square // [sv.x² >> 12, sv.y² >> 12, sv.z² >> 12] -> lv, sv // 5 cycles template static inline void sqr() { +#ifndef PS1_PC_PORT if constexpr (sf == Shifted) { asm volatile("cop2 0x0a80428"); } else { asm volatile("cop2 0x0a00428"); } +#endif } // Light Source Calculations @@ -106,7 +132,11 @@ static inline void sqr() { // limit(([ll]·[v0]) >> 12) -> sv // limit(([lc]·[sv]) >> 12) + [bk] -> rgb2 // 14 cycles -static inline void ncs() { asm volatile("cop2 0x0c8041e"); } +static inline void ncs() { +#ifndef PS1_PC_PORT + asm volatile("cop2 0x0c8041e"); +#endif +} // NCT - Normal color (triple) // limit(([ll]·[v0]) >> 12) -> sv @@ -116,14 +146,22 @@ static inline void ncs() { asm volatile("cop2 0x0c8041e"); } // limit(([ll]·[v2]) >> 12) -> sv // limit(([lc]·[sv]) >> 12) + [bk] -> rgb2 // 30 cycles -static inline void nct() { asm volatile("cop2 0x0d80420"); } +static inline void nct() { +#ifndef PS1_PC_PORT + asm volatile("cop2 0x0d80420"); +#endif +} // NCDS - Normal color depth cue (single vector) // limit(([ll]·[v0]) >> 12) -> sv // limit(([lc]·[sv]) >> 12) + [bk] -> sv // (1 - dp)·[rgb·sv] + dp·[fc] -> rgb2 // 19 cycles -static inline void ncds() { asm volatile("cop2 0x0e80413"); } +static inline void ncds() { +#ifndef PS1_PC_PORT + asm volatile("cop2 0x0e80413"); +#endif +} // NCDT - Normal color depth cue (triple vectors) // limit(([ll]·[v0]) >> 12) -> sv @@ -136,14 +174,22 @@ static inline void ncds() { asm volatile("cop2 0x0e80413"); } // limit(([lc]·[sv]) >> 12) + [bk] -> sv // (1 - dp)·[rgb·sv] + dp·[fc] -> rgb2 // 44 cycles -static inline void ncdt() { asm volatile("cop2 0x0f80416"); } +static inline void ncdt() { +#ifndef PS1_PC_PORT + asm volatile("cop2 0x0f80416"); +#endif +} // NCCS - Normal Color Color (single vector) // limit(([ll]·[v0]) >> 12) -> sv // limit(([lc]·[sv]) >> 12) + [bk] -> sv // [rgb·sv] -> rgb2 // 17 cycles -static inline void nccs() { asm volatile("cop2 0x0108041b"); } +static inline void nccs() { +#ifndef PS1_PC_PORT + asm volatile("cop2 0x0108041b"); +#endif +} // NCCT - Normal Color Color (triple vector) // limit(([ll]·[v0]) >> 12) -> sv @@ -156,19 +202,31 @@ static inline void nccs() { asm volatile("cop2 0x0108041b"); } // limit(([lc]·[sv]) >> 12) + [bk] -> sv // [rgb·sv] -> rgb2 // 39 cycles -static inline void ncct() { asm volatile("cop2 0x0118043f"); } +static inline void ncct() { +#ifndef PS1_PC_PORT + asm volatile("cop2 0x0118043f"); +#endif +} // Color Depth Que // limit(([lc]·[sv]) >> 12) + [bk] -> sv // (1 - dp)·[rgb·sv] + dp·[fc] -> rgb2 // 13 cycles -static inline void cdp() { asm volatile("cop2 0x01280414"); } +static inline void cdp() { +#ifndef PS1_PC_PORT + asm volatile("cop2 0x01280414"); +#endif +} // Color Color // limit(([lc]·[sv]) >> 12) + [bk] -> sv // [rgb·sv] -> rgb2 // 11 cycles -static inline void cc() { asm volatile("cop2 0x0138041c"); } +static inline void cc() { +#ifndef PS1_PC_PORT + asm volatile("cop2 0x0138041c"); +#endif +} // NCLIP - Normal clipping // sx0*sy1 + sx1*sy2 + sx2*sy0 - sx0*sy2 - sx1*sy0 - sx2*sy1 -> opz @@ -176,19 +234,31 @@ static inline void cc() { asm volatile("cop2 0x0138041c"); } // [sx1 - sx0, sy1 - sy0] // [sx2 - sx0, sy2 - sy0] // 8 cycles -static inline void nclip() { asm volatile("cop2 0x01400006"); } +static inline void nclip() { +#ifndef PS1_PC_PORT + asm volatile("cop2 0x01400006"); +#endif +} // Z Average // AVSZ3 - Average of three Z values (for Triangles) // zsf3 * (sz0 + sz1 + sz2) -> otz // 5 cycles -static inline void avsz3() { asm volatile("cop2 0x0158002d"); } +static inline void avsz3() { +#ifndef PS1_PC_PORT + asm volatile("cop2 0x0158002d"); +#endif +} // AVSZ4 - Average of four Z values (for Quads) // zsf4 * (sz0 + sz1 + sz2 + sz4) -> otz // 6 cycles -static inline void avsz4() { asm volatile("cop2 0x0168002e"); } +static inline void avsz4() { +#ifndef PS1_PC_PORT + asm volatile("cop2 0x0168002e"); +#endif +} // Cross Product (improperly named Outer Product in Sony's lingo) // rt.22 * ir3 - rt.33 * ir2 -> ir1 @@ -197,11 +267,13 @@ static inline void avsz4() { asm volatile("cop2 0x0168002e"); } // 6 cycles template static inline void cp() { +#ifndef PS1_PC_PORT if constexpr (sf == Shifted) { asm volatile("cop2 0x0178000c"); } else { asm volatile("cop2 0x0170000c"); } +#endif } // General Interpolation @@ -211,11 +283,13 @@ static inline void cp() { // 5 cycles template static inline void gpf() { +#ifndef PS1_PC_PORT if constexpr (sf == Shifted) { asm volatile("cop2 0x0198003d"); } else { asm volatile("cop2 0x0190003d"); } +#endif } // General purpose interpolation with base @@ -223,11 +297,13 @@ static inline void gpf() { // 5 cycles template static inline void gpl() { +#ifndef PS1_PC_PORT if constexpr (sf == Shifted) { asm volatile("cop2 0x01a8003e"); } else { asm volatile("cop2 0x01a0003e"); } +#endif } // All of the MVMVA operations take 8 cycles to complete. @@ -247,9 +323,11 @@ enum class TV : unsigned { TR, BK, FC, Zero }; // Multiply vector by matrix and add vector template void mvmva() { +#ifndef PS1_PC_PORT constexpr uint32_t op = (4 << 20) | (sf << 19) | (uint32_t(mx) << 17) | (uint32_t(v) << 15) | (uint32_t(cv) << 13) | (lm << 10) | 18; asm volatile("cop2 %0" : : "i"(op)); +#endif } // Coordinate Conversion, Light Source Calculations diff --git a/src/mips/psyqo/gte-registers.hh b/src/mips/psyqo/gte-registers.hh index b210559e1..20e787d74 100644 --- a/src/mips/psyqo/gte-registers.hh +++ b/src/mips/psyqo/gte-registers.hh @@ -161,6 +161,7 @@ enum Safety { */ template static inline void clear() { +#ifndef PS1_PC_PORT if constexpr (reg < Register::R11R12) { asm volatile("mtc2 $0, $%0" ::"i"(static_cast(reg))); } else if constexpr (reg >= Register::R11R12) { @@ -169,6 +170,7 @@ static inline void clear() { if constexpr (safety == Safe) { asm volatile("nop; nop"); } +#endif } /** @@ -180,6 +182,7 @@ static inline void clear() { */ template static inline void write(uint32_t value) { +#ifndef PS1_PC_PORT if (__builtin_constant_p(value) && (value == 0)) { clear(); } else { @@ -192,6 +195,7 @@ static inline void write(uint32_t value) { asm volatile("nop; nop"); } } +#endif } /** @@ -340,6 +344,7 @@ static inline void writeUnsafe(const Vec2& in) { */ template static inline void write(const uint32_t* ptr) { +#ifndef PS1_PC_PORT static_assert(reg < Register::R11R12, "Unable to write to register from memory directly"); if constexpr (reg < Register::R11R12) { asm volatile("lwc2 $%1, 0(%0)" ::"r"(ptr), "i"(static_cast(reg))); @@ -348,6 +353,7 @@ static inline void write(const uint32_t* ptr) { if constexpr (safety == Safe) { asm volatile("nop; nop"); } +#endif } /** @@ -359,6 +365,7 @@ static inline void write(const uint32_t* ptr) { */ template static inline uint32_t readRaw() { +#ifndef PS1_PC_PORT uint32_t value; if constexpr (reg < Register::R11R12) { if constexpr (safety == Safe) { @@ -374,6 +381,9 @@ static inline uint32_t readRaw() { } } return value; +#else + return 0; +#endif } /** @@ -384,10 +394,12 @@ static inline uint32_t readRaw() { */ template static inline void read(uint32_t* ptr) { +#ifndef PS1_PC_PORT static_assert(reg < Register::R11R12, "Unable to read from register to memory directly"); if constexpr (reg < Register::R11R12) { asm volatile("swc2 $%2, 0(%1)" : "=m"(*ptr) : "r"(ptr), "i"(static_cast(reg))); } +#endif } /** @@ -399,8 +411,12 @@ static inline void read(uint32_t* ptr) { */ template static inline PackedVec3 readSafe() { +#ifndef PS1_PC_PORT static_assert(valid, "Unable to read pseudo register as vector"); __builtin_unreachable(); +#else + return {}; +#endif } /** @@ -412,20 +428,28 @@ static inline PackedVec3 readSafe() { */ template static inline PackedVec3 readUnsafe() { +#ifndef PS1_PC_PORT static_assert(valid, "Unable to read pseudo register as vector"); __builtin_unreachable(); +#else + return {}; +#endif } template [[deprecated("Use the reference version instead")]] static inline void read(Vec3* ptr) { +#ifndef PS1_PC_PORT static_assert(valid, "Unable to read pseudo register as vector"); __builtin_unreachable(); +#endif } template static inline void read(Vec3& vec) { +#ifndef PS1_PC_PORT static_assert(valid, "Unable to read pseudo register as vector"); __builtin_unreachable(); +#endif } // The following are template specializations for the various GTE registers. diff --git a/src/mips/psyqo/kernel.hh b/src/mips/psyqo/kernel.hh index 3db18cdc1..a8e1dbe6d 100644 --- a/src/mips/psyqo/kernel.hh +++ b/src/mips/psyqo/kernel.hh @@ -54,12 +54,20 @@ static constexpr bool debugMode = true; namespace Internal { static inline uint32_t getCop0Status() { +#ifndef PS1_PC_PORT uint32_t r; asm("mfc0 %0, $12 ; nop" : "=r"(r)); return r; +#else + return 0; +#endif } -static inline void setCop0Status(uint32_t r) { asm("mtc0 %0, $12 ; nop" : : "r"(r)); } +static inline void setCop0Status(uint32_t r) { +#ifndef PS1_PC_PORT + asm("mtc0 %0, $12 ; nop" : : "r"(r)); +#endif +} [[noreturn]] void abort(const char* msg, std::source_location location = std::source_location::current()); [[noreturn]] void abort(); @@ -72,14 +80,22 @@ static inline void setCop0Status(uint32_t r) { asm("mtc0 %0, $12 ; nop" : : "r"( * @details This function is technically equivalent to `enterCriticalSection`. * @return false if the critical section was already entered, true otherwise. */ -static inline void fastEnterCriticalSection() { asm volatile("mtc0 %0, $12 ; nop ; nop" : : "r"(0x40000000)); } +static inline void fastEnterCriticalSection() { +#ifndef PS1_PC_PORT + asm volatile("mtc0 %0, $12 ; nop ; nop" : : "r"(0x40000000)); +#endif +} /** * @brief A faster version of `leaveCriticalSection`. * * @details This function is technically equivalent to `leaveCriticalSection`. */ -static inline void fastLeaveCriticalSection() { asm volatile("mtc0 %0, $12" : : "r"(0x40000401)); } +static inline void fastLeaveCriticalSection() { +#ifndef PS1_PC_PORT + asm volatile("mtc0 %0, $12" : : "r"(0x40000401)); +#endif +} enum class DMA : unsigned { MDECin, From 82c57b8bb2bf7298d772ace6a483d015888ff856 Mon Sep 17 00:00:00 2001 From: Nicolas 'Pixel' Noble Date: Wed, 8 Jul 2026 13:31:01 -0700 Subject: [PATCH 2/2] psyqo: abstract the DMA chain link word behind ChainEntry Fragments and ordering tables encode their DMA linked-list pointer as a packed 24-bit-address + 8-bit-count word ("head"). That packing is PS1-specific: on a 64-bit host the next-pointer alone fills the word, so a PC port needs the pointer and size stored separately. Introduce a ChainEntry type that resolves to the packed head on PS1 (ChainEntryPS1) and to a {next, size} pair on the host (ChainEntryPC), and have every fragment type inherit it. Because the DMA node begins at the link word, the fixed fragments' count field moves after the payload so it is never transferred. Descend the head type through the GPU chaining API: chain(), scheduleOTC() and the internal chain-walk pointers become uintptr_t so the link word is pointer-width on the host. sendFragment() keeps uint32_t*, since it carries primitive words rather than a link pointer. Co-authored-by: Elias Daler Signed-off-by: Nicolas 'Pixel' Noble --- src/mips/psyqo/fragment-concept.hh | 2 + src/mips/psyqo/fragments.hh | 55 ++++++++++++++++++++++----- src/mips/psyqo/gpu.hh | 21 +++++----- src/mips/psyqo/ordering-table.hh | 22 +++++++---- src/mips/psyqo/src/gpu.cpp | 12 +++--- src/mips/psyqo/src/ordering-table.cpp | 7 ++-- 6 files changed, 82 insertions(+), 37 deletions(-) diff --git a/src/mips/psyqo/fragment-concept.hh b/src/mips/psyqo/fragment-concept.hh index c240affb1..cfd5f5f33 100644 --- a/src/mips/psyqo/fragment-concept.hh +++ b/src/mips/psyqo/fragment-concept.hh @@ -49,10 +49,12 @@ struct has_explicit_copy_constructor< template 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() } -> std::convertible_to; { frag.getActualFragmentSize() } -> std::convertible_to; diff --git a/src/mips/psyqo/fragments.hh b/src/mips/psyqo/fragments.hh index 44a5df6d1..bb8be102c 100644 --- a/src/mips/psyqo/fragments.hh +++ b/src/mips/psyqo/fragments.hh @@ -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 -struct SimpleFragment { +struct SimpleFragment : ChainEntry { constexpr size_t maxSize() const { return 1; } template SimpleFragment(Args &&...args) : primitive(eastl::forward(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; }; @@ -89,18 +122,19 @@ struct SimpleFragment { */ template -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 primitives; + unsigned count = N; }; /** @@ -118,19 +152,20 @@ struct FixedFragment { */ template -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 primitives; + unsigned count = N; }; } // namespace Fragments diff --git a/src/mips/psyqo/gpu.hh b/src/mips/psyqo/gpu.hh index 99dea7d72..1a32d1d97 100644 --- a/src/mips/psyqo/gpu.hh +++ b/src/mips/psyqo/gpu.hh @@ -228,7 +228,7 @@ class GPU { */ template void sendFragment(const Frag &fragment) { - sendFragment(&fragment.head + 1, fragment.getActualFragmentSize()); + sendFragment(reinterpret_cast(&fragment.head + 1), fragment.getActualFragmentSize()); } /** @@ -243,7 +243,8 @@ class GPU { template void sendFragment(const Frag &fragment, eastl::function &&callback, DMA::DmaCallback dmaCallback = DMA::FROM_MAIN_LOOP) { - sendFragment(&fragment.head + 1, fragment.getActualFragmentSize(), eastl::move(callback), dmaCallback); + sendFragment(reinterpret_cast(&fragment.head + 1), fragment.getActualFragmentSize(), + eastl::move(callback), dmaCallback); } /** @@ -354,8 +355,8 @@ class GPU { */ template void chain(OrderingTable &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); } /** @@ -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(); @@ -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 { @@ -562,11 +563,11 @@ class GPU { }; eastl::fixed_list m_timers; struct ScheduledOTC { - uint32_t *start; + uintptr_t *start; uint32_t count; }; eastl::fixed_list m_OTCs[2]; - uint32_t *m_chainNext = nullptr; + uintptr_t *m_chainNext = nullptr; uint16_t m_lastHSyncCounter = 0; bool m_interlaced = false; diff --git a/src/mips/psyqo/ordering-table.hh b/src/mips/psyqo/ordering-table.hh index b64415f6d..995223d40 100644 --- a/src/mips/psyqo/ordering-table.hh +++ b/src/mips/psyqo/ordering-table.hh @@ -30,6 +30,7 @@ SOFTWARE. #include #include "psyqo/fragment-concept.hh" +#include "psyqo/fragments.hh" #include "psyqo/shared.hh" namespace psyqo { @@ -37,8 +38,8 @@ namespace psyqo { class GPU; class OrderingTableBase { - protected: - static void clear(uint32_t* table, size_t size); + public: + static void clear(psyqo::Fragments::ChainEntry* table, size_t size); }; /** @@ -56,7 +57,7 @@ class OrderingTableBase { * the more precise the sorting will be, but the more memory will be used. */ template -class OrderingTable : private OrderingTableBase { +class OrderingTable : public OrderingTableBase { public: OrderingTable() { clear(); } @@ -84,17 +85,22 @@ class OrderingTable : private OrderingTableBase { template void insert(Frag& frag, int32_t z) { // TODO: cater for big packets - uint32_t* table = m_table + 1; - uint32_t* head = &frag.head; + auto* table = m_table + 1; if constexpr (safety == Safe::Yes) { z = eastl::clamp(z, int32_t(0), int32_t(N - 1)); } - *head = (frag.getActualFragmentSize() << 24) | table[z]; - table[z] = reinterpret_cast(head) & 0xffffff; +#ifdef PS1_PC_PORT + frag.set(table[z].next, frag.getActualFragmentSize()); + table[z].set(&frag, 0); +#else + frag.set(&table[z], frag.getActualFragmentSize()); + table[z].head = reinterpret_cast(&frag) & 0xffffff; +#endif } + // NOTE: can't use from other classes (PCGPU) otherwise + psyqo::Fragments::ChainEntry m_table[N + 1]; private: - uint32_t m_table[N + 1]; friend class GPU; }; diff --git a/src/mips/psyqo/src/gpu.cpp b/src/mips/psyqo/src/gpu.cpp index 2ccb89285..d4e96c2f9 100644 --- a/src/mips/psyqo/src/gpu.cpp +++ b/src/mips/psyqo/src/gpu.cpp @@ -148,7 +148,7 @@ void psyqo::GPU::initialize(const psyqo::GPU::Configuration &config) { if (count > (c_chainThreshold / 4)) { // next one still too big head &= 0xffffff; - m_chainNext = head == 0xffffff ? nullptr : reinterpret_cast(head & 0x7fffff); + m_chainNext = head == 0xffffff ? nullptr : reinterpret_cast(head & 0x7fffff); scheduleNormalDMA(reinterpret_cast(chainNext) + 4, count); } else { // next one is small enough @@ -164,12 +164,12 @@ void psyqo::GPU::initialize(const psyqo::GPU::Configuration &config) { // Did we get interrupted in the middle of a chain? // It means we linked a node too big for the DMA engine to handle, // so we need to send it manually - uint32_t *next = reinterpret_cast(madr | 0x80000000); + uintptr_t *next = reinterpret_cast(madr | 0x80000000); uint32_t head = *next; uint32_t count = head >> 24; head &= 0xffffff; if (head != 0xffffff) { - m_chainNext = reinterpret_cast(head & 0x7fffff); + m_chainNext = reinterpret_cast(head & 0x7fffff); } scheduleNormalDMA(madr + 4, count); return; @@ -422,7 +422,7 @@ void psyqo::GPU::scheduleNormalDMA(uintptr_t data, size_t count) { DMA_CTRL[DMA_GPU].CHCR = 0x01000201; } -void psyqo::GPU::chain(uint32_t *first, uint32_t *last, size_t count) { +void psyqo::GPU::chain(uintptr_t *first, uintptr_t *last, size_t count) { Kernel::assert(count < 256, "Fragment too big to be chained"); if (!m_chainHead) { m_chainHead = first; @@ -464,7 +464,7 @@ void psyqo::GPU::sendChain(eastl::function &&callback, DMA::DmaCallback uint32_t count = head >> 24; head &= 0xffffff; if (count > (c_chainThreshold / 4)) { - m_chainNext = head == 0xffffff ? nullptr : reinterpret_cast(head & 0x7fffff); + m_chainNext = head == 0xffffff ? nullptr : reinterpret_cast(head & 0x7fffff); scheduleNormalDMA(ptr + 4, count); } else { scheduleChainedDMA(ptr); @@ -579,7 +579,7 @@ void psyqo::GPU::pumpCallbacks() { m_lastHSyncCounter = hsyncCounter; } -void psyqo::GPU::scheduleOTC(uint32_t *start, uint32_t count) { m_OTCs[m_parity].emplace_back(start, count); } +void psyqo::GPU::scheduleOTC(uintptr_t *start, uint32_t count) { m_OTCs[m_parity].emplace_back(start, count); } extern uint16_t psyqoExceptionHandlerAdjustFrameCount[]; diff --git a/src/mips/psyqo/src/ordering-table.cpp b/src/mips/psyqo/src/ordering-table.cpp index 107e15449..d40e0f144 100644 --- a/src/mips/psyqo/src/ordering-table.cpp +++ b/src/mips/psyqo/src/ordering-table.cpp @@ -26,9 +26,10 @@ SOFTWARE. #include "psyqo/ordering-table.hh" -void psyqo::OrderingTableBase::clear(uint32_t* table, size_t size) { - table[0] = 0xffffff; +void psyqo::OrderingTableBase::clear(psyqo::Fragments::ChainEntry* table, size_t size) +{ + table[0].setEndMarker(); for (size_t i = 1; i <= size; i++) { - table[i] = reinterpret_cast(&table[i - 1]) & 0xffffff; + table[i].set(&table[i - 1], 0); } }