diff --git a/src/cic.c b/src/cic.c new file mode 100644 index 0000000..3b0ab79 --- /dev/null +++ b/src/cic.c @@ -0,0 +1,73 @@ +#include +#include "bits.h" +#include "config.h" +#include "uart.h" +#include "cic.h" + +char *cicstatenames[3] = { "CIC_OK", "CIC_FAIL", "CIC_PAIR" }; + +void print_cic_state() { + printf("CIC state: %s\n", get_cic_statename(get_cic_state())); +} + +inline char *get_cic_statename(enum cicstates state) { + return cicstatenames[state]; +} + +enum cicstates get_cic_state() { + uint32_t count; + uint32_t togglecount = 0; + uint8_t state, state_old; + + state_old = BITBAND(SNES_CIC_STATUS_REG->FIOPIN, SNES_CIC_STATUS_BIT); + for(count=0; count<1000; count++) { + state = BITBAND(SNES_CIC_STATUS_REG->FIOPIN, SNES_CIC_STATUS_BIT); + if(state != state_old) { + togglecount++; + } + state_old = state; + } + if(togglecount > 20) { + printf("%ld\n", togglecount); + return CIC_PAIR; + } + if(state) { + return CIC_OK; + } + return CIC_FAIL; +} + +void cic_init(int allow_pairmode) { + BITBAND(SNES_CIC_PAIR_REG->FIODIR, SNES_CIC_PAIR_BIT) = 1; + if(allow_pairmode) { + BITBAND(SNES_CIC_PAIR_REG->FIOCLR, SNES_CIC_PAIR_BIT) = 1; + } else { + BITBAND(SNES_CIC_PAIR_REG->FIOSET, SNES_CIC_PAIR_BIT) = 1; + } +} + +/* prepare GPIOs for pair mode + set initial modes */ +void cic_pair(int init_vmode, int init_d4) { + cic_videomode(init_vmode); + cic_d4(init_d4); + + BITBAND(SNES_CIC_D0_REG->FIODIR, SNES_CIC_D0_BIT) = 1; + BITBAND(SNES_CIC_D1_REG->FIODIR, SNES_CIC_D1_BIT) = 1; +} + +void cic_videomode(int value) { + if(value) { + BITBAND(SNES_CIC_D0_REG->FIOSET, SNES_CIC_D0_BIT) = 1; + } else { + BITBAND(SNES_CIC_D0_REG->FIOCLR, SNES_CIC_D0_BIT) = 1; + } +} + +void cic_d4(int value) { + if(value) { + BITBAND(SNES_CIC_D1_REG->FIOSET, SNES_CIC_D1_BIT) = 1; + } else { + BITBAND(SNES_CIC_D1_REG->FIOCLR, SNES_CIC_D1_BIT) = 1; + } +} + diff --git a/src/cic.h b/src/cic.h new file mode 100644 index 0000000..9a384b6 --- /dev/null +++ b/src/cic.h @@ -0,0 +1,19 @@ +#ifndef _CIC_H +#define _CIC_H + +#include +#include "bits.h" + +enum cicstates { CIC_OK = 0, CIC_FAIL, CIC_PAIR }; +enum cic_region { CIC_NTSC = 0, CIC_PAL }; + +void print_cic_state(void); +char *get_cic_statename(enum cicstates state); +enum cicstates get_cic_state(void); +void cic_init(int allow_pairmode); + +void cic_pair(int init_vmode, int init_d4); +void cic_videomode(int value); +void cic_d4(int value); + +#endif diff --git a/src/cli.c b/src/cli.c new file mode 100644 index 0000000..8fce0e4 --- /dev/null +++ b/src/cli.c @@ -0,0 +1,440 @@ +/* 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 "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; + + } + } +} diff --git a/src/cli.h b/src/cli.h new file mode 100644 index 0000000..69c2276 --- /dev/null +++ b/src/cli.h @@ -0,0 +1,34 @@ +/* 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.h: Definitions for cli.c + +*/ + +#ifndef CLI_H +#define CLI_H + +void cli_init(void); +void cli_loop(void); +void cli_entrycheck(void); + +#endif