Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методички по ассемблеру / Методичка_часть2.doc
Скачиваний:
601
Добавлен:
02.05.2014
Размер:
545.28 Кб
Скачать

4.5. Графические видеорежимы

4.5.1. Работа с vga-режимами

Функция 00 прерывания BIOS 10h позволяет переключаться не только в текстовые режимы, использовавшиеся в предыдущих главах, но и в некоторые графические. Эти видеорежимы стандартны и поддерживаются всеми видеоадаптерами (начиная с VGA), см. табл. 19.

Таблица 19. Основные графические режимы VGA

Номер режима

Разрешение

Число цветов

11h

640x480

2

12h

640x480

16

13h

320x200

256

Существуют еще несколько видеорежимов, использовавшихся более старыми видеоадаптерами CGA и EGA (с номерами от 4 до 10h); их список приведен в приложении 2.

BIOS также предоставляет видеофункции чтения и записи точки на экране в графических режимах, но эти функции настолько медленно исполняются, что никогда не используются в реальных программах.

INТ 10h АН = 0Ch— Вывести точку на экран

Ввод:

АН = 0Ch ВН = номер видеостраницы (игнорируется для режима 13h, поддерживающего только одну страницу) DX = номер строки СХ = номер столбца AL = номер цвета (для режимов 10h и llh, если старший бит 1, номер цвета точки на экране будет результатом операции «исключающее ИЛИ»)

Вывод:

Никакого

INТ 10h AH = 0Dh— Считать точку с экрана

Ввод:

АН = 0Dh ВН = номер видеостраницы (игнорируется для режима 13h, поддерживающего только одну страницу) DX = номер строки СХ = номер столбца

Вывод:

AL = номер цвета

Попробуем тем не менее воспользоваться средствами BIOS для вывода на экран. Следующая программа переводит экран в графический режим 13h (320x200), заселяет его точками случайным образом, после чего эти точки эволюционируют согласно законам алгоритма «Жизнь»: если у точки меньше двух или больше трех соседей, она погибает, а если у пустой позиции есть три соседа, в ней появляется новая точка. Мы будем использовать очень простой, но неоптимальный способ реализации этого алгоритма: сначала для каждой точки вычисляется число соседей, затем каждая точка преобразуется в соответствии с полученным числом соседей, и затем каждая точка выводится на экран.

; lifebios.asm

; Игра "Жизнь" на поле 320x200, использующая вывод на экран средствами BIOS

.model small

.stack 100h ; явное задание стека - для ЕХЕ-программ

.code

.186 ; для команд shl al,4 и shr al,4

start:

push FAR_BSS ; сегментный адрес буфера в DS

pop ds

; заполнение массива ячеек псевдослучайными значениями

xor ах,ах

int 1Ah ; Функция АН = 0 INT 1Ah: получить текущее

; время DX теперь содержит число секунд,

; прошедших с момента включения компьютера,

; которое используется как начальное значение

; генератора случайных чисел

mov di,320*200+1 ; максимальный номер ячейки

fill_buffer:

imul dx,4E35h ; простой генератор случайных чисел

inc dx ; из двух команд

mov ax,dx ; текущее случайное число копируется в АХ,

shr ax,15 ; от него оставляется только один бит,

mov byte ptr [di],al ; и в массив копируется 00, если ячейка

; пуста, и 01, если заселена

dec di ; следующая ячейка

jnz fill_buffer ; продолжить цикл, если DI не стал равен нулю

mov ах,0013h ; графический режим 320x200, 256 цветов

int 10h

; основной цикл

new_cycle:

; Шаг 1: для каждой ячейки вычисляется число соседей

; и записывается в старшие 4 бита этой ячейки

mov di,320*200+1 ; максимальный номер ячейки

step_1:

mov al,byte ptr [di+1] ; в AL вычисляется сумма

add al,byte ptr [di-1] ; значений восьми соседних ячеек,

add al,byte ptr [di+319] ; при этом в младших четырех

add al,byte ptr [di-319] ; битах накапливается число

add al,byte ptr [di+320] ; соседей

add al,byte ptr [di-320]

add al,byte ptr [di+321]

add al,byte ptr [di-321]

shl al,4 ; теперь старшие четыре бита AL - число

; соседей текущей ячейки

or byte ptr [di],al ; поместить их в старшие четыре бита

; текущей ячейки

dec di ; следующая ячейка

jnz step_1 ; продолжить цикл, если DI не стал равен нулю

; Шаг 2: изменение состояния ячеек в соответствии с полученными в шаге 1

; значениями числа соседей

mov di,320*200+1 ; максимальный номер ячейки

flip_cycle:

mov al,byte ptr [di] ; считать ячейку из массива

shr al,4 ; AL = число соседей

cmp al,3 ; если число соседей = 3,

je birth ; ячейка заселяется,

cmp al,2 ; если число соседей = 2,

je f_c_continue ; ячейка не изменяется,

mov byte ptr [di],0 ; иначе - ячейка погибает

jmp short f_c_continue

birth:

mov byte ptr [di],1

f_c_continue:

and byte ptr [di],0Fh ; обнулить число соседей в старших

; битах ячейки

dec di ; следующая ячейка

jnz flip_cycle

;

; Вывод массива на экран средствами BIOS

;

mov si,320*200+1 ; максимальный номер ячейки

mov сх,319 ; максимальный номер столбца

mov dx,199 ; максимальный номер строки

zdisplay:

mov al,byte ptr [si] ; цвет точки (00 - черный, 01 - синий)

mov ah,0Ch ; номер видеофункции в АН

int 10h ; вывести точку на экран

dec si ; следующая ячейка

dec cx ; следующий номер столбца

jns zdisplay ; если столбцы не закончились - продолжить,

mov сх,319 ; иначе: снова максимальный номер столбца в СХ

dec dx ; и следующий номер строки в DX,

jns zdisplay ; если и строки закончились - выход из цикла

mov ah,1 ; если не нажата клавиша

int 16h

jz new_cycle ; следующий шаг жизни

mov ах,0003h ; восстановить текстовый режим

int 10h

mov ax,4C00h ; и завершить программу

int 21h

.fardata? ; сегмент дальних неинициализированных данных

db 320*200+1 dup(?) ; содержит массив ячеек

end start

Этот пример оформлен как ЕХЕ-программа, так как используется массив, близкий по размерам к размеру сегмента, и если разместить его в одном сегменте с СОМ-программой, стек, растущий от самых старших адресов, может затереть область данных. Наш пример не использует стек, но это делает обработчик прерывания BIOS 10h.

Скорость работы этой программы — в среднем 200 тактов процессора Pentium на точку (измерения выполнены с помощью команды RDTSC, см. главу 10.2), то есть всего 16 поколений в секунду для Pentium-200 (200 миллионов тактов в секунду разделить на 200 тактов на точку и на 320x200 точек). Разумеется, используемый алгоритм крайне нерационален и кажется очевидным, что его оптимизация приведет к значительному выигрышу во времени. Но если измерить скорость выполнения каждого из трех циклов, то окажется, что первый цикл выполняется в среднем за 20,5 такта на точку, второй — за 13, а третий — за 170,5!

Исправить эту ситуацию весьма просто — достаточно отказаться от видеофункций BIOS для работы с графикой и перейти к прямому копированию в видеопамять.

В видеорежиме 13h каждый байт в области памяти, начинающейся с адреса A000h:0000h, соответствует одной точке на экране, а значение, которое может принимать этот байт (0 – 255), соответствует номеру цвета этой точки. (Цвета, которые соответствуют этим номерам, могут быть перепрограммированы с помощью видеофункции 10h BIOS.) В видеорежимах 11h и 12h каждый бит соответствует одной точке на экране, так что простым копированием в видеопамять можно получить только черно-белое изображение (для вывода цветного изображения в режиме 12h необходимо перепрограммировать видеоадаптер, об этом см. в главе 5.10.4).

В нашем примере для хранения информации о каждой ячейке также используется один байт, так что для вывода данных на экран в режиме 13h достаточно выполнить простое копирование. Переименуем программу LIFEBIOS.ASM в LIFEDIR.ASM, заменив цикл вывода на экран от команды

mov si,320*200+1

до команды

jns zdisplay

следующим фрагментом кода:

push 0A000h ; сегментный адрес видеопамяти

pop es ; в ES

mov cx,320*200 ; максимальный номер точки

mov di,cx ; в видеопамяти - 320 * 200

mov si,cx ; а в массиве -

inc si ; 320 * 200 + 1

rep movsb ; выполнить копирование в видеопамять

Теперь программа обрабатывает одну точку приблизительно за 61,5 такта процессора Pentium, что дает 51 поколение в секунду на Pentium-200. Кроме того, теперь эту программу можно переписать в виде СОМ-файла, так как и код, и массив, и стек точно умещаются в одном сегменте размером 64 Кб. Такая СОМ-программа (LIFECOM.ASM) займет 143 байта.

Оптимизация программы «Жизнь» — хорошее упражнение для программирования на ассемблере. В 1997 году проводился конкурс на самую короткую и на самую быструю программу, выполняющую в точности то же, что и наш пример, — заполнение экрана случайными точками, их эволюция и выход по нажатию любой клавиши. Самой короткой тогда оказалась программа размером в 72 байта, которая с тех пор была усовершенствована до 64 байт (ее скорость 52 такта на точку), а самая быстрая из 16-битных программ тратит на каждую точку в среднем всего 6 тактов процессора Pentium и имеет размер 689 байт. В ней состояния ячеек описываются отдельными битами массива, а для их обработки используются команды логических операций над целыми словами, так что одна команда обрабатывает сразу 16 точек. Использование 32-битных команд с тем же алгоритмом позволяет ускорить программу до 1,5 такта на точку.