sd2snes/src/cli.c

483 lines
10 KiB
C

/* 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 "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 <filename>\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;
}
}
}