/****************************************************************************
 ■ HoneyBox
   2010(C) Mr.Honey
****************************************************************************/

#include "fmr.h"
#include "romdat.h"
#include "ff.h"
#include "resource.h"

//=================================================================

static void SetFreq(U16 freq);
static U16 GetFreq(void);
static void Write(U8 addr, U16 data);
static U16 Read(U8 addr);
static void Update(U8 seek);

//=================================================================

// 基本レジスタ値

// REG 02H
// B15: Audio Output High-Z Disable = High impedance
// B14: Mute Disable = Mute
// B13: Mono Select = Stereo
// B12: Bass Boost = Disabled
// B11: 1 
//  B9: Seek Up = Seek up
//  B8: Seek = Disable
//  B7: 1 
//  B0: Power Up Enable = Disabled
const static U16 s_nBase02 = 0x0A80;

// REG 04H
// B14: Seek/Tune Complete Interrupt Enable = Disable Interrupt
// B11: De-emphasis = 75 μs
//  B6: I2S Bus Enable = disabled
// B5:4: GPIO 3 = Mono/Stereo indicator (ST)
// B3:2: GPIO 2 = High impedance
// B1:0: GPIO 1 = High impedance
const static U16 s_nBase04 = 0x0010;

// REG 05H
// B15: INT Mode Select = Generate 5ms interrupt
// B13:8: Seek Threshold in Logarithmic = 011000
// B7:4: DSP Volume Control = min -15db
// B3:0: DAC Gain Control Bits (Volume) = min
const static U16 s_nBase05 = 0x1000;

//=================================================================

#pragma udata overlay share=0x500
static FATFS _s_FS_;
static Channel_t ChanList[EEP_CHAN_ARRAY];

#pragma udata
static U16 s_nReg02;
static U16 s_nReg04;
static U16 s_nReg05;
static U8 s_nChanNum;
static U8 s_nChan;
static U16 s_nFreq;
static U8 s_nNameR;
static U8 s_nPosL;

//=================================================================

#define MAX_FREQS	7600
#ifdef ENABLE_TVCH
#define MAX_FREQW	2050
#else
#define MAX_FREQW	1500
#endif

//=================================================================

// ●初期化
extern void InitFMR(void)
{
	// レジスタ初期化
	Write(0x02, s_nReg02 = s_nBase02);
	Write(0x04, s_nReg04 = s_nBase04);
	Write(0x05, s_nReg05 = s_nBase05);

	// ラスト情報ロード
	s_nChan = Read_b_eep(&LAST_PRESET);
	s_nFreq = MAKEWORD(
		Read_b_eep(&LAST_FREQ_H),
		Read_b_eep(&LAST_FREQ_L));
}

// ●ON/OFF
extern void FMR_Enable(U8 flag)
{
	if (flag)
	{
		U8 i, *p, *e;

		// B0: Power Up Enable -> Enable
		Write(0x02, s_nReg02 |= 0x0001);
		DelayMs(5); // 念のため

		// (ノイズのせい?でPower UP する前に行うと正常に動作しない)
		DrawBase(FMR_TITLE, FMR_AREA);
		s_nNameR = 0;
		s_nPosL = 0;
		
		// チャンネルリストロード
		i = (U8)CHAN_LIST;
		p = (U8*)ChanList;
		e = p + sizeof(ChanList);
		do *p = Read_b_eep(i++);
		while (++p < e);

		for (s_nChanNum = 0; s_nChanNum < EEP_CHAN_ARRAY; s_nChanNum++)
		{
			if (*ChanList[s_nChanNum].sName == '\0')
				break;
		}

		// ラスト選局
		SetFreq(s_nFreq);
	}
	else
	{
		// B0: Power Up Enable -> Disabled
		Write(0x02, s_nReg02 &= ~0x0001);

		// ラスト保存
		Write_b_eep(&LAST_PRESET, s_nChan);
		Busy_eep();
		Write_b_eep(&LAST_FREQ_H, HIBYTE(s_nFreq));
		Busy_eep();
		Write_b_eep(&LAST_FREQ_L, LOBYTE(s_nFreq));
		Busy_eep();
	}
}

// ●音量設定
extern void FMR_Volume(U8 vol)
{
	if (vol > 0)
	{
		// 本当に小さい音量にするためにはDAC側の制御だけでは無理
		U16 n;
		switch (vol)
		{
		case 1: n = 0x10; break;
		case 2: n = 0xB0; break;
		default:
			n = 0xB0 | (U8)((((vol - 1) * 0xF) / MAX_VOLUME));
		}
		// B7:4: DSP Volume Control -> 0x0 or 0xD
		// B3:0: DAC Gain Control Bits (Volume)
		s_nReg05 = (s_nReg05 & 0xFF00) | n;
		Write(0x05, s_nReg05);

		// B14: Mute Disable -> Normal operation
		Write(0x02, s_nReg02 |= 0x4000);
	}
	else
	{
		// B14: Mute Disable -> Mute
		Write(0x02, s_nReg02 &= ~0x4000);
	}
}

// ●再生開始
extern void FMR_Start(void)
{
	// B15: Audio Output High-Z Disable -> Normal operation
	Write(0x02, s_nReg02 |= 0x8000);

	// アンプミュート解除
	DelayMs(200);
	MUTE = 0;
}

// ●再生停止
extern void FMR_Stop(void)
{
	// アンプミュート
	MUTE = 1;
	DelayMs(50);

	// B15: Audio Output High-Z Disable -> High impedance
	Write(0x02, s_nReg02 &= ~0x8000);
}

// ●選局(UP/DOWN)
extern void FMR_Select(U8 flag)
{
	if (flag) {
		if (++s_nChan >= s_nChanNum)
			s_nChan = 0;
	}
	else {
		if (--s_nChan == 0xFF)
			s_nChan = s_nChanNum - 1;
	}
	s_nFreq = ChanList[s_nChan].nFreq;
	SetFreq(s_nFreq);
}

// ●選局(Seek)
extern void FMR_Seek(void)
{
	U8 i = 0, idx = s_nChanNum - 1;

	// アンプミュート
	MUTE = 1;
	DelayMs(50);

	// LCD更新でノイズが入るためミュート
	Write(0x02, s_nReg02 & ~0x4000);
	Update(1);

	// B8: Seek = Enable
	Write(0x02, s_nReg02 | 0x0100);
	while (Read(0x02) & 0x0100) {
		DelayMs(10);
	}
	Write(0x02, s_nReg02 & ~0x4000);// ミュート解除されるため

	// B14: Seek/Tune Complete
	if ((Read(0x0A) & 0x2000) == 0)
	{
		// ロックした周波数以下で最も近いプリセットにしておく
		s_nFreq = GetFreq();
		do {
			if (ChanList[i].nFreq > s_nFreq)
				break;
			idx = i;
		}
		while (++i < s_nChanNum);
		s_nChan = idx;
	}

	Update(0);
	Write(0x02, s_nReg02);

	// アンプミュート解除
	DelayMs(100);
	MUTE = 0;
}

//=================================================================

// ●周波数を設定する
static void SetFreq(U16 freq)
{
	// REG 03H
	// B15:8: Channel Select
	// B2: Channel Spacing = 50 kHz
	// B1: Band Select
	// B0: Channel Spacing = 100 kHz
	U8 n;
	if (freq <= 9100) {
		// B15:8: Channel Select -> freq - 7600
		// B1: Band Select = 76-91 MHz (Japan)
		freq = (freq - 7600) / 5;
		n = 0x06;
	}
	else {
		// B15:8: Channel Select -> freq - 8750
		// B1: Band Select -> 87.5-108 MHz (US/Europe)
		freq = (freq - 8750) / 5;
		n = 0x04;
	}
	
	// LCD更新でノイズが入るためミュート
	Write(0x02, s_nReg02 & ~0x4000);

	Write(0x03, MAKEWORD(freq, n));
	DelayMs(250); // 待たないと後の(恐らく)ノイズのせいで正しく選局できない

	Update(0);
	Write(0x02, s_nReg02);
}

// ●周波数を取得する
static U16 GetFreq(void)
{
	U16 reg = Read(0x03);
	U8 sep = (reg & 0x0004)? 5 : ((reg & 0x0001)? 20 : 10);
	return HIBYTE(reg) * sep + ((reg & 0x0002)? 7600 : 8750);
}

// ●送信
static void Write(U8 addr, U16 data)
{
	U16 bits, mask;

	DisableINT();
	FM_SEN = 0;

	// Address (A8-A5,0,A3-A0)
	bits = ((U16)addr << 1) & 0x1E0;
	bits |= addr & 0x0F;

	mask = 0x0100; // 9Bits
	do {
		FM_SDIO = (bits & mask)? 1 : 0;
		FM_SCLK = 0;
		FM_SCLK = 1;
	}
	while (mask >>= 1);

	// Data
	mask = 0x8000; // 16Bits
	do {
		FM_SDIO = (data & mask)? 1 : 0;
		FM_SCLK = 0;
		FM_SCLK = 1;
	}
	while (mask >>= 1);

	FM_SEN = 1;
	FM_SDIO = 0;
	EnableINT();
}

// ●受信
static U16 Read(U8 addr)
{
	U16 bits, mask, data = 0;

	DisableINT();
	FM_SEN = 0;

	// Address (A8-A5,1,A3-A0)
	bits = ((U16)addr << 1) & 0x1E0;
	bits |= 0x10 | (addr & 0x0F);

	mask = 0x0100; // 9Bits
	do {
		FM_SDIO = (bits & mask)? 1 : 0;
		FM_SCLK = 0;
		FM_SCLK = 1;
	}
	while (mask >>= 1);

	FM_SDIO = 0;
	DIR_FM_SDIO = 1;

	// Data
	mask = 0x8000; // 16Bits
	do {
		FM_SCLK = 0;
		FM_SCLK = 1;
		data = (data << 1) | FM_SDIO;
	}
	while (mask >>= 1);

	FM_SEN = 1;
	DIR_FM_SDIO = 0;
	EnableINT();

	return data;
}

//=================================================================

// ●表示更新
static void Update(U8 seek)
{
	static rect_t NameR = {
		FMR_NAME_X, FMR_NAME_Y, 0, FMR_NAME_S };
	static rect_t PosR = {
		0, FMR_POS_Y, FMR_POS_W, FMR_POS_H };

	static text_t NameI = {
		FMR_NAME_X, FMR_NAME_Y, 0, FMR_NAME_S, 0, 0, 0, 0 };
	static text_t FreqI = {
		0, FMR_FREQ_Y, FMR_FREQ_C, FMR_FREQ_S, 0, 0, 0, TXT_2DIG };

	if (s_nNameR > FMR_NAME_X)
	{
		NameR.w = s_nNameR - FMR_NAME_X;
		LCD_Fill(&NameR, COL_SCREEN);
	}

	if (!seek)
	{
		// 局名領域
		NameI.c = FMR_NAME_C1;
		if (s_nFreq == ChanList[s_nChan].nFreq)
		{
			NameI.rams = ChanList[s_nChan].sName;
			NameI.roms = 0;
		}
		else {
			NameI.roms = "- -";
			NameI.rams = 0;
		}
		s_nNameR = LCD_Text(&NameI);

		// 周波数領域
		FreqI.x = FMR_FREQ_X1;
		FreqI.num = s_nFreq / 100;
		LCD_Text(&FreqI);

		FreqI.x = FMR_FREQ_X2;
		FreqI.num = s_nFreq % 100;
		LCD_Text(&FreqI);

		// POS領域
		if (s_nPosL > 0)
		{
			PosR.x = s_nPosL;
			LCD_Fill(&PosR, COL_SCREEN);
		}
		PosR.x = s_nPosL = FMR_POS_X + 
			((U32)(s_nFreq - MAX_FREQS) * FMR_POS_T + MAX_FREQW - 1) / MAX_FREQW;
		LCD_Fill(&PosR, FMR_POS_C);
	}
	else
	{
		// 局名領域
		NameI.c = FMR_NAME_C2;
		NameI.roms = "> Seeking..";
		NameI.rams = 0;
		s_nNameR = LCD_Text(&NameI);
	}
}
