1/* trace.c --- tracing output for the RL78 simulator.
2
3   Copyright (C) 2005-2023 Free Software Foundation, Inc.
4   Contributed by Red Hat, Inc.
5
6   This file is part of the GNU simulators.
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 3 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20*/
21
22/* This must come before any other includes.  */
23#include "defs.h"
24
25#include <stdio.h>
26#include <stdarg.h>
27#include <string.h>
28#include <stdlib.h>
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <ctype.h>
32
33#include "libiberty.h"
34#include "bfd.h"
35#include "dis-asm.h"
36
37#include "cpu.h"
38#include "mem.h"
39#include "load.h"
40#include "trace.h"
41
42static disassembler_ftype rl78_disasm_fn = NULL;
43
44static int
45sim_dis_read (bfd_vma memaddr, bfd_byte * ptr, unsigned int length,
46	      struct disassemble_info *info)
47{
48  mem_get_blk (memaddr, ptr, length);
49  return 0;
50}
51
52/* Filter out (in place) symbols that are useless for disassembly.
53   COUNT is the number of elements in SYMBOLS.
54   Return the number of useful symbols. */
55
56static long
57remove_useless_symbols (asymbol ** symbols, long count)
58{
59  register asymbol **in_ptr = symbols, **out_ptr = symbols;
60
61  while (-- count >= 0)
62    {
63      asymbol *sym = *in_ptr ++;
64
65      if (strstr (sym->name, "gcc2_compiled"))
66	continue;
67      if (sym->name == NULL || sym->name[0] == '\0')
68	continue;
69      if (sym->flags & (BSF_DEBUGGING))
70	continue;
71      if (bfd_is_und_section (sym->section)
72	  || bfd_is_com_section (sym->section))
73	continue;
74
75      *out_ptr++ = sym;
76    }
77  return out_ptr - symbols;
78}
79
80static int
81compare_symbols (const void *ap, const void *bp)
82{
83  const asymbol *a = *(const asymbol **) ap;
84  const asymbol *b = *(const asymbol **) bp;
85
86  if (bfd_asymbol_value (a) > bfd_asymbol_value (b))
87    return 1;
88  else if (bfd_asymbol_value (a) < bfd_asymbol_value (b))
89    return -1;
90  return 0;
91}
92
93static char opbuf[1000];
94
95static int ATTRIBUTE_PRINTF (2, 3)
96op_printf (char *buf, char *fmt, ...)
97{
98  int ret;
99  va_list ap;
100
101  va_start (ap, fmt);
102  ret = vsprintf (opbuf + strlen (opbuf), fmt, ap);
103  va_end (ap);
104  return ret;
105}
106
107static int ATTRIBUTE_PRINTF (3, 4)
108op_styled_printf (char *buf, enum disassembler_style style, char *fmt, ...)
109{
110  int ret;
111  va_list ap;
112
113  va_start (ap, fmt);
114  ret = vsprintf (opbuf + strlen (opbuf), fmt, ap);
115  va_end (ap);
116  return ret;
117}
118
119static bfd *       current_bfd = NULL;
120static asymbol **  symtab = NULL;
121static int         symcount = 0;
122static asection *  code_section = NULL;
123static bfd_vma     code_base = 0;
124static struct disassemble_info info;
125
126void
127sim_disasm_init (bfd *prog)
128{
129  current_bfd = prog;
130  rl78_disasm_fn = NULL;
131}
132
133typedef struct Files
134{
135  struct Files *next;
136  char *filename;
137  int nlines;
138  char **lines;
139  char *data;
140} Files;
141Files *files = 0;
142
143static char *
144load_file_and_line (const char *filename, int lineno)
145{
146  Files *f;
147  for (f = files; f; f = f->next)
148    if (strcmp (f->filename, filename) == 0)
149      break;
150  if (!f)
151    {
152      int i;
153      struct stat s;
154      const char *found_filename, *slash;
155      FILE *file;
156      size_t ret;
157
158      found_filename = filename;
159      while (1)
160	{
161	  if (stat (found_filename, &s) == 0)
162	    break;
163	  slash = strchr (found_filename, '/');
164	  if (!slash)
165	    return "";
166	  found_filename = slash + 1;
167	}
168
169      f = (Files *) xmalloc (sizeof (Files));
170      f->next = files;
171      files = f;
172      f->filename = xstrdup (filename);
173      f->data = (char *) xmalloc (s.st_size + 2);
174      file = fopen (found_filename, "rb");
175      ret = fread (f->data, 1, s.st_size, file);
176      f->data[ret] = 0;
177      fclose (file);
178
179      f->nlines = 1;
180      for (i = 0; i < s.st_size; i ++)
181	if (f->data[i] == '\n')
182	  f->nlines ++;
183      f->lines = (char **) xmalloc (f->nlines * sizeof (char *));
184      f->lines[0] = f->data;
185      f->nlines = 1;
186      for (i = 0; i < s.st_size; i ++)
187	if (f->data[i] == '\n')
188	  {
189	    f->lines[f->nlines] = f->data + i + 1;
190	    while (*f->lines[f->nlines] == ' '
191		   || *f->lines[f->nlines] == '\t')
192	      f->lines[f->nlines] ++;
193	    f->nlines ++;
194	    f->data[i] = 0;
195	  }
196    }
197  if (lineno < 1 || lineno > f->nlines)
198    return "";
199  return f->lines[lineno - 1];
200}
201
202int
203sim_get_current_source_location (const char **  pfilename,
204				 const char **  pfunctionname,
205				 unsigned int * plineno)
206{
207  static int   initted = 0;
208  int          mypc = pc;
209
210  if (current_bfd == NULL)
211    return 0;
212
213  if (!initted)
214    {
215      int storage;
216      asection * s;
217
218      initted = 1;
219      memset (& info, 0, sizeof (info));
220      INIT_DISASSEMBLE_INFO (info, stdout, op_printf, op_styled_printf);
221      info.read_memory_func = sim_dis_read;
222      info.arch = bfd_get_arch (current_bfd);
223      info.mach = bfd_get_mach (current_bfd);
224      if (info.mach == 0)
225	info.arch = bfd_arch_rl78;
226
227      disassemble_init_for_target (& info);
228
229      storage = bfd_get_symtab_upper_bound (current_bfd);
230      if (storage > 0)
231	{
232	  symtab = (asymbol **) xmalloc (storage);
233	  symcount = bfd_canonicalize_symtab (current_bfd, symtab);
234	  symcount = remove_useless_symbols (symtab, symcount);
235	  qsort (symtab, symcount, sizeof (asymbol *), compare_symbols);
236	}
237
238      for (s = current_bfd->sections; s; s = s->next)
239	{
240	  if (s->flags & SEC_CODE || code_section == 0)
241	    {
242	      code_section = s;
243	      code_base = bfd_section_lma (s);
244	      break;
245	    }
246	}
247    }
248
249  *pfilename = *pfunctionname = NULL;
250  *plineno = 0;
251
252  bfd_find_nearest_line
253    (current_bfd, code_section, symtab, mypc - code_base,
254     pfilename, pfunctionname, plineno);
255
256  return 1;
257}
258
259void
260sim_disasm_one (void)
261{
262  static int           last_sym = -1;
263  static const char *  prev_filename = "";
264  static int           prev_lineno = 0;
265  const char *  filename;
266  const char *  functionname;
267  unsigned int  lineno;
268  int           sym, bestaddr;
269  int           min, max, i;
270  int           save_trace = trace;
271  int           mypc = pc;
272
273  if (! sim_get_current_source_location (& filename, & functionname, & lineno))
274    return;
275
276  trace = 0;
277
278  if (!rl78_disasm_fn)
279    {
280      if (rl78_g10_mode)
281	rl78_disasm_fn = print_insn_rl78_g10;
282      else if (g14_multiply)
283	rl78_disasm_fn = print_insn_rl78_g14;
284      else if (g13_multiply)
285	rl78_disasm_fn = print_insn_rl78_g13;
286      else
287	rl78_disasm_fn = print_insn_rl78;
288    }
289
290  if (filename && functionname && lineno)
291    {
292      if (lineno != prev_lineno || strcmp (prev_filename, filename))
293	{
294	  char *       the_line = load_file_and_line (filename, lineno);
295	  const char * slash = strrchr (filename, '/');
296
297	  if (!slash)
298	    slash = filename;
299	  else
300	    slash ++;
301	  printf
302	    ("========================================"
303	     "=====================================\n");
304	  printf ("\033[37;41m %s:%d: \033[33;40m %s\033[K\033[0m\n",
305		  slash, lineno, the_line);
306	}
307      prev_lineno = lineno;
308      prev_filename = filename;
309    }
310
311  min = -1;
312  max = symcount;
313  while (min < max - 1)
314    {
315      bfd_vma sa;
316
317      sym = (min + max) / 2;
318      sa = bfd_asymbol_value (symtab[sym]);
319      /*printf ("checking %4d %08x %s\n",
320	sym, sa, bfd_asymbol_name (symtab[sym])); */
321      if (sa > mypc)
322	max = sym;
323      else if (sa < mypc)
324	min = sym;
325      else
326	{
327	  min = sym;
328	  break;
329	}
330    }
331
332  if (min != -1 && min != last_sym)
333    {
334      bestaddr = bfd_asymbol_value (symtab[min]);
335      printf ("\033[43;30m%s", bfd_asymbol_name (symtab[min]));
336      if (bestaddr != mypc)
337	printf ("+%d", mypc - bestaddr);
338      printf (":\t\t\t\033[0m\n");
339      last_sym = min;
340#if 0
341      if (trace == 1)
342	if (strcmp (bfd_asymbol_name (symtab[min]), "abort") == 0
343	    || strcmp (bfd_asymbol_name (symtab[min]), "exit") == 0)
344	  trace = 0;
345#endif
346    }
347
348#define TCR0	0xf0180
349
350  opbuf[0] = 0;
351#ifdef CYCLE_ACCURATE
352  printf ("\033[33m %04u %06x: ", (int)(regs.cycle_count % 10000), mypc);
353#else
354  printf ("\033[33m %08llx %06x: ", total_clocks, mypc);
355#endif
356
357  max = rl78_disasm_fn (mypc, & info);
358
359  for (i = 0; i < max; i ++)
360    printf ("%02x", mem_get_qi (mypc + i));
361
362  do
363    {
364      printf ("  ");
365      i ++;
366    }
367  while (i < 6);
368
369  printf ("%-16s  ", opbuf);
370
371  printf ("\033[0m\n");
372  trace = save_trace;
373}
374