
Допустим, есть глобальный фрейм g_Frame = 20 мс или 50 Гц
В этот фрейм влазит:
1. видео кадр. (половина от секамовского интерлейса)
1. звуковой фрейм 20 мс или 50 Гц
3. CPU_Clk / g_Frame тактов процессора
 
Видеокадр. 312.5 строк, 256 строк видимых + 56.5 строк на поля и обратный ход луча
1 строка - 64 мкс. 512 точек. 48 циклов доступа к памяти
512 точек == 32 слова == 32 цикла доступа к памяти. 
1 цикл доступа к памяти - 8 тактов 6МГц (считаем сразу 4 на экран + 4 на проц)
312.5 строк == 15000 циклов доступа к памяти

дальше надо как-то согласовать CPU_Clk, 3МГц и формирование экрана. 

сперва 28 строк * 48 циклов = 1344 цикла ничего не делаем

потом вывод на экран 12288 циклов
for (int i = 0; i < 256; i++)
{
	8 циклов ничего не делаем
	32 цикла выводим 32 слова
	8 циклов ничего не делаем
}

потом 28.5 строк * 48 циклов = 1368 циклов снова ничего не делаем

Просто надо сделать такую процедуру, как и с аудио фреймом. примерный множитель.
и сколько аудио фреймов влазит в такты инструкции проца, столько и делать.




!. Так вот, интервал времени между КСИ и началом отображения первой видимой строки в реальной БК0011М гораздо больше, чем в эмуляторе уважаемого GID prod., благодаря которому, удалось таки "вспомнить молодость". За что GID-у большое спасибо! :)

Прерывание по вектору 100 сделано на внешней схеме - триггер D3.2, логика D6.3, счетчик D28, триггер D35. Из сигнала синхронизации выделяется ССИ (строчный синхросигнал) и взводит счетчик D20, если приходит сигнал WTI - запись в сдвиговый регистр (вывод видео), происходит сброс счетчика. Если WTI больше нет (вывод на экран закончился) следующий ССИ запускает триггер D3.2 - происходит перывание по вектору 100. 
т.е прерывание происходит сразу после окончания вывода на экран, а не по КСИ. 

Достаточно интересным является вопрос момента фактической загрузки содержимого регистра смещения адреса начала видеобуфера в счетчик видеоадреса. Строб записи в старшие разряды счетчика видеоадреса достаточно длинный. То есть процессор может успеть поменять значение регистра несколько раз, но на выводимое изображение многократная замена не повлияет никак, поскольку строб записи формируется в момент кадрового синхроимпульса и изображение бланкировано. Окончательная фиксация значения в счетчике (ниспадающий фронт строба "переписывания") происходит за 40*256*8 (десятичное) тактов CLK до начала вывода изображения. Это объясняет странное значение 330 (восьмеричное) которое надо записать в регистр чтобы начало изображения соответствовало адресу 140000. При записи значения 330 из регистра смещения в счетчик видеоадреса, последний как раз "дотикает" до переполнения и условного нулевого значения к моменту начала вывода видеоизображения.


int nScreenStage = 0;	//номер этапа: 0 - верхнее поле, 1 - прорисовка экрана, 2 - нижнее поле
DWORD_PTR nAddress = 0;
int nScreenCycleCount = 0;	//счётчик циклов доступа к памяти
int nScreenLinesCount = 0;	//счётчик прорисованных строк
int nScreenLineBytesCount = 0; //счётчик прорисованных байтов в строке
int nScreenLinesStage = 0; //номер этапа прорисовки строки: 0 - левое поле, 1 - информация (32слова), 2 - правое поле
int nScreenLineCycleCount = 0; //счётчик циклов доступа к памяти при прорисовке строки
BYTE *pScreenAddress;

Make_One_Screen_Cycle()
{
	nScreenCycleCount++;

	switch(nScreenStage)
	{
		case 0:
		{	//этап 0 - верхнее поле
			if (nScreenCycleCount >= 50 * 48)
			{
				m_pScreenBufferAddress = m_pMemory;
				nScreenStage = 1;
				nScreenLinesCount = 0;
				nScreenLinesStage = 0;
				nScreenLineCycleCount = 0;
			}
			break;
		}
		case 1:
		{	//этап 1 - рисуем экран, 256 информационных строк
			nScreenLineCycleCount++;

			switch(nScreenLinesStage)
			{
				case 0:
				{
					if (nScreenLineCycleCount >= 8)
					{
						nScreenLinesStage = 1;
						nScreenLineBytesCount = 0;
					}
					break;
				}
				case 1:
				{
					DWORD_PTR nScreenAddr = GetScreenPage() << 14;
					m_pScreen->PrepareScreenLineByteRGB32(nScreenLinesCount, nScreenLineBytesCount++, m_pScreenBufferAddress[nScreenAddr]);
					m_pScreenBufferAddress++;
					m_pScreen->PrepareScreenLineByteRGB32(nScreenLinesCount, nScreenLineBytesCount++, m_pScreenBufferAddress[nScreenAddr]);
					m_pScreenBufferAddress++;

					if (nScreenLineBytesCount >= 64)
					{
						nScreenLinesStage = 2;

						if (++nScreenLinesCount >= 256)
						{
							CView *view = m_pParent->GetActiveView();

							if (view)
							{
								view->Invalidate(FALSE);
							}

							nScreenStage = 2;
							//вот тут происходит прерывание по вектору 100?
						}
					}
					break;
				}
				case 2:
				{
					if (nScreenLineCycleCount >= 48)
					{
						nScreenLinesStage = 0;
						nScreenLineCycleCount = 0;
					}
					break;
				}
			}
			break;
		}
		case 2:
		{	//этап 2 - нижнее поле + обратный ход луча
			if (nScreenCycleCount >= 15000)
			{
				nScreenStage = 0;
				nScreenCycleCount = 0;
			}
			break;
		}
	}
}


