input: make it more modular
[picodrive.git] / cpu / debug.c
CommitLineData
5686d931 1/*
2 * vim:shiftwidth=2:expandtab
3 * PDB, the PicoDrive debugger
4 */
5#define _GNU_SOURCE
6#include <stdio.h>
7
8#include "../pico/pico_int.h"
9#include "debug.h"
10
11static char pdb_pending_cmds[128];
12static char pdb_event_cmds[128];
13
14static struct pdb_cpu {
15 void *context;
16 int type;
17 int id;
18 const char *name;
19 unsigned int bpts[16];
20 int bpt_count;
21 int icount;
22} pdb_cpus[5];
23static int pdb_cpu_count;
24
25static int pdb_global_icount;
26
27#ifdef PDB_NET
28#include <stddef.h>
29#include <sys/types.h>
30#include <sys/socket.h>
31#include <netinet/in.h>
32#include <arpa/inet.h>
33#include <unistd.h>
34
35#include "debug_net.h"
36
37static int pdb_net_sock = -1;
38
39int pdb_net_connect(const char *host, const char *port)
40{
41 struct sockaddr_in sockadr;
42 int sock = -1;
43 int ret;
44
45 sock = socket(PF_INET, SOCK_STREAM, 0);
46 if (sock == -1) {
47 perror("socket");
48 return -1;
49 }
50
51 sockadr.sin_addr.s_addr = inet_addr(host);
52 sockadr.sin_family = AF_INET;
53 sockadr.sin_port = htons(atoi(port));
54
55 ret = connect(sock, (struct sockaddr *)&sockadr, sizeof(sockadr));
56 if (ret != 0) {
57 perror("pdb_net: connect");
58 close(sock);
59 return -1;
60 }
61
62 printf("pdb_net: connected to %s:%s\n", host, port);
63
64 pdb_net_sock = sock;
65 return 0;
66}
67
68static int pdb_net_send(struct pdb_cpu *cpu, unsigned int pc)
69{
70 packet_t packet;
71 int ret;
72
73 if (pdb_net_sock < 0)
74 return 0; // not connected
75
76 if (cpu->type == PDBCT_SH2) {
77 SH2 *sh2 = cpu->context;
78 int rl = offsetof(SH2, macl) + sizeof(sh2->macl);
79 packet.header.type = PDBCT_SH2;
80 packet.header.cpuid = cpu->id;
81 packet.regs[0] = pc;
82 memcpy(&packet.regs[1], sh2->r, rl);
83 packet.regs[1+24+0] = sh2->pdb_io_csum[0];
84 packet.regs[1+24+1] = sh2->pdb_io_csum[1];
85 packet.header.len = 4 + rl + 4*2;
86 sh2->pdb_io_csum[0] = sh2->pdb_io_csum[1] = 0;
87 }
88
89 ret = send(pdb_net_sock, &packet, sizeof(packet.header) + packet.header.len, MSG_NOSIGNAL);
90 if (ret != sizeof(packet.header) + packet.header.len) {
91 if (ret < 0)
92 perror("send");
93 else
94 printf("send: %d/%d\n", ret, sizeof(packet.header) + packet.header.len);
95 close(pdb_net_sock);
96 pdb_net_sock = -1;
97 ret = -1;
98 }
99 return ret;
100}
101#else
102#define pdb_net_send(a,b) 0
103#endif // PDB_NET
104
105#ifdef HAVE_READLINE
106#include <readline/readline.h>
107#include <readline/history.h>
108#endif
109
110static char *my_readline(const char *prompt)
111{
112 char *line = NULL;
113
114#ifdef HAVE_READLINE
115 line = readline("(pdb) ");
116 if (line == NULL)
117 return NULL;
118 if (line[0] != 0)
119 add_history(line);
120#else
121 size_t size = 0;
122 ssize_t ret;
123
124 printf("(pdb) ");
125 fflush(stdout);
126 ret = getline(&line, &size, stdin);
127 if (ret < 0)
128 return NULL;
129 if (ret > 0 && line[ret - 1] == '\n')
130 line[ret - 1] = 0;
131#endif
132
133 return line;
134}
135
136static struct pdb_cpu *context2cpu(const void *context)
137{
138 int i;
139 for (i = 0; i < pdb_cpu_count; i++)
140 if (pdb_cpus[i].context == context)
141 return &pdb_cpus[i];
142 return NULL;
143}
144
145static const char *get_token(char *buf, int blen, const char *str)
146{
147 const char *p, *s, *e;
148 int len;
149
150 p = str;
151 while (isspace_(*p))
152 p++;
153 if (*p == 0)
154 return NULL;
155 if (*p == ';') {
156 strcpy(buf, ";");
157 return p + 1;
158 }
159
160 s = p;
161 while (*p != 0 && *p != ';' && !isspace_(*p))
162 p++;
163 e = p;
164 while (isspace_(*e))
165 e++;
166
167 len = p - s;
168 if (len > blen - 1)
169 len = blen - 1;
170 memcpy(buf, s, len);
171 buf[len] = 0;
172 return e;
173}
174
175static const char *get_arg(char *buf, int blen, const char *str)
176{
177 if (*str == ';')
178 return NULL;
179 return get_token(buf, blen, str);
180}
181
182enum cmd_ret_e {
183 CMDRET_DONE, // ..and back to prompt
184// CMDRET_PROMPT, // go to prompt
185 CMDRET_CONT_DO_NEXT, // continue and do remaining cmds on next event
186 CMDRET_CONT_REDO, // continue and redo all cmds on next event
187};
188
189static int do_print(struct pdb_cpu *cpu, const char *args)
190{
5686d931 191 elprintf(EL_STATUS, "cpu %d (%s)", cpu->id, cpu->name);
f3a57b2d 192#ifndef NO_32X
5686d931 193 if (cpu->type == PDBCT_SH2) {
194 SH2 *sh2 = cpu->context;
f3a57b2d 195 int i;
5686d931 196 printf("PC,SR %08x, %03x\n", sh2->pc, sh2->sr & 0x3ff);
197 for (i = 0; i < 16/2; i++)
198 printf("R%d,%2d %08x,%08x\n", i, i + 8, sh2->r[i], sh2->r[i + 8]);
199 printf("gb,vb %08x,%08x\n", sh2->gbr, sh2->vbr);
200 printf("IRQs/mask: %02x/%02x\n", Pico32x.sh2irqi[sh2->is_slave],
201 Pico32x.sh2irq_mask[sh2->is_slave]);
202 printf("cycles %d/%d (%d)\n", sh2->cycles_done, sh2->cycles_aim, (signed int)sh2->sr >> 12);
203 }
f3a57b2d 204#endif
5686d931 205 return CMDRET_DONE;
206}
207
208static int do_step_all(struct pdb_cpu *cpu, const char *args)
209{
210 char tmp[32];
211 if (!get_arg(tmp, sizeof(tmp), args)) {
212 printf("step_all: missing arg\n");
213 return CMDRET_DONE;
214 }
215
216 pdb_global_icount = atoi(tmp);
217 return CMDRET_CONT_DO_NEXT;
218}
219
220static int do_continue(struct pdb_cpu *cpu, const char *args)
221{
222 char tmp[32];
223 if (get_arg(tmp, sizeof(tmp), args))
224 cpu->icount = atoi(tmp);
225 return CMDRET_CONT_DO_NEXT;
226}
227
228static int do_step(struct pdb_cpu *cpu, const char *args)
229{
230 cpu->icount = 1;
231 return do_continue(cpu, args);
232}
233
234static int do_waitcpu(struct pdb_cpu *cpu, const char *args)
235{
236 char tmp[32];
237 if (!get_arg(tmp, sizeof(tmp), args)) {
238 printf("waitcpu: missing arg\n");
239 return CMDRET_DONE;
240 }
241 if (strcmp(tmp, cpu->name) == 0)
242 return CMDRET_DONE;
243
244 return CMDRET_CONT_REDO;
245}
246
247static int do_help(struct pdb_cpu *cpu, const char *args);
248
249static struct {
250 const char *cmd;
251 const char *help;
252 int (*handler)(struct pdb_cpu *cpu, const char *args);
253} pdb_cmds[] = {
254 { "help", "", do_help },
255 { "continue", "[insns]", do_continue },
256 { "step", "[insns]", do_step },
257 { "step_all", "<insns>", do_step_all },
258 { "waitcpu", "<cpuname>", do_waitcpu },
259 { "print", "", do_print },
260};
261
262static int do_help(struct pdb_cpu *cpu, const char *args)
263{
264 int i;
265 for (i = 0; i < ARRAY_SIZE(pdb_cmds); i++)
266 printf("%s %s\n", pdb_cmds[i].cmd, pdb_cmds[i].help);
267 return CMDRET_DONE;
268}
269
270static int do_comands(struct pdb_cpu *cpu, const char *cmds)
271{
272 const char *p = cmds;
273 while (p != NULL)
274 {
275 const char *pcmd;
276 char cmd[32];
277 int i, len;
278 int ret = 0;
279
280 pcmd = p;
281 p = get_token(cmd, sizeof(cmd), p);
282 if (p == NULL)
283 break;
284 if (cmd[0] == ';')
285 continue;
286
287 len = strlen(cmd);
288 for (i = 0; i < ARRAY_SIZE(pdb_cmds); i++)
289 if (strncmp(pdb_cmds[i].cmd, cmd, len) == 0) {
290 ret = pdb_cmds[i].handler(cpu, p);
291 break;
292 }
293
294 if (i == ARRAY_SIZE(pdb_cmds)) {
295 printf("bad cmd: %s\n", cmd);
296 break;
297 }
298
299 // skip until next command
300 while (1) {
301 p = get_token(cmd, sizeof(cmd), p);
302 if (p == NULL || cmd[0] == ';')
303 break;
304 }
305
306 pdb_event_cmds[0] = 0;
307 if (ret == CMDRET_CONT_DO_NEXT) {
308 pdb_pending_cmds[0] = 0;
309 if (p != NULL)
310 strcpy(pdb_event_cmds, p);
311 return 0;
312 }
313 if (ret == CMDRET_CONT_REDO) {
314 if (pcmd != pdb_pending_cmds)
315 strncpy(pdb_pending_cmds, pcmd, sizeof(pdb_pending_cmds));
316 return 0;
317 }
318 pdb_pending_cmds[0] = 0;
319 }
320
321 return 1;
322}
323
324static void do_prompt(struct pdb_cpu *cpu)
325{
326 static char prev[128];
327 int ret;
328
329 while (1) {
330 char *line, *cline;
331
332 line = my_readline("(pdb) ");
333 if (line == NULL)
334 break;
335 if (line[0] == 0)
336 cline = prev;
337 else {
338 cline = line;
339 strncpy(prev, line, sizeof(prev));
340 }
341
342 ret = do_comands(cpu, cline);
343 free(line);
344
345 if (ret == 0)
346 break;
347 }
348}
349
350void pdb_register_cpu(void *context, int type, const char *name)
351{
352 int i = pdb_cpu_count;
353 memset(&pdb_cpus[i], 0, sizeof(pdb_cpus[i]));
354 pdb_cpus[i].context = context;
355 pdb_cpus[i].type = type;
356 pdb_cpus[i].id = pdb_cpu_count;
357 pdb_cpus[i].name = name;
358 pdb_cpus[i].icount = -1;
359 pdb_cpu_count++;
360}
361
362void pdb_step(void *context, unsigned int pc)
363{
364 struct pdb_cpu *cpu = context2cpu(context);
365 int i;
366
367 if (pdb_net_send(cpu, pc) < 0)
368 goto prompt;
369
370 if (pdb_pending_cmds[0] != 0)
371 if (do_comands(cpu, pdb_pending_cmds))
372 goto prompt;
373
374 // breakpoint?
375 for (i = 0; i < cpu->bpt_count; i++)
376 if (cpu->bpts[i] == pc)
377 goto prompt;
378
379 // hit num of insns?
380 if (pdb_global_icount > 0)
381 if (--pdb_global_icount == 0)
382 goto prompt;
383
384 if (cpu->icount > 0)
385 if (--(cpu->icount) == 0)
386 goto prompt;
387
388 return;
389
390prompt:
391 if (pdb_event_cmds[0] != 0)
392 if (!do_comands(cpu, pdb_event_cmds))
393 return;
394
395 printf("%s @%08x\n", cpu->name, pc);
396 do_prompt(cpu);
397}
398
399void pdb_command(const char *cmd)
400{
401 strncpy(pdb_pending_cmds, cmd, sizeof(pdb_pending_cmds));
402 pdb_pending_cmds[sizeof(pdb_pending_cmds) - 1] = 0;
403}
404
405void pdb_cleanup(void)
406{
407 pdb_cpu_count = 0;
408}
409