|
@@ -17,6 +17,8 @@
|
17
|
17
|
* See <http://www.gnu.org/licenses/>.
|
18
|
18
|
*/
|
19
|
19
|
|
|
20
|
+#include <gbdk/emu_debug.h>
|
|
21
|
+
|
20
|
22
|
#include "game.h"
|
21
|
23
|
#include "timer.h"
|
22
|
24
|
#include "multiplayer.h"
|
|
@@ -24,7 +26,8 @@
|
24
|
26
|
#define MASTER_HELLO 0x42
|
25
|
27
|
#define SLAVE_HELLO 0x23
|
26
|
28
|
|
27
|
|
-#define RETRANSMIT_TIME 200
|
|
29
|
+#define RETRANSMIT_TIME_HELLO 200
|
|
30
|
+#define RETRANSMIT_TIME_GAME 10
|
28
|
31
|
|
29
|
32
|
#define PKT_TYPE_PLAYER 0x00
|
30
|
33
|
#define PKT_TYPE_SHOT 0x01
|
|
@@ -38,12 +41,9 @@ struct mp_packet {
|
38
|
41
|
union {
|
39
|
42
|
struct mp_player_state player;
|
40
|
43
|
struct mp_shot_state shot;
|
41
|
|
- } data;
|
|
44
|
+ };
|
42
|
45
|
};
|
43
|
46
|
|
44
|
|
-#define PKT_SIZE_PLAYER (sizeof(struct mp_player_state) + sizeof(struct mp_header))
|
45
|
|
-#define PKT_SIZE_SHOT (sizeof(struct mp_shot_state) + sizeof(struct mp_header))
|
46
|
|
-
|
47
|
47
|
enum mp_state {
|
48
|
48
|
MP_M_SEND = 0,
|
49
|
49
|
MP_M_WAIT = 1,
|
|
@@ -58,14 +58,10 @@ static enum mp_state state = 0;
|
58
|
58
|
static uint16_t next_t = 0;
|
59
|
59
|
static uint8_t our_turn = 0;
|
60
|
60
|
|
61
|
|
-static uint8_t *data = NULL;
|
62
|
|
-static uint8_t remaining = 0;
|
63
|
|
-
|
64
|
|
-static uint8_t rx_buff[sizeof(struct mp_packet)] = {0};
|
65
|
|
-static uint8_t rx_len = 0;
|
66
|
|
-
|
67
|
61
|
uint8_t mp_connection_status = 0;
|
68
|
62
|
|
|
63
|
+static void mp_game_init(void);
|
|
64
|
+
|
69
|
65
|
static inline void Tx(uint8_t x) {
|
70
|
66
|
SB_REG = x;
|
71
|
67
|
SC_REG = SIOF_XFER_START | SIOF_CLOCK_INT;
|
|
@@ -76,23 +72,19 @@ static inline void Rx(uint8_t x) {
|
76
|
72
|
SC_REG = SIOF_XFER_START | SIOF_CLOCK_EXT;
|
77
|
73
|
}
|
78
|
74
|
|
79
|
|
-static inline void tx_rx(uint8_t x) {
|
80
|
|
- our_turn ? Tx(x) : Rx(x);
|
81
|
|
-}
|
82
|
|
-
|
83
|
|
-static inline void transmit(struct mp_packet *pkt) {
|
84
|
|
-
|
85
|
|
-}
|
86
|
|
-
|
87
|
75
|
static inline uint8_t transmitting(void) {
|
88
|
76
|
return SC_REG & SIOF_XFER_START;
|
89
|
77
|
}
|
90
|
78
|
|
|
79
|
+// ----------------------------------------------------------------------------
|
|
80
|
+// Initial Handshake
|
|
81
|
+// ----------------------------------------------------------------------------
|
|
82
|
+
|
91
|
83
|
uint8_t mp_master_ready(void) BANKED {
|
92
|
84
|
switch (state) {
|
93
|
85
|
case MP_M_SEND:
|
94
|
86
|
Tx(MASTER_HELLO);
|
95
|
|
- next_t = timer_get() + RETRANSMIT_TIME;
|
|
87
|
+ next_t = timer_get() + RETRANSMIT_TIME_HELLO;
|
96
|
88
|
mp_connection_status++;
|
97
|
89
|
state = MP_M_WAIT;
|
98
|
90
|
break;
|
|
@@ -101,7 +93,7 @@ uint8_t mp_master_ready(void) BANKED {
|
101
|
93
|
if (!transmitting()) {
|
102
|
94
|
if (SB_REG == SLAVE_HELLO) {
|
103
|
95
|
Rx(SLAVE_HELLO);
|
104
|
|
- next_t = timer_get() + RETRANSMIT_TIME;
|
|
96
|
+ next_t = timer_get() + RETRANSMIT_TIME_HELLO;
|
105
|
97
|
mp_connection_status++;
|
106
|
98
|
state = MP_M_REPLY;
|
107
|
99
|
}
|
|
@@ -134,11 +126,8 @@ uint8_t mp_master_ready(void) BANKED {
|
134
|
126
|
|
135
|
127
|
void mp_master_start(void) BANKED {
|
136
|
128
|
our_turn = 1;
|
137
|
|
- remaining = 0;
|
138
|
|
- next_t = timer_get() + RETRANSMIT_TIME;
|
139
|
|
-
|
|
129
|
+ mp_game_init();
|
140
|
130
|
game(GM_MULTI);
|
141
|
|
-
|
142
|
131
|
state = 0;
|
143
|
132
|
}
|
144
|
133
|
|
|
@@ -146,7 +135,7 @@ uint8_t mp_slave_ready(void) BANKED {
|
146
|
135
|
switch (state) {
|
147
|
136
|
case MP_S_START:
|
148
|
137
|
Rx(SLAVE_HELLO);
|
149
|
|
- next_t = timer_get() + RETRANSMIT_TIME;
|
|
138
|
+ next_t = timer_get() + RETRANSMIT_TIME_HELLO;
|
150
|
139
|
mp_connection_status++;
|
151
|
140
|
state = MP_S_WAIT;
|
152
|
141
|
break;
|
|
@@ -155,7 +144,7 @@ uint8_t mp_slave_ready(void) BANKED {
|
155
|
144
|
if (!transmitting()) {
|
156
|
145
|
if (SB_REG == MASTER_HELLO) {
|
157
|
146
|
Tx(MASTER_HELLO);
|
158
|
|
- next_t = timer_get() + RETRANSMIT_TIME;
|
|
147
|
+ next_t = timer_get() + RETRANSMIT_TIME_HELLO;
|
159
|
148
|
mp_connection_status++;
|
160
|
149
|
state = MP_S_REPLY;
|
161
|
150
|
} else {
|
|
@@ -188,36 +177,139 @@ uint8_t mp_slave_ready(void) BANKED {
|
188
|
177
|
|
189
|
178
|
void mp_slave_start(void) BANKED {
|
190
|
179
|
our_turn = 0;
|
191
|
|
- remaining = 0;
|
192
|
|
- next_t = timer_get() + RETRANSMIT_TIME;
|
193
|
|
-
|
|
180
|
+ mp_game_init();
|
194
|
181
|
game(GM_MULTI);
|
195
|
|
-
|
196
|
182
|
state = 0;
|
197
|
183
|
}
|
198
|
184
|
|
199
|
|
-uint8_t mp_handle(void) BANKED {
|
200
|
|
- if ((our_turn) && (timer_get() >= next_t) && data && (remaining > 0)) {
|
201
|
|
- if (rx_len < sizeof(struct mp_packet)) {
|
202
|
|
- rx_buff[rx_len++] = SB_REG;
|
203
|
|
- }
|
204
|
|
- tx_rx(*(data++));
|
205
|
|
- remaining--;
|
206
|
|
- } else if ((!our_turn) && (!transmitting()) && data && (remaining > 0)) {
|
207
|
|
- if (rx_len < sizeof(struct mp_packet)) {
|
208
|
|
- rx_buff[rx_len++] = SB_REG;
|
|
185
|
+// ----------------------------------------------------------------------------
|
|
186
|
+// Game Runtime
|
|
187
|
+// ----------------------------------------------------------------------------
|
|
188
|
+
|
|
189
|
+#define QUEUE_LEN 5
|
|
190
|
+
|
|
191
|
+static struct mp_packet queue[QUEUE_LEN];
|
|
192
|
+static uint8_t q_head = 0;
|
|
193
|
+static uint8_t q_tail = 0;
|
|
194
|
+static uint8_t q_full = 0;
|
|
195
|
+static uint8_t byte_pos = 0;
|
|
196
|
+
|
|
197
|
+static uint8_t q_len(void) {
|
|
198
|
+ if (q_head == q_tail) {
|
|
199
|
+ if (q_full) {
|
|
200
|
+ return QUEUE_LEN;
|
|
201
|
+ } else {
|
|
202
|
+ return 0;
|
209
|
203
|
}
|
210
|
|
- tx_rx(*(data++));
|
211
|
|
- remaining--;
|
|
204
|
+ } else if (q_head > q_tail) {
|
|
205
|
+ return q_head - q_tail;
|
|
206
|
+ } else {
|
|
207
|
+ return QUEUE_LEN - q_tail + q_head;
|
212
|
208
|
}
|
|
209
|
+}
|
213
|
210
|
|
214
|
|
- return our_turn && (remaining == 0);
|
|
211
|
+static inline void q_inc_head(void) {
|
|
212
|
+ if (q_full && (++q_tail == QUEUE_LEN)) {
|
|
213
|
+ q_tail = 0;
|
|
214
|
+ }
|
|
215
|
+ if (++q_head == QUEUE_LEN) {
|
|
216
|
+ q_head = 0;
|
|
217
|
+ }
|
|
218
|
+ q_full = (q_head == q_tail);
|
|
219
|
+}
|
|
220
|
+
|
|
221
|
+static inline void q_inc_tail(void) {
|
|
222
|
+ q_tail++;
|
|
223
|
+ if (q_tail >= QUEUE_LEN) {
|
|
224
|
+ q_tail = 0;
|
|
225
|
+ }
|
|
226
|
+}
|
|
227
|
+
|
|
228
|
+static void mp_game_init(void) {
|
|
229
|
+ q_head = 0;
|
|
230
|
+ q_tail = 0;
|
|
231
|
+ q_full = 0;
|
|
232
|
+ byte_pos = sizeof(struct mp_packet); // immediately query packet from game
|
|
233
|
+ next_t = timer_get() + RETRANSMIT_TIME_GAME;
|
|
234
|
+}
|
|
235
|
+
|
|
236
|
+static inline void handle_rx(struct mp_packet *pkt) {
|
|
237
|
+ switch (pkt->header.type) {
|
|
238
|
+ case PKT_TYPE_PLAYER:
|
|
239
|
+ game_set_mp_player2(&pkt->player);
|
|
240
|
+ break;
|
|
241
|
+
|
|
242
|
+ case PKT_TYPE_SHOT:
|
|
243
|
+ game_set_mp_shot(&pkt->shot);
|
|
244
|
+ break;
|
|
245
|
+
|
|
246
|
+ default:
|
|
247
|
+#ifdef DEBUG
|
|
248
|
+ EMU_printf("%s: unknown type %hx\n", __func__, (uint8_t)pkt->header.type);
|
|
249
|
+#endif // DEBUG
|
|
250
|
+ break;
|
|
251
|
+ }
|
|
252
|
+}
|
|
253
|
+
|
|
254
|
+static inline void tx_rx(uint8_t x) {
|
|
255
|
+ our_turn ? Tx(x) : Rx(x);
|
|
256
|
+ next_t = timer_get() + RETRANSMIT_TIME_GAME;
|
|
257
|
+}
|
|
258
|
+
|
|
259
|
+void mp_handle(void) BANKED {
|
|
260
|
+ // ensure we always have something in the queue
|
|
261
|
+ if ((q_len() == 0) && (byte_pos >= sizeof(struct mp_packet))) {
|
|
262
|
+ game_get_mp_state();
|
|
263
|
+ }
|
|
264
|
+
|
|
265
|
+ if (byte_pos < sizeof(struct mp_packet)) {
|
|
266
|
+ if ((our_turn && (timer_get() >= next_t)) || ((!our_turn) && (!transmitting()))) {
|
|
267
|
+ uint8_t to_send = ((uint8_t *)(&queue[q_tail]))[byte_pos];
|
|
268
|
+ ((uint8_t *)(&queue[q_tail]))[byte_pos] = SB_REG;
|
|
269
|
+ tx_rx(to_send);
|
|
270
|
+
|
|
271
|
+ byte_pos++;
|
|
272
|
+ if (byte_pos >= sizeof(struct mp_packet)) {
|
|
273
|
+ handle_rx(&queue[q_tail]);
|
|
274
|
+ q_inc_tail();
|
|
275
|
+ if (q_len() > 0) {
|
|
276
|
+ byte_pos = 0; // keep going
|
|
277
|
+ }
|
|
278
|
+ }
|
|
279
|
+
|
|
280
|
+ our_turn = our_turn ? 0 : 1;
|
|
281
|
+ }
|
|
282
|
+ }
|
215
|
283
|
}
|
216
|
284
|
|
217
|
285
|
void mp_new_state(struct mp_player_state *state) BANKED {
|
218
|
|
- // TODO
|
|
286
|
+#ifdef DEBUG
|
|
287
|
+ if (q_full) {
|
|
288
|
+ EMU_printf("%s: queue overflow\n", __func__);
|
|
289
|
+ }
|
|
290
|
+#endif // DEBUG
|
|
291
|
+
|
|
292
|
+ queue[q_head].header.type = PKT_TYPE_PLAYER;
|
|
293
|
+ queue[q_head].player = *state;
|
|
294
|
+ q_inc_head();
|
|
295
|
+
|
|
296
|
+ if ((q_len() == 1) && (byte_pos >= sizeof(struct mp_packet))) {
|
|
297
|
+ byte_pos = 0; // start transmitting
|
|
298
|
+ }
|
219
|
299
|
}
|
220
|
300
|
|
221
|
301
|
void mp_add_shot(struct mp_shot_state *state) BANKED {
|
222
|
|
- // TODO
|
|
302
|
+#ifdef DEBUG
|
|
303
|
+ if (q_full) {
|
|
304
|
+ EMU_printf("%s: queue overflow\n", __func__);
|
|
305
|
+ }
|
|
306
|
+#endif // DEBUG
|
|
307
|
+
|
|
308
|
+ queue[q_head].header.type = PKT_TYPE_SHOT;
|
|
309
|
+ queue[q_head].shot = *state;
|
|
310
|
+ q_inc_head();
|
|
311
|
+
|
|
312
|
+ if ((q_len() == 1) && (byte_pos >= sizeof(struct mp_packet))) {
|
|
313
|
+ byte_pos = 0; // start transmitting
|
|
314
|
+ }
|
223
|
315
|
}
|