/* 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 main.c: initialization and flow */ #include #include #include #include #include #include #include #include #include "config.h" #include "diskio.h" #include "ff.h" #include "led.h" /* #include "timer.h" */ #include "fpga.h" #include "uart.h" #include "ustring.h" #include "utils.h" #include "snes.h" #include "fileops.h" #include "memory.h" #include "fpga_spi.h" #include "spi.h" #include "avrcompat.h" #include "filetypes.h" #include "sdcard.h" void writetest(void) { // HERE BE LIONS, GET IN THE CAR char teststring[58]; while(1) { sram_writeblock((void*)"Testtext of DOOM!!1! 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", SRAM_SCRATCHPAD+0x20, 58); sram_readblock((void*)teststring, SRAM_SCRATCHPAD+0x20, 58); teststring[57]=0; dprintf("%s\n", teststring); } // END OF LIONS } void memtest(void) { /* HERE BE DRAGONS */ uint32_t dbg_i; for(dbg_i=0; dbg_i < 65536; dbg_i++) { sram_writeshort((uint16_t)dbg_i&0xffff, dbg_i*2); } save_sram((uint8_t*)"/sd2snes/memtest", 0x20000, 0); set_pwr_led(0); while(1); /* END OF DRAGONS */ } /* Make sure the watchdog is disabled as soon as possible */ /* Copy this code to your bootloader if you use one and your */ /* MCU doesn't disable the WDT after reset! */ void get_mcusr(void) \ __attribute__((naked)) \ __attribute__((section(".init3"))); void get_mcusr(void) { MCUSR = 0; wdt_disable(); } #ifdef CONFIG_MEMPOISON void poison_memory(void) \ __attribute__((naked)) \ __attribute__((section(".init1"))); void poison_memory(void) { register uint16_t i; register uint8_t *ptr; asm("clr r1\n"); /* There is no RAMSTARt variable =( */ if (RAMEND > 2048 && RAMEND < 4096) { /* 2K memory */ ptr = (void *)RAMEND-2047; for (i=0;i<2048;i++) ptr[i] = 0x55; } else if (RAMEND > 4096 && RAMEND < 8192) { /* 4K memory */ ptr = (void *)RAMEND-4095; for (i=0;i<4096;i++) ptr[i] = 0x55; } else { /* Assume 8K memory */ ptr = (void *)RAMEND-8191; for (i=0;i<8192;i++) ptr[i] = 0x55; } } #endif #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 1) int main(void) __attribute__((OS_main)); #endif int main(void) { #if defined __AVR_ATmega644__ || defined __AVR_ATmega644P__ || defined __AVR_ATmega2561__ asm volatile("in r24, %0\n" "ori r24, 0x80\n" "out %0, r24\n" "out %0, r24\n" : : "I" (_SFR_IO_ADDR(MCUCR)) : "r24" ); #elif defined __AVR_ATmega32__ asm volatile ("in r24, %0\n" "ori r24, 0x80\n" "out %0, r24\n" "out %0, r24\n" : : "I" (_SFR_IO_ADDR(MCUCSR)) : "r24" ); #elif defined __AVR_ATmega128__ || defined __AVR_ATmega1281__ /* Just assume that JTAG doesn't hurt us on the m128 */ #else # error Unknown chip! #endif #ifdef CLOCK_PRESCALE clock_prescale_set(CLOCK_PRESCALE); #endif set_pwr_led(0); set_busy_led(1); spi_none(); snes_reset(1); uart_init(); sei(); _delay_ms(100); disk_init(); snes_init(); /* timer_init(); */ uart_puts_P(PSTR("\nsd2snes " VERSION)); uart_putcrlf(); file_init(); FATFS fatfs; f_mount(0,&fatfs); uart_putc('W'); fpga_init(); fpga_pgm((uint8_t*)"/sd2snes/main.bit"); _delay_ms(100); set_pwr_led(1); fpga_spi_init(); uart_putc('!'); _delay_ms(100); restart: set_avr_ena(0); snes_reset(1); *fs_path=0; uint16_t saved_dir_id; get_db_id(&saved_dir_id); uint16_t mem_dir_id = sram_readshort(SRAM_DIRID); uint32_t mem_magic = sram_readlong(SRAM_SCRATCHPAD); while(0) { SD_SPI_OFFLOAD=0; set_avr_addr(0L); sd_read(0, file_buf, 0L, 1); uart_trace((void*)file_buf, 0, 0x200); // sram_writeblock((void*)file_buf, 0, 0x200); // sram_hexdump(0,0x200); uart_putc('+'); } /* here be strange monsters */ while(0){ // uint16_t hurdur1 = 0, hurdur2 = 0; spiTransferByte(0x00); spiTransferByte(0x00); spiTransferByte(0x00); spiTransferByte(0x00); spiTransferByte(0x00); spiTransferByte(0x00); spiTransferByte(0x00); spiTransferByte(0x00); spiTransferByte(0x00); spiTransferByte(0x00); spiTransferByte(0x00); spiTransferByte(0x00); spiTransferByte(0x00); PORTB |= _BV(PB2); DDRB |= _BV(PB2); PORTB &= ~_BV(PB2); DDRB &= ~_BV(PB7); // tristate SCK PORTB |= _BV(PB2); DDRB &= ~_BV(PB2); while(!(PINB & _BV(PB2))) { } DDRB |= _BV(PB7); _delay_ms(1); // dprintf("hurdur1=%d hurdur2=%d\n", hurdur1, hurdur2); } if((mem_magic != 0x12345678) || (mem_dir_id != saved_dir_id)) { uint16_t curr_dir_id = scan_dir(fs_path, 0, 0); // generate files footprint dprintf("curr dir id = %x\n", curr_dir_id); if((get_db_id(&saved_dir_id) != FR_OK) // no database? || saved_dir_id != curr_dir_id) { // files changed? // XXX dprintf("saved dir id = %x\n", saved_dir_id); dprintf("rebuilding database..."); _delay_ms(50); curr_dir_id = scan_dir(fs_path, 1, 0); // then rebuild database sram_writeblock(&curr_dir_id, SRAM_DB_ADDR, 2); uint32_t endaddr, direndaddr; sram_readblock(&endaddr, SRAM_DB_ADDR+4, 4); sram_readblock(&direndaddr, SRAM_DB_ADDR+8, 4); dprintf("%lx %lx\n", endaddr, direndaddr); save_sram((uint8_t*)"/sd2snes/sd2snes.db", endaddr-SRAM_DB_ADDR, SRAM_DB_ADDR); save_sram((uint8_t*)"/sd2snes/sd2snes.dir", direndaddr-(SRAM_DIR_ADDR), SRAM_DIR_ADDR); dprintf("done\n"); // sram_hexdump(SRAM_DB_ADDR, 0x400); } else { dprintf("saved dir id = %x\n", saved_dir_id); dprintf("different card, consistent db, loading db...\n"); load_sram((uint8_t*)"/sd2snes/sd2snes.db", SRAM_DB_ADDR); load_sram((uint8_t*)"/sd2snes/sd2snes.dir", SRAM_DIR_ADDR); } // save_sram((uint8_t*)"/debug.smc", 0x400000, 0); // uart_putc('['); // load_sram((uint8_t*)"/test.srm", SRAM_SAVE_ADDR); // uart_putc(']'); sram_writeshort(curr_dir_id, SRAM_DIRID); sram_writelong(0x12345678, SRAM_SCRATCHPAD); } else { dprintf("same card, loading db...\n"); load_sram((uint8_t*)"/sd2snes/sd2snes.db", SRAM_DB_ADDR); load_sram((uint8_t*)"/sd2snes/sd2snes.dir", SRAM_DIR_ADDR); } led_pwm(); // sram_hexdump(0, 0x200); uart_putc('('); load_rom((uint8_t*)"/sd2snes/menu.bin", SRAM_MENU_ADDR); set_rom_mask(0x3fffff); // force mirroring off set_avr_mapper(0x7); // menu mapper XXX uart_putc(')'); uart_putcrlf(); // sram_hexdump(0x7ffff0, 0x10); // sram_hexdump(0, 0x400); // save_sram((uint8_t*)"/sd2snes/dump", 65536, 0); sram_writebyte(0, SRAM_CMD_ADDR); set_busy_led(0); set_avr_ena(1); _delay_ms(100); uart_puts_P(PSTR("SNES GO!\r\n")); snes_reset(0); // writetest(); /* snes_reset(1); set_avr_ena(0); led_std(); set_busy_led(1); save_sram((uint8_t*)"/sd2snes/dump", 65536, SRAM_MENU_ADDR); set_busy_led(0); set_avr_ena(1); snes_reset(0); */ uint8_t cmd = 0; while(!sram_reliable()); while(!cmd) { cmd=menu_main_loop(); switch(cmd) { case 0x01: // SNES_CMD_LOADROM: get_selected_name(file_lfn); _delay_ms(100); // snes_reset(1); set_avr_ena(0); dprintf("Selected name: %s\n", file_lfn); load_rom(file_lfn, SRAM_ROM_ADDR); // save_sram((uint8_t*)"/sd2snes/test.smc", romprops.romsize_bytes, 0); if(romprops.ramsize_bytes) { strcpy(strrchr((char*)file_lfn, (int)'.'), ".srm"); dprintf("SRM file: %s\n", file_lfn); load_sram(file_lfn, SRAM_SAVE_ADDR); } else { dprintf("No SRAM\n"); } set_avr_ena(1); snes_reset(1); _delay_ms(100); snes_reset(0); break; default: dprintf("unknown cmd: %d\n", cmd); cmd=0; // unknown cmd: stay in loop break; } } dprintf("cmd was %x, going to snes main loop\n", cmd); led_std(); cmd=0; uint8_t snes_reset_prev=0, snes_reset_now=0, snes_reset_state=0; uint16_t reset_count=0; while(fpga_test() == FPGA_TEST_TOKEN) { snes_reset_now=get_snes_reset(); if(snes_reset_now) { if(!snes_reset_prev) { dprintf("RESET BUTTON DOWN\n"); snes_reset_state=1; // reset reset counter reset_count=0; } } else { if(snes_reset_prev) { dprintf("RESET BUTTON UP\n"); snes_reset_state=0; } } if(snes_reset_state) { _delay_ms(10); reset_count++; } else { sram_reliable(); snes_main_loop(); } if(reset_count>100) { reset_count=0; led_std(); set_avr_ena(0); snes_reset(1); _delay_ms(100); if(romprops.ramsize_bytes && fpga_test() == 0xa5) { set_busy_led(1); save_sram(file_lfn, romprops.ramsize_bytes, SRAM_SAVE_ADDR); set_busy_led(0); } _delay_ms(1000); set_busy_led(1); goto restart; } snes_reset_prev = snes_reset_now; } // FPGA TEST FAIL. PANIC. led_panic(); /* HERE BE LIONS */ while(1) { set_avr_addr(0x600000); spi_fpga(); spiTransferByte(0x81); // read w/ increment... hopefully spiTransferByte(0x00); // 1 dummy read uart_putcrlf(); uint8_t buff[21]; for(uint8_t cnt=0; cnt<21; cnt++) { uint8_t data=spiTransferByte(0x00); buff[cnt]=data; } for(uint8_t cnt=0; cnt<21; cnt++) { uint8_t data = buff[cnt]; _delay_ms(2); if(data>=0x20 && data <= 0x7a) { uart_putc(data); } else { // uart_putc('.'); uart_putc("0123456789ABCDEF"[data>>4]); uart_putc("0123456789ABCDEF"[data&15]); uart_putc(' '); } // set_avr_bank(3); } spi_none(); } while(1); }