FAQ для раздела Assembler, MASM, TASM - Assembler - Ответ 4847854

Анимация в 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

Нужно добавить все написанные функции и можно компилировать и запускать:

Результат:

Полный код и сом-файл —

здесь

В репозитории находится также makefile. Для полной компиляции достаточно написать:

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

Вернуться к обсуждению:

FAQ для раздела Assembler, MASM, TASM Assembler