Continuing the discussion from Teletype v2 proposals:
I’ve had some ideas about refactoring the teletype codebase, it’s more complex than the other modules and has the potential to be even more complex due to it’s open ended nature.
As it stands the firmware is mainly split between 2 files, main.c which handles the hardware, and teletype.c which handles the algorithm, teletype.c is well insulated from the hardware via the update_* function pointers.
The main thing that I think we should do is reduce the number of global variables in teletype.c and reduce the amount of direct access to those variables. (Ideally, we’d remove direct access entirely.)
I’ve started exploring some ideas on a branch called state on my GitHub account.
Essentially I’ve added a new file state.h that defines a type teletype_t that is designed to encapsulate the entire state of the teletype, so far I’ve only dealt with the stack, e.g.
// state.h
typedef struct {
int16_t values[STACK_SIZE];
int16_t top;
} teletype_stack_t;
typedef struct {
teletype_stack_t stack;
} teletype_t;
as well as some functions to manipulate that state…
// state.h
// I've used a static inline header function instead of a macro (LTO is another option...)
static inline int16_t tt_pop(teletype_t *t) {
t->stack.top--;
return t->stack.values[t->stack.top];
}
static inline void tt_push(teletype_t *t, int16_t data) {
t->stack.values[t->stack.top] = data;
t->stack.top++;
}
// etc, etc
In teletype.c I’ve created a global variable state and some shim functions to allow existing code to continue working:
// teletype.c
teletype_t state;
int16_t pop() {
return tt_pop(&state);
}
void push(int16_t data) {
tt_push(&state, data);
}
I’ve also modified the prototype for ops, from
void (*func)(void)
to
void (*func)(teletype_t *t)
e.g. op_ADD has changed from:
static void op_ADD() {
push(pop() + pop());
}
to
void op_ADD(teletype_t *t) {
tt_push(t, tt_pop(t) + tt_pop(t));
}
and I’ve moved it into a new file ops/maths.c (along with some other ops).
Thoughts? It will end up increasing the verbosity of code and there may be a slight performance hit. But I think it will make it a lot easier to rationalise about the behaviour of code. Also if we limit ourselves to accessing teletype_t via public functions it makes it much easier to adjust the internal structure.
As an added bonus, if we were to completely remove all the global variables from teletype.c, and instead just pass around a teletype_t to all the functions you then have the option of unit testing the bits of code that might really challenge your sanity (e.g. converting to and from text for USB saving).