/* tapplay - TAP file playback for sd2iec hardware Copyright (C) 2009 Ingo Korb 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 #include #include #include #include #include #include #include #include #include #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 "fpga_spi.h" #include "cic.h" #include "xmodem.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\0saveraw\0put\0d4\0vmode\0mapper\0"; enum { CMD_CD = 0, CMD_RESET, CMD_DIR, CMD_LS, CMD_TEST, CMD_RESUME, CMD_LOADROM, CMD_LOADRAW, CMD_SAVERAW, CMD_PUT, CMD_D4, CMD_VMODE, CMD_MAPPER }; /* ------------------------------------------------------------------------- */ /* 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_saveraw(void) { uint32_t address = parse_unsigned(0,16777216); uint32_t length = parse_unsigned(0,16777216); set_mcu_ovr(0); save_sram((uint8_t*)curchar, length, address); } 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 void cmd_put(void) { if(*curchar != 0) { file_open((uint8_t*)curchar, FA_CREATE_ALWAYS | FA_WRITE); if(file_res) { printf("FAIL: error opening file %s\n", curchar); } else { printf("OK, start xmodem transfer now.\n"); xmodem_rxfile(&file_handle); } file_close(); } else { printf("Usage: put \n"); } } void cmd_mapper(void) { int32_t mapper; mapper = parse_unsigned(0,7); set_mapper((uint8_t)mapper & 0x7); printf("mapper set to %ld\n", mapper); } /* ------------------------------------------------------------------------- */ /* 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_SAVERAW: cmd_saveraw(); break; case CMD_D4: cmd_d4(); break; case CMD_VMODE: cmd_vmode(); break; case CMD_PUT: cmd_put(); break; case CMD_MAPPER: cmd_mapper(); break; } } }