Main Page

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
181 display_close (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 }
display_open
int display_open(char *prog, int no_xterm, int flash_sz, int sram_sz, int sram_start, int eeprom_sz)
Open a display as a coprocess.
Definition: display.c:85
display_close
void display_close(void)
Close a display and send coprocess a quit message.
Definition: display.c:181
avr_free
void avr_free(void *ptr)
Free malloc&#39;d memory.
Definition: avrmalloc.c:187
display_reg
void display_reg(int reg, uint8_t val)
Update a register in the display.
Definition: display.c:284
display_send_msg
void display_send_msg(char *msg)
Encode the message and send to display.
Definition: display.c:220
avr_warning
#define avr_warning(fmt, args...)
Print a warning message to stderr.
Definition: avrerror.c:46
avr_error
#define avr_error(fmt, args...)
Print an error message to stderr and terminate program.
Definition: avrerror.c:50
display_io_reg_name
void display_io_reg_name(int reg, char *name)
Specify a name for an IO register.
Definition: display.c:316
display_pc
void display_pc(int val)
Update the Program Counter in the display.
Definition: display.c:269
display_sram
void display_sram(int addr, int len, uint8_t *vals)
Update a block of sram addresses in the display.
Definition: display.c:372
display_io_reg
void display_io_reg(int reg, uint8_t val)
Update an IO register in the display.
Definition: display.c:299
display_flash
void display_flash(int addr, int len, uint16_t *vals)
Update a block of flash addresses in the display.
Definition: display.c:340
display_clock
void display_clock(int clock)
Update the time in the display.
Definition: display.c:255
display_eeprom
void display_eeprom(int addr, int len, uint8_t *vals)
Update a block of eeprom addresses in the display.
Definition: display.c:404
avr_new0
#define avr_new0(type, count)
Macro for allocating memory and initializing it to zero.
Definition: avrmalloc.c:67

Automatically generated by Doxygen 1.8.12 on Sun Apr 2 2017.