
-- create the module's table
local nes = {}

-- import required modules
local dict = require "scripts.app.dict"

-- file constants
local PPU_A13N_HI = 0x8000	--PPU /A13 is connected to mcu A15
local PPU_A13_HI =  0x2000	--PPU /A13 is connected to mcu A15
local FC_RF_HI = 0x20		--FC RF audio pin is EXP6 (bit5)

-- local functions

-- Desc:check if PPU /A13 -> CIRAM /CE jumper present
--    Does NOT check if PPU A13 is inverted and then drives CIRAM /CE
-- Pre: nes_init() been called to setup i/o
-- Post:PPU /A13 left high (disabled), all other ADDRH signals low
-- Rtn: true if jumper is set
local function jumper_ciramce_ppuA13n( debug )

	--check that we can clear CIRAM /CE with PPU /A13
	dict.pinport( "ADDR_SET", 0x0000 )
	--read CIRAM /CE pin
	if dict.pinport( "CTL_RD", "CICE" ) ~= 0 then
		if debug then print("CIRAM /CE high when /A13 low ") end
		return false
	end

	--set PPU /A13 high
	dict.pinport( "ADDR_SET", PPU_A13N_HI )
	--read CIRAM /CE pin
	if dict.pinport( "CTL_RD", "CICE" ) == 0 then
		if debug then print("CIRAM /CE low when /A13 high") end
		return false
	end

	--CICE low jumper appears to be present
	if debug then print("CIRAM /CE <- PPU /A13 jumper present") end
	return true
end

-- Desc:check if PPU A13 is inverted then drives CIRAM /CE 
--	Some mappers may do this including INLXO-ROM boards
--	Does NOT check if PPU /A13 is drives CIRAM /CE
-- Pre: nes_init() been called to setup i/o
-- Post:PPU A13 left disabled (hi)
-- Rtn: true if inverted PPU A13 drives CIRAM /CE
local function ciramce_inv_ppuA13 (debug)

	--set PPU A13 low
	dict.pinport( "ADDR_SET", 0x0000 )
	-- CIRAM /CE should be high if inverted A13 is what drives it
	if dict.pinport( "CTL_RD", "CICE" ) == 0 then
		if debug then print("CIRAM /CE low when A13 low") end
		return false
	end

	--check that we can clear CIRAM /CE with PPU A13 high
	dict.pinport( "ADDR_SET", PPU_A13_HI )
	-- CIRAM /CE should be low if inverted A13 is what drives it
	if dict.pinport( "CTL_RD", "CICE" ) ~= 0 then
		if debug then print("CIRAM /CE high when A13 high") end
		return false
	end

	--CICE low jumper appears to be present
	if debug then print("CIRAM /CE <- inverse PPU A13") end
	return true
end

-- Desc:check for famicom audio in->out jumper
--	This drives EXP6 (RF out) -> EXP0 (APU in) which is backwards..
--	not much can do about that for old avr kazzo designs
--	There are probably caps/resistors for synth carts anyway
--	but to be safe only apply short pulses.
--	While we typically don't want to apply 5v to EXP port on NES carts,
--	this only does so for EXP6 which is safe on current designs.
--	All other EXP1-8 pins are only driven low.
-- Pre: nes_init() been called to setup i/o
--	which makes EXP0 floating i/p
-- Post:EXP FF left disabled and EXP0 floating
--	AXLOE pin returned to input with pullup
-- Rtn: true if jumper/connection is present
-- Test:Works on non-expansion sound carts obviously
--	Works on VRC6 and VRC7
--	Others untested
local function jumper_famicom_sound (debug)

	--EXP0 should be floating input
	--AXLOE pin needs to be set as output and
	--EXP FF needs enabled before we can clock it, 
	--but don't leave it enabled before exiting function
--
	--set AXLOE to output
	dict.pinport("EXP_ENABLE")
	--Latch low first
	dict.pinport("EXP_SET", 0x00)
--	pull up FCAPU
	dict.pinport("CTL_IP_PU", "FCAPU")
	--read EXP0 Famicom APU audio pin
	if dict.pinport( "CTL_RD", "FCAPU" ) ~= 0 then
		if debug then print("RF audio out (EXP6) didn't drive APU audio in (EXP0) low") end
		dict.pinport("EXP_DISABLE")
		dict.pinport("CTL_IP_FL", "EXP0")
		return false
	end

	--Latch RF audio sound pin high 
	dict.pinport("EXP_SET", FC_RF_HI)
	--read Famicom APU audio pin
	if dict.pinport( "CTL_RD", "FCAPU" ) == 0 then
		if debug then print("RF audio out (EXP6) didn't drive APU audio in (EXP0) high") end
		dict.pinport("EXP_DISABLE")
		dict.pinport("CTL_IP_FL", "EXP0")
		return false
	end

	--Famicom audio jumper appears to be present
	if debug then print("RF audio out (EXP6) is connected to APU audio in") end
	--disable EXP PORT and return EXP0 to floating if it was used
	dict.pinport("EXP_DISABLE")
	dict.pinport("CTL_IP_FL", "EXP0")
	return true
end


-- Desc:Run through supported mapper mirroring modes to help detect mapper.
-- Pre: 
-- Post:cart mirroring set to found mirroring
-- Rtn: SUCCESS if nothing bad happened, neg if error with kazzo etc
local function detect_mapper_mirroring (debug)

	local rv

	if(debug) then print("attempting to detect NES/FC mapper via mirroring...") end
--		//TODO call mmc3 detection function
--
--		//TODO call mmc1 detection function
--
--		//fme7 and many other ASIC mappers
--
--		//none of ASIC mappers passed, assume fixed/discrete style mirroring

		dict.pinport("ADDR_SET", 0x0800)
		local readH = dict.pinport("CTL_RD", "CIA10")
		dict.pinport("ADDR_SET", 0x0400)
		local readV = dict.pinport("CTL_RD", "CIA10")

		--print(readH, readV)

		---[[
		if readV == 0 and readH == 0 then
			if debug then print("1screen A mirroring sensed") end
			return "1SCNA"
		elseif readV ~= 0 and readH ~= 0 then
			if debug then print("1screen B mirroring sensed") end
			return "1SCNB"

		elseif readV ~= 0 and readH == 0 then
			if debug then print("vertical mirroring sensed") end
			return "VERT"
		elseif readV == 0 and readH ~= 0 then
			if debug then print("horizontal mirroring sensed") end
			return "HORZ"
		end
		--]]

		--[[
		rv = dict.nes("CIRAM_A10_MIRROR")
		if (rv == op_nes["MIR_VERT"]) then
			if debug then print("vertical mirroring sensed") end
			return "VERT"
		elseif rv == op_nes["MIR_HORZ"] then
			if debug then print("horizontal mirroring sensed") end
			return "HORZ"
		elseif rv == op_nes["MIR_1SCNA"] then
			if debug then print("1screen A mirroring sensed") end
			return "1SCNA"
		elseif rv == op_nes["MIR_1SCNB"] then
			if debug then print("1screen B mirroring sensed") end
			return "1SCNB"
		end
		--]]

		-- Rtn: VERT/HORIZ/1SCNA/1SCNB
	return nil
end


-- verify the ciccom software mirroring switch is working properly
local function test_cic_soft_switch (debug)
end

-- Desc:CHR-ROM flash manf/prod ID sense test
--	Only senses SST flash ID's
--	Does not make CHR bank writes so A14-A13 must be made valid outside of this funciton
--	An NROM board does this by tieing A14:13 to A12:11
--	Other mappers will pass this function if PT0 has A14:13=01, PT1 has A14:13=10
--	Assumes that isn't getting tricked by having manf/prodID at $0000/0001
--	could add check and increment read address to ensure doesn't get tricked..
-- Pre: nes_init() been called to setup i/o
-- Post:memory manf/prod ID set to read values if passed
--	memory wr_dict and wr_opcode set if successful
--	Software mode exited if entered successfully
-- Rtn: SUCCESS if flash sensed, GEN_FAIL if not, neg if error 
local function read_flashID_chrrom_8K (debug)
	
	local rv
	--enter software mode
	--NROM has A13 tied to A11, and A14 tied to A12.
	--So only A0-12 needs to be valid
	--A13 needs to be low to address CHR-ROM
	--	    15 14 13 12
	-- 0x5 = 0b  0  1  0  1	-> $1555
	-- 0x2 = 0b  0  0  1  0	-> $0AAA
	dict.nes("NES_PPU_WR", 0x1555, 0xAA)
	dict.nes("NES_PPU_WR", 0x0AAA, 0x55)
	dict.nes("NES_PPU_WR", 0x1555, 0x90)
	--read manf ID
	rv = dict.nes("NES_PPU_RD", 0x0000)
	if debug then print("attempted read CHR-ROM manf ID:", string.format("%X", rv)) end
--	if ( rv[RV_DATA0_IDX] != SST_MANF_ID ) {
--		return GEN_FAIL;
--		//no need for software exit since failed to enter
--	}
--
	--read prod ID
	rv = dict.nes("NES_PPU_RD", 0x0001)
	if debug then print("attempted read CHR-ROM prod ID:", string.format("%X", rv)) end
--	if ( (rv[RV_DATA0_IDX] == SST_PROD_128)
--	||   (rv[RV_DATA0_IDX] == SST_PROD_256)
--	||   (rv[RV_DATA0_IDX] == SST_PROD_512) ) {
--		//found expected manf and prod ID
--		flash->manf = SST_MANF_ID;
--		flash->part = rv[RV_DATA0_IDX];
--		flash->wr_dict = DICT_NES;
--		flash->wr_opcode = NES_PPU_WR;
--	}
--
	--exit software
	dict.nes("NES_PPU_WR", 0x0000, 0xF0)

	--return true
end


--/* Desc:Simple CHR-RAM sense test
-- *	A more thourough test should be implemented in firmware
-- *	This one simply tests one address in PPU address space
-- * Pre: nes_init() been called to setup i/o
-- * Post:
-- * Rtn: SUCCESS if ram sensed, GEN_FAIL if not, neg if error 
-- */
--int ppu_ram_sense( USBtransfer *transfer, uint16_t addr ) {
local function ppu_ram_sense( addr, debug )
	local rv
	--write 0xAA to addr 
	dict.nes("NES_PPU_WR", addr, 0xAA)
	--try to read it back
	if (dict.nes("NES_PPU_RD", addr) ~= 0xAA) then
		if debug then print("could not write 0xAA to PPU $", string.format("%X", addr)) end
		return false
	end
	--write 0x55 to addr 
	dict.nes("NES_PPU_WR", addr, 0x55)
	--try to read it back
	if (dict.nes("NES_PPU_RD", addr) ~= 0x55) then
		if debug then print("could not write 0x55 to PPU $", string.format("%X", addr)) end
		return false
	end

	if debug then print("detected RAM @ PPU $", string.format("%X", addr)) end
	return true
end

-- Desc:PRG-ROM flash manf/prod ID sense test
--	Using EXP0 /WE writes 
--	Only senses SST flash ID's
--	Assumes that isn't getting tricked by having manf/prodID at $8000/8001
--	could add check and increment read address to ensure doesn't get tricked..
-- Pre: nes_init() been called to setup i/o
--	exp0 pullup test must pass
--	if ROM A14 is mapper controlled it must be low when CPU A14 is low
--	controlling A14 outside of this function acts as a means of bank size detection
-- Post:memory manf/prod ID set to read values if passed
--	memory wr_dict and wr_opcode set if successful
--	Software mode exited if entered successfully
-- Rtn: SUCCESS if flash sensed, GEN_FAIL if not, neg if error 
local function read_flashID_prgrom_exp0 (debug)
	local rv
	--enter software mode
	--ROMSEL controls PRG-ROM /OE which needs to be low for flash writes
	--So unlock commands need to be addressed below $8000
	--DISCRETE_EXP0_PRGROM_WR doesn't toggle /ROMSEL by definition though, so A15 is unused
	--	    15 14 13 12
	-- 0x5 = 0b  0  1  0  1	-> $5555
	-- 0x2 = 0b  0  0  1  0	-> $2AAA
	dict.nes("DISCRETE_EXP0_PRGROM_WR", 0x5555, 0xAA)
	dict.nes("DISCRETE_EXP0_PRGROM_WR", 0x2AAA, 0x55)
	dict.nes("DISCRETE_EXP0_PRGROM_WR", 0x5555, 0x90)
	--read manf ID
	rv = dict.nes("NES_CPU_RD", 0x8000)
	if debug then print("attempted read PRG-ROM manf ID:", string.format("%X", rv)) end
--	debug("manf id: %x", rv[RV_DATA0_IDX]);
--	if ( rv[RV_DATA0_IDX] != SST_MANF_ID ) {
--		return GEN_FAIL;
--		//no need for software exit since failed to enter
--	}
--
	--read prod ID
	rv = dict.nes("NES_CPU_RD", 0x8001)
	if debug then print("attempted read PRG-ROM prod ID:", string.format("%X", rv)) end
--	if ( (rv[RV_DATA0_IDX] == SST_PROD_128)
--	||   (rv[RV_DATA0_IDX] == SST_PROD_256)
--	||   (rv[RV_DATA0_IDX] == SST_PROD_512) ) {
--		//found expected manf and prod ID
--		flash->manf = SST_MANF_ID;
--		flash->part = rv[RV_DATA0_IDX];
--		flash->wr_dict = DICT_NES;
--		flash->wr_opcode = DISCRETE_EXP0_PRGROM_WR;
--	}
--
	--exit software
	dict.nes("DISCRETE_EXP0_PRGROM_WR", 0x8000, 0xF0)
	--verify exited
--	rv = dict.nes("NES_CPU_RD", 0x8001)
--	if debug then print("attempted read PRG-ROM prod ID:", string.format("%X", rv)) end
	
	return true
end


-- global variables so other modules can use them


-- call functions desired to run when script is called/imported


-- functions other modules are able to call
nes.jumper_ciramce_ppuA13n = jumper_ciramce_ppuA13n
nes.ciramce_inv_ppuA13 = ciramce_inv_ppuA13 
nes.jumper_famicom_sound = jumper_famicom_sound
nes.detect_mapper_mirroring = detect_mapper_mirroring
nes.test_cic_soft_switch = test_cic_soft_switch
nes.ppu_ram_sense = ppu_ram_sense
nes.read_flashID_chrrom_8K = read_flashID_chrrom_8K
nes.read_flashID_prgrom_exp0 = read_flashID_prgrom_exp0

-- return the module's table
return nes

-- old C file:
--
--
--
--/* Desc:PRG-ROM flash manf/prod ID sense test
-- *	Using mapper 30 defined PRG-ROM flash writes
-- *	Only senses SST flash ID's
-- *	Assumes that isn't getting tricked by having manf/prodID at $8000/8001
-- *	could add check and increment read address to ensure doesn't get tricked..
-- * Pre: nes_init() been called to setup i/o
-- * Post:memory manf/prod ID set to read values if passed
-- *	memory wr_dict and wr_opcode set if successful
-- *	Software mode exited if entered successfully
-- * Rtn: SUCCESS if flash sensed, GEN_FAIL if not, neg if error 
-- */
--int read_flashID_prgrom_map30( USBtransfer *transfer, memory *flash ) {
--
--	uint8_t rv[RV_DATA0_IDX];
--
	--enter software mode
	--$8000-BFFF writes to flash
	--$C000-FFFF writes to mapper
	--	    15 14 13 12
	-- 0x5 = 0b  0  1  0  1	-> $9555
	-- 0x2 = 0b  0  0  1  0	-> $2AAA
	--set A14 in mapper reg for $5555 command
--	dictionary_call( transfer, DICT_NES, 	NES_CPU_WR,	0xC000,		0x01,	
--									USB_IN,		NULL,	1);
	--write $5555 0xAA
--	dictionary_call( transfer, DICT_NES, 	NES_CPU_WR,	0x9555,		0xAA,	
--									USB_IN,		NULL,	1);
	--clear A14 in mapper reg for $2AAA command
--	dictionary_call( transfer, DICT_NES, 	NES_CPU_WR,	0xC000,		0x00,	
--									USB_IN,		NULL,	1);
	--write $2AAA 0x55
--	dictionary_call( transfer, DICT_NES, 	NES_CPU_WR,	0xAAAA,		0x55,	
--									USB_IN,		NULL,	1);
	--set A14 in mapper reg for $5555 command
--	dictionary_call( transfer, DICT_NES, 	NES_CPU_WR,	0xC000,		0x01,	
--									USB_IN,		NULL,	1);
	--write $5555 0x90 for software mode
--	dictionary_call( transfer, DICT_NES, 	NES_CPU_WR,	0x9555,		0x90,	
--									USB_IN,		NULL,	1);
--
	--read manf ID
--	dictionary_call( transfer, DICT_NES, 	NES_CPU_RD,			0x8000,		NILL,	
--								USB_IN,		rv,	RV_DATA0_IDX+1);
--	debug("manf id: %x", rv[RV_DATA0_IDX]);
--	if ( rv[RV_DATA0_IDX] != SST_MANF_ID ) {
--		return GEN_FAIL;
--		//no need for software exit since failed to enter
--	}
--
	--read prod ID
--	dictionary_call( transfer, DICT_NES, 	NES_CPU_RD,			0x8001,		NILL,	
--								USB_IN,		rv,	RV_DATA0_IDX+1);
--	debug("prod id: %x", rv[RV_DATA0_IDX]);
--	if ( (rv[RV_DATA0_IDX] == SST_PROD_128)
--	||   (rv[RV_DATA0_IDX] == SST_PROD_256)
--	||   (rv[RV_DATA0_IDX] == SST_PROD_512) ) {
--		//found expected manf and prod ID
--		flash->manf = SST_MANF_ID;
--		flash->part = rv[RV_DATA0_IDX];
--		flash->wr_dict = DICT_NES;
--		flash->wr_opcode = NES_CPU_WR;
--	}
--
	--exit software
--	dictionary_call( transfer, DICT_NES, 	NES_CPU_WR,	0x8000,		0xF0,	
--									USB_IN,		NULL,	1);
--
	--verify exited
--	dictionary_call( transfer, DICT_NES, 	NES_CPU_RD,			0x8000,		NILL,	
--								USB_IN,		rv,	RV_DATA0_IDX+1);
--	debug("prod id: %x", rv[RV_DATA0_IDX]);
--
--	return SUCCESS;
--}
