/// \file spiusb.c Winbond 25X16 SPI flash/USB/Vorbis player demo project
/*
  
  Copyright 2008 VLSI Solution Oy. Absolutely no warranty.

  SPI FLASH USB DRIVE  +  MUSIC PLAYER  +  USB AUDIO EXAMPLE
  ----------------------------------------------------------

  This example code uses the 18 kilobytes of mallocAreaY to implement
  a disk read/write cache and work are that is capable of erasing and 
  programming an SPI flash chip such as the Winbond 25X16 in 4 kilobyte 
  chunks when the VS1000B is connected to USB. Tested with Windows XP.
   
  Note: The SPI flash is quite slow to write to (about the speed of
  a floppy disk). Writing small files (a few kilobytes) is fast due
  to the cache system. Large files may cause timeouts. 
  If write timeouts happen, just try again and be patient.

  Note FAT12 Subdirectory Limitations.

  Requirements: An SPI flash eeprom with 4 kilobyte erasable blocks.

  Tested with VS1000B, Winbond 25X16 and Windows XP Professional.
  Measured performance: 1 megabyte file was written in 55 seconds when
  the eeprom was blank (no need to erase). When writing over already
  written data, writing the same file took 1 minute 35 seconds.

  Builds with vskit133 build script (BUILD SPIUSB)
  eeprom.img can be then prommed to the eeprom for bootable system.
  Then you can use your development board to make a master EEPROM image,
  which you can copy to the production units using an eeprommer. 

  At first connect the USB disk is unformatted.
  Suggestion: use "Quick Format" when formatting the disk with WinXP.
  
*/  

 
// The number of 512-byte blocks totally available in the SPI Flash chip
#define CHIP_TOTAL_BLOCKS 16384  /* 4096 * 512 bytes = 2M (Winbond 25X16) */
//#define CHIP_TOTAL_BLOCKS 512  /* 512 * 512 bytes = 256K (Winbond 25X20) */

// Set aside some blocks for VS1000 boot code (and optional parameter data) 
#define RESERVED_BLOCKS 32

#define LOGICAL_DISK_BLOCKS  (CHIP_TOTAL_BLOCKS-RESERVED_BLOCKS)

#define PRINT_VS3EMU_DEBUG_MESSAGES 0

#define SPI_CLOCK_DIVIDER 2

// number of 512-blocks in RAM cache (MUST BE 16 DO NOT ALTER! I MEAN IT!)
#define CACHE_BLOCKS 16


// storing the disk data inverted is optimal for the system.
// storing the disk data uninverted (as is) makes it easier to debug the SPI image
#define USE_INVERTED_DISK_DATA 1


// Command set of the SPI flash
#define SPI_EEPROM_COMMAND_WRITE_ENABLE  0x06
#define SPI_EEPROM_COMMAND_WRITE_DISABLE  0x04
#define SPI_EEPROM_COMMAND_READ_STATUS_REGISTER  0x05
#define SPI_EEPROM_COMMAND_WRITE_STATUS_REGISTER  0x01
#define SPI_EEPROM_COMMAND_READ  0x03
#define SPI_EEPROM_COMMAND_WRITE 0x02
#define SPI_EEPROM_COMMAND_CLEAR_ERROR_FLAGS 0x30
#define SPI_EEPROM_COMMAND_ERASE_4K_SECTOR 0x20
#define SPI_EEPROM_COMMAND_ERASE_CHIP 0xC7


// Position of workspace in Y ram, do not change.
#define WORKSPACE (mallocAreaY + 6144)


#include <stdio.h> //Standard io
#include <stdlib.h> // VS_DSP Standard Library
#include <vs1000.h> // VS1000B register definitions
#include <vectors.h> // VS1000B vectors (interrupts and services)
#include <minifat.h> // Read Only Fat Filesystem
#include <mapper.h> // Logical Disk
#include <string.h> // memcpy etc
#include <player.h> // VS1000B default ROM player
#include <audio.h> // DAC output
#include <codec.h> // CODEC interface
#include <vsNand.h>
#include <mappertiny.h>
#include <usb.h>


#define DT_LANGUAGES 0
#define DT_VENDOR 1
#define DT_MODEL 2
#define DT_SERIAL 3
#define DT_DEVICE 4
#define DT_CONFIGURATION 5

#define VENDOR_NAME_LENGTH 7
const u_int16 myVendorNameStr[] = {
  ((VENDOR_NAME_LENGTH * 2 + 2)<<8) | 0x03,
  'V'<<8,'l'<<8,'s'<<8,'i'<<8,'F'<<8,'i'<<8,'n'<<8};

#define MODEL_NAME_LENGTH 8
const u_int16 myModelNameStr[] = {
  ((MODEL_NAME_LENGTH * 2 + 2)<<8) | 0x03,
  'S'<<8,'P'<<8,'I'<<8,'A'<<8,'u'<<8,'d'<<8,'i'<<8,'o'<<8};

#define SERIAL_NUMBER_LENGTH 12
u_int16 mySerialNumberStr[] = {
  ((SERIAL_NUMBER_LENGTH * 2 + 2)<<8) | 0x03,
  '0'<<8, // You should put your own serial number here
  '0'<<8,
  '0'<<8,
  '0'<<8,
  '0'<<8,
  '0'<<8,
  '0'<<8,
  '0'<<8,
  '0'<<8,
  '0'<<8,
  '0'<<8,
  '1'<<8, // Serial number should be unique for each unit
};

// This is the new Device Descriptor. See the USB specification! 
const u_int16  myDeviceDescriptor [] = "\p"
  "\x12" // Length
  "\x01" // Type (Device Descriptor)
  "\x10" // LO(bcd USB Specification version) (.10)
  "\x01" // HI(bcd USB Specification version) (1.)
  "\x00" // Device Class (will be specified in configuration descriptor)
  "\x00" // Device Subclass
  "\x00" // Device Protocol
  "\x40" // Endpoint 0 size (64 bytes)

  "\xfb" // LO(Vendor ID) (0x19fb=VLSI Solution Oy)
  "\x19" // HI(Vendor ID)

  "\xe3" // LO(Product ID) (0xeee0 - 0xeeef : VLSI Customer Testing)
  "\xee" // HI(Product ID) (customers can request an ID from VLSI)

  "\x00" // LO(Release Number)
  "\x00" // HI(Release Number)
  "\x01" // Index of Vendor (manufacturer) name string
  "\x02" // Index of Product (model) name string (most shown by windows)
  "\x03" // Index of serial number string
  "\x01" // Number of configurations (1)
;



#define CONFIG_DESC_SIZE 123

u_int16 myConfigurationDescriptor[] = "\p"
  
  // ______Configuration_descriptor____ at offset 0
  "\x09"   // Length: 9 bytes // (9)
  "\x02"   // Descriptor type: Configuration // (2)
  "\x7b"    // LO(TotalLength) (123)
  "\x00"    // HI(TotalLength) (0)
  "\x03"    // Number of Interfaces (3)
  "\x01"    // bConfigurationValue (1)
  "\x00"    // iConfiguration (0)
  "\x80"    // Attributes (128)
  "\x32"    // Max. Power 100mA (50)
  

  // ______Interface_descriptor____ at offset 9
  "\x09"   // Length: 9 bytes // (9)
  "\x04"   // Descriptor type: Interface // (4)
  "\x00"   // Interface number  // (1)
  "\x00"   // Alternate setting  // (0)
  "\x00"   // Number of endpoints  // (0)
  "\x01"   // Interface Class: Audio // (1)
  "\x01"   // Interface Subclass: Audio Control // (1)
  "\x00"   // Interface Protocol  // (0)
  "\x00"   // String index  // (0)
  
  // ______Audio Control Class Interface_descriptor____ at offset 18
  "\x09"   // Length: 9 bytes // (9)
  "\x24"   // Descriptor type: Audio Control Class Interface // (36)
  "\x01"   // Subtype Header // (1)
  "\x00"    // (0)
  "\x01"    // (1)
  "\x1E"    // (30)
  "\x00"    // (0)
  "\x01"    // (1)
  "\x01"    // (1)
  
  // ______Audio Control Class Interface_descriptor____ at offset 27
  "\x0C"   // Length: 12 bytes // (12)
  "\x24"   // Descriptor type: Audio Control Class Interface // (36)
  "\x02"   // Subtype Input Terminal // (2)
  "\x01"    // (1)
  "\x01"    // (1)
  "\x01"    // (1)
  "\x00"    // (0)
  "\x02"    // (2)
  "\x00"    // (0)
  "\x00"    // (0)
  "\x00"    // (0)
  "\x00"    // (0)
  
  // ______Audio Control Class Interface_descriptor____ at offset 39
  "\x09"   // Length: 9 bytes // (9)
  "\x24"   // Descriptor type: Audio Control Class Interface // (36)
  "\x03"   // Subtype Output Terminal // (3)
  "\x02"    // (2)
  "\x04"    // (4)
  "\x03"    // (3)
  "\x00"    // (0)
  "\x01"    // (1)
  "\x00"    // (0)
  
  // ______Interface_descriptor____ at offset 48
  "\x09"   // Length: 9 bytes // (9)
  "\x04"   // Descriptor type: Interface // (4)
  "\x01"   // Interface number  // (1)
  "\x00"   // Alternate setting  // (0)
  "\x00"   // Number of endpoints  // (0)
  "\x01"   // Interface Class: Audio // (1)
  "\x02"   // Interface Subclass: Audio Streaming // (2)
  "\x00"   // Interface Protocol  // (0)
  "\x00"   // String index  // (0)
  
  // ______Interface_descriptor____ at offset 57
  "\x09"   // Length: 9 bytes // (9)
  "\x04"   // Descriptor type: Interface // (4)
  "\x01"   // Interface number  // (1)
  "\x01"   // Alternate setting  // (1)
  "\x01"   // Number of endpoints  // (1)
  "\x01"   // Interface Class: Audio // (1)
  "\x02"   // Interface Subclass: Audio Streaming // (2)
  "\x00"   // Interface Protocol  // (0)
  "\x00"   // String index  // (0)
  
  // ______Audio Streaming Class Interface_descriptor____ at offset 66
  "\x07"   // Length: 7 bytes // (7)
  "\x24"   // Descriptor type: Audio Streaming Class Interface // (36)
  "\x01"   // Subtype  // (1)
  "\x01"    // (1)
  "\x0C"    // (12)
  "\x01"    // (1)
  "\x00"    // (0)
  
  // ______Audio Streaming Class Interface_descriptor____ at offset 73
  "\x0B"   // Length: 11 bytes // (11)
  "\x24"   // Descriptor type: Audio Streaming Class Interface // (36)
  "\x02"   // Subtype  // (2)
  "\x01"    // (1)
  "\x02"    // (2)
  "\x02"    // (2)
  "\x10"    // (16)
  "\x01"    // (1)
  "\x44"    // (68)
  "\xAC"    // (172)
  "\x00"    // (0)
  
  // ______Endpoint_descriptor____ at offset 84
  "\x09"   // Length: 9 bytes // (9)
  "\x05"   // Descriptor type: Endpoint // (5)
  "\x01"   // Endpoint Address  // (1)
  "\x09"   // Attributes.TransferType Isochronous(1) // (9)
  "\xFF"   //EP Size LSB // (255)
  "\x03"   //EP Size MSB (total 1023 bytes) // (3)
  "\x01"   //Polling Interval ms // (1)
  "\x00"   //Refresh // (0)
  "\x00"   //Sync Address // (0)
  
  // ______Class Endpoint_descriptor____ at offset 93
  "\x07"   // Length: 7 bytes // (7)
  "\x25"   // Descriptor type: Class Endpoint // (37)
  "\x01"   // Subtype  // (1)
  "\x00"    // (0)
  "\x00"    // (0)
  "\x00"    // (0)
  "\x00"    // (0)


  // ______Interface_descriptor____ at offset 9
  "\x09"   // Length: 9 bytes // (9)
  "\x04"   // Descriptor type: Interface // (4)
  "\x02"   // Interface number  // (2)
  "\x00"   // Alternate setting  // (0)
  "\x02"   // Number of endpoints  // (2)
  "\x08"   // Interface Class: Mass Storage // (8)
  "\x06"   // Interface Subclass:  // (6)
  "\x50"   // Interface Protocol  // (80)
  "\x00"   // String index  // (0)
  
  // ______Endpoint_descriptor____ at offset 18
  "\x07"   // Length: 7 bytes // (7)
  "\x05"   // Descriptor type: Endpoint // (5)
  "\x82"   // Endpoint Address  // (130)
  "\x02"   // Attributes.TransferType Bulk(2) // (2)
  "\x40"   //EP Size LSB // (64)
  "\x00"   //EP Size MSB (total 64 bytes) // (0)
  "\x10"   //Polling Interval ms // (16)
  
  // ______Endpoint_descriptor____ at offset 25
  "\x07"   // Length: 7 bytes // (7)
  "\x05"   // Descriptor type: Endpoint // (5)
  "\x03"   // Endpoint Address  // (3)
  "\x02"   // Attributes.TransferType Bulk(2) // (2)
  "\x40"   //EP Size LSB // (64)
  "\x00"   //EP Size MSB (total 64 bytes) // (0)
  "\x10"   //Polling Interval ms // (16)
  // end of descriptor at offset 31
  

    /*
    u_int16 myDescriptor[] = "\p"    
    // ______Configuration_descriptor____ at offset 0
    "\x09"   // Length: 9 bytes // (9)
    "\x02"   // Descriptor type: Configuration // (2)
    "\x64"    // (100)
    "\x00"    // (0)
    "\x02"    // (2)
    "\x01"    // (1)
    "\x00"    // (0)
    "\x80"    // (128)
    "\x32"    // (50)
    */

  
  ;



// When a USB setup packet is received, install our descriptors 
// and then proceed to the ROM function RealDecodeSetupPacket.
void RealInitUSBDescriptors(u_int16 initDescriptors);
void MyInitUSBDescriptors(u_int16 initDescriptors){
  RealInitUSBDescriptors(1); // ROM set descriptors for mass storage
  USB.descriptorTable[DT_VENDOR] = myVendorNameStr;
  USB.descriptorTable[DT_MODEL]  = myModelNameStr;
  USB.descriptorTable[DT_SERIAL] = mySerialNumberStr;
  USB.descriptorTable[DT_DEVICE] = myDeviceDescriptor;
  USB.descriptorTable[DT_CONFIGURATION] = myConfigurationDescriptor;
  USB.configurationDescriptorSize = CONFIG_DESC_SIZE;
}

#if 0
// this can be helpful in making a serial number
const u_int16  bHexChar16[] = { // swapped Unicode hex characters
  0x3000, 0x3100, 0x3200, 0x3300, 0x3400, 0x3500, 0x3600, 0x3700,
  0x3800, 0x3900, 0x4100, 0x4200, 0x4300, 0x4400, 0x4400, 0x4500
};My
void MakeSerialNumber(u_int32 newSerialNumber){
  u_int16 i;
  u_int32 mySerialNumber = newSerialNumber;
  // Put unique serial number to serial number descriptor
  for (i=5; i<13; i++){
    mySerialNumberStr[i]=bHexChar16[mySerialNumber>>28];
    mySerialNumber <<= 4;
  }
}
#endif

extern struct FsNandPhys fsNandPhys;
extern struct FsPhysical *ph;
extern struct FsMapper *map;
extern struct Codec *cod;
extern struct CodecServices cs;
extern u_int16 codecVorbis[];

/* cache info */
u_int16 blockPresent;
u_int16 blockAddress[CACHE_BLOCKS];
s_int16 lastFoundBlock = -1;
u_int16 shouldFlush = 0;


// Do we want to get debug screen output? It's available if the code
// is loaded with vs3emu (build script) with RS-232 cable.

#if PRINT_VS3EMU_DEBUG_MESSAGES
__y const char hex[] = "0123456789abcdef";
void puthex(u_int16 a) {
  char tmp[6];
  tmp[0] = hex[(a>>12)&15];
  tmp[1] = hex[(a>>8)&15];
  tmp[2] = hex[(a>>4)&15];
  tmp[3] = hex[(a>>0)&15];
  tmp[4] = ' ';
  tmp[5] = '\0';
  fputs(tmp, stdout);
}
void PrintCache(){
  register u_int16 i;
  for (i=0; i<CACHE_BLOCKS; i++){
    if (blockPresent & 1 << i){
      puthex(blockAddress[i]);
    }else{
      fputs("- ",stdout);
    }
  }
  puts ("=cache");    
}
#define do__not__puts(x) puts(x)
#define do__not__puthex(x) puthex(x)
#else
#define do__not__puts(a)
#define do__not__puthex(a)
#define PrintCache()
#endif

//macro to set SPI to MASTER; 8BIT; FSYNC Idle => xCS high
#define SPI_MASTER_8BIT_CSHI   PERIP(SPI0_CONFIG) = \
        SPI_CF_MASTER | SPI_CF_DLEN8 | SPI_CF_FSIDLE1

//macro to set SPI to MASTER; 8BIT; FSYNC not Idle => xCS low
#define SPI_MASTER_8BIT_CSLO   PERIP(SPI0_CONFIG) = \
        SPI_CF_MASTER | SPI_CF_DLEN8 | SPI_CF_FSIDLE0

//macro to set SPI to MASTER; 16BIT; FSYNC not Idle => xCS low
#define SPI_MASTER_16BIT_CSLO  PERIP(SPI0_CONFIG) = \
        SPI_CF_MASTER | SPI_CF_DLEN16 | SPI_CF_FSIDLE0

void SingleCycleCommand(u_int16 cmd){
  SPI_MASTER_8BIT_CSHI; 
  SPI_MASTER_8BIT_CSLO;
  SpiSendReceive(cmd);
  SPI_MASTER_8BIT_CSHI;
}

/// Wait for not_busy (status[0] = 0) and return status
u_int16 SpiWaitStatus(void) {
  u_int16 status;
  SPI_MASTER_8BIT_CSHI;
  SPI_MASTER_8BIT_CSLO;
  SpiSendReceive(SPI_EEPROM_COMMAND_READ_STATUS_REGISTER);
  do {
    status = SpiSendReceive(0);
    if (PERIP(USB_STATUS) & USB_STF_BUS_RESET){
      USBHandler();
      SPI_MASTER_8BIT_CSHI;
      return -1; /* USB HAS BEEN RESET */
    }
  } while (status & 0x01);
    ; //Wait until chip is ready or return -1 if USB bus reset

  SPI_MASTER_8BIT_CSHI;
  
  return status;
}


void EeUnprotect(){
  SingleCycleCommand(SPI_EEPROM_COMMAND_WRITE_ENABLE);
  SPI_MASTER_8BIT_CSLO;
  SpiSendReceive(SPI_EEPROM_COMMAND_WRITE_STATUS_REGISTER);
  SpiSendReceive(0x02); //Sector Protections Off
  SPI_MASTER_8BIT_CSHI;
  SpiWaitStatus();
  SingleCycleCommand(SPI_EEPROM_COMMAND_WRITE_ENABLE);
}

void EePutReadBlockAddress(register u_int16 blockn){
  SPI_MASTER_8BIT_CSLO;
  SpiSendReceive(SPI_EEPROM_COMMAND_READ);
  SpiSendReceive(blockn>>7);            // Address[23:16] = blockn[14:7]
  SpiSendReceive((blockn<<1)&0xff);     // Address[15:8]  = blockn[6:0]0
  SpiSendReceive(0);                    // Address[7:0]   = 00000000
  SPI_MASTER_16BIT_CSLO;
}

// Check is a 4K block completely blank
u_int16 EeIsBlockErased(u_int16 blockn){  
  SpiWaitStatus();
  EePutReadBlockAddress(blockn);
  {
    register u_int16 n;
    for (n=0; n<2048; n++){
      if (SpiSendReceive(0) != 0xffff) {
	SPI_MASTER_8BIT_CSHI;
	return 0;
      }
    }
    SPI_MASTER_8BIT_CSHI;
    return 1;
  }  
}

s_int16 EeProgram4K(u_int16 blockn, __y u_int16 *dptr){

  PERIP(USB_EP_ST3) |= (0x0001); //Force NAK on EP3 (perhaps not needed?)

  do__not__puthex(blockn);
  do__not__puts("= write 4K");

  if (!EeIsBlockErased(blockn)){ //don't erase if not needed
    // Erase 4K sector
    SingleCycleCommand(SPI_EEPROM_COMMAND_WRITE_ENABLE);
    SingleCycleCommand(SPI_EEPROM_COMMAND_CLEAR_ERROR_FLAGS);
    EeUnprotect();
    SPI_MASTER_8BIT_CSLO;
    SpiSendReceive(SPI_EEPROM_COMMAND_ERASE_4K_SECTOR);
    SpiSendReceive(blockn>>7);            // Address[23:16] = blockn[14:7]
    SpiSendReceive((blockn<<1)&0xff);     // Address[15:8]  = blockn[6:0]0
    SpiSendReceive(0);                    // Address[7:0]   = 00000000
    SPI_MASTER_8BIT_CSHI;
  }

  if (SpiWaitStatus() == -1) return -1; /* USB HAS BEEN RESET */
  // Write 8 512-byte sectors
  { 
    u_int16 i;
    for (i=0; i<8; i++){
      
      // Put first page (256 bytes) of sector.
      EeUnprotect();
      SPI_MASTER_8BIT_CSLO;
      SpiSendReceive(SPI_EEPROM_COMMAND_WRITE);
      SpiSendReceive(blockn>>7);            // Address[23:16] = blockn[14:7]
      SpiSendReceive((blockn<<1)&0xff);     // Address[15:8]  = blockn[6:0]0
      SpiSendReceive(0);                    // Address[7:0]   = 00000000
      SPI_MASTER_16BIT_CSLO;
      {
	u_int16 n;
	for (n=0; n<128; n++){
#if USE_INVERTED_DISK_DATA
	  SpiSendReceive(~(*dptr++));
#else
	  SpiSendReceive((*dptr++));
#endif
	}
      }
      SPI_MASTER_8BIT_CSHI;
      if (SpiWaitStatus() == -1) return -1; /* USB HAS BEEN RESET */
       
      // Put second page (256 bytes) of sector.
      EeUnprotect();
      SPI_MASTER_8BIT_CSLO;
      SpiSendReceive(SPI_EEPROM_COMMAND_WRITE);
      SpiSendReceive(blockn>>7);            // Address[23:16] = blockn[14:7]
      SpiSendReceive(((blockn<<1)+1)&0xff);     // Address[15:8]  = blockn[6:0]1
      SpiSendReceive(0);                    // Address[7:0]   = 00000000
      SPI_MASTER_16BIT_CSLO;
      {
	u_int16 n;
	for (n=0; n<128; n++){
#if USE_INVERTED_DISK_DATA
	  SpiSendReceive(~(*dptr++));
#else
	  SpiSendReceive((*dptr++));
#endif
	}
      }
      SPI_MASTER_8BIT_CSHI;
      if (SpiWaitStatus() == -1) return -1; /* USB HAS BEEN RESET */
      blockn++;
    }
  }
  do__not__puts("written");
  
  PERIP(USB_EP_ST3) &= ~(0x0001); //Un-Force NAK on EP3
  return 0;

}



// Block Read for SPI EEPROMS with 24-bit address e.g. up to 16MB 
u_int16 EeReadBlock(u_int16 blockn, u_int16 *dptr) {
  SpiWaitStatus();
  
  EePutReadBlockAddress(blockn);
  {
    int n;
    for (n=0; n<256; n++){
#if USE_INVERTED_DISK_DATA
      *dptr++ = ~SpiSendReceive(0);
#else
      *dptr++ = SpiSendReceive(0);
#endif
    }
  }
  SPI_MASTER_8BIT_CSHI;
  return 0;
}

// Returns 1 if block differs from data, 0 if block is the same
u_int16 EeCompareBlock(u_int16 blockn, u_int16 *dptr) {
  SpiWaitStatus();

  EePutReadBlockAddress(blockn);
  {
    int n;
    for (n=0; n<256; n++){

#if USE_INVERTED_DISK_DATA      
      if ((*dptr++) != (~SpiSendReceive(0)))
#else
      if ((*dptr++) != (SpiSendReceive(0)))	  
#endif      	  
	{	  
	  SPI_MASTER_8BIT_CSHI;
	  return 1;
	}
    }
  }
  SPI_MASTER_8BIT_CSHI;
  return 0;
}

u_int16 EeRead4KSectorYToWorkspace(u_int16 blockn) {
  register __y u_int16 *dptr;
  dptr = WORKSPACE;
  SpiWaitStatus();
  blockn &= 0xfff8; //always point to first block of 4k sector


  EePutReadBlockAddress(blockn);
  {
    int n;
    for (n=0; n<2048; n++){
#if USE_INVERTED_DISK_DATA      
      *dptr++ = ~SpiSendReceive(0);
#else
      *dptr++ = SpiSendReceive(0);
#endif
    }
  }
  SPI_MASTER_8BIT_CSHI;
  return 0;
}

void InitSpi(u_int16 clockDivider){
  SPI_MASTER_8BIT_CSHI;
  PERIP(SPI0_FSYNC) = 0;
  PERIP(SPI0_CLKCONFIG) = SPI_CC_CLKDIV * (clockDivider-1);
  PERIP(GPIO1_MODE) |= 0x1f; /* enable SPI pins */
}


__y u_int16 *FindCachedBlock(u_int16 blockNumber){
  register int i;
  lastFoundBlock = -1;
  for (i=0; i<CACHE_BLOCKS; i++){
    if ((blockPresent & 1/*was L*/<<i) && (blockAddress[i] == blockNumber)){
      lastFoundBlock = i;
      return mallocAreaY+256*i;
    }
  }
  return NULL;
}


u_int16 WriteContinuous4K(){
  u_int16 i,k;
  for (i=0; i<CACHE_BLOCKS-7; i++){ //for all cached blocks...
    if ((blockPresent & 1/*was L*/<<i) && ((blockAddress[i] & 0x0007) == 0)){
      //cached block i contains first 512 of 4K
      for (k=1; k<8; k++){
	if (blockAddress[i+k] != blockAddress[i]+k) goto ohi;
      }
      do__not__puthex(blockAddress[i]); do__not__puts(" starts continuous 4K ");
      
      if (-1 != EeProgram4K (blockAddress[i], mallocAreaY+256*i)){
	for (k=0; k<8; k++){
	  blockPresent &= ~(1/*was L*/<<(i+k));
	}
      } else {
	return 0; /* USB HAS BEEN RESET */
      }      
      return 1;
    }
  ohi:
    {}    
  }
  return 0;
}

__y u_int16 *GetEmptyBlock(u_int16 blockNumber){
  register int i;
  for (i=0; i<CACHE_BLOCKS; i++){
    if (!(blockPresent & 1/*was L*/<<i)) {
      blockPresent |= 1/*was L*/<<i;
      blockAddress[i] = blockNumber;
      return mallocAreaY+256*i;
    }
  }
  //do__not__puts("cannot allocate empty block");
  return NULL;
}



struct FsMapper *FsMapSpiFlashCreate(struct FsPhysical *physical,
				u_int16 cacheSize);
s_int16 FsMapSpiFlashRead(struct FsMapper *map, u_int32 firstBlock,
		     u_int16 blocks, u_int16 *data);
s_int16 FsMapSpiFlashWrite(struct FsMapper *map, u_int32 firstBlock,
		      u_int16 blocks, u_int16 *data);
s_int16 FsMapSpiFlashFlush(struct FsMapper *map, u_int16 hard);


const struct FsMapper spiFlashMapper = {
    0x010c, /*version*/
    256,    /*blocksize*/
    LOGICAL_DISK_BLOCKS,      /*blocks*/
    0,      /*cacheBlocks*/
    FsMapSpiFlashCreate,
    FsMapFlNullOk,//RamMapperDelete,
    FsMapSpiFlashRead,
    FsMapSpiFlashWrite,
    NULL,//FsMapFlNullOk,//RamMapperFree,
    FsMapSpiFlashFlush,//RamMapperFlush,
    NULL /* no physical */
};
struct FsMapper *FsMapSpiFlashCreate(struct FsPhysical *physical,
				     u_int16 cacheSize) {

  do__not__puts("CREATE");
  InitSpi(SPI_CLOCK_DIVIDER);  
  blockPresent = 0;
  shouldFlush = 0;
  return &spiFlashMapper;
}



s_int16 FsMapSpiFlashRead(struct FsMapper *map, u_int32 firstBlock,
			  u_int16 blocks, u_int16 *data) {
  register s_int16 bl = 0;

  if (shouldFlush) return 0;
  firstBlock += RESERVED_BLOCKS;

  while (bl < blocks) {
    __y u_int16 *source = FindCachedBlock(firstBlock);
    do__not__puthex(firstBlock); 
    do__not__puthex((u_int16)source); 
    do__not__puts("=rd_lba, addr");
    if (source) {
      memcpyYX(data, source, 256);
    } else {
      EeReadBlock(firstBlock, data);
      //memset(data, 0, 256);
    }      
    data += 256;
    firstBlock++;
    bl++;
  }
  return bl;
}


s_int16 FsMapSpiFlashWrite(struct FsMapper *map, u_int32 firstBlock,
			   u_int16 blocks, u_int16 *data) {
  s_int16 bl = 0;

  firstBlock += RESERVED_BLOCKS;

  if (shouldFlush){
    do__not__puts("flush-reject");
    return 0; // don't accept write while flushing
  }
  while (bl < blocks) {           
    // Is the block to be written different than data already in EEPROM?
    if (EeCompareBlock(firstBlock, data)){        
      __y u_int16 *target = FindCachedBlock(firstBlock);
      if (target) {
	do__not__puthex(firstBlock); 
	do__not__puthex((u_int16)target); 
	do__not__puts("=rewrite_lba");
      } else {
	target = GetEmptyBlock(firstBlock);
	do__not__puthex(firstBlock); 
	do__not__puts("=write_lba");
      }
      if (!target){ //cache is full
	//must do a cache flush to get cache space
	FsMapSpiFlashFlush(NULL,1);
	target = GetEmptyBlock(firstBlock);
      }      
      if (target){
	memcpyXY(target, data, 256);	
	PrintCache();
      }else{       		
	puts("FATAL ERROR: NO CACHE SPACE. THIS NEVER HAPPENS.");
	while(1)
	  ;
      }
      WriteContinuous4K();
    } else {
      do__not__puthex(firstBlock);
      do__not__puts("=lba; Redundant write skipped");
    }
    
    if (PERIP(USB_STATUS) & USB_STF_BUS_RESET){
      do__not__puts("USB: Reset");
    };
    data += 256;
    firstBlock++;
    bl++;
  }
  return bl;
}


s_int16 FsMapSpiFlashFlush(struct FsMapper *map, u_int16 hard){
  u_int16 i,j,lba;
  u_int16 __y *dptr;
  u_int16 newBlockPresent;

  do__not__puts("FLUSH");
  PrintCache();

  if (shouldFlush>1) return 0;
  shouldFlush = 2; //flushing

  for (i=0; i<CACHE_BLOCKS; i++){
    if (blockPresent & (1/*was L*/<<i)){
      do__not__puthex(i);do__not__puthex(blockAddress[i]);do__not__puts("slot, lba  is dirty");
      lba = blockAddress[i] & 0xfff8;
      EeRead4KSectorYToWorkspace (lba);
      newBlockPresent = blockPresent;
      for (j=0; j<8; j++) {
	do__not__puthex (lba+j);
	if (dptr = FindCachedBlock(lba+j)){	  
	  memcpyYY (WORKSPACE+(256*j), dptr, 256);
	  newBlockPresent &= ~(1/*was L*/<<lastFoundBlock);

	  do__not__puts("from cache");
	} else {
	  do__not__puts("from disk");
	}
      }
      if (-1 != EeProgram4K (lba, WORKSPACE)){
	blockPresent = newBlockPresent;
	shouldFlush = 0;
      } else {
	shouldFlush = 1;
	return 0; /* USB HAS BEEN RESET */
      }
    }
  }
  shouldFlush = 0;
  return 0;

}      


auto void MyMassStorage(void) {
  register __b0 int usbMode = 0;
  do__not__puts("MyMassStorage");

  voltages[voltCoreUSB] = 31; //30:ok
  voltages[voltIoUSB] = 31; // set maximum IO voltage (about 3.6V)
  do__not__puthex (voltages[voltCoreUSB]);
  do__not__puts("=USB Core Voltage"); //default:27
  PowerSetVoltages(&voltages[voltCoreUSB]);
  BusyWait10();
  LoadCheck(NULL, 1); /* Set 48 MHz Clock */
  SetRate(44100U);

  SetHookFunction((u_int16)InitUSBDescriptors, MyInitUSBDescriptors);
  do__not__puts("before usb init");
  InitUSB(USB_MASS_STORAGE);
  do__not__puts("after usb init");

  while (USBIsAttached() && (!(ReadGPIO()&(1<<6))) ) {
    USBHandler();
    if (shouldFlush){      
	FsMapSpiFlashFlush(NULL,1);
    }
    if (USBWantsSuspend()) {
      if (USBIsDetached()) {
	break;
      }
    }
    if (usbMode == 1) {
      if (AudioBufFill() < 32) {
	memset(tmpBuf, 0, sizeof(tmpBuf));
	AudioOutputSamples(tmpBuf, sizeof(tmpBuf)/2);
      }
      Sleep();
    }
  }

  hwSampleRate = 1;
  PERIP(SCI_STATUS) &= ~SCISTF_USB_PULLUP_ENA;
  PERIP(USB_CONFIG) = 0x8000U;
  map->Flush(map, 1);
  PowerSetVoltages(&voltages[voltCorePlayer]);
}

// Try to Load and execute a file
void Exec (u_int32 extension){
  static u_int32 allowedExtensions[] = {0,0};  
  allowedExtensions[0] = extension;
  // Initialize the filesystem for nand flash physical "just in case".
  // If filesystem has already been initialized,
  // then these 3 lines can be omitted:
  map = FsMapSpiFlashCreate(NULL, 0); 
  InitFileSystem();  /* Reinitialize the filing system */
  minifatInfo.supportedSuffixes = allowedExtensions;
  if (OpenFile(0)<0){
    if (ReadFile(mallocAreaX, 0, 0x2000)) {
      BootFromX (mallocAreaX+8); // Exit to new image     
      { // Exec failed.
	// BootFromX returns only in case of invalid prg file.
	// Program execution would be unsafe after failed BootFromX
	// so the system falls to playing an "Error tone".
	register u_int16 *g = (u_int16*) g_yprev0; // get ptr to sintest params
	*g++ = 4; *g++ = 44100; *g++ = 0x5050; *g++ = 120; *g++ = 200;
	SinTest(); //Exit to SinTest: Play weird tone at low volume
      }	
    }
  }
}


// FAT12 binary patch
//auto u_int16 Fat12OpenFile(register __c0 u_int16 n);

void main(void) {

  do__not__puts("Hello.");

  InitAudio();
  
  PERIP(INT_ENABLEL) = INTF_RX | INTF_TIM0;
  PERIP(INT_ENABLEH) = INTF_DAC;
  
  PERIP(SCI_STATUS) &= ~SCISTF_USB_PULLUP_ENA;
  PERIP(USB_CONFIG) = 0x8000U;
  
  PERIP(GPIO1_ODATA) |=  LED1|LED2;
  PERIP(GPIO1_DDR)   |=  LED1|LED2;
  PERIP(GPIO1_MODE)  &= ~(LED1|LED2);
  
  player.volumeOffset = 0;
  player.pauseOn = 0;
  
  keyOld = KEY_POWER;
  keyOldTime = -32767;

  //SetHookFunction((u_int16)OpenFile, Fat12OpenFile);
  SetHookFunction((u_int16)IdleHook, NullHook); //Disable keyboard scanning

  
  // Use our SPI flash mapper as logical disk
  map = FsMapSpiFlashCreate(NULL, 0); 
  player.volume = 0;
  PlayerVolume();


  // When USB is attached, go to mass storage handler
  if (USBIsAttached()) {                 
    do__not__puts("MassStorage");
    MyMassStorage();
    do__not__puts("From MassStorage");
  }
  

  // If EXECFILE.PRG is found on disk, run it. (Actually any *.PRG file)
  // (this is an easy way to do program development on the device, just
  // compile your own code to EXECFILE.PRG and drag-and-drop it to the
  // usb disk. It will replace the Ogg Vorbis player code that is below.
  Exec(FAT_MKID('P','R','G'));
  // If EXECFILE.PRG was not found, continue below...


  map = FsMapSpiFlashCreate(NULL, 0); 
   // Use custom main loop to take over total control of chip
  while (1) {
    // Try to use a FAT filesystem on logical disk
    if (InitFileSystem() == 0) {
      
      minifatInfo.supportedSuffixes = supportedFiles; //.ogg
      
      // Look for playable files
      player.totalFiles = OpenFile(0xffffU);
      if (player.totalFiles == 0) {
	goto noFSnorFiles;
      }
           
      // Playable file(s) found. Play.
      player.nextStep = 1;
      player.nextFile = 0;
      while (1) {
	if (player.randomOn) {
	  register s_int16 nxt;
	retoss:
	  nxt = rand() % player.totalFiles;
	  if (nxt == player.currentFile && player.totalFiles > 1)
	    goto retoss;
	  player.currentFile = nxt;
	} else {
	  player.currentFile = player.nextFile;
	}
	if (player.currentFile < 0)
	  player.currentFile += player.totalFiles;
	if (player.currentFile >= player.totalFiles)
	  player.currentFile -= player.totalFiles;
	player.nextFile = player.currentFile + 1;
	if (OpenFile(player.currentFile) < 0) {
	  player.ffCount = 0;
	  cs.cancel = 0;
	  cs.goTo = -1;
	  cs.fileSize = cs.fileLeft = minifatInfo.fileSize;
	  cs.fastForward = 1;
	  {
	    register s_int16 oldStep = player.nextStep;
	    register s_int16 ret;

	    ret = PlayCurrentFile(); // Decode and Play.
	    // See separate examples about keyboard handling.

	    if (ret == ceFormatNotFound)
	      player.nextFile = player.currentFile + player.nextStep;
	    if (ret == ceOk && oldStep == -1)
	      player.nextStep = 1;
	  }
	} else {
	  player.nextFile = 0;
	}
	
	// If USB is attached, return to main loop
	//if (USBIsAttached()) {
	//  break;
	//}
      }
    } else {
    noFSnorFiles:
      LoadCheck(&cs, 32);
      memset(tmpBuf, 0, sizeof(tmpBuf)); /* silence */
      AudioOutputSamples(tmpBuf, sizeof(tmpBuf)/2); /* silence */
    }
  }
}
