#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_tim.h"



GPIO_InitTypeDef  GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;
TIM_OCInitTypeDef TIM_OC_InitStructure;

uint16_t mod_type = 0;


//фильтр ВЧ Бесселя 2-го порядка, многопроходный...
int filter_data [64];
//константы фильтра
int filter_const [3] = {0x7ed5fabb, 0x7dadc573, 0x7daa2576}; //100Hz срез
						//{0x7A50FB29, 0x74CDB0FA, 0x74763BB1} ;//500Hz срез
int filter_pass = 1; //количество проходов фильтра в разах [0-4]
					 //умножить на 2 = порядок фильтра







int k_gen, t_gen;




//описание структуры управляющих регистров Анны)))
//оригинальный чип MOS8364 называется Paula, почему бы не назвать свой алгоритм Анна?)))

struct Anna_registers  {
	int sample_offset; //адрес во флеше контроллера
	int play_position; //текущая позиция воспроизведения [16]:[16] = [целая]:[дробная]
	int play_speed; //скорость воспроизведения [16]:[16] относительно частоты инта tim2
	int interpolation_buffer; //пребуфер кубического интерполятора 4 байта
	uint16_t sample_length;//длинна в байтах
	uint16_t sample_loop;//позиция перехода play_position по достижению sample_length
	uint16_t volume;//громкость микшинга [8]:[8]
	uint16_t output;//результат вычислений. итоговая выборка
};

struct Anna_registers anna[8];




//описание структуры параметров семплов

struct sa {
	int adress; //адрес во флеше контроллера
	int length; //длинна в байтах
	int loop_point; //смещение от начала до начала цикла в байтах
	int loop_len; //длинна цикла в байтах
	uint8_t volume; //громкость по умолчанию
	uint8_t fine_tune; //подстройка частоты
};

struct sa sample[32];

//переменные для работы декодера паттернов и ордерлиста
uint8_t order_list[128], last_instrument[8];
uint8_t order_len, max_pattern;

//установка параметров по умолчанию
int mod_adress = 0x08002000,
	play_tempo = 125,
	timer_tick = 0;


uint8_t play_speed = 6,
		current_tick = 0,
		current_order = 0,
		current_row = 0;



//переменные хранения текущих эффектов
int volume_slide[8],
	portamento[8];


//константы для корректного воспроизведения
//получается почти Amiga PAL - частота тиков 50.5 Гц; частота С-2 - 8362.9
//если поиграться - возможны забавные результаты
int speed_constant = 90150;
int pitch_constant = 6434088 ;





//инициализия тактового генератора
void Init_MainClock()
{

	 RCC->CR |= RCC_CR_HSION; //Включить генератор HSI
	 while((RCC->CR & RCC_CR_HSIRDY)==0) {} //Ожидание готовности HSI
	 RCC->CFGR &= ~RCC_CFGR_PLLSRC; //Источником сигнала для PLL выбран HSI с делением на 2
	 RCC->CR &= ~RCC_CR_PLLON; //Отключить генератор PLL
	 RCC->CFGR &= ~RCC_CFGR_PLLMULL; //Очистить PLLMULL
	 RCC->CFGR |= RCC_CFGR_PLLMULL14; //Коефициент умножения
	 RCC->CR |= RCC_CR_PLLON; //Включить генератор PLL
	 while((RCC->CR & RCC_CR_PLLRDY)==0) {} //Ожидание готовности PLL
	 RCC->CFGR &= ~RCC_CFGR_SW; //Очистка битов выбора источника тактового сигнала
	 RCC->CFGR |= RCC_CFGR_SW_PLL; //Выбрать источником тактового сигнала PLL
	 while((RCC->CFGR&RCC_CFGR_SWS)!=0x08){} //Ожидание переключения на PLL
	 RCC->CFGR &= ~RCC_CFGR_HPRE; //Очистка битов предделителя "AHB Prescaler"
	 RCC->CFGR |= RCC_CFGR_HPRE_DIV1; //Установить "AHB Prescaler"
	 RCC->CFGR &= ~RCC_CFGR_PPRE1; //Очистка битов предделителя "APB1 Prescaler"
	 RCC->CFGR |= RCC_CFGR_PPRE1_DIV1; //Установить "APB1 Prescaler"
	 RCC->CFGR &= ~RCC_CFGR_PPRE2; //Очистка битов предделителя "APB2 Prescaler"
	 RCC->CFGR |= RCC_CFGR_PPRE2_DIV1; //Установить "APB2 Prescaler
	 }


//чтение байта из flash
uint8_t Flash_Read(uint32_t address) {

	return (*(__IO uint8_t*) address);
}




//инициализация шим, портов, и прерываний
void InitPWM()
{
	     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

	     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	     GPIO_Init(GPIOA, &GPIO_InitStructure);

	     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
	     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	     GPIO_Init(GPIOA, &GPIO_InitStructure);

	     TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	     TIM_TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	     TIM_TimeBase_InitStructure.TIM_Period = 1536;  //было 512
	     TIM_TimeBase_InitStructure.TIM_Prescaler = 0;
	     TIM_TimeBaseInit(TIM2, &TIM_TimeBase_InitStructure);

	     TIM_OC_InitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	     TIM_OC_InitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
	     TIM_OC_InitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
	     TIM_OC_InitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	     TIM_OC_InitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
	     TIM_OC_InitStructure.TIM_OutputState = TIM_OutputState_Enable;
	     TIM_OC_InitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
	     TIM_OC_InitStructure.TIM_Pulse = 128;
	     TIM_OC4Init(TIM2, &TIM_OC_InitStructure);

	     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	     GPIO_Init(GPIOA, &GPIO_InitStructure);

	     TIM_OC_InitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	     TIM_OC_InitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
	     TIM_OC_InitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
	     TIM_OC_InitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	     TIM_OC_InitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
	     TIM_OC_InitStructure.TIM_OutputState = TIM_OutputState_Enable;
	     TIM_OC_InitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
	     TIM_OC_InitStructure.TIM_Pulse = 128;
	     TIM_OC3Init(TIM2, &TIM_OC_InitStructure);

	     TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	     TIM_Cmd(TIM2, ENABLE);
	     NVIC_EnableIRQ(TIM2_IRQn);
			}






//обработчик прерывания TIM2
//слабонервным это лучше пропустить
void TIM2_IRQHandler(void)
{

//сохранение окружения
	asm ("push {r4 - r11}");

//сброс флага прерывания таймера 2
	asm ("mov r0, 0x42000000");
	asm ("mov r1, 0x0");
	asm ("str r1, [r0, #512]");

//микшер

	asm ("mov r8, 0");

	asm ("ldr r0, =anna");
	asm ("add r0, 22");
	asm ("ldrsh r1, [r0]");
	asm ("add r8, r1");

	asm ("add r0, 24");
	asm ("ldrsh r1, [r0]");
	asm ("add r8 , r1");

	asm ("add r0, 24");
	asm ("ldrsh r1, [r0]");
	asm ("add r8, r1");

	asm ("add r0, 24");
	asm ("ldrsh r1, [r0]");
	asm ("add r8, r1");

	asm ("add r0, 24");
	asm ("ldrsh r1, [r0]");
	asm ("add r8, r1");

	asm ("add r0, 24");
	asm ("ldrsh r1, [r0]");
	asm ("add r8, r1");

	asm ("add r0, 24");
	asm ("ldrsh r1, [r0]");
	asm ("add r8, r1");

	asm ("add r0, 24");
	asm ("ldrsh r1, [r0]");
	asm ("add r8, r1");

//фильтр ВЧ

  	asm ("ldr r10, =filter_data");
	asm ("ldr r11, =filter_const");

asm ("next_iteration:");

asm ("ldr r0, =filter_data");
asm ("ldr r2, =filter_pass");
asm ("ldr r1, [r2, 0]");
asm ("add r0, r0, r1, lsl 6");
asm ("cmp r10, r0");
		// все проходы посчитали?
asm ("it cs");
asm ("bcs skip_filter");  //выкл фильтра

	asm ("ldrd r2, r3, [r10, 8]");
	asm ("strd r2, r3, [r10, 0]");
//xv0 = xv1
	asm ("ldrd r2, r3, [r10, 16]");
	asm ("strd r2, r3, [r10, 8]");
//xv1 = xv2
	asm ("ldr r2, [r11, 0]");
	asm ("mov r1, 0");
			asm ("smull r3, r0, r2, r8");
	    	asm ("adds r3, r3, r1");
	    	asm ("adc r0, r0, 0");
	    	asm ("lsl r0, r0, 1");
	    	asm ("lsls r1, r3, 1");
	    	asm ("adc r0, r0 ,0");
	asm ("strd r0, r1, [r10, 16]");
//xv2 = input * gain
	asm ("ldrd r2, r3, [r10, 32]");
	asm ("strd r2, r3, [r10, 24]");
	asm ("ldrd r2, r3, [r10, 40]");
	asm ("strd r2, r3, [r10, 32]");
// yv0 = yv1;
// yv1 = yv2;
	asm ("ldrd r0, r1, [r10, 0]");
	asm ("ldrd r2, r3, [r10, 16]");
	asm ("add r0, r0, r2");
	asm ("adds r1, r1, r3");
	asm ("adc r0, r0, 0");
// r0:r1 = xv0 + xv2
	asm ("ldrd r2, r3, [r10, 8]");
	asm ("add r2, r2, r2");
	asm ("adds r3, r3, r3");
	asm ("adc r2, r2, 0");
// r2:r3 = 2*xv1
	asm ("sub r4, r0, r2");
	asm ("subs r5, r1, r3");
	asm ("sbc r4, r4, 0");
// r4:r5 = xv0 + xv2 - 2* xv1
	asm ("ldrd r0, r1, [r10, 24]");
	asm ("ldr r2, [r11, 4]");
			asm ("umull r3, r1, r2, r1");
			asm ("smull r3, r0, r2, r0");
			asm ("adds r3, r3, r1");
			asm ("adc r0, r0, 0");
			asm ("lsl r0, r0, 1");
			asm ("lsls r1, r3, 1");
			asm ("adc r0, r0 ,0");
//r0:r1 = yv0 * k1
	 asm ("sub r4, r4, r0");
	 asm ("subs r5, r5, r1");
	 asm ("sbc r4, r4, 0");
// r4:r5 = xv0 + xv2 - 2* xv1 - yv0 * k1
	 asm ("ldrd r0, r1, [r10, 32]");
	 asm ("add r4, r4, r0");
	 asm ("adds r5, r5, r1");
	 asm ("adc r4, r4, 0");
	 asm ("ldr r2, [r11, 8]");
			asm ("umull r3, r1, r2, r1");
			asm ("smull r3, r0, r2, r0");
			asm ("adds r3, r3, r1");
			asm ("adc r0, r0, 0");
			asm ("lsl r0, r0, 1");
			asm ("lsls r1, r3, 1");
			asm ("adc r0, r0 ,0");
	asm ("add r4, r4, r0");
	asm ("adds r5, r5, r1");
	asm ("adc r8, r4, 0");
	asm ("strd r8, r5, [r10, 40]");
	asm ("add r10, 64");

			asm ("b next_iteration");
			asm ("skip_filter:");

//аттенюатор громкости в зависимости от типа модуля
	asm ("ldr r0, =mod_type");
	asm ("ldrh r0, [r0]");
	asm ("add r0, 1");
	asm ("lsl r0, 3");
	asm ("sdiv r4, r8, r0");
	asm ("mov r0, 0x3"); //финальный регулятор громкости чем больше число - тем тише; номинальное значение 0х3
	asm ("sdiv r3, r4, r0");

//коррекция знаковый->беззнаковый
	asm ("add r3, 1536");

//проверка на клиппинг
	asm ("cmp r3, 0");
	asm ("it lt");
	asm ("movlt r3, 1");
	asm ("cmp.w r3, 3072");
	asm ("it gt");
	asm ("movwgt r3, 3071");

//деление на 2 и вывод в TIM2_CH4
	asm ("mov r7, 0x40000000");
	asm ("lsr r0, r3, 1");
	asm ("strh r0, [r7, 0x40]");
//виртуальный + 1 бит к шиму; только для мостового выхода; вывод в TIM2_CH3
	asm ("sub r3, r3, r0");
	asm ("rsb r3, r3, 1536");
	asm ("strh r3, [r7, 0x3c]");

//прибавили 1 к счетчику прерываний; тайминг для декодера паттернов
	asm ("ldr r7, =timer_tick");
	asm ("ldr r0, [r7, 0]");
	asm ("add r0, r0, 1");
	asm ("str r0, [r7, 0]");


//плеер семпла с накоплением фазы
	asm ("ldr 		r7, =anna");
	asm ("next_channel:");

	asm ("ldr		r1, [r7, 8]"); //r1 = Play_speed
	asm ("cmp		r1, 0");
	asm ("beq		skip_channel");
	asm ("ldr		r2, [r7, 4]"); //r2 = Play_position
	asm ("mov		r3, r2, lsr 16");
	asm ("add		r2, r2, r1"); //модификация позиции воспроизведения
	asm ("ldrh		r1, [r7, 16]");// r1 = sample_lenght
	asm ("cmp		r1, r2, lsr 16"); //доиграли до конца семпла?
	asm ("bcs		skip_check");
//если доиграли до конца - устанавливаем громкость на 0 и заглушаем канал
	asm ("ldrh		r4, [r7, 18]"); //r4 = sample_loop
	asm ("cmp		r1, r4");
	asm ("itt 		eq");
	asm ("moveq		r1, 0x1");
	asm ("strheq	r1, [r7, 20]");
	asm ("lsl		r2, 16");
	asm ("lsl   	r4, 16");
	asm ("add		r2, r4, r2, lsr 16");

asm ("skip_check:");
	asm ("str	r2, [r7, 4]"); // play_position = r2
	asm ("ldr	r4, [r7, 12]"); //r4 = interpolation_buffer
	asm ("sub	r3, r3, r2, lsr 16"); //проверяем на следующий семпл
	asm ("cbz	r3, skip_new_byte"); //переход если не требуется новый семпл
	asm ("ldr	r3, [r7, 0]"); //r3 = sample_addres
	asm ("add	r3, r3, r2, lsr 16");//r3 = текущий адрес в семпле
	asm ("ldrb	r1, [r3, 0]");// r1 = текущая выборка в семле
	asm ("add	r4, r1, r4, lsl 8"); //впихнули следующий семпл в буфер интерполяции
	asm ("str	r4, [r7, 12]"); //interpolation_buffer = r4
asm ("skip_new_byte:");

//самая важная инструкция; без неё не работает))
asm ("nop");

//кубическая интерполяция
	asm ("ubfx r10, r2, 8, 8");

	asm ("sbfx r3, r4, 0, 8");
	asm ("sbfx r2, r4, 8, 8");
	asm ("sbfx r1, r4, 16, 8");
	asm ("sbfx r0, r4, 24, 8");
// натыкали значений из буфера

	asm ("sub r4, r1, r2");
// r4 = r1 - r2

	asm ("sub r5, r3, r0");
	asm ("mov r6, 3");
	asm ("mla r4, r4, r6, r5");
// r4 = 3 * (r1 - r2) + r3 - r0

	asm ("revsh r3, r3");
	asm ("mul r4, r4, r10");
	asm ("sub r4, r4, r3");
	asm ("revsh r0, r0");
	asm ("revsh r1, r1");
	asm ("revsh r2, r2");
	asm ("mov r6, 2");
	asm ("mla r5, r0, r6, r4");
	asm ("mov r6, -5");
	asm ("mla r5, r1, r6, r5");
	asm ("mov r6, 4");
	asm ("mla r4, r2, r6, r5");
// r4 = 2r0 - 5*r1 + 4*r2 - r3 + x(3.......

	asm ("lsl r0, r0, 8");
	asm ("lsl r1, r1, 8");
	asm ("lsl r2, r2, 8");
	asm ("sub r5, r2, r0");
	asm ("mla r4, r4, r10, r5");
	asm ("asr r4, r4, 9");
	asm ("mla r4, r4, r10, r1");

// вычисление громкости текущей выборки
	asm ("ldrh r0, [r7, 20]");
	asm ("mov r1, r0, lsr 8");
	asm ("mul r4, r4, r1");
	asm ("mov r2, r4, asr 15");
	asm ("strh r2, [r7, 22]");

asm	("skip_channel:");

// все каналы посчитали?
asm ("add r7, 24");
asm ("ldr r0, =anna");
asm ("add r0, 168");//168");
asm ("cmp r0, r7");

asm ("bcs next_channel");//следующий канал



//обслуживание и обработка импульсов с генератора
	asm ("mov.w r7, 0x40000000");
	asm ("add r7, 0x10800");
	asm ("ldr r0, [r7, 8]");
	asm ("and.w r0, r0, 256");
	asm ("ldr r7, =k_gen");
	asm ("ldrh r1, [r7, 0]");
	asm ("lsr r1, r1, 1");
	asm ("orr r1, r0");
	asm ("ubfx r1, r1, 0, 9");
	asm ("strh r1, [r7, 0]");
	asm ("ldr r7, =t_gen");
	asm ("ldr r0, [r7, 0]");
	asm ("cmp r1, 255");
	asm ("it eq");
	asm ("addeq r0, 1");
	asm ("str r0, [r7, 0]");



//восстановление окружения; компилятор сохраняет только r0-r3 при входе в прерывание
	asm ("pop {r4 - r11}");
}

//инициализация структур семплов и ордерлиста
void Load_Mod_Header()
{
	int i, adr, current_sample_adress;
	adr = mod_adress + 950;
	order_len = Flash_Read (adr) ;
	adr  = adr + 2;
	max_pattern = 0;
	if (Flash_Read (mod_adress + 0x438) == 0x04D) mod_type = 0;
	else mod_type = 1;
	for (i=0;i<128;i++)
	{
		order_list[i] = Flash_Read (adr) ;
		if (order_list[i]>max_pattern) max_pattern = order_list[i];
		adr++;
	}
	adr = mod_adress + 20;
	current_sample_adress = mod_adress + 1084 + (1024*(1+mod_type)*(max_pattern+1));// для 8 каналов
	for (i=1;i<32;i++)
	{
		sample[i].adress = current_sample_adress;
		adr = adr + 22;
		sample[i].length = (Flash_Read (adr) * 256 + Flash_Read (adr + 1))*2;
		adr = adr + 2;
		sample[i].fine_tune = Flash_Read (adr) & 0xF;
		adr++;
		sample[i].volume = Flash_Read (adr);
		adr++;
		sample[i].loop_point = (Flash_Read (adr) * 256 + Flash_Read (adr + 1))*2;
		adr = adr + 2;
		sample[i].loop_len = (Flash_Read (adr) * 256 + Flash_Read (adr + 1))*2;
		adr = adr + 2;
		current_sample_adress = current_sample_adress + sample[i].length;
	}
}



//играем строчку патттерна
void Play_Row()
{
	int row_adr;
	uint8_t channel, instrument, effect, effect_param;
	uint16_t note;
	GPIOC->ODR ^=GPIO_Pin_9;

	row_adr = mod_adress + 1084 + (order_list[current_order]<<(10+mod_type)) + ((current_row-1)*16*(1+mod_type)); // для 8 каналов
	for (channel = 0; channel <(4+4*mod_type); channel++)
	{
		note = ((Flash_Read (row_adr) & 0xF)<<8) + Flash_Read (row_adr + 1);
		if (note != 0)
		{

			anna[channel].play_speed = (pitch_constant) / note;
			anna[channel].play_position = 0;
			instrument = (Flash_Read (row_adr) & 0xF0) + ((Flash_Read (row_adr + 2) & 0xF0)>>4);
			if (instrument != 0)
			{
				last_instrument[channel] = instrument;
				anna[channel].sample_offset = sample[instrument].adress;
				if (sample[instrument].loop_len == 2)
					{
					anna[channel].sample_length = sample[instrument].length ;
					anna[channel].sample_loop = sample[instrument].length;
					}
				else
					{
					anna[channel].sample_length = sample[instrument].loop_point + sample[instrument].loop_len  ;
					anna[channel].sample_loop = sample[instrument].loop_point ;
					}
			}
			else
				{
				instrument = last_instrument[channel];
				anna[channel].sample_offset = sample[instrument].adress;
				}
			anna[channel].volume = ((sample[instrument].volume) << 8);

		}
		effect = (Flash_Read(row_adr + 2) & 0xF);
		effect_param = Flash_Read (row_adr + 3);
//декодер команд; многие команды не обрабатываются за ненадобностью в данном случае
		switch (effect)
		{
			case 0:		//Arpegio
			break;

			case 1:		//Portamento Up :)))
				portamento[channel] = - effect_param ;
			break;

			case 2:		//Portamento Down :)))
				portamento[channel] = effect_param ;
			break;

			case 3:		//TonePortamento
			break;

			case 4:		//Vibrato
			break;

			case 5:		//ToneP + VolSlide
			break;

			case 6:		//Vibra + VolSlide
			break;

			case 7:		//Tremolo
			break;

			case 8:		//NOT USED
			break;

			case 9:		//SampleOffset :)))
				anna[channel].play_position = (effect_param << 8) * 0x10000;
			break;

			case 10:	//Volume Slide :)))
				if (effect_param > 15) volume_slide[channel] = (effect_param & 0xF0) << 4 ;
				else volume_slide[channel] = - ((effect_param & 0x0f) << 8) ;
			break;

			case 11:	//PositionJump :)))
				current_order = effect_param;
				current_row = 0;
			break;

			case 12:	//Set Volume :)))
				 anna[channel].volume = (effect_param << 8);
			break;

			case 13:	//PatternBreak :)))
				current_order++;
				if (current_order >= order_len)	current_order = 0;
				current_row = effect_param;
			break;

			case 14:	//Misc. Cmds

				switch(effect_param & 0xF0)
				{
					case 0xA0:
						volume_slide[channel] = ((effect_param & 0xF) << 8) / play_speed;
					break;

					case 0xB0:
						volume_slide[channel] = - ((effect_param & 0xF) << 8) / play_speed;
					break;
				}

			break;

			case 15:	//Set Speed :)))
				if (effect_param > 15) play_tempo = effect_param;
				else play_speed = effect_param;
			break;
		}
		row_adr = row_adr + 4;
		}

}

//играем один тик из строчки паттерна
void play_tick (void)
{
	uint8_t i;
			current_tick++;
			if (current_tick == play_speed)
			{
			current_tick = 0;
//сбрасываем параметры эффектов на новой строчке
			for (i = 0; i<(4+4*mod_type); i++)
				{
					volume_slide[i] = 0;
					portamento[i] = 0;
				}
			current_row++;
			Play_Row();
			if (current_row >= 64)
				{
					current_row = 0;
					current_order++;
					if (current_order >= order_len)
						{
						current_order = 0;
						}
				}
			}
			else for (i = 0; i<(4+4*mod_type); i++)
				{
				anna[i].play_speed = pitch_constant / ((pitch_constant / anna[i].play_speed) + portamento[i]);
				if (anna[i].play_speed > 0x20000) anna[i].play_speed = 0x20000;
				if (anna[i].play_speed < 0x20) anna[i].play_speed = 0x20;
				}

			for (i = 0; i<(4+4*mod_type); i++)
				{
				if ((anna[i].volume + volume_slide[i]) <= 0)
				{
					anna[i].volume = 0;
					volume_slide[i] = 0;
				}
				anna[i].volume = anna[i].volume + volume_slide[i];

					if (anna[i].volume > 0x4000) anna[i].volume = 0x4000;
					if (anna[i].volume < 0) anna[i].volume = 0;
				}
		}

//воспроизведение
void play (void)
{

	int z;
	z = ((timer_tick/1.2) + t_gen * 40) * play_tempo; //генераторный вариант
//коэффициенты 1.2 и 40 подбираются под текущий генератор

//	z = timer_tick * play_tempo; //батарейный вариант; получается почти Amiga PAL - частота тиков 50.56 Гц

	if (z > speed_constant)

	{
		timer_tick = 0;
		t_gen = 0;

	play_tick();
	}

}




//четыре процедуры и вся программа)))
int main(void)
{

	Init_MainClock();

	Load_Mod_Header();

	InitPWM();

    while(1)

	{
    	play();
	}
}



