static void op_RAND_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t a = cs_pop(cs); if (a == -1) cs_push(cs, 0); else cs_push(cs, rand() % (a + 1)); }
// Check every op manipulates the stack correctly TEST op_stack_size() { for (size_t i = 0; i < E_OP__LENGTH; i++) { const tele_op_t *op = tele_ops[i]; if (op->get != NULL) { scene_state_t ss = {}; // initalise to empty // (needs dedicated initaliser) exec_state_t es; es_init(&es); es_push(&es); es_variables(&es)->script_number = 1; command_state_t cs; cs_init(&cs); // add params to stack (plus an extra 2, to check that too many // values aren't removed, warning: note the maximum stack size in // state.h) const int16_t stack_extra = 2; for (int j = 0; j < op->params + stack_extra; j++) cs_push(&cs, 0); // execute get op->get(op->data, &ss, &es, &cs); // check that the stack has the correct number of items in it if (op->returns) { ASSERT_EQm(op->name, cs_stack_size(&cs), stack_extra + 1); } else { ASSERT_EQm(op->name, cs_stack_size(&cs), stack_extra); } } if (op->set != NULL) { scene_state_t ss = {}; // initalise to empty // (needs dedicated initaliser) exec_state_t es; es_init(&es); command_state_t cs; cs_init(&cs); // add params to stack (plus an extra 2, to check that too many // values aren't removed, warning: note the maximum stack size in // state.h) // set functions require an extra value on the stack const int16_t stack_extra = 2; for (int j = 0; j < op->params + stack_extra + 1; j++) cs_push(&cs, 0); // execute get op->set(op->data, &ss, &es, &cs); // check that the stack has the correct number of items in it ASSERT_EQm(op->name, cs_stack_size(&cs), stack_extra); } } PASS(); }
static void op_MAX_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t a, b; a = cs_pop(cs); b = cs_pop(cs); if (a > b) cs_push(cs, a); else cs_push(cs, b); }
static void op_LIM_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t a, b, i; i = cs_pop(cs); a = cs_pop(cs); b = cs_pop(cs); if (i < a) cs_push(cs, a); else if (i > b) cs_push(cs, b); else cs_push(cs, i); }
static void op_R_get(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t min = ss->variables.r_min; int16_t max = ss->variables.r_max; if (max < min) { int16_t temp = min; min = max; max = temp; } int16_t range = max - min + 1; if (range == 0) cs_push(cs, min); else cs_push(cs, rand() % range + min); }
// Check every mod manipulates the stack correctly TEST mod_stack_size() { for (size_t i = 0; i < E_MOD__LENGTH; i++) { const tele_mod_t *mod = tele_mods[i]; scene_state_t ss = {}; // initalise to empty // (needs dedicated initaliser) exec_state_t es; es_init(&es); command_state_t cs; cs_init(&cs); // add params to stack (plus an extra 2, to check that too many // values aren't removed, warning: note the maximum stack size in // state.h) const int16_t stack_extra = 2; for (int j = 0; j < mod->params + stack_extra; j++) cs_push(&cs, 0); // execute func const tele_command_t sub_command = { .length = 1, .separator = 0, .data = { { .tag = OP, .value = E_OP_A } } }; mod->func(&ss, &es, &cs, &sub_command); // check that the stack has the correct number of items in it ASSERT_EQm(mod->name, cs_stack_size(&cs), stack_extra); }
static void op_peek_seed_i16(const void *data, scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { char *base = (char *)ss; size_t offset = (size_t)data; tele_rand_t *ptr = (tele_rand_t *)(base + offset); cs_push(cs, ptr->seed); }
static void op_MOD_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t a = cs_pop(cs); int16_t b = cs_pop(cs); int16_t out = b != 0 ? a % b : 0; cs_push(cs, out); }
static void op_MUL_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), exec_state_t *NOTUSED(es), command_state_t *cs) { int32_t r = cs_pop(cs); r *= cs_pop(cs); if (r > INT16_MAX) r = INT16_MAX; if (r < INT16_MIN) r = INT16_MIN; cs_push(cs, (int16_t)r); }
static void op_RRAND_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t a, b, min, max, range; a = cs_pop(cs); b = cs_pop(cs); if (a < b) { min = a; max = b; } else { min = b; max = a; } range = max - min + 1; if (range == 0) cs_push(cs, a); else cs_push(cs, rand() % range + min); }
static void op_QT_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), exec_state_t *NOTUSED(es), command_state_t *cs) { // this rounds negative numbers rather than quantize (choose closer) int16_t a, b, c, d, e; b = cs_pop(cs); a = cs_pop(cs); if (a == 0) { cs_push(cs, 0); return; } c = b / a; d = c * a; e = (c + 1) * a; if (abs(b - d) < abs(b - e)) cs_push(cs, d); else cs_push(cs, e); }
static void op_O_get(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t min = ss->variables.o_min; int16_t max = ss->variables.o_max; int16_t wrap = ss->variables.o_wrap; // restrict current_value to (wrapped) bounds int16_t current_value = normalise_value(min, max, wrap, ss->variables.o); cs_push(cs, current_value); // calculate new value ss->variables.o = normalise_value(min, max, wrap, current_value + ss->variables.o_inc); }
static void op_DRUNK_get(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t min = ss->variables.drunk_min; int16_t max = ss->variables.drunk_max; int16_t wrap = ss->variables.drunk_wrap; // restrict current_value to (wrapped) bounds int16_t current_value = normalise_value(min, max, wrap, ss->variables.drunk); cs_push(cs, current_value); // calculate new value int16_t new_value = current_value + (rand() % 3) - 1; ss->variables.drunk = normalise_value(min, max, wrap, new_value); }
static void op_WRAP_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t a, b, i, c; i = cs_pop(cs); a = cs_pop(cs); b = cs_pop(cs); if (a < b) { c = b - a + 1; while (i >= b) i -= c; while (i < a) i += c; } else { c = a - b + 1; while (i >= a) i -= c; while (i < b) i += c; } cs_push(cs, i); }
void TXReceive(uint8_t model, command_state_t *cs, uint8_t mode, bool shift) { // zero-index the output uint8_t input = cs_pop(cs) - 1; // send the port, device and address uint8_t port = input & 3; uint8_t device = input >> 2; uint8_t address = model + device; // inputs are numbered 0-7 for each device - shift is for the second half // mode pushes it up so it can read quantized values and note numbers port += (shift ? 4 : 0) + (mode << 3); // tell the device what value you are going to query uint8_t buffer[2]; buffer[0] = port; tele_ii_tx(address, buffer, 1); // now read the value buffer[0] = 0; buffer[1] = 0; tele_ii_rx(address, buffer, 2); int16_t value = (buffer[0] << 8) + buffer[1]; cs_push(cs, value); }
static void op_EZ_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), exec_state_t *NOTUSED(es), command_state_t *cs) { cs_push(cs, cs_pop(cs) == 0); }
static void op_AVG_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), exec_state_t *NOTUSED(es), command_state_t *cs) { int32_t ret = (((int32_t)cs_pop(cs) * 2) + ((int32_t)cs_pop(cs) * 2)) / 2; if (ret % 2) ret += 1; cs_push(cs, (int16_t)(ret / 2)); }
static void op_S_L_get(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { cs_push(cs, ss->stack_op.top); }
static void op_TOSS_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), exec_state_t *NOTUSED(es), command_state_t *cs) { cs_push(cs, rand() & 1); }
static void op_R_MAX_get(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { cs_push(cs, ss->variables.r_max); }
// run a single command inside a given exec_state process_result_t process_command(scene_state_t *ss, exec_state_t *es, const tele_command_t *c) { command_state_t cs; cs_init(&cs); // initialise this here as well as inside the loop, in case // the command has 0 length // 1. Do we have a PRE seperator? // ------------------------------ // if we do then only process the PRE part, the MOD will determine if the // POST should be run and take care of running it ssize_t start_idx = 0; ssize_t end_idx = c->separator == -1 ? c->length : c->separator; // 2. Determine the location of all the SUB commands // ------------------------------------------------- // an array of structs to hold the start and end of each sub command struct sub_idx { ssize_t start; ssize_t end; } subs[COMMAND_MAX_LENGTH]; ssize_t sub_len = 0; ssize_t sub_start = 0; // iterate through c->data to find all the SUB_SEPs and add to the array for (ssize_t idx = start_idx; idx < end_idx; idx++) { tele_word_t word_type = c->data[idx].tag; if (word_type == SUB_SEP && idx > sub_start) { subs[sub_len].start = sub_start; subs[sub_len].end = idx - 1; sub_len++; sub_start = idx + 1; } } // the last sub command won't have been added, manually add it here if (end_idx > sub_start) { subs[sub_len].start = sub_start; subs[sub_len].end = end_idx - 1; sub_len++; } // 3. Loop through each sub command and execute it // ----------------------------------------------- // iterate through sub commands from left to right for (ssize_t sub_idx = 0; sub_idx < sub_len && !es_variables(es)->breaking; sub_idx++) { const ssize_t sub_start = subs[sub_idx].start; const ssize_t sub_end = subs[sub_idx].end; // initialise the command state for each sub, otherwise a value left on // the stack for the previous sub, can cause the set fn to trigger when // it shouldn't cs_init(&cs); // as we are using a stack based language, we must process commands from // right to left for (ssize_t idx = sub_end; idx >= sub_start; idx--) { const tele_word_t word_type = c->data[idx].tag; const int16_t word_value = c->data[idx].value; if (word_type == NUMBER) { cs_push(&cs, word_value); } else if (word_type == OP) { const tele_op_t *op = tele_ops[word_value]; // if we're in the first command position, and there is a set fn // pointer and we have enough params, then run set, else run get if (idx == sub_start && op->set != NULL && cs_stack_size(&cs) >= op->params + 1) op->set(op->data, ss, es, &cs); else op->get(op->data, ss, es, &cs); } else if (word_type == MOD) { tele_command_t post_command; post_command.comment = false; copy_post_command(&post_command, c); tele_mods[word_value]->func(ss, es, &cs, &post_command); } } } // 4. Return // --------- // sometimes we have single value left of the stack, if so return it if (cs_stack_size(&cs)) { process_result_t o = {.has_value = true, .value = cs_pop(&cs) }; return o; }
static void op_I_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), exec_state_t *es, command_state_t *cs) { cs_push(cs, es_variables(es)->i); }
static void op_M_ACT_get(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { cs_push(cs, ss->variables.m_act); }
static void op_LAST_get(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t script_number = cs_pop(cs) - 1; int16_t last = ss_get_script_last(ss, script_number); cs_push(cs, last); }
static void op_FLIP_get(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { int16_t flip = ss->variables.flip; cs_push(cs, flip); ss->variables.flip = flip == 0; }
static void op_SEED_get(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { cs_push(cs, ss->variables.seed); }
static void op_M_SYM_EXCLAMATION_get(const void *NOTUSED(data), scene_state_t *ss, exec_state_t *NOTUSED(es), command_state_t *cs) { cs_push(cs, ss->variables.m); }