Commit 670668846c62f42c84d75c17e97373bc0ca99f1b
authorMichel Pollet <buserror@gmail.com>
Wed, 29 Mar 2017 10:19:14 +0000 (11:19 +0100)
committerMichel Pollet <buserror@gmail.com>
Wed, 10 Jan 2018 10:05:59 +0000 (10:05 +0000)
Some bits I spliced out of simreprap to have a shell to interract with a
running firmware. Very prototype, just a placeholder for now.

Signed-off-by: Michel Pollet <buserror@gmail.com>
16 files changed:
Makefile.common
shavr/Makefile [new file with mode: 0644]
shavr/src/hcmd_adc.c [new file with mode: 0644]
shavr/src/hcmd_symbols.c [new file with mode: 0644]
shavr/src/history.c [new file with mode: 0644]
shavr/src/history.h [new file with mode: 0644]
shavr/src/history_avr.c [new file with mode: 0644]
shavr/src/history_avr.h [new file with mode: 0644]
shavr/src/history_cmd.c [new file with mode: 0644]
shavr/src/history_cmd.h [new file with mode: 0644]
shavr/src/shavr.c [new file with mode: 0644]
shavr/src/sim_args.c [new file with mode: 0644]
shavr/src/sim_args.h [new file with mode: 0644]
shavr/src/sim_prepare.c [new file with mode: 0644]
shavr/src/uart_pty.c [new file with mode: 0644]
shavr/src/uart_pty.h [new file with mode: 0644]

index 9b56e27606928699bcef2d9d8e42dbf940c809e6..8d32924d853e3486370b9f3d3c0e1421e8cbb27b 100644 (file)
@@ -41,7 +41,7 @@ ARCH          := ${shell $(CC) -dumpmachine | sed -e 's/^x/i/' -e 's/\(.\).*/\1/'}
 
 CFLAGS         += -O2 -Wall -Wextra -Wno-unused-parameter \
                        -Wno-unused-result -Wno-missing-field-initializers \
-                       -Wno-sign-compare
+                       -Wno-sign-compare -Wno-unused-function
 CFLAGS         += -g
 CORE_CFLAGS    = -DAVR_CORE=1
 
diff --git a/shavr/Makefile b/shavr/Makefile
new file mode 100644 (file)
index 0000000..79f05ea
--- /dev/null
@@ -0,0 +1,47 @@
+#
+#      Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+#
+#      This file is part of simavr.
+#
+#      simavr is free software: you can redistribute it and/or modify
+#      it under the terms of the GNU General Public License as published by
+#      the Free Software Foundation, either version 3 of the License, or
+#      (at your option) any later version.
+#
+#      simavr is distributed in the hope that it will be useful,
+#      but WITHOUT ANY WARRANTY; without even the implied warranty of
+#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#      GNU General Public License for more details.
+#
+#      You should have received a copy of the GNU General Public License
+#      along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+
+target = shavr
+simavr = ../
+
+IPATH = .
+IPATH += ${simavr}/include
+IPATH += ${simavr}/simavr/sim
+IPATH += ${simavr}/simavr/
+
+VPATH = src
+
+srcs := ${wildcard src/*.c}
+LDFLAGS += -lpthread -lutil
+
+all: obj ${target}
+
+include ${simavr}/Makefile.common
+
+objs   := ${patsubst src/%.c, ${OBJ}/%.o, ${srcs}}
+
+board = ${OBJ}/${target}.elf
+
+${board} : ${objs}
+${board} : ${OBJ}/${target}.o
+
+${target}: ${board}
+       @echo $@ done
+
+clean: clean-${OBJ}
+       rm -rf *.a *.axf ${target} *.vcd *.hex
diff --git a/shavr/src/hcmd_adc.c b/shavr/src/hcmd_adc.c
new file mode 100644 (file)
index 0000000..4bdd3d7
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * hcmd_adc.c
+ *
+ *  Created on: 15 Oct 2015
+ *      Author: michel
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <libgen.h>
+#include <string.h>
+#include <ctype.h>
+#include <wordexp.h>
+
+#include "history_avr.h"
+#include "avr_adc.h"
+
+static uint16_t adc_mask = 0;
+static uint16_t        adc_val[16];
+struct avr_irq_t * adc0_irq = NULL;
+
+static void
+adc_update_hook(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       union {
+               avr_adc_mux_t mux;
+               uint32_t v;
+       } m = { .v = value };
+//     /printf("read mux %d\r\n", m.mux.src);
+       if (!adc0_irq)
+               adc0_irq = avr_io_getirq(avr, AVR_IOCTL_ADC_GETIRQ, ADC_IRQ_ADC0);
+       if (adc_mask & (1 << m.mux.src)) {
+               int noise = 0; //(random() % 3) - 1;
+               int adc = (int)adc_val[m.mux.src] + noise;
+       //      printf("ADC %d %d:%d\n", m.mux.src, adc, noise);
+               avr_raise_irq(adc0_irq + m.mux.src, noise < 0 ? 0 : adc);
+       }
+}
+
+/*
+ * called when the AVR change any of the pins we listen to
+ */
+static void
+pin_changed_hook(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+ //   pin_state = (pin_state & ~(1 << irq->irq)) | (value << irq->irq);
+       AVR_LOG(avr, LOG_OUTPUT, "PORT B%d changed to %d\n", irq->irq,  value);
+}
+
+
+static int
+_cmd_adc(
+               wordexp_t * l)
+{
+       char *cmd = l->we_wordv[0];
+       char * it = l->we_wordv[1];
+       int index = 0;
+       if (!sscanf(it, "%d", &index) || index > 15) {
+               fprintf(stderr, "%s: invalid adc number '%s'\r\n", cmd, it);
+       } else {
+               char * val = l->we_wordv[2];
+               if (val) {
+                       int v = 0;
+                       if (!sscanf(val, "%d", &v) || v < 0 || v > 5500) {
+                               fprintf(stderr, "%s: invalid adc value '%s'\r\n", cmd,
+                                               val);
+                       } else {
+                               adc_val[index] = v;
+                               adc_mask |= (1 << index);
+                       }
+               }
+               fprintf(stderr, "ADC %d = %d (0x%04x)\r\n", index,
+                               adc_val[index], adc_val[index]);
+       }
+       return 0;
+}
+
+
+const history_cmd_t cmd_adc = {
+       .names[0] = "adc",
+       .usage = "<adc number> [<value in mV>]: Get/set ADC value",
+       .help = "Get and sets ADC input IRQ value",
+       .parameter_map = (1 << 1) | (1 << 2),
+       .execute = _cmd_adc,
+};
+HISTORY_CMD_REGISTER(cmd_adc);
+
diff --git a/shavr/src/hcmd_symbols.c b/shavr/src/hcmd_symbols.c
new file mode 100644 (file)
index 0000000..0cf4c8f
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * hcmd_symbols.c
+ *
+ *  Created on: 15 Oct 2015
+ *      Author: michel
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <libgen.h>
+#include <string.h>
+#include <ctype.h>
+#include <wordexp.h>
+
+#include "history_avr.h"
+
+static const char *
+demangle(
+               const char *symbol)
+{
+       if (symbol[0] == '_' && symbol[1] == 'Z') {
+               symbol += 3;
+               while (isdigit(*symbol))
+                       symbol++;
+       }
+       return symbol;
+}
+
+int
+_locate_sym(
+               const char *name,
+               avr_symbol_t ** out,
+               int * outsize)
+{
+       int size = 1;
+       avr_symbol_t * sym = NULL;
+       for (int i = 0; i < code.symbolcount && !sym; i++) {
+               if (!strcmp(demangle(code.symbol[i]->symbol), name)) {
+                       sym = code.symbol[i];
+                       if (i < code.symbolcount-1) {
+                               if ((code.symbol[i+1]->addr >> 16) == (sym->addr >> 16)) {
+                                       size = code.symbol[i+1]->addr - sym->addr;
+                               //      printf("deduced size %d\r\n", size);
+                               }
+                       }
+               }
+       }
+       *out = sym;
+       *outsize = size;
+
+       return sym != NULL;
+}
+
+static int
+_cmd_print(
+               wordexp_t * l)
+{
+       char *cmd = l->we_wordv[0];
+       char * nt = l->we_wordv[1];
+       char * st = l->we_wordv[2];
+
+       int size = 1;
+       avr_symbol_t * sym = NULL;
+       _locate_sym(nt, &sym, &size);
+
+       if (st) {
+               if (!strncmp(st, "0x", 2)) {
+                       st += 2;
+                       sscanf(st, "%x", &size);
+               } else if (st[0] == 'x') {
+                       st++;
+                       sscanf(st, "%x", &size);
+               } else
+                       sscanf(st, "%d", &size);
+       }
+
+       if (!sym) {
+               fprintf(stderr, "%s: invalid symbol name '%s'\r\n", cmd, nt);
+       } else if ((sym->addr >> 16) != 0x80) {
+               fprintf(stderr, "%s: '%s' is not in sram\r\n", cmd, nt);
+       } else {
+               int offset = sym->addr & 0xffff;
+               printf("%s (%04x): ", demangle(sym->symbol), offset);
+               if (size > 23)
+                       printf("\r\n  ");
+               for (int i = 0; i < size; i++, offset++)
+                       printf("%s%02x%s",
+                                       i > 1 && (i % 32) == 1 ? "  " : "",
+                                       avr->data[offset],
+                                       i > 1 && (i % 32) == 0 ? "\r\n" : " ");
+               printf("\r\n");
+       }
+       return 0;
+}
+
+
+const history_cmd_t cmd_print = {
+       .names = { "print", "p" },
+       .usage = "<name> [size]: print SRAM variable data",
+       .help = "Prints SRAM variable",
+       .parameter_map = (1 << 1) | (1 << 2),
+       .execute = _cmd_print,
+};
+HISTORY_CMD_REGISTER(cmd_print);
+
+
+static int
+_cmd_set(
+               wordexp_t * l)
+{
+       if (l->we_wordc < 3) {
+               fprintf(stderr, "%s: invalid syntax\r\n", l->we_wordv[0]);
+               return 0;
+       }
+       char *cmd = l->we_wordv[0];
+       char * nt = l->we_wordv[1];
+
+       int size = 1;
+       avr_symbol_t * sym = NULL;
+       _locate_sym(nt, &sym, &size);
+
+       if (!sym) {
+               fprintf(stderr, "%s: invalid symbol name '%s'\r\n", cmd, nt);
+       } else if ((sym->addr >> 16) != 0x80) {
+               fprintf(stderr, "%s: '%s' is not in sram\r\n", cmd, nt);
+       } else {
+               int offset = sym->addr & 0xffff;
+               printf("%s (%04x): ", demangle(sym->symbol), offset);
+
+               for (int i = 2; i < l->we_wordc; i++) {
+                       char * ht = l->we_wordv[i];
+
+                       while (isxdigit(ht[0]) && isxdigit(ht[1])) {
+                               const char *hex = "0123456789abcdef";
+                               uint8_t b = ((index(hex, tolower(ht[0])) - hex) << 4) |
+                                               (index(hex, tolower(ht[1])) - hex);
+                               avr->data[offset++] = b;
+                               printf("%02x", b);
+                               ht += 2;
+                       }
+               }
+               printf("\r\n");
+       }
+       return 0;
+}
+
+
+const history_cmd_t cmd_set = {
+       .names = { "set" },
+       .usage = "<name> <hex values...>: set SRAM variable data",
+       .help = "Sets SRAM variable",
+       .parameter_map = 0, // any numbers
+       .execute = _cmd_set,
+};
+HISTORY_CMD_REGISTER(cmd_set);
+
+static int
+_cmd_dump(
+               wordexp_t * l)
+{
+       int size = 1;
+       avr_symbol_t * sym = NULL;
+       for (int i = 0; i < code.symbolcount; i++) {
+               sym = code.symbol[i];
+               size = 0;
+               if (i < code.symbolcount-1) {
+                       if ((code.symbol[i+1]->addr >> 16) == (sym->addr >> 16))
+                               size = code.symbol[i+1]->addr - sym->addr;
+               }
+               if ((sym->addr >> 16) == 0x80 &&
+                                       strncmp(sym->symbol, "__", 2) &&
+                                       sym->symbol[0] != ' ' &&
+                                       strcmp(sym->symbol, "_edata") &&
+                                       strcmp(sym->symbol, "_end"))
+                       printf("%04x %s (%d bytes)\n",
+                                       sym->addr, demangle(sym->symbol), size);
+       }
+
+       return 0;
+}
+
+const history_cmd_t cmd_dump = {
+       .names = { "dump", "du" },
+       .usage = "dump SRAM ELF symbols and matching size",
+       .help = "dump SRAM ELF symbols and matching size",
+       .parameter_map = 0, // any numbers
+       .execute = _cmd_dump,
+};
+HISTORY_CMD_REGISTER(cmd_dump);
diff --git a/shavr/src/history.c b/shavr/src/history.c
new file mode 100644 (file)
index 0000000..812697a
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * history.c
+ *
+ *  Created on: 15 Oct 2015
+ *      Author: michel
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "history.h"
+
+static struct termios orig_termios;  /* TERMinal I/O Structure */
+
+void fatal(char *message) {
+       fprintf(stderr, "fatal error: %s\n", message);
+       exit(1);
+}
+
+
+/* put terminal in raw mode - see termio(7I) for modes */
+static void
+tty_raw(void)
+{
+       struct termios raw;
+
+       raw = orig_termios; /* copy original and then modify below */
+
+       /* input modes - clear indicated ones giving: no break, no CR to NL,
+        no parity check, no strip char, no start/stop output (sic) control */
+       raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+
+       /* Contrary to normal raw mode, we want to keep the post processing on output
+        * otherwise the printf() all over fail due to their lack of \r
+        */
+//     raw.c_oflag &= ~(OPOST);
+
+       /* control modes - set 8 bit chars */
+       raw.c_cflag |= (CS8);
+
+       /* local modes - clear giving: echoing off, canonical off (no erase with
+        backspace, ^U,...),  no extended functions, no signal chars (^Z,^C) */
+       raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+
+       /* control chars - set return condition: min number of bytes and timer */
+#if 0
+       raw.c_cc[VMIN] = 5;
+       raw.c_cc[VTIME] = 8; /* after 5 bytes or .8 seconds
+        after first byte seen      */
+       raw.c_cc[VMIN] = 0;
+       raw.c_cc[VTIME] = 0; /* immediate - anything       */
+       raw.c_cc[VMIN] = 2;
+       raw.c_cc[VTIME] = 0; /* after two bytes, no timer  */
+#endif
+       raw.c_cc[VMIN] = 0;
+       raw.c_cc[VTIME] = 8; /* after a byte or .8 seconds */
+
+       /* put terminal in raw mode after flushing */
+       if (tcsetattr(1, TCSAFLUSH, &raw) < 0)
+               fatal("can't set raw mode");
+}
+
+/* reset tty - useful also for restoring the terminal when this process
+   wishes to temporarily relinquish the tty
+*/
+static int
+tty_reset(void)
+{
+       /* flush and reset */
+       if (tcsetattr(1, TCSAFLUSH, &orig_termios) < 0)
+               return -1;
+       return 0;
+}
+
+/* exit handler for tty reset */
+static void
+tty_atexit(void) /* NOTE: If the program terminates due to a signal   */
+{ /* this code will not run.  This is for exit()'s     */
+       tty_reset(); /* only.  For resetting the terminal after a signal, */
+} /* a signal handler which calls tty_reset is needed. */
+
+static line_p
+line_new(
+               history_p h)
+{
+       line_p res = malloc(sizeof(line_t));
+       memset(res, 0, sizeof(*res));
+       res->prev = h->tail;
+       if (!h->tail)
+               h->head = res;
+       else
+               h->tail->next = res;
+       h->tail = res;
+       h->current = res;
+       return res;
+}
+
+
+line_p
+line_dup(
+               history_p h,
+               line_p l)
+{
+       line_p n = line_new(h);
+       n->pos = l->pos;
+       n->size = l->size;
+       n->len = l->len;
+       n->line = malloc(n->size);
+       memcpy(n->line, l->line, n->size);
+       h->current = n;
+       return n;
+}
+
+void
+history_display(
+               history_p h)
+{
+       char s[16];
+       int pl = strlen(h->prompt);
+       write(h->ttyout, "\033[0m\r", 5);
+       write(h->ttyout, h->prompt, pl);
+       if (h->current) {
+               write(h->ttyout, h->current->line, h->current->len);
+               pl += h->current->pos;
+       }
+       // kill rest of line, return to 0, move back to X
+       sprintf(s, "\033[K\r\033[%dC", pl);
+       write(h->ttyout, s, strlen(s));
+}
+
+/*
+ * this is a redone skeleton protothread bunch of macros
+ * this will only work with gcc and clang. Use the old, bigger
+ * protothread if porting to another compiler
+ */
+// these are to force preprocessor to evaluate
+#define PP_SUB_CONCAT(p1, p2) p1##p2
+#define PP_CONCAT(p1, p2) PP_SUB_CONCAT(p1, p2)
+
+#define PT_START(__s) do { if (__s) goto *__s; } while (0)
+#define PT_END(__s)  PP_CONCAT(_e, __FUNCTION__):
+#define PT_YIELD(__s) \
+       (__s) = &&PP_CONCAT(pt_, __LINE__); \
+       goto PP_CONCAT(_e, __FUNCTION__); \
+       PP_CONCAT(pt_, __LINE__):\
+       (__s) = NULL;
+
+int
+line_key(
+               history_p h,
+               uint8_t key)
+{
+       int insert = 0;
+       int delete = 0;
+       int cursor_move = 0;
+       int res = 0;
+
+       if (!h->current)
+               line_new(h);
+
+       PT_START(h->state);
+
+       switch (key) {
+               case 0x7f: goto del_char;
+               case 0 ... ' '-1: {
+                       switch(key) {
+                               case 3: printf("\r\n"); exit(0); break;
+                               case 8: // control h delete
+                                       del_char:
+                                       delete++;
+                                       cursor_move = -1;
+                                       break;
+                               case 2: // control b : cursor -1
+                                       previous_char:
+                                       cursor_move = -1;
+                                       break;
+                               case 6: // control f : cursor + 1
+                                       next_char:
+                                       cursor_move = 1;
+                                       break;
+                               case 5: // control e : end of line
+                                       if (h->current)
+                                               h->current->pos = h->current->len;
+                                       break;
+                               case 1: // control a : start of line
+                                       if (h->current)
+                                               h->current->pos = 0;
+                                       break;
+                               case 23: {// control-W : delete word
+                                       if (!h->current->pos)
+                                               break;
+                                       char *d = h->current->line + h->current->pos - 1;
+                                       // first skip any trailing spaces
+                                       while (*d == ' ' && h->current->pos - delete > 0) {
+                                               delete++;
+                                               cursor_move -= 1;
+                                               d--;
+                                       }
+                                       // then delete any word before the cursor
+                                       while (*d != ' ' && h->current->pos - delete > 0) {
+                                               delete++;
+                                               cursor_move -= 1;
+                                               d--;
+                                       }
+                               }       break;
+                               case 11: { // control-K : kill remains of line
+                                       delete = h->current->len - h->current->pos;
+                               }       break;
+                               case 13: { // return!!
+                                       if (h->current->len) {
+                                               if (!h->process_line || h->process_line(h, h->current)) {
+                                                       res++;
+                                                       if (h->current != h->tail)
+                                                               line_dup(h, h->current);
+                                                       line_new(h);
+                                               }
+                                       } else
+                                               write(1, "\012", 1);    // next line
+                               }       break;
+                               case 16: { // control-P -- previous line
+                                       previous_line:
+                                       if (h->current->prev)
+                                               h->current = h->current->prev;
+                               }       break;
+                               case 14: {      // control-N -- next line
+                                       next_line:
+                                       if (h->current->next)
+                                               h->current = h->current->next;
+                               }       break;
+                               case 27: {
+                                       PT_YIELD(h->state);
+                                       switch (key) {
+                                               case '[': {
+                                                       h->temp = 0;
+                                                       do {
+                                                               PT_YIELD(h->state);
+                                                               if (!isdigit(key))
+                                                                       break;
+                                                               h->temp = (h->temp*10) + (key - '0');
+                                                       } while (1);
+                                                       // now handle last key of command
+                                                       switch (key) {
+                                                               // arrow keys
+                                                               case 'A': goto previous_line;
+                                                               case 'B': goto next_line;
+                                                               case 'C': goto next_char;
+                                                               case 'D': goto previous_char;
+                                                               default: printf("Unknown Key '%d%c'\r\n", h->temp, key);        break;
+                                                       }
+                                               }       break;
+                                               default:
+                                                       printf("Unknown ESC key '%c'\r\n", key);        break;
+                                                       break;
+                                       }
+                               }       break;
+                       }
+               }       break;
+               default: {
+                       insert = 1;
+                       cursor_move = 1;
+               }       break;
+       }
+
+       if ((insert || delete) && h->current != h->tail) {
+               line_dup(h, h->current);
+       }
+       line_p l = h->current;
+       if (insert) {
+               if (l->len + insert >= l->size) {
+                       while (l->len + insert >= l->size)
+                               l->size += 8;
+                       l->line = realloc(l->line, l->size);
+               }
+               if (l->pos < l->len)
+                       memmove(l->line + l->pos + insert, l->line + l->pos,
+                               l->len - l->pos + insert);
+               l->line[l->pos] = key;
+               l->len += insert;
+               l->line[l->len] = 0;
+       }
+       if (cursor_move) {
+               if (l->pos + cursor_move < 0) {
+                       cursor_move = 0;
+                       delete = 0;
+                       l->pos = 0;
+               }
+               if (l->pos + cursor_move > l->len) {
+                       cursor_move = 0;
+                       l->pos = l->len;
+               }
+               l->pos += cursor_move;
+       }
+       if (delete) {
+               if (l->len - l->pos) {
+                       memmove(l->line + l->pos, l->line + l->pos + delete, l->len - l->pos - delete);
+                       l->len -= delete;
+               }
+       }
+       history_display(h);
+       PT_END(h->state);
+       return res;
+}
+
+
+void
+history_init(
+               history_p h)
+{
+       atexit(tty_atexit);
+
+       /* store current tty settings in orig_termios */
+       if (tcgetattr(h->ttyout, &orig_termios) < 0)
+               fatal("can't get tty settings");
+       atexit(tty_atexit);
+       tty_raw();
+
+       int fl = fcntl(h->ttyin, F_GETFL);
+       fcntl(0, F_SETFL, fl | O_ASYNC);
+       history_display(h);
+}
+
+int
+history_idle(
+               history_p h)
+{
+       uint8_t c;
+       ssize_t r = read(h->ttyin, &c, 1);
+       if (r <= 0)
+               return 0;
+       return line_key(h, c);
+}
diff --git a/shavr/src/history.h b/shavr/src/history.h
new file mode 100644 (file)
index 0000000..2048c4c
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * history.h
+ *
+ *  Created on: 15 Oct 2015
+ *      Author: michel
+ */
+
+#ifndef HISTORY_H_
+#define HISTORY_H_
+
+
+typedef struct line_t {
+       struct line_t * next;
+       struct line_t * prev;
+
+       int size, len, pos;
+       char * line;
+} line_t, *line_p;
+
+
+typedef struct history_t {
+       int ttyin;
+       int ttyout;
+
+       line_p head, tail;
+       line_p current;
+       char prompt[32];
+       void * state;
+       int temp;
+
+       void *param;
+       int (*process_line)(struct history_t *h, line_p l);
+
+       //struct history_cmd_list_t * cmd_list;
+} history_t, *history_p;
+
+
+void
+history_init(
+               history_p h);
+
+int
+history_idle(
+               history_p h);
+
+void
+history_display(
+               history_p h);
+
+#endif /* HISTORY_H_ */
diff --git a/shavr/src/history_avr.c b/shavr/src/history_avr.c
new file mode 100644 (file)
index 0000000..6a67072
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * hcmd_misc.c
+ *
+ *  Created on: 15 Oct 2015
+ *      Author: michel
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <libgen.h>
+#include <string.h>
+#include <ctype.h>
+#include <wordexp.h>
+#include <stdarg.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include "history_avr.h"
+
+int history_redisplay = 0;
+
+
+static int
+_history_process_line(
+               struct history_t *h,
+               line_p l )
+{
+       printf("\r\n");
+       history_cmd_execute(NULL, l->line);
+
+       return 1;
+}
+
+static void
+raw_std_logger(
+               avr_t* avr,
+               const int level,
+               const char * format,
+               va_list args)
+{
+       if (!avr || avr->log >= level) {
+               FILE * d = (level > LOG_ERROR) ?  stdout : stderr;
+               fprintf(d, "\r");
+               vfprintf(d , format, args);
+               fprintf(d, "\r");
+               history_redisplay++;
+       }
+}
+
+static int
+_cmd_quit(
+               wordexp_t * l)
+{
+       avr_terminate(avr);
+       exit(0);
+       return 0;
+}
+
+const history_cmd_t cmd_quit = {
+       .names = { "quit", "q", "exit", },
+       .usage = "quit simavr",
+       .help = "exits the program",
+       .parameter_map = 0,
+       .execute = _cmd_quit,
+};
+HISTORY_CMD_REGISTER(cmd_quit);
+
+int prompt_fd = -1;
+int prompt_event = 0;
+
+static void
+callback_sleep_prompt(
+               avr_t * avr,
+               avr_cycle_count_t howLong)
+{
+       uint32_t usec = avr_pending_sleep_usec(avr, howLong);
+       if (prompt_fd == -1) {
+               usleep(usec);
+               return;
+       }
+#if 1
+       usleep(usec / 2);
+#else
+       struct pollfd ev = { .fd = prompt_fd, .events = POLLIN };
+       int r = poll(&ev, 1, usec / 1000 / 2);
+//     printf("poll %d %x\r\n", r, ev.revents);
+       if (r > 0 && ev.revents)
+               prompt_event++;
+#endif
+}
+
+history_t history = {
+       .ttyin = 0,
+       .ttyout = 1,
+       .prompt = "avr: ",
+       .process_line = _history_process_line,
+};
+
+void history_avr_init()
+{
+       history_init(&history);
+       prompt_fd = history.ttyin;
+//     avr->sleep = callback_sleep_prompt;
+    avr_global_logger_set(raw_std_logger);
+}
+
+void history_avr_idle()
+{
+       int prompt_event = 0;
+       struct pollfd ev = { .fd = prompt_fd, .events = POLLIN };
+       int r = poll(&ev, 1, 1000 / 2);
+//     printf("poll %d %x\r\n", r, ev.revents);
+       if (r > 0 && ev.revents)
+               prompt_event++;
+       if (history_redisplay) {
+               history_redisplay = 0;
+               history_display(&history);
+               prompt_event++;
+       }
+       if (prompt_event)
+               history_idle(&history);
+}
diff --git a/shavr/src/history_avr.h b/shavr/src/history_avr.h
new file mode 100644 (file)
index 0000000..0ed153c
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * history_avr.h
+ *
+ *  Created on: 18 Oct 2015
+ *      Author: michel
+ */
+
+#ifndef _HISTORY_AVR_H_
+#define _HISTORY_AVR_H_
+
+#include "sim_avr.h"
+#include "sim_elf.h"
+#include "history.h"
+#include "history_cmd.h"
+
+extern elf_firmware_t code;
+
+extern avr_t * avr;
+
+extern int history_redisplay;
+
+void history_avr_init();
+void history_avr_idle();
+
+#endif /* _HISTORY_AVR_H_ */
diff --git a/shavr/src/history_cmd.c b/shavr/src/history_cmd.c
new file mode 100644 (file)
index 0000000..17f58da
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * history_cmd.c
+ *
+ *  Created on: 15 Oct 2015
+ *      Author: michel
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "history_cmd.h"
+#include "history.h"
+
+history_cmd_list_p
+history_cmd_list_new()
+{
+       history_cmd_list_p res = (history_cmd_list_p)malloc(sizeof(*res));
+       memset(res, 0, sizeof(*res));
+
+       return res;
+}
+
+void
+history_cmd_list_dispose(
+               history_cmd_list_p list )
+{
+       if (list->list)
+               free(list->list);
+       list->list = NULL;
+       free(list);
+}
+
+
+static history_cmd_list_p global = NULL;
+
+void
+history_cmd_list_register(
+               history_cmd_list_p list,
+               const history_cmd_p cmd )
+{
+       if (!list) {
+               if (!global)
+                       global = history_cmd_list_new();
+               list = global;
+       }
+
+       if (!list || !cmd)
+               return;
+       if ((list->count % 8) == 0)
+               list->list = realloc(list->list, sizeof(list->list[0]) * (list->count + 8));
+
+       int ins = -1;
+       for (int i = 0; i < list->count && ins == -1; i++) {
+               history_cmd_p c = list->list[i];
+               if (c == cmd || strcmp(cmd->names[0], c->names[0]) <= 0)
+                       ins = i;
+       }
+       if (ins == -1)
+               ins = list->count;
+       else
+               memmove(list->list + ins + 1, list->list + ins, (list->count - ins) * sizeof(list->list[0]));
+       list->list[ins] = cmd;
+       list->count++;
+}
+
+history_cmd_p
+history_cmd_lookup(
+               history_cmd_list_p list,
+               const char * cmd_name,
+               int * out_alias)
+{
+       if (!list)
+               list = global;
+       if (!list || !cmd_name)
+               return NULL;
+       for (int i = 0; i < list->count; i++) {
+               const history_cmd_p cmd = list->list[i];
+               int alias = -1;
+               for (int ai = 0; ai < 8 && cmd->names[ai] && alias == -1; ai++)
+                       if (!strcmp(cmd->names[ai], cmd_name)) {
+                               if (out_alias)
+                                       *out_alias = ai;
+                               return cmd;
+                       }
+       }
+       return NULL;
+}
+
+
+int
+history_cmd_execute(
+               history_cmd_list_p list,
+               const char * cmd_line )
+{
+       if (!list)
+               list = global;
+       if (!list || !cmd_line)
+               return -1;
+
+       int res = -1;
+       wordexp_t words = {0};
+       while (*cmd_line == ' ' || *cmd_line == '\t')
+               cmd_line++;
+
+       if (wordexp(cmd_line, &words, WRDE_NOCMD)) {
+               wordfree(&words);
+               fprintf(stderr, "Syntax error\r\n");
+               goto out;
+       }
+       if (words.we_wordc == 0) {
+               res = 0;
+               goto out;
+       }
+       int alias = 0;
+       const history_cmd_p cmd = history_cmd_lookup(list, words.we_wordv[0], &alias);
+       if (!cmd) {
+               fprintf(stderr, "Unknown command '%s'\r\n", words.we_wordv[0]);
+               goto out;
+       }
+       if (cmd->parameter_map && !(cmd->parameter_map & (1 << (words.we_wordc-1)))) {
+               fprintf(stderr, "%s: %s\r\n", words.we_wordv[0], cmd->usage);
+               goto out;
+       }
+       if (cmd->execute_list)
+               res = cmd->execute_list(list, &words);
+       else if (cmd->execute)
+               res = cmd->execute(&words);
+       else {
+               fprintf(stderr, "%s: Internal error, no execute function", words.we_wordv[0]);
+       }
+out:
+       wordfree(&words);
+
+       return res;
+}
+
+static int
+history_cmd_help(
+               struct history_cmd_list_t *list,
+               wordexp_t * l)
+{
+       if (l->we_wordc == 1) {
+               for (int i = 0; i < list->count; i++) {
+                       const history_cmd_p cmd = list->list[i];
+                       fprintf(stdout, "%-12.12s %s\r\n", cmd->names[0], cmd->usage);
+               }
+       } else {
+               for (int i = 1; i < l->we_wordc; i++) {
+                       const history_cmd_p cmd = history_cmd_lookup(list, l->we_wordv[i], NULL);
+                       if (cmd) {
+                               fprintf(stderr, "%s: %s\r\n", l->we_wordv[i], cmd->usage);
+                               fprintf(stderr, "\t%s\r\n", cmd->help);
+                       } else {
+                               fprintf(stderr, "help: Unknown command '%s'\r\n", l->we_wordv[i]);
+                       }
+               }
+       }
+       return 0;
+}
+
+const history_cmd_t cmd_help = {
+       .names[0] = "help",
+       .names[1] = "h",
+       .usage = "[cmd]: Display help for commands",
+       .help = "This helps",
+       .parameter_map = (1 << 0) | (1 << 1),
+       .execute_list = history_cmd_help,
+};
+HISTORY_CMD_REGISTER(cmd_help);
+
diff --git a/shavr/src/history_cmd.h b/shavr/src/history_cmd.h
new file mode 100644 (file)
index 0000000..f5671c9
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * history_cmd.h
+ *
+ *  Created on: 15 Oct 2015
+ *      Author: michel
+ */
+
+#ifndef HISTORY_CMD_H_
+#define HISTORY_CMD_H_
+
+#include <stdint.h>
+#include <wordexp.h>
+
+struct history_cmd_list_t;
+
+typedef struct history_cmd_t {
+       const char * names[8];
+       const char * usage;
+       const char * help;
+       uint8_t parameter_map;  // 0 means 'any number' otherwise, bitfield of valid options
+       int (*execute)(wordexp_t * l);
+       int (*execute_list)(struct history_cmd_list_t *list, wordexp_t * l);
+} history_cmd_t, *history_cmd_p;
+
+
+typedef struct history_cmd_list_t {
+       int count;
+       history_cmd_p * list;
+} history_cmd_list_t, *history_cmd_list_p;
+
+history_cmd_list_p
+history_cmd_list_new();
+
+void
+history_cmd_list_dispose(
+               history_cmd_list_p list );
+void
+history_cmd_list_register(
+               history_cmd_list_p list,
+               const history_cmd_p cmd );
+
+#define HISTORY_CMD_REGISTER(__cmd) \
+       __attribute__((constructor)) static void _history_register_##__cmd() { \
+               history_cmd_list_register(NULL, (const history_cmd_p)&(__cmd));\
+       }
+
+history_cmd_p
+history_cmd_lookup(
+               history_cmd_list_p list,
+               const char * cmd_name,
+               int * out_alias);
+int
+history_cmd_execute(
+               history_cmd_list_p list,
+               const char * cmd_line );
+
+
+#endif /* HISTORY_CMD_H_ */
diff --git a/shavr/src/shavr.c b/shavr/src/shavr.c
new file mode 100644 (file)
index 0000000..5d75790
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+       shavr.c
+
+       Copyright 2017 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <libgen.h>
+
+#include <pthread.h>
+
+#include "sim_avr.h"
+#include "avr_ioport.h"
+#include "sim_elf.h"
+#include "sim_hex.h"
+#include "sim_gdb.h"
+#include "uart_pty.h"
+#include "sim_vcd_file.h"
+
+#include "sim_args.h"
+
+uart_pty_t uart_pty;
+avr_t * avr = NULL;
+avr_vcd_t vcd_file;
+elf_firmware_t code;// = {0};
+
+avr_t *
+sim_prepare(
+       sim_args_t * a );
+
+
+typedef struct avr_flash_desc_t {
+       char avr_flash_path[1024];
+       int avr_flash_fd;
+} avr_flash_desc_t;
+
+// avr special flash initalization
+// here: open and map a file to enable a persistent storage for the flash memory
+static void
+avr_special_init(
+               avr_t * avr,
+               void * data)
+{
+       avr_flash_desc_t *flash_data = (avr_flash_desc_t *)data;
+
+       printf("%s\n", __func__);
+       // open the file
+       flash_data->avr_flash_fd = open(flash_data->avr_flash_path,
+                                                                       O_RDWR|O_CREAT, 0644);
+       if (flash_data->avr_flash_fd < 0) {
+               perror(flash_data->avr_flash_path);
+               exit(1);
+       }
+       // resize and map the file the file
+       (void)ftruncate(flash_data->avr_flash_fd, avr->flashend + 1);
+       ssize_t r = read(flash_data->avr_flash_fd, avr->flash, avr->flashend + 1);
+       if (r != avr->flashend + 1) {
+               fprintf(stderr, "unable to load flash memory\n");
+               perror(flash_data->avr_flash_path);
+               exit(1);
+       }
+}
+
+// avr special flash deinitalization
+// here: cleanup the persistent storage
+static void
+avr_special_deinit(
+               avr_t* avr,
+               void * data)
+{
+       avr_flash_desc_t *flash_data = (avr_flash_desc_t *)data;
+
+       printf("%s\n", __func__);
+       lseek(flash_data->avr_flash_fd, SEEK_SET, 0);
+       ssize_t r = write(flash_data->avr_flash_fd, avr->flash, avr->flashend + 1);
+       if (r != avr->flashend + 1) {
+               fprintf(stderr, "unable to save flash memory\n");
+               perror(flash_data->avr_flash_path);
+       }
+       close(flash_data->avr_flash_fd);
+       uart_pty_stop(&uart_pty);
+}
+
+int
+main(
+               int argc,
+               const char *argv[])
+{
+       avr_flash_desc_t flash_data;
+       sim_args_t args;
+
+       if (sim_args_parse(&args, argc, argv, NULL)) {
+               exit(1);
+       }
+
+       avr = sim_prepare(&args);
+       if (!avr) {
+               fprintf(stderr, "%s: Error creating the AVR core\n", argv[0]);
+               exit(1);
+       }
+       if (args.flash_file[0]) {
+               strncpy(flash_data.avr_flash_path, args.flash_file, sizeof(flash_data.avr_flash_path));
+               flash_data.avr_flash_fd = 0;
+               // register our own functions
+               avr->custom.init = avr_special_init;
+               avr->custom.deinit = avr_special_deinit;
+               avr->custom.data = &flash_data;
+       }
+       avr_init(avr);
+
+       uart_pty_init(avr, &uart_pty);
+       uart_pty_connect(&uart_pty, '0');
+
+       printf("Running...\n");
+       while (1) {
+               int state = avr_run(avr);
+               if ( state == cpu_Done || state == cpu_Crashed)
+                       break;
+       }
+
+}
diff --git a/shavr/src/sim_args.c b/shavr/src/sim_args.c
new file mode 100644 (file)
index 0000000..4fda3ef
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+       sim_args.c
+
+       Copyright 2017 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <libgen.h>
+
+#include "sim_avr.h"
+#include "sim_elf.h"
+#include "sim_core.h"
+#include "sim_gdb.h"
+#include "sim_hex.h"
+#include "sim_vcd_file.h"
+
+#include "sim_core_decl.h"
+#include "sim_args.h"
+
+void
+sim_args_display_usage(
+       const char * app)
+{
+       printf("Usage: %s [...] <firmware>\n", app);
+       printf( "               [--freq|-f <freq>]  Sets the frequency for an .hex firmware\n"
+                       "               [--mcu|-m <device>] Sets the MCU type for an .hex firmware\n"
+                       "       [--list-cores]      List all supported AVR cores and exit\n"
+                       "       [--help|-h]         Display this usage message and exit\n"
+                       "       [--trace, -t]       Run full scale decoder trace\n"
+                       "       [-ti <vector>]      Add traces for IRQ vector <vector>\n"
+                       "       [--gdb|-g]          Listen for gdb connection on port 1234\n"
+                       "       [-ff <.hex file>]   Load next .hex file as flash\n"
+                       "       [-ee <.hex file>]   Load next .hex file as eeprom\n"
+                       "       [--input|-i <file>] A .vcd file to use as input signals\n"
+                       "       [--persist|-p <file>] Save/Load flash+eeprom from <file>\n"
+                       "       [-v]                Raise verbosity level\n"
+                       "                           (can be passed more than once)\n"
+                       "       <firmware>          A .hex or an ELF file. ELF files are\n"
+                       "                           prefered, and can include debugging syms\n");
+}
+
+void
+sim_args_list_cores()
+{
+       printf( "Supported AVR cores:\n");
+       for (int i = 0; avr_kind[i]; i++) {
+               printf("       ");
+               for (int ti = 0; ti < 4 && avr_kind[i]->names[ti]; ti++)
+                       printf("%s ", avr_kind[i]->names[ti]);
+               printf("\n");
+       }
+}
+
+
+int
+sim_args_parse(
+       sim_args_t *a,
+       int argc,
+       const char *argv[],
+       sim_args_parse_t passthru)
+{
+       uint32_t loadBase = AVR_SEGMENT_OFFSET_FLASH;
+
+       if (!a || !argc || !argv)
+               return -1;
+       memset(a, 0, sizeof(*a));
+
+       const char * progname = basename(strdup(argv[0]));
+       if (argc == 1)
+               sim_args_display_usage(progname);
+
+       for (int pi = 1; pi < argc; pi++) {
+               if (!strcmp(argv[pi], "--list-cores")) {
+                       sim_args_list_cores();
+               } else if (!strcmp(argv[pi], "-h") || !strcmp(argv[pi], "--help")) {
+                       sim_args_display_usage(progname);
+               } else if (!strcmp(argv[pi], "-m") || !strcmp(argv[pi], "--mcu")) {
+                       if (pi < argc-1)
+                               strncpy(a->name, argv[++pi], sizeof(a->name));
+                       else
+                               sim_args_display_usage(progname);
+               } else if (!strcmp(argv[pi], "-f") || !strcmp(argv[pi], "--freq")) {
+                       if (pi < argc-1)
+                               a->f_cpu = atoi(argv[++pi]);
+                       else
+                               sim_args_display_usage(progname);
+               } else if (!strcmp(argv[pi], "-i") || !strcmp(argv[pi], "--input")) {
+                       if (pi < argc-1)
+                               strncpy(a->vcd_input, argv[++pi], sizeof(a->vcd_input));
+                       else
+                               sim_args_display_usage(progname);
+               } else if (!strcmp(argv[pi], "-p") || !strcmp(argv[pi], "--persist")) {
+                       if (argv[pi + 1] && argv[pi + 1][0] != '-')
+                               strncpy(a->flash_file, argv[++pi], sizeof(a->flash_file));
+                       else
+                               strncpy(a->flash_file, "simavr_flash.bin", sizeof(a->flash_file));
+               } else if (!strcmp(argv[pi], "-t") || !strcmp(argv[pi], "--trace")) {
+                       a->trace++;
+               } else if (!strcmp(argv[pi], "-ti")) {
+                       if (pi < argc-1)
+                               a->trace_vectors[a->trace_vectors_count++] = atoi(argv[++pi]);
+               } else if (!strcmp(argv[pi], "-g") || !strcmp(argv[pi], "--gdb")) {
+                       a->gdb++;
+               } else if (!strcmp(argv[pi], "-v")) {
+                       a->log++;
+               } else if (!strcmp(argv[pi], "-ee")) {
+                       loadBase = AVR_SEGMENT_OFFSET_EEPROM;
+               } else if (!strcmp(argv[pi], "-ff")) {
+                       loadBase = AVR_SEGMENT_OFFSET_FLASH;
+               } else if (argv[pi][0] == '-') {
+                       // call the passthru callback
+                       int npi = passthru(a, argc, argv, pi);
+                       if (npi < 0)
+                               return npi;
+                       if (npi > pi)
+                               pi = npi;
+               } else if (argv[pi][0] != '-') {
+                       const char * filename = argv[pi];
+                       char * suffix = strrchr(filename, '.');
+                       if (suffix && !strcasecmp(suffix, ".hex")) {
+                               if (!a->name[0] || !a->f_cpu) {
+                                       fprintf(stderr,
+                                                       "%s: --mcu and --freq are mandatory to load .hex files\n",
+                                                       argv[0]);
+                                       return -1;
+                               }
+                               ihex_chunk_p chunk = NULL;
+                               int cnt = read_ihex_chunks(filename, &chunk);
+                               if (cnt <= 0) {
+                                       fprintf(stderr, "%s: Unable to load IHEX file %s\n",
+                                               argv[0], argv[pi]);
+                                       return -1;
+                               }
+                               if (a->log)
+                                       printf("Loaded %d section of ihex\n", cnt);
+                               for (int ci = 0; ci < cnt; ci++) {
+                                       if (chunk[ci].baseaddr < (1*1024*1024)) {
+                                               a->f.flash = chunk[ci].data;
+                                               a->f.flashsize = chunk[ci].size;
+                                               a->f.flashbase = chunk[ci].baseaddr;
+                                               if (a->log)
+                                                       printf("Load HEX flash %08x, %d\n", a->f.flashbase, a->f.flashsize);
+                                       } else if (chunk[ci].baseaddr >= AVR_SEGMENT_OFFSET_EEPROM ||
+                                                       chunk[ci].baseaddr + loadBase >= AVR_SEGMENT_OFFSET_EEPROM) {
+                                               // eeprom!
+                                               a->f.eeprom = chunk[ci].data;
+                                               a->f.eesize = chunk[ci].size;
+                                               if (a->log)
+                                                       printf("Load HEX eeprom %08x, %d\n", chunk[ci].baseaddr, a->f.eesize);
+                                       }
+                               }
+                       } else {
+                               if (elf_read_firmware(filename, &a->f) == -1) {
+                                       fprintf(stderr, "%s: Unable to load firmware from file %s\n",
+                                                       argv[0], filename);
+                                       return -1;
+                               }
+                       }
+               }
+       }
+
+       if (strlen(a->name))
+               strcpy(a->f.mmcu, a->name);
+       if (a->f_cpu)
+               a->f.frequency = a->f_cpu;
+
+       return 0;
+}
+
diff --git a/shavr/src/sim_args.h b/shavr/src/sim_args.h
new file mode 100644 (file)
index 0000000..5365af5
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+       sim_args.h
+
+       Copyright 2017 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SIM_ARGS_H__
+#define __SIM_ARGS_H__
+
+#include <stdint.h>
+
+struct sim_args_t;
+
+typedef int (*sim_args_parse_t)(
+       struct sim_args_t *a,
+       int argc,
+       const char *argv[],
+       int index );
+
+typedef struct sim_args_t {
+       elf_firmware_t f;       // ELF firmware
+       uint32_t f_cpu; // AVR core frequency
+       char name[24];  // AVR core name
+       uint32_t trace : 4, gdb : 1,  log : 4;
+
+
+       uint8_t trace_vectors[8];
+       int trace_vectors_count;
+
+       char vcd_input[1024];
+       char flash_file[1024];
+} sim_args_t;
+
+int
+sim_args_parse(
+       sim_args_t *a,
+       int argc,
+       const char *argv[],
+       sim_args_parse_t passthru);
+
+void
+sim_args_display_usage(
+       const char * app);
+void
+sim_args_list_cores();
+
+
+#endif /* __SIM_ARGS_H__ */
diff --git a/shavr/src/sim_prepare.c b/shavr/src/sim_prepare.c
new file mode 100644 (file)
index 0000000..80c6c75
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+       sim_prepare.c
+
+       Copyright 2017 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <libgen.h>
+#include <signal.h>
+
+#include "sim_avr.h"
+#include "sim_elf.h"
+#include "sim_core.h"
+#include "sim_gdb.h"
+#include "sim_hex.h"
+#include "sim_vcd_file.h"
+
+#include "sim_core_decl.h"
+#include "sim_args.h"
+
+static avr_t * avr = NULL;
+static avr_vcd_t input;
+
+static void
+sig_int(
+               int sign)
+{
+       printf("signal caught, simavr terminating\n");
+       if (avr)
+               avr_terminate(avr);
+       exit(0);
+}
+
+extern char * __progname;
+
+avr_t *
+sim_prepare(
+       sim_args_t * a )
+{
+//     avr_t * avr;
+
+       avr = avr_make_mcu_by_name(a->f.mmcu);
+       if (!avr) {
+               fprintf(stderr, "%s: AVR '%s' not known\n", __progname, a->f.mmcu);
+               return NULL;
+       }
+       avr_init(avr);
+       avr_load_firmware(avr, &a->f);
+       if (a->f.flashbase) {
+               printf("Attempted to load a bootloader at %04x\n", a->f.flashbase);
+               avr->pc = a->f.flashbase;
+       }
+       avr->log = a->log > LOG_TRACE ? LOG_TRACE : a->log;
+       avr->trace = a->trace;
+       for (int ti = 0; ti < a->trace_vectors_count; ti++) {
+               for (int vi = 0; vi < avr->interrupts.vector_count; vi++)
+                       if (avr->interrupts.vector[vi]->vector == a->trace_vectors[ti])
+                               avr->interrupts.vector[vi]->trace = 1;
+       }
+       if (a->vcd_input[0]) {
+               if (avr_vcd_init_input(avr, a->vcd_input, &input)) {
+                       fprintf(stderr, "%s: Warning: VCD input file %s failed\n",
+                                       __progname, a->vcd_input);
+               }
+       }
+
+       // even if not setup at startup, activate gdb if crashing
+       avr->gdb_port = 1234;
+       if (a->gdb) {
+               avr->state = cpu_Stopped;
+               avr_gdb_init(avr);
+       }
+
+       signal(SIGINT, sig_int);
+       signal(SIGTERM, sig_int);
+       return avr;
+}
diff --git a/shavr/src/uart_pty.c b/shavr/src/uart_pty.c
new file mode 100644 (file)
index 0000000..4f4edd0
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+       uart_pty.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "sim_network.h"
+#include <stdlib.h>
+#include <pthread.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#ifdef __APPLE__
+#include <util.h>
+#else
+#include <pty.h>
+#endif
+
+#include "uart_pty.h"
+#include "avr_uart.h"
+#include "sim_time.h"
+#include "sim_hex.h"
+
+DEFINE_FIFO(uint8_t,uart_pty_fifo);
+
+//#define TRACE(_w) _w
+#ifndef TRACE
+#define TRACE(_w)
+#endif
+
+/*
+ * called when a byte is send via the uart on the AVR
+ */
+static void
+uart_pty_in_hook(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+       TRACE(printf("uart_pty_in_hook %02x\n", value);)
+       uart_pty_fifo_write(&p->pty.in, value);
+
+       if (p->tap.s) {
+               if (p->tap.crlf && value == '\n')
+                       uart_pty_fifo_write(&p->tap.in, '\r');
+               uart_pty_fifo_write(&p->tap.in, value);
+       }
+}
+
+// try to empty our fifo, the uart_pty_xoff_hook() will be called when
+// other side is full
+static void
+uart_pty_flush_incoming(
+               uart_pty_t * p)
+{
+       while (p->xon && !uart_pty_fifo_isempty(&p->pty.out)) {
+               TRACE(int r = p->pty.out.read;)
+               uint8_t byte = uart_pty_fifo_read(&p->pty.out);
+               TRACE(printf("uart_pty_flush_incoming send r %03d:%02x\n", r, byte);)
+               avr_raise_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, byte);
+
+               if (p->tap.s) {
+                       if (p->tap.crlf && byte == '\n')
+                               uart_pty_fifo_write(&p->tap.in, '\r');
+                       uart_pty_fifo_write(&p->tap.in, byte);
+               }
+       }
+       if (p->tap.s) {
+               while (p->xon && !uart_pty_fifo_isempty(&p->tap.out)) {
+                       uint8_t byte = uart_pty_fifo_read(&p->tap.out);
+                       if (p->tap.crlf && byte == '\r') {
+                               uart_pty_fifo_write(&p->tap.in, '\n');
+                       }
+                       if (byte == '\n')
+                               continue;
+                       uart_pty_fifo_write(&p->tap.in, byte);
+                       avr_raise_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, byte);
+               }
+       }
+}
+
+avr_cycle_count_t
+uart_pty_flush_timer(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+
+       uart_pty_flush_incoming(p);
+       /* always return a cycle NUMBER not a cycle count */
+       return p->xon ? when + avr_hz_to_cycles(p->avr, 1000) : 0;
+}
+
+/*
+ * Called when the uart has room in it's input buffer. This is called repeateadly
+ * if necessary, while the xoff is called only when the uart fifo is FULL
+ */
+static void
+uart_pty_xon_hook(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+       TRACE(if (!p->xon) printf("uart_pty_xon_hook\n");)
+       p->xon = 1;
+
+       uart_pty_flush_incoming(p);
+
+       // if the buffer is not flushed, try to do it later
+       if (p->xon)
+                       avr_cycle_timer_register(p->avr, avr_hz_to_cycles(p->avr, 1000),
+                                               uart_pty_flush_timer, param);
+}
+
+/*
+ * Called when the uart ran out of room in it's input buffer
+ */
+static void
+uart_pty_xoff_hook(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+       TRACE(if (p->xon) printf("uart_pty_xoff_hook\n");)
+       p->xon = 0;
+       avr_cycle_timer_cancel(p->avr, uart_pty_flush_timer, param);
+}
+
+static void *
+uart_pty_thread(
+               void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+
+       while (1) {
+               fd_set read_set, write_set;
+               int max = 0;
+               FD_ZERO(&read_set);
+               FD_ZERO(&write_set);
+
+               for (int ti = 0; ti < 2; ti++) if (p->port[ti].s) {
+                       // read more only if buffer was flushed
+                       if (p->port[ti].buffer_len == p->port[ti].buffer_done) {
+                               FD_SET(p->port[ti].s, &read_set);
+                               max = p->port[ti].s > max ? p->port[ti].s : max;
+                       }
+                       if (!uart_pty_fifo_isempty(&p->port[ti].in)) {
+                               FD_SET(p->port[ti].s, &write_set);
+                               max = p->port[ti].s > max ? p->port[ti].s : max;
+                       }
+               }
+
+               // short, but not too short interval
+               struct timeval timo = { 0, 500 };
+               int ret = select(max+1, &read_set, &write_set, NULL, &timo);
+
+               if (!ret)
+                       continue;
+               if (ret < 0)
+                       break;
+
+               for (int ti = 0; ti < 2; ti++) if (p->port[ti].s) {
+                       if (FD_ISSET(p->port[ti].s, &read_set)) {
+                               ssize_t r = read(p->port[ti].s, p->port[ti].buffer,
+                                                                       sizeof(p->port[ti].buffer)-1);
+                               p->port[ti].buffer_len = r;
+                               p->port[ti].buffer_done = 0;
+                               TRACE(if (!p->port[ti].tap)
+                                               hdump("pty recv", p->port[ti].buffer, r);)
+                       }
+                       if (p->port[ti].buffer_done < p->port[ti].buffer_len) {
+                               // write them in fifo
+                               while (p->port[ti].buffer_done < p->port[ti].buffer_len &&
+                                               !uart_pty_fifo_isfull(&p->port[ti].out)) {
+                                       int index = p->port[ti].buffer_done++;
+                                       TRACE(int wi = p->port[ti].out.write;)
+                                       uart_pty_fifo_write(&p->port[ti].out,
+                                                       p->port[ti].buffer[index]);
+                                       TRACE(printf("w %3d:%02x (%d/%d) %s\n",
+                                                               wi, p->port[ti].buffer[index],
+                                                               p->port[ti].out.read,
+                                                               p->port[ti].out.write,
+                                                               p->xon ? "XON" : "XOFF");)
+                               }
+                       }
+                       if (FD_ISSET(p->port[ti].s, &write_set)) {
+                               uint8_t buffer[512];
+                               // write them in fifo
+                               uint8_t * dst = buffer;
+                               while (!uart_pty_fifo_isempty(&p->port[ti].in) &&
+                                               dst < (buffer + sizeof(buffer)))
+                                       *dst++ = uart_pty_fifo_read(&p->port[ti].in);
+                               size_t len = dst - buffer;
+                               TRACE(size_t r =) write(p->port[ti].s, buffer, len);
+                               TRACE(if (!p->port[ti].tap) hdump("pty send", buffer, r);)
+                       }
+               }
+               /* DO NOT call this, this create a concurency issue with the
+                * FIFO that can't be solved cleanly with a memory barrier
+                       uart_pty_flush_incoming(p);
+                 */
+       }
+       return NULL;
+}
+
+static const char * irq_names[IRQ_UART_PTY_COUNT] = {
+       [IRQ_UART_PTY_BYTE_IN] = "8<uart_pty.in",
+       [IRQ_UART_PTY_BYTE_OUT] = "8>uart_pty.out",
+};
+
+void
+uart_pty_init(
+               struct avr_t * avr,
+               uart_pty_t * p)
+{
+       memset(p, 0, sizeof(*p));
+
+       p->avr = avr;
+       p->irq = avr_alloc_irq(&avr->irq_pool, 0, IRQ_UART_PTY_COUNT, irq_names);
+       avr_irq_register_notify(p->irq + IRQ_UART_PTY_BYTE_IN, uart_pty_in_hook, p);
+
+       int hastap = (getenv("SIMAVR_UART_TAP") && atoi(getenv("SIMAVR_UART_TAP"))) ||
+                       (getenv("SIMAVR_UART_XTERM") && atoi(getenv("SIMAVR_UART_XTERM"))) ;
+
+       for (int ti = 0; ti < 1 + hastap; ti++) {
+               int m, s;
+
+               if (openpty(&m, &s, p->port[ti].slavename, NULL, NULL) < 0) {
+                       fprintf(stderr, "%s: Can't create pty: %s", __FUNCTION__, strerror(errno));
+                       return ;
+               }
+               struct termios tio;
+               tcgetattr(m, &tio);
+               cfmakeraw(&tio);
+               tcsetattr(m, TCSANOW, &tio);
+               p->port[ti].s = m;
+               p->port[ti].tap = ti != 0;
+               p->port[ti].crlf = ti != 0;
+               printf("uart_pty_init %s on port *** %s ***\n",
+                               ti == 0 ? "bridge" : "tap", p->port[ti].slavename);
+       }
+
+       pthread_create(&p->thread, NULL, uart_pty_thread, p);
+
+}
+
+void
+uart_pty_stop(
+               uart_pty_t * p)
+{
+       puts(__func__);
+       pthread_kill(p->thread, SIGINT);
+       for (int ti = 0; ti < 2; ti++)
+               if (p->port[ti].s)
+                       close(p->port[ti].s);
+       void * ret;
+       pthread_join(p->thread, &ret);
+}
+
+void
+uart_pty_connect(
+               uart_pty_t * p,
+               char uart)
+{
+       // disable the stdio dump, as we are sending binary there
+       uint32_t f = 0;
+       avr_ioctl(p->avr, AVR_IOCTL_UART_GET_FLAGS(uart), &f);
+       f &= ~AVR_UART_FLAG_STDIO;
+       avr_ioctl(p->avr, AVR_IOCTL_UART_SET_FLAGS(uart), &f);
+
+       avr_irq_t * src = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUTPUT);
+       avr_irq_t * dst = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_INPUT);
+       avr_irq_t * xon = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XON);
+       avr_irq_t * xoff = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XOFF);
+       if (src && dst) {
+               avr_connect_irq(src, p->irq + IRQ_UART_PTY_BYTE_IN);
+               avr_connect_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, dst);
+       }
+       if (xon)
+               avr_irq_register_notify(xon, uart_pty_xon_hook, p);
+       if (xoff)
+               avr_irq_register_notify(xoff, uart_pty_xoff_hook, p);
+
+       for (int ti = 0; ti < 1; ti++) if (p->port[ti].s) {
+               char link[128];
+               sprintf(link, "/tmp/simavr-uart%s%c", ti == 1 ? "tap" : "", uart);
+               unlink(link);
+               if (symlink(p->port[ti].slavename, link) != 0) {
+                       fprintf(stderr, "WARN %s: Can't create %s: %s", __func__, link, strerror(errno));
+               } else {
+                       printf("%s: %s now points to %s\n", __func__, link, p->port[ti].slavename);
+               }
+       }
+       if (getenv("SIMAVR_UART_XTERM") && atoi(getenv("SIMAVR_UART_XTERM"))) {
+               char cmd[256];
+               sprintf(cmd, "xterm -e picocom -b 115200 %s >/dev/null 2>&1 &",
+                               p->tap.slavename);
+               system(cmd);
+       } else
+               printf("note: export SIMAVR_UART_XTERM=1 and install picocom to get a terminal\n");
+}
+
diff --git a/shavr/src/uart_pty.h b/shavr/src/uart_pty.h
new file mode 100644 (file)
index 0000000..c6e0603
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+       uart_pty.h
+
+       Copyright 2012 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __UART_PTY_H___
+#define __UART_PTY_H___
+
+#include <pthread.h>
+#include "sim_irq.h"
+#include "fifo_declare.h"
+
+enum {
+       IRQ_UART_PTY_BYTE_IN = 0,
+       IRQ_UART_PTY_BYTE_OUT,
+       IRQ_UART_PTY_COUNT
+};
+
+DECLARE_FIFO(uint8_t,uart_pty_fifo, 512);
+
+typedef struct uart_pty_port_t {
+       int                     tap : 1, crlf : 1;
+       int             s;                      // socket we chat on
+       char            slavename[64];
+       uart_pty_fifo_t in;
+       uart_pty_fifo_t out;
+       uint8_t         buffer[512];
+       size_t          buffer_len, buffer_done;
+} uart_pty_port_t, *uart_pty_port_p;
+
+typedef struct uart_pty_t {
+       avr_irq_t *     irq;            // irq list
+       struct avr_t *avr;              // keep it around so we can pause it
+
+       pthread_t       thread;
+       int                     xon;
+
+       union {
+               struct {
+                       uart_pty_port_t         pty;
+                       uart_pty_port_t         tap;
+               };
+               uart_pty_port_t port[2];
+       };
+} uart_pty_t;
+
+void
+uart_pty_init(
+               struct avr_t * avr,
+               uart_pty_t * b);
+void
+uart_pty_stop(uart_pty_t * p);
+
+void
+uart_pty_connect(
+               uart_pty_t * p,
+               char uart);
+
+#endif /* __UART_PTY_H___ */