|
[Назад] [Далее] | |
VGA-совместимые видеоадаптеры управляются при помощи портов ввода-вывода 03C0h – 03CFh, 03B4h, 03B5h, 03D4h, 03D5h, 03DAh, причем реальное число внутренних регистров видеоадаптера, к которым можно обращаться через это окно, превышает 50. Так как BIOS предоставляет хорошую поддержку для большинства стандартных функций, мы не будем рассматривать подробно программирование видеоадаптера на уровне портов, а только рассмотрим основные действия, для которых принято обращаться к видеоадаптеру напрямую.
Доступ к этим регистрам осуществляется прямым обращением к соответствующим портам ввода-вывода.
Регистр состояния ввода 0 (ISR0) — доступен для чтения из порта 03С2
бит 7: произошло прерывание обратного хода луча IRQ2
бит 6: дополнительное устройство 1 (линия FEAT1)
бит 5: дополнительное устройство 0 (линия FEAT0)
бит 4: монитор присутствует
Регистр вывода (MOR) — доступен для чтения из порта 3CCh и для записи как 3C2h
биты 7 – 6: полярность сигналов развертки — (01, 10, 11) = (350, 400, 480) линий
бит 5: 1/0 — нечетная/четная страница видеопамяти
биты 3 – 2: частота — (00, 01) = (25,175 MHz, 28,322 MHz)
бит 1: 1/0 — доступ CPU к видеопамяти разрешен/запрещен
бит 0: 1/0 — адрес порта контроллера CRT = 03D4h/03B4h
Регистр состояния ввода 1 (ISR1) — доступен для чтения из порта 03DAH
бит 3: происходит вертикальный обратный ход луча
бит 0: происходит любой обратный ход луча
Лучший момент для вывода данных в видеопамять — момент, когда электронный луч двигается от конца экрана к началу и экран не обновляется, то есть вертикальный обратный ход луча. Перед копированием в видеопамять полезно вызывать, например, следующую процедуру:
; процедура wait_retrace ; возвращает управление в начале обратного вертикального хода луча ; wait_retrace proc near push ax push dx mov dx,03DAh ; порт регистра ISR1 wait_retrace_end: in al,dx test al,1000b ; проверить бит 3 ; Если не ноль - jnz wait_retrace_end ; подождать конца ; текущего обратного хода wait_retrace_start: in al,dx test al,1000b ; а теперь подождать ; начала следующего jz wait_retrace_start pop dx pop ax ret wait_retrace endp
Контроллер атрибутов преобразовывает значения байта атрибута символа в цвета символа и фона. Для записи в эти регистры надо записать в порт 03C0h номер регистра, а затем (второй командой out) — данные для этого регистра. Чтобы убедиться, что 03C0h находится в состоянии приема номера, а не данных, надо выполнить чтение из ISR1 (порт 03DAh). Порт 03C1h можно использовать для чтения последнего записанного индекса или данных.
00h – 0Fh: Регистры палитры EGA
биты 5 – 0: номер регистра в текущей странице VGA DAC, соответствующего данному EGA-цвету.
10h: Регистр управления режимом
бит 7: разбиение регистров VGA DAC для 16-цветных режимов: 1 = 16 страниц по 16 регистров, 0 = 4 страницы по 64 регистра
бит 6: 1 = 8-битный цвет, 0 = 4-битный цвет
бит 5: горизонтальное панорамирование разрешено
бит 3: 1/0 — бит 7 атрибута управляет миганием символа/цветом фона
бит 2: девятый пиксель в каждой строке повторяет восьмой
бит 1: 1/0 — генерация атрибутов для монохромных/цветных режимов
бит 0: 1/0 — генерация атрибутов для текстовых/графических режимов
11h: Регистр цвета бордюра экрана (по умолчанию 00h)
биты 7 – 0: номер регистра VGA DAC
12h: Регистр разрешения использования цветовых плоскостей
бит 3: разрешить плоскость 3
бит 2: разрешить плоскость 2
бит 1: разрешить плоскость 1
бит 0: разрешить плоскость 0
13h: Регистр горизонтального панорамирования
биты 3 – 0: величина сдвига по горизонтали в пикселях (деленная на 2 для режима 13h)
14h: Регистр выбора цвета (по умолчанию 00h)
Функции INT 10h AX = 1000h – 1009h позволяют использовать большинство из этих регистров, но кое-что, например панорамирование, оказывается возможным только при программировании на уровне портов.
Для обращения к регистрам графического контроллера следует записать индекс нужного регистра в порт 03CEh, после чего можно будет читать и писать данные для выбранного регистра в порт 03CFh. Если требуется только запись в регистры, можно просто поместить индекс в AL, посылаемый байт — в АН и выполнить команду вывода слова в порт 03CEh. Этот контроллер, в первую очередь, предназначен для обеспечения передачи данных между процессором и видеопамятью в режимах, использующих цветовые плоскости, как, например, режим 12h (640x480x16).
00h: Регистр установки/сброса
биты 3 – 0: записывать FFh в цветовую плоскость 3 – 0 соответственно
01h: Регистр разрешения установки/сброса
биты 3 – 0: включить режим установки/сброса для цветовой плоскости 3 – 0
В этом режиме данные для одних цветовых слоев получают от CPU, а для других — из регистра установки/сброса. Режим действует только в нулевом режиме работы (см. регистр 05h).
02h: Регистр сравнения цвета
биты 3 – 0: искомые биты для цветовых плоскостей 3 – 0
Используется для поиска пикселя заданного цвета, чтобы не обращаться по очереди во все цветовые слои.
03h: Регистр циклического сдвига данных
биты 4 – 3: выбор логической операции:
00 — данные от CPU записываются без изменений
01 — операция AND над CPU и регистром-защелкой
10 — операция OR над CPU и регистром-защелкой
11 — операция XOR над CPU и регистром-защелкой
биты 2 – 0: на сколько бит выполнять вправо циклический сдвиг данных перед записью в видеопамять
04h: Регистр выбора читаемой плоскости
биты 1 – 0: номер плоскости (0 – 3)
Запись сюда изменяет номер цветовой плоскости, данные из которой получает CPU при чтении из видеопамяти.
05h: Регистр выбора режима работы
бит 6: 1/0 — 256/16 цветов
бит 4: четные адреса соответствуют плоскостям 0, 2, нечетные — 1,3
бит 3: 1 — режим сравнения цветов
биты 1 – 0: режим:
00: данные из CPU (бит на пиксель) + установка/сброс + циклический сдвиг + логические функции
01: данные в/из регистра-защелки (прочитать в него и записать в другую область памяти быстрее, чем через CPU)
10: данные из CPU, байт на пиксель, младшие 4 бита записываются в соответствующие плоскости
11: то же самое + режим битовой маски
06h: Многоцелевой регистр графического контроллера
биты 3 – 2: видеопамять:
00: A0000h – BFFFFh (128 Кб)
01: A0000h – AFFFFh (64 Кб)
10: B0000h – B7FFFh (32 Кб)
11: B8000h – BFFFFh (32 Кб)
бит 0: 1/0 — графический/текстовый режим
07h: Регистр игнорирования цветовых плоскостей
биты 3 – 0: игнорировать цветовую плоскость 3 – 0
08h: Регистр битовой маски
Если бит этого регистра 0 — соответствующий бит будет браться из регистра-защелки, а не от CPU. (Чтобы занести данные в регистр-защелку, надо выполнить одну операцию чтения из видеопамяти, при этом в каждый из четырех регистров-защелок будет помещено по одному байту из соответствующей цветовой плоскости.)
Графический контроллер предоставляет весьма богатые возможности по управлению режимами, использующими цветовые плоскости. В качестве примера напишем процедуру, выводящую точку на экран в режиме 12h (640x480x16) с использованием механизма установки/сброса:
; процедура putpixel12h ; выводит на экран точку с заданным цветом в режиме 12h (640x480x16) ; Ввод: DX = строка ; СХ = столбец ; ВР = цвет ; ES = 0A000h putpixel12h proc near pusha ; вычислить номер байта в видеопамяти xor bx,bx mov ax,dx ; AX = строка lea еах,[еах+еах*4] ; АХ = АХ * 5 shl ах,4 ; АХ = АХ * 16 ; АХ = строка * байт_в_строке ; (строка * 80) push cx shr cx,3 ; CX = номер байта в строке add ax,cx ; АХ = номер байта в видеопамяти mov di,ax ; сохранить его в DI ; вычислить номер бита в байте pop сх and cx,07h ; остаток от деления на 8 - номер ; бита в байте, считая справа налево mov bx,0080h shr bx,cl ; в BL теперь нужный бит установлен в 1 ; программирование портов mov dx,03CEh ; индексный порт ; графического контроллера mov ax,0F01h ; регистр 01h: разрешение ; установки/сброса out dx,ax ; разрешить установку/сброс для ; всех плоскостей (эту часть лучше ; сделать однажды в программе, например сразу после установки ; видеорежима, и не повторять каждый раз при вызове процедуры) mov ax,bp shl ax,8 ; регистр 00h: регистр ; установки/сброса out dx,ax ; АН = цвет mov al,08 ; порт 08h: битовая маска mov ah,bl ; записать в битовую маску нули ; всюду, кроме out dx,ax ; бита, соответствующего выводимому пикселю mov ah,byte ptr es:[di] ; заполнить ; регистры-защелки mov byte ptr es:[di],ah ; вывод на экран: ; выводится единственный бит ; в соответствии с содержимым регистра битовой маски, остальные ; биты берутся из защелки, то есть не изменяются. Цвет выводимого ; бита полностью определяется значением регистра установки/сброса рора ret putpixel12h endp
Контроллер CRT управляет разверткой и формированием кадров на дисплее. Как и для графического контроллера, для обращения к регистрам контроллера CRT следует записать индекс нужного регистра в порт 03D4h, после чего можно будет читать и писать данные для выбранного регистра в порт 03D5h. Если требуется только запись в регистры, можно просто поместить индекс в AL, посылаемый байт — в АН и выполнить команду вывода слова в порт 03D4h.
00h: Общая длина горизонтальной развертки
01h: Длина отображаемой части горизонтальной развертки минус один
02h: Начало гашения луча горизонтальной развертки
03h: Конец гашения луча горизонтальной развертки
биты 6 – 5: горизонтальное смещение в текстовых режимах
биты 4 – 0: конец импульса
04h: Начало горизонтального обратного хода луча
05h: Конец горизонтального обратного хода луча
биты 7, 4 – 0: конец импульса
биты 6 – 5: горизонтальное смещение импульса
06h: Число вертикальных линий растра без двух старших бит
07h: Дополнительный регистр
бит 7: бит 9 регистра 10h
бит 6: бит 9 регистра 12h
бит 5: бит 9 регистра 06h
бит 4: бит 8 регистра 18h
бит 3: бит 8 регистра 15h
бит 2: бит 8 регистра 10h
бит 1: бит 8 регистра 12h
бит 0: бит 8 регистра 06h
08h: Предварительная горизонтальная развертка
биты 6 – 5: биты 5 и 4 регистра горизонтального панорамирования
биты 4 – 0: номер линии в верхней строке, с которой начинается изображение
09h: Высота символов
бит 7: двойное сканирование (400 линий вместо 200)
бит 6: бит 9 регистра 18h
бит 5: бит 9 регистра 15h
биты 4 – 0: высота символов минус один (от 0 до 31)
0Ah: Начальная линия курсора (бит 5: гашение курсора)
0Bh: Конечная линия курсора (биты 6 – 5: отклонение курсора вправо)
0Ch: Старший байт начального адреса
0Dh: Младший байт начального адреса (это адрес в видеопамяти, начиная с которого выводится изображение)
0Eh: Старший байт позиции курсора
0Fh: Младший байт позиции курсора
10h: Начало вертикального обратного хода луча без старшего бита
11h: Конец вертикального обратного хода луча без старшего бита
бит 7: защита от записи в регистры 00 – 07 (кроме бита 4 в 07h)
бит 6: 1/0 — 5/3 цикла регенерации за время обратного хода луча
бит 5: 1/0 — выключить/включить прерывание по обратному ходу луча
бит 4: запись нуля сюда заканчивает обработку прерывания
биты 3 – 0: конец вертикального обратного хода луча
12h: Число горизонтальных линий минус один без двух старших бит
13h: Логическая ширина экрана (в словах/двойных словах на строку)
14h: Положение символа подчеркивания
бит 6: 1/0 — адресация словами/двойными словами
бит 5: увеличение счетчика адреса регенерации на 4
биты 4 – 0: положение подчеркивания
15h: Начало импульса гашения луча вертикальной развертки без двух старших бит
16h: Конец импульса гашения вертикальной развертки
17h: Регистр управления режимом
бит 7: горизонтальный и вертикальный ходы луча отключены
бит 6: 1/0 — адресация байтами/словами
бит 4: 1 — контроллер выключен
бит 3: 1/0 — счетчик адреса регенерации растет на 2/1 на каждый символ
бит 2: увеличение в 2 раза разрешения по вертикали
18h: Регистр сравнения линий без двух старших бит
(от начала экрана до линии с номером из этого регистра отображается начало видеопамяти, а от этой линии до конца — видеопамять, начиная с адреса, указанного в регистрах 0Ch и 0Dh)
22h: Регистр-защелка (только для чтения)
23h: Состояние контроллера атрибутов
биты 7 – 3: текущее значение индекса контроллера атрибутов
бит 2: источник адреса палитры
бит 0: состояние порта контроллера атрибутов — 0/1 = индекс/данные
BIOS заполняет регистры этого контроллера соответствующими значениями при переключении видеорежимов. Так как одного контроллера CRT мало для полного переключения в новый видеорежим, мы вернемся к этому чуть позже, а пока посмотрим, как внести небольшие изменения в действующий режим, например, как превратить текстовый режим 80x25 в 80x30:
; 80x30.asm ; переводит экран в текстовый режим 80x30 (размер символов 8x16) ; (Norton Commander 5.0 в отличие от, например, FAR восстанавливает режим по ; окончании программы, но его можно обмануть, если предварительно нажать ; Alt-F9) .model tiny .code .186 ; для команды outsw org 100h ; СОМ-программа start: mov ax,3 ; установить режим 03h (80x25), int 10h ; чтобы только внести небольшие изменения mov dx,3CCh ; порт 3CCh: регистр вывода (MOR) на чтение in al,dx mov dl,0C2h ; порт 03C2h: регистр вывода (MOR) на запись or al,0C0h ; установить полярности 1,1 - для 480 строк out dx,al mov dx,03D4h ; DX = порт 03D4h: индекс CRT mov si,offset crt480 ; DS:SI = адрес таблицы данных для CRT mov cx,crt480_l ; CX = ее размер rep outsw ; послать все устанавливаемые параметры ; в порты 03D4h и 03D5h ; нельзя забывать сообщать BIOS об изменениях в видеорежиме push 0040h pop es ; ES = 0040h mov byte ptr es:[84h],29 ; 0040h:0084h - число строк ret ; данные для контроллера CRT в формате индекс в младшем байте, данные в ; старшем - для записи при помощи команды outsw crt480 dw 0C11h ; регистр 11h всегда надо записывать первым, ; так как его бит 7 разрешает запись в другие dw 0B06h,3E07h,0EA10h,0DF12h,0E715h,0416h ; регистры crt480_l = ($-crt480)/2 end start
Еще одна интересная возможность, которую предоставляет контроллер CRT, — плавная прокрутка экрана при помощи регистра 08h:
; vscroll.asm ; Плавная прокрутка экрана по вертикали. Выход - клавиша Esc ; .model tiny .code .186 ; для push 0B400h org 100h ; СОМ-программа start: push 0B800h pop es xor si,si ; ES:SI - начало видеопамяти mov di,80*25*2 ; ES:DI - начало второй страницы видеопамяти mov cx,di rep movs es:any_label,es:any_label ; скопировать первую ; страницу во вторую mov dx,03D4h ; порт 03D4h: индекс CRT screen_loop: ; цикл по экранам mov cx,80*12*2 ; СХ = начальный адрес - адрес середины экрана line_loop: ; цикл по строкам mov al,0Ch ; регистр 0Ch - старший байт начального адреса mov ah,ch ; байт данных - СН out dx,ax ; вывод в порты 03D4, 03D5 inc ax ; регистр 0Dh - младший байт начального адреса mov ah,cl ; байт данных - CL out dx,ax ; вывод в порты 03D4, 03D5 mov bx,15 ; счетчик линий в строке sub cx,80 ; переместить начальный адрес на начало ; предыдущей строки (так как это движение вниз) pel_loop: ; цикл по линиям в строке call wait_retrace ; подождать обратного хода луча mov al,8 ; регистр 08h - выбор номера линии в первой ; строке, с которой начинается вывод изображения mov ah,bl ; (номер линии из BL) out dx,ax dec bx ; уменьшить число линий, jge pel_loop ; если больше или = нулю - строка еще не ; прокрутилась до конца и цикл по линиям ; продолжается in al,60h ; прочитать скан-код последнего символа, cmp al,81h ; если это 81h (отпускание клавиши Esc), jz done ; выйти из программы, cmp cx,0 ; если еще не прокрутился целый экран, jge line_loop ; продолжить цикл по строкам, jmp short screen_loop ; иначе: продолжить цикл по экранам done: ; выход из программы mov ax,8 ; записать в регистр CRT 08h out dx,ax ; байт 00 (никакого сдвига по вертикали), add ax,4 ; а также 00 в регистр 0Ch out dx,ax inc ax ; и 0Dh (начальный адрес совпадает out dx,ax ; с началом видеопамяти) ret wait_retrace proc near push dx mov dx,03DAh VRTL1: in al,dx ; порт 03DAh - регистр ISR1 test al,8 jnz VRTL1 ; подождать конца текущего обратного хода луча, VRTL2: in al,dx test al,8 jz VRTL2 ; а теперь начала следующего wait_retrace endp any_label label byte ; метка для переопределения сегмента в movs end start
Горизонтальная прокрутка осуществляется аналогично, только с использованием регистра горизонтального панорамирования 13h из контроллера атрибутов.
Для обращения к регистрам синхронизатора следует записать индекс нужного регистра в порт 03C4h, после чего можно будет читать и писать данные для выбранного регистра в порт 03C5h. Точно так же, если требуется только запись в регистры, можно просто поместить индекс в AL, посылаемый байт — в АН и выполнить команду вывода слова в порт 03CEh.
00h: Регистр сброса синхронизации
бит 1: запись нуля сюда вызывает синхронный сброс
бит 0: запись нуля сюда вызывает асинхронный сброс
01h: Регистр режима синхронизации
бит 5: 1 — обмен данными между видеопамятью и дисплеем выключен
бит 3: 1 — частота обновления для символов уменьшена в два раза
бит 0: 1/0 — ширина символа 8/9 точек
02h: Регистр маски записи
бит 3: разрешена запись CPU в цветовую плоскость 3
бит 2: разрешена запись CPU в цветовую плоскость 2
бит 1: разрешена запись CPU в цветовую плоскость 1
бит 0: разрешена запись CPU в цветовую плоскость 0
03h: Регистр выбора шрифта
бит 5: если бит 3 атрибута символа = 1, символ берется из шрифта 2
бит 4: если бит 3 атрибута символа = 0, символ берется из шрифта 2
биты 3 – 2: номер таблицы для шрифта 2
биты 1 – 0: номер таблицы для шрифта 1
(00, 01, 10, 11) = (0 Кб, 16 Кб, 32 Кб, 48 Кб от начала памяти шрифтов VGA)
04h: Регистр организации видеопамяти
бит 3: 1 — режим CHAIN-4 (используется только в видеорежиме 13h)
бит 2: 0 — четные адреса обращаются к плоскостям 0, 2, нечетные — к 1, 3
бит 1: объем видеопамяти больше 64 Кб
Хотя BIOS и позволяет использовать некоторые возможности этих регистров, в частности работу со шрифтами (INT 10h АН = 11h) и выключение обмена данными между видеопамятью и дисплеем (INT 10h, АН = 12h, BL = 32h), прямое программирование регистров синхронизатора вместе с регистрами контроллера CRT позволяет значительно изменять характеристики видеорежимов VGA, вплоть до установки нестандартных видеорежимов. Наиболее популярными режимами являются так называемые режимы «X» с 256 цветами и с разрешением 320 или 360 пикселей по горизонтали и 200, 240, 400 или 480 пикселей по вертикали. Так как такие режимы не поддерживаются BIOS, для их реализации нужно написать все необходимые процедуры — установку видеорежима, вывод пикселя, чтение пикселя, переключение страниц, изменение палитры, загрузку шрифтов. При этом для всех режимов из этой серии, кроме 320x240x256, приходится также учитывать измененное соотношение размеров экрана по вертикали и горизонтали, чтобы круг, выведенный на экран, не выглядел как эллипс, а квадрат — как прямоугольник.
Установка нового режима выполняется почти точно так же, как и в предыдущем примере, — путем модификации существующего. Кроме того, нам придется изменять частоту кадров (биты 3 – 2 регистра MOR), а это приведет к сбою синхронизации, если мы не выключим синхронизатор на время изменения частоты (записью в регистр 00h):
; процедура set_modex ; переводит видеоадаптер VGA в один из режимов X с 256 цветами ; ввод: DI = номер режима ; 0: 320x200, соотношение сторон 1,2:1 ; 1: 320x400, соотношение сторон 2,4:1 ; 2: 360x200, соотношение сторон 1,35:1 ; 3: 360x400, соотношение сторон 2,7:1 ; 4: 320x240, соотношение сторон 1:1 ; 5: 320x480, соотношение сторон 2:1 ; 6: 360x240, соотношение сторон 1,125:1 ; 7: 360x480, соотношение сторон 2,25:1 ; DS = CS ; Для вывода информации на экран в этих режимах ; см. процедуру putpixel_x setmode_x proc near mov ax,12h ; очистить все четыре цветовые int 10h ; плоскости видеопамяти, mov ax,13h ; установить режим 13h, который будем int 10h ; модифицировать cmp di,7 ; если нас вызвали с DI > 7, ja exit_modex ; выйти из процедуры ; (оставшись в режиме 13h), shl di,1 ; умножить на 2, так как x_modes - ; таблица слов, mov di,word ptr x_modes[di] ; прочитать ; адрес таблицы настроек для ; выбранного режима mov dx,03C4h ; порт 03C4h - индекс синхронизатора mov ax,0100h ; регистр 00h, значение 01 out dx,ax ; асинхронный сброс mov ax,0604h ; регистр 04h, значение 06h out dx,ax ; отключить режим CHAIN4 mov dl,0C2h ; порт 03C2h - регистр ; MOR на запись mov al,byte ptr [di] ; записать в него ; значение частоты кадров out dx,al ; и полярности развертки ; для выбранного режима mov dl,0D4h ; порт 03D4h - индекс ; контроллера CRT mov si,word ptr offset [di+2] ; адрес строки с настройками ; для выбранной ширины в DS:SI mov cx,8 ; длина строки настроек в СХ rep outsw ; вывод строки слов ; в порты 03D4/03D5 mov si,word ptr offset [di+4] ; настройки для ; выбранной высоты в DS:SI mov сх,7 ; длина строки настроек в СХ rep outsw mov si,word ptr offset [di+6] ; настройки ; для включения/выключения удвоения ; по вертикали (200/400 и 240/480 строк) mov сх,3 rep outsw mov ax, word ptr offset [di+8] ; число байт в строке mov word ptr x_width,ax ; сохранить ; в переменной x_width mov dl,0C4h ; порт 03C4h - индекс синхронизатора mov ах,0300h ; регистр 00h, значение 03 out dx,ax ; выйти из состояния сброса exit_modex: ret ; таблица адресов таблиц с настройками режимов x_modes dw offset mode_0,offset mode_1 dw offset mode_2,offset mode_3 dw offset mode_4,offset mode_5 dw offset mode_6,offset mode_7 ; таблица настроек режимов: значение регистра MOR, адрес строки ; настроек ширины, адрес строки настроек высоты, адрес строки ; настроек удвоения по вертикали, число байт в строке mode_0 dw 63h,offset mode_320w,offset mode_200h,offset mode_double,320/4 mode_1 dw 63h,offset mode_320w,offset mode_400h,offset mode_single,320/4 mode_2 dw 67h,offset mode_360w,offset mode_200h,offset mode_double,360/4 mode_3 dw 67h,offset mode_360w,offset mode_400h,offset mode_single,360/4 mode_4 dw 0E3h,offset mode_320w,offset mode_240h,offset mode_double,320/4 mode_5 dw 0E3h,offset mode_320w,offset mode_480h,offset mode_single,320/4 mode_6 dw 0E7h,offset mode_360w,offset mode_240h,offset mode_double,360/4 mode_7 dw 0E7h,offset mode_360w,offset mode_480h,offset mode_single,360/4 ; настройки CRT. В каждом слове младший байт - номер регистра, ; старший - значение, которое в этот регистр заносится mode_320w: ; настройка ширины 320 ; Первый регистр обязательно 11h, хотя он и не относится ; к ширине - он разрешает запись в остальные регистры, ; если она была запрещена (!) dw 0E11h,5F00h,4F01h,5002h,8203h,5404h,8005h,2813h mode_360w: ; настройка ширины 360 dw 0E11h,6B00h,5901h,5A02h,8E03h,5E04h,8A05h,2D13h mode_200h: mode_400h: ; настройка высоты 200/400 dw 0BF06h,1F07h,9C10h,0E11h,8F12h,9615h,0B916h mode_240h: mode_480h: ; настройка высоты 240/480 dw 0D06h,3E07h,0EA10h,0C11h,0DF12h,0E715h,0616h mode_single: ; настройка режимов без удвоения dw 4009h,0014h,0E317h mode_double: ; настройка режимов с удвоением dw 4109h,0014h,0E317h setmode_x endp x_width dw ? ; число байт в строке ; эту переменную инициализирует setmode_x, а использует putpixel_x ; процедура putpixel_x ; выводит точку с заданным цветом в текущем режиме X ; Ввод: DX = строка ; СХ = столбец ; ВР = цвет ; ES = A000h ; DS = сегмент, в котором находится переменная x_width putpixel_x proc near pusha mov ax, dx mul word ptr x_width ; AX = строка * число байт в строке mov di,cx ; DI = столбец shr di,2 ; DI = столбец/4 (номер байта в строке) add di,ax ; DI = номер байта в видеопамяти mov ax,0102h ; AL = 02h (номер регистра), ; АН = 01 (битовая маска) and cl,03h ; CL = остаток от деления ; столбца на 4 = номер ; цветовой плоскости shl ah,cl ; теперь в АН выставлен в 1 бит, ; соответствующий нужной ; цветовой плоскости mov dx,03C4h ; порт 03C4h - индекс ; синхронизатора out dx,ax ; разрешить запись только ; в нужную плоскость mov ax,bp ; цвет в AL stosb ; вывод байта в видеопамять рора ret putpixel_x endp
Таблица цветов VGA на самом деле представляет собой 256 регистров, в каждом из которых записаны три 6-битных числа, соответствующих уровням красного, зеленого и синего цвета. Подфункции INT 10h AX =1010h – 101Bh позволяют удобно работать с этими регистрами, но, если требуется максимальная скорость, программировать их на уровне портов ввода-вывода не намного сложнее.
03C6h для чтения/записи: Регистр маскирования пикселей (по умолчанию FFh)
При обращении к регистру DAC выполняется операция AND над его номером и содержимым этого регистра.
03C7h для записи: Регистр индекса DAC для режима чтения
Запись байта сюда переводит DAC в режим чтения, так что следующее чтение из 03C9h вернет значение регистра палитры с этим индексом.
03C7h для чтения: Регистр состояния DAC
биты 1 – 0: 00/11 — DAC в режиме записи/чтения
03C8h для чтения/записи: Регистр индекса DAC для режима записи
Запись байта сюда переводит DAC в режим записи, так что следующие записи в 03C3h будут записывать новые значения в регистры палитры, начиная с этого индекса.
03C3h для чтения/записи: Регистр данных DAC
Чтение отсюда считывает значение регистра палитры с индексом, записанным предварительно в 03C8h, запись — записывает новое значение в регистр палитры с индексом, записанным в 03C8h. На каждый регистр требуются три операции чтения/записи, передающие три 6-битных значения уровня цвета: красный, зеленый, синий. После третьей операции чтения/записи индекс текущего регистра палитры увеличивается на 1, так что можно считывать/записывать сразу несколько регистров
Команды insb/outsb серьезно облегчают работу с регистрами DAC в тех случаях, когда требуется считывать или загружать значительные участки палитры или всю палитру целиком, — такие процедуры оказываются и быстрее, и меньше аналогичных, написанных с использованием прерывания INT 10h. Посмотрим, как это реализуется на примере программы плавного гашения экрана.
; fadeout.asm ; выполняет плавное гашение экрана .model tiny .code .186 ; для команд insb/outsb org 100h ; СОМ-программа start: cld ; для команд строковой обработки mov di,offset palettes call read_palette ; сохранить текущую палитру, чтобы ; восстановить в самом конце программы, mov di,offset palettes+256*3 call read_palette ; а также записать еще одну копию ; текущей палитры, которую будем ; модифицировать mov cx,64 ; счетчик цикла изменения палитры main_loop: push cx call wait_retrace ; подождать начала обратного хода луча mov di,offset palettes+256*3 mov si,di call dec_palette ; уменьшить яркость всех цветов call wait_retrace ; подождать начала следующего mov si,offset palettes+256*3 ; обратного хода луча call write_palette ; записать новую палитру pop cx loop main_loop ; цикл выполняется 64 раза - достаточно для ; обнуления самого яркого цвета (максимальное ; значение 6-битной компоненты - 63) mov si,offset palettes call write_palette ; восстановить первоначальную палитру ret ; конец программы ; процедура read_palette ; помещает палитру VGA в строку по адресу ES:DI read_palette proc near mov dx,03C7h ; порт 03C7h - индекс DAC/режим чтения mov al,0 ; начинать с нулевого цвета out dx,al mov dl,0C9h ; порт 03C9h - данные DAC mov cx,256*3 ; прочитать 256 * 3 байта rep insb ; в строку по адресу ES:DI ret read_palette endp ; процедура write_palette ; загружает в DAC VGA палитру из строки по адресу DS:SI write_palette proc near mov dx,03C8h ; порт 03C8h - индекс DAC/режим записи mov al,0 ; начинать с нулевого цвета out dx,al mov dl,0C9h ; порт 03C9h - данные DAC mov cx,256*3 ; записать 256 * 3 байта rep outsb ; из строки в DS:SI ret write_palette endp ; процедура dec_palette ; уменьшает значение каждого байта на 1 с насыщением (то есть, после того как ; байт становится равен нулю, он больше не уменьшается из строки в DS:SI ; и записывает результат в строку в DS:SI dec_palette proc near mov cx,256*3 ; длина строки 256 * 3 байта dec_loop: lodsb ; прочитать байт, test al,al ; если он ноль, jz already_zero ; пропустить следующую команду dec ax ; уменьшить байт на 1 already_zero: stosb ; записать его обратно loop dec_loop ; повторить 256 * 3 раза ret dec_palette endp ; процедура wait_retrace ; ожидание начала следующего обратного хода луча wait_retrace proc near push dx mov dx,03DAh VRTL1: in al,dx ; порт 03DAh - регистр ISR1 test al,8 jnz VRTL1 ; подождать конца текущего обратного хода луча, VRTL2: in al,dx test al,8 jz VRTL2 ; а теперь начала следующего pop dx ret wait_retrace endp palettes: ; за концом программы мы храним две копии ; палитры - всего 1,5 Кб end start