Форум программистов, компьютерный форум, киберфорум
Наши страницы

Assembler, MASM, TASM

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 415, средняя оценка - 4.79
Mikl___
Заблокирован
Автор FAQ
25.06.2013, 13:20  [ТС] #16
Поиск файлов и вывод содержимого каталога на экран. DOS
Поиск файлов в DOS реализуется, как правило, через функции 4Eh и 4Fh прерывания 21h. Эти функции позволяют обнаруживать в заданном каталоге или на заданном устройстве файлы с именами, соответствующими заданному образцу.
В DOS существует возможность обращаться к группе файлов, задав так называемую маску имени. При задании маски используются обозначения: ? - любой символ, * - любая группа символов. Например:
  • *.* - все файлы
  • *.СОМ - все файлы с расширением .СОМ
  • ?С.ЕХЕ - под ? понимается любая ОДНА буква, таким образом BC.EXE и NC.EXE удовлетворяют условию, а TCC.EXE - нет.
Функции 4h и 4Fh помещают результат своей работы в область передачи данных (DTA), организованную следующим образом:
Байты Содержимое
0 - 14h Зарезервировано
15h Атрибут
16h - 17h Время и дата файла
1Ah - 1Dh Размер файла
1Eh - 2Ah Имя файла
По умолчанию область DTA размещается в программе по адресу DS:80h, то есть там же, где и параметры командной строки. Для того, чтобы программа могла одновременно пользоваться и командной строкой и функциями поиска, часто применяется функция 1Ah прерывания 21h, предназначенная для перемещения области DTA в другое место памяти.
Приведем формат вызова функций поиска:
Код
AH:=4Eh       или          4Fh
    CX:=Атрибут файла
    DS:DX:= Адрес строки маски имени
Атрибут файла - это байт, определяющий внутренние характеристики файла:
БитНазваниеПереводЗначение
0 Read only Только для чтения в этот файл нельзя писать и его нельзя удалить
1 HiddenСкрытыйфайл скрывается от показа, пока явно не указано обратно
2 System Системный содержание файла критично для работы операционной системы
3 Label Метка тома  
4 Directory Каталогфайл, содержащий записи о входящих в него файлах. Каталоги могут содержать записи о других каталогах, образуя древовидную структуру
5 Archive Архивныйфайл изменен после резервного копирования или не был скопирован программами резервного копирования
6 Не используется 
7 Не используется 
8SharedРазделяемый (Novell NetWare)возможность одновременной работы с файлом в локальной вычислительной сети нескольким пользователям одновременно
Если в байте атрибутов установить бит 4 в 1, то функции поиска будут искать каталоги (которые с точки зрения DOS также являются файлами). При установленном бите 1 функции поиска обнаружат скрытые файлы, например в корневом каталоге диска С: файлы MSDOS.SYS и IO.SYS. Для поиска обычных файлов достаточно просто заказать нулевое значение байта атрибутов.
Приведем пример программы, ищущей и отображающей на экране имена всех файлов с расширением .СОМ в текущем каталоге:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
; masm dos com #
.286
.model tiny
.code
org    80h
; Область передачи данных по умолчанию
dta    db     15h dup (0)
attrib db     0
timdat dw     0
       dw     0
fsize  dd     0
fname  db     0Eh dup (0)
       org    100h
; Начало программы
start: mov    ah,4Eh; Ищем 1-й файл
       xor    cx,cx
       mov    dx,offset maska
       int    21h
       jc     net
povtor:mov    si,offset fname
pechat:lodsb; Печатаем имя
       test   al,al
       je     poisk
       int    29h
       jmp    pechat
poisk: mov    al,0Dh; Переводим строку
       int    29h
       mov    al,0Ah
       int    29h
       mov    ah,4Fh; Ищем следующий файл
       int    21h
       jnc    povtor
net:   ret    ; Файлов больше нет, конец
maska  db     '*.com',0; Маска для поиска
end start

Поиск файлов и вывод содержимого каталога на экран.
Используем системную команду DIR
Вывод списка файлов и подкаталогов из указанного каталога.
Код
DIR [диск:][путь][имя_файла] [/A[[:]атрибуты]] [/B] [/C] [/D] [/L] [/N]
  [/O[[:]порядок]] [/P] [/Q] [/S] [/T[[:]время]] [/W] [/X] [/4]
где [диск:][путь][имя_файла] Диск, каталог и/или файлы, которые следует включить в список.
/A Вывод файлов с указанными атрибутами.
 атрибуты
D Каталоги
R Доступные только для чтения
H Скрытые файлы
A Файлы для архивирования
S Системные файлы
  Префикс "-" имеет значение НЕ
/B Вывод только имен файлов.
/C Применение разделителя групп разрядов для вывода размеров файлов (по умолчанию). Для отключения этого режима служит ключ /-C.
/D Вывод списка в несколько столбцов с сортировкой по столбцам.
/L Использование нижнего регистра для имен файлов.
/N Отображение имен файлов в крайнем правом столбце.
/O Сортировка списка отображаемых файлов.
 порядок
N По имени (алфавитная)
S По размеру (сперва меньшие)
E По расширению (алфавитная)
D По дате (сперва более старые)
G Начать список с каталогов
Префикс "-" обращает порядок
/P Пауза после заполнения каждого экрана.
/Q Вывод сведений о владельце файла.
/S Вывод списка файлов из указанного каталога и его подкаталогов.
/T Выбор поля времени для отображения и сортировки
 время
C Создание
A Последнее использование
W Последнее изменение
/W Вывод списка в несколько столбцов.
/X Отображение коротких имен для файлов, чьи имена не соответствуют стандарту 8.3. Формат аналогичен выводу с ключом /N, но короткие имена файлов выводятся слева от длинных. Если короткого имени у файла нет, вместо него выводятся пробелы.
/4Вывод номера года в четырехзначном формате
Стандартный набор ключей можно записать в переменную среды DIRCMD. Для отмены
их действия введите в команде те же ключи с префиксом "-", например: /-W.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
; masm dos com #
.286
.model tiny
.code
org 100h
start:  mov ah,4Ah  ;allow mem alloc.
    mov bx,10h  ;новый размер блока в параграфах
    int 21h     
    mov si,offset command_line
    int 2Eh ; execute a command using BASE LEVEL
    mov ah,4Ch  ;выходим из программы
    int 21h
command_line db N,'DIR',0Dh
N = $ - command_line - 1
end  start
или так
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
; masm dos com #
.286
.model tiny
.code
org 100h
start:  mov si,offset string    
    mov cx,N
@@: push cx
    lodsb
    mov ah,5      ;номер функции
    mov cl,al;передача параметра через регистр CL, содержимое CH=0 
    int 16h
    pop cx
    loop @b
    retn
string db 'DIR',0Dh; не более 15 символов
N = $ - string
end start
или так
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
; masm dos com #
.286
.model tiny
.code
org 100h
start:  mov bx,100h     ;выделим блок памяти в 256 параграфов
        mov ah,4Ah               
        int 21h
        mov bx,offset parametrs ;указываем на блок параметров
        mov [bx+4],cs
        mov dx,offset filename
        mov ax,4B00h;загрузить и выполнить программу из командной строки
        int 21h
        retn        ;выход в DOS
command_line db N,'/c DIR',0Dh
N = $-command_line-1;длина командной строки
;командная строка типа pascal, начинается с байта длины строки, заканчивается
;ASCII-кодом клавиши Enter (0Dh). При передаче команды CMD.EXE нужно указать /С перед 
;строкой (требование вызова вторичного командного процессора). Программу cmd.exe
;из папки windows\system32\ проще разместить в том же каталоге, что и программа 
filename db 'cmd.exe',0
parametrs dw 0,command_line,5 dup(0);блок параметров
end start
или вот так
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
; masm dos com #
.286
.model tiny
.code
org     100h
start:  mov ah,4Ah      ;модифицируем назначенную память
        mov bx,100h     ;новый размер блока в параграфах
        int 21h         
        or bx,-1        ;требуемое число параметров для выполнения
        mov ah,48h      ;вделить блок памяти
        int 21h         ;возвращает в bx размер наибольшего доступного блока 
;памяти в параграфах
        mov ah,48h      ;вделить блок памяти, в bx действительно доступная память
        int 21h         ;возвращает в ах сегментный адрес выделенного блока
        mov es,ax       ;es:=new PSP
        mov ss,ax       ;ss:=new PSP
        xchg dx,ax      ;dx:=new PSP
        mov ah,26h      ;создать новый префикс программного сегмента
        int 21h         
        mov dx,offset filename
        mov di,80h      ;указатель на командную строку
        mov cx,(N+1)/2  ;копируем по два байта за раз, N округлен в большую 
;сторону к числу кратному два, поэтому добавочный movsb не нужен
        mov si,offset command_line
        rep movsw       ;создаем командную строку для запуска command.com
        mov ax,3D00h    ;открыть command.com на чтение
        int 21h
        xchg bx,ax      ;дескриптор файла в bx
        xor cx,cx       ;cx=dx=0
        xor dx,dx
        mov ax,4202h    ;установить файловый указатель на конец файла
        int 21h
        push ax         ;сохраним в стеке длину файла command.com
        mov ax,4200h    ;установить файловый указатель на начало файла
        int 21h
        mov dx,es       ;пересчитываем чему должен быть равен dx из расчета,
        add dx,10h      ;что cs*10h+dx=es*10h+100h
        mov cx,cs
        sub dx,cx
        shl dx,4        ;в dx адрес буфера, куда будет скопирован command.com
        pop cx          ;в сх число байтов для чтения 
        mov ah,3Fh      ;читаем command.com в буфер
        int 21h
        mov ah,3Eh      ;закрыть файл command.com
        int 21h
        mov ax,es       ;ds:=new PSP
        mov ds,ax       ;при старте СОМ-файла cs=ds=es=ss=PSP ip=100h
        push es         ;новое значение cs:=new PSP
        push 100h       ;новое значение ip:=100h
        retf            ;запускаем command.com для создания файла myfile.txt
filename db 'c:\windows\system32\command.com',0;полный путь к command.com
command_line db N-1,'/c DIR',0Dh
N = $ - command_line
db 0; если N нечетное, то копируется и добавочный нулевой символ 
end     start

Поиск файлов и вывод содержимого каталога на экран. Windows
Пример из книги Пирогова "Ассемблер для Windows". Программа осуществляет поиск в указанном или текущем каталоге. Поиск проходит по дереву каталогов, начиная с заданного
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
; masm windows console #
.686
.model flat
include \masm32\include\windows.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
;объявление используемых функций Windows (внешние ссылки)
extern _imp__wsprintfA:dword
extern _imp__CharToOemA@8:dword;
extern _imp__GetStdHandle@4:dword;
extern _imp__WriteConsoleA@20:dword;
extern _imp__ReadConsoleA@20:dword;
extern _imp__ExitProcess@4:dword;
extern _imp__GetCommandLineA@0:dword;
extern _imp__lstrcat@8:dword;
extern _imp__lstrcpyA@8:dword;
extern _imp__FindFirstFileA@8:dword;
extern _imp__FindNextFileA@8:dword;
extern _imp__FindClose@4:dword;
extern _imp__lstrlenA@4:dword
_FIND STRUC;структура, используемая для поиска файлов при помощи
;функций FindFirstFileA и FindNextFileA
ATR     dd ?;атрибут файла
CRTIME  dd ?;время создания файла
        dd ?
ACTIME  dd ?;время доступа к файлу
    dd ?    
WRTIME  dd ?;время модификации файла
    dd ?
SIZEH   dd ?;размер файла
SIZEL     dd ?
    dd ?;резерв
    dd ?
NAM db 260 dup(0);длинное имя файла
ANAM db 14 dup(0);короткое имя файла
_FIND ENDS
.code
start:   push STD_INPUT_HANDLE
         call _imp__GetStdHandle@4;получаем HANDL1 для ввода
         mov HANDL1,eax
         push STD_OUTPUT_HANDLE
         call _imp__GetStdHandle@4;получаем HANDL для вывода
         mov HANDL,eax
         call NUMPAR ;получить количество параметров
     mov PAR,eax
     dec eax;если параметр один, то искать в текущем каталоге
     je NO_PAR
     mov edx,2;получить параметр номером edi
     lea ebx,BUF
     call GETPAR
     jb NO_PAR
         mov edi,3;подключить параметр - маску поиска
     lea ebx,MASKA
     call GETPAR
NO_PAR:  push offset BUFER
     call FIND
     push NUMF;вывести количество файлов
     push offset FORM
     push offset BUFER
     call _imp__wsprintfA
         add esp,12
     lea eax,BUFER
     mov edi,1
     call WRITE
     push NUMD;вывести количество каталогов
     push offset FORM1
     push offset BUFER
     call _imp__wsprintfA
         add esp,12
     lea eax,BUFER
     mov edi,1
     call WRITE
_END:    push 0
         call _imp__ExitProcess@4
;---------------------------------------------------------------
WRITE PROC;вывести строку с переводом строки
     push eax
         push eax
     call _imp__lstrlenA@4;получить длину параметра
     pop ebx
     mov dword ptr [ebx+eax],0D0Ah
     add eax,2
NO_ENT:  push 0;резерв
     push offset LENS;выведено символов
     push eax;длина строки
     push ebx;адрес строки
     push HANDL;HANDL вывода
         call _imp__WriteConsoleA@20;вывести строку 
         RET
WRITE ENDP
;-------------------------------------------------
NUMPAR PROC;определение параметров в командной строке
    call _imp__GetCommandLineA@0
    mov esi,eax
    xor ecx,ecx
    mov edx,1
L1: cmp byte ptr [esi],0
    je L4
        cmp byte ptr [esi],32
    je L3
    add ecx,edx;номер параметра
        xor edx,edx
    jmp L2
L3: or edx,1
L2: inc esi
    jmp L1
L4: mov eax,ecx
    ret
NUMPAR ENDP
;------------------------------------------------------
GETPAR PROC;в edi указатель на буфер, куда помещен параметр
;в ebx номер параметра
        call _imp__GetCommandLineA@0
    mov esi,eax
    xor ecx,ecx
    mov edx,1
L10:    cmp byte ptr [esi],0
    je L40
        cmp byte ptr [esi],32
    je L30
    add ecx,edx;номер параметра
        xor edx,edx
    jmp L20
L30:    or edx,1
L20:    cmp ecx,edi
    jne L50
        mov al,[esi]
    mov [ebx],al
    inc ebx
L50:    inc esi
    jmp L10
L40:    mov byte ptr [ebx],0
    ret
GETPAR ENDP
FIND PROC;поиск в каталоге файлов и вывод имени каталога в BUF
FINDH   equ [ebp-4]
DIRS    equ [ebp-304]
DIRSS   equ [ebp-604]
DIRV    equ [ebp-904]
DIR equ [ebp+8]
    enter 904,0
    xor eax,eax
    lea edi,DIRV
    mov ecx,900/4
    rep stosd
    push DIR
    call _imp__lstrlenA@4
    mov ebx,eax
    mov edi,DIR
    cmp byte ptr [edi],0
    je _OK
    cmp byte ptr [edi+ebx-1],'\'
    je _OK
        push offset AP
    push DIR
    call _imp__lstrcat@8
_OK:    push DIR
    lea eax,DIRSS
    push eax
    call _imp__lstrcpyA@8
    push offset MASKA;путь с маской
    push DIR
    call _imp__lstrcat@8
        push offset FIN;здесь начало поиска
    push DIR
    call _imp__FindFirstFileA@8
    inc eax;cmp eax,-1
    je _ERR
    dec eax
    mov FINDH,eax;сохранить дескриптор поиска
LF: cmp byte ptr FIN.NAM,".";исключить файлы "." и ".."
    je _FF
    lea eax,DIRSS
    push eax
    lea eax,DIRS
    push eax
    call _imp__lstrcpyA@8
        push offset FIN.NAM
    lea eax,DIRS
        push eax
    call _imp__lstrcat@8
    test byte ptr FIN.ATR,10h;не каталог ли?
    je NO_DIR
    push offset DIRN
    lea eax,DIRS
        push eax
    call _imp__lstrcat@8
        inc NUMD
    dec NUMF
    mov PRIZN,1
    lea eax,DIRS
    push eax
    call OUTF
    jmp _NO
NO_DIR: lea eax,DIRS
    push eax
    call OUTF
    mov PRIZN,0
_NO:    cmp PRIZN,0
        jz _F
        lea eax,DIRSS
    push eax
    lea eax,DIRV
    push eax
    call _imp__lstrcpyA@8
    push offset FIN.NAM
    lea eax,DIRV
    push eax
    call _imp__lstrcat@8
    lea eax,DIRV
    push eax
    call FIND
_F: inc NUMF
_FF:    push offset FIN
    push FINDH
    call _imp__FindNextFileA@8;продолжение поиска
    test eax,eax;cmp eax,0
    jne LF
    push FINDH
    call _imp__FindClose@4;закрыть поиск
_ERR:   leave
    ret 4
FIND ENDP
OUTF proc
STRN    equ [ebp+8]
    push ebp
    mov ebp,esp
    push STRN
    push STRN
    call _imp__CharToOemA@8;преобразовать строку
    mov eax,STRN
    mov edi,1
    call WRITE;здесь вывод результата
    inc NUM
    cmp NUM,22;конец страницы?
    jne NO
    mov NUM,0
    mov edi,1
    lea eax,TEXT
    call WRITE;ждать вывод строки
    push 0
    push offset LENS
    push 10
    push offset BUFIN
    push HANDL1
    call _imp__ReadConsoleA@20
NO: pop ebp
    ret 4
OUTF endp
;data------------------------------------------
BUF     db 0
    db 100 dup (?)
LENS    dd 100;количество выведенных символов
HANDL   dd ?
HANDL1  dd ?
MASKA   db "*.*",0
AP  db "\",0
FIN _FIND <0>
TEXT    db '„«п Їа®¤®«¦Ґ*Ёп **¦¬ЁвҐ Є«*ўЁиг ENTER',13,10,0;"Нажмите клавишу Enter" в кодировке CP-866
BUFIN   db 10 dup(0)
NUM db 0
NUMF    dd 0;счетчик файлов
NUMD    dd 0;счетчик каталогов
FORM    db '—Ёб«® **©¤Ґ*ле д*©«®ў: %lu',0;"Число найденных файлов"
FORM1   db '—Ёб«® **©¤Ґ*ле Є*в*«®Ј®ў: %lu',0;"Число найденных каталогов"
BUFER   db 100 dup(0)
DIRN    db " <DIR>",0
PAR     dd 0;количество параметров
PRIZN   db 0      
end start
5
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
25.06.2013, 13:20
Здравствуйте! Я подобрал для вас темы с ответами на вопрос FAQ для раздела Assembler, MASM, TASM (Assembler):

Глоссарий для раздела Assembler, MASM, TASM - Assembler
Thread, AFP, Charles Kludge, 6a6kin, Убежденный, Ethereal, выкладывайте свои глоссарии - разложу ваши сообщения по алфавиту, если найдете...

Организация тем в разделе Assembler, MASM, TASM - Assembler
Всем привет! Друзья, возник вопрос: как лучше организовать темы в разделе Assembler, MASM, TASM? Какие ветки-подразделы сделали бы...

Полезные макросы для MASM и TASM - Assembler
Не претендую на создание чего-то нового и гениального, но макросы довольно полезные. Часть из того, что я сделал, уже делали другие. Тем не...

Есть ли компиляторы Tasm или Masm для 64-разрядных систем - Assembler
Есть ли компиляторы Tasm или Masm для 64-разрядной с-мы??? если есть просьба скинуть...

MASM, TASM, FASM: что выбрать для программирования в ядре - Assembler
Какой асемлер выбрать для проганья в едре? вынь

Видеоуроки по Ассемблеру MASM/TASM (для DOS) на русском языке - Assembler
Всем доброго времени суток. Вобщем, ищу видеоуроки на русском языке по Ассемблеру. Нужно для подготовки к экзамену (нужно будет...

62
Mikl___
Заблокирован
Автор FAQ
25.06.2013, 13:20  [ТС] #17
ОСНОВНЫЕ ПРАВИЛА НАПИСАНИЯ ПРОГРАММ НА ЯЗЫКЕ АССЕМБЛЕРА
Данные правила относятся не только к программированию на языке ассемблера, но и к программированию на других языках. Может быть, их трудно понять, не имея навыка в программировании, но «незнание основ не освобождает от ответственности».
Начинайте с комментариев
Начните с написания инструкции для пользователя — для чего создается и каковы возможности вашей программы. А теперь немного усложните вашу инструкцию по применению вашей программы, подразумевая под «пользователем» программиста, использующего написанный вами код — зачастую этим программистом-исследователем будете вы сами.
Акт записи на обычном языке описания того, что делает программа и что делает каждая функция в программе, является критическим шагом в мыслительном процессе. Хорошо построенное, грамматически правильное предложение — признак ясного мышления. Если вы не можете это записать, то велика вероятность того, что вы не полностью продумали задачу или метод ее решения. Плохая грамматика и построение предложения являются также показателем поверхностного мышления. Поэтому первый шаг в написании любой программы — записать, что именно и как делает программа.
Итак, комментарии для вашей программы уже готовы. Теперь возьмите ваше описание по использованию и добавьте вслед за каждым абзацем блоки кода, реализующие функции, описанные в этом абзаце. Оправдание: «У меня не было времени, чтобы добавить комментарии» на самом деле означает — «Я писал этот код без проекта системы и у меня нет времени воспроизвести его». Если создатель программы не может воспроизвести идеи, воплощенные в программный проект, то кто же тогда сможет?
Работа программиста состоит из двух частей: разработать приложение для пользователя и сделать возможным дальнейшее сопровождение программы. Единственный способ решить вторую часть задачи — комментировать код. Причем комментарии должны описывать не только, что делает код, но и предположения, принятый подход и причины, по которым вы выбрали именно его. Кроме того, необходимо, чтобы комментарии также соответствовали коду. Пишите код так, словно тот, кто будет заниматься его поддержкой, — опасный психопат, знающий где вы живете. Хотя вы можете считать, что ваш код полностью очевиден и может служить примером ясности, без правильных комментариев понять его постороннему достаточно трудно. Парадокс заключается в том, что спустя неделю вы сами можете оказаться в роли этого постороннего.
Старайтесь использовать следующий подход при написании комментариев:
  • перед каждой функцией или методом размещается одно или два предложения со следующей информацией:
    • что делает программа;
    • возникающие при этом предположения о программе;
    • что должно содержаться во входных параметрах;
    • что должно содержаться во выходном параметре в случае успешного или неудачного завершения;
    • все возможные выходные значения;
  • перед каждой не совсем очевидной частью функции следует поместить одно или два предложения, объясняющие выполняемые действия;
  • любой интересный алгоритм заслуживает подробного описания;
  • любая нетривиальная ошибка, устраненная в коде, должна комментироваться, при этом нужно привести номер ошибки и описать сделанное исправление;
  • правильно размещенные операторы диагностики, проверки условий, а также соглашения об именах переменных могут также служить хорошими комментариями и передавать содержание кода;
  • писать комментарии так, будто сами собираетесь заниматься его поддержкой через пять лет;
  • если возникла мысль «это хитро сделано» или «это ловкий трюк» — лучше переписать данную функцию, а не комментировать ее.
Если вы будете правильно писать комментарии – вы в безопасности, даже если программист из службы поддержки окажется психопатом.
Читайте код
Все писатели — это читатели. Вы учитесь, когда смотрите, что делают другие писатели. Я настоятельно рекомендую, чтобы, как минимум, члены группы программирования читали код друг у друга. Читатель может найти ошибки, которые вы не увидели, и подать мысль, как улучшить код. Для вас лучше присесть с коллегой и просто разобрать код строка за строкой, объясняя, что и как делается, получить какую-то обратную связь и совет. Для того чтобы подобное упражнение принесло пользу, автор кода не должен делать никаких предварительных пояснений. Читатель должен быть способен понимать код в процессе чтения. Если вам пришлось объяснять что-то вашему читателю, то это значит, что ваше объяснение должно быть в коде в качестве комментария. Добавьте этот комментарий, как только Вы его произнесли; не откладывайте этого до окончания просмотра.
Разлагайте сложные проблемы на задачи меньшего размера
На самом деле это также и правило литературного стиля. Если очень трудно объяснить точку зрения за один раз, то разбейте изложение на меньшие части и по очереди объясняйте каждую. То же самое назначение у глав в книге и параграфов в главе.
Используйте язык полностью
Некоторые программисты считают одним из недостатков языка ассемблера большее, по сравнению с языками высокого уровня, количество команд, но, по-моему, это одно из достоинств языка ассемблера.
Проблема должна быть хорошо продумана перед тем, как она сможет быть решена
Это относится не только к программированию на языке ассемблера.
Отредактируйте свой код. Программа должна писаться не менее двух раз
Раньше, когда вы изучали в школе литературу, вам никогда не приходило в голову сдавать черновик письменного задания, если Вы, конечно, рассчитывали на оценку выше тройки. Тем не менее, многие компьютерные программы являются просто черновиками и содержат столько же ошибок, сколько и черновики ваших сочинений. Хороший код программы должен быть сначала написан, а затем отредактирован в целях улучшения (под «редактированием» имеется в виду «исправление»). Редактирование, как правило, приводит к сокращению кода, а небольшие программы выполняются быстрее.
Оптимизация программ на языке ассемблера
Итак ваша программа заработала, а теперь постарайтесь переделать ее так, чтобы она стала максимально компактной и в тоже время максимально быстродействующей.
Такая оптимизация достигается в три этапа:
  • Алгоритмическая оптимизация то есть подбор алгоритма, который выполняет вашу задачу более быстрым способом и позволит сократить не пять, а пятьдесят операторов;
  • Подстройка программы под конкретное оборудование;
  • Замена некоторых ассемблерных команд на машинный код. Тщательный анализ машинного кода, вырабатываемого транслятором, позволяют прийти к выводу, что некоторые коды человек может выработать более оптимально, чем программа.

Для написания этого топика были использованы статьи из следующих книг
  1. Ален И. Голуб «Правила программирования С & С++» — М. : БИНОМ, 1996. — 272 с.
  2. Джон Роббинс «Отладка Windows-приложений» — М.: ДМК Пресс, 2001. — 448 с., ил.
4
Mikl___
Заблокирован
Автор FAQ
25.06.2013, 15:13  [ТС] #18
Графика DOS. Двигаемся в тоннеле
Tunnel - 256 bytes
01-11-2002
256 bytes intro: realtime rendering, software rendering, MsDos, x86 real mode, assembler, demoscene
Автор Iсigo Quñlez, взято здесь и немного доработано
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
; masm dos com #
.model tiny
.code
.386
org 100h
WIDTH_SREEN equ 320
HEIGHT_SCREEN   equ 200
IMPUT_STATUS_0  equ 3DAh
VGA_SEGMENT equ 0A000h
 
start:  mov bh,10h      ; изменить размер блока памяти
    mov ah,4Ah      ; ES = сегментный адрес изменяемого блока
    int 21h     ; BX = новый размер блока в параграфах
    mov ah,48h      ; выделить блок памяти
    int 21h     ; BX = требуемое число параграфов для выделения
    mov gs,ax       ; сегментный адрес выделенного блока передали в GS 
    mov bh,20h      ; выделить блок памяти
    mov ah,48h      ; BX = требуемое число параграфов для выделения
    int 21h                         
    mov fs,ax       ; сегментный адрес выделенного блока передали в FS и в ES
    mov es,ax        
    mov bp,offset a2;начало данных
    mov cl,200  ;высота экрана
a0: xor di,di
    mov bx,-WIDTH_SREEN/2;bx меняется от -160 до 159, то есть 320 точек (ширина экрана)
@@: mov ax,cx
    sub ax,100
    mov [bp+3],ax
    mov [bp+5],bx
    fild word ptr [bp+5]
    fild word ptr [bp+3]
    fld st
    fmul st,st;квадрат ax
    fld st(2)
    fmul st,st;квадрат bx
    faddp st(1),st;st = bx^2 + ax^2
    fsqrt   ;st=sqrt(bx^2 + ax^2)
    fidivr word ptr [bp+1];sqrt(bx^2 + ax^2)/2560
    fistp word ptr es:[di]
    fpatan   ;arctg(bx/ax)
    fimul word ptr [bp+0];arctg(bx/ax)*41
    fistp word ptr es:[di+1]
    add di,2
    inc bx
    cmp bx,159
    jle @b
    mov ax,es
    add ax,40
    mov es,ax; es := es + 40
    loop a0
@@: mov al,cl
    xor al,ch
    mov bx,cx
    mov gs:[bx],al
    loop @b
    mov cl,16
@@: mov al,gs:[bx]
    add al,gs:[bx-1]
    add al,gs:[bx+100h]
    add al,gs:[bx-100h]
    shr ax,2
    mov gs:[bx],al
    inc bx
    jnz @b
    loop @b
    mov al,13h  ;графический режим 320х200х256
    int 10h     
    push VGA_SEGMENT;es настроен на начало графического видеобуфера
    pop es
@@: mov al,cl       ;устанавливаем палитру
    mov ah,al
    mov dx,3C8h
    out dx,ax
    inc dx
    out dx,al
    out dx,al
    loop @b
begin:  inc bp; DH=3 + DL=0DAh  DX=IMPUT_STATUS_0
    mov dl,0DAh     ;ждем обратного хода луча
WaitVerticalSync:   in al,dx        
    test al,8
    jz WaitVerticalSync
WaitNotVerticalSync:    in al,dx        
    test al,8
    jnz WaitNotVerticalSync
    push fs
    xor di,di
    mov cl,HEIGHT_SCREEN    ;высота экрана
a1: mov si,(WIDTH_SREEN-1)*2;ширина экрана, так как si уменьшается через 2
@@: mov bx,fs:[si]
    add bx,bp
    mov al,gs:[bx]
    stosb
    sub si,2
    jns @b
    mov ax,fs
    add ax,40
    mov fs,ax   ;fs := fs + 40
    loop a1
    pop fs
    mov ah,11h
    int 16h ;ждем нажатия на Esc
    jz begin
    mov ax,3
    int 10h ;восстанавливаем текстовый режим
    retn    ;выходим из программы
a2  db 41,0,10
end start
Графика DOS. Еще один тоннель
Здесь используется bmp-файл в качестве подложки, поэтому можно организовать путешествие сквозь водоворот, ледяную или каменную пещеру, кишечник и т.д. всё зависит от степени вашей испорченности
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
; masm dos com #
.model tiny
.code
.386
org 100h
WIDTH_SREEN equ 320
HEIGHT_SCREEN   equ 200
SCREENSIZE  equ WIDTH_SREEN*HEIGHT_SCREEN
IMPUT_STATUS_0  equ 3DAh
VGA_SEGMENT equ 0A000h
MEMBLOCKSIZE    equ SCREENSIZE/16
start:      
 
u       equ dword ptr [bp-4]
v       equ dword ptr [bp-8]
x       equ dword ptr [bp-12]
y       equ dword ptr [bp-16]
x2      equ dword ptr [bp-20]
radius      equ dword ptr [bp-24]
screen      equ word ptr [bp-26]
tunnel      equ word ptr [bp-28]
pal     equ byte ptr [bp-1052]
 
        mov sp,0FB0h
        mov ah,4Ah; ADJUST MEMORY BLOCK SIZE (SETBLOCK)
        mov bx,0FBh; ES = segment address of block  to change
        int 21h ; BX = new size in paragraphs
        enter 1052,0
        mov bx,MEMBLOCKSIZE
        mov ah,48h; ALLOCATE MEMORY
        int 21h ; BX = number of 16-byte paragraphs desired
        mov screen,ax
        mov bx,1000h
        mov ah,48h; ALLOCATE MEMORY
        int 21h ; BX = number of 16-byte paragraphs desired
        mov gs,ax;gs=texture
        mov bx,MEMBLOCKSIZE*2
        mov ah,48h; ALLOCATE MEMORY
        int 21h ; BX = number of 16-byte paragraphs desired
        mov tunnel,ax
        mov ax,13h
        int 10h     ; - VIDEO - SET VIDEO MODE
;readbmp--------------------------------
        mov dx,offset filename; DS:DX   -> ASCIZ filename
        mov ax,3D00h       ; OPEN DISK FILE WITH HANDLE FOR READ ONLY
        int 21h     
        mov bx,ax
        xor cx,cx
        mov dx,54;заголовок BMP-файла
        mov ax,4200h; MOVE FILE READ/WRITE POINTER (LSEEK)
        int 21h ; offset from beginning of file
                mov ah,3Fh; READ FROM FILE WITH HANDLE
                mov cx,1024; CX = number of bytes to read
        lea dx,pal ; DS:DX -> buffer
        int 21h; BX = file handle           
        mov di,1023 ;палитра
@@:     shr pal[di],2
        dec di
        jns @b
        push ds
        push gs
        pop ds;ds=texture
        mov si,255
@@:     mov dx,si  ; BX = file handle
        shl dx,8   ; DS:DX -> buffer
        mov cx,100h; CX = number of bytes to read
        mov ah,3Fh ; READ FROM FILE WITH HANDLE
        int 21h     
        dec si
        jns @b
        pop ds
        mov ah,3Eh; CLOSE A FILE WITH HANDLE
        int 21h     ; BX = file handle
;inittunnel------------------------------------------------
        xor di,di
        mov es,tunnel
        mov y,-HEIGHT_SCREEN/2
        mov si,HEIGHT_SCREEN
@:      mov x,-WIDTH_SREEN/2
        mov cx,WIDTH_SREEN
@_:     mov eax,x
        imul eax
        mov x2,eax;x2=x*x
        mov eax,y
        imul eax
        add eax,x2;eax=x*x+y*y
        mov radius,eax
        fild radius
        fsqrt
        fistp radius
        cmp radius, 1
        jge @f
        mov radius,1
@@:     fild x
        fild y
        fpatan
        fldpi
        faddp st(1),st
        fmul const128_pi
        fistp x2
        mov eax,8196*256
        cdq
        idiv radius
        and eax,0FF00h;eax=(8196*256/radius)&0FF00h
        mov radius,eax
        add al,byte ptr x2
        add ah,0;ax = radius + (x2 & 0FFh)
        stosw
        test di,di
        jnz @f
        mov ax,es
        add ah,10h
        mov es,ax
@@:     inc x
        loop @_
        inc y
        dec si
        jnz @
;setcolortable------------------------------------------
        mov cx,256
        lea bx,pal
        xor ax,ax
        mov dx,3C8h
        out dx,al
        inc dx
@@:             mov al,[bx+2]
        out dx,al
        mov al,[bx+1]
        out dx,al
        mov al,[bx]
        out dx,al
        add bx,4
        loop @b
;-------------------------------------------
        fldz;time=0
mainloop:   ;rendertunnel
        fld st;time
        fld st
        fcos
        fmul const256
        fistp u       ;u=256*cos(time)
        fmul const512
        fistp v       ;v=512*time
        shl v,8    ;v=131072*time
        xor bx,bx
        mov es,screen
        mov fs,tunnel
        xor di,di
        mov cx,SCREENSIZE
@__:        mov si,fs:[bx]
        add si,word ptr u
        add si,word ptr v
        movs byte ptr es:[di],gs:[si]
        inc bx
        inc bx
        jnz @f
                mov ax,fs
        add ah,10h
        mov fs,ax
@@:     loop @__
        fadd const001;time+=0.01
;copyvirtualscreen-----------------------------------
        mov dx,IMPUT_STATUS_0
WaitVerticalSync:in al,dx
        test al,8
        jnz WaitVerticalSync
WaitNotVerticalSync:in al,dx
        test al,8
        jz WaitNotVerticalSync
        mov cx,SCREENSIZE/4
        xor si,si
        mov fs,screen
        xor di,di
        push VGA_SEGMENT
        pop es
        rep movs dword ptr es:[di],fs:[si]
        mov es,cx;es=0
        mov ax,es:[41Ah]
        sub ax,es:[41Ch]
        jz mainloop
exit:       mov ax,3
        int 10h     ; - VIDEO - SET VIDEO MODE
        int 20h
 
const001    dd 0.01
const128_pi dd 40.743665431525205956834243423364;128/pi
const256    dd 256.0
const512    dd 512.0
filename    db 'texture.bmp',0
end start
COM-файл, исходный текст и bmp-файл во вложении
Шаманизм! Тоннель - 64 байта
Автор farbrausch release date 2003-01-25 взято здесь
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
org 100h
use16 
    mov al, 0x13;Сюда указывает si
    cmc;Эти две инструкции - данные
    inc dx;Используются при обращении к dword[si]
    int 0x10;графический режим 320х200х256
    push word 0xa000
    pop es;es настроен на начало графического видеобуфера 
pix: mul bx ;обнуляем ax и dx
    inc ax
    add ax, di
;переменная в [si] - это float, отвечающий за угол поворота тоннеля
    adc [si], bx   ; изначально 20CDh - приращение угла в начале каждого кадра (при 
;переполнении di)
    div word [si+12]
clp: fmul dword [si]
    fiadd word [si]
    fistp word [bx]
    sub ax, 83h ; устанавливает центр 
    xchg ax, [bx]
    xor cx, ax
    fild word [bx]
    fmul st,st
    xor bx, bp;На первой итерации bx=0, на второй bx=bp, на третьей bx=0
    xchg ax, dx
    fxch st1
    jnz clp ;Цикл выполнится 2 раза
    faddp st1, st0
    fsqrt
    fdivr dword [si]
    fild word [bx]
    fild word [bp]
    fpatan 
    setalc;Обнуляем ax
    xchg ax, cx
    or al, 0x87
    stosb;Вывод пикселя на экран
    jmp short pix
Графика DOS. Цветок
В общем, палитру выставляйте как хотите, главное, чтобы переходы между цветами были плавными.

Для хранения изображения "цветка", заведем массив flower, размером 640x400. Нарисуем в нем наш "цветок". Формулу для рисования "цветка" можно найти в любом нормальном учебнике по "вышке". Для всех тех кто уже успел забыть этот кошмар или у кого он еще не начался, так уж быть, вот процедура инициализации массива:
C
1
2
3
4
5
 for(long y=-200;y<200;y++)
   for(long x=-320;x<320;x++)
     flower[(x+320)+(y+200)*640]=0.75*cos(16*atan2(y,x))*256/TWO_PI+
                                   1.5*sin(8*atan2(y,x))*256/TWO_PI+
                                   sqrt(x*x+y*y);
Меняя коэффициенты в формуле можно получить довольно забавные версии этого эффекта. В результате мы получим в массиве flower красивый такой цветок или не совсем красивый или совсем не красивый...
Психоделическая составляющая эффекта
Теперь определимся сколько нам потребуется "цветков" на экране, мне хватило трех, при большем количестве "цветков" на экране довольно трудно становиться понять, что твориться на экране. Правда если хочешь "словить глюков" - поставь цветков так 5 - 7 и смотри на экран минут этак пять, глюков может и не словишь, но глаза заболят обязательно.

Но перед тем, как мы начнем наслаждаться резью в глазах надо еще сделать кое-что.

Возьмем какую-нибудь точку в массиве flower, причем не любую, а такую, чтобы X координата лежала в промежутке от 0 до 320, а Y координата в промежутке от 0 до 200. Начиная с этой точки скопируем кусок массива, размером 320x200, на экран. Получим изображение куска "цветка". Если теперь сдвигать начальную точку, в каждом новом кадре, на новое место, то получим изображение перемещающегося цветка.

Теперь допустим, что начальных точек две, и на экран копируется не один кусок "цветка", а два. Причем накладывается они друг на друга обычным сложением цветов. В результате получим требуемый эффект.
Вот. Реализуется это примерно так:
C
1
2
3
4
5
6
7
8
9
10
11
12
    x=160+120*sin(time*0.5+1.0);
    y=100+90*cos(time*1.5+2.0);
    offset1=x+(y<<7)+(y<<9);
    x=160+120*sin(time*1.0+1.5);
    y=100+90*cos(time*2.0+0.5);
    offset2=x+(y<<7)+(y<<9);
    for(y=0;y<200;y++)
      for(x=0;x<320;x++)
      {
        screen[x+(y<<6)+(y<<8)]=flowers[x+(y<<7)+(y<<9)+offset1]+
                                flowers[x+(y<<7)+(y<<9)+offset2];
      }
Добавить еще "цветков" думаю вам не составит труда.
Взято здесь, текст программы немного переделан, в результате СОМ-файл уменьшился с 1106 байт до 885 байт
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
; masm dos com #
.model tiny
.code
.686p
.mmx
org 100h
VGA_SEGMENT equ 0A000h
IMPUT_STATUS_0  equ 3DAh    ;регистр статуса ввода 0
WIDTH_SCREEN    equ 320
HEIGHT_SCREEN   equ 200
SCREENSIZE  equ WIDTH_SCREEN*HEIGHT_SCREEN
MEMBLOCKSIZE    equ SCREENSIZE/16
start:
flowers     equ word ptr [bp-40]
screen      equ word ptr [bp-38]
i       equ word ptr [bp-36]
c0      equ word ptr [bp-34]
v       equ dword ptr [bp-32]
u       equ dword ptr [bp-28]
offset1     equ dword ptr [bp-24]
offset2     equ dword ptr [bp-20]
offset3     equ dword ptr [bp-16]
x       equ dword ptr [bp-12]
y       equ dword ptr [bp-8]
time        equ dword ptr [bp-4]
 
        mov sp, 0D60h
        mov ah, 4Ah; ADJUST MEMORY BLOCK SIZE (SETBLOCK)
        mov bx, 0D6h; ES = segment address of block to change
        int 21h ; BX = new size in paragraphs   
        enter   40, 0
        mov time, 0
        mov bx, MEMBLOCKSIZE
        mov ah, 48h; DOS -  2+ - ALLOCATE MEMORY
        int 21h ; BX = number of 16-byte paragraphs desired                 
        mov screen, ax
        mov bx, MEMBLOCKSIZE*4
        mov ah, 48h; DOS -  2+ - ALLOCATE MEMORY
        int 21h ; BX = number of 16-byte paragraphs desired                 
        mov flowers, ax
;initflower------------------------------------------
    mov y, -HEIGHT_SCREEN
a0: mov x, -WIDTH_SCREEN
@@: mov eax, x
    imul    eax
    mov v, eax; v = x^2
    mov eax, y
    imul    eax
    add eax, v
    mov u, eax
    fild    u     ;u = x^2 + y^2
    fsqrt
    fild    x
    fild    y
    fpatan
    fld st
    fmul    const16
    fcos
    fmul    const96
    fxch    st(1)
    fmul    const8
    fsin
    fmul    const192
    faddp   st(1), st
    fldpi
    fdivp   st(1), st
    faddp   st(1), st
    fistp   v
    mov edi, y
    add edi, HEIGHT_SCREEN
    imul    edi, WIDTH_SCREEN*2
    add edi, x
    add edi, WIDTH_SCREEN;esi=(y+200)*640 + x + 320
    mov eax, edi
    shr eax, 16
    shl ax, 12
    add ax,flowers
    mov es, ax
    mov al, byte ptr v
    stosb;flowers[(x+320)+(y+200)*640]=
         ;= 0.75*cos(16*atan2(y,x))*256/TWO_PI+
             ;+ 1.5*sin(8*atan2(y,x))*256/TWO_PI+
             ;+ sqrt(x*x+y*y);
    inc x
        cmp x,WIDTH_SCREEN
    jl  @b
    inc y
        cmp y,HEIGHT_SCREEN
    jl  a0
    mov ax, 13h
    int 10h     ; - VIDEO - SET VIDEO MODE
;setcolortable-------------------------------------------
    mov i,255
    mov cx,43;256-213
@@: mov ax, i
    sub ax, 213
    imul    ax,3
    shr ax,1
    mov c0,ax
    neg ax
    add ax,63
    push    i
    push    63
    push    0
    push    ax ;63-(i-213)*3/2
    call    setrgbpalette
    dec i
    loop @b
    mov cx,42;213-171
@@: push    i
    push    c0
    push    0
    push    63
    call    setrgbpalette
    dec i
    loop @b
    mov cx,43;171-128
@@: mov ax, i
    sub ax, 128;c=(i-128)*3/2;
    imul    ax,3
    shr ax,1
    mov c0, ax
    neg ax
    add ax,63
    push    i
    push    0
    push    ax
    push    63
    call    setrgbpalette
    dec i
    loop @b
    mov cx,43;128-85
@@: mov ax, i
    sub ax, 85
    imul    ax,3
    shr ax,1
    mov c0, ax
    push    i
    push    0
    push    63
    push    c0
    call    setrgbpalette
    dec i
    loop    @b
    mov cx,42;85-43
@@: mov ax, i
    sub ax, 42
    imul    ax,3
    shr ax,1
    mov c0, ax
    neg ax
    add ax,63
    push    i
    push    ax
    push    63
    push    0
    call    setrgbpalette
    dec i
    loop @b
 
    mov cx,43
@@: mov ax, i
    imul    ax,3
    shr ax,1
    mov c0, ax
    push    i
    push    63
    push    c0
    push    0
    call    setrgbpalette
    dec i
    loop @b
;setcolortable----------------------------------------
 
begin:  fld time
    fld st
    fmul    const05
    fadd    const1
    fsin
    fmul    const120
    fistp   x
    add x,160;x=160+120*sin(time*0.5+1.0);
    fld time
    fmul    const1_5
    fadd    const2
    fcos
    fmul    const90
    fistp   y
    mov eax, y;y=90*cos(time*1.5+2.0)
    add eax,100
    imul eax,WIDTH_SCREEN*2;eax=(y+100)*640
    add eax, x
    mov offset1, eax
    fld time
    fadd    const1_5
    fsin
    fmul    const120
    fistp   x
    add x,160;x=160+120*sin(time*1.5+2.0);
    fld time
    fmul    const2
    fadd    const05
    fcos
    fmul    const90
    fistp   y;y=90*cos(time*0.5+1.0);
    mov eax, y
    add eax, 100
    imul eax,WIDTH_SCREEN*2;eax=(y+100)*640
    add eax, x
    mov offset2, eax;eax=(y+100)*640 + x
    fld time      
    fmul    const1_5
    fadd    const2
    fsin              
    fmul    const120
    fistp   x         
    add x,160     ;x=120*sin(time*1.5+2)+160
    fld time
    fmul    const05   
    fadd    const1
    fcos              
    fmul    const90
    fistp   y         ;y=90*cos(time*0.5+1)
    mov eax, y
    add eax, 100
    imul eax,WIDTH_SCREEN*2
    add eax, x
    mov offset3, eax;eax=(y+100)*640 + x
    mov fs, screen
    mov ecx,(HEIGHT_SCREEN-1)*WIDTH_SCREEN
a1: mov edx,WIDTH_SCREEN-1
@@: lea edi,[edx+ecx*2];edi=x+y*640
    mov esi, edi
    add esi, offset1
    mov eax, esi
    shr eax, 16
    shl ax, 12
    add ax,flowers
    mov es,ax
    lods byte ptr es:[si]
    mov bl,al;bl=c1
    mov esi,edi
    add esi,offset2
    mov eax,esi
    shr eax,16
    shl ax,12
    add ax,flowers
    mov es,ax
    lods byte ptr es:[si]
    add bl,al;bl=c1+c2
    mov esi,edi
    add esi,offset3
    mov eax,esi
    shr eax,16
    shl ax,12
    add ax,flowers
    mov es, ax
    lods byte ptr es:[si]
    lea esi,[edx+ecx];si=y*320+x
    add al,bl; al=c1+c2+c3  
    mov fs:[si], al;screen[x+y*320]=c1+c2+c3
    dec edx
    jns @b
    sub ecx,WIDTH_SCREEN
    jns a1
    fadd    const002
    fst time ;time+=0.02
;copyvirtualscreen--------------------------------------
    mov dx,IMPUT_STATUS_0
WaitVerticalSync:in al,dx
    test al,8
    jz WaitVerticalSync
WaitNotVerticalSync:in al,dx
    test al,8
    jnz WaitNotVerticalSync
 
    mov cx,SCREENSIZE/4
    xor si,si
    push ds
    mov ds,screen
    xor di,di
    push VGA_SEGMENT
    pop es
    rep movsd
    xor ax,ax
    mov ds,ax
    mov ax,ds:[41Ah]
    sub ax,ds:[41Ch]
    pop ds
    jz begin
exit:   mov ax, 3
    int 10h     
    int 20h     
setrgbpalette   proc 
blue        equ byte ptr  [bp+4]
green       equ byte ptr  [bp+6]
red     equ byte ptr  [bp+8]
color       equ byte ptr  [bp+10]
 
        push    bp
        mov bp, sp
        mov dx, 3C8h
        mov al,color
        mov ah,red
        out dx, ax
        inc dx
        mov al,green
        out dx, al
        mov al,blue
        out dx, al
        pop bp
        retn    8
setrgbpalette   endp
 
const05     dd 0.5
const1      dd 1.0
const1_5    dd 1.5
const2      dd 2.0
const90     dd 90.0
const120    dd 120.0
const96     dd 96.0
const192    dd 192.0
const8      dd 8.0
const16     dd 16.0
const002    dd 0.02
end start
2
Mikl___
Заблокирован
Автор FAQ
25.06.2013, 15:13  [ТС] #19
Скрываем от посторонних глаз наиболее критичные части программ

Шифрование

Для затруднения изучения алгоритма программы со стороны постороннего человека часто используется шифрование кода. При использовании дизассемблера исследователь не сможет получить адекватный листинг
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
; masm dos com #
.286
.model tiny
.code
org       100h
start:  mov si,offset crypt
        mov di,si
        mov cx,num
;Цикл расшифровки, вычитающий из каждого байта 1
@@: lodsb
        dec ax
        stosb
        loop @b
;Отсюда начинается неверная интерпретация кода
crypt:  mov bx,216h
        mov ch,0Ah
        into
    and al,ah
        nop
        loopz $-55h
        mov ds:[0E3A6h],ax
    and ah,[di]
num = $ - crypt
end       start
Если оттранслировать и скомпоновать эту программу, то она после запуска выведет на экран сообщение "Привет!", но этот факт невозможно установить, изучая листинг программы. "Изюминкой" данной программы является фрагмент расшифровки, прибавляющий к каждому байту программы, начиная с адреса crypt число 1.
Чрезвычайно усилить эффект кодирования позволяет техника, предусматривающая постоянное изменение алгоритма шифровки/дешифровки.
Так, например, можно использовать не только прибавление/вычитание единицы, но и других чисел, а также умножения/деления, логические операции над байтами и так далее. Сама реализация дешифрующего фрагмента также может быть различной. Меняя регистры для хранения служебных данных, способы записи арифметических и логически операций, способы организации цикла и прочее, можно получить сколько угодно различных вариантов, исполняющих один и тот же алгоритм расшифровки.

Прячем «лишние» ассемблерные команды в обычном коде.

Данный метод полезен для усложнения дизассемблирования кода, особенно, если генерацию «скрытых» команд автоматизировать.
Взглянем на следующий код, в котором скрыто намного больше команд, чем видно на первый взгляд:
Assembler
1
2
3
4
5
MOV EAX,1EBC031
MOV EBX,90DB3190
CMP EAX,EBX
JNE $-9Eh
NOP
Как вы думаете, данный код будет выполняться бесконечно, так как EAX и EBX не равны, а команда JNE будет делать переход в район первой команды пока эти два регистра не будут одинаковы?
Давайте поставим break-point на последний оператор NOP, запустим и посмотрим результат.
адрес кодировка команды
0100739FB831C0EB01MOV EAX,1EBC031
010073A4BB9031DB90MOV EBX,90DB3190
010073A939D8CMP EAX,EBX
010073AB75F3JNE 010073A0h
010073AD90NOP
Как ни странно, но программа не зависла и значение регистра EAX и EBX равны нулю. Но как же это могло произойти?
Посмотрим на код повнимательнее. Первая команда помещает в регистр EAX значение 1EBC031, вторая команда помещает в EBX значение 90DB3190. CMP сравнивает два регистра. JNE делает переход, если значения регистров не совпадают. А вот тут самое интересное — переход делается не в начало первой команды, а на второй байт первой команды. Выполнился короткий переход, и что же мы видим? Две спрятанные команды — XOR EAX,EAX выполняет обнуление регистра EAX. И короткий переход, который передает управление внутрь другого MOV-а.
010073A031C0XOR EAX,EAX
010073A2EB01JMP SHORT 010073A5
010073A4B89031DB90MOV EBX,90DB3190
010073A939D8CMP EAX,EBX
010073AB75F3JNE SHORT 010073A0
Обнуляем второй регистр. Доходим до проверки.
010073A590NOP
010073A631DBXOR EBX,EBX
010073A890NOP
010073A939DBCMP EAX,EBX
010073AB75F3JNE SHORT 010073A0
В это время регистры EAX и EBX равны нулю.
Дело в том, что в регистр EAX записано не простое число, а машинный код команды. Из-за таких «накладок» код практически не возможно представить на языке ассемблера. Ассемблерные команды имеют разный размер. Команда «MOV EAX,1EBC031» занимает 5 байт, когда как команда XOR EAX,EAX — 2 байта. С помощью команд бинарного сдвига и, используя полу-регистры AL и AH, можно такими «прыжками» набрать обработку и ввод целого регистра EAX.
Хочу отметить, что при вставке машинных команд, как параметр MOV, нужно менять последовательность байтов, так например команда 31CO(XOR EAX,EAX), помещенная в команду MOV будет выглядеть как «MOV EAX,C031».

Данный метод применяется во многих системах защиты программного обеспечения, затрудняя поиски перехода.
Взято здесь
3
Mikl___
Заблокирован
Автор FAQ
25.06.2013, 15:13  [ТС] #20
Графика DOS. Эффект кипящей лавы.
Взято здесь, текст программы немного переделан, в результате СОМ-файл уменьшился с 369 байт до 234 байт
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
; masm dos com #
.model tiny
.686
.code
org 100h
WIDTH_SCREEN    equ 320
HEIGHT_SCREEN   equ 200
SCREENSIZE  equ WIDTH_SCREEN*HEIGHT_SCREEN
MEMBLOCKSIZE    equ SCREENSIZE/16
IMPUT_STATUS_0  equ 3DAh    ;регистр статуса ввода 0
VGA_SEGMENT equ 0A000h
 
 
temp1   equ word ptr [bp-2]
temp2   equ word ptr [bp-4]
 
start:  mov sp, 0A80h
    mov ah, 4Ah; ADJUST MEMORY BLOCK SIZE (SETBLOCK)
    mov bx, 0A8h; ES = segment address of block to change
    int 21h; BX = new size  in paragraphs                   
    enter 4,0
;RANDOMIZE---------------------------------------
    push    0
    pop es
;получаем случайное значение
    rdtsc
    mov dword ptr temp1,eax
;-------------------------------------------------------
    mov ax,13h
    int 10h     ; - VIDEO - SET VIDEO MODE
;setcolortable------------------------------------
;Палитру надо выставить вот так:
;Номер цвета:    0         255
;Цвет:        красный -> желтый
;  for(short k=0;k<=255;k++)
;   setrgbpalette(k,63,k/4,0);
    mov cx,256
    xor ax,ax
    xor bx,bx
    mov dx, 3C8h
    out dx, al
    inc dx
@@: mov al,63
        out dx,al;красный
    mov al,bl
    sar al,2
    out dx,al;зеленый
    mov al,0
    out dx,al;синий
    inc bx
    loop    @b
;setcolortable-----------------------------------
    mov bx,MEMBLOCKSIZE
    mov ah,48h; ALLOCATE MEMORY
    int 21h; BX = number of 16-byte paragraphs desired                  
    mov fs,ax;fs=screen
;CLEARBLOCK-----------------------------------
    mov es,ax
    xor eax,eax
    xor di,di
    mov cx,SCREENSIZE/4;16000
    rep stosd
;fillscreen------------------------------------
    mov di,WIDTH_SCREEN+1
    mov dx,HEIGHT_SCREEN-2
a0: mov si,WIDTH_SCREEN-2
@@: mov ax, temp2
    mov cx, temp1
    shld ax,cx,8
    shl cx,8
    rcr ax,1
    rcr cx,1
    add temp1,cx
    adc ax,temp2
    add temp1,25321
    adc ax,13849
    mov temp2,ax
    stosb
    dec si
    jnz @b
    add di,2
    dec dx
    jnz a0
;lavascreen---------------------------------------------
;screen[x][y]=(screen[x-1][y]+screen[x][y+1]+
;                  screen[x][y-1]+screen[x+1][y])/4+number;
;От числа number зависит интенсивность "кипения", чем оно больше,
;тем интенсивней "кипит" лава.
a1: push fs
    pop es;es=screen
    mov di,WIDTH_SCREEN+1
    mov si,HEIGHT_SCREEN-2
a2: mov bx,WIDTH_SCREEN-2
@@: movzx ax,byte ptr es:[di-WIDTH_SCREEN]
    movzx cx,byte ptr es:[di-1]
    add ax,cx
    movzx cx,byte ptr es:[di+1]
    add ax,cx
    movzx cx,byte ptr es:[di+WIDTH_SCREEN]
    add ax,cx
    shr ax,2
    add ax,2
    stosb
    dec bx
    jnz @b
    add di,2
    dec si
    jnz a2
;copyvirtualscreen-------------------------------
    mov dx,IMPUT_STATUS_0
WaitVerticalSync:in al,dx
    test al,8
    jz WaitVerticalSync
WaitNotVerticalSync:in al,dx
    test al,8
    jnz WaitNotVerticalSync
    mov cx,SCREENSIZE/4
    xor si,si
    xor di,di
    push VGA_SEGMENT
    pop es
    rep movs dword ptr es:[di],fs:[si]
;-----------------------------------
    mov es,cx;es=0
    mov ax,es:[41Ah];указатель на логическое начало буфера клавиатуры
    sub ax,es:[41Ch];указатель на логический конец буфера клавиатуры
    jz a1;если АХ не равно нулю, значит на клавиатуру нажали
exit:   mov ax,3
    int 10h     ; - VIDEO - SET VIDEO MODE
    int 20h
end start
Теперь применим наши знания на практике, красный оттенок будут меняться пилообразно от 0 до 63 и от 63 до 0
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
        mov cx,255
m0:     mov dx,3C8h
        mov al,cl
        mov ah,cl
        test al,40h
        jz @f
        not ah
@@:     out dx,ax
        inc dx
              mov al,0
              out dx,al
              out dx,al
        dec cx
          jnz m0
пусть "лава" заполнит не весь экран, а ограниченную область 64х120,
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
; masm dos com #
.model tiny
.code
.386
org 100h 
SCREEN_WIDTH    equ 320
SCREEN_SQR  equ 64
 
start:      mov ax,13h
        int 10h
        push 0A000h
        pop es
        push 0
        pop ds
        mov cx,255
m0:     mov dx,3C8h
        mov al,cl
        mov ah,cl
        test al,40h
        jz @f
        not ah
@@:     out dx,ax
        inc dx
              mov al,0
              out dx,al
              out dx,al
        dec cx
          jnz m0
M2:           mov di,140;начало квадрата по формуле di = y * 320 + x
              mov cx,120;высота
M4:           push cx
              mov cl,SCREEN_SQR;ширина
@@:           push cx
              xor dx,dx
              movzx ax,byte ptr es:[di-1]
              add al,es:[di+1]
              adc ah,0
              add al,es:[di-SCREEN_WIDTH]
              adc ah,0
              add al,es:[di+SCREEN_WIDTH]
              adc ah,0
              shr ax,2
              add ax,2
              stosb
              pop cx
              loop @b
              add di,SCREEN_WIDTH-SCREEN_SQR
              pop cx
              loop M4
              mov ax,ds:[41Ah]
          sub ax,ds:[41Ch]
              jz M2
              mov ax,3
              int 10h
              ret ;выход из программы
end start
наслаждаемся результатом
стоит уменьшить или увеличить величину SCREEN_WIDTH-SCREEN_SQR на единичку и мы получаем из прямоугольника параллелограмм
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
; masm dos com #
.model tiny
.code
.386
org 100h 
SCREEN_WIDTH    equ 320
SCREEN_SQR  equ 64
 
start:      mov ax,13h
        int 10h
        push 0A000h
        pop es
        push 0
        pop ds
        mov cx,255
m0:     mov dx,3C8h
        mov al,cl
        mov ah,cl
        test al,40h
        jz @f
        not ah
@@:     out dx,ax
        inc dx
              mov al,0
              out dx,al
              out dx,al
        dec cx
          jnz m0
M2:           mov di,140;начало квадрата по формуле di = y * 320 + x
              mov cx,120;высота
M4:           push cx
              mov cl,SCREEN_SQR;ширина
@@:           push cx
              xor dx,dx
              movzx ax,byte ptr es:[di-1]
              add al,es:[di+1]
              adc ah,0
              add al,es:[di-SCREEN_WIDTH]
              adc ah,0
              add al,es:[di+SCREEN_WIDTH]
              adc ah,0
              shr ax,2
              add ax,2
              stosb
              pop cx
              loop @b
              add di,SCREEN_WIDTH-SCREEN_SQR + 1
              pop cx
              loop M4
              mov ax,ds:[41Ah]
          sub ax,ds:[41Ch]
              jz M2
              mov ax,3
              int 10h
              ret; выход из программы
end start
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
; masm dos com #
.model tiny
.code
.386
org 100h 
SCREEN_WIDTH    equ 320
SCREEN_SQR  equ 64
 
start:      mov ax,13h
        int 10h
        push 0A000h
        pop ds
        push 0
        pop es
        mov cx,255
m0:     mov dx,3C8h
        mov al,cl
        mov ah,cl
        test al,40h
        jz @f
        not ah
@@:     out dx,ax
        inc dx
              mov al,0
              out dx,al
              out dx,al
        dec cx
          jnz m0
M2:             mov si,1
        mov bx,340;начало квадрата по формуле bx = y * 320 + x  y=1 x=20
        mov bp,257;SCREEN_WIDTH-SCREEN_SQR + 1
        call crest
        neg si
        mov bp,383; 1*320 + 63
        mov bx,604;=1*320 + 284
                call crest
              
              mov ax,es:[41Ah]
          sub ax,es:[41Ch]
              jz M2   ;;проверяем не нажата ли клавиша
              mov ax,3;устанавливаем текстовый режим
              int 10h
              ret   ;и выходим из программы                         
crest proc
              mov cx,200;высота
M4:           push cx
              mov cl,SCREEN_SQR;ширина
@@:           push cx
              movzx ax,byte ptr [bx-1]
              add al,[bx+1]
              adc ah,0
              add al,[bx-SCREEN_WIDTH]
              adc ah,0
              add al,[bx+SCREEN_WIDTH]
              adc ah,0
              shr ax,2
              add ax,2
              mov [bx],al
        add bx,si
              pop cx
              loop @b
              add bx,bp
              pop cx
        loop M4
        ret
crest endp
end start
а теперь заглянем сюда
________________________________________
Графика DOS. Блюр-эффект.
Довольно простой эффект, как раз для начинающих. К тому же, этот эффект лежит в основе целого семейства эффектов, и знать его необходимо!

Blur, в переводе с английского: неясные очертания, размытое пятно.

Допустим, у нас есть некое изображение размером 320x200x8bit, в буфере экрана screen. Создаем дополнительный буфер экрана, buffer.

Цвет точки с координатами X,Y в массиве buffer будет рассчитываться по формуле: цвет_точки = среднее арифметическое от значений цвета точек, окружающих точку с координатами X,Y в массиве screen.
После применения данной формулы ко всем точкам массива buffer, в этом массиве будет находиться сBlur-енное изображение.
Некоторые замечания:
  1. Естественно, палитра должна содержать плавные переходы от цвета к цвету, иначе вместо сBlur-енного изображения получим невесть что .
  2. Если это условие не выполняется, то надо преобразовать исходное изображение в Hi или TrueColor и производить размыв изображения отдельно для каждого цвета.
  3. При получении размытого изображения значения цвета на границах buffer не рассчитывается, иначе нам бы пришлось бы залезть за границы массива screen.


Пример:
C
1
2
3
4
5
6
for(short y=1;y<199;y++)
  for(short x=1;x<319;x++)
    buffer[x][y]=(screen[x-1][y+1]+screen[x-1][y]+
                  screen[x-1][y-1]+screen[x][y+1]+
                  screen[x][y-1]+screen[x+1][y+1]+
                  screen[x+1][y]+screen[x+1][y-1])/8;
В данном примере мы усредняем значение цвета 8 точек, в принципе выбор точек, по которым мы находим усредненное значение цвета, за вами. Например, для более высокой скорости можно выбрать только 4 близлежащих точки:
C
1
2
    buffer[x][y]=(screen[x-1][y]+screen[x][y+1]+
                  screen[x][y-1]+screen[x+1][y])/4;
Но тогда появляется неприятный побочный эффект: в местах плавных переходов цветов появляется решетка... В общем, не знаю как объяснить, попробуйте и увидите сами.

Как от этого избавиться? В true блюре - никак. Но есть вариант блюра, в котором этот эффект не появляется:
C
1
2
    screen[x][y]=(screen[x-1][y]+screen[x][y+1]+
                  screen[x][y-1]+screen[x+1][y])/4;
Относительно true блюра имеем:

Преимущества:
  1. Скорость
  2. Отпадает необходимость использования дополнительного frame-буфера.
Недостаток:
Искажение геометрических соотношений изображения.

Обычно такой вариант Blur`a используется для улучшения качества картинки, на различных стадиях вывода изображения.
Взято здесь, текст программы немного переделан, в результате СОМ-файл уменьшился с 498 байт до 408 байт.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
; masm dos com #
.model tiny
.code
.686p
.mmx
WIDTH_SCREEN    equ 320
HEIGHT_SCREEN   equ 200
SCREENSIZE  equ WIDTH_SCREEN*HEIGHT_SCREEN
MEMBLOCKSIZE    equ SCREENSIZE/16
IMPUT_STATUS_0  equ 3DAh    ;регистр статуса ввода 0
VGA_SEGMENT equ 0A000h
screen      equ word ptr [bp-2]
buffer          equ word ptr [bp-4]
org 100h
start:  mov sp,0B00h      ; ADJUST MEMORY BLOCK SIZE (SETBLOCK)
    mov ah,4Ah        ; ES = segment address of block   to change
    mov bx,0B0h       ; BX = new size   in paragraphs
    int 21h
    enter 4,0       
    mov ax,13h
    int 10h
;setcolortable-------------------------------
;   FOR(CL=0;CL<=63;CL++){
;       setrgbpalette(CL,CL,0,0);
;       setrgbpalette(CL+64,63,CL,0);
;       setrgbpalette(CL+128,63,63,CL);
;       setrgbpalette(CL+192,63,63,63);
    mov cx,63
@@: push    cx
    push    cx
    push    0
    push    0
    call    setrgbpalette
    mov al, cl
    add al,64
    push    ax
    push    63
    push    cx
    push    0
    call    setrgbpalette
    mov al,cl
    add al,128
    push    ax
    push    63
    push    63
    push    cx
    call    setrgbpalette
    mov al,cl
    add al,192
    push    ax
    push    63
    push    63
    push    63
    call    setrgbpalette
    dec cx
    jns @b
;setcolortable--------------------------------
    mov bx, MEMBLOCKSIZE
    mov ah,48h ; ALLOCATE MEMORY
    int 21h    ; BX = number of 16-byte paragraphs desired
    mov screen, ax
    mov bx, MEMBLOCKSIZE
        mov ah,48h ; ALLOCATE MEMORY
    int 21h    ; BX = number of 16-byte paragraphs desired
    mov buffer, ax
;CLEARBLOCK------------------------------------------
    mov es, ax
    xor eax, eax
    xor di, di
    mov cx,SCREENSIZE/4
    rep stosd
;fillscreen-------------------------------------------
    mov es, screen
    mov di,WIDTH_SCREEN+1
    mov si,HEIGHT_SCREEN-2
    or  ax,-1
@@: mov cx,WIDTH_SCREEN/2 - 1
    rep stosw
    add di,2
    dec si
    jnz @b
;trueblurscreen----------------------------------------
;   ES=screen;
;   FS=buffer;
;   DI=321;
;   FOR(SI=1;SI<199;SI++){
;       FOR(BX=1;BX<319;BX++,DI++){
;           FSBYTE[DI]=int ESBYTE[DI-321]+ESBYTE[DI-320]+ESBYTE[DI-319]+
;               ESBYTE[DI-1]+ESBYTE[DI+1]+ESBYTE[DI+319]+ESBYTE[DI+320]+
;               ESBYTE[DI+321]>>3;
a0: mov es,screen
    mov fs,buffer
    mov di, WIDTH_SCREEN+1
    mov si,HEIGHT_SCREEN-2
a1: mov bx,WIDTH_SCREEN-2 
@@: movzx   ax, byte ptr es:[di-WIDTH_SCREEN-1]
    movzx   cx, byte ptr es:[di-WIDTH_SCREEN]
    add ax, cx
    movzx   cx, byte ptr es:[di-WIDTH_SCREEN+1]
    add ax, cx
    movzx   cx, byte ptr es:[di-1]
    add ax, cx
    movzx   cx, byte ptr es:[di+1]
    add ax, cx
    movzx   cx, byte ptr es:[di+WIDTH_SCREEN-1]
    add ax, cx
    movzx   cx, byte ptr es:[di+WIDTH_SCREEN]
    add ax, cx
    movzx   cx, byte ptr es:[di+WIDTH_SCREEN+1]
    add ax, cx
    sar ax, 3
    mov fs:[di], al
    inc di
        dec bx
    jnz @b
    add di,2
    dec si
    jnz a1
;copybufferscreen---------------------------
    push ds
    mov ds,buffer
    xor di,di
    mov cx,SCREENSIZE/4
    rep movsd
    pop ds
    call    copyvirtualscreen
    jz  a0
    mov ah, 0
    int 16h     ; Return: AH = scan code, AL = character
;fillscreen------------------------------------------------
    mov es, screen
    mov di,WIDTH_SCREEN+1
    mov si,HEIGHT_SCREEN-2
    or  ax,-1
@@: mov cx,WIDTH_SCREEN/2 - 1
    rep stosw
    add di,2
    dec si
    jnz @b
a2:;fastblurscreen
;ES=screen;
;   DI=321;
;   FOR(SI=1;SI<199;SI++){
;       FOR(BX=1;BX<319;BX++,DI++)
;ES:[DI]=(ES:[DI-320]+ES:[DI-1]+ES:[DI+1]+ES:[DI+320])/4
    mov es,screen
    mov di,WIDTH_SCREEN+1
    mov si,HEIGHT_SCREEN-2
a3: mov bx,WIDTH_SCREEN-2
@@: movzx   ax,byte ptr es:[di-WIDTH_SCREEN]
    movzx   cx,byte ptr es:[di-1]
    add ax,cx
    movzx   cx,byte ptr es:[di+1]
    add ax,cx
    movzx   cx,byte ptr es:[di+WIDTH_SCREEN]
    add ax,cx
    sar ax,2
    mov es:[di], al
    inc di
    dec bx
    jnz @b
    add di,2
    dec si
    jnz a3
;fastblurscreen---------------------------------
    call    copyvirtualscreen
    jz  a2
    mov ax,3
    int 10h     ; - VIDEO - SET VIDEO MODE
    mov ah,0
    int 16h     ; KEYBOARD - READ CHAR FROM BUFFER, WAIT IF EMPTY
    int 20h 
 
 
setrgbpalette   proc 
 
blue        equ byte ptr  [bp+4]
green       equ byte ptr  [bp+6]
red     equ byte ptr  [bp+8]
color       equ byte ptr  [bp+0Ah]
 
    push    bp
    mov bp, sp
    mov dx, 3C8h
    mov al, color
    mov ah, red
    out dx, ax
    inc dx
    mov al, green
    out dx, al
    mov al, blue
    out dx, al
    pop bp
    retn    8
setrgbpalette   endp
 
copyvirtualscreen   proc 
    mov dx,IMPUT_STATUS_0
WaitVerticalSync:in al,dx
    test al,8
    jz WaitVerticalSync
WaitNotVerticalSync:in al,dx
    test al,8
    jnz WaitNotVerticalSync
    mov cx,SCREENSIZE/4
    xor si,si
    push    ds
    mov ds,screen
    xor di,di
    push    VGA_SEGMENT
    pop es
    rep movsd
    xor ax, ax
    mov ds, ax
    mov ax, ds:[41Ah]
    sub ax, ds:[41Ch]
    pop ds
    retn
copyvirtualscreen   endp
end start
Графика DOS. Anis-эффект.
Как скролить (вправо-влево, вверх-вниз), надеюсь, вы знаете. Что такое полярные координаты, наверно то же. Так вот этот эффект основан на скролинге, но в полярных координатах.
Соответственно скролинг вдоль координаты r - выглядит как втягивание-вытягивание, а изменение угла alpha - как повороты по часовой стрелке или против. Вот и вся теория. Теперь начнется практика.
Пусть центр нашего anis`a находиться в центре экрана. Тектуру которую мы хотим извращать, возьмем размером 256x256. Основное требование к этой текстуре - быть бесшовной, чтобы удобно было скроллить. Посмотрите на мою текстуру, чтобы понять о чем речь:

в не имеющих названия единицах, которых в круге 256. Теперь сопоставим углу alpha - координату x в нашей текстуре, а координате r - y нашей текстуры.

Теперь, что бы втягивать-вытягивать и вращать текстуру, достаточно изменить начальные смещения по x и y в нашей текстуры.

Реализуем наши соображения в алгоритме, он разделится на две части:

1) Инициализация.
Заведем два массива texture и anis. В первом храним нашу текстурку, а во втором (типа unsigned short, размер такой же как у экрана) будем хранить смещения текстуры. Далее в цикле проходим по всем точкам массива anis, преобразуем декартовы координаты текущей точки в полярные, по этим координатам вычисляем смещение от начала массива texture, и заносим его в текущую ячейку массива anis:
C
1
2
3
4
5
6
7
8
9
10
11
12
  long x,y,u,v,p=0;
  double angle, radius;
  for(y=-100;y<100;y++)
    for(x=-160;x<160;x++)
    {
      radius=sqrt(x*x+y*y);
      if(radius<1) radius=1;
      angle=atan2(y,x)+PI;
      v=radius;
      u=angle*128/PI;
      anis[p++]=(u&0xFF)+((v&0xFF)<<8);
    }
2) Рендеринг.

Для каждого выводимого кадра решаем насколько нам надо повернуть, втянуть-вытянуть нашу текстуру, то есть находим сдвиг по координате r и углу alpha. В цикле проходимся по массиву anis, считываем из него смещение, прибавляем вычисленное ранее сдвиг, и по этому смещению считываем пиксель из массива texture. Записываем его по текущим координатам в буфер экрана:
C
1
2
3
4
5
6
  long u=256*cos(time);
  long v=512*sin(time);
  long screen_ptr=(long)&screen;
  long i=0;
  for(long k=0;k<64000;k++)
    *((char*)screen_ptr++)=texture[(anis[i++]+u+(v<<8))&0xFFFF];
Взято здесь, текст программы немного переделан, в результате СОМ-файл уменьшился с 668 байт до 428 байт
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
; masm dos com #
.286
.model tiny
.code
.386
org 100h
WIDTH_SREEN equ 320
HEIGHT_SCREEN   equ 200
SCREENSIZE  equ WIDTH_SREEN*HEIGHT_SCREEN
MEMBLOCKSIZE    equ SCREENSIZE/16
IMPUT_STATUS_0  equ 3DAh    ;регистр статуса ввода 0
VGA_SEGMENT equ 0A000h
 
u       equ dword ptr [bp-4]
v       equ dword ptr [bp-8]
x       equ dword ptr [bp-12]
y       equ dword ptr [bp-16]
x2      equ dword ptr [bp-20];x2=x*x
radius      equ dword ptr [bp-24]
anis        equ word ptr [bp-26]
texture     equ word ptr [bp-28]
screen      equ word ptr [bp-30]
 
start:  mov sp,0FB0h
    mov ah,4Ah ; ADJUST MEMORY BLOCK SIZE (SETBLOCK)
    mov bx,0FBh; BX = new size  in paragraphs
    int 21h; ES = segment address of block to change                    
    enter 30,0
    mov bx,MEMBLOCKSIZE 
    mov ah,48h ; ALLOCATE MEMORY
    int 21h ; BX = number of 16-byte paragraphs desired
    mov screen,ax
    mov bx,1000h
    mov ah,48h ; ALLOCATE MEMORY
    int 21h ; BX = number of 16-byte paragraphs desired
    mov texture,ax
    mov bx,MEMBLOCKSIZE*2 
    mov ah,48h ; ALLOCATE MEMORY
    int 21h ; BX = number of 16-byte paragraphs desired
    mov fs,ax;
    mov anis,ax
    mov ax,13h
    int 10h     ; - VIDEO - SET VIDEO MODE
;read bmp-file--------------------------------------------------------------
    mov dx,offset filename; DS:DX-> ASCIZ filename "texture.bmp"
    mov ax,3D00h     ; OPEN FILE FOR READ
    int 21h                 
    mov bx,ax 
    xor cx,cx
    mov dx,54;Первые 54 байта - заголовок, в котором  хранится разнообразная информация о картинке.
    mov ax,4200h; MOVE FILE READ/WRITE POINTER (LSEEK)
    int 21h ; AL = method: offset from beginning of file
;Эта информация нам не нужна, так как мы и так знаем какую картинку считываем.  
;Если наша картинка 8-битная то следующие 1024 байта занимает палитра нашей картинки.                       
    mov dx,offset pal
    mov cx,1024
    mov ah,3Fh         ; READ FROM FILE
    int 21h     ; BX = file handle, CX = number of bytes to read
    mov di,1023
@@: shr pal[di],2
    dec di
    jns @b
    push ds
    mov ds,texture; word ptr ds:39Eh
    mov dx,0FF00h
@@:     mov cx,256  ;dx=y*256
    mov ah,3Fh  ; READ FROM FILE 
    int 21h ; BX = file handle, CX = number of bytes to read
    sub dh,1
    jnc @b
    pop ds
    mov ah,3Eh ; CLOSE A FILE WITH HANDLE
    int 21h    ; BX = file handle
;init_anis-----------------------------------------------
;Заведем два массива texture и anis. В первом храним нашу текстурку, а во втором
;(типа unsigned short, размер такой же как у экрана) будем хранить смещения текс
;туры. Далее в цикле проходим по всем точкам массива anis, преобразуем декартовы
;координаты текущей точки в полярные, по этим координатам вычисляем смещение от
;начала массива texture, и заносим его в текущую ячейку массива anis
    xor di, di
    mov es,anis 
    mov y,-HEIGHT_SCREEN/2
a0: mov x,-WIDTH_SREEN/2
a1: mov eax,x
    imul eax
    mov x2,eax;x2=x*x
    mov eax,y
    imul eax
    add eax,x2
    mov radius,eax;radius=y*y+x2
    fild radius
    fsqrt
    fistp radius;radius=sqrt(x*x+y*y)
    cmp radius,1;if (radius < 1) radius = 1;
    jge @f
    mov radius,1
@@: fild x
    fild y
    fpatan
    fldpi
    faddp st(1), st   ;angle=atan2(y,x)+PI;
    fmul const_128_pi
    fistp x2          ;u=angle*128/PI;
    mov ah,byte ptr radius
    mov al,byte ptr x2
    stosw;anis[p++]=(u&0xFF)+((v&0xFF)<<8);
    test di,di
    jnz @f
    mov ax,es
    add ax,1000h
    mov es,ax
@@: inc x
    cmp x,WIDTH_SREEN/2
    jl a1
    inc y
    cmp y,HEIGHT_SCREEN/2
    jl a0
;set color table----------------------------------
    xor bx,bx;mov bx,255*4
    xor ax,ax
    mov cx,256
;set rgb palette
    mov dx,3C8h
    out dx,al
    inc dx
@@: mov al,pal[bx+2];красный
    out dx,al
    mov al,pal[bx+1];зеленый
    out dx,al
    mov al,pal[bx];синий
    out dx,al
    add bx,4
    loop @b
;-------------------------------------------------
;Рендеринг
;Для  каждого выводимого кадра решаем насколько нам надо повернуть, втянуть-
;вытянуть нашу текстуру, то есть находим сдвиг по координате r и углу alpha. В 
;цикле проходимся по массиву anis, считываем из него смещение, прибавляем  
;вычисленный ранее сдвиг, и по этому смещению считываем пиксель из массива 
;texture. Записываем его по текущим координатам в буфер экрана:
        fldz; time=0
@@: fld st
    fsincos
    fmul    const_512
    fistp   v        ;long v=512*sin(time);
    fmul    const_256
    fistp   u        ;long u=256*cos(time);
    shl v,8      ;long screen_ptr=(long)&screen;
    xor bx,bx ;long i=0;
    mov es,screen;for(long k=0;k<64000;k++)
    mov fs,anis  ;*((char*)screen_ptr++)=texture[(anis[i++]+u+(v<<8))&0xFFFF];
    mov gs,texture 
    mov cx,SCREENSIZE
    xor di,di
a2: mov si,fs:[bx]
    add si,word ptr u
    add si,word ptr v
    movs byte ptr es:[di],gs:[si]
    inc bx
    inc bx
    jnz a3
    mov ax,fs
    add ax,1000h
    mov fs,ax
a3: loop a2
;copy virtual screen
    mov dx,IMPUT_STATUS_0
WaitVerticalSync:in al,dx
    test al,8
    jz WaitVerticalSync
WaitNotVerticalSync:in al,dx
    test al,8
    jnz WaitNotVerticalSync
    push VGA_SEGMENT
    pop es
    xor di,di
    xor si,si
    fadd const_001;time+=0.01
    push ds
    mov ds,screen
    mov cx,SCREENSIZE/4
    rep movsd
    push 0
    pop ds
    mov ax,ds:[41Ah]
    sub ax,ds:[41Ch]
    pop ds
    jz @b
@@: mov ax,3
    int 10h     ; - VIDEO - SET VIDEO MODE
    int 20h
const_001   dd 0.01     
const_128_pi    dd 40.743665431525205956834243423364;128/3.14
const_256   dd 256.0
const_512   dd 512.0
filename    db 'texture.bmp'    
pal     db 0
end start
Графика DOS. Светлячки.
На самом деле этот эффект называется blow ("раздувание").
Установим палитру так: 0 http://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow 63 от черного цвета к зеленому оттенку.

Наши светлячки - обыкновенные спрайты, точнее, один спрайт размером 256x256. Назовем массив, в котором будет храниться этот спрайт, blow.
Формула по которой мы будем рисовать выглядит так:
color=max_radius2/(radius2+max_radius), где max_radius - это требуемый радиус огонька, radius - радиус текущей точки.
Сущность этой формулы: смягченная обратная зависимость яркости от радиуса, квадраты радиусов используются из-за того, что вычислить квадрат радиуса текущей точки намного легче, чем просто радиус (не надо извлекать корень). Максимальное значение цвета ограниченно числом 63.

Процедура инициализации будет выглядеть так:
C
1
2
3
4
5
6
7
8
9
10
11
void initblow()
{
  long c;
  for(long y=0;y<256;y++)
    for(long x=0;x<256;x++)
    {
      c=(16384/((x-128)*(x-128)+(y-128)*(y-128)+128));
      if(c>63) c=63;
      blow[x+(y<<8)]=c;
    }
}
Выводя спрайт на экран, мы обязательно столкнемся с необходимостью clliping`a, то есть, отсечения кусков спрайта, вылезающих за границы экрана. Эта проблема решается довольно просто. Допустим, что мы начинаем выводить спрайт на экран с точки, координаты которой dx,dy.
Тогда процедура вывода спрайта будет выглядеть так:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void drawblow(long dx,long dy)
{
  long c,s;
  long minx=0;
  if(dx<0) minx=-dx;
  long miny=0;
  if(dy<0) miny=-dy;
  long maxx=320-dx;
  if(maxx>255) maxx=255;
  long maxy=200-dy;
  if(maxy>255) maxy=255;
  for(long y=miny;y<maxy;y++)
    for(long x=minx;x<maxx;x++)
    {
      s=(x+dx)+((y+dy)<<6)+((y+dy)<<8);
      c=screen[s]+blow[x+(y<<8)];
      if(c>63) c=63;
      screen[s]=c;
    }
}
Идея эффекта заключается в том, что при выводе точки спрайта на экран мы суммируем цвет точки экрана и спрайта, и результат записываем в экранную точку. Естественно, если сумма > 63, то результат должен быть равен 63, иначе мы вылезем за границу используемой части палитры. Собственно сам эффект получается автоматически, самое сложным было выдумать формулу.
Взято здесь, текст программы немного переделан, в результате СОМ-файл уменьшился с 912 байт до 740 байт
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
; masm dos com #
.286
.model tiny
.code
.686p
.mmx
WIDTH_SREEN equ 320
HEIGHT_SCREEN   equ 200
SCREENSIZE  equ WIDTH_SREEN*HEIGHT_SCREEN
MEMBLOCKSIZE    equ SCREENSIZE/16
IMPUT_STATUS_0  equ 3DAh
VGA_SEGMENT equ 0A000h
org 100h
start:
x1      equ word ptr [bp-16]
y1      equ word ptr [bp-14]
x       equ dword ptr [bp-12]
y       equ dword ptr [bp-8]
time        equ dword ptr [bp-4]
 
        mov sp, 0CA0h
        mov ah, 4Ah; ADJUST MEMORY BLOCK SIZE (SETBLOCK)
        mov bx, 0CAh; ES = segment address of block to change
        int 21h; BX = new size in paragraphs                    
        enter   10h, 0
;initblow----------------------------------------
        mov bx, 0FA0h
        mov ah, 48h; ALLOCATE MEMORY
        int 21h; BX = number of 16-byte paragraphs desired
        mov screen, ax
        mov bx, 1000h
        mov ah, 48h; ALLOCATE MEMORY
        int 21h; BX = number of 16-byte paragraphs desired
        mov fs,ax; адрес буфера спрайта
        mov es, ax
        mov y1,-128 
a0:     mov di,y1
        imul    di,di
        mov x1,-128
a1:     mov bx, x1
        imul    bx,bx
        lea bx,[bx+di+256]
        mov ax,16384 ;4000h
        xor dx, dx
        div bx
        cmp al,63
        jbe @f
        mov al,63
@@:     mov si, y1
        add si, 128
        shl si, 8
        add si, x1
        mov es:[si+128], al
        inc x1
                cmp x1,128
        jl  a1
        inc y1
        cmp y1,128
        jl  a0
;initblow----------------------------------------
        mov ax, 13h
        int 10h     ; - VIDEO - SET VIDEO MODE
;setcolortable--------------------------------------------------------
        mov cx,64;3
        xor ax,ax
        xor bx,bx
        mov dx,3C8h
        out dx,al;color
        inc dx
@@:             mov al,0 ;red
        out dx,al
        mov al,bl;green
        out dx,al
        mov al,0;blue
        out dx,al
        inc bx
        loop @b
;setcolortable--------------------------------------------------------
        fldz;time=0
begin:;clearvirtualscreen--------------------------------------
        mov es,screen
        mov cx,SCREENSIZE/4;16000
        xor eax, eax
        xor di, di
        rep stosd
;main-----------------------------------
        fld st
        fmul    const05
        fadd    const1
        fsin
        fmul    const92
        fistp   x
        add x,32;x=32+92*sin(time*0.5+1.0);
        fld time
        fmul    const1_5
        fadd    const2
        fcos
        fmul    const58
        fistp   y
        sub y,28;y=-28+58*cos(time*1.5+2.0);
        push    word ptr x
        push    word ptr y
        call    drawblow
        fld time
        fadd    const1_5
        fsin
        fmul    const92
        fistp   x
        add x, 32;x=32+92*sin(time+1.5);
        fld time
        fmul    const2
        fadd    const2_5
        fcos
        fmul    const58
        fistp   y
        sub y,28;y=-28+58*cos(time*2.0+2.5);
        push    word ptr x
        push    word ptr y
        call    drawblow
        fld time
        fmul    const1_5
        fadd    const2
        fsin
        fmul    const92
        fistp   x
        add x, 32;x=32+92*sin(time*1.5+2.0);
        fld time
        fmul    const2_5
        fadd    const3
        fcos
        fmul    const58
        fistp   y
        sub y,28;y=-28+58*cos(time*2.5+3.0);
        push    word ptr x
        push    word ptr y
        call    drawblow
        fld time
        fmul    const2
        fadd    const2_5
        fsin
        fmul    const92
        fistp   x
        add x, 32;x=32+92*sin(time*2.0+2.5);
        fld time
        fmul    const3
        fadd    const05
        fcos
        fmul    const58
        fistp   y
        sub y,28;y=-28+58*cos(time*3.0+0.5);
        push    word ptr x
        push    word ptr y
        call    drawblow
        fld time
        fmul    const2_5
        fadd    const3
        fsin
        fmul    const92
        fistp   x
        add x,32;x=32+92*sin(time*2.5+3.0);
        fld time
        fmul    const05
        fadd    const1
        fcos
        fmul    const58
        fistp   y
        sub y,28;y=-28+58*cos(time*0.5+1.0);
        push    word ptr x
        push    word ptr y
        call    drawblow
        fld time
        fmul    const3
        fadd    const05
        fsin
        fmul    const92
        fistp   x
        add x,32;x=32+92*sin(time*3.0+0.5);
        fld time
        fadd    const1_5
        fcos
        fmul    const58
        fistp   y
        sub y,28;y=-28+58*cos(time*1.0+1.5);
        push word ptr x
        push word ptr y
        call    drawblow
        fadd    const002
        fst time
;copyvirtualscreen-----------------------------------------
        mov dx,IMPUT_STATUS_0
WaitVerticalSync:in al,dx
            test al,8
            jz WaitVerticalSync
WaitNotVerticalSync:in al,dx
            test al,8
            jnz WaitNotVerticalSync
        mov cx,SCREENSIZE/4
        xor si,si
        push ds
        mov ds,screen
        xor di,di
        push VGA_SEGMENT
        pop es
        rep movsd
                xor ax,ax
        mov ds,ax
        mov ax,ds:[41Ah]
        sub ax,ds:[41Ch]
        pop ds
        jz begin
exit:       mov ax,3
        int 10h ; - VIDEO - SET VIDEO MODE
        int 20h
 
drawblow    proc 
 
x2      equ word ptr [bp-8]
y2      equ word ptr [bp-6]
maxX        equ word ptr [bp-4]
maxY        equ word ptr [bp-2]
inY     equ word ptr [bp+4]
inX     equ word ptr [bp+6]
 
        enter 8,0
        mov x2,0
        mov y2,0
        mov ax,inX     ;IF(dx<0)x=-dx;
        test ax,ax
        jns @f
        neg ax
        mov x2,ax
@@:     mov ax,inY     ;IF(dy<0)y=-dy;
        test ax,ax
        jns @f
        neg ax
        mov y2,ax
@@:     mov ax,WIDTH_SREEN
        sub ax,inX     ;AX=320-dx
        cmp ax,255
        jbe @f
        mov ax,255           ;IF(AX>255)AX=255;
@@:     mov maxX,ax    ;maxx=AX;
        mov ax,HEIGHT_SCREEN
        sub ax,inY     ;AX=200-dy;
        cmp ax,255
        jbe @f
        mov ax,255           ;IF(AX>255)AX=255;
@@:     mov maxY,ax     ;maxy=AX;
        mov es,screen
a2:     mov bx,y2       ;BX=(y+dy)*320;
        add bx,inY
        imul bx,WIDTH_SREEN
        mov si,x2       ;FOR(DI=x;DI<maxx;DI++){
a3:     lea di,[si+bx]
        add di,inX
        push di
        mov al,es:[di]
        imul di,y2,256
        add di,si
        add al,fs:[di]
        cmp al,63
        jbe @f
        mov al,63
@@:     pop di
        stosb
        inc si
        cmp maxX,si
        jg a3
        inc y2
        mov ax, maxY
        cmp y2, ax
        jb a2
        leave
        retn 4
drawblow    endp
 
const002    dd 0.02
screen dw ?; адрес буфера экрана
const05 dd 0.5
const1  dd 1.0
const1_5 dd 1.5
const2  dd 2.0
const2_5 dd 2.5
const3  dd 3.0
const58 dd 58.0
const92 dd 92.0
end start
Графика DOS. Эффект пламя.
В сущности, пламя, это тот же эффект Blur, изменений совсем немного.
Допустим, мы хотим изобразить самый простой вариант: Пламя поднимается снизу экрана и постепенно гаснет. Наша формула true-blur`а изменится совсем чуть-чуть:
C
1
2
3
4
    buffer[x][y+1]=(screen[x-1][y+1]+screen[x-1][y]+
                    screen[x-1][y-1]+screen[x][y+1]+
                    screen[x][y-1]+screen[x+1][y+1]+
                    screen[x+1][y]+screen[x+1][y-1])/8;
Уяснили? Для того, чтобы пламя красиво гасло, установим палитру так:
Номер цвета: 0 64 128 192 255
Цвет: черныйhttp://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrowкрасный http://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrowжелтыйhttp://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrowярко-желтыйhttp://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow белый
Пример:
C
1
2
3
4
5
6
7
  for(short k=0;k<=63;k++)
  {
    setrgbpalette(k,k,0,0);
    setrgbpalette(k+64,63,k,0);
    setrgbpalette(k+128,63,63,k);
    setrgbpalette(k+192,63,63,63);
  }
Если вам кажется, что пламя слишком медленно гаснет, то формулу можно переписать вот так:
C
1
2
3
4
5
6
    short color=(screen[x-1][y+1]+screen[x-1][y]+
                 screen[x-1][y-1]+screen[x][y+1]+
                 screen[x][y-1]+screen[x+1][y+1]+
                 screen[x+1][y]+screen[x+1][y-1])/8;
    if(color>number) color-=number; else color=0;
    buffer[x][y+1]=color;
Значение number выберите по своему вкусу, чем оно больше, тем быстрее будет гаснуть пламя.

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

Добавится вот такая строка:
C
1
2
3
4
5
6
7
8
  for(x=0;x<320;x+=4)
  {
    char color=rand()%256;
    screen[x][199]=c;
    screen[x+1][199]=c;
    screen[x+2][199]=c;
    screen[x+3][199]=c;
  }
Забивая сразу четыре пикселя случайным цветом, мы получаем более красивое пламя. Естественно, эту и несколько следующих строк на экран мы не выводим. Почему? Догадайтесь сами!

Опять же, для ускорения можно использовать вариант блюра с четырьмя усредняемыми пикселями, немного его изменив:
C
1
2
  screen[x][y]=(screen[x-1][y-1]+screen[x][y-1]+
                screen[x][y-1]+screen[x+1][y-2])/4;
Вот еще одна быстрая формула:
C
1
2
3
4
5
6
7
8
9
for(short y=0;y<197;y+=2)
  for(short x=1;x<319;x++)
  {
    short high=screen[x-1][y-2]+screen[x][y-2]+screen[x+1][y-2];
    char low=screen[x][y-4];
    char pixel=(high+low)/4;
    screen[x][y]=pixel;
    screen[x][y-1]=(pixel+low)/2
  }
Взято здесь, текст программы немного переделан, в результате СОМ-файл уменьшился с 693 байт до 583 байт
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
; masm dos com #
.model tiny
.code
.386
org 100h
VGA_SEGMENT equ 0A000h
IMPUT_STATUS_0  equ 3DAh    ;регистр статуса ввода 0
WIDTH_SCREEN    equ 320
HEIGHT_SCREEN   equ 200
SCREENSIZE  equ WIDTH_SCREEN*HEIGHT_SCREEN
MEMBLOCKSIZE    equ SCREENSIZE/16
 
screen  equ word ptr [bp-2]
temp1   equ word ptr [bp-4]
temp2   equ word ptr [bp-6]
 
start:      mov sp, 0BC0h
        mov ah, 4Ah; ADJUST MEMORY BLOCK SIZE (SETBLOCK)
        mov bx, 0BCh; ES = segment address of block to change
        int 21h; BX = new size  in paragraphs
        enter 6,0
;RANDOMIZE
        push    0
        pop es
;получаем системное время, накапливаемое в 4-х байтовой ячейке с адресом 46Ch
        mov ax, es:[46Ch]
        mov temp1, ax
        mov ax, es:[46Eh]
        mov temp2, ax
        mov ax, 13h
        int 10h
;setcolortable---------------------------------------
        mov cx,63
@@:     push    cx
        push    cx
        push    0
        push    0
        call    setrgbpalette
        mov al, cl
        add al, 64
        push    ax
        push    63
        push    cx
        push    0
        call    setrgbpalette
        mov al, cl
        add al, 128
        push    ax
        push    63
        push    63
        push    cx
        call    setrgbpalette
        mov al, cl
        add al,192
        push    ax
        push    63
        push    63
        push    63
        call    setrgbpalette
        dec cx
        jns @b
;setcolortable-------------------------------------------
        mov bx, MEMBLOCKSIZE
        mov ah, 48h; ALLOCATE MEMORY
        int 21h; BX = number of 16-byte paragraphs desired
        mov screen, ax
;clearblock--------------------------------------------
        mov es, ax
        xor eax, eax
        xor di, di
        mov cx,SCREENSIZE/4
        rep stosd
        mov bx, MEMBLOCKSIZE
        mov ah, 48h; ALLOCATE MEMORY
        int 21h; BX = number of 16-byte paragraphs desired
        mov fs,ax;buffer
;clearblock
        mov es, ax
        xor eax, eax
        xor di, di
        mov cx,SCREENSIZE/4
        rep stosd
loc_10143:  xor ax, ax
        mov es, ax
        mov ax, es:[41Ah]
        sub ax, es:[41Ch]
        jnz loc_10158;нажали на клавиатуру?
        call    incfire
;trueflamescreen---------------------------------
        mov di,WIDTH_SCREEN
        xor si, si
        mov dx,HEIGHT_SCREEN-2
loc_10218:  mov bx,13Fh
loc_10220:  movzx   ax, byte ptr es:[di-WIDTH_SCREEN-1]
        movzx   cx, byte ptr es:[di-WIDTH_SCREEN]
        add ax, cx
        movzx   cx, byte ptr es:[di-WIDTH_SCREEN+1]
        add ax, cx
        movzx   cx, byte ptr es:[di-1]
        add ax, cx
        movzx   cx, byte ptr es:[di+1]
        add ax, cx
        movzx   cx, byte ptr es:[di+WIDTH_SCREEN-1]
        add ax, cx
        movzx   cx, byte ptr es:[di+WIDTH_SCREEN]
        add ax, cx
        movzx   cx, byte ptr es:[di+WIDTH_SCREEN+1]
        add ax, cx
        shr ax, 3
        test al,al
        je @f
        dec al
@@:     mov fs:[si], al
        inc di
        inc si
        dec bx
        jns loc_10220
        dec dx
        jnz loc_10218
;copybufferscreen-----------------------------------
        push    ds
        push    fs
        pop ds;buffer
        xor si, si
        xor di, di
        mov cx,SCREENSIZE/4
        rep movsd
        pop ds
        call    copyvirtualscreen
        jmp loc_10143
loc_10158:  mov ah, 0
        int 16h     ; KEYBOARD - READ CHAR FROM BUFFER, WAIT IF EMPTY
loc_1015C:  xor ax, ax
        mov es, ax
        mov ax, es:[41Ah]
        sub ax, es:[41Ch]
        jnz loc_1016E
        call    incfire
;fastflamescreen--------------------------------------
        xor di, di
        mov si, HEIGHT_SCREEN-4
loc_102B7:  mov bx, WIDTH_SCREEN-1
loc_102BF:  movzx   ax, byte ptr es:[di+WIDTH_SCREEN-1]
        movzx   cx, byte ptr es:[di+WIDTH_SCREEN]
        add ax, cx
        movzx   cx, byte ptr es:[di+WIDTH_SCREEN+1]
        add ax, cx
        movzx   cx, byte ptr es:[di+WIDTH_SCREEN*2]
        add ax, cx
        shr ax, 2
        test al,al
        je @f
        dec al
@@:     stosb
        dec bx
        jns loc_102BF
        dec si
        jns loc_102B7
;---------------------------------------------------
        call    copyvirtualscreen
        jmp short loc_1015C
loc_1016E:  mov ah, 0
        int 16h     ; KEYBOARD - READ CHAR FROM BUFFER, WAIT IF EMPTY
loc_10172:  xor ax, ax
        mov es, ax
        mov ax, es:[41Ah]
        sub ax, es:[41Ch]
        jnz short loc_10184
        call    incfire
;fastestflamescreen----------------------------------------------
        xor di, di
        mov bx, HEIGHT_SCREEN-4
loc_10300:  mov si, WIDTH_SCREEN-1
loc_10308:  movzx   ax, byte ptr es:[di+WIDTH_SCREEN*2-1]
        movzx   cx, byte ptr es:[di+WIDTH_SCREEN*2]
        add ax, cx
        movzx   cx, byte ptr es:[di+WIDTH_SCREEN*2+1]
        add ax, cx
        xchg    ax, dx
        movzx   cx, byte ptr es:[di+WIDTH_SCREEN*4]
        mov ax, cx
        add ax, dx
        shr ax, 2
        cmp al, 2
        jbe short loc_1033A
        sub al, 2
        jmp short loc_1033C
loc_1033A:  mov al, 0
loc_1033C:  stosb
        cbw
        mov ch,0
        add ax, cx
        sar ax, 1
        mov es:[di+WIDTH_SCREEN-1], al
        dec si
        jns loc_10308
        add di,WIDTH_SCREEN
        sub bx,2
        jns loc_10300
;fastestflamescreen----------------------------------------
        call    copyvirtualscreen
        jmp short loc_10172
loc_10184:  mov ax, 3
        int 10h     ; - VIDEO - SET VIDEO MODE
        mov ah, 0
        int 16h     ; KEYBOARD - READ CHAR FROM BUFFER, WAIT IF EMPTY
        int 20h
setrgbpalette   proc near       ; CODE XREF: setcolortable+Dp
                    ; setcolortable+1Ap ...
 
blue        equ byte ptr  [bp+4]
green       equ byte ptr  [bp+6]
red     equ byte ptr  [bp+8]
color       equ byte ptr  [bp+10]
 
        push    bp
        mov bp, sp
        mov dx, 3C8h
        mov al, color
        mov ah, red
        out dx, ax
        inc dx
        mov al, green
        out dx, al
        mov al, blue
        out dx, al
        pop bp
        retn    8
setrgbpalette   endp
 
incfire proc 
        mov es, screen
        mov di,WIDTH_SCREEN*(HEIGHT_SCREEN-2)
        mov cx,80
@@:             mov ax, temp2
        mov dx, temp1
        shld    ax,dx,8
        shl dx,8
        rcr ax, 1
        rcr dx, 1
        add temp1, dx
        adc ax, temp2
        add temp1,25321
        adc ax,13849
        mov temp2, ax
        mov ah, al
        push    ax
        push    ax
        pop eax
        stosd
        mov es:[di+WIDTH_SCREEN-4], eax
        loop    @b
        retn
incfire endp
 
 
copyvirtualscreen   proc 
        mov dx,IMPUT_STATUS_0
WaitVerticalSync:in al,dx
    test al,8
    jz WaitVerticalSync
WaitNotVerticalSync:in al,dx
    test al,8
    jnz WaitNotVerticalSync
        mov cx,WIDTH_SCREEN*(HEIGHT_SCREEN-4)/4
        xor si,si
        push ds
        mov ds,screen
        xor di,di
        push    VGA_SEGMENT
        pop es
        rep movsd
        pop ds
        retn
copyvirtualscreen   endp
end start
3
Mikl___
Заблокирован
Автор FAQ
25.06.2013, 15:16  [ТС] #21
Графика в DOS. Эффект RADIAL BLUR
Если вы захотите подставить какую-нибудь другую картинку, то проделайте следующую последовательность действий:
  1. нарисуйте или считайте другую двух-цветную картинку в PaintBrush
  2. сохраните ее как монохромную картинку
  3. потом как 256-цветную
  4. и вот эту 256 цветную картинку можно подставлять вместо intel.bmp.
Размер картинки естественно должен составлять 320x200, и bmp-файл не должен быть упакован, иначе у вас ничего не получиться.

intel.bmp
Идея radial blur`a проста: блюрить по кругу, то есть как в обычном случае блюрения по одной координате, но с переходом к полярным координатам.

Выберем центр нашего блюра, пусть это будет, для простоты, центр экрана. Допустим разрешение экрана 320x200. Тогда центр экрана придется на точку с координатами (160,100). Сместим начало координат в эту точку: x'=x-160; y'=y-100. Теперь рассмотрим точку М с координатами, например, (185,65), эта точка будет нашей текущей точкой, на примере ее я расскажу как выбирать вторую точку, чтобы сблюрить их цвета. Координаты в нашей новой системе координат, у этой точки будут M'(25,-35). Вектор этой точки тоже будет иметь координаты (25,-35).

Теперь надо решить, как находить вектор, по которому мы будем находить вторую точку. Есть два варианта:
  1. Изменить длину вектора нашей текущей точки так, что-бы она лежала в пределах от 1 до некоторого значения max, которое вы выберите сами, на свой вкус. (Обычно выбирают 8 чтобы использовать сдвиги).
  2. Зафиксировать длину вектора. (Опять же выбор длины остается за вами, а но можно опять выбрать 8).
Здесь используется второй вариант.
Вычислим по ней координаты вектора N, округляя до целого: N.x= 4, N.y= -6.
Тогда координаты искомой точки P будут находиться так: P(x'-N.x,y'-N.y). То есть, P(21,-29).
Теперь осталось сблюрить точки M и P, и записать получившиеся значение в точку M.
Осталось рассказать в каком порядке надо обрабатывать точки, чтобы получить правильное, радиально-сблюренное изображение.
Разобьем экран на четыре сектора:
Рассмотрим сектор #1:
Обрабатывать пиксели по координате X, надо в порядке: 1,2,3..., а по координате Y, по-строчно, в порядке: A,B,C... .
По такому же принципу надо поступать и в других секторах: обрабатывать сектор по строкам, начиная с ближней к центру блюра, а в строке тоже двигаться от центра блюра.
Естественно, линии с координатами центра блюра надо тоже обработать. Ну вот и все, загляните в прилагаемые исходники, что бы посмотреть, как можно прооптимизировать этот эффект.
Взято здесь, текст программы немного переделан, в результате СОМ-файл уменьшился с 1242 байт до 810 байт
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
; masm dos com #
.model tiny
.code
.386
org 100h
WIDTH_SCREEN    equ 320
HEIGHT_SCREEN   equ 200
SCREENSIZE  equ WIDTH_SCREEN*HEIGHT_SCREEN
MEMBLOCKSIZE    equ SCREENSIZE/16
IMPUT_STATUS_0  equ 3DAh    ;регистр статуса ввода 0
VGA_SEGMENT equ 0A000h
 
time        equ dword ptr [bp-4]
n       equ dword ptr [bp-8]
y       equ word ptr [bp-10]
vx      equ word ptr [bp-12]
p1      equ word ptr [bp-14]
screen      equ word ptr [bp-16]
pics        equ word ptr [bp-18]
bbx     equ dword ptr [bp-22]
bby     equ dword ptr [bp-26]
 
start:      mov sp,0DF0h
        mov ah,4Ah      ;ADJUST MEMORY BLOCK SIZE (SETBLOCK)
        mov bx,0DFh     ;ES = segment address of block  to change
        int 21h         ;BX = new size in paragraphs
        enter 26,0
        mov time,0
        mov bx,MEMBLOCKSIZE ;4000
        mov ah,48h      ;ALLOCATE MEMORY
        int 21h         ;BX = number of 16-byte paragraphs desired      
        mov screen,ax       ;screen[64000]
        mov bx,MEMBLOCKSIZE ;4000
                mov ah,48h      ;ALLOCATE MEMORY
        int 21h         ;BX = number of 16-byte paragraphs desired
        mov pics, ax        ;pics[64000]
        mov bx,MEMBLOCKSIZE*2
                mov ah,48h      ;ALLOCATE MEMORY
        int 21h         ;BX = number of 16-byte paragraphs desired
        mov es,ax ;es=sqr       sqr[128000]
        mov fs,ax ;fs=sqr
        add ax,4000
        mov gs,ax ;gs=sqr+64000/16
        mov ax,13h
        int 10h     
;initsqr--------------------------------------
        mov si,WIDTH_SCREEN-1
a0:     mov y,HEIGHT_SCREEN-1
@@:     mov ax,y
        mul ax
        movzx ecx,ax;ecx=y*y
        movsx eax,si
        mul eax
        add eax,ecx;eax=x*x + y*y
        mov n,eax
        fild n
        fsqrt
        fistp n
        mov eax,n;n=sqrt(x*x + y*y)
        shr eax,3
        inc eax
        mov n,eax;n=(sqrt(x*x + y*y))/8 + 1
        imul di,y,WIDTH_SCREEN
        mov ax,si
        add di,ax;di=y*320 + x
        cwd
        idiv word ptr n;ax=x/((sqrt(x*x + y*y))/8 + 1)
        mov gs:[di],al
        mov ax,y
        cwd
        idiv word ptr n;ax=y/((sqrt(x*x + y*y))/8 + 1)
        stosb   ;mov es:[di],al
        dec y
        jns @b
        dec si
        jns a0
;readbmp-----------------------------------------------------------
        mov dx, offset aIntel_bmp ; "intel.bmp"
        mov ax, 3D00h     ; OPEN DISK FILE WITH HANDLE
        int 21h; DS:DX  -> ASCIZ filename
        mov bx,ax
        xor cx,cx
        mov dx,1078;заголовок и палитра BMP-файла
        mov ax,4200h; MOVE FILE READ/WRITE POINTER (LSEEK)
        int 21h ; AL = method: offset from beginning of file                    
        push    ds
        mov ds, pics
        mov di,HEIGHT_SCREEN-1
@@:     imul    dx,di,WIDTH_SCREEN
        mov cx,WIDTH_SCREEN
        mov ah, 3Fh; READ FROM FILE WITH HANDLE
        int 21h; BX = file handle, CX = number of bytes to read, DS:DX -> buffer
        dec di
        jnz @b
        push    ds
        pop     es;es=pics  di=0
        pop ds
        mov ah, 3Eh; CLOSE A FILE WITH HANDLE
        int 21h; BX = file handle
        mov cx,64000
            inc si;si=0FFFFh+1=0
a1:     lods byte ptr es:[si];c=fgetc(in)
        dec al
        jz  @f
        or  al,-1
@@:     not al
        stosb ;if(c==1)pics[x+320*y]=255 else pics[x+320*y]=0
                loop    a1
;blurpics-----------------------------------------------------
        mov di, WIDTH_SCREEN
        mov cx,63040
@@:     movzx   ax, byte ptr es:[di-WIDTH_SCREEN]
        add al, byte ptr es:[di-1]
        adc ah, 0
        add al, byte ptr es:[di+1]
        adc ah, 0
        add al, byte ptr es:[di+WIDTH_SCREEN]
        adc ah, 0
        shr ax, 2
        stosb
        loop    @b
;setcolortable--------------------------------------------
        mov cx, 255
@@:     mov bl, 255
        sub bl, cl
        shr bl, 2
        mov dx, 3C8h
        mov al, cl;color
                mov ah, bl;red
        out dx, ax
        inc dx
        mov al,bl;green
        out dx,al
        out dx,al
        dec cx
        jns @b
;-----------------------------------------------------------
a2: fld time
    fadd    const005
    fst time; time+=0,05
    fmul    const1_1;time*1,1
    fadd    const1_5;time*1,1+1,5
    fsin                      ;sin(time*1,1+1,5)
    fmul    const120;120*sin(time*1,1+1,5)
    fistp   bbx   ;bbx=120*sin(time*1,1+1,5)
    add bbx,WIDTH_SCREEN/2;bbx=160+120*sin(time*1,1+1,5)
    fld time
    fmul    const08;time*0,8
    fadd    const1_1;1,1+time*0,8
    fcos                      ;cos(1,1+time*0,8)
    fmul    const90;90*cos(1,1+time*0,8)
    fistp   bby  ;bby=90*cos(1,1+time*0,8)
    add bby,HEIGHT_SCREEN/2;bby=100+90*cos(1,1+time*0,8) 
;100 и 160 координаты середины экрана
;copypicsvirtual        
    push ds
    mov ds,pics
    mov es,screen
    xor si,si
    xor di,di
    mov cx,SCREENSIZE/4
    rep movsd
    pop ds
;radialblurscreen
    xor dx,dx
    mov bx,word ptr bby
    dec bx         ; bx = bby - 1
    imul di,bx,WIDTH_SCREEN; di = (bby - 1)*320
    add di,word ptr bbx; di = (bby - 1)*320 + bbx
    dec di        ; di = (bby - 1)*320 + bbx - 1
a3: mov cx,word ptr bbx
@@: mov ah,0
    mov al,es:[di]
    mov p1,ax
    mov si,dx
    mov al,gs:[si]
    mov vx,ax        ; vx = gs[t]
    lods byte ptr fs:[si]
    imul ax,WIDTH_SCREEN;      ax=320*fs:[t]
    mov si,di
    add si,vx
    add si,ax
    mov ah,0
    lods byte ptr es:[si];movzx ax,byte ptr es:[si]
    add ax,p1
    shr ax,1
    stosb;mov es:[di], al
    dec di
    dec di
    inc dx
    dec cx
    jnz @b
    sub di,WIDTH_SCREEN
    add di,word ptr bbx
    mov ax,WIDTH_SCREEN
    sub ax,word ptr bbx
    add dx,ax
    dec bx
    jns a3
        mov ax,word ptr bby
        dec ax
        imul di,ax,WIDTH_SCREEN
        add di,word ptr bbx
        xor dx,dx
        mov cx,word ptr bby
a4:     mov bx,word ptr bbx
@@:     movzx   ax, byte ptr es:[di]
        mov p1,ax
        mov si,dx
        movzx   ax,byte ptr gs:[si]
        mov vx,ax
        movzx   ax,byte ptr fs:[si]
        imul ax,WIDTH_SCREEN
        mov si, di
        sub si, vx
        add si, ax
        movzx   ax, byte ptr es:[si]
        add ax, p1
        shr ax, 1
        stosb
        inc dx
        inc bx
                cmp bx,WIDTH_SCREEN
        jl  @b
        sub di,WIDTH_SCREEN*2
        add di,word ptr bbx
        mov ax,word ptr bbx
        add dx,ax
        dec cx
        jnz a4
        mov cx,word ptr bby
        imul di,cx,WIDTH_SCREEN
        add di,word ptr bbx
        dec di
        xor dx,dx
a5:     mov bx,word ptr bbx
@@:     movzx   ax, byte ptr es:[di]
        mov p1,ax
        mov si,dx
        movzx   ax, byte ptr gs:[si]
        mov vx,ax
        movzx   ax, byte ptr fs:[si]
        imul ax,WIDTH_SCREEN
        mov si, di
        add si, vx
        sub si, ax
        movzx   ax, byte ptr es:[si]
        add ax, p1
        shr ax, 1
        stosb;mov   es:[di], al
        dec     di
        dec di
        inc dx
        dec bx
        jnz @b
        add di, WIDTH_SCREEN
        add di,word ptr bbx
        mov ax, WIDTH_SCREEN
        sub ax,word ptr bbx
        add dx,ax
        inc cx
                cmp cx,HEIGHT_SCREEN
        jl  a5
        mov cx,word ptr bby
        imul    di,cx,WIDTH_SCREEN
        add di,word ptr bbx
        xor dx,dx
a6: mov bx,word ptr bbx
@@:     movzx   ax, byte ptr es:[di]
        mov p1,ax
        mov si,dx; t
        mov ah,0
        mov al,gs:[si]
        mov vx,ax
        lods    byte ptr fs:[si]
        mov dx,si
        imul ax,WIDTH_SCREEN
        mov si, di
        sub si, vx
        sub si, ax
        movzx   ax, byte ptr es:[si]
        add ax, p1
        shr ax, 1
        stosb
        inc bx
                cmp bx,WIDTH_SCREEN
        jl  @b
        mov ax,word ptr bbx
        add di,ax
        add dx,ax
        inc cx
        cmp cx,HEIGHT_SCREEN
        jl  a6
;blurscreen --------------------------------------
        mov di, WIDTH_SCREEN
        mov cx,63040
loc_104E7:  movzx   ax, byte ptr es:[di-WIDTH_SCREEN]
        add al, byte ptr es:[di-1]
        adc ah, 0
        add al, byte ptr es:[di+1]
        adc ah, 0
        add al, byte ptr es:[di+WIDTH_SCREEN]
        adc ah, 0
        shr ax, 2
        stosb
        loop    loc_104E7
;copyvirtualscreen
        mov dx, IMPUT_STATUS_0
WaitVerticalSync:in al,dx
            test al,8
            jz WaitVerticalSync
WaitNotVerticalSync:in al,dx
            test al,8
            jnz WaitNotVerticalSync
        mov cx,SCREENSIZE/4
        xor si,si
        push ds
        push es
        pop ds
        xor di,di
            push VGA_SEGMENT
        pop es
        rep movsd
        xor ax,ax
        mov ds,ax
        mov ax,ds:[41Ah]
        sub ax,ds:[41Ch]
        pop ds
        jz a2
exit:       mov ax,3
        int 10h     ; - VIDEO - SET VIDEO MODE
        int 20h
 
const005    dd 0.05
const08     dd 0.8
const1_1    dd 1.1
const1_5    dd 1.5
const90     dd 90.0
const120    dd 120.0
aIntel_bmp  db 'intel.bmp',0
end start
Исходный текст, СОМ-файл и картинка здесь
2
Mikl___
Заблокирован
Автор FAQ
25.06.2013, 15:16  [ТС] #22
Графика в DOS. Эффект след от водомерки на поверхности воды
Этап первый
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
; masm dos com #
.model tiny
.code
.386
org 100h
PALSIZE equ 300h
WIDTH_SCREEN    equ 320
HEIGHT_SCREEN   equ 200
SCREENSIZE  equ WIDTH_SCREEN*HEIGHT_SCREEN
IMPUT_STATUS_0  equ 3DAh    ;регистр статуса ввода 0
VGA_SEGMENT equ 0A000h
 
start:          mov ax, 13h;установили необходимый режим 320х200х256
        int 10h     
        mov ax, 4309h;создать палитру
        mov dx, 3D4h
        out dx, ax
        
        xor bx, bx
        mov dl, 0C8h
        mov al, 0
        out dx, al
        inc dx
 
@0:     mov al, 0
        cmp bl, 128
        jnb @f
        mov al, 127
        sub al, bl
        shr al, 1
 
@@:     out dx, al
        out dx, al
        mov cl, bl
        shr cl, 1
        mov al, 127
        sub al, cl
        shr al, 1
        out dx, al
        inc bl
        jnz @0
;-----------------------------------------
    mov ax,cs
    add ax,1000h
    mov fs,ax   ; watertable1=AX
    mov es,ax
    add ax,2000
    mov gs,ax   ; watertable2=AX+WATERTABLE PARAGRAPH SIZE
;очистить буфер
    xor di,di
    mov cx, SCREENSIZE/4    ; CX=WATERTABLESIZE
    xor eax, eax
    rep stosd       ; initialize wt1 and wt2
    push    VGA_SEGMENT
    pop es
main_loop:mov dx,IMPUT_STATUS_0
WaitVerticalSync:in al,dx; притормозить вывод на экран до следующего кадра
    test al,8
    jz WaitVerticalSync
WaitNotVerticalSync:in al,dx
    test al,8
    jnz WaitNotVerticalSync
;Вывод на экран из буфера
    xor di, di
    mov cx, SCREENSIZE/4
@@: mov ax, fs:[di]
    sub ax, fs:[di-2]   
    sar ax, 3; AX=(FS:[DI]-FS:[DI-2])/8
    add al, HEIGHT_SCREEN/2
    stosb
    stosb
    loop    @b  
;Эффект воды
    mov di,WIDTH_SCREEN+2
    mov cx,SCREENSIZE/4 - WIDTH_SCREEN
@@: mov ax,fs:[di-WIDTH_SCREEN]
    add ax,fs:[di-2]
    add ax,fs:[di+2]
    add ax,fs:[di+WIDTH_SCREEN] 
    sar ax,1; AX=(FS:[DI-320]+FS:[DI-2]+FS:[DI+2]+FS:[DI+320])/2
    sub ax,gs:[di]  ; AX-=GS:[DI]
    mov bx,ax
    sar bx,3
    sub ax,bx
    mov gs:[di],ax
    add     di,2
    loop    @b  
    push    gs
    push    fs
    pop gs
    pop fs  ; watertable2 <-> watertable1
;перемещение объекта
    mov bx,offset locobj
    cmp word ptr [bx+4],WIDTH_SCREEN*2
    sbb dx,dx
    or  dl,1; IF( DS:[BX+4] > 640 ) DS:[BX]--
    sub     [bx],dx; ELSE DSWORD[BX]++
    cmp word ptr [bx+6],HEIGHT_SCREEN*2
    sbb dx,dx   ; IF( DS:[BX+6] > 400 ) DS:[BX+2]--
    or  dl,1
    sub     [bx+2],dx; ELSE DS:[BX+2]++
    mov ax,[bx]
    add [bx+4],ax   ; DS:[BX+4]+=DS:[BX]
    mov ax,[bx+2]
    add [bx+6],ax   ; DS:[BX+6]+=DS:[BX+2]
    mov di,[bx+4]
    shr di,3        ; di=x-координата
    mov ax,[bx+6]
    shr ax,3
    imul    ax,WIDTH_SCREEN/2;ax=y-координата    
    add di,ax       ; di=x+y
    add di,di       ; DI*= 2
 
    mov dword ptr gs:[di],3840384h; GS=watertable1
    mov dword ptr fs:[di],1C401C4h; FS=watertable2
    add di,4
    mov ah,1
    int 16h     ;нажали на любую клавишу?
    jz  main_loop
    mov ax, 3           ;восстанавливаем текстовый режим
    int 10h     
    retn                    ;завершаем программу
locobj dw 5,0,80,40
end start
Размер COM-файла 265 байт. Во вложении исходный текст и СОМ-файл.
Этап второй
. Усложняем программу. Теперь у нас водомерка на поверхности воды и след от рыбы, которая пытается поймать водомерку
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
; masm dos com #
.model tiny
.code
.386
org 100h
PALSIZE equ 300h
WIDTH_SCREEN    equ 320
HEIGHT_SCREEN   equ 200
SCREENSIZE  equ WIDTH_SCREEN*HEIGHT_SCREEN
IMPUT_STATUS_0  equ 3DAh    ;регистр статуса ввода 0
VGA_SEGMENT equ 0A000h
palette equ byte ptr ende
 
start:          mov ax, 13h;установили необходимый режим 320х200х256
        int 10h     
        mov ax, 4309h;создать палитру
        mov dx, 3D4h
        out dx, ax
        
        xor bx, bx
        mov di,offset palette
        push    ds
        pop es
 
@0:     xor ax, ax
        cmp bl, 128
        jnb @f
        mov al, 127
        sub al, bl
        shr al, 1
        mov ah, al
@@:     stosw
        mov cl, bl
        shr cl, 1
        mov al, 127
        sub al, cl
        shr al, 1
        stosb
        inc bl
        jnz @0
        xor ax, ax
        mov cx, PALSIZE
        mov si, offset palette+3
        mov dx, 3C8h
        out dx, al
        inc dx
        rep outsb
        push    ds
        pop ax
        add ah, 10h
        mov fs,ax   ; watertable1=AX
        mov es,ax
        add ax,2000
        mov gs,ax   ; watertable2=AX+WATERTABLEPARAGRAPHSIZE
        xor di,di
        mov cx, SCREENSIZE/4    ; CX=WATERTABLESIZE
        xor eax, eax
        rep stosd       ; initialize wt1 and wt2
 
    xor si, si
main_loop:mov dx,IMPUT_STATUS_0
WaitVerticalSync:in al,dx         ; притормозить
    test al,8
    jz WaitVerticalSync
WaitNotVerticalSync:in al,dx
    test al,8
    jnz WaitNotVerticalSync
;ShowWater
    push    VGA_SEGMENT
    pop es
    xor di, di
    mov cx, SCREENSIZE/4
@@: mov ax, fs:[di]
    sub ax, fs:[di-2]   
    sar ax, 3; AX=(FS:[DI]-FS:[DI-2])/8
    add al, HEIGHT_SCREEN/2
    stosb
    stosb
    loop    @b  
;CalcWaterEffect
    mov di,WIDTH_SCREEN+2
    mov cx,(HEIGHT_SCREEN-4)*WIDTH_SCREEN/4
@@: mov ax,fs:[di-WIDTH_SCREEN]
    add ax,fs:[di-2]
    add ax,fs:[di+2]
    add ax,fs:[di+WIDTH_SCREEN] 
    sar ax,1; AX=(FS:[DI-320]+FS:[DI-2]+FS:[DI+2]+FS:[DI+320])/2
    sub ax,gs:[di]  ; AX-=GS:[DI]
    mov bx,ax
    sar bx,3
    sub ax,bx
    mov gs:[di],ax
    add     di,2
    loop    @b  
    push    gs
    push    fs
    pop gs
    pop fs  ; watertable2 <-> watertable1
 
    mov bx,offset small 
    call    CALCADR
    mov dword ptr gs:[di],3840384h; GS=watertable1
    mov dword ptr fs:[di],1C401C4h; FS=watertable2
    add di,4
    test    si,100h ; IF(SI&256)
    jz  @5
    mov bx, offset big  
    call    CALCADR
    mov cx, 5
@4: mov bx, 5
@@: mov word ptr gs:[di],384h; GS=watertable1
    mov word ptr fs:[di],1C4h; FS=watertable2
    add     di,2
    dec bx
    jnz @b  
    add di,WIDTH_SCREEN-10
    loop    @4
@5: add     si,2
    mov ah,1
    int 16h     ; KEYBOARD - CHECK BUFFER, DO NOT CLEAR
    jz  main_loop
    mov ax, 3
    int 10h     ; - VIDEO - SET VIDEO MODE
    retn
 
CALCADR proc
    cmp word ptr [bx+4],WIDTH_SCREEN*2
    sbb dx,dx
    or  dl,1; IF(DSWORD[BX+4] > XSIZE*4) DSWORD[BX]--
    sub     [bx],dx; ELSE DSWORD[BX]++
    cmp word ptr [bx+6],HEIGHT_SCREEN*2
    sbb dx,dx   ; IF(DSWORD[BX+6] > YSIZE*4) DSWORD[BX+2]--
    or  dl,1
    sub     [bx+2],dx; ELSE DSWORD[BX+2]++
    mov ax,[bx]
    add [bx+4],ax   ; DSWORD[BX+4]+=DSWORD[BX]
    mov ax,[bx+2]
    add [bx+6],ax   ; DSWORD[BX+6]+=DSWORD[BX+2]
    mov di,[bx+4]
    shr di,3        ; DI=DSWORD[BX+4]>>3
    mov ax,[bx+6]
    shr ax,3
    imul    ax,WIDTH_SCREEN/2
    add di,ax       ; DI+=AX
    add di,di       ; DI+=DI
    retn
CALCADR endp
 
small dw 5,0,80,40
big   dw -3,0,304,120
ende:
end start
Размер COM-файла 342 байт. Во вложении исходный текст и СОМ-файл.
Заключительный этап
Довольно загадочный, для непосвященных, но простой, эффект. В основе лежит, все тот же Blur. Я выбрал 8-пиксельный блюр, так как он дает более красивую картинку.

Установим палитру так:
0 http://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow 255
Синий Белый
Заведем два буфера: buf1 и buf2, и указатели на них: buf1_ptr и buf2_ptr. Важно понять, что, хоть указатель называется buf1_ptr, но он может указывать совсем не на buf1, а на buf2. Аналогично для buf2_ptr. Далее по тексту, если массив будет называться buf1_ptr, то имеется в виду тот массив, на который, в данный момент, ссылается buf1_ptr. Тоже самое подразумевается под массивом buf2_ptr.

Далее описывается последовательность, которую надо проделать, для расчета очередного кадра:
  1. Если buf1_ptr, действительно указывает на buf1, то заносим в buf1_ptr точки возмущения.
  2. Проходим по всем точкам массива buf1_ptr и buf2_ptr вот таким алгоритмом:
    2.1. Складываем значения точек окружающих текущую, в массиве buf1_ptr.
    2.2. Делим эту сумму на 4.
    2.3 Вычитаем из этой суммы значение текущей точки массива buf2_ptr.
    2.4 Если результат меньше 0, то результат равен 0.
    2.5 Записываем результат в текущую точку массива buf2_ptr.
  3. Теперь меняем местами buf1 и buf2 (собственно для этого нам и были нужны указатели):
    C
    1
    2
    3
    
      long temp_ptr=buf1_ptr;
      buf1_ptr=buf2_ptr;
      buf2_ptr=temp_ptr;
  4. Выводим на экран buf2_ptr.
В результате получим эффект поверхности воды с всплывающими и лопающимися пузырьками и бегающими водомерками.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
; masm dos com #
.model tiny
.code
.686
org 100h
VGA_SEGMENT equ 0A000h
IMPUT_STATUS_0  equ 3DAh
SCREEN_WIDTH    equ 320
SCREEN_HEIGHT   equ 200
SCREENSIZE  equ SCREEN_HEIGHT*SCREEN_WIDTH
MEMBLOCKSIZE    equ SCREENSIZE/16
yd      equ dword ptr [bp-4]
xd      equ dword ptr [bp-8]
buf1        equ word ptr [bp-10]
buf2        equ word ptr [bp-12]
buf1_ptr    equ word ptr [bp-14]
buf2_ptr    equ word ptr [bp-16]
temp1       equ word ptr [bp-18]
temp2       equ word ptr [bp-20]
        
start:      mov sp, 1260h
        mov ah, 4Ah; ADJUST MEMORY BLOCK SIZE (SETBLOCK)
        mov bx, 126h; ES = segment address of block to change
        int 21h; BX = new size  in paragraphs
        enter   20,0
        mov bx, MEMBLOCKSIZE
        mov ah, 48h; ALLOCATE MEMORY
        int 21h; BX = number of 16-byte paragraphs desired
        mov buf1, ax
        mov buf1_ptr, ax
        mov es,ax
        xor eax,eax
        xor di, di
        mov cx,SCREENSIZE/4
        rep stosd
        rdtsc
        mov dword ptr temp1, eax
        mov bx, MEMBLOCKSIZE
        mov ah, 48h; ALLOCATE MEMORY
        int 21h; BX = number of 16-byte paragraphs desired
        mov buf2, ax
        mov buf2_ptr, ax
        mov es,ax
        xor eax,eax
        xor di,di
        mov cx,SCREENSIZE/4
        rep stosd
        mov ax,13h
        int 10h     ; - VIDEO - SET VIDEO MODE
;setcolortable--------------------------------
        mov cx,255
@@:     mov dx, 3C8h
        mov al,cl;color
        mov ah,cl
        sar ah,2;red
        out dx, ax
        inc dx
        mov al,ah;gren=red
        out dx, al
        mov al,63;blue
        out dx, al
        dec cx
        jns @b
        fldz
;-------------------------------------------
main:           mov ax, buf1
        cmp buf1_ptr, ax
        jnz @f
        mov es, ax;buf1
        mov ax, temp2
        mov cx, temp1
        shld    ax,cx,8
        shl cx,8
        rcr ax, 1
        rcr cx, 1
        add temp1, cx
        adc ax, temp2
        add word ptr temp1,25321
        adc ax,13849
        mov temp2, ax
        movsx   eax, ax
        add eax, eax
        xor edx, edx
        mov ecx,62000
        div ecx
        mov si, dx ;RAND()<<1%62000
        add si, SCREEN_WIDTH
        mov dword ptr es:[si], 7FFFFF7Fh
        mov dword ptr es:[si+SCREEN_WIDTH],0FFFFFFFFh
        mov dword ptr es:[si+SCREEN_WIDTH*2],0FFFFFFFFh
        mov dword ptr es:[si+SCREEN_WIDTH*3],7FFFFF7Fh
        fld st;time
        fld st
        fld     st
        fld     st
        fmul    const2_5
        fadd    const1_5
        fsin
        fmul    const120
        fistp   xd      
        add xd,160;xd=sin(time*2.5+1.5)*120+160
        ;fld    time
        fmul    const1_5
        fadd    const2_5
        fcos
        fmul    const60
        fistp   yd      
        add yd,100;yd=cos(time*1.5+2.5)*60+100
        imul    si,word ptr yd,SCREEN_WIDTH
        add si, word ptr xd
        mov word ptr es:[si+SCREEN_WIDTH], 0FFFFh
        mov word ptr es:[si+SCREEN_WIDTH*2], 0FFFFh
        ;fld    time
        fmul    const1_5
        fadd    const2_5
        fsin
        fmul    const120
        fistp   xd      
        add xd,160;xd=160+120*sin(time*1.5+2.5)
        fmul    const2_5
        fadd    const1_5
        fcos
        fmul    const60
        fistp   yd      
        add yd,100;yd=100+60*cos(time*2.5+1.5)
        imul    si,word ptr yd,SCREEN_WIDTH
        add si, word ptr xd
        mov word ptr es:[si+SCREEN_WIDTH], 0FFFFh
        mov word ptr es:[si+SCREEN_WIDTH*2], 0FFFFh
        fadd    const003;time=+0.03
;waterscreen--------------------------------
@@:     mov fs, buf1_ptr
        mov es, buf2_ptr
        mov di, SCREEN_WIDTH+1
        mov cx,0F8BFh-SCREEN_WIDTH-1
loc_103BE:  movzx   ax, byte ptr fs:[di-SCREEN_WIDTH-1]
        add al, byte ptr fs:[di-SCREEN_WIDTH]
        adc ah, 0
        add al, byte ptr fs:[di-SCREEN_WIDTH+1]
        adc ah, 0
        add al, byte ptr fs:[di-1]
        adc ah, 0
        add al, byte ptr fs:[di+1]
        adc ah, 0
        add al, byte ptr fs:[di+SCREEN_WIDTH-1]
        adc ah, 0
        add al, byte ptr fs:[di+SCREEN_WIDTH]
        adc ah, 0
        add al, byte ptr fs:[di+SCREEN_WIDTH+1]
        adc ah, 0
        shr ax, 2
        sub al, byte ptr es:[di]
        sbb ah, 0
        test    ax, ax
        jns @f
        xor ax, ax
@@:     stosb
                loop loc_103BE
        mov ax, buf1_ptr
        xchg    ax, buf2_ptr
        mov buf1_ptr, ax
        push    ds
        mov ds,ax;buf1_ptr
;copyvirtualscreen--------------------------
        mov dx,IMPUT_STATUS_0 ;=3DAh
WaitVerticalSync:in al, dx
        test al, 8
        jz WaitVerticalSync
WaitNotVerticalSync:in al, dx
        test al, 8
        jnz WaitNotVerticalSync
        mov cx,SCREENSIZE/4
        xor si,si
        xor di,di
        push    VGA_SEGMENT
        pop es
        rep movsd
        mov ds,cx;ds=0
        mov ax, ds:[41Ah]; было ли нажатие на клавиатуру?
        sub ax, ds:[41Ch]
        pop ds
        jz  main
exit:       mov ax, 3
        int 10h     ; - VIDEO - SET VIDEO MODE
        int 20h
;---------------------------------
const1_5    dd 1.5
const2_5    dd 2.5
const60     dd 60.0
const120    dd 120.0
const003    dd 0.03
end start
Неплохо бы было добавить background (какую-нибудь картинку), да еще чтоб волны искажали его. Что ж нет ничего невозможного...Добавим два буфера: pics и screen_end. В pics будем хранить картинку, которую используем в качестве background`а. А screen_end мы будем выводить на экран. Палитру надо взять из считываемой картинки.
Как же нам добиться эффекта искажения, из-за преломления лучей света на границе двух разнородных сред? Сейчас подумаем.
Берем в руки учебник физики, ищем раздел "Оптика" и находим формулу расчета угла преломленного луча. Применив знания, полученные на уроках геометрии, выведем формулу, с помощью которой будем находить смещение, относительно текущего пикселя, по которому находиться пиксель, который и надо вывести на экран вместо текущего:
Код
D=h*tg(arcsin(0.75*sin(a)))
D-искомое смещение, h-высота слоя воды (высота волны), а-угол между нормалью к поверхности воды в данной точке и вектором наблюдения.
Нужно искать тангенс арксинуса синуса, да еще расчет нормали к поверхности, плюс это надо разложить на нахождение смещения по X и Y, сложновато...
Конечно, можно воспользоваться табличным разложением, но таблица получиться слишком уж большой. Как же быть? Взглянем еще раз на нашу формулу и подумаем, нельзя ее как нибудь сократить.
Допустим, что наш тангенс арксинуса синуса, есть величина величина постоянная и равная, например 0.25. (Воспользуемся калькулятором). Опыт показал, что такое допущение вполне возможно, если ширина волны небольшая. Тогда формула значительно упроститься и будет выглядеть так:
D=h/4
Теперь, чтобы найти координаты нужного пикселя, надо прибавить к текущим, D:
x'=x+D; y'=y+D;
В наш алгоритм добавиться пункт 2.5.
2.5. Делим результат на 4, полученное значение прибавляем к текущим X и Y. Если X, больше максимально допустимого значения, то X приравняем максимальному допустимому значению. Аналогичную проверку надо сделать и для Y. Считываем по этим координатам из массива pics, число и записываем по текущим координатам в screen_end.
Теперь модифицируем этот алгоритм чтобы получить более реалистичное изображение.
Недостаток описанного выше метода - смещение не зависит от наклона нормали к поверхности воды. Как исправить этот недостаток, не усложняя алгоритм? Довольно просто. Достаточно знать куда наклонена нормаль, тогда сможем смещать координаты в соответствии с этим направлением. Для того чтобы найти смещения по x и y, используем следующие формулы:
C
1
2
Dx=(buf1_ptr[x+1][y]-buf1_ptr[x-1][y])>>2;
Dy=(buf1_ptr[x][y+1]-buf1_ptr[x][y-1])>>2;
Как надо изменить пункт 2.6., вы наверняка догадаетесь сами.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
; masm dos com #
.model tiny
.code
.686
org 100h
IMPUT_STATUS_0  equ 3DAh
VGA_SEGMENT equ 0A000h
SCREEN_HEIGHT   equ 200
SCREEN_WIDTH    equ 320
SCREENSIZE  equ SCREEN_WIDTH*SCREEN_HEIGHT
MEMBLOCKSIZE    equ SCREENSIZE/16
yd      equ dword ptr [bp-4]
xd      equ dword ptr [bp-8]
buf1        equ word ptr [bp-10]
buf2        equ word ptr [bp-12]
screen_end  equ word ptr [bp-14]
pics        equ word ptr [bp-16]
buf1_ptr    equ word ptr [bp-18]
buf2_ptr    equ word ptr [bp-20]
temp1       equ word ptr [bp-22]
temp2       equ word ptr [bp-24]
pal     equ byte ptr [bp-1048]
 
 
start:      mov sp, 1260h
        mov ah, 4Ah; ADJUST MEMORY BLOCK SIZE (SETBLOCK)
        mov bx, 126h; ES = segment address of block to change
        int 21h; BX = new size in paragraphs
 
        enter 1048, 0
        rdtsc
        mov dword ptr temp1,eax
        mov bx,MEMBLOCKSIZE
        mov ah,48h; ALLOCATE MEMORY
        int 21h; BX = number of 16-byte paragraphs desired
        mov buf1, ax
        mov buf1_ptr, ax
        mov es,ax
        xor eax,eax
        xor di,di
        mov cx,SCREENSIZE/4
        rep stosd
        mov bx,MEMBLOCKSIZE
        mov ah,48h; ALLOCATE MEMORY
        int 21h; BX = number of 16-byte paragraphs desired
        mov buf2, ax
        mov buf2_ptr, ax
        mov es,ax
        xor eax,eax
        xor di, di
        mov cx,SCREENSIZE/4
        rep stosd
        mov bx,MEMBLOCKSIZE
        mov ah,48h; ALLOCATE MEMORY
        int 21h; BX = number of 16-byte paragraphs desired
        mov screen_end,ax
        mov es,ax
        xor eax,eax
        xor di,di
        mov cx,SCREENSIZE/4
        rep stosd
        mov bx,MEMBLOCKSIZE
        mov ah,48h; ALLOCATE MEMORY
        int 21h; BX = number of 16-byte paragraphs desired
        mov pics,ax
        mov ax,13h
        int 10h     ; - VIDEO - SET VIDEO MODE
;readbmp---------------------------------------------------------
        mov dx,offset filename; OPEN DISK FILE WITH HANDLE
        mov ax,3D00h       ; DS:DX  -> ASCIZ filename
        int 21h     ; AL = access mode 0 - read
        mov bx,ax;bx=handle
        xor cx,cx
        mov dx,54;размер заголовка BMP-файла
        mov ax,4200h; MOVE FILE READ/WRITE POINTER (LSEEK)
        int 21h; AL = method: offset from beginning of file
        lea dx,pal
        mov cx,1024
        mov ah,3Fh; READ FROM FILE WITH HANDLE
        int 21h; BX = file handle, CX = number of bytes to read
                ; DS:DX -> buffer
        mov di,1023
@@:     shr byte ptr pal[di],2
        dec di
        jns @b
        push ds
        mov ds,pics
        mov di,SCREEN_HEIGHT-1
@@:     imul dx,di,SCREEN_WIDTH
        mov cx,SCREEN_WIDTH
        mov ah,3Fh; READ FROM FILE WITH HANDLE
        int 21h; BX = file handle, CX = number of bytes to read
                ; DS:DX -> buffer
        dec di
        jns @b
        pop ds
        mov ah,3Eh; CLOSE A FILE WITH HANDLE
        int 21h ; BX = file handle
;--------------------------------------------------
        mov cx,255
@@:     mov di,cx
        shl di,2
        mov dx,3C8h
        mov al,cl;color
        mov     ah,pal[di+2];red
        out dx,ax
        inc dx
        mov al,pal[di+1];green
        out dx,al
        mov al,pal[di];blue
        out dx,al
        dec cx
        jns @b
;-----------------------------------------------------
        fldz    ;time=0
main:       mov ax,buf1
        cmp buf1_ptr,ax
        jnz @f
        mov es,ax;buf1
        mov ax,temp2
        mov cx,temp1
        shld ax,cx,8
        shl cx,8
        rcr ax,1
        rcr cx,1
        add temp1,cx
        adc ax,temp2
        add word ptr temp1,25321
        adc ax,13849
        mov temp2,ax
        movsx eax,ax
        add eax,eax
        xor edx,edx
        mov ecx,62000
        div ecx
        mov si,dx
        add si,SCREEN_WIDTH
        mov dword ptr es:[si],7FFFFF7Fh
        mov dword ptr es:[si+SCREEN_WIDTH],0FFFFFFFFh
        mov dword ptr es:[si+SCREEN_WIDTH*2],0FFFFFFFFh
        mov dword ptr es:[si+SCREEN_WIDTH*3],7FFFFF7Fh
        fld st;time
        fld st
        fld st
        fld st
        fmul const2_5
        fadd const1_5
        fsin
        fmul const120
        fistp xd        
        add xd,160;xd=sin(time*2.5+1.5)*120+160
        fmul const1_5
        fadd const2_5
        fcos
        fmul const60
        fistp yd        
        add yd,100;yd=cos(time*1.5+2.5)*60+100
        imul si,word ptr yd,SCREEN_WIDTH
        add si,word ptr xd
        mov word ptr es:[si+SCREEN_WIDTH], 0FFFFh
        mov word ptr es:[si+SCREEN_WIDTH*2], 0FFFFh
        fmul    const1_5
        fadd    const2_5
        fsin
        fmul    const120
        fistp   xd      
        add xd, 160;xd=sin(time*1.5+2.5)*120+160
        fmul    const2_5
        fadd    const1_5
        fcos
        fmul    const60
        fistp   yd      
        add yd,100;yd=cos(time*2.5+1.5)*60+100
        imul    si,word ptr yd,SCREEN_WIDTH
        add si,word ptr xd
        mov word ptr es:[si+SCREEN_WIDTH], 0FFFFh
        mov word ptr es:[si+SCREEN_WIDTH*2], 0FFFFh
        fadd    const003;time=+0.03
;waterscreen---------------------------------------------------
@@:     mov gs,buf1_ptr
        mov fs,buf2_ptr
        mov di, SCREEN_WIDTH+1
        mov cx,64000-321
loc_10440:  movzx   ax, byte ptr gs:[di-SCREEN_WIDTH-1]
        add al, byte ptr gs:[di-SCREEN_WIDTH]
        adc ah, 0
        add al, byte ptr gs:[di-SCREEN_WIDTH+1]
        adc ah, 0
        add al, byte ptr gs:[di-1]
        adc ah, 0
        add al, byte ptr gs:[di+1]
        add ah, 0
        add al, byte ptr gs:[di+SCREEN_WIDTH-1]
        adc ah, 0
        add al, byte ptr gs:[di+SCREEN_WIDTH]
        adc ah, 0
        add al, byte ptr gs:[di+SCREEN_WIDTH+1]
        adc ah, 0
        shr ax, 2
        sub al, byte ptr fs:[di]
        sbb ah, 0
        test    ax, ax
        jns @f
        xor ax, ax
@@:     mov fs:[di], al
        shr ax,2
        imul    si,ax,SCREEN_WIDTH+1
        add si, di;si=ax*321+di
        cmp si,SCREENSIZE-1
        jbe @f
        mov si,SCREENSIZE-1
@@:     mov es,pics
        lods byte ptr es:[si]
        mov es,screen_end
        stosb
        loop loc_10440
        mov ax, buf1_ptr
        xchg    ax, buf2_ptr
        mov buf1_ptr, ax
;copyvirtualscreen------------------------------------
        mov dx,IMPUT_STATUS_0
WaitVerticalSync:in al, dx
        test al, 8
        jz WaitVerticalSync
WaitNotVerticalSync:in al, dx
        test al, 8
        jnz WaitNotVerticalSync
        mov cx,SCREENSIZE/4
        xor si,si
        push ds
        push es
        pop ds;ds=screen_end
        xor di,di
        push VGA_SEGMENT
        pop es
        rep movsd
        mov ds,cx;ds=0
        mov ax,ds:[41Ah]
        sub ax,ds:[41Ch]
        pop ds
        jz main
exit:       mov ax,3
        int 10h     ; - VIDEO - SET VIDEO MODE
        int 20h
 
filename    db 'aqua11.bmp',0
const1_5    dd 1.5
const2_5    dd 2.5
const60     dd 60.0
const120    dd 120.0
const003    dd 0.03
end start
результат
4
Mikl___
Заблокирован
Автор FAQ
25.06.2013, 15:16  [ТС] #23
Графика DOS. Падающий снег в 3D
Размер COM-файла 187 байт. Во вложении исходный текст и СОМ-файл
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
; masm dos com #
.model tiny
.code
.386
org 100h
maxpix  equ 5000
WIDTH_SCREEN    equ 320
HEIGHT_SCREEN   equ 200
SCREENSIZE  equ WIDTH_SCREEN*HEIGHT_SCREEN
IMPUT_STATUS_0  equ 3DAh    ;регистр статуса ввода 0
VGA_SEGMENT equ 0A000h
ftmp    equ dword ptr [bp-4]
tmp equ word ptr [bp-6]
c_  equ byte ptr [bp-6-maxpix]
s   equ word ptr [bp-6-3*maxpix]
 
start:  enter   6+3*maxpix,0
    mov ax,13h
    int 10h     ; - VIDEO - SET VIDEO MODE
    mov ax,cs
    add ax,1000h
    mov gs,ax
    finit
; Инициализация снега
    mov si,maxpix
    xor ax,ax
@@: xor ax,bx          ;Random by Alexander Matchugovsky (2:5020/996.21)
    add ax,ax
    adc bx,0
    xor bx,ax
    mov s[si],ax
    shr al,4
    inc ax
    mov c_[si],al
    dec si
    jnz @b
    fldz    ;stf=0
mainloop:push gs
    pop es;es=gs
    xor di,di
    xor eax,eax
    mov cx,SCREENSIZE/4
    rep stosd
    mov si,maxpix
@@: fld st; fld stf
    fsin
    movzx eax,byte ptr c_[si];ftmp=c[SI]*5;
    imul eax,5
    mov ftmp,eax
    fmul ftmp
    fistp tmp
    mov ax, s[si]
    add tmp, ax      ;tmp+=s[SI];DI=tmp;
    mov di, tmp
    mov al, c_[si]
    add al,15        ;AL=c[SI]+15;
    stosb  ;GS:[DI]=AL;
    xor ax,ax
    mov al,c_[si]
    add ax,16
    shr ax,3
    imul ax,WIDTH_SCREEN
    add s[si], ax   ;s[SI]+=((c[SI]+16)/8)*320;
    sub si,2
    jnz @b
    fadd const005  ;stf+=0.05
    mov dx,IMPUT_STATUS_0; тормозим вывод на экран до следующего кадра
WaitVerticalSync:in al,dx
    test al,8
    jz WaitVerticalSync
WaitNotVerticalSync:in al,dx
    test al,8
    jnz WaitNotVerticalSync
    push VGA_SEGMENT
    pop es
    xor di,di
    mov cx,SCREENSIZE/4
    rep movs dword ptr es:[di],gs:[si]
    mov es,cx
    mov ax,es:[41Ah]
    sub ax,es:[41Ch]; было ли нажатие на клавиатуру?
    jz mainloop
    mov ax,3;восстанавливаем текстовый режим
    int 10h
    int 20h; выход из программы
const005    dd 0.05
end start
3
6a6kin
231 / 101 / 2
Регистрация: 18.04.2010
Сообщений: 294
16.07.2013, 00:20 #24
Анимация в DOS. Часть первая.
В данной статье рассмотрим создание простой анимации в графическом режиме 13H. Использовать будем TASM. Результатом будет небольшая COM-программа.
Подробно про графические режимы в DOS и рисование написал Mikl___ здесь: FAQ для раздела Assembler, MASM, TASM
В данной статье рисовать будем напрямую в видеопамять, так как это удобнее и быстрее.
Что вообще мы будем рисовать? Как видно из картинки — это развевающийся флаг Белоруссии на однородном фоне с надписью вверху. Соотношение сторон у флага — 1:2. Программа завершается по нажатию клавиши Enter.

Самый простой способ нарисовать развевающийся флаг — рисовать его вертикальными полосами, координата Y которых зависит от времени и координаты X. Формулу лучше всего проверить, написав пример на каком-нибудь высокоуровневом языке(C, Wolfram Mathematica, Delphi). Так можно быстро протестировать формулу и получить нужный результат.

Я для начала написал простую программу с использованием Delphi 7. Формула вышла следующей:
Delphi
1
Y := 10*sin(i/20+t)*i/(H*2)-i/5;
где i — текущая X координата полоски.


Как стало ясно из вышеприведённой статьи (я надеюсь), чтобы нарисовать пиксель на экране в режиме 13H, нужно загрузить цвет пикселя (1 байт) по определённому адресу в массив пикселей. Адрес вычисляется по просто форумуле:
Код
адрес = 0A000h + X + Y*320
где X, Y — координаты точки на экране (320x200)

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

Двойная буферизация — принцип, согласно которому части изображения записываются не сразу в видеопамять, а сначала в промежуточный буфер (обычный массив), который потом сразу копируется в видеопамять. Если рисование занимает некоторое время, то изображение на экране может прорисовываться не сразу, а частями, т.к. данные из видеопамяти считываются быстрее, чем алгоритм рисования заполнит видеопамять новыми данными. Двойная буферизация сокращает время заполнения видеопамяти до времени копирования массива.

Вертикальной синхронизация — принцип, согласно которому заполнение видеопамяти происходит в специальный момент времени, в который данные из видеопамяти не считываются. Раньше в кинескопах поток электронов (луч), подсвечивал специальное вещество (люминофор), нанесенное на стекло. Если рассказывать примитивно, то луч построчно перемещался от начала дисплея до его конца и потом необходимо было его установить опять в начало. Для этого необходимо было некоторое время, которое называли обратным ходом луча. Пока луч шел обратно, его необходимо было гасить, для чего передавались специальные гасящие сигналы. Существует два вида таких сигналов: от конца строки до начала (горизонтальный или строчный импульс) и от конца кадра до его начала (вертикальный или кадровый импульс). Пока луч был погашен и перемещался в начало дисплея, можно было записать необходимую информацию в видеопамять, так как отображаться промежуточная информация не будет. Хотя в современных мониторах отсутствует именно такая система, но название осталось.

Сочетание этих двух принципов позволяет получить достаточно плавную анимацию, без мерцаний.

Обычно для рисования анимации применяется следующий подход: в цикле последовательно выполняются следующие действия:
  • очистка экрана
  • рисование кадра анимации
  • задержка на время показа кадра
  • изменение параметра времени (переменная T)
При использовании вышеприведённых методов (двойная буферизация и вертикальная синхронизация) добавляется ещё и ожидание обратного хода луча и последующее копирование буфера в видеопамять.

Так как требуется завершать программу по нажатию Enter, нужно также добавить проверку нажатия клавиши, и если нажат Enter, выходить из цикла. Для этого есть две функции прерывания 16H:
Код
AH = 0
	Ждёт нажатия клавиши
	На выходе в AX содержится код клавиши (AL = ASCII или 0; 
	AH = сканкод или расширенный ASCII)
AH = 1
	Проверяет было ли нажатие (но не ожидает его)
	На выходе ZF = 1, если клавиша не нажата, ZF = 0, 
	если была нажата и в AX то же, что и в 0 функции
Сформируем главную часть программы (после останется добавить лишь вспомогательные функции)
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
.MODEL TINY
.CODE
.386
ORG 100H
 
X = 40        ;начальная X-координата левого угла флага
Y = 80        ;начальная Y-координата левого угла флага
H = 90        ;ширина флага
W = 2*H       ;длина флага
BACK EQU 9    ;цвет фона (9 — светлосиний)
 
MAIN PROC
LOCALS @@
    ;инициализируем ES адресом начала видеопамяти
    PUSH 0A000h
    POP     ES
    ;устанавливаем видеорежим
    ;за это отвечает функция 0 прерывания 10h
    ;AH = 00H
    ;AL = <номер режима>
    MOV     AX, 13h
    INT     10h
    ;функция рисует заголовок и заливает его фоном
    CALL    PAINT_TITLE
@@CYCL:
    ;функция заполняет буфер пикселями цвета фона
    CALL    CLR_BUFF
    ;функция рисует сам флаг
    CALL    DRAW_FLAG
    ;функция ожидает обратного хода луча и копирует буфер в видеопамять
    CALL    COPY_BUFF
    ;функция выполняет задержку в 40 мс
    CALL    DELAY
    ;наращивается переменная времени
    INC     T
    ;проверяем была ли нажата клавиша (но не ждем нажатия)
    ;функция 1 прерывания 16h сбросит флаг ZF, если клавиша была нажата
    ;если клавиша была нажата, то информация о нажатии сохранится
    MOV     AH, 1
    INT     16h
    ;если клавиша не была нажата, продолжаем рисовать анимацию
    JZ      @@CYCL
    ;если всё-таки нажата, информацию о нажатии надо удалить и 
    ;проверить что за клавиша нажата
    ;функция 1 прерывания 16h удалит информацию и
    ;в AX вернет информацию о клавише
    MOV     AH,0
    INT     16h
    ;если нажат не Enter (код — 13 или 0DH)
    CMP     AL, 13
    ;то продолжаем рисовать
    JNE     @@CYCL
    ;устанавливаем текстовый видеорежим
    MOV     AX, 3
    INT     10h
    ;завершаем программу
    MOV     AH, 4Ch
    INT     21h
MAIN ENDP
 
TEXT          DB '6a6kin, cyberforum.ru','$' 
T             DW 0
TMP           DW 0
BUFF          DB 64000 DUP (?)
END MAIN
Если закомментировать все вызовы функций и скомпилировать программу (пример для файла с именем graph.asm):
Bash
1
2
tasm graph
tlink /t graph
то при запуске будет виден чёрный экран и программа завершиться по нажатию Enter.

Так как необходимо нарисовать ещё и надпись вверху экрана, то придется опять прибегнуть к хитростям
Нарисовать текст может всё та же функция 9 прерывания 21h, но постоянно её вызывать — издержки, которых можно избежать, так как текст не изменяется со временем и ещё записывается сразу в видеопамять. Ещё одна проблема — текст рисуется белыми буквами на чёрном фоне, поэтому фон нужно будет заменить.

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

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PAINT_TITLE PROC
LOCALS @@
    PUSH    SI
    ;выводим текст с помощью 9 функции
    MOV     AH, 9
    MOV     DX, OFFSET TEXT
    INT     21H
    ;далее в цикле для первых восьми строк меняем
    ;все пиксели черного цвета (код — 0) на цвет фона
    MOV     SI, -1
@@CYCL:
    INC     SI
    CMP     SI, 320*8
    JAE     @@END
    CMP     BYTE PTR ES:[SI], 0
    JNE     @@CYCL
    MOV     BYTE PTR ES:[SI], BACK
    JMP     @@CYCL
@@END:  POP     SI
    RET 
PAINT_TITLE ENDP
Если добавить данную функцию в приведенную выше программу, то при запуске в самом верху будет текст на заданном фоне.

Рассмотрим также другие вспомогательные функции: DELAY, CLR_BUFF, COPY_BUFF

Функция задержки очень проста — это вызов функции 86h прерывания 15h
В CX : DX располагается количество микросекунд, на которое нужно остановить программу (прерывание запускает цикл на это время)
Ничего сложного в ней нет.
Assembler
1
2
3
4
5
6
7
DELAY PROC
    XOR     CX, CX
    MOV     DX, 40000
    MOV     AH, 86h
    INT     15h
    RET
DELAY ENDP
Очистка буфера — просто заполнение буфера пикселями фона. Для этого используется команда STOSB с префиксом REP.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CLR_BUFF PROC
    PUSH    DI
    PUSH    ES
    ;так как для STOSB адрес должен быть в ES:DI,
    ;то необходимо запомнить ES и записать в него DS
    PUSH    DS
    POP     ES
    MOV     DI, OFFSET BUFF
    MOV     CX, 320*100
    MOV     AX, BACK*101h
    REP     STOSW
    POP     ES
    POP     DI
    RET
CLR_BUFF ENDP
Функция COPY_BUFF выполняет сразу две задачи:
  • ожидает обратный ход луча
  • копирует буфер в видеопамять
Нужно кое-что пояснить: во-первых, копировать нужно буфер без первых 8 строк, так как там заголовок. Там всё просто реализуется командой MOVSB. Во-вторых, это как происходит ожидание. Чтобы получить информацию о текущем состоянии луча, нужно считать байт из порта 03DAh, в котором четвёртый бит — состояние луча.
Всего в данной функции два цикла. Проверяет, идет ли на данный момент этот несуществующий луч обратно, и если он идет, то необходимо дождаться его завершения. Это необходимо, так как возможно луч уже почти дошел до начала и начнет рисоваться кадр и буфер скопироваться не успеет (представьте, что лыжник поднимается на подъёмнике, чтобы потом спуститься с холма, с подножья холма можно узнать только занят ли сейчас подъёмник или нет, а кто-то решил очистить холм от препятствий (и успеет пока лыжник поднимается от начала) — для этого ему нужно дождаться пока лыжник поднимется до вершины на подъёмнике (потому что он не знает где лыжник на данный момент находится), съедет с горы и опять начнет подъём).
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
COPY_BUFF PROC
LOCALS @@
    PUSH    SI
    PUSH    DI
    MOV     DX, 3DAh
@@CHK1: IN      AL, DX    ;считываем необходимый байт
    TEST    AL, 1000b ;проверяем на обратный ход луча
    JNZ     @@CHK1    ;ждём, если если луч идёт обратно
                 ;(лыжник где-то на подъёмнике, но непонятно где)
@@CHK2: IN      AL, DX
    TEST    AL, 1000b
    ;то же, только ждём по рисуется изображение (лыжник спускается с холма)
    JZ      @@CHK2
    MOV     SI, OFFSET BUFF 
    MOV     DI, 320*8
    MOV     CX, 320*192/2
    REP     MOVSW     ;копируем буфер
    POP     DI
    POP     SI
    RET
COPY_BUFF ENDP
Добавив эти функции, расскомментируйте их вызовы. При запуске весь экран будет залит фоновым цветом, а сверху будет виден заголовок.

Осталось самое главное — рисование самого флага. Для этого реализуем несколько дополнительных функций:
  • рисование вертикальной линии заданного цвета, длины и положения
  • вычисление положения вертикальной линии (координаты Y по необходимой формуле)
Как рисовать линию есть в посте Mikl___, я лишь сделал функцию, принимающую в качестве параметров свойства линии
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
;VOID DRAW_VERTICAL_LINE(WORD X, WORD Y, WORD HEIGHT, WORD COLOR)
DRAW_VERTICAL_LINE PROC
LOCALS @@
    PUSH    BP
    MOV     BP, SP
    PUSH    SI
 
    ;вычисляем индекс в массиве SI = X + Y * 320
    IMUL     SI,WORD PTR [BP+6],320
    ADD     SI, [BP+4]
    MOV     CX, [BP+8]  ;высота линии
    MOV     AL, [BP+10] ;цвет линии
@@CYCL:
    MOV     BYTE PTR BUFF[SI], AL
    ADD     SI, 320
    LOOP     @@CYCL
    POP     SI
    POP     BP
    RET
DRAW_VERTICAL_LINE ENDP
В этой функции выполняются необходимые арифметические операции (надо пояснить, что от i - текущей X-координаты полосы я отнимаю X-координату начального положения, чтобы сместить центр координат в начало левого верхнего угла флага).
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
;WORD GET_Y(WORD i, WORD Yorig)
GET_Y PROC
    PUSH    BP
    MOV     BP, SP
 
    SUB     WORD PTR [BP+4], X
    FINIT
    FILD    WORD PTR [BP+4] ;ST(0) = i - X0
    MOV     TMP, 20
    FIDIV   TMP             ;ST(0) = (i - X0)/20
    FIADD   T               ;ST(0) = (i - X0)/20 + T
    FSIN                    ;ST(0) = sin((i - X0)/20 + T)
    FIMUL   WORD PTR [BP+4] ;ST(0) = sin((i - X0)/20 + T)*i
    MOV     TMP, 2*H
    FIDIV   TMP             ;ST(0) = sin((i - X0)/20 + T)*i/(2*H)
    MOV     TMP, 10
    FIMUL   TMP             ;ST(0) = 10*sin((i - X0)/20 + T)*i/(2*H)
    FIST    TMP             ;TMP = 10*sin((i - X0)/20 + T)*i/(2*H)
    MOV     DX, TMP
    ADD     DX, [BP+6]      ;DX = Yorig + 10*sin((i - X0)/20 + T)*i/(2*H)
    MOV     AX, [BP+4]
    MOV     BL, 5
    DIV     BL
    MOV     AH, 0           ;AX = i/5
    SUB     DX, AX
    MOV     AX, DX          ;AX = Yorig + 10*sin((i - X0)/20 + T)*i/(2*H) - i/5
    POP     BP
    RET
GET_Y ENDP
Дело за малым — функция рисования самого флага. Тут всё просто — в цикле рисуем по две полосы каждого цвета, вызывая написанные ранее вспомогательные функции.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
DRAW_FLAG PROC
LOCALS @@
    PUSH    SI
    PUSH    DI
 
    ;в SI — X-координата полосы
    ;в DI — Y-координата полосы
    MOV     SI, X
@@CYCL:
    MOV     DI, Y
 
    ;формируем окончательный вариант Y-координаты
    PUSH    DI
    PUSH    SI
    CALL    GET_Y
    ADD     SP, 4
 
    MOV     DI, AX
 
    ;рисуем верхнюю красную линию
    PUSH    12    ;цвет — светлокрасный
    PUSH    2*H/3 ;высота — две-трети от высоты всего флага
    PUSH    DI
    PUSH    SI
    CALL    DRAW_VERTICAL_LINE
    ADD     SP, 8
 
    ;рисуем нижнюю зелёную линию
    PUSH    10    ;цвет — светлозелёный
    PUSH    H/3   ;высота — одна-треть от высоты флага
    MOV     AX, DI
    ADD     AX, 2*H/3 
    PUSH    AX    ;смещаем положение по вертикали на две трети от высоты флага
    PUSH    SI
    CALL    DRAW_VERTICAL_LINE
    ADD     SP, 8
 
    ;и так для каждой полосы по всей длине флага
    INC     SI
    CMP     SI, X+W
    JB      @@CYCL
 
    POP     DI
    POP     SI
    RET
DRAW_FLAG ENDP
Остается только раскомментировать вызовы всех функций и добавить недостающие. Компилируете и запускаете:
Bash
1
2
3
tasm graph
tlink /t graph
graph
Полный код и сом-файл — здесь
В репозитории находится также makefile. Для полной компиляции достаточно написать:
Bash
1
make
5
6a6kin
231 / 101 / 2
Регистрация: 18.04.2010
Сообщений: 294
16.07.2013, 09:47 #25
Анимация в DOS. Часть вторая.
В продолжение предыдущей статьи про анимацию, в этой статье мы рассмотрим другой вид анимации в режиме 13h. Программа опять будет написана под TASM и представлять из себя COM-файл.

Особенность данной анимации в том, что видеопамять заполняется лишь единожды. Соответственно, ни двойная буферизация, ни вертикальная синхронизация не нужны, так как информация больше не будет меняться. Изображением будет состоять из последовательных элементов (например, линий), но полностью разных цветов. Меняться будет палитра цветов, в которой находится информация о цвете каждого кода (код тех байтов, которые заносятся в видеопамять). Достаточно в цикле сдвигать цвета в палитре, чтобы создать простую и красивую анимацию.

Основная часть программы практически такая же, как и в предыдущей статье, только убраны некоторые ненужные части и изменены функции, управляющие анимацией.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
.MODEL TINY
.CODE
ORG 100H
 
MAIN PROC
LOCALS @@
    ;инициализируем ES адресом начала сегмента видеопамяти
    MOV     AX, 0A000h
    MOV     ES, AX
    ;устанавливаем нужный видеорежим
    MOV     AX, 13h
    INT     10h
;функция, отвечающая за начальное наполнение видеопамяти изображением
    CALL    DRAW_SQUARES
@@CYCL:
    ;функция, меняющая палитру (что автоматически меняет вид изображения на экране)
    CALL    SHIFT_PALETTE
    ;задержка на время показа кадра
    CALL    DELAY
    ;меняет переменную времени
    INC     T
    ;далее те же проверки на нажатие клавиши Enter
    MOV     AH,1
    INT     16h
    JZ      @@CYCL
    MOV     AH,0
    INT     16h
    CMP     AL,13
    JNE     @@CYCL
    ;устанавливаем текстовый видеорежим
    MOV     AX,3
    INT     10h
    ;завершаем программу
    MOV     AH,4Ch
    INT     21h
MAIN ENDP
 
DELAY PROC
    XOR     CX,CX
    MOV     DX,40000
    MOV     AH,86h
    INT     15h
    RET
DELAY ENDP
 
T DB 0 ;переменная времени
 
END MAIN
Как видно из изображения вверху поста, оно состоит из прямоугольников, вложенных друг в друга. Для их рисования понадобятся две функции:
  • нарисовать горизонтальную прямую
  • нарисовать вертикальную прямую
Вертикальная прямая рисуется так же, как и в предыдущей статье, только сразу в видеопамять.
Горизонтальная прямая легко рисуется используя STOSB, так как пиксели располагаются в памяти один за одним.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
;VOID DRAW_VERTICAL_LINE(WORD X, WORD Y, WORD HEIGHT, WORD COLOR)
DRAW_VERTICAL_LINE PROC
LOCALS @@
    PUSH    BP
    MOV     BP, SP
    PUSH    SI
 
    ;вычисляем индекс в массиве SI = X + Y * 320
    MOV     AX, 320
    MUL     WORD PTR [BP+6]
    MOV     SI, AX
    ADD     SI, [BP+4]
 
    MOV     CX, [BP+8]  ;высота линии
    MOV     AL, [BP+10] ;цвет линии
@@CYCL:
    MOV     BYTE PTR ES:[SI], AL
    ADD     SI, 320
    DEC     CX
    JNZ     @@CYCL
 
    POP     SI
    POP     BP
    RET
DRAW_VERTICAL_LINE ENDP
 
;VOID DRAW_HORIZONTAL_LINE(WORD X, WORD Y, WORD WIDTH, WORD COLOR)
DRAW_HORIZONTAL_LINE PROC
    PUSH    BP
    MOV     BP, SP
    PUSH    DI
 
    ;вычисляем индекс в массиве SI = X + Y * 320
    MOV     AX, 320
    MUL     WORD PTR [BP+6]
    MOV     DI, AX
    ADD     DI, [BP+4]
 
    MOV     CX, [BP+8]  ;длина линии
    MOV     AL, [BP+10] ;цвет линии
    REP     STOSB
 
    POP     DI
    POP     BP
    RET
DRAW_HORIZONTAL_LINE ENDP
Можно приступать к рисованию вложенных прямоугольников. Высота экрана — 200 пикселей, прямоугольников будет в два раза меньше, так как есть два горизонтальных ребра по одному пикселю. Ещё не будем рисовать самый внутренний прямоугольник, так как он нулевой высоты. Соответственно, 99 раз будем вызывать четыре раза функции рисования линий в цикле. Цвет линий будет просто равен текущему индексу, что нам и нужно.

Нужно заметить, что рисование вертикальных линий идёт с опережением, так как их высота меньше и Y-координата меньше.

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
DRAW_SQUARES PROC
LOCALS @@
    PUSH    SI
    MOV     SI, 0
 
@@CYCL:
    ;рисуем верхнюю горизонтальную черту
    PUSH    SI        ;помещаем в стек первый параметр — цвет, равен индексу
    MOV     AX, 320
    SUB     AX, SI
    SUB     AX, SI    ;вычисляем длину линии, в зависимости от того,
    PUSH    AX        ;какой по счёту прямоугольник
    PUSH    SI        ;начальные координаты равны счётчику
    PUSH    SI
    CALL    DRAW_HORIZONTAL_LINE
    ADD     SP, 4     ;удаляем из стека только первые два параметра
 
    ;рисуем нижнюю горизонтальную черту
    MOV     AX, 199
    SUB     AX, SI    ;вычисляем изменённые начальные координаты для нижней линии
    PUSH    AX
    PUSH    SI
    CALL    DRAW_HORIZONTAL_LINE
    ADD     SP, 8     ;удаляем все параметры из стека
 
    ;рисуем левую вертикальную черту
    INC     SI        ;увеличиваем счётчик
    PUSH    SI        ;вертикальные линии рисуются для следующего прямоугольника
    MOV     AX, 200   ;и строятся по принципу горизонтальных линий
    SUB     AX, SI
    SUB     AX, SI
    PUSH    AX
    PUSH    SI
    PUSH    SI
    CALL    DRAW_VERTICAL_LINE
    ADD     SP, 4
 
    PUSH    SI
    MOV     AX, 319
    SUB     AX, SI
    PUSH    AX
    CALL    DRAW_VERTICAL_LINE
    ADD     SP, 8
 
    CMP     SI, 99   ;рисуем только 99 штук
    JB      @@CYCL
 
    POP     SI
    RET
DRAW_SQUARES ENDP
Осталась самая главная функция, которая непосредственно изменяет палитру. Для изменения цветов в палитре есть специальные порты — 3C8h и 3C9h. В первый порт нужно занести номер цвета в палитре, который мы хотим поменять. Во второй нужно по очереди занести три цветовые компоненты RGB. Значащими в байте являются первые 6 бит, то есть возможны 64 значения одной компоненты. Для нашей анимации их я просто подобрал алгоритм, чтобы достойно выглядело.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
SHIFT_PALETTE PROC
LOCALS @@
    MOV     CL, 1      ;нулевой всегда будет чёрным
 
@@CYCL:
    MOV     AL, CL     ;заносим номер цвета
    MOV     DX, 03C8h
    OUT     DX, AL
 
    MOV     AL, T      ;в AL будет значение цветовой компоненты
    INC     DX         ;увеличиваем значение порта
 
    ADD     AL, CL     ; некоторое преобразования для эффекта
    OUT     DX, AL     ;заносим красную компоненту
 
    ADD     AL, CL
    OUT     DX, AL     ;заносим зелёную компоненту
 
    ADD     AL, CL
    OUT     DX, AL     ;заносим синюю компоненту
 
    INC     CL
    CMP     CL, 100
    JB      @@CYCL
 
    RET
SHIFT_PALETTE ENDP
Нужно добавить все написанные функции и можно компилировать и запускать:
Bash
1
2
3
tasm graph
tlink /t graph
graph
Результат:
Полный код и сом-файл — здесь
В репозитории находится также makefile. Для полной компиляции достаточно написать:
Bash
1
make
P.S. Процедуру SHIFT_PALETTE можно улучшить и по скорости и по размеру
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
SHIFT_PALETTE PROC
    MOV     CX, 99      ;номер последнего цвета 
@@: MOV     DX, 3C8h
    MOV     AL, CL     ;номер цвета
    MOV     AH,Red      ;значение красной компоненты
    OUT     DX, AX    
    INC     DX         ;увеличиваем значение порта  
    MOV     AL,Green
    OUT     DX, AL     ;заносим зелёную компоненту 
    MOV     AL, Blue
    OUT     DX, AL     ;заносим синюю компоненту 
    loop      @b
    RET
SHIFT_PALETTE ENDP
2
Mikl___
Заблокирован
Автор FAQ
20.08.2013, 17:17  [ТС] #26
Графика DOS. Эффект дождь
Размер COM-файла 701 байт. Во вложении исходный текст, картинка в формате PCX и COM-файл
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
; masm dos com #
.model tiny
.code
.686
org 100h
PALSIZE     equ 256*3
WIDTH_SCREEN    equ 320
HEIGHT_SCREEN   equ 200
SIZESCREEN  equ WIDTH_SCREEN*HEIGHT_SCREEN
VGA_SEGMENT equ 0A000h
PCXBUFSIZE  equ 400h
 
PCX_HEADER struct 
  PCX_manufacturer  db ?;        manufacturer byte (always 0A0h)
  PCX_version       db ?;        pcx Version
  PCX_encoding      db ?;        (always 1)
  PCX_bits_per_pixel    db ?;        color bits per pixel
  PCX_xmin      dw ?;        image origin x
  PCX_ymin      dw ?;        image origin y
  PCX_xmax      dw ?;        image end x
  PCX_ymax      dw ?;        image end y
  PCX_hres      dw ?;        horizontal resolution
  PCX_vres      dw ?;        vertical resolution
  PCX_palette1      db 48 dup(?);(color palette, for older not 256 col vers.)
  PCX_reserved      db ?;        reserved byte
  PCX_color_planes  db ?;        number of color planes
  PCX_bytes_per_line    dw ?;        line buffer size
  PCX_palette_type  dw ?;        grey or color palette indicator
  PCX_reserved2     db 58 dup(?);reserved
PCX_HEADER ends
 
start   proc    
local xcount:word
local ycount:word
local local_bytesperline:word
local readbuf[400h]:byte
local hpcx:PCX_HEADER
local temp:dword
local buf3_seg:word
local LineTable[HEIGHT_SCREEN]:word
 
        mov ax, 13h
        int 10h
        mov dx, 3C4h; индексный регистр последовательного контроллера
        mov al, 4;MEMORY MODE; индекс регистра режима выбора памяти
        out dx, al  
        inc dx
        in  al, dx  
        and al, 0F7h;выключим адресацию по 4 байта
        or  al, 4   ;выключим чет/нечет
        out dx, al  
        mov dx, 3CEh; индексный регистр графического контроллера
        mov al, 5;GRAPHICS MODE ;индекс регистра графического режима
        out dx, al
        inc dx
        in  al, dx      
        and al, 0EFh;выключим чет/нечет
        out dx, al      
        dec dx
        mov al, 6;MISCELLANEOUS;индекс дополнительного регистра
        out dx, al      
        inc dx
        in  al, dx      
        and al, 0FDh;выключим последовательности
        out dx, al      
;разрешим запись во все плоскости, чтобы мы могли очистить по 4 плоскости за один раз
        mov dx, 3C4h; индексный регистр последовательного контроллера
        mov ax,0F02h;2 - индекс регистра маски карты, 0F - 4 плоскости
        out dx,ax; al       
        push    VGA_SEGMENT
        pop es
        xor di, di
        xor ax, ax
        mov cx, 8000h; число слов в 64 Кбайт
        rep stosw; чистим видеопамять
;переключение в режим 320х400 256 цветов не сканируя каждую линию дважды
        mov dx, 3D4h;индексный регистр КЭЛТ
        mov al, 9;MAX_SCAN_LINE;индекс регистра максимальной линии сканирования в КЭЛТ
        out dx, al      
        inc dx
        in  al, dx      
        and al, 0E0h;установим максимальную линию сканирования = 0
        out dx, al      
        dec dx
;изменим сканирование КЭЛТ и перейдем от 4-байтового режима в 1-байтовый, 
;позволив КЭЛТ сканировать более 64 Кбайт видеопамяти
        mov al, 14h;UNDERLINE;индекс регистра позиции подчеркивания в КЭЛТ
        out dx, al      
        inc dx
        in  al, dx      
        and al, 0BFh;выключим 4-байтовый режим
        out dx, al      
        dec dx
        mov al, 17h;MODE CONTROL;регистр управления режимом в КЭЛТ
        out dx, al      
        inc dx
        in  al, dx
        or  al, 40h;включим побайтовый режим, чтобы память 
;сканировалась как бы линейно, как в режимах 10h и 12h
        out dx, al
 
        mov ax, cs
        add ah, 10h
        mov gs,ax
        add ah, 10h
        mov fs,ax
        add ah, 10h
        mov buf3_seg, ax
;SetupLineTable
        push    ss
        pop es
        lea di,LineTable
        xor ax, ax
        mov cx, HEIGHT_SCREEN
@@:     stosw
        add ax, WIDTH_SCREEN
        loop    @b
;RANDOMIZE()
        rdtsc
        mov temp, eax
;CleanSegments
        xor eax, eax
        xor di, di
        push    gs
        pop es; es=buf1_seg
        mov cx, 8000h
        rep stosd       
;load PCX--------------------------------------------------------------
        mov dx,offset filename; OPEN DISK FILE ONLY FOR READ 
        mov ax,3D00h    ;WITH HANDLE
        int 21h     ; DS:DX -> ASCIZ filename
        mov bx, ax
        mov cx, 80h        ;sizeof(hpcx)
        lea dx,hpcx     ;#hpcx
        mov ah, 3Fh; READ FROM FILE WITH HANDLE
        int 21h; BX = file handle, CX = number of bytes to read, DS:DX -> buffer
        mov ax,hpcx.PCX_ymax
        sub ax,hpcx.PCX_ymin
        inc ax
        mov ycount, ax ;ycount=hpcx.PCX_ymax-hpcx.PCX_ymin+1
        mov ax,hpcx.PCX_xmax
        sub ax,hpcx.PCX_xmin
        inc ax
        mov local_bytesperline,ax;local_bytesperline=hpcx.PCX_xmax-hpcx.PCX_xmin+1
        push    fs
        pop     es  ;es=buf2_seg
 
        xor di, di
        mov si, PCXBUFSIZE;SI=PCXBUFSIZE
 
@0:     mov ax,local_bytesperline
        mov xcount,ax   ;xcount=local_bytesperline
 
@1:     cmp si, PCXBUFSIZE        ;IF(SI==PCXBUFSIZE)
        jnz @f
        mov cx,PCXBUFSIZE        
        lea dx,readbuf
        mov ah, 3Fh; READ FROM FILE WITH HANDLE
        int 21h; BX = file handle, CX = number of bytes to read, DS:DX -> buffer
 
        xor si, si
 
@@:     mov al,readbuf[si]
        inc si
        cmp al, 0C0h;IF(AL>=0xC0)
        jb  @f
        and al, 3Fh ;AL &= 0x3F
        xor cx, cx
        mov cl, al
        sub xcount,cx   ;xcount-=CX
        cmp si, PCXBUFSIZE  ;IF(SI==PCXBUFSIZE)
        jnz @2
        push    cx
        mov cx, PCXBUFSIZE
        lea dx,readbuf  ;#readbuf
        mov ah, 3Fh; READ FROM FILE WITH HANDLE, BX = file handle,
        int 21h; CX = number of bytes to read, DS:DX -> buffer
        pop cx
        xor si, si
 
@2:     mov al,readbuf[si]
        inc si
        rep     stosb
        jmp short @3
 
@@:     dec xcount
        stosb
 
@3:     cmp xcount,0
        ja  @1
        dec ycount
        jnz @0
        mov cx, -1
        mov dx,-(PALSIZE+1)
        mov ax, 4202h
        int 21h     ; MOVE FILE READ/WRITE POINTER (LSEEK)
                    ; AL = method: offset from end of file
        mov cx, PALSIZE+1
        lea dx,readbuf;#readbuf
        mov ah, 3Fh; READ FROM FILE WITH HANDLE
        int 21h; BX = file handle, CX = number of bytes to read, DS:DX -> buffer
        lea di,readbuf+1
        mov cx, PALSIZE
 
@@:     shr byte ptr [di], 2
        inc di
        loop    @b
;SETVGADAC(0,,768,,,#readbuf[1])
        xor ax, ax
        mov cx, PALSIZE
        lea si,readbuf+1
        mov dx, 3C8h
        out dx, al
        inc dx
        rep outsb
        mov ah, 3Eh; CLOSE A FILE WITH HANDLE
        int 21h     ; BX = file handle
;CHANGEPICTOR
        xor si, si
        xor dx, dx
        push    VGA_SEGMENT
        pop es
@4: xor di, di
@@:     push    dx
        mov cx, di
;WRITEPIXEL400
        push    di
        imul    di,dx,80
        push    cx
        shr cx, 2
        add di,cx
        pop cx
        and cl, 3
        mov ah, 1
        shl ah, cl
        mov dx, 3C4h; EGA: sequencer address reg
        mov al, 2; sequencer reset.
        out dx, ax; Bits of data 0-1 indicate asynchronous/synchronous reset.
        movs byte ptr es:[di],fs:[si]
        pop di
        pop dx
        inc di
        cmp di, WIDTH_SCREEN
        jb  @b
        inc dx
        cmp dx, HEIGHT_SCREEN
        jb  @4
 
        xor di, di
        mov dx, HEIGHT_SCREEN-1
@5:     xor si, si
@@:     push    dx
        mov cx, si
        push    si
        imul    si,dx,80
        push    cx
        shr cx, 2
        add si, cx
        pop cx
        and cl, 3
        mov ah, cl
        mov dx, 3CEh
        mov al, 4
        out dx, ax      ; EGA: graph 1 and 2 addr reg:
                    ; set/reset.
                    ; Data bits 0-3 select planes for write mode 00
        mov al, es:[si]
        pop si
        pop dx
        mov fs:[di],al
        inc     di
        inc si
        cmp si, WIDTH_SCREEN
        jb  @b
        dec dx
        jns short @5
mainloop:;SetRandomPoint
        mov ax,word ptr temp+2
        mov cx,word ptr temp
        shld    ax,cx,8
        shl     cx,8
        rcr ax, 1
        rcr cx, 1
        add word ptr temp, cx
        adc ax, word ptr temp+2
        add word ptr temp, 25321
        adc ax, 13849
        mov word ptr temp+2, ax
        xor dx, dx
        mov cx, SIZESCREEN
        div cx
        mov     bx, dx
        mov byte ptr gs:[bx], 7Fh
;DoWater
        mov es, buf3_seg
        xor ax, ax
        xor di, di;cx=SIZESCREEN
@6:     xor bx, bx
        lea si, gs:[di-WIDTH_SCREEN-1]
        lods    byte ptr gs:[si]
        add bx, ax
        lods    byte ptr gs:[si]
        add bx, ax
        lods    byte ptr gs:[si]
        add bx, ax
        add si, WIDTH_SCREEN-3
                lods    byte ptr gs:[si]
        add bx, ax
        lods    byte ptr gs:[si]
        add bx, ax
        lods    byte ptr gs:[si]
        add bx, ax
        add si, WIDTH_SCREEN-3
                lods    byte ptr gs:[si]
        add bx, ax
        lods    byte ptr gs:[si]
        add bx,ax
        lods    byte ptr gs:[si]
        add ax,bx
        add si,WIDTH_SCREEN-3
        sub al,gs:[di]
        sbb ah,0
        sar ax,2
        sub al,es:[di]
        jns @f
        xor ax,ax
@@:     stosb
                loop    @6
;DupeToScreen
        push    VGA_SEGMENT
        pop es
        xor si,si
        mov dx,HEIGHT_SCREEN
 
@7:     xor di,di
@@:     xor bx,bx
        mov bl,gs:[si]
        push    si
        mov ax,si
        shl bx,1
        mov si,bx
        add si,LineTable[si]
        add si,ax
        push    dx
        mov cx,di
;WRITEPIXEL400
        push    di
        imul    di,dx,80
        push    cx
        shr cx,2
        add di,cx
        pop cx
        and cl,3
        mov ah,1
        shl ah,cl
        mov dx,3C4h   ; EGA: sequencer address reg
        mov al,2      ; sequencer reset.
        out dx,ax   ; Bits of data 0-1 indicate asynchronous/synchronous reset.
        movs    byte ptr es:[di],fs:[si]
                pop di
        pop dx
        pop si
        inc si
        inc di
        cmp di,WIDTH_SCREEN
        jb  @b
        inc dx
        cmp dx,HEIGHT_SCREEN*2
        jb  @7
        push gs
        push buf3_seg
        pop gs  ; buf1_seg <--> buf3_seg
        pop buf3_seg
        mov ah, 1
        int 16h     ; KEYBOARD - CHECK BUFFER, DO NOT CLEAR
        jz  mainloop
        mov ax, 3
        int 10h     ; - VIDEO - SET VIDEO MODE
        int 20h
start   endp
filename db 'forest.pcx',0
end start
3
murderer
3202 / 1426 / 75
Регистрация: 06.10.2010
Сообщений: 3,145
01.09.2013, 09:37 #27
Заставка дождь.
Продолжаем разговор на тему "Эффект след от водомерки на поверхности воды". DOS - это конечно хорошо, но мы живём в эпоху AMD64, а не X86. В этой статье я опишу процесс создания программы-заставки под Windows с использованием инструкций SSE и FMA3, ну и конечно прикрутим шейдеры.

Программа заставка - это обычный PE с расширением scr. Но небольшие различия всё-таки есть
  1. В ресурсах программы должна находиться строка с названием заставки (это нужно для того, чтобы название отображалось не зависимо от имени файла).
  2. При запуске заставки Windows может передать один из трёх параметров:
    • /c - запуск диалогового окна с настройками заставки. Выход из этого режима происходит при закрытии диалогового окна.
    • /p - запуск предварительного просмотра. После этого параметра через пробел идёт хендл окна предпросмотра в десятичном представлении. Выход из этого режима происходит при скрытии окна предпросмотра (проверяем через IsWindowVisible).
    • /s - запуск полноэкранного просмотра. Выход из этого режима происходит при проявлении активности пользователя.


Я буду использовать тот же алгоритм, что описал Mikl___, но для получения более крупных волн карта высот будет в 16 раз меньше текстуры.

Генерация капли.

В связи с особенностями реализации капля не должна падать слишком близко к краям экрана (не ближе радиуса волны) иначе волна будет уходить за правый край и выходить с левого. Экспериментально я установил, что радиус волны приблизительно равен 50 пикселям. Проинициализируем регистры перед основным циклом.
Assembler
1
2
3
cvtpi2ps xmm7,qword[TexWidth]
cvtpi2ps xmm8,qword[TexWidth-4] ;xmm8 - {1; Width}
subps    xmm7,dqword[Border]    ;xmm7 - {Width-100; Height-100}
Теперь уже в цикле генерируем каплю. Координаты капли вычисляются так
Код
x = random mod (Width-100) + 50
y = random mod (Height-100) + 50
На самом деле не обязательно учитывать радиус волны при расчёте y-координаты - я это сделал для симметрии. Целью данного урока было попрактиковаться в программировании SSE/FMA, поэтому перепишем формулу для вещественных чисел.
Код
x = round(frac(random/(Width-100))*(Width-100)+50)
y = round(frac(random/(Height-100))*(Height-100)+50)
На первый взгляд может показаться, что формула очень усложнилась, но давайте посмотрим как это будет на ассемблере
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
rdtsc
xor    al,ah 
test   al,15
jne @f
    ;Генерируем случайные координаты
    shr         eax,1
    mov         [esp-8],eax
    mov         [esp-4],eax
    cvtpi2ps    xmm0,[esp-8]                 ;xmm0 = {           random,                                         random                               }
    divps       xmm0,xmm7                    ;xmm0 = {           random/(Width-100),                             random/(Height-100)                  }
    vfrczps     xmm0,xmm0                    ;xmm0 = {      frac(random/(Width-100)),                       frac(random/(Height-100))                 }
    vfmadd213ps xmm0,xmm7,dqword[HalfBorder] ;xmm0 = {      frac(random/(Width-100))*(Width-100)+50,        frac(random/(Height-100))*(Height-100)+50 }
    roundps     xmm0,xmm0,0                  ;xmm0 = {round(frac(random/(Width-100))*(Width-100)+50), round(frac(random/(Height-100))*(Height-100)+50)}
    ;Вычисляем адрес
    dpps        xmm0,xmm8,110001b            ;xmm0 = xmm0[1]*Width+xmm0[0]*1.0
    cvtss2si    eax,xmm0
    ;Бросаем каплю
    mov         word[rsi+rax],$FFFF
    add         eax,[TexWidth]
    mov         word[rsi+rax],$FFFF
@@:
В качестве ГПСЧ я использовал инструкцию rdtsc. Однако в новых процессорах Intel Broadwell появится инструкция rdrand для получения случайного (не псевдо) числа. Я не стал её использовать т.к. эмулятор Broadwell не работает с OpenGL, а мой процессор не поддерживает rdrand.

Размытие карты высот

Тут проще
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
mov    rcx,<размер карты высот - ширина*2>
center:movd    xmm6,[rdi+rcx+Width+1]     ;центральный пиксель [x,y]
    up:pinsrd  xmm4,[rsi+rcx-1],0         ;верхние 3 пикселя  {[x-1,y-1],[x,y-1],[x+1,y-1]}
  down:pinsrd  xmm4,[rsi+rcx+Width*2-1],1 ;нижние 3 пикселя   {[x-1,y+1],[x,y+1],[x+1,y+1]}
  left:pinsrb  xmm4,[rsi+rcx+Width],0     ;левый пиксель       [x-1,y]
 right:pinsrb  xmm4,[rsi+rcx+Width+2],4   ;правый пиксель      [x+1,y]
       psadbw  xmm4,xmm5                  ;xmm4 = <сумма 8 соседних пикселей>; xmm5 = 0
       psrld   xmm4,2                     ;xmm4 = xmm4/4
       psubusb xmm4,xmm6                  ;xmm4 = <сумма 8 соседних пикселей>/4-<центральный пиксель>
result:pextrb  [rdi+rcx+Width+1],xmm4,0   ;сохраняем результат
       dec     rcx
jns center
xchg   rsi,rdi ;меняем указатели местамих.
Однако ширина карты высот нам не известна, т.к. заставка универсальная и может запускаться на любом разрешении. Чтобы не добавлять в цикл новые инструкции - просто модифицируем код, подставив нужные значения вместо Width (смотри в аттаче).

Окончательный рендер

За расчёт смещений отвечает несложный шейдер
Assembler
1
2
3
4
5
6
7
8
9
10
11
!!ARBfp1.0
TEMP coord1,coord2,texcoord;
MUL  texcoord,fragment.position.xyxy,program.local[1].xyxy; ;вычисляем текстурные координаты texcoord={FragCoord.x/ScreenWidth,FragCoord.y/ScreenHeight}
TEX  coord1,texcoord,texture[1],2D;                         ;coord1   =HeightMap[y,x]
SUB  coord2,texcoord.xyxy,program.local[0];                 ;coord2   ={x-1,y,x,y-1}
TEX  coord2.x,coord2.xyxy,texture[1],2D;                    ;coord2.xy=HeightMap[y,x-1]
TEX  coord2.y,coord2.zwzw,texture[1],2D;                    ;coord2.zw=HeightMap[y-1,x]
SUB  coord1,coord1.xxxx,coord2;                             ;coord1   ={HeightMap[y,x]-HeightMap[y,x-1], HeightMap[y,x]-HeightMap[y-1,x]}
MAD  texcoord,coord1,{0.25,0.25,0.25,0.25},texcoord;        ;texcoord ={x+(HeightMap[y,x]-HeightMap[y,x-1])*0.25, y+(HeightMap[y,x]-HeightMap[y-1,x])*0.25}
TEX  result.color,texcoord,texture[0],2D;
END
Как видите шейдер даже не требует указания текстурных координат. В парметры передаём следующее
Код
program.local[0] = {1/Width, 0, 0, 1/Height}
program.local[1] = {1/ScreenWidth, 1/ScreenHeight}
Установка заставки осуществляется через контекстное меню.


ссылки:
Описание инструкций CPU
ARB Fragment Program
Соглашение fastcall x64
Использование регистров в fastcall x64
6
Mikl___
Заблокирован
Автор FAQ
03.09.2013, 04:14  [ТС] #28
Графика DOS - эффект "линза" + сталкивающиеся шары
Если линза на экране - окружность (x0,y0,r0), то в точках (x,y), для которых (x-x0)2+(y-y0)2http://www.cyberforum.ru/cgi-bin/latex.cgi?\leqr02,рисуется точка, которая, если бы линзы не было, изображалась бы в точке (x0+(x-x0)*k,y0+(y-y0)*k), где http://www.cyberforum.ru/cgi-bin/latex.cgi?k= \frac{const_{1}}{\sqrt{(x-x_{0})^{2}+(y-y_{0})^{2}}+ const_{2}}. Можно заранее рассчитать таблицу смещений [-r0..r0,-r0..r0].
Размер COM-файла 1030 байт. Исходный текст, СОМ-файл, картинка в формате pcx во вложении
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
; masm dos com #
.model tiny
.code
.686
org 100h
NUMBALL     equ 3
PCXBUFSIZE  equ 400h
PALSIZE     equ 256*3
WIDTH_SCREEN    equ 320
HEIGHT_SCREEN   equ 200
SIZESCREEN  equ WIDTH_SCREEN*HEIGHT_SCREEN
VGA_SEGMENT equ 0A000h
IMPUT_STATUS_0  equ 3DAh    ;регистр статуса ввода 0
 
BALLINFO struct 
    X dw ?;+0
    Y dw ?;+2
    R dw ?;+4
    xD dw ?;+6
    yD dw ?;+8
    Clinch dw ?;+10
BALLINFO ends
PCX_HEADER struct 
  PCX_manufacturer db ?; manufacturer byte (always 0A0h)
  PCX_version db ?; pcx Version
  PCX_encoding db ?;(always 1)
  PCX_bits_per_pixel db ?;     color bits per pixel
  PCX_xmin dw ?;           image origin x
  PCX_ymin dw ?;               image origin y
  PCX_xmax dw ?;               image end x
  PCX_ymax dw ?;               image end y
  PCX_hres dw ?;               horizontal resolution
  PCX_vres dw ?;               vertical resolution
  PCX_palette1 db 48 dup(?);   (color palette, for older not 256 col vers.)
  PCX_reserved db ?;           reserved byte
  PCX_color_planes db ?;       number of color planes
  PCX_bytes_per_line dw ?;     line buffer size
  PCX_palette_type dw ?;       grey or color palette indicator
  PCX_reserved2 db 58 dup(?);  reserved
PCX_HEADER ends
 
start   proc
local   z:dword
local   i:word
local   hpcx:PCX_HEADER
local   xcount:word
local   ycount:word
local   local_bytesperline:word
local   readbuf[400h]:byte
 
        mov ax, 13h
        int 10h     ; - VIDEO - SET VIDEO MODE
        mov ax, cs
        add ax, 1000h
        mov segm_buf,ax ; segm_buf=CS+0x1000
        mov es,ax;es=segm_buf
        add ax, 1000h
        mov seg2, ax    ; seg2=AX+0x1000
        rdtsc
        mov temp,eax; RANDOMIZE()
;загрузить PCX-файл---------------------------------------------------------
        mov dx,offset filename; DS:DX -> ASCIZ filename
        mov ax,3D00h; OPEN DISK FILE for read WITH HANDLE
        int 21h
        mov bx,ax;BX = file handle
        mov cx, 80h;CX = number of bytes to read
        lea dx,hpcx; DS:DX -> buffer
        mov ah, 3Fh; READ FROM FILE WITH HANDLE  
        int 21h
        mov ax, hpcx.PCX_ymax
        sub ax, hpcx.PCX_ymin
        inc ax
        mov ycount,ax
        mov ax,hpcx.PCX_xmax
        sub ax,hpcx.PCX_xmin
        inc ax
        mov local_bytesperline, ax
        xor di, di
        mov si, PCXBUFSIZE
@1:     mov ax,local_bytesperline
        mov xcount,ax
@2:     cmp si,PCXBUFSIZE
        jnz @f
        mov cx, PCXBUFSIZE  ;CX = number of bytes to read 
        lea dx, readbuf ;DS:DX -> buffer
        mov ah, 3Fh; READ FROM FILE WITH HANDLE BX = file handle, 
        int 21h
        xor si, si
@@:     mov al, readbuf[si]
        inc si
        cmp al, 192
        jb  short @3
        and al, 3Fh
        xor cx, cx
        mov cl, al
        sub xcount, cx
        cmp si, PCXBUFSIZE
        jnz @f
        push    cx
        mov cx, PCXBUFSIZE
        lea dx, readbuf
        mov ah, 3Fh; READ FROM FILE WITH HANDLE BX = file handle, 
        int 21h; CX = number of bytes to read DS:DX -> buffer
        pop cx
        xor si, si
@@:     mov al,readbuf[si]
        inc si
        rep stosb
        jmp @f
@3:     dec xcount
        stosb
@@:     cmp xcount,0
        ja  short @2
        dec ycount
        jnz short @1
        or  cx,-1
        mov dx,-(PALSIZE+1)
        mov ax, 4202h; MOVE FILE READ/WRITE POINTER (LSEEK)
        int 21h     ; AL = method: offset from end of file
        mov cx,PALSIZE+1    ; CX = number of bytes to read 
        lea dx,readbuf  ; DS:DX -> buffer
        mov ah, 3Fh     ; READ FROM FILE WITH HANDLE
        int 21h     ; BX = file handle
        lea di, readbuf+1
        mov cx, PALSIZE
@@:     shr byte ptr [di], 2
        inc di
        loop    @b
        xor ax, ax
        mov cx, PALSIZE
        lea si, readbuf+1
        mov dx, 3C8h
        out dx, al
        inc dx
        rep outsb
        mov ah, 3Eh; CLOSE A FILE WITH HANDLE
        int 21h     ; BX = file handle
;инициализируем шары--------------------------------------
        mov i,0
@0:     imul    di,i,sizeof(BALLINFO)
        add di,offset ballinfox 
        call    RAND
        and ax,1Fh
        add ax,10
        assume di: ptr BALLINFO
        mov [di].R, ax
@6:     mov bx,[di].R
        add bx,bx
        mov ax,WIDTH_SCREEN
        sub ax,bx
        mov [di].X,ax
        sub ax,(WIDTH_SCREEN-HEIGHT_SCREEN)
        mov [di].Y,ax
        call    RAND
        xor dx,dx
        div word ptr [di].X
        add dx,[di].R
        mov [di].X, dx
        call    RAND
        xor dx,dx
        div word ptr [di].Y
        add dx,[di].R
        mov [di].Y,dx
;вычисляем расстояние--------------------------------
        xor cx, cx
B1:     cmp i,cx
        jbe short B9
        imul    si,cx,sizeof(BALLINFO)
        add si, offset ballinfox    
        assume si: ptr BALLINFO
        mov ax,[di].X
        sub ax,[si].X
        jnb short @f
        neg ax
@@:     movzx   eax, ax
        mul eax
        mov z,eax
        mov ax,[di].Y
        sub ax,[si].Y
        jnb short @f
        neg ax
@@:     movzx   eax,ax
        mul eax
        add z,eax
        fild    z
        fsqrt
        fistp   z
        mov ax,[si].R
        add ax,[di].R
        cmp word ptr z,ax
        ja  short @f
        mov ax, cx
        jmp short BC
@@:     inc cx
        jmp short B1
B9:     or  ax,-1
BC:     inc ax  ;cmp ax,0FFFFh
        jnz @6
;выбираем направление-------------------------------------
        call    RAND
        and ax,1
        jnz @f
        dec ax
@@:             mov [di].xD,ax
;выбираем направление-------------------------------------
        call    RAND
        and ax, 1
        jnz @f
        dec ax
@@:             mov [di].yD,ax
        mov [di].Clinch,0
        inc i
                cmp i,NUMBALL
        jl  @0
main_loop:  mov dx,IMPUT_STATUS_0;тормозим вывод на экран до следующего кадра
WaitVerticalSync:in al,dx
        test al,8
        jz WaitVerticalSync
WaitNotVerticalSync:in al,dx
        test al,8
        jnz WaitNotVerticalSync
        push    ds
        mov es,seg2
        mov ds,segm_buf
        xor si,si
        xor di,di
        mov cx,SIZESCREEN/4
        rep movsd
        pop ds
        mov i,cx    ;cx=0
@@:     imul    si,i,sizeof(BALLINFO)
        add si,offset ballinfox 
        push    [si].X
        push    [si].Y
        push    [si].R
        call    Ball
        inc i
        cmp i,NUMBALL
        jl  @b
        push    ds
        push    VGA_SEGMENT
        pop es
        mov ds,seg2
        xor si,si
        xor di,di
        mov cx,SIZESCREEN/4
        rep movsd
        pop ds
        xor bx,bx
loc_10512:  imul    di,bx,sizeof(BALLINFO)
        add di,offset ballinfox
        mov ax,[di].X
        add ax,[di].R
        add ax,[di].xD
        cmp ax,WIDTH_SCREEN
        jbe @f
        mov word ptr [di].xD,-1
        jmp short @8
@@:     mov ax,[di].X
        sub ax,[di].R
        add ax,[di].xD
        jge @8
        mov word ptr [di].xD,1
@8:     mov ax,[di].Y
        add ax,[di].R
        add ax,[di].yD
        cmp ax,HEIGHT_SCREEN
        jbe @f
        mov word ptr [di].yD,-1
        jmp @9
@@:     mov ax,[di].Y
        sub ax,[di].R
        add ax,[di].yD
        jge @9
        mov word ptr [di].yD,1
@9:     xor cx, cx
@4:     cmp bx,cx
        jbe loc_10509
        imul    si,cx,sizeof(BALLINFO)
        add si,offset ballinfox 
        mov ax,[di].X
        add ax,[di].xD
        sub ax,[si].X
        jnb @f
        neg ax
@@:     movzx   eax, ax
        mul eax
        mov z,eax
        mov ax,[di].Y
        add ax,[di].yD
        sub ax,[si].Y
        jnb @f
        neg ax
@@:     movzx   eax,ax
        mul eax
        add z,eax
        fild    z
        fsqrt
        fistp   z
        mov ax,[si].R       
        add ax,[di].R
        cmp word ptr z,ax
        ja  @f
        mov ax,cx
        jmp locret_1050C
@@:     inc cx
        jmp @4
loc_10509:  or  ax,-1
locret_1050C:   inc ax;cmp  ax, 0FFFFh
        jz  loc_105CB
        imul    si,ax,sizeof(BALLINFO)
        add si, offset ballinfox - sizeof(BALLINFO)
        mov bx,[di].Clinch
        cmp bx,NUMBALL 
        ja  loc_105BB
        add bx, bx
        jmp switchtable[bx]
 
case0::     neg [si].xD
case1::     neg [di].xD
case2::     neg [si].yD
case3::     neg [di].yD
loc_105BB:  mov ax, [si].xD
        add [si].X,ax
        mov ax,[si].yD
        add [si].Y,ax
        inc [di].Clinch
        jmp loc_105D0
 
 
loc_105CB:  mov [di].Clinch, 0
loc_105D0:  mov ax,[di].xD
        add [di].X,ax
        mov ax,[di].yD
        add [di].Y,ax
        inc bx
        cmp bx,NUMBALL 
        jb  loc_10512
        assume  si: nothing
                assume  di: nothing
        mov ah, 1
        int 16h
        jz  main_loop   
        mov ax, 3
        int 10h     ; - VIDEO - SET VIDEO MODE
        int 20h
start   endp
Ball        proc 
 
Rad_V_Kv    = word ptr -6
Y       = word ptr -4
Z       = word ptr -2
sr      = word ptr  4
Yc      = word ptr  6
Xc      = word ptr  8
 
        enter   6, 0
        push    ds
        mov ax,[bp+sr]
        imul    ax
        mov [bp+Rad_V_Kv],ax
        mov es,seg2
        mov ds,segm_buf
        mov ax,[bp+sr]
        neg ax
        mov [bp+Y],ax   ;Y = -sr
@5:     mov cx,[bp+sr]
        neg cx
@7:     mov bx,cx
        imul    bx,cx          ;bx = (sr)^2
        mov ax,[bp+Y]
        mul ax
        add bx,ax          ;bx = (sr)^2 + Y^2
        cmp [bp+Rad_V_Kv],bx
        jb  @f
        mov ax, [bp+Rad_V_Kv]
        sub ax, bx
        mov [bp+Z], ax
        mov bx, [bp+Xc]
        add bx, cx
        js  @f
        cmp bx, WIDTH_SCREEN
        jnb @f
        mov ax, [bp+Yc]
        add ax, [bp+Y]
        js  @f
        cmp ax, HEIGHT_SCREEN
        jnb @f
        imul    di,ax,WIDTH_SCREEN
        add di, bx
        fild    [bp+Z]
        fsqrt
        fistp   [bp+Z]
        mov bx, [bp+Z]
        add bx, [bp+sr]
        mov ax, [bp+Y]
        imul    [bp+sr]
        idiv    bx
        add ax, [bp+Yc]
        imul    si,ax,WIDTH_SCREEN
        mov ax, cx
        imul    [bp+sr]
        idiv    bx
        add ax, [bp+Xc]
        add si,ax
        movsb
@@:     inc cx
                cmp [bp+sr], cx
        jg  @7
        inc [bp+Y]
        mov ax, [bp+sr]
        cmp [bp+Y], ax
        jl  @5
        pop ds
        leave
        retn    6
Ball        endp
 
RAND        proc 
        mov ax,word ptr temp+2
        mov cx,word ptr temp
        shld    ax,cx,8
        shl cx,8
        rcr ax, 1
        rcr cx, 1
        add word ptr temp, cx
        adc ax,word ptr temp+2
        add word ptr temp,25321
        adc ax,13849
        mov word ptr temp+2,ax
        retn
RAND        endp
 
switchtable dw case0,case1,case2,case3
filename    db 'forest.pcx',0
segm_buf    dw ?
seg2        dw ?
ballinfox   BALLINFO <?>,<?>,<?>
temp        dd ?
end start
Результат
4
Mikl___
Заблокирован
Автор FAQ
10.09.2013, 12:36  [ТС] #29
Что такое стек и для чего он нужен?
Стек — специально отведенная программе область памяти для хранения временных данных, содержимого регистров или адресов команд, следующих за командами call (при вызове процедур). На вершину стека (т.е. последнее записанное значение) указывает пара регистров ss:sp (или ss:esp для 32-разрядных программ). При записи слова (или двойного слова) в стек (командами push, call), оно записывается в память по адресу ss:[sp-2] (или ss:[sp-4], в зависимости от разрядности записываемого значения), а затем из регистра sp/esp вычитается 2 (или 4), то есть стек "растет" в сторону младших адресов памяти. При извлечении значения из стека (командами pop, ret) происходит обратная операция. Для чего это нужно? Предположим, Вам необходимо сохранить значение регистра (допустим, ax), которое понадобиться через несколько команд. Для этого надо выполнить команду push ax, а в том месте, где это значение понадобится — pop ax (или, скажем, pop dx, если Вам необходимо записать это значение в регистр dx). Но! Важно помнить, что запись в стек производится по принципу LIFO (Last In, First Out — последним зашел, первым вышел). Нельзя делать так: push ax ... call ... pushf, а затем pop ax ... popf ... ret, так как в этом случае в регистр ax будут занесено значение регистра флагов, в регистр флагов — адрес возврата из процедуры, а после выполнения команды ret управление перейдет по адресу, который будет взят из сохраненного регистр ax. Таким образом, данные записываются последовательно, а не каждый регистр по определенному адресу! В стек может быть записано как 16-битное, так и 32-битное значение, но не 8-битное. Соответственно, при записи 16-битных значений будет записано 2 байта, а при записи 32-битных значений - 4 байта. Но есть одно исключение: при записи в стек сегментных регистров (ds, es, cs, ss, fs или gs) 32-битные программы (например, программы, работающие под Windows) будут заносить не 2, а 4 байта(!), несмотря на то, что размер сегментных регистров составляет 16 бит. При этом на процессорах Pentium и выше старшее записываемое слово будет содержать нулевое значение. Что же касается команд типа call и ret, то они работают со значениями, размер которых соответствует размеру сегмента кода (то есть 16 бит для DOS-программ, 32 бита — для Win32). Компиляторы ассемблера предлагают несколько модификаций одних и тех же команд, работающих со стеком. Например, команды pusha/popa предназначены для сохранения и восстановления всех (восьми) регистров общего назначения (16-битных значений ax, cx, dx, bx, sp, bp, si и di либо 32-битных eax, ecx и так далее в зависимости от разрядности сегмента кода), а их модификации pushaw/popaw и pushad/popad — 16- и 32-битных соответственно независимо от разрядности сегмента. Что же касается команд ret и retf, то они не имеют модификаций и всегда вынимают из стека столько байт, сколько соответствует текущему размеру сегмента, то есть соответственно 2 и 4 для 16-битных сегментов кода и 4 и 8 для 32-битных. Похожая команда iret имеет модификации iretw и iretd в TASM и только iretd в MASM, причем MASM интерпретирует команду iret как 16-битную (даже в 32-битных программах), а iretd — как 32-битную. Аналогичная ситуация с командами pushf/popf и pushfw/popfw, pushfd/popfd. Записать в стек можно как значение регистра, так и значение из памяти (например, push es:[bx]) и число (push 4000h)

Что такое процедуры и зачем они нужны?
Процедура — некоторая область кода, которая вызывается специальным образом. Механизм вызова отделяет этот код от остального кода. Прежде чем дать определение процедуре, следует рассмотреть ее свойства. Процедура вызывается инструкцией CALL1. При этом в стеке сохраняется адрес возврата (адрес следующей за ветвлением инструкции) и управление передается на процедуру. При возврате инструкцией RET адрес извлекается из стека и на него выполняется переход, при этом из стека может удалится N байт, если инструкция имела вид RET N

В процедуре могут использоваться локальные переменные. Это некоторая область в стеке, "выше" начала которой адрес дна стека не может быть увеличен без возврата из процедуры и разрушения локальных переменных. Для адресации локальных переменных используется регистр ЕВР. Так как архитектура NT требует сохранения в процедурах регистров EBX, ESI, EDI и EBP, то вначале процедуры в стеке сохраняется регистр EBP, затем в этот регистр загружается текущее значение ESP, в стеке аллоцируется пространство под локальные переменные. Далее в пределах процедуры до возврата из нее, значение в регистре EBP не изменяется. Таким образом, этот регистр адресует предыдущее значение регистра EBP (предыдущей процедуры), ниже в стеке рассположены локальные переменные и значения регистров, которые процедура сохранила в стеке при входе и восстановит при выходе, выше адрес возврата из процедуры, еще выше — аргументы переданные процедуре, если у функции есть аргументы, либо локальные переменные принадлежащие "материнской" процедуры, которая вызывала "нашу" процедуру. Начало прцедуры выглядит следующим образом:
Assembler
1
2
3
4
5
6
7
8
push ebp ;сохраняем предыдущее значение регистра ЕВР
mov ebp,esp ; загружаем ссылку в ЕВР
sub esp,32 ;выделяем в стеке 32 байта под локальные переменные
...
mov esp,ebp ;восстанавливаем стек. На дне лежит предыдущее значение регистра ЕВР, 
;выше - адрес возврата из процедуры
pop ebp ; восстанавливаем значение регистра EBP
ret 16 ; удаляем из стека четыре параметра типа DWORD и возвращаемся из процедуры
Стек после выполнения инструкции sub esp,32
ESP -> ... 
 Localsлокальные переменные
EBP -> rEbp
предыдущее значение регистра ЕВР, сохраненное
инструкцией push ebp в начале процедуры
 lpадрес возврата, сохраненный инструкцией Call
 Arg1первый параметр процедуры
 Arg2
второй параметр процедуры (в stdcall-конвенции
вызова аргументы передаются справа налево)
 ... 
Два слова в стеке всегда будут находится рядом, это адрес возврата из функции и сохраненное значение регистра ЕВР. Эта часть стека называется базовым стековым фреймом — Bp-frame2 и описывается структурой STACK_FRAME
Assembler
1
2
3
4
STACK_FRAME struct
rEbp PVOID ? ; предыдущее значение EBP
rEip  PVOID ? ; адрес возврата из процедуры
STACK_FRAME ends
Эту структуру адресует регистр ЕВР в любой момент в пределах текущей процедуры. Очевидно для иной процедуры, вызываемой из данной, в этой структуре будет сохранена ссылка на текущий фрейм, таким образом формируется цепочка стековых фреймов (SFS). Поэтому изменим имена полей структуры следующим образом
Assembler
1
2
3
4
5
STACK_FRAME struct
Next PVOID ? ; ссылка на фрейм (РSTACK_FRAME) предыдущей процедуры, которая
;вызвала текущую. Это предыдущее значение регистра EBP
lp  PVOID ? ; адрес возврата из текущей процедуры
STACK_FRAME ends
Для определения адреса возврата из предыдущей процедуры необходимо взять ссылку на ее стековый фрейм из стекового фрейма текущей процедуры, то есть
lp = STACK_FRAME.Next.lp[ebp]
Такой проход по цепочке стековых фреймов называется бэктресом (back trace). Он может выполняться по достижению последнего стекового фрейма, при этом ссылка на следующий фрейм инвалидна (указывает за пределы стека3)
Относительно начала цепочки стековых фреймов (текущий фрейм, ссылка на который находится в регистре ЕВР контекста) у каждого фрейма есть свой номер. Этот номер определяет, сколько итераций бэктрейса необходимо выполнить для достижения необходимого стекового фрейма и называется номером стекового фрейма (Stack Frame Number — SFN). Так как в пределах текущей процедуры значение ЕВР не меняется, то можно сказать, что код процедуры исполняется на некотором уровне относительно других процедур, от которого зависит SFN (этот уровень равен ΔSFN двух процедур). Этот уровень называется уровнем вложенности процедур (Nesting Level — NL)4.
Важные свойства процедур:
  • NL не зависит от стека, это значение определяется расположением процедур в коде
  • Одно процедурное ветвление повышает NL на 1
  • Одна инструкция Ret понижает NL на 1
  • SFN зависит от NL, то есть SFN=f(NL). При изменении SFN на некоторое значение, NL изменится на то же (все процедуры Bp-based) или большее значение (ΔSFN <= ΔNL). Из данного свойства вытекает следующее — зная NL (он фиксирован) и текущий Bp-фрейм можно определить стековый фрейм искомой процедуры, выполнив NL итераций бэктрейса и таким образом можно получить локальные переменные искомой процедуры, адрес возврата и аргументы
  • Число итераций бэктрейса, необходимых для достижения фрейма целевой процедуры относительно текущей, равно NL текущей процедуры относительно целевой
  • Число инструкций Ret необходимых для возврата в целевую процедуру из текущей равно NL текущей процедуры относительно целевой5
  • Для всех инструкций, начиная от следующих за формирующими Bp-фрейм и до инструкций удаляющих Bp-фрейм, NL остается неизменным (NL=Const) и значение регистра EBP в контексте не изменяется
  • Адрес возврата из процедуры зависит от NL (так как от NL зависит SFN), то есть — lp=f(NL). Область стека процедуры, включая аргументы процедуры, базовый стековый фрейм, локальные переменные и рабочую область стека называют полным стековым фреймом.
  • Базовый стековый фрейм (EBP) находится в стеке выше, чем рабочая область (ESP)
  • Смещение N-ой локальной переменной относительно базового стекового фрейма постоянно и отрицательно (локальные переменные расположены ниже Bp-фрейма)
  • Смещение N-ого аргумента процедуры относительно базового стекового фрейма постоянно и равно
    sizeof(STACK_FRAME) + 4*N
  • Если процедура изменяет регистры EBX, ESI или EDI, то эти регистры должны сохраняться во фрейме по фиксированному для данной процедуры смещению
Теперь можно дать определение процедуры — участок кода, являющийся ветвью, используемый на определенном NL, более высоком, чем у вызывающего кода и содержащий механизм возврата в вызывающий код называется процедурой6) Соответственно инструкция, завершающая вызов процедуры называется процедурным ветвлением.
Стековая маршрутизация
Стековый фрейм содержит адрес возврата из текущей процедуры, следующий фрейм содержит адрес возврата из вызывающей процедуры и так далее, то можно заменить во фрейме адрес возврата. Тогда после снижения NL (SFN) до нулевого уровня (относительно текущей процедуры), из стека будет извлечен соответствующий процедуре фрейм и выполнен возврат на адрес, находящийся в этом фрейме. Так как он изменен, то возврат произойдет на новый адрес. Эта манипуляция называется процедурным post-редиректом или стековой маршрутизацией.
____________________________________________

1 Процедурное ветвление в текущем кодовом сегменте осуществляется инструкцией Call near. Межсегментное ветвление — Call far (селектор описывает кодовый сегмент, а не шлюз вызова) сохраняют в стеке помимо адреса возврата также и селектор кодового сегмента (CS:EIP), куда будет выполнен возврат инструкцией RETF.

2 Для формирования и удаления BP-фреймов предназначены инструкции enter и leave соответственно

3 База и лимит стека находятся в TIB, поля StackBase и StackLimit, смещения которых соответственно +4 и +8 (в ks386 описываются константы с префиксом Tb или Te). В Kernel mode TIB находится в начале PCR, в User mode — в начале TEB. Учитывая, что TEB и PCR адресуются через один и тот же регистр FS, то мод не имеет значения. FS:[+4] в обоих режимах будет адресовать базу стека.

4 По сути это уровень определяющий сколько раз был вызван участок кода через некоторый механизм вызова. В данном случае это процедурное ветвление. Примером не процедурного ветвления может быть ISR — хэндлер прерывания является процедурой, но вызывается через особые механизмы. Непосредственно через инструкции Int, либо косвенно, когда вектор прерывания вызывается при некотором событии (прерывания и исключения). Другим примером могут служить быстрые вызовы сервисных функций ядра посредством инструкций Sysenter. При этом используется совершенно другой механизм (использование MSR), в отличие от прерываний. Важной особенностью процедуры является наличие механизма возврата. Для процедурного ветвления используется инструкция Ret, для прерываний — Iret, для возврата из быстрых сервисных вызовов — инструкция Sysexit и т.д. Механизм вызова и возврата из процедуры не обязательно должен использовать стек (пример — Sysexit). Для ISR NL будет определяться рекурсивным обращением к IDT, например, возникновение #GP из хэндлера #GP увеличит NL

5 Все процедуры Bp-based, то есть формируют стековые фреймы.
5
Mikl___
Заблокирован
Автор FAQ
12.09.2013, 06:11  [ТС] #30
Кто и зачем до сих пор использует DOS. Какой смысл сейчас писать что-либо под эту ОС?
  • Под DOS программировать во многом проще. Структура работы логична и понятна, исходники открыты. Для новичков – DOS лучший полигон для работы.
  • По привычке. Многие начинали программировать, когда еще не было Windows 98/95/NT/XP и прочего.
  • Маленький размер и быстрая работа откомпилированных программ. Маленький размер исходников.
  • Возможность пощупать код на низком уровне, дабы лучше разобраться в работе программы, а не строить её из «кубиков», как часто это делается в Delphi\Buider, после чего непонятно с чем связана та или иная ошибка
  • Пока еще не все машины имеют на борту Windows\Linux, поэтому не стоит хоронить DOS
3
12.09.2013, 06:11
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
12.09.2013, 06:11
Привет! Вот еще темы с ответами:

Подскажите книгу для программирования под Win32, MASM/TASM с большим количеством примеров - Assembler
Ув. форумчане, подскажите пожалуйста какую-нибудь обучающую книгу для программирования на ассемблере под Win32, MASM/TASM с большим...

Какие существуют среды программирования Assebler'a для процессоров ix86 помимо tasm, fasm, masm - Assembler
Какие существуют среды программирования Assebler'a для процессоров ix86 помимо tasm, fasm, masm? Посоветуйте литературу по этому вопросу?...

MASM,TASM - Assembler
Очевидно, что программирование на макроассемблере легче для восприятия чем на уровне команд процессору, но значит ли это что екзешники в...

TASM-MASM - Assembler
Здравствуйте стоит задача, написать программу на MASM32 ( Отображение списка файлов в каталоге ). С MASM'ом я раньше не сталкивался...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
30
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru