/*****************************************************************************

   This file is part of x2600, the Atari 2600 Emulator
   ===================================================
   
   Copyright 1996 Alex Hornby. For contributions see the file CREDITS.

   This software is distributed under the terms of the GNU General Public
   License. This is free software with ABSOLUTELY NO WARRANTY.
   
   See the file COPYING for Details.
   
   $Id: memory.c,v 2.23 1997/04/06 02:19:12 ahornby Exp $
******************************************************************************/
//This file was modified from its original version for use in PocketVCS
// by Stuart Russell

/* 
 * Holds the memory access routines to both memory and memory mapped
 * i/o, hence memory.c
 *
 * Uses GNU C extensions.
 */

#include <stdio.h>
#include "types.h"
#include "address.h"
#include "vmachine.h"
#include "display.h"
#include "raster.h"
#include "tiasound.h"
#include "collision.h"
#include "col_mask.h"
#include "options.h"
#include "keyboard.h"
#include "sound.h"
#include "resource.h"


extern CLOCK clkcount;
extern CLOCK clk;
extern int beamadj;
//extern keyboard_keypad();
extern keytrig();
extern keyjoy();
extern keycons();
extern int nOptions_Landscape;
extern int nOptions_SoundOn;


/* Undecoded Read, for executable code etc. */
/* a: address to read */
/* returns: byte at address a */
BYTE
undecRead (ADDRESS a)
{
  if (a & 0x1000)
    return theRom[a & 0xfff];
  else
    return theRam[a & 0x7f];
}


__inline void
bank_switch_write (ADDRESS a, BYTE b)
{
  a&=0xfff;
  switch (base_opts.bank)
    {

    case 1:
      /* Atari 8k F8 */
      switch (a)
	{
	case 0xff8:
	  theRom = &theCart[0];
	  break;
	case 0xff9:
	  theRom = &theCart[4096];
	  break;
	}
      break;

    case 2:
      /* Atari 16k F6 */
      switch (a)
	{
	case 0xff6:
	  theRom = &theCart[0];
	  break;
	case 0xff7:
	  theRom = &theCart[4096];
	  break;
	case 0xff8:
	  theRom = &theCart[8192];
	  break;
	case 0xff9:
	  theRom = &theCart[12288];
	  break;
	}
      break;

    case 3:
      /* Parker Brothers 8k E0 */
      {
	ADDRESS a1;
	if (a > 0xfdf && a < 0xfe8) 
	  {
	    a1=(a&0x07)<<10;
	    memcpy(&cartScratch[0],&theCart[a1],0x400);
	  }
	else if (a > 0xfe7 && a < 0xff0) 
	  {
	    a1=(a&0x07)<<10;
	    memcpy(&cartScratch[0x400],&theCart[a1],0x400);
	  } 
	else if (a > 0xfef && a < 0xff8) 
	  {
	    a1=(a&0x07)<<10;
	    memcpy(&cartScratch[0x800],&theCart[a1],0x400);
	  }
      }
      break;

    case 4:
      /* CBS Ram Plus FA */
      if (a < 0x100) 
	cartRam[a & 0xff]=b;
      else
	{	  
	  switch (a)
	    {
	    case 0xff8:
	      theRom = &theCart[0];
	      break;
	    case 0xff9:
	      theRom = &theCart[4096];
	      break;
	    case 0xffa:
	      theRom = &theCart[8192];
	      break;
	    }
	}
      break;

    case 5:
      /* Atari 16k + super chip ram F6SC */
      if (a < 0x80) 
	cartRam[a & 0x7f] = b;
      else
	{
	  switch (a)
	    {
	    case 0xff6:
	      theRom = &theCart[0];
	      break;
	    case 0xff7:
	      theRom = &theCart[4096];
	      break;
	    case 0xff8:
	      theRom = &theCart[8192];
	      break;
	    case 0xff9:
	      theRom = &theCart[12288];
	      break;
	    }
	}
      break;
    }
}

__inline BYTE
bank_switch_read (ADDRESS a)
{
  BYTE res;

  a&=0xfff;
  switch (base_opts.bank)
    {
    case 1:
      /* Atari 8k F8 */
      switch (a)
	{
	case 0xff8:
	  theRom = &theCart[0];
	  break;
	case 0xff9:
	  theRom = &theCart[4096];
	  break;
	}
      res=theRom[a];
      break;

    case 2:
      /* Atari 16k F6 */
      switch (a)
	{
	case 0xff6:
	  theRom = &theCart[0];
	  break;
	case 0xff7:
	  theRom = &theCart[4096];
	  break;
	case 0xff8:
	  theRom = &theCart[8192];
	  break;
	case 0xff9:
	  theRom = &theCart[12288];
	  break;
	}
      res=theRom[a];
      break;

    case 3:
      /* Parker Brothers 8k E0 */
      /* Parker Brothers 8k E0 */
      {
	ADDRESS a1;
	if (a > 0xfdf && a < 0xfe8) 
	  {
	    a1=(a&0x07)<<10;
	    memcpy(&cartScratch[0],&theCart[a1],0x400);
	  }
	else if (a > 0xfe7 && a < 0xff0) 
	  {
	    a1=(a&0x07)<<10;
	    memcpy(&cartScratch[0x400],&theCart[a1],0x400);
	  } 
	else if (a > 0xfef && a < 0xff8) 
	  {
	    a1=(a&0x07)<<10;
	    memcpy(&cartScratch[0x800],&theCart[a1],0x400);
	  }
      }
      res=theRom[a];
      break;

    case 4:
      /* CBS Ram Plus FA */
      if (a > 0xff && a < 0x200) 
	  res=cartRam[a & 0xff];
      else
	{	  
	  switch (a)
	    {
	    case 0xff8:
	      theRom = &theCart[0];
	      break;
	    case 0xff9:
	      theRom = &theCart[4096];
	      break;
	    case 0xffa:
	      theRom = &theCart[8192];
	      break;
	    }
	  res=theRom[a];
	}
      break;

    case 5:
      /* Atari 16k + super chip ram F6SC */
      if (a > 0x7f && a < 0x100) 
	res=cartRam[a & 0x7f];
      else
	{
	  switch (a)
	    {
	    case 0xff6:
	      theRom = &theCart[0];
	      break;
	    case 0xff7:
	      theRom = &theCart[4096];
	      break;
	    case 0xff8:
	      theRom = &theCart[8192];
	      break;
	    case 0xff9:
	      theRom = &theCart[12288];
	      break;
	    }
	  res=theRom[a];
	}
      break;
    default:
      res=theRom[a];
      break;
    }
  return res;
}


/* Decoded write to memory */
/* a: address written to */
/* b: byte value written */
void
decWrite (ADDRESS a, BYTE b)
{
  int i;

  /* A Write to the ROM area */
  if (a & 0x1000)
    {
      bank_switch_write (a,b);
    }
  /* A Write to the RAM area in Page 0 and 1 */
  else if ((a & 0x280) == 0x80)
    {
      theRam[a & 0x7f] = b;
    }
  /* TIA */
  else if (!(a & 0x80))
    {
      switch (a & 0x7f)
	{
	case VSYNC:
	  if (b & 0x02)
	    {
	      /* Start vertical sync */
	      vbeam_state = VSYNCSTATE;
	    }
	  break;
	case VBLANK:
	  do_vblank (b);
	  /* Ground paddle pots */
	  if (b & 0x80)
	    {
	      /* Grounded ports */
	      tiaRead[INPT0] = 0x00;
	      tiaRead[INPT1] = 0x00;
	    }
	  else
	    {
	      /* Processor now measures time for a logic 1 to appear
	         at each paddle port */
	      tiaRead[INPT0] = 0x80;
	      tiaRead[INPT1] = 0x80;
	      paddle[0].val = clk;
	      paddle[1].val = clk;
	    }
	  /* Logic for dumped input ports */
	  if (b & 0x40)
	    {
	      if (tiaWrite[VBLANK] & 0x40)
		{
		  tiaRead[INPT4] = 0x80;
		  tiaRead[INPT5] = 0x80;
		}
//	      else
//		{
//		  //read_trigger ();
//		}
	    }
	  tiaWrite[VBLANK] = b;
	  break;
	case WSYNC:
	  /* Skip to HSYNC pulse */
	  do_hsync ();
	  break;
	case RSYNC:
	  /* used in chip testing */
	  //dbg_message(DBG_LOTS,"ARGHH an undocumented RSYNC!\n");
	  break;
	case NUSIZ0:
	  /*
	     printf("P0 nusize: ebeamx=%d, ebeamy=%d, nusize=%02x\n",
	     ebeamx, ebeamy, (int)b);
	   */
	  pl[0].nusize = b & 0x07;
	  ml[0].width = (b & 0x30) >> 4;
	  break;
	case NUSIZ1:
	  /*
	     printf("P1 nusize: ebeamx=%d, ebeamy=%d, nusize=%02x\n",
	     ebeamx, ebeamy, (int)b);
	   */
	  pl[1].nusize = b & 0x07;
	  ml[1].width = (b & 0x30) >> 4;
	  break;
	case COLUP0:
	  do_unified_change (0, b);
	  break;
	case COLUP1:
	  do_unified_change (1, b);
	  break;
	case COLUPF:
	  do_unified_change (2, b);
	  break;
	case COLUBK:
	  /*printf("BKcolour = %d, line=%d\n", (int)(b>>1), ebeamy); */
	  do_unified_change (3, b);
	  break;
	case CTRLPF:
	  tiaWrite[CTRLPF] = b & 0x37;	/* Bitmask 00110111 */
	  do_pfraster_change (0, 3, b & 0x01);	/* Reflection */
	  
	  /* Normal/Alternate priority */
	  do_unified_change(4, (b & 0x04));
	  
	  /* Scores/Not scores */
	  do_unified_change(5, (b & 0x02));

	  break;
	case REFP0:
	  pl[0].reflect = (b & 0x08) >> 3;
	  break;
	case REFP1:
	  pl[1].reflect = (b & 0x08) >> 3;
	  break;
	case PF0:
	  do_pfraster_change (0, 0, b & 0xf0);
	  break;
	case PF1:
	  do_pfraster_change (0, 1, b);
	  break;
	case PF2:
	  do_pfraster_change (0, 2, b);
	  break;
	case RESP0:
	  /* Ghost in pacman!
	     if(beamadj == 0) {
	     printf("RESP0: ebeamx=%d, ebeamy=%d\n",
	     ebeamx, ebeamy); 
	     show();
	     } */
	  pl[0].x = ebeamx + beamadj;
	  /* As per page 20 Stella Programmers Guide */
	  if (pl[0].x < 0)
	    pl[0].x = 0;
	  break;
	case RESP1:
	  /*if(beamadj == 0) {
	     printf("RESP1: ebeamx=%d, ebeamy=%d\n",
	     ebeamx, ebeamy);
	     show(); 
	     } */
	  pl[1].x = ebeamx + beamadj;
	  /* As per page 20 Stella Programmers Guide */
	  if (pl[1].x < 0)
	    pl[1].x = 0;
	  break;
	case RESM0:
	  ml[0].x = ebeamx + beamadj;
	  /* As per page 20 Stella Programmers Guide */
	  if (ml[0].x < 0)
	    ml[0].x = 0;
	  break;
	case RESM1:
	  ml[1].x = ebeamx + beamadj;
	  /* As per page 20 Stella Programmers Guide */
	  if (ml[1].x < 0)
	    ml[1].x = 0;
	  break;
	case RESBL:
	  ml[2].x = ebeamx + beamadj;
	  /* As per page 20 Stella Programmers Guide */
	  if (ml[2].x < 0)
	    ml[2].x = 0;
	  break;
	case AUDC0:
  	  if (nOptions_SoundOn)		  Update_tia_sound(AUDC0,b & 0x0f);
	  break;
	case AUDC1:
  	  if (nOptions_SoundOn) 	  Update_tia_sound(AUDC1,b & 0x0f);
	  break;
	case AUDF0:
  	  if (nOptions_SoundOn) 	  Update_tia_sound(AUDF0,b & 0x1f);
	  break;
	case AUDF1:
  	  if (nOptions_SoundOn) 	  Update_tia_sound(AUDF1,b & 0x1f);
	  break;
	case AUDV0:
  	  if (nOptions_SoundOn) 	  Update_tia_sound(AUDV0 ,  b & 0x0f);
	  break;
	case AUDV1:
  	  if (nOptions_SoundOn) 	  Update_tia_sound(AUDV1 ,  b & 0x0f);
	  break;
	case GRP0:
	  do_plraster_change (0, 0, b);
	  do_plraster_change (1, 1, b);
	  break;
	case GRP1:
	  do_plraster_change (1, 0, b);
	  do_plraster_change (0, 1, b);
	  ml[2].vdel = ml[2].enabled;
	  break;
	case ENAM0:
	  ml[0].enabled = b & 0x02;
	  if (tiaWrite[RESMP0])
	    ml[0].enabled = 0;
	  break;
	case ENAM1:
	  ml[1].enabled = b & 0x02;
	  if (tiaWrite[RESMP1])
	    ml[1].enabled = 0;
	  break;
	case ENABL:
	  ml[2].enabled = b & 0x02;
	  break;
	case HMP0:
	  pl[0].hmm = (b >> 4);
	  break;
	case HMP1:
	  pl[1].hmm = (b >> 4);
	  break;
	case HMM0:
	  ml[0].hmm = (b >> 4);
	  break;
	case HMM1:
	  ml[1].hmm = (b >> 4);
	  break;
	case HMBL:
	  ml[2].hmm = (b >> 4);
	  break;
	case VDELP0:
	  pl[0].vdel_flag = b & 0x01;
	  break;
	case VDELP1:
	  pl[1].vdel_flag = b & 0x01;
	  break;
	case VDELBL:
	  ml[2].vdel_flag = b & 0x01;
	  break;
	case RESMP0:
	  tiaWrite[RESMP0] = b & 0x02;
	  if (b & 0x02)
	    {
	      ml[0].x = pl[0].x + 4;
	      ml[0].enabled = 0;
	    }
	  break;
	case RESMP1:
	  tiaWrite[RESMP1] = b & 0x02;
	  if (b & 0x02)
	    {
	      ml[1].x = pl[1].x + 4;
	      ml[1].enabled = 0;
	    }
	  break;
	case HMOVE:
	  /* Player 0 */
	  if (pl[0].hmm & 0x08)
	    pl[0].x += ((pl[0].hmm ^ 0x0f) + 1);
	  else
	    pl[0].x -= pl[0].hmm;
	  if (pl[0].x > 160)
	    pl[0].x = -68;
	  else if (pl[0].x < -68)
	    pl[0].x = 160;

	  /* Player 2 */
	  if (pl[1].hmm & 0x08)
	    pl[1].x += ((pl[1].hmm ^ 0x0f) + 1);
	  else
	    pl[1].x -= pl[1].hmm;
	  if (pl[1].x > 160)
	    pl[1].x = -68;
	  else if (pl[1].x < -68)
	    pl[1].x = 160;

	  /* Missiles */
	  for (i = 0; i < 3; i++)
	    {
	      if (ml[i].hmm & 0x08)
		ml[i].x += ((ml[i].hmm ^ 0x0f) + 1);
	      else
		ml[i].x -= ml[i].hmm;
	      if (ml[i].x > 160)
		ml[i].x = -68;
	      else if (ml[i].x < -68)
		ml[i].x = 160;
	    }
	  break;
	case HMCLR:
	  pl[0].hmm = 0;
	  pl[1].hmm = 0;
	  for (i = 0; i < 3; i++)
	    ml[i].hmm = 0;
	  break;
	case CXCLR:
	  col_state=0;
	  break;
	}
    }
  else
    {
      switch (a & 0x2ff)
	{
	  /* RIOT I/O ports */
	case SWCHA:
	  riotWrite[SWCHA] = b;
	  break;
	case SWACNT:
	  riotWrite[SWACNT] = b;
	  break;
	case SWCHB:
	case SWBCNT:
	  /* Do nothing */
	  break;

	  /* Timer ports */
	case TIM1T:
	  set_timer (0, b, clkcount);
	  break;
	case TIM8T:
	  set_timer (3, b, clkcount);
	  break;
	case TIM64T:
	  set_timer (6, b, clkcount);
	  break;
	case T1024T:
	  set_timer (10, b, clkcount);
	  break;
	default:
	  //printf ("Unknown write %x\n", a);
	  //show ();
	  break;
	}
    }
}


/* Decoded read from memory */
/* a: address to read */
/* returns: byte value from address a */
BYTE
decRead (ADDRESS a)
{
  BYTE res = 65;

  if (a & 0x1000)
    {
      a = a & 0xfff;
      if (base_opts.bank != 0)
	res= bank_switch_read (a);
      else
	res = theRom[a];
    }
  else if ((a & 0x280) == 0x80)
    {
      res = theRam[a & 0x7f];
    }
  else if (!(a & 0x80))
    {
      switch (a & 0x0f)
	{
	  /* TIA */
	case CXM0P:
	  res = (col_state & CXM0P_MASK) << 6;
	  break;
	case CXM1P:
	  res = (col_state & CXM1P_MASK) << 4;
	  break;
	case CXP0FB:
	  res = (col_state & CXP0FB_MASK) << 2;
	  break;
	case CXP1FB:
	  res = (col_state & CXP1FB_MASK);
	  break;
	case CXM0FB:
	  res = (col_state & CXM0FB_MASK) >> 2;
	  break;
	case CXM1FB:
	  res = (col_state & CXM1FB_MASK) >> 4;
	  break;
	case CXBLPF:
	  res = (col_state & CXBLPF_MASK) >> 5;
	  break;
	case CXPPMM:
	  res = (col_state & CXPPMM_MASK) >> 7;
	  break;
	case INPT0:
	  if (base_opts.lcon== PADDLE)
	    {
	      tiaRead[INPT0] = do_paddle (0);
	    }
	  else if (base_opts.lcon== KEYPAD)
	    tiaRead[INPT0] = do_keypad (0, 0);
	  res = tiaRead[INPT0];
	  break;
	case INPT1:
	  if (base_opts.lcon== PADDLE)
	    {
	      tiaRead[INPT1] = do_paddle (1);
	    }
	  if (base_opts.lcon== KEYPAD)
	    tiaRead[INPT1]=do_keypad (0, 1);
	  res = tiaRead[INPT1];
	  break;
	case INPT2:
	  if (base_opts.rcon == KEYPAD)
	    tiaRead[INPT1]=do_keypad (1, 0);
	  res = tiaRead[INPT2];
	  break;
	case INPT3:
	  if (base_opts.rcon == KEYPAD)
	     tiaRead[INPT3]=do_keypad ( 1, 1);
	  res = tiaRead[INPT3];
	  break;
	case INPT4:
	  switch (base_opts.lcon)
	    {
	    case KEYPAD:
	      tiaRead[INPT4]=do_keypad ( 0, 2);
	      break;
	    case STICK:
		case PADDLE:
			keytrig();
			res=tiaRead[INPT4];
	      break;
	    }
	  res =tiaRead[INPT4];
	  break;
	case INPT5:
	  switch (base_opts.rcon)
	    {
	    case KEYPAD:
	      tiaRead[INPT5]=do_keypad (1, 2);
	      break;
	    case STICK:
		case PADDLE:
			keytrig();
			res=tiaRead[INPT5];
	      break;
	    }
	  res = tiaRead[INPT5];
	  break;
	case 0x0e:
	case 0x0f:
	  res = 0x0f;
	  /* RAM, mapped to page 0 and 1 */
	}
    }
  else
    {
      switch (a & 0x2ff)
	{
	  /* Timer output */
	case INTIM:
	case 0x285:
	case 0x286:
	case TIM1T:
	case TIM8T:
	case TIM64T:
	case T1024T:
	  res = do_timer (clkcount);
	  /*printf("Timer is %d res is %d\n", res, timer_res); */
	  break;
	case SWCHA:
	  switch (base_opts.lcon)
	    {
	    case PADDLE:
	      if (base_opts.lcon == PADDLE)
		{
				keytrig();
				res=tiaRead[INPT4];
		}
	      else if (base_opts.rcon == PADDLE)
		{
				keytrig();
				res=tiaRead[INPT4];
		}
	      break;
	    case STICK:
				keyjoy();
				res=riotRead[SWCHA];
	      break;
	    }
	  res = riotRead[SWCHA];
	  break;
	  /* Switch B is hardwired to input */
	case SWCHB:
	case SWCHB + 0x100:
	  keycons ();
	  res = riotRead[SWCHB];
	  break;
	default:
	  //printf ("Unknown read 0x%x\n", a & 0x2ff);
	  //show ();
	  res = 65;
	  break;
	}
    }
  return res;
}

