Main Page

- Main Page
- Related Pages
- Files

- File List
- Globals

# display.c

Go to the documentation of this file.

    00001 /*
    00002  * $Id: display.c,v 1.14 2003/12/01 09:10:14 troth Exp $
    00003  *
    00004  ****************************************************************************
    00005  *
    00006  * simulavr - A simulator for the Atmel AVR family of microcontrollers.
    00007  * Copyright (C) 2001, 2002, 2003  Theodore A. Roth
    00008  *
    00009  * This program is free software; you can redistribute it and/or modify
    00010  * it under the terms of the GNU General Public License as published by
    00011  * the Free Software Foundation; either version 2 of the License, or
    00012  * (at your option) any later version.
    00013  *
    00014  * This program is distributed in the hope that it will be useful,
    00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    00017  * GNU General Public License for more details.
    00018  *
    00019  * You should have received a copy of the GNU General Public License
    00020  * along with this program; if not, write to the Free Software
    00021  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    00022  *
    00023  ****************************************************************************
    00024  */
    00025
    00026 /**
    00027  * \file display.c
    00028  * \brief Interface for using display coprocesses.
    00029  *
    00030  * Simulavr has the ability to use a coprocess to display register and memory
    00031  * values in near real time.
    00032  */
    00033
    00034 #include <config.h>
    00035
    00036 #include <stdio.h>
    00037 #include <string.h>
    00038 #include <stdlib.h>
    00039 #include <errno.h>
    00040 #include <sys/types.h>
    00041 #include <sys/wait.h>
    00042 #include <unistd.h>
    00043 #include <fcntl.h>
    00044 #include <sys/ioctl.h>
    00045 #include <signal.h>
    00046
    00047 #include "avrerror.h"
    00048 #include "avrmalloc.h"
    00049 #include "display.h"
    00050
    00051 enum
    00052 {
    00053     MAX_BUF = 1024,
    00054 };
    00055
    00056 /* I really don't want to use a global here, but I also don't want to have to
    00057    track the pipe's fd in the core. */
    00058
    00059 static int global_pipe_fd = -1;
    00060
    00061 /* Need to store the child's pid so that we can kill and waitpid it when you
    00062    close the display. Otherwise we have problems with zombies. */
    00063
    00064 static pid_t global_child_pid = -1;
    00065
    00066 /** \brief Open a display as a coprocess.
    00067     \param prog        The program to use as a display coprocess.
    00068     \param no_xterm    If non-zero, don't run the disply in an xterm.
    00069     \param flash_sz    The size of the flash memory space in bytes.
    00070     \param sram_sz     The size of the sram memory space in bytes.
    00071     \param sram_start  The addr of the first byte of sram (usually 0x60 or
    00072                        0x100).
    00073     \param eeprom_sz   The size of the eeprom memory space in bytes.
    00074
    00075     Try to start up a helper program as a child process for displaying
    00076     registers and memory. If the prog argument is NULL, don't start up a
    00077     display.
    00078
    00079     Returns an open file descriptor of a pipe used to send data to
    00080     the helper program.
    00081
    00082     Returns -1 if something failed. */
    00083
    00084 int
    00085 display_open (char *prog, int no_xterm, int flash_sz, int sram_sz,
    00086               int sram_start, int eeprom_sz)
    00087 {
    00088     pid_t pid;
    00089     int pfd[2];                 /* pipe file desc: pfd[0] is read, pfd[1] is
    00090                                    write */
    00091     int res;
    00092
    00093     if (prog == NULL)
    00094     {
    00095         prog = getenv ("SIM_DISP_PROG");
    00096         if (prog == NULL)
    00097             return -1;
    00098     }
    00099
    00100     /* Open a pipe for writing from the simulator to the display program.
    00101        We don't want to use popen() since the display program might need to
    00102        use stdin/stdout for it's own uses. */
    00103
    00104     res = pipe (pfd);
    00105     if (res < 0)
    00106     {
    00107         avr_warning ("pipe failed: %s\n", strerror (errno));
    00108         return -1;
    00109     }
    00110
    00111     /* Fork off a new process. */
    00112
    00113     pid = fork ();
    00114     if (pid < 0)
    00115     {
    00116         avr_warning ("fork failed: %s\n", strerror (errno));
    00117         return -1;
    00118     }
    00119     else if (pid > 0)           /* parent process */
    00120     {
    00121         /* close the read side of the pipe */
    00122         close (pfd[0]);
    00123
    00124         /* remember the child's pid */
    00125         global_child_pid = pid;
    00126
    00127         global_pipe_fd = pfd[1];
    00128         return global_pipe_fd;
    00129     }
    00130     else                        /* child process */
    00131     {
    00132         char pfd_env[20];
    00133         char fl_sz[20], sr_sz[20], sr_start[20], eep_sz[20];
    00134         char spfd[10];
    00135
    00136         /* close the write side of the pipe */
    00137         close (pfd[1]);
    00138
    00139         /* setup the args for display program */
    00140         snprintf (fl_sz, sizeof (fl_sz) - 1, "%d", flash_sz);
    00141         snprintf (sr_sz, sizeof (sr_sz) - 1, "%d", sram_sz);
    00142         snprintf (sr_start, sizeof (sr_start) - 1, "%d", sram_start);
    00143         snprintf (eep_sz, sizeof (eep_sz) - 1, "%d", eeprom_sz);
    00144         snprintf (spfd, sizeof (spfd) - 1, "%d", pfd[0]);
    00145
    00146         /* set the SIM_PIPE_FD env variable */
    00147         snprintf (pfd_env, sizeof (pfd_env), "SIM_PIPE_FD=%d", pfd[0]);
    00148         putenv (pfd_env);
    00149
    00150         /* The user can specify not to use an xterm since some display
    00151            programs might not need (or want) to be run in an xterm. For
    00152            example, a gtk+ program would be able to handle it's own
    00153            windowing. Of course, starting 'prog' up with it's own xterm, will
    00154            not hurt and 'prog' will put stdout/stderr there instead of mixing
    00155            with simulavr's output. The default is to start prog in an
    00156            xterm. */
    00157
    00158         if (no_xterm)
    00159         {
    00160             execlp (prog, prog, "--pfd", spfd, fl_sz, sr_sz, sr_start, eep_sz,
    00161                     NULL);
    00162         }
    00163         else
    00164         {
    00165             /* try to start up the display program in it's own xterm */
    00166             execlp ("xterm", "xterm", "-geom", "100x50", "-e", prog, "--pfd",
    00167                     spfd, fl_sz, sr_sz, sr_start, eep_sz, NULL);
    00168         }
    00169
    00170         /* if the exec returns, an error occurred */
    00171         avr_warning ("exec failed: %s\n", strerror (errno));
    00172         _exit (1);
    00173     }
    00174
    00175     return -1;                  /* should never get here */
    00176 }
    00177
    00178 /** \brief Close a display and send coprocess a quit message. */
    00179
    00180 void
    00181 display_close (void)
    00182 {
    00183     if (global_pipe_fd < 0)
    00184         return;
    00185
    00186     display_send_msg ("q");
    00187     close (global_pipe_fd);
    00188     global_pipe_fd = -1;
    00189
    00190     kill (global_child_pid, SIGINT);
    00191     waitpid (0, NULL, 0);
    00192 }
    00193
    00194 static unsigned char
    00195 checksum (char *s)
    00196 {
    00197     unsigned char CC = 0;
    00198     while (*s)
    00199     {
    00200         CC += *s;
    00201         s++;
    00202     }
    00203
    00204     return CC;
    00205 }
    00206
    00207 /** \brief Encode the message and send to display.
    00208     \param msg   The message string to be sent to the display process.
    00209
    00210     Encoding is the same as that used by the gdb remote protocol: '\$...\#CC'
    00211     where '...' is msg, CC is checksum. There is no newline termination for
    00212     encoded messages.
    00213
    00214     FIXME: TRoth: This should be a private function. It is only public so that
    00215     dtest.c can be kept simple. dtest.c should be changed to avoid direct use
    00216     of this function. [dtest.c has served it's purpose and will be retired
    00217     soon.] */
    00218
    00219 void
    00220 display_send_msg (char *msg)
    00221 {
    00222     int len = strlen (msg) + 4 + 1;
    00223     int res;
    00224     char *enc_msg;              /* the encoded msg */
    00225
    00226     enc_msg = avr_new0 (char, len + 1);
    00227
    00228     snprintf (enc_msg, len, "$%s#%02x", msg, checksum (msg));
    00229 #if defined(DISP_DEBUG_OUTPUT_ON)
    00230     fprintf (stderr, "DISP: %s\n", enc_msg);
    00231 #endif
    00232
    00233     res = write (global_pipe_fd, enc_msg, len);
    00234     if ((res < 0) && (errno == EINTR))
    00235     {
    00236         /* write() was interrupted, try again and if it still fails, let it be
    00237            fatal. */
    00238         avr_warning ("Interrupted write()\n");
    00239         res = write (global_pipe_fd, enc_msg, len);
    00240     }
    00241     if (res < 0)
    00242         avr_error ("write failed: %s\n", strerror (errno));
    00243     if (res < len)
    00244         avr_error ("incomplete write\n");
    00245
    00246     avr_free (enc_msg);
    00247 }
    00248
    00249 static char global_buf[MAX_BUF + 1];
    00250
    00251 /** \brief Update the time in the display.
    00252     \param clock   The new time in number of clocks. */
    00253
    00254 void
    00255 display_clock (int clock)
    00256 {
    00257     if (global_pipe_fd < 0)
    00258         return;
    00259
    00260     snprintf (global_buf, MAX_BUF, "n%x", clock);
    00261     global_buf[MAX_BUF] = '\0';
    00262     display_send_msg (global_buf);
    00263 }
    00264
    00265 /** \brief Update the Program Counter in the display.
    00266     \param val   The new value of the program counter. */
    00267
    00268 void
    00269 display_pc (int val)
    00270 {
    00271     if (global_pipe_fd < 0)
    00272         return;
    00273
    00274     snprintf (global_buf, MAX_BUF, "p%x", val);
    00275     global_buf[MAX_BUF] = '\0';
    00276     display_send_msg (global_buf);
    00277 }
    00278
    00279 /** \brief Update a register in the display.
    00280     \param reg   The register number.
    00281     \param val   The new value of the register. */
    00282
    00283 void
    00284 display_reg (int reg, uint8_t val)
    00285 {
    00286     if (global_pipe_fd < 0)
    00287         return;
    00288
    00289     snprintf (global_buf, MAX_BUF, "r%x:%02x", reg, val);
    00290     global_buf[MAX_BUF] = '\0';
    00291     display_send_msg (global_buf);
    00292 }
    00293
    00294 /** \brief Update an IO register in the display.
    00295     \param reg   The IO register number.
    00296     \param val   The new value of the register. */
    00297
    00298 void
    00299 display_io_reg (int reg, uint8_t val)
    00300 {
    00301     if (global_pipe_fd < 0)
    00302         return;
    00303
    00304     snprintf (global_buf, MAX_BUF, "i%x:%02x", reg, val);
    00305     global_buf[MAX_BUF] = '\0';
    00306     display_send_msg (global_buf);
    00307 }
    00308
    00309 /** \brief Specify a name for an IO register.
    00310     \param reg    The IO register number.
    00311     \param name   The symbolic name of the register.
    00312
    00313     Names of IO registers may be different from device to device. */
    00314
    00315 void
    00316 display_io_reg_name (int reg, char *name)
    00317 {
    00318     if (global_pipe_fd < 0)
    00319         return;
    00320
    00321     snprintf (global_buf, MAX_BUF, "I%x:%s", reg, name);
    00322     global_buf[MAX_BUF] = '\0';
    00323     display_send_msg (global_buf);
    00324 }
    00325
    00326 /** \brief Update a block of flash addresses in the display.
    00327     \param addr  Address of beginning of the block.
    00328     \param len   Length of the block (number of words).
    00329     \param vals  Pointer to an array of \a len words.
    00330
    00331     The display will update each addr of the block to the coresponding value
    00332     in the \a vals array.
    00333
    00334     Each address in the flash references a single 16-bit wide word (or opcode
    00335     or instruction). Therefore, flash addresses are aligned to 16-bit
    00336     boundaries. It is simplest to consider the flash an array of 16-bit values
    00337     indexed by the address. */
    00338
    00339 void
    00340 display_flash (int addr, int len, uint16_t * vals)
    00341 {
    00342     int bytes;
    00343     int i;
    00344
    00345     if (global_pipe_fd < 0)
    00346         return;
    00347
    00348     bytes = snprintf (global_buf, MAX_BUF, "f%x,%x:", addr, len);
    00349
    00350     for (i = 0; i < len; i++)
    00351     {
    00352         if (MAX_BUF - bytes < 0)
    00353             avr_error ("buffer overflow");
    00354
    00355         bytes +=
    00356             snprintf (global_buf + bytes, MAX_BUF - bytes, "%04x", vals[i]);
    00357     }
    00358
    00359     global_buf[MAX_BUF] = '\0';
    00360     display_send_msg (global_buf);
    00361 }
    00362
    00363 /** \brief Update a block of sram addresses in the display.
    00364     \param addr  Address of beginning of the block.
    00365     \param len   Length of the block (number of bytes).
    00366     \param vals  Pointer to an array of \a len bytes.
    00367
    00368     The display will update each addr of the block to the coresponding value
    00369     in the \a vals array. */
    00370
    00371 void
    00372 display_sram (int addr, int len, uint8_t * vals)
    00373 {
    00374     int bytes;
    00375     int i;
    00376
    00377     if (global_pipe_fd < 0)
    00378         return;
    00379
    00380     bytes = snprintf (global_buf, MAX_BUF, "s%x,%x:", addr, len);
    00381
    00382     for (i = 0; i < len; i++)
    00383     {
    00384         if (MAX_BUF - bytes < 0)
    00385             avr_error ("buffer overflow");
    00386
    00387         bytes +=
    00388             snprintf (global_buf + bytes, MAX_BUF - bytes, "%02x", vals[i]);
    00389     }
    00390
    00391     global_buf[MAX_BUF] = '\0';
    00392     display_send_msg (global_buf);
    00393 }
    00394
    00395 /** \brief Update a block of eeprom addresses in the display.
    00396     \param addr  Address of beginning of the block.
    00397     \param len   Length of the block (number of bytes).
    00398     \param vals  Pointer to an array of \a len bytes.
    00399
    00400     The display will update each addr of the block to the coresponding value
    00401     in the \a vals array. */
    00402
    00403 void
    00404 display_eeprom (int addr, int len, uint8_t * vals)
    00405 {
    00406     int bytes;
    00407     int i;
    00408
    00409     if (global_pipe_fd < 0)
    00410         return;
    00411
    00412     bytes = snprintf (global_buf, MAX_BUF, "e%x,%x:", addr, len);
    00413
    00414     for (i = 0; i < len; i++)
    00415     {
    00416         if (MAX_BUF - bytes < 0)
    00417             avr_error ("buffer overflow");
    00418
    00419         bytes +=
    00420             snprintf (global_buf + bytes, MAX_BUF - bytes, "%02x", vals[i]);
    00421     }
    00422
    00423     global_buf[MAX_BUF] = '\0';
    00424     display_send_msg (global_buf);
    00425 }

Automatically generated by Doxygen 1.7.2 on Wed Aug 15 2012.