// Atari XL/XE SAP core CPU and RAM emulator

// Game_Music_Emu 0.6-pre
#ifndef SAP_CORE_H
#define SAP_CORE_H

#include "Sap_Apu.h"
#include "Nes_Cpu.h"

class Sap_Core {
public:
	
	// Sound chips and common state
	Sap_Apu& apu()                              { return apu_; }
	Sap_Apu& apu2()                             { return apu2_; }
	Sap_Apu_Impl& apu_impl()                    { return apu_impl_; }
	
	// Adjusts music tempo, where 1.0 is normal. Can be changed while playing.
	void set_tempo( double );
	
	// Clears RAM and sets up default vectors, etc.
	void setup_ram();
	
	// 64K RAM to load file data blocks into
	BOOST::uint8_t* ram()                       { return mem.ram; }
	
	// Calls init routine and configures playback. RAM must have been
	// set up already.
	struct info_t {
		int init_addr;
		int play_addr;
		int music_addr;
		int type;
		int fastplay;
		bool stereo;
	};
	blargg_err_t start_track( int track, info_t const& );
	
	// Ends time frame at time t, then begins new at time 0
	typedef Nes_Cpu::time_t time_t; // Clock count
	blargg_err_t end_frame( time_t t );
	

// Implementation
public:
	Sap_Core();

private:
	enum { base_scanline_period = 114 };
	enum { lines_per_frame = 312 };
	typedef Nes_Cpu::addr_t addr_t;
	
	time_t scanline_period;
	time_t next_play;
	time_t time_mask;
	time_t frame_start;
	Nes_Cpu cpu;
	Nes_Cpu::registers_t saved_state;
	info_t info;
	Sap_Apu apu_;
	Sap_Apu apu2_;
	
	// large items
	struct {
		BOOST::uint8_t padding1 [  0x100];
		BOOST::uint8_t ram      [0x10000];
		BOOST::uint8_t padding2 [  0x100];
	} mem; // TODO: put on freestore
	Sap_Apu_Impl apu_impl_;

	void push( int b );
	void jsr_then_stop( addr_t );
	void run_routine( addr_t );
	void call_init( int track );
	bool run_cpu( time_t end );
	int  play_addr();
	int  read_d40b();
	int  read_mem( addr_t );
	void write_D2xx( int d2xx, int data );
	
	time_t time() const                     { return cpu.time() & time_mask; }
	blargg_err_t run_until( time_t t );
	time_t play_period() const { return info.fastplay * scanline_period; }
};

inline void Sap_Core::set_tempo( double t )
{
	scanline_period = (int) (base_scanline_period / t + 0.5);
}

#endif
