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
--- /dev/null
+#
+# 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
--- /dev/null
+/*
+ * 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);
+
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ 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;
+ }
+
+}
--- /dev/null
+/*
+ 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;
+}
+
--- /dev/null
+/*
+ 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__ */
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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");
+}
+
--- /dev/null
+/*
+ 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___ */