/// \file tinymic.c TinyPlayer With Microphone Demo Project
/// \author Panu-Kristian Poiksalo, VLSI Solution Oy 2008

/*
  
  Copyright 2008 VLSI Solution Oy. Absolutely no warranty.

  SPI FLASH USB DRIVE  +  MUSIC PLAYER  +  USB AUDIO IN/OUT 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. Or use
  a faster SPI flash chip.
  
  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 using BuildTinyPlayer script
  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 4096  /* 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


#define MIC_RESET_BIT 12
#define MIC_INPUT_BIT 13
#define BUTTON_BIT 6

// 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>
#include <scsi.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)

  "\xf4" // 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)
  "\x00" // Index of serial number string
  "\x01" // Number of configurations (1)
;

#define CONFIG_DESC_SIZE 197

u_int16 myConfigurationDescriptor[] = "\p"
  
  // ______Configuration_descriptor____ at offset 0
  "\x09"   // Length: 9 bytes // (9)
  "\x02"   // Descriptor type: Configuration // (2)
  "\xc5"    // LO(TotalLength) (197)
  "\x00"    // HI(TotalLength) (0)
  "\x04"    // Number of Interfaces (4)
  "\x01"    // bConfigurationValue (1)
  "\x00"    // iConfiguration (0)
  "\x80"    // Attributes (128)
  "\x32"    // Max. Power 100mA (50)
  
// ---------- AUDIO CONTROL INTERFACE --------------

// ______Interface_descriptor____ at offset 9
  "\x09"   // Length: 9 bytes // (9)
  "\x04"   // Descriptor type: Interface // (4)
  "\x00"   // Interface number  // (0)
  "\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
  "\x0a"   // Length: 10 bytes // (10)
  "\x24"   // Descriptor type: Audio Control Class Interface // (36)
  "\x01"   // Subtype Header // (1)
  "\x00"    // (0)
  "\x01"    // (1)
  "\x34"    // (51) size 30 + 21 = 51 + 1 = 52
  "\x00"    // 
  "\x02"    // Number of interfaces
  "\x01"    // (1) audio interface 1
  "\x02"    // (2) audio interface 2
// ______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) Terminal 1
  "\x01"    // (1)
  "\x01"    // (1) 0101 usb streaming
  "\x00"    // (0) assoc. terminal 
  "\x02"    // (2) 2 channels
  "\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) Terminal 2
  "\x04"    // (4)
  "\x03"    // (3) 0304 headphone audio etc
  "\x00"    // (0)
  "\x01"    // (1) input is terminal 1 usb audio
  "\x00"    // (0) no string
// ______Audio Control Class Interface_descriptor____ 48
  "\x0C"   // Length: 12 bytes // (12)
  "\x24"   // Descriptor type: Audio Control Class Interface // (36)
  "\x02"   // Subtype Input Terminal // (2)
  "\x03"    // (3) Terminal 3
  "\x01"    // (1)
  "\x02"    // (2) 0x01,0x02 = 0x0201 = Microphone
  "\x00"    // (0)
  "\x01"    // (1) 1 channel
  "\x00"    // (0)
  "\x00"    // (0)
  "\x00"    // (0)
  "\x00"    // (0) no string
// ______Audio Control Class Interface_descriptor____ 60
  "\x09"   // Length: 9 bytes // (9)
  "\x24"   // Descriptor type: Audio Control Class Interface // (36)
  "\x03"   // Subtype Output Terminal // (3)
  "\x04"    // (4) Terminal 4
  "\x01"    // (4)
  "\x01"    // (3) 0101 usb streaming
  "\x00"    // (0)
  "\x03"    // (3) input is terminal 3 microphone
  "\x00"    // (0) no string


// ---------- AUDIO STREAMING INTERFACE (SPK) ------------

// ______Interface_descriptor____ at offset 69
  "\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 78
  "\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 87
  "\x07"   // Length: 7 bytes // (7)
  "\x24"   // Descriptor type: Audio Streaming Class Interface // (36)
  "\x01"   // Subtype  // (1) GENERAL
  "\x01"    // (1) INPUT TERMNAL 1
  "\x0C"    // (12) Delay 12 frames
  "\x01"    // (1) 
  "\x00"    // (0)  PCM
// ______Audio Streaming Class Interface_descriptor____ at offset 94
  "\x0B"   // Length: 11 bytes // (11)
  "\x24"   // Descriptor type: Audio Streaming Class Interface // (36)
  "\x02"   // Subtype  // (2) FORMAT
  "\x01"    // (1) FORMAT TYPE 1
  "\x02"    // (2) CHANNELS 2
  "\x02"    // (2) SUBFRAMESIZE 2
  "\x10"    // (16) Used bits: 16
  "\x01"    // (1) Frequencies 1
  "\x44"    // (68) 
  "\xAC"    // (172) 
  "\x00"    // (0)     0x00AC44 = 44100
// ______Endpoint_descriptor____ at offset 105
  "\x09"   // Length: 9 bytes // (9)
  "\x05"   // Descriptor type: Endpoint // (5)
  "\x01"   // Endpoint Address  // (1)
  "\x01"   // Attributes.TransferType Isochronous(1) // (9)
  "\xFF"   // EP Size LSB // (255)
  "\x00"   // EP Size MSB (total 1023 bytes) // (3)
  "\x01"   // Polling Interval ms // (1)
  "\x00"   // Refresh // (0)
  "\x00"   // Sync Address // (0)
// ______Class Endpoint_descriptor____ at offset 114
  "\x07"   // Length: 7 bytes // (7)
  "\x25"   // Descriptor type: Class Endpoint // (37)
  "\x01"   // Subtype  // (1) GENERAL
  "\x00"    // (0)
  "\x00"    // (0)
  "\x00"    // (0)
  "\x00"    // (0)
  

// ---------- AUDIO STREAMING INTERFACE (MIC) ------------

// ______Interface_descriptor____ at offset 121
  "\x09"   // Length: 9 bytes // (9)
  "\x04"   // Descriptor type: Interface // (4)
  "\x02"   // Interface number  // (2)
  "\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 130
  "\x09"   // Length: 9 bytes // (9)
  "\x04"   // Descriptor type: Interface // (4)
  "\x02"   // Interface number  // (2)
  "\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 139
  "\x07"   // Length: 7 bytes // (7)
  "\x24"   // Descriptor type: Audio Streaming Class Interface // (36)
  "\x01"   // Subtype  // (1) GENERAL
  "\x04"    // (1) OUTPUT TERMNAL 4 (USB)
  "\x0C"    // (12) Delay 12 frames
  "\x01"    // (1) 
  "\x00"    // (0)  PCM
// ______Audio Streaming Class Interface_descriptor____ at offset 146
  "\x0B"   // Length: 11 bytes // (11)
  "\x24"   // Descriptor type: Audio Streaming Class Interface // (36)
  "\x02"   // Subtype  // (2) FORMAT
  "\x01"    // (1) FORMAT TYPE 1
  "\x01"    // (1) CHANNELS 1
  "\x02"    // (2) SUBFRAMESIZE 2
  "\x10"    // (16) Used bits: 16
  "\x01"    // (1) Frequencies 1
  "\x22"    // ( ) 
  "\x56"    // ( ) 
  "\x00"    // (0)     0x001F40=8K  0x002B11=11025 0x005622=22050
// ______Endpoint_descriptor____ at offset 157
  "\x09"   // Length: 9 bytes // (9)
  "\x05"   // Descriptor type: Endpoint // (5)
  "\x81"   // Endpoint Address  // (1) IN
  "\x01"   // Attributes.TransferType Isochronous(1)
  "\xFF"   // EP Size LSB // (255)
  "\x00"   // EP Size MSB (total 1023 bytes) // (3)
  "\x01"   // Polling Interval ms // (1)
  "\x00"   // Refresh // (0)
  "\x00"   // Sync Address // (0)
// ______Class Endpoint_descriptor____ at offset 166
  "\x07"   // Length: 7 bytes // (7)
  "\x25"   // Descriptor type: Class Endpoint // (37)
  "\x01"   // Subtype  // (1) GENERAL
  "\x00"    // (0) 
  "\x00"    // (0)
  "\x00"    // (0)
  "\x00"    // (0)


// ------------ MASS STORAGE INTERFACE ------------
// ______Interface_descriptor____ at offset 9

  "\x09"   // Length: 9 bytes // (9)
  "\x04"   // Descriptor type: Interface // (4)
  "\x03"   // Interface number  // (3)
  "\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
;



// 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;

/* Microphone Buffer Ptr */
/* Use USB out-packet-buffer 6 for storing sample data */
s_int16 *micBufStart = USB_SEND_MEM + (6 * 64);
s_int16 *micBufEnd = USB_SEND_MEM + (6 * 64) + 32;
  s_int16 *micBufPtr = USB_SEND_MEM + (6 * 64);
volatile u_int16 edgeTime = 0;

/* Conversion Interrupt Handler */
#define RESETTING 0
#define GOING_UP 1
#define MIC_GAIN 50
#define MIC_TIMER_START 10000
#define MIC_OFFSET (MIC_TIMER_START-(1088/2))
void SetInterruptVector_Timer1(void);
void SetInterruptVector_GPIO0(void);
void InterruptHandler_Timer1(void){}
void SetInterruptVector_DAC(void);
void InterruptHandler_DAC(void){ 
#if 1
  static u_int16 mode = GOING_UP;
  
  if (mode == GOING_UP) {
    static s_int16 currentSample;
    
    mode = RESETTING;   

    PERIP(GPIO0_ODATA) &= ~(1<<MIC_RESET_BIT); //output state is low
    PERIP(GPIO0_DDR) |= (1<<MIC_RESET_BIT); //is output
    if (edgeTime > MIC_TIMER_START-1040 && edgeTime < MIC_TIMER_START-40)
      currentSample = (s_int16)((edgeTime)-MIC_OFFSET) * MIC_GAIN;

    *micBufPtr++ = SwapWord(currentSample);
    if (micBufPtr > micBufEnd) micBufPtr = micBufStart;

    // EP1 IN is isochronous. Make it so. Always.
    PERIP(USB_EP_ST1) |= USB_STF_IN_ENABLE | USB_STF_IN_ISO;
    
    if (PERIP(USB_EP_ST1)& USB_STF_IN_EMPTY){
      register u_int16 *i;
      register s_int16 *p;
      register u_int16 n;
      p = USB_SEND_MEM + (5 /*currentSendBuffer*/ * 64);
      i = micBufStart;
      n = 0;
      while (i < micBufPtr){
	*p++ = *i++;
	n++;
      }
      micBufPtr = micBufStart;
      PERIP(USB_EP_SEND1) = 0x8000 | (5 /*currentSendBuffer*/ << 10) |(n*2);
    }
    
  }else{
    mode = GOING_UP;
    PERIP(GPIO0_DDR) &= ~(1<<MIC_RESET_BIT); //is input (HiZ)
    PERIP(TIMER_T1CNTL) = MIC_TIMER_START; //Current Value Low

#if 1 /* Handle timeCount here, because timer interrupt is disabled.
         USBWantsSuspend() does not work without timeCount. */
    /*22050Hz*/
    {
      static int cnt = 0;
      if (++cnt > 22050/TIMER_TICKS) {
	cnt = 0;
	timeCount++;
      }
    }
#endif
  }
#endif
}





// 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{       		
	do__not__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);
  PERIP(TIMER_CONFIG) = 0; // 48 MHz


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

  while (!(ReadGPIO()&(1<<BUTTON_BIT))) {
    
    if (PERIP(USB_STATUS) & 0x8000U) {
      InitUSB(0);
    }
    if (PERIP(USB_STATUS) & USB_STF_SOF) {
      PERIP(USB_STATUS) = USB_STF_SOF; /** clear SOF */
      //USB.lastSofTimeout = 0;
      USB.lastSofTime = ReadTimeCount();
      USB.lastSofFill = AudioBufFill();
    }
    if (USB.EPReady[0] == 0) {
      USBContinueTransmission(0);
    }
    if (USB.EPReady[MSC_BULK_IN_ENDPOINT] == 0) {
      USBContinueTransmission(MSC_BULK_IN_ENDPOINT);
    }
    // Are there packet(s) in the receive buffer?
    if (PERIP(USB_RDPTR) != PERIP(USB_WRPTR)) {
      __y u_int16 ep = USBReceivePacket(&USB.pkt);
      if (USB.pkt.length) { //Does it actually contain data?
	/* This order prevents wrong SETUP detection when isochronous
	   packets are received in the middle of transactions.. */
	if (ep == AUDIO_ISOC_OUT_EP) {
	  AudioPacketFromUSB(USB.pkt.payload, (USB.pkt.length+1)/2);
	} else if (ep == MSC_BULK_OUT_ENDPOINT) {
	  MSCPacketFromPC(&USB.pkt);
	} else if (ep==0) {
	  DecodeSetupPacket();
	}
      }
    }
    ScsiTaskHandler();
    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);



// Ok, Let's Go.
void main(void) {

  do__not__puts("Hello.");

  PERIP(INT_ENABLE) = 0; // Disable All Interrupts

  SetInterruptVector_GPIO0();
  InitAudio();
  SetInterruptVector_DAC();

  //Mic input GPIOs
  PERIP(GPIO0_CLEAR_MASK) = (1<<MIC_RESET_BIT); // output level is low
  PERIP(GPIO0_MODE) &= ~(1<<MIC_RESET_BIT); //is GPIO
  PERIP(GPIO0_DDR) &= ~(1<<MIC_INPUT_BIT);; //is input
  PERIP(GPIO0_MODE) &= ~(1<<MIC_INPUT_BIT); //is GPIO
  PERIP(GPIO0_INT_FALL) |= (1 << MIC_INPUT_BIT); //Falling edge int

  //Set reload value high
  PERIP(TIMER_T1L) = 30000; //374=16 kHz //2176 ~is about 11025
  PERIP(TIMER_T1H) = 0x0;
  PERIP(TIMER_T1CNTL) = 1000; //Current Value Low
  PERIP(TIMER_T1CNTH) = 0; //Current Value High
  PERIP(TIMER_CONFIG) = 0; //Timer Clock is 48MHz
  PERIP(TIMER_ENABLE) |= (1 << 1); //Enable timer 1

  // Enable Interrupts
  PERIP(INT_ENABLEH) = 0; 
  PERIP(INT_ENABLEL) = INTF_GPIO0 | INTF_RX | INTF_DAC;

  // Drop from USB
  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");
  }

  PERIP(TIMER_CONFIG) = 7; //Timer Clock divider is 7+1;
  PERIP(INT_ENABLEH) = INTF_DAC;
  PERIP(INT_ENABLEL) = INTF_RX | INTF_TIM0;


  // 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 */
    }
  }
}



#if 0
// ASM support functions, save to file fat12adc.s
// \file fat12adc.s, compile with vsa to fat12adc.o and link with C source.


#define FILSYSTYPE 13
#define	ASM
#include <vs1000.h>


// Prototypes for C calling:	
// auto u_int16 OpenFile(register __c0 u_int16 fileNum); 
// auto u_int16 Fat12OpenFile(register __c0 u_int16 fileNum); 
// void SetInterruptVector_Timer1(void);
// void SetInterruptVector_GPIO0(void);
// void InterruptHandler_Timer1(void); 

			
	.sect code,Fat12OpenFile
	.export _Fat12OpenFile
_Fat12OpenFile:
	.import _minifatInfo
	ldc _minifatInfo+FILSYSTYPE,i7
	stx d0,(i6)+1	; sty b0,(i6)
	stx ls,(i6)+1	; sty lc,(i6)
	ldy (i7),d0	; stx le,(i6)
	ldc 0x3231,b0
	sub d0,b0,d0	; sty lr0,(i6)+1
	ldc 0x6eab,le	//directory detected
	jzc $0
	ldc 0xffff,lc

	ldc 0x6f06,ls	//update position etc..
	//ldc 0x6ecb,ls

$0:	.import _FatOpenFile
	call _FatOpenFile
	nop

	ldx (i6)-1,le	; ldy (i6),lr0
	ldx (i6)-1,ls	; ldy (i6),lc
	jr
	ldx (i6)-1,d0	; ldy (i6),b0



//This fuction sets the vector to point to caller stub...
//This is done by copying a caller opcode to the vector(!)
	.sect code,SetInterruptVector_Timer1
	.export _SetInterruptVector_Timer1	
_SetInterruptVector_Timer1:
	ldx (i6)+1,null
	stx a0,(i6) ; sty a1,(i6)
	ldc timer1_int_stub - 1,i7
	ldi (i7),a    //Read caller opcode from timer1_int_stub-1
	ldc 0x0027,i7 //Select vector... 0x0027 is Timer 1 vector
	sti a,(i7)    //Insert the caller opcode to the vector
	nop           //no control change possible just after sti/ldi!
	jr
	ldx (i6)-1,a0 ; ldy (i6),a1 //executed in jr delay slot



	
//This is the Interrupt Caller Stub:	
	jmpi timer1_int_stub,(i6)+1 // DO NOT MOVE!!!	
timer1_int_stub:

  // Context Save
	stx mr0,(i6)+1    ; sty i7,(i6)
	stx lr1,(i6)+1    ; sty lr0,(i6)
	stx a2,(i6)+1     ; sty b2,(i6)
	stx c2,(i6)+1     ; sty d2,(i6)
	stx a1,(i6)+1     ; sty a0,(i6)
	add null,p,a
	stx a1,(i6)+1     ; sty a0,(i6)

	.import _InterruptHandler_Timer1
	call _InterruptHandler_Timer1
	sty i5,(i6)

  // Context Restore	
	ldy (i6)-1,i5
	ldx (i6)-1,a1     ; ldy (i6),a0
	resp a0,a1
	ldx (i6)-1,a1     ; ldy (i6),a0
	ldx (i6)-1,c2     ; ldy (i6),d2
	ldx (i6)-1,a2     ; ldy (i6),b2
	ldx (i6)-1,lr1    ; ldy (i6),lr0
	ldc INT_GLOB_EN,i7 //Acknowledge
	ldx (i6),mr0
	reti
	stx i7,(i7)       ; ldy (i6)-1,i7 //Allow interrupts



//This fuction sets the vector to point to caller stub...
//This is done by copying a caller opcode to the vector(!)
	.sect code,SetInterruptVector_GPIO0
	.export _SetInterruptVector_GPIO0	
_SetInterruptVector_GPIO0:
	ldx (i6)+1,null
	stx a0,(i6) ; sty a1,(i6)
	ldc gpio0_int - 1,i7
	ldi (i7),a    //Read caller opcode from timer1_int_stub-1
	ldc 0x0029,i7 //Select vector... 0x0029 is GPIO0 vector
	sti a,(i7)    //Insert the caller opcode to the vector
	nop           //no control change possible just after sti/ldi!
	jr
	ldx (i6)-1,a0 ; ldy (i6),a1 //executed in jr delay slot


//This is a GPIO interrupt that stores 16 LSBs of timer 1 to X variable _edgeTime
  jmpi gpio0_int,(i6)+1 // DO NOT MOVE!!!	
gpio0_int:
	  // Context Save
	stx i0,(i6)    ; sty i1,(i6)
	
	ldc TIMER_T1CNTL,i0
	ldx (i0),i1
	.import _edgeTime
	ldc _edgeTime,i0
	stx i1,(i0)

	ldc 8,i0
	ldc GPIO0_INT_PEND,i1
	stx i0,(i1)

#if 0
// Debugging code: Toggle D4 whenever GPIO int occurs	
	ldx (i6)+1,null
	stx mr0,(i6)+1;	sty a0,(i6)
	stx b0,(i6);	sty c0,(i6)

	ldc 1<<4,a0
	ldc GPIO0_ODATA,i0
	ldx (i0),b0
	xor b0,a0,a0
	stx a0,(i0)

	ldx (i6)-1,b0;	ldy (i6),c0
	ldx (i6)-1,mr0;	ldy (i6),a0
#endif

	ldc INT_GLOB_EN,i1 //Acknowledge
	ldx (i6),i0
	reti
	stx i1,(i1)       ; ldy (i6)-1,i1 //Allow interrupts


	
//This fuction sets the vector to point to caller stub...
//This is done by copying a caller opcode to the vector(!)
	.sect code,SetInterruptVector_DAC
	.export _SetInterruptVector_DAC	
_SetInterruptVector_DAC:
	ldx (i6)+1,null
	stx a0,(i6) ; sty a1,(i6)
	ldc my_dac_int - 1,i7
	ldi (i7),a    //Read caller opcode from timer1_int_stub-1
	ldc 0x0020,i7 //Select vector... 0x0029 is GPIO0 vector
	sti a,(i7)    //Insert the caller opcode to the vector
	nop           //no control change possible just after sti/ldi!
	jr
	ldx (i6)-1,a0 ; ldy (i6),a1 //executed in jr delay slot

	
	
	jmpi my_dac_int,(i6)+1 // DO NOT MOVE!!!	
my_dac_int:
	STY I1,(I6)+1
	STX I3,(I6)+1 ; STY I2,(I6)
	STX MR0,(I6)+1 ; STY I7,(I6)
#if 1
	  // Context Save
	stx lr1,(i6)+1    ; sty lr0,(i6)
	stx a2,(i6)+1     ; sty b2,(i6)
	stx c2,(i6)+1     ; sty d2,(i6)
	stx a1,(i6)+1     ; sty a0,(i6)
	add null,p,a
	stx a1,(i6)+1     ; sty a0,(i6)

	.import _InterruptHandler_DAC
	call _InterruptHandler_DAC
	sty i5,(i6)

  // Context Restore	
	ldy (i6)-1,i5
	ldx (i6)-1,a1     ; ldy (i6),a0
	resp a0,a1
	ldx (i6)-1,a1     ; ldy (i6),a0
	ldx (i6)-1,c2     ; ldy (i6),d2
	ldx (i6)-1,a2     ; ldy (i6),b2
	ldx (i6),lr1    ; ldy (i6),lr0
#endif			

	
	j 0x7df2
	nop		
	
	.end

#endif
