소스 검색

mk2 fw wip

ikari 13 년 전
부모
커밋
fa28f6035e
28개의 변경된 파일1760개의 추가작업 그리고 153개의 파일을 삭제
  1. 4 1
      src/Makefile
  2. 17 0
      src/config.h
  3. 51 0
      src/crc16.c
  4. 54 0
      src/crc16.h
  5. 20 0
      src/faulthandler.c
  6. 1 0
      src/fileops.h
  7. 51 29
      src/filetypes.c
  8. 3 0
      src/filetypes.h
  9. 152 0
      src/fpga.c
  10. 74 0
      src/fpga.h
  11. 99 0
      src/fpga_spi.c
  12. 59 0
      src/fpga_spi.h
  13. 16 0
      src/led.c
  14. 3 0
      src/led.h
  15. 207 8
      src/main.c
  16. 329 0
      src/memory.c
  17. 63 0
      src/memory.h
  18. 1 0
      src/printf.c
  19. 20 20
      src/sdcard.c
  20. 5 0
      src/sdcard.h
  21. 173 0
      src/smc.c
  22. 62 0
      src/smc.h
  23. 17 28
      src/snes.c
  24. 4 0
      src/snes.h
  25. 125 0
      src/sort.c
  26. 15 0
      src/sort.h
  27. 119 60
      src/spi.c
  28. 16 7
      src/spi.h

+ 4 - 1
src/Makefile

@@ -55,7 +55,7 @@ TARGET = $(OBJDIR)/sd2snes
 
 
 # List C source files here. (C dependencies are automatically generated.)
-SRC = main.c ff.c ccsbcs.c clock.c uart.c power.c led.c timer.c printf.c sdcard.c spi.c fileops.c rtc.c fpga.c
+SRC = main.c ff.c ccsbcs.c clock.c uart.c power.c led.c timer.c printf.c sdcard.c spi.c fileops.c rtc.c fpga.c fpga_spi.c snes.c smc.c memory.c crc16.c filetypes.c faulthandler.c sort.c
 
 
 # List Assembler source files here.
@@ -218,6 +218,9 @@ program: bin
 debug: bin
 	openocd -f openocd-usb.cfg -f lpc1754.cfg
 
+reset:
+	openocd -f openocd-usb.cfg -f lpc1754.cfg -f reset.cfg
+
 # Display size of file.
 HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex
 ELFSIZE = $(SIZE) -A $(TARGET).elf

+ 17 - 0
src/config.h

@@ -1,6 +1,8 @@
 #ifndef _CONFIG_H
 #define _CONFIG_H
 
+#define IN_AHBRAM    __attribute__ ((section(".ahbram")))
+
 #define SDCARD_DETECT		(1)
 #define SDCARD_WP		(0)
 #define SD_SUPPLY_VOLTAGE	(1L<<21) /* 3.3V - 3.4V */
@@ -15,4 +17,19 @@
 #define CONFIG_UART_TX_BUF_SHIFT	5
 #define CONFIG_UART_BAUDRATE		921600
 #define CONFIG_UART_DEADLOCKABLE
+
+#define SPI_SD				1
+#define SPI_FPGA			0
+
+#define SSP_CLK_DIVISOR_FAST 4
+#define SSP_CLK_DIVISOR_SLOW 250
+#define SSP_CLK_DIVISOR_FPGA_FAST 6
+#define SSP_CLK_DIVISOR_FPGA_SLOW 16
+
+#define SNES_RESET_REG  LPC_GPIO1
+#define SNES_RESET_BIT  29
+// Rev.B: 26
+
+#define QSORT_MAXELEM   1024
+
 #endif

+ 51 - 0
src/crc16.c

@@ -0,0 +1,51 @@
+/**
+ * \file stdout
+ * Functions and types for CRC checks.
+ *
+ * Generated on Tue Jun 30 23:02:59 2009,
+ * by pycrc v0.7.1, http://www.tty1.net/pycrc/
+ * using the configuration:
+ *    Width        = 16
+ *    Poly         = 0x8005
+ *    XorIn        = 0x0000
+ *    ReflectIn    = True
+ *    XorOut       = 0x0000
+ *    ReflectOut   = True
+ *    Algorithm    = bit-by-bit-fast
+ *    Direct       = True
+ *****************************************************************************/
+#include <stdint.h>
+#include "crc16.h"
+/**
+ * Update the crc value with new data.
+ *
+ * \param crc      The current crc value.
+ * \param data     Pointer to a buffer of \a data_len bytes.
+ * \param data_len Number of bytes in the \a data buffer.
+ * \return         The updated crc value.
+ *****************************************************************************/
+crc_t crc16_update(crc_t crc, const unsigned char *data, size_t data_len)
+{
+    unsigned int i;
+    uint8_t bit;
+    unsigned char c;
+
+    while (data_len--) {
+        c = *data++;
+        for (i = 0x01; i & 0xff; i <<= 1) {
+            bit = (crc & 0x8000 ? 1 : 0);
+            if (c & i) {
+                bit ^= 1;
+            }
+            crc <<= 1;
+            if (bit) {
+                crc ^= 0x8005;
+            }
+        }
+        crc &= 0xffff;
+    }
+    return crc & 0xffff;
+}
+
+
+

+ 54 - 0
src/crc16.h

@@ -0,0 +1,54 @@
+/**
+ * \file stdout
+ * Functions and types for CRC checks.
+ *
+ * Generated on Tue Jun 30 23:02:56 2009,
+ * by pycrc v0.7.1, http://www.tty1.net/pycrc/
+ * using the configuration:
+ *    Width        = 16
+ *    Poly         = 0x8005
+ *    XorIn        = 0x0000
+ *    ReflectIn    = True
+ *    XorOut       = 0x0000
+ *    ReflectOut   = True
+ *    Algorithm    = bit-by-bit-fast
+ *    Direct       = True
+ *****************************************************************************/
+#ifndef __STDOUT__
+#define __STDOUT__
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The definition of the used algorithm.
+ *****************************************************************************/
+#define CRC_ALGO_BIT_BY_BIT_FAST 1
+
+/**
+ * The type of the CRC values.
+ *
+ * This type must be big enough to contain at least 16 bits.
+ *****************************************************************************/
+typedef uint16_t crc_t;
+
+/**
+ * Update the crc value with new data.
+ *
+ * \param crc      The current crc value.
+ * \param data     Pointer to a buffer of \a data_len bytes.
+ * \param data_len Number of bytes in the \a data buffer.
+ * \return         The updated crc value.
+ *****************************************************************************/
+crc_t crc16_update(crc_t crc, const unsigned char *data, size_t data_len);
+
+#ifdef __cplusplus
+}           /* closing brace for extern "C" */
+#endif
+
+#endif      /* __STDOUT__ */
+

+ 20 - 0
src/faulthandler.c

@@ -0,0 +1,20 @@
+#include <arm/NXP/LPC17xx/LPC17xx.h>
+#include "uart.h"
+
+void HardFault_Handler(void) {
+  printf("HFSR: %lx\n", SCB->HFSR);
+  while (1) ;
+}
+
+void MemManage_Handler(void) {
+  printf("MemManage - CFSR: %lx; MMFAR: %lx\n", SCB->CFSR, SCB->MMFAR);
+}
+
+void BusFault_Handler(void) {
+  printf("BusFault - CFSR: %lx; BFAR: %lx\n", SCB->CFSR, SCB->BFAR);
+}
+
+void UsageFault_Handler(void) {
+  printf("UsageFault - CFSR: %lx; BFAR: %lx\n", SCB->CFSR, SCB->BFAR);
+}
+

+ 1 - 0
src/fileops.h

@@ -26,6 +26,7 @@
 
 #ifndef FILEOPS_H
 #define FILEOPS_H
+#include <arm/NXP/LPC17xx/LPC17xx.h>
 #include "ff.h"
 
 BYTE file_buf[512];

+ 51 - 29
src/filetypes.c

@@ -26,8 +26,6 @@
 
 #include <stdio.h>
 #include <string.h>
-#include <avr/pgmspace.h>
-#include <util/delay.h>
 #include "config.h"
 #include "uart.h"
 #include "filetypes.h"
@@ -37,13 +35,14 @@
 #include "crc16.h"
 #include "memory.h"
 #include "led.h"
+#include "sort.h"
 
 uint16_t scan_flat(const char* path) {
 	DIR dir;
 	FRESULT res;
 	FILINFO fno;
-	fno.lfn = NULL;
-	res = f_opendir(&dir, (unsigned char*)path);
+	fno.lfname = NULL;
+	res = f_opendir(&dir, (TCHAR*)path);
 	uint16_t numentries = 0;
 	if (res == FR_OK) {
 		for (;;) {
@@ -60,7 +59,7 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) {
 	FILINFO fno;
 	FRESULT res;
 	uint8_t len;
-	unsigned char* fn;
+	TCHAR* fn;
 	static unsigned char depth = 0;
 	static uint16_t crc;
 	static uint32_t db_tgt;
@@ -81,10 +80,11 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) {
 		next_subdir_tgt = SRAM_DIR_ADDR;
 		this_dir_tgt = SRAM_DIR_ADDR;
 		parent_tgt = 0;
-		dprintf("root dir @%lx\n", dir_tgt);
+		printf("root dir @%lx\n", dir_tgt);
 	}	
 	
-	fno.lfn = file_lfn;
+	fno.lfsize = 255;
+	fno.lfname = (TCHAR*)file_lfn;
 	numentries=0;
 	for(pass = 0; pass < 2; pass++) {
 		if(pass) {
@@ -95,24 +95,24 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) {
 			if(next_subdir_tgt > dir_end) {
 				dir_end = next_subdir_tgt;
 			}
-			dprintf("path=%s depth=%d ptr=%lx entries=%d parent=%lx next subdir @%lx\n", path, depth, db_tgt, numentries, parent_tgt, next_subdir_tgt /*dir_tgt_next*/);
+			printf("path=%s depth=%d ptr=%lx entries=%d parent=%lx next subdir @%lx\n", path, depth, db_tgt, numentries, parent_tgt, next_subdir_tgt /*dir_tgt_next*/);
 //			_delay_ms(50);
 			if(mkdb) {
-				dprintf("d=%d Saving %lX to Address %lX  [end]\n", depth, 0L, next_subdir_tgt - 4);
+				printf("d=%d Saving %lx to Address %lx  [end]\n", depth, 0L, next_subdir_tgt - 4);
 //				_delay_ms(50);	
 				sram_writelong(0L, next_subdir_tgt - 4);
 			}
 		}
-		res = f_opendir(&dir, (unsigned char*)path);
+		res = f_opendir(&dir, (TCHAR*)path);
 		if (res == FR_OK) {
 			if(pass && parent_tgt && mkdb) {
 				// write backlink to parent dir
 				// switch to next bank if record does not fit in current bank
 				if((db_tgt&0xffff) > ((0x10000-(sizeof(next_subdir_tgt)+sizeof(len)+4))&0xffff)) {
-					dprintf("switch! old=%lx ", db_tgt);
+					printf("switch! old=%lx ", db_tgt);
 					db_tgt &= 0xffff0000;
 					db_tgt += 0x00010000;
-					dprintf("new=%lx\n", db_tgt);
+					printf("new=%lx\n", db_tgt);
 				}
 				sram_writelong((parent_tgt-SRAM_MENU_ADDR), db_tgt);
 				sram_writebyte(0, db_tgt+sizeof(next_subdir_tgt));
@@ -123,7 +123,7 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) {
 			}
 			len = strlen((char*)path);
 			for (;;) {
-				toggle_busy_led();
+				toggle_read_led();
 				res = f_readdir(&dir, &fno);
 				if (res != FR_OK || fno.fname[0] == 0) {
 					if(pass) {
@@ -131,8 +131,8 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) {
 					}
 					break;
 				}
-				fn = *fno.lfn ? fno.lfn : fno.fname;
-	//			dprintf("%s\n", fn);
+				fn = *fno.lfname ? fno.lfname : fno.fname;
+//				printf("%s\n", fn);
 	//			_delay_ms(100);
 				if (*fn == '.') continue;
 				if (fno.fattrib & AM_DIR) {
@@ -144,7 +144,7 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) {
 						if(mkdb) {
 							uint16_t pathlen = strlen(path);
 							// write element pointer to current dir structure
-							dprintf("d=%d Saving %lX to Address %lX  [dir]\n", depth, db_tgt, dir_tgt);
+							printf("d=%d Saving %lx to Address %lx  [dir]\n", depth, db_tgt, dir_tgt);
 //							_delay_ms(50);
 							sram_writelong((db_tgt-SRAM_MENU_ADDR)|((uint32_t)0x80<<24), dir_tgt);
 //							sram_writeblock((uint8_t*)&db_tgt, dir_tgt_save, sizeof(dir_tgt_save));
@@ -153,12 +153,12 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) {
 							//  - path name
 							//  - pointer to sub dir structure
 							if((db_tgt&0xffff) > ((0x10000-(sizeof(next_subdir_tgt) + sizeof(len) + pathlen + 2))&0xffff)) {
-								dprintf("switch! old=%lx ", db_tgt);
+								printf("switch! old=%lx ", db_tgt);
 								db_tgt &= 0xffff0000;
 								db_tgt += 0x00010000;
-								dprintf("new=%lx\n", db_tgt);
+								printf("new=%lx\n", db_tgt);
 							}
-							dprintf("    Saving dir descriptor to %lX, tgt=%lX, path=%s\n", db_tgt, next_subdir_tgt, path);
+							printf("    Saving dir descriptor to %lx tgt=%lx, path=%s\n", db_tgt, next_subdir_tgt, path);
 //							_delay_ms(100);
 							sram_writelong((next_subdir_tgt-SRAM_MENU_ADDR), db_tgt);
 							sram_writebyte(len+1, db_tgt+sizeof(next_subdir_tgt));
@@ -189,20 +189,20 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) {
 									case TYPE_SMC:
 // XXX										file_open_by_filinfo(&fno);
 // XXX										if(file_res){
-// XXX											dprintf("ZOMG NOOOO %d\n", file_res);
+// XXX											printf("ZOMG NOOOO %d\n", file_res);
 // XXX											_delay_ms(30);
 // XXX										}
 // XXX										smc_id(&romprops);
 // XXX										file_close();
 		//								_delay_ms(30);
 										// write element pointer to current dir structure
-//										dprintf("d=%d Saving %lX to Address %lX  [file]\n", depth, db_tgt, dir_tgt);
+//										printf("d=%d Saving %lX to Address %lX  [file]\n", depth, db_tgt, dir_tgt);
 //										_delay_ms(50);
 										if((db_tgt&0xffff) > ((0x10000-(sizeof(romprops) + sizeof(len) + pathlen + 1))&0xffff)) {
-											dprintf("switch! old=%lx ", db_tgt);
+											printf("switch! old=%lx ", db_tgt);
 											db_tgt &= 0xffff0000;
 											db_tgt += 0x00010000;
-											dprintf("new=%lx\n", db_tgt);
+											printf("new=%lx\n", db_tgt);
 										}
 										sram_writelong((db_tgt-SRAM_MENU_ADDR), dir_tgt);
 //										sram_writeblock((uint8_t*)&db_tgt, dir_tgt, sizeof(db_tgt));
@@ -220,23 +220,23 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) {
 										break;
 								}
 								path[len]=0;
-		//						dprintf("%s ", path);
+		//						printf("%s ", path);
 		//						_delay_ms(30);
 							}
 						} else {
-							unsigned char* sfn = fno.fname;
+							TCHAR* sfn = fno.fname;
 							while(*sfn != 0) {
-								crc += crc16_update(crc, sfn++, 1);
+								crc += crc16_update(crc, (unsigned char*)sfn++, 1);
 							}
 						}
 					}
-	//					dprintf("%s/%s\n", path, fn);
+	//					printf("%s/%s\n", path, fn);
 	//					_delay_ms(50);
 				}
 			}
 		} else uart_putc(0x30+res);
 	}
-//	dprintf("%x\n", crc);
+//	printf("%x\n", crc);
 //	_delay_ms(50);
 	sram_writelong(db_tgt, SRAM_DB_ADDR+4);
 	sram_writelong(dir_end, SRAM_DB_ADDR+8);
@@ -248,7 +248,7 @@ SNES_FTYPE determine_filetype(char* filename) {
 	char* ext = strrchr(filename, '.');
 	if(ext == NULL)
 		return TYPE_UNKNOWN;
-	if(!strcasecmp_P(ext+1, PSTR("SMC"))) {
+	if(!strcasecmp(ext+1, "SMC")) {
 		return TYPE_SMC;
 	}/* later
 	if(!strcasecmp_P(ext+1, PSTR("SRM"))) {
@@ -271,3 +271,25 @@ FRESULT get_db_id(uint16_t* id) {
 	}
 	return file_res;
 }
+
+int get_num_dirent(uint32_t addr) {
+  int result = 0;
+  while(sram_readlong(addr+result*4)) {
+    result++;
+  }
+  return result;
+}
+
+void sort_all_dir(uint32_t endaddr) {
+  uint32_t entries = 0;
+  uint32_t current_base = SRAM_DIR_ADDR;
+  while(current_base<(endaddr)) {
+    while(sram_readlong(current_base+entries*4)) {
+      entries++;
+    }
+    printf("sorting dir @%lx, entries: %ld\n", current_base, entries);
+    sort_dir(current_base, entries);
+    current_base += 4*entries + 4;
+    entries = 0;
+  }
+}

+ 3 - 0
src/filetypes.h

@@ -39,7 +39,10 @@ typedef enum {
 char fs_path[256];
 SNES_FTYPE determine_filetype(char* filename);
 //uint32_t scan_fs();
+uint16_t scan_flat(const char* path);
 uint16_t scan_dir(char* path, char mkdb, uint32_t this_subdir_tgt);
 FRESULT get_db_id(uint16_t*);
+int get_num_dirent(uint32_t addr);
+void sort_all_dir(uint32_t endaddr);
 
 #endif

+ 152 - 0
src/fpga.c

@@ -0,0 +1,152 @@
+/* sd2snes - SD card based universal cartridge for the SNES
+   Copyright (C) 2009-2010 Maximilian Rehkopf <otakon@gmx.net>
+   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.c: FPGA (re)configuration
+*/
+
+
+/*
+   FPGA pin mapping
+   ================
+   CCLK        P0.11 out
+   PROG_B      P1.10 out
+   INIT_B      P2.9  in
+   DIN         P2.8  out
+   DONE        P0.22 in
+ */
+
+#include <arm/NXP/LPC17xx/LPC17xx.h>
+#include "bits.h"
+
+#include "fpga.h"
+#include "fpga_spi.h"
+#include "config.h"
+#include "uart.h"
+#include "sdcard.h"
+#include "diskio.h"
+#include "integer.h"
+#include "ff.h"
+#include "fileops.h"
+// XXX #include "fpga_spi.h"
+#include "spi.h"
+#include "led.h"
+#include "timer.h"
+
+/*DWORD get_fattime(void) {
+	return 0L;
+}*/
+void fpga_set_prog_b(uint8_t val) {
+	if(val)
+	  BITBAND(PROGBREG->FIOSET, PROGBBIT) = 1;
+	else
+	  BITBAND(PROGBREG->FIOCLR, PROGBBIT) = 1;
+}
+
+void fpga_set_cclk(uint8_t val) {
+	if(val)
+	  BITBAND(CCLKREG->FIOSET, CCLKBIT) = 1;
+	else
+	  BITBAND(CCLKREG->FIOCLR, CCLKBIT) = 1;
+}
+
+int fpga_get_initb() {
+	return BITBAND(INITBREG->FIOPIN, INITBBIT);
+}
+
+void fpga_init() {
+/* mainly GPIO directions */
+	BITBAND(CCLKREG->FIODIR, CCLKBIT) = 1; /* CCLK */
+	BITBAND(DONEREG->FIODIR, DONEBIT) = 0; /* DONE */
+	BITBAND(PROGBREG->FIODIR, PROGBBIT) = 1; /* PROG_B */
+	BITBAND(DINREG->FIODIR, DINBIT) = 1; /* DIN */
+	BITBAND(INITBREG->FIODIR, INITBBIT) = 0; /* INIT_B */
+
+	LPC_GPIO2->FIOMASK1 = 0;
+
+	SPI_OFFLOAD=0;
+	fpga_set_cclk(0);    /* initial clk=0 */
+}
+
+int fpga_get_done(void) {
+	return BITBAND(LPC_GPIO0->FIOPIN, 22);
+}
+
+void fpga_postinit() {
+	LPC_GPIO2->FIOMASK1 = 0;
+}
+
+void fpga_pgm(uint8_t* filename) {
+	int MAXRETRIES = 10;
+	int retries = MAXRETRIES;
+	do {
+		fpga_set_prog_b(0);
+		uart_putc('P');
+		fpga_set_prog_b(1);
+		while(!fpga_get_initb());
+		LPC_GPIO2->FIOMASK1 = ~(BV(0));
+		uart_putc('p');
+		
+		UINT bytes_read;
+
+		// open configware file
+		file_open(filename, FA_READ);
+		if(file_res) {
+			uart_putc('?');
+			uart_putc(0x30+file_res);
+			return;
+		}
+	
+		for (;;) {
+			bytes_read = file_read();
+			if (file_res || bytes_read == 0) break;   // error or eof
+			for(int i=0; i<bytes_read; i++) {
+				FPGA_SEND_BYTE_SERIAL(file_buf[i]);
+			}
+		}
+		file_close();
+	} while (!fpga_get_done() && retries--);
+	if(!fpga_get_done()) {
+		printf("FPGA failed to configure after %d tries.\n", MAXRETRIES);
+		delay_ms(50);
+		led_panic();
+	}
+	printf("FPGA configured\n");
+	fpga_postinit();
+}
+
+void set_mcu_ovr(uint8_t val) {
+	if(!val) { // shared mode
+		FPGA_SPI_SLOW();
+		SET_MCU_OVR();
+		// Disable SPI double speed mode -> clock = f/4
+// XXX		SPSR = 0;
+		printf("SPI slow\n");
+	} else { // mcu only
+		FPGA_SPI_FAST();
+		CLR_MCU_OVR();
+		// Enable SPI double speed mode -> clock = f/2
+// XXX		SPSR = _BV(SPI2X);
+		printf("SPI fast\n");
+	}
+}
+

+ 74 - 0
src/fpga.h

@@ -0,0 +1,74 @@
+/* sd2snes - SD card based universal cartridge for the SNES
+   Copyright (C) 2009-2010 Maximilian Rehkopf <otakon@gmx.net>
+   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.h: FPGA (re)configuration
+*/
+
+#ifndef FPGA_H
+#define FPGA_H
+
+#include <arm/NXP/LPC17xx/LPC17xx.h>
+#include "bits.h"
+
+void fpga_set_prog_b(uint8_t val);
+void fpga_set_cclk(uint8_t val);
+int fpga_get_initb(void);
+
+void fpga_init(void);
+uint8_t fpga_test(void);
+void fpga_postinit(void);
+void fpga_pgm(uint8_t* filename);
+
+void set_mcu_ovr(uint8_t val);
+
+
+uint8_t SPI_OFFLOAD;
+
+#define CCLKREG  LPC_GPIO0
+#define PROGBREG LPC_GPIO1
+#define INITBREG LPC_GPIO2
+#define DINREG   LPC_GPIO2
+#define DONEREG  LPC_GPIO0
+
+#define CCLKBIT  (11)
+#define PROGBBIT (10)
+#define INITBBIT (9)
+#define DINBIT   (8)
+#define DONEBIT  (22)
+
+
+#define FPGA_TEST_TOKEN	(0xa5)
+
+// some macros for bulk transfers (faster)
+#define FPGA_SEND_BYTE_SERIAL(data)	do {SET_FPGA_DIN(data>>7); CCLK();\
+SET_FPGA_DIN(data>>6); CCLK(); SET_FPGA_DIN(data>>5); CCLK();\
+SET_FPGA_DIN(data>>4); CCLK(); SET_FPGA_DIN(data>>3); CCLK();\
+SET_FPGA_DIN(data>>2); CCLK(); SET_FPGA_DIN(data>>1); CCLK();\
+SET_FPGA_DIN(data); CCLK();} while (0)
+#define SET_CCLK()			do {BITBAND(LPC_GPIO0->FIOSET, 11) = 1;} while (0)
+#define CLR_CCLK()			do {BITBAND(LPC_GPIO0->FIOCLR, 11) = 1;} while (0)
+#define CCLK()				do {SET_CCLK(); CLR_CCLK();} while (0)
+#define SET_MCU_OVR()			do {BITBAND(LPC_GPIO2->FIOSET, 8) = 1;} while (0)
+#define CLR_MCU_OVR()			do {BITBAND(LPC_GPIO2->FIOCLR, 8) = 1;} while (0)
+#define SET_FPGA_DIN(data)		do {LPC_GPIO2->FIOPIN1 = data;} while (0)
+#endif

+ 99 - 0
src/fpga_spi.c

@@ -0,0 +1,99 @@
+/* sd2snes - SD card based universal cartridge for the SNES
+   Copyright (C) 2009-2010 Maximilian Rehkopf <otakon@gmx.net>
+   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	
+   =============================================
+	00	bb[hh[ll]]	set address to 0xbb0000, 0xbbhh00, or 0xbbhhll
+	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, 7=Menu
+	80	-		read with increment
+	81	-		read w/o increment
+	90	{xx}*		write xx with increment
+	91	{xx}*		write xx w/o increment
+	F0	-		receive test token (to see if FPGA is alive)
+	
+*/
+
+#include <arm/NXP/LPC17xx/LPC17xx.h>
+#include "bits.h"
+#include "fpga.h"
+#include "config.h"
+#include "uart.h"
+#include "spi.h"
+#include "fpga_spi.h"
+#include "timer.h"
+
+void fpga_spi_init(void) {
+	spi_init(SPI_SPEED_FPGA_FAST, SPI_FPGA);
+}
+
+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
+	FPGA_TX_BYTE(0x00); // dummy
+	uint8_t result = FPGA_RX_BYTE();
+	FPGA_DESELECT();
+        return result;
+}
+

+ 59 - 0
src/fpga_spi.h

@@ -0,0 +1,59 @@
+/* sd2snes - SD card based universal cartridge for the SNES
+   Copyright (C) 2009-2010 Maximilian Rehkopf <otakon@gmx.net>
+   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
+*/
+
+#ifndef _FPGA_SPI_H
+#define _FPGA_SPI_H
+
+#include <arm/NXP/LPC17xx/LPC17xx.h>
+#include "bits.h"
+
+#define FPGA_SS_BIT	16
+#define FPGA_SS_REG	LPC_GPIO0
+
+#define FPGA_SELECT()	do {FPGA_TX_SYNC(); BITBAND(FPGA_SS_REG->FIOCLR, FPGA_SS_BIT) = 1;} while (0)
+#define FPGA_DESELECT()	do {FPGA_TX_SYNC(); BITBAND(FPGA_SS_REG->FIOSET, FPGA_SS_BIT) = 1;} while (0)
+
+#define FPGA_TX_SYNC()     spi_tx_sync(SPI_FPGA)
+#define FPGA_TX_BYTE(x)    spi_tx_byte(x, SPI_FPGA)
+#define FPGA_RX_BYTE()     spi_rx_byte(SPI_FPGA)
+#define FPGA_TXRX_BYTE(x)  spi_txrx_byte(x, SPI_FPGA)
+#define FPGA_TX_BLOCK(x,y) spi_tx_block(x,y,SPI_FPGA)
+#define FPGA_RX_BLOCK(x,y) spi_rx_block(x,y,SPI_FPGA)
+
+#define FPGA_SPI_FAST()    spi_set_speed(SPI_SPEED_FPGA_FAST, SPI_FPGA)
+#define FPGA_SPI_SLOW()    spi_set_speed(SPI_SPEED_FPGA_SLOW, SPI_FPGA)
+
+void fpga_spi_init(void);
+void fpga_spi_test(void);
+void spi_fpga(void);
+void spi_sd(void);
+void spi_none(void);
+void set_mcu_addr(uint32_t);
+void set_saveram_mask(uint32_t);
+void set_rom_mask(uint32_t);
+void set_mapper(uint8_t val);
+
+#endif

+ 16 - 0
src/led.c

@@ -6,6 +6,8 @@
 #include "led.h"
 
 int led_rdyledstate = 0;
+int led_readledstate = 0;
+int led_writeledstate = 0;
 
 void rdyled(unsigned int state) {
   BITBAND(LPC_GPIO2->FIODIR, 0) = state;
@@ -14,6 +16,12 @@ void rdyled(unsigned int state) {
 
 void readled(unsigned int state) {
   BITBAND(LPC_GPIO2->FIODIR, 1) = state;
+  led_readledstate = state;
+}
+
+void writeled(unsigned int state) {
+  BITBAND(LPC_GPIO2->FIODIR, 2) = state;
+  led_writeledstate = state;
 }
 
 void led_clkout32(uint32_t val) {
@@ -31,6 +39,14 @@ void toggle_rdy_led() {
   rdyled(~led_rdyledstate);
 }
 
+void toggle_read_led() {
+  readled(~led_readledstate);
+}
+
+void toggle_write_led() {
+  writeled(~led_writeledstate);
+}
+
 void led_panic() {
   while(1) {
     LPC_GPIO2->FIODIR |= BV(0) | BV(1) | BV(2);

+ 3 - 0
src/led.h

@@ -4,9 +4,12 @@
 #define _LED_H
 
 void readled(unsigned int state);
+void writeled(unsigned int state);
 void rdyled(unsigned int state);
 void led_clkout32(uint32_t val);
 void toggle_rdy_led(void);
+void toggle_read_led(void);
+void toggle_write_led(void);
 void led_panic(void);
 
 #endif

+ 207 - 8
src/main.c

@@ -1,4 +1,5 @@
 #include <arm/NXP/LPC17xx/LPC17xx.h>
+#include <string.h>
 #include "config.h"
 #include "clock.h"
 #include "uart.h"
@@ -11,6 +12,12 @@
 #include "sdcard.h"
 #include "fileops.h"
 #include "fpga.h"
+#include "fpga_spi.h"
+#include "filetypes.h"
+#include "memory.h"
+#include "snes.h"
+#include "led.h"
+#include "sort.h"
 
 #define EMC0TOGGLE	(3<<4)
 #define MR0R		(1<<1)
@@ -23,21 +30,31 @@ volatile enum diskstates disk_state;
 int main(void) {
   LPC_GPIO2->FIODIR = BV(0) | BV(1) | BV(2);
   LPC_GPIO1->FIODIR = 0;
-  uint32_t p1;
+  LPC_GPIO0->FIODIR = BV(16);
 
- /* connect UART3 on P0[25:26] + SSP1 on P0[6:9] + MAT3.0 on P0[10] */
-  LPC_PINCON->PINSEL1=(0xf<<18);
-  LPC_PINCON->PINSEL0=BV(13) | BV(15) | BV(17) | BV(19) | BV(20) | BV(21);
+ /* connect UART3 on P0[25:26] + SSP0 on P0[15:18] SSP1 on P0[6:9] + MAT3.0 on P0[10] */
+  LPC_PINCON->PINSEL1 = BV(18) | BV(19) | BV(20) | BV(21) /* UART3 */
+                      | BV(3) | BV(5);                    /* SSP0 (FPGA) except SS */
+  LPC_PINCON->PINSEL0 = BV(31)                            /* SSP0 */
+                      | BV(13) | BV(15) | BV(17) | BV(19) /* SSP1 (SD) */
+                      | BV(20) | BV(21);                  /* MAT3.0 */
 
+ /* enable pull-downs for CIC data lines */
+  LPC_PINCON->PINMODE3 = BV(18) | BV(19) | BV(20) | BV(21);
   clock_disconnect();
-
+  snes_init();
+  snes_reset(1);
   power_init();
   timer_init();
   uart_init();
-  spi_init(SPI_SPEED_SLOW);
-  sd_init();
+  fpga_spi_init();
+  spi_preinit(SPI_FPGA);
+  spi_preinit(SPI_SD);
 /* do this last because the peripheral init()s change PCLK dividers */
   clock_init();
+
+  sd_init();
+  fpga_spi_init();
   delay_ms(10);
   printf("\n\nsd2snes mk.2\n============\ncpu clock: %d Hz\n", CONFIG_CPU_FREQUENCY);
   file_init();
@@ -57,12 +74,194 @@ int main(void) {
   LPC_TIM3->MCR=MR0R;
   LPC_TIM3->MR0=1;
   LPC_TIM3->TCR=1;
-  uart_puts("hurr durr derpderpderp\n");
   fpga_init();
   fpga_pgm((uint8_t*)"/sd2snes/main.bit");
+restart:
+  rdyled(1);
+  readled(0);
+  writeled(0);
+  set_mcu_ovr(1); // exclusive mode
+ 
+  *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);
+
+    if((mem_magic != 0x12345678) || (mem_dir_id != saved_dir_id)) {
+    uint16_t curr_dir_id = scan_dir(fs_path, 0, 0); // generate files footprint
+    printf("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
+      printf("saved dir id = %x\n", saved_dir_id);
+      printf("rebuilding database...");
+      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);
+      printf("%lx %lx\n", endaddr, direndaddr);
+      printf("sorting database...");
+      sort_all_dir(direndaddr);
+      printf("done\n");
+      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);
+      printf("done\n");
+//  sram_hexdump(SRAM_DB_ADDR, 0x400);
+    } else {
+      printf("saved dir id = %x\n", saved_dir_id);
+      printf("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 {
+    printf("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);
+  }
+
+  uart_putc('(');
+  load_rom((uint8_t*)"/sd2snes/menu.bin", SRAM_MENU_ADDR);
+  set_rom_mask(0x3fffff); // force mirroring off
+  set_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_mcu_ovr(0);
+
+  printf("SNES GO!\n");
+  snes_reset(0);
+
+  uint8_t cmd = 0;
+  printf("test sram\n");
+  while(!sram_reliable()) uart_puts("DERP");
+  printf("ok\n");
+
+/*  uint8_t *teststring=(uint8_t*)"Wer das liest ist doof! 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+  uint8_t testbuf[80];
+while(1) {
+  sram_writeblock(teststring, 0xd00000, 61);
+  sram_readblock(testbuf, 0xd00000, 61);
+  testbuf[60]=0;
+  printf("%p %s\n",testbuf, (char*)testbuf);
+} */
+
+  while(!cmd) {
+    cmd=menu_main_loop();
+    switch(cmd) {
+      case SNES_CMD_LOADROM:
+        get_selected_name(file_lfn);
+//                              snes_reset(1);
+        set_mcu_ovr(1);
+        printf("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");
+          printf("SRM file: %s\n", file_lfn);
+          load_sram(file_lfn, SRAM_SAVE_ADDR);
+        } else {
+          printf("No SRAM\n");
+        }
+        set_mcu_ovr(0);
+        snes_reset(1);
+        delay_ms(100);
+        snes_reset(0);
+        break;
+      case SNES_CMD_SETRTC:
+        break;
+      default:
+        printf("unknown cmd: %d\n", cmd);
+        cmd=0; // unknown cmd: stay in loop
+        break;
+    }
+  }
+
+  printf("cmd was %x, going to snes main loop\n", cmd);
+  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) {
+        printf("RESET BUTTON DOWN\n");
+        snes_reset_state=1;
+        // reset reset counter
+        reset_count=0;
+      }
+    } else {
+      if(snes_reset_prev) {
+        printf("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;
+      set_mcu_ovr(1);
+      snes_reset(1);
+      delay_ms(100);
+      if(romprops.ramsize_bytes && fpga_test() == 0xa5) {
+        writeled(1);
+        save_sram(file_lfn, romprops.ramsize_bytes, SRAM_SAVE_ADDR);
+        writeled(0);
+      }
+      delay_ms(1000);
+      goto restart;
+    }
+    snes_reset_prev = snes_reset_now;
+  }
+  // FPGA TEST FAIL. PANIC.
+  led_panic();
+
+while(1);
+}
+
+void teststuff(void) {
+  uint8_t i=0, data;
+  uint32_t p1;
+  sram_writebyte(0x5e, 0);
+  sram_writebyte(0x6f, 1);
+  sram_writelong(0x3451451e, 4);
+  sram_hexdump(0x0, 0x10);
+  sram_hexdump(0x620000, 0x200);
+
+while(i<10) {
+  FPGA_SELECT();
+  FPGA_TX_BYTE(0xff);
+  FPGA_TX_BYTE(0xff);
+  data=FPGA_TXRX_BYTE(i);
+  FPGA_DESELECT();
+  printf("%x %x\n", i, data);
+  i++;
+}
+  while(1) {
+//    if((data=fpga_test())!=0xa5) printf("Unexpected FPGA test token 0x%x\n", data);
+  }
 
   while (1) {
     p1 = LPC_GPIO1->FIOPIN;
     BITBAND(LPC_GPIO2->FIOPIN, 1) = (p1 & BV(29))>>29;
   }
+
 }

+ 329 - 0
src/memory.c

@@ -0,0 +1,329 @@
+/* sd2snes - SD card based universal cartridge for the SNES
+   Copyright (C) 2009-2010 Maximilian Rehkopf <otakon@gmx.net>
+   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
+
+   memory.c: RAM operations
+*/
+
+
+#include "config.h"
+#include "uart.h"
+#include "fpga.h"
+#include "crc16.h"
+#include "ff.h"
+#include "fileops.h"
+#include "spi.h"
+#include "fpga_spi.h"
+#include "led.h"
+#include "smc.h"
+#include "memory.h"
+#include "snes.h"
+#include "timer.h"
+
+char* hex = "0123456789ABCDEF";
+
+void sram_hexdump(uint32_t addr, uint32_t len) {
+	static uint8_t buf[16];
+	uint32_t ptr;
+	for(ptr=0; ptr < len; ptr += 16) {
+		sram_readblock((void*)buf, ptr+addr, 16);
+		uart_trace(buf, 0, 16);
+	}
+}
+
+void sram_writebyte(uint8_t val, uint32_t addr) {
+	set_mcu_addr(addr);
+	FPGA_SELECT();
+	FPGA_TX_BYTE(0x91); // WRITE
+	FPGA_TX_BYTE(val);
+	FPGA_TX_BYTE(0x00); // dummy
+	FPGA_DESELECT();
+}
+
+uint8_t sram_readbyte(uint32_t addr) {
+	set_mcu_addr(addr);
+	FPGA_SELECT();
+	FPGA_TX_BYTE(0x81); // READ
+	FPGA_TX_BYTE(0x00); // dummy
+	uint8_t val = FPGA_TXRX_BYTE(0x00);
+	FPGA_DESELECT();
+	return val;
+}
+
+void sram_writeshort(uint16_t val, uint32_t addr) {
+	set_mcu_addr(addr);
+	FPGA_SELECT();
+	FPGA_TX_BYTE(0x91); // WRITE
+	FPGA_TX_BYTE(val&0xff);		// 7-0
+	FPGA_TX_BYTE((val>>8)&0xff);		// 15-8
+	FPGA_TX_BYTE(0x00); // dummy
+	FPGA_DESELECT();
+}
+
+void sram_writelong(uint32_t val, uint32_t addr) {
+	set_mcu_addr(addr);
+	FPGA_SELECT();
+	FPGA_TX_BYTE(0x91); // WRITE
+	FPGA_TX_BYTE(val&0xff);		// 7-0
+	FPGA_TX_BYTE((val>>8)&0xff);	// 15-8
+	FPGA_TX_BYTE((val>>16)&0xff);	// 23-15
+	FPGA_TX_BYTE((val>>24)&0xff);	// 31-24
+	FPGA_TX_BYTE(0x00); // dummy
+	FPGA_DESELECT();
+}
+
+uint16_t sram_readshort(uint32_t addr) {
+	set_mcu_addr(addr);
+	FPGA_SELECT();
+	FPGA_TX_BYTE(0x81);
+	FPGA_TX_BYTE(0x00);
+
+	uint32_t val = FPGA_TXRX_BYTE(0x00);
+	val |= ((uint32_t)FPGA_TXRX_BYTE(0x00)<<8);
+	FPGA_DESELECT();
+	return val;
+}
+
+uint32_t sram_readlong(uint32_t addr) {
+	set_mcu_addr(addr);
+	FPGA_SELECT();
+	FPGA_TX_BYTE(0x81);
+	FPGA_TX_BYTE(0x00);
+	uint32_t val = FPGA_TXRX_BYTE(0x00);
+	val |= ((uint32_t)FPGA_TXRX_BYTE(0x00)<<8);
+	val |= ((uint32_t)FPGA_TXRX_BYTE(0x00)<<16);
+	val |= ((uint32_t)FPGA_TXRX_BYTE(0x00)<<24);
+	FPGA_DESELECT();
+	return val;
+}
+
+void sram_readlongblock(uint32_t* buf, uint32_t addr, uint16_t count) {
+	set_mcu_addr(addr);
+	FPGA_SELECT();
+	FPGA_TX_BYTE(0x81);
+	FPGA_TX_BYTE(0x00);
+        uint16_t i=0;
+	while(i<count) {
+	        uint32_t val = (uint32_t)FPGA_TXRX_BYTE(0x00)<<24;
+	        val |= ((uint32_t)FPGA_TXRX_BYTE(0x00)<<16);
+	        val |= ((uint32_t)FPGA_TXRX_BYTE(0x00)<<8);
+	        val |= FPGA_TXRX_BYTE(0x00);
+		buf[i++] = val;
+	}
+        FPGA_DESELECT();
+}
+
+void sram_readblock(void* buf, uint32_t addr, uint16_t size) {
+	uint16_t count=size;
+	uint8_t* tgt = buf;
+	set_mcu_addr(addr);
+	FPGA_SELECT();
+	FPGA_TX_BYTE(0x81);	// READ
+	FPGA_TX_BYTE(0x00);	// dummy
+	while(count--) {
+		*(tgt++) = FPGA_TXRX_BYTE(0x00);
+	}
+	FPGA_DESELECT();
+}
+
+void sram_writeblock(void* buf, uint32_t addr, uint16_t size) {
+	uint16_t count=size;
+	uint8_t* src = buf;
+	set_mcu_addr(addr);
+	FPGA_SELECT();
+	FPGA_TX_BYTE(0x91);	// WRITE 
+	while(count--) {
+		FPGA_TX_BYTE(*src++);
+	}
+	FPGA_TX_BYTE(0x00);	// dummy
+	FPGA_DESELECT();
+}
+
+uint32_t load_rom(uint8_t* filename, uint32_t base_addr) {
+//	uint8_t dummy;
+	UINT bytes_read;
+	DWORD filesize;
+	UINT count=0;
+	file_open(filename, FA_READ);
+	filesize = file_handle.fsize;
+	smc_id(&romprops);
+	set_mcu_addr(base_addr);
+	printf("no nervous breakdown beyond this point! or else!\n");
+	if(file_res) {
+		uart_putc('?');
+		uart_putc(0x30+file_res);
+		return 0;
+	}
+	f_lseek(&file_handle, romprops.offset);
+	FPGA_DESELECT();
+	for(;;) {
+//		SPI_OFFLOAD=1;
+		bytes_read = file_read();
+		if (file_res || !bytes_read) break;
+		if(!(count++ % 32)) {
+			toggle_read_led();
+//			bounce_busy_led();
+			uart_putc('.');
+		}
+		FPGA_SELECT();
+		FPGA_TX_BYTE(0x91); // write w/ increment
+		if(!(count++ % 8)) {
+//			toggle_busy_led();
+//			bounce_busy_led();
+			uart_putc('.');
+		}
+		for(int j=0; j<bytes_read; j++) {
+			FPGA_TX_BYTE(file_buf[j]);
+/*			SPDR = file_buf[j];
+			loop_until_bit_is_set(SPSR, SPIF);
+			dummy = SPDR;*/
+		}
+		FPGA_TX_BYTE(0x00); // dummy tx for increment+write pulse		*/
+		FPGA_DESELECT();
+	}
+	file_close();
+	set_mapper(romprops.mapper_id);
+	printf("rom header map: %02x; mapper id: %d\n", romprops.header.map, romprops.mapper_id);
+
+	uint32_t rammask;
+	uint32_t rommask;
+	
+	if(filesize > (romprops.romsize_bytes + romprops.offset)) {
+		romprops.romsize_bytes <<= 1;
+	}
+
+	if(romprops.header.ramsize == 0) {
+		rammask = 0;
+	} else {
+		rammask = romprops.ramsize_bytes - 1;
+	}
+	rommask = romprops.romsize_bytes - 1;
+	printf("ramsize=%x rammask=%lx\nromsize=%x rommask=%lx\n", romprops.header.ramsize, rammask, romprops.header.romsize, rommask);
+	set_saveram_mask(rammask);
+	set_rom_mask(rommask);
+
+	return (uint32_t)filesize;
+}
+
+uint32_t load_sram(uint8_t* filename, uint32_t base_addr) {
+	set_mcu_addr(base_addr);
+	UINT bytes_read;
+	DWORD filesize;
+	file_open(filename, FA_READ);
+	filesize = file_handle.fsize;
+	if(file_res) return 0;
+	for(;;) {
+		bytes_read = file_read();
+		if (file_res || !bytes_read) break;
+		FPGA_SELECT();
+		FPGA_TX_BYTE(0x91);
+		for(int j=0; j<bytes_read; j++) {
+			FPGA_TX_BYTE(file_buf[j]);
+		}
+		FPGA_TX_BYTE(0x00); // dummy tx
+		FPGA_DESELECT();
+	}
+	file_close();
+	return (uint32_t)filesize;
+}
+
+
+void save_sram(uint8_t* filename, uint32_t sram_size, uint32_t base_addr) {
+	uint32_t count = 0;
+	uint32_t num = 0;
+
+	FPGA_DESELECT();
+	file_open(filename, FA_CREATE_ALWAYS | FA_WRITE);
+	if(file_res) {
+		uart_putc(0x30+file_res);
+	}
+	while(count<sram_size) {
+		set_mcu_addr(base_addr+count);
+		FPGA_SELECT();
+		FPGA_TX_BYTE(0x81); // read
+		FPGA_TX_BYTE(0); // dummy
+		for(int j=0; j<sizeof(file_buf); j++) {
+			file_buf[j] = FPGA_TXRX_BYTE(0x00);
+			count++;
+		}
+		FPGA_DESELECT();
+		num = file_write();
+		if(file_res) {
+			uart_putc(0x30+file_res);
+		}
+	}
+	file_close();
+}
+
+
+uint32_t calc_sram_crc(uint32_t base_addr, uint32_t size) {
+	uint8_t data;
+	uint32_t count;
+	uint16_t crc;
+	crc=0;
+	crc_valid=1;
+	set_mcu_addr(base_addr);
+	FPGA_SELECT();
+	FPGA_TX_BYTE(0x81);
+	FPGA_TX_BYTE(0x00);
+	for(count=0; count<size; count++) {
+		data = FPGA_TXRX_BYTE(0x00);
+		if(get_snes_reset()) {
+			crc_valid = 0;
+			break;
+		}
+		crc += crc16_update(crc, &data, 1);
+	}
+	FPGA_DESELECT();
+	return crc;
+}
+
+uint8_t sram_reliable() {
+	uint16_t score=0;
+	uint32_t val;
+//	uint32_t val = sram_readlong(SRAM_SCRATCHPAD);
+	uint8_t result = 0;
+/*	while(score<SRAM_RELIABILITY_SCORE) {
+		if(sram_readlong(SRAM_SCRATCHPAD)==val) {
+			score++;
+		} else {
+			set_pwr_led(0);
+			score=0;
+		}
+	}*/
+	for(uint16_t i = 0; i < SRAM_RELIABILITY_SCORE; i++) {
+		val=sram_readlong(SRAM_SCRATCHPAD);
+		if(val==0x12345678) {
+			score++;
+//		} else {
+//			dprintf("i=%d val=%08lX\n", i, val);
+		}
+	}
+	if(score<SRAM_RELIABILITY_SCORE) {
+		result = 0;
+//		dprintf("score=%d\n", score);
+	} else {
+		result = 1;
+	}
+	rdyled(result);
+	return result;
+}

+ 63 - 0
src/memory.h

@@ -0,0 +1,63 @@
+/* sd2snes - SD card based universal cartridge for the SNES
+   Copyright (C) 2009-2010 Maximilian Rehkopf <otakon@gmx.net>
+   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
+
+   memory.h: RAM operations
+*/
+
+#ifndef MEMORY_H
+#define MEMORY_H
+
+#include <arm/NXP/LPC17xx/LPC17xx.h>
+
+#define SRAM_ROM_ADDR (0x000000L)
+#define SRAM_SAVE_ADDR (0xE00000L)
+
+#define SRAM_MENU_ADDR (0xE00000L)
+#define SRAM_DB_ADDR (0xE20000L)
+#define SRAM_DIR_ADDR (0xE10000L)
+#define SRAM_CMD_ADDR (0xFF1004L)
+#define SRAM_PARAM_ADDR (0xFF1000L)
+#define SRAM_MENU_SAVE_ADDR (0xFF0000L)
+#define SRAM_SCRATCHPAD (0xFFFF00L)
+#define SRAM_DIRID (0xFFFFF0L)
+#define SRAM_RELIABILITY_SCORE (0x100)
+
+	uint32_t load_rom(uint8_t* filename, uint32_t base_addr);
+	uint32_t load_sram(uint8_t* filename, uint32_t base_addr);
+	void sram_hexdump(uint32_t addr, uint32_t len);
+	uint8_t sram_readbyte(uint32_t addr);
+	uint16_t sram_readshort(uint32_t addr);
+	uint32_t sram_readlong(uint32_t addr);
+	void sram_writebyte(uint8_t val, uint32_t addr);
+	void sram_writeshort(uint16_t val, uint32_t addr);
+	void sram_writelong(uint32_t val, uint32_t addr);
+	void sram_readblock(void* buf, uint32_t addr, uint16_t size);
+	void sram_readlongblock(uint32_t* buf, uint32_t addr, uint16_t count);
+	void sram_writeblock(void* buf, uint32_t addr, uint16_t size);
+	void save_sram(uint8_t* filename, uint32_t sram_size, uint32_t base_addr);
+	uint32_t calc_sram_crc(uint32_t base_addr, uint32_t size);
+	uint8_t sram_reliable(void);
+
+#include "smc.h"
+	snes_romprops_t romprops;
+#endif

+ 1 - 0
src/printf.c

@@ -179,6 +179,7 @@ static int internal_nprintf(void (*output_function)(char c), const char *fmt, va
       output_function('x');
       width -= 2;
     case 'x':
+    case 'X':
       base = 16;
       flags |= FLAG_UNSIGNED;
       break;

+ 20 - 20
src/sdcard.c

@@ -184,7 +184,7 @@ static uint8_t wait_for_response(uint8_t expected) {
   tick_t timeout = getticks() + HZ/2;
 
   while (time_before(getticks(), timeout)) {
-    uint8_t byte = spi_rx_byte();
+    uint8_t byte = spi_rx_byte(1);
 
     if (expected == 0 && byte != 0)
       return 1;
@@ -199,7 +199,7 @@ static uint8_t wait_for_response(uint8_t expected) {
 static void deselectCard(uint8_t card) {
   // Send 8 clock cycles
   set_sd_led(0);
-  spi_rx_byte();
+  spi_rx_byte(1);
 }
 
 /**
@@ -244,15 +244,15 @@ static int sendCommand(const uint8_t  card,
 #endif
 
     // Transfer command
-    spi_tx_byte(0x40+command);
+    spi_tx_byte(0x40+command, 1);
     uint32_t tmp = swap_word(parameter);
-    spi_tx_block(&tmp, 4);
-    spi_tx_byte(crc);
+    spi_tx_block(&tmp, 4, 1);
+    spi_tx_byte(crc, 1);
 
     // Wait for a valid response
     timeout = getticks() + HZ/2;
     do {
-      i = spi_rx_byte();
+      i = spi_rx_byte(1);
     } while (i & 0x80 && time_before(getticks(), timeout));
 
 #ifdef CONFIG_TWINSD
@@ -291,7 +291,7 @@ static uint8_t extendedInit(const uint8_t card) {
   }
 
   // No error, continue SDHC initialization
-  spi_rx_block(&answer, 4);
+  spi_rx_block(&answer, 4, 1);
   answer = swap_word(answer);
   deselectCard(card);
 
@@ -420,16 +420,16 @@ printf("sd_initialize\n");
    * IEC lines tied to SPI, so I moved it here to resolve the
    * conflict.
    */
-  spi_init(SPI_SPEED_SLOW);
+  spi_init(SPI_SPEED_SLOW, 1);
   disk_state = DISK_ERROR;
 
   cardtype[drv] = 0;
 
   set_sd_led(0);
 
-  // Send 80 clks
+  // Send 8000 clks
   for (counter=0; counter<1000; counter++) {
-    spi_tx_byte(0xff);
+    spi_tx_byte(0xff, 1);
   }
 
   // Reset card
@@ -455,7 +455,7 @@ printf("sd_initialize\n");
   } while (i > 1 && counter-- > 0);
 
   if (counter > 0) {
-    spi_rx_block(&answer, 4);
+    spi_rx_block(&answer, 4, 1);
     answer = swap_word(answer);
 
     // See if the card likes our supply voltage
@@ -501,7 +501,7 @@ printf("sd_initialize\n");
   }
 
   // Thats it!
-  spi_set_speed(SPI_SPEED_FAST);
+  spi_set_speed(SPI_SPEED_FAST, 1);
   disk_state = DISK_OK;
   return sd_status(drv);
 }
@@ -554,9 +554,9 @@ DRESULT sd_read(BYTE drv, BYTE *buffer, DWORD sector, BYTE count) {
 
 #ifdef CONFIG_SD_BLOCKTRANSFER
       /* Transfer data first, calculate CRC afterwards */
-      spi_rx_block(buffer, 512);
+      spi_rx_block(buffer, 512, 1);
 
-      recvcrc = spi_rx_byte() << 8 | spi_rx_byte();
+      recvcrc = spi_rx_byte(1) << 8 | spi_rx_byte(1);
 #ifdef CONFIG_SD_DATACRC
       crc = crc_xmodem_block(0, buffer, 512);
 #endif
@@ -653,10 +653,10 @@ DRESULT sd_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count) {
       }
 
       // Send data token
-      spi_tx_byte(0xfe);
+      spi_tx_byte(0xfe, 1);
 
       // Send data
-      spi_tx_block(buffer, 512);
+      spi_tx_block(buffer, 512, 1);
 #ifdef CONFIG_SD_DATACRC
       crc = crc_xmodem_block(0, buffer, 512);
 #else
@@ -664,11 +664,11 @@ DRESULT sd_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count) {
 #endif
 
       // Send CRC
-      spi_tx_byte(crc >> 8);
-      spi_tx_byte(crc & 0xff);
+      spi_tx_byte(crc >> 8, 1);
+      spi_tx_byte(crc & 0xff, 1);
 
       // Get and check status feedback
-      status = spi_rx_byte();
+      status = spi_rx_byte(1);
 
       // Retry if neccessary
       if ((status & 0x0F) != 0x05) {
@@ -725,7 +725,7 @@ DRESULT sd_getinfo(BYTE drv, BYTE page, void *buffer) {
     return RES_ERROR;
   }
 
-  spi_rx_block(buf, 18);
+  spi_rx_block(buf, 18, 1);
   deselectCard(drv);
 
   if (cardtype[drv] & CARD_SDHC) {

+ 5 - 0
src/sdcard.h

@@ -29,6 +29,11 @@
 
 #include "diskio.h"
 
+#define SD_TX_BYTE(x)		spi_tx_byte(x, SPI_SD);
+#define SD_RX_BYTE(x)		spi_rx_byte(x, SPI_SD);
+#define SD_TX_BLOCK(x,y)	spi_tx_block(x,y, SPI_SD);
+#define SD_RX_BLOCK(x,y)	spi_rx_block(x,y, SPI_SD);
+
 /* These functions are weak-aliased to disk_... */
 void    sd_init(void);
 DSTATUS sd_status(BYTE drv);

+ 173 - 0
src/smc.c

@@ -0,0 +1,173 @@
+/* sd2snes - SD card based universal cartridge for the SNES
+   Copyright (C) 2009-2010 Maximilian Rehkopf <otakon@gmx.net>
+   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
+
+   smc.c: SMC file related operations
+*/
+
+#include "fileops.h"
+#include "config.h"
+#include "uart.h"
+#include "smc.h"
+
+uint32_t hdr_addr[6] = {0xffb0, 0x101b0, 0x7fb0, 0x81b0, 0x40ffb0, 0x4101b0};
+uint8_t countAllASCII(uint8_t* data, int size) {
+	uint8_t res = 0;
+	do {
+		size--;
+		if(data[size] >= 0x20 && data[size] <= 0x7e) {
+			res++;
+		}
+	} while (size);
+	return res;
+}
+
+uint8_t countAllJISX0201(uint8_t* data, int size) {
+	uint8_t res = 0;
+	do {
+		size--;
+		if((data[size] >= 0x20 && data[size] <= 0x7e)
+           ||(data[size] >= 0xa1 && data[size] <= 0xdf)) {
+			res++;
+		}
+	} while (size);
+	return res;
+}
+
+uint8_t isFixed(uint8_t* data, int size, uint8_t value) {
+	uint8_t res = 1;
+	do {
+		size--;
+		if(data[size] != value) {
+			res = 0;
+		}
+	} while (size);
+	return res;
+}
+
+uint8_t checkChksum(uint16_t cchk, uint16_t chk) {
+	uint32_t sum = cchk + chk;
+	uint8_t res = 0;
+	if(sum==0x0000ffff) {
+		res = 0x10;
+	}
+	return res;
+}
+
+void smc_id(snes_romprops_t* props) {
+	uint8_t score, maxscore=1, score_idx=2; // assume LoROM
+
+	snes_header_t* header = &(props->header);
+	
+	for(uint8_t num = 0; num < 6; num++) {
+		if(!file_readblock(header, hdr_addr[num], sizeof(snes_header_t)) 
+		|| file_res) {
+//			dprintf("uh oh... %d\n", file_res);
+//			_delay_ms(30);
+			score = 0;
+		} else {
+			score = smc_headerscore(header)/(1+(num&1));
+			if((file_handle.fsize & 0x2ff) == 0x200) {
+				if(num&1) {
+					score+=20;
+				} else {
+					score=0;
+				}
+			} else {
+				if(!(num&1)) {
+					score+=20;
+				} else {
+					score=0;
+				}
+			}
+		}
+//		dprintf("%d: offset = %lX; score = %d\n", num, hdr_addr[num], score);
+//		_delay_ms(100);
+		if(score>=maxscore) {
+			score_idx=num;
+			maxscore=score;
+		}		
+	}
+
+	if(score_idx & 1) {
+		props->offset = 0x200;
+	} else {
+		props->offset = 0;
+	}
+	
+	// restore the chosen one
+//	dprintf("winner is %d\n", score_idx);
+//	_delay_ms(30);
+	file_readblock(header, hdr_addr[score_idx], sizeof(snes_header_t));
+	switch(header->map & 0xef) {
+		case 0x20:
+			props->mapper_id = 1;
+			break;
+		case 0x21:
+			props->mapper_id = 0;
+			break;
+		case 0x25:
+			props->mapper_id = 2;
+			break;
+		default: // invalid/unsupported mapper, use header location
+			switch(score_idx) {
+				case 0:
+				case 1:
+					props->mapper_id = 0;
+					break;
+				case 2:
+				case 3:
+					props->mapper_id = 1;
+					break;
+				case 4:
+				case 5:
+					props->mapper_id = 2;
+					break;
+				default:
+					props->mapper_id = 1; // whatever
+			}
+	}
+	if(header->romsize == 0 || header->romsize > 13) {
+		header->romsize = 13;
+	}
+	props->ramsize_bytes = (uint32_t)1024 << header->ramsize;
+	props->romsize_bytes = (uint32_t)1024 << header->romsize;
+	props->expramsize_bytes = (uint32_t)1024 << header->expramsize;
+//	dprintf("ramsize_bytes: %ld\n", props->ramsize_bytes);
+	if(props->ramsize_bytes > 32768 || props->ramsize_bytes < 2048) {
+		props->ramsize_bytes = 0;
+	}
+//	dprintf("ramsize_bytes: %ld\n", props->ramsize_bytes);
+	f_lseek(&file_handle, 0);
+}
+
+uint8_t smc_headerscore(snes_header_t* header) {
+	uint8_t score=0;
+	score += countAllASCII(header->maker, sizeof(header->maker));
+	score += countAllASCII(header->gamecode, sizeof(header->gamecode));
+	score += isFixed(header->fixed_00, sizeof(header->fixed_00), 0x00);
+	score += countAllJISX0201(header->name, sizeof(header->name));
+	score += 3*isFixed(&header->fixed_33, sizeof(header->fixed_33), 0x33);
+	score += checkChksum(header->cchk, header->chk);
+	return score;
+}
+

+ 62 - 0
src/smc.h

@@ -0,0 +1,62 @@
+/* sd2snes - SD card based universal cartridge for the SNES
+   Copyright (C) 2009-2010 Maximilian Rehkopf <otakon@gmx.net>
+   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
+
+   smc.h: SMC file structures
+*/
+
+#ifndef SMC_H
+#define SMC_H
+
+typedef struct _snes_header {
+	uint8_t maker[2];		// 0xB0
+	uint8_t gamecode[4];	// 0xB2
+	uint8_t fixed_00[7];	// 0xB6
+	uint8_t expramsize;		// 0xBD
+	uint8_t specver;		// 0xBE
+	uint8_t carttype2; 		// 0xBF
+	uint8_t name[21];		// 0xC0
+	uint8_t map;			// 0xD5
+	uint8_t carttype;		// 0xD6
+	uint8_t romsize;		// 0xD7
+	uint8_t ramsize;		// 0xD8
+	uint8_t destcode;		// 0xD9
+	uint8_t fixed_33;		// 0xDA
+	uint8_t ver;			// 0xDB
+	uint16_t cchk;			// 0xDC
+	uint16_t chk;			// 0xDE
+} snes_header_t;
+
+typedef struct _snes_romprops {
+	uint16_t offset;			// start of actual ROM image
+	uint8_t mapper_id;			// FPGA mapper
+	uint8_t pad1;				// for alignment
+	uint32_t expramsize_bytes;	// ExpRAM size in bytes
+	uint32_t ramsize_bytes;		// CartRAM size in bytes
+	uint32_t romsize_bytes;		// ROM size in bytes (rounded up)
+	snes_header_t header;		// original header from ROM image
+} snes_romprops_t;
+
+void smc_id(snes_romprops_t*);
+uint8_t smc_headerscore(snes_header_t*);
+
+#endif

+ 17 - 28
src/snes.c

@@ -24,8 +24,8 @@
    snes.c: SNES hardware control and monitoring
 */
 
-#include <avr/io.h>
-#include "avrcompat.h"
+#include <arm/NXP/LPC17xx/LPC17xx.h>
+#include "bits.h"
 #include "config.h"
 #include "uart.h"
 #include "snes.h"
@@ -38,8 +38,9 @@
 uint8_t initloop=1;
 uint32_t saveram_crc, saveram_crc_old;
 void snes_init() {
-	DDRD |= _BV(PD5);	// PD5 = RESET_DIR
-	DDRD |= _BV(PD6); 	// PD6 = RESET
+	/* put reset level on reset pin */
+	BITBAND(SNES_RESET_REG->FIOCLR, SNES_RESET_BIT) = 1;
+	/* reset the SNES */
 	snes_reset(1); 
 }
 
@@ -49,15 +50,7 @@ void snes_init() {
  *  state: put SNES in reset state when 1, release when 0
  */
 void snes_reset(int state) {
-	if(state) {
-		DDRD |= _BV(PD6);	// /RESET pin -> out
-		PORTD &= ~_BV(PD6);	// /RESET = 0
-		PORTD |= _BV(PD5);	// RESET_DIR = 1;
-	} else {
-		PORTD &= ~_BV(PD5);	// RESET_DIR = 0;
-		DDRD &= ~_BV(PD6);	// /RESET pin -> in
-		PORTD |= _BV(PD6);	// /RESET = pullup
-	}
+	BITBAND(SNES_RESET_REG->FIODIR, SNES_RESET_BIT) = state;
 }
 
 /*
@@ -66,9 +59,7 @@ void snes_reset(int state) {
  * returns: 1 when reset, 0 when not reset
  */
 uint8_t get_snes_reset() {
-//	DDRD &= ~_BV(PD6);	// /RESET pin -> in
-//	PORTD &= ~_BV(PD5);	// RESET_DIR (external buffer) = 0
-	return !(PIND & _BV(PD6));
+	return !BITBAND(SNES_RESET_REG->FIOPIN, SNES_RESET_BIT);
 }
 
 /*
@@ -99,25 +90,23 @@ void snes_main_loop() {
 			samecount++;
 		}
 		if(diffcount>=1 && samecount==5) {
-			uart_putc('U');
-			uart_puthexshort(saveram_crc);
-			uart_putcrlf();
-			set_busy_led(1);
+			printf("SaveRAM CRC: 0x%04lx; saving\n", saveram_crc);
+			writeled(1);
 			save_sram(file_lfn, romprops.ramsize_bytes, SRAM_SAVE_ADDR);
-			set_busy_led(0);
+			writeled(0);
 			didnotsave=0;
 		}
 		if(didnotsave>50) {
+			printf("periodic save (sram contents keep changing...)\n");
 			diffcount=0;
-			uart_putc('V');
-			set_busy_led(1);
+			writeled(1);
 			save_sram(file_lfn, romprops.ramsize_bytes, SRAM_SAVE_ADDR);
 			didnotsave=0;	
-                        set_busy_led(0);
+                        writeled(0);
 		}
 		saveram_crc_old = saveram_crc;
 	}
-	dprintf("crc_valid=%d sram_valid=%d diffcount=%ld samecount=%ld, didnotsave=%ld\n", crc_valid, sram_valid, diffcount, samecount, didnotsave);
+	printf("crc_valid=%d sram_valid=%d diffcount=%ld samecount=%ld, didnotsave=%ld\n", crc_valid, sram_valid, diffcount, samecount, didnotsave);
 }
 
 /*
@@ -129,7 +118,7 @@ uint8_t menu_main_loop() {
 	sram_writebyte(0, SRAM_CMD_ADDR);
 	while(!cmd) {
 		if(!get_snes_reset()) {
-			while(!sram_reliable());
+			while(!sram_reliable())printf("hurr\n");;
 			cmd = sram_readbyte(SRAM_CMD_ADDR);
 		}
 		if(get_snes_reset()) {
@@ -140,7 +129,7 @@ uint8_t menu_main_loop() {
 }
 
 void get_selected_name(uint8_t* fn) {
-	uint32_t addr = sram_readlong(SRAM_FD_ADDR);
-	dprintf("fd addr=%lX\n", addr);
+	uint32_t addr = sram_readlong(SRAM_PARAM_ADDR);
+	printf("fd addr=%lx\n", addr);
 	sram_readblock(fn, addr+0x41+SRAM_MENU_ADDR, 256);
 }

+ 4 - 0
src/snes.h

@@ -26,6 +26,10 @@
 
 #ifndef SNES_H
 #define SNES_H
+
+#define SNES_CMD_LOADROM   (1)
+#define SNES_CMD_SETRTC    (2)
+
 uint8_t crc_valid;
 
 void snes_init(void);

+ 125 - 0
src/sort.c

@@ -0,0 +1,125 @@
+
+#include <arm/NXP/LPC17xx/LPC17xx.h>
+#include <string.h>
+#include <stdlib.h>
+#include "config.h"
+#include "uart.h"
+#include "memory.h"
+#include "sort.h"
+
+/* 
+   heap sort algorithm for data located outside RAM 
+   addr:     start address of pointer table
+   i:        index (in 32-bit elements)
+   heapsize: size of heap (in 32-bit elements)
+*/
+
+uint32_t stat_getstring = 0;
+static char sort_str1[21], sort_str2[21];
+uint32_t ptrcache[QSORT_MAXELEM] IN_AHBRAM;
+
+/* get element from pointer table in external RAM*/
+uint32_t sort_get_elem(uint32_t base, unsigned int index) {
+  return sram_readlong(base+4*index);
+}
+
+/* put element from pointer table in external RAM */
+void sort_put_elem(uint32_t base, unsigned int index, uint32_t elem) {
+  sram_writelong(elem, base+4*index);
+}
+
+/* compare strings pointed to by elements of pointer table */
+int sort_cmp_idx(uint32_t base, unsigned int index1, unsigned int index2) {
+  uint32_t elem1, elem2;
+  elem1 = sort_get_elem(base, index1);
+  elem2 = sort_get_elem(base, index2);
+  return sort_cmp_elem((void*)&elem1, (void*)&elem2);
+}
+
+int sort_cmp_elem(const void* elem1, const void* elem2) {
+  uint32_t el1 = *(uint32_t*)elem1;
+  uint32_t el2 = *(uint32_t*)elem2;
+  sort_getstring_for_dirent(sort_str1, el1);
+  sort_getstring_for_dirent(sort_str2, el2);
+//sort_getlong_for_dirent(sort_long1, elem1);
+//sort_getlong_for_dirent(sort_long2, elem2);
+//  printf("i1=%d i2=%d elem1=%lx elem2=%lx ; compare %s   ---   %s\n", index1, index2, elem1, elem2, sort_str1, sort_str2);
+
+  if ((el1 & 0x80000000) && !(el2 & 0x80000000)) {
+    return -1;
+  }
+
+  if (!(el1 & 0x80000000) && (el2 & 0x80000000)) {
+    return 1;
+  }
+/*  
+  uint16_t cmp_i;
+  for(cmp_i=0; cmp_i<8 && sort_long1[cmp_i] == sort_long2[cmp_i]; cmp_i++);
+  if(cmp_i==8) {
+    return 0;
+  }
+  return sort_long1[cmp_i]-sort_long2[cmp_i];*/
+  return strcasecmp(sort_str1, sort_str2);
+}
+
+/* get truncated string from database */
+void sort_getstring_for_dirent(char *ptr, uint32_t addr) {
+stat_getstring++;
+  if(addr & 0x80000000) {
+    /* is directory link, name offset 6 */
+    sram_readblock(ptr, addr+0x6+SRAM_MENU_ADDR, 20);
+  } else {
+    /* is file link, name offset 65 */
+    sram_readblock(ptr, addr+0x41+SRAM_MENU_ADDR, 20);
+  }
+  ptr[20]=0;
+}
+
+void sort_heapify(uint32_t addr, unsigned int i, unsigned int heapsize)
+{
+    while(1)
+    {
+        unsigned int l = 2*i+1;
+        unsigned int r = 2*i+2;
+
+        unsigned int largest = (l < heapsize && sort_cmp_idx(addr, i, l) < 0) ? l : i;
+
+        if(r < heapsize && sort_cmp_idx(addr, largest, r) < 0)
+            largest = r;
+
+        if(largest != i)
+        {
+            uint32_t tmp = sort_get_elem(addr, i);
+            sort_put_elem(addr, i, sort_get_elem(addr, largest));
+            sort_put_elem(addr, largest, tmp);
+            i = largest;
+        }
+        else break;
+    }
+}
+
+void sort_dir(uint32_t addr, unsigned int size)
+{
+stat_getstring=0;
+  if(size > QSORT_MAXELEM) {
+    printf("more than %d dir entries, doing slower in-place sort\n", QSORT_MAXELEM);
+    ext_heapsort(addr, size);
+  } else {
+    /* retrieve, sort, and store dir table */
+    sram_readblock(ptrcache, addr, size*4);
+    qsort((void*)ptrcache, size, 4, sort_cmp_elem);
+    sram_writeblock(ptrcache, addr, size*4);
+  }
+}
+
+void ext_heapsort(uint32_t addr, unsigned int size) {
+  for(unsigned int i = size/2; i > 0;) sort_heapify(addr, --i, size);
+
+  for(unsigned int i = size-1; i>0; --i) {
+      uint32_t tmp = sort_get_elem(addr, 0);
+      sort_put_elem(addr, 0, sort_get_elem(addr, i));
+      sort_put_elem(addr, i, tmp);
+      sort_heapify(addr, 0, i);
+  }
+}
+

+ 15 - 0
src/sort.h

@@ -0,0 +1,15 @@
+#ifndef _SORT_H
+#define _SORT_H
+
+#include <arm/NXP/LPC17xx/LPC17xx.h>
+
+uint32_t sort_get_elem(uint32_t base, unsigned int index);
+void sort_put_elem(uint32_t base, unsigned int index, uint32_t elem);
+int sort_cmp_idx(uint32_t base, unsigned int index1, unsigned int index2);
+int sort_cmp_elem(const void* elem1, const void* elem2);
+void sort_getstring_for_dirent(char *ptr, uint32_t addr);
+void sort_getlong_for_dirent(uint32_t* ptr, uint32_t addr);
+void sort_heapify(uint32_t addr, unsigned int i, unsigned int heapsize);
+void sort_dir(uint32_t addr, unsigned int size);
+void ext_heapsort(uint32_t addr, unsigned int size);
+#endif

+ 119 - 60
src/spi.c

@@ -28,6 +28,7 @@
 #include "bits.h"
 #include "config.h"
 #include "spi.h"
+#include "uart.h"
 
 #define SSP_TFE 0   // Transmit FIFO empty
 #define SSP_TNF 1   // Transmit FIFO not full
@@ -35,10 +36,6 @@
 #define SSP_RFF 3   // Receive FIFO full
 #define SSP_BSY 4   // Busy
 
-// FIXME: Move to config.h!
-#define SSP_CLK_DIVISOR_FAST 4
-#define SSP_CLK_DIVISOR_SLOW 250
-
 // #define SSP_REGS LPC_SSP1
 // #define SSP_PCLKREG PCLKSEL0
 /* SSP0: PCLKSEL1
@@ -54,118 +51,173 @@
    SSP1: 3 */
 
 typedef struct {
-  LPC_SSP_TypeDef   *SSP_REGS;
-  LPC_GPDMA_TypeDef *SSP_DMAC;
-  __IO uint32_t      SSP_PCLKREG;
-  int                SSP_PCLKBIT;
-  int                SSP_DMAID_TX;
-  int                SSP_DMAID_RX;
+  LPC_SSP_TypeDef     *SSP_REGS;
+  LPC_GPDMACH_TypeDef *SSP_DMACH;
+  uint32_t            *SSP_PCLKREG;
+  int                  SSP_PCLKBIT;
+  int                  SSP_DMAID_TX;
+  int                  SSP_DMAID_RX;
 } ssp_props;
 
 
 static ssp_props SSP_SEL[2] = {
-  { LPC_SSP0, LPC_GPDMA0, PCLKSEL1, 10, 0, 1 }, /* SSP0 */
-  { LPC_SSP1, LPC_GPDMA1, PCLKSEL0, 20, 2, 3 }
+  { LPC_SSP0, LPC_GPDMACH0, (uint32_t*)&(LPC_SC->PCLKSEL1), 10, 0, 1 }, /* SSP0 */
+  { LPC_SSP1, LPC_GPDMACH1, (uint32_t*)&(LPC_SC->PCLKSEL0), 20, 2, 3 }  /* SSP1 */
 };
 
-void spi_init(spi_speed_t speed) {
+void spi_preinit(int device) {
+  /* select interface */
+  ssp_props *ssp = &(SSP_SEL[device]);
+
   /* Set clock prescaler to 1:1 */
-  BITBAND(LPC_SC->SSP_PCLKREG, SSP_PCLKBIT) = 1;
+  BITBAND(*(ssp->SSP_PCLKREG), ssp->SSP_PCLKBIT) = 1;
+}
+
+void spi_init(spi_speed_t speed, int device) {
+
+  /* select interface */
+  ssp_props *ssp = &(SSP_SEL[device]);
 
   /* configure data format - 8 bits, SPI, CPOL=0, CPHA=0, 1 clock per bit */
-  SSP_REGS->CR0 = (8-1);
+  ssp->SSP_REGS->CR0 = (8-1);
 
   /* set clock prescaler */
   if (speed == SPI_SPEED_FAST) {
-    SSP_REGS->CPSR = SSP_CLK_DIVISOR_FAST;
+    ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FAST;
+  } else if (speed == SPI_SPEED_SLOW) {
+    ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW;
+  } else if (speed == SPI_SPEED_FPGA_FAST) {
+    ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FPGA_FAST;
   } else {
-    SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW;
+    ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FPGA_SLOW;
   }
 
   /* Enable SSP */
-  SSP_REGS->CR1 = BV(1);
+  ssp->SSP_REGS->CR1 = BV(1);
 
   /* Enable DMA controller, little-endian mode */
   BITBAND(LPC_SC->PCONP, 29) = 1;
   LPC_GPDMA->DMACConfig = 1;
 }
 
-void spi_tx_byte(uint8_t data) {
+void spi_tx_sync(int device) {
+  /* select interface */
+  ssp_props *ssp = &(SSP_SEL[device]);
+  
+  /* Wait until TX fifo is flushed */
+  while (BITBAND(ssp->SSP_REGS->SR, SSP_BSY)) ;
+}
+
+void spi_tx_byte(uint8_t data, int device) {
+
+  /* select interface */
+  ssp_props *ssp = &(SSP_SEL[device]);
+
   /* Wait until TX fifo can accept data */
-  while (!BITBAND(SSP_REGS->SR, SSP_TNF)) ;
+  while (!BITBAND(ssp->SSP_REGS->SR, SSP_TNF)) ;
 
   /* Send byte */
-  SSP_REGS->DR = data;
+  ssp->SSP_REGS->DR = data;
 }
 
-uint8_t spi_rx_byte(void) {
+uint8_t spi_txrx_byte(uint8_t data, int device) {
+  /* select interface */
+  ssp_props *ssp = &(SSP_SEL[device]);
+
   /* Wait until SSP is not busy */
-  while (BITBAND(SSP_REGS->SR, SSP_BSY)) ;
+  while (BITBAND(ssp->SSP_REGS->SR, SSP_BSY)) ;
 
   /* Clear RX fifo */
-  while (BITBAND(SSP_REGS->SR, SSP_RNE))
-    (void) SSP_REGS->DR;
+  while (BITBAND(ssp->SSP_REGS->SR, SSP_RNE))
+    (void) ssp->SSP_REGS->DR;
 
   /* Transmit a single dummy byte */
-  SSP_REGS->DR = 0xff;
+  ssp->SSP_REGS->DR = data;
 
   /* Wait until answer has been received */
-  while (!BITBAND(SSP_REGS->SR, SSP_RNE)) ;
+  while (!BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) ;
 
-  return SSP_REGS->DR;
+  return ssp->SSP_REGS->DR;
 }
 
-void spi_tx_block(const void *ptr, unsigned int length) {
+uint8_t spi_rx_byte(int device) {
+
+  /* select interface */
+  ssp_props *ssp = &(SSP_SEL[device]);
+
+  /* Wait until SSP is not busy */
+  while (BITBAND(ssp->SSP_REGS->SR, SSP_BSY)) ;
+
+  /* Clear RX fifo */
+  while (BITBAND(ssp->SSP_REGS->SR, SSP_RNE))
+    (void) ssp->SSP_REGS->DR;
+
+  /* Transmit a single dummy byte */
+  ssp->SSP_REGS->DR = 0xff;
+
+  /* Wait until answer has been received */
+  while (!BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) ;
+
+  return ssp->SSP_REGS->DR;
+}
+
+void spi_tx_block(const void *ptr, unsigned int length, int device) {
   const uint8_t *data = (const uint8_t *)ptr;
 
+  /* select interface */
+  ssp_props *ssp = &(SSP_SEL[device]);
+
   while (length--) {
     /* Wait until TX fifo can accept data */
-    while (!BITBAND(SSP_REGS->SR, SSP_TNF)) ;
+    while (!BITBAND(ssp->SSP_REGS->SR, SSP_TNF)) ;
 
-    SSP_REGS->DR = *data++;
+    ssp->SSP_REGS->DR = *data++;
   }
 }
 
-void spi_rx_block(void *ptr, unsigned int length) {
+void spi_rx_block(void *ptr, unsigned int length, int device) {
   uint8_t *data = (uint8_t *)ptr;
   unsigned int txlen = length;
 
+  /* select interface */
+  ssp_props *ssp = &(SSP_SEL[device]);
+
   /* Wait until SSP is not busy */
-  while (BITBAND(SSP_REGS->SR, SSP_BSY)) ;
+  while (BITBAND(ssp->SSP_REGS->SR, SSP_BSY)) ;
 
   /* Clear RX fifo */
-  while (BITBAND(SSP_REGS->SR, SSP_RNE))
-    (void) SSP_REGS->DR;
+  while (BITBAND(ssp->SSP_REGS->SR, SSP_RNE))
+    (void) ssp->SSP_REGS->DR;
 
   if ((length & 3) != 0 || ((uint32_t)ptr & 3) != 0) {
     /* Odd length or unaligned buffer */
     while (length > 0) {
       /* Wait until TX or RX FIFO are ready */
-      while (txlen > 0 && !BITBAND(SSP_REGS->SR, SSP_TNF) &&
-             !BITBAND(SSP_REGS->SR, SSP_RNE)) ;
+      while (txlen > 0 && !BITBAND(ssp->SSP_REGS->SR, SSP_TNF) &&
+             !BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) ;
 
       /* Try to receive data */
-      while (length > 0 && BITBAND(SSP_REGS->SR, SSP_RNE)) {
-        *data++ = SSP_REGS->DR;
+      while (length > 0 && BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) {
+        *data++ = ssp->SSP_REGS->DR;
         length--;
       }
 
       /* Send dummy data until TX full or RX ready */
-      while (txlen > 0 && BITBAND(SSP_REGS->SR, SSP_TNF) && !BITBAND(SSP_REGS->SR, SSP_RNE)) {
+      while (txlen > 0 && BITBAND(ssp->SSP_REGS->SR, SSP_TNF) && !BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) {
         txlen--;
-        SSP_REGS->DR = 0xff;
+        ssp->SSP_REGS->DR = 0xff;
       }
     }
   } else {
     /* Clear interrupt flags of DMA channels 0 */
-    LPC_GPDMA->DMACIntTCClear = BV(0);
-    LPC_GPDMA->DMACIntErrClr  = BV(0);
+    LPC_GPDMA->DMACIntTCClear = BV(device);
+    LPC_GPDMA->DMACIntErrClr  = BV(device);
 
     /* Set up RX DMA channel */
-    LPC_GPDMACH0->DMACCSrcAddr  = (uint32_t)&SSP_REGS->DR;
-    LPC_GPDMACH0->DMACCDestAddr = (uint32_t)ptr;
-    LPC_GPDMACH0->DMACCLLI      = 0; // no linked list
-    LPC_GPDMACH0->DMACCControl  = length
+    ssp->SSP_DMACH->DMACCSrcAddr  = (uint32_t)&ssp->SSP_REGS->DR;
+    ssp->SSP_DMACH->DMACCDestAddr = (uint32_t)ptr;
+    ssp->SSP_DMACH->DMACCLLI      = 0; // no linked list
+    ssp->SSP_DMACH->DMACCControl  = length
       | (0 << 12) // source burst size 1 (FIXME: Check if larger possible/useful)
       | (0 << 15) // destination burst size 1
       | (0 << 18) // source transfer width 1 byte
@@ -173,45 +225,52 @@ void spi_rx_block(void *ptr, unsigned int length) {
       | (0 << 26) // source address not incremented
       | (1 << 27) // destination address incremented
       ;
-    LPC_GPDMACH0->DMACCConfig = 1 // enable channel
-      | (SSP_DMAID_RX << 1) // data source SSP RX
+    ssp->SSP_DMACH->DMACCConfig = 1 // enable channel
+      | (ssp->SSP_DMAID_RX << 1) // data source SSP RX
       | (2 << 11) // transfer from peripheral to memory
       ;
 
     /* Enable RX FIFO DMA */
-    SSP_REGS->DMACR = 1;
+    ssp->SSP_REGS->DMACR = 1;
 
     /* Write <length> bytes into TX FIFO */
     // FIXME: Any value in doing this using DMA too?
     while (txlen > 0) {
-      while (txlen > 0 && BITBAND(SSP_REGS->SR, SSP_TNF)) {
+      while (txlen > 0 && BITBAND(ssp->SSP_REGS->SR, SSP_TNF)) {
         txlen--;
-        SSP_REGS->DR = 0xff;
+        ssp->SSP_REGS->DR = 0xff;
       }
     }
 
     /* Wait until DMA channel disables itself */
-    while (LPC_GPDMACH0->DMACCConfig & 1) ;
+    while (ssp->SSP_DMACH->DMACCConfig & 1) ;
 
     /* Disable RX FIFO DMA */
-    SSP_REGS->DMACR = 0;
+    ssp->SSP_REGS->DMACR = 0;
   }
 }
 
-void spi_set_speed(spi_speed_t speed) {
+void spi_set_speed(spi_speed_t speed, int device) {
+  /* select interface */
+  ssp_props *ssp = &(SSP_SEL[device]);
+
   /* Wait until TX fifo is empty */
-  while (!BITBAND(SSP_REGS->SR, 0)) ;
+  while (!BITBAND(ssp->SSP_REGS->SR, 0)) ;
 
   /* Disable SSP (FIXME: Is this required?) */
-  SSP_REGS->CR1 = 0;
+  ssp->SSP_REGS->CR1 = 0;
 
   /* Change clock divisor */
   if (speed == SPI_SPEED_FAST) {
-    SSP_REGS->CPSR = SSP_CLK_DIVISOR_FAST;
+    ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FAST;
+  } else if (speed == SPI_SPEED_SLOW) {
+    ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW;
+  } else if (speed == SPI_SPEED_FPGA_FAST) {
+    ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FPGA_FAST;
   } else {
-    SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW;
+    ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FPGA_SLOW;
   }
 
   /* Enable SSP */
-  SSP_REGS->CR1 = BV(1);
+  ssp->SSP_REGS->CR1 = BV(1);
 }

+ 16 - 7
src/spi.h

@@ -30,24 +30,33 @@
 #define SPI_H
 
 /* Low speed 400kHz for init, fast speed <=20MHz (MMC limit) */
-typedef enum { SPI_SPEED_FAST, SPI_SPEED_SLOW } spi_speed_t;
+typedef enum { SPI_SPEED_FAST, SPI_SPEED_SLOW, SPI_SPEED_FPGA_FAST, SPI_SPEED_FPGA_SLOW } spi_speed_t;
+
+/* Pre-Initialize SPI interface (PCLK divider before PLL setup) */
+void spi_preinit(int device);
 
 /* Initialize SPI interface */
-void spi_init(spi_speed_t speed);
+void spi_init(spi_speed_t speed, int device);
 
 /* Transmit a single byte */
-void spi_tx_byte(uint8_t data);
+void spi_tx_byte(uint8_t data, int device);
+
+/* Transmit a single byte and return received data */
+uint8_t spi_txrx_byte(uint8_t data, int device);
 
 /* Transmit a data block */
-void spi_tx_block(const void *data, unsigned int length);
+void spi_tx_block(const void *data, unsigned int length, int device);
 
 /* Receive a single byte */
-uint8_t spi_rx_byte(void);
+uint8_t spi_rx_byte(int device);
 
 /* Receive a data block */
-void spi_rx_block(void *data, unsigned int length);
+void spi_rx_block(void *data, unsigned int length, int device);
 
 /* Switch speed of SPI interface */
-void spi_set_speed(spi_speed_t speed);
+void spi_set_speed(spi_speed_t speed, int device);
+
+/* wait for SPI TX FIFO to become empty */
+void spi_tx_sync(int device);
 
 #endif