/* sd2snes - SD card based universal cartridge for the SNES Copyright (C) 2009-2010 Maximilian Rehkopf AVR firmware portion Inspired by and based on code from sd2iec, written by Ingo Korb et al. See sdcard.c|h, config.h. FAT file system access based on code by ChaN, Jim Brain, Ingo Korb, 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 fpga_spi.h: functions for SPI ctrl, SRAM interfacing and feature configuration */ /* SPI commands cmd param function ============================================= 0t bbhhll set address to 0xbbhhll t = target target: 0 = RAM 1 = MSU Audio buffer 2 = MSU Data buffer targets 1 & 2 only require 2 address bytes to be written. 10 bbhhll set SNES input address mask to 0xbbhhll 20 bbhhll set SRAM address mask to 0xbbhhll 3m - set mapper to m 0=HiROM, 1=LoROM, 2=ExHiROM, 6=SF96, 7=Menu 4s - trigger SD DMA (512b from SD to memory) s: Bit 2 = partial, Bit 1:0 = target target: see above 60 xsssyeee set SD DMA partial transfer parameters x: 0 = read from sector start (skip until start offset reached) 8 = assume mid-sector position and read immediately sss = start offset (msb first) y: 0 = skip rest of SD sector 8 = stop mid-sector if end offset reached eee = end offset (msb first) 8p - read (RAM only) p: 0 = no increment after read 8 = increment after read 9p {xx}* write xx p: i-tt tt = target (see above) i = increment (see above) E0 ssrr set MSU-1 status register (=FPGA status [7:0]) ss = bits to set in status register (1=set) rr = bits to reset in status register (1=reset) E1 - pause DAC E2 - resume/play DAC E3 - reset DAC playback pointer (0) E4 hhll set MSU read pointer E5 tt{7} set RTC (SPC7110 format + 1000s of year, nibbles packed) eg 0x20111210094816 is 2011-12-10, 9:48:16 E6 ssrr set/reset BS-X status register [7:0] E7 - reset SRTC state E8 - reset DSP program and data ROM write pointers E9 hhmmllxxxx write+incr. DSP program ROM (xxxx=dummy writes) EA hhllxxxx write+incr. DSP data ROM (xxxx=dummy writes) EB - put DSP into reset EC - release DSP from reset ED - set feature enable bits (see below) EE - set $213f override value (0=NTSC, 1=PAL) F0 - receive test token (to see if FPGA is alive) F1 - receive status (16bit, MSB first), see below F2 - get MSU data address (32bit, MSB first) F3 - get MSU audio track no. (16bit, MSB first) F4 - get MSU volume (8bit) FE - get SNES master clock frequency (32bit, MSB first) measured 1x/sec FF {xx}* echo (returns the sent data in the next byte) FPGA status word: bit function ========================================================================== 15 SD DMA busy (0=idle, 1=busy) 14 DAC read pointer MSB 13 MSU read pointer MSB 12 reserved (0) 11 reserved (0) 10 reserved (0) 9 reserved (0) 8 reserved (0) 7 reserved (0) 6 reserved (0) 5 MSU1 Audio request from SNES 4 MSU1 Data request from SNES 3 reserved (0) 2 MSU1 Audio control status: 0=no repeat, 1=repeat 1 MSU1 Audio control status: 0=pause, 1=play 0 MSU1 Audio control request FPGA feature enable bits: bit function ========================================================================== 7 - 6 - 5 - 4 enable $213F override 3 enable MSU1 registers 2 enable SRTC registers 1 enable ST0010 mapping 0 enable DSPx mapping */ #include #include "bits.h" #include "fpga.h" #include "config.h" #include "uart.h" #include "spi.h" #include "fpga_spi.h" #include "timer.h" #include "sdnative.h" void fpga_spi_init(void) { spi_init(); BITBAND(FPGA_MCU_RDY_REG->FIODIR, FPGA_MCU_RDY_BIT) = 0; } void set_msu_addr(uint16_t address) { FPGA_SELECT(); FPGA_TX_BYTE(0x02); FPGA_TX_BYTE((address>>8)&0xff); FPGA_TX_BYTE((address)&0xff); FPGA_DESELECT(); } void set_dac_addr(uint16_t address) { FPGA_SELECT(); FPGA_TX_BYTE(0x01); FPGA_TX_BYTE((address>>8)&0xff); FPGA_TX_BYTE((address)&0xff); FPGA_DESELECT(); } void set_mcu_addr(uint32_t address) { FPGA_SELECT(); FPGA_TX_BYTE(0x00); FPGA_TX_BYTE((address>>16)&0xff); FPGA_TX_BYTE((address>>8)&0xff); FPGA_TX_BYTE((address)&0xff); FPGA_DESELECT(); } void set_saveram_mask(uint32_t mask) { FPGA_SELECT(); FPGA_TX_BYTE(0x20); FPGA_TX_BYTE((mask>>16)&0xff); FPGA_TX_BYTE((mask>>8)&0xff); FPGA_TX_BYTE((mask)&0xff); FPGA_DESELECT(); } void set_rom_mask(uint32_t mask) { FPGA_SELECT(); FPGA_TX_BYTE(0x10); FPGA_TX_BYTE((mask>>16)&0xff); FPGA_TX_BYTE((mask>>8)&0xff); FPGA_TX_BYTE((mask)&0xff); FPGA_DESELECT(); } void set_mapper(uint8_t val) { FPGA_SELECT(); FPGA_TX_BYTE(0x30 | (val & 0x0f)); FPGA_DESELECT(); } uint8_t fpga_test() { FPGA_SELECT(); FPGA_TX_BYTE(0xF0); /* TEST */ uint8_t result = FPGA_RX_BYTE(); FPGA_DESELECT(); return result; } uint16_t fpga_status() { FPGA_SELECT(); FPGA_TX_BYTE(0xF1); /* STATUS */ uint16_t result = (FPGA_RX_BYTE()) << 8; result |= FPGA_RX_BYTE(); FPGA_DESELECT(); return result; } void fpga_set_sddma_range(uint16_t start, uint16_t end) { FPGA_SELECT(); FPGA_TX_BYTE(0x60); /* DMA_RANGE */ FPGA_TX_BYTE(start>>8); FPGA_TX_BYTE(start&0xff); FPGA_TX_BYTE(end>>8); FPGA_TX_BYTE(end&0xff); //if(tgt==1 && (test=FPGA_RX_BYTE()) != 0x41) printf("!!!!!!!!!!!!!!! -%02x- \n", test); FPGA_DESELECT(); } void fpga_sddma(uint8_t tgt, uint8_t partial) { uint32_t test = 0; uint8_t status = 0; BITBAND(SD_CLKREG->FIODIR, SD_CLKPIN) = 0; FPGA_SELECT(); FPGA_TX_BYTE(0x40 | (tgt & 0x3) | ((partial & 1) << 2) ); /* DO DMA */ FPGA_TX_BYTE(0x00); /* dummy for falling DMA_EN edge */ //if(tgt==1 && (test=FPGA_RX_BYTE()) != 0x41) printf("!!!!!!!!!!!!!!! -%02x- \n", test); FPGA_DESELECT(); FPGA_SELECT(); FPGA_TX_BYTE(0xF1); /* STATUS */ DBG_SD printf("FPGA DMA request sent, wait for completion..."); while((status=FPGA_RX_BYTE()) & 0x80) { FPGA_RX_BYTE(); /* eat the 2nd status byte */ test++; } DBG_SD printf("...complete\n"); FPGA_DESELECT(); // if(test<5)printf("loopy: %ld %02x\n", test, status); BITBAND(SD_CLKREG->FIODIR, SD_CLKPIN) = 1; } void set_dac_vol(uint8_t volume) { FPGA_SELECT(); FPGA_TX_BYTE(0x50); FPGA_TX_BYTE(volume); FPGA_TX_BYTE(0x00); /* latch rise */ FPGA_TX_BYTE(0x00); /* latch fall */ FPGA_DESELECT(); } void dac_play() { FPGA_SELECT(); FPGA_TX_BYTE(0xe2); FPGA_TX_BYTE(0x00); /* latch reset */ FPGA_DESELECT(); } void dac_pause() { FPGA_SELECT(); FPGA_TX_BYTE(0xe1); FPGA_TX_BYTE(0x00); /* latch reset */ FPGA_DESELECT(); } void dac_reset() { FPGA_SELECT(); FPGA_TX_BYTE(0xe3); FPGA_TX_BYTE(0x00); /* latch reset */ FPGA_TX_BYTE(0x00); /* latch reset */ FPGA_DESELECT(); } void msu_reset(uint16_t address) { FPGA_SELECT(); FPGA_TX_BYTE(0xe4); FPGA_TX_BYTE((address>>8) & 0xff); /* address hi */ FPGA_TX_BYTE(address & 0xff); /* address lo */ FPGA_TX_BYTE(0x00); /* latch reset */ FPGA_TX_BYTE(0x00); /* latch reset */ FPGA_DESELECT(); } void set_msu_status(uint8_t set, uint8_t reset) { FPGA_SELECT(); FPGA_TX_BYTE(0xe0); FPGA_TX_BYTE(set); FPGA_TX_BYTE(reset); FPGA_TX_BYTE(0x00); /* latch reset */ FPGA_DESELECT(); } uint8_t get_msu_volume() { FPGA_SELECT(); FPGA_TX_BYTE(0xF4); /* MSU_VOLUME */ uint8_t result = FPGA_RX_BYTE(); FPGA_DESELECT(); return result; } uint16_t get_msu_track() { FPGA_SELECT(); FPGA_TX_BYTE(0xF3); /* MSU_TRACK */ uint16_t result = (FPGA_RX_BYTE()) << 8; result |= FPGA_RX_BYTE(); FPGA_DESELECT(); return result; } uint32_t get_msu_offset() { FPGA_SELECT(); FPGA_TX_BYTE(0xF2); /* MSU_OFFSET */ uint32_t result = (FPGA_RX_BYTE()) << 24; result |= (FPGA_RX_BYTE()) << 16; result |= (FPGA_RX_BYTE()) << 8; result |= (FPGA_RX_BYTE()); FPGA_DESELECT(); return result; } uint32_t get_snes_sysclk() { FPGA_SELECT(); FPGA_TX_BYTE(0xFE); /* GET_SYSCLK */ FPGA_TX_BYTE(0x00); /* dummy (copy current sysclk count to register) */ uint32_t result = (FPGA_RX_BYTE()) << 24; result |= (FPGA_RX_BYTE()) << 16; result |= (FPGA_RX_BYTE()) << 8; result |= (FPGA_RX_BYTE()); FPGA_DESELECT(); return result; } void set_bsx_regs(uint8_t set, uint8_t reset) { FPGA_SELECT(); FPGA_TX_BYTE(0xe6); FPGA_TX_BYTE(set); FPGA_TX_BYTE(reset); FPGA_TX_BYTE(0x00); /* latch reset */ FPGA_DESELECT(); } void set_fpga_time(uint64_t time) { FPGA_SELECT(); FPGA_TX_BYTE(0xe5); FPGA_TX_BYTE((time >> 48) & 0xff); FPGA_TX_BYTE((time >> 40) & 0xff); FPGA_TX_BYTE((time >> 32) & 0xff); FPGA_TX_BYTE((time >> 24) & 0xff); FPGA_TX_BYTE((time >> 16) & 0xff); FPGA_TX_BYTE((time >> 8) & 0xff); FPGA_TX_BYTE(time & 0xff); FPGA_TX_BYTE(0x00); FPGA_DESELECT(); } void fpga_reset_srtc_state() { FPGA_SELECT(); FPGA_TX_BYTE(0xe7); FPGA_TX_BYTE(0x00); FPGA_TX_BYTE(0x00); FPGA_DESELECT(); } void fpga_reset_dspx_addr() { FPGA_SELECT(); FPGA_TX_BYTE(0xe8); FPGA_TX_BYTE(0x00); FPGA_TX_BYTE(0x00); FPGA_DESELECT(); } void fpga_write_dspx_pgm(uint32_t data) { FPGA_SELECT(); FPGA_TX_BYTE(0xe9); FPGA_TX_BYTE((data>>16)&0xff); FPGA_TX_BYTE((data>>8)&0xff); FPGA_TX_BYTE((data)&0xff); FPGA_TX_BYTE(0x00); FPGA_TX_BYTE(0x00); FPGA_DESELECT(); } void fpga_write_dspx_dat(uint16_t data) { FPGA_SELECT(); FPGA_TX_BYTE(0xea); FPGA_TX_BYTE((data>>8)&0xff); FPGA_TX_BYTE((data)&0xff); FPGA_TX_BYTE(0x00); FPGA_TX_BYTE(0x00); FPGA_DESELECT(); } void fpga_dspx_reset(uint8_t reset) { FPGA_SELECT(); FPGA_TX_BYTE(reset ? 0xeb : 0xec); FPGA_TX_BYTE(0x00); FPGA_DESELECT(); } void fpga_set_features(uint8_t feat) { printf("set features: %02x\n", feat); FPGA_SELECT(); FPGA_TX_BYTE(0xed); FPGA_TX_BYTE(feat); FPGA_DESELECT(); } void fpga_set_213f(uint8_t data) { printf("set 213f: %d\n", data); FPGA_SELECT(); FPGA_TX_BYTE(0xee); FPGA_TX_BYTE(data); FPGA_DESELECT(); }