/*  Copyright (C) 2010-2011 DeSmuME team

    This file is part of DeSmuME

    DeSmuME 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.

    DeSmuME 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DeSmuME; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

#include "../slot1.h"
#include "../registers.h"
#include "../MMU.h"
#include "../NDSSystem.h"

static void info(char *info) { strcpy(info, "Slot1 Retail card emulation"); }
static void config() {}

static bool init() { return true; }

static void reset() {}

static void close() {}

static void write08(uint8_t, uint32_t, uint8_t) {}
static void write16(uint8_t, uint32_t, uint16_t) {}

static void write32_GCROMCTRL(uint8_t PROCNUM, uint32_t)
{
	nds_dscard &card = MMU.dscard[PROCNUM];

	switch (card.command[0])
	{
		case 0x00: //Data read
		case 0xB7:
			card.address = (card.command[1] << 24) | (card.command[2] << 16) | (card.command[3] << 8) | card.command[4];
			card.transfer_count = 0x80;
			break;

		case 0xB8: // Chip ID
			card.address = 0;
			card.transfer_count = 1;
			break;

		default:
			card.address = 0;
			card.transfer_count = 0;
	}
}

static void write32(uint8_t PROCNUM, uint32_t adr, uint32_t val)
{
	switch (adr)
	{
		case REG_GCROMCTRL:
			write32_GCROMCTRL(PROCNUM, val);
	}
}

static uint8_t read08(uint8_t, uint32_t)
{
	return 0xFF;
}

static uint16_t read16(uint8_t, uint32_t)
{
	return 0xFFFF;
}

static uint32_t read32_GCDATAIN(uint8_t PROCNUM)
{
	nds_dscard &card = MMU.dscard[PROCNUM];

	switch (card.command[0])
	{
		// Get ROM chip ID
		case 0x90:
		case 0xB8:
			{
				// Note: the BIOS stores the chip ID in main memory
				// Most games continuously compare the chip ID with
				// the value in memory, probably to know if the card
				// was removed.
				// As DeSmuME boots directly from the game, the chip
				// ID in main mem is zero and this value needs to be
				// zero too.

				// staff of kings verifies this (it also uses the arm7 IRQ 20)
				if (nds.cardEjected) // TODO - handle this with ejected card slot1 device (and verify using this case)
					return 0xFFFFFFFF;
				else
					return 0;
			}
			break;

		// Data read
		case 0x00:
		case 0xB7:
			{
				//it seems that etrian odyssey 3 doesnt work unless we mask this to cart size.
				//but, a thought: does the internal rom address counter register wrap around? we may be making a mistake by keeping the extra precision
				//but there is no test case yet
				uint32_t address = card.address & (gameInfo.mask);

				// Make sure any reads below 0x8000 redirect to 0x8000+(adr&0x1FF) as on real cart
				if((card.command[0] == 0xB7) && (address < 0x8000))
				{
					// TODO - refactor this to include the PROCNUM, for debugging purposes if nothing else
					// (can refactor gbaslot also)

					//INFO("Read below 0x8000 (0x%04X) from: ARM%s %08X\n", card.address, (PROCNUM ? "7":"9"), (PROCNUM ? NDS_ARM7:NDS_ARM9).instruct_adr);

					address = (0x8000 + (address&0x1FF));
				}

				// as a sanity measure for funny-sized roms (homebrew and perhaps truncated retail roms)
				// we need to protect ourselves by returning 0xFF for things still out of range
				if (address >= gameInfo.romsize)
				{
					//DEBUG_Notify.ReadBeyondEndOfCart(address, gameInfo. romsize);
					return 0xFFFFFFFF;
				}

				return T1ReadLong(MMU.CART_ROM, address);
			}
			break;
		default:
			return 0;
	} //switch(card.command[0])
} //read32_GCDATAIN

static uint32_t read32(uint8_t PROCNUM, uint32_t adr)
{
	switch(adr)
	{
		case REG_GCDATAIN:
			return read32_GCDATAIN(PROCNUM);
		default:
			return 0;
	}
}

SLOT1INTERFACE slot1Retail =
{
	"Retail",
	init,
	reset,
	close,
	config,
	write08,
	write16,
	write32,
	read08,
	read16,
	read32,
	info
};

	//		///writetoGCControl:
	//// --- Ninja SD commands -------------------------------------

	//	// NJSD init/reset
	//	case 0x20:
	//		{
	//			card.address = 0;
	//			card.transfer_count = 0;
	//		}
	//		break;

	//	// NJSD_sendCLK()
	//	case 0xE0:
	//		{
	//			card.address = 0;
	//			card.transfer_count = 0;
	//			NDS_makeInt(PROCNUM, 20);
	//		}
	//		break;

	//	// NJSD_sendCMDN() / NJSD_sendCMDR()
	//	case 0xF0:
	//	case 0xF1:
	//		switch (card.command[2])
	//		{
	//		// GO_IDLE_STATE
	//		case 0x40:
	//			card.address = 0;
	//			card.transfer_count = 0;
	//			NDS_makeInt(PROCNUM, 20);
	//			break;

	//		case 0x42:  // ALL_SEND_CID
	//		case 0x43:  // SEND_RELATIVE_ADDR
	//		case 0x47:  // SELECT_CARD
	//		case 0x49:  // SEND_CSD
	//		case 0x4D:
	//		case 0x77:  // APP_CMD
	//		case 0x69:  // SD_APP_OP_COND
	//			card.address = 0;
	//			card.transfer_count = 6;
	//			NDS_makeInt(PROCNUM, 20);
	//			break;

	//		// SET_BLOCKLEN
	//		case 0x50:
	//			card.address = 0;
	//			card.transfer_count = 6;
	//			card.blocklen = card.command[6] | (card.command[5] << 8) | (card.command[4] << 16) | (card.command[3] << 24);
	//			NDS_makeInt(PROCNUM, 20);
	//			break;

	//		// READ_SINGLE_BLOCK
	//		case 0x51:
	//			card.address = card.command[6] | (card.command[5] << 8) | (card.command[4] << 16) | (card.command[3] << 24);
	//			card.transfer_count = (card.blocklen + 3) >> 2;
	//			NDS_makeInt(PROCNUM, 20);
	//			break;
	//		}
	//		break;

	//	// --- Ninja SD commands end ---------------------------------



	//		//GCDATAIN:
	//	// --- Ninja SD commands -------------------------------------

	//	// NJSD_sendCMDN() / NJSD_sendCMDR()
	//	case 0xF0:
	//	case 0xF1:
	//		switch (card.command[2])
	//		{
	//		// ALL_SEND_CID
	//		case 0x42:
	//			if (card.transfer_count == 2) val = 0x44534A4E;
	//			else val = 0x00000000;

	//		// SEND_RELATIVE_ADDR
	//		case 0x43:
	//		case 0x47:
	//		case 0x49:
	//		case 0x50:
	//			val = 0x00000000;
	//			break;

	//		case 0x4D:
	//			if (card.transfer_count == 2) val = 0x09000000;
	//			else val = 0x00000000;
	//			break;

	//		// APP_CMD
	//		case 0x77:
	//			if (card.transfer_count == 2) val = 0x00000037;
	//			else val = 0x00000000;
	//			break;

	//		// SD_APP_OP_COND
	//		case 0x69:
	//			if (card.transfer_count == 2) val = 0x00008000;
	//			else val = 0x00000000;
	//			break;

	//		// READ_SINGLE_BLOCK
	//		case 0x51:
	//			val = 0x00000000;
	//			break;
	//		}
	//		break;

	//	// --- Ninja SD commands end ---------------------------------