6.4.3. Вывод на экран через линейный кадровый буфер

; lfbfire.asm

Программа, работающая с SVGA при помощи линейного кадрового буфера Демонстрирует стандартный алгоритм генерации пламени.

Требуется поддержка LFB видеоплатой   (или загруженный эмулятор univbe), для компиляции необходим DOS-расширитель. . • .

start:

. 486р

flat

. code

assume

short _main

clb "WATCOM"

Для команды хаСС. Основная модель памяти в  защищенном режиме.

Нужно только для МАБМ.

Нужно только для DOS/4GW.

Начало программы.

На входе обычно 08, Р8 и 88 указывают на один и тот же сегмент с лимитом 4 Гб, Е8 указывает на сегмент с Р8Р на ЮОп байт,   остальные регистры не определены.сінин

_main:

st і eld

mov mov int

jc

mov

ax,0100h bx,100h

3lh

DPMI_error fs, dx

Даже флаг прерываний не определен, не говоря уже о флаге направления.

Функция DPMI OlOOh.

Размер в 16-байтных параграфах.

Выделить блок памяти ниже 1 Мб.

Получитв блок общей информации о VBE 2.0

FS ■ (см.

селектор для раздел 4.5.2).

выделенного блока.

mov

dword ptrfs:[0],'2EBV

; Сигнатура VBE2 в начало блока.

mov

word ptr v86 eax,4F00h

; Функция VBE OOh.

,mov

word ptr v86_es,ax

Сегмент, выделенный DPMI.

mov

ax,0300h

; Функция DPMI 300h.

mov

bx,0010h

; Эмуляция прерывания 10h.

xor

ecx,ecx

 

mov

edi,offset v86 regs

 

push

ds

 

pop

es

; ES:E0I - структура v86 regs.

int

31h

; Получить общую информацию VBE2.

jc

DPMI error

 

cmp

byte ptr

; Проверка поддержки VBE.

jne

VBE_error

 

movzx

ebp.word ptr fs: [18]

; Объем SVGA-памяти в ЕВР

shl

ebp,6

; в килобайтах.

Получить информацию о видеорежиме

mov mov

mov mov хог

int

jc

cmp

jne

test

word ptr v86_eax, Номер функции INT lOh.

word ptr

ax,0300h bx,0010h ecx,ecx 31h

DPMI_error

byte ptr v86_eax,4Fh

VBE_error

byte ptr fs:[O],8Oh

LFB_error

; Режим lOlh - 640x480x256. ' ; ES.-EDI - те же самые.

; Функция DPMI 3OOh - эмуляция прерывания INT lOh.

Получить данные о режиме.

Бит 7 байта атрибутов

LFB есть.

Построение

дескриптора

сегмента, описывающего LFB.

Лимит.

 

 

 

mov

eax.ebp

 

;  Видеопамять в килобайтах.

shl

еах, lO

 

; Теперь в байтах.

dec

еах

 

; Лимит = размер - 1.

shr

eax,12

 

; Лимит в 4-килобайтных единицах.

mov

word ptr

videodsc+O,ax

;  Записать биты 15-0 лимита ■

shr

еах , 8

 

 

and

ah.OFh

 

 

l

; Права.

or byte ptr videodsc+6,ah ;

;

shl eax.10 ;

mov edx.dword ptr fs: [40] ;

mov cx.dx.

shld ebx,edx,l6

mov di.ax

shld esi,eax,l6

mov ax,800h ;

int 31 h ;

jc DPMI_error

shrd edx,ebx,l6 ;

mov dx.cx ;

mov word ptr videodsc+2,dx ;

shr edx,16

mov byte ptr videodsc+4,dl

mov byte ptr videodsc+7,dh ;

mov bx.cs

lar cx, bx

and cx,6000h

or byte ptr videodsc+5,ch

Программирование в PM

и биты 19-16 лимита.

Видеопамять в килобайтах. В байтах. ; Физический адрес LFB.

;  Поместить его в CX:DX,

а размер - в SI:DI и вызвать функцию DPMI O80Oh:.

Отобразить физический адрес в линейный. Перенести полученный линейный адрес

из BS:CX в EDX

и записать биты 15-0 базы,

биты 23-16 и биты 31-24.

Прочитатв права нашего сегмента и перенести биты ОРІ в строящийся дескриптор.

Поместитв наш дескриптор в■LDT и получитв селектор.

mov

сх, 1

Получить один новый дескриптор

mov

ах, 0

у DPMI.

int

31bi

 

jc

DPMI error

 

mov

word ptr

Записать его селектор.

push

ds

 

pop

es

 

mov

edi,offset videodsc

ES:EDI - адрес нашего дескриптора.

mov

bx, ax

BX - выданный нам селектор.

mov

ax.OCh

Функция DPMI ОСЬ:.

int

3lh

Загрузитв дескриптор- в LDT.

jc

DPMI_error

Теперь в videosel лежит селектор на

LFB.

; Переключение Е

mov • mov

mov mov mov xor

int

mov

enter_flame:

mov

4F02h - установитв SVGA-режим. Режим 4101 h = 101rt + LFB. ES:EDI - структура v86_regs. Функция DPMI 300h. Эмуляция прерывания 10h.

режим 4l0lh (lOlh + LFB). word ptr v86_eax,4F02h word ptr v86_ebx,4101h edi,offset v86_regs ax,030Oh bx,0O1Oh ecx,ecx 3lh

ax,word ptr videosel       ; AX - наш селектор.

; Сюда придет управление с селектором в АХ на AOOOh:ОООО,

; если произошла ошибка в любой VBE-функции.

;  ES - селектор видеопамяти или LFB.

Отсюда начинается процедура генерации пламени.

і Генерация палитры для пламени.

хог edi,edi

хог есх.есх palette_gen:

хог еах.еах

mov cl,63 palette_H:

stosb

inc eax

cmpsw

loop palette_H

push

mov

palette_12:

stosw

inc loop

pop

inc

jns

edi

cl,192

di

palette_12

edi di

palette_gen

Начать написание палитру с адреса ES:0000,

Цвета начинаются с 0,  0, 0.

Число значений для одного компонента.

Записать байт, увеличить компонент, пропустить два байта, и так 64 раза.

Записать два байта и' пропустить один, и так 192 раза (до Восстановить EDI.

конца палитры).

Палитра сгенерирована,  записатв ее в регистры VGA DAC (см. раздел 5.10.4).

mov

al.O

; Начать с регистра 0.

mov

dx,03C8h

;   Индексный регистр на запись.

out

dx, al

 

push

es

 

push

ds

;   Поменять местами ES и DS.

pop

es

 

pop

ds

 

xor

esi,esi

 

mov

ecx,256«3

;  Писать все 256 регистров

mov.

edx,03C9h

;   в порт данных VGA DAC.

rep

outsb

 

push

es.

 

push

ds

;   Поменять местами ES и DS.

pop

es

 

pop

ds

 

і Основной цикл xor mov mov

main_loop:

push push

pop

- анимация пламени,  пока не

edx.edx і

ebp,7777h і

ecx.dword ptr scr_width і

es ;

ds

es і   ES = DS

і   ES =

Анимация пламени (классический алгоритм)

inc ecx

будет нажата любая клавиша. Должен быть равен нулю. Любое число. Ширина экрана.

Сохранить К.

- мы работаем только с буфером.

Программирование

fflov mov

shr dec

mov

animate_flame:

mov

add setc

mov

add

mov add shr

jz

dec

already_zero:

stosb add

shr

mov

loop

mov

add dec

jnz

edi,offset buffer ebx.dword ptr scr_height

ebx,1 ebx

esi,scr_width

ax, [edi+esi*2-1] al, ah

ah

dl,[edi+esi*2+1]

ax,

dl, [edi+esi*4]

ax, dx

ax, 2

already_zero ax

eax.edx

eax, 1

byte ptr [edi+esi-1],al animate_flame ecx.esi edi,ecx

ebx

animate_flame

Вычислить среднее значение цвета в данной точке (EDI) из значений цвета в точке слева и на две строки вниз,  справа    на две строки вниз

и на четыре строки вниз, причем первое значение

модифицировать. Уменьшить яркость цвета.

Записать       цвет в буфер.

; Псевдослучайная полоска в низу экрана, которая служит генератором пламени. депегаЬог_Ьаг:

xadd

bp, ax

 

stosw

 

 

stosw

 

 

loop

generator_bar

 

pop

es

; Восстановитв     для вывода на'экран.

 

 

; Вывод пламени на экран.

xor

edi,edi

;  ЕЗ:ЕШ - ЬРБ.

push

esi

 

add

esi,offset buffer

; ОЗіЕБІ - буфер.

mov

ptr scr_size

; Размер буфера в двойных словах.

rep

movsd

; Скопироватв буфер на экран.

pop

esi

 

mov

ah, 1

; Если не была нажата

int

16h

; никакая клавиша,

jz ■

main_loop

; продолжитв основной цикл.

mov

ah,0

; Иначе -

int

I6h

; считатв эту клавишу.

all:

mov

ax,03h

Восстановить текстовый режим.

int 10h

mov ax,4C00h ; АН = 4Ch

int 21h : Выход из программы под расширителем DOS.

; Различные обработчики ошибок.

DPMI_error: ; Ошибка при выполнении одной из функций DPMI,

mov edx,offset DPMI_error_msg

mov ah,9

int 21h ; Вывести сообщение об ошибке

jmp short exit.all ; и выйти.

VBE_error: ; Не поддерживается VBE.

mov edx,offset VBE_error_msg

mov ah,9

int 21h ; Вывести сообщение об ошибке

jmp short start_with_vga       ; и исполвзоватв VGA. LFB_error: ; He поддерживается LFB.

mov edx, offset LFB_error_msg

mov ah,9 ; Вывести сообщение об ошибке,

int 21h start_with_vga:

mov ah,0 ; Подождать нажатия любой клавиши,

int 16h

mov ax,13h ; Переключитвся в видеорежим 13h,

int lOh ; 320x200x256.

mov ax, 2 ; Функция DPMI 0002h:

mov bx.OAOOOh ; построитв дескриптор для реалвного

int .     31h ; сегмента.

mov dword ptr scr_width,320   ; Установитв параметры режима

mov dword ptr scr_height,200

mov dword ptr scr_size, 320*200/4

jmp enterjlame ; и перейти к пламени.

.data

; Различные сообщения об ошибках.

VBE_error_msg      db "Ошибка VBE 2.0",ODh,OAh

db "Будет исполвзоватвся режим VGA 320x200",ODh,OAh,'$'

DPMI_error_msg    db "Ошибка DPMI$"

LFB_error_msg      db "LFB недоступен",ODh,OAh

db "Будет исполвзоватвся режим VGA 320x200", ODh, OAh,'$' ; Параметры видеорежима.

scr_width          dd 640

scr height          dd 480

scr_size            dd 640*480/4 ; Структура, исполвзуемая функцией DPMI ОЗОСп. v86_regs label byte

v86_edi'            dd 0

v86_esi             dd 0

v86_ebp             dd 0.

v86_res             dd 0

v86_ebx             dd 0

Программирование в F*M

dd 0

v86_ecx dd      • 0

v86_eax dd 0

v86_flags dw 0

v86_es dw 0

v86_ds dw 0

v86_fs dw 0

v86_gs dw 0

v86_ip dw 0

v86_cs dw 0

v86_sp ' dw 0

v86_SS dw 0

; Дескриптор сегмента, соответствующего

videodsc dw 0

dw 0

db 0

db 10010010b

db 10000000b

db о

; Селектор сегмента, описывающего LFB. videosel dw 0 .data?

;  Буфер для экрана.

buffer                db 640*483 dup(?)

LFB.

; Биты  15-0 лимита.

; Биты  15-0 базы. ; Биты  16-23 базы.

;   Доступ.

Биты 16-19 лимита и другие биты.

Биты 24-31 базы.

; стек

.stack 1000h

end _start

Программирование с DOS-расширителями - один из лучших выходов для приложений, которые должны работать в любых условиях, включая старые вер­сии DOS, и в то же время требуют 32-битный режим. Еще совсем недавно боль­шинство компьютерных игр, в частности знаменитые Doom и Quake, выпускались именно как программы, использующие расширители DOS. На сегодняшний день в связи с повсеместным распространением операционных систем для PC, работа­ющих в 32-битном защищенном режиме, требование работы в любых версиях

DOS становится менее актуальным и все больше программ выходят только в вер­сиях для Windows 95 или NT, чему и посвящена следующая глава.