diff options
Diffstat (limited to 'avr')
-rw-r--r-- | avr/Makefile | 114 | ||||
-rw-r--r-- | avr/background.c | 60 | ||||
-rw-r--r-- | avr/bcd.c | 21 | ||||
-rw-r--r-- | avr/cli.c | 433 | ||||
-rw-r--r-- | avr/cli_readline.c | 697 | ||||
-rw-r--r-- | avr/cmd_attach.c | 126 | ||||
-rw-r--r-- | avr/cmd_boot.c | 466 | ||||
-rw-r--r-- | avr/cmd_cpu.c | 571 | ||||
-rw-r--r-- | avr/cmd_date.c | 176 | ||||
-rw-r--r-- | avr/cmd_fat.c | 664 | ||||
-rw-r--r-- | avr/cmd_gpio.c | 326 | ||||
-rw-r--r-- | avr/cmd_loadcpm3.c | 195 | ||||
-rw-r--r-- | avr/cmd_loadihex.c | 216 | ||||
-rw-r--r-- | avr/cmd_mem.c | 774 | ||||
-rw-r--r-- | avr/cmd_misc.c | 110 | ||||
-rw-r--r-- | avr/cmd_run.c | 86 | ||||
-rw-r--r-- | avr/cmd_sd.c | 373 | ||||
-rw-r--r-- | avr/command.c | 812 | ||||
-rw-r--r-- | avr/command_tbl.c | 402 | ||||
-rw-r--r-- | avr/con-utils.c | 126 | ||||
-rw-r--r-- | avr/debug.c | 257 | ||||
-rw-r--r-- | avr/env.c | 881 | ||||
-rw-r--r-- | avr/eval_arg.c | 154 | ||||
-rw-r--r-- | avr/getopt-min.c | 75 | ||||
-rw-r--r-- | avr/gpio.c | 376 | ||||
-rw-r--r-- | avr/i2c.c | 377 | ||||
-rw-r--r-- | avr/main.c | 276 | ||||
-rw-r--r-- | avr/mmc.c | 807 | ||||
-rw-r--r-- | avr/pcf8583.c | 105 | ||||
-rw-r--r-- | avr/print-utils.c | 97 | ||||
-rw-r--r-- | avr/serial.c | 123 | ||||
-rw-r--r-- | avr/strerror.c | 38 | ||||
-rw-r--r-- | avr/timer.c | 70 | ||||
-rw-r--r-- | avr/z180-serv.c | 826 | ||||
-rw-r--r-- | avr/z80-if.c | 835 |
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, ¢ury) || + 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]; + } +} |