summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo C2014-08-12 12:35:28 +0200
committerLeo C2014-08-12 12:35:28 +0200
commitd684c21619905153eff68c43927207248925f6c2 (patch)
treec4bfb04a20512679103c6ad39fd9885dea6d9e76
parent9b6b4b31e8cb284ad6a68fe16d458e36bbfb46fa (diff)
downloadz180-stamp-d684c21619905153eff68c43927207248925f6c2.zip
New U-Boot like AVR main program.
Uses U-Boot source code taken from: git://git.denx.de/u-boot.git
-rw-r--r--.gitignore15
-rw-r--r--avr/Tupfile17
-rw-r--r--avr/bootc.c175
-rw-r--r--avr/cli.c369
-rw-r--r--avr/cli.h64
-rw-r--r--avr/cli_readline.c595
-rw-r--r--avr/cli_readline.h50
-rw-r--r--avr/cmd_echo.c58
-rw-r--r--avr/cmd_help.c32
-rw-r--r--avr/command.c525
-rw-r--r--avr/command.h157
-rw-r--r--avr/command_tbl.c76
-rw-r--r--avr/common.h57
-rw-r--r--avr/con-utils.c91
-rw-r--r--avr/con-utils.h22
-rw-r--r--avr/config.h24
-rw-r--r--avr/crc.h12
-rw-r--r--avr/debug.h20
-rw-r--r--avr/env.c808
-rw-r--r--avr/env.h12
-rw-r--r--avr/serial.c8
-rw-r--r--avr/serial.h3
-rw-r--r--avr/timer.c87
-rw-r--r--avr/timer.h7
-rw-r--r--avr/xmalloc.c27
-rw-r--r--avr/xmalloc.h8
-rw-r--r--avr/z180-stamp-avr.c6
-rw-r--r--configs/debug.config2
-rw-r--r--configs/gcc.tup40
29 files changed, 3350 insertions, 17 deletions
diff --git a/.gitignore b/.gitignore
index 1cb02ea..725065f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,11 @@
-/.tup
-Makefiledev
-/.settings
+#Makefiledev
/.cproject
/.project
-/doc/
-/joe/
-/scratch/
+/.settings
+/.tup
+/build-debug
+/doc
+/fatfs
+/joe
+/scratch
+
diff --git a/avr/Tupfile b/avr/Tupfile
index a90b7b5..d90f225 100644
--- a/avr/Tupfile
+++ b/avr/Tupfile
@@ -1,15 +1,22 @@
include_rules
-PROG = z180-stamp-avr
-SRC = z180-stamp-avr.c serial.c z80-if.c
-SRC_Z = ../z180/hdrom.c
+PROG = stamp-bootc
+SRC = bootc.c
+SRC += cli.c cli_readline.c command.c command_tbl.c
+SRC += cmd_help.c cmd_echo.c
+SRC += env.c xmalloc.c
+SRC += timer.c con-utils.c serial.c
+SRC += z80-if.c
+
+#SRC_Z = ../z180/hdrom.c
+
#TARGETS = $(PROG).elf
MCU_TARGET = atmega1281
F_CPU = 18432000UL
DEFS = -DF_CPU=$(F_CPU) -DBAUD=115200UL
-INCLUDES += ../z180
+#INCLUDES += ../z180
###############################################################################
@@ -34,7 +41,7 @@ endif
CFLAGS = -g -Os
CFLAGS += -mmcu=$(MCU_TARGET)
CFLAGS += -std=gnu99
-CFLAGS += -Wall -Wextra -Wimplicit-function-declaration
+CFLAGS += -Wall -Wextra
CFLAGS += -Wredundant-decls
#CFLAGS += -fno-common -ffunction-sections -fdata-sections
#CFLAGS += -I $(INCLUDES)
diff --git a/avr/bootc.c b/avr/bootc.c
new file mode 100644
index 0000000..6dbcdef
--- /dev/null
+++ b/avr/bootc.c
@@ -0,0 +1,175 @@
+/*
+ */
+
+
+#include "common.h"
+
+#include <util/delay.h>
+//#include <avr/power.h>
+//#include <avr/pgmspace.h>
+#include <avr/interrupt.h>
+//#include <util/atomic.h>
+//#include <avr/sleep.h>
+//#include <string.h>
+
+#include <util/delay.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+
+#include "config.h"
+#include "debug.h"
+#include "z80-if.h"
+#include "con-utils.h"
+#include "serial.h"
+#include "timer.h"
+#include "cli.h"
+#include "env.h"
+
+/*--------------------------------------------------------------------------*/
+
+void setup_avr(void)
+{
+ /* WD */
+
+ /* CPU */
+
+ /* Disable JTAG Interface regardless of the JTAGEN fuse setting. */
+ MCUCR = _BV(JTD);
+ MCUCR = _BV(JTD);
+
+ /* disable unused periphels */
+ PRR0 = _BV(PRTIM2) | _BV(PRTIM0) | _BV(PRADC);
+ PRR1 = _BV(PRTIM5) | _BV(PRTIM4) | _BV(PRTIM3) |
+ _BV(PRUSART3) | _BV(PRUSART2) | _BV(PRUSART1);
+
+ /* disable analog comparator */
+ ACSR = _BV(ACD);
+ /* Ports */
+
+ /* Clock */
+ CLKPR = _BV(CLKPCE);
+ CLKPR = 0;
+
+ /* Timer */
+
+ OCR1A = F_CPU / 8 / 1000 - 1; // Timer1: 1000Hz interval (OC1A)
+ TCCR1B = 0b00001010;
+ TIMSK1 = _BV(OCIE1A); // Enable TC1.oca interrupt
+}
+
+
+
+/*******************************************************************************/
+/*******************************************************************************/
+
+#define udelay(n) _delay_us(n)
+
+
+/* Stored value of bootdelay, used by autoboot_command() */
+static int stored_bootdelay;
+
+
+/***************************************************************************
+ * Watch for 'delay' seconds for autoboot stop.
+ * returns: 0 - no key, allow autoboot
+ * 1 - got key, abort
+ */
+
+static int abortboot(int bootdelay)
+{
+ int abort = 0;
+ uint32_t ts;
+
+ if (bootdelay >= 0)
+ printf_P(PSTR("Hit any key to stop autoboot: %2d "), bootdelay);
+
+#if defined CONFIG_ZERO_BOOTDELAY_CHECK
+ /*
+ * Check if key already pressed
+ * Don't check if bootdelay < 0
+ */
+ if (bootdelay >= 0) {
+ if (tstc()) { /* we got a key press */
+ (void) my_getchar(); /* consume input */
+ my_puts_P(PSTR("\b\b\b 0"));
+ abort = 1; /* don't auto boot */
+ }
+ }
+#endif
+
+ while ((bootdelay > 0) && (!abort)) {
+ --bootdelay;
+ /* delay 1000 ms */
+ ts = get_timer(0);
+ do {
+ if (tstc()) { /* we got a key press */
+ abort = 1; /* don't auto boot */
+ bootdelay = 0; /* no more delay */
+ break;
+ }
+ udelay(10000);
+ } while (!abort && get_timer(ts) < 1000);
+
+ printf_P(PSTR("\b\b\b%2d "), bootdelay);
+ }
+
+ putchar('\n');
+
+ return abort;
+}
+
+const char *bootdelay_process(void)
+{
+ char *s;
+ int bootdelay;
+
+ s = getenv("bootdelay");
+ bootdelay = s ? atoi(s) : CONFIG_BOOTDELAY;
+
+
+ debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);
+ _delay_ms(20);
+
+ s = getenv("bootcmd");
+ stored_bootdelay = bootdelay;
+ return s;
+}
+
+void autoboot_command(const char *s)
+{
+ debug("### main_loop: bootcmd=\"%s\"\n", s ? s : PSTR("<UNDEFINED>"));
+ _delay_ms(20);
+
+ if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
+ run_command_list(s, -1);
+ }
+}
+
+
+void main_loop(void)
+{
+ const char *s;
+
+ s = bootdelay_process();
+ autoboot_command(s);
+ cli_loop();
+}
+
+int main(void)
+{
+ setup_avr();
+ z80_setup_bus();
+
+ serial_setup();
+ sei();
+
+ if (env_check() == 0)
+ set_default_env();
+ env_init();
+
+ printf_P(PSTR("\n(ATMEGA1281+HD64180)_stamp Tester\n"));
+
+ main_loop();
+}
diff --git a/avr/cli.c b/avr/cli.c
new file mode 100644
index 0000000..44cf5b8
--- /dev/null
+++ b/avr/cli.c
@@ -0,0 +1,369 @@
+#include "common.h"
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "config.h"
+#include "command.h"
+#include "xmalloc.h"
+#include "debug.h"
+#include "env.h"
+#include "cli_readline.h"
+#include "con-utils.h"
+#include "cli.h"
+
+#define DEBUG_PARSER 0 /* set to 1 to debug */
+
+#define debug_parser(fmt, args...) \
+ debug_cond(DEBUG_PARSER, fmt, ##args)
+
+static int cli_parse_line(char *line, char *argv[])
+{
+ static const FLASH char delim[] = {" \t"};
+
+ char *ptr;
+ uint_fast8_t nargs = 0;
+
+ debug_parser("parse_line: \"%s\"\n", line);
+
+ ptr = strtok_P(line, delim);
+ while(nargs < CONFIG_SYS_MAXARGS && ptr != NULL) {
+ argv[nargs++] = ptr;
+ ptr = strtok_P(NULL, delim);
+ }
+
+ if (ptr != NULL)
+ printf_P(PSTR("** Too many args (max. %d) **\n"), CONFIG_SYS_MAXARGS);
+
+ argv[nargs] = NULL;
+ debug_parser("parse_line: nargs=%d\n", nargs);
+
+ return nargs;
+}
+
+static void process_macros(const char *input, char *output)
+{
+ char c, prev;
+ const char *varname_start = NULL;
+#if 0 && (CONFIG_SYS_CBSIZE < __UINT_FAST8_MAX__)
+ uint_fast8_t inputcnt = strlen(input);
+ uint_fast8_t outputcnt = CONFIG_SYS_CBSIZE;
+#else
+ int inputcnt = strlen(input);
+ int outputcnt = CONFIG_SYS_CBSIZE;
+#endif
+ uint_fast8_t state = 0; /* 0 = waiting for '$' */
+
+ /* 1 = waiting for '(' or '{' */
+ /* 2 = waiting for ')' or '}' */
+ /* 3 = waiting for ''' */
+ char *output_start = output;
+
+ debug_parser("[PROCESS_MACROS] INPUT len %d: \"%s\"\n", strlen(input),
+ input);
+
+ prev = '\0'; /* previous character */
+
+ while (inputcnt && outputcnt) {
+ c = *input++;
+ inputcnt--;
+
+ if (state != 3) {
+ /* remove one level of escape characters */
+ if ((c == '\\') && (prev != '\\')) {
+ if (inputcnt-- == 0)
+ break;
+ prev = c;
+ c = *input++;
+ }
+ }
+
+ switch (state) {
+ case 0: /* Waiting for (unescaped) $ */
+ if ((c == '\'') && (prev != '\\')) {
+ state = 3;
+ break;
+ }
+ if ((c == '$') && (prev != '\\')) {
+ state++;
+ } else {
+ *(output++) = c;
+ outputcnt--;
+ }
+ break;
+ case 1: /* Waiting for ( */
+ if (c == '(' || c == '{') {
+ state++;
+ varname_start = input;
+ } else {
+ state = 0;
+ *(output++) = '$';
+ outputcnt--;
+
+ if (outputcnt) {
+ *(output++) = c;
+ outputcnt--;
+ }
+ }
+ break;
+ case 2: /* Waiting for ) */
+ if (c == ')' || c == '}') {
+ char envname[CONFIG_SYS_ENV_NAMELEN+1], *envval;
+ /* Varname # of chars */
+ uint_fast8_t envcnt = input - varname_start - 1;
+ if (envcnt > CONFIG_SYS_ENV_NAMELEN)
+ envcnt = CONFIG_SYS_ENV_NAMELEN;
+
+ memcpy(envname, varname_start, envcnt);
+ envname[envcnt] = '\0';
+
+ /* Get its value */
+ envval = getenv(envname);
+
+ /* Copy into the line if it exists */
+ if (envval != NULL)
+ while ((*envval) && outputcnt) {
+ *(output++) = *(envval++);
+ outputcnt--;
+ }
+ /* Look for another '$' */
+ state = 0;
+ }
+ break;
+ case 3: /* Waiting for ' */
+ if ((c == '\'') && (prev != '\\')) {
+ state = 0;
+ } else {
+ *(output++) = c;
+ outputcnt--;
+ }
+ break;
+ }
+ prev = c;
+ }
+
+ if (outputcnt)
+ *output = 0;
+ else
+ *(output - 1) = 0;
+
+ debug_parser("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n",
+ strlen(output_start), output_start);
+}
+
+ /*
+ * WARNING:
+ *
+ * We must create a temporary copy of the command since the command we get
+ * may be the result from getenv(), which returns a pointer directly to
+ * the environment data, which may change magicly when the command we run
+ * creates or modifies environment variables (like "bootp" does).
+ */
+static int cli_run_command(const char *cmd, int flag)
+{
+ char *cmdbuf; /* working copy of cmd */
+ char *token; /* start of token in cmdbuf */
+ char *sep; /* end of token (separator) in cmdbuf */
+ char *finaltoken;
+ char *str;
+ char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
+ int argc;
+ uint_fast8_t inquotes, repeatable = 1;
+ int rc = 0;
+
+ debug_parser("[RUN_COMMAND] cmd[%p]=\"%s\"\n",
+ cmd, cmd ? cmd : "NULL");
+
+ clear_ctrlc(); /* forget any previous Control C */
+
+ if (!cmd || !*cmd)
+ return -1; /* empty command */
+
+
+ cmdbuf = xmalloc(strlen(cmd) + 1);
+ finaltoken = xmalloc(CONFIG_SYS_CBSIZE);
+
+ strcpy(cmdbuf, cmd);
+ str = cmdbuf;
+
+ /* Process separators and check for invalid
+ * repeatable commands
+ */
+
+ debug_parser("[PROCESS_SEPARATORS] %s\n", cmd);
+ while (*str) {
+ /*
+ * Find separator, or string end
+ * Allow simple escape of ';' by writing "\;"
+ */
+ for (inquotes = 0, sep = str; *sep; sep++) {
+ if ((*sep == '\'') &&
+ (*(sep - 1) != '\\'))
+ inquotes = !inquotes;
+
+ if (!inquotes &&
+ (*sep == ';' || *sep == '\n') && /* separator */
+ (sep != str) && /* past string start */
+ (*(sep - 1) != '\\')) /* and NOT escaped */
+ break;
+ }
+
+ /*
+ * Limit the token to data between separators
+ */
+ token = str;
+ if (*sep) {
+ str = sep + 1; /* start of command for next pass */
+ *sep = '\0';
+ } else {
+ str = sep; /* no more commands for next pass */
+ }
+ debug_parser("token: \"%s\"\n", token);
+
+ /* find macros in this token and replace them */
+ process_macros(token, finaltoken);
+
+ /* Extract arguments */
+ argc = cli_parse_line(finaltoken, argv);
+ if (argc == 0) {
+ rc = -1; /* no command at all */
+ continue;
+ }
+
+ if (cmd_process(flag, argc, argv, &repeatable))
+ rc = -1;
+
+ /* Did the user stop this? */
+ if (had_ctrlc()) {
+ rc = -1; /* if stopped then not repeatable */
+ break;
+ }
+ }
+
+ free(cmdbuf);
+ free(finaltoken);
+
+ return rc ? rc : repeatable;
+}
+
+static int cli_run_command_list(const char *cmd)
+{
+ return (cli_run_command(cmd, 0) < 0);
+}
+
+/******************************************************************************/
+
+
+/*
+ * Run a command using the selected parser.
+ *
+ * @param cmd Command to run
+ * @param flag Execution flags (CMD_FLAG_...)
+ * @return 0 on success, or != 0 on error.
+ */
+int run_command(const char *cmd, int flag)
+{
+ /*
+ * cli_run_command can return 0 or 1 for success, so clean up
+ * its result.
+ */
+ if (cli_run_command(cmd, flag) == -1)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Run a command using the selected parser, and check if it is repeatable.
+ *
+ * @param cmd Command to run
+ * @param flag Execution flags (CMD_FLAG_...)
+ * @return 0 (not repeatable) or 1 (repeatable) on success, -1 on error.
+ */
+static int run_command_repeatable(const char *cmd, int flag)
+{
+ return cli_run_command(cmd, flag);
+}
+
+int run_command_list(const char *cmd, int len)
+{
+ (void) len;
+
+ return cli_run_command_list(cmd);
+}
+
+/****************************************************************************/
+
+#define DYN_BUFFER 1
+
+void cli_loop(void)
+{
+#if DYN_BUFFER
+ char *lastcommand = NULL;
+#else
+ static char lastcommand[CONFIG_SYS_CBSIZE];
+#endif
+ int len;
+ int flag;
+ int rc = 1;
+
+ for (;;) {
+ len = cli_readline(PSTR(CONFIG_SYS_PROMPT));
+
+ flag = 0; /* assume no special flags for now */
+ if (len > 0) {
+#if DYN_BUFFER
+ lastcommand = (char *) xrealloc(lastcommand, len+1);
+ if (lastcommand != NULL) {
+ strncpy(lastcommand, console_buffer, len+1);
+ lastcommand[len] = '\0';
+ }
+#else
+ strcpy(lastcommand, console_buffer);
+#endif
+ } else if (len == 0)
+ flag |= CMD_FLAG_REPEAT;
+
+ if (len == -1)
+ my_puts_P(PSTR("<INTERRUPT>\n"));
+ else
+ rc = run_command_repeatable(lastcommand, flag);
+
+ if (rc <= 0) {
+ /* invalid command or not repeatable, forget it */
+#if DYN_BUFFER
+ free(lastcommand);
+ lastcommand = NULL;
+#else
+ lastcommand[0] = 0;
+#endif
+ }
+ }
+}
+
+
+int do_run(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ int i;
+ (void) cmdtp;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ for (i = 1; i < argc; ++i) {
+ char *arg;
+
+ arg = getenv(argv[i]);
+ if (arg == NULL) {
+ printf_P(PSTR("## Error: \"%s\" not defined\n"), argv[i]);
+ return 1;
+ }
+
+ if (run_command(arg, flag) != 0)
+ return 1;
+ }
+ return 0;
+}
+
diff --git a/avr/cli.h b/avr/cli.h
new file mode 100644
index 0000000..67ff63b
--- /dev/null
+++ b/avr/cli.h
@@ -0,0 +1,64 @@
+#ifndef CLI_H
+#define CLI_H
+
+/**
+ * Go into the command loop
+ *
+ * This will return if we get a timeout waiting for a command, but only for
+ * the simple parser (not hush). See CONFIG_BOOT_RETRY_TIME.
+ */
+void cli_loop(void);
+
+/**
+ * cli_simple_run_command() - Execute a command with the simple CLI
+ *
+ * @cmd: String containing the command to execute
+ * @flag Flag value - see CMD_FLAG_...
+ * @return 1 - command executed, repeatable
+ * 0 - command executed but not repeatable, interrupted commands are
+ * always considered not repeatable
+ * -1 - not executed (unrecognized, bootd recursion or too many args)
+ * (If cmd is NULL or "" or longer than CONFIG_SYS_CBSIZE-1 it is
+ * considered unrecognized)
+ */
+//int cli_simple_run_command(const char *cmd, int flag);
+
+/**
+ * cli_simple_run_command_list() - Execute a list of command
+ *
+ * The commands should be separated by ; or \n and will be executed
+ * by the built-in parser.
+ *
+ * This function cannot take a const char * for the command, since if it
+ * finds newlines in the string, it replaces them with \0.
+ *
+ * @param cmd String containing list of commands
+ * @param flag Execution flags (CMD_FLAG_...)
+ * @return 0 on success, or != 0 on error.
+ */
+//int cli_simple_run_command_list(char *cmd, int flag);
+
+/**
+ * parse_line() - split a command line down into separate arguments
+ *
+ * The argv[] array is filled with pointers into @line, and each argument
+ * is terminated by \0 (i.e. @line is changed in the process unless there
+ * is only one argument).
+ *
+ * #argv is terminated by a NULL after the last argument pointer.
+ *
+ * At most CONFIG_SYS_MAXARGS arguments are permited - if there are more
+ * than that then an error is printed, and this function returns
+ * CONFIG_SYS_MAXARGS, with argv[] set up to that point.
+ *
+ * @line: Command line to parse
+ * @args: Array to hold arguments
+ * @return number of arguments
+ */
+//int cli_simple_parse_line(char *line, char *argv[]);
+
+int run_command_list(const char *cmd, int len);
+
+
+#endif /* CLI_H */
+
diff --git a/avr/cli_readline.c b/avr/cli_readline.c
new file mode 100644
index 0000000..17d5494
--- /dev/null
+++ b/avr/cli_readline.c
@@ -0,0 +1,595 @@
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * Add to readline cmdline-editing by
+ * (C) Copyright 2005
+ * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "common.h"
+
+#include <avr/pgmspace.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "config.h"
+#include "con-utils.h"
+#include "command.h"
+#include "cli_readline.h"
+
+char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */
+
+static const FLASH char erase_seq[] = "\b \b"; /* erase sequence */
+static const FLASH char tab_seq[] = " "; /* used to expand TABs */
+
+#ifndef CONFIG_CMDLINE_EDITING
+static char *delete_char (char *buffer, char *p, int *colp, int *np, int plen)
+{
+ char *s;
+
+ if (*np == 0)
+ return p;
+
+ if (*(--p) == '\t') { /* will retype the whole line */
+ while (*colp > plen) {
+ my_puts_P(erase_seq);
+ (*colp)--;
+ }
+ for (s = buffer; s < p; ++s) {
+ if (*s == '\t') {
+ my_puts_P(tab_seq + ((*colp) & 07));
+ *colp += 8 - ((*colp) & 07);
+ } else {
+ ++(*colp);
+ putchar(*s);
+ }
+ }
+ } else {
+ my_puts_P(erase_seq);
+ (*colp)--;
+ }
+ (*np)--;
+
+ return p;
+}
+#endif /* CONFIG_CMDLINE_EDITING */
+
+
+#ifdef CONFIG_CMDLINE_EDITING
+
+/*
+ * cmdline-editing related codes from vivi.
+ * Author: Janghoon Lyu <nandy@mizi.com>
+ */
+
+#define putnstr(str, n) printf_P(PSTR("%.*s"), (int)n, str)
+
+#define CTL_CH(c) ((c) - 'a' + 1)
+#define CTL_BACKSPACE ('\b')
+#define DEL ((char)255)
+#define DEL7 ((char)127)
+#define CREAD_HIST_CHAR ('!')
+
+#define getcmd_putch(ch) putchar(ch)
+#define getcmd_getch() my_getchar()
+#define getcmd_cbeep() getcmd_putch('\a')
+
+#define HIST_MAX 5
+#define HIST_SIZE CONFIG_SYS_CBSIZE
+
+static int hist_max;
+static int hist_add_idx;
+static int hist_cur = -1;
+static unsigned hist_num;
+
+static char *hist_list[HIST_MAX];
+static char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */
+
+#define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1)
+
+static void hist_init(void)
+{
+ int i;
+
+ hist_max = 0;
+ hist_add_idx = 0;
+ hist_cur = -1;
+ hist_num = 0;
+
+ for (i = 0; i < HIST_MAX; i++) {
+ hist_list[i] = hist_lines[i];
+ hist_list[i][0] = '\0';
+ }
+}
+
+static void cread_add_to_hist(char *line)
+{
+ strcpy(hist_list[hist_add_idx], line);
+
+ if (++hist_add_idx >= HIST_MAX)
+ hist_add_idx = 0;
+
+ if (hist_add_idx > hist_max)
+ hist_max = hist_add_idx;
+
+ hist_num++;
+}
+
+static char *hist_prev(void)
+{
+ char *ret;
+ int old_cur;
+
+ if (hist_cur < 0)
+ return NULL;
+
+ old_cur = hist_cur;
+ if (--hist_cur < 0)
+ hist_cur = hist_max;
+
+ if (hist_cur == hist_add_idx) {
+ hist_cur = old_cur;
+ ret = NULL;
+ } else {
+ ret = hist_list[hist_cur];
+ }
+
+ return ret;
+}
+
+static char *hist_next(void)
+{
+ char *ret;
+
+ if (hist_cur < 0)
+ return NULL;
+
+ if (hist_cur == hist_add_idx)
+ return NULL;
+
+ if (++hist_cur > hist_max)
+ hist_cur = 0;
+
+ if (hist_cur == hist_add_idx)
+ ret = "";
+ else
+ ret = hist_list[hist_cur];
+
+ return ret;
+}
+
+#ifndef CONFIG_CMDLINE_EDITING
+static void cread_print_hist_list(void)
+{
+ int i;
+ unsigned n;
+
+ n = hist_num - hist_max;
+
+ i = hist_add_idx + 1;
+ while (1) {
+ if (i > hist_max)
+ i = 0;
+ if (i == hist_add_idx)
+ break;
+ printf_P(PSTR("%s\n"), hist_list[i]);
+ n++;
+ i++;
+ }
+}
+#endif /* CONFIG_CMDLINE_EDITING */
+
+#define BEGINNING_OF_LINE() { \
+ while (num) { \
+ getcmd_putch(CTL_BACKSPACE); \
+ num--; \
+ } \
+}
+
+#define ERASE_TO_EOL() { \
+ if (num < eol_num) { \
+ printf_P(PSTR("%*S"), (int)(eol_num - num), PSTR("")); \
+ do { \
+ getcmd_putch(CTL_BACKSPACE); \
+ } while (--eol_num > num); \
+ } \
+}
+
+#define REFRESH_TO_EOL() { \
+ if (num < eol_num) { \
+ wlen = eol_num - num; \
+ putnstr(buf + num, wlen); \
+ num = eol_num; \
+ } \
+}
+
+static void cread_add_char(char ichar, int insert, unsigned long *num,
+ unsigned long *eol_num, char *buf, unsigned long len)
+{
+ unsigned long wlen;
+
+ /* room ??? */
+ if (insert || *num == *eol_num) {
+ if (*eol_num > len - 1) {
+ getcmd_cbeep();
+ return;
+ }
+ (*eol_num)++;
+ }
+
+ if (insert) {
+ wlen = *eol_num - *num;
+ if (wlen > 1)
+ memmove(&buf[*num+1], &buf[*num], wlen-1);
+
+ buf[*num] = ichar;
+ putnstr(buf + *num, wlen);
+ (*num)++;
+ while (--wlen)
+ getcmd_putch(CTL_BACKSPACE);
+ } else {
+ /* echo the character */
+ wlen = 1;
+ buf[*num] = ichar;
+ putnstr(buf + *num, wlen);
+ (*num)++;
+ }
+}
+
+static void cread_add_str(char *str, int strsize, int insert,
+ unsigned long *num, unsigned long *eol_num,
+ char *buf, unsigned long len)
+{
+ while (strsize--) {
+ cread_add_char(*str, insert, num, eol_num, buf, len);
+ str++;
+ }
+}
+
+static int cread_line(const FLASH char *const prompt, char *buf, unsigned int *len)
+{
+ unsigned long num = 0;
+ unsigned long eol_num = 0;
+ unsigned long wlen;
+ char ichar;
+ int insert = 1;
+ int esc_len = 0;
+ char esc_save[8];
+ int init_len = strlen(buf);
+
+ (void) prompt;
+
+ if (init_len)
+ cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len);
+
+ while (1) {
+ ichar = getcmd_getch();
+
+ if ((ichar == '\n') || (ichar == '\r')) {
+ putchar('\n');
+ break;
+ }
+
+ /*
+ * handle standard linux xterm esc sequences for arrow key, etc.
+ */
+ if (esc_len != 0) {
+ if (esc_len == 1) {
+ if (ichar == '[') {
+ esc_save[esc_len] = ichar;
+ esc_len = 2;
+ } else {
+ cread_add_str(esc_save, esc_len,
+ insert, &num, &eol_num,
+ buf, *len);
+ esc_len = 0;
+ }
+ continue;
+ }
+
+ switch (ichar) {
+ case 'D': /* <- key */
+ ichar = CTL_CH('b');
+ esc_len = 0;
+ break;
+ case 'C': /* -> key */
+ ichar = CTL_CH('f');
+ esc_len = 0;
+ break; /* pass off to ^F handler */
+ case 'H': /* Home key */
+ ichar = CTL_CH('a');
+ esc_len = 0;
+ break; /* pass off to ^A handler */
+ case 'A': /* up arrow */
+ ichar = CTL_CH('p');
+ esc_len = 0;
+ break; /* pass off to ^P handler */
+ case 'B': /* down arrow */
+ ichar = CTL_CH('n');
+ esc_len = 0;
+ break; /* pass off to ^N handler */
+ default:
+ esc_save[esc_len++] = ichar;
+ cread_add_str(esc_save, esc_len, insert,
+ &num, &eol_num, buf, *len);
+ esc_len = 0;
+ continue;
+ }
+ }
+
+ switch (ichar) {
+ case 0x1b:
+ if (esc_len == 0) {
+ esc_save[esc_len] = ichar;
+ esc_len = 1;
+ } else {
+ my_puts_P(PSTR("impossible condition #876\n"));
+ esc_len = 0;
+ }
+ break;
+
+ case CTL_CH('a'):
+ BEGINNING_OF_LINE();
+ break;
+ case CTL_CH('c'): /* ^C - break */
+ *buf = '\0'; /* discard input */
+ return -1;
+ case CTL_CH('f'):
+ if (num < eol_num) {
+ getcmd_putch(buf[num]);
+ num++;
+ }
+ break;
+ case CTL_CH('b'):
+ if (num) {
+ getcmd_putch(CTL_BACKSPACE);
+ num--;
+ }
+ break;
+ case CTL_CH('d'):
+ if (num < eol_num) {
+ wlen = eol_num - num - 1;
+ if (wlen) {
+ memmove(&buf[num], &buf[num+1], wlen);
+ putnstr(buf + num, wlen);
+ }
+
+ getcmd_putch(' ');
+ do {
+ getcmd_putch(CTL_BACKSPACE);
+ } while (wlen--);
+ eol_num--;
+ }
+ break;
+ case CTL_CH('k'):
+ ERASE_TO_EOL();
+ break;
+ case CTL_CH('e'):
+ REFRESH_TO_EOL();
+ break;
+ case CTL_CH('o'):
+ insert = !insert;
+ break;
+ case CTL_CH('x'):
+ case CTL_CH('u'):
+ BEGINNING_OF_LINE();
+ ERASE_TO_EOL();
+ break;
+ case DEL:
+ case DEL7:
+ case 8:
+ if (num) {
+ wlen = eol_num - num;
+ num--;
+ memmove(&buf[num], &buf[num+1], wlen);
+ getcmd_putch(CTL_BACKSPACE);
+ putnstr(buf + num, wlen);
+ getcmd_putch(' ');
+ do {
+ getcmd_putch(CTL_BACKSPACE);
+ } while (wlen--);
+ eol_num--;
+ }
+ break;
+ case CTL_CH('p'):
+ case CTL_CH('n'):
+ {
+ char *hline;
+
+ esc_len = 0;
+
+ if (ichar == CTL_CH('p'))
+ hline = hist_prev();
+ else
+ hline = hist_next();
+
+ if (!hline) {
+ getcmd_cbeep();
+ continue;
+ }
+
+ /* nuke the current line */
+ /* first, go home */
+ BEGINNING_OF_LINE();
+
+ /* erase to end of line */
+ ERASE_TO_EOL();
+
+ /* copy new line into place and display */
+ strcpy(buf, hline);
+ eol_num = strlen(buf);
+ REFRESH_TO_EOL();
+ continue;
+ }
+#ifdef CONFIG_AUTO_COMPLETE
+ case '\t': {
+ int num2, col;
+
+ /* do not autocomplete when in the middle */
+ if (num < eol_num) {
+ getcmd_cbeep();
+ break;
+ }
+
+ buf[num] = '\0';
+ col = strlen_P(prompt) + eol_num;
+ num2 = num;
+ if (cmd_auto_complete(prompt, buf, &num2, &col)) {
+ col = num2 - num;
+ num += col;
+ eol_num += col;
+ }
+ break;
+ }
+#endif
+ default:
+ cread_add_char(ichar, insert, &num, &eol_num, buf,
+ *len);
+ break;
+ }
+ }
+ *len = eol_num;
+ buf[eol_num] = '\0'; /* lose the newline */
+
+ if (buf[0] && buf[0] != CREAD_HIST_CHAR)
+ cread_add_to_hist(buf);
+ hist_cur = hist_add_idx;
+
+ return 0;
+}
+
+#endif /* CONFIG_CMDLINE_EDITING */
+
+/****************************************************************************/
+
+static int cli_readline_into_buffer(const FLASH char *const prompt, char *buffer)
+{
+ char *p = buffer;
+#ifdef CONFIG_CMDLINE_EDITING
+ unsigned int len = CONFIG_SYS_CBSIZE;
+ int rc;
+ static int initted;
+
+ if (!initted) {
+ hist_init();
+ initted = 1;
+ }
+
+ if (prompt)
+ my_puts_P(prompt);
+
+ rc = cread_line(prompt, p, &len);
+ return rc < 0 ? rc : (int) len;
+
+#else /* CONFIG_CMDLINE_EDITING */
+ char *p_buf = p;
+ int n = 0; /* buffer index */
+ int plen = 0; /* prompt length */
+ int col; /* output column cnt */
+ char c;
+
+ /* print prompt */
+ if (prompt) {
+ plen = strlen_P(prompt);
+ my_puts_P(prompt);
+ }
+ col = plen;
+
+ for (;;) {
+
+ c = my_getchar();
+
+ /*
+ * Special character handling
+ */
+ switch (c) {
+ case '\r': /* Enter */
+ case '\n':
+ *p = '\0';
+ my_puts_P(PSTR("\r\n"));
+ return p - p_buf;
+
+ case '\0': /* nul */
+ continue;
+
+ case 0x03: /* ^C - break */
+ p_buf[0] = '\0'; /* discard input */
+ return -1;
+
+ case 0x15: /* ^U - erase line */
+ while (col > plen) {
+ my_puts_P(erase_seq);
+ --col;
+ }
+ p = p_buf;
+ n = 0;
+ continue;
+
+ case 0x17: /* ^W - erase word */
+ p = delete_char(p_buf, p, &col, &n, plen);
+ while ((n > 0) && (*p != ' '))
+ p = delete_char(p_buf, p, &col, &n, plen);
+ continue;
+
+ case 0x08: /* ^H - backspace */
+ case 0x7F: /* DEL - backspace */
+ p = delete_char(p_buf, p, &col, &n, plen);
+ continue;
+
+ default:
+ /*
+ * Must be a normal character then
+ */
+ if (n < CONFIG_SYS_CBSIZE-2) {
+ if (c == '\t') { /* expand TABs */
+#ifdef CONFIG_AUTO_COMPLETE
+ /*
+ * if auto completion triggered just
+ * continue
+ */
+ *p = '\0';
+ if (cmd_auto_complete(prompt,
+ console_buffer,
+ &n, &col)) {
+ p = p_buf + n; /* reset */
+ continue;
+ }
+#endif
+ my_puts_P(tab_seq + (col & 07));
+ col += 8 - (col & 07);
+ } else {
+ char buf[2];
+
+ /*
+ * Echo input using puts() to force an
+ * LCD flush if we are using an LCD
+ */
+ ++col;
+ buf[0] = c;
+ buf[1] = '\0';
+ my_puts(buf);
+ }
+ *p++ = c;
+ ++n;
+ } else { /* Buffer full */
+ putchar('\a');
+ }
+ }
+ }
+#endif /* CONFIG_CMDLINE_EDITING */
+}
+
+int cli_readline(const FLASH char *const prompt)
+{
+ /*
+ * If console_buffer isn't 0-length the user will be prompted to modify
+ * it instead of entering it from scratch as desired.
+ */
+ console_buffer[0] = '\0';
+
+ return cli_readline_into_buffer(prompt, console_buffer);
+}
+
diff --git a/avr/cli_readline.h b/avr/cli_readline.h
new file mode 100644
index 0000000..5b25762
--- /dev/null
+++ b/avr/cli_readline.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 2014 Google, Inc
+ * Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef CLI_READLINE_H
+#define CLI_READLINE_H
+
+extern char console_buffer[]; /* console I/O buffer */
+
+/**
+ * cli_readline() - read a line into the console_buffer
+ *
+ * This is a convenience function which calls cli_readline_into_buffer().
+ *
+ * @prompt: Prompt to display
+ * @return command line length excluding terminator, or -ve on error
+ */
+int cli_readline(const FLASH char *const prompt);
+
+/**
+ * readline_into_buffer() - read a line into a buffer
+ *
+ * Display the prompt, then read a command line into @buffer. The
+ * maximum line length is CONFIG_SYS_CBSIZE including a \0 terminator, which
+ * will always be added.
+ *
+ * The command is echoed as it is typed. Command editing is supported if
+ * CONFIG_CMDLINE_EDITING is defined. Tab auto-complete is supported if
+ * CONFIG_AUTO_COMPLETE is defined. If CONFIG_BOOT_RETRY_TIME is defined,
+ * then a timeout will be applied.
+ *
+ * If CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0,
+ * time out when time goes past endtime (timebase time in ticks).
+ *
+ * @prompt: Prompt to display
+ * @buffer: Place to put the line that is entered
+ * @timeout: Timeout in milliseconds, 0 if none
+ * @return command line length excluding terminator, or -ve on error: of the
+ * timeout is exceeded (either CONFIG_BOOT_RETRY_TIME or the timeout
+ * parameter), then -2 is returned. If a break is detected (Ctrl-C) then
+ * -1 is returned.
+ */
+//int cli_readline_into_buffer(const char *const prompt, char *buffer, int timeout);
+
+
+#endif /* CLI_READLINE_H */
+
diff --git a/avr/cmd_echo.c b/avr/cmd_echo.c
new file mode 100644
index 0000000..d142ab6
--- /dev/null
+++ b/avr/cmd_echo.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2000-2009
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "common.h"
+#include "command.h"
+
+
+int do_echo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ uint_fast8_t putnl = 1;
+
+ (void) cmdtp; (void) flag;
+
+ for (uint_fast8_t i = 1; i < argc; i++) {
+
+ uint_fast8_t backslash = 0;
+ char *p = argv[i];
+ char c;
+
+ if (i != 1)
+ putchar(' ');
+ while ((c = *p++) != '\0') {
+
+ if(backslash) {
+ backslash = 0;
+ if (c == 'c') {
+ putnl = 0;
+ continue;
+ } else
+ putchar('\\');
+ } else {
+ if (c == '\\') {
+ backslash = 1;
+ continue;
+ }
+ }
+ putchar(c);
+ }
+ }
+
+ if (putnl)
+ putchar('\n');
+
+ return 0;
+}
+
+#if 0
+U_BOOT_CMD(
+ echo, CONFIG_SYS_MAXARGS, 1, do_echo,
+ "echo args to console",
+ "[args..]\n"
+ " - echo args to console; \\c suppresses newline"
+);
+#endif
diff --git a/avr/cmd_help.c b/avr/cmd_help.c
new file mode 100644
index 0000000..317eb5d
--- /dev/null
+++ b/avr/cmd_help.c
@@ -0,0 +1,32 @@
+
+#include "common.h"
+#include "command.h"
+
+int do_help(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ const int len = cmd_tbl_item_count();
+ return _do_help(cmd_tbl, len, cmdtp, flag, argc, argv);
+}
+
+
+#if 0
+
+U_BOOT_CMD(
+ help, CONFIG_SYS_MAXARGS, 1, do_help,
+ "print command description/usage",
+ "\n"
+ " - print brief description of all commands\n"
+ "help command ...\n"
+ " - print detailed usage of 'command'"
+);
+
+/* This does not use the U_BOOT_CMD macro as ? can't be used in symbol names */
+ll_entry_declare(cmd_tbl_t, question_mark, cmd) = {
+ "?", CONFIG_SYS_MAXARGS, 1, do_help,
+ "alias for 'help'",
+#ifdef CONFIG_SYS_LONGHELP
+ ""
+#endif /* CONFIG_SYS_LONGHELP */
+};
+
+#endif
diff --git a/avr/command.c b/avr/command.c
new file mode 100644
index 0000000..ad39006
--- /dev/null
+++ b/avr/command.c
@@ -0,0 +1,525 @@
+
+/*
+ * Command Processor Table
+ */
+
+#include "common.h"
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "config.h"
+#include "debug.h"
+#include "con-utils.h"
+#include "env.h"
+#include "timer.h"
+#include "command.h"
+
+
+static void print_blanks(int_fast8_t count)
+{
+ while(count--)
+ putchar(' ');
+}
+
+static void print_usage_line(const FLASH char *name, const FLASH char *usage)
+{
+ my_puts_P(name);
+ print_blanks(CONFIG_SYS_MAXARGS - strlen_P(name));
+ my_puts_P(PSTR(" - "));
+ my_puts_P(usage);
+ my_puts_P(PSTR("\n"));
+}
+
+int strcmp_PP(const FLASH char *s1, const FLASH char *s2)
+{
+ unsigned char c1, c2;
+
+ while ((c1 = *(const FLASH unsigned char *)s1++)
+ == (c2 = *(const FLASH unsigned char *)s2++))
+ if (c1 == 0)
+ return 0;
+
+ return c1 - c2;
+}
+
+int cmpstringp(const void *p1, const void *p2)
+{
+ return strcmp_PP((*(const FLASH cmd_tbl_t **) p1)->name,
+ (*(const FLASH cmd_tbl_t **) p2)->name);
+}
+
+int cmd_tbl_item_count(void)
+{
+ cmd_tbl_t * p = cmd_tbl;
+ int count = 0;
+
+ while (p->name != NULL) {
+ p++; count++;
+ }
+ return count;
+}
+
+/*
+ * Use puts() instead of printf() to avoid printf buffer overflow
+ * for long help messages
+ */
+
+int _do_help (cmd_tbl_t *cmd_start, int cmd_items, cmd_tbl_t * cmdtp,
+ int flag, int argc, char * const argv[])
+{
+ uint_fast8_t i;
+ int rcode = 0;
+
+ (void) flag;
+
+ if (argc == 1) { /*show list of commands */
+ cmd_tbl_t *cmd_array[cmd_items];
+ int i;
+
+ /* Make array of commands from .uboot_cmd section */
+ cmdtp = cmd_start;
+ for (i = 0; i < cmd_items; i++) {
+ cmd_array[i] = cmdtp++;
+ }
+
+ /* Sort command list */
+ qsort(cmd_array, cmd_items, sizeof (cmd_tbl_t *), cmpstringp);
+
+ /* print short help (usage) */
+ for (i = 0; i < cmd_items; i++) {
+ const FLASH char *usage = cmd_array[i]->usage;
+
+ /* allow user abort */
+ if (ctrlc ())
+ return 1;
+ if (usage == NULL)
+ continue;
+#ifdef GCC_BUG_61443
+ print_usage_line(cmd_array[i]->name, usage);
+#else
+ printf_P(PSTR("%-" stringify(CONFIG_SYS_MAXARGS) "S - %S\n"),
+ cmd_array[i]->name, usage);
+#endif
+ }
+ return 0;
+ }
+ /*
+ * command help (long version)
+ */
+ for (i = 1; i < argc; ++i) {
+ if ((cmdtp = find_cmd_tbl (argv[i], cmd_start, cmd_items )) != NULL) {
+ rcode |= cmd_usage(cmdtp);
+ } else {
+ printf_P(PSTR("Unknown command '%s' - try 'help'"
+ " without arguments.\n\n"), argv[i]
+ );
+ rcode = 1;
+ }
+ }
+ return rcode;
+}
+
+/***************************************************************************
+ * find command table entry for a command
+ */
+cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len)
+{
+ cmd_tbl_t *cmdtp;
+ cmd_tbl_t *cmdtp_temp = table; /*Init value */
+ int len;
+ uint_fast8_t n_found = 0;
+
+ if (!cmd)
+ return NULL;
+
+ len = strlen(cmd);
+
+ for (cmdtp = table;
+ cmdtp != table + table_len;
+ cmdtp++) {
+ if (strncmp_P (cmd, cmdtp->name, len) == 0) {
+ if (len == strlen (cmdtp->name))
+ return cmdtp; /* full match */
+
+ cmdtp_temp = cmdtp; /* abbreviated command ? */
+ n_found++;
+ }
+ }
+ if (n_found == 1) { /* exactly one match */
+ return cmdtp_temp;
+ }
+
+ return NULL; /* not found or ambiguous command */
+}
+
+
+cmd_tbl_t *find_cmd (const char *cmd)
+{
+ return find_cmd_tbl(cmd, cmd_tbl, cmd_tbl_item_count());
+}
+
+
+int cmd_usage(const FLASH cmd_tbl_t *cmdtp)
+{
+// printf("%s - %s\n\n", cmdtp->name, cmdtp->usage);
+ print_usage_line(cmdtp->name, cmdtp->usage);
+#if 0
+ my_puts_P(cmdtp->name);
+ print_blanks(CONFIG_SYS_MAXARGS - strlen_P(cmdtp->name));
+ my_puts_P(PSTR(" - "));
+ my_puts_P(cmdtp->usage);
+ my_puts_P(PSTR("\n\n"));
+#endif
+#ifdef CONFIG_SYS_LONGHELP
+// printf("Usage:\n%s ", cmdtp->name);
+ my_puts_P(PSTR("Usage:\n"));
+ my_puts_P(cmdtp->name);
+
+ if (!cmdtp->help) {
+ my_puts_P(PSTR(" - No additional help available.\n"));
+ return 1;
+ }
+
+ my_puts_P(cmdtp->help);
+ my_puts_P(PSTR("\n"));
+#endif /* CONFIG_SYS_LONGHELP */
+ return 1;
+}
+
+#ifdef CONFIG_AUTO_COMPLETE
+
+int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
+{
+ static char tmp_buf[512];
+ int space;
+
+ space = last_char == '\0' || isblank(last_char);
+
+ if (space && argc == 1)
+ return env_complete("", maxv, cmdv, sizeof(tmp_buf), tmp_buf);
+
+ if (!space && argc == 2)
+ return env_complete(argv[1], maxv, cmdv, sizeof(tmp_buf), tmp_buf);
+
+ return 0;
+}
+
+/*************************************************************************************/
+
+static int complete_cmdv(int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
+{
+ cmd_tbl_t *cmdtp = cmd_tbl;
+// const int count = ARRAY_SIZE(cmd_tbl);
+// const cmd_tbl_t *cmdend = cmdtp + count;
+// const char *p;
+ int len, clen;
+ int n_found = 0;
+ const char *cmd;
+
+ /* sanity? */
+ if (maxv < 2)
+ return -2;
+
+ cmdv[0] = NULL;
+
+ if (argc == 0) {
+ /* output full list of commands */
+ for (; cmdtp->name[0] != '\0'; cmdtp++) {
+ if (n_found >= maxv - 2) {
+ cmdv[n_found++] = "...";
+ break;
+ }
+ cmdv[n_found++] = cmdtp->name;
+ }
+ cmdv[n_found] = NULL;
+ return n_found;
+ }
+
+ /* more than one arg or one but the start of the next */
+ if (argc > 1 || (last_char == '\0' || isblank(last_char))) {
+ cmdtp = find_cmd(argv[0]);
+ if (cmdtp == NULL || cmdtp->complete == NULL) {
+ cmdv[0] = NULL;
+ return 0;
+ }
+ return (*cmdtp->complete)(argc, argv, last_char, maxv, cmdv);
+ }
+
+ cmd = argv[0];
+
+ len = strlen(cmd);
+
+ /* return the partial matches */
+ for (; cmdtp->name[0] != '\0'; cmdtp++) {
+
+ clen = strlen(cmdtp->name);
+ if (clen < len)
+ continue;
+
+ if (memcmp(cmd, cmdtp->name, len) != 0)
+ continue;
+
+ /* too many! */
+ if (n_found >= maxv - 2) {
+ cmdv[n_found++] = "...";
+ break;
+ }
+
+ cmdv[n_found++] = cmdtp->name;
+ }
+
+ cmdv[n_found] = NULL;
+ return n_found;
+}
+
+static int make_argv(char *s, int argvsz, char *argv[])
+{
+ int argc = 0;
+
+ /* split into argv */
+ while (argc < argvsz - 1) {
+
+ /* skip any white space */
+ while (isblank(*s))
+ ++s;
+
+ if (*s == '\0') /* end of s, no more args */
+ break;
+
+ argv[argc++] = s; /* begin of argument string */
+
+ /* find end of string */
+ while (*s && !isblank(*s))
+ ++s;
+
+ if (*s == '\0') /* end of s, no more args */
+ break;
+
+ *s++ = '\0'; /* terminate current arg */
+ }
+ argv[argc] = NULL;
+
+ return argc;
+}
+
+static void print_argv(const char *banner, const char *leader, const char *sep, int linemax, char * const argv[])
+{
+ int ll = leader != NULL ? strlen(leader) : 0;
+ int sl = sep != NULL ? strlen(sep) : 0;
+ int len, i;
+
+ if (banner) {
+ my_puts_P(PSTR("\n"));
+ my_puts(banner);
+ }
+
+ i = linemax; /* force leader and newline */
+ while (*argv != NULL) {
+ len = strlen(*argv) + sl;
+ if (i + len >= linemax) {
+ my_puts_P(PSTR("\n"));
+ if (leader)
+ my_puts(leader);
+ i = ll - sl;
+ } else if (sep)
+ my_puts(sep);
+ my_puts(*argv++);
+ i += len;
+ }
+ my_puts_P(PSTR("\n"));
+}
+
+static int find_common_prefix(char * const argv[])
+{
+ int i, len;
+ char *anchor, *s, *t;
+
+ if (*argv == NULL)
+ return 0;
+
+ /* begin with max */
+ anchor = *argv++;
+ len = strlen(anchor);
+ while ((t = *argv++) != NULL) {
+ s = anchor;
+ for (i = 0; i < len; i++, t++, s++) {
+ if (*t != *s)
+ break;
+ }
+ len = s - anchor;
+ }
+ return len;
+}
+
+static char tmp_buf[CONFIG_SYS_CBSIZE]; /* copy of console I/O buffer */
+
+int cmd_auto_complete(const FLASH char *const prompt, char *buf, int *np, int *colp)
+{
+ int n = *np, col = *colp;
+ char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
+ char *cmdv[20];
+ char *s, *t;
+ const char *sep;
+ int i, j, k, len, seplen, argc;
+ int cnt;
+ char last_char;
+
+ if (strcmp_PP(prompt, CONFIG_SYS_PROMPT) != 0)
+ return 0; /* not in normal console */
+
+ cnt = strlen(buf);
+ if (cnt >= 1)
+ last_char = buf[cnt - 1];
+ else
+ last_char = '\0';
+
+ /* copy to secondary buffer which will be affected */
+ strcpy(tmp_buf, buf);
+
+ /* separate into argv */
+ argc = make_argv(tmp_buf, sizeof(argv)/sizeof(argv[0]), argv);
+
+ /* do the completion and return the possible completions */
+ i = complete_cmdv(argc, argv, last_char, sizeof(cmdv)/sizeof(cmdv[0]), cmdv);
+
+ /* no match; bell and out */
+ if (i == 0) {
+ if (argc > 1) /* allow tab for non command */
+ return 0;
+ putchar('\a');
+ return 1;
+ }
+
+ s = NULL;
+ len = 0;
+ sep = NULL;
+ seplen = 0;
+ if (i == 1) { /* one match; perfect */
+ k = strlen(argv[argc - 1]);
+ s = cmdv[0] + k;
+ len = strlen(s);
+ sep = " ";
+ seplen = 1;
+ } else if (i > 1 && (j = find_common_prefix(cmdv)) != 0) { /* more */
+ k = strlen(argv[argc - 1]);
+ j -= k;
+ if (j > 0) {
+ s = cmdv[0] + k;
+ len = j;
+ }
+ }
+
+ if (s != NULL) {
+ k = len + seplen;
+ /* make sure it fits */
+ if (n + k >= CONFIG_SYS_CBSIZE - 2) {
+ putchar('\a');
+ return 1;
+ }
+
+ t = buf + cnt;
+ for (i = 0; i < len; i++)
+ *t++ = *s++;
+ if (sep != NULL)
+ for (i = 0; i < seplen; i++)
+ *t++ = sep[i];
+ *t = '\0';
+ n += k;
+ col += k;
+ my_puts(t - k);
+ if (sep == NULL)
+ putchar('\a');
+ *np = n;
+ *colp = col;
+ } else {
+ print_argv(NULL, " ", " ", 78, cmdv);
+
+ my_puts_P(prompt);
+ my_puts(buf);
+ }
+ return 1;
+}
+
+#endif /* CONFIG_AUTO_COMPLETE */
+
+
+
+/**
+ * Call a command function. This should be the only route in U-Boot to call
+ * a command, so that we can track whether we are waiting for input or
+ * executing a command.
+ *
+ * @param cmdtp Pointer to the command to execute
+ * @param flag Some flags normally 0 (see CMD_FLAG_.. above)
+ * @param argc Number of arguments (arg 0 must be the command text)
+ * @param argv Arguments
+ * @return 0 if command succeeded, else non-zero (CMD_RET_...)
+ */
+static int cmd_call(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ int result;
+
+ result = (cmdtp->cmd)(cmdtp, flag, argc, argv);
+ if (result)
+ debug("Command failed, result=%d\n", result);
+ return result;
+}
+
+enum command_ret_t cmd_process(int flag, int argc, char * const argv[],
+ uint_fast8_t *repeatable)
+{
+ enum command_ret_t rc = CMD_RET_SUCCESS;
+ cmd_tbl_t *cmdtp;
+
+ /* Look up command in command table */
+ cmdtp = find_cmd(argv[0]);
+ if (cmdtp == NULL) {
+ printf_P(PSTR("Unknown command '%s' - try 'help'\n"), argv[0]);
+ return 1;
+ }
+ if (!cmdtp->cmd) {
+ debug("### Command '%s' found, but ->cmd == NULL \n", argv[0]);
+ return 1;
+ }
+
+ /* found - check max args */
+ if (argc > cmdtp->maxargs)
+ rc = CMD_RET_USAGE;
+
+#if defined(CONFIG_CMD_BOOTD)
+ /* avoid "bootd" recursion */
+ else if (cmdtp->cmd == do_bootd) {
+ if (flag & CMD_FLAG_BOOTD) {
+ my_puts_P(PSTR("'bootd' recursion detected\n"));
+ rc = CMD_RET_FAILURE;
+ } else {
+ flag |= CMD_FLAG_BOOTD;
+ }
+ }
+#endif
+
+ /* If OK so far, then do the command */
+ if (!rc) {
+ rc = cmd_call(cmdtp, flag, argc, argv);
+ *repeatable &= cmdtp->repeatable;
+ }
+ if (rc == CMD_RET_USAGE)
+ rc = cmd_usage(cmdtp);
+ return rc;
+}
+
+int cmd_process_error(cmd_tbl_t *cmdtp, int err)
+{
+ char buf[strlen_P(cmdtp->name) + 1];
+ strcpy_P(buf, cmdtp->name);
+
+ if (err) {
+ printf_P(PSTR("Command '%s' failed: Error %d\n"), buf, err);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/avr/command.h b/avr/command.h
new file mode 100644
index 0000000..82b83ba
--- /dev/null
+++ b/avr/command.h
@@ -0,0 +1,157 @@
+
+/*
+ * Definitions for Command Processor
+ */
+#ifndef __COMMAND_H
+#define __COMMAND_H
+
+#include "common.h"
+#include "config.h"
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* Default to a width of 8 characters for help message command width */
+#ifndef CONFIG_SYS_HELP_CMD_WIDTH
+#define CONFIG_SYS_HELP_CMD_WIDTH 8
+#endif
+
+/*
+ * Monitor Command Table
+ */
+
+struct cmd_tbl_s {
+ const FLASH char *name; /* Command Name */
+ int maxargs; /* maximum number of arguments */
+ int repeatable; /* autorepeat allowed? */
+ /* Implementation function */
+ int (*cmd)(const FLASH struct cmd_tbl_s *, int, int, char * const []);
+ const FLASH char *usage; /* Usage message (short) */
+#ifdef CONFIG_SYS_LONGHELP
+ const FLASH char *help; /* Help message (long) */
+#endif
+#ifdef CONFIG_AUTO_COMPLETE
+ /* do auto completion on the arguments */
+ int (*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]);
+#endif
+};
+
+typedef const FLASH struct cmd_tbl_s cmd_tbl_t;
+
+extern int do_run(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]);
+
+/* command.c */
+int _do_help (cmd_tbl_t *cmd_start, int cmd_items, cmd_tbl_t * cmdtp, int
+ flag, int argc, char * const argv[]);
+cmd_tbl_t *find_cmd(const char *cmd);
+cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len);
+
+int cmd_tbl_item_count(void);
+int cmd_usage(cmd_tbl_t *cmdtp);
+
+#ifdef CONFIG_AUTO_COMPLETE
+extern int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]);
+extern int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp);
+#endif
+
+/**
+ * cmd_process_error() - report and process a possible error
+ *
+ * @cmdtp: Command which caused the error
+ * @err: Error code (0 if none, -ve for error, like -EIO)
+ * @return 0 if there is not error, 1 (CMD_RET_FAILURE) if an error is found
+ */
+int cmd_process_error(cmd_tbl_t *cmdtp, int err);
+
+/*
+ * Monitor Command
+ *
+ * All commands use a common argument format:
+ *
+ * void function (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]);
+ */
+
+
+#ifdef CONFIG_CMD_BOOTD
+extern int do_bootd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]);
+#endif
+#ifdef CONFIG_CMD_BOOTM
+extern int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]);
+extern int bootm_maybe_autostart(cmd_tbl_t *cmdtp, const char *cmd);
+#else
+static inline int bootm_maybe_autostart(cmd_tbl_t *cmdtp, const char *cmd)
+{
+ (void) cmdtp; (void) cmd;
+
+ return 0;
+}
+#endif
+
+extern int common_diskboot(cmd_tbl_t *cmdtp, const char *intf, int argc,
+ char *const argv[]);
+
+extern int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]);
+
+/*
+ * Error codes that commands return to cmd_process(). We use the standard 0
+ * and 1 for success and failure, but add one more case - failure with a
+ * request to call cmd_usage(). But the cmd_process() function handles
+ * CMD_RET_USAGE itself and after calling cmd_usage() it will return 1.
+ * This is just a convenience for commands to avoid them having to call
+ * cmd_usage() all over the place.
+ */
+enum command_ret_t {
+ CMD_RET_SUCCESS, /* 0 = Success */
+ CMD_RET_FAILURE, /* 1 = Failure */
+ CMD_RET_USAGE = -1, /* Failure, please report 'usage' error */
+};
+
+/**
+ * Process a command with arguments. We look up the command and execute it
+ * if valid. Otherwise we print a usage message.
+ *
+ * @param flag Some flags normally 0 (see CMD_FLAG_.. above)
+ * @param argc Number of arguments (arg 0 must be the command text)
+ * @param argv Arguments
+ * @param repeatable This function sets this to 0 if the command is not
+ * repeatable. If the command is repeatable, the value
+ * is left unchanged.
+ * @return 0 if the command succeeded, 1 if it failed
+ */
+int cmd_process(int flag, int argc, char * const argv[], uint_fast8_t *repeatable);
+
+
+/*
+ * Command Flags:
+ */
+#define CMD_FLAG_REPEAT 0x0001 /* repeat last command */
+#define CMD_FLAG_BOOTD 0x0002 /* command is from bootd */
+
+#ifdef CONFIG_AUTO_COMPLETE
+# define _CMD_COMPLETE(x) x,
+#else
+# define _CMD_COMPLETE(x)
+#endif
+#ifdef CONFIG_SYS_LONGHELP
+# define _CMD_HELP(x) x,
+#else
+# define _CMD_HELP(x)
+#endif
+
+
+#define CMD_TBL_ITEM_COMPLETE(_name, _maxargs, _rep, _cmd, \
+ _usage, _help, _comp) \
+ { FSTR(#_name), _maxargs, _rep, _cmd, FSTR(_usage), \
+ _CMD_HELP(FSTR(_help)) _CMD_COMPLETE(_comp) }
+
+#define CMD_TBL_ITEM(_name, _maxargs, _rep, _cmd, _usage, _help) \
+ CMD_TBL_ITEM_COMPLETE(_name, _maxargs, _rep, _cmd, \
+ _usage, _help, NULL)
+
+typedef int (*do_cmd_t)(cmd_tbl_t *, int, int, char * const []);
+
+extern cmd_tbl_t cmd_tbl[];
+
+
+#endif /* __COMMAND_H */
diff --git a/avr/command_tbl.c b/avr/command_tbl.c
new file mode 100644
index 0000000..c0579bc
--- /dev/null
+++ b/avr/command_tbl.c
@@ -0,0 +1,76 @@
+
+#include "common.h"
+
+#include "command.h"
+
+
+
+extern int do_help(cmd_tbl_t *, int, int, char * const []);
+extern int do_echo(cmd_tbl_t *, int, int, char * const []);
+extern int do_env_print(cmd_tbl_t *, int, int, char * const []);
+extern int do_env_set(cmd_tbl_t *, int, int, char * const []);
+
+
+cmd_tbl_t cmd_tbl[] = {
+
+CMD_TBL_ITEM(
+ echo, CONFIG_SYS_MAXARGS, 1, do_echo,
+ "echo args to console",
+ "[args..]\n"
+ " - echo args to console; \\c suppresses newline"
+),
+CMD_TBL_ITEM_COMPLETE(
+ run, CONFIG_SYS_MAXARGS, 1, do_run,
+ "run commands in an environment variable",
+ "var [...]\n"
+ " - run the commands in the environment variable(s) 'var'",
+ var_complete
+),
+CMD_TBL_ITEM_COMPLETE(
+ printenv, CONFIG_SYS_MAXARGS, 1, do_env_print,
+ "print environment variables",
+ "\n - print [all] values of all environment variables\n"
+ "printenv name ...\n"
+ " - print value of environment variable 'name'",
+ var_complete
+),
+CMD_TBL_ITEM_COMPLETE(
+ setenv, CONFIG_SYS_MAXARGS, 0, do_env_set,
+ "set environment variables",
+ "[-f] name value ...\n"
+ " - [forcibly] set environment variable 'name' to 'value ...'\n"
+ "setenv [-f] name\n"
+ " - [forcibly] delete environment variable 'name'",
+ var_complete
+),
+CMD_TBL_ITEM(
+ help, CONFIG_SYS_MAXARGS, 1, do_help,
+ "print command description/usage",
+ "\n"
+ " - print brief description of all commands\n"
+ "help command ...\n"
+ " - print detailed usage of 'command'"
+),
+
+/* This does not use the U_BOOT_CMD macro as ? can't be used in symbol names */
+ {FSTR("?"), CONFIG_SYS_MAXARGS, 1, do_help,
+ FSTR("alias for 'help'"),
+#ifdef CONFIG_SYS_LONGHELP
+ FSTR(""),
+#endif /* CONFIG_SYS_LONGHELP */
+#ifdef CONFIG_AUTO_COMPLETE
+ 0,
+#endif
+},
+/* Mark end of table */
+{ 0 },
+};
+
+
+
+
+
+
+
+
+
diff --git a/avr/common.h b/avr/common.h
new file mode 100644
index 0000000..ace0fcb
--- /dev/null
+++ b/avr/common.h
@@ -0,0 +1,57 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+#ifdef __AVR__
+#include <avr/io.h>
+
+//TODO:
+// Known to work: 4.8.4, 4.9.1
+// Known to fail: 4.8.3, 4.9.0
+#define GCC_BUG_61443 1
+
+#else
+// TODO: stm32
+#endif
+
+#include <stdio.h>
+
+#ifdef __FLASH
+#define FLASH __flash
+#else
+#define FLASH
+#endif
+
+#define stringify(s) tostring(s)
+#define tostring(s) #s
+
+#define FSTR(X) ((const FLASH char[]) { X } )
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+
+#ifdef __AVR__
+#define Stat GPIOR0
+#else
+extern volatile uint_least8_t Stat;
+#endif
+
+#define S_10MS_TO (1<<0)
+
+
+#include <util/delay.h>
+
+static inline
+void my_puts(const char *s)
+{
+ fputs(s, stdout);
+// _delay_ms(10);
+}
+
+static inline
+void my_puts_P(const char *s)
+{
+ fputs_P(s, stdout);
+// _delay_ms(10);
+}
+
+#endif /* COMMON_H */
+
diff --git a/avr/con-utils.c b/avr/con-utils.c
new file mode 100644
index 0000000..430ba98
--- /dev/null
+++ b/avr/con-utils.c
@@ -0,0 +1,91 @@
+
+#include <string.h>
+#include <stdio.h>
+
+#include "serial.h"
+#include "con-utils.h"
+
+
+uint_fast8_t tstc(void)
+{
+ return serial_tstc();
+}
+
+int my_getchar(void)
+{
+ int c;
+
+ while((c = serial_getc()) < 0)
+ ;
+ return c;
+}
+
+
+/* test if ctrl-c was pressed */
+
+static uint_fast8_t ctrlc_disabled = 0; /* see disable_ctrl() */
+static uint_fast8_t ctrlc_was_pressed = 0;
+
+uint_fast8_t ctrlc(void)
+{
+ if (!ctrlc_disabled) {
+ switch (serial_getc()) {
+ case 0x03: /* ^C - Control C */
+ ctrlc_was_pressed = 1;
+ return 1;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+/* Reads user's confirmation.
+ Returns 1 if user's input is "y", "Y", "yes" or "YES"
+*/
+uint_fast8_t confirm_yesno(void)
+{
+ unsigned int i;
+ char str_input[5];
+
+ /* Flush input */
+ while (serial_getc())
+ ;
+ i = 0;
+ while (i < sizeof(str_input)) {
+ str_input[i] = my_getchar();
+ putchar(str_input[i]);
+ if (str_input[i] == '\r')
+ break;
+ i++;
+ }
+ putchar('\n');
+ if (strncmp(str_input, "y\r", 2) == 0 ||
+ strncmp(str_input, "Y\r", 2) == 0 ||
+ strncmp(str_input, "yes\r", 4) == 0 ||
+ strncmp(str_input, "YES\r", 4) == 0)
+ return 1;
+ return 0;
+}
+
+/* pass 1 to disable ctrlc() checking, 0 to enable.
+ * returns previous state
+ */
+uint_fast8_t disable_ctrlc(uint_fast8_t disable)
+{
+ uint_fast8_t prev = ctrlc_disabled; /* save previous state */
+
+ ctrlc_disabled = disable;
+ return prev;
+}
+
+uint_fast8_t had_ctrlc (void)
+{
+ return ctrlc_was_pressed;
+}
+
+void clear_ctrlc(void)
+{
+ ctrlc_was_pressed = 0;
+}
+
diff --git a/avr/con-utils.h b/avr/con-utils.h
new file mode 100644
index 0000000..f03dfb3
--- /dev/null
+++ b/avr/con-utils.h
@@ -0,0 +1,22 @@
+#ifndef CON_UTILS_H
+#define CON_UTILS_H
+
+uint_fast8_t tstc(void);
+
+int my_getchar(void);
+
+/* test if ctrl-c was pressed */
+uint_fast8_t ctrlc(void);
+
+
+/* pass 1 to disable ctrlc() checking, 0 to enable.
+ * returns previous state
+ */
+uint_fast8_t disable_ctrlc(uint_fast8_t disable);
+
+uint_fast8_t had_ctrlc (void);
+void clear_ctrlc(void);
+
+#endif /* CON_UTILS_H */
+
+
diff --git a/avr/config.h b/avr/config.h
new file mode 100644
index 0000000..9e6d4d7
--- /dev/null
+++ b/avr/config.h
@@ -0,0 +1,24 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define CONFIG_ENV_SIZE 2000
+#define CONFIG_ENV_OFFSET 0
+#define CONFIG_ENVVAR_MAX 20
+
+#define CONFIG_BOOTDELAY 4
+//#define CONFIG_ZERO_BOOTDELAY_CHECK 1
+
+#define CONFIG_SYS_CBSIZE 250
+#define CONFIG_SYS_ENV_NAMELEN 16
+#define CONFIG_SYS_MAXARGS 8
+
+#define CONFIG_SYS_PROMPT "=> "
+
+
+//#define CONFIG_CMDLINE_EDITING 1
+//#define CONFIG_AUTO_COMPLETE 1
+
+#define CONFIG_SYS_LONGHELP 1
+
+#endif /* CONFIG_H */
+
diff --git a/avr/crc.h b/avr/crc.h
new file mode 100644
index 0000000..c38bae4
--- /dev/null
+++ b/avr/crc.h
@@ -0,0 +1,12 @@
+#ifndef CRC_H
+#define CRC_H
+
+#include <util/crc16.h>
+
+static inline
+uint16_t crc16(uint16_t crc, uint8_t data)
+{
+ return _crc_ccitt_update(crc, data);
+}
+
+#endif /* CRC_H */
diff --git a/avr/debug.h b/avr/debug.h
index ea67a99..1815166 100644
--- a/avr/debug.h
+++ b/avr/debug.h
@@ -2,13 +2,33 @@
#ifndef DEBUG_H_
#define DEBUG_H_
+#include "common.h"
#include <avr/pgmspace.h>
#ifdef DEBUG
+#define _DEBUG 1
+#else
+#define _DEBUG 0
+#endif
+
+#define debug_cond(cond, fmt, args...) \
+ do { \
+ if (cond) \
+ printf_P(PSTR(fmt), ##args); \
+ } while (0)
+
+#define debug(fmt, args...) \
+ debug_cond(_DEBUG, fmt, ##args)
+
+
+#if 1
+#ifdef DEBUG
#define DBG_P(lvl, format, ...) if (DEBUG>=lvl) \
fprintf_P( stdout, PSTR(format), ##__VA_ARGS__ )
#else
#define DBG_P(lvl, ...)
#endif
+#endif /* 0 */
#endif /* DEBUG_H_ */
+
diff --git a/avr/env.c b/avr/env.c
new file mode 100644
index 0000000..97bd2e3
--- /dev/null
+++ b/avr/env.c
@@ -0,0 +1,808 @@
+#include "common.h"
+#include <string.h>
+#include <stdlib.h>
+
+#include <avr/eeprom.h>
+
+#include "config.h"
+#include "debug.h"
+#include "xmalloc.h"
+#include "crc.h"
+#include "command.h"
+
+#include "env.h"
+
+
+
+#define DELIM "\0"
+#define ENV_SIZE CONFIG_ENV_SIZE-2
+
+
+#define ENV_GET_VAL (1<<0)
+
+
+/*
+ * Debugging
+ *
+ * TODO: move elsewhere
+ */
+#include <ctype.h>
+
+static void print_blanks(uint_fast8_t count)
+{
+ while(count--)
+ putchar(' ');
+}
+
+
+void dump_ram(const uint8_t *startaddr, int len, char *title)
+{
+ uint8_t llen = 16;
+ uint8_t pre = (size_t) startaddr % 16;
+ const uint8_t *addr = (uint8_t *) ((size_t) startaddr & ~0x0f);
+ len += pre;
+ uint8_t i;
+
+ if (title && *title)
+ printf_P(PSTR("%s\n"),title);
+
+ while (len) {
+ if (len < 16)
+ llen = len;
+
+ printf_P(PSTR(" %.4x:"), (size_t) addr);
+ print_blanks(3 * pre);
+ for (i = pre; i < llen; i++)
+ printf_P(PSTR(" %.2x"), addr[i]);
+ print_blanks(3 * (16 - i + 1) + pre);
+ for (i = pre; i < llen; i++)
+ printf_P(PSTR("%c"), isprint(addr[i]) ? addr[i] : '.');
+ putchar('\n');
+
+ pre = 0;
+ addr += 16;
+ len -= llen;
+ }
+}
+
+void dump_heap(void)
+{
+ extern unsigned int __brkval;
+
+ dump_ram((uint8_t *) __malloc_heap_start,
+ __brkval - (unsigned int) __malloc_heap_start,
+ "=== Heap:");
+}
+
+/* TODO: combine with dump_ram() */
+void dump_eep(const uint8_t *addr, int len)
+{
+ int i;
+ uint8_t buf[16];
+
+ printf_P(PSTR("eeprom dump:"));
+ while (len) {
+ printf_P(PSTR("\n 0x%.4x:"), (unsigned int) addr);
+ for (i = 0; i<16; i++)
+ buf[i] = eeprom_read_byte(addr + i);
+ for (i = 0; i<16; i++)
+ printf_P(PSTR(" %.2x"), buf[i]);
+ printf_P(PSTR(" "));
+ for (i = 0; i<16; i++)
+ printf_P(PSTR("%c"), isprint(buf[i]) ? buf[i] : '.');
+
+ addr += 16;
+ len -= len > 16 ? 16 : len;
+ }
+ putchar('\n');
+}
+
+
+const FLASH char default_env[] = {
+ "bootdelay=" "3" DELIM
+ "bootcmd=" "echo hallo" DELIM
+ "baudrate=" "115200" DELIM
+ DELIM
+};
+
+
+typedef struct environment_s {
+ uint16_t crc; /* CRC16 over data bytes */
+ char data[ENV_SIZE]; /* Environment data */
+} env_t;
+
+
+
+typedef struct env_item_s {
+#define EF_N_EEP (1<<7)
+#define EF_V_EEP (1<<6)
+#define EF_DIRTY (1<<0)
+ uint8_t flags;
+ union {
+ uint16_t eep;
+ char *ram;
+ } name;
+ union {
+ uint16_t eep;
+ char *ram;
+ } val;
+} env_item_t;
+
+
+static env_item_t env_list[CONFIG_ENVVAR_MAX];
+static int entrycount;
+
+
+static char env_get_char(uint16_t index)
+{
+ unsigned int off = CONFIG_ENV_OFFSET;
+
+ return (char) eeprom_read_byte((const uint8_t *)off + index +
+ offsetof(env_t, data));
+}
+
+
+uint16_t ee_name_get(char *buf, env_item_t *ep)
+{
+ int i = 0;
+ char c;
+
+ if (ep->flags & EF_N_EEP) {
+
+ while ((c = env_get_char(ep->name.eep + i)) != '=' &&
+ c != '\0' && i < CONFIG_SYS_ENV_NAMELEN) {
+
+ buf[i] = c;
+ i++;
+ }
+ if (i > 0 && c != '=') {
+ debug("** ee_name: '%s' not '=' terminated!\n", buf);
+ }
+ } else {
+ strncpy(buf, ep->name.ram, CONFIG_SYS_ENV_NAMELEN);
+ i = strnlen(buf, CONFIG_SYS_ENV_NAMELEN);
+ }
+ buf[i] = '\0';
+ return i;
+}
+
+
+uint16_t ee_val_get(char *buf, uint16_t index, int len)
+{
+ int i = 0;
+ char c;
+
+ while ((c = env_get_char (index + i)) != '\0' && i < len) {
+ buf[i] = c;
+ i++;
+ };
+
+ buf[i] = '\0';
+
+ /* TODO: len check */
+
+ return i;
+}
+
+static
+int comp_env_key_item(const void *key, const void *item)
+{
+ char buf[CONFIG_SYS_ENV_NAMELEN+1];
+ env_item_t *ep = (env_item_t *) item;
+
+ ee_name_get(buf, ep);
+
+ return strcmp((char *) key, buf);
+}
+
+static
+int comp_env_items(const void *m1, const void *m2)
+{
+ char b1[CONFIG_SYS_ENV_NAMELEN+1];
+ char b2[CONFIG_SYS_ENV_NAMELEN+1];
+
+ env_item_t *ep1 = (env_item_t *) m1;
+ env_item_t *ep2 = (env_item_t *) m2;
+
+ ee_name_get(b1, ep1);
+ ee_name_get(b2, ep2);
+
+ return strcmp(b1, b2);
+}
+
+#if 0
+env_item_t * ee_entry_get(const char *s, env_item_t *ep, uint_fast8_t flag)
+{
+ char name[CONFIG_SYS_ENV_NAMELEN+1];
+ uint16_t idx = 0;
+ int nlen;
+
+//printf_P(PSTR("*** ee_entry_get: >>>>>>>>> ENTER <<<<<<<<<\n"));
+
+
+ while ((nlen = ee_name_get(name, idx)) != 0 && idx < CONFIG_ENV_SIZE) {
+
+//printf_P(PSTR("** idx: %d, name: '%s', s: '%s', nlen: %d, cpres:%d\n"),
+// idx, name, s, nlen, strncmp(name, s, nlen));
+
+ if (strncmp(name, s, nlen) == 0)
+ break;
+
+ idx += nlen + 1;
+ while (env_get_char(idx++) != 0)
+ ;
+ }
+
+ if (nlen) {
+ char *vp;
+ ep->flags = EF_N_EEP;
+ ep->name.eep = idx;
+ if (flag & ENV_GET_VAL) {
+ vp = xmalloc(CONFIG_SYS_CBSIZE);
+ nlen = ee_val_get(vp, idx + nlen + 1, CONFIG_SYS_CBSIZE);
+ ep->val = xrealloc(vp, nlen + 1);
+ } else
+ ep->val = NULL;
+
+
+//printf_P(PSTR("*** ee_entry_get: >>>>>> LEAVE 0x%.4x <<<<<\n"),
+// (unsigned int) ep);
+ return ep;
+ }
+
+//printf_P(PSTR("*** ee_entry_get: >>>>>> LEAVE <NULL> <<<<<\n"));
+ return NULL;
+}
+#endif
+
+#if 0
+static char p_env_name_buf[CONFIG_SYS_ENV_NAMELEN+1];
+
+static char *dbg_p_env_name(env_item_t *p)
+{
+ if (p->flags & EF_N_EEP) {
+ if (ee_name_get(p_env_name_buf, p) != 0)
+ return p_env_name_buf;
+ else
+ return "<NULL>";
+ }
+ return "<NO EEP_NAME>";
+}
+#endif
+
+int env_item_print(env_item_t *ep)
+{
+ char buf[CONFIG_SYS_ENV_NAMELEN+1];
+ int len;
+ env_item_t e = *ep;
+
+ ee_name_get(buf, ep);
+ len = printf_P(PSTR("%s="), buf);
+
+ if (e.val.ram != NULL) {
+ while (1) {
+ char c;
+ if (e.flags & EF_V_EEP)
+ c = env_get_char(e.val.eep++);
+ else
+ c = *e.val.ram++;
+
+ if (c != '\0') {
+ putchar(c);
+ len++;
+ } else
+ break;
+ }
+ }
+ putchar('\n');
+ len ++;
+
+ return len;
+}
+
+
+static env_item_t *envlist_search(const char *name)
+{
+ return bsearch(name, env_list, entrycount,
+ sizeof(env_item_t), comp_env_key_item);
+}
+
+
+env_item_t *envlist_insert(const char *key, env_item_t *e)
+{
+ const size_t size = sizeof(env_item_t);
+
+ if (entrycount < CONFIG_ENVVAR_MAX) {
+ env_list[entrycount++] = *e;
+ qsort(env_list, entrycount, size, comp_env_items);
+
+ return bsearch(key, env_list, entrycount,
+ size, comp_env_key_item);
+
+ } else
+ return NULL;
+}
+
+
+env_item_t *envlist_enter(env_item_t *e)
+{
+ char *key = e->name.ram;
+ const size_t size = sizeof(env_item_t);
+ env_item_t *ep;
+
+ ep = bsearch(key, env_list, entrycount,
+ size, comp_env_key_item);
+
+ if (ep == NULL) {
+ if (entrycount < CONFIG_ENVVAR_MAX) {
+
+ env_list[entrycount++] = *e;
+ qsort(env_list, entrycount, size, comp_env_items);
+ ep = bsearch(key, env_list, entrycount,
+ size, comp_env_key_item);
+ }
+ } else {
+ if ((ep->flags & EF_V_EEP) == 0) {
+ free(ep->val.ram);
+ }
+ ep->val.ram = e->val.ram;
+ }
+
+ if (ep != NULL) {
+ ep->flags |= EF_DIRTY;
+ ep->flags &= ~EF_V_EEP;
+
+ if ((ep->flags & EF_N_EEP) == 0) {
+ int nlen = strnlen(key, CONFIG_SYS_ENV_NAMELEN);
+ char *name = xmalloc(nlen + 1);
+ if (name == NULL) {
+ printf_P(PSTR("## Can't malloc %d bytes\n"),
+ nlen + 1);
+ return NULL;
+ }
+ strcpy(name, key);
+ name[nlen] = '\0';
+ ep->name.ram = name;
+ }
+ }
+
+ return ep;
+}
+
+
+static env_item_t *envlist_get(const char *name, uint_fast8_t flag)
+{
+ env_item_t *ep;
+
+ ep = envlist_search(name);
+
+ if (ep != NULL && (flag & ENV_GET_VAL)) {
+ if (ep->flags & EF_V_EEP) {
+ char *vp;
+ uint_fast8_t len;
+ vp = xmalloc(CONFIG_SYS_CBSIZE);
+ len = ee_val_get(vp, ep->val.eep, CONFIG_SYS_CBSIZE);
+ ep->val.ram = xrealloc(vp, len + 1);
+ ep->flags &= ~EF_V_EEP;
+ }
+ }
+
+ return ep;
+}
+
+
+static int envlist_delete(const char *name)
+{
+ size_t size = sizeof(env_item_t);
+ env_item_t *ep = bsearch(name, env_list, entrycount,
+ sizeof(env_item_t), comp_env_key_item);
+ int rc = 0;
+
+ if (ep != NULL) {
+
+ if ((ep->flags & EF_V_EEP) == 0)
+ free(ep->val.ram);
+ if ((ep->flags & EF_N_EEP) == 0)
+ free(ep->name.ram);
+
+ entrycount--;
+printf_P(PSTR("*** envlist_delete memmove: 0x%.4x, 0x%.4x, %d\n"),
+ (unsigned) ep, (unsigned) ep + size,
+ (env_list - ep)*size + entrycount*size);
+ memmove(ep, ep + 1, (env_list - ep)*size + entrycount*size);
+
+ rc = 1;
+ }
+#if 1
+ dump_ram((uint8_t *) &env_list[0], entrycount * sizeof(env_item_t),
+ "=== env_list:");
+ dump_heap();
+ debug("** entrycount: %d\n", entrycount);
+ for (int i=0; i<entrycount; i++) {
+ printf_P(PSTR("** env var [%d] "), i);
+ env_item_print(&env_list[i]);
+ }
+#endif
+
+ return rc;
+}
+
+
+char *getenv(const char *name)
+{
+ env_item_t *ep;
+ char *ret = NULL;
+
+ debug("** getenv: %s\n", name);
+
+ ep = envlist_get(name, ENV_GET_VAL);
+ if (ep != NULL)
+ ret = ep->val.ram;
+
+#if 1
+ dump_ram((uint8_t *) &env_list[0], entrycount * sizeof(env_item_t),
+ "=== env_list:");
+ dump_heap();
+ debug("** entrycount: %d\n", entrycount);
+ for (int i=0; i<entrycount; i++) {
+ printf_P(PSTR("** env var [%d] "), i);
+ env_item_print(&env_list[i]);
+ }
+#endif
+
+ return ret;
+}
+
+int saveenv(void)
+{
+ int rc = 0;
+
+
+// rc = env_export(&env_new);
+ if (rc)
+ return rc;
+
+
+// rc = eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
+// off, (uchar *)&env_new, CONFIG_ENV_SIZE);
+
+ return rc;
+}
+
+
+/*-----------------------------------------------------------------------------*/
+
+
+int set_default_env(void)
+{
+ char buf[64];
+ uint16_t crc = 0xffff;
+ uint16_t eep = CONFIG_ENV_OFFSET + offsetof(env_t, data);
+ unsigned int len = CONFIG_ENV_SIZE - offsetof(env_t, data);
+ unsigned int i, src = 0;
+ char c = 0xff, c0 = c;
+
+printf_P(PSTR("\n\n** set_default_env()\n"));
+
+ while (len) {
+
+ memcpy_P(buf, default_env+src, sizeof(buf));
+
+ for (i=0; i < (len < sizeof(buf) ? len : sizeof(buf)) &&
+ !(c == 0 && c0 == 0);
+ i++)
+ {
+ c0 = c; c = buf[i];
+ crc = crc16(crc, c);
+ }
+
+ eeprom_update_block(buf, (char *) eep, i);
+/**/ printf_P(PSTR("eeprom_update_block: eep: 0x%.4x, i:%d\n"),
+ (unsigned int) eep, i);
+/**/ dump_ram((uint8_t *) buf, i, "=== buf:");
+/**/ dump_eep((const uint8_t *) eep, i);
+
+ if (c == 0 && c0 == 0)
+ len = 0;
+ if (len > sizeof(buf))
+ len -= sizeof(buf);
+ src += sizeof(buf);
+ eep += sizeof(buf);
+ }
+
+printf_P(PSTR("** crc adr: 0x%.4x, crc: 0x%.4x\n"),
+ (unsigned int) (uint16_t *) CONFIG_ENV_OFFSET + offsetof(env_t,crc), crc);
+
+ eeprom_update_word((uint16_t *) CONFIG_ENV_OFFSET + offsetof(env_t,crc), crc);
+
+/**/ dump_eep(0, 128);
+ return 0;
+}
+
+
+int env_check(void)
+{
+ uint16_t crc, stored_crc;
+ uint16_t i;
+ char c, c0;
+
+ debug("\n\n** env_check()\n");
+
+
+ /* read old CRC */
+ stored_crc = eeprom_read_word((const uint16_t *) CONFIG_ENV_OFFSET +
+ offsetof(env_t, crc));
+ crc = 0xffff;
+ c = 0xff;
+ for (i = offsetof(env_t, data);
+ !(c == 0 && c0 == 0) && i < CONFIG_ENV_SIZE;
+ i++)
+ {
+ c0 = c;
+ c = eeprom_read_byte((const uint8_t *) CONFIG_ENV_OFFSET + i);
+ crc = crc16(crc, c);
+ }
+ debug("** crc eep: 0x%.4x, crc new: 0x%.4x\n", stored_crc, crc);
+
+ return crc == stored_crc;
+}
+
+int env_init(void)
+{
+ char name[CONFIG_SYS_ENV_NAMELEN+1];
+ uint16_t idx = 0;
+ int nlen;
+ env_item_t e;
+
+ e.flags = EF_N_EEP | EF_V_EEP;
+ e.name.eep = idx;
+ while ((nlen = ee_name_get(name, &e)) != 0 && idx < ENV_SIZE) {
+
+ if (entrycount <= CONFIG_ENVVAR_MAX) {
+ e.val.eep = idx + nlen + 1;
+
+ env_list[entrycount++] = e;
+
+ idx += nlen + 1;
+ while (env_get_char(idx++) != 0 && idx < ENV_SIZE)
+ ;
+ e.name.eep = idx;
+ } else {
+ debug("** Too many environment variables!\n");
+ break;
+ }
+ }
+ qsort(env_list, entrycount, sizeof(env_item_t), comp_env_items);
+
+ return 0;
+}
+
+
+/*
+ * Command interface: print one or all environment variables
+ *
+ * Returns -1 in case of error, or length of printed string
+ */
+static int env_print(char *name)
+{
+ int len = -1;
+
+ if (name) { /* print a single name */
+
+ env_item_t *ep = envlist_search(name);
+ if (ep != NULL)
+ len = env_item_print(ep);
+
+ } else { /* print whole list */
+ len = 0;
+ for (int i = 0 ; i < entrycount; i++) {
+ len += env_item_print(&env_list[i]);
+ }
+ }
+ return len;
+}
+
+
+
+int do_env_print(cmd_tbl_t *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ int i;
+ int rcode = 0;
+
+ (void) cmdtp; (void) flag;
+
+ if (argc == 1) {
+ /* print all env vars */
+ rcode = env_print(NULL);
+ if (rcode < 0)
+ return 1;
+ printf_P(PSTR("\nEnvironment size: %d/%d bytes\n"),
+ rcode, ENV_SIZE);
+ return 0;
+ }
+
+ /* print selected env vars */
+ for (i = 1; i < argc; ++i) {
+ int rc = env_print(argv[i]);
+ if (rc < 0) {
+ printf_P(PSTR("## Error: \"%s\" not defined\n"), argv[i]);
+ ++rcode;
+ }
+ }
+
+ return rcode;
+}
+
+
+/*
+ * Set a new environment variable,
+ * or replace or delete an existing one.
+ */
+static int _do_env_set(int flag, int argc, char * const argv[])
+{
+ int i, len;
+ char *name, *value, *s;
+ env_item_t e, *ep;
+
+ (void) flag;
+ debug("Initial value for argc=%d\n", argc);
+
+ name = argv[1];
+ value = argv[2];
+
+ if (strchr(name, '=')) {
+ printf_P(PSTR("## Error: illegal character '='"
+ "in variable name \"%s\"\n"), name);
+ return 1;
+ }
+ if (strlen(name) > CONFIG_SYS_ENV_NAMELEN) {
+ printf_P(PSTR("## Error: Variable name \"%s\" too long. "
+ "(max %d characters)\n"), name, CONFIG_SYS_ENV_NAMELEN);
+ return 1;
+ }
+/*
+ env_id++;
+*/
+ /* Delete only ? */
+ if (argc < 3 || argv[2] == NULL) {
+ int rc = envlist_delete(name);
+ return !rc;
+ }
+
+ /*
+ * Insert / replace new value
+ */
+ for (i = 2, len = 0; i < argc; ++i)
+ len += strlen(argv[i]) + 1;
+
+ value = xmalloc(len);
+ if (value == NULL) {
+ printf_P(PSTR("## Can't malloc %d bytes\n"), len);
+ return 1;
+ }
+ for (i = 2, s = value; i < argc; ++i) {
+ char *v = argv[i];
+
+ while ((*s++ = *v++) != '\0')
+ ;
+ *(s - 1) = ' ';
+ }
+ if (s != value)
+ *--s = '\0';
+
+ e.flags = EF_DIRTY;
+ e.name.ram = name;
+ e.val.ram = value;
+ ep = envlist_enter(&e);
+ if (!ep) {
+ printf_P(PSTR("## Error inserting \"%s\" variable.\n"),
+ name);
+ return 1;
+ }
+
+ return 0;
+}
+
+int setenv(const char *varname, const char *varvalue)
+{
+ const char * const argv[3] = { NULL, varname, varvalue };
+ int argc = 3;
+
+ if (varvalue == NULL || varvalue[0] == '\0')
+ --argc;
+
+ return _do_env_set(0, argc, (char * const *)argv);
+}
+
+#if 0
+/**
+ * Set an environment variable to an integer value
+ *
+ * @param varname Environment variable to set
+ * @param value Value to set it to
+ * @return 0 if ok, 1 on error
+ */
+int setenv_ulong(const char *varname, unsigned long value)
+{
+ /* TODO: this should be unsigned */
+ char *str = simple_itoa(value);
+
+ return setenv(varname, str);
+}
+#endif
+
+
+/**
+ * Set an environment variable to an value in hex
+ *
+ * @param varname Environment variable to set
+ * @param value Value to set it to
+ * @return 0 if ok, 1 on error
+ */
+int setenv_hex(const char *varname, unsigned long value)
+{
+ char str[sizeof(unsigned long) *2 + 1];
+
+ sprintf_P(str, PSTR("%lx"), value);
+ return setenv(varname, str);
+}
+
+unsigned long getenv_hex(const char *varname, unsigned long default_val)
+{
+ const char *s;
+ unsigned long value;
+ char *endp;
+
+ s = getenv(varname);
+ if (s)
+ value = strtoul(s, &endp, 16);
+ if (!s || endp == s)
+ return default_val;
+
+ return value;
+}
+
+int do_env_set(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ (void) cmdtp;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ return _do_env_set(flag, argc, argv);
+}
+
+
+#if defined(CONFIG_AUTO_COMPLETE)
+int env_complete(char *var, int maxv, char *cmdv[], int bufsz, char *buf)
+{
+ ENTRY *match;
+ int found, idx;
+
+ idx = 0;
+ found = 0;
+ cmdv[0] = NULL;
+
+ while ((idx = hmatch_r(var, idx, &match, &env_htab))) {
+ int vallen = strlen(match->key) + 1;
+
+ if (found >= maxv - 2 || bufsz < vallen)
+ break;
+
+ cmdv[found++] = buf;
+ memcpy(buf, match->key, vallen);
+ buf += vallen;
+ bufsz -= vallen;
+ }
+
+ qsort(cmdv, found, sizeof(cmdv[0]), strcmp_compar);
+
+ if (idx)
+ cmdv[found++] = "...";
+
+ cmdv[found] = NULL;
+ return found;
+}
+#endif
+
diff --git a/avr/env.h b/avr/env.h
new file mode 100644
index 0000000..ac66846
--- /dev/null
+++ b/avr/env.h
@@ -0,0 +1,12 @@
+#ifndef ENV_H
+#define ENV_H
+
+int set_default_env(void);
+int env_check(void);
+int env_init(void);
+
+char *getenv(const char *name);
+int env_complete(char *var, int maxv, char *cmdv[], int maxsz, char *buf);
+
+#endif /* ENV_H */
+
diff --git a/avr/serial.c b/avr/serial.c
index 2d2c551..b2fea51 100644
--- a/avr/serial.c
+++ b/avr/serial.c
@@ -106,7 +106,7 @@ int serial_getc(void)
return ring_read_ch(&rx_ring);
}
-void serial_putc(uint8_t data)
+void serial_putc(char data)
{
while (ring_write_ch(&tx_ring, data) < 0)
;
@@ -115,3 +115,9 @@ void serial_putc(uint8_t data)
UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0) | _BV(UDRIE0);
}
+uint_fast8_t serial_tstc(void)
+{
+ return !ring_is_empty(&rx_ring);
+}
+
+
diff --git a/avr/serial.h b/avr/serial.h
index 1a0f510..7414ef1 100644
--- a/avr/serial.h
+++ b/avr/serial.h
@@ -2,7 +2,8 @@
#define SERIAL_H
void serial_setup(void);
-void serial_putc(uint8_t);
+void serial_putc(char);
int serial_getc(void);
+uint_fast8_t serial_tstc(void);
#endif /* SERIAL_H */
diff --git a/avr/timer.c b/avr/timer.c
new file mode 100644
index 0000000..84a9737
--- /dev/null
+++ b/avr/timer.c
@@ -0,0 +1,87 @@
+/*
+ */
+
+
+#include "common.h"
+
+//#include <avr/power.h>
+//#include <avr/pgmspace.h>
+#include <avr/interrupt.h>
+#include <util/atomic.h>
+
+//#include <stdio.h>
+
+
+#include "timer.h"
+
+/* timer interrupt/overflow counter */
+static volatile uint32_t timestamp;
+
+
+/*---------------------------------------------------------*/
+/* 1000Hz timer interrupt generated by OC1A */
+/*---------------------------------------------------------*/
+
+ISR(TIMER1_COMPA_vect)
+{
+ static int_fast8_t tick_10ms;
+ int_fast8_t i;
+
+
+ timestamp++;
+
+ i = tick_10ms + 1;
+ if (i == 10) {
+ i = 0;
+ Stat |= S_10MS_TO;
+
+ /* Drive timer procedure of low level disk I/O module */
+ //disk_timerproc();
+ }
+ tick_10ms = i;
+
+}
+
+
+
+/*--------------------------------------------------------------------------*/
+
+#if 0
+
+void do_10ms(void)
+{
+ if (to_counter)
+ to_counter--;
+}
+
+#endif
+
+/*--------------------------------------------------------------------------*/
+
+
+void timer_setup(void)
+{
+
+ /* Clock */
+ CLKPR = _BV(CLKPCE);
+ CLKPR = 0;
+
+ /* Timer */
+
+ OCR1A = F_CPU / 1000 - 1; // Timer1: 1000Hz interval (OC1A)
+ TCCR1B = 0b00001001;
+ TIMSK1 = _BV(OCIE1A); // Enable TC1.oca interrupt
+}
+
+
+uint32_t get_timer(uint32_t base)
+{
+ uint32_t ret;
+
+ ATOMIC_BLOCK(ATOMIC_FORCEON)
+ {
+ ret = timestamp;
+ }
+ return ret - base;
+}
+
diff --git a/avr/timer.h b/avr/timer.h
new file mode 100644
index 0000000..bed3eb0
--- /dev/null
+++ b/avr/timer.h
@@ -0,0 +1,7 @@
+#ifndef TIMER_H
+#define TIMER_H
+
+uint32_t get_timer(uint32_t);
+
+#endif /* TIMER_H */
+
diff --git a/avr/xmalloc.c b/avr/xmalloc.c
new file mode 100644
index 0000000..9bf2684
--- /dev/null
+++ b/avr/xmalloc.c
@@ -0,0 +1,27 @@
+#include <stdlib.h>
+
+#include "debug.h"
+#include "xmalloc.h"
+
+void* xmalloc(size_t size)
+{
+ void *p;
+
+ p = malloc(size);
+
+ if (p == NULL)
+ debug("*** Out of memory!\n");
+
+ return p;
+}
+
+
+void* xrealloc(void *p, size_t size)
+{
+ p = realloc(p, size);
+
+ if (p == NULL)
+ debug("*** Out of memory!\n");
+
+ return p;
+}
diff --git a/avr/xmalloc.h b/avr/xmalloc.h
new file mode 100644
index 0000000..cb0019f
--- /dev/null
+++ b/avr/xmalloc.h
@@ -0,0 +1,8 @@
+
+#ifndef XMALLOC_H
+#define XMALLOC_H
+
+void* xmalloc(size_t size);
+void* xrealloc(void *p, size_t size);
+
+#endif /* XMALLOC_H */
diff --git a/avr/z180-stamp-avr.c b/avr/z180-stamp-avr.c
index 09f323c..b5a3fb8 100644
--- a/avr/z180-stamp-avr.c
+++ b/avr/z180-stamp-avr.c
@@ -242,13 +242,13 @@ const FLASH struct msg_item z80_messages[] =
{
{ 0, /* fct nr. */
0, 0, /* sub fct nr. from, to */
- &do_msg_ini_msgfifo},
+ do_msg_ini_msgfifo},
{ 0,
1, 2,
- &do_msg_ini_memfifo},
+ do_msg_ini_memfifo},
{ 1,
1, 1,
- &do_msg_char_out},
+ do_msg_char_out},
{ 0xff, /* end mark */
0, 0,
0},
diff --git a/configs/debug.config b/configs/debug.config
index 04be440..1a54f76 100644
--- a/configs/debug.config
+++ b/configs/debug.config
@@ -1 +1 @@
-CONFIG_DEBUG=2
+CONFIG_DEBUG=0
diff --git a/configs/gcc.tup b/configs/gcc.tup
new file mode 100644
index 0000000..ad2d788
--- /dev/null
+++ b/configs/gcc.tup
@@ -0,0 +1,40 @@
+CC = $(TOOLCHAIN)-gcc
+LD = $(TOOLCHAIN)-gcc
+AR = $(TOOLCHAIN)-ar
+AS = $(TOOLCHAIN)-as
+OBJCOPY = $(TOOLCHAIN)-objcopy
+OBJDUMP = $(TOOLCHAIN)-objdump
+SIZE = $(TOOLCHAIN)-size
+GDB = $(TOOLCHAIN)-gdb
+
+
+CFLAGS += -g -Os
+CFLAGS += -std=gnu99
+CFLAGS += -Wall -Wextra
+CFLAGS += -Wredundant-decls
+#CFLAGS += -fno-common -ffunction-sections -fdata-sections
+
+
+LDFLAGS += -Wl,--gc-sections
+LDFLAGS += -Wl,--cref
+
+ifneq ($(LDSCRIPT),)
+LDFLAGS += -T$(LDSCRIPT)
+endif
+
+
+!cc = |> ^ CC %f^ $(CC) $(CFLAGS) $(CPPFLAGS) -c %f -o %o |> %B.o
+!LINK = |> ^ LINK %o^ $(LD) $(CFLAGS) $(LDFLAGS) -Wl,-Map=%O.map %f $(LDLIBS) -o %o |> | %O.map
+!OBJCOPY= |> ^ OBJCOPY %o^ $(OBJCOPY) -Oihex %f %o |>
+!OBJDUMP= |> ^ OBJDUMP %o^ $(OBJDUMP) -h -S %f > %o |> %O.lss
+!SIZE = |> ^ SIZE %f^ $(SIZE) %f |>
+
+
+: foreach $(SRC) | $(PREDEP) |> !cc |> {objs}
+: $(SRC_Z) |> !cc $(CPPFLAGS_Z) |> {objs}
+
+: {objs} |> !LINK |> $(PROG).elf {elf}
+: {elf} |> !OBJCOPY |> %B.hex {aux}
+: {elf} |> !OBJDUMP |> %B.lss {aux}
+: {elf} | {aux} |> !SIZE |>
+