Skip to content

Commit 4cb8be2

Browse files
authored
Merge pull request #2008 from nicolasnoble/snitch-integration
Add snitch-based unit tests for PSYQo
2 parents ae272bd + 45ac260 commit 4cb8be2

23 files changed

Lines changed: 9235 additions & 0 deletions

LICENSES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ list of the libraries used by PCSX-Redux. Some of them have been
1616
vendored with modifications. If this is the case, you will also
1717
find a link to the vendored version itself.
1818

19+
- [arith64](https://github.com/glitchub/arith64) ([vendored](https://github.com/grumpycoders/pcsx-redux/tree/main/third_party/arith64))
1920
- [Capstone](https://github.com/capstone-engine/capstone)
2021
- [Clip](https://github.com/dacap/clip) ([vendored](https://github.com/grumpycoders/pcsx-redux/tree/main/third_party/clip))
2122
- [CompletionQueue](https://github.com/bhhbazinga/ConcurrentQueue) ([vendored](https://github.com/grumpycoders/pcsx-redux/tree/main/third_party/cq))
@@ -55,6 +56,7 @@ find a link to the vendored version itself.
5556
- [noto](https://fonts.google.com/noto) ([vendored](https://github.com/grumpycoders/pcsx-redux/tree/main/third_party/noto))
5657
- [PEGTL](https://github.com/taocpp/PEGTL)
5758
- [pprint.lua](https://github.com/jagt/pprint.lua) ([vendored](https://github.com/grumpycoders/pcsx-redux/tree/main/third_party/pprint.lua))
59+
- [snitch](https://github.com/snitch-org/snitch) ([vendored](https://github.com/grumpycoders/pcsx-redux/tree/main/third_party/snitch))
5860
- [stb](https://github.com/nothings/stb)
5961
- [tracy](https://github.com/wolfpld/tracy)
6062
- [typestring](https://github.com/irrequietus/typestring) ([vendored](https://github.com/grumpycoders/pcsx-redux/blob/main/third_party/typestring.hh))

src/mips/common/crt0/cxxglue.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,3 +230,7 @@ __attribute__((weak)) void __cxa_guard_release(uint32_t* guardObject) {
230230
guardObject[1] = 0;
231231
atomic_signal_fence(memory_order_release);
232232
}
233+
234+
__attribute__((weak)) void _ZSt24__throw_out_of_range_fmtPKcz(const char* format, ...) {
235+
abort();
236+
}

src/mips/psyqo/snitch.mk

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
ifndef SNITCHDIR
2+
SNITCHDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))snitch/
3+
4+
LIBRARIES += $(SNITCHDIR)libsnitch.a
5+
CPPFLAGS += -I$(SNITCHDIR)../../../../third_party/snitch
6+
7+
include $(SNITCHDIR)../../psyqo/psyqo.mk
8+
9+
$(SNITCHDIR)libsnitch.a:
10+
$(MAKE) -C $(SNITCHDIR) BUILD=$(BUILD)
11+
12+
clean::
13+
$(MAKE) -C $(SNITCHDIR) clean
14+
15+
.PHONY: clean $(SNITCHDIR)libsnitch.a
16+
endif

src/mips/psyqo/snitch/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
TARGET = snitch
2+
TYPE = library
3+
4+
SRCS = \
5+
../../../../third_party/arith64/arith64.c \
6+
snitch-impl.cpp \
7+
8+
CPPFLAGS = -I../../../../third_party/snitch
9+
CXXFLAGS = -std=c++20
10+
11+
include ../../common.mk
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Single translation unit for snitch header-only library.
2+
#define SNITCH_IMPLEMENTATION
3+
#include "snitch_all.hpp"

src/mips/tests/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ all:
88
$(MAKE) -C memcpy all
99
$(MAKE) -C memset all
1010
$(MAKE) -C pcdrv all
11+
$(MAKE) -C psyqo all
1112
$(MAKE) -C timers all
1213

1314
clean:
@@ -20,4 +21,5 @@ clean:
2021
$(MAKE) -C memcpy clean
2122
$(MAKE) -C memset clean
2223
$(MAKE) -C pcdrv clean
24+
$(MAKE) -C psyqo clean
2325
$(MAKE) -C timers clean

src/mips/tests/psyqo/Makefile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
TARGET = psyqo-tests
2+
TYPE = ps-exe
3+
4+
SRCS = \
5+
psyqo-tests.cpp \
6+
test-fixed-point.cpp \
7+
test-vector.cpp \
8+
test-trigonometry.cpp \
9+
test-soft-math.cpp \
10+
test-msf.cpp \
11+
test-adler32.cpp \
12+
test-bezier.cpp \
13+
test-primitives.cpp \
14+
15+
CXXFLAGS = -std=c++20
16+
17+
include ../../psyqo/snitch.mk
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
3+
MIT License
4+
5+
Copyright (c) 2026 PCSX-Redux authors
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in all
15+
copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
SOFTWARE.
24+
25+
*/
26+
27+
#include "common/hardware/pcsxhw.h"
28+
#include "common/syscalls/syscalls.h"
29+
30+
#include "snitch_all.hpp"
31+
32+
static void psyqo_console_print(std::string_view message) noexcept {
33+
for (char c : message) {
34+
syscall_putchar(c);
35+
}
36+
}
37+
38+
int main() {
39+
snitch::cli::console_print = &psyqo_console_print;
40+
snitch::tests.print_callback = &psyqo_console_print;
41+
42+
bool success = snitch::tests.run_tests("psyqo");
43+
44+
if (success) {
45+
ramsyscall_printf("All tests passed!\n");
46+
} else {
47+
ramsyscall_printf("Some tests FAILED!\n");
48+
}
49+
50+
// Signal to the emulator via exit code.
51+
pcsx_exit(success ? 0 : 1);
52+
return success ? 0 : 1;
53+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
3+
MIT License
4+
5+
Copyright (c) 2026 PCSX-Redux authors
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in all
15+
copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
SOFTWARE.
24+
25+
*/
26+
27+
#include "psyqo/adler32.hh"
28+
29+
#include "snitch_all.hpp"
30+
31+
using namespace psyqo;
32+
33+
// --- Basic checksums ---
34+
35+
TEST_CASE("Adler32 empty buffer") {
36+
uint32_t sum = adler32(nullptr, 0);
37+
REQUIRE(sum == 1);
38+
}
39+
40+
TEST_CASE("Adler32 single byte") {
41+
uint8_t data[] = {0x01};
42+
uint32_t sum = adler32(data, 1);
43+
// Initial: a=1, b=0. After byte 1: a = 1+1 = 2, b = 0+2 = 2.
44+
REQUIRE(sum == ((2 << 16) | 2));
45+
}
46+
47+
TEST_CASE("Adler32 known vector: 123456789") {
48+
// RFC 1950 test vector
49+
uint8_t data[] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'};
50+
uint32_t sum = adler32(data, 9);
51+
REQUIRE(sum == 0x091e01de);
52+
}
53+
54+
TEST_CASE("Adler32 all zeros") {
55+
uint8_t data[10] = {};
56+
uint32_t sum = adler32(data, 10);
57+
// Initial: a=1, b=0. Each zero byte: a stays 1, b += a = b+1.
58+
// After 10 zeros: a=1, b=10.
59+
REQUIRE(sum == ((10 << 16) | 1));
60+
}
61+
62+
// --- Chaining ---
63+
64+
TEST_CASE("Adler32 chaining produces same result") {
65+
uint8_t data[] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'};
66+
uint32_t whole = adler32(data, 9);
67+
68+
// Compute in two chunks
69+
uint32_t partial = adler32(data, 4);
70+
uint32_t chained = adler32(data + 4, 5, partial);
71+
REQUIRE(chained == whole);
72+
}
73+
74+
TEST_CASE("Adler32 chaining three chunks") {
75+
uint8_t data[] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'};
76+
uint32_t whole = adler32(data, 9);
77+
78+
uint32_t s1 = adler32(data, 3);
79+
uint32_t s2 = adler32(data + 3, 3, s1);
80+
uint32_t s3 = adler32(data + 6, 3, s2);
81+
REQUIRE(s3 == whole);
82+
}
83+
84+
// --- Byte vs word consistency ---
85+
86+
TEST_CASE("Adler32 byte and word variants match") {
87+
// Word-aligned data
88+
uint32_t words[] = {0x04030201, 0x08070605};
89+
uint8_t *bytes = reinterpret_cast<uint8_t *>(words);
90+
91+
uint32_t byte_sum = adler32_bytes(bytes, 8);
92+
uint32_t word_sum = adler32_words(words, 2);
93+
REQUIRE(byte_sum == word_sum);
94+
}
95+
96+
// --- Larger buffer ---
97+
98+
TEST_CASE("Adler32 sequential byte pattern") {
99+
uint8_t data[256];
100+
for (int i = 0; i < 256; i++) data[i] = i;
101+
uint32_t sum = adler32(data, 256);
102+
// Just verify it's non-trivial and deterministic
103+
REQUIRE(sum != 0);
104+
REQUIRE(sum != 1);
105+
// Re-compute to verify determinism
106+
uint32_t sum2 = adler32(data, 256);
107+
REQUIRE(sum == sum2);
108+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
3+
MIT License
4+
5+
Copyright (c) 2026 PCSX-Redux authors
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in all
15+
copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
SOFTWARE.
24+
25+
*/
26+
27+
#include "psyqo/bezier.hh"
28+
29+
#include "snitch_all.hpp"
30+
31+
using namespace psyqo;
32+
using namespace psyqo::fixed_point_literals;
33+
34+
// --- Endpoint interpolation ---
35+
36+
TEST_CASE("Bezier cubic at t=0 returns start point") {
37+
Vec2 a, b, c, d;
38+
a.x = 0.0; a.y = 0.0;
39+
b.x = 1.0; b.y = 2.0;
40+
c.x = 3.0; c.y = 4.0;
41+
d.x = 5.0; d.y = 6.0;
42+
auto p = Bezier::cubic(a, b, c, d, 0.0_fp);
43+
REQUIRE(p.x.raw() == a.x.raw());
44+
REQUIRE(p.y.raw() == a.y.raw());
45+
}
46+
47+
TEST_CASE("Bezier cubic at t=1 returns end point") {
48+
Vec2 a, b, c, d;
49+
a.x = 0.0; a.y = 0.0;
50+
b.x = 1.0; b.y = 2.0;
51+
c.x = 3.0; c.y = 4.0;
52+
d.x = 5.0; d.y = 6.0;
53+
auto p = Bezier::cubic(a, b, c, d, 1.0_fp);
54+
auto dx = p.x.raw() - d.x.raw();
55+
auto dy = p.y.raw() - d.y.raw();
56+
REQUIRE(dx >= -5);
57+
REQUIRE(dx <= 5);
58+
REQUIRE(dy >= -5);
59+
REQUIRE(dy <= 5);
60+
}
61+
62+
// --- Linear case ---
63+
64+
TEST_CASE("Bezier cubic with collinear control points is linear") {
65+
Vec2 a, b, c, d;
66+
a.x = 0.0; a.y = 0.0;
67+
b.x = 1.0; b.y = 1.0;
68+
c.x = 2.0; c.y = 2.0;
69+
d.x = 3.0; d.y = 3.0;
70+
auto mid = Bezier::cubic(a, b, c, d, 0.5_fp);
71+
auto diff_x = mid.x.raw() - FixedPoint<>(1.5).raw();
72+
auto diff_y = mid.y.raw() - FixedPoint<>(1.5).raw();
73+
REQUIRE(diff_x >= -5);
74+
REQUIRE(diff_x <= 5);
75+
REQUIRE(diff_y >= -5);
76+
REQUIRE(diff_y <= 5);
77+
}
78+
79+
// --- 3D variant ---
80+
81+
TEST_CASE("Bezier 3D cubic at t=0 returns start") {
82+
Vec3 a, b, c, d;
83+
a.x = 1.0; a.y = 2.0; a.z = 3.0;
84+
b.x = 4.0; b.y = 5.0; b.z = 6.0;
85+
c.x = 7.0; c.y = 8.0; c.z = 9.0;
86+
d.x = 10.0; d.y = 11.0; d.z = 12.0;
87+
auto p = Bezier::cubic(a, b, c, d, 0.0_fp);
88+
REQUIRE(p.x.raw() == a.x.raw());
89+
REQUIRE(p.y.raw() == a.y.raw());
90+
REQUIRE(p.z.raw() == a.z.raw());
91+
}
92+
93+
TEST_CASE("Bezier 3D cubic at t=1 returns end") {
94+
Vec3 a, b, c, d;
95+
a.x = 1.0; a.y = 2.0; a.z = 3.0;
96+
b.x = 4.0; b.y = 5.0; b.z = 6.0;
97+
c.x = 7.0; c.y = 8.0; c.z = 9.0;
98+
d.x = 10.0; d.y = 11.0; d.z = 12.0;
99+
auto p = Bezier::cubic(a, b, c, d, 1.0_fp);
100+
auto dx = p.x.raw() - d.x.raw();
101+
auto dy = p.y.raw() - d.y.raw();
102+
auto dz = p.z.raw() - d.z.raw();
103+
REQUIRE(dx >= -5);
104+
REQUIRE(dx <= 5);
105+
REQUIRE(dy >= -5);
106+
REQUIRE(dy <= 5);
107+
REQUIRE(dz >= -5);
108+
REQUIRE(dz <= 5);
109+
}
110+
111+
// --- Midpoint with symmetric curve ---
112+
113+
TEST_CASE("Bezier symmetric curve midpoint") {
114+
// Symmetric curve: a=(0,0), b=(0,1), c=(1,1), d=(1,0)
115+
// At t=0.5, the midpoint should be approximately (0.5, 0.75)
116+
Vec2 a, b, c, d;
117+
a.x = 0.0; a.y = 0.0;
118+
b.x = 0.0; b.y = 1.0;
119+
c.x = 1.0; c.y = 1.0;
120+
d.x = 1.0; d.y = 0.0;
121+
auto mid = Bezier::cubic(a, b, c, d, 0.5_fp);
122+
auto dx = mid.x.raw() - FixedPoint<>(0.5).raw();
123+
auto dy = mid.y.raw() - FixedPoint<>(0.75).raw();
124+
REQUIRE(dx >= -10);
125+
REQUIRE(dx <= 10);
126+
REQUIRE(dy >= -10);
127+
REQUIRE(dy <= 10);
128+
}

0 commit comments

Comments
 (0)