На данный момент существует огромное количество различных редакторов, программ или мобильных приложений, позволяющих взаимодействовать с графикой. Как правило, разрабатываются они для различных целей и пишутся на совершенно разных языках программирования. Столь огромный спектр различных редакторов ставит перед разработчиком выбор наиболее подходящей для конкретной задачи среды программирования для создания и обработки графики. Все языки программирования во многом отличаются друг от друга, а, значит, каждый из них имеет как свои преимущества в плане графики, так и недостатки. В этой статье рассмотрим графику на языке ассемблера.
Ассемблер – достаточно сложный низкоуровневый язык программирования, поэтому работа с графикой в нем – процесс настолько же сложный и неоднозначный. С одной стороны, ассемблер позволяет создавать примитивы графики, а также оптимизировать разрабатываемое с его помощью ПО, с другой, - требования к графике в современном мире, а особенно в игровой индустрии, гораздо более высоки, чем те возможности, что предоставляет сам язык. Все дело в том, что эффективность ассемблера заключается в скорости вычислений и обработки данных, а не в создании графических изображений. То есть создавать графические объекты гораздо практичнее именно на специализированных языках программирования, а при помощи ассемблера можно оптимизировать процесс их обработки.
К примеру, рассмотрим программу, позволяющую нарисовать на экране окружность.
.model tiny
.code
ORG 100h
RADIUS EQU 30 ;рисуем окружность с радиусом 30
RADIUS2 EQU RADIUS*RADIUS ;квадратрадиуса
DIAMETR EQU RADIUS*2 ;диаметр окружности
N EQU 157*RADIUS/200 ;количество точек на 1/8
COLOR EQU 10 ;цвет окружности
start: MOV AH,0Fh ;узнать номер текущего видеорежима
INT 10h
MOV VIDEOR,AL ;запомним текущий видеорежим
MOV AX,13h ;установить видеорежим 320х200х256
INT 10h
PUSH 0A000h ;установить регистр ES на сегмент
POP ES ; видеопамяти
XOR BP,BP ;будем увеличивать X и Y
MOV Y,RADIUS-1 ;координаты X=0 и Y=R
CALL DRAW_OCT1 ;рисуем восьмушку окружности
MOV BP,RADIUS-1 ;координата X=2*R
MOV Y,0 ;координата Y=0
CALL DRAW_OCT2 ;рисуем восьмушку окружности
NEG DELTA_X ;увеличиваем Y и уменьшаем X
MOV Y,RADIUS
MOV BP,DIAMETR ;координаты Y=R и X=2*R
CALL DRAW_OCT1 ;рисуем восьмушку окружности
MOV BP,RADIUS ;координата X=R
MOV Y,0 ;координата Y=0
CALL DRAW_OCT2 ;рисуем восьмушку окружности
NEG DELTA_Y ;уменьшаем координаты Y и X
MOV Y,RADIUS ;координата Y=R
MOV BP,DIAMETR ;координата X=2*R
CALL DRAW_OCT1 ;рисуем восьмушку окружности
MOV BP,RADIUS ;координата X=R
MOV Y,DIAMETR ;координата Y=2*R
CALL DRAW_OCT2 ;рисуем восьмушку окружности
NEG DELTA_X ;уменьшаем Y и увеличиваем X
XOR BP,BP ;координата X=0
MOV Y,RADIUS ;координата Y=R
CALL DRAW_OCT1 ;рисуем восьмушку окружности
MOV BP,RADIUS ;координата X=R
MOV Y,DIAMETR ;координата Y=2*R
CALL DRAW_OCT2 ;рисуем восьмушку окружности
XOR AX,AX ;ожидание нажатия любой клавиши
INT 16h
MOV AX,WORD PTR VIDEOR ;восстановление видеорежима
INT 10h
RET ;выход из программы
DELTA_CALC PROC ;рассчитаем ошибку накопления
MOV BX,AX ;в AX значение координаты X или Y
DEC AX ;вычислим (Y+0,5)2 Y2+Y
MUL AX ;или (X+0,5)2 X2+X
ADD AX,BX
MOV DELTA,AX ;и поместим это значение в DELTA
RET
DELTA_CALC ENDP
;процедура прорисовки 1/8 окружности с вычислением
DRAW_OCT1 PROC ; координаты X
MOV AX,Y
SHL AX,6 ;должно быть DI=Y*320, но для умножения
MOV DI,AX ;на 320 используем сдвиги, AX= Y*64,
SHL AX,2 ;сохраним AX в DI и умножим Y*64 на 4
ADD DI,AX ;DI=Y*(256+64)=Y*320.
MOV AX,BP
SUB AX,RADIUS ;BP=X AX=R-X
CALL DELTA_CALC ;расчет ошибки накопления по X
MOV CX,N
CIRC1: MOV AX,Y
SUB AX,RADIUS ;AX=Y-R
MUL AX
NEG AX
ADD AX,RADIUS2 ;AX=R2-Y2
CMP DELTA,AX ;сравнить текущий X2=R2-Y2 с ошибкой
JBE A3 ;накопления, если меньше, увеличиваем или
ADD BP,DELTA_X ;уменьшаем только Y, иначе
MOV AX,BP ;увеличиваем или уменьшаем еще и X и
SUB AX,RADIUS ;вычисляем новую ошибку накопления
CALL DELTA_CALC
A3: CMP DELTA_Y,1
JNE A1
ADD DI,320
JMP SHORT A2
A1: SUB DI,320
A2: MOV BYTE PTR ES:[DI][BP],COLOR ;выводимточкуна
MOV AX,DELTA_Y ; экран
ADD Y,AX
LOOP CIRC1 ;повторяемцикл
RET
DRAW_OCT1 ENDP
;процедура прорисовки 1/8 окружности с вычислением
DRAW_OCT2 PROC ;координаты X
MOV AX,Y
SHL AX,6 ;должно быть DI=Y*320, но для умножения
MOV DI,AX ;на 320 используем сдвиги, AX= Y*64,
SHL AX,2 ;сохраним AX в DI и умножим Y*64 на 4
ADD DI,AX ;DI=Y*(256+64)=Y*320.
MOV AX, Y
SUB AX,RADIUS
CALL DELTA_CALC
MOV CX,N
CIRC2: MOV AX,BP
SUB AX,RADIUS
MUL AX
NEG AX
ADD AX,RADIUS2 ;AX=R2-(X-R)2
CMP DELTA,AX
JBE A5
MOV AX,DELTA_Y
ADD Y,AX
MOV AX,Y
SUB AX,RADIUS
CALL DELTA_CALC
CMP DELTA_Y,1
JNE A4
ADD DI,320
JMP SHORT A5
A4: SUB DI,320
A5: ADD BP,DELTA_X
MOV BYTE PTR ES:[DI][BP],COLOR
LOOP CIRC2
RET
DRAW_OCT2 ENDP
VIDEOR DB 0,0 ;значение текущего видеорежима
DELTA DW 0 ;ошибка накопления
DELTA_X DW 1 ;смещение по оси X
DELTA_Y DW 1 ;смещение по оси Y
Y DW 0 ;координата Y
END start
На языке C++ окружность можно нарисовать так:
#include <windows.h>
int main()
{
HWND handle = FindWindowA("ConsoleWindowClass", NULL);
HDC hdc = GetDC(handle);
// Создаёмперо (контур)
HPEN hPen = CreatePen(PS_SOLID, 1, RGB(0, 255, 72));
// Создаёмкисть (заливка)
HBRUSH hBrush = CreateSolidBrush(RGB(12, 12, 12));
SelectObject(hdc, hPen); // Указываемперо
SelectObject(hdc, hBrush); // Указываемкисть
int radius = 30; // Радиусокружности
// Верхний левый угол описывающего прямоугольника
int leftX = 0, leftY = 65;
// Нижний правый угол описывающего прямоугольника
int rightX = leftX + 2 * radius, rightY = leftY + 2 * radius;
Ellipse(hdc, leftX, leftY, rightX, rightY); // Рисуемокружность
return 0;
}
Как видно из примера, нарисовать даже простейший графический примитив на чистом ассемблере сложно. Изобразить ту же окружность можно при помощи стороннего API, однако нужно ли в таком случае использовать для этого ассемблер?
Обобщая вышесказанное, можно сказать, что ассемблер действительно позволяет разработчику взаимодействовать с графикой, однако создать что-то действительно стоящее и, самое главное, отвечающее современным требованиям на этом языке очень непросто. Ассемблер позволяет оптимизировать процесс, например, вычислений для отрисовки графики, созданной на специально разработанном для этих целей языке. То есть язык ассемблера в полной мере раскрывает свой потенциал именно в комбинации с другими, более высокоуровневыми языками программирования.
Библиографический список
Создание графических примитивов на Ассемблере [Электронный ресурс] // URL: https://habr.com/post/134495/
Абрамова О.Ф. Компьютерная графика. Избранные лекционные темы. Ч. 1: учеб. пособие / О.Ф. Абрамова. - Saarbrucken, 2012. - 129 с.
Графика на ассемблере [Электронный ресурс] // URL: http://www.chipinfo.ru/literature/chipnews/200007/48.html
Абрамова О.Ф. Компьютерная графика : лабораторный практикум [Электронный ресурс]: учеб. пособие / О.Ф. Абрамова, Д.Н. Лясин; ВПИ (филиал) ВолгГТУ. - Волгоград, 2017. - 1 электрон. опт. диск (CD-ROM). - 74 с.
Абрамова О.Ф. Компьютерная графика : конспект лекций для студентов направлений 230100.62 «Информатика и вычислительная техника» и 231000.62 «Программная инженерия» [Электронный ресурс]: учеб. пособие / О.Ф. Абрамова; ВПИ (филиал) ВолгГТУ // Учебные пособия : сб. Серия «Естественнонаучные и технические дисциплины». Вып. 3. - 1 электрон. опт. диск (CD-ROM) ; формат pdf. - Волжский, 2012. - 165 с.