@@ -72,37 +72,39 @@ void process_pattern_keys(uint8_t k, uint8_t m, bool is_held_key) {
offset = 56;
}
else if (match_no_mod(m, k, HID_OPEN_BRACKET)) { // [
int16_t v = tele_get_pattern_val(pattern, base + offset);
int16_t v = ss_get_pattern_val(&scene_state, pattern, base + offset);
if (v > INT16_MIN) { // -32767
tele_set_pattern_val(pattern, base + offset, v - 1);
ss_set_pattern_val(&scene_state, pattern, base + offset, v - 1);
r_edit_dirty |= R_ALL;
}
}
else if (match_no_mod(m, k, HID_CLOSE_BRACKET)) { // ]
int16_t v = tele_get_pattern_val(pattern, base + offset);
int16_t v = ss_get_pattern_val(&scene_state, pattern, base + offset);
if (v < INT16_MAX) { // 32766
tele_set_pattern_val(pattern, base + offset, v + 1);
ss_set_pattern_val(&scene_state, pattern, base + offset, v + 1);
r_edit_dirty |= R_ALL;
}
}
else if (match_no_mod(m, k, HID_BACKSPACE)) { // backspace
int16_t v = tele_get_pattern_val(pattern, base + offset) / 10;
tele_set_pattern_val(pattern, base + offset, v);
int16_t v =
ss_get_pattern_val(&scene_state, pattern, base + offset) / 10;
ss_set_pattern_val(&scene_state, pattern, base + offset, v);
r_edit_dirty |= R_ALL;
}
else if (match_shift(m, k, HID_BACKSPACE)) { // shift + backspace
for (size_t i = base + offset; i < 63; i++) {
int16_t v = tele_get_pattern_val(pattern, i + 1);
tele_set_pattern_val(pattern, i, v);
int16_t v = ss_get_pattern_val(&scene_state, pattern, i + 1);
ss_set_pattern_val(&scene_state, pattern, i, v);
}

uint16_t l = tele_get_pattern_l(pattern);
if (l > base + offset) tele_set_pattern_l(pattern, l - 1);
uint16_t l = ss_get_pattern_len(&scene_state, pattern);
if (l > base + offset) ss_set_pattern_len(&scene_state, pattern, l - 1);
r_edit_dirty |= R_ALL;
}
else if (match_no_mod(m, k, HID_ENTER)) { // enter
uint16_t l = tele_get_pattern_l(pattern);
if (base + offset == l && l < 64) tele_set_pattern_l(pattern, l + 1);
uint16_t l = ss_get_pattern_len(&scene_state, pattern);
if (base + offset == l && l < 64)
ss_set_pattern_len(&scene_state, pattern, l + 1);
base++;
if (base == 8) {
base = 7;
@@ -112,49 +114,51 @@ void process_pattern_keys(uint8_t k, uint8_t m, bool is_held_key) {
}
else if (match_shift(m, k, HID_ENTER)) { // shift + enter
for (int i = 63; i > base + offset; i--) {
int16_t v = tele_get_pattern_val(pattern, i - 1);
tele_set_pattern_val(pattern, i, v);
int16_t v = ss_get_pattern_val(&scene_state, pattern, i - 1);
ss_set_pattern_val(&scene_state, pattern, i, v);
}
uint16_t l = tele_get_pattern_l(pattern);
if (l < 64) { tele_set_pattern_l(pattern, l + 1); }
uint16_t l = ss_get_pattern_len(&scene_state, pattern);
if (l < 64) { ss_set_pattern_len(&scene_state, pattern, l + 1); }
r_edit_dirty |= R_ALL;
}
else if (match_alt(m, k, HID_X)) { // alt + x = cut
copy_buffer = tele_get_pattern_val(pattern, base + offset);
copy_buffer = ss_get_pattern_val(&scene_state, pattern, base + offset);
for (int i = base + offset; i < 63; i++) {
int16_t v = tele_get_pattern_val(pattern, i + 1);
tele_set_pattern_val(pattern, i, v);
int16_t v = ss_get_pattern_val(&scene_state, pattern, i + 1);
ss_set_pattern_val(&scene_state, pattern, i, v);
}

uint16_t l = tele_get_pattern_l(pattern);
if (l > base + offset) { tele_set_pattern_l(pattern, l - 1); }
uint16_t l = ss_get_pattern_len(&scene_state, pattern);
if (l > base + offset) {
ss_set_pattern_len(&scene_state, pattern, l - 1);
}
r_edit_dirty |= R_ALL;
}
else if (match_alt(m, k, HID_C)) { // alt + c = copy
copy_buffer = tele_get_pattern_val(pattern, base + offset);
copy_buffer = ss_get_pattern_val(&scene_state, pattern, base + offset);
}
else if (match_alt(m, k, HID_V)) { // alt + v = paste
tele_set_pattern_val(pattern, base + offset, copy_buffer);
ss_set_pattern_val(&scene_state, pattern, base + offset, copy_buffer);
r_edit_dirty |= R_ALL;
}
else if (match_shift_alt(m, k, HID_V)) { // shift + alt + v = insert paste
for (int i = 63; i > base + offset; i--) {
int16_t v = tele_get_pattern_val(pattern, i - 1);
tele_set_pattern_val(pattern, i, v);
int16_t v = ss_get_pattern_val(&scene_state, pattern, i - 1);
ss_set_pattern_val(&scene_state, pattern, i, v);
}
uint16_t l = tele_get_pattern_l(pattern);
uint16_t l = ss_get_pattern_len(&scene_state, pattern);
if (l >= base + offset && l < 63) {
tele_set_pattern_l(pattern, l + 1);
ss_set_pattern_len(&scene_state, pattern, l + 1);
}
tele_set_pattern_val(pattern, base + offset, copy_buffer);
ss_set_pattern_val(&scene_state, pattern, base + offset, copy_buffer);
r_edit_dirty |= R_ALL;
}
else if (match_shift(m, k, HID_L)) { // shift + l
tele_set_pattern_l(pattern, base + offset + 1);
ss_set_pattern_len(&scene_state, pattern, base + offset + 1);
r_edit_dirty |= R_ALL;
}
else if (match_alt(m, k, HID_L)) { // alt + l
uint16_t l = tele_get_pattern_l(pattern);
uint16_t l = ss_get_pattern_len(&scene_state, pattern);
if (l) {
offset = ((l - 1) >> 3) << 3;
base = (l - 1) & 0x7;
@@ -171,11 +175,11 @@ void process_pattern_keys(uint8_t k, uint8_t m, bool is_held_key) {
r_edit_dirty |= R_ALL;
}
else if (match_shift(m, k, HID_S)) { // shift + s
tele_set_pattern_start(pattern, offset + base);
ss_set_pattern_start(&scene_state, pattern, offset + base);
r_edit_dirty |= R_ALL;
}
else if (match_alt(m, k, HID_S)) { // alt + s
int16_t start = tele_get_pattern_start(pattern);
int16_t start = ss_get_pattern_start(&scene_state, pattern);
if (start) {
offset = (start >> 3) << 3;
base = start & 0x7;
@@ -192,11 +196,11 @@ void process_pattern_keys(uint8_t k, uint8_t m, bool is_held_key) {
r_edit_dirty |= R_ALL;
}
else if (match_shift(m, k, HID_E)) { // shift + e
tele_set_pattern_end(pattern, offset + base);
ss_set_pattern_end(&scene_state, pattern, offset + base);
r_edit_dirty |= R_ALL;
}
else if (match_alt(m, k, HID_E)) { // alt + e
int16_t end = tele_get_pattern_end(pattern);
int16_t end = ss_get_pattern_end(&scene_state, pattern);
if (end) {
offset = (end >> 3) << 3;
base = end & 0x7;
@@ -213,41 +217,41 @@ void process_pattern_keys(uint8_t k, uint8_t m, bool is_held_key) {
r_edit_dirty |= R_ALL;
}
else if (match_no_mod(m, k, HID_UNDERSCORE)) { // -
int16_t v = tele_get_pattern_val(pattern, base + offset);
tele_set_pattern_val(pattern, base + offset, -v);
int16_t v = ss_get_pattern_val(&scene_state, pattern, base + offset);
ss_set_pattern_val(&scene_state, pattern, base + offset, -v);
r_edit_dirty |= R_ALL;
}
else if (match_no_mod(m, k, HID_SPACEBAR)) { // space
if (tele_get_pattern_val(pattern, base + offset))
tele_set_pattern_val(pattern, base + offset, 0);
if (ss_get_pattern_val(&scene_state, pattern, base + offset))
ss_set_pattern_val(&scene_state, pattern, base + offset, 0);
else
tele_set_pattern_val(pattern, base + offset, 1);
ss_set_pattern_val(&scene_state, pattern, base + offset, 1);
r_edit_dirty |= R_ALL;
}
else if ((m == HID_MODIFIER_NONE) && (k >= HID_1 && k <= HID_0)) {
uint8_t n = (k - HID_1 + 1) % 10; // convert HID numbers to decimal,
// taking care of HID_0
int16_t v = tele_get_pattern_val(pattern, base + offset);
int16_t v = ss_get_pattern_val(&scene_state, pattern, base + offset);
if (v && v < 3276 && v > -3276) {
v = v * 10;
if (v > 0)
tele_set_pattern_val(pattern, base + offset, v + n);
ss_set_pattern_val(&scene_state, pattern, base + offset, v + n);
else
tele_set_pattern_val(pattern, base + offset, v - n);
ss_set_pattern_val(&scene_state, pattern, base + offset, v - n);
}
else
tele_set_pattern_val(pattern, base + offset, n);
ss_set_pattern_val(&scene_state, pattern, base + offset, n);
r_edit_dirty |= R_ALL;
}
}

void process_pattern_knob(uint16_t knob, uint8_t m) {
if (m == HID_MODIFIER_LEFT_CTRL || m == HID_MODIFIER_RIGHT_CTRL) {
if (m == HID_MODIFIER_LEFT_SHIFT || m == HID_MODIFIER_RIGHT_SHIFT) {
tele_set_pattern_val(pattern, base + offset, knob >> 2);
ss_set_pattern_val(&scene_state, pattern, base + offset, knob >> 2);
}
else {
tele_set_pattern_val(pattern, base + offset, knob >> 7);
ss_set_pattern_val(&scene_state, pattern, base + offset, knob >> 7);
}
r_edit_dirty |= R_ALL;
}
@@ -264,29 +268,29 @@ void screen_refresh_pattern() {

for (uint8_t x = 0; x < 4; x++) {
uint8_t a = 1;
if (tele_get_pattern_l(x) > y + offset) a = 6;
if (ss_get_pattern_len(&scene_state, x) > y + offset) a = 6;

itoa(tele_get_pattern_val(x, y + offset), s, 10);
itoa(ss_get_pattern_val(&scene_state, x, y + offset), s, 10);
font_string_region_clip_right(&line[y], s, (x + 1) * 30 + 4, 0, a,
0);

if (y + offset >= tele_get_pattern_start(x)) {
if (y + offset <= tele_get_pattern_end(x)) {
if (y + offset >= ss_get_pattern_start(&scene_state, x)) {
if (y + offset <= ss_get_pattern_end(&scene_state, x)) {
for (uint8_t i = 0; i < 8; i += 2) {
line[y].data[i * 128 + (x + 1) * 30 + 6] = 1;
}
}
}

if (y + offset == tele_get_pattern_i(x)) {
if (y + offset == ss_get_pattern_idx(&scene_state, x)) {
line[y].data[2 * 128 + (x + 1) * 30 + 6] = 11;
line[y].data[3 * 128 + (x + 1) * 30 + 6] = 11;
line[y].data[4 * 128 + (x + 1) * 30 + 6] = 11;
}
}
}

itoa(tele_get_pattern_val(pattern, base + offset), s, 10);
itoa(ss_get_pattern_val(&scene_state, pattern, base + offset), s, 10);
font_string_region_clip_right(&line[base], s, (pattern + 1) * 30 + 4, 0,
0xf, 0);

@@ -57,9 +57,9 @@ void process_preset_r_keys(uint8_t k, uint8_t m, bool is_held_key) {
}
else if (match_no_mod(m, k, HID_ENTER) && !is_held_key) { // enter
flash_read(preset_select);
tele_set_scene(preset_select);
ss_set_scene(&scene_state, preset_select);

run_script(INIT_SCRIPT);
run_script(&scene_state, INIT_SCRIPT);

set_mode(last_mode);
}
@@ -75,10 +75,10 @@ void tele_usb_disk() {
0xa, 0);
region_draw(&line[0]);

memcpy(tele_script_ptr(), &f.s[i].script,
tele_script_size());
memcpy(tele_patterns_ptr(), &f.s[i].patterns,
tele_patterns_size());
memcpy(ss_script_ptr(&scene_state), &f.s[i].script,
ss_script_size());
memcpy(ss_patterns_ptr(&scene_state), &f.s[i].patterns,
ss_patterns_size());
memcpy(&scene_text, &f.s[i].text, sizeof(scene_text));

if (!nav_file_create((FS_STRING)filename)) {
@@ -130,9 +130,12 @@ void tele_usb_disk() {
else
file_putc(s + 49);

for (int l = 0; l < tele_get_script_l(s); l++) {
for (int l = 0; l < ss_get_script_len(&scene_state, s);
l++) {
file_putc('\n');
print_command(tele_get_script_c(s, l), input);
print_command(
ss_get_script_command(&scene_state, s, l),
input);
file_write_buf((uint8_t*)input, strlen(input));
}
}
@@ -144,7 +147,7 @@ void tele_usb_disk() {
file_putc('\n');

for (int b = 0; b < 4; b++) {
itoa(tele_get_pattern_l(b), input, 10);
itoa(ss_get_pattern_len(&scene_state, b), input, 10);
file_write_buf((uint8_t*)input, strlen(input));
if (b == 3)
file_putc('\n');
@@ -153,7 +156,7 @@ void tele_usb_disk() {
}

for (int b = 0; b < 4; b++) {
itoa(tele_get_pattern_wrap(b), input, 10);
itoa(ss_get_pattern_wrap(&scene_state, b), input, 10);
file_write_buf((uint8_t*)input, strlen(input));
if (b == 3)
file_putc('\n');
@@ -162,7 +165,7 @@ void tele_usb_disk() {
}

for (int b = 0; b < 4; b++) {
itoa(tele_get_pattern_start(b), input, 10);
itoa(ss_get_pattern_start(&scene_state, b), input, 10);
file_write_buf((uint8_t*)input, strlen(input));
if (b == 3)
file_putc('\n');
@@ -171,7 +174,7 @@ void tele_usb_disk() {
}

for (int b = 0; b < 4; b++) {
itoa(tele_get_pattern_end(b), input, 10);
itoa(ss_get_pattern_end(&scene_state, b), input, 10);
file_write_buf((uint8_t*)input, strlen(input));
if (b == 3)
file_putc('\n');
@@ -183,7 +186,8 @@ void tele_usb_disk() {

for (int l = 0; l < 64; l++) {
for (int b = 0; b < 4; b++) {
itoa(tele_get_pattern_val(b, l), input, 10);
itoa(ss_get_pattern_val(&scene_state, b, l), input,
10);
file_write_buf((uint8_t*)input, strlen(input));
if (b == 3)
file_putc('\n');
@@ -306,8 +310,9 @@ void tele_usb_disk() {
validate(&temp, error_msg);

if (status == E_OK) {
overwrite_script_command(
s, l, &temp);
ss_overwrite_script_command(
&scene_state, s, l,
&temp);
memset(input, 0,
sizeof(input));
}
@@ -325,7 +330,8 @@ void tele_usb_disk() {
print_dbg(input);
char pcmd[32];
print_command(
tele_get_script_c(s, l),
ss_get_script_command(
&scene_state, s, l),
pcmd);
print_dbg(pcmd);
}
@@ -345,8 +351,9 @@ void tele_usb_disk() {
if (c == '\n' || c == '\t') {
if (b < 4) {
if (l > 3) {
tele_set_pattern_val(b, l - 4,
neg * num);
ss_set_pattern_val(&scene_state,
b, l - 4,
neg * num);
// print_dbg("\r\nset: ");
// print_dbg_ulong(b);
// print_dbg(" ");
@@ -355,16 +362,20 @@ void tele_usb_disk() {
// print_dbg_ulong(num);
}
else if (l == 0) {
tele_set_pattern_l(b, num);
ss_set_pattern_len(&scene_state,
b, num);
}
else if (l == 1) {
tele_set_pattern_wrap(b, num);
ss_set_pattern_wrap(
&scene_state, b, num);
}
else if (l == 2) {
tele_set_pattern_start(b, num);
ss_set_pattern_start(
&scene_state, b, num);
}
else if (l == 3) {
tele_set_pattern_end(b, num);
ss_set_pattern_end(&scene_state,
b, num);
}
}

@@ -425,7 +436,7 @@ void tele_usb_disk() {
}

void mem_clear() {
memset(tele_script_ptr(), 0, tele_script_size());
memset(tele_patterns_ptr(), 0, tele_patterns_size());
memset(ss_script_ptr(&scene_state), 0, ss_script_size());
memset(ss_patterns_ptr(&scene_state), 0, ss_patterns_size());
memset(&scene_text, 0, sizeof(scene_text));
}
@@ -119,10 +119,11 @@ int main() {

in = malloc(256);

tele_init();

printf("teletype. (blank line quits)\n\n");

scene_state_t ss;
ss_init(&ss);

do {
printf("> ");
fgets(in, 256, stdin);
@@ -144,7 +145,7 @@ int main() {
if (error_msg[0]) printf(": %s", error_msg);
printf("\n");
if (status == E_OK) {
process_result_t output = process_command(&es, &temp);
process_result_t output = process_command(&ss, &es, &temp);
if (output.has_value) { printf(">>> %i\n", output.value); }
}
}
@@ -43,41 +43,41 @@ const tele_op_t op_SCENE =
MAKE_GET_SET_OP(SCENE, op_SCENE_get, op_SCENE_set, 0, true);


static void mod_PROB_func(scene_state_t *NOTUSED(ss), exec_state_t *es,
static void mod_PROB_func(scene_state_t *ss, exec_state_t *es,
command_state_t *cs,
const tele_command_t *post_command) {
int16_t a = cs_pop(cs);

if (rand() % 101 < a) { process_command(es, post_command); }
if (rand() % 101 < a) { process_command(ss, es, post_command); }
}

static void mod_IF_func(scene_state_t *NOTUSED(ss), exec_state_t *es,
static void mod_IF_func(scene_state_t *ss, exec_state_t *es,
command_state_t *cs,
const tele_command_t *post_command) {
es->if_else_condition = false;
if (cs_pop(cs)) {
es->if_else_condition = true;
process_command(es, post_command);
process_command(ss, es, post_command);
}
}

static void mod_ELIF_func(scene_state_t *NOTUSED(ss), exec_state_t *es,
static void mod_ELIF_func(scene_state_t *ss, exec_state_t *es,
command_state_t *cs,
const tele_command_t *post_command) {
if (!es->if_else_condition) {
if (cs_pop(cs)) {
es->if_else_condition = true;
process_command(es, post_command);
process_command(ss, es, post_command);
}
}
}

static void mod_ELSE_func(scene_state_t *NOTUSED(ss), exec_state_t *es,
static void mod_ELSE_func(scene_state_t *ss, exec_state_t *es,
command_state_t *NOTUSED(cs),
const tele_command_t *post_command) {
if (!es->if_else_condition) {
es->if_else_condition = true;
process_command(es, post_command);
process_command(ss, es, post_command);
}
}

@@ -89,7 +89,7 @@ static void mod_L_func(scene_state_t *ss, exec_state_t *es, command_state_t *cs,

for (int16_t i = 0; i <= loop_size; i++) {
ss->variables.i = a < b ? a + i : a - i;
process_command(es, post_command);
process_command(ss, es, post_command);
}
}

@@ -110,9 +110,9 @@ static void op_SCRIPT_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss),
uint16_t a = cs_pop(cs);
if (a > 0 && a < 9) tele_script(a);
}
static void op_KILL_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss),
static void op_KILL_get(const void *NOTUSED(data), scene_state_t *ss,
exec_state_t *NOTUSED(es),
command_state_t *NOTUSED(cs)) {
clear_delays();
clear_delays(ss);
tele_kill();
}
@@ -34,9 +34,8 @@ static void mod_DEL_func(scene_state_t *ss, exec_state_t *NOTUSED(es),
}
}

static void op_DEL_CLR_get(const void *NOTUSED(data),
scene_state_t *NOTUSED(ss),
static void op_DEL_CLR_get(const void *NOTUSED(data), scene_state_t *ss,
exec_state_t *NOTUSED(es),
command_state_t *NOTUSED(cs)) {
clear_delays();
clear_delays(ss);
}

Large diffs are not rendered by default.

@@ -35,7 +35,8 @@ static void mod_S_func(scene_state_t *ss, exec_state_t *NOTUSED(es),
static void op_S_ALL_get(const void *NOTUSED(data), scene_state_t *ss,
exec_state_t *es, command_state_t *NOTUSED(cs)) {
for (int16_t i = 0; i < ss->stack_op.top; i++) {
process_command(es, &ss->stack_op.commands[ss->stack_op.top - i - 1]);
process_command(ss, es,
&ss->stack_op.commands[ss->stack_op.top - i - 1]);
}
ss->stack_op.top = 0;
tele_s(0);
@@ -45,7 +46,7 @@ static void op_S_POP_get(const void *NOTUSED(data), scene_state_t *ss,
exec_state_t *es, command_state_t *NOTUSED(cs)) {
if (ss->stack_op.top) {
ss->stack_op.top--;
process_command(es, &ss->stack_op.commands[ss->stack_op.top]);
process_command(ss, es, &ss->stack_op.commands[ss->stack_op.top]);
if (ss->stack_op.top == 0) tele_s(0);
}
}
@@ -1,10 +1,238 @@
#include "state.h"

#include <string.h>


////////////////////////////////////////////////////////////////////////////////
// SCENE STATE /////////////////////////////////////////////////////////////////

// scene init

void ss_init(scene_state_t *ss) {
ss_variables_init(ss);
ss_patterns_init(ss);
ss->delay.count = 0;
for (size_t i = 0; i < TR_COUNT; i++) { ss->tr_pulse_timer[i] = 0; }
ss->stack_op.top = 0;
for (size_t i = 0; i < SCRIPT_COUNT; i++) { ss->scripts[i].l = 0; }
}

void ss_variables_init(scene_state_t *ss) {
const scene_variables_t default_variables = {
// variables that haven't been explicitly initialised, will be set to 0
.a = 1,
.b = 2,
.c = 3,
.cv_slew = { 1, 1, 1, 1 },
.d = 4,
.drunk_min = 0,
.drunk_max = 255,
.m = 1000,
.m_act = 1,
.o_inc = 1,
.o_min = 0,
.o_max = 63,
.o_wrap = 1,
.q_n = 1,
.time_act = 1,
.tr_pol = { 1, 1, 1, 1 },
.tr_time = { 100, 100, 100, 100 }
};

memcpy(&ss->variables, &default_variables, sizeof(default_variables));
}

void ss_patterns_init(scene_state_t *ss) {
for (size_t i = 0; i < PATTERN_COUNT; i++) { ss_pattern_init(ss, i); }
}

void ss_pattern_init(scene_state_t *ss, size_t pattern_no) {
if (pattern_no >= PATTERN_COUNT) return;

scene_pattern_t *p = &ss->patterns[pattern_no];
p->idx = 0;
p->len = 0;
p->wrap = 1;
p->start = 0;
p->end = 63;
for (size_t i = 0; i < PATTERN_LENGTH; i++) { p->val[i] = 0; }
}

// external variable setting

void ss_set_in(scene_state_t *ss, int16_t value) {
ss->variables.in = value;
}

void ss_set_param(scene_state_t *ss, int16_t value) {
ss->variables.param = value;
}

void ss_set_scene(scene_state_t *ss, int16_t value) {
ss->variables.scene = value;
}

// pattern getters and setters

int16_t ss_get_pattern_idx(scene_state_t *ss, size_t pattern) {
return ss->patterns[pattern].idx;
}

void ss_set_pattern_idx(scene_state_t *ss, size_t pattern, int16_t i) {
ss->patterns[pattern].idx = i;
}

int16_t ss_get_pattern_len(scene_state_t *ss, size_t pattern) {
return ss->patterns[pattern].len;
}

void ss_set_pattern_len(scene_state_t *ss, size_t pattern, int16_t l) {
ss->patterns[pattern].len = l;
}

uint16_t ss_get_pattern_wrap(scene_state_t *ss, size_t pattern) {
return ss->patterns[pattern].wrap;
}

void ss_set_pattern_wrap(scene_state_t *ss, size_t pattern, uint16_t wrap) {
ss->patterns[pattern].wrap = wrap;
}

int16_t ss_get_pattern_start(scene_state_t *ss, size_t pattern) {
return ss->patterns[pattern].start;
}

void ss_set_pattern_start(scene_state_t *ss, size_t pattern, int16_t start) {
ss->patterns[pattern].start = start;
}

int16_t ss_get_pattern_end(scene_state_t *ss, size_t pattern) {
return ss->patterns[pattern].end;
}

void ss_set_pattern_end(scene_state_t *ss, size_t pattern, int16_t end) {
ss->patterns[pattern].end = end;
}

int16_t ss_get_pattern_val(scene_state_t *ss, size_t pattern, size_t idx) {
return ss->patterns[pattern].val[idx];
}

void ss_set_pattern_val(scene_state_t *ss, size_t pattern, size_t idx,
int16_t val) {
ss->patterns[pattern].val[idx] = val;
}

scene_pattern_t *ss_patterns_ptr(scene_state_t *ss) {
return ss->patterns;
}

size_t ss_patterns_size() {
return sizeof(scene_pattern_t);
}

// script manipulation

uint8_t ss_get_script_len(scene_state_t *ss, size_t idx) {
return ss->scripts[idx].l;
}

// private
static void ss_set_script_len(scene_state_t *ss, size_t idx, uint8_t l) {
ss->scripts[idx].l = l;
}

const tele_command_t *ss_get_script_command(scene_state_t *ss,
size_t script_idx, size_t c_idx) {
return &ss->scripts[script_idx].c[c_idx];
}

// private
static void ss_set_script_command(scene_state_t *ss, size_t script_idx,
size_t c_idx, const tele_command_t *cmd) {
memcpy(&ss->scripts[script_idx].c[c_idx], cmd, sizeof(tele_command_t));
}

void ss_overwrite_script_command(scene_state_t *ss, size_t script_idx,
size_t command_idx,
const tele_command_t *cmd) {
if (command_idx >= SCRIPT_MAX_COMMANDS) return;

ss_set_script_command(ss, script_idx, command_idx, cmd);

const uint8_t script_len = ss_get_script_len(ss, script_idx);

if (script_len < SCRIPT_MAX_COMMANDS && command_idx >= script_len) {
ss_set_script_len(ss, script_idx, script_len + 1);
}
}

void ss_insert_script_command(scene_state_t *ss, size_t script_idx,
size_t command_idx, const tele_command_t *cmd) {
if (command_idx >= SCRIPT_MAX_COMMANDS) return;

uint8_t script_len = ss_get_script_len(ss, script_idx);
if (script_len == SCRIPT_MAX_COMMANDS) { // no room to insert
ss_delete_script_command(ss, script_idx, script_len - 1); // make room
script_len = ss_get_script_len(ss, script_idx);
}

// shuffle down
for (size_t i = script_len; i > command_idx; i--) {
const tele_command_t *cmd =
ss_get_script_command(ss, script_idx, i - 1);
ss_set_script_command(ss, script_idx, i, cmd);
}

// increase length
ss_set_script_len(ss, script_idx, script_len + 1);

// overwrite at command_idx
ss_overwrite_script_command(ss, script_idx, command_idx, cmd);
}

void ss_delete_script_command(scene_state_t *ss, size_t script_idx,
size_t command_idx) {
if (command_idx >= SCRIPT_MAX_COMMANDS) return;

uint8_t script_len = ss_get_script_len(ss, script_idx);
if (script_len &&
ss_get_script_command(ss, script_idx, command_idx)->length) {
script_len--;
ss_set_script_len(ss, script_idx, script_len);

for (size_t n = command_idx; n < script_len; n++) {
const tele_command_t *cmd =
ss_get_script_command(ss, script_idx, n + 1);
ss_set_script_command(ss, script_idx, n, cmd);
}

tele_command_t blank_command;
blank_command.length = 0;
ss_set_script_command(ss, script_idx, script_len, &blank_command);
}
}

scene_script_t *ss_script_ptr(scene_state_t *ss) {
return ss->scripts;
}

size_t ss_script_size() {
return sizeof(scene_script_t);
}


////////////////////////////////////////////////////////////////////////////////
// EXEC STATE //////////////////////////////////////////////////////////////////

void es_init(exec_state_t *es) {
es->if_else_condition = false;
}


////////////////////////////////////////////////////////////////////////////////
// COMMAND STATE ///////////////////////////////////////////////////////////////

void cs_init(command_state_t *cs) {
cs->stack.top = 0;
}
@@ -2,6 +2,7 @@
#define _STATE_H_

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#include "command.h"
@@ -15,6 +16,7 @@
#define PATTERN_COUNT 4
#define PATTERN_LENGTH 64
#define SCRIPT_MAX_COMMANDS 6
#define SCRIPT_COUNT 10

#define METRO_SCRIPT 8
#define INIT_SCRIPT 9
@@ -62,12 +64,12 @@ typedef struct {
} scene_variables_t;

typedef struct {
int16_t i;
uint16_t l;
int16_t idx;
uint16_t len;
uint16_t wrap;
int16_t start;
int16_t end;
int16_t v[PATTERN_LENGTH];
int16_t val[PATTERN_LENGTH];
} scene_pattern_t;

typedef struct {
@@ -92,9 +94,50 @@ typedef struct {
scene_delay_t delay;
scene_stack_op_t stack_op;
int16_t tr_pulse_timer[TR_COUNT];
scene_script_t scripts[10];
scene_script_t scripts[SCRIPT_COUNT];
} scene_state_t;

extern void ss_init(scene_state_t *ss);
extern void ss_variables_init(scene_state_t *ss);
extern void ss_patterns_init(scene_state_t *ss);
extern void ss_pattern_init(scene_state_t *ss, size_t pattern_no);

extern void ss_set_in(scene_state_t *ss, int16_t value);
extern void ss_set_param(scene_state_t *ss, int16_t value);
extern void ss_set_scene(scene_state_t *ss, int16_t value);

extern int16_t ss_get_pattern_idx(scene_state_t *ss, size_t pattern);
extern void ss_set_pattern_idx(scene_state_t *ss, size_t pattern, int16_t i);
extern int16_t ss_get_pattern_len(scene_state_t *ss, size_t pattern);
extern void ss_set_pattern_len(scene_state_t *ss, size_t pattern, int16_t l);
extern uint16_t ss_get_pattern_wrap(scene_state_t *ss, size_t pattern);
extern void ss_set_pattern_wrap(scene_state_t *ss, size_t pattern,
uint16_t wrap);
extern int16_t ss_get_pattern_start(scene_state_t *ss, size_t pattern);
extern void ss_set_pattern_start(scene_state_t *ss, size_t pattern,
int16_t start);
extern int16_t ss_get_pattern_end(scene_state_t *ss, size_t pattern);
extern void ss_set_pattern_end(scene_state_t *ss, size_t pattern, int16_t end);
extern int16_t ss_get_pattern_val(scene_state_t *ss, size_t pattern,
size_t idx);
extern void ss_set_pattern_val(scene_state_t *ss, size_t pattern, size_t idx,
int16_t val);
extern scene_pattern_t *ss_patterns_ptr(scene_state_t *ss);
extern size_t ss_patterns_size(void);

uint8_t ss_get_script_len(scene_state_t *ss, size_t idx);
const tele_command_t *ss_get_script_command(scene_state_t *ss,
size_t script_idx, size_t c_idx);
void ss_overwrite_script_command(scene_state_t *ss, size_t script_idx,
size_t command_idx, const tele_command_t *cmd);
void ss_insert_script_command(scene_state_t *ss, size_t script_idx,
size_t command_idx, const tele_command_t *cmd);
void ss_delete_script_command(scene_state_t *ss, size_t script_idx,
size_t command_idx);

scene_script_t *ss_script_ptr(scene_state_t *ss);
size_t ss_script_size(void);

////////////////////////////////////////////////////////////////////////////////
// EXEC STATE //////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
@@ -11,10 +11,6 @@
#include "teletype_io.h"
#include "util.h"

void tele_set_script_l(size_t idx, uint8_t l);
void tele_set_script_c(size_t script_idx, size_t c_idx,
const tele_command_t *cmd);

static const char *errordesc[] = { "OK",
WELCOME,
"UNKNOWN WORD",
@@ -33,44 +29,16 @@ const char *tele_error(error_t e) {
}


/////////////////////////////////////////////////////////////////
// STATE ////////////////////////////////////////////////////////

// eventually these will not be global variables
static scene_state_t scene_state = {
// variables that haven't been explicitly initialised, will be set to 0
.variables = {.a = 1,
.b = 2,
.c = 3,
.cv_slew = { 1, 1, 1, 1 },
.d = 4,
.drunk_min = 0,
.drunk_max = 255,
.m = 1000,
.m_act = 1,
.o_inc = 1,
.o_min = 0,
.o_max = 63,
.o_wrap = 1,
.q_n = 1,
.time_act = 1,
.tr_pol = { 1, 1, 1, 1 },
.tr_time = { 100, 100, 100, 100 } }
};

/////////////////////////////////////////////////////////////////
// DELAY ////////////////////////////////////////////////////////

void clear_delays(void) {
for (int16_t i = 0; i < TR_COUNT; i++) {
scene_state.tr_pulse_timer[i] = 0;
}

for (int16_t i = 0; i < DELAY_SIZE; i++) { scene_state.delay.time[i] = 0; }
void clear_delays(scene_state_t *ss) {
for (int16_t i = 0; i < TR_COUNT; i++) { ss->tr_pulse_timer[i] = 0; }

scene_state.delay.count = 0;
for (int16_t i = 0; i < DELAY_SIZE; i++) { ss->delay.time[i] = 0; }

scene_state.stack_op.top = 0;
ss->delay.count = 0;
ss->stack_op.top = 0;

tele_delay(0);
tele_s(0);
@@ -180,28 +148,30 @@ error_t validate(const tele_command_t *c,
/////////////////////////////////////////////////////////////////
// RUN //////////////////////////////////////////////////////////

process_result_t run_script(size_t script_no) {
process_result_t run_script(scene_state_t *ss, size_t script_no) {
process_result_t result = {.has_value = false, .value = 0 };
exec_state_t es;
es_init(&es);
for (size_t i = 0; i < tele_get_script_l(script_no); i++) {
result = process_command(&es, tele_get_script_c(script_no, i));
for (size_t i = 0; i < ss_get_script_len(ss, script_no); i++) {
result =
process_command(ss, &es, ss_get_script_command(ss, script_no, i));
}
return result;
}

process_result_t run_command(const tele_command_t *cmd) {
process_result_t run_command(scene_state_t *ss, const tele_command_t *cmd) {
exec_state_t es;
es_init(&es);
return process_command(&es, cmd);
return process_command(ss, &es, cmd);
}


/////////////////////////////////////////////////////////////////
// PROCESS //////////////////////////////////////////////////////

// run a single command inside a given exec_state
process_result_t process_command(exec_state_t *es, const tele_command_t *c) {
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);

@@ -262,15 +232,14 @@ process_result_t process_command(exec_state_t *es, const tele_command_t *c) {
// 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, &scene_state, es, &cs);
op->set(op->data, ss, es, &cs);
else
op->get(op->data, &scene_state, es, &cs);
op->get(op->data, ss, es, &cs);
}
else if (word_type == MOD) {
tele_command_t post_command;
copy_post_command(&post_command, c);
tele_mods[word_value]->func(&scene_state, es, &cs,
&post_command);
tele_mods[word_value]->func(ss, es, &cs, &post_command);
}
}
}
@@ -288,203 +257,38 @@ process_result_t process_command(exec_state_t *es, const tele_command_t *c) {
}
}

/////////////////////////////////////////////////////////////////
// GETTERS & SETTERS ////////////////////////////////////////////

void tele_set_in(int16_t value) {
scene_state.variables.in = value;
}

void tele_set_param(int16_t value) {
scene_state.variables.param = value;
}

void tele_set_scene(int16_t value) {
scene_state.variables.scene = value;
}

int16_t tele_get_pattern_i(size_t pattern) {
return scene_state.patterns[pattern].i;
}

void tele_set_pattern_i(size_t pattern, int16_t i) {
scene_state.patterns[pattern].i = i;
}

int16_t tele_get_pattern_l(size_t pattern) {
return scene_state.patterns[pattern].l;
}

void tele_set_pattern_l(size_t pattern, int16_t l) {
scene_state.patterns[pattern].l = l;
}

uint16_t tele_get_pattern_wrap(size_t pattern) {
return scene_state.patterns[pattern].wrap;
}

void tele_set_pattern_wrap(size_t pattern, uint16_t wrap) {
scene_state.patterns[pattern].wrap = wrap;
}

int16_t tele_get_pattern_start(size_t pattern) {
return scene_state.patterns[pattern].start;
}

void tele_set_pattern_start(size_t pattern, int16_t start) {
scene_state.patterns[pattern].start = start;
}

int16_t tele_get_pattern_end(size_t pattern) {
return scene_state.patterns[pattern].end;
}

void tele_set_pattern_end(size_t pattern, int16_t end) {
scene_state.patterns[pattern].end = end;
}

int16_t tele_get_pattern_val(size_t pattern, size_t idx) {
return scene_state.patterns[pattern].v[idx];
}

void tele_set_pattern_val(size_t pattern, size_t idx, int16_t val) {
scene_state.patterns[pattern].v[idx] = val;
}

scene_pattern_t *tele_patterns_ptr() {
return scene_state.patterns;
}

size_t tele_patterns_size() {
return sizeof(scene_state.patterns);
}

uint8_t tele_get_script_l(size_t idx) {
return scene_state.scripts[idx].l;
}

void tele_set_script_l(size_t idx, uint8_t l) {
scene_state.scripts[idx].l = l;
}

const tele_command_t *tele_get_script_c(size_t script_idx, size_t c_idx) {
return &scene_state.scripts[script_idx].c[c_idx];
}

void tele_set_script_c(size_t script_idx, size_t c_idx,
const tele_command_t *cmd) {
memcpy(&scene_state.scripts[script_idx].c[c_idx], cmd,
sizeof(tele_command_t));
}

void overwrite_script_command(size_t script_idx, size_t command_idx,
const tele_command_t *cmd) {
if (command_idx >= SCRIPT_MAX_COMMANDS) return;

tele_set_script_c(script_idx, command_idx, cmd);

const uint8_t script_len = tele_get_script_l(script_idx);

if (script_len < SCRIPT_MAX_COMMANDS && command_idx >= script_len) {
tele_set_script_l(script_idx, script_len + 1);
}
}

void insert_script_command(size_t script_idx, size_t command_idx,
const tele_command_t *cmd) {
if (command_idx >= SCRIPT_MAX_COMMANDS) return;

uint8_t script_len = tele_get_script_l(script_idx);
if (script_len == SCRIPT_MAX_COMMANDS) { // no room to insert
delete_script_command(script_idx, script_len - 1); // make room
script_len = tele_get_script_l(script_idx);
}

// shuffle down
for (size_t i = script_len; i > command_idx; i--) {
const tele_command_t *cmd = tele_get_script_c(script_idx, i - 1);
tele_set_script_c(script_idx, i, cmd);
}

// increase length
tele_set_script_l(script_idx, script_len + 1);

// overwrite at command_idx
overwrite_script_command(script_idx, command_idx, cmd);
}

void delete_script_command(size_t script_idx, size_t command_idx) {
if (command_idx >= SCRIPT_MAX_COMMANDS) return;

uint8_t script_len = tele_get_script_l(script_idx);
if (script_len && tele_get_script_c(script_idx, command_idx)->length) {
script_len--;
tele_set_script_l(script_idx, script_len);

for (size_t n = command_idx; n < script_len; n++) {
const tele_command_t *cmd = tele_get_script_c(script_idx, n + 1);
tele_set_script_c(script_idx, n, cmd);
}

tele_command_t blank_command;
blank_command.length = 0;
tele_set_script_c(script_idx, script_len, &blank_command);
}
}

scene_script_t *tele_script_ptr() {
return scene_state.scripts;
}

size_t tele_script_size() {
return sizeof(scene_state.scripts);
}

/////////////////////////////////////////////////////////////////
// TICK /////////////////////////////////////////////////////////

void tele_tick(uint8_t time) {
void tele_tick(scene_state_t *ss, uint8_t time) {
// inc time
if (scene_state.variables.time_act) scene_state.variables.time += time;
if (ss->variables.time_act) ss->variables.time += time;

// process delays
for (int16_t i = 0; i < DELAY_SIZE; i++) {
if (scene_state.delay.time[i]) {
scene_state.delay.time[i] -= time;
if (scene_state.delay.time[i] <= 0) {
if (ss->delay.time[i]) {
ss->delay.time[i] -= time;
if (ss->delay.time[i] <= 0) {
// sprintf(dbg,"\r\ndelay %d", i);
// DBG
run_command(&scene_state.delay.commands[i]);
scene_state.delay.time[i] = 0;
scene_state.delay.count--;
if (scene_state.delay.count == 0) tele_delay(0);
run_command(ss, &ss->delay.commands[i]);
ss->delay.time[i] = 0;
ss->delay.count--;
if (ss->delay.count == 0) tele_delay(0);
}
}
}

// process tr pulses
for (int16_t i = 0; i < TR_COUNT; i++) {
if (scene_state.tr_pulse_timer[i]) {
scene_state.tr_pulse_timer[i] -= time;
if (scene_state.tr_pulse_timer[i] <= 0) {
scene_state.tr_pulse_timer[i] = 0;
scene_state.variables.tr[i] =
scene_state.variables.tr_pol[i] == 0;
tele_tr(i, scene_state.variables.tr[i]);
if (ss->tr_pulse_timer[i]) {
ss->tr_pulse_timer[i] -= time;
if (ss->tr_pulse_timer[i] <= 0) {
ss->tr_pulse_timer[i] = 0;
ss->variables.tr[i] = ss->variables.tr_pol[i] == 0;
tele_tr(i, ss->variables.tr[i]);
}
}
}
}

/////////////////////////////////////////////////////////////////
// INIT /////////////////////////////////////////////////////////

void tele_init() {
for (size_t i = 0; i < 4; i++) {
tele_set_pattern_i(i, 0);
tele_set_pattern_l(i, 0);
tele_set_pattern_wrap(i, 1);
tele_set_pattern_start(i, 0);
tele_set_pattern_end(i, 63);
}
}
@@ -38,45 +38,15 @@ error_t parse(const char *cmd, tele_command_t *out,
char error_msg[TELE_ERROR_MSG_LENGTH]);
error_t validate(const tele_command_t *c,
char error_msg[TELE_ERROR_MSG_LENGTH]);
process_result_t run_script(size_t script_no);
process_result_t run_command(const tele_command_t *cmd);
process_result_t process_command(exec_state_t *es, const tele_command_t *c);
process_result_t run_script(scene_state_t *ss, size_t script_no);
process_result_t run_command(scene_state_t *ss, const tele_command_t *cmd);
process_result_t process_command(scene_state_t *ss, exec_state_t *es,
const tele_command_t *c);

void tele_tick(uint8_t);
void tele_tick(scene_state_t *ss, uint8_t);

void clear_delays(void);
void clear_delays(scene_state_t *ss);

void tele_init(void);

void tele_set_in(int16_t value);
void tele_set_param(int16_t value);
void tele_set_scene(int16_t value);

int16_t tele_get_pattern_i(size_t pattern);
void tele_set_pattern_i(size_t pattern, int16_t i);
int16_t tele_get_pattern_l(size_t pattern);
void tele_set_pattern_l(size_t pattern, int16_t l);
uint16_t tele_get_pattern_wrap(size_t pattern);
void tele_set_pattern_wrap(size_t pattern, uint16_t wrap);
int16_t tele_get_pattern_start(size_t pattern);
void tele_set_pattern_start(size_t pattern, int16_t start);
int16_t tele_get_pattern_end(size_t pattern);
void tele_set_pattern_end(size_t pattern, int16_t end);
int16_t tele_get_pattern_val(size_t pattern, size_t idx);
void tele_set_pattern_val(size_t pattern, size_t idx, int16_t val);
scene_pattern_t *tele_patterns_ptr(void);
size_t tele_patterns_size(void);

uint8_t tele_get_script_l(size_t idx);
const tele_command_t *tele_get_script_c(size_t script_idx, size_t c_idx);

void overwrite_script_command(size_t script_idx, size_t command_idx,
const tele_command_t *cmd);
void insert_script_command(size_t script_idx, size_t command_idx,
const tele_command_t *cmd);
void delete_script_command(size_t script_idx, size_t command_idx);
scene_script_t *tele_script_ptr(void);
size_t tele_script_size(void);

const char *tele_error(error_t);

@@ -3,10 +3,10 @@
#include "greatest/greatest.h"

#include "teletype.h"

// runs multiple lines of commands and then asserts that the final answer is
// correct
TEST process_helper(size_t n, char* lines[], int16_t answer) {
// correct (allows contiuation of state)
TEST process_helper_state(scene_state_t* ss, size_t n, char* lines[],
int16_t answer) {
process_result_t result = {.has_value = false, .value = 0 };
exec_state_t es;
es_init(&es);
@@ -16,16 +16,25 @@ TEST process_helper(size_t n, char* lines[], int16_t answer) {
error_t error = parse(lines[i], &cmd, error_msg);
if (error != E_OK) { FAIL(); }
if (validate(&cmd, error_msg) != E_OK) { FAIL(); }
result = process_command(&es, &cmd);
result = process_command(ss, &es, &cmd);
}
ASSERT_EQ(result.has_value, true);
ASSERT_EQ(result.value, answer);

PASS();
}


// runs multiple lines of commands and then asserts that the final answer is
// correct
TEST process_helper(size_t n, char* lines[], int16_t answer) {
scene_state_t ss;
ss_init(&ss);

return process_helper_state(&ss, n, lines, answer);
}

TEST test_numbers() {
// beware of global state!!!
char* test1[1] = { "1" };
CHECK_CALL(process_helper(1, test1, 1));

@@ -42,7 +51,6 @@ TEST test_numbers() {
}

TEST test_ADD() {
// beware of global state!!!
char* test1[1] = { "ADD 5 6" };
CHECK_CALL(process_helper(1, test1, 11));

@@ -53,7 +61,6 @@ TEST test_ADD() {
}

TEST test_IF() {
// beware of global state!!!
char* test1[3] = { "X 0", "IF 1 : X 1", "X" };
CHECK_CALL(process_helper(3, test1, 1));

@@ -70,33 +77,22 @@ TEST test_IF() {
}

TEST test_FLIP() {
// beware of global state!!!
char* test1[2] = { "FLIP 0", "FLIP" };
CHECK_CALL(process_helper(2, test1, 0));

char* test2[1] = { "FLIP" };
CHECK_CALL(process_helper(1, test2, 1));
CHECK_CALL(process_helper(1, test2, 0));

char* test3[1] = { "FLIP" };
CHECK_CALL(process_helper(1, test3, 0));
char* test3[2] = { "FLIP", "FLIP" };
CHECK_CALL(process_helper(2, test3, 1));

char* test4[2] = { "FLIP 1", "FLIP" };
char* test4[2] = { "FLIP 100", "FLIP" };
CHECK_CALL(process_helper(2, test4, 1));

char* test5[1] = { "FLIP" };
CHECK_CALL(process_helper(1, test5, 0));

char* test6[2] = { "FLIP 100", "FLIP" };
CHECK_CALL(process_helper(2, test6, 1));

char* test7[1] = { "FLIP" };
CHECK_CALL(process_helper(1, test7, 0));

PASS();
}

TEST test_L() {
// beware of global state!!!
char* test1[3] = { "X 0", "L 1 10 : X I", "X" };
CHECK_CALL(process_helper(3, test1, 10));

@@ -110,35 +106,36 @@ TEST test_L() {
}

TEST test_O() {
// beware of global state!!!
scene_state_t ss;
ss_init(&ss);

char* test1[6] = {
"O.MIN 0", "O.MAX 63", "O.INC 1", "O.WRAP 1", "O 0", "O"
};
CHECK_CALL(process_helper(6, test1, 0));
CHECK_CALL(process_helper_state(&ss, 6, test1, 0));

char* test2[1] = { "O" };
CHECK_CALL(process_helper(1, test2, 1));
CHECK_CALL(process_helper_state(&ss, 1, test2, 1));

char* test3[2] = { "O 0", "O" };
CHECK_CALL(process_helper(2, test3, 0));
CHECK_CALL(process_helper_state(&ss, 2, test3, 0));

char* test4[2] = { "O 63", "O" };
CHECK_CALL(process_helper(2, test4, 63));
CHECK_CALL(process_helper_state(&ss, 2, test4, 63));

char* test5[1] = { "O" };
CHECK_CALL(process_helper(1, test5, 0));
CHECK_CALL(process_helper_state(&ss, 1, test5, 0));

char* test6[4] = { "O 0", "O.INC -1", "O", "O" };
CHECK_CALL(process_helper(4, test6, 63));
CHECK_CALL(process_helper_state(&ss, 4, test6, 63));

char* test7[4] = { "O 0", "O.WRAP 0", "O", "O" };
CHECK_CALL(process_helper(4, test7, 0));
CHECK_CALL(process_helper_state(&ss, 4, test7, 0));

PASS();
}

TEST test_P() {
// beware of global state!!!
char* test1[2] = { "P 0 1", "P 0" };
CHECK_CALL(process_helper(2, test1, 1));

@@ -149,7 +146,6 @@ TEST test_P() {
}

TEST test_PN() {
// beware of global state!!!
char* test1[2] = { "PN 0 0 1", "PN 0 0" };
CHECK_CALL(process_helper(2, test1, 1));

@@ -166,45 +162,46 @@ TEST test_PN() {
}

TEST test_Q() {
// beware of global state!!!
scene_state_t ss;
ss_init(&ss);

char* test1[2] = { "Q.N 16", "Q.N" };
CHECK_CALL(process_helper(2, test1, 16));
CHECK_CALL(process_helper_state(&ss, 2, test1, 16));

for (int i = 1; i <= 16; i++) {
char buf1[20];
char buf2[20];
sprintf(buf1, "Q.N %d", i);
sprintf(buf2, "Q %d", i);
char* test2[3] = { buf1, buf2, "Q" };
CHECK_CALL(process_helper(3, test2, 1));
CHECK_CALL(process_helper_state(&ss, 3, test2, 1));
}

for (int i = 1; i <= 16; i++) {
char buf1[20];
sprintf(buf1, "Q.N %d", i);
char* test3[2] = { buf1, "Q" };
CHECK_CALL(process_helper(2, test3, 17 - i));
CHECK_CALL(process_helper_state(&ss, 2, test3, 17 - i));
}

// 1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16 = 136
// 136 / 16 = 8.5
// TODO fix Q.AVG to return 9 in this circumstance
char* test4[1] = { "Q.AVG" };
CHECK_CALL(process_helper(1, test4, 8));
CHECK_CALL(process_helper_state(&ss, 1, test4, 8));

char* test5[2] = { "Q.AVG 5", "Q.AVG" };
CHECK_CALL(process_helper(2, test5, 5));
CHECK_CALL(process_helper_state(&ss, 2, test5, 5));

for (int i = 1; i <= 16; i++) {
char* test6[1] = { "Q" };
CHECK_CALL(process_helper(1, test6, 5));
CHECK_CALL(process_helper_state(&ss, 1, test6, 5));
}

PASS();
}

TEST test_X() {
// beware of global state!!!
char* test1[2] = { "X 0", "X" };
CHECK_CALL(process_helper(2, test1, 0));