<?xml version='1.0' encoding="utf-8"?>
      <rss version='2.0'>
      <channel>
      <title>Форум на Исходниках.RU</title>
      <link>https://forum.sources.ru</link>
      <description>Форум на Исходниках.RU</description>
      <generator>Форум на Исходниках.RU</generator>
  	
      <item>
        <guid isPermaLink='true'>https://forum.sources.ru/index.php?showtopic=385717&amp;view=findpost&amp;p=3397132</guid>
        <pubDate>Sun, 12 Jan 2014 00:04:41 +0000</pubDate>
        <title>Низкоуровневое программирование видеоадаптеров ATI (часть вторая)</title>
        <link>https://forum.sources.ru/index.php?showtopic=385717&amp;view=findpost&amp;p=3397132</link>
        <description><![CDATA[shm: <div class='tag-align-center'><span class="tag-color tag-color-named" data-value="blue" style="color: blue"><span class='tag-size' data-value='13' style='font-size:13pt;'><strong class='tag-b'>Низкоуровневое программирование видеоадаптеров ATI (часть вторая)</strong></span></span></div><br>
<div class='tag-align-center'><span class="tag-color tag-color-named" data-value="blue" style="color: blue"><span class='tag-size' data-value='12' style='font-size:12pt;'><strong class='tag-b'>Установка видеорежима с нужными параметрами через регистры</strong></span></span></div><br>
	С момента выхода первой части этой статьи мне начали периодически задавать этот вопрос о том как это делать и я вот таки решился заняться исследованием (и таки достиг некоторого успеха). Если раньше это было необходимо для того, чтобы &quot;избавиться&quot; от 60hz на ЭЛТ мониторах, то теперь встала проблема отсутствия поддержки нестандартных видеорежимов в VBE. Сразу оговорюсь, что у меня на руках сейчас две карточки: с rv630 и rv730. Соответственно примеры будут рабочими с большой вероятностью на rv6xx и rv7xx, частично на rv5xx. На более новых карточках работать не будет, но там все очень похоже (по факту просто регистры переместили), поэтому желающие могут портировать. <br>
<br>
<div class='tag-align-center'><span class="tag-color tag-color-named" data-value="green" style="color: green"><span class='tag-size' data-value='11' style='font-size:11pt;'><strong class='tag-b'>EDID</strong></span></span></div><br>
	Для начала я расскажу некоторые общие сведения. Наверное, все замечали, что операционная система &quot;видит&quot; список видеорежимов, которые поддерживает монитор и не дает установить &quot;левые&quot;. Но я так понял, что мало кто задумывался как видеодрайвер &quot;узнает&quot; этот список. Так вот даже у аналогового VGA есть pin&#39;ы зарезервированные для <a class='tag-url' href='http://ru.wikipedia.org/wiki/Display_Data_Channel' target='_blank'>DDC</a>,  через него передается такая штука как <a class='tag-url' href='http://en.wikipedia.org/wiki/Extended_display_identification_data' target='_blank'>EDID</a>. По ссылке есть исчерпывающее описание структуры EDID, поэтому я не буду ее здесь описывать. Она очень проста, все поля имеют абсолютное смещение, ничего вычислять не надо. По сути это просто набор полей с характеристиками монитора, там есть такие &quot;архиважные&quot; вещи как его название, изготовитель, габариты, но они нам не интересны. Самое важно там - это наличие списка доступных видеорежимов, а также  VGA-тайминги для &quot;родного&quot; режима монитора. Именно их мы будем заносить в управляющие регистры CRTC. Поэтому убедительная просьба сходить по <a class='tag-url' href='http://wiki.osdev.org/Video_Signals_And_Timing' target='_blank'>ссылке</a> и почитать, что это такое (про GTF можно не читать). Тайминги стандартных видеорежимов можно без труда найти в гугле по примерно такому запросу &quot;vga timings 800x600 60hz&quot;. Еще раз повторюсь, что тайминги родного пусть даже трижды стандартного режима можно (и нужно) получить из EDID. Теперь о том как получить этот самый EDID. Я потратил на это уйму времени и нашел довольно таки универсальный способ для ati-шных карточек и даже некоторых других (&#33;). Узнал я о нем во время дизассемблирования VGA BIOS карточки. Способ заключается в использование малоизвестной фунции расширения VESA BIOS 4F15h. Читаем <a class='tag-url' href='ftp://ftp.cis.nctu.edu.tw/pub/csie/Software/X11/private/VeSaSpEcS/VESA_Document_Center_Monitor_Interface/VBEDDC11.pdf' target='_blank'>VESA BIOS Extensions/Display Data Channel Standard</a> там есть исчерпывающее описание как это делать. Внимательный читатель заметил примечание, что в CX реально заносится номер монитора. В случае успеха прерывание возвращает в регистре AX код 4F (как и все сервисы VBE). От себя лишь приведу пример (Borland C++ под DOS)<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">char edid[128];</div><div class="code_line">_ES = (unsigned short) ( (unsigned long)&amp;edid &#62;&#62; 16);</div><div class="code_line">_DI = (unsigned short)&edid;</div><div class="code_line">_AX = 0x4F15;</div><div class="code_line">_BL = 0x01;</div><div class="code_line">_CX = 0;</div><div class="code_line">asm int 10h;</div><div class="code_line">&nbsp;</div><div class="code_line">printf(&quot;result = %X&quot;, _AX);</div><div class="code_line">&nbsp;</div><div class="code_line">FILE *f = fopen(&quot;edid.bin&quot;, &quot;wb&quot;);</div><div class="code_line">fwrite(edid, 1, sizeof(edid), f);</div><div class="code_line">fclose(f);</div></ol></div></div></div></div><script>preloadCodeButtons('1');</script><br>
Думаю, комментарии излишни.<br>
EDID можно читать непосредственно через регистры, но это очень специфично для каждой карточки: мой код от rv630 ничего не прочитал на rv730. Вот код для rv630 для жутко любопытных читает EDID  первого монитора с DDC2. Все регистры описаны описаны в <a class='tag-url' href='http://developer.amd.com/wordpress/media/2012/10/42590_m76_rrg_1.01o.pdf' target='_blank'>M76 Register Reference Guide</a>. Но все же лучше использовать VBE: EDID можно сохранить до входа в PM32.<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">OUTREG(0x7D30, (1 &#60;&#60; 8));</div><div class="code_line">OUTREG(0x7D64, (1 &#60;&#60; 0) | (1 &#60;&#60; 12) | (1 &#60;&#60; 13) | (128 &#60;&#60; 16));</div><div class="code_line">OUTREG(0x7D74, (0x1 &#60;&#60; 31) &nbsp;| (1 &#60;&#60; 8) );</div><div class="code_line">OUTREG(0x7D30, INREG(0x7D30) | 1);</div><div class="code_line">while( (INREG(0x7d3c) &amp; (0x1 &#60;&#60; 2)) == 0);</div><div class="code_line">OUTREG(0x7D38, INREG(0x7D38) | (0x1 &#60;&#60; 1)); </div><div class="code_line">OUTREG(0x7D74, (0x1 &#60;&#60; 31) | (0x1 &#60;&#60; 0) &nbsp;| (3 &#60;&#60; 16) );</div><div class="code_line">BYTE edid[128];</div><div class="code_line">for(int i = 0; i &#60; sizeof(edid); i++)</div><div class="code_line">{</div><div class="code_line">&nbsp;&nbsp; &nbsp;DWORD val = INREG(0x7D74);</div><div class="code_line">&nbsp;&nbsp; &nbsp;edid[i] = (val &#62;&#62; 8) &amp; 0xFF;</div><div class="code_line">}</div><div class="code_line">OUTREG(0x7D30, 0x2);</div><div class="code_line">Delay(10);</div><div class="code_line">OUTREG(0x7D30, 0);</div></ol></div></div></div></div><br>
Функция OUTREG - это просто запись двойного слова по соответствующему адресу в MMIO.<br>
<br>
<div class='tag-align-center'><span class="tag-color tag-color-named" data-value="green" style="color: green"><span class='tag-size' data-value='11' style='font-size:11pt;'><strong class='tag-b'>Настройка CRTC для нужного видеорежима</strong></span></span></div><br>
<div class='tag-align-center'><strong class='tag-b'><span class="tag-color tag-color-named" data-value="red" style="color: red">Дисклемейкер: непосредственная запись некорректных значений в блок регистров CRTC или PLL может поломать ваш монитор или карточку.</span></strong></div><br>
Заниматься экспериментами с регистрами контроллера можно и проще прямо из под Windows. Для этого качаем библиотеку WinIo, все необходимое для использования есть в Гугле, я не буду тут останавливаться на этом. Приведу лишь функции для работы с блоком MMIO регистров.<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">DWORD INREG(DWORD Addr)</div><div class="code_line">{</div><div class="code_line">&nbsp;&nbsp; &nbsp;DWORD dwPortVal;</div><div class="code_line">&nbsp;&nbsp; &nbsp;SetPortVal(IoBase, Addr, 4);</div><div class="code_line">&nbsp;&nbsp; &nbsp;GetPortVal(IoBase + 4, &amp;dwPortVal, 4);</div><div class="code_line">&nbsp;&nbsp; &nbsp;return dwPortVal;</div><div class="code_line">}</div><div class="code_line">&nbsp;</div><div class="code_line">void OUTREG(DWORD Addr, DWORD Val)</div><div class="code_line">{</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;SetPortVal(IoBase, Addr, 4);</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;SetPortVal(IoBase + 4, Val, 4);</div><div class="code_line">}</div></ol></div></div></div></div><br>
IoBase можно узнать в диспетчере устройств Windows. Или хитрым способом (узнал из листинга VGA BIOS карточки):<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">GetPortVal(0x3C3, &amp;IoBase, 1);</div><div class="code_line">IoBase &#60;&#60;= 8;</div></ol></div></div></div></div><br>
Как видим, якобы стандартный VGA регистр 0x3C3 содержит на Ati&#39;шных карточках старшую часть адреса блока расширенных IO регистров (младшая всегда равна 0). С помощью нее VGA BIOS работает c MMIO без перехода в PM32 и иных, более хитрых, способов. <br>
	Регистры CRTC описаны в открытой части документации от AMD, а именно в <a class='tag-url' href='http://developer.amd.com/wordpress/media/2012/10/42590_m76_rrg_1.01o.pdf' target='_blank'>M76 Register Reference Guide</a>. Но что интересно: регистры PLL не описаны ни где, а без них невозможно сменить видеорежим. Вообще существует достаточно простой способ настойки CRTC для конкретного видеорежима: достаточно сделать дамп регистров в файл из под Windows и загрузить их уже где требуется.<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">static DWORD CrtcReg[] =</div><div class="code_line">{</div><div class="code_line">&nbsp;&nbsp; &nbsp;0x00404,</div><div class="code_line">&nbsp;&nbsp; &nbsp;0x00430,</div><div class="code_line">&nbsp;&nbsp; &nbsp;0x0043C,</div><div class="code_line">&nbsp;&nbsp; &nbsp;0x00470,</div><div class="code_line">&nbsp;&nbsp; &nbsp;0x06000,</div><div class="code_line">&nbsp;&nbsp; &nbsp;0x06004,</div><div class="code_line">&nbsp;&nbsp; &nbsp;0x06008,</div><div class="code_line">&nbsp;&nbsp; &nbsp;0x0600C,</div><div class="code_line">&nbsp;&nbsp; &nbsp;0x06020,</div><div class="code_line">&nbsp;&nbsp; &nbsp;0x06024,</div><div class="code_line">&nbsp;&nbsp; &nbsp;0x06028,</div><div class="code_line">&nbsp;&nbsp; &nbsp;0x0602C</div><div class="code_line">};</div><div class="code_line">DWORD save_crtc[sizeof(CrtcReg)/sizeof(*CrtcReg)];</div><div class="code_line">for(int i = 0; i &#60; sizeof(CrtcReg)/sizeof(*CrtcReg); i++)</div><div class="code_line">&nbsp;&nbsp; &nbsp;save_crtc[i] = INREG(CrtcReg[i]);</div></ol></div></div></div></div><br>
Загрузка соответственно:<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">for(int i = 0; i &#60; sizeof(CrtcReg)/sizeof(*CrtcReg); i++)</div><div class="code_line">&nbsp;&nbsp; &nbsp;OUTREG(CrtcReg[i], save_crtc[i]);</div></ol></div></div></div></div><br>
<span class="tag-color tag-color-named" data-value="blue" style="color: blue">Примечание: здесь и далее я предполагаю, что перед выполнением переключение в нестандартный видеорежим установлен любой графический (не текстовый) видеорежим через Весу, например 800x600x32, он есть везде. Выход из VGA видеорежима потребует дополнительных строк кода (на данный момент я не занимался этим).</span><br>
Можете проверить этот код: видеорежим должен смениться на нужный, вот только в середине на экрана будет отображаться прямоугольник с данными равный по размерам предыдущиму видеорежиму. Это потому, что мы не установили viewport и surface регистры, но там все значительно проще (см. далее).<br>
Теперь я покажу как рассчитать значения регистров из EDID. Вообще те, кто внимательно читал содержимое моих ссылок сами без проблем смогут это написать.<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">//ищем блок описания &quot;родного&quot; видеорижема</div><div class="code_line">int i;</div><div class="code_line">for(i = 54; i &#60; 126; i += 18)</div><div class="code_line">&nbsp;&nbsp; &nbsp;if(*(WORD*)&amp;edid[i] != 0) //если это именно блок описания видеорежима</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; break;</div><div class="code_line">int new_clock = (int)(*(WORD*)&amp;edid[i]) * 10; //частота в кГц</div><div class="code_line">constint new_screen_width = edid[i + 2] + ((edid[i + 4] &#62;&#62; 4) &#60;&#60; 8);</div><div class="code_line">const int new_screen_height = edid[i + 5] + ((edid[i + 7] &#62;&#62; 4) &#60;&#60; 8);</div><div class="code_line">const int new_h_blank = edid[i + 3] + ((edid[i + 4] &amp; 0x0F) &#60;&#60; 8);</div><div class="code_line">const int new_v_blank = edid[i + 6] + ((edid[i + 7] &amp; 0x0F) &#60;&#60; 8);</div><div class="code_line">const int new_h_sync_offset = edid[i + 8] + ((edid[i + 11] &#62;&#62; 6) &#60;&#60; 8);</div><div class="code_line">const int new_h_sync_pulse = edid[i + 9] + ( ((edid[i + 11] &#62;&#62; 4) &amp; 0x03) &#60;&#60; 8);</div><div class="code_line">const int new_v_sync_offset = (edid[i + 10] &#62;&#62; 4) + ( ((edid[i + 11] &#62;&#62; 2) &amp; 0x03) &#60;&#60; 4);</div><div class="code_line">const int new_v_sync_pulse = (edid[i + 10] &amp; 0x0F) + ( (edid[i + 11] &amp; 0x03) &#60;&#60; 4);</div><div class="code_line">const int new_h_sync_polarity = &nbsp;( (edid[i + 17] &amp; 2) != 0) ? 0 : 1;</div><div class="code_line">const int new_v_sync_polarity = &nbsp;( (edid[i + 17] &amp; 4) != 0) ? 0 : 1;</div></ol></div></div></div></div><br>
Ну и соответственно установка значений:<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">OUTREG(0x6000, new_h_total - 1);</div><div class="code_line">OUTREG(0x6004, new_start_h_blank | (new_end_h_blank &#60;&#60; 16));</div><div class="code_line">OUTREG(0x6008, new_h_sync_pulse &#60;&#60; 16);</div><div class="code_line">OUTREG(0x600C, new_h_sync_polarity );</div><div class="code_line">OUTREG(0x6020, new_v_total - 1);</div><div class="code_line">OUTREG(0x6024, new_start_v_blank | (new_end_v_blank &#60;&#60; 16));</div><div class="code_line">OUTREG(0x6028, new_v_sync_pulse &#60;&#60; 16);</div><div class="code_line">OUTREG(0x602C, new_v_sync_polarity);</div></ol></div></div></div></div><br>
Но только не спешите тестировать. Еще необходимо установить новую частоту генерации пикселов.<br>
<div class='tag-align-center'><span class="tag-color tag-color-named" data-value="green" style="color: green"><span class='tag-size' data-value='11' style='font-size:11pt;'><strong class='tag-b'>Настройка PLL</strong></span></span></div><br>
<div class='tag-align-center'><strong class='tag-b'><span class="tag-color tag-color-named" data-value="red" style="color: red">Дисклемейкер: тут описаны мои способы установки значений делителей частоты. Я не претендую на их правильность. Официальных способов это сделать вы все равно не найдете в открытом доступе. По крайней мере на данный момент.</span></strong></div><br>
Для того, чтобы установить частоту генерации пикселов необходимо установить 3 различных делителя частоты. <br>
1. reference divider (опорный делитель), он содержится в старших 10ти битах регистре 0x0404. <br>
2. post divider (я не знаю как он правильно переводится) содержится в регистре 0x043C.<br>
3. feedback divider (делитель обратной связи) содержится в старших 16ти битах регистра 0x0430, а в младших 3х его дробная часть.<br>
Связано все это дело вот таким соотношением:<br>
<div class='tag-quote'><span class='tag-quote-prefix'>Цитата</span> <div class='quote '>Output frequency == (reference freq * feedback divider) / (reference divider * post divider);</div></div><br>
где reference freq - это постоянное значение для конкретной карточки.<br>
Казалось бы, что подобрать три этих значения проще простого. Но на деле есть 2 проблемы:<br>
1. Значения могут быть только целочисленные.<br>
2. Эти значения могут быть только в определенных пределах для конкретной карточки.<br>
В исходниках открытого линуксового драйвера я нашел 2 алгоритма подбора этих значений, но после портирования они не дали нужных результатов. Есть еще способ основанный на выполнении функций внутри ATOMBIOS карточки, но для этого надо писать интерпретатор байт-кода, что очень трудоемко. Поэтому я стал наблюдать за логикой работы Виндового драйвера и заметил некоторые закономерности: в первую очередь подстраивается post divider, незначительно feedback divider, reference divider вообще почти не трогается.<br>
Вот исходя из этого у меня получился вот такой код:<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">const double pixel_clock = (double)( (INREG(0x6000) + 1) * (INREG(0x6020) + 1) * 60) / 1000; //вычисляем текущую частоту генерации в кГц</div><div class="code_line">const double ref_div = INREG(0x404) &amp; 0x3FF;</div><div class="code_line">const double fb_div = INREG(0x430) &#62;&#62; 16;</div><div class="code_line">const double post_div = INREG(0x43C) &amp; 0x7F;</div><div class="code_line">const double ref_clock = (pixel_clock * ref_div * post_div) / fb_div; //reference freq</div><div class="code_line">double new_div = (new_clock * ref_div) / ref_clock;</div><div class="code_line">int new_post_div = fb_div / new_div + 0.5;</div><div class="code_line">int new_fb_div = new_div * new_post_div + 0.5;</div><div class="code_line">&nbsp;</div><div class="code_line">OUTREG(0x430, new_fb_div &#60;&#60; 16);</div><div class="code_line">OUTREG(0x43C, new_post_div);</div><div class="code_line">//reference divider не трогаем</div></ol></div></div></div></div><br>
Идея основана на том, что все параметры мы можем приближенно вычислить на основе значений для текущего видеорежима и пересчитать по своей стратегии.<br>
Способ оказался работоспособен на обоих карточках. Но имеет существенный недостаток: output frequency тут с вычисляется погрешностью, т. е. вместо 60Hz (верт. синхронизации естественно) может получиться 61 или 59. Хотя мониторы должны работать с этим. Тот же VBE на моем старом RV350 не давал ровно 60Hz.<br>
Поэтому я улучшил метод:<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">const DWORD ref_div = INREG(0x404) &amp; 0x3FF;</div><div class="code_line">const DWORD fb_div = INREG(0x430) &#62;&#62; 16;</div><div class="code_line">const double new_div = ((double)(new_clock * ref_div)) / (double)ref_clock;</div><div class="code_line">const DWORD tmp_post_div = ((double)fb_div) / new_div + 0.5;</div><div class="code_line">&nbsp;</div><div class="code_line">DWORD new_post_div, new_fb_div;</div><div class="code_line">int delta;</div><div class="code_line">unsigned min_delta = -1;</div><div class="code_line">DWORD pd, fd;</div><div class="code_line">&nbsp;</div><div class="code_line">if(tmp_post_div &#62; 2)</div><div class="code_line">&nbsp;&nbsp; &nbsp;pd = tmp_post_div - 1;</div><div class="code_line">else</div><div class="code_line">&nbsp;&nbsp; &nbsp;pd = tmp_post_div;</div><div class="code_line">&nbsp;</div><div class="code_line">for(; pd &#60;= (tmp_post_div + 3); pd++)</div><div class="code_line">{</div><div class="code_line">&nbsp;&nbsp; &nbsp;DWORD tmp_fb_div = new_div * pd + 0.5;</div><div class="code_line">&nbsp;&nbsp; &nbsp;if(tmp_fb_div &#62; 1)</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; fd = tmp_fb_div - 1;</div><div class="code_line">&nbsp;&nbsp; &nbsp;else if(tmp_fb_div == 1)</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; fd = tmp_fb_div;</div><div class="code_line">&nbsp;&nbsp; &nbsp;else</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; continue;</div><div class="code_line">&nbsp;&nbsp; &nbsp;for(; fd &#60;= (tmp_fb_div + 1); fd++)</div><div class="code_line">&nbsp;&nbsp; &nbsp;{</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; delta = new_clock - (int)(((double)(ref_clock * fd)) / (ref_div * pd) + 0.5);</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; if(delta &#60; 0)</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;delta = -delta;</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; if((unsigned)delta &#60; min_delta)</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; {</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;min_delta = delta;</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;new_post_div = pd;</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;new_fb_div = fd;</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; }</div><div class="code_line">&nbsp;&nbsp; &nbsp;}</div><div class="code_line">}</div><div class="code_line">OUTREG(0x430, new_fb_div &#60;&#60; 16);</div><div class="code_line">OUTREG(0x43C, new_post_div);</div></ol></div></div></div></div><br>
Этот код тоже работает на rv630 и на rv730. Идея та же, только теперь параметры подбираются в окрестности и выбирается наилучшее сочетание. Но тут используется refence clock полученный из ATOMBIOS видеокарточки. Конкретно структуры ATOMBIOS смотрите <a class='tag-url' href='http://code.metager.de/source/xref/xorg/driver/xf86-video-radeonhd/src/AtomBios/includes/atombios.h' target='_blank'>тут</a>. Нужное значение содержится в ATOM_FIRMWARE_INFO (usReferenceClock), там только нужно проследить как к нему добраться из корневого каталога. ATOMBIOS расположен в сегменте 0xC0000 (это и есть VGA BIOS видеокарточки), все ссылки из структуры в структуры абсолютные относительно 0xC0000.  Заголовок расположен соответственно 0xC0000 + OFFSET_TO_POINTER_TO_ATOM_ROM_HEADER, корневой каталог данных соответственно 0xC0000 + ATOM_ROM_HEADER.usMasterDataTableOffset и так далее. У RV6XX и у RV7XX usReferenceClock обычно равен 27000.<br>
<br>
<div class='tag-align-center'><span class="tag-color tag-color-named" data-value="green" style="color: green"><span class='tag-size' data-value='11' style='font-size:11pt;'><strong class='tag-b'>Настройка Surface и ViewPort</strong></span></span></div><br>
Тут все просто. После того, как мы настроили CRTC нужно установить ViewPort.<br>
По факту 2 строчки:<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">OUTREG(0x652C, ScreenHeight);</div><div class="code_line">OUTREG(0x6584, ScreenHeight| (ScreenWidth &#60;&#60; 16));</div></ol></div></div></div></div><br>
Насколько я понял, ViewPort определяет область на экране, которую захватывает Surface. Опять же описания регистров ViewPort в официальных источниках я не нашел.<br>
Упрощенно Surface можно рассматривать как поверхность, куда отображается видимая видеопамять. Все регистры описаны в RV630 Register Reference Guide, глава Display Controller Registers. Перечислю просто главные из них:<br>
D1GRPH_CONTROL  - можно установит bpp.<br>
D1GRPH_PITCH - логическая длина строки. После утсановки видеорежима стандартными прерываниями она обычно равна ScreenWidth. Но вы можете округлить до 2<sup class='tag-sup'>n</sup>, чтобы с видеопамятью было удобнее работать. То же самое можно сделать через шестую функцию Весы.<br>
D1GRPH_X_END =  ScreenWidth,<br>
D1GRPH_Y_END = ScreenHeight.<br>
<br>
Все регистры рассмотренные в этой статье  предназначены для первого монитора. Для второго все аналогично, только номера регистров другие (недокументированные можно найти в исходниках линуксового драйвера), но формат тот же самый. В заключение дам ссылку на <a class='tag-url' href='http://www.vgamuseum.info/images/stories/doc/ati/rage128/sdk-g04000_rage_128_software_development_guide_aug99.pdf' target='_blank'>руководство программиста R128</a>, спустя почти 15 лет AMD таки его открыла. Имеет смысл почитать для ознакомления, а для старых Radeon&#39;ов (до RV5xx) многое из того, что там описано является актуальным. По сути большая часть первой части статьи раскрывается в этом документе.<br>
<br>
<a class='tag-url' href='http://forum.sources.ru/index.php?showtopic=327648&st=0&#entry2850180http://' target='_blank'>первая часть</a> (поправил исходники)]]></description>
        <author>shm</author>
        <category>Assembler FAQ</category>
      </item>
	
      </channel>
      </rss>
	