/*  TilEm, TI-Linux Emulator
 *  Copyright (C) 2001 Solignac Julien <x1cygnus@xcalc.org>
 *  Portions copyright (C) 2004 Benjamin Moody <benjamin@ecg.mit.edu>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#define FLASH_READ 0
#define FLASH_AA   1
#define FLASH_55   2
#define FLASH_PROG 3
#define FLASH_ERASE 4
#define FLASH_ERAA 5
#define FLASH_ER55 6

/* Autoselect mode is not implemented.
 * Status bits are not implemented (all operations occur within one
 * clock cycle)
 * Erase suspend is not implemented (there's no chance for it to
 * happen anyway.)
 */

struct sect {long start, size; byte s;};

struct sect xp_sectors[]={{0x000000, 0x10000, 1},
			  {0x010000, 0x10000, 1},
			  {0x020000, 0x10000, 1},
			  {0x030000, 0x10000, 1},
			  {0x040000, 0x10000, 1},
			  {0x050000, 0x10000, 1},
			  {0x060000, 0x10000, 1},
			  {0x070000, 0x08000, 1},
			  {0x078000, 0x02000, 1},
			  {0x07A000, 0x02000, 1},
			  {0x07C000, 0x04000, 3},
			  {0, 0, 0}};



void xp_erase(byte *mem, unsigned long a, long l)
{
  long i;

  for (i=0;i<l;i++)
    mem[a+i]=0xFF;
}

void xp_flash_write(byte *mem, unsigned long pa, byte v, byte lastpage,
		    struct sect* secs)
{
  int i;

  switch (flashmode) {

  case FLASH_READ:
    if (((pa&0xFFF) == 0xAAA) && (v == 0xAA))
      flashmode = FLASH_AA;
    else if (v != 0xF0)
      printf("** flash error (undefined command %02x->%06lx in read mode) **\n",v,pa);
    return;

  case FLASH_AA:
    flashmode = FLASH_READ;
    if (((pa&0xFFF) == 0x555) && (v == 0x55))
      flashmode = FLASH_55;
    else if (v != 0xF0)
      printf("** flash error (undefined command %02x->%06lx after AA) **\n",v,pa);
    return;

  case FLASH_55:
    flashmode = FLASH_READ;
    if ((pa&0xFFF) == 0xAAA) {

      switch (v) {
      case 0x10:
      case 0x30:
	printf("** flash error (attempt to erase without pre-erase) **\n");
	return;
      case 0x80:
	flashmode = FLASH_ERASE;
	return;
      case 0x90:
	printf("** flash error (autoselect is not implemented) **\n");
	return;
      case 0xA0:
	flashmode = FLASH_PROG;
	return;
      }
    }
    if (v != 0xF0)
      printf("** flash error (undefined command %02x->%06lx after AA,55) **\n",v,pa);
    return;

  case FLASH_PROG:
    if (pa >= (0x4000 * lastpage)) {
      printf("** flash error (programming boot page) **\n");
    }
    else {
      if (v & ~(*(mem+pa)))
	printf("** flash error (bad program %02x over %02x) **\n",v,
	       *(mem+pa));

      *(mem+pa) &= v;
    }

    /* Delay not yet implemented */
    flashmode = FLASH_READ;
    return;

  case FLASH_ERASE:
    flashmode = FLASH_READ;
    if (((pa&0xFFF) == 0xAAA) && (v == 0xAA))
      flashmode = FLASH_ERAA;
    else if (v != 0xF0)
      printf("** flash error (undefined command %02x->%06lx after pre-erase) **\n",v,pa);
    return;

  case FLASH_ERAA:
    flashmode = FLASH_READ;
    if (((pa&0xFFF) == 0x555) && (v == 0x55))
      flashmode = FLASH_ER55;
    else if (v != 0xF0)
      printf("** flash error (undefined command %02x->%06lx after pre-erase AA) **\n",v,pa);
    return;

  case FLASH_ER55:
    flashmode = FLASH_READ;
    if (((pa&0xFFF) == 0xAAA) && v==0x10) {
      printf("Congratulations.  You managed to erase the entire chip.\n"
	     "Now what are you planning on doing?");

      xp_erase(mem,0,0x4000*lastpage);
    }
    else if (v == 0x30) {
      for (i=0;secs[i].s && pa>=secs[i].start;i++);
      i--;

      if (secs[i].s&2)
	printf("** flash error (erasing boot page) **\n");
      else {
	printf("Erasing flash sector %d (pa = %06lX)\n",i,pa);
	xp_erase(mem,secs[i].start,secs[i].size);
      }

    }
    else if (v != 0xF0)
      printf("** flash error (undefined command %02x->%06lx after pre-erase AA,55) **\n",v,pa);
    return;
  }

}


void xp_Z80_WRMEM(dword A, byte v)
{
  unsigned long pa;

  pa = (A & 0x3FFF) + 0x4000*pager[(A)>>14];

  if (pa<0x80000)
    xp_flash_write(mpages,pa,v,0x1F,xp_sectors);

  else if (pa < 0x88000)
    *(mpages+pa) = v;
}


unsigned xp_Z80_RDMEM(dword A)
{
  extern Z80_Regs R;
  byte page;
  static byte warned=0;

  A &= 0xFFFF;
  page = pager[A>>14];

  if (A==R.PC.W.l) {
    if (noexec[page/8] & (1<<(page%8))) {
      if (!warned)
	printf("** executing in restricted %s area **\n",
	       page>0x1f?"RAM":"Flash");
      warned = 1;
    }
    else
      warned = 0;
  }
  
  if (page == 0x1E) {
    if (flashlock) {
      /* Bitmask for apps, starting with bit 1, 0 = Enabled, 1 = Disabled ;) */
      if ((A & 0x1FFF) == 0x1FE0 || (A & 0x1FFF) == 0x1FE1)
	return(0x00);
    }
    else {
      printf("** reading from read-protected sector **\n");
      return 0xff;
    }
  }

  return(*(mpages + 0x4000 * page + (A & 0x3FFF)));
}
