/*
** 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
** This routine was so different than the original, that I included 
** diff output at the bottom of the routine. The orginal is also called
** nes.cpp.original
*/

#include "libc.h"
#include "stdlib.h"
//#include <string.h>
#include <math.h>
#include "NES.h"
#include "NES_screen_mgr.h"
#include "NES_ROM.h"
#include "NES_PPU.h"
#include "pixmap.h"
#include "SNSS.h"
#include "dream.h"
#include "vmu.h"

#include "debug.h"

const uint8 NES::NES_preset_palette[NES_NUM_COLORS][3] = 
{
// include the NES palette
#include "NES_pal.h"
};

#define SCREEN_BUFFER_1 76800 // 320 * 240
#define SCREEN_BUFFER_2 153600 // 320 * 240 * 2

//const float NES::CYCLES_PER_LINE = (float)113.6;
//const float NES::CYCLES_PER_LINE = (float)113.852;
//const float NES::CYCLES_PER_LINE = (float)113.75;
const float NES::CYCLES_PER_LINE = (float)113.6666666666666666666;

NES::NES(const char* ROM_name, NES_screen_mgr* _screen_mgr, sound_mgr* _sound_mgr)
{
  scr_mgr = _screen_mgr;
  snd_mgr = _sound_mgr;

  scr_mgr->setParentNES(this);

  cpu = (NES_6502 *)NULL;
  ppu = (NES_PPU *)NULL;
  apu = (NES_APU *)NULL;

  this->invalid_mapper = FALSE;
  this->screen_buffer = 0;

  try {

    cpu = new NES_6502(this);
    if(!cpu) {
		dc_print ("error allocating cpu");
		throw "error allocating cpu";
	}

    ppu = new NES_PPU(this);
    if(!ppu) {
		dc_print ("error allocating ppu");	
		throw "error allocating ppu";
	}
 
    apu = new NES_APU(this);
    if(!apu) {
		dc_print ("error allocating apu");	
		throw "error allocating apu";
	}

    if (loadROM(ROM_name)) {
		this->invalid_mapper = TRUE;
	}

  } catch(...) {
    if(cpu) delete cpu;
    if(ppu) delete ppu;
    if(apu) delete apu;
    throw;
  }

  calculate_palette();
 
  pad1 = (NES_pad *)NULL;
  pad2 = (NES_pad *)NULL;

  is_frozen = FALSE;
  //dc_print ("DONE initializing NES");
}

NES::~NES()
{
//	dc_print ("free rom");
  freeROM();

//  dc_print ("delete cpu");
  if(cpu) delete cpu;
//  dc_print ("delete ppu");
  if(ppu) delete ppu;
//  dc_print ("delete apu");
  if(apu) delete apu;
}

void NES::new_snd_mgr(sound_mgr* _sound_mgr)
{
  snd_mgr = _sound_mgr;
  apu->snd_mgr_changed();
}

uint8 NES::loadROM(const char* fn)
{
  //dc_print ("Loading ROM ...");

  ROM = new NES_ROM(fn);

  // set up the mapper
  mapper = GetMapper(this, ROM);
  if(!mapper)
  {

    //delete ROM;
    //ROM = (NES_ROM *)NULL;
	dc_print ("UNSUPPORTED MAPPER");
	timer_sleep (5000);
	return 1;
    //throw "unsupported mapper";
  }
 
  switch(ROM->get_mirroring())
  {
    case NES_PPU::MIRROR_HORIZ:
     // LOG("H ");
      break;
    case NES_PPU::MIRROR_VERT:
     // LOG("V ");
      break;
    case NES_PPU::MIRROR_FOUR_SCREEN:
   //   LOG("F ");
      break;
  }

  if(ROM->has_save_RAM())
  {
   // LOG("S ");
  }
  if(ROM->has_trainer())
  {
  //  LOG("T ");
  }

  //LOG(16*ROM->get_num_16k_ROM_banks() << "K/" << 8*ROM->get_num_8k_VROM_banks() << "K " << endl);

 // Load_SaveRAM();

  reset();

  return 0;
	
  //dc_print ("Starting emulation");
  //LOG("Starting emulation...\n");
}

void NES::freeROM()
{
//	dc_print ("In free rom");
 // Save_SaveRAM();

//  LOG("Freeing ROM...");
  if(ROM)
  {
    delete ROM;
    ROM = (NES_ROM *)NULL;
  }
  if(mapper)
  {
	//dc_print ("mapper");
    delete mapper;
    mapper = (NES_mapper *)NULL;
	//dc_print ("done");
  }

  //scr_mgr->clear(0x00);
  //scr_mgr->blt();
  
}

const char* NES::getROMname()
{
  return ROM->GetRomName();
}

const char* NES::getROMpath()
{
  return ROM->GetRomPath();
}

void NES::reset()
{

 // Save_SaveRAM();

  // RAM
  memset(RAM, 0x00, sizeof(RAM));

  // SaveRAM
  Load_SaveRAM();

 // dc_print ("setting up cpu");
  // set up CPU
  {
    NES_6502::Context context;

    memset((void*)&context, 0x00, sizeof(context));
    cpu->GetContext(&context);

    context.mem_page[0] = RAM;
    context.mem_page[3] = SaveRAM;

    cpu->SetContext(&context);
  }

  // set up the trainer if present
  if(ROM->has_trainer())
  {
   // LOG("  Setting trainer...");
    // trainer is located at 0x7000; SaveRAM is 0x2000 bytes at 0x6000
    memcpy(&SaveRAM[0x1000], ROM->get_trainer(), NES_ROM::TRAINER_LEN);
   // LOG("OK\n");
  }

 // dc_print ("PPU reset");
  //LOG("  PPU reset...");
  // reset the PPU
   ppu->reset();
 // LOG("OK\n");

 // dc_print ("APU reset");
 // LOG("  APU reset...");
  // reset the APU
  apu->reset();
  //LOG("OK\n");

  if(mapper)
  {
  //  LOG("  mapper reset...");
    // reset the mapper
//	  dc_print ("resetting mapper");
    mapper->Reset();
 //   LOG("OK\n");
  }

  //dc_print ("CPU reset");
 // LOG("  CPU reset...");
  // reset the CPU
  cpu->Reset();
  //LOG("OK\n");

  ideal_cycle_count  = 0.0;
  emulated_cycle_count = 0;

  pad_strobe = FALSE;
  pad1_bits = 0x00;
  pad2_bits = 0x00;
 // dc_print ("finished reseting nes!!!");
 // dc_print ("done resetting NES!!!");
}

void NES::emulate_frame_skip(uint8 sound_on) {

  trim_cycle_counts();

  // do frame
  ppu->start_frame();

  // LINES 0-239
  for(int i = 0; i < NES_NUM_FRAME_LINES; i++)
  {
    // do one line's worth of CPU cycles
    emulate_CPU_cycles(CYCLES_PER_LINE);
	//dc_print ("emulate_CPU_cycles");
    mapper->HSync(i);
	//dc_print ("mapper HSync");

  
    ppu->do_scanline_and_dont_draw();

  }

  ppu->end_frame();

  for(int i = 240; i <= 261; i++)
  {
    if(i == 241)
    {
      // do v-blank
      ppu->start_vblank();
	//  dc_print ("ppu start vblank");
      mapper->VSync();
	//  dc_print ("mapper VSync");
    }
    else if(i == 261)
    {
      ppu->end_vblank();
	//  dc_print ("end vblank");
    }

    if(i == 241)
    {
      // 1 instruction between vblank flag and NMI
      emulate_CPU_cycles(1.0);
	 // dc_print ("emulate CPU cycles");
      if(ppu->NMI_enabled()) cpu->DoNMI();
      emulate_CPU_cycles((float)CYCLES_PER_LINE-(float)1.0);
	 // dc_print ("emulate cpu cycles");
      mapper->HSync(i);
	//  dc_print ("mapper HSynch");
      continue;
    }

    emulate_CPU_cycles(CYCLES_PER_LINE);
	//dc_print ("emu CPU bottom");
    mapper->HSync(i);
	//dc_print ("done emulate frame");
  }

  if (sound_on) {
	apu->DoFrame();
  }

}

void/*boolean*/ NES::emulate_frame(uint8 sound_on)
{
  // set up direct pointer into video memory
  uint16 *cur_line = vram_s + 24;
  uint16 *start_buffer = vram_s;

  trim_cycle_counts();
 //dc_print ("trim cycle counts");

  // do frame
  ppu->start_frame();
 //dc_print ("start emulate frame");

  // triple buffering works well. 
  // single and double buffering are slow as shit
  if (screen_buffer == 0) {
		screen_buffer++;
	}
  else if (screen_buffer == 1) {
		cur_line += SCREEN_BUFFER_1;
		screen_buffer++;
		start_buffer += SCREEN_BUFFER_1;
  }
  else {
		cur_line += SCREEN_BUFFER_2;
		screen_buffer = 0;
		start_buffer += SCREEN_BUFFER_2;
  }
 
  // LINES 0-239
  for(int i = 0; i < NES_NUM_FRAME_LINES; i++)
  {
    // do one line's worth of CPU cycles
    emulate_CPU_cycles(CYCLES_PER_LINE);
	//dc_print ("emulate_CPU_cycles");
    mapper->HSync(i);

    ppu->do_scanline_and_draw(cur_line);

	// rendered resolution = 272*240
	// NES resolution = 256*240, so there are 8 pixels of 
	// garbage on each side of the screen

	// clip 8 pixels 
	for (int i=0; i<2; i++) {
		*cur_line++ = 0;
		*cur_line++ = 0;
		*cur_line++ = 0;
		*cur_line++ = 0;
	}

	cur_line += 256;

	// clip 8 pixels
	for (int i=0; i<2; i++) {
		*cur_line++ = 0;
		*cur_line++ = 0;
		*cur_line++ = 0;
		*cur_line++ = 0;
	}


	// update pointer for the next frame
	cur_line += 48;
	  
  }

  ppu->end_frame();

  for(int i = 240; i <= 261; i++)
  {
    if(i == 241)
    {
      // do v-blank
      ppu->start_vblank();
	//  dc_print ("ppu start vblank");
      mapper->VSync();
	//  dc_print ("mapper VSync");
    }
    else if(i == 261)
    {
      ppu->end_vblank();
	//  dc_print ("end vblank");
    }

    if(i == 241)
    {
      // 1 instruction between vblank flag and NMI
      emulate_CPU_cycles(1.0);
	 // dc_print ("emulate CPU cycles");
      if(ppu->NMI_enabled()) cpu->DoNMI();
      emulate_CPU_cycles((float)CYCLES_PER_LINE-(float)1.0);
	 // dc_print ("emulate cpu cycles");
      mapper->HSync(i);
	//  dc_print ("mapper HSynch");
      continue;
    }

    emulate_CPU_cycles(CYCLES_PER_LINE);
	//dc_print ("emu CPU bottom");
    mapper->HSync(i);
	//dc_print ("done emulate frame");
  }

  if (sound_on) {
	apu->DoFrame();
  }

  // set the vram pointer to the frame that was just rendered
  vid_set_start ((uint32)start_buffer);

}

void NES::freeze()
{
  apu->freeze();
  is_frozen = TRUE;
}

void NES::thaw()
{
  apu->thaw();
  is_frozen = FALSE;
}

boolean NES::frozen()
{
  return is_frozen;
}

uint8 NES::MemoryRead(uint32 addr)
{
//  LOG("Read " << HEX(addr,4) << endl);

  if(addr < 0x2000) // RAM
  {
    return ReadRAM(addr);
  }
  else if(addr < 0x4000) // low registers
  {
    return ReadLowRegs(addr);
  }
  else if(addr < 0x4018) // high registers
  {
    return ReadHighRegs(addr);
  }
  else if(addr < 0x6000) // mapper low
  {
//    LOG("MAPPER LOW READ: " << HEX(addr,4) << endl);
    return((uint8)(addr >> 8)); // what's good for conte is good for me
  }
  else // save RAM, or ROM (mapper 40)
  {
    return cpu->GetByte(addr);
  }
}

void NES::MemoryWrite(uint32 addr, uint8 data)
{
//  LOG("Write " << HEX(addr,4) << " " << HEX(data,2) << endl);

  if(addr < 0x2000) // RAM
  {
    WriteRAM(addr, data);
  }
  else if(addr < 0x4000) // low registers
  {
    WriteLowRegs(addr, data);
  }
  else if(addr < 0x4018) // high registers
  {
    WriteHighRegs(addr, data);
  }
  else if(addr < 0x6000) // mapper low
  {
    mapper->MemoryWriteLow(addr, data);
  }
  else if(addr < 0x8000) // save RAM
  {
    SaveRAM[addr - 0x6000] = data;
    mapper->MemoryWriteSaveRAM(addr, data);
  }
  else // mapper
  {
    mapper->MemoryWrite(addr, data);
  }
}


uint8 NES::ReadRAM(uint32 addr)
{
  return RAM[addr & 0x7FF];
}

void NES::WriteRAM(uint32 addr, uint8 data)
{
  RAM[addr & 0x7FF] = data;
}


uint8 NES::ReadLowRegs(uint32 addr)
{
  return ppu->ReadLowRegs(addr & 0xE007);
}

void NES::WriteLowRegs(uint32 addr, uint8 data)
{
  ppu->WriteLowRegs(addr & 0xE007, data);
}


uint8 NES::ReadHighRegs(uint32 addr)
{
  if(addr == 0x4014) // SPR-RAM DMA
  {
 //   LOG("Read from SPR-RAM DMA reg??" << endl);
    return ppu->Read0x4014();
  }
  else if(addr < 0x4016) // APU
  {
//    LOG("APU READ:" << HEX(addr,4) << endl);
    return apu->Read(addr);
  }
  else // joypad regs
  {
    uint8 retval;

    if(addr == 0x4016)
    {
      // joypad 1
      retval = pad1_bits & 0x01;
      pad1_bits >>= 1;
    }
    else
    {
      // joypad 2
      retval = pad2_bits & 0x01;
      pad2_bits >>= 1;
    }
    return retval;
  }
}

void NES::WriteHighRegs(uint32 addr, uint8 data)
{
  if(addr == 0x4014) // SPR-RAM DMA
  {
    ppu->Write0x4014(data);
    // sprite DMA takes XXX cycles
    cpu->SetDMA(514);
  }
  else if(addr < 0x4016) // APU
  {
//    LOG("APU WRITE" << endl);
    apu->Write(addr, data);
  }
  else // joypad regs
  {
    // bit 0 == joypad strobe
    if(data & 0x01)
    {
      pad_strobe = TRUE;
    }
    else
    {
      if(pad_strobe)
      {
        pad_strobe = FALSE;
        // get input states
        if(pad1)
        {
          pad1_bits = pad1->get_inp_state();
        }
        if(pad2) pad2_bits = pad2->get_inp_state();
      }
    }
  }
}

void NES::emulate_CPU_cycles(float num_cycles)
{
  uint32 cycle_deficit;

  ideal_cycle_count += num_cycles;
  cycle_deficit = ((uint32)ideal_cycle_count) - emulated_cycle_count;
  if(cycle_deficit > 0)
  {
    emulated_cycle_count += cpu->Execute(cycle_deficit);
  }
}

// call every once in a while to avoid cycle count overflow
void NES::trim_cycle_counts()
{
  uint32 trim_amount;

  trim_amount = (uint32)floor(ideal_cycle_count);
  if(trim_amount > emulated_cycle_count) trim_amount = emulated_cycle_count;

  ideal_cycle_count  -= (float)trim_amount;
  emulated_cycle_count -= trim_amount;
}

void NES::Save_SaveRAM()
{
//	dc_print ("before has_save_RAM");
  // does the ROM use save ram?
  //if(!ROM->has_save_RAM()) return;

  uint8 addr = maple_vmu_addr(FIRST_DEVICE);
  if (!addr) return;

  uint32 i=0;

  // has anything been written to Save RAM?
  for(i = 0; i < sizeof(SaveRAM); i++)
  {
    if(SaveRAM[i] != 0x00) break;
  }

  if(i < sizeof(SaveRAM))
  {
	uint8 temp_mem[200];
	uint8 *free_mem = (uint8 *)temp_mem;
	uint8 *temp_ptr = free_mem;

	for (int i=0; i<200; i++) {
		*temp_ptr++ = '0';
	}

	//uint16 crc = calcCRC (SaveRAM, 0x2000);
	//dc_print ("the SaveRAM crc is");
	//print_uint32 ((uint32)crc);
	//timer_sleep (3000);

	//memset (free_mem, 0x00, sizeof (free_mem));
	
	//uint32 free_blocks = vmu_read (free_mem);

	uint16 block_number = check_vmu_for_game(ROM->GetRomName());

	// found the game in SaveRAM
	if (block_number != 0) {

		dc_print ("Saving Game...");

		uint8 temp_buf[9216];
		uint8 *vmu_file = (uint8 *)temp_buf;
		uint8 free_blocks = check_free_blocks (free_mem);

		create_vmu_header (vmu_file, ROM->GetRomName(), "NesterDC SaveRam File", 8576);

		for (int i=640; i<1024; i++) {
			vmu_file[i] = 0;
		}

		for (int i=0; i<8192; i++) {
			vmu_file[i + 1024] = SaveRAM[i];
		}

		do_crc (vmu_file, 9216);

		upload_data_by_block (vmu_file, (uint32)block_number, 18);

	}
	else {
		uint8 free_blocks = check_free_blocks (free_mem);

		if (free_blocks < 18) {
			dc_print ("You need 18 free blocks to Save");
			timer_sleep (5000);
			return;
		}

		dc_print ("Saving Game...");

		uint8 temp_buf[9216];
		uint8 *vmu_file = (uint8 *)temp_buf;

		create_vmu_header (vmu_file, ROM->GetRomName(), "NesterDC SaveRam File", 8576);

		// filler
		for (int i=640; i<1024; i++) {
			vmu_file[i] = 0;
		}

		for (int i=0; i<8192; i++) {
			vmu_file[i + 1024] = SaveRAM[i];
		}

		do_crc (vmu_file, 9216);

		uint8 first_free_block = find_first_free_block(free_mem);

		update_fat (free_mem, 18);

		upload_vmu_data (vmu_file, free_mem, 18);

		update_vmu_dir (first_free_block, ROM->GetRomName(), 18);
	}

  }
  else {
		//dc_print ("nothing has been written to SaveRAM");
  }
}

void NES::Load_SaveRAM()
{

	uint8 addr = maple_vmu_addr(FIRST_DEVICE);
	if (!addr) return;

	unsigned char *temp_ptr = SaveRAM;
	for (int i=0; i<0x2000; i++) {
		*temp_ptr++ = 0x00;
	}

	uint16 block_number = check_vmu_for_game(ROM->GetRomName());
	//print_uint32 ((uint32)block_number);
	//timer_sleep (3000);

	if (block_number == 0) return;

	dc_print ("Loading Game...");

	block_number = get_next_block_from_fat (block_number);
	//print_uint32 ((uint32)block_number);
	//timer_sleep (3000);
	
	for (int i=0; i<16; i++) {

		block_number = get_next_block_from_fat (block_number);
	//	print_uint32 ((uint32)block_number);
	//	timer_sleep (3000);
	
		if (vmu_block_read(addr, block_number, SaveRAM + (i*512)) != 0) {
			dc_print ("error loading block number");
			print_uint32 ((uint32)block_number);
			dc_sleep (3000);
			return;
		}
		
		timer_sleep (50);
	}

}

boolean NES::loadState(const char* fn)
{
  return LoadSNSS(fn, this);
}

boolean NES::saveState(const char* fn)
{
  return SaveSNSS(fn, this);
}

void NES::calculate_palette()
{
  //NESTER_settings.nes.graphics.calculate_palette = true;
  //NESTER_settings.nes.graphics.tint = 255;
  //NESTER_settings.nes.graphics.hue = 128; 

  if(NESTER_settings.nes.graphics.calculate_palette)
  {
//	dc_print ("in calculate palette");
    int x,z;
    float tint = ((float)NESTER_settings.nes.graphics.tint) / 256.0f;
    float hue = 332.0f + (((float)NESTER_settings.nes.graphics.hue - (float)0x80) * (20.0f / 256.0f));
    float s,y;
    int cols[16] = {0,240,210,180,150,120,90,60,30,0,330,300,270,0,0,0};
    float theta;
    float br1[4] = {0.5f, 0.75f, 1.0f, 1.0f};
    float br2[4] = {0.29f, 0.45f, 0.73f, 0.9f};
    float br3[4] = {0.0f, 0.24f, 0.47f, 0.77f};
    float r,g,b;

    for(x = 0; x <= 3; x++)
    {
      for(z = 0; z <= 15; z++)
      {
        s = tint;
        y = br2[x];
        if(z == 0)
        {
          s = 0;
          y = br1[x];
        }
        else if(z == 13)
        {
          s = 0;
          y = br3[x];
        }
        else if((z == 14) || (z == 15))
        {
          s = 0;
          y = 0;
        }

        theta = 3.14159265f * (((float)(cols[z] + hue)) / 180.0f);

        r = y + (s * (float)sin(theta));
        g = y - ((27.0f / 53.0f) * s * (float)sin(theta)) + ((10.0f / 53.0f) * s * (float)cos(theta));
        b = y - (s * (float)cos(theta));

        r = r * 256.0f;
        g = g * 256.0f;
        b = b * 256.0f;

        if(r > 255.0f) r = 255.0f;
        if(g > 255.0f) g = 255.0f;
        if(b > 255.0f) b = 255.0f;

        if(r < 0.0f) r = 0.0;
        if(g < 0.0f) g = 0.0;
        if(b < 0.0f) b = 0.0;

        NES_RGB_pal[(x*16) + z][0] = (uint8)r;
        NES_RGB_pal[(x*16) + z][1] = (uint8)g;
        NES_RGB_pal[(x*16) + z][2] = (uint8)b;
      }
    }
  }
  else
  {
//	dc_print ("load preset palette");
    memcpy(NES_RGB_pal, NES_preset_palette, sizeof(NES_RGB_pal));
  }

  NESTER_settings.nes.graphics.black_and_white = false;
  if(NESTER_settings.nes.graphics.black_and_white)
  {
    int i;

    for(i = 0; i < NES_NUM_COLORS; i++)
    {
      uint8 Y;
      Y = (uint8)(((float)NES_RGB_pal[i][0] * 0.299) +
                  ((float)NES_RGB_pal[i][1] * 0.587) +
                  ((float)NES_RGB_pal[i][2] * 0.114));
      NES_RGB_pal[i][0] = Y;
      NES_RGB_pal[i][1] = Y;
      NES_RGB_pal[i][2] = Y;
    }
  }
}

/*

diff output
key:
< NesterDC code
> nester code

21,30c21
< 
< #include "libc.h"
< #include "stdlib.h"
< //#include <string.h>
---
> #include <string.h>
38,39d28
< #include "dream.h"
< #include "vmu.h"
49,51d37
< #define SCREEN_BUFFER_1 76800 // 320 * 240
< #define SCREEN_BUFFER_2 153600 // 320 * 240 * 2
< 
64,66c50
<   cpu = (NES_6502 *)NULL;
<   ppu = (NES_PPU *)NULL;
<   apu = (NES_APU *)NULL;
---
>   LOG("nester - NES emulator by Darren Ranalli, (c) 2000\n");
68,69c52,54
<   this->invalid_mapper = FALSE;
<   this->screen_buffer = 0;
---
>   cpu = NULL;
>   ppu = NULL;
>   apu = NULL;
72c57
< 
---
>     LOG("Creating NES CPU...");
74,77c59,60
<     if(!cpu) {
< 		dc_print ("error allocating cpu");
< 		throw "error allocating cpu";
< 	}
---
>     if(!cpu) throw "error allocating cpu";
>     LOG("Done.\n");
78a62
>     LOG("Creating NES PPU...");
80,83c64,65
<     if(!ppu) {
< 		dc_print ("error allocating ppu");	
< 		throw "error allocating ppu";
< 	}
---
>     if(!ppu) throw "error allocating ppu";
>     LOG("Done.\n");
84a67
>     LOG("Creating NES APU...");
86,93c69,70
<     if(!apu) {
< 		dc_print ("error allocating apu");	
< 		throw "error allocating apu";
< 	}
< 
<     if (loadROM(ROM_name)) {
< 		this->invalid_mapper = TRUE;
< 	}
---
>     if(!apu) throw "error allocating apu";
>     LOG("Done.\n");
94a72
>     loadROM(ROM_name);
101a80
>   // set up palette and assert it
102a82
>   scr_mgr->assert_palette();
104,105c84,85
<   pad1 = (NES_pad *)NULL;
<   pad2 = (NES_pad *)NULL;
---
>   pad1 = NULL;
>   pad2 = NULL;
108d87
<   //dc_print ("DONE initializing NES");
113d91
< //	dc_print ("free rom");
116d93
< //  dc_print ("delete cpu");
118d94
< //  dc_print ("delete ppu");
120d95
< //  dc_print ("delete apu");
130c105
< uint8 NES::loadROM(const char* fn)
---
> void NES::loadROM(const char* fn)
132c107
<   //dc_print ("Loading ROM ...");
---
>   LOG("Loading ROM...");
139a115,119
>     // unsupported mapper
>     LOG("mapper #" << (int)ROM->get_mapper_num() << " not supported" << endl);
> 
>     delete ROM;
>     ROM = NULL;
141,146c121
<     //delete ROM;
<     //ROM = (NES_ROM *)NULL;
< 	dc_print ("UNSUPPORTED MAPPER");
< 	timer_sleep (5000);
< 	return 1;
<     //throw "unsupported mapper";
---
>     throw "unsupported mapper";
148a124,126
>   LOG("Done\n");
> 
>   LOG(ROM->GetRomName() << ".nes: #" << (int)ROM->get_mapper_num() << " ");
152c130
<      // LOG("H ");
---
>       LOG("H ");
155c133
<      // LOG("V ");
---
>       LOG("V ");
158c136
<    //   LOG("F ");
---
>       LOG("F ");
164c142
<    // LOG("S ");
---
>     LOG("S ");
168c146
<   //  LOG("T ");
---
>     LOG("T ");
171c149
<   //LOG(16*ROM->get_num_16k_ROM_banks() << "K/" << 8*ROM->get_num_8k_VROM_banks() << "K " << endl);
---
>   LOG(16*ROM->get_num_16k_ROM_banks() << "K/" << 8*ROM->get_num_8k_VROM_banks() << "K " << endl);
173c151
<  // Load_SaveRAM();
---
>   Load_SaveRAM();
177,180c155
<   return 0;
< 	
<   //dc_print ("Starting emulation");
<   //LOG("Starting emulation...\n");
---
>   LOG("Starting emulation...\n");
185,186c160
< //	dc_print ("In free rom");
<  // Save_SaveRAM();
---
>   Save_SaveRAM();
188c162
< //  LOG("Freeing ROM...");
---
>   LOG("Freeing ROM...");
192c166
<     ROM = (NES_ROM *)NULL;
---
>     ROM = NULL;
196d169
< 	//dc_print ("mapper");
198,199c171
<     mapper = (NES_mapper *)NULL;
< 	//dc_print ("done");
---
>     mapper = NULL;
202,203c174,175
<   //scr_mgr->clear(0x00);
<   //scr_mgr->blt();
---
>   scr_mgr->clear(0x00);
>   scr_mgr->blt();
204a177,178
>   LOG("Done\n");
>   LOG(endl);
218a193
>   LOG("Resetting NES\n");
220c195,196
<  // Save_SaveRAM();
---
>   // make sure saveRAM is saved
>   Save_SaveRAM();
228d203
<  // dc_print ("setting up cpu");
245c220
<    // LOG("  Setting trainer...");
---
>     LOG("  Setting trainer...");
248c223
<    // LOG("OK\n");
---
>     LOG("OK\n");
251,252c226
<  // dc_print ("PPU reset");
<   //LOG("  PPU reset...");
---
>   LOG("  PPU reset...");
255c229
<  // LOG("OK\n");
---
>   LOG("OK\n");
257,258c231
<  // dc_print ("APU reset");
<  // LOG("  APU reset...");
---
>   LOG("  APU reset...");
261c234
<   //LOG("OK\n");
---
>   LOG("OK\n");
265c238
<   //  LOG("  mapper reset...");
---
>     LOG("  mapper reset...");
267d239
< //	  dc_print ("resetting mapper");
269c241
<  //   LOG("OK\n");
---
>     LOG("OK\n");
272,273c244
<   //dc_print ("CPU reset");
<  // LOG("  CPU reset...");
---
>   LOG("  CPU reset...");
276c247
<   //LOG("OK\n");
---
>   LOG("OK\n");
284,285d254
<  // dc_print ("finished reseting nes!!!");
<  // dc_print ("done resetting NES!!!");
288,338d256
< void NES::emulate_frame_skip(uint8 sound_on) {
< 
<   trim_cycle_counts();
< 
<   // do frame
<   ppu->start_frame();
< 
<   // LINES 0-239
<   for(int i = 0; i < NES_NUM_FRAME_LINES; i++)
<   {
<     // do one line's worth of CPU cycles
<     emulate_CPU_cycles(CYCLES_PER_LINE);
< 	//dc_print ("emulate_CPU_cycles");
<     mapper->HSync(i);
< 	//dc_print ("mapper HSync");
< 
<   
<     ppu->do_scanline_and_dont_draw();
< 
<   }
< 
<   ppu->end_frame();
< 
<   for(int i = 240; i <= 261; i++)
<   {
<     if(i == 241)
<     {
<       // do v-blank
<       ppu->start_vblank();
< 	//  dc_print ("ppu start vblank");
<       mapper->VSync();
< 	//  dc_print ("mapper VSync");
<     }
<     else if(i == 261)
<     {
<       ppu->end_vblank();
< 	//  dc_print ("end vblank");
<     }
< 
<     if(i == 241)
<     {
<       // 1 instruction between vblank flag and NMI
<       emulate_CPU_cycles(1.0);
< 	 // dc_print ("emulate CPU cycles");
<       if(ppu->NMI_enabled()) cpu->DoNMI();
<       emulate_CPU_cycles((float)CYCLES_PER_LINE-(float)1.0);
< 	 // dc_print ("emulate cpu cycles");
<       mapper->HSync(i);
< 	//  dc_print ("mapper HSynch");
<       continue;
<     }
340,352c258
<     emulate_CPU_cycles(CYCLES_PER_LINE);
< 	//dc_print ("emu CPU bottom");
<     mapper->HSync(i);
< 	//dc_print ("done emulate frame");
<   }
< 
<   if (sound_on) {
< 	apu->DoFrame();
<   }
< 
< }
< 
< void NES::emulate_frame(uint8 sound_on)
---
> boolean NES::emulate_frame(boolean draw)
354,356c260,263
<   // set up direct point into video memory
<   uint16 *cur_line = vram_s + 24;
<   uint16 *start_buffer = vram_s;
---
>   uint32 i;
>   pixmap p;
>   uint8* cur_line; // ptr to screen buffer
>   boolean retval = draw;
359d265
<  //dc_print ("trim cycle counts");
363d268
<  //dc_print ("start emulate frame");
365,378c270,275
<   // triple buffering works well. 
<   // single and double buffering are slow as shit
<   if (screen_buffer == 0) {
< 		screen_buffer++;
< 	}
<   else if (screen_buffer == 1) {
< 		cur_line += SCREEN_BUFFER_1;
< 		screen_buffer++;
< 		start_buffer += SCREEN_BUFFER_1;
<   }
<   else {
< 		cur_line += SCREEN_BUFFER_2;
< 		screen_buffer = 0;
< 		start_buffer += SCREEN_BUFFER_2;
---
>   if(retval)
>   {
>     if(!scr_mgr->lock(p))
>       retval = FALSE;
>     else
>       cur_line = p.data;
382c279
<   for(int i = 0; i < NES_NUM_FRAME_LINES; i++)
---
>   for(i = 0; i < NES_NUM_FRAME_LINES; i++)
386d282
< 	//dc_print ("emulate_CPU_cycles");
388a285,287
>     if(retval)
>     {
>       // render line
390,400c289,294
< 
< 	// rendered resolution = 272*240
< 	// NES resolution = 256*240, so there are 8 pixels of 
< 	// garbage on each side of the screen
< 
< 	// clip 8 pixels 
< 	for (int i=0; i<2; i++) {
< 		*cur_line++ = 0;
< 		*cur_line++ = 0;
< 		*cur_line++ = 0;
< 		*cur_line++ = 0;
---
>       // point to next line
>       cur_line += p.pitch;
>     }
>     else
>     {
>       ppu->do_scanline_and_dont_draw();
402,410d295
< 
< 	cur_line += 256;
< 
< 	// clip 8 pixels
< 	for (int i=0; i<2; i++) {
< 		*cur_line++ = 0;
< 		*cur_line++ = 0;
< 		*cur_line++ = 0;
< 		*cur_line++ = 0;
413,416c298,300
< 
< 	// update the pointer
< 	cur_line += 48;
< 	  
---
>   if(retval)
>   {
>     scr_mgr->unlock();
421c305
<   for(int i = 240; i <= 261; i++)
---
>   for(i = 240; i <= 261; i++)
427d310
< 	//  dc_print ("ppu start vblank");
429d311
< 	//  dc_print ("mapper VSync");
434d315
< 	//  dc_print ("end vblank");
441d321
< 	 // dc_print ("emulate CPU cycles");
444d323
< 	 // dc_print ("emulate cpu cycles");
446d324
< 	//  dc_print ("mapper HSynch");
451d328
< 	//dc_print ("emu CPU bottom");
453d329
< 	//dc_print ("done emulate frame");
456,458d331
<   if (sound_on) {
< 	apu->DoFrame();
<   }
460,461c333,339
<   // set the vram pointer to the frame that was just rendered
<   vid_set_start ((uint32)start_buffer);
---
> 
>   // NES DOES NOT DO THIS
>   // HALF-LINE 262.5
>   emulate_CPU_cycles(CYCLES_PER_LINE/2);
> 
> 
>   apu->DoFrame();
462a341
>   return retval;
567c446
<  //   LOG("Read from SPR-RAM DMA reg??" << endl);
---
>     LOG("Read from SPR-RAM DMA reg??" << endl);
654a534
> 
657d536
< //	dc_print ("before has_save_RAM");
659,664c538
<   //if(!ROM->has_save_RAM()) return;
< 
<   uint8 addr = maple_vmu_addr(FIRST_DEVICE);
<   if (!addr) return;
< 
<   uint32 i=0;
---
>   if(!ROM->has_save_RAM()) return;
667c541
<   for(i = 0; i < sizeof(SaveRAM); i++)
---
>   for(uint32 i = 0; i < sizeof(SaveRAM); i++)
671d544
< 
674,712c547,548
< 	uint8 temp_mem[200];
< 	uint8 *free_mem = (uint8 *)temp_mem;
< 	uint8 *temp_ptr = free_mem;
< 
< 	for (int i=0; i<200; i++) {
< 		*temp_ptr++ = '0';
< 	}
< 
< 	//uint16 crc = calcCRC (SaveRAM, 0x2000);
< 	//dc_print ("the SaveRAM crc is");
< 	//print_uint32 ((uint32)crc);
< 	//timer_sleep (3000);
< 
< 	//memset (free_mem, 0x00, sizeof (free_mem));
< 	
< 	//uint32 free_blocks = vmu_read (free_mem);
< 
< 	uint16 block_number = check_vmu_for_game(ROM->GetRomName());
< 
< 	// found the game in SaveRAM
< 	if (block_number != 0) {
< 
< 		dc_print ("Saving Game...");
< 
< 		uint8 temp_buf[9216];
< 		uint8 *vmu_file = (uint8 *)temp_buf;
< 		uint8 free_blocks = check_free_blocks (free_mem);
< 
< 		create_vmu_header (vmu_file, ROM->GetRomName(), "NesterDC SaveRam File", 8576);
< 
< 		for (int i=640; i<1024; i++) {
< 			vmu_file[i] = 0;
< 		}
< 
< 		for (int i=0; i<8192; i++) {
< 			vmu_file[i + 1024] = SaveRAM[i];
< 		}
< 
< 		do_crc (vmu_file, 9216);
---
>     FILE* fp = NULL;
>     char fn[256];
714c550
< 		upload_data_by_block (vmu_file, (uint32)block_number, 18);
---
>     LOG("Saving Save RAM...");
716,724c552,554
< 	}
< 	else {
< 		uint8 free_blocks = check_free_blocks (free_mem);
< 
< 		if (free_blocks < 18) {
< 			dc_print ("You need 18 free blocks to Save");
< 			timer_sleep (5000);
< 			return;
< 		}
---
>     strcpy(fn, ROM->GetRomPath());
>     strcat(fn, ROM->GetRomName());
>     strcat(fn, ".sav");
726,746c556,559
< 		dc_print ("Saving Game...");
< 
< 		uint8 temp_buf[9216];
< 		uint8 *vmu_file = (uint8 *)temp_buf;
< 
< 		create_vmu_header (vmu_file, ROM->GetRomName(), "NesterDC SaveRam File", 8576);
< 
< 		// filler
< 		for (int i=640; i<1024; i++) {
< 			vmu_file[i] = 0;
< 		}
< 
< 		for (int i=0; i<8192; i++) {
< 			vmu_file[i + 1024] = SaveRAM[i];
< 		}
< 
< 		do_crc (vmu_file, 9216);
< 
< 		uint8 first_free_block = find_first_free_block(free_mem);
< 
< 		update_fat (free_mem, 18);
---
>     try
>     {
>       fp = fopen(fn, "wb");
>       if(!fp) throw "can't open save RAM file";
748c561,562
< 		upload_vmu_data (vmu_file, free_mem, 18);
---
>       if(fwrite(SaveRAM, sizeof(SaveRAM), 1, fp) != 1)
>         throw "can't open save RAM file";
750,751c564,565
< 		update_vmu_dir (first_free_block, ROM->GetRomName(), 18);
< 	}
---
>       fclose(fp);
>       LOG("Done." << endl);
752a567,569
>     } catch(...) {
>       LOG("can't save" << endl);
>       if(fp) fclose(fp);
754,755d570
<   else {
< 		//dc_print ("nothing has been written to SaveRAM");
760a576
>   memset(SaveRAM, 0x00, sizeof(SaveRAM));
762,768c578,579
< 	uint8 addr = maple_vmu_addr(FIRST_DEVICE);
< 	if (!addr) return;
< 
< 	unsigned char *temp_ptr = SaveRAM;
< 	for (int i=0; i<0x2000; i++) {
< 		*temp_ptr++ = 0x00;
< 	}
---
>   // does the ROM use save ram?
>   if(!ROM->has_save_RAM()) return;
770,772c581,583
< 	uint16 block_number = check_vmu_for_game(ROM->GetRomName());
< 	//print_uint32 ((uint32)block_number);
< 	//timer_sleep (3000);
---
>   {
>     FILE* fp = NULL;
>     char fn[256];
774c585,587
< 	if (block_number == 0) return;
---
>     strcpy(fn, ROM->GetRomPath());
>     strcat(fn, ROM->GetRomName());
>     strcat(fn, ".sav");
776c589,592
< 	dc_print ("Loading Game...");
---
>     try
>     {
>       fp = fopen(fn, "rb");
>       if(!fp) throw "none found.";
778,780c594
< 	block_number = get_next_block_from_fat (block_number);
< 	//print_uint32 ((uint32)block_number);
< 	//timer_sleep (3000);
---
>       LOG("Loading Save RAM...");
782c596,600
< 	for (int i=0; i<16; i++) {
---
>       if(fread(SaveRAM, sizeof(SaveRAM), 1, fp) != 1)
>       {
>         LOG("error reading Save RAM file" << endl);
>         throw "error reading Save RAM file";
>       }
784,786c602,603
< 		block_number = get_next_block_from_fat (block_number);
< 	//	print_uint32 ((uint32)block_number);
< 	//	timer_sleep (3000);
---
>       fclose(fp);
>       LOG("Done." << endl);
788,792c605,606
< 		if (vmu_block_read(addr, block_number, SaveRAM + (i*512)) != 0) {
< 			dc_print ("error loading block number");
< 			print_uint32 ((uint32)block_number);
< 			dc_sleep (3000);
< 			return;
---
>     } catch(...) {
>       if(fp) fclose(fp);
794,795d607
< 		
< 		timer_sleep (50);
797d608
< 
812,815d622
<   //NESTER_settings.nes.graphics.calculate_palette = true;
<   //NESTER_settings.nes.graphics.tint = 255;
<   //NESTER_settings.nes.graphics.hue = 128; 
< 
818d624
< //	dc_print ("in calculate palette");
878d683
< //	dc_print ("load preset palette");
882d686
<   NESTER_settings.nes.graphics.black_and_white = false;
899,904d702
< 

*/
