-
Notifications
You must be signed in to change notification settings - Fork 1
/
tis_node.c
327 lines (317 loc) · 13.6 KB
/
tis_node.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
#include <stdio.h>
#include <string.h>
#include "tis_io.h"
#include "tis_node.h"
#include "tis_ops.h"
#include "tis_types.h"
tis_node_state_t run(tis_t* tis, tis_node_t* node) {
if(node->type == TIS_NODE_TYPE_COMPUTE) {
int start_index = node->index;
while(node->code[node->index] == NULL || node->code[node->index]->type == TIS_OP_TYPE_INVALID) {
node->index = (node->index + 1) % TIS_NODE_LINE_COUNT;
if(node->index == start_index) {
return TIS_NODE_STATE_IDLE;
}
}
tis_op_result_t result = step(tis, node, node->code[node->index]);
if(result == TIS_OP_RESULT_OK) {
node->index = (node->index + 1) % TIS_NODE_LINE_COUNT;
return TIS_NODE_STATE_RUNNING;
} else if(result == TIS_OP_RESULT_READ_WAIT) {
return TIS_NODE_STATE_READ_WAIT;
} else if(result == TIS_OP_RESULT_WRITE_WAIT) {
return TIS_NODE_STATE_WRITE_WAIT;
} else if(result == TIS_OP_RESULT_ERR) {
error("An error has occurred!!!\n");
bork();
} else {
// BAD INTERNAL ERROR BAD this is out of sync with the enum
error("INTERNAL: An error has occurred!!!\n");
bork();
}
} else if(node->type == TIS_NODE_TYPE_MEMORY_STACK) {
// TODO experiment: can a stack node handle simultaneous read and write? What does this do, even? Should read or write be first? -> can multi-write, in node order; cannot multi-read (one per tick); read+write will read previous value (if present) *before* the write.
tis_node_state_t state = TIS_NODE_STATE_IDLE;
if(node->index < TIS_NODE_LINE_COUNT) {
// if capacity, try to read
spam("Stack node %s attempting to read to index %d\n", node_name(node), node->index);
if(read_register(tis, node, TIS_REGISTER_ANY, &(node->data[node->index])) == TIS_OP_RESULT_OK) {
spam("Stack node %s read success to index %d\n", node_name(node), node->index);
node->index++;
state = TIS_NODE_STATE_RUNNING;
}
}
if(node->index > 0) {
spam("Stack node %s attempting to write from index %d\n", node_name(node), node->index-1);
tis_op_result_t result = write_register(tis, node, TIS_REGISTER_ANY, node->data[node->index-1]);
if(result == TIS_OP_RESULT_OK) {
spam("Stack node %s write immediate success from index %d\n", node_name(node), node->index-1);
node->index--;
state = TIS_NODE_STATE_RUNNING;
} else {
state = TIS_OP_RESULT_WRITE_WAIT;
}
}
return state;
} else if(node->type == TIS_NODE_TYPE_DAMAGED) {
return TIS_NODE_STATE_IDLE;
}
return TIS_NODE_STATE_IDLE;
}
tis_node_state_t run_defer(tis_t* tis, tis_node_t* node) {
if(node->type == TIS_NODE_TYPE_COMPUTE) {
tis_op_result_t result = step_defer(tis, node, node->code[node->index]);
if(result == TIS_OP_RESULT_OK) {
node->index = (node->index + 1) % TIS_NODE_LINE_COUNT;
return TIS_NODE_STATE_RUNNING;
} else if(result == TIS_OP_RESULT_READ_WAIT) {
// internal error
bork();
} else if(result == TIS_OP_RESULT_WRITE_WAIT) {
return TIS_NODE_STATE_WRITE_WAIT;
} else if(result == TIS_OP_RESULT_ERR) {
error("An error has occurred!!!\n");
bork();
} else {
// BAD INTERNAL ERROR BAD this is out of sync with the enum
error("INTERNAL: An error has occurred!!!\n");
bork();
}
} else if(node->type == TIS_NODE_TYPE_MEMORY_STACK) {
spam("Stack node %s attempting to write (defer) from index %d\n", node_name(node), node->index-1);
tis_op_result_t result = write_register_defer(tis, node, TIS_REGISTER_ANY);
if(result == TIS_OP_RESULT_OK) {
spam("Stack node %s write deferred success from index %d\n", node_name(node), node->index-1);
node->index--;
return TIS_NODE_STATE_RUNNING;
} else if(result == TIS_OP_RESULT_READ_WAIT) {
// internal error
bork();
} else if(result == TIS_OP_RESULT_WRITE_WAIT) {
return TIS_NODE_STATE_WRITE_WAIT;
} else if(result == TIS_OP_RESULT_ERR) {
error("An error has occurred!!!\n");
bork();
} else {
// BAD INTERNAL ERROR BAD this is out of sync with the enum
error("INTERNAL: An error has occurred!!!\n");
bork();
}
} else {
// only compute and memory nodes can defer
error("INTERNAL: Cannot run deferred instructions on this node type\n");
bork();
}
return TIS_NODE_STATE_IDLE;
}
/*
* In game, ANY search order for source is LEFT, RIGHT, UP, DOWN
* This is not in the spec, but is convenient and will be maintained
* TODO future enhancement to randomly order, giving a source of randomness
*/
tis_op_result_t read_port_register_maybe(tis_t* tis, tis_node_t* node, tis_register_t reg, int* value) {
if(reg == TIS_REGISTER_ANY) {
tis_op_result_t result;
result = read_port_register_maybe(tis, node, TIS_REGISTER_LEFT, value);
if(result == TIS_OP_RESULT_OK) {
node->last = TIS_REGISTER_LEFT;
return result;
}
result = read_port_register_maybe(tis, node, TIS_REGISTER_RIGHT, value);
if(result == TIS_OP_RESULT_OK) {
node->last = TIS_REGISTER_RIGHT;
return result;
}
result = read_port_register_maybe(tis, node, TIS_REGISTER_UP, value);
if(result == TIS_OP_RESULT_OK) {
node->last = TIS_REGISTER_UP;
return result;
}
result = read_port_register_maybe(tis, node, TIS_REGISTER_DOWN, value);
if(result == TIS_OP_RESULT_OK) {
node->last = TIS_REGISTER_DOWN;
return result;
}
return TIS_OP_RESULT_READ_WAIT;
} else if(reg == TIS_REGISTER_UP) {
if(node->row == 0) { // if reading up from top row, read input instead
if(tis->inputs[node->col] == NULL || tis->inputs[node->col]->writereg != TIS_REGISTER_DOWN) {
return TIS_OP_RESULT_READ_WAIT;
}
*value = tis->inputs[node->col]->writebuf;
tis->inputs[node->col]->writereg = TIS_REGISTER_NIL;
return TIS_OP_RESULT_OK;
}
tis_node_t* neigh = tis->nodes[(node->row-1)*tis->cols + node->col];
if(neigh == NULL || !(neigh->writereg == TIS_REGISTER_DOWN || neigh->writereg == TIS_REGISTER_ANY)) {
return TIS_OP_RESULT_READ_WAIT;
}
*value = neigh->writebuf;
if(neigh->writereg == TIS_REGISTER_ANY) {
neigh->last = TIS_REGISTER_DOWN;
}
neigh->writereg = TIS_REGISTER_NIL;
} else if(reg == TIS_REGISTER_DOWN) {
if(node->row+1 == tis->rows) { // can never read from an output
return TIS_OP_RESULT_READ_WAIT;
}
tis_node_t* neigh = tis->nodes[(node->row+1)*tis->cols + node->col];
if(neigh == NULL || !(neigh->writereg == TIS_REGISTER_UP || neigh->writereg == TIS_REGISTER_ANY)) {
return TIS_OP_RESULT_READ_WAIT;
}
*value = neigh->writebuf;
if(neigh->writereg == TIS_REGISTER_ANY) {
neigh->last = TIS_REGISTER_UP;
}
neigh->writereg = TIS_REGISTER_NIL;
} else if(reg == TIS_REGISTER_LEFT) {
if(node->col == 0) {
return TIS_OP_RESULT_READ_WAIT;
}
tis_node_t* neigh = tis->nodes[node->row*tis->cols + node->col-1];
if(neigh == NULL || !(neigh->writereg == TIS_REGISTER_RIGHT || neigh->writereg == TIS_REGISTER_ANY)) {
return TIS_OP_RESULT_READ_WAIT;
}
*value = neigh->writebuf;
if(neigh->writereg == TIS_REGISTER_ANY) {
neigh->last = TIS_REGISTER_RIGHT;
}
neigh->writereg = TIS_REGISTER_NIL;
} else if(reg == TIS_REGISTER_RIGHT) {
if(node->col+1 == tis->cols) {
return TIS_OP_RESULT_READ_WAIT;
}
tis_node_t* neigh = tis->nodes[node->row*tis->cols + node->col+1];
if(neigh == NULL || !(neigh->writereg == TIS_REGISTER_LEFT || neigh->writereg == TIS_REGISTER_ANY)) {
return TIS_OP_RESULT_READ_WAIT;
}
*value = neigh->writebuf;
if(neigh->writereg == TIS_REGISTER_ANY) {
neigh->last = TIS_REGISTER_LEFT;
}
neigh->writereg = TIS_REGISTER_NIL;
}
return TIS_OP_RESULT_OK;
}
/*
* In game, ANY search order for destination is UP, LEFT, RIGHT, DOWN
* This is not in the spec, but is convenient and will be maintained
* Thus, the winner is determined by the node run order (and not here)
* TODO future enhancement to randomly order, giving a source of randomness
*/
tis_op_result_t write_port_register_maybe(tis_t* tis, tis_node_t* node, tis_register_t reg, int value) {
(void)tis;
(void)reg;
node->writebuf = value;
return TIS_OP_RESULT_WRITE_WAIT;
}
tis_op_result_t write_port_register_defer_maybe(tis_t* tis, tis_node_t* node, tis_register_t reg) {
(void)tis;
if(node->writereg == TIS_REGISTER_NIL) { // if NIL, the previous write was handled, reset it all
node->writereg = TIS_REGISTER_INVALID;
return TIS_OP_RESULT_OK;
}
node->writereg = reg;
return TIS_OP_RESULT_WRITE_WAIT;
}
tis_op_result_t read_register(tis_t* tis, tis_node_t* node, tis_register_t reg, int* value) {
spam("Attempting read from register %s on node %s\n", reg_to_string(reg), node_name(node));
switch(reg) {
case TIS_REGISTER_ACC:
*value = node->acc;
return TIS_OP_RESULT_OK;
case TIS_REGISTER_BAK:
// cannot read from BAK
error("INTERNAL: Attempted to read from BAK on node %s\n", node_name(node));
return TIS_OP_RESULT_ERR;
case TIS_REGISTER_NIL:
*value = 0;
return TIS_OP_RESULT_OK;
case TIS_REGISTER_UP:
case TIS_REGISTER_DOWN:
case TIS_REGISTER_LEFT:
case TIS_REGISTER_RIGHT:
case TIS_REGISTER_ANY:
return read_port_register_maybe(tis, node, reg, value);
case TIS_REGISTER_LAST:
if(node->last == TIS_REGISTER_INVALID) {
error("Attempted to reference LAST before ANY on node %s\n", node_name(node));
return TIS_OP_RESULT_ERR;
}
return read_port_register_maybe(tis, node, node->last, value);
case TIS_REGISTER_INVALID:
default:
// internal error
error("INTERNAL: Attempted to read from INVALID on node %s\n", node_name(node));
return TIS_OP_RESULT_ERR;
}
// Should not reach
error("INTERNAL: switch out of sync with enum\n");
return TIS_OP_RESULT_ERR;
}
tis_op_result_t write_register(tis_t* tis, tis_node_t* node, tis_register_t reg, int value) {
spam("Attempting write to register %s on node %s (value %d)\n", reg_to_string(reg), node_name(node), value);
switch(reg) {
case TIS_REGISTER_ACC:
node->acc = value;
return TIS_OP_RESULT_OK;
case TIS_REGISTER_BAK:
// cannot write to BAK
error("INTERNAL: Attempted to write to BAK on node %s\n", node_name(node));
return TIS_OP_RESULT_ERR;
case TIS_REGISTER_NIL:
// throwaway write
return TIS_OP_RESULT_OK;
case TIS_REGISTER_UP:
case TIS_REGISTER_DOWN:
case TIS_REGISTER_LEFT:
case TIS_REGISTER_RIGHT:
case TIS_REGISTER_ANY:
return write_port_register_maybe(tis, node, reg, value);
case TIS_REGISTER_LAST:
if(node->last == TIS_REGISTER_INVALID) {
error("Attempted to reference LAST before ANY on node %s\n", node_name(node));
return TIS_OP_RESULT_ERR;
}
return write_port_register_maybe(tis, node, node->last, value);
case TIS_REGISTER_INVALID:
default:
// internal error
error("INTERNAL: Attempted to write to INVALID on node %s\n", node_name(node));
return TIS_OP_RESULT_ERR;
}
// Should not reach
error("INTERNAL: switch out of sync with enum\n");
return TIS_OP_RESULT_ERR;
}
tis_op_result_t write_register_defer(tis_t* tis, tis_node_t* node, tis_register_t reg) {
spam("Attempting write to register %s on node %s (defer)\n", reg_to_string(reg), node_name(node));
switch(reg) {
case TIS_REGISTER_ACC:
case TIS_REGISTER_BAK:
case TIS_REGISTER_NIL:
// internal error
error("INTERNAL: Attempted to write (defer) to ACC, NIL, or BAK on node %s\n", node_name(node));
return TIS_OP_RESULT_ERR;
case TIS_REGISTER_UP:
case TIS_REGISTER_DOWN:
case TIS_REGISTER_LEFT:
case TIS_REGISTER_RIGHT:
case TIS_REGISTER_ANY:
return write_port_register_defer_maybe(tis, node, reg);
case TIS_REGISTER_LAST:
if(node->last == TIS_REGISTER_INVALID) {
error("INTERNAL: Attempted to reference LAST before ANY on node %s (this should already have been caught)\n", node_name(node));
return TIS_OP_RESULT_ERR;
}
return write_port_register_defer_maybe(tis, node, node->last);
case TIS_REGISTER_INVALID:
default:
// internal error
error("INTERNAL: Attempted to write (defer) to INVALID on node %s\n", node_name(node));
return TIS_OP_RESULT_ERR;
}
// Should not reach
error("INTERNAL: switch out of sync with enum\n");
return TIS_OP_RESULT_ERR;
}