Низкоуровневое программирование современных видеокарт

Актуальные публикации по вопросам современных информационых технологий и развития компьютерных сетей, систем массовой коммуникации, электронно-вычислительной техники.

NEW КОМПЬЮТЕРЫ И ИНТЕРНЕТ


КОМПЬЮТЕРЫ И ИНТЕРНЕТ: новые материалы (2024)

Меню для авторов

КОМПЬЮТЕРЫ И ИНТЕРНЕТ: экспорт материалов
Скачать бесплатно! Научная работа на тему Низкоуровневое программирование современных видеокарт. Аудитория: ученые, педагоги, деятели науки, работники образования, студенты (18-50). Minsk, Belarus. Research paper. Agreement.

Полезные ссылки

BIBLIOTEKA.BY Беларусь - аэрофотосъемка HIT.BY! Звёздная жизнь


Автор(ы):
Публикатор:

Опубликовано в библиотеке: 2008-08-15

Графика SVGA. Часть первая: программирование современных видеокарт в реальном и виртуальном режимах


Содержание

Пролог
Глава 1. Общая информация о видеокарте
Глава 2. Проверка соответствия видеокарты стандарту VBE
Глава 3. Видеорежимы и стандарт
Глава 4. Информация о видеорежиме
Глава 5. Поиск видеорежима
Глава 6. Установка видеорежима
Глава 7. Окна видеопамяти
Глава 8. Подготовительные действия
Глава 9. Работа с видеокнами
Глава 10. Построение точки на экране
Глава 11. Вычисление адреса точки
Глава 12. Цвет в коде точки
Эпилог




Пролог


В настоящее время существует достаточно возможностей для создания разнообразных графических приложений на любительском и профессиональном уровне. Однако по-прежнему существует ряд задач, решение которых требует умения работать с видеосистемой на самом низком уровне. К таким задачам, в частности, относится создание драйверов или графических приложений. Разработчики собственных операционных систем и программ также иногда сталкиваются с необходимостью непосредственной работы с регистрами видеокарт.
В этой книге кратко рассказывается о способах программирования современных видеоадаптеров. Основной упор сделан на правильном использовании возможностей оборудования, благодаря чему приложение сможет работать на любой видеокарте, т.е. будет переносимым.
Совместимость видеокарт существовала не всегда. Были времена, когда приложения, работающие на одной видеокарте, не работали или работали медленно на другой. Решением стал стандарт VBE (VESA BIOS EXTENSION). Его окончательная третья версия вышла в 1998-ом году и с тех пор почти не изменялась. Оригинальный документ VESA vbe.pdf можно легко найти в сети интернет. Благодаря стандартизации появилась возможность создавать графические приложения, совместимые со всеми моделями видеокарт, соответствующих стандарту VBE.
В первой части книги будет рассказано о программировании видеокарт в реальном и виртуальном режиме. Такие вопросы, как работа с цветом и рисунками, построение геометрических фигур, а так же работа с DAC-регистрами видеокарты рассматриваться не будут. Все это можно легко найти в соответствующей литературе.




Глава 1
Общая информация о видеокарте


На любой видеокарте имеется микросхема памяти, именуемая BIOS. Она содержит данные и различные внутренние подпрограммы, необходимые для нормальной работы видеоадаптера в целом. Стандарт VBE предусматривает размещение в BIOS специальных функций (функции VBE), при помощи которых можно непосредственно работать с видеосистемой.
Для вызова функции следует задать группу и номер функции в группе. Код группы для функций VBE равен 4Fh, он указывается в регистре AH. Номер функции в группе указывается в регистре AL. Вызов функций осуществляется через прерывание INT 10h. При успешном выполнении функции регистр AH очищается, а в AL возвращается код 4Fh. Любые другие значения в регистре AX говорят о том, что функция либо не выполнена, либо не поддерживается.


Функция 00h - запрос общих данных о видеокарте (return VBE controller information)

Перед вызовом функции следует создать буфер размером 512 байт, в котором функция сможет разместить информационный блок VbeInfoBlock, содержащий общую информацию о видеокарте. Создание буфера обязательно. Тут следует оговориться. Стандартом VBE 1.х предусматривалось создание буфера размером 256 байт, однако уже в версиях 2.0 и 3.0 рекомендуется создавать буфер размером 512 байт для получения расширенной информации. Учитывая тот факт, что все выпущенные в последнее десятилетие видеоадаптеры поддерживают вторую или третью версию стандарта, то целесообразно использовать последний способ вызова. В начало буфера следует записать четыре символа в ASCII коде, вместе образующих слово "VBE2". Результатом выполнения функции является создание информационной структуры VbeInfoBlock размером 512 байт. Значения её областей приведены в таблице 1.


Таблица 1. Структура информационного блока VbeInfoBlock

Смещение от Размер Название области
начала блока области

00h - 03h 4 байта "VESA" (VbeSignature)
04h - 05h 2 байта Версия VBE (VbeVersion)
06h - 09h 4 байта Адрес строки изготовителя(OemStringPtr)
0Ah - 0Dh 32 бита Специфические возможности (Capabilities)
0Eh - 11h 4 байта Адрес списка видеорежимов(VideoModePtr)
12h - 13h 2 байта Размер видеопамяти (TotalMemory)

Информация для VBE 2.0+

14h - 15h 2 байта OEM версия VBE (OemSoftwareRev)
16h - 19h 4 байта Адрес строки производителя
(OemVendorNamePtr)
1Ah - 1Dh 4 байта Адрес строки имени продукта
(OemProductNamePtr)
1Eh - 21h 4 байта Версия продукта (OemProductRevPtr)
22h - 0FFh 222 байт Резервная область для списка видеорежимов
(Reserved)
100h - 1FFh 256 байт Данные производителя (OemData)

Примечание: если перед вызовом функции в начало выделенного буфера не записать строку "VBE2", то информационный блок VbeInfoBlock будет иметь размер 256 байт и только первые шесть областей (до адреса 13h) будут заполнены данными.

Область VbeSignature занимает четыре байта и содержит ASCII коды четырех символов, образующих слово "VESA".
Область VbeVersion занимает два байта. Старший байт содержит номер версии VBE, младший - её редакцию.
Область OemStringPtr занимает четыре байта (два слова) и содержит адрес начала строки текста, содержащей наименование изготовителя. Первое слово содержит смещение, а второе - код сегмента, в котором расположена строка. Строка заканчивается пустым байтом (формат строки ASCIIZ). Более расширенная по содержанию строка будет располагаться в области OemData. Ширина области OemData, а следовательно и длина строки может иметь размер не более 256 байт.
Область Capabilities состоит из 32-х независимых битов и содержит данные о поддержке специфических возможностей в графической среде . Из них используются только младшие пять разрядов (флагов), остальные отведены под резерв для будущих расширений. Нулевой и второй бит используются для операций с DAC регистрами. Установка первого бита говорит о том, что видеоконтроллер не совместим с режимом VGA. Третий и четвёртый предназначены для работы со стереоскопическим сигналом.
Область VideoModePtr занимает четыре байта (два слова) и содержит адрес строки, содержащей список номеров видеорежимов. Код каждого режима занимает одно слово (16 бит). В этом списке перечисляются все потенциально поддерживаемые адаптером режимы. То, что режим присутствует в списке, не означает, что он может быть установлен. Проверка работоспособности того или иного режима должна возлагаться на приложение. Список режимов так же размещен в области Reserved.
Область TotalMemory занимает два байта и указывает объём расположенной на видеокарте памяти в блоках по 64К.

Интересные результаты получаются при полном просмотре некоторых адресов, возвращаемых в информационном блоке. Например, для большинства видеокарт адрес одного из диапазонов ввода-вывода (I/O) начинается с сегмента 0C000h (этот сегмент может быть указан в области OemStringPtr). При просмотре этого сегмента в самом его начале могут обнаружиться данные о дате и времени изготовления продукта, номера версии BIOS, расширенное название модели и другое. В операционной системе Windows адреса диапазонов I/O можно узнать на вкладке свойств видеоадаптера.
Если требуется, то можно составить простую программу, которая будет выводить данные информационного блока в файл. Визуально анализировать файл можно в любом hex-редакторе, но следует помнить, что, например, адреса строк будут записаны в обратном порядке и читать их нужно справа налево.






Глава 2
Проверка соответствия видеокарты стандарту VBE


Работу с видеокартой следует начинать с проверки поддержки ею стандарта VESA. Как говорилось выше, перед вызовом функции 00h следует выделить буфер размером 512 байт. Предположим, что буфер создан и имеет имя Vinfo. Адрес буфера должен находиться в регистрах ES:DI.


Пример 1. Проверка видеоадаптера

STEP_1:

;Инициализация регистров ES:DI
MOV AX, Vinfo
MOV ES, AX
XOR DI, DI

;Запись в начало буфера строки "VBE2"
MOV AX,56h
MOV ES:[00h],AX
MOV AX,42h
MOV ES:[01h],AX
MOV AX,45h
MOV ES:[02h],AX
MOV AX,32h
MOV ES:[03h],AX

;Вызов функции
MOV AX,4F00h
INT 10h

;Сравнение и переход на соответствующую метку
CMP AX,4Fh
JZ STEP_2
JMP ERROR

;Переход ко второму тесту
STEP_2:

Примечание: если регистры ES:DI уже используются задачей, то перед выполнением примера 1 их значения следует сохранить в стеке.


Пример начинается с инициализации регистров ES:DI. Так как регистр ES сегментный, то его значение присваивается через аккумулятор AX. Регистр DI очищается для записи информационного блока с начала сегмента. В итоге в регистрах ES:DI находится адрес буфера Vinfo. В начало буфера посимвольно записывается строка "VBE2", затем вызывается функция через прерывание BIOS. Если функция была выполнена успешно, то в AX возвращается код 4Fh. Это означает, что информационная структура VbeInfoBlock была создана и можно переходить к следующему шагу программы - на метку STEP_2. Если код в AX отличается от 4Fh, то происходит переход на метку ERROR, после которой можно завершить программу или предпринять какие-либо другие действия.

В настоящее время области информационной структуры VbeInfoBlock, возвращаемой функцией 00h, почти не представляют практического интереса для прикладных задач, кроме информационного. Области VideoModePtr и Reserved хоть и дают информацию о видеорежимах, но не гарантируют их работоспособность. Доступность видеорежима должна проверяться методом перебора. Анализ области TotalMemory так же вряд ли целесообразен, так как все выпускающиеся видеокарты давно превысили барьер в 16 Мб, что вполне достаточно для установки любого существующего режима. Например, при анализе некоторых видеокарт семейства Radeon в области TotalMemory было указано заниженное значение видеопамяти, чем видеоадаптер имел на самом деле. При работе примера 1 важен статус завершения функции. Если стандарт VBE поддерживается видеокартой, то можно переходить к следующему этапу - проверке поддержки выбранного видеорежима.









Глава 3
Видеорежимы и стандарт


До создания специализированной ассоциации стандартизации видеоэлектроники VESA (Video Electronics Standards Association) разработчики видеокарт имели полную свободу действий в плане создания собственных стандартов. Каждая выпускавшаяся модель обладала индивидуальным набором видеорежимов и многие графические приложения могли работать только на видеокартах определенного производителя. Что бы исправить положение, авторы VBE описали номера и характеристики режимов, полный список которых приведен в таблице 2. Это помогло ситуации, однако вскоре, дабы не ограничивать разработчиков, идея стандартизации видеорежимов была фактически отменена. Это означает, что приложение должно самостоятельно выбрать подходящий режим методом перебора, используя функции VBE. На данный момент изготовитель вправе вводить любые собственные номера режимов, чем он и пользуется. Например, при чтении области VideoModePtr или Reserved информационного блока VbeInfoBlock любой видеокарты можно увидеть много новых кодов режимов (таких как 01A2h или 0182h), которые в таблице 2 отсутствуют. В целях совместимости со старыми приложениями номера режимов VBE по-прежнему поддерживаются видеокартами, но не все. Так, видеоадаптеры GeForce уже несколько лет не поддерживают режим 1024 · 768 · 32K (режим 116h), в то время как в Radeon он до сих пор присутствует.


Таблица 2. Видеорежимы VBE

Номер режима Разрешение Количество цветов Цветовая схема

Текстовые режимы

108h 80 · 60 - -
109h 132 · 25 - -
10Ah 132 · 43 - -
10Bh 132 · 50 - -
10Ch 132 · 60 - -

Графические режимы

100h 640 · 400 256 -
101h 640 · 480 256 -
102h 800 · 600 16 -
103h 800 · 600 256 -
104h 1024 · 768 16 -
105h 1024 · 768 256 -
106h 1280 · 1024 16 -
107h 1280 · 1024 256 -
10Dh 320 · 200 32K 1:5:5:5 (15 бит)
10Eh 320 · 200 64K 5:6:5 (16 бит)
10Fh 320 · 200 16.8M 8:8:8 (32/24 бит)
110h 640 · 480 32K 1:5:5:5 (15 бит)
111h 640 · 480 64K 5:6:5 (16 бит)
112h 640 · 480 16.8M 8:8:8 (32/24 бит)
113h 800 · 600 32K 1:5:5:5 (15 бит)
114h 800 · 600 64K 5:6:5 (16 бит)
115h 800 · 600 16.8M 8:8:8 (32/24 бит)
116h 1024 · 768 32K 1:5:5:5 (15 бит)
117h 1024 · 768 64K 5:6:5 (16 бит)
118h 1024 · 768 16.8M 8:8:8 (32/24)
119h 1280 · 1024 32K 1:5:5:5 (15 бит)
11Ah 1280 · 1024 64K 5:6:5 (16 бит)
11Bh 1280 · 1024 16.8M 8:8:8 (32/24 бит)
81FFh Специальный
режим - -


Каждый режим имеет идентификатор (номер) размером в слово (2 байта), который по специальным таблицам BIOS преобразовывается в 7-значный OEM (Original Equipment Manufacturer) код. Приложение работает с номерами режимов, а соответствующий OEM-код используется видеокартой при установке и получении информации о режиме. Любой видеорежим обладает двумя важными характеристиками: разрешающая способность и количество отображаемых цветов. В таблице 2 для графических режимов разрешающая способность указана в точках и определяет количество пикселей по горизонтали (столбцы) и вертикали (строки). Количество цветов для удобства записывается в краткой форме, их точные значения таковы:

Direct color 32К - 32 768 цветов (2^15) Hi-color ;
Direct color 64K - 65 536 цветов (2^16) Hi-color ;
Direct color 16.8M - 16 777 216 цветов (2^24) True Color ;

Цветовая схема или глубина цвета определяет, каким образом в коде точке распределены значения трёх базовых цветов - красного, зелёного и синего (RGB). Видеорежимы Direct color (непосредственный цвет) делятся на режимы среднего цветового разрешения Hi-color и максимального цветового разрешения True Color. Их особенностью является то, что цвет точки записан в коде самой точки и на данный момент эти режимы обеспечивают самое высокое качество изображения. Запись глубины цвета 32/24 бит означает, что для кодирования цвета требуется всего 3 байта (24 бит), но так как обработать три байта одной командой нельзя, то в код точки приходится включать четвертый пустой байт, увеличивая её размер до 32 бит. Кратко о работе с цветом будет сказано в конце книги.







Глава 4
Информация о видеорежиме


Перед установкой видеорежима следует убедиться, что его характеристики совпадают с требованиями, необходимыми для запуска и нормальной работы приложения. Для этого предназначена функция VBE 01h - запрос информации о видеорежиме (Return VBE Mode Information), которая создает информационный блок ModeInfoBlock с расширенными данными о режиме. Для размещения блока требуется создать буфер размером 256 байт, адрес которого должен находиться в регистрах ES:DI. Следует помнить, что не существует никаких соглашений относительно номеров видеорежимов и единственный способ корректной работы с ними это использование информационной функции 01h.


Таблица 3. Структура информационного блока ModeInfoBlock

Смещение от Размер области Название области
начала блока

Обязательная информация для всех версий VBE

00h - 01h 2 байта Атрибуты режима (ModeAttributes)
02h 1 байт Атрибуты окна А (WinAAttributes)
03h 1 байт Атрибуты окна В (WinBAttributes)
04h - 05h 2 байта Размер ячейки окна в КБ (WinGranularity)
06h - 07h 2 байта Размер окна в КБ (WinSize)
08h - 09h 2 байта Адрес начала окна А (WinASegment)
0Ah - 0Bh 2 байта Адрес начала окна В (WinВSegment)
0Ch - 0Fh 4 байта Адрес подпрограммы BIOS (WinFuncPtr)
10h - 11h 2 байта Размер строки в байтах
(BytesPerScanLine)

Обязательная информация для версий VBE 1.2 и выше

12h - 13h 2 байта Горизонтальное разрешение в точках
или символах (XResolution)
14h - 15h 2 байта Вертикальное разрешение в точках
или символах (YResolution)
16h 1 байт Ширина символа в точках (XCharSize)
17h 1 байт Высота символа в точках (YCharSize)
18h 1 байт Количество плоскостей видеопамяти
(NumberOfPlanes)
19h 1 байт Количество бит на точку (BitsPerPixel)
1Аh 1 байт Количество банков видеопамяти (NumberOfBanks)
1Bh 1 байт Модель видеопамяти (MemoryModel)
1Ch 1 байт Размер банка в КБ (BankSize)
1Dh 1 байт Количество экранных страниц
(NumberOfImagePages)
1Eh 1 байт Резервный байт (Reserved)

Информация для Direct Color

1Fh 1 байт Размер маски красного цвета в битах (RedMaskSize)
20h 1 байт Позиция маски красного цвета (RedFieldPosition)
21h 1 байт Размер маски зелёного цвета в битах
(GreenMaskSize)
22h 1 байт Позиция маски зелёного цвета
(GreenFieldPosition)
23h 1 байт Размер маски синего цвета в битах
(BlueMaskSize)
24h 1 байт Позиция маски синего цвета (BlueFieldPosition)
25h 1 байт Резерв для маски цвета (RsvdMaskSize)
26h 1 байт Резерв для позиции маски цвета (RsvdFieldPosition)
27h 1 байт Флаги для режима DirectColor
(DirectColorModeInfo)

Примечание: в данной таблице приведены не все области блока ModeInfoBlock, поскольку оставшаяся часть требуется только при работе со всем пространством видеопамяти в защищенном режиме (режим LFB).

Область ModeAttributes описывает наиболее важные характеристики видеорежима.
Структура области ModeAttributes:

D0=1 Видеорежим поддерживается;
D1=1 Резерв;
D2=1 Стандартные функции вывода TTY поддерживаются BIOS;
D3=1 Цветной режим;
D3=0 Монохромный режим;
D4=1 Графический режим;
D4=0 Текстовый режим;
D5=1 Режим не совместим с VGA;
D6=1 Невозможна работа с окнами видеопамяти;
D7=1 Возможна работа со всем пространством видеопамяти (режим LFB);
D8=1 Поддерживается двойной режим сканирования;
D9=1 Поддержка чередующегося режима;
D10=1 Аппаратная поддержка тройной буферизации;
D11=1 Стереоскопический сигнал поддерживается ;
D12=1 Поддержка двух стартовых адресов для стереоскопического сигнала;
D13-D15 Резерв.

Бит D7 используется совместно с битом D6, значения их сочетаний показаны ниже:

D7 D6
Только оконный режим 0 0
n/a 0 1
Поддержка обоих режимов 1 0
Только LFB 1 1

Примечание: режим LFB (Linear Frame Buffer) - способ работы с видеопамятью через одно большое окно, расположенное в верхних адресах физической памяти. Используется только в защищённом режиме.

Области WinAAttributes и WinВAttributes описывают характеристики окон А и В. Об их назначении и способах работы будет сказано в следующих главах.
Значения битов (флагов) областей:

D0=1 Доступен механизм переключения окон;
D0=0 Доступно одно неперемещаемое окно;
D1=1 Окно доступно для чтения;
D2=1 Окно доступно для записи;
D3 - D7 Резерв;

Область WinGranularity определяет размер ячейки окна (зернистость) в КБ. Область WinGranularity может иметь значение не только равное 64 КБ, но, например, 4КБ или 16 КБ.
Область WinSize определяет размер видеоокна в КБ. Большинство, если не все видеоадаптеры имеют значение WinSize равное 64 КБ.
Области WinASegment и WinВSegment определяют начальные адреса видеосегментов А и В. Адреса видеоокон предназначены только для использования в реальном режиме. Если оконный режим не поддерживается и доступен только LFB, то значения областей будут установлены в нуль.
Область WinFuncPtr определяет адрес подпрограммы BIOS, которая входит в состав функции VBE 05h (Display Window Control). Прямой вызов подпрограммы обеспечивает более быстрое выполнение задачи, чем при использовании тяжеловесной функции. Если значение области неопределенно, то следует пользоваться функцией 05h.
Область BytesPerScanLine определяет размер строки в байтах для оконного режима. Размер строки может быть больше или равным отображаемой строки на экране.
Области XResolution и YResolution определяют ширину и высоту экрана в точках или символах. Для графического режима будет указано количество точек по горизонтали и вертикали. Для текстового режима соответственно указывается количество символов и строк.
Область MemoryModel определяет тип организации видеопамяти в данном режиме.
Существуют следующие виды организации видеопамяти:

00h - Текстовый режим;
01h - Графический режим CGA;
02h - Графический режим Hercules;
03h - Режим плоскостей;
04h - Режим packed pixel;
05h - Режим Non-chain 4, 256 color;
06h - Режим Direct Color;
07h - Режим YUV;
08h - 0Fh;
10h - FFh.

Области RedMaskSize, GreenMaskSize и BlueMaskSize определяют количество бит, необходимое для кодирования одного из трех цветов RGB. Для YUV модели видеопамяти красный цвет соответствует V, зеленый - Y, а синий - U.
Области RedFieldPosition, GreenFieldPosition и BlueFieldPosition содержат смещение от младшего бита кода точки и определяют младший бит соответствующего цвета.
Шесть вышеуказанных областей как раз и определяют цветовую схему, указанную в таблице 2 для видеорежимов VBE.
Более подробно об областях будет сказано по мере изложения материала.






Глава 5
Поиск видеорежима


Как было сказано ранее, перед установкой видеорежима следует убедиться, что требуемый режим поддерживается видеокартой и может быть установлен на ней. Так как сама идея стандартизации номеров режимов фактически отменена, то единственный способ получить код режима заключается в его поиске методом перебора по основным характеристикам. К таким характеристикам относятся экранное разрешение и количество отображаемых цветов. Приложению нужно по порядку запрашивать данные об имеющихся видеорежимах и сверять заданные характеристики с соответствующими параметрами информационного блока ModeInfoBlock. Если характеристики совпадают, то номер сохраняется в переменной для дальнейшего использования. Если требуемый режим не найден, то можно завершить программу либо попытаться найти другой подходящий режим.



Создадим четыре переменные: Horsize, Versize, Pixelcolor и Vmode - соответственно, размер строки в точках (горизонтальное разрешение), количество строк (вертикальное разрешение), размер точки в битах (глубина цвета) и номер видеорежима. Первые три переменные служат для записи характеристик режима, номер которого нужно найти. Переменные Vmode, Horsize и Versize имеют размер слова, а переменная Pixelcolor - размер байта.

Перед вызовом функции 01h в регистр СХ помещается номер режима, а в регистры ES:DI заносится адрес буфера размером 256 байт. В примере 2 используется прежний буфер Vinfo. Если же приложение будет использовать данные блока VbeInfoBlock, то его адрес следует сохранить в стеке, а затем создать новый буфер для размещения блока ModeInfoBlock, адрес которого нужно занести в регистры ES:DI.

Пример 2. Поиск номера видеорежима

STEP_2:

;Характеристики искомого видеорежима
MOV Horsize,640
MOV Versize,480
MOV Pixelcolor,16

;Начальный номер видеорежима
MOV Vmode,00h

;Начало цикла поиска видеорежима
STEP_3:
MOV AX,4F01h
MOV CX,Vmode
INT 10h

;Сравнение количества точек в строке
MOV AX,ES:[DI+12h]
CMP AX,Horsize
JZ POISK_1:
JMP POISK_0

;Сравение количества строк
POISK_1:
MOV AX,ES:[DI+14h]
CMP AX,Versize
JZ POISK_2:
JMP POISK_0

;Сравнение размера точки
POISK_2:
MOV AL,ES:[DI+19h]
CMP AL,Pixelsize
JZ STEP_4

POISK_0:
INC Vmode
MOV AX,00h
CMP AX,Vmode
JZ ERROR1
JMP STEP_3

;Режим найден, выход из поиска
STEP_4:

Примечание: пример 2 должен выполняться после примера 1.
Примечание: так как команда пересылки MOV не может непосредственно перемещать данные из одной ячейки памяти в другую, то в качестве посредника используется регистр АХ (аккумулятор).

Перед поиском видеорежима задаются его характеристики, в данном случае произвольно выбран режим с экранным разрешением 640 на 480 пикселей и размером кода точки 16 бит (Hi-color). Далее производить проверку можно двумя способами: либо пользоваться данными из области VideoModePtr блока VbeInfoBlock, либо по порядку проверять все существующие и несуществующие номера режимов. Ради удобства выбран последний способ. Переменная Vmode обнуляется для сканирования режимов с самого начала. Так как номер каждого видеорежима занимает 16 бит, то всего может быть не более чем 65536 режимов. В регистр CX заносится первый по счету номер видеорежима (в примере он равен нулю) и вызывается функция 01h.

Если режим поддерживается, то сегмент по адресу ES:DI заполняется данными о нем, если не поддерживается, то области сегмента очищаются. Проверка соответствия режима происходит по трем условиям. Если все или хотя бы одно условие не выполняется, то происходит переход на метку POISK_0, после которой номер видеорежима увеличивается на единицу и проверяется его порядковое значение. Если был исследован последний 65536-ой режим, то значение двухбайтовой переменной Vmode станет равным нулю и произойдет переход на метку ERROR_1. Это означает, что режим с требуемыми характеристиками найти не удалось.

Адрес буфера с данными о видеорежиме находится в регистрах ES:DI, причем регистр смещения DI обнулён, благодаря чему информационный блок ModeInfoBlock записывается с самого начала выделенного буфера. Для того, чтобы получить доступ к нужным полям блока, нужно просто изменять смещение, как это и сделано в примере 2. Смещения полей блока ModeInfoBlock указаны в таблице 3. Сначала происходит сравнение значения областей XResolution и YResolution со значениями переменных Horsize и Versize. При сравнении области BitsPerPixel и переменной Pixelsize используется только младшая часть регистра AX, так как сравниваемые значения имеют размер байта.

Если на каком то этапе выполнения примера 2 все три проверки прошли успешно, то произойдёт переход на метку STEP_4, а в переменной Vmode будет записан номер видеорежима. Приведенный список из трех условий не претендует на полноту, но в большинстве случаев их вполне достаточно. При необходимости можно включить в текст примера проверку состояния битов области атрибутов режима ModeAttributes. Что бы заранее свести к минимуму возникновение такой ситуации, когда режим не будет найден, следует задавать широкоупотребимые в настоящее время параметры. Например, желательно устанавливать глубину цвета 16 или 24 бит, а экранное разрешение выше 640 · 480 пикселей.





Глава 6
Установка видеорежима


После того, как по заданным характеристикам видеорежима был найден соответствующий ему номер, можно приступать к его установке. Для этого служит функция 02h - установка VBE режима (Set VBE Mode). Если режим установить невозможно, то BIOS оставляет текущий режим без изменения и возвращает код ошибки. Пример 3 должен выполняться после примера 2.


Пример 3. Установка видеорежима

STEP_4:

;Задание кода режима и установка флагов
MOV BX,Vmode
BTR BX,11
BTR BX,14
BTR BX,15

;Вызов функции VBE
MOV AX,4F02h
INT 10h

;Проверка статуса возврата функции
CMP AX,4Fh
JZ STEP_5
JMP ERROR2

;Режим успешно установлен
STEP_5:


Перед вызовом функции следует выполнить некоторые подготовительные действия. В регистр BX заносится номер режима (Vmode) и сбрасываются три его бита - D11, D14 и D15. Бит D15 обнуляется для предварительного очищения видеопамяти. Стандартом VBE оговаривается, что в некоторых случаях память не может быть очищена, поэтому приложение должно обладать для этого собственными средствами. Если бит D15 установлен, то память не очищается и на экране может появиться случайное хаотичное изображение. Бит D14 обнуляется для установки оконной модели видеопамяти. Бит D11 определяет свойства частоты обновления экрана. Для задания частоты обновления по умолчанию (обычно 60 Гц) бит D11 должен быть равен нулю.
Разумеется, вместо переменной Vmode можно сразу подставить непосредственное значение кода видеорежима (например, 111h или 114h) взятое из таблицы 3, однако такой способ установки режима давно устарел и имеет два недостатка. Во-первых, подобным образом невозможно установить режим разрешением выше 1280 · 1024 пикселей; во-вторых, это не рекомендуется самим стандартом VBE.

Если установка режима прошла благополучно, то в AX возвращается статус завершения функции 4Fh, видеокарта инициализирует заданный режим (обычно появляется черный экран) и происходит переход на метку STEP_5, после которой продолжается выполнение программы.
При необходимости, номер установленного видеорежима можно узнать с помощью функции 03h - возврат текущего режима (Return Current VBE ModeVBE). После вызова этой функции в регистре BX возвращается код текущего режима, а в АХ - статус завершения функции. Тот же номер режима хранится в переменной Vmode (пример 2).
Для того, что бы выводить точки на экран, одной установки режима недостаточно. Следует еще выполнить ряд подготовительных действий для работы с окнами видеопамяти, о чем будет рассказано далее.








Глава 7
Окна видеопамяти


В реальном режиме работы микропроцессора командам доступно ограниченное пространство адресов размером немного большим 1 Мбайта. Для обращения к любому байту этого пространства необходимо знать базу (segment) и смещение (offset). Эти два компонента адреса указываются в шестнадцатеричных регистрах - сегментном и индексном. Сегментный регистр указывает на номер параграфа, с которого начинается сегмент, а индексный регистр указывает смещение от начала сегмента. Параграф представляет собой блок памяти длиной 16 байт, выровненный по 16-ти байтной границе. Для работы с видеопамятью внутренние схемы всех видеокарт настроены на диапазоны адресов, совпадающих с адресным пространством микропроцессора, обычно они равны A0000h...AFFFFh для графических и B8000h...BFFFFh для текстовых режимов. Как видно, таким образом можно работать только с 64Кб видеопамяти, что явно недостаточно для режимов Direct Color. Объём видеопамяти современных видеоадаптеров гораздо больше 64Кб, поэтому для доступа к этой памяти был разработан механизм переключения окон.
Суть способа состоит в том, что на видеопамять накладывается виртуальный сегмент размером 64Кб, называемый видеоокном. С помощью специальной функции VBE можно перемещать это видеоокно по видеопамяти, тем самым обеспечивая доступ ко всей памяти видеоадаптера. Номер видеоокна нумеруется с нуля и хранится в специальном регистре видеокарты. Подобный механизм позволяет записывать и считывать данные из видеопамяти через узкое окошко размером 64Кб, причем значение сегментного регистра будет неизменным (обычно A0000h), а значения индексного регистра и номера видеоокон будут изменяться приложением в процессе выполнения задачи.

Функция 05h - управление видеоокном (Display Window Control)

Данная функция предназначена для перемещения видеоокна по видеопамяти и получения информации о его местонахождении. Стандартом VBE предусматривается наличие у видеоадаптеров двух видеоокон - А и В. Информация о них содержится в областях WinAAttributes и WinВAttributes структуры ModeInfoBlock (таблица 3). Одно из видеоокнон используется только для чтения, другое - только для записи. Видеоадаптер самостоятельно выбирает нужное окно в зависимости от требуемого действия и приложению следует только вовремя переключать номера окон.

Параметры вызова функции 05h:

Вход: АХ=4F05h код группы и функции VBE
BH = 00h Установить окно
BH = 01h Запросить номер окна
BL = 00h Окно A
BL = 01h Окно B
DX = Номер видеоокна (только при установке окна)

Выход: АХ= код возврата VBE
DX = Номер видеоокна (только при запросе номера окна)

Стандартом VBE не рекомендуется непосредственно пользоваться функцией 05h для установки видеоокон, так как при её вызове происходит выполнение множества вспомогательных действий, замедляющих процесс выполнения программы. Для установки окон следует пользоваться отдельной подпрограммой BIOS, которая выполняет основные действия функции 05h, минуя прерывание INT 10h. Адрес этой процедуры указан в области WinFuncPtr структуры ModeInfoBlock (таблица 3), её входные и выходные параметры идентичны функции 05h.
При выводе изображения на экран видеоадаптер считывает только ту часть видеопамяти, которая необходима для заполнения рабочей области экрана. Остальное пространство видеопамяти не используется. Например, для режима разрешением 1024 · 768 пикселей и глубиной цвета 24 бит требуется около 3Мб видеопамяти, что на фоне общего объёма памяти современных видеокарт величина небольшая. Для того, что бы сделать эту память полезной, на практике иногда используется приём деления видеопамяти на страницы. Отображаемая страница называется активной, а невидимая - пассивной. Можно заранее подготавливать изображение на пассивной странице, а затем резко делать её активной. Стандартом VBE ничего не говорится о страницах видеопамяти и специальный способ переключения страниц отсутствует. Однако существует функция VBE 07h (Set/Get Display Start), которая позволяет перенести начало рабочей области видеопамяти (Display start) в заданную точку, что может служить механизмом переключения страниц.
Подобный способ работы с видеопамятью вряд ли дает выигрыш в быстродействии, но позволяет реализовать некоторые эффекты, недоступные при обычном способе. Рациональность использования видеостраниц должна решаться программистом в зависимости от требований задачи.







Глава 8
Подготовительные действия


Для работы с видеоокнами и адресами точек следует выполнить ряд подготовительных действий, заключающихся в считывании из структуры ModeInfoBlock (таблица 3) необходимых данных.
Создадим следующие переменные:

WinA (2 байта) - Сегментный адрес видеоокна "А"
WinB (1 байт) - Атрибуты окна "В"
DWC (4 байта) - Адрес процедуры BIOS
Sline (2 байта) - Размер строки в байтах
Spixel (2 байта) - Размер точки в байтах


Пример 4. Подготовительные действия

;Адрес начала видеоокна "A"
MOV AX,ES:[DI+08h]
MOV WinA,AX

;Атрибуты видеокна "B"
MOV AL,ES:[DI+03h]
MOV WinB,AL

;Адрес подпрограммы BIOS
MOV EAX,ES:[DI+0Ch]
MOV DWC,EAX

;Размер строки в байтах
MOV AX,ES:[DI+10h]
MOV Sline,AX

;Вычисляем размер точки в байтах
XOR DX,DX
MOV BX,Horsize
DIV BX
MOV Spixel,AX

;Подготовка окон
MOV AX,WinA
MOV ES,AX
XOR DI,DI

;Переход в цикл программы
JMP MAIN

;Участок подпрограмм

;Тело программы
MAIN:

Примечание: не имеет значения, когда будет выполняться пример 4 - после примера 2 либо после примера 3.

Как говорилось в предыдущей главе, для работы с видеоокнами используется сегментный способ адресации к окну. Поэтому из области WinASegment в переменную WinA читается значение сегмента, который определяет адрес начала видеоокна А.
В переменную WinB записываются атрибуты окна B. Используется только младшая часть регистра АХ, так как область WinBAttributes имеет размер байта.
Адрес подпрограммы BIOS, которая входит в состав функции VBE 05h (глава 7), состоит из сегмента и смещения и имеет размер двойного слова, поэтому он записывается в переменную DWC через 32-разрядный регистр ЕАХ.
Значение размера точки в байтах понадобится при вычислении адреса точки, а так как в информационном блоке ModeInfoBlock эта величина явно не указана, то её следует вычислить. Для нахождения размера точки следует разделить размер строки в байтах (Sline) на её длину в точках (Horsize). Команда деления DIV требует, что бы делимое находилось в регистрах DX:AX (DX - старшая часть, AX - младшая часть), а делитель - в регистре общего назначения, например BX. Остаток деления помещается в АХ, а частное - в DX. В регистре AX уже находится значение Sline, а регистр DX очищается. В итоге в DX:AX находится значение размера строки в байтах. В регистр BX заносится горизонтальное разрешение в точках Horsize. Результатом деления всегда будет целое число, оно сохраняется в регистре DX, а остаток равен нулю.
Работа с видеопамятью осуществляется через регистры ES:DI, поэтому когда все необходимые данные из области ModeInfoBlock считаны, в регистр ES заносится адрес начала видеоокна A, а смещение DI очищается. После установки видеоокна по адресу ES:DI будет находится видеосегмент размером 64 Кб, с которым можно работать как с обычным сегментом оперативной памяти (т.е. считывать и записывать в него данные).
В зависимости от требований задачи список подготовительных действий может быть гораздо шире.




Глава 9
Работа с видеоокнами


Одним из распространённых и повторяющихся действий, которое придётся выполнять приложению при работе с сегментами видеопамяти является установка окна. В регистрах ES:DI уже содержится адрес видеоокна А (пример 4), однако для работы с ним следует уметь "накладывать" его на определённые участки видеопамяти. Такую операцию наложения осуществляет функция 05h, но, как уже говорилось, вместо неё используется более быстрая процедура BIOS, адрес которой записан в переменной DWC (Display Window Control). В области WinSize информационного блока ModeInfoBlock содержится значение размера окна, практически у всех моделей видеокарт оно равно 64 Кб и при установке видеоокон можно непосредственно использовать этот размер. Создадим переменную Num_Win размером 2 байта, которая будет являться входным параметром процедуры Set_Win и предназначена для хранения номера окна.

Пример 5. Подпрограмма установки окна Set_Win

;Объявление процедуры
Set_Win PROC

;В регистр DX заносится номер окна
MOV DX,Num_Win
;Регистр BX очищается для установки окна "A"
XOR BX,BX
;Вызов процедуры BIOS
CALL DWORD PTR [DWC]

;Проверка существования окна "В"
TEST WinB,01h
JE ResetB
;Установка окна "В"
MOV BX,01h
CALL DWORD PTR [DWC]

;Возврат в программу
ResetB: RET

;Объявление завершения процедуры
Set_Win ENDP

Пример начинается с объявления начала процедуры Set_Win. В регистр DX заносится номер видеоокна, которое требуется установить. Старшая и младшая часть регистра BX очищается для установки окна "А". Затем происходит косвенный дальний вызов подпрограммы BIOS. Заключение в скобки переменной DWC означает, что адресом подпрограммы является не DWC, а хранящееся в ней значение.
О второй части примера следует рассказать более подробно. Как известно, у видеокарты, кроме окна А, может существовать окно В. Если это второе окно не учитывать при написании программы, то приложение может работать некорректно. Существует два варианта работы с окнами А и В: раздельная работа и синхронная работа. В большинстве случаев удобно работать с одним видеоокном (А или В), синхронно изменяя номера обоих окон, что и реализовано в вышеизложенном примере. Достаточно лишь проверить существование окна В. Если оно существует, то ему присваивается тот же номер, что и окну А.
В примере 4 в переменную WinB были записаны атрибуты окна B, а в главе 4 описаны значения флагов области WinAttributes. Если нулевой бит D0 этой области очищен, то окна В не существует. Для проверки состояния бита используется команда логического сравнения TEST. Если нулевой бит очищен, то флаг ZF устанавливается в состояние "1". Состояние флага анализирует команда условного перехода JE. Если окно В существует, то оно устанавливается под тем же номером, как и окно А, если не существует, то происходит переход на команду RET и выполнение процедуры завершается.

В зависимости от видеорежима на экране может помещаться целое или нецелое число окон. Например, при стандартном размере окна в 64 кб при видеорежиме 116h (таблица 2) на экране умещается ровно 24 видеоокна.
Если в задаче используется функция 05h, то текст примера 5 будет выглядеть так же, но вместо строки с вызовом процедуры следует вызывать функцию 05h через прерывание INT 10h. При этом время выполнения подпрограммы существенно увеличится.






Глава 10
Построение точки на экране


После написания подпрограммы Set_Win и инициализации регистров ES:DI все готово для работы с видеопамятью. Видеоадаптер постоянно сканирует рабочую область видеопамяти и преобразует её содержимое в три цвето-яркостных сигнала RGB, которые передаются монитору для визуализации изображения. Достаточно записать в видеопамять коды точек и на экране возникнет их изображение.

Пример 6. Запись точки в видеопамять

;Цикл программы
MAIN:

;Определение номера окна
MOV Num_Win,00
;Вызов подпрограммы установки окна
CALL Set_Win
;Запись в регистр AX кода цвета точки (синий цвет)
MOV AX,1Fh
;Запись кода точки в видеопамять
MOV ES:[DI+00h],AX

В переменную Num_Win записывается номер окна, которое следует установить, затем вызывается процедура Set_Win, которая была описана в примере 5. После того, как окно установлено, в распоряжении приложения находится сегмент видеопамяти размером 64 Кб. В этот сегмент можно записывать данные, которые немедленно будут отображены на экране. После того, как работа с видеоокном будет окончена, устанавливается следующее окно (обычно отличающееся на единицу) и т.д.
Для получения доступа к любому байту видеосегмента используется смещение DI. В данном примере в первые два байта видеосегмента (00h и 01h) записывается код 1Fh точки. Этот код синего цвета справедлив для режимов с размером точки 15 и 16 бит. В примере 2 был произвольно выбран видеорежим с размером точки 16 бит, поэтому код точки занимает 2 байта. Нетрудно подсчитать, что из-за размера точки в 2 байта в одном видеоокне поместится всего 32768 точек (половина сегмента).
Как известно, точка на экране имеет вертикальную и горизонтальную координату, а видеокарта работает с видеоокном и смещением. Для того, что бы преобразовать экранные координаты точки в значение номера видеоокна и смещения в нем, следует произвести операцию расчёта адреса точки. Подробнее о цветах и адресах точек рассказывается в следующих двух главах.




Глава 11
Вычисление адреса точки


Недостатком использования оконного способа доступа к видеопамяти является малый размер окна. Экран как бы разбит на множество частей и приложению нужно постоянно следить за тем, достигнут ли конец границы окна и вовремя устанавливать другое. На практике координаты точек удобно задавать в декартовых координатах, но для преобразования адреса точки из вида (x,y) в адрес (Num_Win,DI) следует провести ряд математических вычислений, показанных ниже.

Пример 7. Вычисление видеоадреса точки

;Вычисление горизонтальной координаты в байтах
MOV AX,100
MOV DX,Spixel
MUL DX
MOV BX,AX

;Вычисление вертикальной координаты в байтах
MOV AX,Sline
MOV DX,200
MUL DX

;Сложение
ADD AX,BX

;Проверка и исправление переноса
ADC DX,0

;Инициализация номера окна и смещения
MOV Num_Win,DX
MOV DI,AX

;Вызов подпрограммы установки видеоокна
CALL Set_Win

;Определение цвета точки (синий)
MOV AX,1Fh

;Запись точки в видеопамять
MOV ES:[DI],AX

Примечание: данный пример не оптимизирован (целых две операции умножения) по той причине, что в каждом конкретном случае этот алгоритм будет видоизменён в соответствии с требованиями задачи.

В примере произвольно выбрана координата точки равная 100 пикселей по горизонтали и 200 по вертикали. По умолчанию принято, что отсчёт точек экрана ведётся с левого верхнего угла, а первая строка считается нулевой.
Для начала нужно вычислить горизонтальную координату точки в байтах. Для этого горизонтальную координату точки следует умножить на размер точки в байтах (Spixel). Команда умножения MUL требует, что бы один из сомножителей был помещён в регистр АХ. Регистр DX используется в качестве операнда и в него помещается второй сомножитель. Результат записывается в регистры DX:AX (в DX - старшая часть, в AX - младшая часть). В любом случае произведение получается величиной небольшой и потому записывается только в регистр AX, а DX очищается. Далее результат сохраняется в регистре BX, так как аккумулятор АХ будет использоваться в других операциях.
Вертикальная координата точки в байтах вычисляется путём умножения вертикальной координаты точки на размер строки в байтах (Sline). Результат сохраняется в DX:AX, причём в DX автоматически появляется номер окна (т.е. сколько раз в нём уместилось значение 65536), а в АХ - смещение.
Теперь к смещению в АХ прибавляется горизонтальная координата точки в байтах. Очевидно, что может возникнуть такая ситуация, когда результатом сложения будет число большее 65535. При этом произойдёт перенос и установится флаг переноса (CF=1). Поэтому, что бы получить корректные значения, используется команда ADC.
После того, как выполнены все вычисления, в регистре DX находится номер окна, а в АХ - смещение. Смещение записывается в регистр DI. Вызов функции Set_Win устанавливает видеоокно, номер которого пересылается из регистра DХ в переменную Num_Win. Все готово для записи точки в видеопамять по заданным экранным координатам.
Для наглядности вышеперечисленные действия можно отобразить в одной формуле:

Адрес = Spixel · 100 + Sline · 200

На практике при прорисовке объектов подобным способом вычисляют адрес только одной опорной точки, адреса остальных вычисляются более простыми способами.




Глава 12
Цвет в коде точки


Положение точки на экране определяется её адресом в видеопамяти, а цвет точки зависит от записанного кода по этому адресу. В главе 3 уже было сказано о режимах Direct color. В этих режимах код точки может занимать 2, 3 и 4 байта и определяет цвет точки. Байты регистров, куда записываются коды точек, принято нумеровать справа налево. Из видеопамяти видеоконтроллер считывает их в обратном порядке - слева направо.

Таблица 4. Размещение цветов в коде точки для режима Hi-color 32Кб

Красный цвет (R) Зеленый цвет (G) Синий цвет (B)
n/a E D C B А 9 8 7 6 5 4 3 2 1 0

В этом режиме код каждой точки занимает 15 разрядов (2^15 или 32 768 комбинаций цветов), а последний пятнадцатый разряд F не используется. Если биты двух базовых цветов обнулить, то изменяя значения пяти разрядов третьего цвета можно получать его оттенки. Например, коды базовых цветов максимальной интенсивности имеют следующие значения (для наглядности они показаны так же в двоичном виде, о чём говорит символ "b"):

красный — 7С00h (0111110000000000b);
зелёный — ЗЕ0h (0000001111100000b);
синий — 1Fh (0000000000011111b).

Таблица 5. Размещение цветов в коде точки для режима Hi-color 64Кб

Красный цвет (R) Зеленый цвет (G) Синий цвет (B)
F E D C B А 9 8 7 6 5 4 3 2 1 0

В отличие от предыдущего режима, в этом код красного и синего цвета занимают по 5 разрядов, а зелёный - 6 разрядов. Код каждой точки занимает 16 разрядов (2^16 или 65 536 комбинаций цветов). Коды базовых цветов максимальной интенсивности для этого режима имеют следующие значения:

красный — 0F800h (1111100000000000b);
зелёный — 7E0h (0000011111100000b);
синий — 1Fh (0000000000011111b).


Таблица 6. Размещение цветов в коде точки для режима True Color 16.8Мб

Резерв (байт 3) Красный (байт 2) Зелёный (байт 1) Синий (байт 0)
1F ... 18 17 ... 10 F ... 8 7 .... 0

В этом режиме код каждой точки занимает 24 разряда (2^24 или 16 777 216 комбинаций цветов), но из за неудобства работы с нечётным числом байтов используется дополнительный пустой резервный байт. Код каждого базового цвета занимает 8 бит (1 байт), а четвёртый байт пустой. Стандартом VBE допускается отсутствие резервного байта

Если задача рассчитана на работу с различными видеорежимами, то она должна уметь использовать данные участка "Информация для Direct Color" инфоблока ModeInfoBlock (таблица 3). В нём описывается цветовая схема для точки, т.е. то же самое, что и в таблицах 4-6. При установке видеорежима True Color следует обязательно проверять значение области BitsPerPixel (размер точки в битах), так как некоторые видеокарты могут поддерживать только 3-значный код точки.





Эпилог


Реальный режим работы процессора имеет ряд недостатков. Самые главные из них заключаются в том, что процессор может адресоваться к видеопамяти только через небольшие видеоокна, а использование 32-разрядных и 64-разрядных регистров в этом режиме ограничено. Всё это существенно влияет на скорость работы приложений и не позволяет полностью реализовать возможности современных персональных систем. На данный момент все графические приложения создаются для защищённого режима, который даёт значительный выигрыш в быстродействии и совместимости с новейшими программными и аппаратными средствами. Более того, в этом режиме доступно программирование шейдеров при помощи специфических шейдерных языков (OpenGL, Cg, DirectX ASM и др).
При программировании в реальном и виртуальном режиме приложению доступны только функции операционной системы DOS и функции BIOS материнской платы компьютера. Описание этих функций подробно изложены в достаточно большом количестве литературы. Если в задаче используются только функции BIOS, то такая задача является независимой от функций любой операционной системы, так как использует только встроенные подпрограммы оборудования. Фактически, для приложения, использующего только функции BIOS, операционная система нужна лишь для его запуска, всё остальное программа делает самостоятельно.
В данной книге освещены лишь некоторые вопросы относительно программирования видеокарт и для более детального ознакомления следует воспользоваться самим стандартом VBE.

14.08.2008г.


г. Могилёв

HTML версия ser99349059@yandex.ru

Новые статьи на library.by:
КОМПЬЮТЕРЫ И ИНТЕРНЕТ:
Комментируем публикацию: Низкоуровневое программирование современных видеокарт

© Есиповский С.Н. ()

Искать похожие?

LIBRARY.BY+ЛибмонстрЯндексGoogle
подняться наверх ↑

ПАРТНЁРЫ БИБЛИОТЕКИ рекомендуем!

подняться наверх ↑

ОБРАТНО В РУБРИКУ?

КОМПЬЮТЕРЫ И ИНТЕРНЕТ НА LIBRARY.BY

Уважаемый читатель! Подписывайтесь на LIBRARY.BY в VKновости, VKтрансляция и Одноклассниках, чтобы быстро узнавать о событиях онлайн библиотеки.