/*
** nester - NES emulator
** Copyright (C) 2000  Darren Ranalli
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program 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 
** Library General Public License for more details.  To obtain a 
** copy of the GNU Library General Public License, write to the Free 
** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
** Any permitted reproduction of these routines, in whole or in part,
** must bear this legend.
*/

/* 
** NesterDC by Ken Friece
** Any changes that I made to the original source are 
** marked with in the following way:
** KF DD/MM/YYYY <description of the change> 
*/

#ifndef _NES_H_
#define _NES_H_

#include <stdio.h>

#include "types.h"
#include "nes_6502.h"
#include "mapper/nes_mapper_decl.h"
#include "nes_rom.h"
#include "nes_ppu.h"
#include "nes_apu_wrapper.h"
#include "nes_pad.h"
#include "nes_device_cclimber.h"
#include "nes_device_gun.h"
#include "nes_device_kbd.h"
#include "nes_device_hypershot.h"
#include "nes_device_mic.h"
#include "nes_device_paddle.h"
#include "nes_device_mahjong.h"
#include "nnnesterj_cheat.h"

class NES_SNSS;

class NES
{
  // friend classes
  friend class NES_6502;
  friend class NES_PPU;
  friend class NES_APU;
  friend class NES_SNSS;
  friend class NES_mapper;
  friend class NES_device_cclimber;
  friend class NES_device_gun;
  friend class NES_device_hypershot;
  friend class NES_device_mic;
  friend class NES_device_paddle;
  friend class NES_device_mahjong;
#include "mapper/nes_mapper_friend_decl.h"
  
public:
  NES(sound_mgr* _sound_mgr);
  bool8 initialize (const char *filename);
  ~NES();
  
  void new_snd_mgr(sound_mgr* _sound_mgr);
  sound_mgr* get_snd_mgr() { return snd_mgr; }
  
  NES_pad* get_pad(int num) { return pad + num; }
  NES_device_cclimber NES_cclimber;
  NES_device_gun NES_gun;
  NES_device_kbd NES_kbd;
  NES_device_hypershot NES_hypershot;
  NES_device_paddle NES_paddle;
  NES_device_mahjong NES_mahjong;
  
  void emulate_frame (uint16 *screen);
  void emulate_frame_skip (); 
  
  void reset();
  void softreset();
  
  void set_exsound_enable(bool8 exsound_enable) { apu->set_exsound_enable(exsound_enable); }
  
  uint8 get_mapper_num() { return ROM->get_mapper_num (); }
  const char* getROMname();
  const char* getROMpath();
  const char* getROMcrc() {
    return ROM->GetRomCrc(); 
  }
  NES_ROM::rom_monitor_type get_monitor_type () { return ROM->get_monitor_type (); }
  
  bool8 loadState(const char* fn);
  bool8 saveState(const char* fn);
  
  void freeze();
  void thaw();
  bool8 frozen();
  
#ifndef __DREAMCAST__
  /* for draw_pattern_table */
  uint8 *ppu_vram_banks(int bank) { return ppu->PPU_VRAM_banks[bank]; } 
  uint16 *ppu_bg_pal_color() { return ppu->bg_pal_color; }
#endif
  
  enum {
    NES_NUM_VBLANK_LINES = 20,
    NES_NUM_FRAME_LINES = 240,

    // these are 0-based, and actions occur at start of line
    NES_NMI_LINE = 241,
    NES_VBLANK_FLAG_SET_LINE = 241,
    NES_VBLANK_FLAG_RESET_LINE = 261,
    NES_SPRITE0_FLAG_RESET_LINE = 261,

    NES_COLOR_BASE = 0x40, // NES palette is set starting at color 0x40 (64)
    NES_NUM_COLORS = 64    // 64 colors in the NES palette
  };
  
  uint32 crc32()  { return ROM->crc32();  }
  uint32 fds_id() { return ROM->fds_id(); }
  NES_ROM::rom_image_type get_rom_image_type () { return ROM->get_rom_image_type (); }
  
  float CYCLES_PER_LINE;
  float CYCLES_BEFORE_NMI;
  bool8 BANKSWITCH_PER_TILE;
  bool8 DPCM_IRQ;
  
  // Disk System
  uint8 GetDiskData(uint32 pt);
  uint8 GetDiskSideNum();
  uint8 GetDiskSide();
  void SetDiskSide(uint8 side);
  uint8 DiskAccessed();
  char disksys_rom_filename[256];
  
  /* External Device */
  typedef enum {
    EX_NONE,
    EX_FAMILY_KEYBOARD,
    EX_OPTICAL_GUN,
    EX_SPACE_SHADOW_GUN,
    EX_HYPER_SHOT,
    EX_CRAZY_CLIMBER,
    EX_ARKANOID_PADDLE,
    EX_MAHJONG,
    EX_LAST_ITEM,
  } nes_external_device_type_t;
  
  nes_external_device_type_t ex_controller_type;
  const char *get_external_device_name() {
    const char *names[] = {
      "NONE",
      "Family Keyboard",
      "Optical Gun",
      "Space Shadow Gun",
      "Hyper Shot",
      "Crazy Climber", 
      "Arkanoid Paddle", 
      "Mahjong", 
      "",
    };
    return names[ex_controller_type];
  }
  
  void change_external_device() {
    ex_controller_type = nes_external_device_type_t(ex_controller_type + 1);
    if (ex_controller_type == EX_LAST_ITEM)
      ex_controller_type = EX_NONE;
  }
  
  // Game Genie
  void clear_genie ();
  void load_genie (const char *genie);
  void set_genie ();
  uint8 GetGenieCodeNum() { return genie_num; }
  uint32 GetGenieCode(uint8 num) { return genie_code[num]; }
  
  // VS
  uint8 use_vs_palette;
  int8 pad_swap;
  int8 vstopgun_ppu;
  uint8 vstopgun_value;
  
  /* NNNesterJ cheat code */
  static const int NES_NNNESTERJ_CHEATINFO_SIZE = 100;
  nes_nnnesterj_cheatinfo_t nes_nnnesterj_cheatinfo[NES_NNNESTERJ_CHEATINFO_SIZE];
  int nes_nnnesterj_cheatinfo_size;
  int nes_nnnesterj_cheatinfo_alive_num;
  
  /* NNNesterJ cheat code */
  void load_nnnesterj_cheat (const char *filename);
  void set_nnnesterj_cheat_alive_num ();
  void apply_nnnesterj_cheat();
  
  // frame-IRQ
  uint8 frame_irq_enabled;
  uint8 frame_irq_disenabled;
  
  // these are called by the CPU
  uint8 MemoryRead(uint32 addr);
  void  MemoryWrite(uint32 addr, uint8 data);
  
  // SaveRAM control
  void  WriteSaveRAM(uint32 addr, uint8 data) { SaveRAM[addr] = data;}
  uint8 ReadSaveRAM(uint32 addr) { return SaveRAM[addr]; }
  
  // file stuff
  bool8 has_sram () { return ROM->has_save_RAM (); }
  const uint8 *get_SaveRAM () { return SaveRAM; }
  void set_SaveRAM(const uint8 *buffer, int size);
  bool8 is_invalid_sram ();
  
  /* change 0x2000 to 0x10000 for mapper 005 */
  static const int sram_buflen = 0x10000; 
  static const int fds_save_buflen = 16 + (4 * 65500) * 2;
  static const int snss_buflen = sizeof(SNSS_FILE) + 0x8000 + 0x40000 * 2;
  /* buf length must be fds_save_buflen */
  void make_savedata(uint8 *buf);
  void restore_savedata(uint8 *buf);
  NES_PPU* ppu;
  
  void SetCoin(bool8 b)
  {
	  if (b)
	  	pad_bits[5] = 4;
	  else
	  	pad_bits[5] = 0;
  }
  
  NES_ROM* getNES_ROM() { return ROM; }
  
protected:
  sound_mgr* snd_mgr;
  NES_6502* cpu;
  //NES_PPU* ppu;
  NES_APU* apu;
  NES_ROM* ROM;
  NES_mapper* mapper;
  
  bool8 is_frozen;
  
  float  ideal_cycle_count;   // number of cycles that should have executed so far
  uint32 emulated_cycle_count;  // number of cycles that have executed so far
  
  // internal memory
  uint8 RAM[0x800];
  uint8 SaveRAM[sram_buflen];
  
  // joypad stuff
  NES_pad pad[4];
  bool8 pad_strobe;
  uint32 pad_bits[6];
  
  // Disk System
  uint8 disk_side_flag;
  
  bool8 loadROM(const char* fn); 
  void freeROM();
  
  // Game Genie
  uint8 genie_num;
  uint32 genie_code[256];
  
  // internal read/write functions
  uint8 ReadRAM(uint32 addr);
  void  WriteRAM(uint32 addr, uint8 data);
  
  uint8 ReadLowRegs(uint32 addr);
  void  WriteLowRegs(uint32 addr, uint8 data);
  
  uint8 ReadHighRegs(uint32 addr);
  void  WriteHighRegs(uint32 addr, uint8 data);
  
  void  emulate_CPU_cycles(float num_cycles);
  void  trim_cycle_counts();
  
private:
  NES_SNSS* snss;
  
  /* helper for emulate_frame */
  void emulate_frame_skip_line_generic (int i);
  void emulate_frame_skip_line_mmc5 (int i);
  void emulate_frame_skip_line_mmc2 (int i);
  void emulate_frame_skip_line_bankswitch_per_tile (int i);
  
  void emulate_frame_line_generic (int i, uint16 *cur_line);
  void emulate_frame_line_mmc5 (int i, uint16 *cur_line);
  void emulate_frame_line_mmc2 (int i, uint16 *cur_line);
  void emulate_frame_line_bankswitch_per_tile (int i, uint16 *cur_line);
};

#endif

