diff --git a/core/libs/prx/libc/include/SonyCrt.hpp b/core/libs/prx/libc/include/SonyCrt.hpp index 12500b15d1c..39a655f51e9 100644 --- a/core/libs/prx/libc/include/SonyCrt.hpp +++ b/core/libs/prx/libc/include/SonyCrt.hpp @@ -181,8 +181,7 @@ inline int SonyFltrounds(void) { } } -inline unsigned __int128 SonyUdivti3(unsigned __int128 dividend, unsigned __int128 divisor) { - if (divisor == 0) throw std::runtime_error("__udivti3: integer divide by zero"); +inline unsigned __int128 SonyUdivmod128(unsigned __int128 dividend, unsigned __int128 divisor, unsigned __int128* remainderOut) { unsigned __int128 quotient = 0; unsigned __int128 remainder = 0; for (int i = 127; i >= 0; --i) { @@ -192,9 +191,28 @@ inline unsigned __int128 SonyUdivti3(unsigned __int128 dividend, unsigned __int1 quotient |= (unsigned __int128)1 << i; } } + *remainderOut = remainder; return quotient; } +inline unsigned __int128 SonyUdivti3(unsigned __int128 dividend, unsigned __int128 divisor) { + if (divisor == 0) throw std::runtime_error("__udivti3: integer divide by zero"); + unsigned __int128 remainder = 0; + return SonyUdivmod128(dividend, divisor, &remainder); +} + +inline unsigned __int128 SonyUmodti3(unsigned __int128 dividend, unsigned __int128 divisor) { + if (divisor == 0) { + char message[96]; + std::snprintf(message, sizeof(message), "__umodti3: integer divide by zero (0x%016llx%016llx %% 0)", + (unsigned long long)(dividend >> 64), (unsigned long long)dividend); + throw std::runtime_error(message); + } + unsigned __int128 remainder = 0; + SonyUdivmod128(dividend, divisor, &remainder); + return remainder; +} + inline __int128 SonyDivti3(__int128 dividend, __int128 divisor) { if (divisor == 0) throw std::runtime_error("__divti3: integer divide by zero"); const bool negative = (dividend < 0) != (divisor < 0); diff --git a/core/libs/prx/libc/src/RuntimeSupport.cpp b/core/libs/prx/libc/src/RuntimeSupport.cpp index 0d83c1743d8..56a0f8d4993 100644 --- a/core/libs/prx/libc/src/RuntimeSupport.cpp +++ b/core/libs/prx/libc/src/RuntimeSupport.cpp @@ -188,4 +188,8 @@ unsigned __int128 APS5_VABI __udivti3_nid_postfix(unsigned __int128 dividend, un return SonyUdivti3(dividend, divisor); } +unsigned __int128 APS5_VABI __umodti3_nid_postfix(unsigned __int128 dividend, unsigned __int128 divisor) { + return SonyUmodti3(dividend, divisor); +} + } diff --git a/core/libs/tests/GuestCrt.cpp b/core/libs/tests/GuestCrt.cpp index 4aeeabd1ea4..f6600788de1 100644 --- a/core/libs/tests/GuestCrt.cpp +++ b/core/libs/tests/GuestCrt.cpp @@ -32,6 +32,7 @@ char* APS5_VABI strtok_s_nid_postfix(char* str, const char* delim, char** contex int APS5_VABI __fpclassifyd_nid_postfix(double value); unsigned __int128 APS5_VABI __udivti3_nid_postfix(unsigned __int128 dividend, unsigned __int128 divisor); __int128 APS5_VABI __divti3_nid_postfix(__int128 dividend, __int128 divisor); +unsigned __int128 APS5_VABI __umodti3_nid_postfix(unsigned __int128 dividend, unsigned __int128 divisor); SonyLconv* APS5_VABI localeconv_nid_postfix(void); int APS5_VABI sceAmprFpclassifyd(double value); unsigned int APS5_VABI _ZSt14_Random_devicev_nid_postfix(); @@ -109,6 +110,29 @@ int main() { } Require(trapped); + const unsigned __int128 unsignedMaximum = ~(unsigned __int128)0; + const unsigned __int128 largeDivisor = ((unsigned __int128)1 << 64) + 3; + Require(__umodti3_nid_postfix(100, 3) == 1); + Require(__umodti3_nid_postfix(0, 5) == 0); + Require(__umodti3_nid_postfix(3, 100) == 3); + Require(__umodti3_nid_postfix(unsignedMaximum, 1) == 0); + Require(__umodti3_nid_postfix(unsignedMaximum, unsignedMaximum) == 0); + Require(__umodti3_nid_postfix(largeDivisor, largeDivisor) == 0); + Require(__umodti3_nid_postfix(unsignedMaximum, 10) == 5); + Require(__umodti3_nid_postfix((unsigned __int128)1 << 100, 3) == 1); + Require(__umodti3_nid_postfix(unsignedMaximum, (unsigned __int128)1 << 64) == 0xFFFFFFFFFFFFFFFFull); + Require(__umodti3_nid_postfix(unsignedMaximum, largeDivisor) == 8); + Require(__umodti3_nid_postfix((unsigned __int128)1 << 127, largeDivisor) == 0x8000000000000006ull); + Require(__umodti3_nid_postfix(unsignedMaximum - 1, unsignedMaximum) == unsignedMaximum - 1); + Require(__umodti3_nid_postfix(unsignedMaximum, (unsigned __int128)1 << 127) == ((unsigned __int128)1 << 127) - 1); + trapped = false; + try { + __umodti3_nid_postfix(((unsigned __int128)0x1234 << 64) | 0x5678, 0); + } catch (const std::runtime_error& error) { + trapped = std::strcmp(error.what(), "__umodti3: integer divide by zero (0x00000000000012340000000000005678 % 0)") == 0; + } + Require(trapped); + void* previous = sceAmprSetConstraintHandlerS(reinterpret_cast(&CountingHandler)); Require(previous != nullptr); Require(previous != reinterpret_cast(&CountingHandler)); diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 41300fb18f5..6aa1e9e4f53 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -343,6 +343,7 @@ Throughout the project, every function at every stage either **does exactly what - [localeconv](../../core/libs/prx/libc/src/LocaleSupport.cpp) (libc, re-exported by libSceAmpr) - the 0x90-byte Dinkumware struct (empty monetary strings, `CHAR_MAX` for the 14 `char` fields, `decimal_point` ".", `_False`/`_True` "false"/"true") is read from the `libc.prx` that Astro's Playroom ships. It is a fixed C-locale table: whether the console changes it with `setlocale` is not known, and `_Frac_grouping`, `_Frac_sep`, `_No`, `_Yes` are empty strings as in that dump - [set_constraint_handler_s](../../core/libs/prx/libc/src/WideConversion.cpp) (libc, re-exported by libSceAmpr) - the default handler is assumed to abort; each module keeps its own handler state, so a handler set through one library is not seen by the other. `ctime_s` and `asctime_s` follow the Dinkumware 26-byte format with `EINVAL` for a buffer under 26 bytes, a null pointer or an out-of-range weekday/month; `ctime_s` uses the guest time zone of `localtime_s`, and the console's exact checks are not verified - [__udivti3](../../core/libs/prx/libc/src/RuntimeSupport.cpp) (libc, re-exported by libSceAmpr) - the hardware traps on a zero divisor; this throws instead +- [__umodti3](../../core/libs/prx/libc/src/RuntimeSupport.cpp) (libc) - the hardware traps on a zero divisor; this throws instead - [AC1FtjqMCL0](../../core/libs/prx/libkernel/Unimplemented.cpp) (libkernel) - unknown name, signature - [0iEKNAvT600](../../core/libs/prx/libkernel/Unimplemented.cpp) (libkernel) - unknown name, signature - [2cELEBPdYQ0](../../core/libs/prx/libkernel/Unimplemented.cpp) (libkernel) - unknown name, signature