|
@@ -0,0 +1,440 @@
|
|
|
+/* tapplay - TAP file playback for sd2iec hardware
|
|
|
+ Copyright (C) 2009 Ingo Korb <ingo@akana.de>
|
|
|
+
|
|
|
+ Inspiration and low-level SD/MMC access based on code from MMC2IEC
|
|
|
+ by Lars Pontoppidan et al., see sdcard.c|h and config.h.
|
|
|
+
|
|
|
+ FAT filesystem access based on code from ChaN and Jim Brain, see ff.c|h.
|
|
|
+
|
|
|
+ This program is free software; you can redistribute it and/or modify
|
|
|
+ it under the terms of the GNU General Public License as published by
|
|
|
+ the Free Software Foundation; version 2 of the License only.
|
|
|
+
|
|
|
+ This program is distributed in the hope that it will be useful,
|
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+ GNU General Public License for more details.
|
|
|
+
|
|
|
+ You should have received a copy of the GNU General Public License
|
|
|
+ along with this program; if not, write to the Free Software
|
|
|
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
+
|
|
|
+
|
|
|
+ cli.c: The command line interface
|
|
|
+
|
|
|
+*/
|
|
|
+
|
|
|
+#include <arm/NXP/LPC17xx/LPC17xx.h>
|
|
|
+#include <arm/bits.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+#include <ctype.h>
|
|
|
+#include <errno.h>
|
|
|
+#include <string.h>
|
|
|
+#include <stdint.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include "config.h"
|
|
|
+#include "diskio.h"
|
|
|
+#include "ff.h"
|
|
|
+#include "timer.h"
|
|
|
+#include "uart.h"
|
|
|
+#include "fileops.h"
|
|
|
+#include "memory.h"
|
|
|
+#include "snes.h"
|
|
|
+#include "fpga.h"
|
|
|
+#include "cic.h"
|
|
|
+
|
|
|
+#include "cli.h"
|
|
|
+
|
|
|
+#define MAX_LINE 250
|
|
|
+
|
|
|
+/* Variables */
|
|
|
+static char cmdbuffer[MAX_LINE+1];
|
|
|
+static char *curchar;
|
|
|
+
|
|
|
+/* Word lists */
|
|
|
+static char command_words[] =
|
|
|
+ "cd\0reset\0dir\0ls\0test\0resume\0loadrom\0loadraw\0put\0d4\0vmode\0";
|
|
|
+enum { CMD_CD = 0, CMD_RESET, CMD_DIR, CMD_LS, CMD_TEST, CMD_RESUME, CMD_LOADROM, CMD_LOADRAW, CMD_PUT, CMD_D4, CMD_VMODE };
|
|
|
+
|
|
|
+/* ------------------------------------------------------------------------- */
|
|
|
+/* Parse functions */
|
|
|
+/* ------------------------------------------------------------------------- */
|
|
|
+
|
|
|
+/* Skip spaces at curchar */
|
|
|
+static uint8_t skip_spaces(void) {
|
|
|
+ uint8_t res = (*curchar == ' ' || *curchar == 0);
|
|
|
+
|
|
|
+ while (*curchar == ' ')
|
|
|
+ curchar++;
|
|
|
+
|
|
|
+ return res;
|
|
|
+}
|
|
|
+
|
|
|
+/* Parse the string in curchar for an integer with bounds [lower,upper] */
|
|
|
+static int32_t parse_unsigned(uint32_t lower, uint32_t upper) {
|
|
|
+ char *end;
|
|
|
+ uint32_t result;
|
|
|
+
|
|
|
+ if (strlen(curchar) == 1 && *curchar == '?') {
|
|
|
+ printf("Number between %ld[0x%lx] and %ld[0x%lx] expected\n",lower,lower,upper,upper);
|
|
|
+ return -2;
|
|
|
+ }
|
|
|
+
|
|
|
+ result = strtoul(curchar, &end, 10);
|
|
|
+ if ((*end != ' ' && *end != 0) || errno != 0) {
|
|
|
+ printf("Invalid numeric argument\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ curchar = end;
|
|
|
+ skip_spaces();
|
|
|
+
|
|
|
+ if (result < lower || result > upper) {
|
|
|
+ printf("Numeric argument out of range (%ld..%ld)\n",lower,upper);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+/* Parse the string starting with curchar for a word in wordlist */
|
|
|
+static int8_t parse_wordlist(char *wordlist) {
|
|
|
+ uint8_t i, matched;
|
|
|
+ char *cur, *ptr;
|
|
|
+ char c;
|
|
|
+
|
|
|
+ i = 0;
|
|
|
+ ptr = wordlist;
|
|
|
+
|
|
|
+ // Command list on "?"
|
|
|
+ if (strlen(curchar) == 1 && *curchar == '?') {
|
|
|
+ printf("Commands available: \n ");
|
|
|
+ while (1) {
|
|
|
+ c = *ptr++;
|
|
|
+ if (c == 0) {
|
|
|
+ if (*ptr == 0) {
|
|
|
+ printf("\n");
|
|
|
+ return -2;
|
|
|
+ } else {
|
|
|
+ printf("\n ");
|
|
|
+ }
|
|
|
+ } else
|
|
|
+ uart_putc(c);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ cur = curchar;
|
|
|
+ matched = 1;
|
|
|
+ c = *ptr;
|
|
|
+ do {
|
|
|
+ // If current word list character is \0: No match found
|
|
|
+ if (c == 0) {
|
|
|
+ printf("Unknown word: %s\n",curchar);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (tolower(c) != tolower(*cur)) {
|
|
|
+ // Check for end-of-word
|
|
|
+ if (cur != curchar && (*cur == ' ' || *cur == 0)) {
|
|
|
+ // Partial match found, return that
|
|
|
+ break;
|
|
|
+ } else {
|
|
|
+ matched = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ptr++;
|
|
|
+ cur++;
|
|
|
+ c = *ptr;
|
|
|
+ } while (c != 0);
|
|
|
+
|
|
|
+ if (matched) {
|
|
|
+ char *tmp = curchar;
|
|
|
+
|
|
|
+ curchar = cur;
|
|
|
+ // Return match only if whitespace or end-of-string follows
|
|
|
+ // (avoids mismatching partial words)
|
|
|
+ if (skip_spaces()) {
|
|
|
+ return i;
|
|
|
+ } else {
|
|
|
+ printf("Unknown word: %s\n(use ? for help)\n",tmp);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // Try next word in list
|
|
|
+ i++;
|
|
|
+ while (*ptr++ != 0) ;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* Read a line from serial, uses cmdbuffer as storage */
|
|
|
+static char *getline(char *prompt) {
|
|
|
+ int i=0;
|
|
|
+ char c;
|
|
|
+
|
|
|
+ printf("\n%s",prompt);
|
|
|
+ memset(cmdbuffer,0,sizeof(cmdbuffer));
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ c = uart_getc();
|
|
|
+ if (c == 13)
|
|
|
+ break;
|
|
|
+
|
|
|
+ if (c == 27 || c == 3) {
|
|
|
+ printf("\\\n%s",prompt);
|
|
|
+ i = 0;
|
|
|
+ memset(cmdbuffer,0,sizeof(cmdbuffer));
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (c == 127 || c == 8) {
|
|
|
+ if (i > 0) {
|
|
|
+ i--;
|
|
|
+ uart_putc(8); // backspace
|
|
|
+ uart_putc(' '); // erase character
|
|
|
+ uart_putc(8); // backspace
|
|
|
+ } else
|
|
|
+ continue;
|
|
|
+ } else {
|
|
|
+ if (i < sizeof(cmdbuffer)-1) {
|
|
|
+ cmdbuffer[i++] = c;
|
|
|
+ uart_putc(c);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ cmdbuffer[i] = 0;
|
|
|
+ return cmdbuffer;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/* ------------------------------------------------------------------------- */
|
|
|
+/* Command functions */
|
|
|
+/* ------------------------------------------------------------------------- */
|
|
|
+
|
|
|
+/* Reset */
|
|
|
+static void cmd_reset(void) {
|
|
|
+ /* force watchdog reset */
|
|
|
+ LPC_WDT->WDTC = 256; // minimal timeout
|
|
|
+ LPC_WDT->WDCLKSEL = BV(31); // internal RC, lock register
|
|
|
+ LPC_WDT->WDMOD = BV(0) | BV(1); // enable watchdog and reset-by-watchdog
|
|
|
+ LPC_WDT->WDFEED = 0xaa;
|
|
|
+ LPC_WDT->WDFEED = 0x55; // initial feed to really enable WDT
|
|
|
+}
|
|
|
+
|
|
|
+/* Show the contents of the current directory */
|
|
|
+static void cmd_show_directory(void) {
|
|
|
+ FRESULT res;
|
|
|
+ DIR dh;
|
|
|
+ FILINFO finfo;
|
|
|
+ uint8_t *name;
|
|
|
+
|
|
|
+ f_getcwd((TCHAR*)file_lfn, 255);
|
|
|
+
|
|
|
+ res = f_opendir(&dh, (TCHAR*)file_lfn);
|
|
|
+ if (res != FR_OK) {
|
|
|
+ printf("f_opendir failed, result %d\n",res);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ finfo.lfname = (TCHAR*)file_lfn;
|
|
|
+ finfo.lfsize = 255;
|
|
|
+
|
|
|
+ do {
|
|
|
+ /* Read the next entry */
|
|
|
+ res = f_readdir(&dh, &finfo);
|
|
|
+ if (res != FR_OK) {
|
|
|
+ printf("f_readdir failed, result %d\n",res);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Abort if none was found */
|
|
|
+ if (!finfo.fname[0])
|
|
|
+ break;
|
|
|
+
|
|
|
+ /* Skip volume labels */
|
|
|
+ if (finfo.fattrib & AM_VOL)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Select between LFN and 8.3 name */
|
|
|
+ if (finfo.lfname[0])
|
|
|
+ name = (uint8_t*)finfo.lfname;
|
|
|
+ else {
|
|
|
+ name = (uint8_t*)finfo.fname;
|
|
|
+ strlwr((char *)name);
|
|
|
+ }
|
|
|
+
|
|
|
+#if 0
|
|
|
+ if (finfo.fattrib & AM_DIR) {
|
|
|
+ printf("DIR ");
|
|
|
+ } else {
|
|
|
+ printf(" ");
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ printf("%s",name);
|
|
|
+
|
|
|
+ /* Directory indicator (Unix-style) */
|
|
|
+ if (finfo.fattrib & AM_DIR)
|
|
|
+ uart_putc('/');
|
|
|
+
|
|
|
+ printf("\n");
|
|
|
+ } while (finfo.fname[0]);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void cmd_loadrom(void) {
|
|
|
+ uint32_t address = 0;
|
|
|
+ snes_reset(1);
|
|
|
+ set_mcu_ovr(1);
|
|
|
+ load_rom((uint8_t*)curchar, address);
|
|
|
+ set_mcu_ovr(0);
|
|
|
+ snes_reset(0);
|
|
|
+}
|
|
|
+
|
|
|
+static void cmd_d4(void) {
|
|
|
+ int32_t hz;
|
|
|
+
|
|
|
+ if(get_cic_state() != CIC_PAIR) {
|
|
|
+ printf("not in pair mode\n");
|
|
|
+ } else {
|
|
|
+ hz = parse_unsigned(50,60);
|
|
|
+ if(hz==50) {
|
|
|
+ cic_d4(CIC_PAL);
|
|
|
+ } else {
|
|
|
+ cic_d4(CIC_NTSC);
|
|
|
+ }
|
|
|
+ printf("ok\n");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void cmd_vmode(void) {
|
|
|
+ int32_t hz;
|
|
|
+ if(get_cic_state() != CIC_PAIR) {
|
|
|
+ printf("not in pair mode\n");
|
|
|
+ } else {
|
|
|
+ hz = parse_unsigned(50,60);
|
|
|
+ if(hz==50) {
|
|
|
+ cic_videomode(CIC_PAL);
|
|
|
+ } else {
|
|
|
+ cic_videomode(CIC_NTSC);
|
|
|
+ }
|
|
|
+ printf("ok\n");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#if 0
|
|
|
+ uint8_t *buf = malloc(256);
|
|
|
+
|
|
|
+ if (buf == NULL) {
|
|
|
+ printf("buffer allocation failed!\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int i=0;i<256;i++) {
|
|
|
+ buf[i] = i2c_read_register(0xa0, i);
|
|
|
+ }
|
|
|
+
|
|
|
+ uart_trace(buf, 0, 256);
|
|
|
+ free(buf);
|
|
|
+#endif
|
|
|
+
|
|
|
+/* ------------------------------------------------------------------------- */
|
|
|
+/* CLI interface functions */
|
|
|
+/* ------------------------------------------------------------------------- */
|
|
|
+
|
|
|
+void cli_init(void) {
|
|
|
+}
|
|
|
+
|
|
|
+void cli_entrycheck() {
|
|
|
+ if(uart_gotc() && uart_getc() == 27) {
|
|
|
+ printf("*** BREAK\n");
|
|
|
+ cli_loop();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void cli_loop(void) {
|
|
|
+ while (1) {
|
|
|
+ curchar = getline(">");
|
|
|
+ printf("\n");
|
|
|
+
|
|
|
+ /* Process medium changes before executing the command */
|
|
|
+ if (disk_state != DISK_OK && disk_state != DISK_REMOVED) {
|
|
|
+ FRESULT res;
|
|
|
+
|
|
|
+ printf("Medium changed... ");
|
|
|
+ res = f_mount(0,&fatfs);
|
|
|
+ if (res != FR_OK) {
|
|
|
+ printf("Failed to mount new medium, result %d\n",res);
|
|
|
+ } else {
|
|
|
+ printf("Ok\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Remove whitespace */
|
|
|
+ while (*curchar == ' ') curchar++;
|
|
|
+ while (strlen(curchar) > 0 && curchar[strlen(curchar)-1] == ' ')
|
|
|
+ curchar[strlen(curchar)-1] = 0;
|
|
|
+
|
|
|
+ /* Ignore empty lines */
|
|
|
+ if (strlen(curchar) == 0)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Parse command */
|
|
|
+ int8_t command = parse_wordlist(command_words);
|
|
|
+ if (command < 0)
|
|
|
+ continue;
|
|
|
+
|
|
|
+
|
|
|
+ FRESULT res;
|
|
|
+ switch (command) {
|
|
|
+ case CMD_CD:
|
|
|
+#if _FS_RPATH
|
|
|
+ if (strlen(curchar) == 0) {
|
|
|
+ f_getcwd((TCHAR*)file_lfn, 255);
|
|
|
+ printf("%s\n",file_lfn);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ res = f_chdir((const TCHAR *)curchar);
|
|
|
+ if (res != FR_OK) {
|
|
|
+ printf("chdir %s failed with result %d\n",curchar,res);
|
|
|
+ } else {
|
|
|
+ printf("Ok.\n");
|
|
|
+ }
|
|
|
+#else
|
|
|
+ printf("cd not supported.\n");
|
|
|
+ res;
|
|
|
+#endif
|
|
|
+ break;
|
|
|
+ case CMD_RESET:
|
|
|
+ cmd_reset();
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CMD_DIR:
|
|
|
+ case CMD_LS:
|
|
|
+ cmd_show_directory();
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CMD_RESUME:
|
|
|
+ return;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CMD_LOADROM:
|
|
|
+ cmd_loadrom();
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CMD_D4:
|
|
|
+ cmd_d4();
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CMD_VMODE:
|
|
|
+ cmd_vmode();
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|