瀏覽代碼

mk2 fw wip

ikari 13 年之前
父節點
當前提交
40248e61af
共有 35 個文件被更改,包括 3026 次插入44 次删除
  1. 3 3
      src/Makefile
  2. 14 0
      src/bits.h
  3. 531 0
      src/ccsbcs.c
  4. 9 5
      src/clock.c
  5. 2 0
      src/clock.h
  6. 11 3
      src/config.h
  7. 91 0
      src/crc.S
  8. 11 0
      src/crc.h
  9. 36 0
      src/diskio.h
  10. 4 2
      src/ff.c
  11. 4 4
      src/ffconf.h
  12. 80 0
      src/fileops.c
  13. 45 0
      src/fileops.h
  14. 273 0
      src/filetypes.c
  15. 45 0
      src/filetypes.h
  16. 0 1
      src/flash.script
  17. 4 0
      src/init.gdb
  18. 24 0
      src/led.c
  19. 10 0
      src/led.h
  20. 0 8
      src/lpc.h
  21. 39 7
      src/main.c
  22. 10 0
      src/power.c
  23. 14 0
      src/power.h
  24. 290 0
      src/printf.c
  25. 54 0
      src/rtc.c
  26. 62 0
      src/rtc.h
  27. 750 0
      src/sdcard.c
  28. 40 0
      src/sdcard.h
  29. 146 0
      src/snes.c
  30. 37 0
      src/snes.h
  31. 202 0
      src/spi.c
  32. 53 0
      src/spi.h
  33. 69 0
      src/timer.c
  34. 50 0
      src/timer.h
  35. 13 11
      src/uart.c

+ 3 - 3
src/Makefile

@@ -55,7 +55,7 @@ TARGET = $(OBJDIR)/sd2snes
 
 
 # List C source files here. (C dependencies are automatically generated.)
-SRC = main.c ff.c clock.c uart.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
 
 
 # List Assembler source files here.
@@ -65,7 +65,7 @@ SRC = main.c ff.c clock.c uart.c
 #     Even though the DOS/Win* filesystem matches both .s and .S the same,
 #     it will preserve the spelling of the filenames, and gcc itself does
 #     care about how the name is spelled on its command-line.
-ASRC = startup.S
+ASRC = startup.S crc.S
 
 
 # Optimization level, can be [0, 1, 2, 3, s].
@@ -137,7 +137,7 @@ CFLAGS += -O$(OPT)
 CFLAGS += $(CPUFLAGS) -nostartfiles
 #CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
 CFLAGS += -Wall -Wstrict-prototypes -Werror
-#CFLAGS += -Wa,-adhlns=$(OBJDIR)/$(<:.c=.lst)
+CFLAGS += -Wa,-adhlns=$(OBJDIR)/$(<:.c=.lst)
 CFLAGS += -I$(OBJDIR)
 CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
 CFLAGS += $(CSTANDARD)

+ 14 - 0
src/bits.h

@@ -0,0 +1,14 @@
+#ifndef _ARM_BITS_H
+#define _ARM_BITS_H
+
+/* The classic macro */
+#define BV(x) (1<<(x))
+
+/* CM3 bit-band access macro - no error checks! */
+#define BITBAND(addr,bit) \
+  (*((volatile unsigned long *)( \
+     ((unsigned long)&(addr) & 0x01ffffff)*32 + \
+      (bit)*4 + 0x02000000 + ((unsigned long)&(addr) & 0xfe000000) \
+  )))
+
+#endif

文件差異過大導致無法顯示
+ 531 - 0
src/ccsbcs.c


+ 9 - 5
src/clock.c

@@ -4,12 +4,18 @@
 
 #include <arm/NXP/LPC17xx/LPC17xx.h>
 #include "clock.h"
+#include "bits.h"
 
-uint32_t f_cpu;
+uint32_t f_cpu=4000000;
 uint16_t pll_mult = 1;
 uint8_t pll_prediv = 1;
 uint8_t cclk_div = 1;
 
+void clock_disconnect() {
+  disconnectPLL0();
+  disablePLL0();
+}
+
 void clock_init() {
 
 /* set flash access time to 5 clks (80<f<=100MHz) */
@@ -25,16 +31,14 @@ void clock_init() {
    -> FPGA freq = 11289473.7Hz
    First, disable and disconnect PLL0.
 */
-	disconnectPLL0();
-	disablePLL0();
+  clock_disconnect();
 
 /* PLL is disabled and disconnected. setup PCLK NOW as it cannot be changed
    reliably with PLL0 connected.
    see:
    http://ics.nxp.com/support/documents/microcontrollers/pdf/errata.lpc1754.pdf
 */
-	LPC_SC->PCLKSEL1 = ( PCLK_CCLK(PCLK_TIMER3)
-                           | PCLK_CCLK8(PCLK_UART3) );
+
 
 /* continue with PLL0 setup:
    enable the xtal oscillator and wait for it to become stable

+ 2 - 0
src/clock.h

@@ -49,6 +49,8 @@
 #define PCLK_SYSCON	(28)
 #define PCLK_MC		(30)
 
+void clock_disconnect(void);
+
 void clock_init(void);
 
 void setFlashAccessTime(uint8_t clocks);

+ 11 - 3
src/config.h

@@ -1,10 +1,18 @@
 #ifndef _CONFIG_H
 #define _CONFIG_H
 
+#define SDCARD_DETECT		(1)
+#define SDCARD_WP		(0)
+#define SD_SUPPLY_VOLTAGE	(1L<<21) /* 3.3V - 3.4V */
+#define CONFIG_SD_BLOCKTRANSFER 1
+#define CONFIG_SD_AUTO_RETRIES	10
+#define CONFIG_SD_DATACRC	1
+
+
 #define CONFIG_UART_NUM			3
 #define CONFIG_CPU_FREQUENCY		90315789
 #define CONFIG_UART_PCLKDIV		1
-#define CONFIG_UART_TX_BUF_SHIFT	7
-#define CONFIG_UART_BAUDRATE		115200
-
+#define CONFIG_UART_TX_BUF_SHIFT	5
+#define CONFIG_UART_BAUDRATE		921600
+#define CONFIG_UART_DEADLOCKABLE
 #endif

+ 91 - 0
src/crc.S

@@ -0,0 +1,91 @@
+/* CRC-7/CRC-16 (XModem) implementation for Cortex M3
+ *
+ * Written 2010 by Ingo Korb
+ */
+
+        .syntax unified
+        .section .text
+
+        /* uint8_t crc7update(uint8_t crc, const uint8_t data) */
+        .global crc7update
+        .thumb_func
+crc7update:
+        mov     r2, #8          // number of bits to process
+        lsl     r1, r1, #24     // pre-shift data byte to top of word
+
+loop:
+        lsl     r0, r0, #1      // shift CRC
+        lsls    r1, r1, #1      // shift data byte (highest bit now in C)
+        bcc     0f              // jump if bit was 0
+        eor     r0, r0, #0x80   // invert top bit of CRC if not
+0:      tst     r0, #0x80       // test top bit of CRC
+        beq     1f              // skip if top bit is clear
+        eor     r0, r0, #0x09   // apply polinomial
+1:      subs    r2, r2, #1      // decrememt bit cointer
+        bne     loop            // loop for next bit
+        uxtb    r0, r0          // clear top bits of result
+        bx      lr              // return
+
+        
+        /* uint16_t crc_xmodem_block(uint16_t crc, uint8_t *data, uint32_t len) */
+        .global crc_xmodem_block
+        .thumb_func
+crc_xmodem_block:
+        adr     r12, crc_table        // load address of crc table
+blockloop:
+        ldrb.w  r3, [r1], #1          // read data byte
+        eor     r3,  r3, r0, lsr #8   // EOR data byte
+        ldrh    r3, [r12, r3, lsl #1] // load value from CRC table
+        eor     r0,  r3, r0, lsl #8   // update CRC
+        uxth    r0, r0                // clear top bits of result
+        subs    r2, r2, #1            // decrement length
+        bne     blockloop             // loop while length > 0
+        bx      lr                    // return
+
+        /* uint16_t crc_xmodem_block(uint16_t crc, uint8_t *data, uint32_t len) */
+        .global crc_xmodem_update
+        .thumb_func
+crc_xmodem_update:
+        adr     r2, crc_table        // load address of crc table
+        eor     r1,  r1, r0, lsr #8  // EOR data byte
+        ldrh    r3, [r2, r1, lsl #1] // load value from CRC table
+        eor     r0,  r3, r0, lsl #8  // update CRC
+        uxth    r0, r0               // clear top bits of result
+        bx      lr                   // return
+        
+        
+crc_table:
+        .short 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7
+        .short 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef
+        .short 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6
+        .short 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de
+        .short 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485
+        .short 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d
+        .short 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4
+        .short 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc
+        .short 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823
+        .short 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b
+        .short 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12
+        .short 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a
+        .short 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41
+        .short 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49
+        .short 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70
+        .short 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78
+        .short 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f
+        .short 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067
+        .short 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e
+        .short 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256
+        .short 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d
+        .short 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405
+        .short 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c
+        .short 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634
+        .short 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab
+        .short 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3
+        .short 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a
+        .short 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92
+        .short 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9
+        .short 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1
+        .short 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8
+        .short 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
+
+        .end

+ 11 - 0
src/crc.h

@@ -0,0 +1,11 @@
+#ifndef CRC_H
+#define CRC_H
+
+uint8_t crc7update(uint8_t crc, uint8_t data);
+uint16_t crc_xmodem_update(uint16_t crc, uint8_t data);
+uint16_t crc_xmodem_block(uint16_t crc, const uint8_t *data, uint32_t length);
+
+// AVR-libc compatibility
+#define _crc_xmodem_update(crc,data) crc_xmodem_update(crc,data)
+
+#endif

+ 36 - 0
src/diskio.h

@@ -7,6 +7,8 @@
 #define _READONLY	0	/* 1: Remove write functions */
 #define _USE_IOCTL	1	/* 1: Use disk_ioctl fucntion */
 
+#include <arm/NXP/LPC17xx/LPC17xx.h>
+
 #include "integer.h"
 
 
@@ -22,6 +24,26 @@ typedef enum {
 	RES_PARERR		/* 4: Invalid Parameter */
 } DRESULT;
 
+/**
+ * struct diskinfo0_t - disk info data structure for page 0
+ * @validbytes : Number of valid bytes in this struct
+ * @maxpage    : Highest diskinfo page supported
+ * @disktype   : type of the disk (DISK_TYPE_* values)
+ * @sectorsize : sector size divided by 256
+ * @sectorcount: number of sectors on the disk
+ *
+ * This is the struct returned in the data buffer when disk_getinfo
+ * is called with page=0.
+ */
+typedef struct {
+  uint8_t  validbytes;
+  uint8_t  maxpage;
+  uint8_t  disktype;
+  uint8_t  sectorsize;   /* divided by 256 */
+  uint32_t sectorcount;  /* 2 TB should be enough... (512 byte sectors) */
+} diskinfo0_t;
+
+
 
 /*---------------------------------------*/
 /* Prototypes for disk control functions */
@@ -35,6 +57,20 @@ DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE);
 #endif
 DRESULT disk_ioctl (BYTE, BYTE, void*);
 
+void disk_init(void);
+
+/* Will be set to DISK_ERROR if any access on the card fails */
+enum diskstates { DISK_CHANGED = 0, DISK_REMOVED, DISK_OK, DISK_ERROR };
+
+extern volatile enum diskstates disk_state;
+
+/* Disk type - part of the external API except for ATA2! */
+#define DISK_TYPE_ATA        0
+#define DISK_TYPE_ATA2       1
+#define DISK_TYPE_SD         2
+#define DISK_TYPE_DF         3
+#define DISK_TYPE_NONE       7
+
 
 
 /* Disk Status Bits (DSTATUS) */

+ 4 - 2
src/ff.c

@@ -87,7 +87,7 @@
 
 #include "ff.h"			/* FatFs configurations and declarations */
 #include "diskio.h"		/* Declarations of low level disk I/O functions */
-
+#include "uart.h"
 
 /*--------------------------------------------------------------------------
 
@@ -319,7 +319,7 @@ void mem_cpy (void* dst, const void* src, UINT cnt) {
 
 #if _WORD_ACCESS == 1
 	while (cnt >= sizeof(int)) {
-		*(int*)d = *(int*)s;
+		*((int*)d) = *((int*)s);
 		d += sizeof(int); s += sizeof(int);
 		cnt -= sizeof(int);
 	}
@@ -1815,6 +1815,8 @@ FRESULT chk_mounted (	/* FR_OK(0): successful, !=0: any error occurred */
 	fs->fs_type = 0;					/* Clear the file system object */
 	fs->drv = (BYTE)LD2PD(vol);			/* Bind the logical drive and a physical drive */
 	stat = disk_initialize(fs->drv);	/* Initialize low level disk I/O layer */
+printf("disk_initialize status: %d\n", stat);
+
 	if (stat & STA_NOINIT)				/* Check if the initialization succeeded */
 		return FR_NOT_READY;			/* Failed to initialize due to no media or hard error */
 #if _MAX_SS != 512						/* Get disk sector size (variable sector size cfg only) */

+ 4 - 4
src/ffconf.h

@@ -48,7 +48,7 @@
 /* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */
 
 
-#define	_USE_FASTSEEK	0	/* 0:Disable or 1:Enable */
+#define	_USE_FASTSEEK	1	/* 0:Disable or 1:Enable */
 /* To enable fast seek feature, set _USE_FASTSEEK to 1. */
 
 
@@ -57,7 +57,7 @@
 / Locale and Namespace Configurations
 /----------------------------------------------------------------------------*/
 
-#define _CODE_PAGE	932
+#define _CODE_PAGE	1252
 /* The _CODE_PAGE specifies the OEM code page to be used on the target system.
 /  Incorrect setting of the code page can cause a file open failure.
 /
@@ -110,7 +110,7 @@
 /  enable LFN feature and set _LFN_UNICODE to 1. */
 
 
-#define _FS_RPATH	2		/* 0 to 2 */
+#define _FS_RPATH	0		/* 0 to 2 */
 /* The _FS_RPATH option configures relative path feature.
 /
 /   0: Disable relative path feature and remove related functions.
@@ -125,7 +125,7 @@
 / Physical Drive Configurations
 /----------------------------------------------------------------------------*/
 
-#define _VOLUMES	2
+#define _VOLUMES	1
 /* Number of volumes (logical drives) to be used. */
 
 

+ 80 - 0
src/fileops.c

@@ -0,0 +1,80 @@
+/* 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
+
+   fileops.c: simple file access functions
+*/
+
+#include "config.h"
+#include "uart.h"
+#include "ff.h"
+#include "fileops.h"
+
+/*WCHAR ff_convert(WCHAR w, UINT dir) {
+	return w;	
+}*/
+
+void file_init() {
+	file_res=f_mount(0, &fatfs);
+}
+
+/*void file_open_by_filinfo(FILINFO* fno) {
+	file_res = l_openfilebycluster(&fatfs, &file_handle, (UCHAR*)"", fno->clust, fno->fsize);
+}*/
+
+void file_open(uint8_t* filename, BYTE flags) {
+	file_res = f_open(&file_handle, (TCHAR*)filename, flags);
+}
+
+void file_close() {
+	file_res = f_close(&file_handle);
+}
+
+UINT file_read() {
+	UINT bytes_read;
+	file_res = f_read(&file_handle, file_buf, sizeof(file_buf), &bytes_read);
+	return bytes_read;
+}
+
+UINT file_write() {
+	UINT bytes_written;
+	file_res = f_write(&file_handle, file_buf, sizeof(file_buf), &bytes_written);
+	return bytes_written;
+}
+
+UINT file_readblock(void* buf, uint32_t addr, uint16_t size) {
+	UINT bytes_read;
+	file_res = f_lseek(&file_handle, addr);
+	if(file_handle.fptr != addr) {
+		return 0;
+	}
+	file_res = f_read(&file_handle, buf, size, &bytes_read);
+	return bytes_read;
+}
+
+UINT file_writeblock(void* buf, uint32_t addr, uint16_t size) {
+	UINT bytes_written;
+	file_res = f_lseek(&file_handle, addr);
+	if(file_res) return 0;
+	file_res = f_write(&file_handle, buf, size, &bytes_written);
+	return bytes_written;
+}

+ 45 - 0
src/fileops.h

@@ -0,0 +1,45 @@
+/* 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
+
+   fileops.h: simple file access functions
+*/
+
+#ifndef FILEOPS_H
+#define FILEOPS_H
+#include "ff.h"
+
+BYTE file_buf[4096];
+FATFS fatfs;
+FIL file_handle;
+FRESULT file_res;
+uint8_t file_lfn[256];
+
+void file_init(void);
+void file_open(uint8_t* filename, BYTE flags);
+void file_open_by_filinfo(FILINFO* fno);
+void file_close(void);
+UINT file_read(void);
+UINT file_write(void);
+UINT file_readblock(void* buf, uint32_t addr, uint16_t size);
+UINT file_writeblock(void* buf, uint32_t addr, uint16_t size);
+#endif

+ 273 - 0
src/filetypes.c

@@ -0,0 +1,273 @@
+/* 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
+
+   filetypes.c: directory scanning and file type detection
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <avr/pgmspace.h>
+#include <util/delay.h>
+#include "config.h"
+#include "uart.h"
+#include "filetypes.h"
+#include "ff.h"
+#include "smc.h"
+#include "fileops.h"
+#include "crc16.h"
+#include "memory.h"
+#include "led.h"
+
+uint16_t scan_flat(const char* path) {
+	DIR dir;
+	FRESULT res;
+	FILINFO fno;
+	fno.lfn = NULL;
+	res = f_opendir(&dir, (unsigned char*)path);
+	uint16_t numentries = 0;
+	if (res == FR_OK) {
+		for (;;) {
+			res = f_readdir(&dir, &fno);
+			if(res != FR_OK || fno.fname[0] == 0)break;
+			numentries++;
+		}
+	}
+	return numentries;
+}
+
+uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) {
+	DIR dir;
+	FILINFO fno;
+	FRESULT res;
+	uint8_t len;
+	unsigned char* fn;
+	static unsigned char depth = 0;
+	static uint16_t crc;
+	static uint32_t db_tgt;
+	static uint32_t next_subdir_tgt;
+	static uint32_t parent_tgt;
+	static uint32_t dir_end = 0;
+	static uint8_t was_empty = 0;
+	uint32_t dir_tgt;
+	uint16_t numentries;
+	uint32_t dirsize;
+	uint8_t pass = 0;
+
+	dir_tgt = this_dir_tgt;
+	if(depth==0) {
+		crc = 0;
+		db_tgt = SRAM_DB_ADDR+0x10;
+		dir_tgt = SRAM_DIR_ADDR;
+		next_subdir_tgt = SRAM_DIR_ADDR;
+		this_dir_tgt = SRAM_DIR_ADDR;
+		parent_tgt = 0;
+		dprintf("root dir @%lx\n", dir_tgt);
+	}	
+	
+	fno.lfn = file_lfn;
+	numentries=0;
+	for(pass = 0; pass < 2; pass++) {
+		if(pass) {
+			dirsize = 4*(numentries);
+//			dir_tgt_next = dir_tgt + dirsize + 4; // number of entries + end marker
+			next_subdir_tgt += dirsize + 4;
+			if(parent_tgt) next_subdir_tgt += 4;
+			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*/);
+//			_delay_ms(50);
+			if(mkdb) {
+				dprintf("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);
+		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);
+					db_tgt &= 0xffff0000;
+					db_tgt += 0x00010000;
+					dprintf("new=%lx\n", db_tgt);
+				}
+				sram_writelong((parent_tgt-SRAM_MENU_ADDR), db_tgt);
+				sram_writebyte(0, db_tgt+sizeof(next_subdir_tgt));
+				sram_writeblock("../\0", db_tgt+sizeof(next_subdir_tgt)+sizeof(len), 4);
+				sram_writelong((db_tgt-SRAM_MENU_ADDR)|((uint32_t)0x80<<24), dir_tgt);
+				db_tgt += sizeof(next_subdir_tgt)+sizeof(len)+4;
+				dir_tgt += 4;
+			}
+			len = strlen((char*)path);
+			for (;;) {
+				toggle_busy_led();
+				res = f_readdir(&dir, &fno);
+				if (res != FR_OK || fno.fname[0] == 0) {
+					if(pass) {
+						if(!numentries) was_empty=1;
+					}
+					break;
+				}
+				fn = *fno.lfn ? fno.lfn : fno.fname;
+	//			dprintf("%s\n", fn);
+	//			_delay_ms(100);
+				if (*fn == '.') continue;
+				if (fno.fattrib & AM_DIR) {
+					numentries++;
+					if(pass) {
+						path[len]='/';
+						strncpy(path+len+1, (char*)fn, sizeof(fs_path)-len);
+						depth++;
+						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);
+//							_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));
+
+							// save element:
+							//  - 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);
+								db_tgt &= 0xffff0000;
+								db_tgt += 0x00010000;
+								dprintf("new=%lx\n", db_tgt);
+							}
+							dprintf("    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));
+							sram_writeblock(path, db_tgt+sizeof(next_subdir_tgt)+sizeof(len), pathlen);
+							sram_writeblock("/\0", db_tgt + sizeof(next_subdir_tgt) + sizeof(len) + pathlen, 2);
+//							sram_writeblock((uint8_t*)&dir_tgt, db_tgt+256, sizeof(dir_tgt));
+							db_tgt += sizeof(next_subdir_tgt) + sizeof(len) + pathlen + 2;
+						}
+						parent_tgt = this_dir_tgt;
+						scan_dir(path, mkdb, next_subdir_tgt);
+						dir_tgt += 4;
+//						if(was_empty)dir_tgt_next += 4;
+						was_empty = 0;
+						depth--;
+						path[len]=0;
+					}
+				} else {
+					SNES_FTYPE type = determine_filetype((char*)fn);
+					if(type != TYPE_UNKNOWN) {
+						numentries++;
+						if(pass) {
+							if(mkdb) {
+								snes_romprops_t romprops;
+								path[len]='/';
+								strncpy(path+len+1, (char*)fn, sizeof(fs_path)-len);
+								uint16_t pathlen = strlen(path);
+								switch(type) {
+									case TYPE_SMC:
+// XXX										file_open_by_filinfo(&fno);
+// XXX										if(file_res){
+// XXX											dprintf("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);
+//										_delay_ms(50);
+										if((db_tgt&0xffff) > ((0x10000-(sizeof(romprops) + sizeof(len) + pathlen + 1))&0xffff)) {
+											dprintf("switch! old=%lx ", db_tgt);
+											db_tgt &= 0xffff0000;
+											db_tgt += 0x00010000;
+											dprintf("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));
+										dir_tgt += 4;
+										// save element:
+										//  - SNES header information
+										//  - file name
+										sram_writeblock((uint8_t*)&romprops, db_tgt, sizeof(romprops));
+										sram_writebyte(len+1, db_tgt + sizeof(romprops));
+										sram_writeblock(path, db_tgt + sizeof(romprops) + sizeof(len), pathlen + 1);
+										db_tgt += sizeof(romprops) + sizeof(len) + pathlen + 1;
+										break;
+									case TYPE_UNKNOWN:
+									default:
+										break;
+								}
+								path[len]=0;
+		//						dprintf("%s ", path);
+		//						_delay_ms(30);
+							}
+						} else {
+							unsigned char* sfn = fno.fname;
+							while(*sfn != 0) {
+								crc += crc16_update(crc, sfn++, 1);
+							}
+						}
+					}
+	//					dprintf("%s/%s\n", path, fn);
+	//					_delay_ms(50);
+				}
+			}
+		} else uart_putc(0x30+res);
+	}
+//	dprintf("%x\n", crc);
+//	_delay_ms(50);
+	sram_writelong(db_tgt, SRAM_DB_ADDR+4);
+	sram_writelong(dir_end, SRAM_DB_ADDR+8);
+	return crc;
+}
+
+
+SNES_FTYPE determine_filetype(char* filename) {
+	char* ext = strrchr(filename, '.');
+	if(ext == NULL)
+		return TYPE_UNKNOWN;
+	if(!strcasecmp_P(ext+1, PSTR("SMC"))) {
+		return TYPE_SMC;
+	}/* later
+	if(!strcasecmp_P(ext+1, PSTR("SRM"))) {
+		return TYPE_SRM;
+	}
+	if(!strcasecmp_P(ext+1, PSTR("SPC"))) {
+		return TYPE_SPC;
+	}*/
+	return TYPE_UNKNOWN;
+}
+
+FRESULT get_db_id(uint16_t* id) {
+	file_open((uint8_t*)"/sd2snes/sd2snes.db", FA_READ);
+	if(file_res == FR_OK) {
+		file_readblock(id, 0, 2);
+/* XXX */// *id=0xdead;
+		file_close();
+	} else {
+		*id=0xdead;
+	}
+	return file_res;
+}

+ 45 - 0
src/filetypes.h

@@ -0,0 +1,45 @@
+/* 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
+
+   filetypes.h: directory scanning and file type detection
+*/
+
+#ifndef FILETYPES_H
+#define FILETYPES_H
+
+#include "ff.h"
+typedef enum {
+	TYPE_UNKNOWN = 0,	/* 0 */
+	TYPE_SMC,		/* 1 */
+	TYPE_SRM,		/* 2 */
+	TYPE_SPC		/* 3 */
+} SNES_FTYPE;
+
+
+char fs_path[256];
+SNES_FTYPE determine_filetype(char* filename);
+//uint32_t scan_fs();
+uint16_t scan_dir(char* path, char mkdb, uint32_t this_subdir_tgt);
+FRESULT get_db_id(uint16_t*);
+
+#endif

+ 0 - 1
src/flash.script

@@ -7,7 +7,6 @@
 
 reset init
 flash write_image erase unlock obj/sd2snes.bin 0 bin
-sleep 200
 reset run
 shutdown
 

+ 4 - 0
src/init.gdb

@@ -0,0 +1,4 @@
+target remote localhost:3333
+add-symbol-file obj/sd2snes.elf 0x0
+load obj/sd2snes.elf
+

+ 24 - 0
src/led.c

@@ -0,0 +1,24 @@
+/* ___DISCLAIMER___ */
+
+#include <arm/NXP/LPC17xx/LPC17xx.h>
+#include "bits.h"
+#include "timer.h"
+
+void rdyled(unsigned int state) {
+  BITBAND(LPC_GPIO2->FIODIR, 0) = state;
+}
+
+void readled(unsigned int state) {
+  BITBAND(LPC_GPIO2->FIODIR, 1) = state;
+}
+
+void led_clkout32(uint32_t val) {
+  while(1) {
+    rdyled(1);
+    delay_ms(400);
+    readled((val & BV(31))>>31);
+    rdyled(0);
+    val<<=1;
+    delay_ms(400);
+  }
+}

+ 10 - 0
src/led.h

@@ -0,0 +1,10 @@
+/* ___DISCLAIMER___ */
+
+#ifndef _LED_H
+#define _LED_H
+
+void readled(unsigned int state);
+void rdyled(unsigned int state);
+void led_clkout32(uint32_t val);
+
+#endif

+ 0 - 8
src/lpc.h

@@ -1,8 +0,0 @@
-#ifndef _LPC_H
-#define _LPC_H
-
-#define BV(x) (1<<(x))
-#define BITBAND(addr,bit) (*((volatile unsigned long *)(((unsigned long)&(addr)-0x20000000)*32 + bit*4 + 0x22000000)))
-
-#endif
-

+ 39 - 7
src/main.c

@@ -1,35 +1,67 @@
 #include <arm/NXP/LPC17xx/LPC17xx.h>
 #include "clock.h"
 #include "uart.h"
-#include "lpc.h"
+#include "bits.h"
+#include "power.h"
+#include "timer.h"
+#include "ff.h"
+#include "diskio.h"
+#include "spi.h"
+#include "sdcard.h"
+#include "fileops.h"
 
 #define EMC0TOGGLE	(3<<4)
 #define MR0R		(1<<1)
-#define PCTIM3		(1<<23)
 
 int i;
 
+/* FIXME HACK */
+volatile enum diskstates disk_state;
+
 int main(void) {
   LPC_GPIO2->FIODIR = BV(0) | BV(1) | BV(2);
   LPC_GPIO1->FIODIR = 0;
   uint32_t p1;
 
+ /* connect UART3 on P0[25:26] + SSP1 on P0[6:9] */
+  LPC_PINCON->PINSEL1=(0xf<<18);
+  LPC_PINCON->PINSEL0=BV(13) | BV(15) | BV(17) | BV(19);
 
-  clock_init();
+  clock_disconnect();
+
+  power_init();
+  timer_init();
   uart_init();
+  delay_ms(10);
+  spi_init(SPI_SPEED_SLOW);
+  sd_init();
+/* do this last because the peripheral init()s change PCLK dividers */
+  clock_init();
+
+  file_init();
+  printf("fileres: %d diskstate: %d\n", file_res, disk_state);
+  file_open((uint8_t*)"/mon.smc", FA_READ);
+  spi_set_speed(SPI_SPEED_FAST);
+  printf("fileres: %d diskstate: %d\n", file_res, disk_state);
+  uart_putc('S');
+  for(p1=0; p1<2048; p1++) {
+    file_read();
+  }
+  uart_putc('E');
+  uart_putcrlf();
+  printf("sizeof(struct FIL): %d\n", sizeof(file_handle));
+  uart_trace(file_buf, 0, 512);
 
 /* setup timer (fpga clk) */
-  LPC_SC->PCONP |= PCTIM3;	/* enable power */
   LPC_TIM3->CTCR=0;
   LPC_TIM3->EMR=EMC0TOGGLE;
   LPC_PINCON->PINSEL0=(0x3<<20);
   LPC_TIM3->MCR=MR0R;
   LPC_TIM3->MR0=1;
   LPC_TIM3->TCR=1;
-  
-
+  uart_puts("hurr durr derpderpderp\n");
   while (1) {
     p1 = LPC_GPIO1->FIOPIN;
-    BITBAND(LPC_GPIO2->FIOPIN, 0) = (p1 & BV(29))>>29;
+    BITBAND(LPC_GPIO2->FIOPIN, 1) = (p1 & BV(29))>>29;
   }
 }

+ 10 - 0
src/power.c

@@ -0,0 +1,10 @@
+/* ___DISCLAIMER___ */
+
+#include <arm/NXP/LPC17xx/LPC17xx.h>
+#include "bits.h"
+#include "power.h"
+
+
+void power_init() {
+  LPC_SC->PCONP |= (BV(PCTIM3) | BV(PCUART3));
+}

+ 14 - 0
src/power.h

@@ -0,0 +1,14 @@
+/* ___DISCLAIMER___ */
+
+#ifndef _POWER_H
+#define _POWER_H
+
+#include "bits.h"
+
+#define PCUART3		(25)
+#define PCUART0		(3)
+#define PCTIM3          (23)
+
+void power_init(void);
+
+#endif

+ 290 - 0
src/printf.c

@@ -0,0 +1,290 @@
+/* Small, noncompliant, not-full-featured printf implementation
+ *
+ *
+ * Copyright (c) 2010, Ingo Korb
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Ingo Korb nor the
+ *       names of the contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * FIXME: Selection of output function should be more flexible
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include "config.h"
+#include "uart.h"
+
+#define outfunc(x) uart_putc(x)
+
+#define FLAG_ZEROPAD   1
+#define FLAG_LEFTADJ   2
+#define FLAG_BLANK     4
+#define FLAG_FORCESIGN 8
+#define FLAG_WIDTH     16
+#define FLAG_LONG      32
+#define FLAG_UNSIGNED  64
+#define FLAG_NEGATIVE  128
+
+/* Digits used for conversion */
+static const char hexdigits[] = "0123456789abcdef";
+
+/* Temporary buffer used for numbers - just large enough for 32 bit in octal */
+static char buffer[12];
+
+/* Output string length */
+static unsigned int outlength;
+
+/* Output pointer */
+static char *outptr;
+static int maxlen;
+
+/* printf */
+static void outchar(char x) {
+  if (maxlen) {
+    maxlen--;
+    outfunc(x);
+    outlength++;
+  }
+}
+
+/* sprintf */
+static void outstr(char x) {
+  if (maxlen) {
+    maxlen--;
+    *outptr++ = x;
+    outlength++;
+  }
+}
+
+static int internal_nprintf(void (*output_function)(char c), const char *fmt, va_list ap) {
+  unsigned int width;
+  unsigned int flags;
+  unsigned int base = 0;
+  char *ptr = NULL;
+
+  outlength = 0;
+
+  while (*fmt) {
+    while (1) {
+      if (*fmt == 0)
+        goto end;
+
+      if (*fmt == '%') {
+        fmt++;
+        if (*fmt != '%')
+          break;
+      }
+
+      output_function(*fmt++);
+    }
+
+    flags = 0;
+    width = 0;
+
+    /* read all flags */
+    do {
+      if (flags < FLAG_WIDTH) {
+        switch (*fmt) {
+        case '0':
+          flags |= FLAG_ZEROPAD;
+          continue;
+
+        case '-':
+          flags |= FLAG_LEFTADJ;
+          continue;
+
+        case ' ':
+          flags |= FLAG_BLANK;
+          continue;
+
+        case '+':
+          flags |= FLAG_FORCESIGN;
+          continue;
+        }
+      }
+
+      if (flags < FLAG_LONG) {
+        if (*fmt >= '0' && *fmt <= '9') {
+          unsigned char tmp = *fmt - '0';
+          width = 10*width + tmp;
+          flags |= FLAG_WIDTH;
+          continue;
+        }
+
+        if (*fmt == 'h')
+          continue;
+
+        if (*fmt == 'l') {
+          flags |= FLAG_LONG;
+          continue;
+        }
+      }
+
+      break;
+    } while (*fmt++);
+
+    /* Strings */
+    if (*fmt == 'c' || *fmt == 's') {
+      switch (*fmt) {
+      case 'c':
+        buffer[0] = va_arg(ap, int);
+        ptr = buffer;
+        break;
+
+      case 's':
+        ptr = va_arg(ap, char *);
+        break;
+      }
+
+      goto output;
+    }
+
+    /* Numbers */
+    switch (*fmt) {
+    case 'u':
+      flags |= FLAG_UNSIGNED;
+    case 'd':
+      base = 10;
+      break;
+
+    case 'o':
+      base = 8;
+      flags |= FLAG_UNSIGNED;
+      break;
+
+    case 'p': // pointer
+      output_function('0');
+      output_function('x');
+      width -= 2;
+    case 'x':
+      base = 16;
+      flags |= FLAG_UNSIGNED;
+      break;
+    }
+
+    unsigned int num;
+
+    if (!(flags & FLAG_UNSIGNED)) {
+      int tmp = va_arg(ap, int);
+      if (tmp < 0) {
+        num = -tmp;
+        flags |= FLAG_NEGATIVE;
+      } else
+        num = tmp;
+    } else {
+      num = va_arg(ap, unsigned int);
+    }
+
+    /* Convert number into buffer */
+    ptr = buffer + sizeof(buffer);
+    *--ptr = 0;
+    do {
+      *--ptr = hexdigits[num % base];
+      num /= base;
+    } while (num != 0);
+
+    /* Sign */
+    if (flags & FLAG_NEGATIVE) {
+      output_function('-');
+      width--;
+    } else if (flags & FLAG_FORCESIGN) {
+      output_function('+');
+      width--;
+    } else if (flags & FLAG_BLANK) {
+      output_function(' ');
+      width--;
+    }
+
+  output:
+    /* left padding */
+    if ((flags & FLAG_WIDTH) && !(flags & FLAG_LEFTADJ)) {
+      while (strlen(ptr) < width) {
+        if (flags & FLAG_ZEROPAD)
+          output_function('0');
+        else
+          output_function(' ');
+        width--;
+      }
+    }
+
+    /* data */
+    while (*ptr) {
+      output_function(*ptr++);
+      if (width)
+        width--;
+    }
+
+    /* right padding */
+    if (flags & FLAG_WIDTH) {
+      while (width) {
+        output_function(' ');
+        width--;
+      }
+    }
+
+    fmt++;
+  }
+
+ end:
+  return outlength;
+}
+
+int printf(const char *format, ...) {
+  va_list ap;
+  int res;
+
+  maxlen = -1;
+  va_start(ap, format);
+  res = internal_nprintf(outchar, format, ap);
+  va_end(ap);
+  return res;
+}
+
+int snprintf(char *str, size_t size, const char *format, ...) {
+  va_list ap;
+  int res;
+
+  maxlen = size;
+  outptr = str;
+  va_start(ap, format);
+  res = internal_nprintf(outstr, format, ap);
+  va_end(ap);
+  if (res < size)
+    str[res] = 0;
+  return res;
+}
+
+/* Required for gcc compatibility */
+int puts(const char *str) {
+  uart_puts(str);
+  uart_putc('\n');
+  return 0;
+}
+
+#undef putchar
+int putchar(int c) {
+  uart_putc(c);
+  return 0;
+}

+ 54 - 0
src/rtc.c

@@ -0,0 +1,54 @@
+#include <arm/NXP/LPC17xx/LPC17xx.h>
+#include <arm/bits.h>
+#include "config.h"
+#include "rtc.h"
+
+rtcstate_t rtc_state;
+
+#define CLKEN  0
+#define CTCRST 1
+
+void rtc_init(void) {
+  if (LPC_RTC->CCR & BV(CLKEN)) {
+    rtc_state = RTC_OK;
+  } else {
+    rtc_state = RTC_INVALID;
+  }
+}
+
+void read_rtc(struct tm *time) {
+  do {
+    time->tm_sec  = LPC_RTC->SEC;
+    time->tm_min  = LPC_RTC->MIN;
+    time->tm_hour = LPC_RTC->HOUR;
+    time->tm_mday = LPC_RTC->DOM;
+    time->tm_mon  = LPC_RTC->MONTH;
+    time->tm_year = LPC_RTC->YEAR - 1900;
+    time->tm_wday = LPC_RTC->DOW;
+  } while (time->tm_sec != LPC_RTC->SEC);
+}
+
+void set_rtc(struct tm *time) {
+  LPC_RTC->CCR   = BV(CTCRST);
+  LPC_RTC->SEC   = time->tm_sec;
+  LPC_RTC->MIN   = time->tm_min;
+  LPC_RTC->HOUR  = time->tm_hour;
+  LPC_RTC->DOM   = time->tm_mday;
+  LPC_RTC->MONTH = time->tm_mon;
+  LPC_RTC->YEAR  = time->tm_year + 1900;
+  LPC_RTC->DOW   = time->tm_wday;
+  LPC_RTC->CCR   = BV(CLKEN);
+}
+
+
+uint32_t get_fattime(void) {
+  struct tm time;
+
+  read_rtc(&time);
+  return ((uint32_t)time.tm_year-80) << 25 |
+    ((uint32_t)time.tm_mon+1) << 21 |
+    ((uint32_t)time.tm_mday)  << 16 |
+    ((uint32_t)time.tm_hour)  << 11 |
+    ((uint32_t)time.tm_min)   << 5  |
+    ((uint32_t)time.tm_sec)   >> 1;
+}

+ 62 - 0
src/rtc.h

@@ -0,0 +1,62 @@
+/* sd2iec - SD/MMC to Commodore serial bus interface/controller
+   Copyright (C) 2007-2010  Ingo Korb <ingo@akana.de>
+
+   Inspiration and low-level SD/MMC access based on code from MMC2IEC
+     by Lars Pontoppidan et al., see sdcard.c|h and config.h.
+
+   FAT filesystem access based on code from ChaN and Jim Brain, see ff.c|h.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License only.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+   rtc.h: Definitions for RTC support
+
+   There is no rtc.c, the functions defined here are implemented by a
+   device-specific .c file, e.g. pcf8583.c.
+
+*/
+
+#ifndef RTC_H
+#define RTC_H
+
+#include <stdint.h>
+
+typedef enum {
+  RTC_NOT_FOUND,  /* No RTC present                    */
+  RTC_INVALID,    /* RTC present, but contents invalid */
+  RTC_OK          /* RTC present and working           */
+} rtcstate_t;
+
+struct tm {
+  uint8_t tm_sec;  // 0..59
+  uint8_t tm_min;  // 0..59
+  uint8_t tm_hour; // 0..23
+  uint8_t tm_mday; // 1..[28..31]
+  uint8_t tm_mon;  // 0..11
+  uint8_t tm_year; // since 1900, i.e. 2000 is 100
+  uint8_t tm_wday; // 0 to 6, sunday is 6
+  // A Unix struct tm has a few more fields we don't need in this application
+};
+
+extern rtcstate_t rtc_state;
+
+void rtc_init(void);
+
+/* Return current time in struct tm */
+void read_rtc(struct tm *time);
+
+/* Set time from struct tm */
+void set_rtc(struct tm *time);
+
+#endif

+ 750 - 0
src/sdcard.c

@@ -0,0 +1,750 @@
+/* sd2iec - SD/MMC to Commodore serial bus interface/controller
+   Copyright (C) 2007-2010  Ingo Korb <ingo@akana.de>
+
+   Inspiration and low-level SD/MMC access based on code from MMC2IEC
+     by Lars Pontoppidan et al., see sdcard.c|h and config.h.
+
+   FAT filesystem access based on code from ChaN and Jim Brain, see ff.c|h.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License only.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+   sdcard.c: SD/MMC access routines
+
+   Extended, optimized and cleaned version of code from MMC2IEC,
+   original copyright header follows:
+
+//
+// Title        : SD/MMC Card driver
+// Author       : Lars Pontoppidan, Aske Olsson, Pascal Dufour,
+// Date         : Jan. 2006
+// Version      : 0.42
+// Target MCU   : Atmel AVR Series
+//
+// CREDITS:
+// This module is developed as part of a project at the technical univerisity of
+// Denmark, DTU.
+//
+// DESCRIPTION:
+// This SD card driver implements the fundamental communication with a SD card.
+// The driver is confirmed working on 8 MHz and 14.7456 MHz AtMega32 and has
+// been tested successfully with a large number of different SD and MMC cards.
+//
+// DISCLAIMER:
+// The author is in no way responsible for any problems or damage caused by
+// using this code. Use at your own risk.
+//
+// LICENSE:
+// This code is distributed under the GNU Public License
+// which can be found at http://www.gnu.org/licenses/gpl.txt
+//
+
+  The exported functions in this file are weak-aliased to their corresponding
+  versions defined in diskio.h so when this file is the only diskio provider
+  compiled in they will be automatically used by the linker.
+
+*/
+
+#include <arm/NXP/LPC17xx/LPC17xx.h>
+#include <arm/bits.h>
+#include "config.h"
+#include "crc.h"
+#include "diskio.h"
+#include "spi.h"
+#include "timer.h"
+#include "uart.h"
+#include "sdcard.h"
+
+// FIXME: Move, make configurable
+static void set_sd_led(uint8_t state) {
+  BITBAND(LPC_GPIO2->FIODIR, 2) = state;
+}
+
+// FIXME: Move, add generic C or AVR ASM version
+static uint32_t swap_word(uint32_t input) {
+  uint32_t result;
+  asm("rev %[result], %[input]" : [result] "=r" (result) : [input] "r" (input));
+  return result;
+}
+
+#ifdef CONFIG_TWINSD
+#  define MAX_CARDS 2
+#else
+#  define MAX_CARDS 1
+#endif
+
+// SD/MMC commands
+#define GO_IDLE_STATE           0
+#define SEND_OP_COND            1
+#define SWITCH_FUNC             6
+#define SEND_IF_COND            8
+#define SEND_CSD                9
+#define SEND_CID               10
+#define STOP_TRANSMISSION      12
+#define SEND_STATUS            13
+#define SET_BLOCKLEN           16
+#define READ_SINGLE_BLOCK      17
+#define READ_MULTIPLE_BLOCK    18
+#define WRITE_BLOCK            24
+#define WRITE_MULTIPLE_BLOCK   25
+#define PROGRAM_CSD            27
+#define SET_WRITE_PROT         28
+#define CLR_WRITE_PROT         29
+#define SEND_WRITE_PROT        30
+#define ERASE_WR_BLK_STAR_ADDR 32
+#define ERASE_WR_BLK_END_ADDR  33
+#define ERASE                  38
+#define LOCK_UNLOCK            42
+#define APP_CMD                55
+#define GEN_CMD                56
+#define READ_OCR               58
+#define CRC_ON_OFF             59
+
+// SD ACMDs
+#define SD_STATUS                 13
+#define SD_SEND_NUM_WR_BLOCKS     22
+#define SD_SET_WR_BLK_ERASE_COUNT 23
+#define SD_SEND_OP_COND           41
+#define SD_SET_CLR_CARD_DETECT    42
+#define SD_SEND_SCR               51
+
+// R1 status bits
+#define STATUS_IN_IDLE          1
+#define STATUS_ERASE_RESET      2
+#define STATUS_ILLEGAL_COMMAND  4
+#define STATUS_CRC_ERROR        8
+#define STATUS_ERASE_SEQ_ERROR 16
+#define STATUS_ADDRESS_ERROR   32
+#define STATUS_PARAMETER_ERROR 64
+
+
+/* Card types - cardtype == 0 is MMC */
+#define CARD_SD   (1<<0)
+#define CARD_SDHC (1<<1)
+
+static uint8_t cardtype[MAX_CARDS];
+
+/**
+ * getbits - read value from bit buffer
+ * @buffer: pointer to the data buffer
+ * @start : index of the first bit in the value
+ * @bits  : number of bits in the value
+ *
+ * This function returns a value from the memory region passed as
+ * buffer, starting with bit "start" and "bits" bit long. The buffer
+ * is assumed to be MSB first, passing 0 for start will read starting
+ * from the highest-value bit of the first byte of the buffer.
+ */
+static uint32_t getbits(void *buffer, uint16_t start, int8_t bits) {
+  uint8_t *buf = buffer;
+  uint32_t result = 0;
+
+  if ((start % 8) != 0) {
+    /* Unaligned start */
+    result += buf[start / 8] & (0xff >> (start % 8));
+    bits  -= 8 - (start % 8);
+    start += 8 - (start % 8);
+  }
+  while (bits >= 8) {
+    result = (result << 8) + buf[start / 8];
+    start += 8;
+    bits -= 8;
+  }
+  if (bits > 0) {
+    result = result << bits;
+    result = result + (buf[start / 8] >> (8-bits));
+  } else if (bits < 0) {
+    /* Fraction of a single byte */
+    result = result >> -bits;
+  }
+  return result;
+}
+
+/**
+ * wait_for_response - waits for a response from the SD card
+ * @expected: expected data byte (0 for anything != 0)
+ *
+ * This function waits until reading from the SD card returns the
+ * byte in expected or until reading returns a non-zero byte if
+ * expected is 0. Returns false if the expected response wasn't
+ * received within 500ms or true if it was.
+ */
+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();
+
+    if (expected == 0 && byte != 0)
+      return 1;
+
+    if (expected != 0 && byte == expected)
+      return 1;
+  }
+
+  return 0;
+}
+
+static void deselectCard(uint8_t card) {
+  // Send 8 clock cycles
+  set_sd_led(0);
+  spi_rx_byte();
+}
+
+/**
+ * sendCommand - send a command to the SD card
+ * @card     : card number to be accessed
+ * @command  : command to be sent
+ * @parameter: parameter to be sent
+ * @deselect : Flags if the card should be deselected afterwards
+ *
+ * This function calculates the correct CRC7 for the command and
+ * parameter and transmits all of it to the SD card. If requested
+ * the card will be deselected afterwards.
+ */
+static int sendCommand(const uint8_t  card,
+                       const uint8_t  command,
+                       const uint32_t parameter,
+                       const uint8_t  deselect) {
+  union {
+    uint32_t l;
+    uint8_t  c[4];
+  } long2char;
+
+  uint8_t i,crc,errorcount;
+  tick_t  timeout;
+
+  long2char.l = parameter;
+  crc = crc7update(0  , 0x40+command);
+  crc = crc7update(crc, long2char.c[3]);
+  crc = crc7update(crc, long2char.c[2]);
+  crc = crc7update(crc, long2char.c[1]);
+  crc = crc7update(crc, long2char.c[0]);
+  crc = (crc << 1) | 1;
+
+  errorcount = 0;
+  while (errorcount < CONFIG_SD_AUTO_RETRIES) {
+    // Select card
+    set_sd_led(1);
+#ifdef CONFIG_TWINSD
+    if (card == 0 && command == GO_IDLE_STATE)
+      /* Force both cards to SPI mode simultaneously */
+      SPI_SS_LOW(1);
+#endif
+
+    // Transfer command
+    spi_tx_byte(0x40+command);
+    uint32_t tmp = swap_word(parameter);
+    spi_tx_block(&tmp, 4);
+    spi_tx_byte(crc);
+
+    // Wait for a valid response
+    timeout = getticks() + HZ/2;
+    do {
+      i = spi_rx_byte();
+    } while (i & 0x80 && time_before(getticks(), timeout));
+
+#ifdef CONFIG_TWINSD
+    if (card == 0 && command == GO_IDLE_STATE)
+      SPI_SS_HIGH(1);
+#endif
+
+    // Check for CRC error
+    // can't reliably retry unless deselect is allowed
+    if (deselect && (i & STATUS_CRC_ERROR)) {
+      uart_putc('x');
+      deselectCard(card);
+      errorcount++;
+      continue;
+    }
+
+    if (deselect) deselectCard(card);
+    break;
+  }
+
+  return i;
+}
+
+// Extended init sequence for SDHC support
+static uint8_t extendedInit(const uint8_t card) {
+  uint8_t  i;
+  uint32_t answer;
+
+  // Send CMD8: SEND_IF_COND
+  //   0b000110101010 == 2.7-3.6V supply, check pattern 0xAA
+  i = sendCommand(card, SEND_IF_COND, 0b000110101010, 0);
+  if (i > 1) {
+    // Card returned an error, ok (MMC or SD1.x) but not SDHC
+    deselectCard(card);
+    return 1;
+  }
+
+  // No error, continue SDHC initialization
+  spi_rx_block(&answer, 4);
+  answer = swap_word(answer);
+  deselectCard(card);
+
+  if (((answer >> 8) & 0x0f) != 0b0001) {
+    // Card didn't accept our voltage specification
+    return 0;
+  }
+
+  // Verify echo-back of check pattern
+  if ((answer & 0xff) != 0b10101010) {
+    // Check pattern mismatch, working but not SD2.0 compliant
+    // The specs say we should not use the card, but let's try anyway.
+    return 1;
+  }
+
+  return 1;
+}
+
+// SD common initialisation
+static void sdInit(const uint8_t card) {
+  uint8_t i;
+  uint16_t counter;
+printf("sdInit\n");
+  counter = 0xffff;
+  do {
+    // Prepare for ACMD, send CMD55: APP_CMD
+    i = sendCommand(card, APP_CMD, 0, 1);
+    if (i > 1) {
+      // Command not accepted, could be MMC
+      return;
+    }
+
+    // Send ACMD41: SD_SEND_OP_COND
+    //   1L<<30 == Host has High Capacity Support
+    i = sendCommand(card, SD_SEND_OP_COND, 1L<<30, 1);
+    // Repeat while card card accepts command but isn't ready
+  } while (i == 1 && --counter > 0);
+
+  // Ignore failures, there is at least one Sandisk MMC card
+  // that accepts CMD55, but not ACMD41.
+  if (i == 0)
+    /* We know that a card is SD if ACMD41 was accepted. */
+    cardtype[card] |= CARD_SD;
+}
+
+/* Detect changes of SD card 0 */
+#ifdef SD_CHANGE_VECT
+ISR(SD_CHANGE_VECT) {
+  if (SDCARD_DETECT)
+    disk_state = DISK_CHANGED;
+  else
+    disk_state = DISK_REMOVED;
+}
+#endif
+
+#ifdef CONFIG_TWINSD
+/* Detect changes of SD card 1 */
+ISR(SD2_CHANGE_VECT) {
+  if (SD2_DETECT)
+    disk_state = DISK_CHANGED;
+  else
+    disk_state = DISK_REMOVED;
+}
+#endif
+
+//
+// Public functions
+//
+void sd_init(void) {
+  /*
+  SDCARD_DETECT_SETUP();
+  SDCARD_WP_SETUP();
+  SD_CHANGE_SETUP();
+  */
+#ifdef CONFIG_TWINSD
+  /* Initialize the control lines for card 2 */
+  SD2_SETUP();
+  SD2_CHANGE_SETUP();
+#endif
+}
+void disk_init(void) __attribute__ ((weak, alias("sd_init")));
+
+
+DSTATUS sd_status(BYTE drv) {
+#ifdef CONFIG_TWINSD
+  if (drv != 0) {
+    if (SD2_DETECT) {
+      if (SD2_PIN & SD2_WP) {
+        return STA_PROTECT;
+      } else {
+        return RES_OK;
+      }
+    } else {
+      return STA_NOINIT|STA_NODISK;
+    }
+  } else
+#endif
+  if (SDCARD_DETECT)
+    if (SDCARD_WP)
+      return STA_PROTECT;
+    else
+      return RES_OK;
+  else
+    return STA_NOINIT|STA_NODISK;
+}
+DSTATUS disk_status(BYTE drv) __attribute__ ((weak, alias("sd_status")));
+
+
+/**
+ * sd_initialize - initialize SD card
+ * @drv   : drive
+ *
+ * This function tries to initialize the selected SD card.
+ */
+DSTATUS sd_initialize(BYTE drv) {
+  uint8_t  i;
+  uint16_t counter;
+  uint32_t answer;
+
+  if (drv >= MAX_CARDS)
+    return STA_NOINIT|STA_NODISK;
+  /* Don't bother initializing a card that isn't there */
+  if (sd_status(drv) & STA_NODISK)
+    return sd_status(drv);
+  /* JLB: Should be in sd_init, but some uIEC versions have
+   * IEC lines tied to SPI, so I moved it here to resolve the
+   * conflict.
+   */
+  spi_init(SPI_SPEED_SLOW);
+  disk_state = DISK_ERROR;
+
+  cardtype[drv] = 0;
+
+  set_sd_led(0);
+
+  // Send 80 clks
+  for (counter=0; counter<1000; counter++) {
+    spi_tx_byte(0xff);
+  }
+
+  // Reset card
+  i = sendCommand(drv, GO_IDLE_STATE, 0, 1);
+  if (i != 1) {
+    return STA_NOINIT | STA_NODISK;
+  }
+
+  if (!extendedInit(drv)) {
+    return STA_NOINIT | STA_NODISK;
+  }
+
+  sdInit(drv);
+
+  counter = 0xffff;
+  // According to the spec READ_OCR should work at this point
+  // without retries. One of my Sandisk-cards thinks otherwise.
+  do {
+    // Send CMD58: READ_OCR
+    i = sendCommand(drv, READ_OCR, 0, 0);
+    if (i > 1)
+      deselectCard(drv);
+  } while (i > 1 && counter-- > 0);
+
+  if (counter > 0) {
+    spi_rx_block(&answer, 4);
+    answer = swap_word(answer);
+
+    // See if the card likes our supply voltage
+    if (!(answer & SD_SUPPLY_VOLTAGE)) {
+      // The code isn't set up to completely ignore the card,
+      // but at least report it as nonworking
+      deselectCard(drv);
+      return STA_NOINIT | STA_NODISK;
+    }
+
+    // See what card we've got
+    if (answer & 0x40000000) {
+      cardtype[drv] |= CARD_SDHC;
+    }
+  }
+
+  // Keep sending CMD1 (SEND_OP_COND) command until zero response
+  counter = 0xffff;
+  do {
+    i = sendCommand(drv, SEND_OP_COND, 1L<<30, 1);
+    counter--;
+  } while (i != 0 && counter > 0);
+
+  if (counter==0) {
+    return STA_NOINIT | STA_NODISK;
+  }
+
+#ifdef CONFIG_SD_DATACRC
+  // Enable CRC checking
+  // The SD spec says that the host "should" send CRC_ON_OFF before ACMD_SEND_OP_COND.
+  // The MMC manual I have says that CRC_ON_OFF isn't allowed before SEND_OP_COND.
+  // Let's just hope that all SD cards work with this order. =(
+  i = sendCommand(drv, CRC_ON_OFF, 1, 1);
+  if (i > 1) {
+    return STA_NOINIT | STA_NODISK;
+  }
+#endif
+
+  // Send MMC CMD16(SET_BLOCKLEN) to 512 bytes
+  i = sendCommand(drv, SET_BLOCKLEN, 512, 1);
+  if (i != 0) {
+    return STA_NOINIT | STA_NODISK;
+  }
+
+  // Thats it!
+  spi_set_speed(SPI_SPEED_FAST);
+  disk_state = DISK_OK;
+  return sd_status(drv);
+}
+DSTATUS disk_initialize(BYTE drv) __attribute__ ((weak, alias("sd_initialize")));
+
+
+/**
+ * sd_read - reads sectors from the SD card to buffer
+ * @drv   : drive
+ * @buffer: pointer to the buffer
+ * @sector: first sector to be read
+ * @count : number of sectors to be read
+ *
+ * This function reads count sectors from the SD card starting
+ * at sector to buffer. Returns RES_ERROR if an error occured or
+ * RES_OK if successful. Up to SD_AUTO_RETRIES will be made if
+ * the calculated data CRC does not match the one sent by the
+ * card. If there were errors during the command transmission
+ * disk_state will be set to DISK_ERROR and no retries are made.
+ */
+DRESULT sd_read(BYTE drv, BYTE *buffer, DWORD sector, BYTE count) {
+  uint8_t sec,res,errorcount;
+  uint16_t crc,recvcrc;
+  if (drv >= MAX_CARDS)
+    return RES_PARERR;
+
+  for (sec=0;sec<count;sec++) {
+    errorcount = 0;
+    while (errorcount < CONFIG_SD_AUTO_RETRIES) {
+      if (cardtype[drv] & CARD_SDHC)
+        res = sendCommand(drv, READ_SINGLE_BLOCK, sector+sec, 0);
+      else
+        res = sendCommand(drv, READ_SINGLE_BLOCK, (sector+sec) << 9, 0);
+
+      if (res != 0) {
+        set_sd_led(0);
+        disk_state = DISK_ERROR;
+        return RES_ERROR;
+      }
+
+      // Wait for data token
+      if (!wait_for_response(0xFE)) {
+        set_sd_led(0);
+        disk_state = DISK_ERROR;
+        return RES_ERROR;
+      }
+
+      // Get data
+      crc = 0;
+
+#ifdef CONFIG_SD_BLOCKTRANSFER
+      /* Transfer data first, calculate CRC afterwards */
+      spi_rx_block(buffer, 512);
+
+      recvcrc = spi_rx_byte() << 8 | spi_rx_byte();
+#ifdef CONFIG_SD_DATACRC
+      crc = crc_xmodem_block(0, buffer, 512);
+#endif
+#else
+      /* Interleave transfer/CRC calculation, AVR-specific */
+      // Initiate data exchange over SPI
+      SPDR = 0xff;
+      uint8_t tmp;
+
+      for (i=0; i<512; i++) {
+        // Wait until data has been received
+        loop_until_bit_is_set(SPSR, SPIF);
+        tmp = SPDR;
+        // Transmit the next byte while we store the current one
+        SPDR = 0xff;
+
+        *(buffer++) = tmp;
+#ifdef CONFIG_SD_DATACRC
+        crc = _crc_xmodem_update(crc, tmp);
+#endif
+      }
+      // Wait until the first CRC byte is received
+      loop_until_bit_is_set(SPSR, SPIF);
+
+      // Check CRC
+      recvcrc = (SPDR << 8) + spiTransferByte(0xff);
+#endif
+
+#ifdef CONFIG_SD_DATACRC
+      if (recvcrc != crc) {
+        uart_putc('X');
+        deselectCard(drv);
+        errorcount++;
+        continue;
+      }
+#endif
+
+      break;
+    }
+    deselectCard(drv);
+
+    if (errorcount >= CONFIG_SD_AUTO_RETRIES) return RES_ERROR;
+  }
+
+  return RES_OK;
+}
+DRESULT disk_read(BYTE drv, BYTE *buffer, DWORD sector, BYTE count) __attribute__ ((weak, alias("sd_read")));
+
+
+
+/**
+ * sd_write - writes sectors from buffer to the SD card
+ * @drv   : drive
+ * @buffer: pointer to the buffer
+ * @sector: first sector to be written
+ * @count : number of sectors to be written
+ *
+ * This function writes count sectors from buffer to the SD card
+ * starting at sector. Returns RES_ERROR if an error occured,
+ * RES_WPRT if the card is currently write-protected or RES_OK
+ * if successful. Up to SD_AUTO_RETRIES will be made if the card
+ * signals a CRC error. If there were errors during the command
+ * transmission disk_state will be set to DISK_ERROR and no retries
+ * are made.
+ */
+DRESULT sd_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count) {
+  uint8_t res,sec,errorcount,status;
+  uint16_t crc;
+
+  if (drv >= MAX_CARDS)
+    return RES_PARERR;
+
+#ifdef CONFIG_TWINSD
+  if (drv != 0) {
+    if (SD2_PIN & SD2_WP)
+      return RES_WRPRT;
+  } else
+#endif
+
+  if (SDCARD_WP) return RES_WRPRT;
+
+  for (sec=0;sec<count;sec++) {
+    errorcount = 0;
+    while (errorcount < CONFIG_SD_AUTO_RETRIES) {
+      if (cardtype[drv] & CARD_SDHC)
+        res = sendCommand(drv, WRITE_BLOCK, sector+sec, 0);
+      else
+        res = sendCommand(drv, WRITE_BLOCK, (sector+sec)<<9, 0);
+
+      if (res != 0) {
+        set_sd_led(0);
+        disk_state = DISK_ERROR;
+        return RES_ERROR;
+      }
+
+      // Send data token
+      spi_tx_byte(0xfe);
+
+      // Send data
+      spi_tx_block(buffer, 512);
+#ifdef CONFIG_SD_DATACRC
+      crc = crc_xmodem_block(0, buffer, 512);
+#else
+      crc = 0;
+#endif
+
+      // Send CRC
+      spi_tx_byte(crc >> 8);
+      spi_tx_byte(crc & 0xff);
+
+      // Get and check status feedback
+      status = spi_rx_byte();
+
+      // Retry if neccessary
+      if ((status & 0x0F) != 0x05) {
+        uart_putc('X');
+        deselectCard(drv);
+        errorcount++;
+        continue;
+      }
+
+      // Wait for write finish
+      if (!wait_for_response(0)) {
+        set_sd_led(0);
+        disk_state = DISK_ERROR;
+        return RES_ERROR;
+      }
+      break;
+    }
+    deselectCard(drv);
+
+    if (errorcount >= CONFIG_SD_AUTO_RETRIES) {
+      if (!(status & STATUS_CRC_ERROR))
+        disk_state = DISK_ERROR;
+      return RES_ERROR;
+    }
+  }
+
+  return RES_OK;
+}
+DRESULT disk_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count) __attribute__ ((weak, alias("sd_write")));
+
+DRESULT sd_getinfo(BYTE drv, BYTE page, void *buffer) {
+  uint8_t buf[18];
+  uint32_t capacity;
+
+  if (drv >= MAX_CARDS)
+    return RES_NOTRDY;
+
+  if (sd_status(drv) & STA_NODISK)
+    return RES_NOTRDY;
+
+  if (page != 0)
+    return RES_ERROR;
+
+  /* Try to calculate the total number of sectors on the card */
+  /* FIXME: Write a generic data read function and merge with sd_read */
+  if (sendCommand(drv, SEND_CSD, 0, 0) != 0) {
+    deselectCard(drv);
+    return RES_ERROR;
+  }
+
+  /* Wait for data token */
+  if (!wait_for_response(0xfe)) {
+    deselectCard(drv);
+    return RES_ERROR;
+  }
+
+  spi_rx_block(buf, 18);
+  deselectCard(drv);
+
+  if (cardtype[drv] & CARD_SDHC) {
+    /* Special CSD for SDHC cards */
+    capacity = (1 + getbits(buf,127-69,22)) * 1024;
+  } else {
+    /* Assume that MMC-CSD 1.0/1.1/1.2 and SD-CSD 1.1 are the same... */
+    uint8_t exponent = 2 + getbits(buf, 127-49, 3);
+    capacity = 1 + getbits(buf, 127-73, 12);
+    exponent += getbits(buf, 127-83,4) - 9;
+    while (exponent--) capacity *= 2;
+  }
+
+  diskinfo0_t *di = buffer;
+  di->validbytes  = sizeof(diskinfo0_t);
+  di->disktype    = DISK_TYPE_SD;
+  di->sectorsize  = 2;
+  di->sectorcount = capacity;
+
+  return RES_OK;
+}
+DRESULT disk_getinfo(BYTE drv, BYTE page, void *buffer) __attribute__ ((weak, alias("sd_getinfo")));

+ 40 - 0
src/sdcard.h

@@ -0,0 +1,40 @@
+/* sd2iec - SD/MMC to Commodore serial bus interface/controller
+   Copyright (C) 2007-2010  Ingo Korb <ingo@akana.de>
+
+   Inspiration and low-level SD/MMC access based on code from MMC2IEC
+     by Lars Pontoppidan et al., see sdcard.c|h and config.h.
+
+   FAT filesystem access based on code from ChaN and Jim Brain, see ff.c|h.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License only.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+   sdcard.h: Definitions for the SD/MMC access routines
+
+*/
+
+#ifndef SDCARD_H
+#define SDCARD_H
+
+#include "diskio.h"
+
+/* These functions are weak-aliased to disk_... */
+void    sd_init(void);
+DSTATUS sd_status(BYTE drv);
+DSTATUS sd_initialize(BYTE drv);
+DRESULT sd_read(BYTE drv, BYTE *buffer, DWORD sector, BYTE count);
+DRESULT sd_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count);
+DRESULT sd_getinfo(BYTE drv, BYTE page, void *buffer);
+
+#endif

+ 146 - 0
src/snes.c

@@ -0,0 +1,146 @@
+/* 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
+
+   snes.c: SNES hardware control and monitoring
+*/
+
+#include <avr/io.h>
+#include "avrcompat.h"
+#include "config.h"
+#include "uart.h"
+#include "snes.h"
+#include "memory.h"
+#include "fileops.h"
+#include "ff.h"
+#include "led.h"
+#include "smc.h"
+
+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
+	snes_reset(1); 
+}
+
+/*
+ * sets the SNES reset state.
+ *
+ *  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
+	}
+}
+
+/*
+ * gets the SNES reset 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));
+}
+
+/*
+ * SD2SNES main loop.
+ * monitors SRAM changes and other things
+ */
+uint32_t diffcount = 0, samecount = 0, didnotsave = 0;
+uint8_t sram_valid = 0;
+void snes_main_loop() {
+	if(!romprops.ramsize_bytes)return;
+	if(initloop) {
+		saveram_crc_old = calc_sram_crc(SRAM_SAVE_ADDR, romprops.ramsize_bytes);
+		initloop=0;
+	}
+	saveram_crc = calc_sram_crc(SRAM_SAVE_ADDR, romprops.ramsize_bytes);
+	sram_valid = sram_reliable();
+	if(crc_valid && sram_valid) {
+		if(saveram_crc != saveram_crc_old) {
+			if(samecount) {
+				diffcount=1;
+			} else {
+				diffcount++;
+				didnotsave++;
+			}
+			samecount=0;
+		}
+		if(saveram_crc == saveram_crc_old) {
+			samecount++;
+		}
+		if(diffcount>=1 && samecount==5) {
+			uart_putc('U');
+			uart_puthexshort(saveram_crc);
+			uart_putcrlf();
+			set_busy_led(1);
+			save_sram(file_lfn, romprops.ramsize_bytes, SRAM_SAVE_ADDR);
+			set_busy_led(0);
+			didnotsave=0;
+		}
+		if(didnotsave>50) {
+			diffcount=0;
+			uart_putc('V');
+			set_busy_led(1);
+			save_sram(file_lfn, romprops.ramsize_bytes, SRAM_SAVE_ADDR);
+			didnotsave=0;	
+                        set_busy_led(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);
+}
+
+/*
+ * SD2SNES menu loop.
+ * monitors menu selection. return when selection was made.
+ */
+uint8_t menu_main_loop() {
+	uint8_t cmd = 0;
+	sram_writebyte(0, SRAM_CMD_ADDR);
+	while(!cmd) {
+		if(!get_snes_reset()) {
+			while(!sram_reliable());
+			cmd = sram_readbyte(SRAM_CMD_ADDR);
+		}
+		if(get_snes_reset()) {
+			cmd = 0;
+		}
+	}
+	return cmd;
+}
+
+void get_selected_name(uint8_t* fn) {
+	uint32_t addr = sram_readlong(SRAM_FD_ADDR);
+	dprintf("fd addr=%lX\n", addr);
+	sram_readblock(fn, addr+0x41+SRAM_MENU_ADDR, 256);
+}

+ 37 - 0
src/snes.h

@@ -0,0 +1,37 @@
+/* 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
+
+   snes.h: SNES hardware control and monitoring
+*/
+
+#ifndef SNES_H
+#define SNES_H
+uint8_t crc_valid;
+
+void snes_init(void);
+void snes_reset(int state);
+uint8_t get_snes_reset(void);
+void snes_main_loop(void);
+uint8_t menu_main_loop(void);
+void get_selected_name(uint8_t* lfn);
+#endif

+ 202 - 0
src/spi.c

@@ -0,0 +1,202 @@
+/* Sd2iec - SD/MMC to Commodore serial bus interface/controller
+   Copyright (C) 2007-2010  Ingo Korb <ingo@akana.de>
+
+   Inspiration and low-level SD/MMC access based on code from MMC2IEC
+     by Lars Pontoppidan et al., see sdcard.c|h and config.h.
+
+   FAT filesystem access based on code from ChaN and Jim Brain, see ff.c|h.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License only.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+   spi.c: Low-level SPI routines
+
+*/
+
+#include <arm/NXP/LPC17xx/LPC17xx.h>
+#include <arm/bits.h>
+#include "config.h"
+#include "spi.h"
+
+#define SSP_TFE 0   // Transmit FIFO empty
+#define SSP_TNF 1   // Transmit FIFO not full
+#define SSP_RNE 2   // Receive FIFO not empty
+#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
+   SSP1: PCLKSEL0 */
+#define SSP_PCLKBIT 20
+/* SSP0: 10
+   SSP1: 20 */
+#define SSP_DMAID_TX 2
+/* SSP0: 0
+   SSP1: 2 */
+#define SSP_DMAID_RX 3
+/* SSP0: 1
+   SSP1: 3 */
+
+void spi_init(spi_speed_t speed) {
+  /* Set clock prescaler to 1:1 */
+  BITBAND(LPC_SC->SSP_PCLKREG, SSP_PCLKBIT) = 1;
+
+  /* configure data format - 8 bits, SPI, CPOL=0, CPHA=0, 1 clock per bit */
+  SSP_REGS->CR0 = (8-1);
+
+  /* set clock prescaler */
+  if (speed == SPI_SPEED_FAST) {
+    SSP_REGS->CPSR = SSP_CLK_DIVISOR_FAST;
+  } else {
+    SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW;
+  }
+
+  /* Enable 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) {
+  /* Wait until TX fifo can accept data */
+  while (!BITBAND(SSP_REGS->SR, SSP_TNF)) ;
+
+  /* Send byte */
+  SSP_REGS->DR = data;
+}
+
+uint8_t spi_rx_byte(void) {
+  /* Wait until SSP is not busy */
+  while (BITBAND(SSP_REGS->SR, SSP_BSY)) ;
+
+  /* Clear RX fifo */
+  while (BITBAND(SSP_REGS->SR, SSP_RNE))
+    (void) SSP_REGS->DR;
+
+  /* Transmit a single dummy byte */
+  SSP_REGS->DR = 0xff;
+
+  /* Wait until answer has been received */
+  while (!BITBAND(SSP_REGS->SR, SSP_RNE)) ;
+
+  return SSP_REGS->DR;
+}
+
+void spi_tx_block(const void *ptr, unsigned int length) {
+  const uint8_t *data = (const uint8_t *)ptr;
+
+  while (length--) {
+    /* Wait until TX fifo can accept data */
+    while (!BITBAND(SSP_REGS->SR, SSP_TNF)) ;
+
+    SSP_REGS->DR = *data++;
+  }
+}
+
+void spi_rx_block(void *ptr, unsigned int length) {
+  uint8_t *data = (uint8_t *)ptr;
+  unsigned int txlen = length;
+
+  /* Wait until SSP is not busy */
+  while (BITBAND(SSP_REGS->SR, SSP_BSY)) ;
+
+  /* Clear RX fifo */
+  while (BITBAND(SSP_REGS->SR, SSP_RNE))
+    (void) 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)) ;
+
+      /* Try to receive data */
+      while (length > 0 && BITBAND(SSP_REGS->SR, SSP_RNE)) {
+        *data++ = 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)) {
+        txlen--;
+        SSP_REGS->DR = 0xff;
+      }
+    }
+  } else {
+    /* Clear interrupt flags of DMA channels 0 */
+    LPC_GPDMA->DMACIntTCClear = BV(0);
+    LPC_GPDMA->DMACIntErrClr  = BV(0);
+
+    /* 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
+      | (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
+      | (2 << 21) // destination transfer width 4 bytes
+      | (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
+      | (2 << 11) // transfer from peripheral to memory
+      ;
+
+    /* Enable RX FIFO DMA */
+    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)) {
+        txlen--;
+        SSP_REGS->DR = 0xff;
+      }
+    }
+
+    /* Wait until DMA channel disables itself */
+    while (LPC_GPDMACH0->DMACCConfig & 1) ;
+
+    /* Disable RX FIFO DMA */
+    SSP_REGS->DMACR = 0;
+  }
+}
+
+void spi_set_speed(spi_speed_t speed) {
+  /* Wait until TX fifo is empty */
+  while (!BITBAND(SSP_REGS->SR, 0)) ;
+
+  /* Disable SSP (FIXME: Is this required?) */
+  SSP_REGS->CR1 = 0;
+
+  /* Change clock divisor */
+  if (speed == SPI_SPEED_FAST) {
+    SSP_REGS->CPSR = SSP_CLK_DIVISOR_FAST;
+  } else {
+    SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW;
+  }
+
+  /* Enable SSP */
+  SSP_REGS->CR1 = BV(1);
+}

+ 53 - 0
src/spi.h

@@ -0,0 +1,53 @@
+/* sd2iec - SD/MMC to Commodore serial bus interface/controller
+   Copyright (C) 2007-2010  Ingo Korb <ingo@akana.de>
+
+   Inspiration and low-level SD/MMC access based on code from MMC2IEC
+     by Lars Pontoppidan et al., see sdcard.c|h and config.h.
+
+   FAT filesystem access based on code from ChaN and Jim Brain, see ff.c|h.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License only.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+   spi.h: Definitions for the low-level SPI routines
+
+   Based on original code by Lars Pontoppidan et al., see spi.c
+   for full copyright details.
+
+*/
+#ifndef SPI_H
+#define SPI_H
+
+/* Low speed 400kHz for init, fast speed <=20MHz (MMC limit) */
+typedef enum { SPI_SPEED_FAST, SPI_SPEED_SLOW } spi_speed_t;
+
+/* Initialize SPI interface */
+void spi_init(spi_speed_t speed);
+
+/* Transmit a single byte */
+void spi_tx_byte(uint8_t data);
+
+/* Transmit a data block */
+void spi_tx_block(const void *data, unsigned int length);
+
+/* Receive a single byte */
+uint8_t spi_rx_byte(void);
+
+/* Receive a data block */
+void spi_rx_block(void *data, unsigned int length);
+
+/* Switch speed of SPI interface */
+void spi_set_speed(spi_speed_t speed);
+
+#endif

+ 69 - 0
src/timer.c

@@ -0,0 +1,69 @@
+/* ___INGO___ */
+
+#include <arm/NXP/LPC17xx/LPC17xx.h>
+#include "bits.h"
+#include "config.h"
+#include "timer.h"
+#include "clock.h"
+
+
+/* bit definitions */
+#define RITINT 0
+#define RITEN  3
+
+#define PCRIT 16
+
+volatile tick_t ticks;
+extern uint32_t f_cpu;
+
+void __attribute__((weak,noinline)) SysTick_Hook(void) {
+  // Empty function for hooking the systick handler
+}
+
+/* Systick interrupt handler */
+void SysTick_Handler(void) {
+  ticks++;
+  SysTick_Hook();
+}
+
+void timer_init(void) {
+  /* turn on power to RIT */
+  BITBAND(LPC_SC->PCONP, PCRIT) = 1;
+
+  /* clear RIT mask */
+  LPC_RIT->RIMASK = 0; //xffffffff;
+
+  /* PCLK = CCLK */
+  BITBAND(LPC_SC->PCLKSEL1, 26) = 1;
+  BITBAND(LPC_SC->PCLKSEL1, PCLK_TIMER3) = 1;
+  /* enable SysTick */
+  SysTick_Config(SysTick->CALIB & SysTick_CALIB_TENMS_Msk);
+}
+
+extern int testval;
+
+void delay_us(unsigned int time) {
+  /* Prepare RIT */
+  LPC_RIT->RICOUNTER = 0;
+  LPC_RIT->RICOMPVAL = (CONFIG_CPU_FREQUENCY / 1000000) * time;
+  LPC_RIT->RICTRL    = BV(RITEN) | BV(RITINT);
+
+  /* Wait until RIT signals an interrupt */
+  while (!(BITBAND(LPC_RIT->RICTRL, RITINT))) ;
+
+  /* Disable RIT */
+  LPC_RIT->RICTRL = 0;
+}
+
+void delay_ms(unsigned int time) {
+  /* Prepare RIT */
+  LPC_RIT->RICOUNTER = 0;
+  LPC_RIT->RICTRL    = BV(RITEN) | BV(RITINT);
+  LPC_RIT->RICOMPVAL = (f_cpu / 1000) * time;
+
+  /* Wait until RIT signals an interrupt */
+  while (!(BITBAND(LPC_RIT->RICTRL, RITINT))) ;
+
+  /* Disable RIT */
+  LPC_RIT->RICTRL = 0;
+}

+ 50 - 0
src/timer.h

@@ -0,0 +1,50 @@
+#ifndef TIMER_H
+#define TIMER_H
+
+#include <stdint.h>
+
+typedef unsigned int tick_t;
+
+extern volatile tick_t ticks;
+#define HZ 100
+
+/**
+ * getticks - return the current system tick count
+ *
+ * This inline function returns the current system tick count.
+ */
+static inline tick_t getticks(void) {
+  return ticks;
+}
+
+#define MS_TO_TICKS(x) (x/10)
+
+/* Adapted from Linux 2.6 include/linux/jiffies.h:
+ *
+ *      These inlines deal with timer wrapping correctly. You are
+ *      strongly encouraged to use them
+ *      1. Because people otherwise forget
+ *      2. Because if the timer wrap changes in future you won't have to
+ *         alter your driver code.
+ *
+ * time_after(a,b) returns true if the time a is after time b.
+ *
+ * Do this with "<0" and ">=0" to only test the sign of the result. A
+ * good compiler would generate better code (and a really good compiler
+ * wouldn't care). Gcc is currently neither.
+ * (">=0" refers to the time_after_eq macro which wasn't copied)
+ */
+#define time_after(a,b)         \
+         ((int)(b) - (int)(a) < 0)
+#define time_before(a,b)        time_after(b,a)
+
+
+void timer_init(void);
+
+/* delay for "time" microseconds - uses the RIT */
+void delay_us(unsigned int time);
+
+/* delay for "time" milliseconds - uses the RIT */
+void delay_ms(unsigned int time);
+
+#endif

+ 13 - 11
src/uart.c

@@ -5,9 +5,10 @@
 */
 
 #include <arm/NXP/LPC17xx/LPC17xx.h>
-#include <arm/bits.h>
+#include "bits.h"
 #include "config.h"
 #include "uart.h"
+#include "led.h"
 
 /* A few symbols to make this code work for all four UARTs */
 #if defined(CONFIG_UART_NUM) && CONFIG_UART_NUM == 0
@@ -51,14 +52,14 @@ static uint8_t uart_lookupratio(float f_fr) {
                        750,769,778,786,800,818,833,846,857,
                        867,875,889,900,909,917,923,929,933};
 
-  uint8_t ratios[72]={0x01,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,
-                      0x2f,0x17,0x2d,0x16,0x2b,0x15,0x3e,0x29,0x3d,
-                      0x14,0x4f,0x3b,0x27,0x3a,0x4d,0x13,0x5e,0x4b,
-                      0x38,0x5d,0x25,0x5c,0x37,0x49,0x5b,0x6d,0x7f,
-                      0x12,0x8f,0x7d,0x6b,0x59,0x47,0x7c,0x35,0x8d,
-                      0x58,0x7b,0x9e,0x23,0x9d,0x7a,0x57,0x8b,0xbf,
-                      0x34,0xad,0x79,0xbe,0x45,0x9b,0x56,0xbd,0x67,
-                      0xdf,0x78,0x89,0x9a,0xab,0xbc,0xcd,0xde,0xef};
+  uint8_t ratios[72]={0x10,0xf1,0xe1,0xd1,0xc1,0xb1,0xa1,0x91,0x81,
+                      0xf2,0x71,0xd2,0x61,0xb2,0x51,0xe3,0x92,0xd3,
+                      0x41,0xf4,0xb3,0x72,0xa3,0xd4,0x31,0xe5,0xb4,
+                      0x83,0xd5,0x52,0xc5,0x73,0x94,0xb5,0xd6,0xf7,
+                      0x21,0xf8,0xd7,0xb6,0x95,0x74,0xc7,0x53,0xd8,
+                      0x85,0xb7,0xe9,0x32,0xd9,0xa7,0x75,0xb8,0xfb,
+                      0x43,0xda,0x97,0xeb,0x54,0xb9,0x65,0xdb,0x76,
+                      0xfd,0x87,0x98,0xa9,0xba,0xcb,0xdc,0xed,0xfe};
 
   int fr = (f_fr-1)*1000;
   int i=0, i_result=0;
@@ -78,10 +79,10 @@ static uint8_t uart_lookupratio(float f_fr) {
 }
 
 static uint32_t baud2divisor(unsigned int baudrate) {
-  float f_ratio;
   uint32_t int_ratio;
   uint32_t error;
   uint32_t dl=0;
+  float f_ratio;
   float f_fr;
   float f_dl;
   float f_pclk = (float)CONFIG_CPU_FREQUENCY / CONFIG_UART_PCLKDIV;
@@ -207,10 +208,11 @@ void uart_init(void) {
 
   UART_REGS->DLL = div & 0xff;
   UART_REGS->DLM = (div >> 8) & 0xff;
+  BITBAND(UART_REGS->LCR, 7) = 0;
+
   if (div & 0xff0000) {
     UART_REGS->FDR = (div >> 16) & 0xff;
   }
-  BITBAND(UART_REGS->LCR, 7) = 0;
 
   /* reset and enable FIFO */
   UART_REGS->FCR = BV(0);

部分文件因文件數量過多而無法顯示