Assembler - язык неограниченных возможностей

         

Работа с SVGA-режимами


В режиме VGA 320x200 с 256 цветами для отображения видеопамяти на основное адресное пространство используется 64 000 байт, располагающихся с адреса A000h:0000h. Дальнейшее увеличение разрешения или числа цветов приводит к тому, что объем видеопамяти превышает максимальные границы сегмента в реальном режиме (65 535 байт), а затем и размер участка адресного пространства, отводимого для видеопамяти (160 Кб, от A000h:0000h до B800h:FFFFh. С адреса C800h:0000h начинается область ROM BIOS). Чтобы вывести изображение, используются два механизма — переключение банков видеопамяти для реального режима и LFB (линейный кадровый буфер) для защищенного.

Во втором случае вся видеопамять отображается на непрерывный кусок адресного пространства, но начинающегося не с адреса 0A0000h, а с какого-нибудь другого адреса, так чтобы весь образ видеопамяти, который может занимать несколько мегабайтов, отобразился в один непрерывный массив. В защищенном режиме максимальный размер сегмента составляет 4 гигабайта, поэтому никаких сложностей с адресацией этого буфера не возникает. Буфер LFB можно использовать, только если видеоадаптер поддерживает спецификацию VBE 2.0 (см. пример в главе 6.4).

В реальном режиме вывод на экран осуществляется по-прежнему копированием данных в 64-килобайтный сегмент, обычно начинающийся с адреса A000h:0000h, но эта область памяти соответствует только части экрана. Чтобы вывести изображение в другую часть экрана, требуется вызвать функцию перемещения окна (или, что то же самое, переключения банка видеопамяти), изменяющую область видеопамяти, которой соответствует сегмент A000h. Например, в режиме 640x480 с 256 цветами требуется 307 200 байт для хранения всего видеоизображения. Заполнение сегмента A000h:0000h – A000h:FFFFh приводит к закраске приблизительно 1/5 экрана, перемещение окна А на позицию 1 (или переключение на банк 1) и повторное заполнение этой же области приводит к закраске следующей 1/5 экрана, и т.д. Перемещение окна осуществляется подфункцией 05 видеофункции 4Fh или передачей управления прямо на процедуру, адрес которой можно получить, вызвав подфункцию 01, как будет показано ниже. Некоторые видеорежимы позволяют использовать сразу два таких 64-килобайтных окна, окно А и окно В, так что можно записать 128 Кб данных, не вызывая прерывания.


Стандартные графические режимы SVGA могут быть 4-, 8-, 15-, 16-, 24- и 32-битными.

4-битные режимы (16 цветов):

VGA

012h: 640x480 (64 Кб)

VESA VBE 1.0

102h: 800x600 (256 Кб)

104h: 1024x768 (192 Кб)

106h: 1280x1024 (768 Кб)

Каждый пиксель описывается одним битом, для вывода цветного изображения требуется программирование видеоадаптера на уровне портов ввода-вывода (глава 5.10.4).



8-битные режимы (256 цветов):

VGA

013h: 320x200 (64 Кб)

VBE 1.0

100h: 640x400 (256 Кб)

101h: 640x480 (320 Кб)

103h: 800x600 (512 Кб)

105h: 1024x768 (768 Кб)

107h: 1280x1024 (1,3 Мб)

VBE 2.0

120h: 1600x1200 (1,9 Мб)

Каждый пиксель описывается ровно одним байтом. Значение байта — нoмер цвета из палитры, значения цветов которой можно изменять, например вызывая подфункцию 09 видеофункции 4Fh.

15-битные режимы (32 К цветов):

VBE 1.2

10Dh: 320x200 (128 Кб)

110h: 640x480 (768 Кб)

113h: 800x600 (1 Мб)

116h: 1024x768 (1,5 Мб)

119h: 1280x1024 (2,5 Мб)

VBE 2.0

121h: 1600x1200 (3,8 Мб)

Каждый пиксель описывается ровно одним словом (16 бит), в котором биты 0 – 4 содержат значение синей компоненты цвета, биты 5 – 9 — зеленой, а биты 10 – 14 — красной. Бит 15 не используется.

16-битные режимы (64 К цветов):

VBE 1.2

10Eh: 320x200 (128 Кб)

111h: 640x480 (768 Кб)

114h: 800x600 (1 Мб)

117h: 1024x768 (1,5 Мб)

11Ah: 1280x1024 (2,5 Мб)

VBE 2.0

121h: 1600x1200 (3,8 Мб)

Так же как и в 15-битных режимах, каждый пиксель описывается ровно одним словом. Обычно биты 0 – 4 (5 бит) содержат значение синей компоненты, биты 5 – 10 (6 бит) — зеленой, а биты 11 – 15 (5 бит) — красной. В нестандартных режимах число бит, отводимое для каждого цвета, может отличаться, так что при их использовании следует вызвать подфункцию 01 видеофункции 4Fh и получить информацию о видеорежиме, включающую битовые маски и битовые смещения для цветов.



24-битные и 32-битные режимы (16 М цветов):

VBE 1.2

10Fh: 320x200 (192 Кб)

112h: 640x480 (1 Мб)

115h: 800x600 (1,4 Мб)

118h: 1024x768 (2,3 Мб)

11Bh: 1280x1024 (3,7 Мб)

VBE 2.0

122h: 1600x1200 (7,7 Мб)

В режимах с 24-битным и 32- битным цветом каждому пикселю на экране соответствуют три байта и одно двойное слово (4 байта). Если видеорежим использует модель памяти 6 (Direct Color), то младший байт (байт 0) содержит значение синего цвета, байт 1 содержит значение зеленого, байт 2 — значение красного, а байт 3 — в 32-битных режимах резервный и используется либо для выравнивания, либо содержит значение для альфа-канала. Некоторые видеорежимы могут использовать не Direct Color, a YUV (модель памяти 7) — здесь младший байт соответствует насыщенности красного, байт 1 — насыщенности синего, а байт 2 — яркости.

Видеоадаптер может поддерживать и собственные нестандартные видеорежимы. Список их номеров можно получить, вызвав подфункцию 00h, а получить информацию о режиме по его номеру — вызвав подфункцию 01h видеофуикции 4Fh. Более того, для стандартных режимов также следует вызывать подфункцию 01h, чтобы проверить реальную доступность режима (например, режим может быть в списке, но не поддерживаться из-за нехватки памяти). VBE 2.0 разрешает видеоадаптерам не поддерживать никаких стандартных режимов вообще.

INT 10h АН = 4Fh, AL = 00 — Получить общую SVGA-информацию

Ввод: AX = 4F00h
ES:DI = адрес буфера (512 байт)
Вывод: AL = 4Fh, если функция поддерживается
АН = 01, если произошла ошибка
АН = 00, если данные получены и записаны в буфер
Буфер для общей SVGA-информации:

+00h: 4 байта — будет содержать «VESA» после вызова прерывания, чтобы получить поля, начиная с 14h, здесь надо предварительно записать строку «VBE2»
+04h: слово — номер версии VBE в двоично-десятичном формате (0102h — для 1.2, 0200h — для 2.0)
+06h: 4 байта — адрес строки-идентификатора производителя
+0Ah: 4 байта — флаги:


    бит 0 — АЦП поддерживает 8-битные цветовые компоненты (см. подфункцию 08h)

    бит 1 — видеоадаптер несовместим с VGA

    бит 2 — АЦП можно программировать только при обратном ходе луча

    бит 3 — поддерживается спецификация аппаратного ускорения графики VBE/AF 1.0

    бит 4 — требуется вызов EnableDirectAccess перед использованием LFB

    бит 5 — поддерживается аппаратный указатель мыши

    бит 6 — поддерживается аппаратное отсечение

    бит 7 — поддерживается аппаратное копирование блоков

    биты 8 – 31 зарезервированы
+0Eh: 4 байта — адрес списка номеров поддерживаемых видеорежимов (массив слов, последнее слово = FFFFh, после которого обычно следует список нестандартных режимов, также заканчивающийся словом FFFFh)
+12h: слово — объем видеопамяти в 64-килобайтных блоках
+14h: слово — внутренняя версия данной реализации VBE
+16h: 4 байта — адрес строки с названием производителя
+1Ah: 4 байта — адрес строки с названием видеоадаптера
+1Eh: 4 байта — адрес строки с версией видеоадаптера
+22h: слово — версия VBE/AF (BCD, то есть 0100h для 1.0)
+24h: 4 байта — адрес списка номеров режимов, поддерживающих аппаратное ускорение (если бит поддержки VBE/AF установлен в 1)
+28h: 216 байт — зарезервировано VESA
+100h: 256 байт — зарезервировано для внутренних данных VBE. Так, например, в эту область копируются строки с названиями производителя, видеоадаптера, версии и т.д.
<


/p> INT 10h АН = 4Fh, AL = 01 — Получить информацию о режиме

Ввод: AX = 4F01h
СХ = номер SVGA-режима ( бит 14 соответствует использованию LFB, бит 13 — аппаратному ускорению)
ES:DI = адрес буфера для информации о режиме (256 байт)
Вывод: AL = 4Fh, если функция поддерживается
АН = 01, если произошла ошибка
АН = 00, если данные получены и записаны в буфер
Буфер для информации о SVGA-режиме:

+00h:

слово — атрибуты режима:


    бит 0 — режим присутствует

    бит 1 — дополнительная информация (смещения 12h – 1Eh) присутствует (для VBE 2.0 эта информация обязательна и бит всегда установлен)

    бит 2 — поддерживается вывод текста на экран средствами BIOS

    бит 3 — режим цветной

    бит 4 — режим графический

    бит 5 — режим несовместим с VGA

    бит 6 — переключение банков не поддерживается

    бит 7 — LFB не поддерживается

    бит 8 — не определен

    бит 9 — (для VBE/AF) приложения должны вызвать EnableDirectAccess, прежде чем переключать банки
+02h: байт — атрибуты окна А:


    бит 1 — окно существует

    бит 2 — чтение из окна разрешено

    бит 3 — запись в окно разрешена
+03h: байт — атрибуты окна В
+04h: слово — гранулярность окна — число килобайтов, которому всегда кратен адрес начала окна в видеопамяти (обычно 64)
+06h: слово — размер окна в килобайтах (обычно 64)
+08h: слово — сегментный адрес окна А (обычно A000h)
+0Ah: слово — сегментный адрес окна В
+0Ch: 4 байта — адрес процедуры перемещения окна (аналог подфункции 05h, но выполняется быстрее)
+10h: слово — число целых байт в логической строке
+12h: слово — ширина в пикселях (для графики) или символах (для текста)
+14h: слово — высота в пикселях (для графики) или символах (для текста)
+16h: байт — высота символов в пикселях
+17h: байт — ширина символов в пикселях
+18h: байт — число плоскостей памяти (4 — для 16-цветных режимов, 1 — для обычных, число переключений банков, требуемое для доступа ко всем битам (4 или 8), — для модели памяти 5)
+19h: байт — число бит на пиксель
+1Ah: байт — число банков для режимов, в которых строки группируются в банки (2 — для CGA, 4 — для HGC)
+1Bh: байт — модель памяти:

00h — текст

01h — CGA-графика

02h — HGC-графика

03h — EGA-графика (16 цветов)

04h — VGA-графика (256 цветов в одной плоскости)

05h — Режим X (256 цветов в разных плоскостях)

06h — RGB (15-битные и выше)

07h — YUV

08h – 0Fh — зарезервированы VESA

10h – FFh — нестандартные модели
+1Ch: байт — размер банка в килобайтах (8 — для CGA и HGC, 0 — для остальных)
+1Dh: байт — число видеостраниц
+1Eh: байт — зарезервирован
+1Fh: байт — битовая маска красной компоненты
+20h: байт — первый бит красной компоненты
+21h: байт — битовая маска зеленой компоненты
+22h: байт — первый бит зеленой компоненты
+23h: байт — битовая маска синей компоненты
+24h: байт — первый бит синей компоненты
+25h: байт — битовая маска зарезервированной компоненты
+26h: байт — первый бит зарезервированной компоненты
+27h: байт — дополнительные флаги:


    бит 0: —поддерживается перепрограммирование цветов (подфункция 09h)

    бит 1 — приложение может использовать биты в зарезервированной компоненте
+28h: 4 байта — физический адрес начала LFB
+2Ch: 4 байта — смещение от начала LFB, указывающее на первый байт после конца участка памяти, отображающейся на экране
+30h: слово — размер памяти в LFB, не отображающейся на экране, в килобайтах
+32h: 206 байт — зарезервировано
<


/p> INT 10h АН = 4FH, AL = 02 — Установить режим

Ввод: AX=4F02h
ВХ = номер режима:


    биты 0 – 6 — собственно номер режима

    бит 7 — видеопамять не очищается при установке режима, если все следующие биты — нули

    бит 8 — стандартный VBE SVGA-режим

    бит 9 — нестандартный SVGA-режим

    биты 10 – 12 — зарезервированы

    бит 13 — режим использует аппаратное ускорение

    бит 14 — режим использует LFB

    бит 15 — видеопамять не очищается при установке режима


Кроме того, специальный номер режима 81FFh соответствует доступу ко всей видеопамяти и может использоваться для сохранения ее содержимого.
Вывод: AL = 4Fh, если функция поддерживается
АН = 00, если режим установлен
АН = 01 или 02, если произошла ошибка
INT 10h АН = 4Fh, AL = 03 — Узнать номер текущего видеорежима

Ввод: АХ = 4F03h
Вывод: AL = 4Fh, если функция поддерживается
ВХ = номер режима
INT 10h АН = 4Fh AL = 05 — Перемещение окна (переключение банка видеопамяти)

Ввод: АХ = 4F03h
ВН = 00 — установить окно
ВН = 01 — считать окно
BL = 00 — окно А
BL = 01 — окно В
DX = адрес окна в видеопамяти в единицах гранулярности (номер банка), если ВН = 0
Вывод: AL = 4Fh, если функция поддерживается
DX = адрес окна в единицах гранулярности (номер банка), если ВН = 1
АН = 03, если функция была вызвана в режиме, использующем LFB
Всегда предпочтительнее переключать банки прямым дальним вызовом процедуры, адрес которой возвращается подфункцией 01h в блоке информации о видеорежиме. Все параметры передаются в процедуру точно так же, как и в подфункцию 05h, но содержимое регистров АХ и DX по возвращении не определено.

INT 10h АН = 4Fh AL = 07 — Установка начала изображения

Ввод: АХ = 4F07h
ВН = 00
BL = 00 — считать начало изображения
BL = 80h — установить начало изображения (в VBE 2.0 автоматически выполняется при следующем обратном ходе луча)
СХ = первый изображаемый пиксель в строке (для BL = 80h)
DX = первая изображаемая строка (для BL = 80h)
Вывод: AL = 4Fh, если функция поддерживается
АН = 01, если произошла ошибка
АН = 00, если функция выполнилась успешно
ВН = 00 (для BL = 00)
СХ = первый изображаемый пиксель в строке (для BL = 00)
DX = первая изображаемая строка (для BL = 00)
<


/p> С помощью этой функции можно выполнять как плавный сдвиг экрана, перемещая начало изображения на одну строку за один раз, так и быстро отображать на экране два разных изображения, изменяя одно, пока на экране показано другое, так что создается эффект плавной анимации.

: scrolls.asm ; Изображает в разрешении 1024x768x64К окрашенный конус, который можно ; плавно перемещать по экрану стрелками вверх и вниз. ; .model tiny .code .386 ; используется команда shrd org 100h ; СОМ-файл start: mov ax,4F01h ; получить информацию о видеорежиме mov cx,116h ; 1024x768x64К mov di,offset vbe_mode_buffer int 10h ; здесь для простоты опущена проверка наличия режима mov ax,4F02h ; установить режим mov bx,116h int 10h push word ptr [vbe_mode_buffer+8] pop es ; поместить в ES адрес начала видеопамяти ; (обычно A000h) cld

; вывод конуса на экран

mov cx,-1 ; начальное значение цвета (белый) mov si,100 ; начальный радиус mov bx,300 ; номер столбца mov ax,200 ; номер строки main_loop: inc si ; увеличить радиус круга на 1 inc ax ; увеличить номер строки inc bx ; увеличить номер столбца call fast_circle ; нарисовать круг sub cx,0000100000100001b ; изменить цвет cmp si,350 ; если еще не нарисовано 250 кругов, jb main_loop ; продолжить, xor сх,сх ; иначе: выбрать черный цвет, call fast_circle ; нарисовать последний круг

; плавное перемещение изображения по экрану с помощью функции 4F07

xor bx,bx ; ВХ = 0 - установить начало экрана xor dx,dx ; номер строки = 0 ; номер столбца в СХ уже ноль main_loop_2: mov ax,4F07h int 10h ; переместить начало экрана mov ah,7 ; считать нажатую клавишу с ожиданием, без эха int 21h ; и без проверки на Ctrl-Break, test al,al ; если это обычная клавиша - jnz exit_loop_2 ; завершить программу, int 21h ; иначе: получить расширенный ASCII-код, cmp al,50h ; если это стрелка вниз je key_down cmp al,48h ; или вверх - вызвать обработчик, je key_up exit_loop_2: ; иначе - завершить программу mov ах,З ; текстовый режим int 10h ret ; завершить СОМ-программу

key_down: ; обработчик нажатия стрелки вниз dec dx ; уменьшить номер строки начала экрана, jns main_loop_2 ; если знак не изменился - продолжить цикл, ; иначе (если номер был 0, а стал -1) - ; увеличить номер строки key_up: ; обработчик нажатия стрелки вверх inc dx ; увеличить номер строки начала экрана jmp short main_loop_2



; Процедура вывода точки на экран в 16-битном видеорежиме ; Ввод: DX = номер строки, ВХ = номер столбца, ES = А000, СХ = цвет ; модифицирует АХ

putpixel16b: push dx push di хоr di,di shrd di,dx,6 ; DI = номер строки * 1024 mod 65 536 shr dx,5 ; DX = номер строки / 1024 * 2 inc dx cmp dx,current_bank ; если номер банка для выводимой точки jne bank_switch ; отличается от текущего - переключить банки switched: add di,bx ; добавить к DI номер столбца mov ax,cx ; цвет в АХ shl di,1 ; DI = DI * 2, так как адресация идет в словах stosw ; вывести точку на экран pop di ; восстановить регистры pop dx ret bank_switch: ; переключение банка push bx xor bx,bx ; BX = 0 -> Установить начало экрана mov current_bank,dx ; сохранить новый номер текущего банка call dword ptr [vbe_mode_buffer+0Ch] ; переключить ; банк pop bx jmp short switched

; Алгоритм рисования круга, используя только сложение, вычитание и сдвиги ; (упрощенный алгоритм промежуточной точки). ; Ввод: SI = радиус, СХ = цвет, АХ = номер столбца центра круга, ; ВХ = номер строки центра круга модифицирует DI, DX

fast_circle: push si push ax push bx xor di,di ; DI - относительная Х-координата текущей точки dec di ; (SI - относительная Y-координата, начальное mov ax,1 ; значение - радиус) sub ax,si ; AX - наклон (начальное значение 1-Радиус) circle_loop: inc di ; следующий X (начальное значение - 0) cmp di,si ; цикл продолжается, пока X < Y ja exit_main_loop

pop bx ; BX = номер строки центра круга pop dx ; DX = номер столбца центра круга push dx push bx

push ax ; сохранить АХ (putpixel16b его изменяет) add bx,di ; вывод восьми точек на окружности: add dx,si call putpixel16b ; центр_Х + X, центр_Y + Y sub dx,si sub dx,si call putpixel16b ; центр_X + X, центр_Y - Y sub bx,di sub bx,di call putpixel16b ; центр_Х - X, центр_Y - Y add dx,si add dx,si call putpixel16b ; центр_Х - X, центр_Y + Y sub dx,si add dx,di add bx,di add bx,si call putpixel16b ; центр_Х + Y, центр_Y + X sub dx,di sub dx,di call putpixel16b ; центр_Х + Y, центр_Y - X sub bx,si sub bx,si call putpixel16b ; центр_Х - Y, центр_Y - X add dx,di add dx,di call putpixel16b ; центр_Х - Y, центр_Y + X pop ax



test ax,ax ; если наклон положительный js slop_negative mov dx,di sub dx,si shl dx,1 inc dx add ax,dx ; наклон = наклон + 2(Х - Y) + 1 dec si ; Y = Y - 1 jmp circle_loop slop_negative: ; если наклон отрицательный mov dx,di shl dx,1 inc dx add ax,dx ; наклон = наклон + 2X + 1 jmp circle_loop ; и Y не изменяется exit_main_loop: pop bx pop ax pop si ret

current_bank dw 0 ; номер текущего банка vbe_mode_buffer: ; начало буфера данных о видеорежиме end start

В этом примере для наглядности отсутствуют необходимые проверки на поддержку VBE (все прерывания должны возвращать 4Fh в AL), на поддержку видеорежима (атрибут видеорежима в первом байте буфера, заполняемого подфункцией 02) или на объем видеопамяти (должно быть как минимум 2 Мб) и на другие ошибки (все прерывания должны возвращать 0 в АН).

Для вывода точки на экран используется выражение типа

номер_банка = номер_строки * байт_в_строке / байт_в_банке смещение = номер_строки * байт_в_строке MOD байт_в_банке

Но так как и число байт в строке, и число байт в банке являются степенями двойки, умножение, деление и вычисление остатка от деления можно заменить более быстрыми операциями сдвига, как это сделано в процедуре putpixel16b.

Переключение банков всегда отнимает значительное время, так что по возможности программированием для SVGA-режимов лучше всего заниматься в 32-битном режиме с линейным кадровым буфером, например используя DOS-расширители, как показано в главе 6.4.


Содержание раздела