
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <avr/eeprom.h> 

typedef unsigned char  u08;
typedef   signed char  s08;
typedef unsigned short u16;
typedef   signed short s16;
typedef unsigned long  u32;
typedef   signed long  s32;



#define FALSE 0
#define TRUE 1

#define outp(x,y) y=x
#define inp(x) x

#if defined (__AVR_ATmega8__)

#define MEGA8 1

#define MMC_Disable()  PORTB &= ~(1<<2)		/* EN low */
#define MMC_Enable()  PORTB |= (1<<2)		/* EN high */

#define CPLD_Disable()	PORTB &= ~0x02
#define CPLD_Enable()	PORTB |= 0x02

#define ROM_nibble()      PORTD |= (1<<6)
#define ROM_prodos()      PORTD &= ~(1<<6)

#define STATUS_READY()      PORTD |= (1<<4)
#define STATUS_NOT_READY()      PORTD &= ~(1<<4)

#define UCR UCSRB
#define UBRR UBRRL

#define SW2 (PINC & (1<<4))
#define CPLD_REQUEST (PIND & (1<<5))

#define LED_ON()     PORTD |= 0x80
#define LED_OFF()     PORTD &= ~0x80


#endif 

#if defined (__AVR_ATmega162__)

#define MMC_Disable()  PORTE &= ~(1<<1)		/* EN low */
#define MMC_Enable()  PORTE |= (1<<1)		/* EN high */
#define CPLD_Disable()	PORTC &= ~0x08
#define CPLD_Enable()	PORTC |= 0x08

#define UCR UCSR0B
#define UDR UDR0
#define UBRR UBRR0L

#define SW2 (PINA & 0x02)
#define CPLD_REQUEST (PINE & 0x1)

#define ROM_nibble()      PORTD |= (1<<2)
#define ROM_prodos()      PORTD &= ~(1<<2)

#define STATUS_READY()      PORTD |= (1<<7)
#define STATUS_NOT_READY()      PORTD &= ~(1<<7)

#define LED_ON()     PORTD |= 0x40
#define LED_OFF()     PORTD &= ~0x40

#endif

unsigned char Read_Byte_MMC(void);
void Write_Byte_MMC(unsigned char);
unsigned char mmcRead(int sector, unsigned char *buffer);
unsigned char get_next_byte(void);

unsigned char command_byte;
unsigned char command_state;
unsigned char image_number;
unsigned char image_offset;
unsigned short block_num;


unsigned char read_buff[512];
//unsigned char write_buff[256];

unsigned char count, n, next_byte;
unsigned char track, last_track;
unsigned char phase;
unsigned char last_phase;

//enum states_t {IDLE, SIZE, REC, CHECKSUM};

unsigned char ticks;

unsigned char flags;
unsigned char res;
unsigned char state_flags;
unsigned char idle_loops;

//register unsigned char data6502 asm("r3");
register unsigned char addr6502 asm("r4");

int partition_start_sector;
int image_start_sector, curr_sector;
short curr_byte;

unsigned short floppy_byte;


unsigned char buf_counter;
unsigned char offset_in_blocks;

extern unsigned char rw_spi(unsigned char newval); 


SIGNAL(SIG_INTERRUPT1){
  CPLD_Enable();
  SPDR = 0;// count | 0x80;
  while(!(SPSR & (1<<SPIF)));
  addr6502 = SPDR;
  CPLD_Disable();
  phase = (addr6502 & 0x7) >> 1;
	    
  if(phase == 0){
    if(last_phase == 3)
      track++;
    if(last_phase == 1)
      if(track > 0)
	track--;
  }
  if(phase == 2){
    if(last_phase == 1)
      track++;
    if(last_phase == 3)
      if(track > 0)
	track--;
  }
  last_phase = phase;
  if(last_track != track){
    last_track = track;
    curr_sector = partition_start_sector + block_num + track * 13;
    // curr_sector += image_number * 455;
    res = mmcRead(curr_sector, read_buff);
    curr_byte = 0;
    offset_in_blocks = 0;
  }
} 



SIGNAL(SIG_OVERFLOW0){
  ticks++;
} 


/* Initialize UART */
void InitUART( unsigned char baudrate )
{
  outp(0x18 ,UCR);       /* enable RxD/TxD */
  outp(  baudrate, UBRR);     
}


void SPIinit(void) {
	SPCR = (1 << SPE) | (1 << MSTR);// | (1 << SPR1) | (1 << SPR0);
	SPSR = (1 << SPI2X); // double-rate, i.e. clk/2
}

char SPI(char d) {  // send character over SPI
	char received = 0;
	SPDR = d;
	while(!(SPSR & (1<<SPIF)));
	received = SPDR;
	return (received);
}


char Command(char befF, uint16_t AdrH, uint16_t AdrL, char befH )
{	// sends a command to the MMC
	SPI(0xFF);
	SPI(befF);
	SPI((uint8_t)(AdrH >> 8));
	SPI((uint8_t)AdrH);
	SPI((uint8_t)(AdrL >> 8));
	SPI((uint8_t)AdrL);
	SPI(befH);
	SPI(0xFF);
	res = SPI(0xFF);	// return the last received character
	return res;
}

int MMC_Init(void) { // init SPI
	char i;
	MMC_Disable();
	// start MMC in SPI mode
	for(i=0; i < 10; i++) SPI(0xFF); // send 10*8=80 clock pulses

	MMC_Enable();

	if (Command(0x40,0,0,0x95) != 1) goto mmcerror; // reset MMC

st: // if there is no MMC, prg. loops here
	if (Command(0x41,0,0,0xFF) !=0) goto st;
	return 1;
mmcerror:
	return 0;
}

#define MMC_DR_MASK   0x1F
#define MMC_DR_ACCEPT   0x05
#define	MMC_READ_SINGLE_BLOCK   17
#define	MMC_STARTBLOCK_READ   0xFE
#define	MMC_WRITE_BLOCK   24
#define	MMC_STARTBLOCK_WRITE   0xFE

int last_block_read;
int wait_bytes;


unsigned char StartMMCRead(int sector)
{
     unsigned char r1;
     short high_addr;

     high_addr = sector >> 7; 
 
     MMC_Enable();

     // issue command
     r1 = Command(0x40 | MMC_READ_SINGLE_BLOCK, high_addr, sector<<9,0);

     // check for valid response
     if(r1 != 0x00){
       MMC_Disable();
       return r1;
     }
	 
     // wait for block start

     wait_bytes = 0;
     while(SPI(0xFF) != MMC_STARTBLOCK_READ){
       wait_bytes++;
       if( wait_bytes == 0x8000){
	 MMC_Disable();
	 return 0x80;
       }
     }

     return 0;
 }

unsigned char mmcRead(int sector, unsigned char *buffer)
{
     unsigned char r1;
     short i;
     short high_addr;

     high_addr = sector >> 7; 
 
     // assert chip select
     // PORTB &= ~(1 << SPICS); // enable MMC
     MMC_Enable();

     // issue command
     r1 = Command(0x40 | MMC_READ_SINGLE_BLOCK, high_addr, sector<<9,0);

     // check for valid response
     if(r1 != 0x00){
       MMC_Disable();
       return r1;
     }
	 
     // wait for block start

     wait_bytes = 0;
     while(SPI(0xFF) != MMC_STARTBLOCK_READ){
       wait_bytes++;
       if( wait_bytes == 0x8000){
	 MMC_Disable();
	 return 0x80;
       }
     }

     // read in data
     for(i=0; i<0x200; i++){
       *buffer++ = SPI(0xFF);
     }
#if 1
     // read 16-bit CRC
     r1 = SPI(0xFF);
     r1 = SPI(0xFF);
#endif
     // release chip select
     // PORTB |= (1 << SPICS); // disable MMC
     MMC_Disable();
     // return success

     last_block_read = sector;

     return 0;
 }
 
unsigned char mmcWrite(int sector, unsigned char* buffer) {
     unsigned char r1;
     int i;
     short high_addr;

     if(sector == 0)
       return;

     high_addr = sector >> 7; 
 
     // assert chip select
     MMC_Enable();

     // issue command
     r1 = Command(0x40 | MMC_WRITE_BLOCK, high_addr, sector<<9,0);

     // check for valid response
     if(r1 != 0x00)
         return r1;
     // send dummy
     SPI(0xFF);
     // send data start token
     SPI(MMC_STARTBLOCK_WRITE);
     // write data
     for(i=0; i<0x200; i++)
     {
         SPI(*buffer++);
     }
     // write 16-bit CRC (dummy values)
     r1 = SPI(0xFF);
     r1 = SPI(0xFF);
     // read data response token
     r1 = SPI(0xFF);
     if( (r1&MMC_DR_MASK) != MMC_DR_ACCEPT)
         return r1;
     // wait until card not busy
     while(!SPI(0xFF));
     // release chip select
     MMC_Disable();

     // return success
     return 0;
}

unsigned char checksum;

unsigned char get_next_byte(){
  unsigned char data;

  if(curr_byte == 512){
    if(flags & 0x01){ // direct (ProDOS) mode
      curr_byte = 0;
      return checksum;
    } else { // nibble mode
      flags |= 0x02;		/* time to reload */
      return 0;			/* next byte is set to 0 */
    }
  }

  data = read_buff[curr_byte++];

#if 0
  // don't read the next block if in ProDOS mode
  if(curr_byte == 512){
    if((flags & 0x01)==0){
      if(++offset_in_blocks ==13)
	offset_in_blocks = 0;
      curr_sector = partition_start_sector  + track * 13 + offset_in_blocks +
	block_num;
      if(last_block_read != curr_sector){
	res = mmcRead(curr_sector, read_buff);
	curr_byte = 0;
	if(res != 0){
	  MMC_Init();
	  res = mmcRead(curr_sector, read_buff);
	  if(res != 0)
	    UDR = '!';
	}
	//	data = 0;  
      }
    }
  }
#endif
  checksum ^= data;
  return data;

}

int test_CPLD(){
  int i;
  unsigned char data, r_data;
  CPLD_Enable();

  for(i=0; i< 512; i++){
    data = read_buff[i];

    SPDR = data;		/* write a byte */
    while(!(SPSR & (1<<SPIF)));

    r_data = SPDR;
    if(i > 0)
      if(r_data != read_buff[i-1])
	break;
    //	return (0x8000 + i);

    SPDR = 0;
    while(!(SPSR & (1<<SPIF))); /* getting addr reg */
    

  }
  CPLD_Disable();
  return i;
}


timer_delay(){			/* should be some 20 mS */
  TCNT0=0;
  TIFR |= 0x02;			/* clear overflow */
  while((TIFR & 0x02) == 0);
}

blink_LED(){
  char n;
  char state = 0;
  while(1){
    if(state){
      LED_ON();
    } else
      LED_OFF();

    state ^= 1;
    for(n=0; n<25; n++)
      timer_delay();
  }
}


int main(void)
{
  unsigned char tries;

#if MEGA8
  PORTB = 0;
  /* PORTB 0 is reset to Apple */
  DDRB = 1 | (1<<1) | (1<<2) | (1<<3)  | (1<<5);

  PORTC = 0xFF; 
  DDRC = 0x00;			/* all inputs with pull-ups */

  PORTD = 0;
  DDRD =  (1<<4) |(1<<6) | (1<<7) ; // status(D4) ROM8, LED(d7) are out 

#else
  outp(0xbb ,DDRB);      /* out except for PB2(RXD1) and PB6(MISO) */
  outp(0x0, PORTB);

  DDRA = 0;		/* all input */
  PORTA = 0xff;		/* pull-ups on */


  outp(0x05 ,TCCR0);            /* Timer 0 prescale 1024  */
    //  outp(0x02, TIMSK);     /* enable interrupt on T0 overflow */

  PORTC = 0x00; 
  DDRC = 0x0d;			/* c1 is input - MISO */

  PORTD = 0x10;			/* MMC power off */
  DDRD = 0xD4;			/* D2 out - control EPROM addr 8 */
				/* D7 out - status control */
				/* D6 out - LED */
                                /* D5 out - MMC power */


  PORTE |= 0; // spi_mode[0] for CPLD
  DDRE = 0x2; // e1-sel

#endif

  MCUCR |= 0x0c; // int 1 on rising edge

  GICR = (1<<7);

  //  GICR = (1<<7) |(1<<5) ; //int 1 and 2 enabled
  // GICR = 1<<5; //int 2 enabled

  //  DDRC |= 1;

  InitUART(25);  /* set baud rate to 38.4k at 16 MHz) */
  sei();                 /* enable interrupts */
    
  SPIinit();
  
  if(MMC_Init() != 1){
    DDRB &= 0xfe;  
    blink_LED();		/* forever */
  }
  track = 0;
  flags = 0x01; //defult to  ProDOS mode

  tries = 3;
  while(tries){
    res = mmcRead(0, read_buff);
    if(res == 0)
      break;
    tries--;
  }

  DDRB &= 0xfe;			/* release reset */

  partition_start_sector = *(int *)(read_buff + 0x1be + 0x08) ;
  if(partition_start_sector > 0)
    LED_ON();
    // there are 13x512 bytes in the simulated track
  image_start_sector = partition_start_sector;
  curr_sector = partition_start_sector  + track * 13;

  if(SW2){
    image_offset = ~(PORTC & 0x1f); 
    flags &= ~0x01; // set nibble mode
    ROM_nibble();
      
    block_num = (image_offset + 1) * 455;
    curr_sector = partition_start_sector + block_num + 
      track * 13;
  }

  res = mmcRead(curr_sector, read_buff);
  curr_byte=0;

  outp('#', UDR);		/* serial debug output */
    
  SPDR = 0;
  while(!(SPSR & (1<<SPIF)));

    for (;;) {            /* loop forever */

      if(CPLD_REQUEST){
	  
	//	zeros_served++;
	CPLD_Enable();


	if(flags & 0x02){ //time to reload nibble buffer
	  flags &= ~0x02;
	  if(++offset_in_blocks ==13)
	    offset_in_blocks = 0;
	  curr_sector = partition_start_sector  + track * 13 + 
	    offset_in_blocks +  block_num;
	  if(last_block_read != curr_sector){
	    res = mmcRead(curr_sector, read_buff);
	    if(res != 0){
	      MMC_Init();
	      res = mmcRead(curr_sector, read_buff);
	      if(res != 0)
		UDR = '!';
	    }
	  } // not current sector
	  next_byte = read_buff[0];
	  curr_byte = 1;
	} // if flags

	SPDR = next_byte;
	while(!(SPSR & (1<<SPIF)));
	addr6502 = SPDR;

	if((addr6502 & 0x1f) == 0x0d){ //write to C0XD 
	  //if((addr6502 & 0x10) == 0){ //write 

	  SPDR = 0; // now read the data byte written by Apple
	  while(!(SPSR & (1<<SPIF)));
	  command_byte = SPDR;

	  //	  write_buff[count++] = command_byte;

	  switch(command_state){
	  case 0:
	    if((command_byte & 0x80) == 0){
	      command_state = command_byte;
	    } else {
	      switch(command_byte){
	      case 0x80:
		flags &= ~0x01; // set nibble mode
		ROM_nibble();
		curr_sector = partition_start_sector + block_num + 
		  track * 13;
		res = mmcRead(curr_sector, read_buff);
		curr_byte = 0;
		offset_in_blocks = 0;

		break;

	      case 0x81:
		flags |= 0x01; // set ProDOS mode
		ROM_prodos();
		break;

	      case 0x82: //read a block

		curr_sector = partition_start_sector  + block_num;
		curr_byte = 0;
		checksum = 0;

#if 1
		if(last_block_read != curr_sector)
		  res = mmcRead(curr_sector, read_buff);
		//		SPDR = res | 0x80;
		//	while(!(SPSR & (1<<SPIF)));

		STATUS_READY();

		SPDR = get_next_byte();
		while(!(SPSR & (1<<SPIF)));
		next_byte = get_next_byte();

#else // direct read

		StartMMCRead(curr_sector);
		SPDR = 0xff;
		while(!(SPSR & (1<<SPIF)));
#endif		


		break;
		
	      case 0x83: //read an absolute block
		curr_sector = block_num;
		res = mmcRead(curr_sector, read_buff);
		SPDR = res | 0x80;
		while(!(SPSR & (1<<SPIF)));
		curr_byte = 0;
		SPDR = get_next_byte();
		while(!(SPSR & (1<<SPIF)));
		break;

	      case 0xA2: //write a block
		curr_sector = partition_start_sector  + block_num;
		res = mmcWrite(curr_sector, read_buff);
		SPDR = res | 0x80;
		while(!(SPSR & (1<<SPIF)));
		curr_byte = 0;
		break;

	      }
	    }
	      
	    break;

	  case 1:
	    image_number = command_byte;
	    command_state = 0;
      
	  case 2: // high byte of block
	    *((unsigned char *)&block_num + 1) = command_byte;
	    command_state = 3;

	    CPLD_Disable();
	    STATUS_NOT_READY();
	    break;

	  case 3: // low byte of block
	    *(unsigned char *)&block_num  = command_byte;
	    command_state = 0;
	    break;

	  case 4: // start writing
	    curr_byte = 0;
	    command_state = 5;
	    //  break; fall through
		
	  case 5:
	    read_buff[curr_byte++]=  command_byte;
	    if(curr_byte == 512)
	      command_state = 0;
	    break;
		
	  default:
	    command_state = 0;
	  }
	} else { 
	  if((flags & 0x01) == 0){
	    if(next_byte == 0)
	      next_byte = get_next_byte();
	    else
	      next_byte =0;
	  } else
	    next_byte = get_next_byte();
	  //	   idle_loops = 0;
	}
	CPLD_Disable();
      }

    }// main loop
}

