Якщо відеоадаптер працює в одному з текстових режимів кольорового (режими 0 - 3) або монохроматичного (режим 7) адаптерів, можливий фізичний доступ до комірок пам'яті відеобуфера. Як відзначено в 8.2, ця пам'ять є двосторонньою. З одного боку, вміст активної відеосторінки читає адаптер, виконуючи періодичне “освіження” екрана. Інтерпретуючи уміст відеопам'яті байт за байтом, відеоадаптер “малює” телевізійні крапки в різних місцях екрана, утворити обрису символів і їхнє тло, різні графічні зображення. З іншого боку, пам'ять відеоадаптера включена в загальне поле оперативної пам'яті, і доступ до неї може виконувати центральний процесор (у текстових режимах - для усіх відеоадаптерів, у графічних – для CGA–адаптера і режиму 13h EGA– і VGA- адаптерів). Можливість одночасного доступу до одній і тому ж осередку відеопам'яті з боку процесора і з боку адаптера викликає між ними конфліктну ситуацію. Для CGA- адаптерів застарілої конструкції цей конфлікт породжує на екрані ефект, називаний “сніг”. Виникнення “снігу”порозумівається так. Приимущество в конфлікті при одночасному доступі до одній і тій же комірці пам'яті відеобуфера апаратно отданно процесору, і адаптер змушений чекати своєї черги. За час чекання телевізійний промінь монітора зрушується на деяку відстань. Коли адаптер очікує доступу, телевізійний промінь монітора зрушується на деяку відстань. Коли адаптер очікує доступу, телевизионый промінь йде без його “піклування”, прочерчивая на екрані світлу смугу. Ця смуга при наступному “освіженні” екрана зникає в старому місці, але може з'явитися знову при виникненні нового конфлікту. У сучасних відеоадаптерах за рахунок іншої організації апаратури “сніг” не виникає. Один зі способів боротьби з “снігом” буде розглянутий далі в цій же главі. При роботі з відеоадаптером через BIOS або функції Turbo C “сніг” не виникає саме тому, що використовується програмний захист від конфліктів.

Безпосередній доступ до відеопам'яті – найбільш швидкий спосіб висновку інформації на екран, але і найбільш трудомісткий. Природно, що при висновку тексту у відеобуфер не буде смещатся курсор, якщо тільки не виконувати його примусове позиционирование. Далі розглядаються способи безпосереднього висновку у відеобуфер текстової інформації. За замовчуванням мова йде про текстові режими роботи відеосистеми.

Для доступу відеопам'яті використовується far – покажчик. Наприклад, що приводиться далі фрагмент коли буде записувати у відеобуфер символ ‘А’ з атрибутом інверсного зображення 0х70 – чорний символ на білому тлі. Символ з'явиться на екрані в рядку 5 (вважаючи від рядка 0) і стовпці 40 (вважаючи від стовпця 0). Висновок виконується для одного з текстових режимів 20х80 кольорового відеоадаптера на нульову сторінку:

сhаr far*vid_buf=(char far*) 0xb800000;

vid_buf+=5*160+40*2;

*vid_buf++=‘A’;

*vid_buf=atr;

Тому що для одного символу на екрані потрібно 2 байти (байт для символу і байт для атрибута), зсув від початку відеопам'яті обчислюється по формулі

зсув=рядок * 160 + стовпець *2=(рядок *80 + стовпець) *2.

У приведеному раніше фрагменті Си – коду запис у відеопам'ять виконується побайтно: спочатку код символу, потім байт атрибута символу. Для підвищення швидкості запису інформації у відеобуфер можна виконати перенос відразу цілого слова, утвореного з байта символу і байта атрибута, наприклад, так:

unsigned word;

word = (unsigned) ((symbol<<8) | attribut);

І, нарешті, використання спеціального типу near – покажчика – покажчика unsigned_es * work – дозволяє ще більш підвищити швидкість висновку інформації на екран. Покажчик _es буде містити тільки зсув щодо регістра _es. Записавши перед доступом до відеопам'яті в регістр ES значення сегмента адреси початку відеопам'яті (B800h – для CGA – режимів, B000h – для монохроматичного адаптера), можна потім використовувати покажчик типу _es.

Ще більш високу швидкість переносу можна одержати, використовуючи функції бібліотеки Turbo, прототипи яких приведені у файлі <mem.h> (їхній опис див. у першій книзі комплексу).

При виконанні безпосереднього доступу до відеопам'яті для обчислення зсуву необхідно знати максимальне число стовпців тексту в поточному режимі, номер поточної відеосторінки і сегмент адреси початку відеопам'яті.

Ці перемінні змінюються досточно рідко.

Для підвищення продуктивності функцій розумно оголосити їхній як зовнішні і инициализировать при найпершому звертанні до функції запису у відеопам'ять і при кожній зміні режиму роботи відеоадаптера. Джерелом необхідної інформації може служити область даних BIOS (див. табл. 8.2).

Приведемо приклад функції printche(), що виводить символ із заданим атрибутом задане число раз, починаючи з задані рядки і стовпця екрана (параметри і повертаються функція значення такі ж, як і у функції L9_3.C). Для підвищення продуктивності функція використовує зовнішні перемінні:

vid_memory – початковий параграф відеопам'яті адаптера;

start_adr – зсув до початку активної сторінки;

max_stolb – максимальне число стовпців на екрані (40, 80);

max_str - максимальне число рядків на екрані (25, 43, 50).

/*L9_7.C*/

#include “screen.h”

int printche (int str, int stolb, char symb, char atr, int n)

{ extern unsigned vid_memory, start_adr, max_stolb, max_str;

  registr unsigned _es*work, word;

  /*Перевірка параметрів на допустимість.*/

  if (str>=0&&str<=max_str&&stolb>=0&&stolb<=max_stolb)

  { /*Обчислення зсуву до першого байта видобуфера.*/

   work=(unsigned_es*)(start_adr+(max_stolb*str+stolb)*2);

   /*Формуємо слово для запису у відеобуфер.*/

   word=(unsigned)(atr<<8) | symb;

   ES=vidmemory;

   while(n--)

   { *work=word; work++; }

   return OK; }

  else return BAD_PARAM;

}

Розглянута функція забезпечує майже максимально досяжну швидкість висновку, але для деяких типів CGA – адаптерів (EC – 1840, EC – 1841, “Ізот”) приводить до появи “снігу” на екрані. Далі буде розглянутий спосіб боротьби з “снігом” при безпосередньому доступі до відеопам'яті.

Спеціальний порт 3DAh CGA - адаптери містить інформацію поточного стану адаптера. Два, біта байта, прочитаного з порту, використовуються для придушення “снігу”:

біт 0 – напрямок руху лучачи ЭЛТ по горизонталі; біт дорівнює нулю, коли промінь рухається в “прямому” напрямку (тобто адаптер зчитує байти відеопам'яті і малює “пикселы” ТВ - рядка); біт дорівнює одиниці, коли промінь “виключений” і рухається в зворотному напрямку (тобто адаптер не виконує читання інформації);

біт 3 – стан зворотного ходу лучачи ЭЛТ; біт дорівнює нулю, коли адаптер робить черговий цикл “освіження” екрана; біт дорівнює одиниці, коли адаптер очікує чергового циклу “освіження”. Промінь, досягши правого нижнього кута екрана, виключається і переміщається в лівий верхній кут екрана (зворотний хід лучачи).

Доступ до відеопам'яті адаптера в ті моменти, коли виконується зворотний горизонтальний або вертикальний хід лучачи, не породжує “сніг”, тому що відсутні конфлікти між адаптером і процесором. Тривалість інтервалу зворотного ходу лучачи колеблица для різних адаптерів від одиниць до десяти миллисекунд. Переодичность інтервалу в залежності від типу адаптера – 1/25, 1/30, 1/50 або 1/60 с. Таким чином, інтервал зворотного ходу лучачи виявляється достатнім для переносу навіть повної копії екрана. Однак настають інтервали досить рідко, і чекання завершення чергового циклу освіження різко зменшує швидкість роботи програми. Тривалість горизонтального зворотного ходу лучачи мала і дозволяє перенести лише одне слово, але період горизонтальної синхронізації в кілька сотень раз вище періоду “освіження” екрана. Звідси випливає основна ідея побудови найпростішого алгоритму висновку інформації безпосередньо у відеобуфер без “снігу”: виконується нескінченний цикл читання порту 3DAh доти, поки біт 0 не установиться в 1; після цього виконується запис символу й атрибута у відеобуфер. Чекання трохи сповільнює швидкість висновку інформації на екран, що є природною “платою за задоволення” не мати “сніг”. Для надійної роботи функція пропускає один такт вільного стану відеоадаптера. Можливо, що цей такт дуже незабаром може завершитися і процесор просто не встигне записати слово у відеопам'ять. Це теж може породжувати “сніг” (правда, слабкий). І, нарешті, перед початком висновку маскуються всі зовнішні переривання. У реалізації алгоритму приходиться “вичавлювати” максимальну продуктивність коду функції. Справа в тім, що якщо слово у відеоадаптер переноситься “повільною” машинною командою, велика імовірність того, що перенос не завершиться до початку циклу “освіження”. Далі приводиться приклад функції printche(), що виводить інформацію безпосередньо у відеобуфер без “снігу”. В іншому функція подібна варіанту L9_7.C.

/*L9_8.C*/

# include <dos.h>

# include “screen.h”

int printche( int str, int stolb, char symb, char atr, int n)

            { extern unsigned vid_memory, start_adr, max_stolb, max_str;

             register unsigned _es * work, word;

              /* Перевірка параметрів на допустимість. */

              if(str >= 0 && str <= max_str && stolb >= 0 && stolb <= max_stolb)

               { /*  Обчислення зсуву до першого байта відеобуфера*/

                work=( unsigned_es*)

                        (start_adr + ((max_stolb*str+stolb)<<1))

                /* Формування слова для запису у відеопаяти*/

                word = (unsigned)(atr<<8) | symb;

                /*Висновок символу/атрибуа задане число раз. */

                _ ES = vid_memory;

                 while(n--)

                   {/*Предвращение “снігу”.*/

                    _DX=0x3da

                    while((inportb(_DX) & 0x01));

                     /* Чекання завершення циклу “освіження” екрана */

                    while(!(inport(_DX) & 0x01));

                     /* Висновок символу й атрибута у відеопам'ять. */

                     disable(); * work = word; enable(); work++

                  }

                return OK

              }

             else return BAD_PARAM

            }

           

            Функція працює коректно для всіх моделей адаптерів. Але якщо адаптер не породжує “сніг”, варто використовувати варіант функції printche() L9_7.C.Можливе поліпшення алгоритму придушення “снігу ” – використання як горизонтального, так і вертикального зворотного ходу лучачи. Наприклад, знайшовши перехід з нуля в одиницю біта 3 порти 3Dаh, функція може зовсім безпечно вивести всю інформацію, що залишилася, не перевіряючи стан горизонтальної синхронізації.

            Приведемо приклад функції hor_prn(), що збігає по параметрах і значенням, що повертаються, з варіантом L9_2.C, але працюючий значно швидше за рахунок безпосереднього висновку інформації у відеобуфер і при цьому не породжує “сніг” Для підвищення продуктивності функція використовує наступні зовнішні перемінні:

            vid_memory – початковий параграф відеопам'яті адаптера;

            start_adr – зсув до початку активної сторінки;

            max_stolb – максимальне число стовпців на екрані (40,80);

            max_str – максимальне число рядків на екрані (20,43,50).

  /*l9_9.C*/

# include <dos.h>

# include “screen.h”

int hor_prn( int str, int stolb, char *ptr, char attr)

            { extern unsigned vid_memory, start_adr, max_stolb, max_str;

             register unsigned _es * work, word;

             /* Перевірка параметрів на допустимість. */

             if(str >= 0 && str <= max_str && stolb >= 0 && stolb <= max_stolb)

             { /*  Обчислення зсуву до першого байта відеобуфера*/

             work=start_adr + ((max_stolb*str+stolb)<<1)

             while(*ptr)/* цикл висновку символів рядка*/

 {word=(unsigned)(attr<<8) | (*ptr++);

 /*Запобігання “снігу”.*/  

 _DX=0x3da

             /*Чекання вільного такту відеоадаптера.*/ 

 while((inportb(_DX) & 0x01));

                     /* Чекання завершення циклу “освіження” екрана */

                    while(!(inport(_DX) & 0x01));

                     /* Висновок символу й атрибута у відеопам'ять. */

                     disable(); ES=vidmemory * work = word;

         enable(); work++

                  }

                return OK

              }

             else return BAD_PARAM

            }