diff options
Diffstat (limited to 'avr')
-rw-r--r-- | avr/Tupfile | 86 | ||||
-rw-r--r-- | avr/background.c | 56 | ||||
-rw-r--r-- | avr/cli.c | 353 | ||||
-rw-r--r-- | avr/cli_readline.c | 594 | ||||
-rw-r--r-- | avr/cmd_boot.c | 275 | ||||
-rw-r--r-- | avr/cmd_date.c | 182 | ||||
-rw-r--r-- | avr/cmd_help.c | 32 | ||||
-rw-r--r-- | avr/cmd_mem.c | 868 | ||||
-rw-r--r-- | avr/cmd_misc.c | 91 | ||||
-rw-r--r-- | avr/cmd_pin.c | 328 | ||||
-rw-r--r-- | avr/command.c | 530 | ||||
-rw-r--r-- | avr/command_tbl.c | 272 | ||||
-rw-r--r-- | avr/con-utils.c | 95 | ||||
-rw-r--r-- | avr/date.c | 139 | ||||
-rw-r--r-- | avr/debug.c | 230 | ||||
-rw-r--r-- | avr/env.c | 696 | ||||
-rw-r--r-- | avr/getopt-min.c | 75 | ||||
-rw-r--r-- | avr/i2c.c | 376 | ||||
-rw-r--r-- | avr/main.c | 252 | ||||
-rw-r--r-- | avr/pcf8583.c | 100 | ||||
-rw-r--r-- | avr/pin.c | 372 | ||||
-rw-r--r-- | avr/print-utils.c | 10 | ||||
-rw-r--r-- | avr/serial.c | 123 | ||||
-rw-r--r-- | avr/timer.c | 86 | ||||
-rw-r--r-- | avr/xmalloc.c | 27 | ||||
-rw-r--r-- | avr/z180-serv.c | 288 | ||||
-rw-r--r-- | avr/z180-stamp-avr.c | 591 | ||||
-rw-r--r-- | avr/z80-if.c | 597 |
28 files changed, 7724 insertions, 0 deletions
diff --git a/avr/Tupfile b/avr/Tupfile new file mode 100644 index 0000000..acb4aa5 --- /dev/null +++ b/avr/Tupfile @@ -0,0 +1,86 @@ +include_rules + +PROG = stamp-monitor +SRC = main.c +SRC += cli.c cli_readline.c command.c command_tbl.c +SRC += cmd_help.c cmd_date.c cmd_mem.c cmd_boot.c cmd_pin.c cmd_misc.c +SRC += env.c xmalloc.c date.c con-utils.c print-utils.c getopt-min.c +SRC += timer.c serial.c i2c.c pcf8583.c +SRC += background.c z180-serv.c z80-if.c pin.c + +SRC_Z = ../z180/hdrom.c + +#TARGETS = $(PROG).elf + +MCU_TARGET = atmega1281 +F_CPU = 18432000UL +DEFS = -DF_CPU=$(F_CPU) + +INCLUDES += -I$(TOP)/include + +#INCLUDES += -I../z180 + +############################################################################### + +TOOLCHAINDIR = +TOOLCHAIN = avr + +CC = $(TOOLCHAIN)-gcc +LD = $(TOOLCHAIN)-gcc +AR = $(TOOLCHAIN)-ar +AS = $(TOOLCHAIN)-as +OBJCOPY = $(TOOLCHAIN)-objcopy +OBJDUMP = $(TOOLCHAIN)-objdump +SIZE = $(TOOLCHAIN)-size +GDB = $(TOOLCHAIN)-gdb + +############################################################################### + +ifdef DEBUG +SRC += debug.c +DEFS += -DDEBUG=2 +endif + +CFLAGS = -g -Os +CFLAGS += -mmcu=$(MCU_TARGET) +CFLAGS += -std=gnu99 +CFLAGS += -Wall -Wextra +CFLAGS += -Wredundant-decls +CFLAGS += -mrelax +CFLAGS += -fno-common +CFLAGS += -ffunction-sections +CFLAGS += -fdata-sections +CFLAGS += -fno-tree-loop-optimize +CFLAGS += -fno-move-loop-invariants +CFLAGS += -fno-split-wide-types +#CFLAGS += -flto +CFLAGS += -fshort-enums + +#CFLAGS += -fdiagnostics-color=always + +#CFLAGS += -save-temps + + +CFLAGS += $(INCLUDES) + +CPPFLAGS += $(DEFS) + +# Linker flags +LDFLAGS += -Wl,--gc-sections +LDFLAGS += -Wl,--cref + + +!cc = |> ^ CC %f^ $(CC) $(CFLAGS) $(CPPFLAGS) -c %f -o %o |> %B.o +!LINK = |> ^ LINK %o^ $(LD) $(CFLAGS) $(LDFLAGS) -Wl,-Map=%O.map %f $(LDLIBS) -o %o |> | %O.map +!OBJCOPY= |> ^ OBJCOPY %o^ $(OBJCOPY) -Oihex %f %o |> +!OBJDUMP= |> ^ OBJDUMP %o^ $(OBJDUMP) -h -S %f > %o |> %O.lss +!SIZE = |> ^ SIZE^ $(SIZE) %f |> + +: foreach $(SRC) | ../z180/hdrom.h |> !cc |> {objs} +: $(SRC_Z) |> !cc -D'const=const __flash' |> {objs} + +: {objs} |> !LINK |> $(PROG).elf +: $(PROG).elf |> !OBJCOPY |> %B.hex +: $(PROG).elf |> !OBJDUMP |> %B.lss +: $(PROG).elf |> !SIZE |> + diff --git a/avr/background.c b/avr/background.c new file mode 100644 index 0000000..0e1ca40 --- /dev/null +++ b/avr/background.c @@ -0,0 +1,56 @@ +#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/cli.c b/avr/cli.c new file mode 100644 index 0000000..b310f79 --- /dev/null +++ b/avr/cli.c @@ -0,0 +1,353 @@ +#include "common.h" + +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> + +#include "config.h" +#include "command.h" +#include "xmalloc.h" +#include "debug.h" +#include "env.h" +#include "cli_readline.h" +#include "con-utils.h" +#include "cli.h" + +#define DEBUG_PARSER 0 /* set to 1 to debug */ + +#define debug_parser(fmt, args...) \ + debug_cond(DEBUG_PARSER, fmt, ##args) + +static int cli_parse_line(char *line, char *argv[]) +{ + static const FLASH char delim[] = {" \t"}; + + char *ptr; + uint_fast8_t nargs = 0; + + debug_parser("parse_line: \"%s\"\n", line); + + ptr = strtok_P(line, delim); + while(nargs < CONFIG_SYS_MAXARGS && ptr != NULL) { + argv[nargs++] = ptr; + ptr = strtok_P(NULL, delim); + } + + if (ptr != NULL) + printf_P(PSTR("** Too many args (max. %d) **\n"), CONFIG_SYS_MAXARGS); + + argv[nargs] = NULL; + debug_parser("parse_line: nargs=%d\n", nargs); + + return nargs; +} + +static +void append_char(uint_fast8_t pass, char **p, char c) +{ + + if (pass) { + **p = c; + } + ++(*p); +} + +static +char *process_macros(char *input, char *output) +{ + char c, prev, *inp, *outp; + const char *varname = NULL; + + 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) { + outp = output; + } else { + int outputlen = outp - output; + outp = xrealloc(output, outputlen); + output = outp; + } + + inp = input; + prev = '\0'; /* previous character */ + + debug_parser("[PROCESS_MACROS] INPUT len %d: \"%s\"\n", strlen(inp), + inp); + + while ((c = *inp++) != '\0') { + + if (state != 3) { + /* remove one level of escape characters */ + if ((c == '\\') && (prev != '\\')) { + if (*inp == '\0') + break; + prev = c; + c = *inp++; + } + } + + switch (state) { + case 0: /* Waiting for (unescaped) $ */ + if ((c == '\'') && (prev != '\\')) { + state = 3; + break; + } + if ((c == '$') && (prev != '\\')) + state++; + else + append_char(pass, &outp, c); + break; + case 1: /* Waiting for { */ + if (c == '{') { + state++; + varname = inp; + } else { + state = 0; + append_char(pass, &outp, '$'); + append_char(pass, &outp, c); + } + break; + case 2: /* Waiting for } */ + if (c == '}') { + /* Terminate variable name */ + *(inp-1) = '\0'; + const char *envval = getenv(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; + } + break; + case 3: /* Waiting for ' */ + if ((c == '\'') && (prev != '\\')) + state = 0; + else + append_char(pass, &outp, c); + break; + } + prev = 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(), 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, int flag) +{ + char *cmdbuf; /* working copy of cmd */ + char *token; /* start of token in cmdbuf */ + char *sep; /* end of token (separator) in cmdbuf */ + char *finaltoken = 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; + + debug_parser("[RUN_COMMAND] cmd[%p]=\"%s\"\n", + cmd, cmd ? cmd : "NULL"); + + clear_ctrlc(); /* forget any previous Control C */ + + if (!cmd || !*cmd) + return -1; /* empty command */ + + cmdbuf = strdup(cmd); + if (!cmdbuf) + return -1; /* not enough memory */ + + str = cmdbuf; + + /* Process separators and check for invalid + * repeatable commands + */ + + debug_parser("[PROCESS_SEPARATORS] %s\n", cmd); + while (*str) { + /* + * Find separator, or string end + * Allow simple escape of ';' by writing "\;" + */ + for (inquotes = 0, sep = str; *sep; sep++) { + if ((*sep == '\'') && + (*(sep - 1) != '\\')) + inquotes = !inquotes; + + if (!inquotes && + (*sep == ';' || *sep == '\n') && /* separator */ + (sep != str) && /* past string start */ + (*(sep - 1) != '\\')) /* and NOT escaped */ + break; + } + + /* + * Limit the token to data between separators + */ + token = str; + if (*sep) { + str = sep + 1; /* start of command for next pass */ + *sep = '\0'; + } else { + str = sep; /* no more commands for next pass */ + } + debug_parser("token: \"%s\"\n", token); + + /* find macros in this token and replace them */ + finaltoken = process_macros(token, finaltoken); + + /* Extract arguments */ + argc = cli_parse_line(finaltoken, argv); + if (argc == 0) { + rc = -1; /* no command at all */ + continue; + } + + if (cmd_process(flag, argc, argv, &repeatable) != CMD_RET_SUCCESS) + rc = -1; + + /* Did the user stop this? */ + if (had_ctrlc()) { + rc = -1; /* if stopped then not repeatable */ + break; + } + } + + free(cmdbuf); + free(finaltoken); + + return rc ? rc : repeatable; +} + +static int cli_run_command_list(const char *cmd) +{ + return (cli_run_command(cmd, 0) < 0); +} + +/******************************************************************************/ + + +/* + * Run a command using the selected parser. + * + * @param cmd Command to run + * @param flag Execution flags (CMD_FLAG_...) + * @return 0 on success, or != 0 on error. + */ +int run_command(const char *cmd, int flag) +{ + /* + * cli_run_command can return 0 or 1 for success, so clean up + * its result. + */ + if (cli_run_command(cmd, flag) == -1) + return 1; + + return 0; +} + +/* + * Run a command using the selected parser, and check if it is repeatable. + * + * @param cmd Command to run + * @param flag Execution flags (CMD_FLAG_...) + * @return 0 (not repeatable) or 1 (repeatable) on success, -1 on error. + */ +static int run_command_repeatable(const char *cmd, int flag) +{ + return cli_run_command(cmd, flag); +} + +int run_command_list(const char *cmd, int len) +{ + (void) len; + + return cli_run_command_list(cmd); +} + +/****************************************************************************/ + + +void cli_loop(void) +{ + char *lastcommand = NULL; + int len; + int flag; + int rc = 1; + + for (;;) { + len = cli_readline(PSTR(CONFIG_SYS_PROMPT)); + + flag = 0; /* assume no special flags for now */ + if (len > 0) { + free (lastcommand); + lastcommand = strdup(console_buffer); + } else if (len == 0) + flag |= CMD_FLAG_REPEAT; + + if (len == -1) + my_puts_P(PSTR("<INTERRUPT>\n")); + else + rc = run_command_repeatable(lastcommand, flag); + + if (rc <= 0) { + /* invalid command or not repeatable, forget it */ + free(lastcommand); + lastcommand = NULL; + } + } +} + + +command_ret_t do_run(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i; + (void) cmdtp; + + if (argc < 2) + return CMD_RET_USAGE; + + for (i = 1; i < argc; ++i) { + char *arg; + + arg = getenv(argv[i]); + if (arg == NULL) { + printf_P(PSTR("## Error: \"%s\" not defined\n"), argv[i]); + return CMD_RET_FAILURE; + } + + if (run_command(arg, flag) != 0) + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + diff --git a/avr/cli_readline.c b/avr/cli_readline.c new file mode 100644 index 0000000..1dbc73b --- /dev/null +++ b/avr/cli_readline.c @@ -0,0 +1,594 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Add to readline cmdline-editing by + * (C) Copyright 2005 + * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "common.h" + +#include <string.h> +#include <stdio.h> + +#include "config.h" +#include "con-utils.h" +#include "command.h" +#include "cli_readline.h" + +char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */ + +static const FLASH char erase_seq[] = "\b \b"; /* erase sequence */ +static const FLASH char tab_seq[] = " "; /* used to expand TABs */ + +#ifndef CONFIG_CMDLINE_EDITING +static char *delete_char (char *buffer, char *p, int *colp, int *np, int plen) +{ + char *s; + + if (*np == 0) + return p; + + if (*(--p) == '\t') { /* will retype the whole line */ + while (*colp > plen) { + my_puts_P(erase_seq); + (*colp)--; + } + for (s = buffer; s < p; ++s) { + if (*s == '\t') { + my_puts_P(tab_seq + ((*colp) & 07)); + *colp += 8 - ((*colp) & 07); + } else { + ++(*colp); + putchar(*s); + } + } + } else { + my_puts_P(erase_seq); + (*colp)--; + } + (*np)--; + + return p; +} +#endif /* CONFIG_CMDLINE_EDITING */ + + +#ifdef CONFIG_CMDLINE_EDITING + +/* + * cmdline-editing related codes from vivi. + * Author: Janghoon Lyu <nandy@mizi.com> + */ + +#define putnstr(str, n) printf_P(PSTR("%.*s"), (int)n, str) + +#define CTL_CH(c) ((c) - 'a' + 1) +#define CTL_BACKSPACE ('\b') +#define DEL ((char)255) +#define DEL7 ((char)127) +#define CREAD_HIST_CHAR ('!') + +#define getcmd_putch(ch) putchar(ch) +#define getcmd_getch() my_getchar(1) +#define getcmd_cbeep() getcmd_putch('\a') + +#define HIST_MAX 5 +#define HIST_SIZE CONFIG_SYS_CBSIZE + +static int hist_max; +static int hist_add_idx; +static int hist_cur = -1; +static unsigned hist_num; + +static char *hist_list[HIST_MAX]; +static char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */ + +#define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1) + +static void hist_init(void) +{ + int i; + + hist_max = 0; + hist_add_idx = 0; + hist_cur = -1; + hist_num = 0; + + for (i = 0; i < HIST_MAX; i++) { + hist_list[i] = hist_lines[i]; + hist_list[i][0] = '\0'; + } +} + +static void cread_add_to_hist(char *line) +{ + strcpy(hist_list[hist_add_idx], line); + + if (++hist_add_idx >= HIST_MAX) + hist_add_idx = 0; + + if (hist_add_idx > hist_max) + hist_max = hist_add_idx; + + hist_num++; +} + +static char *hist_prev(void) +{ + char *ret; + int old_cur; + + if (hist_cur < 0) + return NULL; + + old_cur = hist_cur; + if (--hist_cur < 0) + hist_cur = hist_max; + + if (hist_cur == hist_add_idx) { + hist_cur = old_cur; + ret = NULL; + } else { + ret = hist_list[hist_cur]; + } + + return ret; +} + +static char *hist_next(void) +{ + char *ret; + + if (hist_cur < 0) + return NULL; + + if (hist_cur == hist_add_idx) + return NULL; + + if (++hist_cur > hist_max) + hist_cur = 0; + + if (hist_cur == hist_add_idx) + ret = ""; + else + ret = hist_list[hist_cur]; + + return ret; +} + +#ifndef CONFIG_CMDLINE_EDITING +static void cread_print_hist_list(void) +{ + int i; + unsigned n; + + n = hist_num - hist_max; + + i = hist_add_idx + 1; + while (1) { + if (i > hist_max) + i = 0; + if (i == hist_add_idx) + break; + printf_P(PSTR("%s\n"), hist_list[i]); + n++; + i++; + } +} +#endif /* CONFIG_CMDLINE_EDITING */ + +#define BEGINNING_OF_LINE() { \ + while (num) { \ + getcmd_putch(CTL_BACKSPACE); \ + num--; \ + } \ +} + +#define ERASE_TO_EOL() { \ + if (num < eol_num) { \ + printf_P(PSTR("%*S"), (int)(eol_num - num), PSTR("")); \ + do { \ + getcmd_putch(CTL_BACKSPACE); \ + } while (--eol_num > num); \ + } \ +} + +#define REFRESH_TO_EOL() { \ + if (num < eol_num) { \ + wlen = eol_num - num; \ + putnstr(buf + num, wlen); \ + num = eol_num; \ + } \ +} + +static void cread_add_char(char ichar, int insert, unsigned long *num, + unsigned long *eol_num, char *buf, unsigned long len) +{ + unsigned long wlen; + + /* room ??? */ + if (insert || *num == *eol_num) { + if (*eol_num > len - 1) { + getcmd_cbeep(); + return; + } + (*eol_num)++; + } + + if (insert) { + wlen = *eol_num - *num; + if (wlen > 1) + memmove(&buf[*num+1], &buf[*num], wlen-1); + + buf[*num] = ichar; + putnstr(buf + *num, wlen); + (*num)++; + while (--wlen) + getcmd_putch(CTL_BACKSPACE); + } else { + /* echo the character */ + wlen = 1; + buf[*num] = ichar; + putnstr(buf + *num, wlen); + (*num)++; + } +} + +static void cread_add_str(char *str, int strsize, int insert, + unsigned long *num, unsigned long *eol_num, + char *buf, unsigned long len) +{ + while (strsize--) { + cread_add_char(*str, insert, num, eol_num, buf, len); + str++; + } +} + +static int cread_line(const FLASH char *const prompt, char *buf, unsigned int *len) +{ + unsigned long num = 0; + unsigned long eol_num = 0; + unsigned long wlen; + char ichar; + int insert = 1; + int esc_len = 0; + char esc_save[8]; + int init_len = strlen(buf); + + (void) prompt; + + if (init_len) + cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len); + + while (1) { + ichar = getcmd_getch(); + + if ((ichar == '\n') || (ichar == '\r')) { + putchar('\n'); + break; + } + + /* + * handle standard linux xterm esc sequences for arrow key, etc. + */ + if (esc_len != 0) { + if (esc_len == 1) { + if (ichar == '[') { + esc_save[esc_len] = ichar; + esc_len = 2; + } else { + cread_add_str(esc_save, esc_len, + insert, &num, &eol_num, + buf, *len); + esc_len = 0; + } + continue; + } + + switch (ichar) { + case 'D': /* <- key */ + ichar = CTL_CH('b'); + esc_len = 0; + break; + case 'C': /* -> key */ + ichar = CTL_CH('f'); + esc_len = 0; + break; /* pass off to ^F handler */ + case 'H': /* Home key */ + ichar = CTL_CH('a'); + esc_len = 0; + break; /* pass off to ^A handler */ + case 'A': /* up arrow */ + ichar = CTL_CH('p'); + esc_len = 0; + break; /* pass off to ^P handler */ + case 'B': /* down arrow */ + ichar = CTL_CH('n'); + esc_len = 0; + break; /* pass off to ^N handler */ + default: + esc_save[esc_len++] = ichar; + cread_add_str(esc_save, esc_len, insert, + &num, &eol_num, buf, *len); + esc_len = 0; + continue; + } + } + + switch (ichar) { + case 0x1b: + if (esc_len == 0) { + esc_save[esc_len] = ichar; + esc_len = 1; + } else { + my_puts_P(PSTR("impossible condition #876\n")); + esc_len = 0; + } + break; + + case CTL_CH('a'): + BEGINNING_OF_LINE(); + break; + case CTL_CH('c'): /* ^C - break */ + *buf = '\0'; /* discard input */ + return -1; + case CTL_CH('f'): + if (num < eol_num) { + getcmd_putch(buf[num]); + num++; + } + break; + case CTL_CH('b'): + if (num) { + getcmd_putch(CTL_BACKSPACE); + num--; + } + break; + case CTL_CH('d'): + if (num < eol_num) { + wlen = eol_num - num - 1; + if (wlen) { + memmove(&buf[num], &buf[num+1], wlen); + putnstr(buf + num, wlen); + } + + getcmd_putch(' '); + do { + getcmd_putch(CTL_BACKSPACE); + } while (wlen--); + eol_num--; + } + break; + case CTL_CH('k'): + ERASE_TO_EOL(); + break; + case CTL_CH('e'): + REFRESH_TO_EOL(); + break; + case CTL_CH('o'): + insert = !insert; + break; + case CTL_CH('x'): + case CTL_CH('u'): + BEGINNING_OF_LINE(); + ERASE_TO_EOL(); + break; + case DEL: + case DEL7: + case 8: + if (num) { + wlen = eol_num - num; + num--; + memmove(&buf[num], &buf[num+1], wlen); + getcmd_putch(CTL_BACKSPACE); + putnstr(buf + num, wlen); + getcmd_putch(' '); + do { + getcmd_putch(CTL_BACKSPACE); + } while (wlen--); + eol_num--; + } + break; + case CTL_CH('p'): + case CTL_CH('n'): + { + char *hline; + + esc_len = 0; + + if (ichar == CTL_CH('p')) + hline = hist_prev(); + else + hline = hist_next(); + + if (!hline) { + getcmd_cbeep(); + continue; + } + + /* nuke the current line */ + /* first, go home */ + BEGINNING_OF_LINE(); + + /* erase to end of line */ + ERASE_TO_EOL(); + + /* copy new line into place and display */ + strcpy(buf, hline); + eol_num = strlen(buf); + REFRESH_TO_EOL(); + continue; + } +#ifdef CONFIG_AUTO_COMPLETE + case '\t': { + int num2, col; + + /* do not autocomplete when in the middle */ + if (num < eol_num) { + getcmd_cbeep(); + break; + } + + buf[num] = '\0'; + col = strlen_P(prompt) + eol_num; + num2 = num; + if (cmd_auto_complete(prompt, buf, &num2, &col)) { + col = num2 - num; + num += col; + eol_num += col; + } + break; + } +#endif + default: + cread_add_char(ichar, insert, &num, &eol_num, buf, + *len); + break; + } + } + *len = eol_num; + buf[eol_num] = '\0'; /* lose the newline */ + + if (buf[0] && buf[0] != CREAD_HIST_CHAR) + cread_add_to_hist(buf); + hist_cur = hist_add_idx; + + return 0; +} + +#endif /* CONFIG_CMDLINE_EDITING */ + +/****************************************************************************/ + +static int cli_readline_into_buffer(const FLASH char *const prompt, char *buffer) +{ + char *p = buffer; +#ifdef CONFIG_CMDLINE_EDITING + unsigned int len = CONFIG_SYS_CBSIZE; + int rc; + static int initted; + + if (!initted) { + hist_init(); + initted = 1; + } + + if (prompt) + my_puts_P(prompt); + + rc = cread_line(prompt, p, &len); + return rc < 0 ? rc : (int) len; + +#else /* CONFIG_CMDLINE_EDITING */ + char *p_buf = p; + int n = 0; /* buffer index */ + int plen = 0; /* prompt length */ + int col; /* output column cnt */ + char c; + + /* print prompt */ + if (prompt) { + plen = strlen_P(prompt); + my_puts_P(prompt); + } + col = plen; + + for (;;) { + + c = my_getchar(1); + + /* + * Special character handling + */ + switch (c) { + case '\r': /* Enter */ + case '\n': + *p = '\0'; + my_puts_P(PSTR("\r\n")); + return p - p_buf; + + case '\0': /* nul */ + continue; + + case 0x03: /* ^C - break */ + p_buf[0] = '\0'; /* discard input */ + return -1; + + case 0x15: /* ^U - erase line */ + while (col > plen) { + my_puts_P(erase_seq); + --col; + } + p = p_buf; + n = 0; + continue; + + case 0x17: /* ^W - erase word */ + p = delete_char(p_buf, p, &col, &n, plen); + while ((n > 0) && (*p != ' ')) + p = delete_char(p_buf, p, &col, &n, plen); + continue; + + case 0x08: /* ^H - backspace */ + case 0x7F: /* DEL - backspace */ + p = delete_char(p_buf, p, &col, &n, plen); + continue; + + default: + /* + * Must be a normal character then + */ + if (n < CONFIG_SYS_CBSIZE-2) { + if (c == '\t') { /* expand TABs */ +#ifdef CONFIG_AUTO_COMPLETE + /* + * if auto completion triggered just + * continue + */ + *p = '\0'; + if (cmd_auto_complete(prompt, + console_buffer, + &n, &col)) { + p = p_buf + n; /* reset */ + continue; + } +#endif + my_puts_P(tab_seq + (col & 07)); + col += 8 - (col & 07); + } else { + char buf[2]; + + /* + * Echo input using puts() to force an + * LCD flush if we are using an LCD + */ + ++col; + buf[0] = c; + buf[1] = '\0'; + my_puts(buf); + } + *p++ = c; + ++n; + } else { /* Buffer full */ + putchar('\a'); + } + } + } +#endif /* CONFIG_CMDLINE_EDITING */ +} + +int cli_readline(const FLASH char *const prompt) +{ + /* + * If console_buffer isn't 0-length the user will be prompted to modify + * it instead of entering it from scratch as desired. + */ + console_buffer[0] = '\0'; + + return cli_readline_into_buffer(prompt, console_buffer); +} + diff --git a/avr/cmd_boot.c b/avr/cmd_boot.c new file mode 100644 index 0000000..2c3a533 --- /dev/null +++ b/avr/cmd_boot.c @@ -0,0 +1,275 @@ + +/* + * Misc boot support + */ +#include "common.h" +#include <stdlib.h> +#include <ctype.h> +#include <util/atomic.h> + +#include "command.h" +#include "con-utils.h" +#include "z80-if.h" +#include "z180-serv.h" +#include "debug.h" + +/* ugly hack to get Z180 loadfile into flash memory */ +#define const const FLASH +#include "../z180/hdrom.h" +#undef const + + + +static void z80_load_mem(void) +{ + unsigned sec = 0; + uint32_t sec_base = hdrom_start; + + printf_P(PSTR("Loading Z180 memory... \n")); + + while (sec < hdrom_sections) { + printf_P(PSTR(" From: 0x%.5lX to: 0x%.5lX (%5li bytes)\n"), + hdrom_address[sec], + hdrom_address[sec]+hdrom_length_of_sections[sec] - 1, + hdrom_length_of_sections[sec]); + + z80_bus_cmd(Request); + z80_write_block((const FLASH unsigned char *) &hdrom[sec_base], /* src */ + hdrom_address[sec], /* dest */ + hdrom_length_of_sections[sec]); /* len */ + z80_bus_cmd(Release); + sec_base+=hdrom_length_of_sections[sec]; + sec++; + } +} + +command_ret_t do_loadf(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + (void) cmdtp; (void) flag; (void) argc; (void) argv; + + if (z80_bus_state() & ZST_RUNNING) { + printf_P(PSTR("## Can't load while CPU is running!\n")); + return CMD_RET_FAILURE; + } + + z80_load_mem(); + + return CMD_RET_SUCCESS; +} + + +command_ret_t do_busreq_pulse(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + uint16_t count=1; + + (void) cmdtp; (void) flag; + + if (!(z80_bus_state() & ZST_RUNNING)) { + printf_P(PSTR("## CPU is not running!\n")); + return CMD_RET_FAILURE; + } + + if (argc > 1) + count = (uint16_t) strtoul(argv[2], NULL, 16); + + z80_bus_cmd(Request); + while (count--) + z80_bus_cmd(M_Cycle); + + return CMD_RET_SUCCESS; +} + + +command_ret_t do_go(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + uint32_t addr; + + (void) cmdtp; (void) flag; + + if (argc < 2) + return CMD_RET_USAGE; + addr = strtoul(argv[1], NULL, 16); + if (addr >= (1UL<<16)) { + printf_P(PSTR("## Startaddress 0x%05lx too high.\n" + " (Out of logical address space (0x00000-0x0ffff))\n"), + addr); + return CMD_RET_FAILURE; + } + + if (z80_bus_state() & ZST_RUNNING) { + printf_P(PSTR("## CPU allready running!\n")); + return CMD_RET_FAILURE; + } + + printf_P(PSTR("## Starting application at 0x%04lx ...\n"), addr); + + if (addr != 0) { + uint8_t tmp[3]; + uint_fast8_t i; + + z80_bus_cmd(Request); + for (i = 0; i < 3; i++) + tmp[i] = z80_read(i); + z80_write(0, 0xc3); + z80_write(1, addr); + z80_write(2, (addr >> 8)); + + z80_bus_cmd(Run); + z80_bus_cmd(M_Cycle); + z80_bus_cmd(M_Cycle); + for (i = 0; i < 3; i++) + z80_write(i, tmp[i]); + } else + z80_bus_cmd(Run); + + 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, int flag, int argc, char * const argv[]) +{ + (void) cmdtp; (void) flag; (void) argc; (void) argv; + + 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, int flag, int argc, char * const argv[]) +{ + (void) cmdtp; (void) flag; (void) argc; (void) argv; + + reset_cpu(Restart); + + return CMD_RET_SUCCESS; +} + +static +void print_con_usage(char esc) +{ printf_P(PSTR("\n" + "------------------------------------------------\n" + " ?,H - This Help\n" + " R - Reset (Restart) CPU\n" + " Q,X - Return to command line\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, int flag, int argc, char * const argv[]) +{ + int ch; + uint8_t pending; +// uint8_t help_prompt = 0; + uint8_t code = 0; + uint8_t state = 0; + + (void) cmdtp; (void) flag; (void) argc; (void) argv; + + + while (1) { + + ATOMIC_BLOCK(ATOMIC_FORCEON) { + pending = (Stat & S_CON_PENDING) != 0; + Stat &= ~S_CON_PENDING; + } + if (pending) + while ((ch = z80_memfifo_getc(fifo_conout)) >= 0) + putchar(ch); + + if ((ch = my_getchar(0)) >= 0) { + switch (state) { + case 0: + if (ch == CONFIG_ESC_CHAR) { + state = 1; + /* TODO: Timer starten */ + } else { + z80_memfifo_putc(fifo_conin, ch); + } + break; + case 2: + printf_P(PSTR("\n" + "------------------------------------------------\n")); + case 1: + state = 0; + switch (toupper(ch)) { + + case '?': + case 'H': + print_con_usage(CONFIG_ESC_CHAR); + state = 2; + break; + + case 'R': + reset_cpu(Restart); + break; + + case 'X': + case 'Q': + printf_P(PSTR("\n")); + goto quit; + break; + + case '\\': + code = 0; + state = 3; + break; + + case CONFIG_ESC_CHAR: + z80_memfifo_putc(fifo_conin, ch); + break; + default: + 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++; + } + 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++; + } + if (state > 7) { + z80_memfifo_putc(fifo_conin, code); + state = 0; + } + break; + } + } + } +quit: + return CMD_RET_SUCCESS; +} + diff --git a/avr/cmd_date.c b/avr/cmd_date.c new file mode 100644 index 0000000..ad0d3ac --- /dev/null +++ b/avr/cmd_date.c @@ -0,0 +1,182 @@ +/* + * (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 <common.h> +#include <string.h> +#include <command.h> +#include <rtc.h> +#include <i2c.h> + + +static const char * const weekdays[] = { + "Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur", +}; + +int mk_date (const char *, struct rtc_time *); + +command_ret_t do_date(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct rtc_time tm; + int rcode = CMD_RET_SUCCESS; + + (void) cmdtp; (void) flag; + + switch (argc) { + case 2: /* set date & time */ + /* initialize tm with current time */ + rcode = rtc_get (&tm); + + if(!rcode) { + /* insert new date & time */ + if (mk_date (argv[1], &tm) != 0) { + my_puts_P(PSTR("## Bad date format\n")); + break; + } + /* and write to RTC */ + rcode = rtc_set (&tm); + if(rcode) + my_puts_P(PSTR("## Set date failed\n")); + } else { + my_puts_P(PSTR("## Get date failed\n")); + } + /* FALL TROUGH */ + case 1: /* get date & time */ + rcode = rtc_get (&tm); + + if (rcode) { + my_puts_P(PSTR("## Get date failed\n")); + break; + } + /* TODO: put weekdays[] in flash */ + printf_P(PSTR("Date: %4d-%02d-%02d (%sday) Time: %2d:%02d:%02d\n"), + tm.tm_year, tm.tm_mon, tm.tm_mday, + (tm.tm_wday<0 || tm.tm_wday>6) ? + "unknown " : weekdays[tm.tm_wday], + tm.tm_hour, tm.tm_min, tm.tm_sec); + + break; + default: + rcode = CMD_RET_USAGE; + } + + return rcode; +} + +/* + * 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 rtc_time *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; + } else if (len == 10) { /* MMDDhhmmYY */ + int year, century; + + century = tmp->tm_year / 100; + if (cnvrt2 (datestr+ 8, &year)) + return (-1); + tmp->tm_year = 100 * century + year; + } + + 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; + if (cnvrt2 (datestr+2, &val) || + val > ((tmp->tm_mon==2) ? 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; + + /* calculate day of week */ + GregorianDay (tmp); + + return (0); + default: + break; + } + + return (-1); +} + diff --git a/avr/cmd_help.c b/avr/cmd_help.c new file mode 100644 index 0000000..b42fc87 --- /dev/null +++ b/avr/cmd_help.c @@ -0,0 +1,32 @@ + +#include "common.h" +#include "command.h" + +command_ret_t do_help(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + const int len = cmd_tbl_item_count(); + return _do_help(cmd_tbl, len, cmdtp, flag, argc, argv); +} + + +#if 0 + +U_BOOT_CMD( + help, CONFIG_SYS_MAXARGS, 1, do_help, + "print command description/usage", + "\n" + " - print brief description of all commands\n" + "help command ...\n" + " - print detailed usage of 'command'" +); + +/* This does not use the U_BOOT_CMD macro as ? can't be used in symbol names */ +ll_entry_declare(cmd_tbl_t, question_mark, cmd) = { + "?", CONFIG_SYS_MAXARGS, 1, do_help, + "alias for 'help'", +#ifdef CONFIG_SYS_LONGHELP + "" +#endif /* CONFIG_SYS_LONGHELP */ +}; + +#endif diff --git a/avr/cmd_mem.c b/avr/cmd_mem.c new file mode 100644 index 0000000..500b973 --- /dev/null +++ b/avr/cmd_mem.c @@ -0,0 +1,868 @@ +/* + * (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 "common.h" +#include <stdlib.h> +#include <ctype.h> + +#include "command.h" +#include "cli_readline.h" +#include "print-utils.h" +#include "con-utils.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; + +/*--------------------------------------------------------------------------*/ + +int z180_dump_mem(uint32_t startaddr, uint32_t len, const char *title) +{ + uint8_t buf[16]; + uint8_t llen = 16; + uint8_t pre = startaddr % 16; + uint32_t addr = startaddr & ~0x0f; + len += pre; + uint8_t i; + + if (title && *title) + printf_P(PSTR("%s\n"),title); + + while (len) { + if (len < 16) + llen = len; + + z80_bus_cmd(Request); + for (i = pre; i < llen; i++) + buf[i] = z80_read(addr + i); + z80_bus_cmd(Release); + + printf_P(PSTR("%.5lx:"), addr); +#if 0 + print_blanks(3 * pre); + + /* Print hex values */ + for (i = pre; i < llen; i++) + printf_P(PSTR(" %.2x"), buf[i]); +#else + for (i = 0; i < llen; i++) { + if ((i % 8) == 0) + putchar(' '); + if (i < pre) + printf_P(PSTR(".. ")); + else + printf_P(PSTR("%.2x "), buf[i]); + } +#endif + /* fill line with whitespace for nice ASCII print */ +#if 1 + print_blanks(3 * (16u - i) + (16u-i)/8 + 1 + pre); +#else + +#endif + /* Print data in ASCII characters */ + for (i = pre; i < llen; i++) + printf_P(PSTR("%c"), isprint(buf[i]) ? buf[i] : '.'); + putchar('\n'); + + pre = 0; + addr += 16; + len -= llen; + + if (ctrlc()) + return -1; + } + return 0; +} + + +/*--------------------------------------------------------------------------*/ + +/* Memory Display + * + * Syntax: + * md {addr} {len} + */ +command_ret_t do_mem_md(cmd_tbl_t *cmdtp, int 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 = strtoul(argv[1], NULL, 16); + addr += base_address; + + /* If another parameter, it is the length to display. */ + if (argc > 2) + length = strtoul(argv[2], NULL, 16); + } + + /* Print the lines. */ + z180_dump_mem(addr, length, NULL); + + 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, int 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 = strtoul(argv[1], NULL, 16); + addr += base_address; + } + + /* Print the address, followed by value. Then accept input for + * the next value. A non-converted value exits. + */ + do { + z80_bus_cmd(Request); + data = z80_read(addr); + printf_P(PSTR("%05lx: %02x"), addr, data); + z80_bus_cmd(Release); + + nbytes = cli_readline(PSTR(" ? ")); + 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 = strtoul(console_buffer, &endp, 16); + nbytes = endp - console_buffer; + if (nbytes) { + z80_bus_cmd(Request); + z80_write(addr, data); + z80_bus_cmd(Release); + if (incrflag) + addr++; + } + } + } while (nbytes); + + mm_last_addr = addr; + return CMD_RET_SUCCESS; +} + + +command_ret_t do_mem_mm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return mod_mem (cmdtp, 1, flag, argc, argv); +} +command_ret_t do_mem_nm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return mod_mem (cmdtp, 0, flag, argc, argv); +} + +command_ret_t do_mem_mw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + uint8_t writeval; + uint32_t addr, count; + + (void) cmdtp; + (void) flag; + + if ((argc < 3) || (argc > 4)) + return CMD_RET_USAGE; + + /* Address is specified since argc > 1 + */ + addr = strtoul(argv[1], NULL, 16); + addr += base_address; + + /* Get the value to write. + */ + writeval = (uint8_t) strtoul(argv[2], NULL, 16); + + /* Count ? */ + if (argc == 4) { + count = strtoul(argv[3], NULL, 16); + } else { + count = 1; + } + + z80_bus_cmd(Request); + while (count-- > 0) { + z80_write(addr, writeval); + ++addr; + } + z80_bus_cmd(Release); + + return CMD_RET_SUCCESS; +} + +#ifdef CONFIG_MX_CYCLIC +command_ret_t do_mem_mdc ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i; + uint32_t count; + + if (argc < 4) + return CMD_RET_USAGE; + + count = strtoul(argv[3], NULL, 10); + + for (;;) { + do_mem_md (NULL, 0, 3, argv); + + /* delay for <count> ms... */ +/* TODO: use timer */ + for (i=0; i<count; i++) + udelay (1000); + + /* 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_mwc ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i; + uint32_t count; + + if (argc < 4) + return CMD_RET_USAGE; + + count = strtoul(argv[3], NULL, 10); + + for (;;) { + do_mem_mw (NULL, 0, 3, argv); + + /* delay for <count> ms... */ +/* TODO: use timer */ + for (i=0; i<count; i++) + udelay (1000); + + /* check for ctrl-c to abort... */ + if (ctrlc()) { + my_puts_P(PSTR("Abort\n")); + return CMD_RET_SUCCESS; + } + } + + return CMD_RET_SUCCESS; +} +#endif /* CONFIG_MX_CYCLIC */ + +command_ret_t do_mem_cmp(cmd_tbl_t *cmdtp, int 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 = strtoul(argv[1], NULL, 16); + addr1 += base_address; + addr2 = strtoul(argv[2], NULL, 16); + addr2 += base_address; + count = strtoul(argv[3], NULL, 16); + + for (ngood = 0; ngood < count; ++ngood) { + z80_bus_cmd(Request); + 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, int 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 = strtoul(argv[1], NULL, 16); + src += base_address; + dest = strtoul(argv[2], NULL, 16); + dest += base_address; + count = strtoul(argv[3], NULL, 16); + + 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_cmd(Request); + 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, int flag, int argc, + char * const argv[]) +{ + (void) cmdtp; + (void) flag; + + if (argc > 1) { + /* Set new base address. */ + base_address = strtoul(argv[1], NULL, 16); + } + /* 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, int 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 = strtoul(argv[1], NULL, 16); + + /* Length is the number of bytes. */ + length = strtoul(argv[2], NULL, 16); + + + /* 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_cmd(Request); + for (;;) + z80_read(addr); + z80_bus_cmd(Release); + } + + z80_bus_cmd(Request); + for (;;) { + uint32_t i = length; + uint32_t p = addr; + while (i-- > 0) + z80_read(p++); + } + z80_bus_cmd(Release); + + return CMD_RET_SUCCESS; +} + +#ifdef CONFIG_LOOPW +command_ret_t do_mem_loopw (cmd_tbl_t *cmdtp, int 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 = strtoul(argv[1], NULL, 16); + + /* Length is the number of bytes. */ + length = strtoul(argv[2], NULL, 16); + + data = strtoul(argv[3], NULL, 16); + + /* 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_cmd(Request); + for (;;) + z80_write(addr, data); + } + + for (;;) { + uint32_t i = length; + uint32_t p = addr; + while (i-- > 0) + z80_write(p++, data); + } +} +#endif /* CONFIG_LOOPW */ + +#ifdef CONFIG_CMD_MEMTEST +static uint32_t mem_test_alt(vu_long *buf, uint32_t start_addr, uint32_t end_addr, + vu_long *dummy) +{ + vu_long *addr; + uint32_t errs = 0; + uint32_t val, readback; + int j; + vu_long offset; + vu_long test_offset; + vu_long pattern; + vu_long temp; + vu_long anti_pattern; + vu_long num_words; + static const FLASH uint32_t bitpattern[] = { + 0x00000001, /* single bit */ + 0x00000003, /* two adjacent bits */ + 0x00000007, /* three adjacent bits */ + 0x0000000F, /* four adjacent bits */ + 0x00000005, /* two non-adjacent bits */ + 0x00000015, /* three non-adjacent bits */ + 0x00000055, /* four non-adjacent bits */ + 0xaaaaaaaa, /* alternating 1/0 */ + }; + + num_words = (end_addr - start_addr) / sizeof(vu_long); + + /* + * 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 = buf; + for (j = 0; j < sizeof(bitpattern) / sizeof(bitpattern[0]); j++) { + val = bitpattern[j]; + for (; val != 0; val <<= 1) { + *addr = val; + *dummy = ~val; /* clear the test data off the bus */ + readback = *addr; + if (readback != val) { + printf_P(PSTR("FAILURE (data line): " + "expected %05lx, actual %05lx\n"), + val, readback); + errs++; + if (ctrlc()) + return -1; + } + *addr = ~val; + *dummy = val; + readback = *addr; + if (readback != ~val) { + printf_P(PSTR("FAILURE (data line): " + "Is %05lx, should be %05lx\n"), + readback, ~val); + errs++; + if (ctrlc()) + return -1; + } + } + } + + /* + * 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. + */ + pattern = (vu_long) 0xaaaaaaaa; + anti_pattern = (vu_long) 0x55555555; + + debug("%s:%d: length = 0x%.5lx\n", __func__, __LINE__, num_words); + /* + * Write the default pattern at each of the + * power-of-two offsets. + */ + for (offset = 1; offset < num_words; offset <<= 1) + addr[offset] = pattern; + + /* + * Check for address bits stuck high. + */ + test_offset = 0; + addr[test_offset] = anti_pattern; + + for (offset = 1; offset < num_words; offset <<= 1) { + temp = addr[offset]; + if (temp != pattern) { + printf_P(PSTR("\nFAILURE: Address bit stuck high @ 0x%.5lx:" + " expected 0x%.5lx, actual 0x%.5lx\n"), + start_addr + offset*sizeof(vu_long), + pattern, temp); + errs++; + if (ctrlc()) + return -1; + } + } + addr[test_offset] = pattern; + + /* + * Check for addr bits stuck low or shorted. + */ + for (test_offset = 1; test_offset < num_words; test_offset <<= 1) { + addr[test_offset] = anti_pattern; + + for (offset = 1; offset < num_words; offset <<= 1) { + temp = addr[offset]; + if ((temp != pattern) && (offset != test_offset)) { + printf_P(PSTR("\nFAILURE: Address bit stuck low or" + " shorted @ 0x%.5lx: expected 0x%.5lx," + " actual 0x%.5lx\n"), + start_addr + offset*sizeof(vu_long), + pattern, temp); + errs++; + if (ctrlc()) + return -1; + } + } + addr[test_offset] = pattern; + } + + /* + * 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_words++; + + /* + * Fill memory with a known pattern. + */ + for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) { + addr[offset] = pattern; + } + + /* + * Check each location and invert it for the second pass. + */ + for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) { + temp = addr[offset]; + if (temp != pattern) { + printf_P(PSTR("\nFAILURE (read/write) @ 0x%.5lx:" + " expected 0x%.5lx, actual 0x%.5lx)\n"), + start_addr + offset*sizeof(vu_long), + pattern, temp); + errs++; + if (ctrlc()) + return -1; + } + + anti_pattern = ~pattern; + addr[offset] = anti_pattern; + } + + /* + * Check each location for the inverted pattern and zero it. + */ + for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) { + WATCHDOG_RESET(); + anti_pattern = ~pattern; + temp = addr[offset]; + if (temp != anti_pattern) { + printf_P(PSTR("\nFAILURE (read/write): @ 0x%.5lx:" + " expected 0x%.5lx, actual 0x%.5lx)\n"), + start_addr + offset*sizeof(vu_long), + anti_pattern, temp); + errs++; + if (ctrlc()) + return -1; + } + addr[offset] = 0; + } + + return 0; +} + +static uint32_t mem_test_quick(vu_long *buf, uint32_t start_addr, uint32_t end_addr, + vu_long pattern, int iteration) +{ + vu_long *end; + vu_long *addr; + uint32_t errs = 0; + uint32_t incr, length; + uint32_t val, readback; + + /* Alternate the pattern */ + incr = 1; + if (iteration & 1) { + incr = -incr; + /* + * Flip the pattern each time to make lots of zeros and + * then, the next time, lots of ones. We decrement + * the "negative" patterns and increment the "positive" + * patterns to preserve this feature. + */ + if (pattern & 0x80000000) + pattern = -pattern; /* complement & increment */ + else + pattern = ~pattern; + } + length = (end_addr - start_addr) / sizeof(uint32_t); + end = buf + length; + printf_P(PSTR("\rPattern %08lX Writing..." + "%12s" + "\b\b\b\b\b\b\b\b\b\b"), + pattern, ""); + + for (addr = buf, val = pattern; addr < end; addr++) { + *addr = val; + val += incr; + } + + my_puts_P(PSTR("Reading...")); + + for (addr = buf, val = pattern; addr < end; addr++) { + readback = *addr; + if (readback != val) { + uint32_t offset = addr - buf; + + printf_P(PSTR("\nMem error @ 0x%08X: " + "found %08lX, expected %08lX\n"), + (unsigned int)(uintptr_t)(start_addr + offset*sizeof(vu_long)), + readback, val); + errs++; + if (ctrlc()) + return -1; + } + val += incr; + } + + return 0; +} + +/* + * 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, int flag, int argc, + char * const argv[]) +{ + uint32_t start, end; + vu_long *buf, *dummy; + int iteration_limit; +/* TODO: command_ret_t */ + int ret; + uint32_t errs = 0; /* number of errors, or -1 if interrupted */ + uint32_t pattern; + int iteration; +#if defined(CONFIG_SYS_ALT_MEMTEST) + const int alt_test = 1; +#else + const int alt_test = 0; +#endif + + if (argc > 1) + start = strtoul(argv[1], NULL, 16); + else + start = CONFIG_SYS_MEMTEST_START; + + if (argc > 2) + end = strtoul(argv[2], NULL, 16); + else + end = CONFIG_SYS_MEMTEST_END; + + if (argc > 3) + pattern = (uint32_t)strtoul(argv[3], NULL, 16); + else + pattern = 0; + + if (argc > 4) + iteration_limit = (uint32_t)strtoul(argv[4], NULL, 16); + else + iteration_limit = 0; + + printf_P(PSTR("Testing %08x ... %08x:\n"), (unsigned int)start, (unsigned int)end); + debug("%s:%d: start %#05lx end %#05lx\n", __func__, __LINE__, + start, end); + +/* TODO: */ +// buf = map_sysmem(start, end - start); +// dummy = map_sysmem(CONFIG_SYS_MEMTEST_SCRATCH, sizeof(vu_long)); + for (iteration = 0; + !iteration_limit || iteration < iteration_limit; + iteration++) { + if (ctrlc()) { + errs = -1UL; + break; + } + + printf_P(PSTR("Iteration: %6d\r"), iteration + 1); + debug("\n"); + if (alt_test) { + errs = mem_test_alt(buf, start, end, dummy); + } else { + errs = mem_test_quick(buf, start, end, pattern, + iteration); + } + if (errs == -1UL) + break; + } + + if (errs == -1UL) { + /* Memory test was aborted - write a newline to finish off */ + putc('\n'); + ret = 1; + } else { + printf_P(PSTR("Tested %d iteration(s) with %lu errors.\n"), + iteration, errs); + ret = errs != 0; + } + + return ret; /* not reached */ +} +#endif /* CONFIG_CMD_MEMTEST */ diff --git a/avr/cmd_misc.c b/avr/cmd_misc.c new file mode 100644 index 0000000..315b959 --- /dev/null +++ b/avr/cmd_misc.c @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "common.h" +#include <stdlib.h> + +#include "command.h" +#include "timer.h" +#include "con-utils.h" + + +command_ret_t do_echo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + uint_fast8_t putnl = 1; + + (void) cmdtp; (void) flag; + + for (uint_fast8_t i = 1; i < argc; i++) { + + uint_fast8_t backslash = 0; + char *p = argv[i]; + char c; + + if (i != 1) + putchar(' '); + while ((c = *p++) != '\0') { + + if(backslash) { + backslash = 0; + if (c == 'c') { + putnl = 0; + continue; + } else + putchar('\\'); + } else { + if (c == '\\') { + backslash = 1; + continue; + } + } + putchar(c); + } + } + + if (putnl) + putchar('\n'); + + return CMD_RET_SUCCESS; +} + + +command_ret_t do_sleep(cmd_tbl_t *cmdtp, int 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 = strtoul(argv[1], &sp, 10); + + 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; +} + diff --git a/avr/cmd_pin.c b/avr/cmd_pin.c new file mode 100644 index 0000000..83a55f7 --- /dev/null +++ b/avr/cmd_pin.c @@ -0,0 +1,328 @@ +#include "common.h" +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "command.h" +#include "print-utils.h" +#include "getopt-min.h" +#include "env.h" +#include "pin.h" +//#include "debug.h" + + + +static const int namestr = PIN_MAX; +static char *pin_names[PIN_MAX+1]; +static uint_least8_t pin_names_width; + +static void pinnames_get(void) +{ + static const FLASH char delim1[] = {":= "}; + static const FLASH char delim2[] = {", "}; + char *lp; + char *ptr; + uint_fast8_t i; + + if (pin_names[namestr] != NULL) + free(pin_names[namestr]); + memset(pin_names, 0, sizeof(pin_names)); + pin_names_width = 0; + + if ((lp = getenv(PSTR(ENV_PINALIAS))) != NULL) { + pin_names[namestr] = strdup(lp); + ptr = strtok_P(pin_names[namestr], delim1); + while (ptr != NULL) { + if (((i = strtoul(ptr, &lp, 10)) < PIN_MAX) && + lp != ptr && + (ptr = strtok_P(NULL, delim2)) != NULL ) { + pin_names[i] = ptr; + ptr = strtok_P(NULL, delim1); + } + } + + for (i = 0; i < PIN_MAX; i++) + if (strlen(pin_names[i]) > pin_names_width) + pin_names_width = strlen(pin_names[i]); + } +} + + +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 int print_pin(int pin, int multi) +{ + int pinconf; + const FLASH char *levelp; + long div; + + pinconf = pin_config_get(pin); + if (pinconf == OUTPUT_TIMER) { + div = pin_clockdiv_get(pin); + levelp = pinlevel_str[2]; + } else + levelp = pinlevel_str[pin_read(pin)]; + + if (multi) { + printf_P(PSTR("%3d "), pin); + if (pin_names_width) { + printf_P(PSTR("%s "), pin_names[pin]); + print_blanks(pin_names_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]); + printf_P(PSTR(", ")); + my_puts_P(levelp); + + if (pinconf == OUTPUT_TIMER) + printf_P(PSTR("divide by %ld (%ldHz)"), + div, F_CPU/div); + } + printf_P(PSTR("\n")); + + return 0; +} + +static int_fast8_t pinarg_insert(int pin, uint_fast8_t count, uint_fast8_t pinarg[]) +{ + uint_fast8_t pos; + + if (pin < 0 || pin >= PIN_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_pin(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char printheader = 1; + uint_fast8_t pinarg[PIN_MAX]; + uint_fast8_t pinargc; + + (void) cmdtp; (void) flag; + + /* reset getopt() */ + optind = 1; + + 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; + + pinnames_get(); + + if (argc == 0) { + /* print cofig of all pins */ + for (pinargc = 0; pinargc < PIN_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 config */ + if (pinargc == 1) + print_pin(pinarg[0], 0); + 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); + } + return CMD_RET_SUCCESS; + } + + /* arguments must be in pairs: pins conf */ + if (argc % 2 != 1) + return CMD_RET_USAGE; + + while (argc > 0) { + char *endp; + pinmode_t mode = NONE; + int level = 0; + unsigned long value = 0; + uint8_t hz_flag = 0; + + switch (toupper(argv[optind][0])) { + case 'H': + level = 1; + 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; + 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: + pin_write(pinarg[i], level); + /* fall thru */ + case INPUT: + case INPUT_PULLUP: + pin_config(pinarg[i], mode); + break; + case OUTPUT_TIMER: + if (pin_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/command.c b/avr/command.c new file mode 100644 index 0000000..bb88794 --- /dev/null +++ b/avr/command.c @@ -0,0 +1,530 @@ +/* + * Command Processor Table + */ + +#include "common.h" +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> + +#include "config.h" +#include "print-utils.h" +#include "con-utils.h" +#ifdef CONFIG_AUTO_COMPLETE +#include "env.h" +#endif +#include "debug.h" +#include "command.h" + + +static void print_usage_line(const FLASH char *name, int width, + const FLASH char *usage) +{ + width -= strlen_P(name); + if (width < 0) + width = 0; + my_puts_P(name); + print_blanks(width); + my_puts_P(PSTR(" - ")); + my_puts_P(usage); + my_puts_P(PSTR("\n")); +} + +int strcmp_PP(const FLASH char *s1, const FLASH char *s2) +{ + unsigned char c1, c2; + + while ((c1 = *(const FLASH unsigned char *)s1++) + == (c2 = *(const FLASH unsigned char *)s2++)) + if (c1 == 0) + return 0; + + return c1 - c2; +} + +int cmpstringp(const void *p1, const void *p2) +{ + return strcmp_PP((*(const FLASH cmd_tbl_t **) p1)->name, + (*(const FLASH cmd_tbl_t **) p2)->name); +} + +int cmd_tbl_item_count(void) +{ + cmd_tbl_t * p = cmd_tbl; + int count = 0; + + while (p->name != NULL) { + p++; count++; + } + return count; +} + +/* + * Use puts() instead of printf() to avoid printf buffer overflow + * for long help messages + */ + +command_ret_t _do_help(cmd_tbl_t *cmd_start, int cmd_items, cmd_tbl_t * cmdtp, + int flag, int argc, char * const argv[]) +{ + uint_fast8_t i, max_len = 0; + command_ret_t rcode = CMD_RET_SUCCESS; + + (void) flag; + + if (argc == 1) { /*show list of commands */ + cmd_tbl_t *cmd_array[cmd_items]; + int i; + + /* Make array of commands from .uboot_cmd section */ + cmdtp = cmd_start; + for (i = 0; i < cmd_items; i++) { + cmd_array[i] = cmdtp++; + uint_fast8_t l = strlen_P(cmd_array[i]->name); + if (l > max_len) + max_len = l; + } + + /* Sort command list */ + qsort(cmd_array, cmd_items, sizeof (cmd_tbl_t *), cmpstringp); + + /* print short help (usage) */ + for (i = 0; i < cmd_items; i++) { + const FLASH char *usage = cmd_array[i]->usage; + + /* allow user abort */ + if (ctrlc ()) + return CMD_RET_FAILURE; + if (usage == NULL) + continue; +#ifdef GCC_BUG_61443 + print_usage_line(cmd_array[i]->name, max_len, usage); +#else + printf_P(PSTR("%-" stringify(8) /*FIXME*/ "S - %S\n"), + cmd_array[i]->name, usage); +#endif + } + return CMD_RET_SUCCESS; + } + /* + * command help (long version) + */ + for (i = 1; i < argc; ++i) { + if ((cmdtp = find_cmd_tbl (argv[i], cmd_start, cmd_items )) != NULL) { + rcode = cmd_usage(cmdtp); + } else { + printf_P(PSTR("Unknown command '%s' - try 'help'" + " without arguments.\n\n"), argv[i] + ); + rcode = CMD_RET_FAILURE; + } + } + return rcode; +} + +/*************************************************************************** + * find command table entry for a command + */ +cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len) +{ + cmd_tbl_t *cmdtp; + cmd_tbl_t *cmdtp_temp = table; /*Init value */ + size_t len; + uint_fast8_t n_found = 0; + + if (!cmd) + return NULL; + + len = strlen(cmd); + + for (cmdtp = table; + cmdtp != table + table_len; + cmdtp++) { + if (strncmp_P (cmd, cmdtp->name, len) == 0) { + if (len == strlen (cmdtp->name)) + return cmdtp; /* full match */ + + cmdtp_temp = cmdtp; /* abbreviated command ? */ + n_found++; + } + } + if (n_found == 1) { /* exactly one match */ + return cmdtp_temp; + } + + return NULL; /* not found or ambiguous command */ +} + + +cmd_tbl_t *find_cmd (const char *cmd) +{ + return find_cmd_tbl(cmd, cmd_tbl, cmd_tbl_item_count()); +} + + +command_ret_t cmd_usage(const FLASH cmd_tbl_t *cmdtp) +{ +// printf("%s - %s\n\n", cmdtp->name, cmdtp->usage); + print_usage_line(cmdtp->name, 0, cmdtp->usage); +#if 0 + my_puts_P(cmdtp->name); + print_blanks(/*FIXME*/ 8 - strlen_P(cmdtp->name)); + my_puts_P(PSTR(" - ")); + my_puts_P(cmdtp->usage); + my_puts_P(PSTR("\n\n")); +#endif +#ifdef CONFIG_SYS_LONGHELP +// printf("Usage:\n%s ", cmdtp->name); + my_puts_P(PSTR("Usage:\n")); + my_puts_P(cmdtp->name); + my_puts_P(PSTR(" ")); + + if (!cmdtp->help) { + my_puts_P(PSTR(" - No additional help available.\n")); + return CMD_RET_FAILURE; + } + + my_puts_P(cmdtp->help); + my_puts_P(PSTR("\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]); + if (cmdtp == NULL || cmdtp->complete == NULL) { + cmdv[0] = NULL; + return 0; + } + return (*cmdtp->complete)(argc, argv, last_char, maxv, cmdv); + } + + cmd = argv[0]; + + len = strlen(cmd); + + /* return the partial matches */ + for (; cmdtp->name[0] != '\0'; cmdtp++) { + + clen = strlen(cmdtp->name); + if (clen < len) + continue; + + if (memcmp(cmd, cmdtp->name, len) != 0) + continue; + + /* too many! */ + if (n_found >= maxv - 2) { + cmdv[n_found++] = "..."; + break; + } + + cmdv[n_found++] = cmdtp->name; + } + + cmdv[n_found] = NULL; + return n_found; +} + +static int make_argv(char *s, int argvsz, char *argv[]) +{ + int argc = 0; + + /* split into argv */ + while (argc < argvsz - 1) { + + /* skip any white space */ + while (isblank(*s)) + ++s; + + if (*s == '\0') /* end of s, no more args */ + break; + + argv[argc++] = s; /* begin of argument string */ + + /* find end of string */ + while (*s && !isblank(*s)) + ++s; + + if (*s == '\0') /* end of s, no more args */ + break; + + *s++ = '\0'; /* terminate current arg */ + } + argv[argc] = NULL; + + return argc; +} + +static void print_argv(const char *banner, const char *leader, const char *sep, int linemax, char * const argv[]) +{ + int ll = leader != NULL ? strlen(leader) : 0; + int sl = sep != NULL ? strlen(sep) : 0; + int len, i; + + if (banner) { + my_puts_P(PSTR("\n")); + my_puts(banner); + } + + i = linemax; /* force leader and newline */ + while (*argv != NULL) { + len = strlen(*argv) + sl; + if (i + len >= linemax) { + my_puts_P(PSTR("\n")); + if (leader) + my_puts(leader); + i = ll - sl; + } else if (sep) + my_puts(sep); + my_puts(*argv++); + i += len; + } + my_puts_P(PSTR("\n")); +} + +static int find_common_prefix(char * const argv[]) +{ + int i, len; + char *anchor, *s, *t; + + if (*argv == NULL) + return 0; + + /* begin with max */ + anchor = *argv++; + len = strlen(anchor); + while ((t = *argv++) != NULL) { + s = anchor; + for (i = 0; i < len; i++, t++, s++) { + if (*t != *s) + break; + } + len = s - anchor; + } + return len; +} + +static char tmp_buf[CONFIG_SYS_CBSIZE]; /* copy of console I/O buffer */ + + +int cmd_auto_complete(const FLASH char *const prompt, char *buf, int *np, int *colp) +{ + int n = *np, col = *colp; + char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ + char *cmdv[20]; + char *s, *t; + const char *sep; + int i, j, k, len, seplen, argc; + int cnt; + char last_char; + + if (strcmp_PP(prompt, CONFIG_SYS_PROMPT) != 0) + return 0; /* not in normal console */ + + cnt = strlen(buf); + if (cnt >= 1) + last_char = buf[cnt - 1]; + else + last_char = '\0'; + + /* copy to secondary buffer which will be affected */ + strcpy(tmp_buf, buf); + + /* separate into argv */ + argc = make_argv(tmp_buf, sizeof(argv)/sizeof(argv[0]), argv); + + /* do the completion and return the possible completions */ + i = complete_cmdv(argc, argv, last_char, sizeof(cmdv)/sizeof(cmdv[0]), cmdv); + + /* no match; bell and out */ + if (i == 0) { + if (argc > 1) /* allow tab for non command */ + return 0; + putchar('\a'); + return 1; + } + + s = NULL; + len = 0; + sep = NULL; + seplen = 0; + if (i == 1) { /* one match; perfect */ + k = strlen(argv[argc - 1]); + s = cmdv[0] + k; + len = strlen(s); + sep = " "; + seplen = 1; + } else if (i > 1 && (j = find_common_prefix(cmdv)) != 0) { /* more */ + k = strlen(argv[argc - 1]); + j -= k; + if (j > 0) { + s = cmdv[0] + k; + len = j; + } + } + + if (s != NULL) { + k = len + seplen; + /* make sure it fits */ + if (n + k >= CONFIG_SYS_CBSIZE - 2) { + putchar('\a'); + return 1; + } + + t = buf + cnt; + for (i = 0; i < len; i++) + *t++ = *s++; + if (sep != NULL) + for (i = 0; i < seplen; i++) + *t++ = sep[i]; + *t = '\0'; + n += k; + col += k; + my_puts(t - k); + if (sep == NULL) + putchar('\a'); + *np = n; + *colp = col; + } else { + print_argv(NULL, " ", " ", 78, cmdv); + + my_puts_P(prompt); + my_puts(buf); + } + return 1; +} + +#endif /* CONFIG_AUTO_COMPLETE */ + + + +/** + * Call a command function. This should be the only route in U-Boot to call + * a command, so that we can track whether we are waiting for input or + * executing a command. + * + * @param cmdtp Pointer to the command to execute + * @param flag Some flags normally 0 (see CMD_FLAG_.. above) + * @param argc Number of arguments (arg 0 must be the command text) + * @param argv Arguments + * @return 0 if command succeeded, else non-zero (CMD_RET_...) + */ +command_ret_t cmd_call(cmd_tbl_t *cmdtp, int 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; +} + +command_ret_t cmd_process(int 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]); + if (cmdtp == NULL) { + printf_P(PSTR("Unknown command '%s' - try 'help'\n"), argv[0]); + return CMD_RET_FAILURE; + } + if (!cmdtp->cmd) { + debug("### Command '%s' found, but ->cmd == NULL \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 OK so far, then do the command */ + if (!rc) { + rc = cmd_call(cmdtp, flag, argc, argv); + *repeatable &= cmdtp->repeatable; + } + if (rc == CMD_RET_USAGE) + rc = cmd_usage(cmdtp); + return rc; +} + +int cmd_process_error(cmd_tbl_t *cmdtp, int err) +{ + char buf[strlen_P(cmdtp->name) + 1]; + strcpy_P(buf, cmdtp->name); + + if (err) { + printf_P(PSTR("Command '%s' failed: Error %d\n"), buf, err); + return 1; + } + + return 0; +} diff --git a/avr/command_tbl.c b/avr/command_tbl.c new file mode 100644 index 0000000..336c608 --- /dev/null +++ b/avr/command_tbl.c @@ -0,0 +1,272 @@ + +#include "common.h" + +#include "command.h" +#include "cmd_mem.h" + + +extern command_ret_t do_help(cmd_tbl_t *, int, int, char * const []); +extern command_ret_t do_echo(cmd_tbl_t *, int, int, char * const []); +extern command_ret_t do_sleep(cmd_tbl_t *, int, int, char * const []); +extern command_ret_t do_env_print(cmd_tbl_t *, int, int, char * const []); +extern command_ret_t do_env_default(cmd_tbl_t *, int, int, char * const []); +extern command_ret_t do_env_set(cmd_tbl_t *, int, int, char * const []); +extern command_ret_t do_env_save(cmd_tbl_t *, int, int, char * const []); +extern command_ret_t do_loadf(cmd_tbl_t *, int, int, char * const []); +extern command_ret_t do_go(cmd_tbl_t *, int, int, char * const []); +extern command_ret_t do_restart(cmd_tbl_t *, int, int, char * const []); +extern command_ret_t do_console(cmd_tbl_t *, int, int, char * const []); +extern command_ret_t do_dump_mem(cmd_tbl_t *, int, int, char * const []); +extern command_ret_t do_eep_cp(cmd_tbl_t *, int, int, char * const []); +extern command_ret_t do_busreq_pulse(cmd_tbl_t *, int, int, char * const []); +extern command_ret_t do_date(cmd_tbl_t *, int, int, char * const []); +extern command_ret_t do_pin(cmd_tbl_t *, int, int, char * const []); + + +cmd_tbl_t cmd_tbl[] = { + +CMD_TBL_ITEM( + date, 2, 1, do_date, + "get/set/reset date & time", + "[MMDDhhmm[[CC]YY][.ss]]\ndate reset\n" + " - without arguments: print date & time\n" + " - with numeric argument: set the system date & time\n" + " - with 'reset' argument: reset the RTC" +), + +#ifdef DEBUG +CMD_TBL_ITEM( + !mdr, 3, 1, do_dump_mem, + "RAM dump", + "address [count]" +), +CMD_TBL_ITEM( + !mde, 3, 1, do_dump_mem, + "EEPROM dump", + "address [count]" +), +CMD_TBL_ITEM( + !cpe, 4, 0, do_eep_cp, + "EEPROM copy", + "source target count" +), +#endif +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, + "echo args to console", + "[args..]\n" + " - echo args to console; \\c suppresses newline" +), +CMD_TBL_ITEM( + sleep , 2, 1, do_sleep, + "delay execution for some time", + "N[m][s]\n" + " - delay execution for decimal 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_COMPLETE( + printenv, CONFIG_SYS_MAXARGS, 1, do_env_print, + "print environment variables", + "\n" + " - print values of all environment variables\n" + "printenv name ...\n" + " - print value of environment variable 'name'", + var_complete +), +CMD_TBL_ITEM_COMPLETE( + setenv, CONFIG_SYS_MAXARGS, 0, do_env_set, + "set environment variables", + "name value ...\n" + " - set environment variable 'name' to 'value ...'\n" + "setenv name\n" + " - delete environment variable 'name'", + var_complete +), +CMD_TBL_ITEM( + saveenv, 1, 0, do_env_save, + "save environment variables to persistent storage", + "" +), +CMD_TBL_ITEM( + defaultenv, 1, 0, do_env_default, + "set all environment variables to their default values", + "" +), + +CMD_TBL_ITEM( + loadf, 1, 0, do_loadf, + "load srec_cat prepared image from controller flash", + "" +), +CMD_TBL_ITEM( + go, 2, 0, do_go, + "start application at address 'addr'", + "addr\n" + " - start application at address 'addr'" +// "\n" +// " passing 'arg' as arguments" +), +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, 1, do_console, + "Connect to CPU console i/o", + "" +), + +#if 0 +CMD_TBL_ITEM( + clock, 2, 0, do_clock, + "Set or get CPU frequency", + "\n" + " - print frequency or state of clock pin\n" + "clock value[K|M]\n" + " - set frequency of clock pin to value\n" + "clock [high|low]\n" + " - set clock pin level high or low" +), +CMD_TBL_ITEM( + clk2, 3, 0, do_clock2, + "Set or get clk2 frequency", + "\n" + " - print frequency or state of clk2 pin\n" + "clk2 [-d] value[K|M]\n" + " - set frequency of clk2 pin to value\n" + "clk2 [high|low]\n" + " - set clk2 pin level high or low" +), +#endif + +CMD_TBL_ITEM( + pin, CONFIG_SYS_MAXARGS, 0, do_pin, + "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, 1, do_mem_md, + "memory display", + "address [# of objects]" +), +CMD_TBL_ITEM( + mm, 2, 1, do_mem_mm, + "memory modify (auto-incrementing address)", + "address" +), +CMD_TBL_ITEM( + nm, 2, 1, do_mem_nm, + "memory modify (constant address)", + "address" +), +CMD_TBL_ITEM( + mw, 4, 1, do_mem_mw, + "memory write (fill)", + "address value [count]" +), +CMD_TBL_ITEM( + cp, 4, 1, do_mem_cp, + "memory copy", + "source target count" +), +CMD_TBL_ITEM( + cmp, 4, 1, do_mem_cmp, + "memory compare", + "addr1 addr2 count" +), +CMD_TBL_ITEM( + base, 2, 1, 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( + loop, 3, 1, do_mem_loop, + "infinite loop on address range", + "address number_of_bytes" +), +#ifdef CONFIG_LOOPW +CMD_TBL_ITEM( + loopw, 4, 1, do_mem_loopw, + "infinite write loop on address range", + "address number_of_bytes data_to_write" +), +#endif /* CONFIG_LOOPW */ + +#ifdef CONFIG_CMD_MEMTEST +CMD_TBL_ITEM( + mtest, 5, 1, do_mem_mtest, + "simple RAM read/write test", + "[start [end [pattern [iterations]]]]" +), +#endif /* CONFIG_CMD_MEMTEST */ + +#ifdef CONFIG_MX_CYCLIC +CMD_TBL_ITEM( + mdc, 4, 1, do_mem_mdc, + "memory display cyclic", + "address count delay(ms)" +), +CMD_TBL_ITEM( + mwc, 4, 1, do_mem_mwc, + "memory write cyclic", + "address value delay(ms)" +), +#endif /* CONFIG_MX_CYCLIC */ + + +CMD_TBL_ITEM( + help, CONFIG_SYS_MAXARGS, 1, do_help, + "print command description/usage", + "\n" + " - print brief description of all commands\n" + "help command ...\n" + " - print detailed usage of 'command'" +), + +/* This does not use the 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 */ +#ifdef CONFIG_AUTO_COMPLETE + 0, +#endif +}, +/* Mark end of table */ +{ 0 }, +}; diff --git a/avr/con-utils.c b/avr/con-utils.c new file mode 100644 index 0000000..b8017ed --- /dev/null +++ b/avr/con-utils.c @@ -0,0 +1,95 @@ + +#include <string.h> +#include "common.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); + + 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) +{ + 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/date.c b/avr/date.c new file mode 100644 index 0000000..c85361f --- /dev/null +++ b/avr/date.c @@ -0,0 +1,139 @@ +/* + * (C) Copyright 2001 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for RTC + */ + +#include <common.h> +#include <command.h> +#include <rtc.h> + + +#define FEBRUARY 2 +#define STARTOFTIME 1970 +#define SECDAY 86400L +#define SECYR (SECDAY * 365) +#define leapyear(year) ((year) % 4 == 0) +#define days_in_year(a) (leapyear(a) ? 366 : 365) +#define days_in_month(a) (month_days[(a) - 1]) + + +static const FLASH int MonthOffset[] = { + 0,31,59,90,120,151,181,212,243,273,304,334 +}; + +/* + * This only works for the Gregorian calendar - i.e. after 1752 (in the UK) + */ +void GregorianDay(struct rtc_time * tm) +{ + int leapsToDate; + int lastYear; + int day; + + lastYear=tm->tm_year-1; + + /* + * Number of leap corrections to apply up to end of last year + */ + leapsToDate = lastYear/4 - lastYear/100 + lastYear/400; + + /* + * This year is a leap year if it is divisible by 4 except when it is + * divisible by 100 unless it is divisible by 400 + * + * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be + */ + if((tm->tm_year%4==0) && + ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) && + (tm->tm_mon>2)) { + /* + * We are past Feb. 29 in a leap year + */ + day=1; + } else { + day=0; + } + + day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + tm->tm_mday; + + tm->tm_wday=day%7; +} + +void to_tm(unsigned long tim, struct rtc_time * tm) +{ + char month_days[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; + register int i; + register long hms, day; + + day = tim / SECDAY; + hms = tim % SECDAY; + + /* Hours, minutes, seconds are easy */ + tm->tm_hour = hms / 3600; + tm->tm_min = (hms % 3600) / 60; + tm->tm_sec = (hms % 3600) % 60; + + /* Number of years in days */ + for (i = STARTOFTIME; day >= days_in_year(i); i++) { + day -= days_in_year(i); + } + tm->tm_year = i; + + /* Number of months in days left */ + if (leapyear(tm->tm_year)) { + days_in_month(FEBRUARY) = 29; + } + for (i = 1; day >= days_in_month(i); i++) { + day -= days_in_month(i); + } + days_in_month(FEBRUARY) = 28; + tm->tm_mon = i; + + /* Days are what is left over (+1) from all that. */ + tm->tm_mday = day + 1; + + /* + * Determine the day of week + */ + GregorianDay(tm); +} + +/* Converts Gregorian date to seconds since 1970-01-01 00:00:00. + * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 + * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. + * + * [For the Julian calendar (which was used in Russia before 1917, + * Britain & colonies before 1752, anywhere else before 1582, + * and is still in use by some communities) leave out the + * -year/100+year/400 terms, and add 10.] + * + * This algorithm was first published by Gauss (I think). + * + * WARNING: this function will overflow on 2106-02-07 06:28:16 on + * machines were long is 32-bit! (However, as time_t is signed, we + * will already get problems at other places on 2038-01-19 03:14:08) + */ +unsigned long +mktime (unsigned int year, unsigned int mon, + unsigned int day, unsigned int hour, + unsigned int min, unsigned int sec) +{ + if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */ + mon += 12; /* Puts Feb last since it has leap day */ + year -= 1; + } + + return ((( + (unsigned long) (year/4 - year/100 + year/400 + 367*mon/12 + day) + + year*365 - 719499 + )*24 + hour /* now have hours */ + )*60 + min /* now have minutes */ + )*60 + sec; /* finally seconds */ +} + diff --git a/avr/debug.c b/avr/debug.c new file mode 100644 index 0000000..47b11b0 --- /dev/null +++ b/avr/debug.c @@ -0,0 +1,230 @@ +#include "common.h" +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <avr/eeprom.h> + +#include "command.h" +#include "debug.h" + +/* + * Debugging + */ +#ifdef DEBUG + +static void print_blanks(uint_fast8_t count) +{ + while(count--) + putchar(' '); +} + +static uint8_t ram_read_byte(const uint8_t *p) +{ + return *p; +} + +void dump_mem(const uint8_t *startaddr, int len, + uint8_t (*readfkt)(const uint8_t *), char *title) +{ + uint8_t buf[16]; + char *indent = NULL; + uint8_t llen = 16; + uint8_t pre = (size_t) startaddr % 16; + const uint8_t *addr = (uint8_t *) ((size_t) startaddr & ~0x0f); + len += pre; + uint8_t i; + + if (title && *title) { + printf_P(PSTR("%s\n"),title); + indent = " "; + } + + while (len) { + if (len < 16) + llen = len; + + for (i = pre; i < llen; i++) + buf[i] = readfkt(addr + i); + + printf_P(PSTR("%s%04x:"),indent, addr); + for (i = 0; i < llen; i++) { + if ((i % 8) == 0) + putchar(' '); + if (i < pre) + printf_P(PSTR(".. ")); + else + printf_P(PSTR("%.2x "), buf[i]); + } + /* 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]) ? buf[i] : '.'); + putchar('\n'); + + pre = 0; + addr += 16; + len -= llen; + } +} + +void dump_eep(const uint8_t *addr, unsigned int len, char *title) +{ + dump_mem(addr, len, eeprom_read_byte, title); +} + +void dump_ram(const uint8_t *addr, unsigned int len, char *title) +{ + dump_mem(addr, len, ram_read_byte, title); +} + + +#if 0 +void dump_heap(void) +{ + extern unsigned int __brkval; + + dump_ram((uint8_t *) __malloc_heap_start, + __brkval - (unsigned int) __malloc_heap_start, + "=== Heap:"); +} +#endif + + +/* + * Memory Display + * md addr {len} + */ +command_ret_t do_dump_mem(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +// static const uint8_t *addr; +// static uint16_t length = 128; + uint8_t (*readhow)(const uint8_t *); + + (void) cmdtp; (void) flag; + + if (argc < 2) + return CMD_RET_USAGE; + + const uint8_t *addr; + uint16_t length = 128; + + if (strchr(argv[0],'r') != NULL) + readhow = ram_read_byte; + else if (strchr(argv[0],'e') != NULL) + readhow = eeprom_read_byte; + else + return CMD_RET_USAGE; + + /* Address is specified since argc > 1 */ + addr = (const uint8_t *) (size_t) strtoul(argv[1], NULL, 16); + + /* If another parameter, it is the length to display. */ + if (argc > 2) + length = (uint16_t) strtoul(argv[2], NULL, 16); + + /* Print the lines. */ + dump_mem(addr, length, readhow, NULL); + + return CMD_RET_SUCCESS; +} + +command_ret_t do_eep_cp(cmd_tbl_t *cmdtp, int 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) strtoul(argv[1], NULL, 16); + dest = (size_t) strtoul(argv[2], NULL, 16); + count = (size_t) strtoul(argv[3], NULL, 16); + + 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; +} + +/*------------------------------------------------------------------------------*/ + + +#if 1 + +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) + +void +printfreelist(const char * title) +{ + struct __freelist *fp1; + int i; + unsigned int freesum = 0; + +/* TODO: printf_P */ + + if (!__flp) { + printf("%s no free list\n", title ? title : ""); + } else { + printf("Free list: %s\n", title ? title : ""); + for (i = 0, fp1 = __flp; fp1; i++, fp1 = fp1->nx) { + printf(" entry %d @ %04x: size %4u, next ", + i, (size_t)fp1, fp1->sz); + if (fp1->nx) + printf("%04x\n", (size_t)fp1->nx); + else + printf("NULL\n"); + freesum += fp1->sz; + } + } + + freesum += (size_t) STACK_POINTER() - __malloc_margin - (size_t) __brkval; + + printf("SP: %04x, __brkval: %04x, Total free: %04u\n", + (size_t) STACK_POINTER(), (size_t) __brkval, freesum); +} + +#endif + +#endif /* DEBUG */ + diff --git a/avr/env.c b/avr/env.c new file mode 100644 index 0000000..017053c --- /dev/null +++ b/avr/env.c @@ -0,0 +1,696 @@ +#include "common.h" +#include <string.h> +#include <stdlib.h> +#include <avr/eeprom.h> + +#include "config.h" +#include "debug.h" +#include "xmalloc.h" +#include "crc.h" +#include "command.h" + +#include "env.h" + + +#define ENV_SIZE (CONFIG_ENV_SIZE - sizeof(uint16_t) -1) +#define ACTIVE_FLAG 1 +#define OBSOLETE_FLAG 0 + +#define ENVLIST_DELETE (1<<0) + + +/* + * Default Environment + */ + +#define DELIM "\0" + +const FLASH char default_env[] = { + "bootdelay=" "3" DELIM + "bootcmd=" "reset; loadf; go ${startaddr}" DELIM + "baudrate=" "115200" DELIM + "startaddr=" "0" DELIM + DELIM +}; + +/* EEPROM storage */ +typedef struct environment_s { + uint16_t crc; /* CRC16 over data bytes */ + uint8_t flags; /* active/obsolete flags */ + char data[ENV_SIZE]; /* Environment data */ +} env_t; + + +/* */ +typedef struct env_item_s { + char * envvar; +} env_item_t; + + +static uint8_t env_valid; +static env_item_t env_list[CONFIG_ENVVAR_MAX]; +static int entrycount; + + +static +char env_get_char(uint16_t index) +{ + unsigned int off = CONFIG_ENV_OFFSET; + char ret; + + switch (env_valid) { + case 2: + off += CONFIG_ENV_SIZE; + 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 const FLASH char *comp_key; + +static +int comp_env_items(const void *m1, const void *m2) +{ + env_item_t *ep1 = (env_item_t *) m1; + env_item_t *ep2 = (env_item_t *) m2; + + if (ep1 == NULL) + return - strcmp_P(ep2->envvar, comp_key); + else + return strcmp(ep1->envvar, ep2->envvar); +} + + +env_item_t *envlist_search(const MEMX char *name) +{ +#ifdef __MEMX + if (__builtin_avr_flash_segment(name) != -1) { + comp_key = name; + return bsearch(0, env_list, entrycount, + sizeof(env_item_t), comp_env_items); + } else { + + env_item_t e; + e.envvar = (char *) name; + + return bsearch(&e, env_list, entrycount, + sizeof(env_item_t), comp_env_items); + } +#else + env_item_t e; + e.envvar = (char *) name; + + return bsearch(&e, env_list, entrycount, + sizeof(env_item_t), comp_env_items); +#endif /* __MEMX */ +} + + +static +env_item_t *envlist_enter(env_item_t *e) +{ + const size_t size = sizeof(env_item_t); + env_item_t *ep; + + ep = bsearch(e, env_list, entrycount, + size, comp_env_items); + + if (ep == NULL) { + if (entrycount < CONFIG_ENVVAR_MAX) { + + env_list[entrycount++] = *e; + qsort(env_list, entrycount, size, comp_env_items); + ep = bsearch(e, env_list, entrycount, + size, comp_env_items); + } + } else { + free(ep->envvar); + ep->envvar = e->envvar; + } + + return ep; +} + + +static +int env_item_delete(env_item_t *ep) +{ + if (entrycount == 0) + return -1; + + free(ep->envvar); + + entrycount--; + size_t size = sizeof(env_item_t); + memmove(ep, ep + 1, (env_list - ep)*size + entrycount*size); + + return 0; +} + +static +int envlist_delete(const MEMX char *name) +{ + env_item_t *ep = envlist_search(name); + + if (ep != NULL) + return env_item_delete(ep); + + return 1; +} + + + +static +int envlist_import(uint8_t flags) +{ + uint16_t index; + + int len; + env_item_t e; + char *np, c; + uint_fast8_t ef; + + if ((flags & ENVLIST_DELETE) != 0) + while (entrycount != 0) + env_item_delete(&env_list[entrycount-1]); + + for (index = 0; env_get_char(index) != '\0'; ) { + for (len = 0; env_get_char(index + len) != '\0'; ++len) { + if ((index + len) >= ENV_SIZE) + return -1; + } + + np = (char *) xmalloc(len+1); + if (np == NULL) { + printf_P(PSTR("## Can't malloc %d bytes\n"), len+1); + return 1; + } + e.envvar = np; + ef = 0; + while ((c = env_get_char(index++)) != '\0') { + if (!ef && (c == '=')) { + *np = '\0'; + ef = 1; + } else + *np = c; + np++; + } + *np = '\0'; + envlist_enter(&e); + } + + + 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(const MEMX char *name) +{ + env_item_t *ep; + char *ret = NULL; + + ep = envlist_search(name); + if (ep != NULL) + ret = ep->envvar + strlen(ep->envvar) +1; + return ret; +} + +static +int env_item_save(env_item_t *ep, uint16_t offset, int space_left) +{ + int nlen, vlen; + char *var = ep->envvar; + + nlen = strlen(var); + if (nlen == 0) + return 0; + vlen = strlen(var + nlen + 1); + if (vlen == 0) + return 0; + if (nlen + vlen + 2 > space_left) + return 0; + + eeprom_update_block(var, (uint8_t *) offset, nlen); + offset += nlen; + eeprom_update_byte((uint8_t *) offset++, '='); + eeprom_update_block(var + nlen +1, (uint8_t *) offset, vlen+1); + + return nlen + vlen + 2; +} + + +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; + for (int i = 0 ; i < entrycount; i++) { + len = env_item_save(&env_list[i], pos, left); + if (len == 0) { + return 1; + } + pos += len; + left -= len; + } + /* 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(env_item_t *ep) +{ + int len; + char *ev = ep->envvar; + + len = printf_P(PSTR("%s="), ev); + len += printf_P(PSTR("%s\n"), ev + len); + + 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 MEMX char *name) +{ + int len = -1; + + if (name) { /* print a single name */ + + env_item_t *ep = envlist_search(name); + if (ep != NULL) + len = env_item_print(ep); + + } else { /* print whole list */ + len = 0; + for (int i = 0 ; i < entrycount; i++) { + len += env_item_print(&env_list[i]); + } + } + return len; +} + + +/** + * 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(int flag, int argc, char * const argv[]) +{ + int i, len; + char *name, *value, *valp, *p; + env_item_t e, *ep; + + (void) flag; + debug("Initial value for argc=%d\n", argc); + + name = argv[1]; + value = argv[2]; + + if (strchr(name, '=')) { + printf_P(PSTR("## Error: illegal character '='" + "in variable name \"%s\"\n"), name); + return CMD_RET_FAILURE; + } + 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; + } +/* + env_id++; +*/ + /* Delete only ? */ + if (argc < 3 || argv[2] == NULL) { + int rc = envlist_delete(name); + return rc ? CMD_RET_FAILURE : CMD_RET_SUCCESS; + } + + /* + * Insert / replace new value + */ + for (i = 2, len += 1; i < argc; ++i) + len += strlen(argv[i]) + 1; + + value = xmalloc(len); + if (value == NULL) { + printf_P(PSTR("## Can't malloc %d bytes\n"), len); + return CMD_RET_FAILURE; + } + strcpy(value, name); + valp = value + strlen(name) + 1; + for (i = 2, p = valp; i < argc; ++i) { + char *v = argv[i]; + + while ((*p++ = *v++) != '\0') + ; + *(p - 1) = ' '; + } + if (p != valp) + *--p = '\0'; + + e.envvar = value; + ep = envlist_enter(&e); + if (!ep) { + printf_P(PSTR("## Error inserting \"%s\" variable.\n"), + name); + return CMD_RET_FAILURE; + } + + 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 + */ +static +int setenv(const char *varname, const char *varvalue) +{ + const char * const argv[3] = { NULL, varname, varvalue }; + int argc = 3; + + if (varvalue == NULL || varvalue[0] == '\0') + --argc; + + return (int) _do_env_set(0, argc, (char * const *)argv); +} + +/** + * 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; + + env_item_t *ep = envlist_search(name); + + if (ep != NULL ) { + vp = ep->envvar + strlen(ep->envvar) +1; + value = strtoul(vp, &endp, base); + if (endp != vp) + return value; + } + + return default_val; +} + + +command_ret_t do_env_print(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + command_ret_t rc = CMD_RET_SUCCESS; + + (void) cmdtp; (void) flag; + + if (argc == 1) { + /* print all env vars */ + int size = env_print(NULL); + if (size < 0) + return CMD_RET_FAILURE; + printf_P(PSTR("\nEnvironment size: %d/%d bytes\n"), + size, ENV_SIZE); + return CMD_RET_SUCCESS; + } + + /* print selected env vars */ + for (int i = 1; i < argc; ++i) { + int rc = env_print(argv[i]); + if (rc < 0) { + printf_P(PSTR("## Error: \"%s\" not defined\n"), argv[i]); + rc = CMD_RET_FAILURE; + } + } + + return rc; +} + + +command_ret_t do_env_set(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + (void) cmdtp; + + if (argc < 2) + return CMD_RET_USAGE; + + return _do_env_set(flag, argc, argv); +} + + +command_ret_t do_env_default(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + (void) cmdtp; (void) flag; (void) argc; (void) argv; + + 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, int flag, int argc, char * const argv[]) +{ + (void) cmdtp; (void) flag; (void) argc; (void) argv; + + 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 diff --git a/avr/getopt-min.c b/avr/getopt-min.c new file mode 100644 index 0000000..8e5dd6c --- /dev/null +++ b/avr/getopt-min.c @@ -0,0 +1,75 @@ +#include "common.h" + + +/* + * 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 <string.h> + +int optind = 1; /* 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 = 1; /* position within argument */ + int osp; /* saved `sp' for param test */ + int c; /* option letter */ + const FLASH char *cp; /* -> option in `optstring' */ + + optarg = NULL; + + 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/i2c.c b/avr/i2c.c new file mode 100644 index 0000000..d181ff6 --- /dev/null +++ b/avr/i2c.c @@ -0,0 +1,376 @@ + +/* + * I2C (TWI) master interface. + */ + +#include "common.h" +#include <avr/interrupt.h> +#include <string.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..2f9a62f --- /dev/null +++ b/avr/main.c @@ -0,0 +1,252 @@ +/* + */ + + +#include "common.h" + +#include <avr/interrupt.h> +#include <stdlib.h> +#include <stdio.h> + +#include "config.h" +#include "debug.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" + +static uint8_t mcusr; + +/*--------------------------------------------------------------------------*/ +#if DEBUG + +__attribute__ ((naked)) __attribute__ ((section (".init3"))) +void preset_ram (void) +{ + for (uint8_t *p = RAMSTART; p <= (uint8_t *) RAMEND; p++) + *p = 0xdd; + +} + +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; + + printf_P(PSTR("### Reset reason(s): %s"), r ? "" : "none"); + for ( ; r; p++, r >>= 1) { + if (r & 1) { + my_puts_P(*p); + if (r & ~1) + printf_P(PSTR(", ")); + } + } + printf_P(PSTR(".\n")); +} + +#endif + +ISR(INT5_vect) +{ + Stat |= S_MSG_PENDING; +} + +ISR(INT6_vect) +{ + Stat |= S_CON_PENDING; +} + +static +void setup_avr(void) +{ + /* save and clear reset reason(s) */ + /* TODO: move to init section? */ + mcusr = MCUSR; + MCUSR = 0; + + /* WD */ + + /* 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; + + /* Timer */ + PRR1 &= ~_BV(PRTIM3); + OCR3A = F_CPU / 1000 - 1; /* Timer3: 1000Hz interval (OC3A) */ + TCCR3B = (0b01<<WGM32)|(0b001<<CS30); /* CTC Mode, Prescaler 1 */ + TIMSK3 = _BV(OCIE3A); /* Enable TC2.oca interrupt */ + + /* 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; +} + +/*--------------------------------------------------------------------------*/ + +/* 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(PSTR(ENV_BOOTCMD)); + stored_bootdelay = bootdelay; + return s; +} + +static +void autoboot_command(const char *s) +{ + debug("### main_loop: bootcmd=\"%s\"\n", s ? s : PSTR("<UNDEFINED>")); + _delay_ms(20); + + if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) { + run_command_list(s, -1); + } +} + + +static +void main_loop(void) +{ + const char *s; + + s = bootdelay_process(); + autoboot_command(s); + cli_loop(); +} + +int main(void) +{ + + setup_avr(); + z80_setup_bus(); + + env_init(); + + 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 + +#if DEBUG + unsigned long i_speed = getenv_ulong(PSTR("i2c_clock"), 10, CONFIG_SYS_I2C_CLOCK); + debug("### Setting I2C clock Frequency to %lu Hz.\n", i_speed); + i2c_init(i_speed); +#else + i2c_init(CONFIG_SYS_I2C_CLOCK); +#endif + + printf_P(PSTR("\nATMEGA1281+Z8S180 Stamp Monitor\n\n")); + + setup_z180_serv(); + + main_loop(); +} diff --git a/avr/pcf8583.c b/avr/pcf8583.c new file mode 100644 index 0000000..af1331d --- /dev/null +++ b/avr/pcf8583.c @@ -0,0 +1,100 @@ +/* + * Date & Time support for Philips PCF8583 RTC + */ + +#include "common.h" +#include <stdlib.h> +#include "debug.h" +#include "command.h" +#include "rtc.h" +#include "i2c.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 rtc_time *tmp) +{ + int rel = 0; + uint8_t rtcbuf[NR_OF_REGS]; + uint16_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, year/date: %02x, wdays/month: %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); + while (year%4 < (rtcbuf[REG_YRDATE]>>6)) { + year++; + /* TODO: update RTC ram */ + } + tmp->tm_year = year; + 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 rel; +} + +int rtc_set (struct rtc_time *tmp) +{ + uint8_t rtcbuf[NR_OF_REGS]; + + 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) | ((tmp->tm_wday) << 5); + rtcbuf[REG_YRDATE] = ((tmp->tm_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 *) &tmp->tm_year, 2); + rtcbuf[REG_CS] = 0x04; + i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, rtcbuf, 1); + + + return 0; +} diff --git a/avr/pin.c b/avr/pin.c new file mode 100644 index 0000000..6e88aa5 --- /dev/null +++ b/avr/pin.c @@ -0,0 +1,372 @@ +#include "common.h" +#include <util/atomic.h> +#include <limits.h> +#include "debug.h" +#include "pin.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[PIN_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 pin_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 pin_config(int pin, pinmode_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: + pin_timer_off(pinlist[pin].timer); + ATOMIC_BLOCK(ATOMIC_FORCEON) { + p->ddr &= ~bit; + p->pout &= ~bit; + } + break; + case INPUT_PULLUP: + pin_timer_off(pinlist[pin].timer); + ATOMIC_BLOCK(ATOMIC_FORCEON) { + p->ddr &= ~bit; + p->pout |= bit; + } + break; + case OUTPUT: + pin_timer_off(pinlist[pin].timer); + case OUTPUT_TIMER: + ATOMIC_BLOCK(ATOMIC_FORCEON) { + p->ddr |= bit; + } + break; + default: + /* Invalid pin mode */ + return -1; + } + } + return 0; +} + +void pin_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 pin_read(int pin) +{ + port_t *p = pinlist[pin].adr; + uint8_t bit = pinlist[pin].mask; + + return (p->pin & bit) != 0; +} + +pinmode_t pin_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 pin_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 (pin_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 pin_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 */ + + pin_config(pin, OUTPUT_TIMER); + + return 0; +} + diff --git a/avr/print-utils.c b/avr/print-utils.c new file mode 100644 index 0000000..b814d97 --- /dev/null +++ b/avr/print-utils.c @@ -0,0 +1,10 @@ +#include <stdio.h> +#include "print-utils.h" + +void print_blanks(uint_fast8_t count) +{ + while(count--) + putchar(' '); +} + + diff --git a/avr/serial.c b/avr/serial.c new file mode 100644 index 0000000..e897c84 --- /dev/null +++ b/avr/serial.c @@ -0,0 +1,123 @@ +/* + */ + +#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; + + /* Initialize ring buffers. */ + ring_init(&rx_ring, rx_ring_buffer, BUFFER_SIZE); + ring_init(&tx_ring, tx_ring_buffer, BUFFER_SIZE); + + UCSR0A = 0; + UBRR0 = F_CPU / baud / 16 - 1; + UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0); + UCSR0C = 3 << UCSZ00; + }; +} + +/*--------------------------------------------------------------------------*/ + +/* 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/timer.c b/avr/timer.c new file mode 100644 index 0000000..67b4a49 --- /dev/null +++ b/avr/timer.c @@ -0,0 +1,86 @@ +/* + */ + + +#include "common.h" + +#include <avr/interrupt.h> +#include <util/atomic.h> + +//#include <stdio.h> + + +#include "timer.h" + +/* timer interrupt/overflow counter */ +volatile uint32_t timestamp; + + +/*---------------------------------------------------------*/ +/* 1000Hz timer interrupt generated by OC3A */ +/*---------------------------------------------------------*/ + +ISR(TIMER3_COMPA_vect) +{ + static int_fast8_t tick_10ms; + int_fast8_t i; + + + timestamp++; + + i = tick_10ms + 1; + if (i == 10) { + i = 0; + Stat |= S_10MS_TO; + + /* Drive timer procedure of low level disk I/O module */ + //disk_timerproc(); + } + tick_10ms = i; + +} + + + +/*--------------------------------------------------------------------------*/ + +#if 0 + +void do_10ms(void) +{ + if (to_counter) + to_counter--; +} + +#endif + +/*--------------------------------------------------------------------------*/ + + +#if 0 +void timer_setup(void) +{ + + /* Clock */ + CLKPR = _BV(CLKPCE); + CLKPR = 0; + + /* Timer */ + + OCR1A = F_CPU / 1000 - 1; // Timer1: 1000Hz interval (OC1A) + TCCR1B = 0b00001001; + TIMSK1 = _BV(OCIE1A); // Enable TC1.oca interrupt +} +#endif + +uint32_t get_timer(uint32_t base) +{ + uint32_t ret; + + ATOMIC_BLOCK(ATOMIC_FORCEON) + { + ret = timestamp; + } + return ret - base; +} + diff --git a/avr/xmalloc.c b/avr/xmalloc.c new file mode 100644 index 0000000..9bf2684 --- /dev/null +++ b/avr/xmalloc.c @@ -0,0 +1,27 @@ +#include <stdlib.h> + +#include "debug.h" +#include "xmalloc.h" + +void* xmalloc(size_t size) +{ + void *p; + + p = malloc(size); + + if (p == NULL) + debug("*** Out of memory!\n"); + + return p; +} + + +void* xrealloc(void *p, size_t size) +{ + p = realloc(p, size); + + if (p == NULL) + debug("*** Out of memory!\n"); + + return p; +} diff --git a/avr/z180-serv.c b/avr/z180-serv.c new file mode 100644 index 0000000..a920465 --- /dev/null +++ b/avr/z180-serv.c @@ -0,0 +1,288 @@ +#include "common.h" +#include <util/atomic.h> + +#include "background.h" +#include "serial.h" +#include "z80-if.h" +#include "debug.h" +#include "z180-serv.h" + + + +/*--------------------------------------------------------------------------*/ + + +uint8_t z80_get_byte(uint32_t adr) +{ + uint8_t data; + + z80_bus_cmd(Request); + data = z80_read(adr); + z80_bus_cmd(Release); + + return data; +} + + +/*--------------------------------------------------------------------------*/ + +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; +} + + +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++); +} + + +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}, + { 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) +{ + uint8_t pending; + + ATOMIC_BLOCK(ATOMIC_FORCEON) { + pending = (Stat & S_MSG_PENDING) != 0; + Stat &= ~S_MSG_PENDING; + } + + if (pending) { + switch (state) { + case 0: + z80_bus_cmd(Request); + uint32_t addr = z80_read(0x40) + + ((uint16_t) z80_read(0x41) << 8) + + ((uint32_t) z80_read(0x42) << 16); + z80_bus_cmd(Release); + if (addr != 0) { + z80_memfifo_init(fifo_msgin, addr); + state = 1; + } + break; + case 1: + check_msg_fifo(); + break; + } + } + + return state; +} + + +static int handle_msg_handling; + +void setup_z180_serv(void) +{ + + handle_msg_handling = bg_register(msg_handling, 0); +} + +void restart_z180_serv(void) +{ + z80_bus_cmd(Request); + z80_write(0x40, 0); + z80_write(0x41, 0); + z80_write(0x42, 0); + 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 +void dump_mem(const FLASH uint8_t *addr, uint32_t len) +{ + DBG_P(1, "hdrom dump:"); + while (len) { + DBG_P(1, "\n %.5x:", addr); + for (unsigned i = 0; i<16; i++) + DBG_P(1, " %.2x", *addr++); + len -= len > 16 ? 16 : len; + } + DBG_P(1, "\n"); +} +#endif +/*--------------------------------------------------------------------------*/ + + +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) +}; + + + diff --git a/avr/z180-stamp-avr.c b/avr/z180-stamp-avr.c new file mode 100644 index 0000000..e6edb33 --- /dev/null +++ b/avr/z180-stamp-avr.c @@ -0,0 +1,591 @@ +/* + */ + + +#include <avr/io.h> +//#include <avr/power.h> +//#include <avr/pgmspace.h> +#include <avr/interrupt.h> +//#include <util/atomic.h> +//#include <avr/sleep.h> +//#include <string.h> + +#include <stdio.h> + + +#include "debug.h" +#include "serial.h" +#include "z80-if.h" + +#define const const __flash +#include "../z180/hdrom.h" +#undef const + +#define FLASH __flash +//#define FLASH + +#define ESCCHAR ('^'-0x40) + +#define S_10MS_TO (1<<0) + + +volatile int_fast8_t timeout_1s; +//volatile uint_least8_t Stat; +#define Stat GPIOR0 + +unsigned int to_counter; + +/****************************************************************/ + +#define P_ADL PORTA +#define P_ADH PORTC +#define P_ADB PORTE +#define PIN_ADB PINE + +#define ADB_WIDTH 3 +#define ADB_SHIFT 2 +//#define ADB_PORT GPIOE + +#define MASK(n) ((1<<(n))-1) +#define SMASK(w,s) (MASK(w) << (s)) + +typedef union { + uint32_t l; + uint16_t w[2]; + uint8_t b[4]; +} addr_t; + + + +/*--------------------------------------------------------------------------*/ + +/*---------------------------------------------------------*/ +/* 1000Hz timer interrupt generated by OC1A */ +/*---------------------------------------------------------*/ + +ISR(TIMER1_COMPA_vect) +{ + static int_fast8_t tick_10ms; +// static int_fast16_t count_ms; + + int_fast8_t i; + + + i = tick_10ms + 1; + if (i == 10) { + i = 0; + Stat |= S_10MS_TO; + + /* Drive timer procedure of low level disk I/O module */ + //disk_timerproc(); + } + tick_10ms = i; + +#if 0 + count_ms++; + if (count_ms == 1000) { + count_ms = 0; + + i = timeout_1s; + if (i) + timeout_1s = i - 1; + } +#endif +} + + +/*--------------------------------------------------------------------------*/ + +static uint32_t z80_sram_cmp(uint32_t addr, uint32_t length, uint8_t wval, int inc) +{ + uint8_t rval; + int_fast8_t errors = 0; + + DBG_P(1, "SRAM: Check 0x%.5lx byte... ", length); + while (length--) { + if ((rval = z80_read(addr)) != wval) { + if (errors == 0) { + DBG_P(1, "\nSRAM: Address W R\n" \ + " ------------------\n"); + } + errors++; + if (errors > 20) { + DBG_P(1, " ...\n"); + break; + } + DBG_P(1, " 0x%.5lx 0x%.2x 0x%.2x\n", addr, wval, rval); + } + addr++; + wval += inc; + } + DBG_P(1, "Done.\n"); + + return addr; +} + +static void z80_sram_fill(uint32_t addr, uint32_t length, uint8_t startval, int inc) +{ + printf("SRAM: Write 0x%.5lx byte... ", length); + while (length--) { + z80_write(addr, startval); + ++addr; + startval += inc; + } + printf("Done.\n"); +} + + +#if 0 +void z80_sram_fill_string(uint32_t addr, int length, const char *text) +{ + char c; + const char *p = text; + + while (length--) { + z80_write(addr++, c = *p++); + if (c == 0) + p = text; + } +} + + +uint32_t z80_sram_cmp_string(uint32_t addr, int length, const char *text) +{ + char c; + const char *p = text; + + while (length--) { + c = *p++; + if (z80_read(addr) != c) + break; + ++addr; + if (c == 0) + p = text; + } + return addr; +} + +const char * const qbfox = "Zhe quick brown fox jumps over the lazy dog!"; +const char * const qbcat = "Zhe quick brown fox jumps over the lazy cat!"; + +#endif + +uint8_t z80_get_byte(uint32_t adr) +{ + uint8_t data; + + z80_request_bus(); + data = z80_read(adr), + z80_release_bus(); + + return data; +} + + +/*--------------------------------------------------------------------------*/ + +static void do_10ms(void) +{ + if (to_counter) + to_counter--; +} + +/*--------------------------------------------------------------------------*/ + +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; +} + +void do_msg_ini_msgfifo(uint8_t subf, int len, uint8_t * msg) +{ + (void)subf; (void)len; + + z80_init_msg_fifo(msg_to_addr(msg)); +} + + +void do_msg_ini_memfifo(uint8_t subf, int len, uint8_t * msg) +{ + (void)len; + + z80_memfifo_init(subf - 1, msg_to_addr(msg)); +} + + +void do_msg_char_out(uint8_t subf, int len, uint8_t * msg) +{ + (void)subf; + + while (len--) + putchar(*msg++); +} + + +const FLASH struct msg_item z80_messages[] = +{ + { 0, /* fct nr. */ + 0, 0, /* sub fct nr. from, to */ + do_msg_ini_msgfifo}, + { 0, + 1, 2, + do_msg_ini_memfifo}, + { 1, + 1, 1, + do_msg_char_out}, + { 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) + ++i; + + if (z80_messages[i].fct == 0xff) { + DBG_P(1, "do_message: Unknown function: %i, %i\n", + fct, sub_fct); + return; /* TODO: unknown message # */ + } + + 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 (state != 3 && (ch = z80_msg_fifo_getc()) >= 0) { + switch (state) { + case 0: /* wait for start of message */ + if (ch == 0x81) { + 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) + state = 3; + break; + } + } + + if (state == 3) { + do_message(msglen, buffer); + state = 0; + } +} + + +/*--------------------------------------------------------------------------*/ + +void dump_mem(const __flash uint8_t *addr, uint32_t len) +{ + DBG_P(1, "hdrom dump:"); + while (len) { + DBG_P(1, "\n %.5x:", addr); + for (unsigned i = 0; i<16; i++) + DBG_P(1, " %.2x", *addr++); + len -= len > 16 ? 16 : len; + } + DBG_P(1, "\n"); +} + +/*--------------------------------------------------------------------------*/ + +void z80_load_mem(void) +{ + unsigned sec = 0; + uint32_t sec_base = hdrom_start; + + DBG_P(1, "Loading z80 memory... \n"); + + while (sec < hdrom_sections) { + DBG_P(2, " From: 0x%.5lX to: 0x%.5lX (%5li bytes)\n", + hdrom_address[sec], + hdrom_address[sec]+hdrom_length_of_sections[sec] - 1, + hdrom_length_of_sections[sec]); + + z80_write_block((const __flash unsigned char *) &hdrom[sec_base], /* src */ + hdrom_address[sec], /* dest */ + hdrom_length_of_sections[sec]); /* len */ + sec_base+=hdrom_length_of_sections[sec]; + sec++; + } +} + +/*--------------------------------------------------------------------------*/ + +void z80_dump_mem(uint32_t addr, uint32_t len) +{ + DBG_P(1, "Memory dump:"); + while (len) { + DBG_P(1, "\n %.5lx:", addr); + for (unsigned i = 0; i<16; i++) + DBG_P(1, " %.2x", z80_read(addr++)); + len -= len > 16 ? 16 : len; + } + DBG_P(1, "\n"); +} + +/*--------------------------------------------------------------------------*/ + +void setup_rtc(void) +{ + /* TODO: */ +} + +void setup_avr(void) +{ + /* WD */ + + /* CPU */ + + /* Disable JTAG Interface regardless of the JTAGEN fuse setting. */ + MCUCR = _BV(JTD); + MCUCR = _BV(JTD); + + /* disable unused periphels */ + PRR0 = _BV(PRTIM2) | _BV(PRTIM0) | _BV(PRADC); + PRR1 = _BV(PRTIM5) | _BV(PRTIM4) | _BV(PRTIM3) | + _BV(PRUSART3) | _BV(PRUSART2) | _BV(PRUSART1); + + /* disable analog comparator */ + ACSR = _BV(ACD); + /* Ports */ + + /* Clock */ + CLKPR = _BV(CLKPCE); + CLKPR = 0; + + /* Timer */ + + OCR1A = F_CPU / 8 / 1000 - 1; // Timer1: 1000Hz interval (OC1A) + TCCR1B = 0b00001010; + TIMSK1 = _BV(OCIE1A); // Enable TC1.oca interrupt +} + +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) +}; + + +int main(void) +{ + int_fast8_t state = 0; + int ch; + + setup_avr(); + serial_setup(); + setup_rtc(); + sei(); + + printf_P(PSTR("\n(ATMEGA1281+HD64180)_stamp Tester\n")); + + DBG_P(1, "z80_setup_bus... "); + z80_setup_msg_fifo(); + z80_setup_bus(); + DBG_P(1, "done.\n"); + + DBG_P(1, "Get bus... "); +/* Now done via S_Z180_RUNNING + z80_busreq(LOW); + z80_reset(HIGH); +*/ + z80_request_bus(); + DBG_P(1, "got it!\n"); + +// z80_sram_fill(0, (uint32_t)512 * 1024, 0x00, 3); +// z80_sram_cmp(0, (uint32_t)512 * 1024, 0x00, 3); +// z80_dump_mem(0, 0x400); + + z80_memset(0, 0x76, 0x80000); +// z80_memset(0, 0x00, 0x80000); +// z80_write_block(test1, 0, sizeof(test1)); + +// z80_dump_mem(0, 0x100); + +// z80_sram_cmp(0, (uint32_t)512 * 1024, 0x76, 0); + + z80_load_mem(); +// z80_write(0, 0x76); +// z80_dump_mem(0, 0x200); + + +/* Now done via S_Z180_RUNNING + z80_reset(LOW); +*/ + z80_release_bus(); + DBG_P(1, "Bus released!\n"); + z80_reset(HIGH); + DBG_P(1, "Reset released!\n"); + + to_counter = 200; + + while (1) { + + if (Stat & S_10MS_TO) { + Stat &= ~S_10MS_TO; + do_10ms(); + } + + + if ((ch = serial_getc()) >= 0) { + switch (state) { + case 0: + if (ch == ESCCHAR) { + state = 1; + /* TODO: Timer starten */ + } else { +// z80_memfifo_putc(fifo_out, ch); + serial_putc(ch); + if (ch == '\r') + serial_putc('\n'); + } + break; + case 1: + switch (ch) { + + case 'r': + z80_reset_pulse(); + break; + + case 'b': + z80_request_bus(); + z80_dump_mem(0, 0x2d20); + z80_release_bus(); + break; + + case 'e': + z80_request_bus(); + z80_dump_mem(0x80000-0x4000, 0x800); + z80_dump_mem(0x80000-0x200, 0x200); + z80_release_bus(); + break; + + case ESCCHAR: + default: +// z80_memfifo_putc(fifo_out, ch); + serial_putc(ch); + if (ch == '\r') + serial_putc('\n'); + } + state = 0; + break; + } + } + +// check_msg_fifo(); + } + + return 0; +} diff --git a/avr/z80-if.c b/avr/z80-if.c new file mode 100644 index 0000000..9492c28 --- /dev/null +++ b/avr/z80-if.c @@ -0,0 +1,597 @@ +/** + * + * Pin assignments + * + * | Z180-Sig | AVR-Port | Dir | Special Function | + * +------------+---------------+-------+-----------------------+ + * | 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 | | + * | IOCS1 | PE 5 | I | | + * |* HALT | P | | | + * |* NMI | P | | | + * | | P | | | + * | | P | | af1 USART1_TX | + * | | P | | af1 USART1_RX | + * | | P |JTDI | remap SPI1_NSS' | + * | | P |JTDO | remap SPI1_SCK' | + * | | P |JTRST | remap SPI1_MISO' | + * | | P | | remap SPI1_MOSI' | + * | | P | | af1 OSC32 | + * | | P | | af1 OSC32 | + + + */ + +#include <avr/io.h> +#include <util/atomic.h> +#include <stdio.h> +#include "debug.h" +#include "z80-if.h" + + +/* Number of array elements */ +#define NELEMS(x) (sizeof x/sizeof *x) + +struct bits { + uint8_t b0:1; + uint8_t b1:1; + uint8_t b2:1; + uint8_t b3:1; + uint8_t b4:1; + uint8_t b5:1; + uint8_t b6:1; + uint8_t b7:1; +} __attribute__((__packed__)); + +typedef struct bits pbit_t; + +#define SBIT(port,pin) ((*(volatile struct bits*)&port).b##pin) + + +//#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 DDR_BUSREQ DDRD +#define P_BUSACK PORTD +#define PIN_BUSACK PIND +#define BUSACK 6 +#define DDR_BUSACK DDRD +//#define P_HALT PORTA +//#define HALT 12 +#define P_IOCS1 PORTE +#define IOCS1 5 +#define DDR_IOCS1 DDRE +//#define P_NMI PORTB +//#define NMI 7 +#define P_RST PORTD +#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_O_NMI SBIT(P_NMI, ) +#define Z80_O_RST SBIT(P_RST, 5) +#define Z80_I_BUSACK SBIT(PIN_BUSACK, 6) +//#define Z80_I_HALT SBIT(P_HALT, ) + + +#define MASK(n) ((1<<(n))-1) +#define SMASK(w,s) (MASK(w) << (s)) + + +typedef union { + uint32_t l; + uint16_t w[2]; + uint8_t b[4]; +} addr_t; + + +static zstate_t zstate; + +/*--------------------------------------------------------------------------*/ + + +static void z80_addrbus_set_tristate(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_active(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_pulse(void) +{ + Z80_O_RST = 0; + _delay_us(10); + Z80_O_RST = 1; +} + + +void z80_setup_bus(void) +{ + /* /ZRESET: Output and low */ + Z80_O_RST = 0; + DDR_RST |= _BV(RST); + + /* /BUSREQ: Output and high */ + Z80_O_BUSREQ = 1; + DDR_BUSREQ |= _BV(BUSREQ); + + /* /BUSACK: Input, no pullup */ + DDR_BUSACK &= ~_BV(BUSACK); + P_BUSACK &= ~_BV(BUSACK); + + /* /IOCS1: Input, no pullup */ + DDR_IOCS1 &= ~_BV(IOCS1); + P_IOCS1 &= ~_BV(IOCS1); + + z80_addrbus_set_tristate(); + z80_dbus_set_in(); + + zstate = RESET; +} + + +zstate_t z80_bus_state(void) +{ + return zstate; +} + + +static void z80_busreq_hpulse(void) +{ + z80_dbus_set_in(); + z80_addrbus_set_tristate(); + + ATOMIC_BLOCK(ATOMIC_FORCEON) { + Z80_O_BUSREQ = 1; + Z80_O_BUSREQ = 1; /* 2 AVR clock cycles */ + Z80_O_BUSREQ = 0; /* 2 AVR clock cycles */ + } + + if (zstate & ZST_ACQUIRED) { + while(Z80_I_BUSACK == 1) + ; + z80_addrbus_set_active(); + } +} + + +/* + + + | | | | | + + 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_tristate(); + Z80_O_RST = 0; + Z80_O_BUSREQ = 1; + zstate = RESET; + break; + + case Request: + switch (zstate) { + case RESET: + Z80_O_BUSREQ = 0; + Z80_O_RST = 1; + while(Z80_I_BUSACK == 1) + ; + z80_addrbus_set_active(); + zstate = RESET_AQRD; + break; + + case RUNNING: + Z80_O_BUSREQ = 0; + while(Z80_I_BUSACK == 1) + ; + z80_addrbus_set_active(); + zstate = RUNNING_AQRD; + break; + + default: + break; + } + break; + + case Release: + switch (zstate) { + case RESET_AQRD: + z80_dbus_set_in(); + z80_addrbus_set_tristate(); + Z80_O_RST = 0; + Z80_O_BUSREQ = 1; + zstate = RESET; + break; + case RUNNING_AQRD: + z80_dbus_set_in(); + z80_addrbus_set_tristate(); + Z80_O_BUSREQ = 1; + zstate = RUNNING; + break; + default: + break; + } + break; + + case Run: + switch (zstate) { + case RESET: + Z80_O_RST = 1; + zstate = RUNNING; + break; + + case RESET_AQRD: + z80_dbus_set_in(); + z80_addrbus_set_tristate(); + z80_reset_pulse(); + z80_addrbus_set_active(); + 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(); + break; + default: + break; + } + } + return zstate; +} + + +/*--------------------------------------------------------------------------*/ + +static +//inline __attribute__ ((always_inline)) +void z80_setaddress(uint32_t addr) +{ + addr_t x; x.l = addr; + + P_ADL = x.b[0]; + P_ADH = x.b[1]; + PIN_ADB = ((x.b[2] << ADB_SHIFT) ^ P_ADB) & MASK(ADB_WIDTH) << ADB_SHIFT ; +} + +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; + while(length--) { + z80_setaddress(addr++); + 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 __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; +} + +/* + 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) { + +DBG_P(2, "z80_memfifo_init: %i, %lx\n", f, addr); + + 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); + } +} + + +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 = 1; + + 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); +} |