summaryrefslogtreecommitdiff
path: root/avr
diff options
context:
space:
mode:
Diffstat (limited to 'avr')
-rw-r--r--avr/Makefile114
-rw-r--r--avr/background.c60
-rw-r--r--avr/bcd.c21
-rw-r--r--avr/cli.c433
-rw-r--r--avr/cli_readline.c697
-rw-r--r--avr/cmd_attach.c126
-rw-r--r--avr/cmd_boot.c466
-rw-r--r--avr/cmd_cpu.c571
-rw-r--r--avr/cmd_date.c176
-rw-r--r--avr/cmd_fat.c664
-rw-r--r--avr/cmd_gpio.c326
-rw-r--r--avr/cmd_loadcpm3.c195
-rw-r--r--avr/cmd_loadihex.c216
-rw-r--r--avr/cmd_mem.c774
-rw-r--r--avr/cmd_misc.c110
-rw-r--r--avr/cmd_run.c86
-rw-r--r--avr/cmd_sd.c373
-rw-r--r--avr/command.c812
-rw-r--r--avr/command_tbl.c402
-rw-r--r--avr/con-utils.c126
-rw-r--r--avr/debug.c257
-rw-r--r--avr/env.c881
-rw-r--r--avr/eval_arg.c154
-rw-r--r--avr/getopt-min.c75
-rw-r--r--avr/gpio.c376
-rw-r--r--avr/i2c.c377
-rw-r--r--avr/main.c276
-rw-r--r--avr/mmc.c807
-rw-r--r--avr/pcf8583.c105
-rw-r--r--avr/print-utils.c97
-rw-r--r--avr/serial.c123
-rw-r--r--avr/strerror.c38
-rw-r--r--avr/timer.c70
-rw-r--r--avr/z180-serv.c826
-rw-r--r--avr/z80-if.c835
35 files changed, 12045 insertions, 0 deletions
diff --git a/avr/Makefile b/avr/Makefile
new file mode 100644
index 0000000..89d13a0
--- /dev/null
+++ b/avr/Makefile
@@ -0,0 +1,114 @@
+
+
+MCU := atmega1281
+F_CPU := 18432000
+DEBUG := 1
+
+TARGET = stamp-monitor
+
+###############################################################################
+
+SRC := main.c
+SRC += cli.c cli_readline.c command.c command_tbl.c
+SRC += cmd_run.c cmd_boot.c cmd_misc.c cmd_cpu.c
+SRC += cmd_date.c cmd_mem.c cmd_gpio.c cmd_attach.c
+SRC += cmd_loadihex.c cmd_loadcpm3.c cmd_sd.c cmd_fat.c
+SRC += env.c con-utils.c print-utils.c getopt-min.c eval_arg.c
+SRC += timer.c serial.c i2c.c bcd.c pcf8583.c mmc.c strerror.c
+SRC += background.c z180-serv.c z80-if.c gpio.c
+
+FATFSDIR := ../fatfs/source
+FATFS := $(FATFSDIR)/ff.c $(FATFSDIR)/ffunicode.c
+ZDIR := ../z180
+SRC_Z := $(ZDIR)/hdrom.c $(ZDIR)/cfboot.c $(ZDIR)/cpuinfo.c
+
+# Sources that may depend on Z180 code:
+Z_DEP := $(shell grep -sl 'include[[:blank:]]\+"$(ZDIR)' $(SRC))
+
+SRC += $(FATFS)
+SRC += $(SRC_Z)
+
+#$(FATFS:%.c=%.o): CFLAGS += -fno-strict-aliasing
+$(SRC_Z:%.c=%.o): CFLAGS += -D'const=const __flash'
+
+
+# Place -D or -U options here
+#CDEFS +=
+
+ifdef DEBUG
+SRC += debug.c
+CDEFS += -DDEBUG=2
+endif
+
+# Place -I options here
+CINCS := -I../include
+
+# Extra C flags
+#TGT_CFLAGS += -funsigned-char
+TGT_CFLAGS += -fno-move-loop-invariants
+TGT_CFLAGS += -fno-tree-loop-optimize
+#TGT_CFLAGS += -fno-split-wide-types
+#TGT_CFLAGS += -flto
+
+
+###############################################################################
+# Programming support using avrdude.
+
+AVRDUDE_PROGRAMMER := dragon_isp
+# Uncomment, if the programmer is /not/ on USB.
+#AVRDUDE_PORT :=
+
+LFUSE = 0x97
+HFUSE = 0xD6
+EFUSE = 0xF5
+
+# Fuse low byte:
+# 0x97 = 1 0 0 1 0 1 1 1
+# ^ ^ \ / \--+--/
+# | | | +------- CKSEL 3..0 (Low Power Crystal)
+# | | +--------------- SUT 1..0 (Crystal Osc, slowly rising power)
+# | +------------------ CKOUT (Clock output enabled)
+# +-------------------- CKDIV8 (don't divide clock by 8)
+# Fuse high byte:
+# 0xd6 = 1 1 0 1 0 1 1 0
+# ^ ^ ^ ^ ^ \+/ ^---- BOOTRST (Boot Reset vector Enabled 0x3c00)
+# | | | | | +------- BOOTSZ 1..0 (Boot Flash section size 512 words)
+# | | | | + --------- EESAVE (Preserve EEPROM over chip erase)
+# | | | +-------------- WDTON (WDT not always on)
+# | | +---------------- SPIEN (allow serial programming)
+# | +------------------ JTAGEN (JTAG disabled)
+# +-------------------- OCDEN (OCD disabled)
+# Fuse Extended byte:
+# 0xf5 = 1 1 1 1 0 1 0 1
+# \-+-/
+# +------ Bodlevel (VCC = 2.7V)
+
+# Uncomment the following if you do /not/ wish a verification to be
+# performed after programming the device.
+#AVRDUDE_NO_VERIFY = -V
+
+# Increase verbosity level. Please use this when submitting bug
+# reports about avrdude. See <http://savannah.nongnu.org/projects/avrdude>
+# to submit bug reports.
+#AVRDUDE_VERBOSE = -v -v
+
+###############################################################################
+#.SECONDEXPANSION:
+
+.PHONY: all build
+# Default target.
+all: build
+
+build: hex lss
+
+clean: clean_z
+$(Z_DEP:%.c=%.o): $(SRC_Z)
+
+$(SRC_Z):
+ $(MAKE) -C $(ZDIR) $(@F)
+
+.PHONY: clean_z
+clean_z:
+ $(MAKE) -C $(ZDIR) clean
+
+include ../mk/avr.rules.mk
diff --git a/avr/background.c b/avr/background.c
new file mode 100644
index 0000000..9c8b5a6
--- /dev/null
+++ b/avr/background.c
@@ -0,0 +1,60 @@
+/*
+ * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "common.h"
+#include "background.h"
+
+
+#define BG_FUNC_MAX 5
+
+static struct {
+ bg_func fct;
+ int param;
+} func_tab[BG_FUNC_MAX];
+
+static int_fast8_t fcount;
+
+int bg_register(bg_func f, int initval)
+{
+ if (fcount < BG_FUNC_MAX) {
+ func_tab[fcount].fct = f;
+ func_tab[fcount].param = initval;
+ return ++fcount - 1;
+ }
+ return -1;
+}
+
+int bg_setstat(int handle, int val)
+{
+ if (handle < fcount) {
+ func_tab[handle].param = val;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int bg_getstat(int handle)
+{
+ if (handle < fcount) {
+ return func_tab[handle].param;
+ }
+ return 0;
+}
+
+
+void bg_shed(void)
+{
+ static int_fast8_t current;
+
+ if (func_tab[current].fct) {
+ int v = func_tab[current].fct(func_tab[current].param);
+ func_tab[current].param = v;
+ }
+ if (++current >= fcount)
+ current = 0;
+}
diff --git a/avr/bcd.c b/avr/bcd.c
new file mode 100644
index 0000000..5154852
--- /dev/null
+++ b/avr/bcd.c
@@ -0,0 +1,21 @@
+/*
+ * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "stdlib.h"
+#include "stdint.h"
+#include "bcd.h"
+
+uint_fast8_t bcd2bin(uint8_t val)
+{
+ return (val >> 4) * 10 + (val & 0x0f);
+}
+
+uint8_t bin2bcd (uint_fast8_t val)
+{
+ div_t d = div(val, 10);
+
+ return (d.quot << 4) | d.rem;
+}
diff --git a/avr/cli.c b/avr/cli.c
new file mode 100644
index 0000000..3e711b3
--- /dev/null
+++ b/avr/cli.c
@@ -0,0 +1,433 @@
+/*
+ * (C) Copyright 2014-2016 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * (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 "cli.h"
+#include "command.h"
+
+#include "config.h"
+#include "debug.h"
+#include "env.h"
+#include "cli_readline.h"
+#include "con-utils.h"
+
+
+/* FIXME: Quoting problems */
+
+#define DEBUG_PARSER 0 /* set to 1 to debug */
+
+#define debug_parser(fmt, args...) \
+ debug_cond(DEBUG_PARSER, fmt, ##args)
+
+
+static int_least8_t exec_flags;
+#define OPT_XTRACE 1
+#define OPT_VERBOSE 2
+
+static int_least8_t command_level;
+
+static void cli_trace_cmd(int_fast8_t level, int argc, char *argv[])
+{
+ while (level-- > 0)
+ putchar('+');
+ for (int_fast8_t i = 0; i < argc; i++)
+ printf_P(PSTR(" %s"), argv[i]);
+ putchar('\n');
+}
+
+
+
+
+static int cli_parse_line(char *line, char *argv[])
+{
+ uint_fast8_t state = 0;
+ uint_fast8_t nargs = 0;
+ char *inp, *outp;
+ char c, quote;
+
+ debug_parser("%s: \"%s\"\n", __func__, line);
+
+ for (outp = inp = line, quote = '\0'; (c = *inp) != '\0'; inp++) {
+
+ switch (state) {
+ case 0: /* before arg string, waiting for arg start */
+ if (isblank(c))
+ continue;
+
+ argv[nargs++] = inp; /* begin of argument string */
+ outp = inp;
+ state = 1;
+ /* fall thru */
+
+ case 1: /* in arg string, waiting for end of arg string */
+ if (c == '\\') {
+ ++state;
+ continue;
+ }
+ if (c == '\"' || c == '\'') {
+ quote = c;
+ state = 3;
+ continue;
+ }
+ if (isblank(c)) {
+ c = '\0';
+ state = 0;
+ }
+ break;
+
+ case 3: /* in quote */
+ if (c == '\\' && quote == '\"') {
+ ++state;
+ continue;
+ }
+ if (c == quote) {
+ state = 1;
+ continue;
+ }
+ break;
+
+ case 2: /* waiting for next char */
+ case 4:
+ --state;
+ break;
+
+ }
+
+ if (nargs > CONFIG_SYS_MAXARGS) {
+ --nargs;
+ break;
+ }
+ *outp++ = c;
+ }
+
+ if (*inp != '\0')
+ printf_P(PSTR("** Too many args (max. %d) **\n"), CONFIG_SYS_MAXARGS);
+
+ *outp = '\0';
+ argv[nargs] = NULL;
+ debug_parser("%s: nargs=%d\n", __func__, nargs);
+#if 0
+ for (int i = 0; i < nargs; i++)
+ debug_parser("%s: arg %d: >%s<\n", __func__, i, argv[i]);
+#endif
+ return nargs;
+
+}
+
+static
+void append_char(uint_fast8_t pass, char **p, char c)
+{
+
+ if (pass) {
+ **p = c;
+ }
+ ++(*p);
+}
+
+static
+char *process_macros(char *input)
+{
+ char c, prev, *inp;
+ char *output = NULL;
+ char *outp = NULL;
+ const char *varname = NULL;
+
+ debug_parser("[PROCESS_MACROS] INPUT len %d: \"%s\"\n",
+ strlen(input), input);
+
+ for(uint_fast8_t pass = 0; pass < 2; pass++)
+ {
+ uint_fast8_t state = 0;
+ /* 0 = waiting for '$' */
+ /* 1 = waiting for '{' */
+ /* 2 = waiting for '}' */
+ /* 3 = waiting for ''' */
+
+ if (pass > 0) {
+ size_t outputlen = outp - (char *) NULL;
+ output = (char *) malloc(outputlen);
+ if (output == NULL) {
+ printf_P(PSTR("** Can't process command: Out of memory! **\n"));
+ return NULL;
+ }
+ outp = output;
+ }
+
+ inp = input;
+
+ for (prev = '\0'; (c = *inp++) != '\0'; prev = c) {
+
+
+
+ switch (state) {
+ case 0: /* Waiting for (unescaped) $ */
+ if ((c == '\'') && (prev != '\\')) {
+ state = 3;
+ break;
+ }
+ if ((c == '$') && (prev != '\\')) {
+ state++;
+ continue;
+ }
+ break;
+ case 1: /* Waiting for { */
+ if (c == '{') {
+ state++;
+ varname = inp;
+ continue;
+ } else {
+ state = 0;
+ append_char(pass, &outp, '$');
+ }
+ break;
+ case 2: /* Waiting for } */
+ if (c == '}') {
+ /* Terminate variable name */
+ *(inp-1) = '\0';
+ const char *envval = getenv_str(varname);
+ *(inp-1) = '}';
+ /* Copy into the line if it exists */
+ if (envval != NULL)
+ while (*envval)
+ append_char(pass, &outp, *(envval++));
+ /* Look for another '$' */
+ state = 0;
+ }
+ continue;
+ case 3: /* Waiting for ' */
+ if (c == '\'')
+ state = 0;
+ break;
+ }
+ append_char(pass, &outp, c);
+ }
+
+ append_char(pass, &outp, 0);
+ }
+
+ debug_parser("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n",
+ strlen(output), output);
+
+ return output;
+}
+
+/**
+ *
+ *
+ * WARNING:
+ *
+ * We must create a temporary copy of the command since the command we get
+ * may be the result from getenv_str(), 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).
+ *
+ *
+ * @param cmd
+ * @param flag
+ * @returns
+ *
+ */
+static int cli_run_command(const char *cmd, uint_fast8_t flag)
+{
+ char cmdbuf[strlen(cmd) + 1]; /* working copy of cmd */
+ char *token; /* start of token in cmdbuf */
+ char *sep; /* end of token (separator) in cmdbuf */
+ char *finaltoken = NULL; /* token after macro expansion */
+ char *str;
+ char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
+ int argc;
+ uint_fast8_t inquotes, repeatable = 1;
+ int rc = 0;
+
+ exec_flags = 0;
+ char *optenv = getenv_str(PSTR("cli"));
+ if (optenv) {
+ if (strstr_P(optenv, PSTR("verbose")) != NULL)
+ exec_flags |= OPT_VERBOSE;
+ if (strstr_P(optenv, PSTR("xtrace")) != NULL)
+ exec_flags |= OPT_XTRACE;
+ }
+
+ debug_parser("[RUN_COMMAND] cmd[%p]=\"%s\"\n",
+ cmd, cmd ? cmd : "NULL");
+
+ if (exec_flags & OPT_VERBOSE)
+ printf_P(PSTR("%s\n"), cmd, cmd ? cmd : "");
+
+ if (!cmd || !*cmd)
+ return -1; /* empty command */
+
+ clear_ctrlc(); /* forget any previous Control C */
+
+ str = strcpy(cmdbuf, cmd);
+ //str = cmdbuf;
+ ++command_level;
+
+ /* 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 != str) && /* past string start */
+ (*(sep - 1) != '\\')) /* and NOT escaped */
+ inquotes = !inquotes;
+
+ if (!inquotes &&
+ (*sep == ';' || *sep == '\n' /* separator */
+ || *sep == '#') && /* or start of comment */
+ ((sep == str) || /* string start */
+ (*(sep - 1) != '\\'))) /* or NOT escaped */
+ break;
+ }
+
+ /* no more commands after unescaped '#' token */
+ if (*sep == '#')
+ *sep = '\0';
+
+ /* 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 */
+ }
+ while (isblank(*token))
+ ++token;
+ if (*token == '\0') /* empty (comment only) */
+ continue;
+ debug_parser("token: \"%s\"\n", token);
+
+ /* find macros in this token and replace them */
+ finaltoken = process_macros(token);
+ if (finaltoken == NULL) {
+ rc = -1; /* no command at all */
+ continue;
+ }
+
+ /* Extract arguments */
+ argc = cli_parse_line(finaltoken, argv);
+ if (argc == 0) {
+ rc = -1; /* no command at all */
+ free(finaltoken);
+ continue;
+ }
+
+ if (exec_flags & OPT_XTRACE)
+ cli_trace_cmd(command_level, argc, argv);
+
+ rc = cmd_process(flag, argc, argv, &repeatable);
+ free(finaltoken);
+ if (rc != CMD_RET_SUCCESS) {
+ if (exec_flags & OPT_VERBOSE)
+ printf_P(PSTR("Command failed, result=%d\n"), rc);
+ rc = -1;
+ }
+
+ /* Did the user stop this? */
+ if (had_ctrlc()) {
+ rc = -1; /* if stopped then not repeatable */
+ break;
+ }
+ }
+
+ --command_level;
+
+ return rc ? rc : repeatable;
+}
+
+static int cli_run_command_list(const char *cmd)
+{
+ return (cli_run_command(cmd, 0) < 0);
+}
+
+/******************************************************************************/
+
+
+/*
+ * Run a command.
+ *
+ * @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, uint_fast8_t 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, uint_fast8_t flag)
+{
+ return cli_run_command(cmd, flag);
+}
+
+int run_command_list(const char *cmd, int len)
+{
+ (void) len;
+
+ return cli_run_command_list(cmd);
+}
+
+/****************************************************************************/
+
+
+void cli_loop(void)
+{
+ char *lastcommand = NULL;
+ int len;
+ uint_fast8_t flag;
+ int rc = 1;
+
+ for (;;) {
+ len = cli_readline(lastcommand ? PSTR(CONFIG_SYS_PROMPT_REPEAT) : PSTR(CONFIG_SYS_PROMPT), 1);
+
+ flag = 0; /* assume no special flags for now */
+ if (len > 0) {
+ free (lastcommand);
+ lastcommand = strdup(console_buffer);
+ } else if (len == 0)
+ flag |= CMD_FLAG_REPEAT;
+
+ if (len == -1) {
+ if (exec_flags & OPT_VERBOSE)
+ my_puts_P(PSTR("<INTERRUPT>\n"));
+ } else
+ rc = run_command_repeatable(lastcommand, flag);
+
+ if (rc <= 0) {
+ /* invalid command or not repeatable, forget it */
+ free(lastcommand);
+ lastcommand = NULL;
+ }
+ }
+}
diff --git a/avr/cli_readline.c b/avr/cli_readline.c
new file mode 100644
index 0000000..81f68df
--- /dev/null
+++ b/avr/cli_readline.c
@@ -0,0 +1,697 @@
+/*
+ * (C) Copyright 2014-2016 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * (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 "cli_readline.h"
+#include "common.h"
+
+#include "config.h"
+#include "con-utils.h"
+#include "print-utils.h"
+#include "command.h"
+#include "debug.h"
+
+
+#define DEBUG_READLINE 0 /* set to 1 to debug */
+
+#define debug_readline(fmt, args...) \
+ debug_cond(DEBUG_READLINE, fmt, ##args)
+
+
+
+char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */
+
+#define CTL_CH(c) ((c) - 'a' + 1)
+#define CTL_BACKSPACE ('\b')
+#define DEL ((char)255)
+#define DEL7 ((char)127)
+
+
+/************************************************************************************************/
+/* TODO:
+ *
+ */
+
+#define ESC 0x1b
+
+#define KEY_TAB '\t' // TAB key
+#define KEY_CR '\r' // RETURN key
+#define KEY_BACKSPACE '\b' // Backspace key
+#define KEY_ESCAPE 0x1B // ESCAPE (pressed twice)
+
+#define KEY_DOWN 0x80 // Down arrow key
+#define KEY_UP 0x81 // Up arrow key
+#define KEY_LEFT 0x82 // Left arrow key
+#define KEY_RIGHT 0x83 // Right arrow key
+#define KEY_HOME 0x84 // Home key
+#define KEY_DC 0x85 // Delete character key
+#define KEY_IC 0x86 // Ins char/toggle ins mode key
+#define KEY_NPAGE 0x87 // Next-page key
+#define KEY_PPAGE 0x88 // Previous-page key
+#define KEY_END 0x89 // End key
+#define KEY_BTAB 0x8A // Back tab key
+#define KEY_F1 0x8B // Function key F1
+#define KEY_F(n) (KEY_F1+(n)-1) // Space for additional 12 function keys
+
+
+struct fkey_tbl_s {
+ const FLASH char *sequence; /* ESC Sequence */
+ int code; /* Keycode */
+};
+
+//typedef const FLASH struct fkey_tbl_s fkey_tbl_t;
+
+#define FKEY_TBL_ITEM(_seq, _code) { FSTR(#_seq), _code }
+
+
+
+static const FLASH struct fkey_tbl_s fkey_table[] = {
+
+FKEY_TBL_ITEM(B, KEY_DOWN), // Down arrow key
+FKEY_TBL_ITEM(A, KEY_UP), // Up arrow key
+FKEY_TBL_ITEM(D, KEY_LEFT), // Left arrow key
+FKEY_TBL_ITEM(C, KEY_RIGHT), // Right arrow key
+FKEY_TBL_ITEM(1~, KEY_HOME), // Home key
+FKEY_TBL_ITEM(3~, KEY_DC), // Delete character key
+FKEY_TBL_ITEM(2~, KEY_IC), // Ins char/toggle ins mode key
+FKEY_TBL_ITEM(6~, KEY_NPAGE), // Next-page key
+FKEY_TBL_ITEM(5~, KEY_PPAGE), // Previous-page key
+FKEY_TBL_ITEM(4~, KEY_END), // End key
+FKEY_TBL_ITEM(Z, KEY_BTAB), // Back tab key
+/* */
+FKEY_TBL_ITEM(H, KEY_HOME), // Home key
+FKEY_TBL_ITEM(F, KEY_END), // End key
+/* VT400: */
+FKEY_TBL_ITEM(11~, KEY_F(1)), // Function key F1
+FKEY_TBL_ITEM(12~, KEY_F(2)), // Function key F2
+FKEY_TBL_ITEM(13~, KEY_F(3)), // Function key F3
+FKEY_TBL_ITEM(14~, KEY_F(4)), // Function key F4
+FKEY_TBL_ITEM(15~, KEY_F(5)), // Function key F5
+/* Linux console */
+FKEY_TBL_ITEM([A, KEY_F(1)), // Function key F1
+FKEY_TBL_ITEM([B, KEY_F(2)), // Function key F2
+FKEY_TBL_ITEM([C, KEY_F(3)), // Function key F3
+FKEY_TBL_ITEM([D, KEY_F(4)), // Function key F4
+FKEY_TBL_ITEM([E, KEY_F(5)), // Function key F5
+
+FKEY_TBL_ITEM(17~, KEY_F(6)), // Function key F6
+FKEY_TBL_ITEM(18~, KEY_F(7)), // Function key F7
+FKEY_TBL_ITEM(19~, KEY_F(8)), // Function key F8
+FKEY_TBL_ITEM(20~, KEY_F(9)), // Function key F9
+FKEY_TBL_ITEM(21~, KEY_F(10)), // Function key F10
+FKEY_TBL_ITEM(23~, KEY_F(11)), // Function key F11
+FKEY_TBL_ITEM(24~, KEY_F(12)), // Function key F12
+{ NULL } /* Mark end of table */
+};
+
+
+
+typedef enum {
+ STATE_GROUND,
+ STATE_ESCAPE,
+ STATE_CSI_ENTRY,
+ STATE_SS3
+} vtparse_state_t;
+
+#define CHB_SIZE 15
+
+static
+int vt_parse (void)
+{
+ static vtparse_state_t state = STATE_GROUND;
+ char buf[CHB_SIZE+1];
+ uint8_t param[2];
+ uint8_t i_buf = 0;
+ uint8_t i_param;
+ int ch;
+
+
+ while (1) {
+ ch = my_getchar(1);
+// debug_getch(state, ch);
+
+ switch (state) {
+ case STATE_GROUND:
+ if (ch == ESC) {
+ state = STATE_ESCAPE;
+ continue;
+ }
+ if (ch == 0x7F) // BACKSPACE on VT200 sends DEL char
+ ch = KEY_BACKSPACE; // map it to '\b'
+ break;
+ case STATE_ESCAPE:
+ if (ch < 0)
+ continue;
+
+ if (ch == '[') {
+ state = STATE_CSI_ENTRY;
+ param[0] = param[1] = 0;
+ i_buf = 0;
+ i_param = 0;
+ continue;
+ }
+ if (ch == 'O') {
+ state = STATE_SS3;
+ continue;
+ }
+ state = STATE_GROUND;
+ break;
+ case STATE_SS3:
+ if (ch == 'F')
+ ch = KEY_END;
+ if (ch == 'H')
+ ch = KEY_HOME;
+ state = STATE_GROUND;
+ break;
+ case STATE_CSI_ENTRY:
+ if (ch < 0)
+ continue;
+
+ buf[i_buf] = ch;
+ if (i_buf < CHB_SIZE)
+ i_buf++;
+ if (ch == ';') {
+ i_param++;
+ continue;
+ }
+ if (isdigit(ch)) {
+ if (i_param < 2)
+ param[i_param] = param[i_param] * 10 + ch - '0';
+ continue;
+ }
+ if (ch >= '@' && ch <= '~' && ch != '[') {
+ buf[i_buf] = '\0';
+ int_fast8_t i = 0;
+ while (fkey_table[i].sequence) {
+ if (! strcmp_P (buf, fkey_table[i].sequence)) {
+ ch = fkey_table[i].code;
+ break;
+ }
+ i++;
+ }
+ if (fkey_table[i].sequence == NULL) {
+ ch = '$'; /* KEY_ESCAPE; */
+ }
+ }
+ state = STATE_GROUND;
+ break;
+ }
+ break; /* while */
+ }
+
+ return ch;
+}
+
+/************************************************************************************************/
+
+/*
+ * cmdline-editing related codes from vivi.
+ * Author: Janghoon Lyu <nandy@mizi.com>
+ */
+
+
+char histbuf[CONFIG_SYS_HISTSIZE+1];
+#define HISTBUFE (histbuf+CONFIG_SYS_HISTSIZE)
+
+static char *hist_head;
+static char *hist_cur;
+
+static void hist_reset(void)
+{
+ if (hist_head == NULL)
+ hist_head = HISTBUFE;
+ hist_cur = hist_head;
+}
+
+static char *hist_entry_next(char *p)
+{
+ if (*p)
+ while (*p++);
+
+ return p;
+}
+
+static char *hist_entry_prev(char *p)
+{
+ if (p == hist_head)
+ return NULL;
+
+ --p;
+ while (p != hist_head && *(p-1) != 0)
+ --p;
+
+ return p;
+}
+
+static char *hist_search_entry(char *line)
+{
+ char *p = hist_head;
+
+ while (*p && strcmp(p, line))
+ p = hist_entry_next(p);
+ return *p ? p : NULL;
+}
+
+static char *hist_delete(size_t amount)
+{
+ char *p;
+
+ p = HISTBUFE - amount;
+ if (p < hist_head)
+ p = hist_head;
+
+ while (p > hist_head && *(p-1))
+ --p;
+
+ if (p == hist_head)
+ hist_head = HISTBUFE;
+ else {
+ size_t shift = HISTBUFE - p;
+ size_t len = p - hist_head;
+ hist_head = memmove(hist_head + shift, hist_head, len);
+ }
+ return hist_head;
+}
+
+static char *hist_delete_entry(char *entry)
+{
+ size_t shift = strlen(entry) + 1;
+ size_t len = entry - hist_head;
+
+ hist_head = memmove(hist_head + shift, hist_head, len);
+
+ return hist_head;
+}
+
+
+static char *hist_add_entry(char *line)
+{
+ char *p = hist_head;
+
+ if (p == NULL)
+ p = HISTBUFE;
+
+ char *q = p - strlen(line) - 1;
+ if (q < histbuf)
+ q = histbuf;
+
+ strlcpy(q, line, p - q);
+
+ hist_head = q;
+
+ return hist_head;
+}
+
+static uint_fast8_t hist_get_count(void)
+{
+ char *p = hist_head;
+ uint_fast8_t n = 0;
+
+ while (*p) {
+ ++n;
+ while (*p++);
+ }
+ return n;
+}
+
+static int hist_get_size(void)
+{
+ return HISTBUFE - hist_head;
+}
+
+static char *cread_add_to_hist(char *line)
+{
+ char *p;
+
+ p = hist_search_entry(line);
+ if (p)
+ hist_delete_entry(p);
+ else {
+ size_t free = hist_head - histbuf;
+ if (free < strlen(line) + 1)
+ hist_delete(strlen(line) + 1 - free);
+ }
+ hist_add_entry(line);
+
+ return p;
+}
+
+static char *hist_prev(void)
+{
+ char *p = hist_cur;
+
+ if (*p == '\0')
+ return NULL;
+ hist_cur = hist_entry_next(p);
+
+ return p;
+}
+
+static char *hist_next(void)
+{
+ char *p = hist_cur;
+
+ if(p == hist_head)
+ return NULL;
+
+ p = hist_entry_prev(p);
+ hist_cur = p;
+
+ return p == hist_head ? "" : hist_entry_prev(p);
+}
+
+static char *hist_search_backward(char* buf, uint8_t num)
+{
+ char *p = hist_cur;
+
+ if (*p == '\0')
+ return NULL;
+
+ while (*p && strncmp(p, buf, num))
+ p = hist_entry_next(p);
+
+ if(!strncmp(p, buf, num)) {
+ hist_cur = hist_entry_next(p);
+ return p;
+ }
+
+ return NULL;
+}
+
+static char *hist_search_forward (char* buf, uint8_t num)
+{
+ char *p = hist_cur;
+
+ if(p != hist_head && (p = hist_entry_prev(p)) != NULL) {
+ do {
+ p = hist_entry_prev(p);
+ } while (p && strncmp(p, buf, num) != 0);
+
+ //if(!strncmp(p, buf, num)) {
+ if(p) {
+ hist_cur = hist_entry_next(p);
+ return p;
+ }
+ }
+
+ return NULL;
+}
+
+static void putnstr(char *str, int n)
+{
+ /* printf_P(PSTR("%.*s"), (int)n, str) */
+ while (n-- && *str)
+ putchar(*str++);
+}
+
+static void getcmd_putch(int ch) { putchar(ch);}
+static int getcmd_getch(void) { return vt_parse();}
+static void getcmd_cbeep(void) { getcmd_putch('\a');}
+
+static void beginning_of_line(uint8_t *num)
+{
+ while (*num) {
+ getcmd_putch(CTL_BACKSPACE);
+ (*num)--;
+ }
+}
+
+static void erase_to_eol(uint_fast8_t *num, uint_fast8_t *eol_num)
+{
+ if (*num < *eol_num) {
+ /* printf_P(PSTR("%*S"), (int)(*eol_num - *num), PSTR("")); */
+ print_blanks(*eol_num - *num);
+ do {
+ getcmd_putch(CTL_BACKSPACE);
+ } while (--(*eol_num) > *num);
+ }
+}
+
+static void refresh_to_eol(char *buf, uint_fast8_t *num, uint_fast8_t *eol_num)
+{
+ if (*num < *eol_num) {
+ uint_fast8_t wlen = *eol_num - *num;
+ putnstr(buf + *num, wlen);
+ *num = *eol_num;
+ }
+}
+
+static void cread_add_char(char ichar, bool insert, uint_fast8_t *num,
+ uint_fast8_t *eol_num, char *buf, uint_fast8_t len)
+{
+ uint_fast8_t 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 */
+ buf[*num] = ichar;
+ putnstr(buf + *num, 1);
+ (*num)++;
+ }
+}
+
+static void cread_add_str(char *str, bool insert, uint_fast8_t *num,
+ uint_fast8_t *eol_num, char *buf, uint_fast8_t len)
+{
+ char c;
+
+ while ((c = *str++) != '\0')
+ cread_add_char(c, insert, num, eol_num, buf, len);
+}
+
+static int cread_line(const FLASH char *const prompt, char *buf,
+ uint_fast8_t len, bool enable_history)
+{
+ uint_fast8_t num = 0;
+ uint_fast8_t eol_num = 0;
+ bool insert = 1;
+
+ (void) prompt;
+
+ if (buf[0])
+ cread_add_str(buf, 1, &num, &eol_num, buf, len);
+
+ hist_reset();
+
+ while (1) {
+ int ichar = getcmd_getch();
+
+ if ((ichar == '\n') || (ichar == '\r')) {
+ putchar('\n');
+ break;
+ }
+
+
+ switch (ichar) {
+
+ case KEY_HOME:
+ case CTL_CH('a'):
+ beginning_of_line(&num);
+ break;
+ case CTL_CH('c'): /* ^C - break */
+ putchar('\n');
+ *buf = '\0'; /* discard input */
+ return -1;
+ case KEY_RIGHT:
+ case CTL_CH('f'): /* forward-char */
+ if (num < eol_num) {
+ getcmd_putch(buf[num]);
+ num++;
+ }
+ break;
+ case KEY_LEFT:
+ case CTL_CH('b'): /* backward-char */
+ if (num) {
+ getcmd_putch(CTL_BACKSPACE);
+ num--;
+ }
+ break;
+ case KEY_DC:
+ case CTL_CH('d'): /* delete-char */
+ if (num < eol_num) {
+ uint_fast8_t 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'): /* kill-line */
+ erase_to_eol(&num, &eol_num);
+ break;
+ case KEY_END:
+ case CTL_CH('e'):
+ refresh_to_eol(buf, &num, &eol_num);
+ break;
+ case KEY_IC:
+ case CTL_CH('o'):
+ insert = !insert;
+ break;
+ case CTL_CH('x'):
+ case CTL_CH('u'): /* kill-whole-line */
+ beginning_of_line(&num);
+ erase_to_eol(&num, &eol_num);
+ break;
+ case DEL:
+ case DEL7:
+ case 8: /* backward-delete-char */
+ if (num) {
+ uint_fast8_t wlen = eol_num - --num;
+ buf[eol_num] = ' ';
+ memmove(&buf[num], &buf[num+1], wlen);
+ getcmd_putch(CTL_BACKSPACE);
+ putnstr(buf + num, wlen);
+ do {
+ getcmd_putch(CTL_BACKSPACE);
+ } while (--wlen);
+ eol_num--;
+ }
+ break;
+ case KEY_UP:
+ case CTL_CH('p'): /* previous-history */
+ case KEY_DOWN:
+ case CTL_CH('n'): /* next-history */
+ if (enable_history) {
+ char *hline;
+
+ if (ichar == CTL_CH('p') || ichar == KEY_UP)
+ hline = hist_prev();
+ else
+ hline = hist_next();
+
+ if (hline) {
+ /* first, go home */
+ beginning_of_line(&num);
+ /* overwrite current line */
+ cread_add_str(hline, 0, &num, &eol_num, buf, len);
+ /* erase to end of line */
+ erase_to_eol(&num, &eol_num);
+
+ } else {
+ getcmd_cbeep();
+ }
+ } else {
+ getcmd_cbeep();
+ }
+ break;
+ case KEY_PPAGE: /* history-search-backward */
+ case KEY_NPAGE: /* history-search-forward */
+ if (enable_history) {
+ char *hline;
+ if (ichar == KEY_PPAGE)
+ hline = hist_search_backward(buf, num);
+ else
+ hline = hist_search_forward(buf, num);
+
+ if (hline) {
+ uint_fast8_t num2 = num;
+ /* overwrite current line from cursor position */
+ cread_add_str(hline+num, 0, &num2, &eol_num, buf, len);
+ /* erase to end of line */
+ erase_to_eol(&num2, &eol_num);
+ /* cursor back */
+ while (num2-- > num)
+ getcmd_putch(CTL_BACKSPACE);
+ } else {
+ getcmd_cbeep();
+ }
+ } else {
+ getcmd_cbeep();
+ }
+ break;
+#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:
+ if (isprint(ichar))
+ cread_add_char(ichar, insert, &num, &eol_num, buf, len);
+ break;
+ }
+ }
+ while (eol_num && buf[eol_num-1] == ' ')
+ --eol_num; /* remove trailing blanks */
+ buf[eol_num] = '\0'; /* lose the newline */
+
+ uint_fast8_t i = 0;
+ while (buf[i] == ' ')
+ ++i; /* remove leading blanks */
+ if (i) {
+ eol_num -= i;
+ memmove(buf, buf+i, eol_num+1);
+ }
+
+ debug_readline("### hist_head: %p, hist_cur: %p\n", hist_head, hist_cur);
+ if (enable_history && buf[0]) {
+ cread_add_to_hist(buf);
+ debug_readline("### hist_head: %p, hist_cur: %p, hist_size: %3d, hist_count: %2d\n",
+ hist_head, hist_cur, hist_get_size(), hist_get_count());
+ }
+ return eol_num;
+}
+
+/****************************************************************************/
+
+int cli_readline(const FLASH char *const prompt, bool enable_history)
+{
+ /*
+ * 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';
+
+ if (prompt)
+ my_puts_P(prompt);
+
+ return cread_line(prompt, console_buffer, CONFIG_SYS_CBSIZE, enable_history);
+}
diff --git a/avr/cmd_attach.c b/avr/cmd_attach.c
new file mode 100644
index 0000000..651404f
--- /dev/null
+++ b/avr/cmd_attach.c
@@ -0,0 +1,126 @@
+/*
+ * (C) Copyright 2016 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+/*
+ * attach channels to devices
+ */
+
+#include "cmd_attach.h"
+
+#include "command.h"
+#include "z180-serv.h"
+#include "getopt-min.h"
+
+
+/*
+ * attach [[options] [unit [diskfile]]]
+ *
+ * detach unit
+ * attach -d unit
+ *
+ * attach -o reattach unit
+ * attach -o reattach unit diskfile
+ *
+ * attach unit diskfile
+ *
+ */
+
+command_ret_t do_attach(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ uint8_t unit = 0;
+ char *filename = NULL;
+ bool detach = false;
+ bool detach_all = false;
+ drv_opt_t options = 0;
+ int res;
+
+ (void) cmdtp; (void) flag;
+
+
+ if (argv[0][0] == 'd') {
+ /* we are called as 'detach' */
+ detach = true;
+ } else if (argc == 1) {
+ /* no arguments */
+ drv_list();
+ return CMD_RET_SUCCESS;
+ }
+
+ int opt;
+ while ((opt = getopt(argc, argv, PSTR("darwo:"))) != -1) {
+ switch (opt) {
+ case 'd':
+ detach = true;
+ break;
+ case 'a':
+ detach_all = true;
+ break;
+ case 'r':
+ options |= DRV_OPT_RO;
+ break;
+ case 'w':
+ options &= ~DRV_OPT_RO;
+ break;
+ case 'o':
+ {
+ static const FLASH char delim[] = {", "};
+ char *p = strtok_P(optarg, delim);
+ while (p != NULL) {
+ if (!strcmp_P(p, PSTR("ro")))
+ options |= DRV_OPT_RO;
+ else if (!strcmp_P(p, PSTR("rw")))
+ options &= ~DRV_OPT_RO;
+ else if (!strcmp_P(p, PSTR("debug")))
+ options |= DRV_OPT_DEBUG;
+ else if (!strcmp_P(p, PSTR("nodebug")))
+ options &= ~DRV_OPT_DEBUG;
+ else if (!strcmp_P(p, PSTR("reattach")))
+ options |= DRV_OPT_REATTATCH;
+ else
+ return CMD_RET_USAGE;
+
+ p = strtok_P(NULL, delim);
+ }
+ }
+ break;
+ default: /* '?' */
+ return CMD_RET_USAGE;
+ }
+ }
+
+ /* remaining arguments */
+ argc -= optind;
+ if ( !( (argc == 0 && detach && detach_all) ||
+ (argc == 1 && detach) ||
+ (argc == 1 && (options & DRV_OPT_REATTATCH)) ||
+ argc == 2) )
+ return CMD_RET_USAGE;
+
+ if (argc > 0 && ((strlen(argv[optind]) != 4) ||
+ strncmp_P(argv[optind], PSTR("dsk"), 3) ||
+ (unit = argv[optind][3] - '0') >= CONFIG_CPM_MAX_DRIVE)) {
+
+ cmd_error(CMD_RET_FAILURE, 0, PSTR("Invalid device: '%s'"), argv[optind]);
+ }
+
+ if (detach) {
+ if (detach_all)
+ for (uint8_t i = 0; i < CONFIG_CPM_MAX_DRIVE; i++)
+ drv_detach(i);
+ else
+ drv_detach(unit);
+ return CMD_RET_SUCCESS;
+ }
+
+ if (argc == 2)
+ filename = argv[++optind];
+
+ res = drv_attach(unit, filename, options);
+ if (res)
+ cmd_error(CMD_RET_FAILURE, res, PSTR("Attachment of '%s' to dsk%d failed"), filename, unit);
+
+ return CMD_RET_SUCCESS;
+}
diff --git a/avr/cmd_boot.c b/avr/cmd_boot.c
new file mode 100644
index 0000000..6b98496
--- /dev/null
+++ b/avr/cmd_boot.c
@@ -0,0 +1,466 @@
+/*
+ * (C) Copyright 2014-2016 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * (C) Copyright 2000-2003
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+/*
+ * Misc boot support
+ */
+#include "cmd_boot.h"
+#include <util/atomic.h>
+
+#include "cli_readline.h" /* console_buffer[] */
+#include "cli.h" /* run_command() */
+#include "env.h"
+#include "eval_arg.h"
+#include "con-utils.h"
+#include "getopt-min.h"
+#include "z80-if.h"
+#include "z180-serv.h" /* restart_z180_serv() */
+#include "debug.h"
+
+/* ugly hack to get Z180 loadfile into flash memory */
+#define const const FLASH
+#include "../z180/hdrom.h"
+#include "../z180/cfboot.h"
+#undef const
+
+
+command_ret_t do_loadf(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
+{
+ if (z80_bus_state() & ZST_RUNNING)
+ cmd_error(CMD_RET_FAILURE, ERUNNING, NULL);
+ z80_bus_request_or_exit();
+ z80_load_mem(2, hdrom,
+ &hdrom_sections,
+ hdrom_address,
+ hdrom_length_of_sections);
+
+ z80_bus_cmd(Release);
+
+ return CMD_RET_SUCCESS;
+}
+
+
+void print_vars(char *title)
+{
+ uint8_t buf[5];
+ zstate_t state = z80_bus_state();
+
+ if((state & ZST_ACQUIRED) == 0)
+ z80_bus_cmd(Request);
+
+ z80_read_block(buf, 9, sizeof buf);
+
+ if((state & ZST_ACQUIRED) == 0)
+ z80_bus_cmd(Release);
+
+ printf_P(PSTR("%s: stage: %d, flag: 0x%.02x, result: %d, IDE stat/error: 0x%.02x/0x%.02x\n"),
+ title, buf[0], buf[1], buf[2], buf[3], buf[4]);
+}
+
+
+/*
+ * bootcf [options]
+ *
+ * -a address (100h)
+ * -s start sector (0)
+ * -c sector count (7)
+ * -i Partition id (52)
+ * -n load only
+ * -t timeout (10000)
+ * -v verbose
+ */
+
+command_ret_t do_bootcf(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ struct {
+ uint8_t jr[2];
+ uint16_t loadaddr;
+ uint8_t sec_start;
+ uint8_t sec_cnt;
+ uint8_t part_id;
+ uint16_t timeout;
+ uint8_t stages;
+ } boot_param;
+
+ struct {
+ uint8_t stages;
+ uint8_t done;
+ uint8_t result;
+ uint8_t ide_stat;
+ uint8_t ide_error;
+ } boot_res;
+
+ int_fast8_t verbosity = 0;
+ uint8_t default_stages;
+ uint32_t val;
+
+ /* get default values */
+ memcpy_P(&boot_param, cfboot, sizeof boot_param);
+ default_stages = boot_param.stages;
+
+ int opt;
+ while ((opt = getopt(argc, argv, PSTR("vna:s:c:t:i:"))) != -1) {
+ switch (opt) {
+ case 'v':
+ verbosity++;
+ break;
+ case 'n':
+ if (boot_param.stages > 0)
+ boot_param.stages--;
+ break;
+ case 'a':
+ val = eval_arg(optarg, NULL);
+ if (val < 0x100 || val > 0xFE00) {
+ printf_P(PSTR("Address out of range: 0x%.4lX\n"), val);
+ return CMD_RET_FAILURE;
+ }
+ boot_param.loadaddr = val;
+ break;
+ case 's':
+ val = eval_arg(optarg, NULL);
+ if (val > 255) {
+ printf_P(PSTR("Start sector out of range: 0x%lX\n"), val);
+ return CMD_RET_FAILURE;
+ }
+ boot_param.sec_start = val;
+ break;
+ case 'c':
+ val = eval_arg(optarg, NULL);
+ if (val > 127) {
+ printf_P(PSTR("Sector count out of range: 0x%lX\n"), val);
+ return CMD_RET_FAILURE;
+ }
+ boot_param.sec_cnt = val;
+ break;
+ case 't':
+ val = eval_arg(optarg, NULL);
+ if (val < 0x1 || val > 0xFFFF) {
+ printf_P(PSTR("Timeout value out of range: 0x%lX\n"), val);
+ return CMD_RET_FAILURE;
+ }
+ boot_param.timeout = val;
+ break;
+ case 'i':
+ val = eval_arg(optarg, NULL);
+ if (val < 0x01 || val > 0xFF) {
+ printf_P(PSTR("Partition id out of range: 0x%lX\n"), val);
+ return CMD_RET_FAILURE;
+ }
+ boot_param.part_id = val;
+ break;
+ default: /* '?' */
+ return CMD_RET_USAGE;
+ }
+ }
+
+ /* remaining arguments */
+ argc -= optind;
+ if (argc) {
+ my_puts_P(PSTR("Argument error!\n"));
+ return CMD_RET_USAGE;
+ }
+
+ if ((val = (uint32_t) boot_param.loadaddr + boot_param.sec_cnt * 512) >= 0xFF00) {
+ printf_P(PSTR("Top address out of range: 0x%.4lX\n"), val);
+ return CMD_RET_FAILURE;
+ }
+
+ if (z80_bus_state() & ZST_RUNNING)
+ cmd_error(CMD_RET_FAILURE, ERUNNING, NULL);
+ z80_bus_request_or_exit();
+ z80_load_mem(verbosity, cfboot,
+ &cfboot_sections,
+ cfboot_address,
+ cfboot_length_of_sections);
+
+ z80_write_block((const uint8_t *) &boot_param,
+ cfboot_address[0], sizeof boot_param);
+ z80_bus_cmd(Release);
+
+ if (boot_param.stages == 0) {
+ printf_P(PSTR("Bootloader loaded at: 0x%.4X\n"), (uint16_t) cfboot_address[0]);
+ } else {
+ printf_P(PSTR("Executing %d of %d Bootloader stages...\n"),
+ boot_param.stages, default_stages);
+
+ z80_bus_cmd(Run);
+ z80_bus_cmd(Release);
+
+ clear_ctrlc(); /* forget any previous Control C */
+ for (boot_res.done = 0; boot_res.done != 0xFF;) {
+ _delay_ms(8);
+ /* check for ctrl-c to abort... */
+ if (had_ctrlc() || ctrlc()) {
+ break;
+ }
+ z80_bus_cmd(Request);
+ z80_read_block((uint8_t *) &boot_res,
+ cfboot_address[0]+sizeof boot_param - 1, sizeof boot_res);
+ z80_bus_cmd(Release);
+ }
+
+ if (boot_res.done != 0xFF) {
+ z80_bus_cmd(Reset);
+ my_puts_P(PSTR("Abort\n"));
+ } else {
+ if (boot_param.stages == default_stages &&
+ boot_res.stages == 0 &&
+ boot_res.result == 0) {
+ my_puts_P(PSTR("Booting...\n"));
+ } else {
+ z80_bus_cmd(Reset);
+ boot_res.stages++;
+ printf_P(PSTR("Bootloader stopped at stage %d, result: %d, IDE stat/error: 0x%.02x/0x%.02x\n"),
+ boot_param.stages - boot_res.stages,
+ boot_res.result, boot_res.ide_stat, boot_res.ide_error);
+ }
+ }
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+command_ret_t do_busreq_pulse(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ uint16_t count=1;
+
+ if (!(z80_bus_state() & ZST_RUNNING)) {
+ printf_P(PSTR("## CPU is not running!\n"));
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc > 1)
+ count = (uint16_t) eval_arg(argv[1], NULL);
+
+ z80_bus_cmd(Request);
+ while (count--)
+ z80_bus_cmd(M_Cycle);
+
+ return CMD_RET_SUCCESS;
+}
+
+
+command_ret_t do_go(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ uint32_t addr;
+ bool hold = 0;
+
+ int opt;
+ while ((opt = getopt(argc, argv, PSTR("h"))) != -1) {
+ switch (opt) {
+ case 'h':
+ hold = 1;
+ break;
+ default: /* '?' */
+ return CMD_RET_USAGE;
+ }
+ }
+ argc -= optind; /* remaining arguments */
+
+ if (argc != 1)
+ return CMD_RET_USAGE;
+ addr = eval_arg(argv[optind], NULL);
+ if (addr >= (1UL<<16)) {
+ printf_P(PSTR("Invalid startaddress: 0x%05lx\n"
+ " (Out of logical address space (0x00000-0x0ffff))\n"),
+ addr);
+ return CMD_RET_FAILURE;
+ }
+
+ if (z80_bus_state() & ZST_RUNNING) {
+ cmd_error(CMD_RET_FAILURE, ERUNNING, NULL);
+ }
+
+ printf_P(PSTR("Starting application at 0x%04lx ...\n"), addr);
+
+ if (addr != 0) {
+// uint8_t tmp[3];
+
+ z80_bus_request_or_exit();
+// z80_read_block (tmp, 0, 3);
+ z80_write(0, 0xc3);
+ z80_write(1, addr);
+ z80_write(2, (addr >> 8));
+
+ z80_bus_cmd(Release);
+ _delay_ms(100);
+ z80_bus_cmd(Run);
+// z80_write_block(tmp, 0, 3);
+ } else {
+ if (!hold)
+ z80_bus_cmd(Request);
+ z80_bus_cmd(Run);
+ }
+ if (!hold)
+ z80_bus_cmd(Release);
+
+ return CMD_RET_SUCCESS;
+}
+
+static
+void reset_cpu(bus_cmd_t mode)
+{
+ restart_z180_serv();
+ z80_bus_cmd(mode);
+}
+
+
+command_ret_t do_reset(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
+{
+ printf_P(PSTR("CPU now in reset state.\n"));
+
+ reset_cpu(Reset);
+ return CMD_RET_SUCCESS;
+}
+
+command_ret_t do_restart(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
+{
+ reset_cpu(Restart);
+
+ return CMD_RET_SUCCESS;
+}
+
+static
+void print_con_usage(char esc)
+{ printf_P(PSTR("\n"
+ "------------------------------------------------\n"
+ " ?,H - This Help\n"
+ " Q,X - Return to command line\n"
+ " R - Reset (Restart) CPU\n"
+ " : - Execute monitor command\n"
+ " \\ - code input:\n"
+ " \\nnn 3 decimal digits character code\n"
+ " \\Xhh 2 hexadecimal digits character code\n"
+ " ^%c - (Escape char) Type again to send itself\n"
+ "key>"
+ ), esc + 0x40);
+}
+
+command_ret_t do_console(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
+{
+ int ch;
+ uint8_t pending;
+ uint8_t code = 0;
+ uint8_t state = 0;
+ char esc_char = (char) getenv_ulong(PSTR(ENV_ESC_CHAR), 16, CONFIG_ESC_CHAR);
+
+ printf_P(PSTR("Connecting to CPU. Escape character is '^%c'.\n"),
+ esc_char + 0x40);
+
+ while (1) {
+
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ pending = (Stat & S_CON_PENDING) != 0;
+ Stat &= ~S_CON_PENDING;
+ }
+ if (pending) {
+ uint8_t count = 100;
+ while ((ch = z80_memfifo_getc(fifo_conout)) >= 0 && --count)
+ putchar(ch);
+ }
+
+ if ((ch = my_getchar(0)) >= 0) {
+ switch (state) {
+ case 0:
+ if (ch == esc_char) {
+ state = 1;
+ /* TODO: Timer starten */
+ } else {
+ z80_memfifo_putc(fifo_conin, ch);
+ }
+ break;
+ case 2:
+ my_puts_P(PSTR("\n"
+ "------------------------------------------------\n"));
+ /* FALL TROUGH */
+ case 1:
+ state = 0;
+ switch (toupper(ch)) {
+
+ case '?':
+ case 'H':
+ print_con_usage(esc_char);
+ state = 2;
+ break;
+
+ case 'R':
+ reset_cpu(Restart);
+ break;
+
+ case 'X':
+ case 'Q':
+ putchar('\n');
+ goto quit;
+ break;
+
+ case ':':
+ putchar('\n');
+ int cmdlen = cli_readline(PSTR(": "), 1);
+ if (cmdlen > 0)
+ run_command(console_buffer, 0);
+ break;
+
+ case '\\':
+ code = 0;
+ state = 3;
+ break;
+
+ default:
+ if (ch == esc_char)
+ z80_memfifo_putc(fifo_conin, ch);
+ break;
+ }
+ break;
+ case 3:
+ if (toupper(ch) == 'X') {
+ state = 6;
+ break;
+ }
+ /* fall thru */
+ case 4:
+ case 5:
+ if (isdigit(ch)) {
+ code = code * 10 + ch - '0';
+ state++;
+ } else {
+ if (state > 3)
+ z80_memfifo_putc(fifo_conin, code);
+ z80_memfifo_putc(fifo_conin, ch);
+ state = 0;
+ }
+ if (state > 5) {
+ z80_memfifo_putc(fifo_conin, code);
+ state = 0;
+ }
+ break;
+ case 6:
+ case 7:
+ if (isxdigit(ch)) {
+ ch = toupper(ch);
+ if (ch >= 'A')
+ ch -= 'A' - 10;
+ code = code * 16 + ch - '0';
+ state++;
+ }else {
+ if (state > 6)
+ z80_memfifo_putc(fifo_conin, code);
+ z80_memfifo_putc(fifo_conin, ch);
+ state = 0;
+ }
+ if (state > 7) {
+ z80_memfifo_putc(fifo_conin, code);
+ state = 0;
+ }
+ break;
+ }
+ }
+ }
+quit:
+ return CMD_RET_SUCCESS;
+}
diff --git a/avr/cmd_cpu.c b/avr/cmd_cpu.c
new file mode 100644
index 0000000..07d2774
--- /dev/null
+++ b/avr/cmd_cpu.c
@@ -0,0 +1,571 @@
+/*
+ * (C) Copyright 2018 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "cmd_cpu.h"
+#include <util/atomic.h>
+
+#include "z80-if.h"
+#include "con-utils.h"
+#include "env.h"
+#include "eval_arg.h"
+#include "timer.h"
+#include "getopt-min.h"
+#include "debug.h"
+
+/* hack to get Z180 loadfile into flash memory */
+#define const const FLASH
+#include "../z180/cpuinfo.h"
+#undef const
+
+#define DEBUG_CPU 1 /* set to 1 to debug */
+
+#define debug_cpu(fmt, args...) \
+ debug_cond(DEBUG_CPU, fmt, ##args)
+
+static
+char * ulltoa (uint64_t val, char *s)
+{
+ char *p = s;
+
+ while (val >= 10) {
+ *p++ = (val % 10) + '0';
+ val = val / 10;
+ }
+ *p++ = val + '0';
+ *p = '\0';
+
+ return strrev(s);
+}
+
+/*
+ * delay for <count> ms...
+ */
+static void test_delay(uint32_t count)
+{
+ uint32_t ts = get_timer(0);
+
+ while (get_timer(ts) <= count);
+}
+
+static uint32_t z80_measure_phi(uint_fast8_t cycles)
+{
+ uint16_t ref_stop;
+ uint16_t ref_ovfl;
+ uint8_t x_ovfl;
+ uint32_t x_freq;
+
+
+ PRR1 &= ~_BV(PRTIM3);
+ TCCR3A = 0;
+ TCCR3B = 0b000<<CS30; /* stop counter */
+ TCNT3 = 0;
+ x_ovfl = 0;
+ TIFR3 = _BV(TOV3);
+ ref_ovfl = 0;
+
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ EIFR = _BV(INTF6); /* Reset pending int */
+ while ((EIFR & _BV(INTF6)) == 0) /* Wait for falling edge */
+ ;
+ OCR4B = TCNT4;
+ TCCR3B = 0b110<<CS30; /* Count falling edges on T3 (==INT6) */
+ TIFR4 = _BV(OCF4B); /* clear compare match flag */
+
+ while (ref_ovfl < 60) {
+ if ((TIFR4 & _BV(OCF4B)) != 0) {
+ TIFR4 = _BV(OCF4B);
+ ++ref_ovfl;
+ }
+ if ((TIFR3 & _BV(TOV3)) != 0) {
+ TIFR3 = _BV(TOV3);
+ ++x_ovfl;
+ }
+ }
+
+ EIFR = _BV(INTF6);
+ for (;;) {
+ if (EIFR & _BV(INTF6)) {
+ TCCR3B = 0b000<<CS30; /* stop counter */
+ ref_stop = TCNT4;
+ break;
+ }
+ if ((TIFR4 & _BV(OCF4B)) != 0) {
+ TIFR4 = _BV(OCF4B);
+ ++ref_ovfl;
+ }
+ }
+ }
+
+ if ((TIFR3 & _BV(TOV3)) != 0) {
+ TIFR3 = _BV(TOV3);
+ x_ovfl++;
+ }
+
+ uint32_t ref_cnt = (ref_stop - OCR4B) + ((uint32_t)ref_ovfl << 16);
+ uint32_t x_cnt = TCNT3 + ((uint32_t) x_ovfl << 16);
+ uint64_t x_tmp = (uint64_t) 100000 * (x_cnt * cycles);
+
+ /* Stop Timer */
+ TCCR3B = 0;
+ PRR1 |= _BV(PRTIM3);
+
+// char x_tmp_str[21];
+//
+// debug_cpu("TCNT3: %6u, ref_cnt: %9lu\n", TCNT3, ref_cnt);
+// ulltoa(x_tmp, x_tmp_str);
+// debug_cpu("x_tmp: %s\n", x_tmp_str);
+
+ x_tmp = (x_tmp * getenv_ulong(PSTR(ENV_FMON), 10, F_CPU) + (ref_cnt / 2)) / ref_cnt;
+
+// ulltoa(x_tmp, x_tmp_str);
+// debug_cpu("x_tmp: %s\n", x_tmp_str);
+
+ /* round to 5 decimal digits */
+ int_fast8_t sc = 5;
+ for ( ; sc > 0 || x_tmp >= 100000; sc--) x_tmp = (x_tmp + 5)/10;
+ x_freq = x_tmp;
+ for ( ; sc < 0; sc++) x_freq *= 10;
+
+ return x_freq;
+}
+
+static const FLASH char * const FLASH cpu_strings[] = {
+ FSTR("Unknown"),
+ FSTR("8080"),
+ FSTR("8085"),
+ FSTR("Z80"),
+ FSTR("x180"),
+ FSTR("HD64180"),
+ FSTR("Z80180"),
+ FSTR("Z80S180"),
+};
+
+#define O_SILENT (1<<0)
+#define O_WENV (1<<1)
+#define O_LOAD_LOOP (1<<2)
+#define O_UNLOAD_LOOP (1<<3)
+
+static const FLASH char * const FLASH opt_strings[] = {
+ FSTR("swnu"), /* Options for chkcpu */
+ FSTR("swnuc:"), /* Oprions for cpufreq */
+};
+
+static const FLASH char * const FLASH env_names[] = {
+ FSTR(ENV_CPU), /* Env var for chkcpu result */
+ FSTR(ENV_CPU_FREQ), /* Env var for cpufreq result */
+};
+
+command_ret_t do_cpu_freq_chk(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ uint_fast8_t options = O_LOAD_LOOP | O_UNLOAD_LOOP;
+ uint_fast8_t cputype = 0;
+ uint32_t cpu_freq = 0;
+ uint_fast8_t lcycles = 0;
+ uint_fast8_t freq_cmd = 0;
+// uint16_t timeout = 1000;
+ uint8_t eimsk_save;
+ ERRNUM err = ESUCCESS;
+
+ if (argv[0][0] == 'f')
+ freq_cmd = 1;
+
+ int opt;
+ while ((opt = getopt(argc, argv, opt_strings[freq_cmd])) != -1) {
+ switch (opt) {
+ case 's':
+ options |= O_SILENT;
+ break;
+ case 'w':
+ options |= O_WENV;
+ break;
+ case 'n':
+ options &= ~O_LOAD_LOOP;
+ break;
+ case 'u':
+ options &= ~O_UNLOAD_LOOP;
+ break;
+ case 'c':
+ lcycles = eval_arg(optarg, NULL);
+ break;
+// case 't':
+// timeout = eval_arg(optarg, NULL);
+// break;
+ default: /* '?' */
+ return CMD_RET_USAGE;
+ }
+ }
+ if (argc - optind != 0)
+ return CMD_RET_USAGE;
+
+ if (z80_bus_state() & ZST_RUNNING)
+ cmd_error(CMD_RET_FAILURE, ERUNNING, NULL);
+
+ uint8_t *mem_save = NULL;
+ if (options & O_LOAD_LOOP) {
+ mem_save = (uint8_t *) malloc(cpuinfo_length);
+ if (mem_save == NULL)
+ cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
+ z80_bus_cmd(Request);
+ z80_read_block(mem_save, 0, cpuinfo_length);
+ z80_load_mem(0, cpuinfo, &cpuinfo_sections, cpuinfo_address,
+ cpuinfo_length_of_sections);
+ z80_bus_cmd(Release);
+ }
+
+ /* Save state and disable INT5/INT6 */
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ eimsk_save = EIMSK;
+ EIMSK &= ~_BV(INT6);
+ EIMSK &= ~_BV(INT5);
+ }
+ EIFR = _BV(INTF5); /* Reset pending int */
+
+ z80_bus_cmd(Run);
+
+ clear_ctrlc(); /* forget any previous Control C */
+ /* Wait for falling edge */
+ do {
+ /* check for ctrl-c to abort... */
+ if (had_ctrlc() || ctrlc()) {
+ err = EINTR;
+ break;
+ }
+ } while ((EIFR & _BV(INTF5)) == 0);
+
+ if (freq_cmd) {
+ if (lcycles == 0) {
+ z80_bus_cmd(Request);
+ if (z80_read(3) == 0xFF)
+ lcycles = z80_read(5);
+ z80_bus_cmd(Release);
+ }
+ if (!err)
+ cpu_freq = z80_measure_phi(lcycles);
+ }
+ z80_bus_cmd(Reset);
+
+ /* Restore INT5/INT6 */
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ if ((eimsk_save & _BV(INT5)) != 0)
+ EIMSK |= _BV(INT5);
+ if ((eimsk_save & _BV(INT6)) != 0)
+ EIMSK |= _BV(INT6);
+ /* Reset pending int */
+ EIFR = _BV(INTF5);
+ EIFR = _BV(INTF6);
+ }
+ Stat &= ~S_MSG_PENDING;
+ Stat &= ~S_CON_PENDING;
+
+ if (freq_cmd == 0) {
+ z80_bus_cmd(Request);
+ if (z80_read(3) == 0xFF)
+ cputype = z80_read(4);
+ z80_bus_cmd(Release);
+ }
+
+ if ((mem_save != NULL) && options & O_UNLOAD_LOOP) {
+ z80_bus_cmd(Request);
+ z80_write_block(mem_save, 0, cpuinfo_length);
+ z80_bus_cmd(Release);
+ }
+ free(mem_save);
+
+ if (err)
+ cmd_error(CMD_RET_FAILURE, err, NULL);
+
+ char result_str[11];
+
+ if (freq_cmd) {
+ ultoa(cpu_freq, result_str, 10);
+ } else {
+ if (cputype >= ARRAY_SIZE(cpu_strings))
+ cputype = 0;
+ strcpy_P(result_str, cpu_strings[cputype]);
+ }
+
+ if (!(options & O_SILENT))
+ printf_P(PSTR("%s\n"), result_str);
+
+ if (options & O_WENV) {
+ if (setenv(env_names[freq_cmd], result_str)) {
+ if (!(options & O_SILENT)) {
+ printf_P(PSTR("'setenv %S %s' failed!\n"), env_names[freq_cmd], result_str);
+ //cmd_error(CMD_RET_FAILURE, ENOMEM, PSTR("'setenv (%S, %s)' failed"), env_names[freq_cmd], result_str);
+ }
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+command_ret_t do_cpu_test(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+
+ uint32_t pulsewidth = 10; /* ms */
+
+ int opt;
+ while ((opt = getopt(argc, argv, PSTR("t:"))) != -1) {
+ switch (opt) {
+ case 't':
+ pulsewidth = eval_arg(optarg, NULL);
+ break;
+ default: /* '?' */
+ return CMD_RET_USAGE;
+ }
+ }
+
+ if ((z80_bus_state() & ZST_ACQUIRED) != RESET)
+ cmd_error(CMD_RET_FAILURE, ERUNNING, NULL);
+
+ clear_ctrlc(); /* forget any previous Control C */
+ do {
+ z80_bus_cmd(Request);
+ test_delay(pulsewidth);
+ z80_bus_cmd(Release);
+ test_delay(pulsewidth);
+ } while (!(had_ctrlc() || ctrlc()));
+
+ return CMD_RET_SUCCESS;
+}
+
+command_ret_t do_bus_test(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
+{
+ int ch;
+
+#if 0
+ int opt;
+ while ((opt = getopt(argc, argv, PSTR("t:"))) != -1) {
+ switch (opt) {
+ case 't':
+ pulsewidth = eval_arg(optarg, NULL);
+ break;
+ default: /* '?' */
+ return CMD_RET_USAGE;
+ }
+ }
+#endif
+
+ my_puts_P(PSTR(
+ " 1: RESET 4: RUN r: Toggle /RESET\n"
+ " 2: REQUEST 5: RESTART b: Toggle /BUSREQ\n"
+ " 3: RELEASE 6: M_CYCLE\n"
+ "\n"
+ //"Bus state: "
+ ));
+
+ do {
+ ch = my_getchar(1);
+ if (ch >= 0) {
+ switch (ch) {
+ case '1': /* bus_cmd RESET */
+ case '2': /* bus_cmd REQUEST */
+ case '3': /* bus_cmd RELEASE */
+ case '4': /* bus_cmd RUN */
+ case '5': /* bus_cmd RESTART */
+ case '6': /* bus_cmd M_CYCLE */
+ z80_bus_cmd(ch - '1' + Reset);
+ break;
+ case 'r': /* Toggle RESET */
+ z80_toggle_reset();
+ break;
+ case 'b': /* Toggle BUSREQ */
+ z80_toggle_busreq();
+ break;
+ }
+ test_delay(10);
+ uint32_t cycles = z80_get_busreq_cycles();
+ printf_P(PSTR("\rState: %.2x, cycles: %lu, time: %luus "),
+ z80_bus_state(), cycles, (uint32_t) (cycles * 1000000LL / F_CPU));
+ }
+ } while (ch != 0x03);
+
+ putchar('\n');
+ return CMD_RET_SUCCESS;
+}
+
+command_ret_t do_busack_test(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
+{
+
+ if ((z80_bus_state() & ZST_ACQUIRED) != RESET)
+ cmd_error(CMD_RET_FAILURE, ERUNNING, NULL);
+
+ z80_bus_cmd(Request);
+ uint32_t result = z80_get_busreq_cycles();
+ test_delay(20);
+ z80_bus_cmd(Release);
+
+#if 0
+ long div;
+
+ pinconf = gpio_config_get(pin);
+ if (pinconf == OUTPUT_TIMER) {
+ div = gpio_clockdiv_get(pin);
+ }
+#endif
+
+
+ printf_P(PSTR("cycles: %lu, time: %luus\n"), result, (uint32_t) (result * 1000000LL / F_CPU));
+
+ return CMD_RET_SUCCESS;
+}
+
+
+/*
+ * command table for subcommands
+ */
+cmd_tbl_t cmd_tbl_cpu[] = {
+CMD_TBL_ITEM(
+ freq, CONFIG_SYS_MAXARGS, CTBL_RPT, do_cpu_freq_chk,
+ "Measure cpu frequency",
+// "[-swnu] [-c loopcycles] [-t timeout]\n"
+ "[-swnu] [-c loopcycles]\n"
+ " -s Be silent\n"
+ " -w Write result to environment variable '"ENV_CPU_FREQ"'\n"
+ " -n Don't load code snippet. \n"
+ " -u Don't unload. Leave code snippet in ram.\n"
+ " -c Overwrite cycles per lopp for in \"l: a,(50h)/jp l\" loop."
+// " -t Timeout (ms)\n"
+),
+CMD_TBL_ITEM(
+ chkcpu, CONFIG_SYS_MAXARGS, CTBL_RPT|CTBL_SUBCMDAUTO, do_cpu_freq_chk,
+ "Check/Identify CPU",
+// "[-swnu] [-c loopcycles] [-t timeout]\n"
+ "[-swnu] [-c loopcycles]\n"
+ " -s Be silent\n"
+ " -w Write result to environment variable '"ENV_CPU"'\n"
+ " -n Don't load code snippet. \n"
+ " -u Don't unload. Leave code snippet in ram."
+// " -t Timeout (ms)\n"
+),
+CMD_TBL_ITEM(
+ buscmd, CONFIG_SYS_MAXARGS, CTBL_RPT, do_bus_test,
+ "Bus commands",
+ ""
+),
+CMD_TBL_ITEM(
+ test, CONFIG_SYS_MAXARGS, CTBL_RPT, do_cpu_test,
+ "Do bus request/release cycles",
+ "[-t pulsewidth]"
+),
+CMD_TBL_ITEM(
+ busack, 2, CTBL_RPT, do_busack_test,
+ "Get time from /Reset high to /BUSACK low",
+ ""
+),
+
+CMD_TBL_ITEM(
+ help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
+ "Print sub command description/usage",
+ "\n"
+ " - print brief description of all sub commands\n"
+ "fat help command ...\n"
+ " - print detailed usage of sub cmd 'command'"
+),
+
+/* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
+ {FSTR("?"), CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
+ NULL,
+#ifdef CONFIG_SYS_LONGHELP
+ FSTR(""),
+#endif /* CONFIG_SYS_LONGHELP */
+ NULL,
+#ifdef CONFIG_AUTO_COMPLETE
+ NULL,
+#endif
+},
+/* Mark end of table */
+CMD_TBL_END(cmd_tbl_cpu)
+};
+
+
+command_ret_t do_cpu(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
+{
+ //puts_P(PSTR("Huch?"));
+
+ return CMD_RET_USAGE;
+}
+
+
+#if 0 /* Z180 Single Step Functions */
+/*
+ * Z180 Single Step Functions
+ *
+ */
+
+
+#define P_RUN PORTG
+#define RUN 1
+#define DDR_RUN DDRG
+#define P_STEP PORTG
+#define STEP 0
+#define DDR_STEP DDRG
+#define P_WAIT PORTG
+#define WAIT 2
+#define DDR_WAIT DDRG
+/* All three signals are on the same Port (PortG) */
+#define PORT_SS PORTG
+#define DDR_SS DDRG
+#define PIN_SS PING
+
+static bool ss_available;
+
+int single_step_setup(void)
+{
+ ss_available = false;
+
+#if 0
+ if (z80_bus_state() & ZST_RUNNING ||
+ !(z80_bus_cmd(Request) & ZST_ACQUIRED))
+ return -1;
+#endif
+
+ /* STEP, RUN output, WAIT input */
+
+ PORT_SS |= _BV(RUN) | _BV(STEP);
+ DDR_SS |= _BV(RUN) | _BV(STEP);
+ DDR_SS &= ~_BV(WAIT);
+
+ /* RUN high, MREQ pulse --> WAIT should be low */
+ z80_mreq_pulse();
+
+ if ((PIN_SS & _BV(WAIT)) == 0) {
+
+ /* RUN high, STEP pulse --> WAIT should be high */
+ PIN_SS = _BV(STEP);
+ PIN_SS = _BV(STEP);
+ if ((PIN_SS & _BV(WAIT)) != 0) {
+
+ /* RUN high, MREQ pulse --> WAIT should be low */
+ z80_mreq_pulse();
+ if ((PIN_SS & _BV(WAIT)) == 0) {
+
+ /* RUN low --> WAIT should be high */
+ PIN_SS = _BV(RUN);
+ if ((PIN_SS & _BV(WAIT)) != 0) {
+
+ /* RUN low, STEP pulse --> WAIT should be high */
+ PIN_SS = _BV(STEP);
+ PIN_SS = _BV(STEP);
+ if ((PIN_SS & _BV(WAIT)) != 0) {
+
+ /* all tests passed */
+ ss_available = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (!ss_available) {
+ DDR_SS &= ~(_BV(STEP) | _BV(RUN));
+ PORT_SS |= _BV(RUN) | _BV(STEP);
+ }
+
+ return ss_available ? 0 : -1;
+}
+#endif
diff --git a/avr/cmd_date.c b/avr/cmd_date.c
new file mode 100644
index 0000000..0bd3ebb
--- /dev/null
+++ b/avr/cmd_date.c
@@ -0,0 +1,176 @@
+/*
+ * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * (C) Copyright 2001
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+/*
+ * RTC, Date & Time support: get and set date & time
+ */
+#include "cmd_date.h"
+#include "time.h"
+#include "rtc.h"
+
+
+/*
+ * simple conversion of two-digit string with error checking
+ */
+static int cnvrt2 (const char *str, int *valp)
+{
+ int val;
+
+ if ((*str < '0') || (*str > '9'))
+ return (-1);
+
+ val = *str - '0';
+
+ ++str;
+
+ if ((*str < '0') || (*str > '9'))
+ return (-1);
+
+ *valp = 10 * val + (*str - '0');
+
+ return (0);
+}
+
+/*
+ * Convert date string: MMDDhhmm[[CC]YY][.ss]
+ *
+ * Some basic checking for valid values is done, but this will not catch
+ * all possible error conditions.
+ */
+int mk_date (const char *datestr, struct tm *tmp)
+{
+ int len, val;
+ char *ptr;
+
+ ptr = strchr (datestr,'.');
+ len = strlen (datestr);
+
+ /* Set seconds */
+ if (ptr) {
+ int sec;
+
+ *ptr++ = '\0';
+ if ((len - (ptr - datestr)) != 2)
+ return (-1);
+
+ len = strlen (datestr);
+
+ if (cnvrt2 (ptr, &sec))
+ return (-1);
+
+ tmp->tm_sec = sec;
+ } else {
+ tmp->tm_sec = 0;
+ }
+
+ if (len == 12) { /* MMDDhhmmCCYY */
+ int year, century;
+
+ if (cnvrt2 (datestr+ 8, &century) ||
+ cnvrt2 (datestr+10, &year) ) {
+ return (-1);
+ }
+ tmp->tm_year = 100 * century + year - 1900;
+ } else if (len == 10) { /* MMDDhhmmYY */
+ int year, century;
+
+ century = (tmp->tm_year + 1900) / 100;
+ if (cnvrt2 (datestr+ 8, &year))
+ return (-1);
+ tmp->tm_year = 100 * century + year -1900;
+ }
+
+ switch (len) {
+ case 8: /* MMDDhhmm */
+ /* fall thru */
+ case 10: /* MMDDhhmmYY */
+ /* fall thru */
+ case 12: /* MMDDhhmmCCYY */
+ if (cnvrt2 (datestr+0, &val) ||
+ val > 12) {
+ break;
+ }
+ tmp->tm_mon = val - 1;
+ if (cnvrt2 (datestr+2, &val) ||
+ val > ((tmp->tm_mon==2-1) ? 29 : 31)) {
+ break;
+ }
+ tmp->tm_mday = val;
+
+ if (cnvrt2 (datestr+4, &val) ||
+ val > 23) {
+ break;
+ }
+ tmp->tm_hour = val;
+
+ if (cnvrt2 (datestr+6, &val) ||
+ val > 59) {
+ break;
+ }
+ tmp->tm_min = val;
+
+ return (0);
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+command_ret_t do_date(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ struct tm t;
+ char buf[30];
+ int rc;
+ command_ret_t rcode = CMD_RET_FAILURE;
+
+ switch (argc) {
+ case 2: /* set date & time */
+ /* initialize t with current time */
+ if(rtc_get(&t) < 0) {
+ my_puts_P(PSTR("## Get date failed\n"));
+ break;
+ } else {
+ /* insert new date & time */
+ if (mk_date (argv[1], &t) != 0) {
+ my_puts_P(PSTR("## Bad date format\n"));
+ break;
+ }
+
+ time_t time;
+ time = mk_gmtime(&t);
+ gmtime_r(&time, &t);
+
+ /* and write to RTC */
+ if(rtc_set(&t) < 0) {
+ my_puts_P(PSTR("## Set date failed\n"));
+ break;
+ }
+ }
+ /* FALL TROUGH */
+ case 1: /* get date & time */
+ rc = rtc_get(&t);
+ if (rc >= 0) {
+ asctime_r(&t, buf);
+ printf_P(PSTR("%s"), buf);
+ if (rc == 1)
+ printf_P(PSTR(" (Invalid)"));
+ putchar('\n');
+ rcode = CMD_RET_SUCCESS;
+ } else {
+ my_puts_P(PSTR("## Get date failed\n"));
+ }
+ break;
+
+ default:
+ rcode = CMD_RET_USAGE;
+ }
+
+ return rcode;
+}
diff --git a/avr/cmd_fat.c b/avr/cmd_fat.c
new file mode 100644
index 0000000..8477fc2
--- /dev/null
+++ b/avr/cmd_fat.c
@@ -0,0 +1,664 @@
+/*
+ * (C) Copyright 2014,2016,2018 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+/*
+ * FAT filesystem commands
+ */
+
+#include "cmd_fat.h"
+#include <time.h>
+uint32_t fat_time(const struct tm * timeptr);
+
+#include "ff.h"
+#include "z80-if.h"
+#include "eval_arg.h"
+#include "con-utils.h"
+#include "print-utils.h"
+#include "timer.h"
+#include "debug.h"
+#include "env.h"
+
+#define DEBUG_FA 0 /* set to 1 to debug */
+
+#define debug_fa(fmt, args...) \
+ debug_cond(DEBUG_FA, fmt, ##args)
+
+
+/* TODO: use memory size test function (detect_ramsize() in cmd_loadihex.c) */
+/* TODO: detect_ramsize() should be moved to z80-if.c */
+#define MAX_MEMORY CONFIG_SYS_RAMSIZE_MAX
+#define BUFFER_SIZE FF_MAX_SS
+#define PATH_MAX CONFIG_SYS_MAX_PATHLEN
+
+
+/*
+ * Multible (fat) partitions per physical drive are not supported,
+ * but we have up to 2 sdcard slots.
+ */
+FATFS FatFs0;
+FATFS FatFs1;
+
+command_ret_t command_ret;
+char *cmdname;
+
+
+void setup_fatfs(void)
+{
+ f_mount(&FatFs0, "0:", 0);
+ f_mount(&FatFs1, "1:", 0);
+}
+
+DWORD get_fattime (void)
+{
+ time_t timer;
+ struct tm tm_timer;
+
+ time(&timer);
+ gmtime_r(&timer, &tm_timer);
+
+ return fat_time(&tm_timer);
+}
+
+
+static bool check_abort(void)
+{
+ bool ret = ctrlc();
+
+ if (ret)
+ printf_P(PSTR("Abort\n"));
+
+ return ret;
+}
+
+
+static const FLASH char * const FLASH rc_strings[] = {
+ FSTR("Success"),
+ FSTR("Disk error"),
+ FSTR("Internal error"),
+ FSTR("Not ready"),
+ FSTR("No file"),
+ FSTR("No path"),
+ FSTR("Invalid name"),
+ FSTR("Denied"),
+ FSTR("Exist"),
+ FSTR("Invalid object"),
+ FSTR("Write protected"),
+ FSTR("Invalid drive"),
+ FSTR("Not enabled"),
+ FSTR("No file system"),
+ FSTR("Mkfs aborted"),
+ FSTR("Timeout"),
+ FSTR("Locked"),
+ FSTR("Not enough core"),
+ FSTR("Too many open files"),
+ FSTR("Invalid parameter")
+ };
+
+
+
+const FLASH char * fat_rctostr(FRESULT rc)
+{
+ return rc < ARRAY_SIZE(rc_strings) ? rc_strings[rc] : PSTR(" Unknown Error");
+}
+
+static void swirl(void)
+{
+ static const FLASH char swirlchar[] = { '-','\\','|','/' };
+ static uint_fast8_t cnt;
+ static uint32_t tstamp;
+
+ if (get_timer(0) > tstamp) {
+ tstamp = get_timer(0) + 250;
+ putchar('\b');
+ cnt = (cnt+1) & 3;
+ putchar(swirlchar[cnt]);
+ }
+}
+
+typedef struct {
+ char *p_end; /* pointer to NULL at end of path */
+ char p_path[PATH_MAX + 1]; /* pointer to the start of a path */
+} PATH_T;
+
+
+static char *path_skip_heading(char *p)
+{
+ if (isdigit(p[0]) && p[1] == ':') {
+ p += 2;
+ } else {
+ char *q = p;
+ if (*q++ == '.') {
+ if (*q == '.')
+ ++q;
+ if (*q == '\0' || *q == '/')
+ p = q;
+ }
+ return p;
+ }
+ if (*p == '/')
+ ++p;
+
+ return p;
+}
+
+static void strip_trailing_slash(PATH_T *p)
+{
+ char *beg = path_skip_heading(p->p_path);
+ char *end = p->p_end;
+
+ while (end > beg && end[-1] == '/')
+ *--end = '\0';
+
+ p->p_end =end;
+}
+
+/*
+ * Move specified string into path. Convert "" to "." to handle BSD
+ * semantics for a null path. Strip trailing slashes.
+ */
+static PATH_T *path_setup(char *string)
+{
+ if (strlen(string) > PATH_MAX) {
+ cmd_error(0, 0, PSTR("'%s': Name too long"), string);
+ return NULL;
+ }
+
+ PATH_T *p = (PATH_T *) malloc(sizeof *p);
+ if (p == NULL) {
+ cmd_error(0, ENOMEM, NULL);
+ return NULL;
+ }
+
+ strcpy(p->p_path, string);
+ size_t len = strlen(string);
+ if (len > 1 && p->p_path[1] == ':' && p->p_path[2] != '/') {
+ if (len < PATH_MAX) {
+ memmove(p->p_path+3, p->p_path+2, len-1);
+ p->p_path[2] = '/';
+ len += 1;
+ } else {
+ cmd_error(0, ENOMEM, NULL);
+ return NULL;
+ }
+ }
+
+ p->p_end = p->p_path + len;
+ if (p->p_path == p->p_end) {
+ *p->p_end++ = '.';
+ *p->p_end = '\0';
+ }
+
+ strip_trailing_slash(p);
+ return p;
+}
+
+/*
+ * pwd - Print current directory of the current drive.
+ *
+ */
+command_ret_t do_pwd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
+{
+ FRESULT res;
+ char *buf;
+
+ buf = (char *) malloc(PATH_MAX);
+ if (buf == NULL)
+ cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
+ *buf = '\0';
+ res = f_getcwd(buf, PATH_MAX); /* Get current directory path */
+
+ debug_fa("### f_getcwd(): buf: '%s', res: %d\n", buf, res);
+
+ if (res == FR_OK) {
+ puts(buf);
+ }
+ free(buf);
+ if (res != FR_OK)
+ cmd_error(CMD_RET_FAILURE, res, NULL);
+
+ return CMD_RET_SUCCESS;
+}
+
+
+/*
+ * cd - Change the current/working directory.
+ *
+ */
+command_ret_t do_cd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ char *arg;
+ FRESULT res = FR_OK;
+
+ if (argc < 2) {
+ arg = getenv_str(PSTR(ENV_HOME));
+ if (arg == NULL) {
+ cmd_error(CMD_RET_FAILURE, 0, PSTR("\"%S\" is not set"), PSTR(ENV_HOME));
+ }
+ } else
+ arg = argv[1];
+
+ PATH_T *path = path_setup(arg);
+ if (path == NULL)
+ return CMD_RET_FAILURE;
+
+ if (strlen(path->p_path) > 1 && path->p_path[1] == ':') {
+ res = f_chdrive(path->p_path);
+ debug_fa("### f_chdrive(): p_path: '%s', res: %d\n", path->p_path, res);
+ }
+ if (res == FR_OK) {
+ res = f_chdir(path->p_path);
+ debug_fa("### f_chdir(): p_path: '%s', res: %d\n", path->p_path, res);
+ }
+ free(path);
+ if (res != FR_OK)
+ cmd_error(CMD_RET_FAILURE, res, NULL);
+
+ return CMD_RET_SUCCESS;
+}
+
+
+/*
+ * ls path - Directory listing
+ *
+ */
+command_ret_t do_ls(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ FATFS *fs;
+ DIR Dir; /* Directory object */
+ FILINFO Finfo;
+ unsigned long p1;
+ unsigned int s1, s2;
+ FRESULT res = FR_OK;
+ char *buf;
+
+
+ buf = (char *) malloc(PATH_MAX);
+ if (buf == NULL)
+ cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
+
+ if (argc < 2)
+ res = f_getcwd(buf, PATH_MAX); /* Get current directory path */
+ else
+ strncpy(buf, argv[1], PATH_MAX);
+
+ if (res == FR_OK)
+ res = f_opendir(&Dir, buf);
+ if (res != FR_OK) {
+ free(buf);
+ cmd_error(CMD_RET_FAILURE, res, NULL);
+ }
+
+ p1 = s1 = s2 = 0;
+ for(;;) {
+ res = f_readdir(&Dir, &Finfo);
+ if ((res != FR_OK) || !Finfo.fname[0])
+ break;
+ if (Finfo.fattrib & AM_DIR) {
+ s2++;
+ } else {
+ s1++; p1 += Finfo.fsize;
+ }
+ printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
+ (Finfo.fattrib & AM_DIR) ? 'D' : '-',
+ (Finfo.fattrib & AM_RDO) ? 'R' : '-',
+ (Finfo.fattrib & AM_HID) ? 'H' : '-',
+ (Finfo.fattrib & AM_SYS) ? 'S' : '-',
+ (Finfo.fattrib & AM_ARC) ? 'A' : '-',
+ (Finfo.fdate >> 9) + 1980, (Finfo.fdate >> 5) & 15, Finfo.fdate & 31,
+ (Finfo.ftime >> 11), (Finfo.ftime >> 5) & 63,
+ Finfo.fsize, Finfo.fname);
+ if (check_abort())
+ break;
+ }
+
+ if (res == FR_OK) {
+ printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1, p1, s2);
+ if (f_getfree(buf, (DWORD*)&p1, &fs) == FR_OK)
+ printf_P(PSTR(", %10luK bytes free\n"), p1 * fs->csize / 2);
+ }
+
+ free(buf);
+ if (res)
+ cmd_error(CMD_RET_FAILURE, res, NULL);
+
+ return CMD_RET_SUCCESS;
+}
+
+#if 0
+static
+FRESULT mkpath(TCHAR *path)
+{
+ /* TODO: */
+ (void) path;
+ FILINFO fd
+ TCHAR *p, *q;
+ FRESULT ret;
+
+ res = f_stat (path, &fd)
+
+ p = strchr(path, ':');
+ if (p == NULL || *++p == '\0' || *p++ != '/')
+ return FR_OK;
+
+ while ((q = strchr(p, '/')) != NULL) {
+ *q = '\0';
+ ret = f_mkdir(path);
+ *q = '/';
+ if (ret != FR_OK && ret != FR_EXIST)
+ return ret;
+ p = q + 1;
+ }
+
+ return FR_OK;
+}
+#endif
+
+/* Work register for fs command */
+struct stat_dat_s {
+ DWORD AccSize;
+ WORD AccFiles, AccDirs;
+ FILINFO Finfo;
+};
+
+static
+FRESULT scan_files (
+ char *path, /* Pointer to the working buffer with start path */
+ struct stat_dat_s *statp
+)
+{
+ DIR dirs;
+ FRESULT res;
+ int i;
+ char *fn;
+
+ res = f_opendir(&dirs, path);
+ swirl();
+ if (res == FR_OK) {
+ i = strlen(path);
+ while (((res = f_readdir(&dirs, &statp->Finfo)) == FR_OK) &&
+ statp->Finfo.fname[0]) {
+ if (FF_FS_RPATH && statp->Finfo.fname[0] == '.')
+ continue;
+ fn = statp->Finfo.fname;
+ if (statp->Finfo.fattrib & AM_DIR) {
+ statp->AccDirs++;
+ path[i] = '/';
+ strcpy(path+i+1, fn);
+ res = scan_files(path, statp);
+ path[i] = '\0';
+ if (res != FR_OK)
+ break;
+ } else {
+ //printf_P(PSTR("%s/%s\n"), path, fn);
+ statp->AccFiles++;
+ statp->AccSize += statp->Finfo.fsize;
+ }
+ if (check_abort()) {
+ res = 255;
+ break;
+ }
+ }
+ }
+
+ return res;
+}
+
+
+/*
+ * fatstat path - Show logical drive status
+ *
+ */
+command_ret_t do_stat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ FATFS *fs;
+ DWORD nfreeclst;
+ FRESULT res;
+ char *buf;
+ char *path = "";
+ struct stat_dat_s statp;
+
+ buf = (char *) malloc(PATH_MAX);
+ if (buf == NULL)
+ cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
+
+ if (argc > 1)
+ path = argv[1];
+ res = f_getfree(path, &nfreeclst, &fs);
+ if (!res) {
+ printf_P(PSTR(
+ "FAT type: %u\n"
+ "Bytes/Cluster: %lu\n"
+ "Number of FATs: %u\n"
+ "Root DIR entries: %u\n"
+ "Sectors/FAT: %lu\n"
+ "Number of clusters: %lu\n"
+ "FAT start (lba): %lu\n"
+ "DIR start (lba,cluster): %lu\n"
+ "Data start (lba): %lu\n"),
+ fs->fs_type, (DWORD)fs->csize * 512, fs->n_fats,
+ fs->n_rootdir, fs->fsize, fs->n_fatent - 2,
+ fs->fatbase, fs->dirbase, fs->database);
+
+#if FF_USE_LABEL
+ DWORD serial;
+ res = f_getlabel(path, buf, &serial);
+ if (!res) {
+ printf_P(PSTR(
+ "Volume name: %s\n"
+ "Volume S/N: %04X-%04X\n"),
+ buf, (WORD)(serial >> 16), (WORD)(serial & 0xFFFF));
+ }
+#endif
+ if (!res) {
+ statp.AccSize = statp.AccFiles = statp.AccDirs = 0;
+ strcpy(buf, path);
+
+ my_puts_P(PSTR("\nCounting... "));
+ res = scan_files(buf, &statp);
+ putchar('\r');
+ }
+ if (!res) {
+ printf_P(PSTR("%u files, %lu bytes.\n%u folders.\n"
+ "%lu KB total disk space.\n%lu KB available.\n"),
+ statp.AccFiles, statp.AccSize, statp.AccDirs,
+ (fs->n_fatent - 2) * (fs->csize / 2), nfreeclst * (fs->csize / 2)
+ );
+ }
+ }
+
+ free(buf);
+ if (res)
+ cmd_error(CMD_RET_FAILURE, res, NULL);
+
+ return CMD_RET_SUCCESS;
+}
+
+/*
+ * fatread/write - load binary file to/from a dos filesystem
+ * read <d:/path/filename> <addr> [bytes [pos]]
+ * write <d:/path/filename> <addr> <bytes>
+ */
+command_ret_t do_rw(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ FIL File;
+ uint32_t addr;
+ unsigned long bytes;
+ unsigned long pos;
+ unsigned long bytes_rw;
+
+ bool dowrite = (argv[0][0] == 'w');
+ FRESULT res = FR_OK;
+ bool buserr = 0;
+ uint32_t timer;
+ uint8_t *buffer;
+
+ if (argc < (dowrite ? 4 : 3))
+ return CMD_RET_USAGE;
+
+ addr = eval_arg(argv[2], NULL);
+ if (addr >= MAX_MEMORY)
+ cmd_error(CMD_RET_FAILURE, 0, PSTR("Address too high: %#lx"), addr);
+
+ if (argc > 3)
+ bytes = eval_arg(argv[3], NULL);
+ else
+ bytes = MAX_MEMORY;
+ if (argc > 4)
+ pos = eval_arg(argv[4], NULL);
+ else
+ pos = 0;
+
+ if (addr + bytes > MAX_MEMORY)
+ bytes = MAX_MEMORY - addr;
+
+ buffer = (uint8_t *) malloc(BUFFER_SIZE);
+ if (buffer == NULL)
+ cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
+
+ res = f_open(&File, argv[1], dowrite ? FA_WRITE | FA_CREATE_ALWAYS
+ : FA_READ );
+
+ if (!res) {
+ res = f_lseek(&File, pos);
+ if (!res) {
+ bytes_rw = 0;
+ timer = get_timer(0);
+ while (bytes) {
+ unsigned int cnt, br;
+
+ if (bytes >= BUFFER_SIZE) {
+ cnt = BUFFER_SIZE;
+ bytes -= BUFFER_SIZE;
+ } else {
+ cnt = bytes; bytes = 0;
+ }
+ if (dowrite) {
+ if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
+ buserr = 1;
+ break;
+ }
+ z80_read_block(buffer, addr, cnt);
+ z80_bus_cmd(Release);
+ res = f_write(&File, buffer, cnt, &br);
+ if (res != FR_OK)
+ break;
+ } else {
+ res = f_read(&File, buffer, cnt, &br);
+ if (res != FR_OK)
+ break;
+ if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
+ buserr = 1;
+ break;
+ }
+ z80_write_block(buffer, addr, br);
+ z80_bus_cmd(Release);
+ }
+ addr += br;
+
+ bytes_rw += br;
+ if (cnt != br) {
+ if (dowrite)
+ printf_P(PSTR("Disk full?\n"));
+ break;
+ }
+ if (check_abort())
+ break;
+ }
+
+ FRESULT fr = f_close(&File);
+ if (!res)
+ res = fr;
+ timer = get_timer(timer);
+ printf_P(PSTR("%lu (%#lx) bytes read/written with %lu bytes/sec.\n"),
+ bytes_rw, bytes_rw, timer ? (bytes_rw * 1000 / timer) : 0);
+ }
+ }
+
+ free(buffer);
+
+ if (res)
+ cmd_error(CMD_RET_FAILURE, res, PSTR("'%s'"), argv[1]);
+ if (buserr)
+ cmd_error(CMD_RET_FAILURE, EBUSTO, NULL);
+
+ return CMD_RET_SUCCESS;
+}
+
+/*
+ * command table for fat subcommands
+ */
+
+cmd_tbl_t cmd_tbl_fat[] = {
+CMD_TBL_ITEM(
+ status, 2, CTBL_RPT, do_stat,
+ "Show logical drive status",
+ "dev"
+),
+CMD_TBL_ITEM(
+ pwd, 2, CTBL_RPT|CTBL_SUBCMDAUTO, do_pwd,
+ "Print name of current/working directory",
+ ""
+),
+CMD_TBL_ITEM(
+ cd, 2, 0|CTBL_SUBCMDAUTO, do_cd,
+ "Change the current/working directory.",
+ "path"
+),
+CMD_TBL_ITEM(
+ ls, 2, CTBL_RPT|CTBL_SUBCMDAUTO, do_ls,
+ "Directory listing",
+ "path"
+),
+CMD_TBL_ITEM(
+ load, 5, 0, do_rw,
+ "load binary file from a dos filesystem",
+ "<d:/path/filename> <addr> [bytes [pos]]\n"
+ " - Load binary file 'path/filename' on logical drive 'd'\n"
+ " to address 'addr' from dos filesystem.\n"
+ " 'pos' gives the file position to start loading from.\n"
+ " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
+ " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
+ " the load stops on end of file."
+),
+CMD_TBL_ITEM(
+ write, 4, 0, do_rw,
+ "write file into a dos filesystem",
+ "<d:/path/filename> <addr> <bytes>\n"
+ " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
+ " starting at address 'addr'.\n"
+),
+
+CMD_TBL_ITEM(
+ help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
+ "Print sub command description/usage",
+ "\n"
+ " - print brief description of all sub commands\n"
+ "fat help command ...\n"
+ " - print detailed usage of sub cmd 'command'"
+),
+
+/* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
+ {FSTR("?"), CONFIG_SYS_MAXARGS, 1, do_help,
+ NULL,
+#ifdef CONFIG_SYS_LONGHELP
+ FSTR(""),
+#endif /* CONFIG_SYS_LONGHELP */
+ NULL,
+#ifdef CONFIG_AUTO_COMPLETE
+ NULL,
+#endif
+},
+/* Mark end of table */
+CMD_TBL_END(cmd_tbl_fat)
+};
+
+
+command_ret_t do_fat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
+{
+ puts_P(PSTR("Huch?"));
+
+ return CMD_RET_USAGE;
+}
diff --git a/avr/cmd_gpio.c b/avr/cmd_gpio.c
new file mode 100644
index 0000000..16c2202
--- /dev/null
+++ b/avr/cmd_gpio.c
@@ -0,0 +1,326 @@
+/*
+ * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "cmd_gpio.h"
+#include "print-utils.h"
+#include "getopt-min.h"
+#include "env.h"
+#include "gpio.h"
+//#include "debug.h"
+
+
+static uint_fast8_t pinnames_get(char *pin_names[GPIO_MAX+1])
+{
+ static const FLASH char delim1[] = {":= "};
+ static const FLASH char delim2[] = {", "};
+ uint_fast8_t width = 0;
+ char *lp;
+ uint_fast8_t i;
+
+ memset(pin_names, 0, (GPIO_MAX+1) * sizeof(char *));
+
+/* TODO: enters endless loop on wrong parameters */
+
+ lp = getenv_str(PSTR(ENV_PINALIAS));
+ if (lp != NULL) {
+ pin_names[GPIO_MAX] = strdup(lp);
+ if (pin_names[GPIO_MAX] != NULL) {
+ char *ptr = strtok_P(pin_names[GPIO_MAX], delim1);
+ while (ptr != NULL) {
+ if (((i = strtoul(ptr, &lp, 10)) < GPIO_MAX) &&
+ lp != ptr &&
+ (ptr = strtok_P(NULL, delim2)) != NULL ) {
+ pin_names[i] = ptr;
+ ptr = strtok_P(NULL, delim1);
+ }
+ }
+
+ for (i = 0; i < GPIO_MAX; i++)
+ if (strlen(pin_names[i]) > width)
+ width = strlen(pin_names[i]);
+ }
+ }
+ return width;
+}
+
+
+static size_t xstrlen(char *s)
+{
+ if (s == NULL)
+ return 0;
+ else
+ return strlen(s);
+}
+
+static const FLASH char * const FLASH pinconf_str[] = {
+ FSTR("?"),
+ FSTR("Input"),
+ FSTR("Pullup"),
+ FSTR("Output"),
+ FSTR("Clock"),
+ };
+
+static const FLASH char * const FLASH pinlevel_str[] = {
+ FSTR("Low"),
+ FSTR("High"),
+ FSTR(""),
+ };
+
+static void print_pin(uint8_t pin, uint_fast8_t multi, char *pin_names[], uint_fast8_t pn_width)
+{
+ gpiomode_t pinconf;
+ const FLASH char *levelp;
+ long div;
+
+ pinconf = gpio_config_get(pin);
+ if (pinconf == OUTPUT_TIMER) {
+ div = gpio_clockdiv_get(pin);
+ levelp = pinlevel_str[2];
+ } else
+ levelp = pinlevel_str[gpio_read(pin)];
+
+ if (multi) {
+ printf_P(PSTR("%3d "), pin);
+ if (pn_width) {
+ printf_P(PSTR("%s "), pin_names[pin]);
+ print_blanks(pn_width - xstrlen(pin_names[pin]));
+ }
+ my_puts_P(pinconf_str[pinconf]);
+ print_blanks(7 - strlen_P(pinconf_str[pinconf]));
+ my_puts_P(levelp);
+ print_blanks(5 - strlen_P(levelp));
+ if (pinconf == OUTPUT_TIMER)
+ printf_P(PSTR("%8ld %8ld"),
+ div, F_CPU/div);
+ } else {
+ printf_P(PSTR("%d: \"%s\", "), pin, pin_names[pin] ? pin_names[pin] : 0);
+ my_puts_P(pinconf_str[pinconf]);
+ my_puts_P(PSTR(", "));
+ my_puts_P(levelp);
+
+ if (pinconf == OUTPUT_TIMER)
+ printf_P(PSTR("divide by %ld (%ldHz)"),
+ div, F_CPU/div);
+ }
+ my_puts_P(PSTR("\n"));
+}
+
+static int_fast8_t pinarg_insert(uint8_t pin, uint_fast8_t count, uint_fast8_t pinarg[])
+{
+ uint_fast8_t pos;
+
+ if (pin >= GPIO_MAX)
+ return -1;
+
+ for (pos = 0; pos < count; pos++) {
+ if (pin == pinarg[pos])
+ return 0;
+ if (pin < pinarg[pos])
+ break;
+ }
+ for (uint_fast8_t i = count-1; i == pos ; i--)
+ pinarg[i+1] = pinarg[i];
+ pinarg[pos] = pin;
+
+ return 1;
+}
+
+static uint_fast8_t pinarg_get(char * arg, uint_fast8_t pinarg[])
+{
+ char *endp;
+ uint_fast8_t pin1;
+ int_fast8_t rc;
+ uint_fast8_t count = 0;
+
+ while (1) {
+ pin1 = strtoul(arg, &endp, 10);
+ if (endp != arg && *endp == '-') {
+ arg = endp+1;
+ uint_fast8_t pin2 = (int) strtoul(arg, &endp, 10);
+ if (pin1 < pin2)
+ for (; pin1 < pin2; pin1++)
+ if ((rc = pinarg_insert(pin1, count, pinarg)) >= 0)
+ count += rc;
+ else
+ return 0;
+ else
+ return 0;
+ }
+ if (endp != arg) {
+ if ((*endp == ',' || *endp == '\0') &&
+ (rc = pinarg_insert(pin1, count, pinarg)) >= 0) {
+ count += rc;
+ if (*endp == '\0')
+ return count;
+ } else
+ return 0;
+ } else
+ return 0;
+
+ arg = endp+1;
+ }
+}
+
+
+command_ret_t do_gpio(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char *const argv[])
+{
+ uint_fast8_t printheader = 1;
+ uint_fast8_t pinarg[GPIO_MAX];
+ uint_fast8_t pinargc;
+
+ int opt;
+ while ((opt = getopt(argc, argv, PSTR("s"))) != -1) {
+ switch (opt) {
+ case 's':
+ printheader = 0;
+ break;
+ default: /* '?' */
+ return CMD_RET_USAGE;
+ }
+ }
+
+ /* remaining arguments */
+ argc -= optind;
+
+ if (argc == 0) {
+ /* print config of all pins */
+ for (pinargc = 0; pinargc < GPIO_MAX; pinargc++)
+ pinarg[pinargc] = pinargc;
+ } else {
+ /* get first arg */
+ pinargc = pinarg_get(argv[optind++], pinarg);
+ if (pinargc == 0)
+ return CMD_RET_USAGE;
+ else
+ argc--;
+ }
+
+
+ if (argc == 0) {
+ /* no more args, print pin config */
+
+ char *pin_names[GPIO_MAX + 1];
+ uint_fast8_t pin_names_width = pinnames_get(pin_names);
+
+ if (pinargc == 1)
+ print_pin(pinarg[0], 0, pin_names, pin_names_width);
+ else {
+ if (printheader) {
+ if (pin_names_width > 0) {
+ if ( strlen("Name") > pin_names_width)
+ pin_names_width = strlen("Name");
+ char s[pin_names_width+1];
+ memset(s, ' ', pin_names_width);
+ s[pin_names_width] = '\0';
+ strncpy_P(s, PSTR("Name"), 4);
+ printf_P(PSTR("Pin %s Config Level Divider Frequency/Hz\n"),s);
+ memset(s, '-', pin_names_width);
+ printf_P(PSTR("----%s-----------------------------------\n"), s);
+ } else
+ printf_P(PSTR("Pin Config Level Divider Frequency/Hz\n"
+ "--------------------------------------\n"));
+ }
+ for (uint_fast8_t i = 0; i < pinargc; i++)
+ print_pin(pinarg[i], 1, pin_names, pin_names_width);
+ }
+ if (pin_names[GPIO_MAX] != NULL)
+ free(pin_names[GPIO_MAX]);
+ return CMD_RET_SUCCESS;
+ }
+
+ /* arguments must be in pairs: pins conf */
+ if (argc % 2 != 1)
+ return CMD_RET_USAGE;
+
+ while (argc > 0) {
+ char *endp;
+ gpiomode_t mode = NONE;
+ int level = 0;
+ unsigned long value = 0;
+ uint8_t hz_flag = 0;
+
+ switch (toupper(argv[optind][0])) {
+ case 'H':
+ level = 1;
+ /* FALL TROUGH */
+ case 'L':
+ mode = OUTPUT;
+ break;
+ case 'P':
+ mode = INPUT_PULLUP;
+ break;
+ case 'I':
+ case 'T':
+ mode = INPUT;
+ break;
+
+ default:
+ value = strtoul(argv[optind], &endp, 10);
+ switch (*endp) {
+ case 'M':
+ value *= 1000;
+ /* FALL TROUGH */
+ case 'K':
+ value *= 1000;
+ endp++;
+ }
+
+ if (*endp && strcmp_P(endp, PSTR("Hz")) == 0) {
+ hz_flag = 1;
+ endp += 2;
+ }
+
+ if (*endp != '\0') {
+ printf_P(PSTR("invalid parameter: '%s'\n"), argv[optind]);
+ return CMD_RET_USAGE;
+ }
+
+ if (value == 0) {
+ printf_P(PSTR("invalid value: %lu \n"));
+ return CMD_RET_USAGE;
+ }
+
+ if (hz_flag) {
+ if (value > F_CPU / 2) {
+ printf_P(PSTR("Max frequency is: %luHz\n"), F_CPU/2);
+ return CMD_RET_USAGE;
+ }
+ value = F_CPU/value;
+ }
+ mode = OUTPUT_TIMER;
+
+ }
+
+ if (mode == NONE)
+ return CMD_RET_USAGE;
+
+ for (uint_fast8_t i = 0; i < pinargc; i++) {
+ switch (mode) {
+ case OUTPUT:
+ gpio_write(pinarg[i], level);
+ /* fall thru */
+ case INPUT:
+ case INPUT_PULLUP:
+ gpio_config(pinarg[i], mode);
+ break;
+ case OUTPUT_TIMER:
+ if (gpio_clockdiv_set(pinarg[i], value) < 0) {
+ printf_P(PSTR("Setting pin %d to %lu failed.\n"),
+ pinarg[i], value);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ optind++;
+ pinargc = pinarg_get(argv[optind++], pinarg);
+ argc -= 2;
+ }
+
+ return CMD_RET_SUCCESS;
+}
diff --git a/avr/cmd_loadcpm3.c b/avr/cmd_loadcpm3.c
new file mode 100644
index 0000000..425d1fd
--- /dev/null
+++ b/avr/cmd_loadcpm3.c
@@ -0,0 +1,195 @@
+/*
+ * (C) Copyright 2015,2016,2018 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+/*
+ * See CP/M 3 System Manual, Appendix D: CPM3.SYS File Format
+ */
+
+#include "cmd_loadcpm3.h"
+
+#include "env.h"
+#include "ff.h"
+#include "eval_arg.h"
+#include "con-utils.h"
+#include "z80-if.h"
+#include "debug.h"
+
+
+#define RS 128 /* CP/M record size */
+
+#define FSIZE_t DWORD
+
+static FRESULT read_record(FIL *fp, uint8_t *buffer)
+{
+ unsigned int br; /* bytes read */
+
+ FRESULT res = f_read(fp, buffer, RS, &br);
+ if (br != RS)
+ return EEOF;
+ return res;
+}
+
+/*
+ * Load Routine
+ *
+ * Input: addr = Page Address of load top
+ * len = Length in pages of module to read
+ *
+ */
+ERRNUM load(FIL *File, uint32_t addr, uint8_t len)
+{
+ uint8_t buffer[RS];
+
+ len *= 2; /* length in records of module */
+ //debug("## load: addr: 0x%.4X, records: 0x%.4X, (%u)\n", addr, len, len);
+
+ for (; len; len--) {
+ addr -= RS;
+ FRESULT res = read_record(File, buffer);
+ if (res)
+ return res;
+
+ if (!(z80_bus_cmd(Request) & ZST_ACQUIRED))
+ return EBUSTO;
+ z80_write_block(buffer, addr, RS);
+ z80_bus_cmd(Release);
+ //debug("## written: 0x%.4X\n", addr);
+ }
+
+ return ESUCCESS;
+}
+
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+
+command_ret_t do_loadcpm3(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ uint16_t mem_top;
+ uint8_t res_len;
+ uint16_t bank_top;
+ uint8_t bank_len;
+ uint16_t osentry_addr = 0;
+ uint32_t common_base = 0;
+ uint32_t banked_base;
+ char *fname;
+ FIL File;
+ char default_fname[strlen_P(PSTR(CONFIG_CPM3_SYSFILE)) + 1];
+ uint8_t buffer[RS];
+ FRESULT res;
+
+
+ //common_base = getenv_ulong(PSTR(ENV_CPM3_COMMON_BASE), 16,
+ // CONFIG_CPM3_COMMON_BASE);
+ banked_base = getenv_ulong(PSTR(ENV_CPM3_BANKED_BASE), 16,
+ CONFIG_CPM3_BANKED_BASE);
+
+ if (argc > 3)
+ banked_base = eval_arg(argv[3], NULL);
+ if (argc > 2)
+ common_base = eval_arg(argv[2], NULL);
+
+ fname = getenv_str(PSTR(ENV_CPM3_SYSFILE));
+ if (fname == NULL || *fname == '\0') {
+ strcpy_P(default_fname, PSTR(CONFIG_CPM3_SYSFILE));
+ fname = default_fname;
+ }
+ if (argc > 1) {
+ fname = argv[1];
+ }
+
+ res = f_open(&File, fname, FA_READ );
+ if (res)
+ goto out;
+
+ printf_P(PSTR("Loading: '%s'...\n"), fname);
+
+ /* read the load record */
+ res = read_record(&File, buffer);
+ if (res)
+ goto out;
+
+ mem_top = buffer[0] << 8;
+ res_len = buffer[1];
+ bank_top = buffer[2] << 8;
+ bank_len = buffer[3];
+ osentry_addr = buffer[4] + (buffer[5] << 8);
+
+ /* read display info */
+ res = read_record(&File, buffer);
+ if (res)
+ goto out;
+
+ /* print the info */
+ buffer[RS-1] = '$';
+ uint8_t *p = memchr(buffer, '$', RS);
+ *p = '\0';
+ my_puts((char *)buffer);
+
+ if (common_base == 0) {
+ /* read common base
+ * http://www.seasip.info/Cpm/scb.html
+ */
+ FSIZE_t common_base_ofs = ((res_len - 6) << 8) + 2*RS + RS-7;
+ FSIZE_t cur_pos = f_tell(&File);
+ unsigned int br; /* bytes read */
+ if ((res = f_lseek(&File, common_base_ofs)) ||
+ (res = f_read(&File, buffer, 2, &br)) ||
+ (res = f_lseek(&File, cur_pos)))
+ goto out;
+ if (br != 2) {
+ res = EEOF;
+ goto out;
+ }
+ common_base = (uint16_t) buffer[0] + (buffer[1] << 8);
+ setenv_hex(PSTR(ENV_CPM3_COMMON_BASE), common_base);
+ }
+
+ setenv_hex(PSTR(ENV_CPM3_SCB), mem_top - ((res_len - (6 - 1)) << 8) + common_base);
+
+ /* Main System Load */
+
+ /* Load Common Portion of System */
+ if ((res = load(&File, common_base + mem_top, res_len)) != 0)
+ goto out;
+
+ /* Load Banked Portion of System */
+ res = load(&File, banked_base + bank_top, bank_len);
+
+out:
+ f_close(&File);
+
+ if (res)
+ cmd_error(CMD_RET_FAILURE, res, PSTR("%s"), fname);
+
+ if (res_len != 0) {
+ if (osentry_addr + common_base > 0xffff) {
+ z80_bus_cmd(Request);
+ if (z80_read(osentry_addr + common_base) == 0xc3) {
+ osentry_addr = z80_read(osentry_addr+common_base+1) +
+ (z80_read(osentry_addr + common_base+2) << 8);
+ }
+ z80_bus_cmd(Release);
+ if (banked_base + osentry_addr > 0xffff)
+ osentry_addr = 0;
+ }
+ setenv_hex(PSTR(ENV_STARTADDRESS), osentry_addr);
+ }
+ printf_P(PSTR("Loaded: Resident: "));
+ if (res_len != 0)
+ printf_P(PSTR("%.5lX-%.5lX, "),
+ (common_base + mem_top) - res_len*256,
+ (common_base + mem_top) - 1);
+ else
+ printf_P(PSTR(" - "));
+ printf_P(PSTR("Banked: "));
+ if (bank_len != 0)
+ printf_P(PSTR("%.5lX-%.5lX\n"),
+ (banked_base + bank_top) - bank_len*256,
+ (banked_base + bank_top) - 1);
+ else
+ printf_P(PSTR(" - \n"));
+
+ return CMD_RET_SUCCESS;
+}
diff --git a/avr/cmd_loadihex.c b/avr/cmd_loadihex.c
new file mode 100644
index 0000000..19cd29b
--- /dev/null
+++ b/avr/cmd_loadihex.c
@@ -0,0 +1,216 @@
+/*
+ * (C) Copyright 2015 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "cmd_loadihex.h"
+
+#include "con-utils.h"
+#include "z80-if.h"
+#include "debug.h"
+
+
+typedef enum {
+ IHX_OK,
+ IHX_BROKEN,
+ IHX_CHKSUMERR
+} ihx_rstat_t;
+
+typedef struct {
+ ihx_rstat_t status;
+ int8_t type;
+ uint8_t len;
+ uint16_t address;
+ uint8_t data[256];
+} ihex_t;
+
+
+static int get_hexdigit(void)
+{
+ int c;
+ c = toupper(my_getchar(1));
+ if (isxdigit(c)) {
+ c -= '0';
+ if (c > 9)
+ c -= ('A' - '0' - 10);
+ return c;
+ } else
+ return -1;
+}
+
+static int get_hexbyte(void)
+{
+ uint8_t i,j;
+
+ if ((i = (uint8_t) get_hexdigit()) < 0x10)
+ if ((j = (uint8_t) get_hexdigit()) < 0x10) {
+ return (i<<4) + j;
+ }
+
+ return -1;
+}
+
+
+static int ihex_get_record(ihex_t *rec)
+{
+ int c;
+ uint8_t sum;
+
+ rec->status = IHX_BROKEN;
+ rec->len = 0;
+ rec->type = 0xff;
+
+ while ((c = my_getchar(0)) != ':') {
+ if (c == 0x03)
+ return -1; /* Control-C */
+ if (c == 0x04) {
+ rec->status = IHX_OK;
+ return 0; /*Control-D, EOF */
+ }
+ }
+
+ if ((c = get_hexbyte()) < 0) /* Start code */
+ return -1;
+ sum = c;
+ rec->len = c;
+ if ((c = get_hexbyte()) < 0) /* Byte Count */
+ return -1;
+ sum += c;
+ rec->address = c * 256;
+ if ((c = get_hexbyte()) < 0) /* Address */
+ return -1;
+ sum += c;
+ rec->address += c;
+ if ((c = get_hexbyte()) < 0) /* Record type */
+ return -1;
+ sum += c;
+ rec->type = c;
+
+ if (rec->len) { /* Record Data */
+ uint8_t n;
+
+ for (n = 0; n < rec->len; n++) {
+ if ((c = get_hexbyte()) < 0)
+ break;
+ sum += c;
+ rec->data[n] = c;
+ }
+
+ if (n < rec->len) {
+ return -1;
+ }
+ }
+
+ c = get_hexbyte(); /* Check sum */
+
+ if (c >= 0) {
+ sum += c;
+ if (sum == 0)
+ rec->status = IHX_OK;
+ else
+ rec->status = IHX_CHKSUMERR;
+ }
+
+ return rec->len;
+}
+
+
+command_ret_t do_loadihex(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ long offset = 0;
+ uint32_t base_address = 0;
+ uint32_t address_high = 0;
+ uint32_t address_max;
+ uint32_t address_low;
+ bool firstrec = true;
+ ihex_t rec;
+
+ if (argc > 1)
+ offset = strtol(argv[1], NULL, 16);
+
+ int32_t ram = z80_memsize_detect();
+ if (ram < 0)
+ cmd_error(CMD_RET_FAILURE, (ERRNUM) -ram, NULL);
+
+ address_max = ram;
+ address_low = address_max;
+
+ my_puts_P(PSTR("Waiting for Intel Hex Records...\n"));
+
+ while (ihex_get_record(&rec) > 0 &&
+ rec.status == IHX_OK &&
+ rec.type != 1 ) {
+
+ switch (rec.type) {
+ case 0: /* Data record */
+ if (firstrec) {
+ my_puts_P(PSTR("Loading: 0x....."));
+ firstrec = false;
+ }
+ if (rec.len) {
+ uint32_t addr = base_address + rec.address + offset;
+ if (addr < address_low)
+ address_low = addr;
+ if (addr+rec.len > address_high)
+ address_high = addr + rec.len;
+
+// debug("low: 0x%.5lX, high: 0x%.5lX, max: 0x%.5lX, addr: 0x%.5lX, len: %d\n",
+// address_low, address_high, address_max, addr, rec.len);
+ printf_P(PSTR("\b\b\b\b\b%.5lX"), addr);
+ if (addr < address_max) {
+ uint32_t tmplen = address_max - addr;
+ if (rec.len > tmplen)
+ rec.len = tmplen;
+
+ z80_bus_request_or_exit();
+ z80_write_block(rec.data, /* src */
+ addr, /* dest */
+ rec.len); /* len */
+ z80_bus_cmd(Release);
+ }
+ }
+ break;
+
+#if 0
+ case 1: /* EOF record */
+ break;
+#endif
+ case 2: /* Extended Segment Address Record */
+ base_address = (uint32_t)((rec.data[0] << 8) + rec.data[1]) << 4;
+ break;
+
+ case 4: /* Extended Linear Address Record */
+ base_address = (uint32_t)((rec.data[0] << 8) + rec.data[1]) << 16;
+ break;
+
+ case 3: /* Start Segment Address Record (ignored)*/
+ case 5: /* Start Linear Address Record (ignored)*/
+ break;
+
+ }
+ }
+
+ if (rec.status != IHX_OK)
+ my_puts_P(PSTR("Broken Hex Record or loading interrupted!\n"));
+
+ for (uint_fast8_t i=0; i<100; ++i) {
+ /* flush input buffer */
+ while (my_getchar(0) > 0)
+ ;
+ udelay(1000);
+ }
+
+ my_puts_P(PSTR("\nData loaded: "));
+ if (address_low >= MIN(address_high, address_max))
+ my_puts_P(PSTR("None.\n"));
+ else
+ printf_P(PSTR("low: 0x%.5lX high: 0x%.5lX\n"), address_low,
+ MIN(address_high, address_max) - 1);
+
+ if (address_high > address_max)
+ printf_P(PSTR("Data above highest RAM address "
+ "(in range 0x%.5lX - 0x%.5lX) ignored!\n"), address_max, address_high - 1);
+
+ return rec.status == IHX_OK ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
diff --git a/avr/cmd_mem.c b/avr/cmd_mem.c
new file mode 100644
index 0000000..3e18770
--- /dev/null
+++ b/avr/cmd_mem.c
@@ -0,0 +1,774 @@
+/*
+ * (C) Copyright 2014,2018 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+/*
+ * Memory Functions
+ *
+ * Copied from FADS ROM, Dan Malek (dmalek@jlc.net)
+ */
+
+#include "cmd_mem.h"
+#include <avr/interrupt.h>
+
+#include "cli_readline.h"
+#include "print-utils.h"
+#include "con-utils.h"
+#include "getopt-min.h"
+#include "eval_arg.h"
+#include "timer.h"
+#include "z80-if.h"
+#include "debug.h"
+
+
+#ifndef CONFIG_SYS_MEMTEST_SCRATCH
+#define CONFIG_SYS_MEMTEST_SCRATCH 0
+#endif
+
+/* Display values from last command.
+ * Memory modify remembered values are different from display memory.
+ */
+static uint32_t dp_last_addr;
+static uint32_t dp_last_length = 0x100;
+static uint32_t mm_last_addr;
+
+static uint32_t base_address = 0;
+
+/*--------------------------------------------------------------------------*/
+
+static ERRNUM z180_read_buf(uint8_t *buf, uint32_t addr, uint8_t count)
+{
+ if (!(z80_bus_cmd(Request) & ZST_ACQUIRED))
+ return EBUSTO;
+
+ z80_read_block (buf, addr, count);
+ z80_bus_cmd(Release);
+ return ESUCCESS;
+}
+
+/*--------------------------------------------------------------------------*/
+
+command_ret_t do_mem_size(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
+{
+ int32_t ramsize = z80_memsize_detect();
+
+ if (ramsize < 0)
+ cmd_error(CMD_RET_FAILURE, (ERRNUM) -ramsize, PSTR("Couldn't access RAM"));
+
+ printf_P(PSTR("Detected RAM: Start %.5lx, End: %.5lx, Size: %.5lx (%ld dec)\n"),
+ 0l, ramsize ? ramsize-1 : 0l, ramsize, ramsize);
+
+ return CMD_RET_SUCCESS;
+}
+
+/* Memory Display
+ *
+ * Syntax:
+ * md {addr} {len}
+ */
+command_ret_t do_mem_md(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ uint32_t addr, length;
+
+ (void) cmdtp;
+
+#if 0
+ printf_P(PSTR("flag: %d, argc: %d"), flag, argc);
+ for (int i = 0; i < argc; i++) {
+ printf_P(PSTR(", argv[%d]: %s"), i, argv[i] ? argv[i] : "<NULL>");
+ }
+ putchar('\n');
+#endif
+
+ /* We use the last specified parameters, unless new ones are
+ * entered.
+ */
+ addr = dp_last_addr;
+ length = dp_last_length;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ if ((flag & CMD_FLAG_REPEAT) == 0) {
+ /* Address is specified since argc > 1 */
+ addr = eval_arg(argv[1], NULL);
+ addr += base_address;
+
+ /* If another parameter, it is the length to display. */
+ if (argc > 2)
+ length = eval_arg(argv[2], NULL);
+ }
+
+ /* Print the lines. */
+ ERRNUM ret = dump_mem(addr, addr, length, z180_read_buf, NULL);
+ if (ret == EBUSTO)
+ cmd_error(CMD_RET_FAILURE, ret, NULL);
+
+ if (ret == ESUCCESS) {
+ dp_last_addr = addr + length;
+ dp_last_length = length;
+ }
+ return CMD_RET_SUCCESS;
+}
+
+/* Modify memory.
+ *
+ * Syntax:
+ * mm {addr}
+ * nm {addr}
+ */
+static command_ret_t
+mod_mem(cmd_tbl_t *cmdtp, int incrflag, uint_fast8_t flag, int argc, char * const argv[])
+{
+ uint32_t addr;
+ uint8_t data;
+ int nbytes;
+
+ (void) cmdtp;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ /* We use the last specified parameters, unless new ones are
+ * entered.
+ */
+ addr = mm_last_addr;
+
+ if ((flag & CMD_FLAG_REPEAT) == 0) {
+ /* New command specified.
+ */
+
+ /* Address is specified since argc > 1
+ */
+ addr = eval_arg(argv[1], NULL);
+ addr += base_address;
+ }
+
+ /* Print the address, followed by value. Then accept input for
+ * the next value. A non-converted value exits.
+ */
+ do {
+ z80_bus_request_or_exit();
+ data = z80_read(addr);
+ z80_bus_cmd(Release);
+ printf_P(PSTR("%05lx: %02x"), addr, data);
+
+ nbytes = cli_readline(PSTR(" ? "), 0);
+ if (nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')) {
+ /* <CR> pressed as only input, don't modify current
+ * location and move to next. "-" pressed will go back.
+ */
+ if (incrflag)
+ addr += nbytes ? -1 : 1;
+ nbytes = 1;
+
+ } else {
+ char *endp;
+ data = eval_arg(console_buffer, &endp);
+ nbytes = endp - console_buffer;
+ if (nbytes) {
+ z80_bus_request_or_exit();
+ z80_write(addr, data);
+ z80_bus_cmd(Release);
+ if (incrflag)
+ addr++;
+ }
+ }
+ } while (nbytes > 0);
+
+ mm_last_addr = addr;
+ return CMD_RET_SUCCESS;
+}
+
+
+command_ret_t do_mem_mm(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ return mod_mem (cmdtp, 1, flag, argc, argv);
+}
+command_ret_t do_mem_nm(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ return mod_mem (cmdtp, 0, flag, argc, argv);
+}
+
+command_ret_t do_mem_mw(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ uint32_t writeval;
+ uint32_t addr;
+ uint32_t count = 1;
+ uint_fast8_t width = 1;
+
+ (void) cmdtp; (void) flag;
+
+ int opt;
+ while ((opt = getopt(argc, argv, PSTR("bwl"))) != -1) {
+ switch (opt) {
+ case 'b':
+ width = 1;
+ break;
+ case 'w':
+ width = 2;
+ break;
+ case 'l':
+ width = 4;
+ break;
+ default: /* '?' */
+ return CMD_RET_USAGE;
+ }
+ }
+
+ /* remaining arguments */
+ argc -= optind;
+ if ((argc < 2) || (argc > 3))
+ return CMD_RET_USAGE;
+
+ /* Address and value are specified since (adjusted) argc >= 2 */
+ addr = eval_arg(argv[optind++], NULL);
+ addr += base_address;
+ writeval = eval_arg(argv[optind++], NULL);
+
+ /* Count ? */
+ if (argc == 3)
+ count = eval_arg(argv[optind], NULL);
+
+ z80_bus_request_or_exit();
+ if (width == 1)
+ z80_memset(addr, writeval, count);
+ else {
+ while (count--) {
+ z80_write_block((const uint8_t *) &writeval, addr, width);
+ addr += width;
+ }
+ }
+ z80_bus_cmd(Release);
+
+ return CMD_RET_SUCCESS;
+}
+
+#ifdef CONFIG_MX_CYCLIC
+command_ret_t do_mem_mdc ( cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ uint32_t count;
+ uint32_t ts;
+
+ (void) cmdtp;
+ (void) flag;
+
+ if (argv[0][1] != 'd') {
+ int opt;
+ while ((opt = getopt(argc, argv, PSTR("bwl"))) != -1)
+ if (opt == '?')
+ return CMD_RET_USAGE;
+ --optind;
+ }
+
+ if (argc-optind != 4)
+ return CMD_RET_USAGE;
+
+ count = eval_arg(argv[optind + 3], NULL);
+
+ clear_ctrlc(); /* forget any previous Control C */
+ for (;;) {
+
+ if (argv[0][1] == 'd')
+ do_mem_md (NULL, 0, argc-1, argv); /* memory display */
+ else
+ do_mem_mw (NULL, 0, argc-1, argv); /* memory write */
+
+
+ /* delay for <count> ms... */
+ ts = get_timer(0);
+ do {
+ /* check for ctrl-c to abort... */
+ if (had_ctrlc() || ctrlc()) {
+ my_puts_P(PSTR("Abort\n"));
+ return CMD_RET_SUCCESS;
+ }
+ } while (get_timer(ts) < count);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+#endif /* CONFIG_MX_CYCLIC */
+
+command_ret_t do_mem_cmp(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ uint32_t addr1, addr2, count, ngood;
+ command_ret_t rcode = CMD_RET_SUCCESS;
+ uint8_t byte1, byte2;
+
+ (void) cmdtp;
+ (void) flag;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+
+ addr1 = eval_arg(argv[1], NULL);
+ addr1 += base_address;
+ addr2 = eval_arg(argv[2], NULL);
+ addr2 += base_address;
+ count = eval_arg(argv[3], NULL);
+
+ for (ngood = 0; ngood < count; ++ngood) {
+ if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
+ my_puts_P(PSTR("Bus timeout\n"));
+ rcode = CMD_RET_FAILURE;
+ break;
+ }
+ byte1 = z80_read(addr1);
+ byte2 = z80_read(addr2);
+ z80_bus_cmd(Release);
+ if (byte1 != byte2) {
+ printf_P(PSTR("byte at 0x%05lx (%#02x) != "
+ "byte at 0x%05lx (%#02x)\n"),
+ addr1, byte1, addr2, byte2);
+ rcode = CMD_RET_FAILURE;
+ break;
+ }
+ addr1++;
+ addr2++;
+
+ /* check for ctrl-c to abort... */
+ if (ctrlc()) {
+ my_puts_P(PSTR("Abort\n"));
+ return CMD_RET_SUCCESS;
+ }
+ }
+
+ printf_P(PSTR("Total of %ld byte(s) (0x%lx) were the same\n"), ngood, ngood);
+ return rcode;
+}
+
+command_ret_t do_mem_cp(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ uint32_t src, dest, count;
+ int_fast8_t step;
+
+ (void) cmdtp;
+ (void) flag;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ src = eval_arg(argv[1], NULL);
+ src += base_address;
+ dest = eval_arg(argv[2], NULL);
+ dest += base_address;
+ count = eval_arg(argv[3], NULL);
+
+ if (count == 0) {
+ my_puts_P(PSTR("Zero length?\n"));
+ return CMD_RET_FAILURE;
+ }
+
+ if (dest > src) {
+ src += count - 1;
+ dest += count - 1;
+ step = -1;
+ } else
+ step = 1;
+
+ while (count-- > 0) {
+ uint8_t data;
+ z80_bus_request_or_exit();
+ data = z80_read(src);
+ z80_write(dest, data);
+ z80_bus_cmd(Release);
+ src += step;
+ dest += step;
+
+ /* check for ctrl-c to abort... */
+ if (ctrlc()) {
+ my_puts_P(PSTR("Abort\n"));
+ return CMD_RET_SUCCESS;
+ }
+ }
+ return CMD_RET_SUCCESS;
+}
+
+command_ret_t do_mem_base(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc,
+ char * const argv[])
+{
+ (void) cmdtp;
+ (void) flag;
+
+ if (argc > 1) {
+ /* Set new base address. */
+ base_address = eval_arg(argv[1], NULL);
+ }
+ /* Print the current base address. */
+ printf_P(PSTR("Base Address: 0x%05lx\n"), base_address);
+ return CMD_RET_SUCCESS;
+}
+
+command_ret_t do_mem_loop(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc,
+ char * const argv[])
+{
+ uint32_t addr, length;
+
+ (void) cmdtp;
+ (void) flag;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ /* Address is always specified. */
+ addr = eval_arg(argv[1], NULL);
+
+ /* Length is the number of bytes. */
+ length = eval_arg(argv[2], NULL);
+
+
+ /* We want to optimize the loops to run as fast as possible.
+ * If we have only one object, just run infinite loops.
+ */
+ if (length == 1) {
+ z80_bus_request_or_exit();
+ cli();
+ for (;;)
+ z80_read(addr);
+ }
+
+ z80_bus_request_or_exit();
+ cli();
+ for (;;) {
+ uint32_t i = length;
+ uint32_t p = addr;
+ while (i-- > 0)
+ z80_read(p++);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+command_ret_t do_mem_loopw (cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ uint32_t addr, length;
+ uint8_t data;
+
+ (void) cmdtp;
+ (void) flag;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ /* Address is always specified. */
+ addr = eval_arg(argv[1], NULL);
+
+ /* Length is the number of bytes. */
+ length = eval_arg(argv[2], NULL);
+
+ data = eval_arg(argv[3], NULL);
+
+ /*
+ * We want to optimize the loops to run as fast as possible.
+ * If we have only one object, just run infinite loops.
+ */
+ if (length == 1) {
+ z80_bus_request_or_exit();
+ cli();
+ for (;;)
+ z80_write(addr, data);
+ }
+
+ z80_bus_request_or_exit();
+ cli();
+ for (;;) {
+ uint32_t i = length;
+ uint32_t p = addr;
+ while (i-- > 0)
+ z80_write(p++, data);
+ }
+}
+
+//#define CONFIG_SYS_ALT_MEMTEST
+
+#ifdef CONFIG_CMD_MEMTEST
+static uint32_t mem_test_alt(uint32_t start_addr, uint32_t end_addr)
+{
+ uint32_t addr;
+ uint32_t dummy;
+ uint32_t errs = 0;
+ uint32_t offset;
+ uint32_t test_offset;
+ uint8_t pattern;
+ uint8_t anti_pattern;
+ uint8_t temp;
+ uint32_t num_bytes;
+
+ static const FLASH uint8_t bitpattern[] = {
+ 0x01, /* single bit */
+ 0x03, /* two adjacent bits */
+ 0x07, /* three adjacent bits */
+ 0x0F, /* four adjacent bits */
+ 0x05, /* two non-adjacent bits */
+ 0x15, /* three non-adjacent bits */
+ 0x55, /* four non-adjacent bits */
+ 0xaa, /* alternating 1/0 */
+ };
+
+ /*
+ * Data line test: write a pattern to the first
+ * location, write the 1's complement to a 'parking'
+ * address (changes the state of the data bus so a
+ * floating bus doesn't give a false OK), and then
+ * read the value back. Note that we read it back
+ * into a variable because the next time we read it,
+ * it might be right (been there, tough to explain to
+ * the quality guys why it prints a failure when the
+ * "is" and "should be" are obviously the same in the
+ * error message).
+ *
+ * Rather than exhaustively testing, we test some
+ * patterns by shifting '1' bits through a field of
+ * '0's and '0' bits through a field of '1's (i.e.
+ * pattern and ~pattern).
+ */
+ addr = start_addr;
+ dummy = start_addr+1;
+ for (unsigned int j = 0; j < ARRAY_SIZE(bitpattern); j++) {
+ pattern = bitpattern[j];
+ for (; pattern != 0; pattern <<= 1) {
+ anti_pattern = ~pattern;
+ z80_write(addr, pattern);
+ z80_write(dummy, anti_pattern); /* clear the test data off the bus */
+ temp = z80_read(addr);
+ if (temp != pattern) {
+ printf_P(PSTR("FAILURE (data line): "
+ "expected %02x, actual %02x\n"),
+ pattern, temp);
+ errs++;
+ }
+ z80_write(addr, anti_pattern);
+ z80_write(dummy, pattern); /* clear the test data off the bus */
+ temp = z80_read(addr);
+ if (temp != anti_pattern) {
+ printf_P(PSTR("FAILURE (data line): "
+ "Is %02x, should be %02x\n"),
+ temp, anti_pattern);
+ errs++;
+ }
+ }
+
+ if (ctrlc())
+ return -1;
+ }
+
+ if (errs)
+ return errs;
+
+ /*
+ * Based on code whose Original Author and Copyright
+ * information follows: Copyright (c) 1998 by Michael
+ * Barr. This software is placed into the public
+ * domain and may be used for any purpose. However,
+ * this notice must not be changed or removed and no
+ * warranty is either expressed or implied by its
+ * publication or distribution.
+ */
+
+ /*
+ * Address line test
+
+ * Description: Test the address bus wiring in a
+ * memory region by performing a walking
+ * 1's test on the relevant bits of the
+ * address and checking for aliasing.
+ * This test will find single-bit
+ * address failures such as stuck-high,
+ * stuck-low, and shorted pins. The base
+ * address and size of the region are
+ * selected by the caller.
+
+ * Notes: For best results, the selected base
+ * address should have enough LSB 0's to
+ * guarantee single address bit changes.
+ * For example, to test a 64-Kbyte
+ * region, select a base address on a
+ * 64-Kbyte boundary. Also, select the
+ * region size as a power-of-two if at
+ * all possible.
+ *
+ * Returns: 0 if the test succeeds, 1 if the test fails.
+ */
+
+ num_bytes = (end_addr - start_addr) / sizeof(uint8_t);
+
+ pattern = 0xaa;
+ anti_pattern = 0x55;
+
+// debug("## %s:%d: length = 0x%.5lx\n", __func__, __LINE__, num_bytes);
+ /*
+ * Write the default pattern at each of the
+ * power-of-two offsets.
+ */
+ for (offset = 1; offset < num_bytes; offset <<= 1)
+ z80_write(addr+offset, pattern);
+
+ /*
+ * Check for address bits stuck high.
+ */
+ z80_write(start_addr, anti_pattern);
+
+ for (offset = 1; offset < num_bytes; offset <<= 1) {
+ temp = z80_read(start_addr + offset);
+ if (temp != pattern) {
+ printf_P(PSTR("FAILURE: Address bit stuck high @ 0x%.5lx:"
+ " expected 0x%.2x, actual 0x%.2x\n"),
+ start_addr + offset, pattern, temp);
+ errs++;
+ if (ctrlc())
+ return -1;
+ }
+ }
+ z80_write(start_addr, pattern);
+
+ /*
+ * Check for addr bits stuck low or shorted.
+ */
+ for (test_offset = 1; test_offset < num_bytes; test_offset <<= 1) {
+ z80_write(start_addr + test_offset, anti_pattern);
+
+ for (offset = 1; offset < num_bytes; offset <<= 1) {
+ temp = z80_read(start_addr + offset);
+ if ((temp != pattern) && (offset != test_offset)) {
+ printf_P(PSTR("FAILURE: Address bit stuck low or shorted"
+ " @ 0x%.5lx: expected 0x%.2x, actual 0x%.2x\n"),
+ start_addr + offset, pattern, temp);
+ errs++;
+ if (ctrlc())
+ return -1;
+ }
+ }
+ z80_write(start_addr + test_offset, pattern);
+ }
+
+ if (errs)
+ return errs;
+
+ /*
+ * Description: Test the integrity of a physical
+ * memory device by performing an
+ * increment/decrement test over the
+ * entire region. In the process every
+ * storage bit in the device is tested
+ * as a zero and a one. The base address
+ * and the size of the region are
+ * selected by the caller.
+ *
+ * Returns: 0 if the test succeeds, 1 if the test fails.
+ */
+ num_bytes++;
+
+ /*
+ * Fill memory with a known pattern.
+ */
+ for (pattern = 1, addr = start_addr; addr <= end_addr; pattern++, addr++)
+ z80_write(addr, pattern);
+
+ /*
+ * Check each location and invert it for the second pass.
+ */
+ for (pattern = 1, addr = start_addr; addr <= end_addr; pattern++, addr++) {
+ temp = z80_read(addr);
+ if (temp != pattern) {
+ printf_P(PSTR("FAILURE (read/write) @ 0x%.5lx:"
+ " expected 0x%.2x, actual 0x%.2x)\n"),
+ addr, pattern, temp);
+ errs++;
+ if (ctrlc())
+ return -1;
+ }
+
+ anti_pattern = ~pattern;
+ z80_write(addr, anti_pattern);
+ }
+
+ /*
+ * Check each location for the inverted pattern and zero it.
+ */
+ for (pattern = 1, addr = start_addr; addr <= end_addr; pattern++, addr++) {
+ anti_pattern = ~pattern;
+ temp = z80_read(addr);
+ if (temp != anti_pattern) {
+ printf_P(PSTR("FAILURE (read/write) @ 0x%.5lx:"
+ " expected 0x%.2x, actual 0x%.2x)\n"),
+ start_addr, anti_pattern, temp);
+ errs++;
+ if (ctrlc())
+ return -1;
+ }
+ z80_write(addr, 0);
+ }
+
+ return errs;
+}
+
+/*
+ * Perform a memory test. A more complete alternative test can be
+ * configured using CONFIG_SYS_ALT_MEMTEST. The complete test loops until
+ * interrupted by ctrl-c or by a failure of one of the sub-tests.
+ */
+command_ret_t do_mem_mtest(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc,
+ char * const argv[])
+{
+ uint32_t start = 0;
+ uint32_t end;
+ unsigned int iteration_limit = 0;
+ unsigned int iteration;
+ uint32_t errs = 0; /* number of errors */
+ int ret;
+
+ (void) cmdtp;
+ (void) flag;
+
+ if (argc > 1)
+ start = eval_arg(argv[1], NULL);
+
+ if (argc > 2)
+ end = eval_arg(argv[2], NULL);
+ else
+ end = CONFIG_SYS_RAMSIZE_MAX - 1;
+
+ if (argc > 3)
+ iteration_limit = (unsigned int) eval_arg(argv[3], NULL);
+
+ printf_P(PSTR("Testing %05lx ... %05lx:\n"), start, end);
+// debug("## %s:%d: start %#05lx end %#05lx\n", __func__, __LINE__, start, end);
+
+ clear_ctrlc(); /* forget any previous Control C */
+
+ for (iteration = 0;
+ !iteration_limit || iteration < iteration_limit;
+ iteration++) {
+
+ printf_P(PSTR("Iteration: %6d\r"), iteration + 1);
+// debug("\n");
+
+ z80_bus_request_or_exit();
+ errs += mem_test_alt(start, end);
+ z80_bus_cmd(Release);
+
+ if (had_ctrlc() || ctrlc()) {
+ break;
+ }
+ }
+
+ if (had_ctrlc()) {
+ /* Memory test was aborted - write a newline to finish off */
+ putchar('\n');
+ ret = CMD_RET_FAILURE;
+ } else {
+ printf_P(PSTR("Tested %d iteration(s) with %lu errors.\n"),
+ iteration, errs);
+ ret = errs ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_CMD_MEMTEST */
diff --git a/avr/cmd_misc.c b/avr/cmd_misc.c
new file mode 100644
index 0000000..63a510c
--- /dev/null
+++ b/avr/cmd_misc.c
@@ -0,0 +1,110 @@
+/*
+ * (C) Copyright 2014,2016 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * Copyright 2000-2009
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "cmd_misc.h"
+#include "eval_arg.h"
+#include <stdbool.h>
+
+#include "timer.h"
+#include "con-utils.h"
+#include "getopt-min.h"
+
+
+command_ret_t do_echo(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ bool put_newline = true;
+
+ (void) cmdtp; (void) flag;
+
+ int opt;
+ while ((opt = getopt(argc, argv, PSTR("n"))) != -1) {
+ switch (opt) {
+ case 'n':
+ put_newline = false;
+ break;
+ default: /* '?' */
+ return CMD_RET_USAGE;
+ }
+ }
+
+ for (uint_fast8_t i = optind; i < argc; i++) {
+
+ if (i != optind)
+ putchar(' ');
+
+ my_puts(argv[i]);
+ }
+
+ if (put_newline)
+ putchar('\n');
+
+ return CMD_RET_SUCCESS;
+}
+
+
+command_ret_t do_sleep(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ unsigned long start = get_timer(0);
+ unsigned long delay;
+ char *sp;
+ uint_fast8_t millisec = 0;
+
+ (void) cmdtp; (void) flag;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ delay = eval_arg(argv[1], &sp);
+
+ if (*sp == 'm') {
+ millisec = 1;
+ sp++;
+ }
+ if (*sp == 's')
+ sp++;
+ if (*sp != '\0')
+ return CMD_RET_USAGE;
+
+ if (!millisec)
+ delay *= 1000;
+
+ while (get_timer(start) < delay) {
+ if (ctrlc())
+ return CMD_RET_FAILURE;
+
+ udelay(100);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+command_ret_t do_time(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ uint32_t elapsed_ms = 0;
+ command_ret_t retval;
+ uint_fast8_t repeatable;
+
+ if (argc == 1)
+ return CMD_RET_USAGE;
+
+ elapsed_ms = get_timer(0);
+ retval = cmd_process(0, argc - 1, argv + 1, &repeatable);
+ elapsed_ms = get_timer(elapsed_ms);
+
+ uint32_t min;
+ uint16_t sec, ms;
+
+ min = elapsed_ms / 1000 / 60;
+ sec = (elapsed_ms / 1000) % 60;
+ ms = elapsed_ms % 1000;
+
+ printf_P(PSTR("\ntime: %lum %u.%03us\n"), min, sec, ms);
+
+ return retval;
+}
diff --git a/avr/cmd_run.c b/avr/cmd_run.c
new file mode 100644
index 0000000..6482d0b
--- /dev/null
+++ b/avr/cmd_run.c
@@ -0,0 +1,86 @@
+/*
+ * (C) Copyright 2016, 2018 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "cmd_run.h"
+
+#include "ff.h"
+#include "cli_readline.h" /* console_buffer[] */
+#include "cli.h" /* run_command() */
+#include "env.h"
+
+
+command_ret_t do_run(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag, int argc, char * const argv[])
+{
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ for (int i = 1; i < argc; ++i) {
+ char *arg;
+
+ arg = getenv_str(argv[i]);
+ if (arg == NULL) {
+ printf_P(PSTR("run: \"%s\" is not set\n"), argv[i]);
+ return CMD_RET_FAILURE;
+ }
+
+ if (run_command(arg, flag) != 0)
+ return CMD_RET_FAILURE;
+ }
+ return CMD_RET_SUCCESS;
+}
+
+command_ret_t do_bootd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag, int argc UNUSED, char * const argv[] UNUSED)
+{
+ return run_command(getenv_str("bootcmd"), flag);
+}
+
+static int source(FIL *fp, uint_fast8_t flag, int argc UNUSED, char * const argv[] UNUSED)
+{
+ int lineno = 0;
+ int res = 0;
+
+ while (!f_eof(fp) && !f_error(fp) && !res) {
+ lineno++;
+ if (f_gets(console_buffer, CONFIG_SYS_CBSIZE, fp)) {
+ int i = strlen(console_buffer) - 1;
+ if (i != 0) {
+ if (i > 0) {
+ if (console_buffer[i] != '\n') {
+ printf_P(PSTR("Error: line %d to long\n"), lineno);
+ res = -1;
+ break;
+ }
+ }
+ console_buffer[i] = 0;
+ res = run_command(console_buffer, flag);
+ }
+ }
+ }
+ return !f_eof(fp) || res;
+}
+
+command_ret_t do_source(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag, int argc, char * const argv[])
+{
+ FIL File;
+ int res;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ res = f_open(&File, argv[1], FA_READ );
+ if (res) {
+ printf_P(PSTR("Error: failed to open script '%s'\n"), argv[1]);
+ return CMD_RET_FAILURE;
+ }
+
+ printf_P(PSTR("Executing script: '%s'...\n"), argv[1]);
+ res = source(&File, flag, --argc, ++argv);
+ f_close(&File);
+ if (res != 0) {
+ return CMD_RET_FAILURE;
+ }
+ return CMD_RET_SUCCESS;
+}
diff --git a/avr/cmd_sd.c b/avr/cmd_sd.c
new file mode 100644
index 0000000..344a197
--- /dev/null
+++ b/avr/cmd_sd.c
@@ -0,0 +1,373 @@
+/*
+ * (C) Copyright 2014, 2018 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "cmd_sd.h"
+
+#include "diskio.h"
+#include "ff.h"
+#include "eval_arg.h"
+#include "print-utils.h"
+#include "z80-if.h"
+
+
+/*
+ * status <pd#> - Show socket status
+ *
+ */
+static
+command_ret_t do_status(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ DSTATUS res;
+ BYTE dev;
+
+ (void) cmdtp; (void) flag;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ dev = (BYTE) eval_arg(argv[1], NULL);
+ res = disk_status(dev);
+ printf_P(PSTR("Socket status: %02x\n"), res);
+
+ return CMD_RET_SUCCESS;
+}
+
+/*
+ * init <pd#> - Initialize disk
+ *
+ */
+static
+command_ret_t do_init(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ DSTATUS res;
+ BYTE dev;
+
+ (void) cmdtp; (void) flag;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ dev = (BYTE) eval_arg(argv[1], NULL);
+
+ if (disk_status(dev) & STA_NODISK)
+ cmd_error(CMD_RET_FAILURE, 0, PSTR("No disk"));
+
+ res = disk_initialize(dev);
+ printf_P(PSTR("rc=%.2x\n"), res);
+
+ if (res & (STA_NODISK | STA_NOINIT))
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
+
+/*
+ * info <pd#> - Show disk info
+ *
+ */
+static
+command_ret_t do_info(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ DSTATUS res;
+ BYTE dev;
+
+ union {
+ unsigned char uca[64];
+ unsigned long ul;
+ unsigned char uc;
+ } dat;
+
+ (void) cmdtp; (void) flag;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ dev = (BYTE) eval_arg(argv[1], NULL);
+
+ res = disk_status(dev);
+ if (res & (STA_NODISK | STA_NOINIT)) {
+ printf_P(res & STA_NODISK ?
+ PSTR("No disk\n") : PSTR("Not initialized\n"));
+ return CMD_RET_FAILURE;
+ }
+
+ if (disk_ioctl(dev, GET_SECTOR_COUNT, &dat.ul) == RES_OK)
+ printf_P(PSTR("Drive size: %lu sectors\n"), dat.ul);
+ if (disk_ioctl(dev, GET_BLOCK_SIZE, &dat.ul) == RES_OK)
+ printf_P(PSTR("Erase block: %lu sectors\n"), dat.ul);
+ if (disk_ioctl(dev, MMC_GET_TYPE, &dat.uc) == RES_OK)
+ printf_P(PSTR("Card type: %u\n"), dat.uc);
+ if (disk_ioctl(dev, MMC_GET_CSD, dat.uca) == RES_OK)
+ dump_ram(dat.uca, 0, 16, "CSD:");
+ if (disk_ioctl(dev, MMC_GET_CID, dat.uca) == RES_OK)
+ dump_ram(dat.uca, 0, 16, "CID:");
+ if (disk_ioctl(dev, MMC_GET_OCR, dat.uca) == RES_OK)
+ dump_ram(dat.uca, 0, 4, "OCR:");
+ if (disk_ioctl(dev, MMC_GET_SDSTAT, dat.uca) == RES_OK)
+ dump_ram(dat.uca, 0, 64, "SD Status:");
+
+ return CMD_RET_SUCCESS;
+}
+
+
+/*
+ * dump <pd#> [<sector> [count]] - Dump sector
+ *
+ */
+static
+command_ret_t do_dump(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ static BYTE dev_last;
+ static DWORD sec_last;
+ BYTE buffer[FF_MAX_SS];
+ char header[20];
+
+ DRESULT res;
+ BYTE dev;
+ DWORD sec;
+ UINT count;
+
+ (void) cmdtp; (void) flag;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ dev = (BYTE) eval_arg(argv[1], NULL);
+ if (dev != dev_last)
+ sec_last = 0;
+ sec = sec_last;
+ count = 1;
+
+ if ((flag & CMD_FLAG_REPEAT) == 0) {
+ /* If another parameter, it is the sector to dump. */
+ if (argc > 2)
+ sec = eval_arg(argv[2], NULL);
+ if (argc > 3)
+ count = (UINT) eval_arg(argv[3], NULL);
+ }
+
+ for ( ; count; count--, sec++) {
+ res = disk_read(dev, buffer, sec, 1);
+
+ if (res)
+ cmd_error(CMD_RET_FAILURE, 0, PSTR("rc=%.2x"), res);
+
+ sprintf_P(header, PSTR("Sector: %lu"), sec);
+ dump_ram(buffer, 0, FF_MAX_SS, header);
+ }
+ dev_last = dev;
+ sec_last = sec;
+
+ return CMD_RET_SUCCESS;
+}
+
+/*
+ * read drive sector count memaddr - Read disk into memory
+ *
+ */
+static
+command_ret_t do_read(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ DRESULT res;
+ BYTE dev;
+ DWORD sec;
+ uint32_t addr;
+ int count, nr;
+ BYTE buffer[FF_MAX_SS];
+
+ static DWORD sec_last;
+ static uint32_t addr_last;
+
+ (void) cmdtp;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ dev = (BYTE) eval_arg(argv[1], NULL);
+ sec = sec_last;
+ count = 1;
+ addr = addr_last;
+
+ if ((flag & CMD_FLAG_REPEAT) == 0) {
+ /* If another parameter, it is the sector to dump. */
+ if (argc > 2)
+ sec = eval_arg(argv[2], NULL);
+ if (argc > 3)
+ count = eval_arg(argv[3], NULL);
+ if (argc > 4)
+ addr = eval_arg(argv[4], NULL);
+ }
+
+ for (nr = 0; nr < count;) {
+ nr++;
+ if ((res = disk_read(dev, buffer, sec, 1)) == RES_OK) {
+ z80_bus_request_or_exit();
+ z80_write_block(buffer, addr /*+ base*/, FF_MAX_SS);
+ z80_bus_cmd(Release);
+ sec++; addr += FF_MAX_SS;
+ } else
+ break;
+ }
+
+ printf_P(PSTR("Read %d sector(s), rc=%.2x.\n"), nr, res);
+ if (res)
+ printf_P(PSTR("Last sector not written!\n"));
+
+ sec_last = sec;
+ addr_last = addr;
+
+ return res ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+
+/*
+ * write <pd#> <sector> <memaddr> [<n>] - Write memory to disk
+ *
+ */
+static
+command_ret_t do_write(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ DRESULT res;
+ BYTE dev;
+ DWORD sec;
+ uint32_t addr;
+ int count, nr;
+ BYTE buffer[FF_MAX_SS];
+
+ static DWORD sec_last;
+ static uint32_t addr_last;
+
+ (void) cmdtp;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ dev = (BYTE) eval_arg(argv[1], NULL);
+ sec = sec_last;
+ addr = addr_last;
+ count = 1;
+
+ if ((flag & CMD_FLAG_REPEAT) == 0) {
+ /* If another parameter, it is the sector to dump. */
+ if (argc > 2)
+ sec = eval_arg(argv[2], NULL);
+ if (argc > 3)
+ count = eval_arg(argv[3], NULL);
+ if (argc > 4)
+ addr = eval_arg(argv[4], NULL);
+ }
+
+ for (nr = 0; nr < count;) {
+ nr++;
+ z80_bus_request_or_exit();
+ z80_read_block(buffer, addr /*+ base*/, FF_MAX_SS);
+ z80_bus_cmd(Release);
+
+ res = disk_write(dev, buffer, sec, 1);
+ if (res != RES_OK)
+ break;
+ sec++; addr += FF_MAX_SS;
+ }
+
+ printf_P(PSTR("%d sector(s) written, rc=%.2x.\n"), nr, res);
+
+ sec_last = sec;
+ addr_last = addr;
+
+ return res ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+
+
+/*
+ * Disk ioctl
+ * sync <pd#> - CTRL_SYNC
+ *
+ */
+static
+command_ret_t do_ioctl_sync(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ BYTE dev;
+
+ (void) cmdtp; (void) flag;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ dev = (BYTE) eval_arg(argv[1], NULL);
+ printf_P(PSTR("rc=%.2x\n"), disk_ioctl(dev, CTRL_SYNC, 0));
+
+ return CMD_RET_SUCCESS;
+}
+
+
+cmd_tbl_t cmd_tbl_sd[] = {
+CMD_TBL_ITEM(
+ status, 2, CTBL_RPT, do_status,
+ "Socket status",
+ "drive"
+),
+CMD_TBL_ITEM(
+ init, 2, CTBL_RPT, do_init,
+ "Initialize disk",
+ "drive"
+),
+CMD_TBL_ITEM(
+ info, 2, CTBL_RPT, do_info,
+ "Disk info",
+ "drive"
+),
+CMD_TBL_ITEM(
+ dump, CONFIG_SYS_MAXARGS, CTBL_RPT, do_dump,
+ "Dump sector(s)",
+ "drive [sector [count ]]"
+),
+CMD_TBL_ITEM(
+ read, 2, CTBL_RPT, do_read,
+ "Read disk sector(s) into meomory",
+ "drive [sector [count [memaddr]]]"
+),
+CMD_TBL_ITEM(
+ write, 2, CTBL_RPT, do_write,
+ "Write sector(s) from meomory to disk",
+ "drive [sector [count [memaddr]]]"
+),
+CMD_TBL_ITEM(
+ sync, 2, CTBL_RPT, do_ioctl_sync,
+ "Device control: SYNC",
+ "drive"
+),
+
+CMD_TBL_ITEM(
+ help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
+ "Print sub command description/usage",
+ "\n"
+ " - print brief description of all sub commands\n"
+ "sd help command ...\n"
+ " - print detailed usage of sub cmd 'command'"
+),
+
+/* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
+ {FSTR("?"), CONFIG_SYS_MAXARGS, 1, do_help,
+ NULL,
+#ifdef CONFIG_SYS_LONGHELP
+ FSTR(""),
+#endif /* CONFIG_SYS_LONGHELP */
+ NULL,
+#ifdef CONFIG_AUTO_COMPLETE
+ NULL,
+#endif
+},
+/* Mark end of table */
+CMD_TBL_END(cmd_tbl_sd)
+};
+
+
+command_ret_t do_sd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
+{
+ puts_P(PSTR("Huch?"));
+ return CMD_RET_USAGE;
+}
diff --git a/avr/command.c b/avr/command.c
new file mode 100644
index 0000000..8a4ca02
--- /dev/null
+++ b/avr/command.c
@@ -0,0 +1,812 @@
+/*
+ * (C) Copyright 2014, 2016, 2018 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * (C) Copyright 2000-2009
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+/*
+ * Command Processor Table
+ */
+
+#include "command.h"
+#include "common.h"
+#include <setjmp.h>
+
+#include "config.h"
+#include "print-utils.h"
+#include "con-utils.h"
+#include "env.h"
+#include "debug.h"
+#include "getopt-min.h"
+#include "strerror.h"
+
+#define DEBUG_CMD 0 /* set to 1 to debug */
+
+#define debug_cmd(fmt, args...) \
+ debug_cond(DEBUG_CMD, fmt, ##args)
+
+
+jmp_buf cmd_jbuf;
+
+char *strcasestr_P2(const FLASH char *haystack, const char *needle)
+{
+ int nlen = strlen(needle);
+ int hlen = strlen_P(haystack) - nlen + 1;
+ int i;
+
+ for (i = 0; i < hlen; i++) {
+ int j;
+ for (j = 0; j < nlen; j++) {
+ unsigned char c1 = haystack[i+j];
+ unsigned char c2 = needle[j];
+ if (toupper(c1) != toupper(c2))
+ goto next;
+ }
+ return (char *) haystack + i;
+ next:
+ ;
+ }
+ return NULL;
+}
+
+
+#if 0
+static int cmd_tbl_item_count(cmd_tbl_t *p)
+{
+ int count = 0;
+
+ while (p->name != NULL) {
+ if (p->subcmd) {
+ cmd_tbl_t *sub = p->subcmd;
+ while (sub->name != NULL) {
+ if (sub->flags & CTBL_SUBCMDAUTO)
+ count++;
+ sub++;
+ }
+ }
+ if ((p->flags & CTBL_SUBCMDAUTO) == 0)
+ count++;
+ p++;
+ }
+ return count;
+}
+#endif
+
+static cmd_tbl_t *get_cmd_tbl_base(cmd_tbl_t *cmdtp)
+{
+ cmd_tbl_t *p = cmdtp;
+
+ while (p->name != NULL)
+ ++p;
+
+ return p->subcmd;
+}
+
+static cmd_tbl_t *get_cmd_tbl_parent(cmd_tbl_t *child)
+{
+ if ((child->flags & CTBL_SUBCMDAUTO) == 0) {
+
+ cmd_tbl_t *tbl_start = get_cmd_tbl_base(child);
+ if (tbl_start != cmd_tbl) {
+ cmd_tbl_t *top = cmd_tbl;
+ while (top->subcmd != tbl_start)
+ ++top;
+ return top;
+ }
+ }
+ return NULL;
+}
+
+static int print_nameprefix(cmd_tbl_t *p)
+{
+ cmd_tbl_t *top = get_cmd_tbl_parent(p);
+
+ int width = 0;
+ if (top) {
+ width = printf_P(PSTR("%S "), top->name);
+ }
+
+ return width;
+}
+
+static int print_prefixed_name(cmd_tbl_t *p)
+{
+ int len;
+
+ len = print_nameprefix(p);
+ len += strlen_P(p->name);
+ my_puts_P(p->name);
+
+ return len;
+}
+
+static void print_usage_line(cmd_tbl_t *p, int width)
+{
+ width -= print_prefixed_name(p);
+ if (width < 0)
+ width = 0;
+ print_blanks(width);
+ my_puts_P(PSTR(" - "));
+ puts_P(p->usage);
+}
+
+#if 0
+static 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;
+}
+#endif
+
+#if 1
+static int cmpstring_PP(const void *p1, const void *p2)
+{
+ char s1[21] = {'\0'};
+ char s2[21] = {'\0'};
+ cmd_tbl_t *cp1 = *(cmd_tbl_t **) p1;
+ cmd_tbl_t *cp2 = *(cmd_tbl_t **) p2;
+ cmd_tbl_t *top1 = get_cmd_tbl_parent(*(cmd_tbl_t **) p1);
+ cmd_tbl_t *top2 = get_cmd_tbl_parent(*(cmd_tbl_t **) p2);
+
+ if (top1)
+ strlcpy_P(s1, top1->name, 21);
+ if (top2)
+ strlcpy_P(s2, top2->name, 21);
+
+ strlcat_P(s1, cp1->name, 21);
+ strlcat_P(s2, cp2->name, 21);
+
+ return strcmp(s1, s2);
+}
+#else
+static int cmpstring_PP(const void *p1, const void *p2)
+{
+ cmd_tbl_t *cp1 = *(cmd_tbl_t **) p1;
+ cmd_tbl_t *cp2 = *(cmd_tbl_t **) p2;
+ cmd_tbl_t *top1 = get_cmd_tbl_parent(*(cmd_tbl_t **) p1);
+ cmd_tbl_t *top2 = get_cmd_tbl_parent(*(cmd_tbl_t **) p2);
+ int res;
+
+ if (top1 && top2) {
+ res = strcmp_PP(top1->name, top2->name);
+ if (res == 0)
+ res = strcmp_PP(cp1->name, cp2->name);
+ return res;
+ }
+ if (top1) {
+ res = strcmp_PP(top1->name, cp2->name);
+ if (res == 0)
+ res = strcmp_PP(cp1->name, cp2->name);
+ return res;
+ }
+ if (top2) {
+ res = strcmp_PP(cp1->name, top2->name);
+ if (res == 0)
+ res = strcmp_PP(cp1->name, cp2->name);
+ return res;
+ }
+ return strcmp_PP(cp1->name, cp2->name);
+}
+#endif
+
+/****************************************************************************/
+
+/*
+ * find command table entry for a command
+ */
+
+static cmd_tbl_t *find_cmd (const char *cmd, cmd_tbl_t *table)
+{
+ if (!cmd)
+ return NULL;
+
+ char *optenv = getenv_str(PSTR("cmd"));
+ uint8_t opt_debug = optenv && strstr_P(optenv, PSTR("debug")) != NULL;
+
+ cmd_tbl_t *cmdtp_ret = NULL;
+ uint_fast8_t n_found = 0;
+ uint_fast8_t len = strlen(cmd);
+
+ for (cmd_tbl_t *cmdtp = table; cmdtp->name != NULL; cmdtp++) {
+ if (cmdtp->subcmd) {
+ for (cmd_tbl_t *sub = cmdtp->subcmd; sub->name != NULL; sub++) {
+ if (sub->flags & CTBL_SUBCMDAUTO &&
+ strncmp_P(cmd, sub->name, len) == 0 &&
+ (opt_debug || !(sub->flags & CTBL_DBG))) {
+ if (len == strlen_P(sub->name))
+ return sub; /* full match */
+ cmdtp_ret = sub; /* abbreviated command ? */
+ ++n_found;
+ }
+ }
+ }
+ if ((cmdtp->flags & CTBL_SUBCMDAUTO) == 0 &&
+ strncmp_P(cmd, cmdtp->name, len) == 0 &&
+ (opt_debug || !(cmdtp->flags & CTBL_DBG))) {
+ if (len == strlen_P(cmdtp->name))
+ return cmdtp; /* full match */
+ cmdtp_ret = cmdtp; /* abbreviated command ? */
+ ++n_found;
+ }
+ }
+
+ if (n_found == 1)
+ return cmdtp_ret; /* exactly one match */
+
+ return NULL; /* not found or ambiguous command */
+}
+
+/*
+ * Use puts() instead of printf() to avoid printf buffer overflow
+ * for long help messages
+ */
+
+command_ret_t do_help(cmd_tbl_t *cmdtp, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ cmd_tbl_t *tbl_start = get_cmd_tbl_base(cmdtp);
+ command_ret_t res = CMD_RET_SUCCESS;
+ uint_fast8_t options = 0;
+#define OPT_DBG_CMDS 0x01
+#define OPT_ALL 0x02
+#define OPT_NAME 0x04
+#define OPT_USAGE 0x08
+#define OPT_LONG 0x10
+
+ int opt;
+ while ((opt = getopt(argc, argv, PSTR("afk"))) != -1) {
+ switch (opt) {
+ case 'a':
+ options |= OPT_ALL;
+ break;
+ case 'f':
+ options |= OPT_NAME | OPT_ALL;
+ break;
+ case 'k':
+ options |= OPT_USAGE | OPT_ALL;
+ break;
+ default: /* '?' */
+ return CMD_RET_USAGE;
+ }
+ }
+
+ /* remaining arguments */
+ argc -= optind;
+ argv += optind;
+
+ if ((options & (OPT_USAGE|OPT_NAME)) == (OPT_USAGE|OPT_NAME)) {
+ puts_P(PSTR("Inkompatible options: -f -k\n"
+ "Try 'help help'"));
+ return CMD_RET_USAGE;
+ }
+ if (options & OPT_USAGE)
+ options |= OPT_NAME;
+
+ if ((options & (OPT_NAME | OPT_USAGE)) == 0 && argc > 0)
+ options |= OPT_LONG;
+
+ char *optenv = getenv_str(PSTR("cmd"));
+ if (optenv && strstr_P(optenv, PSTR("debug")) != NULL)
+ options |= OPT_DBG_CMDS;
+
+ uint_fast8_t maxlen_cmd;
+ int cmd_items;
+ cmd_tbl_t **cmd_list = NULL;
+ for (uint_fast8_t pass = 0; pass < 2; ++pass) {
+ maxlen_cmd = 0;
+ if (pass == 0)
+ cmd_items = 0;
+ else {
+ cmd_list = (cmd_tbl_t **) malloc(cmd_items * sizeof(cmd_tbl_t *));
+ /* TODO: Check error */
+ }
+
+ /* Make array of commands */
+ cmd_tbl_t *tp = tbl_start;
+ int i = 0;
+ while (tp->name != NULL) {
+ if (tp->subcmd) {
+ cmd_tbl_t *sub = tp->subcmd;
+ while (sub->name != NULL) {
+ if (options & OPT_ALL || sub->flags & CTBL_SUBCMDAUTO) {
+ if (pass == 0)
+ ++cmd_items;
+ else {
+ cmd_list[i++] = sub;
+ uint_fast8_t len = strlen_P(sub->name);
+ if ((sub->flags & CTBL_SUBCMDAUTO) == 0) {
+ len += strlen_P(tp->name) + 1;
+ }
+ if (len > maxlen_cmd) {
+ maxlen_cmd = len;
+ }
+//debug_cmd("### i:%3d, maxlen:%3d, tp: '%S', sub: '%S'\n", i, maxlen_cmd, tp->name, sub->name);
+ }
+ }
+ sub++;
+ }
+ }
+ if ((tp->flags & CTBL_SUBCMDAUTO) == 0) {
+ if (pass == 0)
+ ++cmd_items;
+ else {
+ cmd_list[i++] = tp;
+ uint_fast8_t len = strlen_P(tp->name);
+ cmd_tbl_t *top = get_cmd_tbl_parent(tp);
+ if (top)
+ len += strlen_P(top->name) + 1;
+ if (len > maxlen_cmd)
+ maxlen_cmd = len;
+ }
+ }
+ tp++;
+ }
+//debug_cmd("### pass: %d, cmd_items: %d, i: %d, maxlen_cmd: %d\n", pass, cmd_items, i, maxlen_cmd);
+ }
+
+ /* Sort command list */
+ qsort(cmd_list, cmd_items, sizeof (cmd_tbl_t *), cmpstring_PP);
+
+ if ((options & OPT_LONG) == 0) {
+ /* print short help (usage) */
+ for (uint_fast8_t cmdi = 0; cmdi < cmd_items; cmdi++) {
+
+ /* allow user abort */
+ if (ctrlc ()) {
+ res = CMD_RET_FAILURE;
+ break;
+ }
+ if ((cmd_list[cmdi]->flags & CTBL_DBG) && !(options & OPT_DBG_CMDS))
+ continue;
+ if (cmd_list[cmdi]->usage == NULL)
+ continue;
+
+ if (argc == 0)
+ print_usage_line(cmd_list[cmdi], maxlen_cmd);
+ else {
+ for (uint_fast8_t argi = 0; argi < argc; argi++) {
+ if (((options & OPT_NAME) &&
+ strcasestr_P2(cmd_list[cmdi]->name, argv[argi])) ||
+ ((options & OPT_USAGE) &&
+ strcasestr_P2(cmd_list[cmdi]->usage, argv[argi]))) {
+ print_usage_line(cmd_list[cmdi], maxlen_cmd);
+ }
+ }
+ }
+ }
+ } else {
+ /* command help (long version) */
+ for (uint_fast8_t argi = 0; argi < argc; ++argi) {
+ uint_fast8_t got = 0;
+ cmd_tbl_t *tp = find_cmd(argv[argi], tbl_start);
+ if (tp) {
+ cmd_usage(tp);
+ got = 1;
+ }
+ if (options & OPT_ALL) {
+ for (cmd_tbl_t *sub = tbl_start; sub->name != NULL; sub++) {
+ if (sub->subcmd) {
+ tp = find_cmd(argv[argi], sub->subcmd);
+ if (tp) {
+ cmd_usage(tp);
+ got = 1;
+ }
+ }
+ }
+ }
+ if (!got) {
+ printf_P(PSTR("Unknown command '%s' - try 'help help'\n"),
+ argv[argi]);
+ res = CMD_RET_FAILURE;
+ break;
+ }
+ }
+ }
+ free(cmd_list);
+ return res;
+}
+
+
+command_ret_t cmd_usage(cmd_tbl_t *cmdtp)
+{
+ print_usage_line(cmdtp, 0);
+#ifdef CONFIG_SYS_LONGHELP
+ my_puts_P(PSTR("Usage:\n"));
+ print_prefixed_name(cmdtp);
+ my_puts_P(PSTR(" "));
+
+ if (cmdtp->help && *cmdtp->help != '\0')
+ puts_P(cmdtp->help);
+ else
+ my_puts_P(PSTR(" - No additional help available.\n"));
+#endif /* CONFIG_SYS_LONGHELP */
+ return CMD_RET_FAILURE;
+}
+
+#ifdef CONFIG_AUTO_COMPLETE
+
+int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
+{
+ static char tmp_buf[CONFIG_SYS_CBSIZE];
+ 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;
+}
+
+/*************************************************************************************/
+
+/* TODO: cmdtp points to FLASH */
+
+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], cmd_tbl);
+ 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 */
+
+
+static cmd_tbl_t *cmd_invocation_ptr;
+
+/**
+ * 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 command_ret_t cmd_call(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ command_ret_t result;
+
+ result = (cmdtp->cmd)(cmdtp, flag, argc, argv);
+// if (result != CMD_RET_SUCCESS)
+// debug("Command failed, result=%d\n", result);
+ return result;
+}
+
+#pragma GCC diagnostic ignored "-Wclobbered"
+
+command_ret_t cmd_process(uint_fast8_t flag, int argc, char * const argv[],
+ uint_fast8_t *repeatable)
+{
+ command_ret_t rc = CMD_RET_SUCCESS;
+ cmd_tbl_t *cmdtp;
+
+ /* Look up command in command table */
+ cmdtp = find_cmd(argv[0], cmd_tbl);
+ if (cmdtp != NULL) {
+ /* Check if this command has subcommands */
+ if (cmdtp->subcmd && argc > 1) {
+
+ /* Look up subcommand in subcommand table */
+ cmd_tbl_t *cmdtpsub = find_cmd(argv[1], cmdtp->subcmd);
+ if (cmdtpsub == NULL) {
+ printf_P(PSTR("Unknown '%s' subcommand '%s' - try '%s help'\n"), argv[0], argv[1], argv[0]);
+ return CMD_RET_FAILURE;
+ }
+ cmdtp = cmdtpsub;
+ --argc;
+ ++argv;
+ }
+ }
+
+ if (cmdtp == NULL) {
+ printf_P(PSTR("Unknown command '%s' - try 'help'\n"), argv[0]);
+ return CMD_RET_FAILURE;
+ }
+
+ /* 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 (setjmp(cmd_jbuf) != 0)
+ return CMD_RET_FAILURE;
+
+ /* If OK so far, then do the command */
+ if (!rc) {
+ optind = 0; /* reset getopt() */
+ cmd_invocation_ptr = cmdtp;
+ rc = cmd_call(cmdtp, flag, argc, argv);
+ *repeatable &= (cmdtp->flags & CTBL_RPT) != 0;
+ }
+ 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;
+}
+
+void cmd_error(int status, int errnum, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ print_prefixed_name(cmd_invocation_ptr);
+ if (fmt != NULL) {
+ my_puts_P(PSTR(": "));
+ vfprintf_P(stdout, fmt, ap);
+ }
+ va_end(ap);
+
+ if (errnum != 0)
+ printf_P(PSTR(": %S"), my_strerror_P(errnum));
+
+ putchar('\n');
+ _delay_ms(20);
+
+ if (status != 0) {
+ longjmp(cmd_jbuf, 1);
+ }
+}
diff --git a/avr/command_tbl.c b/avr/command_tbl.c
new file mode 100644
index 0000000..5a63e76
--- /dev/null
+++ b/avr/command_tbl.c
@@ -0,0 +1,402 @@
+/*
+ * (C) Copyright 2014-2016, 2018 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "common.h"
+#include "command.h"
+#include "cmd_mem.h"
+#include "cmd_boot.h"
+#include "cmd_cpu.h"
+#include "cmd_misc.h"
+#include "cmd_date.h"
+#include "cmd_run.h"
+#include "cmd_loadcpm3.h"
+#include "cmd_loadihex.h"
+#include "cmd_gpio.h"
+#include "cmd_sd.h"
+#include "cmd_fat.h"
+#include "cmd_attach.h"
+#include "env.h"
+#include "debug.h"
+
+
+cmd_tbl_t cmd_tbl[] = {
+
+CMD_TBL_ITEM_TOP(
+ xx, CONFIG_SYS_MAXARGS, 0, do_cpu,
+ "experimental commands",
+ "<subcommand> args ...\n"
+ "xx help\n",
+ cmd_tbl_cpu
+),
+
+
+CMD_TBL_ITEM(
+ time, CONFIG_SYS_MAXARGS, 0, do_time,
+ "run command and print execution time",
+ "command [args...]\n"
+),
+CMD_TBL_ITEM(
+ date, 2, 1, do_date,
+ "get/set date & time",
+ "[MMDDhhmm[[CC]YY][.ss]]\n"
+ " - without arguments: print date & time\n"
+ " - with numeric argument: set the system date & time\n"
+),
+
+#ifdef DEBUG
+
+CMD_TBL_ITEM(
+ !mdr, 3, CTBL_DBG|CTBL_RPT, do_dump_mem,
+ "RAM dump",
+ "address [count]"
+),
+CMD_TBL_ITEM(
+ !mde, 3, CTBL_DBG|CTBL_RPT, do_dump_mem,
+ "EEPROM dump",
+ "address [count]"
+),
+CMD_TBL_ITEM(
+ !mdf, 3, CTBL_DBG|CTBL_RPT, do_dump_mem,
+ "FLASH dump",
+ "address [count]"
+),
+CMD_TBL_ITEM(
+ !cpe, 4, CTBL_DBG, do_eep_cp,
+ "EEPROM copy",
+ "source target count"
+),
+CMD_TBL_ITEM(
+ !mm, 2, CTBL_DBG|CTBL_RPT, do_mem_mm_avr,
+ "avr memory modify (auto-incrementing address)",
+ "address"
+),
+CMD_TBL_ITEM(
+ !nm, 2, CTBL_DBG|CTBL_RPT, do_mem_nm_avr,
+ "avr memory modify (constant address)",
+ "address"
+),
+CMD_TBL_ITEM(
+ !prfree, 1, CTBL_DBG|CTBL_RPT, do_pr_free_avr,
+ "print avr heap free list",
+ ""
+),
+CMD_TBL_ITEM(
+ !prheap, 1, CTBL_DBG, do_pr_heap_avr,
+ "dump avr heap",
+ ""
+),
+#endif
+CMD_TBL_ITEM(
+ msize, 1, 1, do_mem_size,
+ "Detect memory size",
+ ""
+),
+CMD_TBL_ITEM(
+ mstep, 2, 1, do_busreq_pulse,
+ "execute one M cycle",
+ "[count]\n"
+ " - repeat count times"
+),
+CMD_TBL_ITEM(
+ echo, CONFIG_SYS_MAXARGS, 1, do_echo,
+ "display a line of text",
+ "[-n] [argument ...]\n"
+ "- echo the argument(s) to console.\n"
+ " -n do not output the trailing newline"
+),
+CMD_TBL_ITEM(
+ sleep , 2, 1, do_sleep,
+ "delay execution for some time",
+ "N[m][s]\n"
+ " - delay execution for hexadecimal N (milli) seconds"
+),
+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(
+ boot, 1, 1, do_bootd,
+ "boot default, i.e., run 'bootcmd'",
+ ""
+),
+CMD_TBL_ITEM_COMPLETE(
+ source, CONFIG_SYS_MAXARGS, 1, do_source,
+ "run commands from a file",
+ "filename\n"
+ " - run the commands in the script file 'filename'",
+ var_complete
+),
+
+CMD_TBL_ITEM_TOP(
+ env, CONFIG_SYS_MAXARGS, 0, do_env,
+ "environment handling commands",
+ "",
+ cmd_tbl_env
+),
+
+CMD_TBL_ITEM(
+ loadf, 1, 0, do_loadf,
+ "load srec_cat prepared image from controller flash",
+ ""
+),
+CMD_TBL_ITEM(
+ bootcf, CONFIG_SYS_MAXARGS, 0, do_bootcf,
+ "boot from cf card",
+ "[options]\n"
+ " Load a number of sectors from the first CP/M partition and jump to\n"
+ " the load address.\n"
+ " -a ADDRESS\n"
+ " Load and start address (default 100 hex)\n"
+ " -s NUM\n"
+ " First sector of partition to load (0..255, default 0)\n"
+ " -c NUM\n"
+ " Number of sectors to load (1..127, default 7)\n"
+ " -i NUM\n"
+ " Partition type to look for (default 52 hex)\n"
+ " -n\n"
+ " Load only, do not execute\n"
+ " -t NUM\n"
+ " Timeout for IDE commands (1..65535, default 10000)\n"
+ " -v verbose\n"
+ " TODO: be verbose"
+),
+CMD_TBL_ITEM(
+ loadcpm3, 3, 0, do_loadcpm3,
+ "load CPM3.SYS file",
+ "[filename [common-base [banked-base]]] \n"
+ " - Load CP/M 3 system file from FAT filesystem. This command makes\n"
+ " CPMLDR superfluous. Uses the following environment variables if set:\n"
+ " '"ENV_CPM3_SYSFILE"' File to load. Default is '"CONFIG_CPM3_SYSFILE"'.\n"
+ " '"ENV_CPM3_BANKED_BASE"' Default is '"CONFIG_CPM3_BANKED_BASE_STR"'.\n"
+ " Sets the following environment variables after loading:\n"
+ " '"ENV_CPM3_COMMON_BASE"'\n"
+ " '"ENV_STARTADDRESS"'"
+),
+CMD_TBL_ITEM(
+ loadi, 2, 0, do_loadihex,
+ "load intel hex file over serial line",
+ "[[-]offset]\n"
+ " - load Intel-Hex-Record file over serial line with offset 'offset'"
+),
+
+#if defined(CONFIG_CMD_LOADB)
+CMD_TBL_ITEM(
+ loadb, 1, 0, do_load_serial_bin,
+ "load binary file over serial line (kermit mode)",
+ " - load binary file over serial line"
+),
+
+CMD_TBL_ITEM(
+ loadx, 1, 0, do_load_serial_bin,
+ "load binary file over serial line (xmodem mode)",
+ " - load binary file over serial line"
+),
+
+CMD_TBL_ITEM(
+ loady, 1, 0, do_load_serial_bin,
+ "load binary file over serial line (ymodem mode)",
+ " - load binary file over serial line"
+),
+#endif /* CONFIG_CMD_LOADB */
+
+CMD_TBL_ITEM(
+ go, CONFIG_SYS_MAXARGS, 0, do_go,
+ "start application at address 'addr'",
+ "[-h] addr\n"
+ " - start application at address 'addr'"
+),
+CMD_TBL_ITEM(
+ reset, 1, 0, do_reset,
+ "Keep CPU in RESET state",
+ ""
+),
+CMD_TBL_ITEM(
+ restart, 1, 1, do_restart,
+ "Perform RESET of the CPU",
+ ""
+),
+CMD_TBL_ITEM(
+ connect, 1, 0, do_console,
+ "Connect to CPU console i/o",
+ "\n"
+ " - type the escape character followed by Q to close the connection, \n"
+ " or followed by ? to see other options. The default escape character \n"
+ " is Ctrl-^ (0x1E). It can be changed by setting env var '"ENV_ESC_CHAR"'."
+
+),
+
+CMD_TBL_ITEM(
+ pin, CONFIG_SYS_MAXARGS, 1, do_gpio,
+ "Set or query pin state",
+ "[-s] [<pins>]\n"
+ " - print cofiguration and state or frequency of pins\n"
+ " print all pins, if argument is omitted\n"
+ "pin <pins> h[igh]|l[ow]\n"
+ " - config pins as output and set to level high or low\n"
+ "pin <pins> ts|i[n]|p[ullup]\n"
+ " - config pins as input/tristate or input with pullup\n"
+ "pin <pins> value[K|M][Hz]\n"
+ " - output a clock on pins\n"
+ " value is system clock divider or frequency, if 'Hz' is appended\n"
+ " divider is rounded down to next possible value (depends on pin)\n"
+ "\n"
+ "<pins> is a comma separated list of numbers or ranges, i.e. \"0,9,3-6\"\n"
+),
+
+CMD_TBL_ITEM(
+ md, 3, CTBL_RPT, do_mem_md,
+ "memory display",
+ "address [# of objects]"
+),
+CMD_TBL_ITEM(
+ mm, 2, CTBL_RPT, do_mem_mm,
+ "memory modify (auto-incrementing address)",
+ "address"
+),
+CMD_TBL_ITEM(
+ nm, 2, CTBL_RPT, do_mem_nm,
+ "memory modify (constant address)",
+ "address"
+),
+CMD_TBL_ITEM(
+ mw, CONFIG_SYS_MAXARGS, CTBL_RPT, do_mem_mw,
+ "memory write (fill)",
+ "[-bwl] address value [count]\n"
+ " -b write value as byte (8 bit, default)\n"
+ " -w write value as word (16 bit)\n"
+ " -l write value as long (32 bit)"
+),
+CMD_TBL_ITEM(
+ mcp, 4, CTBL_RPT, do_mem_cp,
+ "memory copy",
+ "source target count"
+),
+CMD_TBL_ITEM(
+ mcmp, 4, CTBL_RPT, do_mem_cmp,
+ "memory compare",
+ "addr1 addr2 count"
+),
+CMD_TBL_ITEM(
+ base, 2, 0, do_mem_base,
+ "print or set address offset",
+ "\n"
+ " - print address offset for memory commands\n"
+ "base offset\n"
+ " - set address offset for memory commands to 'offset'"
+),
+CMD_TBL_ITEM(
+ mloop, 3, CTBL_RPT, do_mem_loop,
+ "infinite loop on address range",
+ "address number_of_bytes"
+),
+CMD_TBL_ITEM(
+ mloopw, 4, CTBL_RPT, do_mem_loopw,
+ "infinite write loop on address range",
+ "address number_of_bytes data_to_write"
+),
+
+#ifdef CONFIG_CMD_MEMTEST
+CMD_TBL_ITEM(
+ mtest, 4, CTBL_RPT, do_mem_mtest,
+ "simple RAM read/write test",
+ "[start [end [iterations]]]"
+),
+#endif /* CONFIG_CMD_MEMTEST */
+
+#ifdef CONFIG_MX_CYCLIC
+CMD_TBL_ITEM(
+ mdc, 4, CTBL_RPT, do_mem_mdc,
+ "memory display cyclic",
+ "address count delay(ms)"
+),
+CMD_TBL_ITEM(
+ mwc, CONFIG_SYS_MAXARGS, CTBL_RPT, do_mem_mdc,
+ "memory write cyclic",
+ "[-bwl] address value delay(ms)\n"
+ " -b write value as byte (8 bit, default)\n"
+ " -w write value as word (16 bit)\n"
+ " -l write value as long (32 bit)"
+),
+#endif /* CONFIG_MX_CYCLIC */
+
+CMD_TBL_ITEM_TOP(
+ sd, CONFIG_SYS_MAXARGS, 0, do_sd,
+ "SD/MMC card handling commands",
+ "<subcommand> args ...\n"
+ "sd help\n"
+ " - print help on subcommands",
+ cmd_tbl_sd
+),
+CMD_TBL_ITEM_TOP(
+ fat, CONFIG_SYS_MAXARGS, 0, do_fat,
+ "fat filesystem commands",
+ "<subcommand> args ...\n"
+ "fat help\n"
+ " - print help on subcommands",
+ cmd_tbl_fat
+),
+
+CMD_TBL_ITEM(
+ attach, CONFIG_SYS_MAXARGS, CTBL_RPT, do_attach,
+ "attach filesystem image file to CP/M drive",
+ "[-rw] [-o options] dsk<n> diskfile\n"
+ " Attach diskfile to dsk<n>, where n in 0.."CONFIG_CPM_MAX_DRNR_STR"\n"
+ " -r File is read only (write protected)\n"
+ " -w File is read/write (default)\n"
+ " -o options\n"
+ " Options is a comma-separated list of\n"
+ " ro, rw, debug, nodebug\n"
+ "\n"
+ "attach [-rw] -o reattach[,other options] dsk<n>\n"
+ " Change options for dsk<n>.\n"
+ " Options as above.\n"
+ "\n"
+ "attach -d -a|dsk<n>\n"
+ "detach -a|dsk<n>\n"
+ " Detach diskfile from dsk<n>.\n"
+ " -a Detach all.\n"
+ "\n"
+ "attach\n"
+ " Without arguments, list current assignments"
+),
+CMD_TBL_ITEM(
+ detach, 2, 0, do_attach,
+ "detach file from CP/M drive",
+ "dsk<n>]\n"
+ " - alias for 'attach -d dsk<n>'"
+),
+
+CMD_TBL_ITEM(
+ help, CONFIG_SYS_MAXARGS, 0, do_help,
+ "print command description/usage",
+ "[-a]\n"
+ " print brief description of all commands\n"
+ " -a print description of subcommands too\n"
+ "help -fk keyword ...\n"
+ " print brief description of commands matching keyword\n"
+ " -f search keyword in command name\n"
+ " -k search keyword in command name and description\n"
+ "help [-a] command ...\n"
+ " print detailed usage of 'command'"
+),
+
+/* This does not use the CMD_TBL_ITEM 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 */
+ NULL,
+#ifdef CONFIG_AUTO_COMPLETE
+ NULL,
+#endif
+},
+/* Mark end of table */
+CMD_TBL_END(cmd_tbl)
+};
diff --git a/avr/con-utils.c b/avr/con-utils.c
new file mode 100644
index 0000000..5ea19fd
--- /dev/null
+++ b/avr/con-utils.c
@@ -0,0 +1,126 @@
+/*
+ * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "common.h"
+#include <avr/wdt.h>
+
+#include "config.h"
+#include "serial.h"
+#include "background.h"
+#include "con-utils.h"
+
+uint_fast8_t tstc(void)
+{
+ bg_shed();
+ return serial_tstc();
+}
+
+int my_getchar(uint_fast8_t waitforchar)
+{
+ int c;
+
+ do {
+ bg_shed();
+ c = serial_getc();
+ } while ((c < 0) && waitforchar);
+
+#ifdef CONFIG_SYS_FBOOTSIG
+ if (c < 0)
+ return c;
+
+ static const FLASH unsigned char bootsig[] = {CONFIG_SYS_FBOOTSIG};
+ static uint8_t pb;
+ unsigned char uc = c;
+
+
+ if (bootsig[pb] == 0) {
+ if (uc == 0xff) {
+ wdt_enable(WDTO_15MS);
+ for(;;)
+ ;
+ } else
+ pb = 0;
+
+ } else {
+ if (bootsig[pb] == uc)
+ pb++;
+ else
+ pb = 0;
+ }
+#endif
+
+ return c;
+}
+
+
+/* test if ctrl-c was pressed */
+
+static uint_fast8_t ctrlc_disabled; /* see disable_ctrl() */
+static uint_fast8_t ctrlc_was_pressed;
+
+uint_fast8_t ctrlc(void)
+{
+ bg_shed();
+ 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(1);
+ 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/debug.c b/avr/debug.c
new file mode 100644
index 0000000..ea21583
--- /dev/null
+++ b/avr/debug.c
@@ -0,0 +1,257 @@
+/*
+ * (C) Copyright 2014,2016 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "debug.h"
+#include "common.h"
+#include <avr/eeprom.h>
+
+#include "command.h"
+#include "cli_readline.h"
+#include "eval_arg.h"
+#include "print-utils.h"
+
+/*
+ * Debugging
+ */
+
+#ifdef DEBUG
+
+/*
+ * Memory Display
+ * md addr {len}
+ */
+command_ret_t do_dump_mem(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ ERRNUM (*readwhat)(uint8_t *buf, uint32_t addr, uint8_t count);
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ uint32_t addr;
+ uint32_t length = 128;
+
+ switch (argv[0][3]) {
+ case 'r':
+ readwhat = ram_read_buf;
+ break;
+ case 'e':
+ readwhat = eeprom_read_buf;
+ break;
+ case 'f':
+ readwhat = flash_read_buf;
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+
+ /* Address is specified since argc > 1 */
+ addr = eval_arg(argv[1], NULL);
+
+ /* If another parameter, it is the length to display. */
+ if (argc > 2)
+ length = (uint16_t) eval_arg(argv[2], NULL);
+
+ /* Print the lines. */
+ dump_mem(addr, addr, length, readwhat, NULL);
+
+ return CMD_RET_SUCCESS;
+}
+
+command_ret_t do_eep_cp(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ uint16_t src, dest, count;
+ int_fast8_t step;
+
+ (void) cmdtp;
+ (void) flag;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ src = (size_t) eval_arg(argv[1], NULL);
+ dest = (size_t) eval_arg(argv[2], NULL);
+ count = (size_t) eval_arg(argv[3], NULL);
+
+ if (src > E2END) {
+ debug("src > EEPROM size: 0x%04x\n", src);
+ return CMD_RET_FAILURE;
+ }
+ if (dest > E2END) {
+ debug("dest > EEPROM size: 0x%04x\n", dest);
+ return CMD_RET_FAILURE;
+ }
+ if (count > E2END+1) {
+ debug("count > EEPROM size: 0x%04x\n", count);
+ return CMD_RET_FAILURE;
+ }
+ if (count == 0) {
+ debug("Zero length?\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (dest > src) {
+ src += count - 1;
+ dest += count - 1;
+ step = -1;
+ } else
+ step = 1;
+
+ while (count-- > 0) {
+ uint8_t data;
+ data = eeprom_read_byte((uint8_t *) src);
+ eeprom_write_byte((uint8_t *) dest, data);
+ src += step;
+ dest += step;
+
+ }
+ return CMD_RET_SUCCESS;
+}
+
+
+/* Modify memory.
+ *
+ * Syntax:
+ * !mm {addr}
+ * !nm {addr}
+ */
+
+ static uint8_t *mm_last_addr;
+
+static command_ret_t
+mod_mem_avr(cmd_tbl_t *cmdtp, int incrflag, uint_fast8_t flag, int argc, char * const argv[])
+{
+ uint8_t *addr;
+ int nbytes;
+
+ (void) cmdtp;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ /* We use the last specified parameters, unless new ones are
+ * entered.
+ */
+ addr = mm_last_addr;
+
+ if ((flag & CMD_FLAG_REPEAT) == 0) {
+ /* New command specified.
+ */
+
+ /* Address is specified since argc > 1
+ */
+ addr = (uint8_t *) (size_t) eval_arg(argv[1], NULL);
+ }
+
+ /* Print the address, followed by value. Then accept input for
+ * the next value. A non-converted value exits.
+ */
+ do {
+ uint8_t data = *addr;
+ printf_P(PSTR("%04x: %02x"), addr, data);
+
+ nbytes = cli_readline(PSTR(" ? "), 0);
+ if (nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')) {
+ /* <CR> pressed as only input, don't modify current
+ * location and move to next. "-" pressed will go back.
+ */
+ if (incrflag)
+ addr += nbytes ? -1 : 1;
+ nbytes = 1;
+
+ } else {
+ char *endp;
+ data = eval_arg(console_buffer, &endp);
+ nbytes = endp - console_buffer;
+ if (nbytes) {
+ *addr = data;
+ if (incrflag)
+ addr++;
+ }
+ }
+ } while (nbytes > 0);
+
+ mm_last_addr = addr;
+ return CMD_RET_SUCCESS;
+}
+
+
+command_ret_t do_mem_mm_avr(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ return mod_mem_avr (cmdtp, 1, flag, argc, argv);
+}
+command_ret_t do_mem_nm_avr(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
+{
+ return mod_mem_avr (cmdtp, 0, flag, argc, argv);
+}
+
+/*------------------------------------------------------------------------------*/
+
+struct __freelist {
+ size_t sz;
+ struct __freelist *nx;
+};
+
+extern char *__brkval; /* first location not yet allocated */
+extern struct __freelist *__flp; /* freelist pointer (head of freelist) */
+
+#define STACK_POINTER() ((char *)AVR_STACK_POINTER_REG)
+
+size_t get_freemem(void)
+{
+ return (size_t) STACK_POINTER() - __malloc_margin - (size_t) __brkval;
+}
+
+void
+printfreelist(const char * title)
+{
+ struct __freelist *fp1;
+ int i;
+ unsigned int freesum = 0;
+
+ if (!__flp) {
+ printf_P(PSTR("%s no free list\n"), title ? title : "");
+ } else {
+ printf_P(PSTR("Free list: %s\n"), title ? title : "");
+ for (i = 0, fp1 = __flp; fp1; i++, fp1 = fp1->nx) {
+ printf_P(PSTR(" entry %d @ %04x: size %4u, next "),
+ i, (size_t)fp1, fp1->sz);
+ if (fp1->nx)
+ printf_P(PSTR("%04x\n"), (size_t)fp1->nx);
+ else
+ printf_P(PSTR("NULL\n"));
+ freesum += fp1->sz;
+ }
+ }
+
+ freesum += get_freemem();
+
+ printf_P(PSTR("SP: %04x, __brkval: %04x, Total free: %04u\n"),
+ (size_t) STACK_POINTER(), (size_t) __brkval, freesum);
+}
+
+command_ret_t do_pr_free_avr(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
+{
+ printfreelist(NULL);
+
+ return CMD_RET_SUCCESS;
+}
+
+void dump_heap(void)
+{
+ //extern unsigned int __brkval;
+
+ dump_ram((uint8_t *)__malloc_heap_start, (size_t) __malloc_heap_start, __brkval - __malloc_heap_start,
+ "=== Heap:");
+}
+
+command_ret_t do_pr_heap_avr(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
+{
+ dump_heap();
+
+ return CMD_RET_SUCCESS;
+}
+
+#endif /* DEBUG */
diff --git a/avr/env.c b/avr/env.c
new file mode 100644
index 0000000..f167859
--- /dev/null
+++ b/avr/env.c
@@ -0,0 +1,881 @@
+/*
+ * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "env.h"
+#include <avr/eeprom.h>
+
+#include "config.h"
+#include "debug.h"
+#include "crc.h"
+#include "getopt-min.h"
+
+#define DEBUG_ENV 0 /* set to 1 to debug */
+
+#define debug_env(fmt, args...) \
+ debug_cond(DEBUG_ENV, fmt, ##args)
+
+
+/*
+ * Default Environment
+ */
+
+#define DELIM "\0"
+
+const FLASH char default_env[] = {
+ ENV_BAUDRATE "=" stringify(CONFIG_BAUDRATE) DELIM
+ ENV_BOOTDELAY "=" stringify(CONFIG_BOOTDELAY) DELIM
+ ENV_BOOTCMD "=" "pin ${pins};loadcpm3;go ${startaddress}" DELIM
+ ENV_CPM3_SYSFILE "=" CONFIG_CPM3_SYSFILE DELIM
+ ENV_PINALIAS "=" "0:PG5,1:PG4,2:PB4,3:PB5,4:PB6,5:PB7,"
+ "6:PG3,7:PG2,8:PG1,9:PG0,10:PE7" DELIM
+ "pins" "=" "2,8 low 9 high 3 2" DELIM
+ DELIM
+};
+
+
+#define ENV_SIZE (CONFIG_ENV_SIZE - sizeof(uint16_t) - sizeof(uint8_t))
+
+#define ENVLIST_DELETE (1<<0)
+
+
+/* EEPROM storage */
+typedef struct environment_s {
+ uint16_t crc; /* CRC16 over data bytes */
+ uint8_t flags; /* active/obsolete flags */
+#define ACTIVE_FLAG 1
+#define OBSOLETE_FLAG 0
+ char data[ENV_SIZE]; /* Environment data */
+} env_t;
+
+
+static uint8_t env_valid;
+static char env_list[ENV_SIZE];
+
+static
+char env_get_char(uint_fast16_t index)
+{
+ unsigned int off = CONFIG_ENV_OFFSET;
+ char ret;
+
+ switch (env_valid) {
+ case 2:
+ off += CONFIG_ENV_SIZE;
+ /* FALL TROUGH */
+ case 1:
+ ret = (char) eeprom_read_byte((const uint8_t *)off + index +
+ offsetof(env_t, data));
+ break;
+
+ default:
+ ret = default_env[index];
+ }
+
+ return ret;
+}
+
+static void envlist_clear(void)
+{
+ memset(env_list, 0, sizeof env_list);
+}
+
+static
+int env_item_delete(char *ep)
+{
+ char *next = ep + strlen(ep) + 1;
+ char *end = next;
+ while (*end++ != 0)
+ while (*end++ != 0);
+
+ memmove(ep, next, end - next);
+
+ return 0;
+}
+
+static int envcmp(const char *s1, const char *s2)
+{
+ const unsigned char *u1 = (const unsigned char *)s1;
+ const unsigned char *u2 = (const unsigned char *)s2;
+ unsigned char c1, c2;
+
+ do {
+ c1 = *u1++; c2 = *u2++;
+ if (c1 == '=') c1 = '\0';
+ if (c2 == '=') c2 = '\0';
+ } while (c1 != 0 && c1 - c2 == 0);
+
+ return c1 - c2;
+}
+
+static char *envlist_search(const MEMX char *name)
+{
+ char key[CONFIG_SYS_ENV_NAMELEN + 1];
+ const MEMX char *np = name;
+ int keylen = 0;
+ char c;
+
+ while (keylen <= CONFIG_SYS_ENV_NAMELEN && (c = *np++) != '\0')
+ key[keylen++] = c;
+ key[keylen] = '\0';
+
+ for (char *lp = env_list; *lp != 0; ++lp) {
+
+ if (envcmp(lp, key) == 0)
+ return lp;
+
+ while (*lp != 0)
+ ++lp;
+ }
+
+ return NULL;
+}
+
+static char *env_item_insert_name(char *pos, const char *name, int len)
+{
+ char *dstp = pos + len + 1;
+ char *end = pos;
+ while (*end++ != 0)
+ while (*end++ != 0);
+
+ if (end + len >= env_list + ENV_SIZE)
+ return NULL;
+
+ memmove(dstp, pos, end - pos);
+ strcpy(pos, name);
+ memset(pos + strlen(name), '=', dstp - pos - strlen(name));
+ *(dstp - 1) = '\0';
+
+ return pos;
+}
+
+
+static char *envlist_alloc(const char *name, int len)
+{
+ char *lp;
+ int rc;
+
+
+ for (lp = env_list; *lp != 0; ++lp) {
+
+ rc = envcmp(lp, name);
+ if (rc >= 0)
+ break;
+
+ /* rc < 0 : check next place */
+ while (*lp != 0)
+ ++lp;
+ }
+
+ /* rc == 0 : entry found, replace */
+ if (rc == 0)
+ env_item_delete(lp);
+
+ /* rc > 0 : entry not in list, insert */
+ /* *lp == 0 : entry not in list, insert */
+
+ lp = env_item_insert_name(lp, name, len);
+
+ return lp;
+}
+
+#if 0
+static char *env_item_insert(char *pos, const char *envstr)
+{
+ char *dstp = pos + strlen(envstr) + 1;
+ char *end = pos;
+ while (*end++ != 0)
+ while (*end++ != 0);
+
+ if (end > env_list + ENV_SIZE)
+ return NULL;
+
+ memmove(dstp, pos, end - pos);
+ strcpy(pos, envstr);
+
+ return pos;
+}
+
+static char *envlist_enter(const char *ep)
+{
+ char *lp;
+ int rc;
+
+ for (lp = env_list; *lp != 0; ++lp) {
+
+ rc = envcmp(lp, ep);
+ if (rc >= 0)
+ break;
+
+ /* rc < 0 : check next place */
+ while (*lp != 0)
+ ++lp;
+ }
+
+ /* rc == 0 : entry found, replace */
+ if (rc == 0)
+ env_item_delete(lp);
+
+ /* rc > 0 : entry not in list, insert */
+ /* *lp == 0 : entry not in list, insert */
+
+ lp = env_item_insert(lp, ep);
+
+ return lp;
+}
+#endif
+
+static
+int envlist_delete(const MEMX char *name)
+{
+ char *ep = envlist_search(name);
+
+ if (ep != NULL)
+ return env_item_delete(ep);
+
+ return 1;
+}
+
+static
+int envlist_import(uint8_t flags)
+{
+ if ((flags & ENVLIST_DELETE) != 0)
+ envlist_clear();
+
+ for (uint_fast16_t index = 0; env_get_char(index) != '\0'; ) {
+ char name[CONFIG_SYS_ENV_NAMELEN+1];
+ uint_fast8_t nlen = 0;
+ size_t len;
+ char ch;
+ for (len = 0; (ch = env_get_char(index + len)) != '\0'; ++len) {
+ if ((index + len) >= ENV_SIZE)
+ return -1;
+ if (nlen == 0) {
+ if (ch == '=') {
+ nlen = len;
+ name[nlen] = '\0';
+ } else {
+ if (len > CONFIG_SYS_ENV_NAMELEN)
+ return -1;
+ name[len] = ch;
+ }
+ }
+ }
+
+ char *ep = envlist_alloc(name, len);
+ if (ep == NULL) {
+ printf_P(PSTR("## Error inserting \"%s\" variable.\n"), name);
+ return 1;
+ }
+
+ index += nlen;
+ char *p = ep + nlen;
+ while ((*p++ = env_get_char(index++)) != '\0')
+ ;
+ }
+ return 0;
+}
+
+
+static
+uint16_t env_crc(uint16_t data_offset)
+{
+ uint16_t crc;
+ uint16_t i;
+ char c, c0;
+
+ crc = 0xffff;
+ c = 0xff;
+ for (i = 0; !(c == 0 && c0 == 0) && i < ENV_SIZE; i++)
+ {
+ c0 = c;
+ c = eeprom_read_byte((const uint8_t *) data_offset + i);
+ crc = crc16(crc, c);
+ }
+ return crc ;
+}
+
+
+/**
+ * return valid env
+ */
+static
+int env_check_valid(void)
+{
+ const uint16_t offset[2] = {CONFIG_ENV_OFFSET,
+ CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE};
+ uint_fast8_t flags[2], crc_ok[2];
+ int rc;
+
+ /* read FLAGS */
+ flags[0] = eeprom_read_byte ((uint8_t *) offset[0] +
+ offsetof(env_t, flags));
+ flags[1] = eeprom_read_byte ((uint8_t *) offset[1] +
+ offsetof(env_t, flags));
+
+ /* check CRC */
+ crc_ok[0] = (
+ eeprom_read_word((uint16_t *) offset[0] +
+ offsetof(env_t, crc))
+ == env_crc(offset[0] + offsetof(env_t, data))
+ );
+ crc_ok[1] = (
+ eeprom_read_word((uint16_t *) offset[1] +
+ offsetof(env_t, crc))
+ == env_crc(offset[1] + offsetof(env_t, data))
+ );
+
+ if (!crc_ok[0] && !crc_ok[1]) {
+ rc = 0;
+
+ } else if (crc_ok[0] && !crc_ok[1]) {
+ rc = 1;
+ } else if (!crc_ok[0] && crc_ok[1]) {
+ rc = 2;
+ } else {
+ /* both ok - check serial */
+#if 1
+ if (flags[1] == ACTIVE_FLAG && flags[0] != ACTIVE_FLAG)
+ rc = 2;
+ else if (flags[1] == OBSOLETE_FLAG && flags[0] == 0xFF)
+ rc = 2;
+ else
+ rc = 1;
+#else
+ if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG)
+ rc = 1;
+ else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG)
+ rc = 2;
+ else if (flags[0] == 0xFF && flags[1] == 0)
+ rc = 2;
+ else if (flags[1] == 0xFF && flags[0] == 0)
+ rc = 1;
+ else /* flags are equal - almost impossible */
+ rc = 1;
+#endif
+ }
+
+ return rc;
+}
+
+
+int env_init(void)
+{
+ env_valid = env_check_valid();
+ if (env_valid == 0) {
+ printf_P(PSTR("*** Warning - bad CRC, "
+ "using default environment\n\n"));
+ }
+
+ envlist_import(ENVLIST_DELETE);
+ return 0;
+}
+
+
+char *getenv_str(const MEMX char *name)
+{
+ char *ep = envlist_search(name);
+ if (ep != NULL)
+ return strchr(ep, '=') + 1;
+ else
+ return NULL;
+}
+
+static
+int env_item_save(char *ep, uint16_t offset, int space_left)
+{
+ int len;
+
+ len = strlen(ep);
+ if (len == 0)
+ return 0;
+ len += 1;
+ if (len > space_left)
+ return 0;
+
+ eeprom_update_block(ep, (uint8_t *) offset, len);
+
+ return len;
+}
+
+
+static
+int saveenv(void)
+{
+ unsigned int off = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
+ unsigned int off_red = CONFIG_ENV_OFFSET;
+ unsigned int pos;
+ int len, left;
+ uint16_t crc;
+ int rc = 0;
+
+ if (env_valid == 2) {
+ off = CONFIG_ENV_OFFSET;
+ off_red = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
+ }
+
+ eeprom_update_byte((uint8_t *) off + offsetof(env_t, flags), 0xff);
+
+ pos = off + offsetof(env_t, data);
+ left = ENV_SIZE - 1;
+
+ char *ep = env_list;
+ while (*ep) {
+ len = env_item_save(ep, pos, left);
+ if (len == 0) {
+ return 1;
+ }
+ pos += len;
+ left -= len;
+ while (*ep++);
+ }
+
+ /* terminate list */
+ eeprom_update_byte((uint8_t *) pos, 0);
+ crc = env_crc(off + offsetof(env_t, data));
+ eeprom_update_word((uint16_t *) off + offsetof(env_t, crc), crc);
+ eeprom_update_byte((uint8_t *) off + offsetof(env_t, flags),
+ ACTIVE_FLAG);
+
+ if (rc == 0) {
+ eeprom_update_byte((uint8_t *) off_red + offsetof(env_t, flags),
+ OBSOLETE_FLAG);
+ env_valid = (env_valid == 2) ? 1 : 2;
+ }
+
+ return rc;
+}
+
+
+static
+int env_item_print(char *ep, bool mode)
+{
+ int len = strlen(ep) + 1;
+
+ if (mode) {
+ my_puts_P(PSTR("setenv "));
+ char ch;
+ while ((ch = *ep++) != '=')
+ putchar(ch);
+ printf_P(PSTR(" '%s'\n"), ep);
+ } else {
+ puts(ep);
+ }
+
+ return len;
+}
+
+
+/*
+ * Command interface: print one or all environment variables
+ *
+ * Returns -1 in case of error, or length of printed string
+ */
+static
+int env_print(const char *name, bool mode)
+{
+ int len = -1;
+ char *ep;
+
+ if (name != NULL) { /* print a single name */
+
+ ep = envlist_search(name);
+ if (ep != NULL)
+ len = env_item_print(ep, mode);
+
+ } else { /* print whole list */
+ len = 0;
+ ep = env_list;
+ while (*ep) {
+ len += env_item_print(ep, mode);
+ while (*ep++);
+ }
+ }
+ return len;
+}
+
+
+/**
+ * Set or delete environment variable
+ *
+ * Set a new environment variable,
+ * or replace or delete an existing one.
+ *
+ * @param flag (not used)
+ * @param argc
+ * @param argv[1] Environment variable to set or delete
+ * if no more args
+ * @param argv[2] ... Value to set it to
+ *
+ * @return 0 if ok, 1 on error
+ */
+static
+command_ret_t _do_env_set(uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ char *name = argv[1];
+
+ if (strchr(name, '=')) {
+ printf_P(PSTR("## Error: illegal character '='"
+ "in variable name \"%s\"\n"), name);
+ return CMD_RET_FAILURE;
+ }
+ size_t len = strlen(name);
+ if (len > CONFIG_SYS_ENV_NAMELEN) {
+ printf_P(PSTR("## Error: Variable name \"%s\" too long. "
+ "(max %d characters)\n"), name, CONFIG_SYS_ENV_NAMELEN);
+ return CMD_RET_FAILURE;
+ }
+
+ /* Delete only ? */
+ if (argc < 3) {
+ int rc = envlist_delete(name);
+ return rc ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+ }
+
+ /* Insert / replace new value */
+
+ for (int_fast8_t i = 2; i < argc; ++i)
+ len += strlen(argv[i]) + 1;
+
+ char *pos = envlist_alloc(name, len);
+ if (pos == NULL) {
+ printf_P(PSTR("## Error inserting \"%s\" variable.\n"), name);
+ return CMD_RET_FAILURE;
+ }
+
+ char *vp = pos + strlen(name) + 1;
+ for (int_fast8_t i = 2; i < argc; ++i) {
+ char *ap = argv[i];
+ while ((*vp++ = *ap++) != '\0');
+ //if (i < argc-1)
+ *(vp-1) = ' ';
+ }
+ *(vp-1) = '\0';
+
+ return CMD_RET_SUCCESS;
+}
+
+/**
+ * Set an environment variable
+ *
+ * @param varname Environment variable to set
+ * @param varvalue Value to set it to
+ * @return 0 if ok, 1 on error
+ */
+
+int setenv(const MEMX char *varname, const char *varvalue)
+{
+ int rc;
+
+#ifdef __MEMX
+ char *tmpname;
+ if (__builtin_avr_flash_segment(varname) != -1) {
+ tmpname = malloc(strlen_P(varname)+1);
+ if (tmpname == NULL) {
+ printf_P(PSTR("setenv: Out of Memory!\n"));
+ return 1;
+ }
+ strcpy_P(tmpname, varname);
+ } else
+ tmpname = (char *) varname;
+
+ const char * const argv[3] = { NULL, tmpname, varvalue };
+#else
+ const char * const argv[3] = { NULL, varname, varvalue };
+#endif
+
+ int argc = 3;
+
+ if (varvalue == NULL || varvalue[0] == '\0')
+ --argc;
+
+ rc = (int) _do_env_set(0, argc, (char * const *)argv);
+
+#ifdef __MEMX
+ if (__builtin_avr_flash_segment(varname) != -1)
+ free(tmpname);
+#endif
+ return rc;
+}
+
+/**
+ * Set an environment variable to an integer value
+ *
+ * @param name Environment variable to set
+ * @param value Value to set it to
+ * @param radix Base
+ * @return 0 if ok, 1 on error
+ */
+static
+int setenv_intval(const MEMX char *name, unsigned long value, int radix)
+{
+ char buf[11];
+
+ ultoa(value, buf, radix);
+
+ return setenv(name, buf);
+}
+
+/**
+ * Set an environment variable to a decimal integer value
+ *
+ * @param name Environment variable to set
+ * @param value Value to set it to
+ * @return 0 if ok, 1 on error
+ */
+int setenv_ulong(const MEMX char *name, unsigned long value)
+{
+ return setenv_intval(name, value, 10);
+}
+
+
+/**
+ * Set an environment variable to a value in hex
+ *
+ * @param name Environment variable to set
+ * @param value Value to set it to
+ * @return 0 if ok, 1 on error
+ */
+int setenv_hex(const MEMX char *name, unsigned long value)
+{
+ return setenv_intval(name, value, 16);
+}
+
+
+/**
+ * Decode the integer value of an environment variable and return it.
+ *
+ * @param name Name of environemnt variable
+ * @param base Number base to use (normally 10, or 16 for hex)
+ * @param default_val Default value to return if the variable is not
+ * found
+ * @return the decoded value, or default_val if not found
+ */
+unsigned long getenv_ulong(const MEMX char *name, int base, unsigned long default_val)
+{
+ unsigned long value;
+ char *vp, *endp;
+
+ vp = getenv_str(name);
+
+ if (vp != NULL ) {
+ value = strtoul(vp, &endp, base);
+ if (endp != vp)
+ return value;
+ }
+
+ return default_val;
+}
+
+
+/*
+ * Read an environment variable as a boolean
+ */
+bool getenv_yesno(const MEMX char *name)
+{
+ char *s = getenv_str(name);
+
+ if (s == NULL)
+ return false;
+
+ return strchr_P(PSTR("1yYtT"), *s) != NULL;
+
+/*
+ return *s == '1' || *s == 'y' || *s == 'Y' || *s == 't' || *s == 'T' ?
+ 1 : 0;
+*/
+}
+
+command_ret_t do_env_print(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED,
+ int argc, char * const argv[])
+{
+ bool mode = 0;
+ command_ret_t rc = CMD_RET_SUCCESS;
+
+ int opt;
+ while ((opt = getopt(argc, argv, PSTR("s"))) != -1) {
+ switch (opt) {
+ case 's':
+ mode = 1;
+ break;
+ default: /* '?' */
+ return CMD_RET_USAGE;
+ }
+ }
+
+ if (optind == argc) {
+ /* print all env vars */
+ int size = env_print(NULL, mode);
+ if (size < 0)
+ return CMD_RET_FAILURE;
+ if (mode == 0)
+ printf_P(PSTR("\nEnvironment size: %d/%d bytes\n"),
+ size, ENV_SIZE - 1);
+ return CMD_RET_SUCCESS;
+ }
+
+ /* print selected env vars */
+ while (optind < argc) {
+ int len = env_print(argv[optind], mode);
+ if (len < 0) {
+ rc = CMD_RET_FAILURE;
+ }
+ optind++;
+ }
+
+ return rc;
+}
+
+
+command_ret_t do_env_set(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag, int argc, char * const argv[])
+{
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ return _do_env_set(flag, argc, argv);
+}
+
+
+command_ret_t do_env_default(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED,
+ int argc UNUSED, char * const argv[] UNUSED)
+{
+ uint8_t tmp = env_valid;
+ env_valid = 0;
+
+ /* Reset the whole environment */
+ printf_P(PSTR("## Resetting to default environment\n"));
+ envlist_import(ENVLIST_DELETE);
+
+ env_valid = tmp;
+
+ return CMD_RET_SUCCESS;
+}
+
+
+command_ret_t do_env_save(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
+{
+ printf_P(PSTR("Saving Environment ...\n"));
+ return saveenv() ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+
+#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
+
+cmd_tbl_t cmd_tbl_env[] = {
+CMD_TBL_ITEM_COMPLETE(
+ printenv, CONFIG_SYS_MAXARGS, 1|CTBL_SUBCMDAUTO, do_env_print,
+ "print environment variables",
+ "[-s] [name ...]\n"
+ " Print value of environment variable(s) 'name'\n"
+ " If no names are given, print values of all environment variables\n"
+ " -s Print in setenv form",
+ var_complete
+),
+CMD_TBL_ITEM_COMPLETE(
+ setenv, CONFIG_SYS_MAXARGS, CTBL_SUBCMDAUTO, do_env_set,
+ "set environment variables",
+ "name value ...\n"
+ " - set environment variable 'name' to 'value ...'\n"
+ "setenv name\n"
+ " - delete environment variable 'name'",
+ var_complete
+),
+CMD_TBL_ITEM(
+ saveenv, 1, CTBL_SUBCMDAUTO, do_env_save,
+ "save environment variables to persistent storage",
+ ""
+),
+CMD_TBL_ITEM(
+ defaultenv, 1, CTBL_SUBCMDAUTO, do_env_default,
+ "set all environment variables to their default values",
+ ""
+),
+
+CMD_TBL_ITEM_COMPLETE(
+ print, CONFIG_SYS_MAXARGS, 1, do_env_print,
+ "print environment variables",
+ "[-s] [name ...]\n"
+ " Print value of environment variable(s) 'name'\n"
+ " If no names are given, print values of all environment variables\n"
+ " -s Print in setenv form",
+ var_complete
+),
+CMD_TBL_ITEM_COMPLETE(
+ set, CONFIG_SYS_MAXARGS, 0, do_env_set,
+ "set environment variables",
+ "name value ...\n"
+ " - set environment variable 'name' to 'value ...'\n"
+ "setenv name\n"
+ " - delete environment variable 'name'",
+ var_complete
+),
+CMD_TBL_ITEM(
+ save, 1, 0, do_env_save,
+ "save environment variables to persistent storage",
+ ""
+),
+CMD_TBL_ITEM(
+ default, 1, 0, do_env_default,
+ "set all environment variables to their default values",
+ ""
+),
+
+CMD_TBL_ITEM(
+ help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
+ "Print sub command description/usage",
+ "\n"
+ " - print brief description of all sub commands\n"
+ "env help command ...\n"
+ " - print detailed usage of sub cmd 'command'"
+),
+
+/* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
+ {FSTR("?"), CONFIG_SYS_MAXARGS, 1, do_help,
+ NULL,
+#ifdef CONFIG_SYS_LONGHELP
+ FSTR(""),
+#endif /* CONFIG_SYS_LONGHELP */
+ NULL,
+#ifdef CONFIG_AUTO_COMPLETE
+ NULL,
+#endif
+},
+/* Mark end of table */
+CMD_TBL_END(cmd_tbl_env)
+};
+
+command_ret_t do_env(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
+{
+ return CMD_RET_USAGE;
+}
diff --git a/avr/eval_arg.c b/avr/eval_arg.c
new file mode 100644
index 0000000..deb601f
--- /dev/null
+++ b/avr/eval_arg.c
@@ -0,0 +1,154 @@
+/*
+ * (C) Copyright 2016 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "eval_arg.h"
+#include "command.h" /* jump_buf */
+#include <setjmp.h>
+#include "print-utils.h"
+
+static jmp_buf eval_jbuf;
+static char ch;
+static char *start_p;
+static char *bp;
+
+
+static long expr(void);
+
+static void print_error_pos(void)
+{
+ printf_P(PSTR("Arg: '%s'\n"
+ " "), start_p);
+ print_blanks(bp - start_p);
+ my_puts_P(PSTR("^syntax error!\n"));
+}
+
+static void error (void)
+{
+ --bp;
+ longjmp (eval_jbuf, 1);
+}
+
+static void next(void)
+{
+ do
+ ch = *bp++;
+ while (isspace(ch));
+}
+
+static long number (void)
+{
+ int base = 16;
+ char *end_p;
+ long n;
+
+ if (ch == '$') { /* FIXME: should be '#' */
+ next();
+ base = 10;
+ }
+ if (!isdigit(ch) && !(base == 16 && isxdigit(ch)))
+ error ();
+
+ n = strtoul(bp - 1, &end_p, base);
+
+ if (end_p == bp - 1)
+ error();
+ bp = end_p;
+ next();
+
+ return n;
+}
+
+static long factor (void)
+{
+ long f;
+
+ if (ch == '(')
+ {
+ next();
+ f = expr();
+ if (ch == ')')
+ next();
+ else
+ error ();
+ } else {
+ char sign = ch;
+ if (sign == '+' || sign == '-') {
+ next();
+ }
+ f = number();
+ if (sign == '-')
+ f = -f;
+ }
+ return f;
+}
+
+static long term (void)
+{
+ long t = factor();
+
+ for (;;)
+ switch (ch) {
+ case '*':
+ next();
+ t *= factor();
+ break;
+ case '/':
+ next();
+ t /= factor();
+ break;
+ case '%':
+ next();
+ t %= factor();
+ break;
+ default:
+ return t;
+ }
+}
+
+
+static long expr(void)
+{
+ long e = term ();
+
+ while (ch == '+' || ch == '-') {
+ char op = ch;
+ next();
+ if (op == '-')
+ e -= term ();
+ else
+ e += term ();
+ }
+ return e;
+}
+
+long eval_arg(char *arg, char **end_ptr)
+{
+ long val;
+
+ start_p = arg;
+ bp = arg;
+ next();
+ if (setjmp (eval_jbuf) != 0) {
+ if (!end_ptr) {
+ print_error_pos();
+ longjmp(cmd_jbuf, 1);
+ }
+ val = -1;
+ } else {
+ val = expr ();
+ --bp;
+ }
+
+ if (!end_ptr) {
+ if (*bp != '\0') {
+ print_error_pos();
+ longjmp(cmd_jbuf, 1);
+ }
+ } else
+ *end_ptr = bp;
+
+ return val;
+}
diff --git a/avr/getopt-min.c b/avr/getopt-min.c
new file mode 100644
index 0000000..4e67a9b
--- /dev/null
+++ b/avr/getopt-min.c
@@ -0,0 +1,75 @@
+/*
+ * Minimum getopt, original version was:
+ */
+
+/*
+ getopt -- public domain version of standard System V routine
+
+ Strictly enforces the System V Command Syntax Standard;
+ provided by D A Gwyn of BRL for generic ANSI C implementations
+*/
+/* $Id: getopt.c,v 1.2 1992/12/07 11:12:52 nickc Exp $ */
+
+#include "common.h" /* definition of FLASH */
+
+int optind; /* next argv[] index */
+char *optarg; /* option parameter if any */
+
+
+int
+getopt( /* returns letter, '?', EOF */
+ int argc, /* argument count from main */
+ char *const argv[], /* argument vector from main */
+ const FLASH char *optstring ) /* allowed args, e.g. "ab:c" */
+{
+ static int sp; /* position within argument */
+ int osp; /* saved `sp' for param test */
+ int c; /* option letter */
+ const FLASH char *cp; /* -> option in `optstring' */
+
+ optarg = NULL;
+ if (optind == 0) { /* start a new argument scan */
+ optind = 1;
+ sp = 1;
+ }
+
+ if ( sp == 1 ) /* fresh argument */
+ {
+ if ( optind >= argc /* no more arguments */
+ || argv[optind][0] != '-' /* no more options */
+ || argv[optind][1] == '\0' /* not option; stdin */
+ )
+ return -1;
+ }
+
+ c = argv[optind][sp]; /* option letter */
+ osp = sp++; /* get ready for next letter */
+
+ if ( argv[optind][sp] == '\0' ) /* end of argument */
+ {
+ ++optind; /* get ready for next try */
+ sp = 1; /* beginning of next argument */
+ }
+
+ if ( c == ':' /* optstring syntax conflict */
+ || (cp = strchr_P( optstring, c )) == NULL /* not found */
+ )
+ return '?';
+
+ if ( cp[1] == ':' ) /* option takes parameter */
+ {
+ if ( osp != 1 )
+ return '?';
+
+ if ( sp != 1 ) /* reset by end of argument */
+ return '?';
+
+ if ( optind >= argc )
+ return '?';
+
+ optarg = argv[optind]; /* make parameter available */
+ ++optind; /* skip over parameter */
+ }
+
+ return c;
+}
diff --git a/avr/gpio.c b/avr/gpio.c
new file mode 100644
index 0000000..6f73cee
--- /dev/null
+++ b/avr/gpio.c
@@ -0,0 +1,376 @@
+/*
+ * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "common.h"
+#include <util/atomic.h>
+#include <limits.h>
+#include "debug.h"
+#include "gpio.h"
+
+/*
+
+Pin Name Port Timer Mode max div max div min f [Hz]
+--------------------------------------------------------------------------------
+0 PG5 OC0B PWM (2**8)*1024 262144 70.31
+1 PG4
+2 CLK2 PB4 OC2A Toggle (2**8)*1024*2 524288 35.16
+3 ZCLK PB5 OC1A PWM (2**16)*1024 67108864 0.2746
+4 PB6 OC1B PWM (2**16)*1024 67108864 0.2746
+5 PB7 OC0A Toggle (2**8)*1024*2 524288 35.16
+6 PG3
+7 PG2
+8 PG1
+9 PG0
+10 CLKO PE7
+--------------------------------------------------------------------------------
+
+
+pre Timer0 Timer1 Timer2
+--------------------------------------------------
+0 0 0 0
+1 1 1 1
+2 8 x8 8 x8 8 x8
+3 64 x8 64 x8 32 x4
+4 256 x4 256 x4 64 x2
+5 1024 x4 1024 x4 128 x2
+6 256 x2
+7 1024 x4
+--------------------------------------------------
+*/
+
+#define PWMTOGGLE 0b01
+#define PWMPOS 0b10
+#define PWMNEG 0b11
+
+
+const FLASH uint8_t prescale_factors_01[] =
+ { 8, 8, 4, 4, 0 };
+
+const FLASH uint8_t prescale_factors_2[] =
+ { 8, 4, 2, 2, 2, 4, 0 };
+
+typedef volatile struct {
+ uint8_t pin;
+ uint8_t ddr;
+ uint8_t pout;
+} port_t ;
+
+struct pindef_s {
+ port_t * const adr;
+ const uint8_t mask;
+#define NO_TIMER 0
+#define TIMER0 (1 << 0)
+#define TIMER1 (2 << 0)
+#define TIMER2 (3 << 0)
+#define TIMER (3 << 0)
+#define T_16BIT (1 << 3)
+#define CHANA (1 << 4)
+#define CHANB (0 << 4)
+ const uint8_t timer;
+};
+
+
+const FLASH struct pindef_s pinlist[GPIO_MAX] = {
+ { (port_t *) &PING, _BV(5), TIMER0 | CHANB },
+ { (port_t *) &PING, _BV(4), NO_TIMER },
+ { (port_t *) &PINB, _BV(4), TIMER2 | CHANA },
+ { (port_t *) &PINB, _BV(5), TIMER1 | CHANA | T_16BIT },
+ { (port_t *) &PINB, _BV(6), TIMER1 | CHANB | T_16BIT },
+ { (port_t *) &PINB, _BV(7), TIMER0 | CHANA },
+ { (port_t *) &PING, _BV(3), NO_TIMER },
+ { (port_t *) &PING, _BV(2), NO_TIMER },
+ { (port_t *) &PING, _BV(1), NO_TIMER },
+ { (port_t *) &PING, _BV(0), NO_TIMER },
+ { (port_t *) &PINE, _BV(7), NO_TIMER },
+};
+
+void gpio_timer_off(uint8_t timertype)
+{
+ uint8_t chan_mask;
+
+ if (timertype & CHANA)
+ chan_mask = 0xc0;
+ else
+ chan_mask = 0x30;
+
+ switch (timertype & TIMER) {
+ case TIMER0:
+ if (TCCR0A & chan_mask) {
+ TCCR0B = 0;
+ TCCR0A = 0;
+ PRR0 |= _BV(PRTIM0);
+ }
+ break;
+ case TIMER1:
+ if (TCCR1A & chan_mask) {
+ TCCR1B = 0;
+ TCCR1A = 0;
+ PRR0 |= _BV(PRTIM1);
+ }
+ break;
+ case TIMER2:
+ if (TCCR2A & chan_mask) {
+ TCCR2B = 0;
+ TCCR2A = 0;
+ PRR0 |= _BV(PRTIM2);
+ }
+ break;
+ }
+}
+
+int gpio_config(int pin, gpiomode_t mode)
+{
+ if ((unsigned) pin >= ARRAY_SIZE(pinlist)) {
+ /* Invalid pin number */
+ return -1;
+ } else {
+
+ port_t *p = pinlist[pin].adr;
+ uint8_t bit = pinlist[pin].mask;
+
+ switch (mode) {
+ case INPUT:
+ gpio_timer_off(pinlist[pin].timer);
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ p->ddr &= ~bit;
+ p->pout &= ~bit;
+ }
+ break;
+ case INPUT_PULLUP:
+ gpio_timer_off(pinlist[pin].timer);
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ p->ddr &= ~bit;
+ p->pout |= bit;
+ }
+ break;
+ case OUTPUT:
+ gpio_timer_off(pinlist[pin].timer);
+ /* FALL TROUGH */
+ case OUTPUT_TIMER:
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ p->ddr |= bit;
+ }
+ break;
+ default:
+ /* Invalid pin mode */
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void gpio_write(int pin, uint8_t val)
+{
+ port_t *p = pinlist[pin].adr;
+ uint8_t bit = pinlist[pin].mask;
+
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ if (val)
+ p->pout |= bit;
+ else
+ p->pout &= ~bit;
+ }
+}
+
+int gpio_read(int pin)
+{
+ port_t *p = pinlist[pin].adr;
+ uint8_t bit = pinlist[pin].mask;
+
+ return (p->pin & bit) != 0;
+}
+
+gpiomode_t gpio_config_get(int pin)
+{
+ uint8_t timertype = pinlist[pin].timer;
+
+ if (timertype & TIMER) {
+
+ uint8_t chan_mask;
+ if (timertype & CHANA)
+ chan_mask = 0xc0;
+ else
+ chan_mask = 0x30;
+
+ switch (timertype & TIMER) {
+ case TIMER0:
+ if (TCCR0A & chan_mask)
+ return OUTPUT_TIMER;
+ break;
+ case TIMER1:
+ if (TCCR1A & chan_mask)
+ return OUTPUT_TIMER;
+ break;
+ case TIMER2:
+ if (TCCR2A & chan_mask)
+ return OUTPUT_TIMER;
+ break;
+ }
+ }
+
+ port_t *p = pinlist[pin].adr;
+ uint8_t bit = pinlist[pin].mask;
+
+ if (p->ddr & bit)
+ return OUTPUT;
+
+ if (p->pout & bit)
+ return INPUT_PULLUP;
+
+ return INPUT;
+}
+
+/*
+ * return -1: pin has no timer output
+ * 0: pin is not configured for timer output
+ * > 0: divider
+ */
+
+long gpio_clockdiv_get(int pin)
+{
+ long divider;
+ uint8_t prescale;
+ const FLASH uint8_t *pstab;
+
+ uint8_t timertype = pinlist[pin].timer;
+ if ((timertype & TIMER) == 0)
+ return -1;
+
+ if (gpio_config_get(pin) != OUTPUT_TIMER)
+ return 0;
+
+ switch (timertype & TIMER) {
+ case TIMER0:
+ prescale = TCCR0B;
+ divider = OCR0A;
+ break;
+
+ case TIMER1:
+ prescale = TCCR1B;
+ divider = ICR1;
+ break;
+
+ case TIMER2:
+ prescale = TCCR2B;
+ divider = OCR2A;
+ break;
+ }
+
+ prescale = (prescale & 0x07) - 1;
+ divider += 1;
+
+ pstab = (timertype & TIMER) == TIMER2 ?
+ prescale_factors_2 : prescale_factors_01;
+
+ while (prescale--)
+ divider *= pstab[prescale];
+
+ if ((timertype & (CHANA|T_16BIT)) == CHANA)
+ divider *= 2;
+
+ return divider;
+}
+
+int gpio_clockdiv_set(int pin, unsigned long divider)
+{
+ unsigned long ltop;
+ uint16_t top;
+ uint8_t prescale;
+ const FLASH uint8_t *pstab;
+
+ uint8_t timertype = pinlist[pin].timer;
+ if ((timertype & TIMER) == 0)
+ return 0;
+
+ if (divider < 2)
+ return -1;
+
+ ltop = divider;
+ if ((timertype & (CHANA|T_16BIT)) == CHANA)
+ ltop /= 2;
+
+ if (ltop > 1024 * ((timertype & T_16BIT) ? (1L<<16) : (1L<<8)))
+ return -1;
+
+ prescale = 1;
+ pstab = (timertype & TIMER) == TIMER2 ?
+ prescale_factors_2 : prescale_factors_01;
+
+// debug("** clockdiv_set: pin: %d, ltop: %lu, prescale: %d\n",
+// pin, ltop, prescale);
+
+ while (ltop > ((timertype & T_16BIT) ? (1L<<16) : (1L<<8))) {
+// debug("** clockdiv_set: pin: %d, ltop: %lu, prescale: %d, *pstab %d\n",
+// pin, ltop, prescale, *pstab);
+
+ if (*pstab == 0)
+ return -1;
+ ltop /= *pstab++;
+ prescale++;
+ }
+
+ if (ltop == 0)
+ return -1;
+
+ top = ltop - 1;
+
+// PING |= _BV(0); /* Debug */
+
+ switch (timertype & TIMER) {
+ case TIMER0:
+ PRR0 &= ~_BV(PRTIM0);
+ TCCR0B = (1 << WGM02);
+ TCNT0 = 0;
+ OCR0A = top;
+ if (timertype & CHANA) {
+ TCCR0A = (PWMTOGGLE << COM0A0) | (0b11 << WGM00);
+ } else {
+ OCR0B = top/2;
+ TCCR0A = (PWMPOS << COM0B0) | (0b11 << WGM10);
+ }
+ TCCR0B = (1 << WGM02) | (prescale << CS10);
+ break;
+
+ case TIMER1:
+ PRR0 &= ~_BV(PRTIM1);
+ TCCR1B = (0b11 << WGM12);
+ TCNT1 = 0;
+ ICR1 = top;
+ if (timertype & CHANA) {
+ OCR1A = top/2;
+ TCCR1A = (PWMPOS << COM1A0) | (0b10 << WGM10);
+ } else {
+ OCR1B = top/2;
+ TCCR1A = (PWMPOS << COM1B0) | (0b10 << WGM10);
+ }
+// debug("pin: %d, top: %u,"
+// " ICR1: %u, OCR1A: %u, OCR1B: %u\n",
+// pin, top, ICR1, OCR1A, OCR1B);
+
+ TCCR1B = (0b11 << WGM12) | (prescale << CS10);
+ break;
+
+ case TIMER2:
+ PRR0 &= ~_BV(PRTIM2);
+ TCCR2B = (1 << WGM22);
+ TCNT2 = 0;
+ OCR2A = top;
+ if (timertype & CHANA) {
+ TCCR2A = (PWMTOGGLE << COM2A0) | (0b11 << WGM20);
+ } else {
+ OCR2B = top/2;
+ TCCR2A = (PWMPOS << COM2B0) | (0b11 << WGM10);
+ }
+ TCCR2B = (1 << WGM22) | (prescale << CS10);
+ break;
+ }
+
+// PING |= _BV(0); /* Debug */
+
+ gpio_config(pin, OUTPUT_TIMER);
+
+ return 0;
+}
diff --git a/avr/i2c.c b/avr/i2c.c
new file mode 100644
index 0000000..a0a12c8
--- /dev/null
+++ b/avr/i2c.c
@@ -0,0 +1,377 @@
+/*
+ * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ * I2C (TWI) master interface.
+ */
+
+#include "common.h"
+#include <avr/interrupt.h>
+
+#include "config.h"
+#include "timer.h"
+#include "debug.h"
+#include "i2c.h"
+
+#define DEBUG_I2C 0
+
+#define debug_i2c(fmt, args...) \
+ debug_cond(DEBUG_I2C, fmt, ##args)
+
+
+/* General TWI Master status codes */
+#define TWI_START 0x08 /* START has been transmitted */
+#define TWI_REP_START 0x10 /* Repeated START has been transmitted */
+#define TWI_ARB_LOST 0x38 /* Arbitration lost */
+
+/* TWI Master Transmitter status codes */
+#define TWI_MTX_ADR_ACK 0x18 /* SLA+W has been transmitted and ACK received */
+#define TWI_MTX_ADR_NACK 0x20 /* SLA+W has been transmitted and NACK received */
+#define TWI_MTX_DATA_ACK 0x28 /* Data byte has been transmitted and ACK received */
+#define TWI_MTX_DATA_NACK 0x30 /* Data byte has been transmitted and NACK received */
+
+/* TWI Master Receiver status codes */
+#define TWI_MRX_ADR_ACK 0x40 /* SLA+R has been transmitted and ACK received */
+#define TWI_MRX_ADR_NACK 0x48 /* SLA+R has been transmitted and NACK received */
+#define TWI_MRX_DATA_ACK 0x50 /* Data byte has been received and ACK transmitted */
+#define TWI_MRX_DATA_NACK 0x58 /* Data byte has been received and NACK transmitted */
+
+/* TWI Miscellaneous status codes */
+#define TWI_NO_STATE 0xF8 /* No relevant state information available */
+#define TWI_BUS_ERROR 0x00 /* Bus error due to an illegal START or STOP condition */
+
+
+/*
+ * TWINT: TWI Interrupt Flag
+ * TWEA: TWI Enable Acknowledge Bit
+ * TWSTA: TWI START Condition Bit
+ * TWSTO: TWI STOP Condition Bit
+ * TWEN: TWI Enable Bit
+ * TWIE: TWI Interrupt Enable
+ *
+ * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)
+ * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)| (1<<TWEA)
+ * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)
+ *
+ * default:
+ * (1<<TWEN)| (1<<TWINT)| (1<<TWSTO)
+ *
+ * Init:
+ * (1<<TWEN)
+ *
+ * start read/write:
+ * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
+ * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
+ * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
+ * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
+ *
+ * wait ready:
+ * (1<<TWIE)|(1<<TWSTO)
+ *
+ *
+ *
+ *i2c_result
+ *
+ * 0b10000000 Busy (Transmission in progress)
+ * 0b01000000 Timeout
+ * 0b00001000 Start transmitted
+ * 0b00000100 Slave acknowledged address
+ * 0b00000010 Data byte(s) transmitted/received
+ * 0b00000001 Transmission completed
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+
+#define TWI_C_DISABLE 0x00
+#define TWI_C_ENABLE (1<<TWEN)
+
+
+
+ typedef struct i2c_msg_s {
+ uint8_t stat;
+ #define XMIT_DONE (1<<0)
+ #define DATA_ACK (1<<1)
+ #define ADDR_ACK (1<<2)
+ #define START (1<<3)
+ #define TIMEOUT (1<<6)
+ #define BUSY (1<<7)
+ uint8_t idx;
+ uint8_t len;
+ uint8_t buf[CONFIG_SYS_I2C_BUFSIZE];
+} i2c_msg_t;
+
+static volatile i2c_msg_t xmit;
+
+ISR(TWI_vect)
+{
+ uint8_t tmp_stat;
+ uint8_t tmp_idx;
+ uint8_t next_twcr;
+ uint8_t n;
+
+ tmp_idx = xmit.idx;
+ tmp_stat = xmit.stat;
+
+ uint8_t twsr = TWSR;
+
+ switch (twsr & 0xf8) {
+
+ case TWI_START:
+ case TWI_REP_START:
+ tmp_stat = BUSY | START;
+ tmp_idx = 0; /* reset xmit_buf index */
+
+ if (tmp_idx < xmit.len) { /* all bytes transmited? */
+ TWDR = xmit.buf[tmp_idx];
+ ++tmp_idx;
+ next_twcr = (1<<TWEN)|(1<<TWIE)|(1<<TWINT);
+ } else {
+ tmp_stat |= XMIT_DONE;
+ tmp_stat &= ~BUSY;
+ next_twcr = (1<<TWEN)|(0<<TWIE)|(1<<TWINT)|(1<<TWSTO);
+ }
+ break;
+
+ case TWI_MTX_ADR_ACK:
+ case TWI_MTX_DATA_ACK:
+ if ((twsr&0xf8) == TWI_MTX_ADR_ACK)
+ tmp_stat |= ADDR_ACK;
+ else
+ tmp_stat |= DATA_ACK;
+
+ if (tmp_idx < xmit.len) { /* all bytes transmited? */
+ TWDR = xmit.buf[tmp_idx];
+ ++tmp_idx;
+ next_twcr = (1<<TWEN)|(1<<TWIE)|(1<<TWINT);
+ } else {
+ tmp_stat |= XMIT_DONE;
+ tmp_stat &= ~BUSY;
+ next_twcr = (1<<TWEN)|(0<<TWIE)|(1<<TWINT)|(1<<TWSTO);
+ }
+ break;
+
+ case TWI_MTX_DATA_NACK:
+ tmp_stat |= XMIT_DONE;
+ tmp_stat &= ~BUSY;
+ next_twcr = (1<<TWEN)|(0<<TWIE)|(1<<TWINT)|(1<<TWSTO);
+ break;
+
+ case TWI_MRX_DATA_ACK:
+ xmit.buf[tmp_idx] = TWDR;
+ ++tmp_idx;
+ /* fall thru */
+ case TWI_MRX_ADR_ACK:
+ if ((twsr&0xf8) == TWI_MRX_ADR_ACK)
+ tmp_stat |= ADDR_ACK;
+ else
+ tmp_stat |= DATA_ACK;
+
+ n = xmit.len-1;
+ if (tmp_idx < n) {
+ next_twcr = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA);
+ } else {
+ next_twcr = (1<<TWEN)|(1<<TWIE)|(1<<TWINT);
+ }
+ break;
+
+ case TWI_MRX_DATA_NACK:
+ tmp_stat |= ADDR_ACK | DATA_ACK;
+
+ xmit.buf[tmp_idx] = TWDR;
+ ++tmp_idx;
+ /* fall thru */
+ default:
+ tmp_stat &= ~BUSY;
+ next_twcr = (1<<TWEN)|(0<<TWIE)|(1<<TWINT)|(1<<TWSTO);
+ break;
+ }
+
+ xmit.stat = tmp_stat;
+ xmit.idx = tmp_idx;
+
+ debug_i2c("|%02x", twsr);
+ TWCR = next_twcr;
+}
+
+
+/*------------------------------------------------------------------*/
+
+static uint8_t twps;
+static uint8_t twbr;
+
+
+static void _init(void)
+{
+ xmit.stat = 0;
+
+ /* Disable TWI, disable TWI interrupt. */
+ /* (Reset TWI hardware state machine.) */
+ TWCR = TWI_C_DISABLE;
+ _delay_us(5);
+#if DEBUG_I2C
+ memset((void *) xmit.buf, 0xdf, sizeof(xmit.buf));
+#endif
+
+ TWDR = 0xff;
+ TWBR = twbr;
+ TWSR = twps & 0x03;
+ TWCR = TWI_C_ENABLE;
+}
+
+void i2c_init(uint32_t speed)
+{
+ twps = 0;
+ uint32_t tmp_twbr = F_CPU /2 / speed - 8;
+
+ while (tmp_twbr > 255) {
+ tmp_twbr >>= 4;
+ twps += 1;
+ }
+ debug_cond((twps > 3), "*** TWCLK too low: %lu Hz\n", speed);
+
+ twbr = (uint8_t) tmp_twbr;
+
+ PRR0 &= ~_BV(PRTWI);
+ _init();
+}
+
+
+int_fast8_t i2c_waitready(void)
+{
+ uint32_t timer = get_timer(0);
+ uint8_t timeout = 0;
+
+ do {
+ if (get_timer(timer) >= 30) {
+ timeout = TIMEOUT;
+ _init();
+ }
+ } while ((TWCR & ((1<<TWIE)|(1<<TWSTO))) != 0 && !timeout);
+
+ xmit.stat |= timeout;
+
+#if DEBUG_I2C
+ dump_ram((uint8_t *) &xmit, 4, "=== i2c_wait ready: (done)");
+ _delay_ms(30);
+#endif
+ return xmit.stat;
+}
+
+static
+int i2c_send(uint8_t chip, uint16_t addr, uint8_t alen, uint8_t *buffer, int8_t len)
+{
+ uint8_t i, n;
+ uint8_t rc;
+
+ rc = i2c_waitready();
+ if ((rc & (BUSY | TIMEOUT)) != 0)
+ return rc;
+
+ xmit.stat = BUSY;
+ xmit.buf[0] = chip<<1;
+ for (i = 1; i < alen+1; i++) {
+ xmit.buf[i] = (uint8_t) addr;
+ addr >>= 8;
+ }
+ for (n = len + i; i < n; i++)
+ xmit.buf[i] = *buffer++;
+ xmit.len = i;
+
+#if DEBUG_I2C
+ dump_ram((uint8_t *) &xmit, 0x20, "=== i2c_send");
+ _delay_ms(30);
+#endif
+ /* Enable TWI, TWI int and initiate start condition */
+ TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA);
+
+ rc = xmit.stat;
+
+ return rc;
+}
+
+static
+int i2c_recv(uint8_t chip, uint8_t *buffer, int8_t len)
+{
+ uint8_t rc;
+
+ rc = i2c_waitready();
+ if ((rc & (BUSY | TIMEOUT)) != 0)
+ return rc;
+
+ xmit.stat = BUSY;
+ xmit.len = len + 1;
+ xmit.buf[0] = (chip<<1) | 1;
+
+#if DEBUG_I2C
+ dump_ram((uint8_t *) &xmit, 0x20, "=== i2c_recv: before start");
+ _delay_ms(30);
+#endif
+ /* Enable TWI, TWI int and initiate start condition */
+ TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA);
+ rc = i2c_waitready();
+
+#if DEBUG_I2C
+ dump_ram((uint8_t *) &xmit, 0x20, "=== i2c_recv: after completion");
+ _delay_ms(30);
+#endif
+ if (rc & DATA_ACK) {
+ /* at least 1 byte received */
+ for (uint8_t i=1, n=xmit.idx; i < n; i++)
+ *buffer++ = xmit.buf[i];
+ }
+
+ return rc;
+}
+
+/*
+ * Read/Write interface:
+ * chip: I2C chip address, range 0..127
+ * addr: Memory (register) address within the chip
+ * alen: Number of bytes to use for addr (typically 1, 2 for larger
+ * memories, 0 for register type devices with only one
+ * register)
+ * buffer: Where to read/write the data
+ * len: How many bytes to read/write
+ *
+ * Returns: 0 on success, not 0 on failure
+ */
+
+int i2c_write(uint8_t chip, unsigned int addr, uint_fast8_t alen,
+ uint8_t *buffer, uint_fast8_t len)
+{
+ int rc;
+
+ if ((alen > 2) || (1 + alen + len > CONFIG_SYS_I2C_BUFSIZE)) {
+ debug("** i2c_write: buffer overflow, alen: %u, len: %u\n",
+ alen, len);
+ return -1;
+ }
+
+ i2c_send(chip, addr, alen, buffer, len);
+ rc = i2c_waitready();
+
+ return (rc & XMIT_DONE) != 0;
+}
+
+int i2c_read(uint8_t chip, unsigned int addr, uint_fast8_t alen,
+ uint8_t *buffer, uint_fast8_t len)
+{
+ int rc;
+
+ if ((alen > 2) || (1 + len > CONFIG_SYS_I2C_BUFSIZE)) {
+ debug("** i2c_read: parameter error: alen: %u, len: %u\n",
+ alen, len);
+ return -1;
+ }
+
+ if (alen != 0) {
+ i2c_send(chip, addr, alen, NULL, 0);
+ }
+ rc = i2c_recv(chip, buffer, len);
+
+ return !((rc & (XMIT_DONE|DATA_ACK)) == (XMIT_DONE|DATA_ACK));
+}
diff --git a/avr/main.c b/avr/main.c
new file mode 100644
index 0000000..09df64b
--- /dev/null
+++ b/avr/main.c
@@ -0,0 +1,276 @@
+/*
+ * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+
+#include "common.h"
+#include <avr/interrupt.h>
+#include <avr/wdt.h>
+#include <stdlib.h>
+
+#include "config.h"
+#include "ff.h"
+#include "z80-if.h"
+#include "i2c.h"
+#include "con-utils.h"
+#include "serial.h"
+#include "timer.h"
+#include "cli.h"
+#include "env.h"
+#include "z180-serv.h"
+#include "gpio.h"
+#include "time.h"
+#include "rtc.h"
+#include "debug.h"
+#include "cmd_fat.h"
+
+
+uint8_t mcusr __attribute__ ((section (".noinit")));
+
+#if DEBUG
+__attribute__ ((naked)) __attribute__ ((section (".init3")))
+void preset_ram (void)
+{
+ for (uint8_t *p = (uint8_t *) RAMSTART; p <= (uint8_t *) RAMEND; p++)
+ *p = 0xdd;
+
+}
+#endif
+
+__attribute__ ((naked)) __attribute__ ((section (".init3")))
+void get_mcusr (void)
+{
+ /* save and clear reset reason(s) */
+ /* TODO: move to init section? */
+ mcusr = MCUSR;
+ MCUSR = 0;
+
+ wdt_disable();
+}
+
+/*--------------------------------------------------------------------------*/
+#if DEBUG
+
+static const FLASH char * const FLASH rreasons[] = {
+ FSTR("Power on"),
+ FSTR("External"),
+ FSTR("Brown out"),
+ FSTR("Watchdog"),
+ FSTR("JTAG"),
+ };
+
+static
+void print_reset_reason(void)
+{
+ uint8_t r = mcusr & 0x1f;
+ const FLASH char * const FLASH *p = rreasons;
+
+ my_puts_P(PSTR("### Reset reason(s): "));
+ if (r == 0)
+ my_puts_P(PSTR("none"));
+ for ( ; r; p++, r >>= 1) {
+ if (r & 1) {
+ my_puts_P(*p);
+ if (r & ~1)
+ my_puts_P(PSTR(", "));
+ }
+ }
+ my_puts_P(PSTR(".\n"));
+}
+
+#endif
+
+ISR(INT5_vect)
+{
+ Stat |= S_MSG_PENDING;
+}
+
+ISR(INT6_vect)
+{
+ Stat |= S_CON_PENDING;
+}
+
+static
+void setup_avr(void)
+{
+ /* CPU */
+
+ /* Disable JTAG Interface regardless of the JTAGEN fuse setting. */
+ MCUCR = _BV(JTD);
+ MCUCR = _BV(JTD);
+
+ /* Disable peripherals. Enable individually in respective init function. */
+ PRR0 = _BV(PRTWI) |
+ _BV(PRTIM2) | _BV(PRTIM0) | _BV(PRTIM1) |
+ _BV(PRSPI) | _BV(PRUSART0) | _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;
+
+ /* System timer */
+ setup_timer();
+
+ /* INT5, INT6: falling edge */
+ EICRB = (EICRB & ~((0b11 << ISC50) | (0b11 << ISC60))) |
+ (0b10 << ISC50) | (0b10 << ISC60);
+ /* Reset pending ints */
+ EIFR |= _BV(INTF5) | _BV(INTF6);
+ /* Enable INT5, and INT6 */
+ EIMSK |= _BV(INT5) | _BV(INT6);
+}
+
+static
+int reset_reason_is_power_on(void)
+{
+ return (mcusr & _BV(PORF)) != 0;
+}
+
+static
+void setup_system_time(void)
+{
+ struct tm rtc_time;
+
+ rtc_get(&rtc_time);
+ rtc_time.tm_isdst = 0;
+ set_system_time(mk_gmtime(&rtc_time) );
+}
+
+
+/*--------------------------------------------------------------------------*/
+
+/* 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(1); /* 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;
+}
+
+static
+const char *bootdelay_process(void)
+{
+ char *s;
+ int bootdelay;
+
+ bootdelay = (int) getenv_ulong(PSTR(ENV_BOOTDELAY), 10, CONFIG_BOOTDELAY);
+
+
+ debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);
+ _delay_ms(20);
+
+ s = getenv_str(PSTR(ENV_BOOTCMD));
+ stored_bootdelay = bootdelay;
+ return s;
+}
+
+static
+void autoboot_command(const char *s)
+{
+ debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "");
+ _delay_ms(20);
+
+ if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
+ run_command_list(s, -1);
+ }
+}
+
+
+static
+void main_loop(void)
+{
+ const char *s;
+
+ s = bootdelay_process();
+ autoboot_command(s);
+ cli_loop();
+}
+
+int main(void)
+{
+ extern void setup_mmc(void);
+
+ __malloc_margin = CONFIG_SYS_MALLOC_MARGIN;
+ setup_avr();
+ for (int i = 0; i < GPIO_MAX; i++)
+ gpio_config(i, INPUT_PULLUP);
+ setup_mmc();
+ env_init();
+ z80_setup_bus();
+
+ if (reset_reason_is_power_on())
+ _delay_ms(CONFIG_PWRON_DELAY);
+
+ serial_setup(getenv_ulong(PSTR(ENV_BAUDRATE), 10, CONFIG_BAUDRATE));
+ sei();
+
+#if DEBUG
+ debug("\n=========================< (RE)START DEBUG >=========================\n");
+ print_reset_reason();
+#endif
+
+ i2c_init(CONFIG_SYS_I2C_CLOCK);
+ setup_system_time();
+ setup_fatfs();
+
+ printf_P(PSTR("\n" stringify(DEVICE_NAME) "+Z8S180 Stamp Monitor - Version: " VERSION " \n\n"));
+
+ setup_z180_serv();
+
+ main_loop();
+}
diff --git a/avr/mmc.c b/avr/mmc.c
new file mode 100644
index 0000000..a40dc31
--- /dev/null
+++ b/avr/mmc.c
@@ -0,0 +1,807 @@
+/*-----------------------------------------------------------------------*/
+/* MMCv3/SDv1/SDv2 (in SPI mode) control module (C)ChaN, 2007 */
+/*-----------------------------------------------------------------------*/
+/* Only spi_rcvr(), spi_xmit(), disk_timerproc() and some macros */
+/* are platform dependent. */
+/*-----------------------------------------------------------------------*/
+
+#include "common.h"
+#include <util/atomic.h>
+#include "timer.h"
+#include "spi.h"
+#include "diskio.h"
+#include "debug.h"
+
+#define MAX_DRV 2
+
+/* Port Controls (Platform dependent) */
+/* SD card socket connections */
+
+/* TODO: config.h cofig macros */
+
+//#define SD_CD_0 SBIT(PORT,) /* Card detect switch */
+//#define SD_CD_0_IN SBIT(PIN,)
+//#define SD_CD_0_DDR SBIT(DDR,)
+
+//#define SD_WP_0 SBIT(PORT,) /* Write protect switch */
+//#define SD_WP_0_IN SBIT(PIN,)
+//#define SD_WP_0_DDR SBIT(DDR,)
+
+#define SD_CS_0 SBIT(PORTB,0) /* Chip select/Card sense pin */
+//#define SD_CS_0_IN SBIT(PINB,0)
+#define SD_CS_0_DDR SBIT(DDRB,0)
+
+
+#define SD_CD_1 SBIT(PORTG,3) /* Card detect switch */
+#define SD_CD_1_IN SBIT(PING,3)
+#define SD_CD_1_DDR SBIT(DDRG,3)
+
+//#define SD_WP_1 SBIT(PORTG,5) /* Write protect switch */
+//#define SD_WP_1_IN SBIT(PING,5)
+//#define SD_WP_1_DDR SBIT(DDRG,5)
+
+#define SD_CS_1 SBIT(PORTG,4) /* Chip select/Card sense pin */
+//#define SD_CS_1_IN SBIT(PING,4)
+#define SD_CS_1_DDR SBIT(DDRG,4)
+
+
+#define SPI_CLK_SLOW() SPISetMMCInitClock() /* Set slow clock (100k-400k) */
+#define SPI_CLK_FAST() SPISetFastClock() /* Set fast clock (depends on the CSD) */
+
+/*--------------------------------------------------------------------------
+ Definitions for MMC/SDC command
+ ---------------------------------------------------------------------------*/
+
+#define CMD0 (0) /* GO_IDLE_STATE */
+#define CMD1 (1) /* SEND_OP_COND (MMC) */
+#define ACMD41 (0x80+41) /* SEND_OP_COND (SDC) */
+#define CMD8 (8) /* SEND_IF_COND */
+#define CMD9 (9) /* SEND_CSD */
+#define CMD10 (10) /* SEND_CID */
+#define CMD12 (12) /* STOP_TRANSMISSION */
+#define ACMD13 (0x80+13) /* SD_STATUS (SDC) */
+#define CMD16 (16) /* SET_BLOCKLEN */
+#define CMD17 (17) /* READ_SINGLE_BLOCK */
+#define CMD18 (18) /* READ_MULTIPLE_BLOCK */
+#define CMD23 (23) /* SET_BLOCK_COUNT (MMC) */
+#define ACMD23 (0x80+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
+#define CMD24 (24) /* WRITE_BLOCK */
+#define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */
+#define CMD55 (55) /* APP_CMD */
+#define CMD58 (58) /* READ_OCR */
+
+
+/*--------------------------------------------------------------------------
+
+ Module Private Functions
+
+ ---------------------------------------------------------------------------*/
+
+struct sdsock_stat_s {
+ volatile DSTATUS stat; /* Disk/socket status */
+ BYTE CardType; /* Card type flags */
+};
+
+static
+struct sdsock_stat_s
+ socket[MAX_DRV] = {
+ {.stat=STA_NOINIT},
+ {.stat=STA_NOINIT}
+ };
+
+/*-----------------------------------------------------------------------*/
+/* Wait for card ready */
+/*-----------------------------------------------------------------------*/
+
+static
+int wait_ready (void) /* 1:OK, 0:Timeout */
+{
+ uint32_t to = get_timer(0);
+
+ /* Wait for ready in timeout of 500ms */
+ do {
+ if (spi_rcvr() == 0xFF) {
+ return 1;
+ }
+ } while (get_timer(to) < 500);
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------------*/
+/* Deselect the card and release SPI bus */
+/*-----------------------------------------------------------------------*/
+
+static
+void deselect(BYTE drv)
+{
+ //debug("*** enter deselect(%.2x)\n", drv);
+ if (drv == 0)
+ SD_CS_0 = 1;
+ else {
+ SD_CS_1 = 1;
+ }
+
+ /* Dummy clock (TODO: force DO hi-z for multiple slave SPI) */
+ if (socket[drv].stat & STA_FAST)
+ SPI_CLK_FAST();
+ else
+ SPI_CLK_SLOW();
+ spi_rcvr();
+ SPI_OFF();
+
+ if (drv == 0) {
+#ifdef SD_CS_0_IN
+ SD_CS_0_DDR = 0; /* Input */
+ SD_CS_0 = 0; /* No Pullup */
+#endif
+ } else {
+#ifdef SD_CS_1_IN
+ SD_CS_1_DDR = 0;
+ SD_CS_1 = 0;
+#endif
+ }
+ //debug("*** exit deselect(%.2x)\n", drv);
+}
+
+/*-----------------------------------------------------------------------*/
+/* Select the card and wait for ready */
+/*-----------------------------------------------------------------------*/
+
+static
+int select(BYTE drv) /* 1:Successful, 0:Timeout */
+{
+ //debug("*** enter select(%.2x)\n", drv);
+ if (drv == 0) {
+#ifdef SD_CS_0_IN
+ SD_CS_0 = 1;
+ SD_CS_0_DDR = 1;
+#endif
+ SD_CS_0 = 0;
+ } else {
+#ifdef SD_CS_1_IN
+ SD_CS_1 = 1;
+ SD_CS_1_DDR = 1;
+#endif
+ SD_CS_1 = 0;
+ }
+
+ if (socket[drv].stat & STA_FAST)
+ SPI_CLK_FAST();
+ else
+ SPI_CLK_SLOW();
+
+ /* Dummy clock (force DO enabled) */
+ spi_rcvr();
+
+ if (wait_ready()) {
+ //debug("*** exit select() == 1\n");
+ return 1; /* OK */
+ }
+ deselect(drv);
+ //debug("*** exit select() == 0\n");
+
+ return 0; /* Timeout */
+}
+
+/*-----------------------------------------------------------------------*/
+/* Power Control (Platform dependent) */
+/*-----------------------------------------------------------------------*/
+/* When the target system does not support socket power control, there */
+/* is nothing to do in these functions and chk_power always returns 1. */
+
+static
+void power_on(BYTE drv)
+{
+ //debug("*** enter power_on(%.2x)\n", drv);
+
+ if (drv == 0) {
+#ifdef SD_PWR_0
+ SD_PWR_0 = 0; /* Drives PWR pin high */
+#endif
+
+ } else {
+#ifdef SD_PWR_1
+ SD_PWR_1 = 0; /* Drives PWR pin high */
+#endif
+ }
+#if defined SD_PWR_0 || defined SD_PWR_1
+ for (uint32_t to = get_timer(0); get_timer(to) < 30;)
+ ; /* Wait for 30ms */
+#endif
+ //debug("*** exit power_on(%.2x)\n", drv);
+}
+
+static
+void power_off(BYTE drv)
+{
+ //debug("*** enter power_off(%.2x)\n", drv);
+ select(drv); /* Wait for card ready */
+ deselect(drv);
+
+ if (drv == 0) {
+#ifdef SD_PWR_0
+ SD_PWR_0 = 1; /* Socket power OFF */
+#endif
+ } else {
+#ifdef SD_PWR_1
+ SD_PWR_1 = 1; /* Socket power OFF */
+#endif
+ }
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ socket[drv].stat |= STA_NOINIT;
+ }
+ //debug("*** exit power_off(%.2x)\n", drv);
+}
+
+#if 0
+static
+int chk_power(BYTE drv) /* Socket power state: 0=off, 1=on */
+{
+ if (drv == 0) {
+#ifdef SD_PWR_0
+ return SD_PWR_0 == 0;
+#else
+ return 1;
+#endif /* SD_PWR_PIN */
+ } else {
+#ifdef SD_PWR_1
+ return SD_PWR_1 == 0;
+#else
+ return 1;
+#endif /* SD_PWR_PIN */
+ }
+}
+#endif
+
+/*-----------------------------------------------------------------------*/
+/* Receive a data packet from MMC */
+/*-----------------------------------------------------------------------*/
+
+static
+int rcvr_datablock (
+ BYTE *buff, /* Data buffer to store received data */
+UINT btr /* Byte count (must be multiple of 4) */
+) {
+ BYTE token, tmp;
+ uint32_t to = get_timer(0);
+
+ /* Wait for data packet in timeout of 200ms */
+ do {
+ token = spi_rcvr();
+ } while ((token == 0xFF) && get_timer(to) < 200);
+ if(token != 0xFE) return 0; /* If not valid data token, retutn with error */
+
+ tmp = spi_rcvr(); /* shift in first byte */
+ spi_write(0xff); /* start shift in next byte */
+ while (--btr) {
+ *buff++ = tmp;
+ asm volatile (""::"r"(buff), "r"(btr));
+ spi_wait();
+ tmp = SPDR;
+ spi_write(0xff);
+ }
+ *buff = tmp; /* store last byte in buffer while SPI module shifts in crc part1 */
+ spi_wait();
+ spi_rcvr(); /* second crc */
+
+ return 1; /* Return with success */
+}
+
+/*-----------------------------------------------------------------------*/
+/* Send a data packet to MMC */
+/*-----------------------------------------------------------------------*/
+
+#if _USE_WRITE
+static
+int xmit_datablock (
+ const BYTE *buff, /* 512 byte data block to be transmitted */
+ BYTE token /* Data/Stop token */
+)
+{
+ BYTE resp, tmp;
+ UINT btr;
+
+ if (!wait_ready()) return 0;
+
+ spi_write(token); /* Xmit data token */
+ if (token != 0xFD) { /* Is data token */
+ btr = 512;
+ do {
+ tmp = *buff++;
+ spi_wait();
+ spi_write(tmp);
+ }while (--btr);
+ spi_wait();
+ spi_xmit(0xff); /* CRC (Dummy) */
+ spi_xmit(0xff);
+ resp = spi_rcvr(); /* Reveive data response */
+ return ((resp & 0x1F) != 0x05) ? 0 : 1; /* If not accepted, return with error */
+ }
+
+ spi_wait();
+ return 1;
+}
+#endif /* _USE_WRITE */
+
+/*-----------------------------------------------------------------------*/
+/* Send a command packet to MMC */
+/*-----------------------------------------------------------------------*/
+
+static
+BYTE send_cmd ( /* Returns R1 resp (bit7==1:Send failed) */
+ BYTE drv, /* Physical drive nmuber (0) */
+ BYTE cmd, /* Command index */
+ DWORD arg /* Argument */
+) {
+ union {
+ DWORD as32;
+ BYTE as8[4];
+ } argtmp;
+ BYTE n, res;
+
+ if (cmd & 0x80) { /* ACMD<n> is the command sequense of CMD55-CMD<n> */
+ cmd &= 0x7F;
+ res = send_cmd(drv, CMD55, 0);
+ if (res > 1)
+ return res;
+ }
+
+ //debug("*** send_cmd( %.2x )", cmd);
+
+ /* Select the card and wait for ready except to stop multiple block read */
+ if (cmd != CMD12) {
+ deselect(drv);
+ if (!select(drv)) {
+ //debug(" == %.2x\n", 0xff);
+ return 0xFF;
+ }
+ }
+
+ /* Send command packet */
+ spi_xmit(0x40 | cmd); /* Start + Command index */
+ argtmp.as32 = arg;
+ spi_xmit(argtmp.as8[3]); /* Argument[31..24] */
+ spi_xmit(argtmp.as8[2]); /* Argument[23..16] */
+ spi_xmit(argtmp.as8[1]); /* Argument[15..8] */
+ spi_xmit(argtmp.as8[0]); /* Argument[7..0] */
+
+ n = 0x01; /* Dummy CRC + Stop */
+ if (cmd == CMD0)
+ n = 0x95; /* Valid CRC for CMD0(0) */
+ if (cmd == CMD8)
+ n = 0x87; /* Valid CRC for CMD8(0x1AA) */
+ spi_xmit(n);
+
+ /* Receive command response */
+ if (cmd == CMD12)
+ spi_rcvr(); /* Skip a stuff byte when stop reading */
+ n = 10; /* Wait for a valid response in timeout of 10 attempts */
+ do
+ res = spi_rcvr();
+ while ((res & 0x80) && --n);
+
+ //debug(" == %.2x\n", res);
+ return res; /* Return with the response value */
+}
+
+/*--------------------------------------------------------------------------
+
+ Public Functions
+
+ ---------------------------------------------------------------------------*/
+
+void setup_mmc(void)
+{
+#ifdef SD_PWR_0
+ SD_PWR_1 = 1; /* Drives PWR pin low */
+ SD_PWR_0_DDR = 1; /* Turns on PWR pin as output */
+#endif
+#ifdef SD_WP_0
+ SD_WP_0_DDR = 0;
+ SD_WP_0 = 1; /* Pullup */
+#endif
+
+#ifdef SD_PWR_1
+ SD_PWR_1 = 1; /* Drives PWR pin low */
+ SD_PWR_1_DDR = 1; /* Turns on PWR pin as output */
+#endif
+#ifdef SD_WP_1
+ SD_WP_1_DDR = 0;
+ SD_WP_1 = 1; /* Pullup */
+#endif
+
+ /* SPI as master */
+ PRR0 &= ~_BV(PRSPI);
+ SPI_DDR = (SPI_DDR & ~(_BV(SPI_MISO) | _BV(SPI_SS)))
+ | _BV(SPI_MOSI) | _BV(SPI_SCK);
+ SPI_PORT = SPI_PORT & ~(_BV(SPI_MOSI) | _BV(SPI_SCK));
+
+#if defined SD_CD_0
+ SD_CD_0_DDR = 0;
+ SD_CD_0 = 1; /* Pullup */
+#elif defined SD_CS_0_IN
+ SD_CS_0_DDR = 0;
+ SD_CS_0 = 0;
+#endif
+#if defined SD_CD_0 || !defined SD_CS_0_IN
+ SD_CS_0 = 1;
+ SD_CS_0_DDR = 1;
+#endif
+
+#if defined SD_CD_1
+ SD_CD_1_DDR = 0;
+ SD_CD_1 = 1; /* Pullup */
+#elif defined SD_CS_1_IN
+ SD_CS_1_DDR = 0;
+ SD_CS_1 = 0; /* No Pullup */
+#endif
+#if defined SD_CD_1 || !defined SD_CS_1_IN
+ SD_CS_1 = 1; /* Set High */
+ SD_CS_1_DDR = 1;
+#endif
+}
+
+/*-----------------------------------------------------------------------*/
+/* Initialize Disk Drive */
+/*-----------------------------------------------------------------------*/
+
+#define MMC_INIT_TO 1000 /* 1s */
+
+DSTATUS disk_initialize (
+ BYTE drv /* Physical drive nmuber (0) */
+)
+{
+ BYTE n, cmd, ty, ocr[4];
+ DSTATUS res;
+
+ if (drv >= MAX_DRV)
+ return STA_NOINIT;
+ res = socket[drv].stat;
+ if (res & STA_NODISK) {
+ return res & STAT_MASK; /* No card in the socket */
+ }
+ power_on(drv); /* Force socket power on */
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ socket[drv].stat &= ~STA_FAST;
+ }
+ SPI_CLK_SLOW();
+ for (n = 10; n; n--)
+ spi_rcvr(); /* 80 dummy clocks */
+
+ ty = 0;
+ if (send_cmd(drv, CMD0, 0) == 1) { /* Enter Idle state */
+ /* Init timeout timer */
+ uint32_t timer = get_timer(0);
+
+ if (send_cmd(drv, CMD8, 0x1AA) == 1) { /* SDv2? */
+ /* Get trailing return value of R7 resp */
+ for (n = 0; n < 4; n++)
+ ocr[n] = spi_rcvr();
+ if (ocr[2] == 0x01 && ocr[3] == 0xAA) {
+ /* The card can work at vdd range of 2.7-3.6V */
+ while (get_timer(timer) < MMC_INIT_TO
+ && send_cmd(drv, ACMD41, 1UL << 30))
+ ; /* Wait for leaving idle state (ACMD41 with HCS bit) */
+ if (get_timer(timer) < MMC_INIT_TO && send_cmd(drv, CMD58, 0) == 0) {
+ /* Check CCS bit in the OCR */
+ for (n = 0; n < 4; n++)
+ ocr[n] = spi_rcvr();
+ ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* SDv2 */
+ }
+ }
+ } else { /* SDv1 or MMCv3 */
+ if (send_cmd(drv, ACMD41, 0) <= 1) {
+ ty = CT_SD1; cmd = ACMD41; /* SDv1 */
+ } else {
+ ty = CT_MMC; cmd = CMD1; /* MMCv3 */
+ }
+
+ /* Wait for leaving idle state */
+ while (get_timer(timer) < MMC_INIT_TO && send_cmd(drv, cmd, 0))
+ ;
+
+ /* Set R/W block length to 512 */
+ if (!(get_timer(timer) < MMC_INIT_TO) || send_cmd(drv, CMD16, 512) != 0)
+ ty = 0;
+ }
+ }
+ socket[drv].CardType = ty;
+ deselect(drv);
+
+ if (ty) { /* Initialization succeded */
+ /* Clear STA_NOINIT */
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ res = (socket[drv].stat & ~STA_NOINIT) | STA_FAST;
+ socket[drv].stat = res;
+ }
+ } else { /* Initialization failed */
+ power_off(drv);
+ }
+
+ return socket[drv].stat & STAT_MASK;
+}
+
+/*-----------------------------------------------------------------------*/
+/* Get Disk Status */
+/*-----------------------------------------------------------------------*/
+
+DSTATUS disk_status (
+ BYTE drv /* Physical drive nmuber (0) */
+)
+{
+ DSTATUS res;
+
+ //debug("***** disk_status(%.2x)", drv);
+ if (drv >= MAX_DRV)
+ res = STA_NOINIT;
+ else
+ res = socket[drv].stat & STAT_MASK;
+
+ //debug(" == %.2x\n", res);
+ return res;
+}
+
+/*-----------------------------------------------------------------------*/
+/* Read Sector(s) */
+/*-----------------------------------------------------------------------*/
+
+DRESULT disk_read (
+ BYTE drv, /* Physical drive nmuber (0) */
+ BYTE *buff, /* Pointer to the data buffer to store read data */
+ DWORD sector, /* Start sector number (LBA) */
+ UINT count /* Sector count (1..255) */
+)
+{
+ BYTE cmd;
+
+ if (drv >= MAX_DRV || !count)
+ return RES_PARERR;
+ if (socket[drv].stat & STA_NOINIT)
+ return RES_NOTRDY;
+
+ /* Convert to byte address if needed */
+ if (!(socket[drv].CardType & CT_BLOCK))
+ sector *= 512;
+
+ /* READ_MULTIPLE_BLOCK : READ_SINGLE_BLOCK */
+ cmd = count > 1 ? CMD18 : CMD17;
+ if (send_cmd(drv, cmd, sector) == 0) {
+ do {
+ if (!rcvr_datablock(buff, 512))
+ break;
+ buff += 512;
+ } while (--count);
+ if (cmd == CMD18)
+ send_cmd(drv, CMD12, 0); /* STOP_TRANSMISSION */
+ }
+ deselect(drv);
+
+ return count ? RES_ERROR : RES_OK;
+}
+
+/*-----------------------------------------------------------------------*/
+/* Write Sector(s) */
+/*-----------------------------------------------------------------------*/
+
+#if _USE_WRITE
+DRESULT disk_write (
+ BYTE drv, /* Physical drive nmuber (0) */
+ const BYTE *buff, /* Pointer to the data to be written */
+ DWORD sector, /* Start sector number (LBA) */
+ UINT count /* Sector count (1..255) */
+)
+{
+ DSTATUS res;
+
+ if (drv >= MAX_DRV || !count)
+ return RES_PARERR;
+ res = socket[drv].stat;
+ if ( res & STA_NOINIT)
+ return RES_NOTRDY;
+ if (res & STA_PROTECT)
+ return RES_WRPRT;
+
+ /* Convert to byte address if needed */
+ if (!(socket[drv].CardType & CT_BLOCK))
+ sector *= 512;
+
+ if (count == 1) {
+ /* Single block write */
+ if ((send_cmd(drv, CMD24, sector) == 0) /* WRITE_BLOCK */
+ && xmit_datablock(buff, 0xFE))
+ count = 0;
+ } else {
+ /* Multiple block write */
+ if (socket[drv].CardType & CT_SDC)
+ send_cmd(drv, ACMD23, count);
+ if (send_cmd(drv, CMD25, sector) == 0) {
+ /* WRITE_MULTIPLE_BLOCK */
+ do {
+ if (!xmit_datablock(buff, 0xFC))
+ break;
+ buff += 512;
+ } while (--count);
+ if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */
+ count = 1;
+ }
+ }
+ deselect(drv);
+
+ return count ? RES_ERROR : RES_OK;
+}
+#endif /* _USE_WRITE */
+
+/*-----------------------------------------------------------------------*/
+/* Miscellaneous Functions */
+/*-----------------------------------------------------------------------*/
+
+#if _USE_IOCTL
+DRESULT disk_ioctl (
+ BYTE drv, /* Physical drive nmuber (0) */
+ BYTE cmd, /* Control code */
+ void *buff /* Buffer to send/receive control data */
+)
+{
+ DRESULT res;
+ BYTE n, csd[16], *ptr = buff;
+ DWORD csize;
+
+ if (drv >= MAX_DRV)
+ return RES_PARERR;
+
+ res = RES_ERROR;
+
+ if (socket[drv].stat & STA_NOINIT)
+ return RES_NOTRDY;
+
+ /* TODO: SPI clock? */
+
+ switch (cmd) {
+ case CTRL_SYNC : /* Make sure that no pending write process. Do not remove this or written sector might not left updated. */
+ if (select(drv))
+ res = RES_OK;
+ break;
+
+ case GET_SECTOR_COUNT: /* Get number of sectors on the disk (DWORD) */
+ if ((send_cmd(drv, CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
+ if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */
+ csize = csd[9] + ((WORD)csd[8] << 8) + ((DWORD)(csd[7] & 63) << 16) + 1;
+ *(DWORD*)buff = csize << 10;
+ } else { /* SDC ver 1.XX or MMC*/
+ n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
+ csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
+ *(DWORD*)buff = csize << (n - 9);
+ }
+ res = RES_OK;
+ }
+ break;
+
+ case GET_BLOCK_SIZE: /* Get erase block size in unit of sector (DWORD) */
+ if (socket[drv].CardType & CT_SD2) { /* SDv2? */
+ if (send_cmd(drv, ACMD13, 0) == 0) { /* Read SD status */
+ spi_rcvr();
+ if (rcvr_datablock(csd, 16)) { /* Read partial block */
+ for (n = 64 - 16; n; n--)
+ spi_rcvr(); /* Purge trailing data */
+ *(DWORD*) buff = 16UL << (csd[10] >> 4);
+ res = RES_OK;
+ }
+ }
+ } else { /* SDv1 or MMCv3 */
+ if ((send_cmd(drv, CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { /* Read CSD */
+ if (socket[drv].CardType & CT_SD1) { /* SDv1 */
+ *(DWORD*)buff = (((csd[10] & 63) << 1) + ((WORD)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1);
+ } else { /* MMCv3 */
+ *(DWORD*)buff = ((WORD)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1);
+ }
+ res = RES_OK;
+ }
+ }
+ break;
+
+ /* Following commands are never used by FatFs module */
+
+ case MMC_GET_TYPE: /* Get card type flags (1 byte) */
+ *ptr = socket[drv].CardType;
+ res = RES_OK;
+ break;
+
+ case MMC_GET_CSD: /* Receive CSD as a data block (16 bytes) */
+ if (send_cmd(drv, CMD9, 0) == 0 /* READ_CSD */
+ && rcvr_datablock(ptr, 16))
+ res = RES_OK;
+ break;
+
+ case MMC_GET_CID: /* Receive CID as a data block (16 bytes) */
+ if (send_cmd(drv, CMD10, 0) == 0 /* READ_CID */
+ && rcvr_datablock(ptr, 16))
+ res = RES_OK;
+ break;
+
+ case MMC_GET_OCR: /* Receive OCR as an R3 resp (4 bytes) */
+ if (send_cmd(drv, CMD58, 0) == 0) { /* READ_OCR */
+ for (n = 4; n; n--)
+ *ptr++ = spi_rcvr();
+ res = RES_OK;
+ }
+ break;
+
+ case MMC_GET_SDSTAT: /* Receive SD status as a data block (64 bytes) */
+ if (send_cmd(drv, ACMD13, 0) == 0) { /* SD_STATUS */
+ spi_rcvr();
+ if (rcvr_datablock(ptr, 64))
+ res = RES_OK;
+ }
+ break;
+
+ case CTRL_POWER_OFF : /* Power off */
+ power_off(drv);
+ res = RES_OK;
+ break;
+
+ default:
+ res = RES_PARERR;
+ }
+
+ deselect(drv);
+
+ return res;
+}
+#endif /* _USE_IOCTL */
+
+/*-----------------------------------------------------------------------*/
+/* Device Timer Interrupt Procedure (Platform dependent) */
+/*-----------------------------------------------------------------------*/
+/* This function must be called in period of 10ms */
+
+void disk_timerproc (void)
+{
+ BYTE s;
+
+ s = socket[0].stat;
+#ifdef SD_WP_0
+ if (SD_WP_0_IN == 0) /* Write protected */
+ s |= STA_PROTECT;
+ else /* Write enabled */
+ s &= ~STA_PROTECT;
+#endif
+
+#if defined SD_CD_0
+ if (SD_CD_0_IN == 0) /* Card inserted */
+ s &= ~STA_NODISK;
+ else /* Socket empty */
+ s |= (STA_NODISK | STA_NOINIT);
+#elif defined SD_CS_0_IN
+ if (SD_CS_0_DDR == 0) {
+ if (SD_CS_0_IN == 1) /* Card inserted */
+ s &= ~STA_NODISK;
+ else /* Socket empty */
+ s |= (STA_NODISK | STA_NOINIT);
+ }
+#endif
+ socket[0].stat = s; /* Update MMC status */
+
+ s = socket[1].stat;
+#ifdef SD_WP_1
+ if (SD_WP_1_IN == 0) /* Write protected */
+ s |= STA_PROTECT;
+ else /* Write enabled */
+ s &= ~STA_PROTECT;
+#endif
+
+#if defined SD_CD_1
+ if (SD_CD_1_IN == 0) /* Card inserted */
+ s &= ~STA_NODISK;
+ else /* Socket empty */
+ s |= (STA_NODISK | STA_NOINIT);
+#elif defined SD_CS_1_IN
+ if (SD_CS_1_DDR == 0) {
+ if (SD_CS_1_IN == 1) /* Card inserted */
+ s &= ~STA_NODISK;
+ else /* Socket empty */
+ s |= (STA_NODISK | STA_NOINIT);
+ }
+#endif
+ socket[1].stat = s; /* Update MMC status */
+}
diff --git a/avr/pcf8583.c b/avr/pcf8583.c
new file mode 100644
index 0000000..bdb779f
--- /dev/null
+++ b/avr/pcf8583.c
@@ -0,0 +1,105 @@
+/*
+ * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ * Date & Time support for Philips PCF8583 RTC
+ */
+
+#include "common.h"
+#include <stdlib.h>
+#include "time.h"
+#include "rtc.h"
+#include "i2c.h"
+#include "command.h"
+#include "debug.h"
+
+#define DEBUG_RTC 0
+
+#define debug_rtc(fmt, args...) \
+ debug_cond(DEBUG_RTC, fmt, ##args)
+
+#define REG_CS 0x00 /* control/status */
+#define REG_CSEC 0x01 /* hundredth of a second */
+#define REG_SEC 0x02 /* seconds */
+#define REG_MIN 0x03 /* minutes */
+#define REG_HOUR 0x04 /* hours */
+#define REG_YRDATE 0x05 /* year/date */
+#define REG_WDMON 0x06 /* weekdays/months */
+#define NR_OF_REGS 7
+
+
+/* ------------------------------------------------------------------------- */
+
+static uint_fast8_t bcd2bin(uint8_t val)
+{
+ return (val >> 4) * 10 + (val & 0x0f);
+}
+
+static uint8_t bin2bcd (uint_fast8_t val)
+{
+ div_t d = div(val, 10);
+
+ return (d.quot << 4) | d.rem;
+}
+
+
+int rtc_get (struct tm *tmp)
+{
+ uint8_t rtcbuf[NR_OF_REGS];
+ int16_t year;
+
+ i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, rtcbuf, NR_OF_REGS);
+ i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0x10, 1, (uint8_t *) &year, 2);
+
+ debug_rtc("Get RTC year: %u, wdays/month: %02x, year/date: %02x, "
+ "hour: %02x, min: %02x, sec: %02x, (stat: %02x)\n", year,
+ rtcbuf[6], rtcbuf[5], rtcbuf[4], rtcbuf[3], rtcbuf[2], rtcbuf[0]);
+
+ tmp->tm_sec = bcd2bin (rtcbuf[REG_SEC] & 0x7F);
+ tmp->tm_min = bcd2bin (rtcbuf[REG_MIN] & 0x7F);
+ tmp->tm_hour = bcd2bin (rtcbuf[REG_HOUR] & 0x3F);
+ tmp->tm_mday = bcd2bin (rtcbuf[REG_YRDATE] & 0x3F);
+ tmp->tm_mon = bcd2bin (rtcbuf[REG_WDMON] & 0x1F) - 1;
+ while (year%4 != (rtcbuf[REG_YRDATE]>>6)) {
+ year++;
+ i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0x10, 1, (uint8_t *) &year, 2);
+ }
+ tmp->tm_year = year - 1900;
+ tmp->tm_wday = rtcbuf[REG_WDMON] >> 5;
+ tmp->tm_yday = 0;
+ tmp->tm_isdst= 0;
+
+ debug_rtc( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
+ tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday,
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+
+ return rtcbuf[REG_CS] == 0x04 ? 0 : 1;
+}
+
+int rtc_set (struct tm *tmp)
+{
+ uint8_t rtcbuf[NR_OF_REGS];
+ int16_t year = tmp->tm_year + 1900;
+
+ debug_rtc("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
+ tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday,
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+
+ rtcbuf[REG_CS] = 0x84;
+ rtcbuf[REG_CSEC] = 0x00;
+ rtcbuf[REG_WDMON ] = (bin2bcd(tmp->tm_mon) + 1) | ((tmp->tm_wday) << 5);
+ rtcbuf[REG_YRDATE] = ((year % 4) << 6) | bin2bcd(tmp->tm_mday);
+ rtcbuf[REG_HOUR ] = bin2bcd(tmp->tm_hour);
+ rtcbuf[REG_MIN ] = bin2bcd(tmp->tm_min);
+ rtcbuf[REG_SEC ] = bin2bcd(tmp->tm_sec);
+
+ i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, rtcbuf, NR_OF_REGS);
+ i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0x10, 1, (uint8_t *) &year, 2);
+ rtcbuf[REG_CS] = 0x04;
+ i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, rtcbuf, 1);
+
+ return 0;
+}
diff --git a/avr/print-utils.c b/avr/print-utils.c
new file mode 100644
index 0000000..ea3b5cf
--- /dev/null
+++ b/avr/print-utils.c
@@ -0,0 +1,97 @@
+/*
+ * (C) Copyright 2014,2018 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "common.h"
+#include <stdint.h>
+#include "con-utils.h"
+#include "print-utils.h"
+
+void print_blanks(uint_fast8_t count)
+{
+ while(count--)
+ putchar(' ');
+}
+
+
+ERRNUM eeprom_read_buf(uint8_t *buf, uint32_t addr, uint8_t count)
+{
+ eeprom_read_block((void *) buf, (const void *) (size_t) addr, count);
+ return ESUCCESS;
+}
+
+ERRNUM ram_read_buf(uint8_t *buf, uint32_t addr, uint8_t count)
+{
+ while (count--)
+ *buf++ = *(uint8_t *) (size_t) addr++;
+ return ESUCCESS;
+}
+
+ERRNUM flash_read_buf(uint8_t *buf, uint32_t addr, uint8_t count)
+{
+ while (count--)
+ *buf++ = *(const __memx uint8_t *) (__uint24) addr++;
+ return ESUCCESS;
+}
+
+ERRNUM dump_mem(uint32_t address, uint32_t offset, uint32_t len,
+ ERRNUM (*readfkt)(uint8_t *, uint32_t, uint8_t), char *title)
+{
+ uint8_t buf[16];
+ uint8_t llen = 16;
+ uint8_t pre = offset % 16;
+ offset = offset & ~0x0f;
+ len += pre;
+ uint8_t i;
+
+ if (title && *title) {
+ printf_P(PSTR("%s\n"),title);
+ }
+
+ while (len) {
+ if (len < 16)
+ llen = len;
+ ERRNUM err = readfkt(buf, address, llen - pre);
+ if (err != ESUCCESS)
+ return err;
+
+ if (title)
+ print_blanks(4);
+ printf_P(PSTR("%.5lx:"),offset);
+ for (i = 0; i < llen; i++) {
+ if ((i % 8) == 0)
+ putchar(' ');
+ if (i < pre)
+ printf_P(PSTR(".. "));
+ else
+ printf_P(PSTR("%.2x "), buf[i-pre]);
+ }
+ /* fill line with whitespace for nice ASCII print */
+ print_blanks(3 * (16u - i) + (16u-i)/8 + 1 + pre);
+ /* Print data in ASCII characters */
+ for (i = pre; i < llen; i++)
+ printf_P(PSTR("%c"), isprint(buf[i-pre]) ? buf[i-pre] : '.');
+ putchar('\n');
+
+ address += llen - pre;
+ offset += 16;
+ pre = 0;
+ len -= llen;
+
+ if (ctrlc())
+ return EINTR;
+ }
+ return ESUCCESS;
+}
+
+void dump_eep(uint32_t addr, unsigned int len, char *title)
+{
+ dump_mem(addr, addr, len, eeprom_read_buf, title);
+}
+
+void dump_ram(uint8_t *addr, size_t offset, unsigned int len, char *title)
+{
+ dump_mem((uint32_t) (size_t) addr, offset, len, ram_read_buf, title);
+}
diff --git a/avr/serial.c b/avr/serial.c
new file mode 100644
index 0000000..d3aeb4e
--- /dev/null
+++ b/avr/serial.c
@@ -0,0 +1,123 @@
+/*
+ * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/atomic.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "ring.h"
+#include "serial.h"
+
+
+static int _write(char c, FILE *stream);
+static FILE mystdout = FDEV_SETUP_STREAM(_write,
+ NULL, _FDEV_SETUP_WRITE);
+
+
+
+#define BUFFER_SIZE 128
+
+#if ((BUFFER_SIZE-1) & BUFFER_SIZE)
+# error: BUFFER_SIZE not power of 2
+#endif
+
+#if ((BUFFER_SIZE) > 256)
+# error: BUFFER_SIZE
+#endif
+
+struct ring rx_ring;
+struct ring tx_ring;
+uint8_t rx_ring_buffer[BUFFER_SIZE];
+uint8_t tx_ring_buffer[BUFFER_SIZE];
+
+
+
+/* Initialize UART */
+
+void usart0_setup(unsigned long baud) {
+
+ ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
+
+ PRR0 &= ~_BV(PRUSART0);
+ UCSR0B = 0;
+ UCSR0A = 0;
+ UBRR0 = (F_CPU + (baud/2) + 8) / baud / 16 - 1;
+ UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0);
+ UCSR0C = 3 << UCSZ00;
+
+ /* Initialize ring buffers. */
+ ring_init(&rx_ring, rx_ring_buffer, BUFFER_SIZE);
+ ring_init(&tx_ring, tx_ring_buffer, BUFFER_SIZE);
+ };
+}
+
+/*--------------------------------------------------------------------------*/
+
+/* UART RXC interrupt */
+
+ISR(USART0_RX_vect)
+{
+ uint8_t d;
+
+ d = UDR0;
+ ring_write_ch(&rx_ring, d);
+}
+
+/* UART UDRE interrupt */
+
+ISR(USART0_UDRE_vect)
+{
+ int d = ring_read_ch(&tx_ring);
+
+ if (d < 0) {
+ /* Disable TX empty interrupt. */
+ UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0);
+ } else {
+ UDR0 = d;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+void serial_setup(unsigned long baud)
+{
+ stdout = &mystdout;
+ usart0_setup(baud);
+}
+
+/*--------------------------------------------------------------------------*/
+
+int _write(char c, FILE *stream)
+{
+ (void) stream;
+
+ if (c == '\n')
+ serial_putc('\r');
+ serial_putc(c);
+
+ return 0;
+}
+
+int serial_getc(void)
+{
+ return ring_read_ch(&rx_ring);
+}
+
+void serial_putc(char data)
+{
+ while (ring_write_ch(&tx_ring, data) < 0)
+ ;
+
+ /* Enable the TXE interrupt. */
+ 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/strerror.c b/avr/strerror.c
new file mode 100644
index 0000000..199504a
--- /dev/null
+++ b/avr/strerror.c
@@ -0,0 +1,38 @@
+/*
+ * (C) Copyright 2018 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "common.h"
+#include "cmd_fat.h"
+
+
+static const FLASH char * const FLASH error_strings[] = {
+ FSTR("Unknown error"),
+ FSTR("Not enough memory"), /* 101 */
+ FSTR("Interrupt"), /* 102 */
+ FSTR("Bus timeout"), /* 103 */
+ FSTR("Unexpected argument"), /* 104 */
+ FSTR("Invalid disk number"), /* 105 */
+ FSTR("Disk already attached"), /* 106 */
+ FSTR("Disk not attached"), /* 107 */
+ FSTR("Error opening file"), /* 108 */
+ FSTR("File already attached to other drive"), /* 109 */
+ FSTR("CPU is running"), /* 110 */
+ FSTR("Invalid argument"), /* 111 */
+ FSTR("Unexpected EOF"), /* 112 */
+
+};
+
+const FLASH char * my_strerror_P(ERRNUM errnum)
+{
+ if (errnum < 100)
+ return fat_rctostr(errnum);
+
+ errnum -= 100;
+ if ((unsigned) errnum >= ARRAY_SIZE(error_strings))
+ errnum = 0;
+
+ return error_strings[errnum];
+}
diff --git a/avr/timer.c b/avr/timer.c
new file mode 100644
index 0000000..03c86c8
--- /dev/null
+++ b/avr/timer.c
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "timer.h"
+#include <avr/interrupt.h>
+#include <util/atomic.h>
+#include "time.h"
+
+/* timer interrupt/overflow counter */
+/* counts up every ms. */
+static volatile
+uint32_t timestamp;
+
+/*
+ * 1000Hz timer interrupt generated by OC4A
+ */
+ISR(TIMER4_COMPA_vect)
+{
+ static int_fast8_t tick_10ms;
+ static int_fast8_t tick_1s;
+ int_fast8_t i, j;
+
+ extern void disk_timerproc(void);
+
+ OCR4A += F_CPU / 1000; /* 1000Hz interval */
+
+ timestamp++;
+
+ i = tick_10ms + 1;
+ if (i == 10) {
+ Stat |= S_10MS_TO;
+
+ /* Drive timer procedure of low level disk I/O module */
+ disk_timerproc();
+
+ j = tick_1s - 1;
+ if (j == 0) {
+ system_tick();
+ j = 100;
+ }
+ tick_1s = j;
+ i = 0;
+ }
+ tick_10ms = i;
+}
+
+/*--------------------------------------------------------------------------*/
+
+void setup_timer(void)
+{
+ PRR1 &= ~_BV(PRTIM4);
+ OCR4A = F_CPU / 1000 - 1; /* Timer4: 1000Hz interval */
+ TCCR4B = (0b00<<WGM42)|(0b001<<CS40); /* Normal Mode, Prescaler 1 */
+ TIMSK4 = _BV(OCIE4A); /* Enable Output Compare A interrupt */
+}
+
+/*--------------------------------------------------------------------------*/
+
+uint32_t get_timer(uint32_t base)
+{
+ uint32_t ret;
+ ATOMIC_BLOCK(ATOMIC_FORCEON)
+ {
+ ret = timestamp;
+ }
+ return ret - base;
+}
diff --git a/avr/z180-serv.c b/avr/z180-serv.c
new file mode 100644
index 0000000..ec3db7d
--- /dev/null
+++ b/avr/z180-serv.c
@@ -0,0 +1,826 @@
+/*
+ * (C) Copyright 2014-2016 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "z180-serv.h"
+#include "common.h"
+#include <util/atomic.h>
+
+#include "config.h"
+#include "background.h"
+#include "env.h"
+#include "ff.h"
+#include "serial.h"
+#include "z80-if.h"
+#include "debug.h"
+#include "print-utils.h"
+#include "timer.h"
+#include "time.h"
+#include "bcd.h"
+#include "rtc.h"
+
+#define DEBUG_CPM_SDIO 0 /* set to 1 to debug */
+
+#define debug_cpmsd(fmt, args...) \
+ debug_cond(DEBUG_CPM_SDIO, fmt, ##args)
+
+
+/*--------------------------------------------------------------------------*/
+
+struct msg_item {
+ uint8_t fct;
+ uint8_t sub_min, sub_max;
+ void (*func)(uint8_t, int, uint8_t *);
+};
+
+uint32_t msg_to_addr(uint8_t *msg)
+{
+ union {
+ uint32_t as32;
+ uint8_t as8[4];
+ } addr;
+
+ addr.as8[0] = msg[0];
+ addr.as8[1] = msg[1];
+ addr.as8[2] = msg[2];
+ addr.as8[3] = 0;
+
+ return addr.as32;
+}
+
+
+static int msg_xmit_header(uint8_t func, uint8_t subf, int len)
+{
+ z80_memfifo_putc(fifo_msgout, 0xAE);
+ z80_memfifo_putc(fifo_msgout, len+2);
+ z80_memfifo_putc(fifo_msgout, func);
+ z80_memfifo_putc(fifo_msgout, subf);
+
+ return 0;
+}
+
+int msg_xmit(uint8_t func, uint8_t subf, int len, uint8_t *msg)
+{
+ msg_xmit_header(func, subf, len);
+ while (len--)
+ z80_memfifo_putc(fifo_msgout, *msg++);
+
+ return 0;
+}
+
+void do_msg_ini_memfifo(uint8_t subf, int len, uint8_t * msg)
+{
+ (void)len;
+
+ z80_memfifo_init(subf, msg_to_addr(msg));
+}
+
+
+void do_msg_char_out(uint8_t subf, int len, uint8_t * msg)
+{
+ (void)subf;
+
+ while (len--)
+ putchar(*msg++);
+}
+
+/* echo message */
+void do_msg_echo(uint8_t subf, int len, uint8_t * msg)
+{
+ (void)subf;
+
+ /* send re-echo */
+ msg_xmit(1, 3, len, msg);
+}
+
+/* get timer */
+void do_msg_get_timer(uint8_t subf, int len, uint8_t * msg)
+{
+ uint32_t time_ms = (len >= 4) ? *(uint32_t *) msg : 0;
+
+ time_ms = get_timer(time_ms);
+ msg_xmit(3, subf, sizeof(time_ms), (uint8_t *) &time_ms);
+}
+
+/* ---------------------------------------------------------------------------*/
+
+#define CPM_DAY_OFFSET ((1978-1900) * 365 + 19) /* 19 leap years */
+
+/*
+ * Convert CP/M time stamp to a broken-down time structure
+ *
+ */
+int mk_date_time (int len, uint8_t *msg, struct tm *tmp)
+{
+ time_t stamp;
+
+ if (len != 5)
+ return -1;
+
+ /* days since 2000-01-01 */
+ long days = msg[3] + (msg[4] << 8) - 8036;
+
+ if (days < 0)
+ return -1;
+
+ stamp = days * ONE_DAY;
+ stamp += bcd2bin(msg[0]);
+ stamp += bcd2bin(msg[1]) * 60 ;
+ stamp += bcd2bin(msg[2]) * 3600L;
+ gmtime_r(&stamp, tmp);
+ return 0;
+}
+
+void mk_cpm_time(struct tm *tmp, uint8_t cpm_time[5])
+{
+ uint16_t days = 1;
+ uint_fast8_t leap=2;
+
+ for (int year=78; year < tmp->tm_year; year++) {
+ days = days + 365 + (leap == 0);
+ leap = (leap+1)%4;
+ }
+ days += tmp->tm_yday;
+
+ cpm_time[0] = bin2bcd(tmp->tm_sec);
+ cpm_time[1] = bin2bcd(tmp->tm_min);
+ cpm_time[2] = bin2bcd(tmp->tm_hour);
+ cpm_time[3] = days;
+ cpm_time[4] = days >> 8;
+}
+
+/* get/set cp/m time */
+void do_msg_get_set_time(uint8_t subf, int len, uint8_t * msg)
+{
+ struct tm t;
+ uint8_t cpm_time[5];
+ int rc;
+
+ memset(cpm_time, 0, ARRAY_SIZE(cpm_time));
+
+ switch (subf) {
+ case 3: /* set date & time */
+ /* initialize t with current time */
+ rc = rtc_get (&t);
+
+ if (rc >= 0) {
+ /* insert new date & time */
+ if (mk_date_time (len, msg, &t) != 0) {
+ my_puts_P(PSTR("## set_time: Bad date format\n"));
+ break;
+ }
+
+ time_t time;
+ time = mk_gmtime(&t);
+ gmtime_r(&time, &t);
+
+ /* and write to RTC */
+ rc = rtc_set (&t);
+ if(rc)
+ my_puts_P(PSTR("## set_time: Set date failed\n"));
+ } else {
+ my_puts_P(PSTR("## set_time: Get date failed\n"));
+ }
+ /* FALL TROUGH */
+ case 2: /* get date & time */
+ rc = rtc_get (&t);
+ if (rc >= 0) {
+ time_t time;
+ time = mk_gmtime(&t);
+ //mktime(&t);
+ gmtime_r(&time, &t);
+
+ mk_cpm_time(&t, cpm_time);
+ } else {
+ my_puts_P(PSTR("## get_time: Get date failed\n"));
+ }
+ break;
+ }
+
+ msg_xmit(3, subf, sizeof(cpm_time), cpm_time);
+}
+
+/* ---------------------------------------------------------------------------*/
+
+static uint8_t drv;
+static uint8_t disk_buffer[CONFIG_CPM_BLOCK_SIZE];
+static struct cpm_drive_s drv_table[CONFIG_CPM_MAX_DRIVE];
+static int handle_cpm_drv_to;
+
+typedef enum {SINGLE, START, MIDDLE, END} dbgmsg_t;
+
+void drv_debug(dbgmsg_t phase, const FLASH char *const fmt, ...) \
+{
+ struct cpm_drive_s *dp = &drv_table[drv];
+
+ if (dp->opt & DRV_OPT_DEBUG) {
+
+ va_list ap;
+ va_start (ap, fmt);
+
+ if (phase == SINGLE || phase == START)
+ printf_P(PSTR("# %7lu dsk%d: "), get_timer(0), drv);
+
+ vfprintf_P (stdout, fmt, ap);
+
+ if (phase == SINGLE || phase == END)
+ putc('\n', stdout);
+
+ va_end (ap);
+ }
+}
+
+int drv_list(void)
+{
+ for (uint8_t i = 0; i < CONFIG_CPM_MAX_DRIVE; i++) {
+ struct cpm_drive_s * p = &drv_table[i];
+ if (p->img_name) {
+ printf_P(PSTR(" dsk%d: %2S %3S attached to %s\n"), i,
+ p->opt&DRV_OPT_RO ? PSTR("RO") : PSTR("RW"),
+ p->opt&DRV_OPT_DEBUG ? PSTR("DBG") : PSTR(""),
+ p->img_name);
+ }
+ }
+ return 0;
+}
+
+int drv_detach(uint8_t unit)
+{
+ drv = unit;
+ if (drv < CONFIG_CPM_MAX_DRIVE) {
+ struct cpm_drive_s *p = &drv_table[drv];
+
+ drv_debug(SINGLE, PSTR("detach from '%s'"), p->img_name ? p->img_name : "-");
+
+ if (p->img_name) {
+ f_close(&p->fd);
+ free(p->img_name);
+ p->opt = 0;
+ p->flags &= ~DRV_FLG_DIRTY;
+ p->img_name = NULL;
+
+ uint32_t scb = getenv_ulong(PSTR(ENV_CPM3_SCB), 16, 0);
+ if (scb && (z80_bus_cmd(Request) & ZST_ACQUIRED)) {
+ z80_write(scb + 0xf0, 0xff);
+ z80_write(p->dph + 11, 0xff);
+ z80_bus_cmd(Release);
+ }
+ }
+ }
+ return 0;
+}
+
+static int drv_find_file_attached(const char *fn)
+{
+ for (uint8_t i = 0; i < CONFIG_CPM_MAX_DRIVE; i++) {
+ struct cpm_drive_s *p = &drv_table[i];
+ if (p->img_name && !strcmp(fn, p->img_name)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int drv_attach(uint8_t unit, const char *filename, drv_opt_t options)
+{
+ int res;
+
+ drv = unit;
+ if (drv >= CONFIG_CPM_MAX_DRIVE)
+ return EATRANGE;
+
+ struct cpm_drive_s *p = &drv_table[drv];
+
+ if (options & DRV_OPT_REATTATCH) {
+ if (filename) {
+ return EUNEXPARG;
+ }
+
+ if (!p->img_name) {
+ return EATNOT;
+ }
+
+ /* change options */
+ if ((p->opt ^ options) & DRV_OPT_RO) {
+ f_close(&p->fd);
+ res = f_open(&p->fd, p->img_name,
+ FA_READ | (options&DRV_OPT_RO ? 0 : FA_WRITE));
+ }
+
+ p->opt = options & ~DRV_OPT_REATTATCH;
+
+ } else {
+
+ if (p->img_name)
+ return EATALRDY;
+ if (drv_find_file_attached(filename) >= 0)
+ return EATOTHER;
+
+ p->opt = options;
+
+ /* new attachment */
+
+ if ((p->img_name = strdup(filename)) == NULL)
+ return ENOMEM;
+
+ res = f_open(&p->fd, p->img_name,
+ FA_READ | (options&DRV_OPT_RO ? 0 : FA_WRITE));
+
+ if (!res && f_size(&p->fd) < CONFIG_CPM_DISKSIZE) {
+#if 0
+ unsigned int bw;
+ debug_cpmsd(" expanding image file from %ld to %ld\n",
+ f_size(&p->fd), CONFIG_CPM_DISKSIZE);
+
+ res = f_lseek(&p->fd, CONFIG_CPM_DISKSIZE-CONFIG_CPM_BLOCK_SIZE);
+ if (!res) {
+ memset(disk_buffer, 0xe5, CONFIG_CPM_BLOCK_SIZE);
+ res = f_write(&p->fd, disk_buffer, CONFIG_CPM_BLOCK_SIZE, &bw);
+ if (res || bw < CONFIG_CPM_BLOCK_SIZE) {
+ debug_cpmsd(" failed! res: %d, bytes written: %u\n", res, bw);
+ }
+ p->flags |= DRV_FLG_DIRTY;
+ bg_setstat(handle_cpm_drv_to, 1);
+ }
+#else
+ drv_debug(SINGLE, PSTR("wrong image file size: %ld, should be %ld"),
+ f_size(&p->fd), CONFIG_CPM_DISKSIZE);
+ res = 64;
+#endif
+ }
+ if (res) {
+ drv_detach(drv);
+ return EATOPEN;
+ }
+ }
+
+ return ESUCCESS;
+}
+
+
+int cpm_drv_to(int state)
+{
+ static uint32_t ts;
+
+ switch(state) {
+ case 0:
+ break;
+
+ case 1:
+ ts = get_timer(0);
+ state = 2;
+ break;
+
+ case 2:
+ if (get_timer(ts) > 1000) {
+ for (uint_fast8_t i=0; i < CONFIG_CPM_MAX_DRIVE; i++) {
+ if (drv_table[i].flags & DRV_FLG_DIRTY) {
+ drv_table[i].flags &= ~DRV_FLG_DIRTY;
+ f_sync(&drv_table[i].fd);
+ drv = i;
+ drv_debug(SINGLE, PSTR("f_sync"));
+ }
+ }
+ state = 0;
+ }
+ }
+ return state;
+}
+
+static const FLASH char * const FLASH rc_messages[] = {
+ FSTR("OK"),
+ FSTR("Internal error: wrong message len"), /* 01 */
+ FSTR("Invalid relative drive #"), /* 02 */
+ FSTR("Bus timeout"), /* 03 */
+ FSTR("Access byond disk size"), /* 04 */
+ FSTR("Write protect"), /* 05 */
+ FSTR("No media"), /* 06 */
+ FSTR("R/W address == 0 !!!!"), /* 07 */
+ };
+
+void msg_cpm_result(uint8_t subf, uint8_t rc, int res)
+{
+ uint8_t result_msg[3];
+
+ if (res)
+ rc |= 0x80;
+
+ result_msg[0] = rc;
+ result_msg[1] = res;
+ result_msg[2] = res >> 8;
+
+ msg_xmit(2, subf, sizeof(result_msg), result_msg);
+
+ if (rc) {
+#if defined(GCC_BUG_61443)
+ char msg[40];
+ strncpy_P(msg, rc_messages[rc & 0x7f], sizeof msg -1);
+ drv_debug(END, PSTR(" rc: %.02x/%d, '%s'"),
+ rc, res, msg);
+#else
+ drv_debug(END, PSTR(" rc: %.02x/%d, '%S'"),
+ rc, res, rc_messages[rc & 0x7f]);
+#endif
+ } else
+ drv_debug(END, PSTR(""));
+
+}
+
+/*
+ db 2 ; disk command
+ ds 1 ; subcommand (login/read/write)
+ ds 1 ; @adrv (8 bits) +0
+ ds 1 ; @rdrv (8 bits) +1
+ ds 3 ; @xdph (24 bits) +2
+*/
+
+void do_msg_cpm_login(uint8_t subf, int len, uint8_t * msg)
+{
+ struct cpm_drive_s *dp;
+ FRESULT res = 0;
+
+ (void)subf;
+
+ /* Get relative drive number */
+ drv = msg[1];
+ drv_debug(START, PSTR("login"));
+
+ if (len != 5) {
+ return msg_cpm_result(subf, 0x01, res);
+ }
+
+ if ( drv >= CONFIG_CPM_MAX_DRIVE) {
+ /* invalid relative drive number */
+ return msg_cpm_result(subf, 0x02, res);
+ }
+
+ dp = &drv_table[drv];
+ dp->flags &= ~DRV_FLG_OPEN;
+ dp->dph = ((uint32_t)msg[4] << 16) + ((uint16_t)msg[3] << 8) + msg[2];
+
+ if (dp->img_name == NULL) {
+ /* no file attached */
+ return msg_cpm_result(subf, 0x06, res);
+ }
+
+ f_close(&dp->fd);
+ res = f_open(&dp->fd, dp->img_name,
+ FA_READ | (dp->opt&DRV_OPT_RO ? 0 : FA_WRITE));
+
+ dp->flags |= DRV_FLG_OPEN;
+
+ /* send result*/
+ msg_cpm_result(subf, 0x00, res);
+}
+
+
+/*
+ db 2 ; disk command
+ ds 1 ; subcommand (login/read/write)
+ ds 1 ; @adrv (8 bits) +0
+ ds 1 ; @rdrv (8 bits) +1
+ ds 2 ; @trk (16 bits) +2
+ ds 2 ; @sect(16 bits) +4
+ ds 1 ; @cnt (8 bits) +6
+ ds 3 ; phys. transfer addr +7
+*/
+
+#define ADRV 0
+#define RDRV 1
+#define TRK 2
+#define SEC 4
+#define CNT 6
+#define ADDR 7
+
+void do_msg_cpm_rw(uint8_t subf, int len, uint8_t * msg)
+{
+ struct cpm_drive_s *dp;
+ uint32_t addr;
+ uint32_t pos;
+ uint16_t track;
+ uint16_t sec;
+ uint8_t secs;
+ bool dowrite;
+ FRESULT res = 0;
+ uint8_t rc = 0;
+ bool buserr = 0;
+
+ drv = msg[RDRV];
+ dowrite = (subf == 2);
+
+ drv_debug(START, PSTR("%2S"), dowrite ? PSTR("W ") : PSTR(" R"));
+
+ if (len != 10) {
+ return msg_cpm_result(subf, 0x01, res);
+ }
+ if ( drv>= CONFIG_CPM_MAX_DRIVE) {
+ return msg_cpm_result(subf, 0x02, res);
+ }
+
+ dp = &drv_table[drv];
+ track = (uint16_t)(msg[TRK+1] << 8) + msg[TRK];
+ sec = (uint16_t)(msg[SEC+1] << 8) + msg[SEC];
+ secs = msg[CNT];
+ addr = ((uint32_t)msg[ADDR+2] << 16) + ((uint16_t)msg[ADDR+1] << 8) + msg[ADDR];
+
+ if (dp->img_name == NULL) {
+ /* no media */
+ return msg_cpm_result(subf, 0x06, res);
+ }
+
+ /* TODO: tracks per sector from dpb */
+ pos = (track * 8UL + sec) * CONFIG_CPM_BLOCK_SIZE;
+
+ drv_debug(MIDDLE, PSTR(" T:%4d, S:%2d, cnt:%2d, lba: %.8lx, addr: %.5lx"),
+ track, sec, secs, pos, addr);
+
+ if (addr == 0) {
+ return msg_cpm_result(subf, 0x07, res);
+ }
+
+ if (dowrite && dp->opt & DRV_OPT_RO) {
+ return msg_cpm_result(subf, 0x05, res);
+ }
+
+
+ if (pos + secs * CONFIG_CPM_BLOCK_SIZE > CONFIG_CPM_DISKSIZE) {
+ drv_debug(MIDDLE, PSTR(" access > DISKSIZE:%.8lx!"),
+ CONFIG_CPM_DISKSIZE);
+ return msg_cpm_result(subf, 0x04, res);
+ }
+
+ res = f_lseek(&dp->fd, pos);
+
+ while (!res && secs--) {
+ unsigned int brw;
+ if (dowrite) {
+ if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
+ buserr = 1;
+ break;
+ } else {
+ z80_read_block(disk_buffer, addr, CONFIG_CPM_BLOCK_SIZE);
+ z80_bus_cmd(Release);
+ }
+ res = f_write(&dp->fd, disk_buffer, CONFIG_CPM_BLOCK_SIZE, &brw);
+ } else {
+ res = f_read(&dp->fd, disk_buffer, CONFIG_CPM_BLOCK_SIZE, &brw);
+ if (res == FR_OK) {
+ if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
+ buserr = 1;
+ break;
+ } else {
+ z80_write_block(disk_buffer, addr, CONFIG_CPM_BLOCK_SIZE);
+ z80_bus_cmd(Release);
+ }
+ }
+ }
+ if (brw != CONFIG_CPM_BLOCK_SIZE) {
+ drv_debug(MIDDLE, PSTR(" short rd/wr: res: %d, brw: %u"),
+ res, brw);
+ res = 64;
+ }
+ addr += CONFIG_CPM_BLOCK_SIZE;
+ }
+
+ if (dowrite && !res) {
+ dp->flags |= DRV_FLG_DIRTY;
+ bg_setstat(handle_cpm_drv_to, 1);
+ }
+
+ if (buserr) {
+ /* Bus timeout. how can this happen? */
+ rc = 0x03;
+ }
+
+ /* send result*/
+ msg_cpm_result(subf, rc, res);
+}
+
+
+const FLASH struct msg_item z80_messages[] =
+{
+ { 0, /* fct nr. */
+ 1, 3, /* sub fct nr. from, to */
+ do_msg_ini_memfifo},
+ { 1,
+ 1, 1,
+ do_msg_char_out},
+ { 1,
+ 2, 2,
+ do_msg_echo},
+ { 2,
+ 0, 0,
+ do_msg_cpm_login},
+ { 2,
+ 1, 2,
+ do_msg_cpm_rw},
+ { 3,
+ 1, 1,
+ do_msg_get_timer},
+ { 3,
+ 2, 3, /* 2: get, 3: set time and date */
+ do_msg_get_set_time},
+ { 0xff, /* end mark */
+ 0, 0,
+ 0},
+
+};
+
+
+
+
+void do_message(int len, uint8_t *msg)
+{
+ uint8_t fct, sub_fct;
+ int_fast8_t i = 0;
+
+ if (len >= 2) {
+ fct = *msg++;
+ sub_fct = *msg++;
+ len -= 2;
+
+ while (fct != z80_messages[i].fct) {
+ if (z80_messages[i].fct == 0xff) {
+ DBG_P(1, "do_message: Unknown function: %i, %i\n",
+ fct, sub_fct);
+ return; /* TODO: unknown message # */
+ }
+
+ ++i;
+ }
+
+ while (fct == z80_messages[i].fct) {
+ if (sub_fct >= z80_messages[i].sub_min &&
+ sub_fct <= z80_messages[i].sub_max )
+ break;
+ ++i;
+ }
+
+ if (z80_messages[i].fct != fct) {
+ DBG_P(1, "do_message: Unknown sub function: %i, %i\n",
+ fct, sub_fct);
+ return; /* TODO: unknown message sub# */
+ }
+
+ (z80_messages[i].func)(sub_fct, len, msg);
+
+
+ } else {
+ /* TODO: error */
+ DBG_P(1, "do_message: to few arguments (%i); this shouldn't happen!\n", len);
+ }
+}
+
+
+
+#define CTRBUF_LEN 256
+
+void check_msg_fifo(void)
+{
+ int ch;
+ static int_fast8_t state;
+ static int msglen,idx;
+ static uint8_t buffer[CTRBUF_LEN];
+
+ while ((ch = z80_memfifo_getc(fifo_msgin)) >= 0) {
+ switch (state) {
+ case 0: /* wait for start of message */
+ if (ch == 0xAE) { /* TODO: magic number */
+ msglen = 0;
+ idx = 0;
+ state = 1;
+ }
+ break;
+ case 1: /* get msg len */
+ if (ch > 0 && ch <= CTRBUF_LEN) {
+ msglen = ch;
+ state = 2;
+ } else
+ state = 0;
+ break;
+ case 2: /* get message */
+ buffer[idx++] = ch;
+ if (idx == msglen) {
+ do_message(msglen, buffer);
+ state = 0;
+ }
+ break;
+ }
+ }
+}
+
+
+int msg_handling(int state)
+{
+ bool pending;
+
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ pending = (Stat & S_MSG_PENDING) != 0;
+ Stat &= ~S_MSG_PENDING;
+ }
+
+ if (pending) {
+ uint8_t init_request;
+ z80_bus_cmd(Request);
+ init_request = z80_read(0x43);
+ z80_bus_cmd(Release);
+ if ( init_request != 0) {
+ /* Get address of fifo 0 */
+ z80_bus_cmd(Request);
+ uint32_t fifo_addr = z80_read(0x40) +
+ ((uint16_t) z80_read(0x40+1) << 8) +
+ ((uint32_t) z80_read(0x40+2) << 16);
+ z80_write(0x43, 0);
+ z80_bus_cmd(Release);
+
+ if (fifo_addr != 0) {
+ z80_memfifo_init(fifo_msgin, fifo_addr);
+ state = 1;
+ } else
+ state = 0;
+
+ } else {
+ check_msg_fifo();
+ }
+ }
+
+ return state;
+}
+
+
+static int handle_msg_handling;
+
+void setup_z180_serv(void)
+{
+
+ handle_msg_handling = bg_register(msg_handling, 0);
+ handle_cpm_drv_to = bg_register(cpm_drv_to, 0);
+}
+
+void restart_z180_serv(void)
+{
+ z80_bus_cmd(Request);
+ z80_memset(0x40, 0, 4);
+ z80_bus_cmd(Release);
+
+ for (int i = 0; i < NUM_FIFOS; i++)
+ z80_memfifo_init(i, 0);
+ bg_setstat(handle_msg_handling, 0);
+
+}
+
+#if 0
+/*--------------------------------------------------------------------------*/
+
+const FLASH uint8_t iniprog[] = {
+ 0xAF, // xor a
+ 0xED, 0x39, 0x36, // out0 (rcr),a ;disable DRAM refresh
+ 0x3E, 0x30, // ld a,030h
+ 0xED, 0x39, 0x32 //out0 (dcntl),a ;0 mem, max i/0 wait states
+};
+
+const FLASH uint8_t sertest[] = {
+ 0xAF, // xor a
+ 0xED, 0x39, 0x36, // out0 (rcr),a ;disable DRAM refresh
+ 0x3E, 0x30, // ld a,030h
+ 0xED, 0x39, 0x32, // out0 (dcntl),a ;0 mem, max i/0 wait states
+ 0x3E, 0x80, // ld a,M_MPBT ;no MP, PS=10, DR=16, SS=0
+ 0xED, 0x39, 0x03, // out0 (cntlb1),a
+ 0x3E, 0x64, // ld a,M_RE + M_TE + M_MOD2 ;
+ 0xED, 0x39, 0x01, // out0 (cntla1),a
+ 0x3E, 0x00, // ld a,0
+ 0xED, 0x39, 0x05, // out0 (stat1),a ;Enable rx interrupts
+ 0xED, 0x38, 0x05, //l0:in0 a,(stat1)
+ 0xE6, 0x80, // and 80h
+ 0x28, 0xF9, // jr z,l0
+ 0xED, 0x00, 0x09, // in0 b,(rdr1)
+ 0xED, 0x38, 0x05, //l1:in0 a,(stat1)
+ 0xE6, 0x02, // and 02h
+ 0x28, 0xF9, // jr z,l1
+ 0xED, 0x01, 0x07, // out0 (tdr1),b
+ 0x18, 0xEA, // jr l0
+};
+
+const FLASH uint8_t test1[] = {
+ 0xAF, // xor a
+ 0xED, 0x39, 0x36, // out0 (rcr),a ;disable DRAM refresh
+ 0x3E, 0x30, // ld a,030h
+ 0xED, 0x39, 0x32, // out0 (dcntl),a ;0 mem, max i/0 wait states
+ 0x21, 0x1E, 0x00, // ld hl,dmclrt ;load DMA registers
+ 0x06, 0x08, // ld b,dmct_e-dmclrt
+ 0x0E, 0x20, // ld c,sar0l
+ 0xED, 0x93, // otimr
+ 0x3E, 0xC3, // ld a,0c3h ;dst +1, src +1, burst
+ 0xED, 0x39, 0x31, // out0 (dmode),a ;
+ 0x3E, 0x62, // ld a,062h ;enable dma0,
+ 0xED, 0x39, 0x30, //cl_1: out0 (dstat),a ;copy 64k
+ 0x18, 0xFB, // jr cl_1 ;
+ 0x00, 0x00, //dmclrt: dw 0 ;src (inc)
+ 0x00, // db 0 ;src
+ 0x00, 0x00, // dw 0 ;dst (inc),
+ 0x00, // db 0 ;dst
+ 0x00, 0x00, // dw 0 ;count (64k)
+};
+#endif
diff --git a/avr/z80-if.c b/avr/z80-if.c
new file mode 100644
index 0000000..5a4104d
--- /dev/null
+++ b/avr/z80-if.c
@@ -0,0 +1,835 @@
+/*
+ * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+/**
+ *
+ * Pin assignments
+ *
+ * | Z180-Sig | AVR-Port | Dir |
+ * +------------+---------------+-------+
+ * | A0 | PA 0 | O |
+ * | A1 | PA 1 | O |
+ * | A2 | PA 2 | O |
+ * | A3 | PA 3 | O |
+ * | A4 | PA 4 | O |
+ * | A5 | PA 5 | O |
+ * | A6 | PA 6 | O |
+ * | A7 | PA 7 | O |
+ * | A8 | PC 0 | O |
+ * | A9 | PC 1 | O |
+ * | A10 | PC 2 | O |
+ * | A11 | PC 3 | O |
+ * | A12 | PC 4 | O |
+ * | A13 | PC 5 | O |
+ * | A14 | PC 6 | O |
+ * | A15 | PC 7 | O |
+ * | A16 | PE 2 | O |
+ * | A17 | PE 3 | O |
+ * | A18 | PE 4 | O |
+ * | D0 | PF 0 | I/O |
+ * | D1 | PF 1 | I/O |
+ * | D2 | PF 2 | I/O |
+ * | D3 | PF 3 | I/O |
+ * | D4 | PF 4 | I/O |
+ * | D5 | PF 5 | I/O |
+ * | D6 | PF 6 | I/O |
+ * | D7 | PF 7 | I/O |
+ * | RD | PD 3 | O |
+ * | WR | PD 2 | O |
+ * | MREQ | PD 4 | O |
+ * | RST | PD 5 | O |
+ * | BUSREQ | PD 7 | O |
+ * | BUSACK | PD 6 | I |
+ * |
+ * | Optional
+ * +------------------------------------+
+ * | STEP | PG 0 | O |
+ * | RUN | PG 1 | O |
+ * | WAIT | PG 2 | I |
+
+ */
+
+
+#include "z80-if.h"
+#include <util/atomic.h>
+#include "debug.h"
+#include "config.h"
+#include "env.h"
+
+
+//#define P_ZCLK PORTB
+//#define ZCLK 5
+//#define DDR_ZCLK DDRB
+#define P_MREQ PORTD
+#define MREQ 4
+#define DDR_MREQ DDRD
+#define P_RD PORTD
+#define RD 3
+#define P_WR PORTD
+#define WR 2
+#define P_BUSREQ PORTD
+#define BUSREQ 7
+#define PIN_BUSREQ PIND
+#define DDR_BUSREQ DDRD
+#define P_BUSACK PORTD
+#define PIN_BUSACK PIND
+#define BUSACK 6
+#define DDR_BUSACK DDRD
+#define P_RST PORTD
+#define PIN_RST PIND
+#define DDR_RST DDRD
+#define RST 5
+
+
+#define P_DB PORTF
+#define PIN_DB PINF
+#define DDR_DB DDRF
+
+#define P_ADL PORTA
+#define P_ADH PORTC
+#define P_ADB PORTE
+#define PIN_ADB PINE
+#define DDR_ADL DDRA
+#define DDR_ADH DDRC
+#define DDR_ADB DDRE
+
+#define ADB_WIDTH 3
+#define ADB_SHIFT 2
+//#define ADB_PORT PORTE
+
+
+//#define Z80_O_ZCLK SBIT(P_ZCLK, 5)
+#define Z80_O_MREQ SBIT(P_MREQ, 4)
+#define Z80_O_RD SBIT(P_RD, 3)
+#define Z80_O_WR SBIT(P_WR, 2)
+#define Z80_O_BUSREQ SBIT(P_BUSREQ, 7)
+#define Z80_I_BUSREQ SBIT(PIN_BUSREQ, 7)
+//#define Z80_O_NMI SBIT(P_NMI, )
+#define Z80_O_RST SBIT(P_RST, 5)
+#define Z80_I_RST SBIT(PIN_RST, 5)
+#define Z80_I_BUSACK SBIT(PIN_BUSACK, 6)
+//#define Z80_I_HALT SBIT(P_HALT, )
+
+/* Optional */
+#define P_RUN PORTG
+#define RUN 1
+#define DDR_RUN DDRG
+#define P_STEP PORTG
+#define STEP 0
+#define DDR_STEP DDRG
+#define P_WAIT PORTG
+#define WAIT 2
+#define DDR_WAIT DDRG
+/* All three signals are on the same Port (PortG) */
+#define PORT_SS PORTG
+#define DDR_SS DDRG
+#define PIN_SS PING
+#define Z80_O_RUN SBIT(PORT_SS, RUN)
+#define Z80_O_STEP SBIT(PORT_SS, STEP)
+#define Z80_I_WAIT SBIT(PORT_SS, WAIT)
+
+
+#define BUS_TO 20
+
+
+#define MASK(n) ((1<<(n))-1)
+#define SMASK(w,s) (MASK(w) << (s))
+
+void z80_bus_request_or_exit(void)
+{
+ if (!(z80_bus_cmd(Request) & ZST_ACQUIRED))
+ cmd_error(CMD_RET_FAILURE, EBUSTO, NULL);
+}
+
+static zstate_t zstate;
+static volatile uint8_t timer; /* used for bus timeout */
+
+
+static volatile uint16_t busack_cycles_ovl;
+
+static uint32_t busack_cycles;
+
+ISR(TIMER4_COMPB_vect)
+{
+ busack_cycles_ovl++;
+}
+
+/*---------------------------------------------------------*/
+/* 10Hz timer interrupt generated by OC5A */
+/*---------------------------------------------------------*/
+
+ISR(TIMER5_COMPA_vect)
+{
+
+ uint8_t i = timer;
+
+ if (i)
+ timer = i - 1;
+}
+
+/*--------------------------------------------------------------------------*/
+
+
+static void z80_addrbus_set_in(void)
+{
+ /* /MREQ, /RD, /WR: Input, no pullup */
+ DDR_MREQ &= ~(_BV(MREQ) | _BV(RD) | _BV(WR));
+ Z80_O_MREQ = 0;
+ Z80_O_RD = 0;
+ Z80_O_WR = 0;
+
+ P_ADL = 0;
+ DDR_ADL = 0;
+ P_ADH = 0;
+ DDR_ADH = 0;
+ PIN_ADB = P_ADB & (MASK(ADB_WIDTH) << ADB_SHIFT);
+ DDR_ADB = DDR_ADB & ~(MASK(ADB_WIDTH) << ADB_SHIFT);
+}
+
+
+static void z80_addrbus_set_out(void)
+{
+ /* /MREQ, /RD, /WR: Output and high */
+ Z80_O_MREQ = 1;
+ Z80_O_RD = 1;
+ Z80_O_WR = 1;
+ DDR_MREQ |= _BV(MREQ) | _BV(RD) | _BV(WR);
+
+ DDR_ADL = 0xff;
+ DDR_ADH = 0xff;
+ DDR_ADB = DDR_ADB | (MASK(ADB_WIDTH) << ADB_SHIFT);
+}
+
+
+static void z80_dbus_set_in(void)
+{
+ DDR_DB = 0;
+ P_DB = 0;
+}
+
+
+static void z80_dbus_set_out(void)
+{
+ DDR_DB = 0xff;
+}
+
+static void z80_reset_active(void)
+{
+ if (Stat & S_RESET_POLARITY)
+ Z80_O_RST = 1;
+ else
+ Z80_O_RST = 0;
+}
+
+static void z80_reset_inactive(void)
+{
+ if (Stat & S_RESET_POLARITY)
+ Z80_O_RST = 0;
+ else
+ Z80_O_RST = 1;
+}
+
+static void z80_reset_pulse(void)
+{
+ z80_reset_active();
+ _delay_us(10);
+ z80_reset_inactive();
+}
+
+
+void z80_setup_bus(void)
+{
+ ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
+
+ /* /ZRESET: Input, no pullup */
+ DDR_RST &= ~_BV(RST);
+ Z80_O_RST = 0;
+
+ /* /BUSREQ: Output and high */
+ Z80_O_BUSREQ = 1;
+ DDR_BUSREQ |= _BV(BUSREQ);
+
+ /* /BUSACK: Input, no pullup */
+ DDR_BUSACK &= ~_BV(BUSACK);
+ P_BUSACK &= ~_BV(BUSACK);
+
+ z80_addrbus_set_in();
+ z80_dbus_set_in();
+
+ if (getenv_yesno(PSTR(ENV_SINGLESTEP))) {
+ /* /RUN & /STEP: output, /WAIT: input */
+
+ PORT_SS = (PORT_SS & ~_BV(RUN)) | _BV(STEP);
+ DDR_SS = (DDR_SS & ~_BV(WAIT)) | _BV(RUN) | _BV(STEP);
+ }
+
+ if (Z80_I_RST)
+ Stat |= S_RESET_POLARITY;
+ else
+ Stat &= ~S_RESET_POLARITY;
+ z80_reset_active();
+ DDR_RST |= _BV(RST);
+
+ zstate = RESET;
+ }
+
+ /* Timer 5 */
+ PRR1 &= ~_BV(PRTIM5);
+ OCR5A = F_CPU / 1024 / 10 - 1; /* Timer: 10Hz interval (OC4A) */
+ TCCR5B = (0b01<<WGM52)|(0b101<<CS40); /* CTC Mode, Prescaler 1024 */
+ TIMSK5 = _BV(OCIE5A); /* Enable oca interrupt */
+
+}
+
+
+uint32_t z80_get_busreq_cycles(void)
+{
+ return busack_cycles;
+}
+
+zstate_t z80_bus_state(void)
+{
+ return zstate;
+}
+
+void z80_toggle_reset(void)
+{
+ Z80_I_RST = 1;
+}
+
+void z80_toggle_busreq(void)
+{
+ Z80_I_BUSREQ = 1;
+}
+
+
+static void z80_busreq_hpulse(void)
+{
+ z80_dbus_set_in();
+ z80_addrbus_set_in();
+
+#if 0
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ Z80_O_BUSREQ = 1;
+ Z80_O_BUSREQ = 1; /* 2 AVR clock cycles */
+ Z80_O_BUSREQ = 0; /* 2 AVR clock cycles */
+ }
+#endif
+
+#if 1
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ Z80_O_BUSREQ = 1;
+
+ do {
+ if (Z80_I_BUSACK == 1) {
+ Z80_O_BUSREQ = 0;
+ break;
+ }
+ } while (1);
+ }
+#endif
+
+ if (zstate & ZST_ACQUIRED) {
+ timer = BUS_TO;
+ while (Z80_I_BUSACK == 1 && timer)
+ ;
+ if (Z80_I_BUSACK == 0)
+ z80_addrbus_set_out();
+ }
+}
+
+
+/*
+
+ + | | | | |
+ + State | RESET | RESET_AQRD | RUNNING | RUNNING_AQRD |
+ + | | | | |
+ + | 0 | 1 | 2 | 3 |
+Event + | | | | |
+----------------+---------------+---------------+---------------+---------------+
+ | | | | |
+Reset | 0 | 0 | 0 | 0 |
+ | | | | |
+ | | | | |
+Request | 1 | | 3 | |
+ | | | | |
+ | | | | |
+Release | | 0 | | 2 |
+ | | | | |
+ | | | | |
+Run | 2 | 3 | | |
+ | | | | |
+ | | | | |
+Restart | | | 2 | 3 |
+ | | | | |
+ | | | | |
+M_Cycle | | | | 3 |
+ | | | | |
+ | | | | |
+*/
+
+zstate_t z80_bus_cmd(bus_cmd_t cmd)
+{
+ switch (cmd) {
+
+ case Reset:
+ z80_dbus_set_in();
+ z80_addrbus_set_in();
+ z80_reset_active();
+ _delay_us(10);
+ Z80_O_BUSREQ = 1;
+ timer = BUS_TO;
+ while (Z80_I_BUSACK == 0 && timer)
+ ;
+ zstate = RESET;
+ break;
+
+ case Request:
+ switch (zstate) {
+ case RESET:
+ Z80_O_BUSREQ = 0;
+ timer = 255; //BUS_TO;
+
+ uint16_t tcnt;
+ uint16_t ovl_cnt;
+ uint8_t ifr;
+ busack_cycles = 0;
+ busack_cycles_ovl = 0;
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ Z80_I_RST = 1; /* Toggle RESET --> inactive */
+ OCR4B = TCNT4;
+ TIFR4 = _BV(OCF4B); /* Clear compare match flag */
+// TIMSK4 &= ~_BV(OCIE4A); /* Disable Output Compare A interrupt */
+ }
+ TIMSK4 |= _BV(OCIE4B); /* Enable compare match interrupt */
+
+ while (Z80_I_BUSACK == 1 && timer)
+ ;
+
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ tcnt = TCNT4 - OCR4B;
+ ovl_cnt = busack_cycles_ovl;
+ ifr = TIFR4;
+ TIMSK4 &= ~_BV(OCIE4B); /* Disable compare match interrupt */
+// TIMSK4 |= _BV(OCIE4A); /* Enable Output Compare A interrupt */
+ }
+ if (Z80_I_BUSACK == 0) {
+ if ((ifr & _BV(OCF4B)) && !(tcnt & (1<<15)))
+ ovl_cnt++;
+ busack_cycles = tcnt + ((uint32_t) ovl_cnt << 16);
+ z80_addrbus_set_out();
+ zstate = RESET_AQRD;
+// debug("### ovl: %u, ifr: %u, beg: %u, end: %u\n", ovl_cnt,
+// (ifr & _BV(OCF4B)) != 0, OCR4B, tcnt);
+ } else {
+ z80_reset_active();
+ Z80_O_BUSREQ = 1;
+ }
+ break;
+
+ case RUNNING:
+ Z80_O_BUSREQ = 0;
+ timer = BUS_TO;
+ while (Z80_I_BUSACK == 1 && timer)
+ ;
+ if (Z80_I_BUSACK == 0) {
+ z80_addrbus_set_out();
+ zstate = RUNNING_AQRD;
+ } else {
+ Z80_O_BUSREQ = 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case Release:
+ switch (zstate) {
+ case RESET_AQRD:
+ z80_dbus_set_in();
+ z80_addrbus_set_in();
+ z80_reset_active();
+ _delay_us(10);
+ Z80_O_BUSREQ = 1;
+ timer = BUS_TO;
+ while (Z80_I_BUSACK == 0 && timer)
+ ;
+ zstate = RESET;
+ break;
+ case RUNNING_AQRD:
+ z80_dbus_set_in();
+ z80_addrbus_set_in();
+ Z80_O_BUSREQ = 1;
+ timer = BUS_TO;
+ while (Z80_I_BUSACK == 0 && timer)
+ ;
+ zstate = RUNNING;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case Run:
+ switch (zstate) {
+ case RESET:
+ _delay_ms(20); /* TODO: */
+ z80_reset_inactive();
+ zstate = RUNNING;
+ break;
+
+ case RESET_AQRD:
+ z80_dbus_set_in();
+ z80_addrbus_set_in();
+ z80_reset_pulse();
+ z80_addrbus_set_out();
+ zstate = RUNNING_AQRD;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case Restart:
+ switch (zstate) {
+ case RUNNING:
+ case RUNNING_AQRD:
+ z80_reset_pulse();
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case M_Cycle:
+ switch (zstate) {
+ case RUNNING_AQRD:
+ z80_busreq_hpulse(); /* TODO: */
+ break;
+ default:
+ break;
+ }
+ }
+ return zstate;
+}
+
+
+/*--------------------------------------------------------------------------*/
+
+static
+//inline __attribute__ ((always_inline))
+void z80_setaddress(uint32_t addr)
+{
+ P_ADL = addr;
+ P_ADH = (addr & 0xff00) >> 8;
+ PIN_ADB = (((addr >> 16) << ADB_SHIFT) ^ P_ADB) & MASK(ADB_WIDTH) << ADB_SHIFT;
+}
+
+int32_t z80_memsize_detect(void)
+{
+ const uint8_t PATTERN_1 = 0x55;
+ const uint8_t PATTERN_2 = ~PATTERN_1;
+ uint32_t addr;
+
+ if (!(z80_bus_cmd(Request) & ZST_ACQUIRED))
+ return -EBUSTO;
+
+ uint8_t ram_0 = z80_read(0);
+ uint8_t ram_1 = z80_read(1);
+
+ z80_write(0, ram_0 ^ 0xff);
+ z80_write(1, ram_1);
+ if ((z80_read(0) ^ ram_0) != 0xff) {
+ addr = 0;
+ } else {
+ z80_write(0, PATTERN_1);
+ for (addr=1; addr < CONFIG_SYS_RAMSIZE_MAX; addr <<= 1) {
+ uint8_t ram_i = z80_read(addr);
+ z80_write(addr, PATTERN_2);
+ if (z80_read(0) != PATTERN_1 || z80_read(addr) != PATTERN_2)
+ break;
+ z80_write(addr, ram_i);
+ }
+ }
+
+ z80_write(0, ram_0);
+ z80_bus_cmd(Release);
+
+ return addr;
+}
+
+/*--------------------------------------------------------------------------*/
+
+void z80_write(uint32_t addr, uint8_t data)
+{
+ z80_setaddress(addr);
+ Z80_O_MREQ = 0;
+ z80_dbus_set_out();
+ P_DB = data;
+ P_DB = data;
+ Z80_O_WR = 0;
+ Z80_O_WR = 0;
+ Z80_O_WR = 1;
+ Z80_O_MREQ = 1;
+}
+
+uint8_t z80_read(uint32_t addr)
+{
+ uint8_t data;
+
+ z80_setaddress(addr);
+ Z80_O_MREQ = 0;
+ z80_dbus_set_in();
+ Z80_O_RD = 0;
+ Z80_O_RD = 0;
+ Z80_O_RD = 0;
+ data = PIN_DB;
+ Z80_O_RD = 1;
+ Z80_O_MREQ = 1;
+
+ return data;
+}
+
+
+void z80_memset(uint32_t addr, uint8_t data, uint32_t length)
+{
+ z80_dbus_set_out();
+ Z80_O_MREQ = 0;
+ P_DB = data;
+ while(length--) {
+ z80_setaddress(addr++);
+ Z80_O_WR = 0;
+ Z80_O_WR = 0;
+ Z80_O_WR = 1;
+ }
+ Z80_O_MREQ = 1;
+}
+
+void z80_write_block_P(const FLASH uint8_t *src, uint32_t dest, uint32_t length)
+{
+ uint8_t data;
+
+ z80_dbus_set_out();
+ Z80_O_MREQ = 0;
+ while(length--) {
+ z80_setaddress(dest++);
+ data = *src++;
+ P_DB = data;
+ P_DB = data;
+ Z80_O_WR = 0;
+ Z80_O_WR = 0;
+ Z80_O_WR = 1;
+ }
+ Z80_O_MREQ = 1;
+}
+
+void z80_write_block(const uint8_t *src, uint32_t dest, uint32_t length)
+{
+ uint8_t data;
+
+ z80_dbus_set_out();
+ Z80_O_MREQ = 0;
+ while(length--) {
+ z80_setaddress(dest++);
+ data = *src++;
+ P_DB = data;
+ P_DB = data;
+ Z80_O_WR = 0;
+ Z80_O_WR = 0;
+ Z80_O_WR = 1;
+ }
+ Z80_O_MREQ = 1;
+}
+
+void z80_read_block (uint8_t *dest, uint32_t src, size_t length)
+{
+ uint8_t data;
+
+ Z80_O_MREQ = 0;
+ z80_dbus_set_in();
+ while(length--) {
+ z80_setaddress(src++);
+ Z80_O_RD = 0;
+ Z80_O_RD = 0;
+ Z80_O_RD = 0;
+ data = PIN_DB;
+ Z80_O_RD = 1;
+ *dest++ = data;
+ }
+ Z80_O_MREQ = 1;
+}
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ 0179' rx.bs_mask: ds 1 ; (buf_len - 1)
+ 017A' rx.in_idx: ds 1 ;
+ 017B' rx.out_idx: ds 1 ;
+ 017C' rx.buf: ds rx.buf_len ;
+ 018B' rx.buf_end equ $-1 ; last byte (start+len-1)
+
+ 018C' tx.bs_mask: ds 1 ; (buf_len - 1)
+ 018D' tx.in_idx: ds 1 ;
+ 018E' tx.out_idx: ds 1 ;
+ 018F' tx.buf: ds tx.buf_len ;
+ 019E' tx.buf_end equ $-1 ; last byte
+*/
+
+
+typedef struct __attribute__((packed)) {
+ uint8_t mask;
+ uint8_t in_idx;
+ uint8_t out_idx;
+ uint8_t buf[];
+} zfifo_t;
+
+
+
+#define FIFO_BUFSIZE_MASK -3
+#define FIFO_INDEX_IN -2
+#define FIFO_INDEX_OUT -1
+
+
+static struct {
+ uint32_t base;
+ uint8_t idx_out,
+ idx_in,
+ mask;
+ } fifo_dsc[NUM_FIFOS];
+
+
+void z80_memfifo_init(const fifo_t f, uint32_t addr)
+{
+ fifo_dsc[f].base = addr;
+
+
+ if (addr != 0) {
+ z80_bus_cmd(Request);
+ fifo_dsc[f].mask = z80_read(addr + FIFO_BUFSIZE_MASK);
+ fifo_dsc[f].idx_in = z80_read(addr + FIFO_INDEX_IN);
+ fifo_dsc[f].idx_out = z80_read(addr + FIFO_INDEX_OUT);
+ z80_bus_cmd(Release);
+
+ if (fifo_dsc[f].idx_in != 0 || fifo_dsc[f].idx_out != 0) {
+ DBG_P(1, "## z80_memfifo_init: %i, %lx, in: %.2x, out: %.2x, mask: %.2x\n",
+ f, addr, fifo_dsc[f].idx_in, fifo_dsc[f].idx_out, fifo_dsc[f].mask);
+ }
+ }
+}
+
+
+int z80_memfifo_is_empty(const fifo_t f)
+{
+ int rc = 1;
+
+ if (fifo_dsc[f].base != 0) {
+
+ uint32_t adr = fifo_dsc[f].base + FIFO_INDEX_IN;
+ uint8_t idx;
+
+ z80_bus_cmd(Request);
+ idx = z80_read(adr);
+ z80_bus_cmd(Release);
+ rc = idx == fifo_dsc[f].idx_out;
+ }
+
+ return rc;
+}
+
+int z80_memfifo_is_full(const fifo_t f)
+{
+ int rc = 0;
+
+ if (fifo_dsc[f].base != 0) {
+ z80_bus_cmd(Request);
+ rc = ((fifo_dsc[f].idx_in + 1) & fifo_dsc[f].mask)
+ == z80_read(fifo_dsc[f].base+FIFO_INDEX_OUT);
+ z80_bus_cmd(Release);
+ }
+ return rc;
+}
+
+
+uint8_t z80_memfifo_getc_wait(const fifo_t f)
+{
+ uint8_t rc, idx;
+
+ while (z80_memfifo_is_empty(f))
+ ;
+
+ z80_bus_cmd(Request);
+ idx = fifo_dsc[f].idx_out;
+ rc = z80_read(fifo_dsc[f].base+idx);
+ fifo_dsc[f].idx_out = ++idx & fifo_dsc[f].mask;
+ z80_write(fifo_dsc[f].base+FIFO_INDEX_OUT, fifo_dsc[f].idx_out);
+ z80_bus_cmd(Release);
+
+ return rc;
+}
+
+int z80_memfifo_getc(const fifo_t f)
+{
+ int rc = -1;
+
+ if (fifo_dsc[f].base != 0) {
+ uint8_t idx = fifo_dsc[f].idx_out;
+ z80_bus_cmd(Request);
+ if (idx != z80_read(fifo_dsc[f].base + FIFO_INDEX_IN)) {
+ rc = z80_read(fifo_dsc[f].base+idx);
+ fifo_dsc[f].idx_out = ++idx & fifo_dsc[f].mask;
+ z80_write(fifo_dsc[f].base+FIFO_INDEX_OUT, fifo_dsc[f].idx_out);
+ }
+ z80_bus_cmd(Release);
+ }
+
+ return rc;
+}
+
+
+void z80_memfifo_putc(fifo_t f, uint8_t val)
+{
+ int idx;
+
+ while (z80_memfifo_is_full(f))
+ ;
+
+ z80_bus_cmd(Request);
+ idx = fifo_dsc[f].idx_in;
+ z80_write(fifo_dsc[f].base+idx, val);
+ fifo_dsc[f].idx_in = ++idx & fifo_dsc[f].mask;
+ z80_write(fifo_dsc[f].base+FIFO_INDEX_IN, fifo_dsc[f].idx_in);
+ z80_bus_cmd(Release);
+}
+
+/*--------------------------------------------------------------------------*/
+
+void z80_load_mem(int_fast8_t verbosity,
+ const FLASH unsigned char data[],
+ const FLASH unsigned long *sections,
+ const FLASH unsigned long address[],
+ const FLASH unsigned long length_of_sections[])
+{
+ uint32_t sec_base = 0;
+
+ if (verbosity > 1)
+ printf_P(PSTR("Loading Z180 memory... \n"));
+
+ for (unsigned sec = 0; sec < *sections; sec++) {
+ if (verbosity > 0) {
+ printf_P(PSTR(" From: 0x%.5lX to: 0x%.5lX (%5li bytes)\n"),
+ address[sec],
+ address[sec]+length_of_sections[sec] - 1,
+ length_of_sections[sec]);
+ }
+
+ z80_write_block_P((const FLASH unsigned char *) &data[sec_base], /* src */
+ address[sec], /* dest */
+ length_of_sections[sec]); /* len */
+ sec_base += length_of_sections[sec];
+ }
+}