/* * (C) Copyright 2014-2016 Leo C. * * (C) Copyright 2000-2003 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * SPDX-License-Identifier: GPL-2.0 */ /* * Misc boot support */ #include "common.h" #include #include #include "command.h" #include "cli_readline.h" /* console_buffer[] */ #include "cli.h" /* run_command() */ #include "env.h" #include "eval_arg.h" #include "con-utils.h" #include "getopt-min.h" #include "z80-if.h" #include "z180-serv.h" /* restart_z180_serv() */ #include "debug.h" /* ugly hack to get Z180 loadfile into flash memory */ #define const const FLASH #include "../z180/hdrom.h" #include "../z180/cfboot.h" #undef const static void z80_load_mem(int_fast8_t verbosity, const FLASH unsigned char data[], const FLASH unsigned long *sections, const FLASH unsigned long address[], const FLASH unsigned long length_of_sections[]) { uint32_t sec_base = 0; if (verbosity > 1) printf_P(PSTR("Loading Z180 memory... \n")); for (unsigned sec = 0; sec < *sections; sec++) { if (verbosity > 0) { printf_P(PSTR(" From: 0x%.5lX to: 0x%.5lX (%5li bytes)\n"), address[sec], address[sec]+length_of_sections[sec] - 1, length_of_sections[sec]); } z80_write_block_P((const FLASH unsigned char *) &data[sec_base], /* src */ address[sec], /* dest */ length_of_sections[sec]); /* len */ sec_base += length_of_sections[sec]; } } 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) { my_puts_P(PSTR("Can't load while CPU is running!\n")); return CMD_RET_FAILURE; } if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) { my_puts_P(PSTR("Bus timeout\n")); return CMD_RET_FAILURE; } z80_load_mem(2, hdrom, &hdrom_sections, hdrom_address, hdrom_length_of_sections); z80_bus_cmd(Release); return CMD_RET_SUCCESS; } void print_vars(char *title) { uint8_t buf[5]; zstate_t state = z80_bus_state(); if((state & ZST_ACQUIRED) == 0) z80_bus_cmd(Request); z80_read_block(buf, 9, sizeof buf); if((state & ZST_ACQUIRED) == 0) z80_bus_cmd(Release); printf_P(PSTR("%s: stage: %d, flag: 0x%.02x, result: %d, IDE stat/error: 0x%.02x/0x%.02x\n"), title, buf[0], buf[1], buf[2], buf[3], buf[4]); } /* * bootcf [options] * * -a address (100h) * -s start sector (0) * -c sector count (7) * -i Partition id (52) * -n load only * -t timeout (10000) * -v verbose */ command_ret_t do_bootcf(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { struct { uint8_t jr[2]; uint16_t loadaddr; uint8_t sec_start; uint8_t sec_cnt; uint8_t part_id; uint16_t timeout; uint8_t stages; } boot_param; struct { uint8_t stages; uint8_t done; uint8_t result; uint8_t ide_stat; uint8_t ide_error; } boot_res; int_fast8_t verbosity = 0; uint8_t default_stages; uint32_t val; (void) cmdtp; (void) flag; /* get default values */ memcpy_P(&boot_param, cfboot, sizeof boot_param); default_stages = boot_param.stages; /* reset getopt() */ optind = 0; int opt; while ((opt = getopt(argc, argv, PSTR("vna:s:c:t:i:"))) != -1) { switch (opt) { case 'v': verbosity++; break; case 'n': if (boot_param.stages > 0) boot_param.stages--; break; case 'a': val = eval_arg(optarg, NULL); if (val < 0x100 || val > 0xFE00) { printf_P(PSTR("Address out of range: 0x%.4lX\n"), val); return CMD_RET_FAILURE; } boot_param.loadaddr = val; break; case 's': val = eval_arg(optarg, NULL); if (val > 255) { printf_P(PSTR("Start sector out of range: 0x%lX\n"), val); return CMD_RET_FAILURE; } boot_param.sec_start = val; break; case 'c': val = eval_arg(optarg, NULL); if (val > 127) { printf_P(PSTR("Sector count out of range: 0x%lX\n"), val); return CMD_RET_FAILURE; } boot_param.sec_cnt = val; break; case 't': val = eval_arg(optarg, NULL); if (val < 0x1 || val > 0xFFFF) { printf_P(PSTR("Timeout value out of range: 0x%lX\n"), val); return CMD_RET_FAILURE; } boot_param.timeout = val; break; case 'i': val = eval_arg(optarg, NULL); if (val < 0x01 || val > 0xFF) { printf_P(PSTR("Partition id out of range: 0x%lX\n"), val); return CMD_RET_FAILURE; } boot_param.part_id = val; break; default: /* '?' */ return CMD_RET_USAGE; } } /* remaining arguments */ argc -= optind; if (argc) { my_puts_P(PSTR("Argument error!\n")); return CMD_RET_USAGE; } if ((val = (uint32_t) boot_param.loadaddr + boot_param.sec_cnt * 512) >= 0xFF00) { printf_P(PSTR("Top address out of range: 0x%.4lX\n"), val); return CMD_RET_FAILURE; } if (z80_bus_state() & ZST_RUNNING) { my_puts_P(PSTR("CPU is allready running!\n")); return CMD_RET_FAILURE; } if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) { my_puts_P(PSTR("Bus timeout\n")); return CMD_RET_FAILURE; } z80_load_mem(verbosity, cfboot, &cfboot_sections, cfboot_address, cfboot_length_of_sections); z80_write_block((const uint8_t *) &boot_param, cfboot_address[0], sizeof boot_param); z80_bus_cmd(Release); if (boot_param.stages == 0) { printf_P(PSTR("Bootloader loaded at: 0x%.4X\n"), (uint16_t) cfboot_address[0]); } else { printf_P(PSTR("Executing %d of %d Bootloader stages...\n"), boot_param.stages, default_stages); z80_bus_cmd(Run); z80_bus_cmd(Release); clear_ctrlc(); /* forget any previous Control C */ for (boot_res.done = 0; boot_res.done != 0xFF;) { _delay_ms(8); /* check for ctrl-c to abort... */ if (had_ctrlc() || ctrlc()) { break; } z80_bus_cmd(Request); z80_read_block((uint8_t *) &boot_res, cfboot_address[0]+sizeof boot_param - 1, sizeof boot_res); z80_bus_cmd(Release); } if (boot_res.done != 0xFF) { z80_bus_cmd(Reset); my_puts_P(PSTR("Abort\n")); } else { if (boot_param.stages == default_stages && boot_res.stages == 0 && boot_res.result == 0) { my_puts_P(PSTR("Booting...\n")); } else { z80_bus_cmd(Reset); boot_res.stages++; printf_P(PSTR("Bootloader stopped at stage %d, result: %d, IDE stat/error: 0x%.02x/0x%.02x\n"), boot_param.stages - boot_res.stages, boot_res.result, boot_res.ide_stat, boot_res.ide_error); } } } return CMD_RET_SUCCESS; } command_ret_t do_busreq_pulse(cmd_tbl_t *cmdtp, 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) eval_arg(argv[1], NULL); z80_bus_cmd(Request); while (count--) z80_bus_cmd(M_Cycle); return CMD_RET_SUCCESS; } command_ret_t do_go(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { uint32_t addr; (void) cmdtp; (void) flag; if (argc < 2) return CMD_RET_USAGE; addr = eval_arg(argv[1], NULL); 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]; z80_bus_cmd(Request); z80_read_block (tmp, 0, 3); 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); z80_write_block(tmp, 0, 3); } 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" " Q,X - Return to command line\n" " R - Reset (Restart) CPU\n" " : - Execute monitor command\n" " \\ - code input:\n" " \\nnn 3 decimal digits character code\n" " \\Xhh 2 hexadecimal digits character code\n" " ^%c - (Escape char) Type again to send itself\n" "key>" ), esc + 0x40); } command_ret_t do_console(cmd_tbl_t *cmdtp, 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; char esc_char = (char) getenv_ulong(PSTR(ENV_ESC_CHAR), 16, CONFIG_ESC_CHAR); (void) cmdtp; (void) flag; (void) argc; (void) argv; printf_P(PSTR("Connecting to CPU. Escape character is '^%c'.\n"), esc_char + 0x40); while (1) { ATOMIC_BLOCK(ATOMIC_FORCEON) { pending = (Stat & S_CON_PENDING) != 0; Stat &= ~S_CON_PENDING; } if (pending) { uint8_t count = 100; while ((ch = z80_memfifo_getc(fifo_conout)) >= 0 && --count) putchar(ch); } if ((ch = my_getchar(0)) >= 0) { switch (state) { case 0: if (ch == esc_char) { state = 1; /* TODO: Timer starten */ } else { z80_memfifo_putc(fifo_conin, ch); } break; case 2: printf_P(PSTR("\n" "------------------------------------------------\n")); case 1: state = 0; switch (toupper(ch)) { case '?': case 'H': print_con_usage(esc_char); state = 2; break; case 'R': reset_cpu(Restart); break; case 'X': case 'Q': printf_P(PSTR("\n")); goto quit; break; case ':': putchar('\n'); int cmdlen = cli_readline(PSTR(": "), 1); if (cmdlen > 0) run_command(console_buffer, 0); break; case '\\': code = 0; state = 3; break; default: if (ch == esc_char) z80_memfifo_putc(fifo_conin, ch); break; } break; case 3: if (toupper(ch) == 'X') { state = 6; break; } /* fall thru */ case 4: case 5: if (isdigit(ch)) { code = code * 10 + ch - '0'; state++; } else { if (state > 3) z80_memfifo_putc(fifo_conin, code); z80_memfifo_putc(fifo_conin, ch); state = 0; } if (state > 5) { z80_memfifo_putc(fifo_conin, code); state = 0; } break; case 6: case 7: if (isxdigit(ch)) { ch = toupper(ch); if (ch >= 'A') ch -= 'A' - 10; code = code * 16 + ch - '0'; state++; }else { if (state > 6) z80_memfifo_putc(fifo_conin, code); z80_memfifo_putc(fifo_conin, ch); state = 0; } if (state > 7) { z80_memfifo_putc(fifo_conin, code); state = 0; } break; } } } quit: return CMD_RET_SUCCESS; }