
// ****************************************************************************
//
//                           Page OSC - oscilloscope
//
// ****************************************************************************

#include "../include.h"

#define OSC_FCHECK	0	// 1=DEBUG display sample frequency, 0=normal mode

// Required number of samples:
#define OSC_SAMPNUM	128	// number of samples in free-runing mode
#define OSC_AUTONUM	512	// number of samples in auto-sync mode
#define OSC_AUTONUMSLOW	256	// number of samples in auto-sync mode, slowest speed
// auto-run buffer: 2 channels, 1 sample u16, total 512*2*2 = 2048 bytes

#if OSC_FCHECK	// 1=DEBUG display sample frequency, 0=normal mode
u32 OSC_Freq;		// debug: sample frequency in [Hz]
#endif

Bool OSC_Auto = True;	// auto-sync
Bool OSC_Hold = False;	// hold mode
int OSC_Base = OSC_BASE_DEF; // current OSC time base
int OSC_ChNum;		// number of channels 1 or 2; 3=XY mode
int OSC_Speed;		// current speed (= time base without channels)
int OSC_SampNum;	// number of transferred samples
u16* OSC_SampPtr;	// pointer to start of samples

// OSC run mode text (4 characters, position 6..9)
const char* OSC_ModeTxtFree = "FREE";	// free fast running
const char* OSC_ModeTxtAuto = "AUTO";	// auto start
const char* OSC_ModeTxtHold = "HOLD";	// hold last sample

// OSC time base text (6 characters, position 10..15)
//  Title: "x OSC AUTO1 10ms"
const char* const OSC_BaseTxt[OSC_BASE_NUM] = {
	"1 10\x60s",	// OSC_BASE1_10US:  1 channel,  time base  10us, sample rate   3 MHz, total time 42.7us
	"1 30\x60s",	// OSC_BASE1_30US:  1 channel,  time base  30us, sample rate   1 MHz, total time  128us
	"1 100\x60",	// OSC_BASE1_100US: 1 channel,  time base 100us, sample rate 300 kHz, total time  427us
	"1 300\x60",	// OSC_BASE1_300US: 1 channel,  time base 300us, sample rate 100 kHz, total time 1.28ms
	"1  1ms",	// OSC_BASE1_1MS:   1 channel,  time base   1ms, sample rate  30 kHz, total time 4.27ms
	"1  3ms",	// OSC_BASE1_3MS:   1 channel,  time base   3ms, sample rate  10 kHz, total time 12.8ms
	"1 10ms",	// OSC_BASE1_10MS:  1 channel,  time base  10ms, sample rate   3 kHz, total time 42.7ms
	"1 30ms",	// OSC_BASE1_30MS:  1 channel,  time base  30ms, sample rate   1 kHz, total time  128ms
	"1 100m",	// OSC_BASE1_100MS: 1 channel,  time base 100ms, sample rate 300 Hz,  total time  427ms

	"2 20\x60s",	// OSC_BASE2_20US:  2 channels, time base  20us, sample rate 1.5 MHz, total time 85.3us
	"2 40\x60s",	// OSC_BASE2_40US:  2 channels, time base  40us, sample rate 750 kHz, total time  171us
	"2 100\x60",	// OSC_BASE2_100US: 2 channels, time base 100us, sample rate 300 kHz, total time  427us
	"2 300\x60",	// OSC_BASE2_300US: 2 channels, time base 300us, sample rate 100 kHz, total time 1.28ms
	"2  1ms",	// OSC_BASE2_1MS:   2 channels, time base   1ms, sample rate  30 kHz, total time 4.27ms
	"2  3ms",	// OSC_BASE2_3MS:   2 channels, time base   3ms, sample rate  10 kHz, total time 12.8ms
	"2 10ms",	// OSC_BASE2_10MS:  2 channels, time base  10ms, sample rate   3 kHz, total time 42.7ms
	"2 30ms",	// OSC_BASE2_30MS:  2 channels, time base  30ms, sample rate   1 kHz, total time  128ms
	"2 100m",	// OSC_BASE2_100MS: 2 channels, time base 100ms, sample rate 300 Hz,  total time  427ms

	"X 20\x60s",	// OSC_BASEX_20US:  XY channels, time base  20us, sample rate 1.5 MHz, total time 85.3us
	"X 40\x60s",	// OSC_BASEX_40US:  XY channels, time base  40us, sample rate 750 kHz, total time  171us
	"X 100\x60",	// OSC_BASEX_100US: XY channels, time base 100us, sample rate 300 kHz, total time  427us
	"X 300\x60",	// OSC_BASEX_300US: XY channels, time base 300us, sample rate 100 kHz, total time 1.28ms
	"X  1ms",	// OSC_BASEX_1MS:   XY channels, time base   1ms, sample rate  30 kHz, total time 4.27ms
	"X  3ms",	// OSC_BASEX_3MS:   XY channels, time base   3ms, sample rate  10 kHz, total time 12.8ms
	"X 10ms",	// OSC_BASEX_10MS:  XY channels, time base  10ms, sample rate   3 kHz, total time 42.7ms
	"X 30ms",	// OSC_BASEX_30MS:  XY channels, time base  30ms, sample rate   1 kHz, total time  128ms
	"X 100m",	// OSC_BASEX_100MS: XY channels, time base 100ms, sample rate 300 Hz,  total time  427ms
};

// Timer period setup, system frequence is 48MHz, prescaler 8-1
const int OSC_TimLoad[OSC_BASE_NUM] = {
	2-1,		// OSC_BASE1_10US:  1 channel,  time base  10us, sample rate   3 MHz, total time 42.7us
	6-1,		// OSC_BASE1_30US:  1 channel,  time base  30us, sample rate   1 MHz, total time  128us
	20-1,		// OSC_BASE1_100US: 1 channel,  time base 100us, sample rate 300 kHz, total time  427us
	60-1,		// OSC_BASE1_300US: 1 channel,  time base 300us, sample rate 100 kHz, total time 1.28ms
	200-1,		// OSC_BASE1_1MS:   1 channel,  time base   1ms, sample rate  30 kHz, total time 4.27ms
	600-1,		// OSC_BASE1_3MS:   1 channel,  time base   3ms, sample rate  10 kHz, total time 12.8ms
	2000-1,		// OSC_BASE1_10MS:  1 channel,  time base  10ms, sample rate   3 kHz, total time 42.7ms
	6000-1,		// OSC_BASE1_30MS:  1 channel,  time base  30ms, sample rate   1 kHz, total time  128ms
	20000-1,	// OSC_BASE1_100MS: 1 channel,  time base 100ms, sample rate 300 Hz,  total time  427ms

	2-1,		// OSC_BASE2_20US:  2 channels, time base  20us, sample rate 1.5 MHz, total time 85.3us
	8-1,		// OSC_BASE2_40US:  2 channels, time base  40us, sample rate 750 kHz, total time  171us
	20-1,		// OSC_BASE2_100US: 2 channels, time base 100us, sample rate 300 kHz, total time  427us
	60-1,		// OSC_BASE2_300US: 2 channels, time base 300us, sample rate 100 kHz, total time 1.28ms
	200-1,		// OSC_BASE2_1MS:   2 channels, time base   1ms, sample rate  30 kHz, total time 4.27ms
	600-1,		// OSC_BASE2_3MS:   2 channels, time base   3ms, sample rate  10 kHz, total time 12.8ms
	2000-1,		// OSC_BASE2_10MS:  2 channels, time base  10ms, sample rate   3 kHz, total time 42.7ms
	6000-1,		// OSC_BASE2_30MS:  2 channels, time base  30ms, sample rate   1 kHz, total time  128ms
	20000-1,	// OSC_BASE2_100MS: 2 channels, time base 100ms, sample rate 300 Hz,  total time  427ms

	2-1,		// OSC_BASEX_20US:  XY channels, time base  20us, sample rate 1.5 MHz, total time 85.3us
	8-1,		// OSC_BASEX_40US:  XY channels, time base  40us, sample rate 750 kHz, total time  171us
	20-1,		// OSC_BASEX_100US: XY channels, time base 100us, sample rate 300 kHz, total time  427us
	60-1,		// OSC_BASEX_300US: XY channels, time base 300us, sample rate 100 kHz, total time 1.28ms
	200-1,		// OSC_BASEX_1MS:   XY channels, time base   1ms, sample rate  30 kHz, total time 4.27ms
	600-1,		// OSC_BASEX_3MS:   XY channels, time base   3ms, sample rate  10 kHz, total time 12.8ms
	2000-1,		// OSC_BASEX_10MS:  XY channels, time base  10ms, sample rate   3 kHz, total time 42.7ms
	6000-1,		// OSC_BASEX_30MS:  XY channels, time base  30ms, sample rate   1 kHz, total time  128ms
	20000-1,	// OSC_BASEX_100MS: XY channels, time base 100ms, sample rate 300 Hz,  total time  427ms
};

// Setup ADC divider
const u8 OSC_AdcDiv[OSC_BASE_FIRST2] = {
	RCC_ADCCLK_DIV2,	// OSC_BASE1_10US:  sample rate   3 MHz or 1.5 MHz ... divider is not used
	RCC_ADCCLK_DIV2,	// OSC_BASE1_30US:  sample rate   1 MHz o 750 kHz ... divider is not used
	RCC_ADCCLK_DIV2,	// OSC_BASE1_100US: sample rate 300 kHz
	RCC_ADCCLK_DIV4,	// OSC_BASE1_300US: sample rate 100 kHz
	RCC_ADCCLK_DIV8,	// OSC_BASE1_1MS:   sample rate  30 kHz
	RCC_ADCCLK_DIV16,	// OSC_BASE1_3MS:   sample rate  10 kHz
	RCC_ADCCLK_DIV32,	// OSC_BASE1_10MS:  sample rate   3 kHz
	RCC_ADCCLK_DIV48,	// OSC_BASE1_30MS:  sample rate   1 kHz
	RCC_ADCCLK_DIV64,	// OSC_BASE1_100MS: sample rate 300 Hz
};

// Real ADC speed (system clock is 48MHz; we must count with 2 samples for 2 channels):
//  0 ( 10us, sample rate   3 or 1.5 MHz): divider 1, sampling time 0: 3.5+12.5=16 clock, real speed 48M/1/16 = 3Msps or 1.5Msps
//  1 ( 30us, sample rate   1 or 0.75 MHz): divider 1, sampling time 0: 3.5+12.5=16 clock, real speed 48M/1/16 = 3Msps or 1.5Msps
//  2 (100us, sample rate 300 kHz): divider  2, sampling time 1: 7.5+12.5=20 clock, real speed 48000/2/20 = 1200ksps or 600ksps
//  3 (300us, sample rate 100 kHz): divider  4, sampling time 2: 13.5+12.5=26 clock, real speed 48000/4/26 = 462ksps or 231ksps
//  4 (  1ms, sample rate  30 kHz): divider  8, sampling time 3: 28.5+12.5=41 clock, real speed 48000/8/41 = 146ksps or 73ksps
//  5 (  3ms, sample rate  10 kHz): divider 16, sampling time 4: 41.5+12.5=54 clock, real speed 48000/16/54 = 56ksps or 28ksps
//  6 ( 10ms, sample rate   3 kHz): divider 32, sampling time 5: 55.5+12.5=68 clock, real speed 48000/32/68 = 22ksps or 11ksps
//  7 ( 30ms, sample rate   1 kHz): divider 48, sampling time 6: 71.5+12.5=84 clock, real speed 48000/48/84 = 12ksps or 6ksps
//  8 (100ms, sample rate 300 Hz ): divider 64, sampling time 7: 239.5+12.5=252 clock, real speed 48000/64/252 = 3ksps or 1.5ksps

// Setup ADC sampling time
const u8 OSC_SampTime[OSC_BASE_FIRST2] = {
	0,	// OSC_BASE1_10US:  sample rate   3 MHz or 1.5 MHz
	0,	// OSC_BASE1_30US:  sample rate   1 MHz or 750 kHz
	1,	// OSC_BASE1_100US: sample rate 300 kHz
	2,	// OSC_BASE1_300US: sample rate 100 kHz
	3,	// OSC_BASE1_1MS:   sample rate  30 kHz
	4,	// OSC_BASE1_3MS:   sample rate  10 kHz
	5,	// OSC_BASE1_10MS:  sample rate   3 kHz
	6,	// OSC_BASE1_30MS:  sample rate   1 kHz
	7,	// OSC_BASE1_100MS: sample rate 300 Hz
};

// update time base
void OSC_SetBase(int base)
{
	OSC_Base = base;
	int chnum = 1;
	if (base >= OSC_BASE_FIRSTX)
	{
		base -= OSC_BASE_FIRSTX;
		chnum = 3;
	}
	else if (base >= OSC_BASE_FIRST2)
	{
		base -= OSC_BASE_FIRST2;
		chnum = 2;
	}
	OSC_ChNum = chnum;
	OSC_Speed = base;
}

// OSC initialize
void OSC_Init()
{
	// setup input pins
	GPIO_Mode(OSC_GPIO1, GPIO_MODE_AIN);
	GPIO_Mode(OSC_GPIO2, GPIO_MODE_AIN);

	// enable DMA1 clock
	RCC_DMA1ClkEnable();

	// Enable Timer clock source
	TIM1_ClkEnable();

	// enable clock for ADC
	RCC_ADC1ClkEnable();

	// reset ADC module
	RCC_ADC1Reset();

	// set 'Hold' key to long mode
	KeyHoldLong();
}

// OSC terminate
void OSC_Term()
{
	// set 'Hold' key to default short mode
	KeyHoldShort();

	// reset Timer
	TIM1_Reset();

	// stop DMA
	DMA_ChanDisable(DMA1_Chan(OSC_DMA));

	// Timer clock disable
	RCC_TIM1ClkDisable();

	// ADC disable
	ADC1_Disable();

	// reset input pins
	GPIO_PinReset(OSC_GPIO1);
	GPIO_PinReset(OSC_GPIO2);
}

// setup ADC
NOINLINE void OSC_SetupADC()
{
	// ADC disable ... needed to synchronize sequence of the channels
	ADC1_Disable();

	// get speed index
	int speed = OSC_Speed;

	// set ADC divider
	RCC_ADCDiv(OSC_AdcDiv[speed]);	// set divider (ignored if divider is disabled)
	if (speed <= 1)		// fast mode, ADC clock speed 48MHz
		RCC_ADCDivDisable();
	else
		RCC_ADCDivEnable();

	// set sampling time
	int samptime = OSC_SampTime[speed];
	ADC1_SampTime(OSC_ADC1, samptime);
	ADC1_SampTime(OSC_ADC2, samptime);

	// select channels to be converted
	ADC1_RSeq(1, OSC_ADC1);
	ADC1_RSeq(2, OSC_ADC2);

	// set number of channels to be converted in a regular channel conversion sequence
	int chnum = OSC_ChNum;
	if (chnum == 3) chnum = 2;
	ADC1_RNum(chnum);

	// scan enable
	if (chnum == 1)
		ADC1_ScanDisable();
	else
		ADC1_ScanEnable();

	// select trigger of rule channel conversion
	if (speed == 0)
	{
		// fast mode - select ADC free running mode
		ADC1_ExtSel(ADC_EXT_SWSTART);
		ADC1_Cont();

		// disable externally triggered conversion for rule channels
		ADC1_ExtTrigDisable();
	}
	else
	{
		// slow mode - trigger with Timer TRGO
		ADC1_ExtSel(ADC_EXT_T1_TRGO);
		ADC1_Single();

		// enable externally triggered conversion for rule channels
		ADC1_ExtTrigEnable();
	}

	// enable DMA transfer
	ADC1_DMAEnable();
}

// setup Timer
NOINLINE void OSC_SetupTimer()
{
	// Reset timer to default setup
	TIM1_Reset();

	// get speed index
	int speed = OSC_Speed;
	if (speed != 0)	// do not use timer in fast mode
	{
		// select input from internal clock CK_INT
		TIM1_InMode(TIM_INMODE_INT);

		// set prescaler to 8 - clock will be 48/8 = 6 MHz
		TIM1_Presc(8-1);

		// set reload value
		TIM1_Load(OSC_TimLoad[OSC_Base]);

		// reload immediately
		TIM1_Update();

		// reset counter
		TIM1_Cnt(0);

		// generate TRGO signal to other peripherals
		TIM1_MSSync(TIM_MSSYNC_UPDATE);
	}
};

// setup DMA channel
NOINLINE void OSC_SetupDMA()
{
	// get address of DMA1 channel
	DMAchan_t* chan = DMA1_Chan(OSC_DMA);

	// set source address - ADC regular data register (as peripheral address)
	DMA_PerAddr(chan, &ADC1->RDATAR);

	// set destination addres (as memory address)
	DMA_MemAddr(chan, Buf);

	// set number of entries
	int num = OSC_SAMPNUM;			// number of samples in free running mode, 1 channel
	if (OSC_Auto) num = OSC_AUTONUM;	// number of samples in auto-sync mode, 1 channel
	if (OSC_Speed == OSC_BASE_LAST1) num = OSC_AUTONUMSLOW; // number of samples in auto-sync mode, slowest speed
	if (OSC_ChNum != 1) num *= 2; 		// double samples if 2 or XY channels
	OSC_SampNum = num;			// save number of samples
	DMA_Cnt(chan, num);

	// configure DMA channel
	DMA_Cfg(chan,
	//	DMA_CFG_EN |			// channel enable
	//	DMA_CFG_COMPINT |		// completion interrupt enable
	//	DMA_CFG_HALFINT |		// over half interrupt enable
	//	DMA_CFG_TRANSERRINT |		// transmission error interrupt enable
	//	DMA_CFG_DIRFROMMEM |		// transfer direction from memory
		DMA_CFG_DIRFROMPER |		// ... transfer direction from peripheral
	//	DMA_CFG_CIRC |			// circular mode enabled
	//	DMA_CFG_PERINC |		// peripheral address increment
		DMA_CFG_MEMINC |		// memory address increment
	//	DMA_CFG_PSIZE(size) |		// peripheral data size DMA_SIZE_*
	//	DMA_CFG_PSIZE_8 |		// ... peripheral data size 8 bits
		DMA_CFG_PSIZE_16 |		// ... peripheral data size 16 bits
	//	DMA_CFG_PSIZE_32 |		// ... peripheral data size 32 bits
	//	DMA_CFG_MSIZE(size) |		// memory data size DMA_SIZE_*
	//	DMA_CFG_MSIZE_8 |		// ... memory data size 8 bits
		DMA_CFG_MSIZE_16 |		// ... memory data size 16 bits
	//	DMA_CFG_MSIZE_32 |		// ... memory data size 32 bits
	//	DMA_CFG_PRIOR(prior) |		// channel priority 0..3
	//	DMA_CFG_PRIOR_LOW |		// ... channel priority 0 low
	//	DMA_CFG_PRIOR_MED |		// ... channel priority 1 medium
	//	DMA_CFG_PRIOR_HIGH |		// ... channel priority 2 high
		DMA_CFG_PRIOR_VERYHIGH |	// ... channel priority 3 very high
	//	DMA_CFG_MERM2MEM |		// memory-to-memory mode enable
		0);

	// clear trasmission completion flag ... must be cleared after ADC setup
	DMA1_CompClr(OSC_DMA);
}

#define OSC_PREFIXW	16	// synchronization prefix width

// Auto-synchronize
void OSC_AutoSync()
{
	int i;

	// pointer to start of samples
	OSC_SampPtr = (u16*)Buf;
	if (!OSC_Auto) return; // not auto-sync

	// sample increment (1 or 2 samples, = number of channels)
	int ds = OSC_ChNum;
	if (ds == 3) ds = 2;

	// total number of double-samples
	int sampnum = OSC_SampNum / ds;

	// pointer to start of prefix
	u16* s1 = (u16*)Buf; 

	// calculate prefix
	u16* s = s1;
	u32 pref = 0;
	for (i = OSC_PREFIXW; i > 0; i--)
	{
		pref += *s & 0xfff;
		s += ds;
	}

	// calculate postfix
	u16* s2 = s;
	u32 post = 0;
	for (i = OSC_PREFIXW; i > 0; i--)
	{
		post += *s2 & 0xfff;
		s2 += ds;
	}

// s1 = pointer to start of prefix
// s = middle pointer (start of postfix)
// s2 = pointer behind end of postfix

	// prepare to search edge
	int bestdif = 0;	// best difference
	u16* bests = s;		// pointer to best sample

	// search edge
	for (i = sampnum - OSC_PREFIXW - OSC_SAMPNUM; i > 0; i--)
	{
		// get current edge
		int dif = post - pref;

		// bonus if flip over zero axis
		if ((pref < OSC_PREFIXW*2048) && (post >= OSC_PREFIXW*2048)) dif += dif;

		// better edge
		if (dif > bestdif)
		{
			bestdif = dif;
			bests = s;
		}

		// shift prefix
		pref -= *s1 & 0xfff;
		s1 += ds;
		pref += *s & 0xfff;

		// shift postfix
		post -= *s & 0xfff;
		s += ds;
		s2 += ds;
		post += *s2 & 0xfff;
	}

	// save result
	OSC_SampPtr = bests;
}

// OSC update
FASTCODE void NOFLASH(OSC_Update)()
{
	int i;

	// setup Timer
	OSC_SetupTimer();

	// setup DMA channel ... must be prepared before ADC setup
	OSC_SetupDMA();

	// setup ADC ... must be prepared after DMA setup
	OSC_SetupADC();

	// clear trasmission completion flag ... must be cleared after ADC setup, buf before DMA start transfer
	DMA1_CompClr(OSC_DMA);

	// start DMA transfer
	DMA_ChanEnable(DMA1_Chan(OSC_DMA));

	// ADC enable and wake-up
	ADC1_Enable();
	WaitMs(1);
	ADC1_Enable();	// 2nd ON to start CONT conversion (or it will not start)

	// start
	if (OSC_Speed == 0)
	{
		// ADC software start ... in fast mode, timer is not used
		ADC1_SwStart();
	}
	else
	{
		// enable timer ... in slow mode, ADC is controlled by the timer
		TIM1_Enable();
	}

#if OSC_FCHECK	// 1=DEBUG display sample frequency, 0=normal mode
	cb();
	di();
	cb();
	u32 t1 = Time();
	cb();
#endif

	// wait for transfer to complete
	while (!DMA1_Comp(OSC_DMA))
	{
		// break with key
		if (KeyBuf != NOKEY) break;
	}

#if OSC_FCHECK	// 1=DEBUG display sample frequency, 0=normal mode
	cb();
	u32 t2 = Time();
	cb();
	ei();
	cb();
#endif

	// stop Timer
	TIM1_Disable();

	// stop DMA
	DMA_ChanDisable(DMA1_Chan(OSC_DMA));

	// disable DMA transfer
	ADC1_DMADisable();

	// clear complete flag
	DMA1_CompClr(OSC_DMA);

	// ADC disable
	ADC1_Disable();

	// Auto-synchronize
	OSC_AutoSync();

#if OSC_FCHECK	// 1=DEBUG display sample frequency, 0=normal mode
	u32 dt = t2 - t1;
	OSC_Freq = (u32)(((s64)SystemCoreClock * (OSC_SampNum/OSC_ChNum) + dt/2) / dt);
#endif
}

// OSC display title
void OSC_DispTitle()
{
	// select font 8x12
	SelFont12();

	// display run mode
	const char* t = OSC_ModeTxtFree;
	if (OSC_Auto) t = OSC_ModeTxtAuto;
	if (OSC_Hold) t = OSC_ModeTxtHold;
	int x = (WIDTH - PageX - 10*8)/2 + PageX;
	DrawText(t, x, ROW0_Y); // display run mode
	x += 4*8;

	// display time base
	DrawText(OSC_BaseTxt[OSC_Base], x, ROW0_Y);

#if OSC_FCHECK	// 1=DEBUG display sample frequency, 0=normal mode
	DrawRectClr(x, ROW0_Y, WIDTH-1-x, FONTH);
	DecUNum(DecNumBuf, OSC_Freq, '\'');
	DrawText(DecNumBuf, x, ROW0_Y);
#endif
}

// OSC display 1 curve
void OSC_DispCurve(u16* s, int ds, int y, int h)
{
	u16 d;
	int x, y2, yold;
	for (x = 0; x < WIDTH; x++)
	{
		// load sample
		d = *s & 0xfff;
		s += ds;

		// recalc to Y coordinate
		y2 = (d*h + (0xfff >> 1)) >> 12;
		if (y2 >= h) y2 = h-1;

		// Y graphics coordinate
		y2 = y + h - 1 - y2;

		// draw line (not first sample)
		if (x > 0)
		{
			DrawLineSetFast(x-1, yold, x, y2);
		}

		// save current Y coordinate
		yold = y2;
	}

	// display middle line
	y += h/2;
	for (x = 0; x < WIDTH; x += 3) DrawPointSetFast(x, y);

	// display scale marks
	y--;
	for (x = 0; x < WIDTH; x += 30) DrawVLineSet(x, y, 3);
}

// OSC display XY
void OSC_DispXY()
{
	int x, y, oldx, oldy, i;
	u16* s = OSC_SampPtr;

#define SCALEY	(HEIGHT-TITLE_H) // height of XY screen

	// prepare start X coordinate
	oldx = ((s[0] & 0xfff)*WIDTH + (0xfff >> 1)) >> 12;
	if (oldx >= WIDTH) oldx = WIDTH-1;

	// prepare start Y coordinate
	oldy = ((s[1] & 0xfff)*SCALEY + (0xfff >> 1)) >> 12;
	if (oldy >= SCALEY) oldy = SCALEY-1;
	oldy = HEIGHT-1 - oldy;

	for (i = OSC_SAMPNUM-1; i > 0; i--)
	{
		// shift pointer
		s += 2;

		// prepare current X coordinate
		x = ((s[0] & 0xfff)*WIDTH + (0xfff >> 1)) >> 12;
		if (x >= WIDTH) x = WIDTH-1;

		// prepare current Y coordinate
		y = ((s[1] & 0xfff)*SCALEY + (0xfff >> 1)) >> 12;
		if (y >= SCALEY) y = SCALEY-1;
		y = HEIGHT-1 - y;

		// draw line
		DrawLineSetFast(oldx, oldy, x, y);

		// shift coordinates
		oldx = x;
		oldy = y;
	}
}

// OSC display
void OSC_Disp()
{
	u16* s = OSC_SampPtr;

	// clear screen
	DrawRectClrFast(0, TITLE_H, WIDTH, HEIGHT-TITLE_H);

	// OSC display title
	OSC_DispTitle();

	// 1 channel
	if (OSC_ChNum == 1)
	{
		OSC_DispCurve(s, 1, TITLE_H, HEIGHT-TITLE_H);
	}

	// 2 channels
	else if (OSC_ChNum == 2)
	{
		OSC_DispCurve(s+1, 2, TITLE_H, 24);
		OSC_DispCurve(s, 2, TITLE_H+24, 24);
	}

	// XY channels
	else
		OSC_DispXY();

	// display update
	DispUpdate();
}

// Page OSC (returns key PREV/NEXT)
u8 PageOSC()
{
	int i;
	u8 key;

	// update time base
	OSC_SetBase(OSC_Base);

	// OSC initialize
	OSC_Init();
	OSC_Disp();	

	while (True)
	{
		// reload watchdog counter
		IWDG_Reload();

		// update in not holding
		if (!OSC_Hold)
		{
			// OSC display
			OSC_Disp();

			// OSC update
			OSC_Update();
		}

		// keyboard input
		key = KeyGet();
		switch (key)
		{
		// Prev
		case KEY_PREV:
		// Next
		case KEY_NEXT:
			// OSC terminate
			OSC_Term();
			return key;

		// Fast - faster mode
		case KEY_FAST:
			i = OSC_Base-1;
			if (i < 0) i = OSC_BASE_NUM-1;
			OSC_Hold = False;
			OSC_SetBase(i);
			OSC_DispTitle();
			DispUpdate();
			break;

		// Slow - slower mode
		case KEY_SLOW:
			i = OSC_Base+1;
			if (i >= OSC_BASE_NUM) i = 0;
			OSC_Hold = False;
			OSC_SetBase(i);
			OSC_DispTitle();
			DispUpdate();
			break;

		// Hold - hold display
		case KEY_HOLD:
			OSC_Hold = !OSC_Hold;
			// do not redraw display, hold current display content - update only title
			OSC_DispTitle();
			DispUpdate();
			break;

		// Hold long - Auto mode
		case KEY_HOLD_LONG:
			OSC_Auto = !OSC_Auto;
			OSC_DispTitle();
			DispUpdate();
			break;
		}
	}
}
