#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "rtns.h"

#define BYTE unsigned char

#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

#define CH 32

int partition_start_sector;
int image_start_sector, curr_sector,cluster_size;
int next_cluster;


unsigned char res;
unsigned char *disk_buff_ptr;

unsigned char *extension;
unsigned char log_cluster_size;

int partition_start; // MMC has a single partition. We can assume the start is a small number.
int fat_start;
int root_start, root_size,  cluster2_start;

extern void   init_mmc(void);
extern void   cls(void);
extern unsigned char Command(unsigned char *);
extern BYTE SPI_byte(BYTE);
extern void restore_from_SNA(void); /* never returns */

extern  void print_hex(int x);
extern  void print_dec(int x);


int  read_sector(unsigned int hi, unsigned int lo);
void dump_dir_sector(void);

extern void my_memcpy(char *src, char *dst, int size);


sfr at 0xAB SPI_start;
sfr at 0xAC SPI_enable;
sfr at 0xAD disable_mmc_mode;
sfr at 0xF0 block_addr_lo;
sfr at 0xF1 block_addr_mid;
sfr at 0xF2 block_addr_hi;
sfr at 0xF3 DMA_addr_lo;
sfr at 0xF4 DMA_addr_hi;

sfr at 0xFE keyboard; 

char line_num;
char cur_pos;
int count;
char *buffer;
char choice;
char entry;
char entry_type;

BYTE resp[8];
BYTE i,n;

//BYTE directory_buf[0x200];

BYTE *directory_buf = 0x2000;

int entry_num;

BYTE *M4K_buf = 0x2000;

int cluster_ptr[16];
char valid_entries[16];
unsigned int screen_ptr;
unsigned int dest_ptr;
char SNA_header[27];

void crlf(void){
    line_num++;
    cur_pos = 0;
}


int read_sector(unsigned int hi, unsigned int lo){

  //  crlf();  puts("read "); print_hex(lo);
  
  block_addr_lo =  lo & 0xff;
  block_addr_mid = lo >> 8;
  SPI_start = 1;// & 0x80;

  while( SPI_start & 1){}
  SPI_start = 0x80;
  return 0;
}  

int read_sector_DMA(unsigned int hi, unsigned int lo, BYTE mem_addr_hi){

  //  crlf();  puts("read "); print_hex(lo);
  /* the limitation is that the low 9 bits of the address always go from 0 to 1ff */

  DMA_addr_hi = mem_addr_hi >> 1; 

  block_addr_lo =  lo & 0xff;
  block_addr_mid = lo >> 8;
  SPI_start = 9 & 0x80;
  while( SPI_start & 1){}
  SPI_start =   0x80;

  crlf(); print_hex(mem_addr_hi);

  return 0;
}  


main(){
  unsigned char result;
  cls();

  line_num = 0;
  cur_pos = 0;
  puts("MMC ");

  res = mount();
  if(res){
    puts(" mount returned ");
    print_dec(res);
    while(1){}
  }

  
  read_sector(0,root_start);
  line_num = 3;
  cur_pos = 0;

  entry_type = 1; //directory

  curr_sector = root_start;

  while(1){
   dump_dir_sector();      
   result = process_directory_sector();
   if(result == 1){
     curr_sector++;
     read_sector(0,curr_sector);
     cls();
     continue;
   }

   if(result == -1){
     curr_sector = root_start;
     read_sector(0,curr_sector);
     cls();
     continue;
   }

   curr_sector = ((next_cluster - 2) << log_cluster_size) + cluster2_start;

   cls();
  
   //   crlf();
   //   print_hex(next_cluster); cur_pos+=4;
   // print_hex((int)curr_sector);
   //crlf();
   if(entry_type == 0) // it's a file to load
     break;
   read_sector(0,curr_sector);
  }


  /* get here to load a file. */


  if((extension[0] == 'N') && (extension[1] == 'I') && (extension[2] == 'B')){

    puts(" NIB");
    
    block_addr_lo =  curr_sector & 0xff;
    block_addr_mid = curr_sector >> 8;
    SPI_start = 0x40; // switch to track mode
    disable_mmc_mode = 1;

  }


  while(1){}
  
}

/* we ask for a selection and act accordingly */
unsigned char process_directory_sector (void){
  char done;

  line_num += 3;
  cur_pos = 0;
  entry = 0;

  puts("Make a selection:  ");
  cur_pos -= 2;

  done = 0;
  while(!done){
    int res;
    res = key_pressed();
    if(res & 0x80){
      choice = res & 0x7f;
      
      if(choice == 'n')
	return 1;

      if(choice == 'p')
	return -1;
      
      if((choice >= 0x30) && (choice <= 0x39)){
	entry = entry * 10 + choice - 0x30; 
	putchar(choice);
      }
      if(choice == 0x0d){ // enter
	choice = valid_entries[entry-1];
	//	print_hex(choice); cur_pos += 4;
	/* point to the first cluster */
	disk_buff_ptr = &directory_buf[choice*32+0x1a]; 

	extension = &directory_buf[choice*32+0x08]; 

	next_cluster =  *(int *)disk_buff_ptr;
	if(directory_buf[choice*32+0x0b] & 0x10)
	  entry_type = 1; // subdir
	else
	  entry_type = 0; //file

	done = 1;
	//	print_hex(res);
	//cur_pos += 4;
      }
    }
  }
  return res;

}

void dump_dir_sector(void){
  entry_num = 0;
  line_num = 3;
  cur_pos = 0;


    for(i=0; i<16; i++){
      if(directory_buf[i*32] == 0)
	continue;
      if((directory_buf[i*32 + 0x1a] == 0) && (directory_buf[i*32 + 0x1b] == 0))
	continue; // zero cluster
      if(directory_buf[i*32] == 0xE5) // deleted
	continue;
      if(directory_buf[i*32] == '.') // directory
	continue;
      if(directory_buf[i*32+0x0b] == 8)
	continue;

      valid_entries[entry_num] = i;

      entry_num++;
      // print_hex(entry_num);
      if(entry_num < 10)
	putchar('0'+entry_num);
      else {
	putchar('1');
	putchar('0'+ entry_num - 10);
      }
      cur_pos = 4;

      for(n=0; n<8; n++){
	putchar(directory_buf[n + i*32]);
      }
      putchar('.');	
      for(n=8; n<11; n++){
	putchar(directory_buf[n + i*32]);
      }
      if(directory_buf[i*32+0x0b] & 0x10)
	puts(" SUBDIRECTORY");

      crlf();
    }
}

int mount(void){

  if(read_sector(0,0) !=0)
    return 1;
  
  if (*(unsigned int *) (M4K_buf + 0x1FE) != 0xAA55)
    return 2;

  partition_start = *(int *)(M4K_buf + 0x1C6);
  
  if(read_sector(0,partition_start) != 0)
    return 3;

  if (*(unsigned int *) (M4K_buf + 0x1FE) != 0xAA55)
    return 4;

  *(char *) (M4K_buf + 0x3D) = 0;
  puts((char *) (M4K_buf + 0x36));


#define fat_copies (M4K_buf[0x10])
  fat_start = *(int *) (M4K_buf + 0x0E) + partition_start;
#define fat_size (*(int *) (M4K_buf + 0x16))

  root_start = fat_size * fat_copies + fat_start;
  root_size = (*(int *) (M4K_buf + 0x11) + 15) >> 4;

  cluster_size = M4K_buf[0x0D];
  switch (cluster_size){
  case 16:
    log_cluster_size = 4;
    break;

  case 32:
    log_cluster_size = 5;
    break;

  case 64:
    log_cluster_size = 6;
    break;

  default:  
    puts("Unsupported cluster size  ");
    print_dec(cluster_size);
    while(1){};
  }


  //  crlf(); puts("Cluster size "); print_hex(cluster_size);
  //crlf(); puts("Root size "); print_hex(root_size);

  cluster2_start = root_start + root_size;
  return 0;

}

