source: buchla-emu/emu/gdb.c@ 7cc6ac0

Last change on this file since 7cc6ac0 was 7cc6ac0, checked in by Thomas Lopatic <thomas@…>, 3 years ago

Successfully attached GDB.

  • Property mode set to 100644
File size: 9.7 KB
Line 
1/*
2 * Copyright (C) 2017 The Contributors
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or (at
7 * your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * A copy of the GNU General Public License can be found in the file
15 * "gpl.txt" in the top directory of this repository.
16 */
17
18#include <all.h>
19
20#define ver(...) _ver(gdb_verbose, 0, __VA_ARGS__)
21#define ver2(...) _ver(gdb_verbose, 1, __VA_ARGS__)
22#define ver3(...) _ver(gdb_verbose, 2, __VA_ARGS__)
23
24int32_t gdb_verbose = 0;
25
26#define PORT 12053
27#define SZ_BUF 10000
28
29#define RES_ERR \
30 ((result_t){ .ok = false })
31
32#define RES_OK \
33 ((result_t){ .ok = true })
34
35#define RES_DAT(_out, _n_out) \
36 ((result_t){ .ok = true, .out = (uint8_t *)_out, .n_out = (int32_t)_n_out })
37
38#define LOCK_NONE 0
39#define LOCK_REQ 1
40#define LOCK_ACK 2
41
42typedef enum {
43 STATE_HEAD,
44 STATE_DATA,
45 STATE_CHECK_1,
46 STATE_CHECK_2,
47 STATE_ACK
48} state_t;
49
50typedef struct {
51 bool ok;
52 const uint8_t *out;
53 int32_t n_out;
54} result_t;
55
56static TCPsocket lis;
57static SDLNet_SocketSet set;
58
59static state_t state;
60static SDL_atomic_t lock;
61
62void gdb_init(void)
63{
64 ver("gdb init");
65
66 IPaddress addr;
67
68 if (SDLNet_ResolveHost(&addr, NULL, PORT) < 0) {
69 fail("SDLNet_ResolveHost() failed: %s", SDLNet_GetError());
70 }
71
72 lis = SDLNet_TCP_Open(&addr);
73
74 if (lis == NULL) {
75 fail("SDLNet_TCP_Open() failed: %s", SDLNet_GetError());
76 }
77
78 set = SDLNet_AllocSocketSet(2);
79
80 if (set == NULL) {
81 fail("SDLNet_AllocSocketSet() failed: %s", SDLNet_GetError());
82 }
83
84 if (SDLNet_TCP_AddSocket(set, lis) < 0) {
85 fail("SDLNet_AddSocket() failed: %s", SDLNet_GetError());
86 }
87
88 SDL_AtomicSet(&lock, 0);
89}
90
91void gdb_quit(void)
92{
93 ver("gdb quit");
94
95 SDLNet_FreeSocketSet(set);
96 SDLNet_TCP_Close(lis);
97}
98
99void gdb_inst(void)
100{
101 ver3("gdb inst");
102
103 if (SDL_AtomicGet(&lock) == LOCK_NONE) {
104 return;
105 }
106
107 if (SDL_UnlockMutex(cpu_mutex) < 0) {
108 fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
109 }
110
111 ver2("<- lock req");
112 ver2("-> lock ack");
113 SDL_AtomicSet(&lock, LOCK_ACK);
114
115 while (SDL_AtomicGet(&lock) == LOCK_ACK) {
116 SDL_Delay(100);
117 }
118
119 ver2("<- lock none");
120
121 if (SDL_LockMutex(cpu_mutex) < 0) {
122 fail("SDL_LockMutex() failed: %s", SDL_GetError());
123 }
124}
125
126static void lock_cpu(void)
127{
128 ver2("-> lock req");
129 SDL_AtomicSet(&lock, LOCK_REQ);
130
131 while (SDL_AtomicGet(&lock) == LOCK_REQ) {
132 SDL_Delay(100);
133 }
134
135 ver2("<- lock ack");
136}
137
138static void free_cpu(void)
139{
140 ver2("-> lock none");
141 SDL_AtomicSet(&lock, LOCK_NONE);
142}
143
144static result_t com_reason(void)
145{
146 return RES_DAT("S0a", 4);
147}
148
149static result_t com_cont(char *req)
150{
151 return RES_DAT("", 0);
152}
153
154static result_t com_step(char *req)
155{
156 return RES_DAT("", 0);
157}
158
159static result_t com_rd_reg(void)
160{
161 static char buf[(8 + 8 + 2) * 8 + 1];
162
163 if (SDL_LockMutex(cpu_mutex) < 0) {
164 fail("SDL_LockMutex() failed: %s", SDL_GetError());
165 }
166
167 uint32_t d0 = m68k_get_reg(NULL, M68K_REG_D0);
168 uint32_t d1 = m68k_get_reg(NULL, M68K_REG_D1);
169 uint32_t d2 = m68k_get_reg(NULL, M68K_REG_D2);
170 uint32_t d3 = m68k_get_reg(NULL, M68K_REG_D3);
171 uint32_t d4 = m68k_get_reg(NULL, M68K_REG_D4);
172 uint32_t d5 = m68k_get_reg(NULL, M68K_REG_D5);
173 uint32_t d6 = m68k_get_reg(NULL, M68K_REG_D6);
174 uint32_t d7 = m68k_get_reg(NULL, M68K_REG_D7);
175
176 uint32_t a0 = m68k_get_reg(NULL, M68K_REG_A0);
177 uint32_t a1 = m68k_get_reg(NULL, M68K_REG_A1);
178 uint32_t a2 = m68k_get_reg(NULL, M68K_REG_A2);
179 uint32_t a3 = m68k_get_reg(NULL, M68K_REG_A3);
180 uint32_t a4 = m68k_get_reg(NULL, M68K_REG_A4);
181 uint32_t a5 = m68k_get_reg(NULL, M68K_REG_A5);
182 uint32_t a6 = m68k_get_reg(NULL, M68K_REG_A6);
183 uint32_t a7 = m68k_get_reg(NULL, M68K_REG_A7);
184
185 uint32_t ps = m68k_get_reg(NULL, M68K_REG_SR);
186 uint32_t pc = m68k_get_reg(NULL, M68K_REG_PC);
187
188 if (SDL_UnlockMutex(cpu_mutex) < 0) {
189 fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
190 }
191
192 sprintf(buf,
193 "%08x%08x%08x%08x%08x%08x%08x%08x"
194 "%08x%08x%08x%08x%08x%08x%08x%08x"
195 "%08x%08x",
196 d0, d1, d2, d3, d4, d5, d6, d7,
197 a0, a1, a2, a3, a4, a5, a6, a7,
198 ps, pc);
199
200 return RES_DAT(buf, sizeof buf - 1);
201}
202
203static result_t com_wr_reg(char *req)
204{
205 uint32_t d0, d1, d2, d3, d4, d5, d6, d7;
206 uint32_t a0, a1, a2, a3, a4, a5, a6, a7;
207 uint32_t ps, pc;
208
209 if (sscanf(req + 1,
210 "%08x%08x%08x%08x%08x%08x%08x%08x"
211 "%08x%08x%08x%08x%08x%08x%08x%08x"
212 "%08x%08x",
213 &d0, &d1, &d2, &d3, &d4, &d5, &d6, &d7,
214 &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7,
215 &ps, &pc) != 8 + 8 + 2) {
216 return RES_DAT("E01", 3);
217 }
218
219 if (SDL_LockMutex(cpu_mutex) < 0) {
220 fail("SDL_LockMutex() failed: %s", SDL_GetError());
221 }
222
223 m68k_set_reg(M68K_REG_D0, d0);
224 m68k_set_reg(M68K_REG_D1, d1);
225 m68k_set_reg(M68K_REG_D2, d2);
226 m68k_set_reg(M68K_REG_D3, d3);
227 m68k_set_reg(M68K_REG_D4, d4);
228 m68k_set_reg(M68K_REG_D5, d5);
229 m68k_set_reg(M68K_REG_D6, d6);
230 m68k_set_reg(M68K_REG_D7, d7);
231
232 m68k_set_reg(M68K_REG_A0, a0);
233 m68k_set_reg(M68K_REG_A1, a1);
234 m68k_set_reg(M68K_REG_A2, a2);
235 m68k_set_reg(M68K_REG_A3, a3);
236 m68k_set_reg(M68K_REG_A4, a4);
237 m68k_set_reg(M68K_REG_A5, a5);
238 m68k_set_reg(M68K_REG_A6, a6);
239 m68k_set_reg(M68K_REG_A7, a7);
240
241 m68k_set_reg(M68K_REG_SR, ps);
242 m68k_set_reg(M68K_REG_PC, pc);
243
244 if (SDL_UnlockMutex(cpu_mutex) < 0) {
245 fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
246 }
247
248 return RES_DAT("OK", 2);
249}
250
251static result_t com_rd_mem(char *req)
252{
253 return RES_DAT("", 0);
254}
255
256static result_t com_wr_mem(char *req)
257{
258 return RES_DAT("", 0);
259}
260
261static result_t handle(char *req)
262{
263 result_t res;
264
265 switch (req[0]) {
266 case '?':
267 res = com_reason();
268 break;
269
270 case 'c':
271 res = com_cont(req);
272 break;
273
274 case 's':
275 res = com_step(req);
276 break;
277
278 case 'g':
279 res = com_rd_reg();
280 break;
281
282 case 'G':
283 res = com_wr_reg(req);
284 break;
285
286 case 'm':
287 res = com_rd_mem(req);
288 break;
289
290 case 'M':
291 res = com_wr_mem(req);
292 break;
293
294 default:
295 res = RES_DAT("", 0);
296 break;
297 }
298
299 if (!res.ok || res.n_out > SZ_BUF - 5) {
300 fail("unexpected result");
301 }
302
303 static const uint8_t *hex = (uint8_t *)"0123456789abcdef";
304 static uint8_t buf[SZ_BUF];
305
306 buf[0] = '$';
307 memcpy(buf + 1, res.out, (size_t)res.n_out);
308
309 int32_t sum = 0;
310
311 for (int32_t i = 0; i < res.n_out; ++i) {
312 sum = (sum + res.out[i]) % 256;
313 }
314
315 buf[res.n_out + 1] = '#';
316 buf[res.n_out + 2] = hex[sum / 16];
317 buf[res.n_out + 3] = hex[sum % 16];
318 buf[res.n_out + 4] = 0;
319
320 ver2("resp %s", (char *)buf);
321 return RES_DAT(buf, res.n_out + 4);
322}
323
324static int32_t hex_digit(uint8_t byte)
325{
326 if (byte >= '0' && byte <= '9') {
327 return byte - '0';
328 }
329
330 if (byte >= 'a' && byte <= 'f') {
331 return 10 + byte - 'a';
332 }
333
334 if (byte >= 'A' && byte <= 'F') {
335 return 10 + byte - 'A';
336 }
337
338 return -1;
339}
340
341static result_t input(uint8_t byte)
342{
343 static int32_t n_buf;
344 static uint8_t buf[SZ_BUF];
345 static int32_t sum;
346 static int32_t check;
347
348 int32_t hex;
349 result_t res;
350
351 ver3("input %c st %d n %d", byte, (int32_t)state, n_buf);
352
353 switch (state) {
354 case STATE_HEAD:
355 if (byte != '$') {
356 err("expected '$'");
357 return RES_ERR;
358 }
359
360 sum = 0;
361 n_buf = 0;
362 state = STATE_DATA;
363 return RES_OK;
364
365 case STATE_DATA:
366 if (n_buf == SZ_BUF - 1) {
367 err("packet too long");
368 return RES_ERR;
369 }
370
371 if (byte == '#') {
372 state = STATE_CHECK_1;
373 return RES_DAT("+", 1);
374 }
375
376 sum = (sum + byte) % 256;
377 buf[n_buf] = byte;
378 ++n_buf;
379 return RES_OK;
380
381 case STATE_CHECK_1:
382 hex = hex_digit(byte);
383
384 if (hex < 0) {
385 err("malformed checksum (0x%02x)", byte);
386 return RES_ERR;
387 }
388
389 check = hex << 4;
390 state = STATE_CHECK_2;
391 return RES_OK;
392
393 case STATE_CHECK_2:
394 hex = hex_digit(byte);
395
396 if (hex < 0) {
397 err("malformed checksum (0x%02x)", byte);
398 return RES_ERR;
399 }
400
401 check |= hex;
402
403 if (sum != check) {
404 err("invalid checksum");
405 return RES_ERR;
406 }
407
408 buf[n_buf] = 0;
409 ver2("pack %s", buf);
410
411 res = handle((char *)buf);
412
413 if (!res.ok) {
414 return RES_ERR;
415 }
416
417 state = STATE_ACK;
418 return RES_DAT(res.out, res.n_out);
419
420 case STATE_ACK:
421 if (byte != '+') {
422 err("invalid ACK (0x%02x)", byte);
423 return RES_ERR;
424 }
425
426 state = STATE_HEAD;
427 return RES_OK;
428
429 default:
430 fail("invalid state");
431 }
432
433 // not reached, but Eclipse doesn't know
434 return RES_ERR;
435}
436
437static void con_close(TCPsocket con)
438{
439 if (SDLNet_TCP_DelSocket(set, con) < 0) {
440 fail("SDLNet_TCP_DelSocket() failed: %s", SDLNet_GetError());
441 }
442
443 SDLNet_TCP_Close(con);
444 free_cpu();
445}
446
447void gdb_loop(void)
448{
449 inf("entering GDB loop");
450 TCPsocket con = NULL;
451
452 while (SDL_AtomicGet(&run) != 0) {
453 int32_t n_act = SDLNet_CheckSockets(set, 250);
454
455 if (n_act < 0) {
456 fail("SDLNet_CheckSockets() failed: %s", SDLNet_GetError());
457 }
458
459 if (n_act == 0) {
460 continue;
461 }
462
463 if (SDLNet_SocketReady(lis) != 0) {
464 ver("incoming connection");
465
466 if (con != NULL) {
467 ver("closing old");
468 con_close(con);
469 }
470
471 ver("accepting new");
472 con = SDLNet_TCP_Accept(lis);
473
474 if (con == NULL) {
475 fail("SDLNet_TCP_Accept() failed: %s", SDLNet_GetError());
476 }
477
478 if (SDLNet_TCP_AddSocket(set, con) < 0) {
479 fail("SDLNet_AddSocket() failed: %s", SDLNet_GetError());
480 }
481
482 lock_cpu();
483 state = STATE_ACK;
484 continue;
485 }
486
487 if (con == NULL || SDLNet_SocketReady(con) == 0) {
488 continue;
489 }
490
491 ver3("reading");
492 uint8_t byte;
493
494 if (SDLNet_TCP_Recv(con, &byte, 1) < 1) {
495 ver("peer closed");
496 con_close(con);
497 con = NULL;
498 continue;
499 }
500
501 result_t res = input(byte);
502
503 if (!res.ok) {
504 err("invalid packet from GDB");
505 con_close(con);
506 con = NULL;
507 continue;
508 }
509
510 if (res.out == NULL) {
511 continue;
512 }
513
514 if (SDLNet_TCP_Send(con, res.out, res.n_out) != res.n_out) {
515 err("connection error");
516 con_close(con);
517 con = NULL;
518 continue;
519 }
520 }
521
522 if (con != NULL) {
523 con_close(con);
524 }
525
526 inf("leaving GDB loop");
527}
Note: See TracBrowser for help on using the repository browser.