2018-09-29 18:38:21 +01:00

2108 lines
80 KiB
C

#include "internal.h"
#include "rurl.h"
#include "smw_snes.h"
#include "galaga_nes.h"
#include <stddef.h>
#include <string.h>
#include <assert.h>
#include "lua.h"
#include "lauxlib.h"
typedef struct {
unsigned char* ram;
int size;
}
memory_t;
static unsigned peekb(unsigned address, memory_t* memory) {
return address < memory->size ? memory->ram[address] : 0;
}
static unsigned peek(unsigned address, unsigned num_bytes, void* ud) {
memory_t* memory = (memory_t*)ud;
switch (num_bytes) {
case 1: return peekb(address, memory);
case 2: return peekb(address, memory) |
peekb(address + 1, memory) << 8;
case 4: return peekb(address, memory) |
peekb(address + 1, memory) << 8 |
peekb(address + 2, memory) << 16 |
peekb(address + 3, memory) << 24;
}
return 0;
}
static void parse_operand(rc_operand_t* self, const char** memaddr) {
int ret = rc_parse_operand(self, memaddr, 1, NULL, 0);
assert(ret >= 0);
assert(**memaddr == 0);
self->previous = 0;
}
static void comp_operand(rc_operand_t* self, char expected_type, char expected_size, unsigned expected_value) {
assert(expected_type == self->type);
assert(expected_size == self->size);
assert(expected_value == self->value);
}
static void parse_comp_operand(const char* memaddr, char expected_type, char expected_size, unsigned expected_value) {
rc_operand_t self;
int ret;
ret = rc_parse_operand(&self, &memaddr, 1, NULL, 0);
assert(ret >= 0);
assert(*memaddr == 0);
comp_operand(&self, expected_type, expected_size, expected_value);
}
static void parse_error_operand(const char* memaddr, int valid_chars) {
rc_operand_t self;
int ret;
const char* begin = memaddr;
ret = rc_parse_operand(&self, &memaddr, 1, NULL, 0);
assert(ret < 0);
assert(memaddr - begin == valid_chars);
}
static void parse_comp_operand_value(const char* memaddr, memory_t* memory, unsigned expected_value) {
rc_operand_t self;
unsigned value;
rc_parse_operand(&self, &memaddr, 1, NULL, 0);
value = rc_evaluate_operand(&self, peek, memory, NULL);
assert(value == expected_value);
}
static void test_operand(void) {
{
/*------------------------------------------------------------------------
TestParseVariableAddress
------------------------------------------------------------------------*/
/* sizes */
parse_comp_operand("0xH1234", RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U);
parse_comp_operand("0x 1234", RC_OPERAND_ADDRESS, RC_OPERAND_16_BITS, 0x1234U);
parse_comp_operand("0x1234", RC_OPERAND_ADDRESS, RC_OPERAND_16_BITS, 0x1234U);
parse_comp_operand("0xW1234", RC_OPERAND_ADDRESS, RC_OPERAND_24_BITS, 0x1234U);
parse_comp_operand("0xX1234", RC_OPERAND_ADDRESS, RC_OPERAND_32_BITS, 0x1234U);
parse_comp_operand("0xL1234", RC_OPERAND_ADDRESS, RC_OPERAND_LOW, 0x1234U);
parse_comp_operand("0xU1234", RC_OPERAND_ADDRESS, RC_OPERAND_HIGH, 0x1234U);
parse_comp_operand("0xM1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_0, 0x1234U);
parse_comp_operand("0xN1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_1, 0x1234U);
parse_comp_operand("0xO1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_2, 0x1234U);
parse_comp_operand("0xP1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_3, 0x1234U);
parse_comp_operand("0xQ1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_4, 0x1234U);
parse_comp_operand("0xR1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_5, 0x1234U);
parse_comp_operand("0xS1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_6, 0x1234U);
parse_comp_operand("0xT1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_7, 0x1234U);
/* sizes (ignore case) */
parse_comp_operand("0Xh1234", RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U);
parse_comp_operand("0xx1234", RC_OPERAND_ADDRESS, RC_OPERAND_32_BITS, 0x1234U);
parse_comp_operand("0xl1234", RC_OPERAND_ADDRESS, RC_OPERAND_LOW, 0x1234U);
parse_comp_operand("0xu1234", RC_OPERAND_ADDRESS, RC_OPERAND_HIGH, 0x1234U);
parse_comp_operand("0xm1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_0, 0x1234U);
parse_comp_operand("0xn1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_1, 0x1234U);
parse_comp_operand("0xo1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_2, 0x1234U);
parse_comp_operand("0xp1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_3, 0x1234U);
parse_comp_operand("0xq1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_4, 0x1234U);
parse_comp_operand("0xr1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_5, 0x1234U);
parse_comp_operand("0xs1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_6, 0x1234U);
parse_comp_operand("0xt1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_7, 0x1234U);
/* addresses */
parse_comp_operand("0xH0000", RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x0000U);
parse_comp_operand("0xH12345678", RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x12345678U);
parse_comp_operand("0xHABCD", RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0xABCDU);
parse_comp_operand("0xhabcd", RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0xABCDU);
}
{
/*------------------------------------------------------------------------
TestParseVariableDeltaMem
------------------------------------------------------------------------*/
/* sizes */
parse_comp_operand("d0xH1234", RC_OPERAND_DELTA, RC_OPERAND_8_BITS, 0x1234U);
parse_comp_operand("d0x 1234", RC_OPERAND_DELTA, RC_OPERAND_16_BITS, 0x1234U);
parse_comp_operand("d0x1234", RC_OPERAND_DELTA, RC_OPERAND_16_BITS, 0x1234U);
parse_comp_operand("d0xW1234", RC_OPERAND_DELTA, RC_OPERAND_24_BITS, 0x1234U);
parse_comp_operand("d0xX1234", RC_OPERAND_DELTA, RC_OPERAND_32_BITS, 0x1234U);
parse_comp_operand("d0xL1234", RC_OPERAND_DELTA, RC_OPERAND_LOW, 0x1234U);
parse_comp_operand("d0xU1234", RC_OPERAND_DELTA, RC_OPERAND_HIGH, 0x1234U);
parse_comp_operand("d0xM1234", RC_OPERAND_DELTA, RC_OPERAND_BIT_0, 0x1234U);
parse_comp_operand("d0xN1234", RC_OPERAND_DELTA, RC_OPERAND_BIT_1, 0x1234U);
parse_comp_operand("d0xO1234", RC_OPERAND_DELTA, RC_OPERAND_BIT_2, 0x1234U);
parse_comp_operand("d0xP1234", RC_OPERAND_DELTA, RC_OPERAND_BIT_3, 0x1234U);
parse_comp_operand("d0xQ1234", RC_OPERAND_DELTA, RC_OPERAND_BIT_4, 0x1234U);
parse_comp_operand("d0xR1234", RC_OPERAND_DELTA, RC_OPERAND_BIT_5, 0x1234U);
parse_comp_operand("d0xS1234", RC_OPERAND_DELTA, RC_OPERAND_BIT_6, 0x1234U);
parse_comp_operand("d0xT1234", RC_OPERAND_DELTA, RC_OPERAND_BIT_7, 0x1234U);
/* ignores case */
parse_comp_operand("D0Xh1234", RC_OPERAND_DELTA, RC_OPERAND_8_BITS, 0x1234U);
/* addresses */
parse_comp_operand("d0xH0000", RC_OPERAND_DELTA, RC_OPERAND_8_BITS, 0x0000U);
parse_comp_operand("d0xH12345678", RC_OPERAND_DELTA, RC_OPERAND_8_BITS, 0x12345678U);
parse_comp_operand("d0xHABCD", RC_OPERAND_DELTA, RC_OPERAND_8_BITS, 0xABCDU);
parse_comp_operand("d0xhabcd", RC_OPERAND_DELTA, RC_OPERAND_8_BITS, 0xABCDU);
}
{
/*------------------------------------------------------------------------
TestParseVariableValue
------------------------------------------------------------------------*/
/* decimal - values don't actually have size, default is RC_OPERAND_8_BITS */
parse_comp_operand("123", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 123U);
parse_comp_operand("123456", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 123456U);
parse_comp_operand("0", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 0U);
parse_comp_operand("0000000000", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 0U);
parse_comp_operand("4294967295", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 4294967295U);
/* hex - 'H' prefix, not '0x'! */
parse_comp_operand("H123", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 0x123U);
parse_comp_operand("HABCD", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 0xABCDU);
parse_comp_operand("h123", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 0x123U);
parse_comp_operand("habcd", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 0xABCDU);
parse_comp_operand("HFFFFFFFF", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 4294967295U);
/* '0x' is an address */
parse_comp_operand("0x123", RC_OPERAND_ADDRESS, RC_OPERAND_16_BITS, 0x123U);
/* hex without prefix */
parse_error_operand("ABCD", 0);
/* more than 32-bits (error), will be constrained to 32-bits */
parse_comp_operand("4294967296", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 4294967295U);
/* negative value (error), will be "wrapped around": -1 = 0x100000000 - 1 = 0xFFFFFFFF = 4294967295 */
parse_comp_operand("-1", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 4294967295U);
}
{
/*------------------------------------------------------------------------
TestVariableGetValue
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
memory.ram = ram;
memory.size = sizeof(ram);
/* value */
parse_comp_operand_value("0", &memory, 0x00U);
/* eight-bit */
parse_comp_operand_value("0xh0", &memory, 0x00U);
parse_comp_operand_value("0xh1", &memory, 0x12U);
parse_comp_operand_value("0xh4", &memory, 0x56U);
parse_comp_operand_value("0xh5", &memory, 0x00U); /* out of range */
/* sixteen-bit */
parse_comp_operand_value("0x 0", &memory, 0x1200U);
parse_comp_operand_value("0x 3", &memory, 0x56ABU);
parse_comp_operand_value("0x 4", &memory, 0x0056U); /* out of range */
/* thirty-two-bit */
parse_comp_operand_value("0xx0", &memory, 0xAB341200U);
parse_comp_operand_value("0xx1", &memory, 0x56AB3412U);
parse_comp_operand_value("0xx3", &memory, 0x000056ABU); /* out of range */
/* nibbles */
parse_comp_operand_value("0xu0", &memory, 0x0U);
parse_comp_operand_value("0xu1", &memory, 0x1U);
parse_comp_operand_value("0xu4", &memory, 0x5U);
parse_comp_operand_value("0xu5", &memory, 0x0U); /* out of range */
parse_comp_operand_value("0xl0", &memory, 0x0U);
parse_comp_operand_value("0xl1", &memory, 0x2U);
parse_comp_operand_value("0xl4", &memory, 0x6U);
parse_comp_operand_value("0xl5", &memory, 0x0U); /* out of range */
/* bits */
parse_comp_operand_value("0xm0", &memory, 0x0U);
parse_comp_operand_value("0xm3", &memory, 0x1U);
parse_comp_operand_value("0xn3", &memory, 0x1U);
parse_comp_operand_value("0xo3", &memory, 0x0U);
parse_comp_operand_value("0xp3", &memory, 0x1U);
parse_comp_operand_value("0xq3", &memory, 0x0U);
parse_comp_operand_value("0xr3", &memory, 0x1U);
parse_comp_operand_value("0xs3", &memory, 0x0U);
parse_comp_operand_value("0xt3", &memory, 0x1U);
parse_comp_operand_value("0xm5", &memory, 0x0U); /* out of range */
}
{
/*------------------------------------------------------------------------
TestVariableGetValueDelta
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_operand_t op;
const char* memaddr;
memory.ram = ram;
memory.size = sizeof(ram);
memaddr = "d0xh1";
parse_operand(&op, &memaddr);
assert(rc_evaluate_operand(&op, peek, &memory, NULL) == 0x00); /* first call gets uninitialized value */
assert(rc_evaluate_operand(&op, peek, &memory, NULL) == 0x12); /* second gets current value */
/* RC_OPERAND_DELTA is always one frame behind */
ram[1] = 0x13;
assert(rc_evaluate_operand(&op, peek, &memory, NULL) == 0x12U);
ram[1] = 0x14;
assert(rc_evaluate_operand(&op, peek, &memory, NULL) == 0x13U);
ram[1] = 0x15;
assert(rc_evaluate_operand(&op, peek, &memory, NULL) == 0x14U);
ram[1] = 0x16;
assert(rc_evaluate_operand(&op, peek, &memory, NULL) == 0x15U);
assert(rc_evaluate_operand(&op, peek, &memory, NULL) == 0x16U);
assert(rc_evaluate_operand(&op, peek, &memory, NULL) == 0x16U);
}
}
static void parse_condition(rc_condition_t* self, const char* memaddr) {
int ret;
rc_scratch_t scratch;
ret = 0;
rc_parse_condition(&ret, self, &scratch, &memaddr, NULL, 0);
assert(ret >= 0);
assert(*memaddr == 0);
}
static void parse_comp_condition(
const char* memaddr, char expected_type,
char expected_left_type, char expected_left_size, unsigned expected_left_value,
char expected_operator,
char expected_right_type, char expected_right_size, unsigned expected_right_value,
int expected_required_hits
) {
rc_condition_t self;
parse_condition(&self, memaddr);
assert(self.type == expected_type);
comp_operand(&self.operand1, expected_left_type, expected_left_size, expected_left_value);
assert(self.oper == expected_operator);
comp_operand(&self.operand2, expected_right_type, expected_right_size, expected_right_value);
assert(self.required_hits == expected_required_hits);
}
static void parse_test_condition(const char* memaddr, memory_t* memory, int value) {
rc_condition_t self;
int ret;
rc_scratch_t scratch;
ret = 0;
rc_parse_condition(&ret, &self, &scratch, &memaddr, NULL, 0);
assert(ret >= 0);
assert(*memaddr == 0);
ret = rc_test_condition(&self, 0, peek, memory, NULL);
assert((ret && value) || (!ret && !value));
}
static void test_condition(void) {
{
/*------------------------------------------------------------------------
TestParseConditionMemoryComparisonValue
------------------------------------------------------------------------*/
/* different comparisons */
parse_comp_condition(
"0xH1234=8",
RC_CONDITION_STANDARD,
RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U,
RC_CONDITION_EQ,
RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U,
0
);
parse_comp_condition(
"0xH1234==8",
RC_CONDITION_STANDARD,
RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U,
RC_CONDITION_EQ,
RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U,
0
);
parse_comp_condition(
"0xH1234!=8",
RC_CONDITION_STANDARD,
RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U,
RC_CONDITION_NE,
RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U,
0
);
parse_comp_condition(
"0xH1234<8",
RC_CONDITION_STANDARD,
RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U,
RC_CONDITION_LT,
RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U,
0
);
parse_comp_condition(
"0xH1234<=8",
RC_CONDITION_STANDARD,
RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U,
RC_CONDITION_LE,
RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U,
0
);
parse_comp_condition(
"0xH1234>8",
RC_CONDITION_STANDARD,
RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U,
RC_CONDITION_GT,
RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U,
0
);
parse_comp_condition(
"0xH1234>=8",
RC_CONDITION_STANDARD,
RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U,
RC_CONDITION_GE,
RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U,
0
);
/* delta */
parse_comp_condition(
"d0xH1234=8",
RC_CONDITION_STANDARD,
RC_OPERAND_DELTA, RC_OPERAND_8_BITS, 0x1234U,
RC_CONDITION_EQ,
RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U,
0
);
/* flags */
parse_comp_condition(
"R:0xH1234=8",
RC_CONDITION_RESET_IF,
RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U,
RC_CONDITION_EQ,
RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U,
0
);
parse_comp_condition(
"P:0xH1234=8",
RC_CONDITION_PAUSE_IF,
RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U,
RC_CONDITION_EQ,
RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U,
0
);
parse_comp_condition(
"A:0xH1234=8",
RC_CONDITION_ADD_SOURCE,
RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U,
RC_CONDITION_EQ,
RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U,
0
);
parse_comp_condition(
"B:0xH1234=8",
RC_CONDITION_SUB_SOURCE,
RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U,
RC_CONDITION_EQ,
RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U,
0
);
parse_comp_condition(
"C:0xH1234=8",
RC_CONDITION_ADD_HITS,
RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U,
RC_CONDITION_EQ,
RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U,
0
);
/* hit count */
parse_comp_condition(
"0xH1234=8(1)",
RC_CONDITION_STANDARD,
RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U,
RC_CONDITION_EQ,
RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U,
1
);
parse_comp_condition(
"0xH1234=8.1.", /* legacy format */
RC_CONDITION_STANDARD,
RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U,
RC_CONDITION_EQ,
RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U,
1
);
parse_comp_condition(
"0xH1234=8(100)",
RC_CONDITION_STANDARD,
RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U,
RC_CONDITION_EQ,
RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U,
100
);
}
{
/*------------------------------------------------------------------------
TestParseConditionMemoryComparisonHexValue
------------------------------------------------------------------------*/
/* hex value is interpreted as a 16-bit memory reference */
parse_comp_condition(
"0xH1234=0x80",
RC_CONDITION_STANDARD,
RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U,
RC_CONDITION_EQ,
RC_OPERAND_ADDRESS, RC_OPERAND_16_BITS, 0x80U,
0
);
}
{
/*------------------------------------------------------------------------
TestParseConditionMemoryComparisonMemory
------------------------------------------------------------------------*/
parse_comp_condition(
"0xL1234!=0xU3456",
RC_CONDITION_STANDARD,
RC_OPERAND_ADDRESS, RC_OPERAND_LOW, 0x1234U,
RC_CONDITION_NE,
RC_OPERAND_ADDRESS, RC_OPERAND_HIGH, 0x3456U,
0
);
}
{
/*------------------------------------------------------------------------
TestConditionCompare
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
memory.ram = ram;
memory.size = sizeof(ram);
/* values */
parse_test_condition("0xH0001=18", &memory, 1);
parse_test_condition("0xH0001!=18", &memory, 0);
parse_test_condition("0xH0001<=18", &memory, 1);
parse_test_condition("0xH0001>=18", &memory, 1);
parse_test_condition("0xH0001<18", &memory, 0);
parse_test_condition("0xH0001>18", &memory, 0);
parse_test_condition("0xH0001>0", &memory, 1);
parse_test_condition("0xH0001!=0", &memory, 1);
/* memory */
parse_test_condition("0xH0001<0xH0002", &memory, 1);
parse_test_condition("0xH0001>0xH0002", &memory, 0);
parse_test_condition("0xH0001=0xH0001", &memory, 1);
parse_test_condition("0xH0001!=0xH0002", &memory, 1);
}
{
/*------------------------------------------------------------------------
TestConditionCompareDelta
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_condition_t cond;
memory.ram = ram;
memory.size = sizeof(ram);
parse_condition(&cond, "0xH0001>d0xH0001");
/* initial delta value is 0, 0x12 > 0 */
assert(rc_test_condition(&cond, 0, peek, &memory, NULL) == 1);
/* delta value is now 0x12, 0x12 = 0x12 */
assert(rc_test_condition(&cond, 0, peek, &memory, NULL) == 0);
/* delta value is now 0x12, 0x11 < 0x12 */
ram[1] = 0x11;
assert(rc_test_condition(&cond, 0, peek, &memory, NULL) == 0);
/* delta value is now 0x13, 0x12 > 0x11 */
ram[1] = 0x12;
assert(rc_test_condition(&cond, 0, peek, &memory, NULL) == 1);
}
}
static void parse_trigger(rc_trigger_t** self, void* buffer, const char* memaddr) {
assert(rc_trigger_size(memaddr) >= 0);
*self = rc_parse_trigger(buffer, memaddr, NULL, 0);
assert(*self != NULL);
}
static void comp_trigger(rc_trigger_t* self, memory_t* memory, int expected_result) {
int ret = rc_test_trigger(self, peek, memory, NULL);
assert(expected_result == ret);
}
static rc_condition_t* condset_get_cond(rc_condset_t* condset, int ndx) {
rc_condition_t* cond = condset->conditions;
while (ndx-- != 0) {
assert(cond != NULL);
cond = cond->next;
}
assert(cond != NULL);
return cond;
}
static rc_condset_t* trigger_get_set(rc_trigger_t* trigger, int ndx) {
rc_condset_t* condset = trigger->alternative;
if (ndx-- == 0) {
assert(trigger->requirement != NULL);
return trigger->requirement;
}
while (ndx-- != 0) {
condset = condset->next;
assert(condset != NULL);
}
assert(condset != NULL);
return condset;
}
static void test_trigger(void) {
{
/*------------------------------------------------------------------------
TestSimpleSets
Only standard conditions, no alt groups
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "0xH0001=18"); /* one condition, true */
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U);
parse_trigger(&trigger, buffer, "0xH0001!=18"); /* one condition, false */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
parse_trigger(&trigger, buffer, "0xH0001=18_0xH0002=52"); /* two conditions, true */
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
parse_trigger(&trigger, buffer, "0xH0001=18_0xH0002>52"); /* two conditions, false */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U);
parse_trigger(&trigger, buffer, "0xH0001=18_0xH0002=52_0xL0004=6"); /* three conditions, true */
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 1U);
parse_trigger(&trigger, buffer, "0xH0001=16_0xH0002=52_0xL0004=6"); /* three conditions, first false */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 1U);
parse_trigger(&trigger, buffer, "0xH0001=18_0xH0002=50_0xL0004=6"); /* three conditions, first false */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 1U);
parse_trigger(&trigger, buffer, "0xH0001=18_0xH0002=52_0xL0004=4"); /* three conditions, first false */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U);
parse_trigger(&trigger, buffer, "0xH0001=16_0xH0002=50_0xL0004=4"); /* three conditions, all false */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U);
}
{
/*------------------------------------------------------------------------
TestPauseIf
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "0xH0001=18_P:0xH0002=52_P:0xL0x0004=6");
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); /* Also true, but processing stops on first PauseIf */
ram[2] = 0;
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); /* PauseIf goes to 0 when false */
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 1U); /* PauseIf stays at 1 when false */
ram[4] = 0;
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); /* PauseIf goes to 0 when false */
}
{
/*------------------------------------------------------------------------
TestPauseIfHitCountOne
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "0xH0001=18_P:0xH0002=52.1.");
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
ram[2] = 0;
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); /* PauseIf with HitCount doesn't automatically go back to 0 */
}
{
/*------------------------------------------------------------------------
TestPauseIfHitCountTwo
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "0xH0001=18_P:0xH0002=52.2.");
comp_trigger(trigger, &memory, 1); /* PauseIf counter hasn't reached HitCount target, non-PauseIf condition still true */
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
comp_trigger(trigger, &memory, 0); /* PauseIf counter has reached HitCount target, non-PauseIf conditions ignored */
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U);
ram[2] = 0;
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U); /* PauseIf with HitCount doesn't automatically go back to 0 */
}
{
/*------------------------------------------------------------------------
TestPauseIfHitReset
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "0xH0001=18_P:0xH0002=52.1._R:0xH0003=1SR:0xH0003=2");
comp_trigger(trigger, &memory, 0); /* Trigger PauseIf, non-PauseIf conditions ignored */
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 0U);
ram[2] = 0;
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); /* PauseIf with HitCount doesn't automatically go back to 0 */
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 0U);
ram[3] = 1;
comp_trigger(trigger, &memory, 0); /* ResetIf in Paused group is ignored */
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 0U);
ram[3] = 2;
comp_trigger(trigger, &memory, 0); /* ResetIf in alternate group is honored, PauseIf does not retrigger and non-PauseIf condition is true */
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* ResetIf causes entire achievement to fail */
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 0U);
ram[3] = 3;
comp_trigger(trigger, &memory, 1); /* ResetIf no longer true, achievement allowed to trigger */
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 0U);
}
{
/*------------------------------------------------------------------------
TestResetIf
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "0xH0001=18_R:0xH0002=50_R:0xL0x0004=4");
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U);
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U);
ram[2] = 50;
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); /* True, but ResetIf also resets true marker */
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U);
ram[4] = 0x54;
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); /* True, but ResetIf also resets true marker */
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); /* Also true, but processing stop on first ResetIf */
ram[2] = 52;
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); /* True, but ResetIf also resets true marker */
ram[4] = 0x56;
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U);
}
{
/*------------------------------------------------------------------------
TestHitCount
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "0xH0001=20(2)_0xH0002=52");
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
ram[1] = 20;
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U);
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 3U);
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); /* hits stop increment once count it reached */
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 4U);
}
{
/*------------------------------------------------------------------------
TestHitCountResetIf
Verifies that ResetIf resets HitCounts
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "0xH0001=18(2)_0xH0002=52_R:0xL0004=4");
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U);
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 3U);
ram[4] = 0x54;
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U);
ram[4] = 0x56;
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U);
}
{
/*------------------------------------------------------------------------
TestHitCountResetIfHitCount
Verifies that ResetIf with HitCount target only resets HitCounts when target is met
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "0xH0001=18(2)_0xH0002=52_R:0xL0004=4.2.");
comp_trigger(trigger, &memory, 0); /* HitCounts on conditions 1 and 2 are incremented */
comp_trigger(trigger, &memory, 1); /* HitCounts on conditions 1 and 2 are incremented, cond 1 is now true so entire achievement is true */
comp_trigger(trigger, &memory, 1); /* HitCount on condition 2 is incremented, cond 1 already met its target HitCount */
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 3U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); /* ResetIf HitCount should still be 0 */
ram[4] = 0x54;
/* first hit on ResetIf should not reset anything */
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); /* condition 1 stopped at it's HitCount target */
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 4U); /* condition 2 continues to increment */
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 1U); /* ResetIf HitCount should be 1 */
/* second hit on ResetIf should reset everything */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); /* ResetIf HitCount should also be reset */
}
{
/*------------------------------------------------------------------------
TestAddHitsResetIf
Verifies that ResetIf works with AddHits
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "C:0xH0001=18_R:0xL0004=6(3)"); /* never(repeated(3, byte(1) == 18 || low(4) == 6)) */
comp_trigger(trigger, &memory, 1); /* result is true, no non-reset conditions */
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
comp_trigger(trigger, &memory, 0); /* total hits met (2 for each condition, only needed 3 total) (2 hits on condition 2 is not enough), result is always false if reset */
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U);
}
{
/*------------------------------------------------------------------------
TestHitCountResetIfHitCountOne
Verifies that ResetIf HitCount(1) behaves like ResetIf without a HitCount
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "0xH0001=18(2)_0xH0002=52_R:0xL0004=4.1.");
comp_trigger(trigger, &memory, 0); /* HitCounts on conditions 1 and 2 are incremented */
comp_trigger(trigger, &memory, 1); /* HitCounts on conditions 1 and 2 are incremented, cond 1 is now true so entire achievement is true */
comp_trigger(trigger, &memory, 1); /* HitCount on condition 2 is incremented, cond 1 already met its target HitCount */
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 3U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); /* ResetIf HitCount should still be 0 */
ram[4] = 0x54;
/* ResetIf HitCount(1) should behave just like ResetIf without a HitCount - all items, including ResetIf should be reset. */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); /* ResetIf HitCount should also be reset */
}
{
/*------------------------------------------------------------------------
TestHitCountPauseIf
Verifies that PauseIf stops HitCount processing
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "0xH0001=18(2)_0xH0002=52_P:0xL0004=4");
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
ram[4] = 0x54;
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
ram[4] = 0x56;
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U);
ram[4] = 0x54;
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U);
ram[4] = 0x56;
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 3U);
}
{
/*------------------------------------------------------------------------
TestHitCountPauseIfResetIf
Verifies that PauseIf prevents ResetIf processing
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "0xH0001=18(2)_R:0xH0002=50_P:0xL0004=4");
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
ram[4] = 0x54; /* pause */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
ram[2] = 50; /* reset (but still paused) */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
ram[4] = 0x56; /* unpause (still reset) */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
ram[2] = 52; /* unreset */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U);
}
{
/*------------------------------------------------------------------------
TestAddSource
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "A:0xH0001=0_0xH0002=22");
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U);
ram[2] = 4; /* sum is correct */
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* AddSource condition does not have hit tracking */
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
ram[1] = 0; /* first condition is true, but not sum */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* AddSource condition does not have hit tracking */
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
ram[2] = 22; /* first condition is true, sum is correct */
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* AddSource condition does not have hit tracking */
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U);
}
{
/*------------------------------------------------------------------------
TestSubSource
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "B:0xH0002=0_0xH0001=14"); /* NOTE: SubSource subtracts the first value from the second! */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U);
ram[2] = 4; /* difference is correct */
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* SubSource condition does not have hit tracking */
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
ram[1] = 0; /* first condition is true, but not difference */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* SubSource condition does not have hit tracking */
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
ram[2] = 14; /* first condition is true, value is negative inverse of expected value */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* SubSource condition does not have hit tracking */
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
ram[1] = 28; /* difference is correct again */
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* SubSource condition does not have hit tracking */
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U);
}
{
/*------------------------------------------------------------------------
TestAddSubSource
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "A:0xH0001=0_B:0xL0002=0_0xL0004=14"); /* byte(1) - low(2) + low(4) == 14 */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U);
ram[1] = 12; /* total is correct */
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* AddSource condition does not have hit tracking */
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); /* SubSource condition does not have hit tracking */
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 1U);
ram[1] = 0; /* first condition is true, but not total */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* AddSource condition does not have hit tracking */
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); /* SubSource condition does not have hit tracking */
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 1U);
ram[4] = 18; /* byte(4) would make total true, but not low(4) */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* AddSource condition does not have hit tracking */
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); /* SubSource condition does not have hit tracking */
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 1U);
ram[2] = 1;
ram[4] = 15; /* difference is correct again */
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* AddSource condition does not have hit tracking */
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); /* SubSource condition does not have hit tracking */
assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 2U);
}
{
/*------------------------------------------------------------------------
TestAddHits
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
rc_condset_t* condset;
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "C:0xH0001=18(2)_0xL0004=6(4)"); /* repeated(4, byte(1) == 18 || low(4) == 6) */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
comp_trigger(trigger, &memory, 1); /* total hits met (2 for each condition) */
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U);
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); /* threshold met, stop incrementing */
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U); /* total met prevents incrementing even though individual tally has not reached total */
rc_reset_condset(trigger->requirement);
for (condset = trigger->alternative; condset != NULL; condset = condset->next) {
rc_reset_condset(condset);
}
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U);
ram[1] = 16;
comp_trigger(trigger, &memory, 0); /* 1 + 2 < 4, not met */
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U);
comp_trigger(trigger, &memory, 1); /* 1 + 3 = 4, met */
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 3U);
}
{
/*------------------------------------------------------------------------
TestAltGroups
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "0xH0001=16S0xH0002=52S0xL0004=6");
/* core not true, both alts are */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 1U);
ram[1] = 16; /* core and both alts true */
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 2U);
assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 2U);
ram[4] = 0; /* core and first alt true */
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 3U);
assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 2U);
ram[2] = 0; /* core true, but neither alt is */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 3U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 3U);
assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 2U);
ram[4] = 6; /* core and second alt true */
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 4U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 3U);
assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 3U);
}
{
/*------------------------------------------------------------------------
TestResetIfInAltGroup
Verifies that a ResetIf resets everything regardless of where it is
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "0xH0001=18(1)_R:0xH0000=1S0xH0002=52(1)S0xL0004=6(1)_R:0xH0000=2");
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 1U);
ram[0] = 1; /* reset in core group resets everything */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 0U);
ram[0] = 0;
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 1U);
ram[0] = 2; /* reset in alt group resets everything */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 0U);
assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 0U);
}
{
/*------------------------------------------------------------------------
TestPauseIfInAltGroup
Verifies that PauseIf only pauses the group it's in
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "0xH0001=18_P:0xH0000=1S0xH0002=52S0xL0004=6_P:0xH0000=2");
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 1U);
ram[0] = 1; /* pause in core group only pauses core group */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 2U);
assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 2U);
ram[0] = 0;
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 3U);
assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 3U);
ram[0] = 2; /* pause in alt group only pauses alt group */
comp_trigger(trigger, &memory, 1);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 3U);
assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 4U);
assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 3U);
}
{
/*------------------------------------------------------------------------
TestPauseIfResetIfAltGroup
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
parse_trigger(&trigger, buffer, "0xH0000=0.1._0xH0000=2SP:0xH0001=18_R:0xH0002=52");
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
ram[0] = 1; /* move off HitCount */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
ram[1] = 16; /* unpause alt group, HitCount should be reset */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U);
ram[0] = 0;
ram[1] = 18; /* repause alt group, reset hitcount target, hitcount should be set */
comp_trigger(trigger, &memory, 0);
assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U);
ram[0] = 2; /* trigger win condition. alt group has no normal conditions, it should be considered false */
comp_trigger(trigger, &memory, 0);
}
}
static void parse_comp_term(const char* memaddr, char expected_var_size, unsigned expected_address, int is_bcd, int is_const) {
rc_term_t self;
rc_scratch_t scratch;
int ret;
ret = 0;
rc_parse_term(&ret, &self, &scratch, &memaddr, NULL, 0);
assert(ret >= 0);
assert(*memaddr == 0);
assert(is_const || self.operand1.size == expected_var_size);
assert(self.operand1.value == expected_address);
assert(self.operand1.is_bcd == is_bcd);
assert(self.invert == 0);
assert(self.operand2.size == RC_OPERAND_8_BITS);
assert(self.operand2.value == 0U);
assert(!is_const || self.operand1.type == RC_OPERAND_CONST);
}
static void parse_comp_term_fp(const char* memaddr, char expected_var_size, unsigned expected_address, double fp) {
rc_term_t self;
rc_scratch_t scratch;
int ret;
ret = 0;
rc_parse_term(&ret, &self, &scratch, &memaddr, NULL, 0);
assert(ret >= 0);
assert(*memaddr == 0);
assert(self.operand1.size == expected_var_size);
assert(self.operand1.value == expected_address);
assert(self.operand2.type == RC_OPERAND_FP);
assert(self.operand2.fp_value == fp);
}
static void parse_comp_term_mem(const char* memaddr, char expected_size_1, unsigned expected_address_1, char expected_size_2, unsigned expected_address_2) {
rc_term_t self;
rc_scratch_t scratch;
int ret;
ret = 0;
rc_parse_term(&ret, &self, &scratch, &memaddr, NULL, 0);
assert(ret >= 0);
assert(*memaddr == 0);
assert(self.operand1.size == expected_size_1);
assert(self.operand1.value == expected_address_1);
assert(self.operand2.size == expected_size_2);
assert(self.operand2.value == expected_address_2);
}
static void parse_comp_term_value(const char* memaddr, memory_t* memory, unsigned value) {
rc_term_t self;
rc_scratch_t scratch;
int ret;
ret = 0;
rc_parse_term(&ret, &self, &scratch, &memaddr, NULL, 0);
assert(ret >= 0);
assert(*memaddr == 0);
assert(rc_evaluate_term(&self, peek, memory, NULL) == value);
}
static void test_term(void) {
{
/*------------------------------------------------------------------------
TestClauseParseFromString
------------------------------------------------------------------------*/
/* sizes */
parse_comp_term("0xH1234", RC_OPERAND_8_BITS, 0x1234U, 0, 0);
parse_comp_term("0x 1234", RC_OPERAND_16_BITS, 0x1234U, 0, 0);
parse_comp_term("0x1234", RC_OPERAND_16_BITS, 0x1234U, 0, 0);
parse_comp_term("0xW1234", RC_OPERAND_24_BITS, 0x1234U, 0, 0);
parse_comp_term("0xX1234", RC_OPERAND_32_BITS, 0x1234U, 0, 0);
parse_comp_term("0xL1234", RC_OPERAND_LOW, 0x1234U, 0, 0);
parse_comp_term("0xU1234", RC_OPERAND_HIGH, 0x1234U, 0, 0);
parse_comp_term("0xM1234", RC_OPERAND_BIT_0, 0x1234U, 0, 0);
parse_comp_term("0xN1234", RC_OPERAND_BIT_1, 0x1234U, 0, 0);
parse_comp_term("0xO1234", RC_OPERAND_BIT_2, 0x1234U, 0, 0);
parse_comp_term("0xP1234", RC_OPERAND_BIT_3, 0x1234U, 0, 0);
parse_comp_term("0xQ1234", RC_OPERAND_BIT_4, 0x1234U, 0, 0);
parse_comp_term("0xR1234", RC_OPERAND_BIT_5, 0x1234U, 0, 0);
parse_comp_term("0xS1234", RC_OPERAND_BIT_6, 0x1234U, 0, 0);
parse_comp_term("0xT1234", RC_OPERAND_BIT_7, 0x1234U, 0, 0);
/* BCD */
parse_comp_term("B0xH1234", RC_OPERAND_8_BITS, 0x1234U, 1, 0);
parse_comp_term("B0xX1234", RC_OPERAND_32_BITS, 0x1234U, 1, 0);
parse_comp_term("b0xH1234", RC_OPERAND_8_BITS, 0x1234U, 1, 0);
/* Value */
parse_comp_term("V1234", 0, 1234, 0, 1);
parse_comp_term("V+1", 0, 1, 0, 1);
parse_comp_term("V-1", 0, 0xFFFFFFFFU, 0, 1);
parse_comp_term("V-2", 0, 0xFFFFFFFEU, 0, 1); /* twos compliment still works for addition */
}
{
/*------------------------------------------------------------------------
TestClauseParseFromStringMultiply
------------------------------------------------------------------------*/
parse_comp_term_fp("0xH1234", RC_OPERAND_8_BITS, 0x1234U, 1.0);
parse_comp_term_fp("0xH1234*1", RC_OPERAND_8_BITS, 0x1234U, 1.0);
parse_comp_term_fp("0xH1234*3", RC_OPERAND_8_BITS, 0x1234U, 3.0);
parse_comp_term_fp("0xH1234*0.5", RC_OPERAND_8_BITS, 0x1234U, 0.5);
parse_comp_term_fp("0xH1234*-1", RC_OPERAND_8_BITS, 0x1234U, -1.0);
}
{
/*------------------------------------------------------------------------
TestClauseParseFromStringMultiplyAddress
------------------------------------------------------------------------*/
parse_comp_term_mem("0xH1234", RC_OPERAND_8_BITS, 0x1234U, RC_OPERAND_8_BITS, 0U);
parse_comp_term_mem("0xH1234*0xH3456", RC_OPERAND_8_BITS, 0x1234U, RC_OPERAND_8_BITS, 0x3456U);
parse_comp_term_mem("0xH1234*0xL2222", RC_OPERAND_8_BITS, 0x1234U, RC_OPERAND_LOW, 0x2222U);
parse_comp_term_mem("0xH1234*0x1111", RC_OPERAND_8_BITS, 0x1234U, RC_OPERAND_16_BITS, 0x1111U);
}
{
/*------------------------------------------------------------------------
TestClauseGetValue
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
memory.ram = ram;
memory.size = sizeof(ram);
/* value */
parse_comp_term_value("V6", &memory, 6);
parse_comp_term_value("V6*2", &memory, 12);
parse_comp_term_value("V6*0.5", &memory, 3);
/* memory */
parse_comp_term_value("0xH01", &memory, 0x12);
parse_comp_term_value("0x0001", &memory, 0x3412);
/* BCD encoding */
parse_comp_term_value("B0xH01", &memory, 12);
parse_comp_term_value("B0x0001", &memory, 3412);
/* multiplication */
parse_comp_term_value("0xH01*4", &memory, 0x12 * 4); /* multiply by constant */
parse_comp_term_value("0xH01*0.5", &memory, 0x12 / 2); /* multiply by fraction */
parse_comp_term_value("0xH01*0xH02", &memory, 0x12 * 0x34); /* multiply by second address */
parse_comp_term_value("0xH01*0xT02", &memory, 0); /* multiply by bit */
parse_comp_term_value("0xH01*~0xT02", &memory, 0x12); /* multiply by inverse bit */
parse_comp_term_value("0xH01*~0xH02", &memory, 0x12 * (0x34 ^ 0xff)); /* multiply by inverse byte */
}
}
static void parse_comp_value(const char* memaddr, memory_t* memory, unsigned expected_value) {
rc_value_t* self;
char buffer[2048];
int ret;
ret = rc_value_size(memaddr);
assert(ret >= 0);
self = rc_parse_value(buffer, memaddr, NULL, 0);
assert(self != NULL);
assert(rc_evaluate_value(self, peek, memory, NULL) == expected_value);
}
static void test_value(void) {
{
/*------------------------------------------------------------------------
TestAdditionSimple
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
memory.ram = ram;
memory.size = sizeof(ram);
parse_comp_value("0xH0001_0xH0002", &memory, 0x12U + 0x34U); /* TestAdditionSimple */
parse_comp_value("0xH0001*100_0xH0002*0.5_0xL0003", &memory, 0x12U * 100 + 0x34U / 2 + 0x0B);/* TestAdditionComplex */
parse_comp_value("0xH0001$0xH0002", &memory, 0x34U);/* TestMaximumSimple */
parse_comp_value("0xH0001_0xH0004*3$0xH0002*0xL0003", &memory, 0x34U * 0xBU);/* TestMaximumComplex */
}
{
/*------------------------------------------------------------------------
TestFormatValue
------------------------------------------------------------------------*/
char buffer[64];
rc_format_value(buffer, sizeof(buffer), 12345, RC_FORMAT_VALUE);
assert(!strcmp("12345", buffer));
rc_format_value(buffer, sizeof(buffer), 12345, RC_FORMAT_OTHER);
assert(!strcmp("012345", buffer));
rc_format_value(buffer, sizeof(buffer), 12345, RC_FORMAT_SCORE);
assert(!strcmp("012345 Points", buffer));
rc_format_value(buffer, sizeof(buffer), 12345, RC_FORMAT_SECONDS);
assert(!strcmp("205:45", buffer));
rc_format_value(buffer, sizeof(buffer), 12345, RC_FORMAT_CENTISECS);
assert(!strcmp("02:03.45", buffer));
rc_format_value(buffer, sizeof(buffer), 12345, RC_FORMAT_FRAMES);
assert(!strcmp("03:25.75", buffer));
rc_format_value(buffer, sizeof(buffer), 345, RC_FORMAT_SECONDS);
assert(!strcmp("05:45", buffer));
rc_format_value(buffer, sizeof(buffer), 345, RC_FORMAT_CENTISECS);
assert(!strcmp("00:03.45", buffer));
rc_format_value(buffer, sizeof(buffer), 345, RC_FORMAT_FRAMES);
assert(!strcmp("00:05.75", buffer));
}
{
/*------------------------------------------------------------------------
TestParseMemValueFormat
------------------------------------------------------------------------*/
assert(rc_parse_format("VALUE") == RC_FORMAT_VALUE);
assert(rc_parse_format("SECS") == RC_FORMAT_SECONDS);
assert(rc_parse_format("TIMESECS") == RC_FORMAT_SECONDS);
assert(rc_parse_format("TIME") == RC_FORMAT_FRAMES);
assert(rc_parse_format("FRAMES") == RC_FORMAT_FRAMES);
assert(rc_parse_format("SCORE") == RC_FORMAT_SCORE);
assert(rc_parse_format("POINTS") == RC_FORMAT_SCORE);
assert(rc_parse_format("MILLISECS") == RC_FORMAT_CENTISECS);
assert(rc_parse_format("OTHER") == RC_FORMAT_OTHER);
assert(rc_parse_format("INVALID") == RC_FORMAT_VALUE);
}
}
static rc_lboard_t* parse_lboard(const char* memaddr, void* buffer) {
int ret;
rc_lboard_t* self;
ret = rc_lboard_size(memaddr);
assert(ret >= 0);
self = rc_parse_lboard(buffer, memaddr, NULL, 0);
assert(self != NULL);
return self;
}
static void lboard_check(const char* memaddr, int expected_ret) {
int ret = rc_lboard_size(memaddr);
assert(ret == expected_ret);
}
typedef struct {
int active, submitted;
}
lboard_test_state_t;
static void lboard_reset(rc_lboard_t* lboard, lboard_test_state_t* state) {
rc_reset_lboard(lboard);
state->active = state->submitted = 0;
}
static unsigned lboard_evaluate(rc_lboard_t* lboard, lboard_test_state_t* test, memory_t* memory) {
unsigned value;
switch (rc_evaluate_lboard(lboard, &value, peek, memory, NULL)) {
case RC_LBOARD_STARTED:
test->active = 1;
break;
case RC_LBOARD_CANCELED:
test->active = 0;
break;
case RC_LBOARD_TRIGGERED:
test->active = 0;
test->submitted = 1;
break;
}
return value;
}
static void test_lboard(void) {
{
/*------------------------------------------------------------------------
TestSimpleLeaderboard
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_lboard_t* lboard;
lboard_test_state_t state;
char buffer[2048];
unsigned value;
memory.ram = ram;
memory.size = sizeof(ram);
lboard = parse_lboard("STA:0xH00=1::CAN:0xH00=2::SUB:0xH00=3::VAL:0xH02", buffer);
state.active = state.submitted = 0;
assert(!state.active);
assert(!state.submitted);
value = lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
assert(!state.submitted);
ram[0] = 3; /* submit value, but not active */
value = lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
assert(!state.submitted);
ram[0] = 2; /* cancel value, but not active */
value = lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
assert(!state.submitted);
ram[0] = 1; /* start value */
value = lboard_evaluate(lboard, &state, &memory);
assert(state.active);
assert(!state.submitted);
ram[0] = 2; /* cancel value */
value = lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
assert(!state.submitted);
ram[0] = 3; /* submit value, but not active */
value = lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
assert(!state.submitted);
ram[0] = 1; /* start value */
value = lboard_evaluate(lboard, &state, &memory);
assert(state.active);
assert(!state.submitted);
ram[0] = 3; /* submit value */
value = lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
assert(state.submitted);
assert(value == 0x34U);
}
{
/*------------------------------------------------------------------------
TestStartAndCancelSameFrame
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_lboard_t* lboard;
lboard_test_state_t state;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
lboard = parse_lboard("STA:0xH00=0::CAN:0xH01=18::SUB:0xH00=3::VAL:0xH02", buffer);
state.active = state.submitted = 0;
lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
assert(!state.submitted);
ram[1] = 0x13; /* disables cancel */
lboard_evaluate(lboard, &state, &memory);
assert(state.active);
assert(!state.submitted);
ram[1] = 0x12; /* enables cancel */
lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
assert(!state.submitted);
ram[1] = 0x13; /* disables cancel, but start condition still true, so it shouldn't restart */
lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
assert(!state.submitted);
ram[0] = 0x01; /* disables start; no effect this frame, but next frame can restart */
lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
assert(!state.submitted);
ram[0] = 0x00; /* enables start */
lboard_evaluate(lboard, &state, &memory);
assert(state.active);
assert(!state.submitted);
}
{
/*------------------------------------------------------------------------
TestStartAndSubmitSameFrame
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_lboard_t* lboard;
lboard_test_state_t state;
char buffer[2048];
unsigned value;
memory.ram = ram;
memory.size = sizeof(ram);
lboard = parse_lboard("STA:0xH00=0::CAN:0xH01=10::SUB:0xH01=18::VAL:0xH02", buffer);
state.active = state.submitted = 0;
value = lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
assert(state.submitted);
assert(value == 0x34U);
ram[1] = 0; /* disable submit, value should not be resubmitted, */
value = lboard_evaluate(lboard, &state, &memory); /* start is still true, but leaderboard should not reactivate */
assert(!state.active);
ram[0] = 1; /* disable start */
value = lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
ram[0] = 0; /* reenable start, leaderboard should reactivate */
value = lboard_evaluate(lboard, &state, &memory);
assert(state.active);
}
{
/*------------------------------------------------------------------------
TestProgress
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_lboard_t* lboard;
lboard_test_state_t state;
char buffer[2048];
unsigned value;
memory.ram = ram;
memory.size = sizeof(ram);
lboard = parse_lboard("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04::VAL:0xH02", buffer);
state.active = state.submitted = 0;
value = lboard_evaluate(lboard, &state, &memory);
assert(state.active);
assert(value == 0x56U);
lboard = parse_lboard("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::VAL:0xH02", buffer);
state.active = state.submitted = 0;
value = lboard_evaluate(lboard, &state, &memory);
assert(state.active);
assert(value == 0x34U);
}
{
/*------------------------------------------------------------------------
TestStartAndCondition
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_lboard_t* lboard;
lboard_test_state_t state;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
lboard = parse_lboard("STA:0xH00=0_0xH01=0::CAN:0xH01=10::SUB:0xH01=18::VAL:0xH02", buffer);
state.active = state.submitted = 0;
lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
ram[1] = 0; /* second part of start condition is true */
lboard_evaluate(lboard, &state, &memory);
assert(state.active);
}
{
/*------------------------------------------------------------------------
TestStartOrCondition
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_lboard_t* lboard;
lboard_test_state_t state;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
lboard = parse_lboard("STA:S0xH00=1S0xH01=1::CAN:0xH01=10::SUB:0xH01=18::VAL:0xH02", buffer);
state.active = state.submitted = 0;
lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
ram[1] = 1; /* second part of start condition is true */
lboard_evaluate(lboard, &state, &memory);
assert(state.active);
ram[1] = 0;
lboard_reset(lboard, &state);
lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
ram[0] = 1; /* first part of start condition is true */
lboard_evaluate(lboard, &state, &memory);
assert(state.active);
}
{
/*------------------------------------------------------------------------
TestCancelOrCondition
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_lboard_t* lboard;
lboard_test_state_t state;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
lboard = parse_lboard("STA:0xH00=0::CAN:S0xH01=12S0xH02=12::SUB:0xH00=3::VAL:0xH02", buffer);
state.active = state.submitted = 0;
lboard_evaluate(lboard, &state, &memory);
assert(state.active);
ram[2] = 12; /* second part of cancel condition is true */
lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
ram[2] = 0; /* second part of cancel condition is false */
lboard_reset(lboard, &state);
lboard_evaluate(lboard, &state, &memory);
assert(state.active);
ram[1] = 12; /* first part of cancel condition is true */
lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
}
{
/*------------------------------------------------------------------------
TestSubmitAndCondition
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_lboard_t* lboard;
lboard_test_state_t state;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
lboard = parse_lboard("STA:0xH00=0::CAN:0xH01=10::SUB:0xH01=18_0xH03=18::VAL:0xH02", buffer);
state.active = state.submitted = 0;
lboard_evaluate(lboard, &state, &memory);
assert(state.active);
ram[3] = 18;
lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
assert(state.submitted);
}
{
/*------------------------------------------------------------------------
TestSubmitOrCondition
------------------------------------------------------------------------*/
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_lboard_t* lboard;
lboard_test_state_t state;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
lboard = parse_lboard("STA:0xH00=0::CAN:0xH01=10::SUB:S0xH01=12S0xH03=12::VAL:0xH02", buffer);
state.active = state.submitted = 0;
lboard_evaluate(lboard, &state, &memory);
assert(state.active);
ram[3] = 12; /* second part of submit condition is true */
lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
assert(state.submitted);
ram[3] = 0;
lboard_reset(lboard, &state);
lboard_evaluate(lboard, &state, &memory);
assert(state.active);
ram[1] = 12; /* first part of submit condition is true */
lboard_evaluate(lboard, &state, &memory);
assert(!state.active);
assert(state.submitted);
}
{
/*------------------------------------------------------------------------
TestUnparsableStringWillNotStart
We'll test for errors in the memaddr field instead
------------------------------------------------------------------------*/
lboard_check("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04::VAL:0xH02::GARBAGE", RC_INVALID_LBOARD_FIELD);
lboard_check("CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04::VAL:0xH02", RC_MISSING_START);
lboard_check("STA:0xH00=0::SUB:0xH00=3::PRO:0xH04::VAL:0xH02", RC_MISSING_CANCEL);
lboard_check("STA:0xH00=0::CAN:0xH00=2::PRO:0xH04::VAL:0xH02", RC_MISSING_SUBMIT);
lboard_check("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04", RC_MISSING_VALUE);
lboard_check("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04::VAL:0xH02::STA:0=0", RC_DUPLICATED_START);
lboard_check("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04::VAL:0xH02::CAN:0=0", RC_DUPLICATED_CANCEL);
lboard_check("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04::VAL:0xH02::SUB:0=0", RC_DUPLICATED_SUBMIT);
lboard_check("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04::VAL:0xH02::VAL:0", RC_DUPLICATED_VALUE);
lboard_check("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04::VAL:0xH02::PRO:0", RC_DUPLICATED_PROGRESS);
}
}
static void test_lua(void) {
{
/*------------------------------------------------------------------------
TestLua
------------------------------------------------------------------------*/
#ifndef RC_DISABLE_LUA
lua_State* L;
const char* luacheevo = "return { test = function(peek, ud) return peek(0, 4, ud) end }";
unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
rc_trigger_t* trigger;
char buffer[2048];
memory.ram = ram;
memory.size = sizeof(ram);
L = luaL_newstate();
luaL_loadbufferx(L, luacheevo, strlen(luacheevo), "luacheevo.lua", "t");
lua_call(L, 0, 1);
memory.ram = ram;
memory.size = sizeof(ram);
trigger = rc_parse_trigger(buffer, "@test=0xX0", L, 1);
assert(rc_test_trigger(trigger, peek, &memory, L) != 0);
#endif /* RC_DISABLE_LUA */
}
}
int main(void) {
test_operand();
test_condition();
test_trigger();
test_term();
test_value();
test_lboard();
test_lua();
return 0;
}