Skip to content

Commit

Permalink
add DOUBLE32 type (#325)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jamiras authored Mar 31, 2024
1 parent 28bf311 commit 5bdd0c4
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 0 deletions.
2 changes: 2 additions & 0 deletions include/rc_runtime_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ enum {
RC_MEMSIZE_MBF32,
RC_MEMSIZE_MBF32_LE,
RC_MEMSIZE_FLOAT_BE,
RC_MEMSIZE_DOUBLE32,
RC_MEMSIZE_DOUBLE32_BE,
RC_MEMSIZE_VARIABLE
};

Expand Down
37 changes: 37 additions & 0 deletions src/rcheevos/memref.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ int rc_parse_memref(const char** memaddr, uint8_t* size, uint32_t* address) {
switch (*aux++) {
case 'f': case 'F': *size = RC_MEMSIZE_FLOAT; break;
case 'b': case 'B': *size = RC_MEMSIZE_FLOAT_BE; break;
case 'h': case 'H': *size = RC_MEMSIZE_DOUBLE32; break;
case 'i': case 'I': *size = RC_MEMSIZE_DOUBLE32_BE; break;
case 'm': case 'M': *size = RC_MEMSIZE_MBF32; break;
case 'l': case 'L': *size = RC_MEMSIZE_MBF32_LE; break;

Expand Down Expand Up @@ -198,6 +200,29 @@ static void rc_transform_memref_float_be(rc_typed_value_t* value) {
value->type = RC_VALUE_TYPE_FLOAT;
}

static void rc_transform_memref_double32(rc_typed_value_t* value)
{
/* decodes the four most significant bytes of an IEEE 754 double into a float */
const uint32_t mantissa = (value->value.u32 & 0x000FFFFF) << 3;
const int32_t exponent = (int32_t)((value->value.u32 >> 20) & 0x7FF) - 1023;
const int sign = (value->value.u32 & 0x80000000);
value->value.f32 = rc_build_float(mantissa, exponent, sign);
value->type = RC_VALUE_TYPE_FLOAT;
}

static void rc_transform_memref_double32_be(rc_typed_value_t* value)
{
/* decodes the four most significant bytes of an IEEE 754 double in big endian format into a float */
const uint32_t mantissa = (((value->value.u32 & 0xFF000000) >> 24) |
((value->value.u32 & 0x00FF0000) >> 8) |
((value->value.u32 & 0x00000F00) << 8)) << 3;
const int32_t exponent = (int32_t)(((value->value.u32 & 0x0000007F) << 4) |
((value->value.u32 & 0x0000F000) >> 12)) - 1023;
const int sign = (value->value.u32 & 0x00000080);
value->value.f32 = rc_build_float(mantissa, exponent, sign);
value->type = RC_VALUE_TYPE_FLOAT;
}

static void rc_transform_memref_mbf32(rc_typed_value_t* value) {
/* decodes a Microsoft Binary Format float */
/* NOTE: 32-bit MBF is stored in memory as big endian (at least for Apple II) */
Expand Down Expand Up @@ -322,6 +347,14 @@ void rc_transform_memref_value(rc_typed_value_t* value, uint8_t size) {
rc_transform_memref_float_be(value);
break;

case RC_MEMSIZE_DOUBLE32:
rc_transform_memref_double32(value);
break;

case RC_MEMSIZE_DOUBLE32_BE:
rc_transform_memref_double32_be(value);
break;

case RC_MEMSIZE_MBF32:
rc_transform_memref_mbf32(value);
break;
Expand Down Expand Up @@ -358,6 +391,8 @@ static const uint32_t rc_memref_masks[] = {
0xffffffff, /* RC_MEMSIZE_MBF32 */
0xffffffff, /* RC_MEMSIZE_MBF32_LE */
0xffffffff, /* RC_MEMSIZE_FLOAT_BE */
0xffffffff, /* RC_MEMSIZE_DOUBLE32 */
0xffffffff, /* RC_MEMSIZE_DOUBLE32_BE*/
0xffffffff /* RC_MEMSIZE_VARIABLE */
};

Expand Down Expand Up @@ -395,6 +430,8 @@ static const uint8_t rc_memref_shared_sizes[] = {
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_MBF32 */
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_MBF32_LE */
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_FLOAT_BE */
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_DOUBLE32 */
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_DOUBLE32_BE*/
RC_MEMSIZE_32_BITS /* RC_MEMSIZE_VARIABLE */
};

Expand Down
2 changes: 2 additions & 0 deletions src/rcheevos/operand.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,8 @@ int rc_operand_is_float_memref(const rc_operand_t* self) {
switch (self->size) {
case RC_MEMSIZE_FLOAT:
case RC_MEMSIZE_FLOAT_BE:
case RC_MEMSIZE_DOUBLE32:
case RC_MEMSIZE_DOUBLE32_BE:
case RC_MEMSIZE_MBF32:
case RC_MEMSIZE_MBF32_LE:
return 1;
Expand Down
51 changes: 51 additions & 0 deletions test/rcheevos/test_memref.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "mock_memory.h"

#include <float.h>
#include <math.h> /* pow */

static void test_mask(char size, uint32_t expected)
{
Expand Down Expand Up @@ -32,6 +33,8 @@ static void test_shared_masks(void)
TEST_PARAMS2(test_mask, RC_MEMSIZE_32_BITS_BE, 0xffffffff);
TEST_PARAMS2(test_mask, RC_MEMSIZE_FLOAT, 0xffffffff);
TEST_PARAMS2(test_mask, RC_MEMSIZE_FLOAT_BE, 0xffffffff);
TEST_PARAMS2(test_mask, RC_MEMSIZE_DOUBLE32, 0xffffffff);
TEST_PARAMS2(test_mask, RC_MEMSIZE_DOUBLE32_BE, 0xffffffff);
TEST_PARAMS2(test_mask, RC_MEMSIZE_MBF32, 0xffffffff);
TEST_PARAMS2(test_mask, RC_MEMSIZE_VARIABLE, 0xffffffff);
}
Expand Down Expand Up @@ -63,6 +66,8 @@ static void test_shared_sizes(void)
TEST_PARAMS2(test_shared_size, RC_MEMSIZE_32_BITS_BE, RC_MEMSIZE_32_BITS);
TEST_PARAMS2(test_shared_size, RC_MEMSIZE_FLOAT, RC_MEMSIZE_32_BITS);
TEST_PARAMS2(test_shared_size, RC_MEMSIZE_FLOAT_BE, RC_MEMSIZE_32_BITS);
TEST_PARAMS2(test_shared_size, RC_MEMSIZE_DOUBLE32, RC_MEMSIZE_32_BITS);
TEST_PARAMS2(test_shared_size, RC_MEMSIZE_DOUBLE32_BE, RC_MEMSIZE_32_BITS);
TEST_PARAMS2(test_shared_size, RC_MEMSIZE_MBF32, RC_MEMSIZE_32_BITS);
TEST_PARAMS2(test_shared_size, RC_MEMSIZE_MBF32_LE, RC_MEMSIZE_32_BITS);
TEST_PARAMS2(test_shared_size, RC_MEMSIZE_VARIABLE, RC_MEMSIZE_32_BITS);
Expand All @@ -86,6 +91,29 @@ static void test_transform_float(uint32_t value, uint8_t size, double expected)
ASSERT_FLOAT_EQUALS(typed_value.value.f32, expected);
}

static double rc_round(double n)
{
return floor(n + 0.5); /* no round() in c89 */
}

static void test_transform_double32(uint32_t value, uint8_t size, double expected)
{
rc_typed_value_t typed_value;
typed_value.type = RC_VALUE_TYPE_UNSIGNED;
typed_value.value.u32 = value;
rc_transform_memref_value(&typed_value, size);

/* a 20-bit mantissa only has 6 digits of precision. round to 6 digits, then do a float comparison. */
if (fabs(expected) != 0.0) {
const double digits = floor(log10(fabs(expected))) + 1;
const double expected_pow = pow(10, 6 - digits);
expected = rc_round(expected * expected_pow) / expected_pow;
typed_value.value.f32 = (float)(rc_round(typed_value.value.f32 * expected_pow) / expected_pow);
}

ASSERT_FLOAT_EQUALS(typed_value.value.f32, expected);
}

static void test_transform_float_inf(uint32_t value, uint8_t size)
{
/* C89 does not provide defines for NAN and INFINITY, nor does it provide isnan() or isinf() functions */
Expand Down Expand Up @@ -162,6 +190,29 @@ static void test_transforms(void)
TEST_PARAMS2(test_transform_float_inf, 0x7F800000, RC_MEMSIZE_FLOAT);
TEST_PARAMS2(test_transform_float_nan, 0x7FFFFFFF, RC_MEMSIZE_FLOAT);

TEST_PARAMS3(test_transform_double32, 0x3FF00000, RC_MEMSIZE_DOUBLE32, 1.0);
TEST_PARAMS3(test_transform_double32, 0x4028C000, RC_MEMSIZE_DOUBLE32, 12.375);
TEST_PARAMS3(test_transform_double32, 0x405107DF, RC_MEMSIZE_DOUBLE32, 68.123);
TEST_PARAMS3(test_transform_double32, 0x00000000, RC_MEMSIZE_DOUBLE32, 0.0);
TEST_PARAMS3(test_transform_double32, 0x80000000, RC_MEMSIZE_DOUBLE32, -0.0);
TEST_PARAMS3(test_transform_double32, 0xC0000000, RC_MEMSIZE_DOUBLE32, -2.0);
TEST_PARAMS3(test_transform_double32, 0x400921FB, RC_MEMSIZE_DOUBLE32, 3.14159274101257324);
TEST_PARAMS3(test_transform_double32, 0x3FD55555, RC_MEMSIZE_DOUBLE32, 0.333333334326744076);
TEST_PARAMS3(test_transform_double32, 0x40534892, RC_MEMSIZE_DOUBLE32, 77.133926);
TEST_PARAMS3(test_transform_double32, 0x406A06E1, RC_MEMSIZE_DOUBLE32, 208.214996);
TEST_PARAMS3(test_transform_double32, 0x40B5C6DD, RC_MEMSIZE_DOUBLE32, 5574.863770);
TEST_PARAMS3(test_transform_double32, 0x430C6BF5, RC_MEMSIZE_DOUBLE32, 1000000000000000.0);
TEST_PARAMS3(test_transform_double32, 0x3C9CD2B2, RC_MEMSIZE_DOUBLE32, 0.0000000000000001);
TEST_PARAMS3(test_transform_double32, 0x3780AD01, RC_MEMSIZE_DOUBLE32, 2.39286e-41);
TEST_PARAMS3(test_transform_double32, 0x3FF3C0CA, RC_MEMSIZE_DOUBLE32, 1.234568);
TEST_PARAMS2(test_transform_float_inf, 0x7FF00000, RC_MEMSIZE_DOUBLE32);
TEST_PARAMS2(test_transform_float_nan, 0x7FFFFFFF, RC_MEMSIZE_DOUBLE32);

TEST_PARAMS3(test_transform_double32, 0x000000C0, RC_MEMSIZE_DOUBLE32_BE, -2.0);
TEST_PARAMS3(test_transform_double32, 0x00003840, RC_MEMSIZE_DOUBLE32_BE, 24.0);
TEST_PARAMS3(test_transform_double32, 0xCAC0F33F, RC_MEMSIZE_DOUBLE32_BE, 1.234568);
TEST_PARAMS3(test_transform_double32, 0xFB210940, RC_MEMSIZE_DOUBLE32_BE, 3.14159274101257324);

TEST_PARAMS3(test_transform_float, 0x0000803F, RC_MEMSIZE_FLOAT_BE, 1.0);
TEST_PARAMS3(test_transform_float, 0x00004641, RC_MEMSIZE_FLOAT_BE, 12.375);
TEST_PARAMS3(test_transform_float, 0xFA3E8842, RC_MEMSIZE_FLOAT_BE, 68.123);
Expand Down

0 comments on commit 5bdd0c4

Please sign in to comment.