display.c
Go to the documentation of this file.
1 /*
2  * $Id: display.c,v 1.14 2003/12/01 09:10:14 troth Exp $
3  *
4  ****************************************************************************
5  *
6  * simulavr - A simulator for the Atmel AVR family of microcontrollers.
7  * Copyright (C) 2001, 2002, 2003 Theodore A. Roth
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22  *
23  ****************************************************************************
24  */
25 
26 /**
27  * \file display.c
28  * \brief Interface for using display coprocesses.
29  *
30  * Simulavr has the ability to use a coprocess to display register and memory
31  * values in near real time.
32  */
33 
34 #include <config.h>
35 
36 #include <stdio.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <errno.h>
40 #include <sys/types.h>
41 #include <sys/wait.h>
42 #include <unistd.h>
43 #include <fcntl.h>
44 #include <sys/ioctl.h>
45 #include <signal.h>
46 
47 #include "avrerror.h"
48 #include "avrmalloc.h"
49 #include "display.h"
50 
51 enum
52 {
53  MAX_BUF = 1024,
54 };
55 
56 /* I really don't want to use a global here, but I also don't want to have to
57  track the pipe's fd in the core. */
58 
59 static int global_pipe_fd = -1;
60 
61 /* Need to store the child's pid so that we can kill and waitpid it when you
62  close the display. Otherwise we have problems with zombies. */
63 
64 static pid_t global_child_pid = -1;
65 
66 /** \brief Open a display as a coprocess.
67  \param prog The program to use as a display coprocess.
68  \param no_xterm If non-zero, don't run the disply in an xterm.
69  \param flash_sz The size of the flash memory space in bytes.
70  \param sram_sz The size of the sram memory space in bytes.
71  \param sram_start The addr of the first byte of sram (usually 0x60 or
72  0x100).
73  \param eeprom_sz The size of the eeprom memory space in bytes.
74 
75  Try to start up a helper program as a child process for displaying
76  registers and memory. If the prog argument is NULL, don't start up a
77  display.
78 
79  Returns an open file descriptor of a pipe used to send data to
80  the helper program.
81 
82  Returns -1 if something failed. */
83 
84 int
85 display_open (char *prog, int no_xterm, int flash_sz, int sram_sz,
86  int sram_start, int eeprom_sz)
87 {
88  pid_t pid;
89  int pfd[2]; /* pipe file desc: pfd[0] is read, pfd[1] is
90  write */
91  int res;
92 
93  if (prog == NULL)
94  {
95  prog = getenv ("SIM_DISP_PROG");
96  if (prog == NULL)
97  return -1;
98  }
99 
100  /* Open a pipe for writing from the simulator to the display program.
101  We don't want to use popen() since the display program might need to
102  use stdin/stdout for it's own uses. */
103 
104  res = pipe (pfd);
105  if (res < 0)
106  {
107  avr_warning ("pipe failed: %s\n", strerror (errno));
108  return -1;
109  }
110 
111  /* Fork off a new process. */
112 
113  pid = fork ();
114  if (pid < 0)
115  {
116  avr_warning ("fork failed: %s\n", strerror (errno));
117  return -1;
118  }
119  else if (pid > 0) /* parent process */
120  {
121  /* close the read side of the pipe */
122  close (pfd[0]);
123 
124  /* remember the child's pid */
125  global_child_pid = pid;
126 
127  global_pipe_fd = pfd[1];
128  return global_pipe_fd;
129  }
130  else /* child process */
131  {
132  char pfd_env[20];
133  char fl_sz[20], sr_sz[20], sr_start[20], eep_sz[20];
134  char spfd[10];
135 
136  /* close the write side of the pipe */
137  close (pfd[1]);
138 
139  /* setup the args for display program */
140  snprintf (fl_sz, sizeof (fl_sz) - 1, "%d", flash_sz);
141  snprintf (sr_sz, sizeof (sr_sz) - 1, "%d", sram_sz);
142  snprintf (sr_start, sizeof (sr_start) - 1, "%d", sram_start);
143  snprintf (eep_sz, sizeof (eep_sz) - 1, "%d", eeprom_sz);
144  snprintf (spfd, sizeof (spfd) - 1, "%d", pfd[0]);
145 
146  /* set the SIM_PIPE_FD env variable */
147  snprintf (pfd_env, sizeof (pfd_env), "SIM_PIPE_FD=%d", pfd[0]);
148  putenv (pfd_env);
149 
150  /* The user can specify not to use an xterm since some display
151  programs might not need (or want) to be run in an xterm. For
152  example, a gtk+ program would be able to handle it's own
153  windowing. Of course, starting 'prog' up with it's own xterm, will
154  not hurt and 'prog' will put stdout/stderr there instead of mixing
155  with simulavr's output. The default is to start prog in an
156  xterm. */
157 
158  if (no_xterm)
159  {
160  execlp (prog, prog, "--pfd", spfd, fl_sz, sr_sz, sr_start, eep_sz,
161  NULL);
162  }
163  else
164  {
165  /* try to start up the display program in it's own xterm */
166  execlp ("xterm", "xterm", "-geom", "100x50", "-e", prog, "--pfd",
167  spfd, fl_sz, sr_sz, sr_start, eep_sz, NULL);
168  }
169 
170  /* if the exec returns, an error occurred */
171  avr_warning ("exec failed: %s\n", strerror (errno));
172  _exit (1);
173  }
174 
175  return -1; /* should never get here */
176 }
177 
178 /** \brief Close a display and send coprocess a quit message. */
179 
180 void
182 {
183  if (global_pipe_fd < 0)
184  return;
185 
186  display_send_msg ("q");
187  close (global_pipe_fd);
188  global_pipe_fd = -1;
189 
190  kill (global_child_pid, SIGINT);
191  waitpid (0, NULL, 0);
192 }
193 
194 static unsigned char
195 checksum (char *s)
196 {
197  unsigned char CC = 0;
198  while (*s)
199  {
200  CC += *s;
201  s++;
202  }
203 
204  return CC;
205 }
206 
207 /** \brief Encode the message and send to display.
208  \param msg The message string to be sent to the display process.
209 
210  Encoding is the same as that used by the gdb remote protocol: '\$...\#CC'
211  where '...' is msg, CC is checksum. There is no newline termination for
212  encoded messages.
213 
214  FIXME: TRoth: This should be a private function. It is only public so that
215  dtest.c can be kept simple. dtest.c should be changed to avoid direct use
216  of this function. [dtest.c has served it's purpose and will be retired
217  soon.] */
218 
219 void
220 display_send_msg (char *msg)
221 {
222  int len = strlen (msg) + 4 + 1;
223  int res;
224  char *enc_msg; /* the encoded msg */
225 
226  enc_msg = avr_new0 (char, len + 1);
227 
228  snprintf (enc_msg, len, "$%s#%02x", msg, checksum (msg));
229 #if defined(DISP_DEBUG_OUTPUT_ON)
230  fprintf (stderr, "DISP: %s\n", enc_msg);
231 #endif
232 
233  res = write (global_pipe_fd, enc_msg, len);
234  if ((res < 0) && (errno == EINTR))
235  {
236  /* write() was interrupted, try again and if it still fails, let it be
237  fatal. */
238  avr_warning ("Interrupted write()\n");
239  res = write (global_pipe_fd, enc_msg, len);
240  }
241  if (res < 0)
242  avr_error ("write failed: %s\n", strerror (errno));
243  if (res < len)
244  avr_error ("incomplete write\n");
245 
246  avr_free (enc_msg);
247 }
248 
249 static char global_buf[MAX_BUF + 1];
250 
251 /** \brief Update the time in the display.
252  \param clock The new time in number of clocks. */
253 
254 void
255 display_clock (int clock)
256 {
257  if (global_pipe_fd < 0)
258  return;
259 
260  snprintf (global_buf, MAX_BUF, "n%x", clock);
261  global_buf[MAX_BUF] = '\0';
262  display_send_msg (global_buf);
263 }
264 
265 /** \brief Update the Program Counter in the display.
266  \param val The new value of the program counter. */
267 
268 void
269 display_pc (int val)
270 {
271  if (global_pipe_fd < 0)
272  return;
273 
274  snprintf (global_buf, MAX_BUF, "p%x", val);
275  global_buf[MAX_BUF] = '\0';
276  display_send_msg (global_buf);
277 }
278 
279 /** \brief Update a register in the display.
280  \param reg The register number.
281  \param val The new value of the register. */
282 
283 void
284 display_reg (int reg, uint8_t val)
285 {
286  if (global_pipe_fd < 0)
287  return;
288 
289  snprintf (global_buf, MAX_BUF, "r%x:%02x", reg, val);
290  global_buf[MAX_BUF] = '\0';
291  display_send_msg (global_buf);
292 }
293 
294 /** \brief Update an IO register in the display.
295  \param reg The IO register number.
296  \param val The new value of the register. */
297 
298 void
299 display_io_reg (int reg, uint8_t val)
300 {
301  if (global_pipe_fd < 0)
302  return;
303 
304  snprintf (global_buf, MAX_BUF, "i%x:%02x", reg, val);
305  global_buf[MAX_BUF] = '\0';
306  display_send_msg (global_buf);
307 }
308 
309 /** \brief Specify a name for an IO register.
310  \param reg The IO register number.
311  \param name The symbolic name of the register.
312 
313  Names of IO registers may be different from device to device. */
314 
315 void
316 display_io_reg_name (int reg, char *name)
317 {
318  if (global_pipe_fd < 0)
319  return;
320 
321  snprintf (global_buf, MAX_BUF, "I%x:%s", reg, name);
322  global_buf[MAX_BUF] = '\0';
323  display_send_msg (global_buf);
324 }
325 
326 /** \brief Update a block of flash addresses in the display.
327  \param addr Address of beginning of the block.
328  \param len Length of the block (number of words).
329  \param vals Pointer to an array of \a len words.
330 
331  The display will update each addr of the block to the coresponding value
332  in the \a vals array.
333 
334  Each address in the flash references a single 16-bit wide word (or opcode
335  or instruction). Therefore, flash addresses are aligned to 16-bit
336  boundaries. It is simplest to consider the flash an array of 16-bit values
337  indexed by the address. */
338 
339 void
340 display_flash (int addr, int len, uint16_t * vals)
341 {
342  int bytes;
343  int i;
344 
345  if (global_pipe_fd < 0)
346  return;
347 
348  bytes = snprintf (global_buf, MAX_BUF, "f%x,%x:", addr, len);
349 
350  for (i = 0; i < len; i++)
351  {
352  if (MAX_BUF - bytes < 0)
353  avr_error ("buffer overflow");
354 
355  bytes +=
356  snprintf (global_buf + bytes, MAX_BUF - bytes, "%04x", vals[i]);
357  }
358 
359  global_buf[MAX_BUF] = '\0';
360  display_send_msg (global_buf);
361 }
362 
363 /** \brief Update a block of sram addresses in the display.
364  \param addr Address of beginning of the block.
365  \param len Length of the block (number of bytes).
366  \param vals Pointer to an array of \a len bytes.
367 
368  The display will update each addr of the block to the coresponding value
369  in the \a vals array. */
370 
371 void
372 display_sram (int addr, int len, uint8_t * vals)
373 {
374  int bytes;
375  int i;
376 
377  if (global_pipe_fd < 0)
378  return;
379 
380  bytes = snprintf (global_buf, MAX_BUF, "s%x,%x:", addr, len);
381 
382  for (i = 0; i < len; i++)
383  {
384  if (MAX_BUF - bytes < 0)
385  avr_error ("buffer overflow");
386 
387  bytes +=
388  snprintf (global_buf + bytes, MAX_BUF - bytes, "%02x", vals[i]);
389  }
390 
391  global_buf[MAX_BUF] = '\0';
392  display_send_msg (global_buf);
393 }
394 
395 /** \brief Update a block of eeprom addresses in the display.
396  \param addr Address of beginning of the block.
397  \param len Length of the block (number of bytes).
398  \param vals Pointer to an array of \a len bytes.
399 
400  The display will update each addr of the block to the coresponding value
401  in the \a vals array. */
402 
403 void
404 display_eeprom (int addr, int len, uint8_t * vals)
405 {
406  int bytes;
407  int i;
408 
409  if (global_pipe_fd < 0)
410  return;
411 
412  bytes = snprintf (global_buf, MAX_BUF, "e%x,%x:", addr, len);
413 
414  for (i = 0; i < len; i++)
415  {
416  if (MAX_BUF - bytes < 0)
417  avr_error ("buffer overflow");
418 
419  bytes +=
420  snprintf (global_buf + bytes, MAX_BUF - bytes, "%02x", vals[i]);
421  }
422 
423  global_buf[MAX_BUF] = '\0';
424  display_send_msg (global_buf);
425 }

Automatically generated by Doxygen 1.8.2 on Mon Jul 22 2013.