Просто примеры работающих программ на masm32, без подробного разбора и т.п. Использование MessageBox, ExitProcess. Вызов WinAPI-функции с помощью invoke. Подключение файлов и библиотек с помощью includelib и include. Инструкции call, push, test, add, jz, jmp, jne, mov, inc, dec, cmp. Реализация цикла через .REPEAT и .UNTIL, .WHILE и .ENDW. Использование .IF, .ENDIF, .CONTINUE и .BREAK.
Программа 1. Получение данных из командной строки
Программа получает данные из командной строки и выводит их в небольшом windows-окне.
|
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 |
.486 .model flat, stdcall option casemap: none include /masm32/include/windows.inc include /masm32/include/user32.inc include /masm32/include/kernel32.inc includelib /masm32/lib/user32.lib includelib /masm32/lib/kernel32.lib include /masm32/macros/macros.asm uselib masm32, comctl32, ws2_32 .data .code start: call GetCommandLine ; результат будет помещен в eax push 0 push chr$("Command Line") push eax ; текст для вывода берем из eax push 0 call MessageBox push 0 call ExitProcess end start |
Код с вызовом функций можно было бы заменить кодом:
|
1 2 3 |
invoke GetCommandLine invoke MessageBox, 0, eax, chr$("Command Line"), 0 invoke ExitProcess, 0 |
invoke — это встроенный макрос для упрощения кода, и при компиляции всё это преобразуется в ассемблерные команды.
Т.е. код
|
1 |
invoke MessageBox, 0, eax, chr$("Command Line"), 0 |
эквивалентен коду
|
1 2 3 4 5 |
push 0 push chr$("Command Line") push eax push 0 call MessageBox |
Стек — это удобное место для хранения информации. Чаще всего он используется при вызове функций.
Все WinAPI-функции созданы по соглашению stdcall, то есть, передача аргументов в них производится через стек в обратном порядке. Возвращают эти функции значение в регистре eax.
Программа 2. Сложение двух чисел
Программа складывает два числа, и проверяет результат. Если сумма равна 0 — выводится одно сообщение, если нет — другое.
|
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 |
.486 .model flat, stdcall option casemap: none include /masm32/include/windows.inc include /masm32/include/user32.inc include /masm32/include/kernel32.inc includelib /masm32/lib/user32.lib includelib /masm32/lib/kernel32.lib include /masm32/macros/macros.asm uselib masm32, comctl32, ws2_32 .data .code start: mov eax, 123 mov ebx, -90 add eax, ebx test eax, eax jz zero invoke MessageBox, 0, chr$("В eax не 0!"), chr$("Info"), 0 jmp lexit zero: invoke MessageBox, 0, chr$("В eax 0!"), chr$("Info"), 0 lexit: invoke ExitProcess, 0 end start |
В данной задаче самое интересное — это работа с метками, командами jz, jmp и test.
test — это операция логического сравнения двух операндов, размерностью байт, слово или двойное слово. В процессе выполняется операцию логического умножения: бит результата равен 1, если соответствующие биты операндов равны 1, в остальных случаях бит результата равен 0. Затем устанавливаются флаги, в том числе флаг ZF (zero flag), который равен 1, если результат логического умножения равен нулю.
Флаг ZF в дальнейшем используется для анализа результата.
jnz — выполняет переход по указанной метке, если не установлен флаг ZF. Данная команда обычно используется с операциями сравнения, которые влияют на состояние флага ZF. Например, test и cmp.
jz — выполняет переход по указанной метке, если установлен флаг ZF. Данная команда обычно используется с операциями сравнения, которые влияют на состояние флага ZF. Например, test и cmp.
jmp — выполняет безусловный переход по указанной метке.
Программа 3. Организация цикла
Программа использует repeat для организации цикла.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
.data msg_title db "Title", 0 A DB 1h buffer db 128 dup(?) format db "%d",0 .code start: mov AL, A .REPEAT inc AL .UNTIL AL==7 invoke wsprintf, addr buffer, addr format, AL invoke MessageBox, 0, addr buffer, addr msg_title, MB_OK invoke ExitProcess, 0 end start |
inc — это увеличение значения операнда в памяти или регистре на 1.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
.data msg_title db "Title", 0 buffer db 128 dup(?) format db "%d",0 .code start: mov eax, 1 mov edx, 1 .WHILE edx==1 inc eax .IF eax==7 .BREAK .ENDIF .ENDW invoke wsprintf, addr buffer, addr format, eax invoke MessageBox, 0, addr buffer, addr msg_title, MB_OK invoke ExitProcess, 0 |
Директиву .BREAK используется, чтобы прервать цикл и продолжить выполнение программы далее. Можно использовать директиву .CONTINUE для прерывания выполнения кода внутри цикла и перехода к
очередной проверке условия в конструкции repeat и while.
Программа 4. Сумма всех элементов массива
Программа суммирует значения всех элементов массива. Интересна работой с массивами и реализацией цикла типа «for».
|
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 |
.486 .model flat, stdcall option casemap: none include /masm32/include/windows.inc include /masm32/include/user32.inc include /masm32/include/kernel32.inc includelib /masm32/lib/user32.lib includelib /masm32/lib/kernel32.lib include /masm32/macros/macros.asm uselib masm32, comctl32, ws2_32 .data msg_title db "Title", 0 A DB 1h x dd 0,1,2,3,4,5,6,7,8,9,10,11 n dd 12 buffer db 128 dup(?) format db "%d",0 .code start: mov eax, 0 mov ecx, n mov ebx, 0 L: add eax, x[ebx] add ebx, type x dec ecx cmp ecx, 0 jne L invoke wsprintf, addr buffer, addr format, eax invoke MessageBox, 0, addr buffer, addr msg_title, MB_OK invoke ExitProcess, 0 end start |
dec — уменьшение значения операнда в памяти или регистре на 1.
cmp — сравнение двух операндов, методом вычитания второго операнда из первого. По результатам вычисления устанавливаются флаги.
jne выполняет переход по указанной метке, если результат сравнения операндов был отрицательным, и операнды НЕ равны друг другу.
Забавно, Вы пишите на masm?
Я как-то тыкал ассемблер, книжку Зубкова (кажется так его звали) прочитал. Но так ничего годного и не написал. Ограничился чуть более сложными чем в статье программами xD. Под контроллеры еще в ВУЗе на ассемблере писал, но то были лабораторные. До промышленного применения я не дошел, да и негде — у нас в городе есть лишь одна контора, которая постоянно размещает соответствующие вакансии (но там опять же не masm, а специфические, урезанные ассемблеры контроллеров).
Но мне казалось, что один человек не может писать и на перле и на ассемблере (очень уж разный уровень абстракции и область применения), как Вам удается? )
Я не пишу на masm, т.к. начала его изучать всего недели 3 назад, за вычетом Новогодних праздников. Да и не планирую им заниматься, в плане выполнения каких-то работ.
Пока что мне просто нравится разбираться с языком, который очень настоящий, близкий к жизни. Именно за счет разных уровней абстракций. Это такой отдых от работы :)
Кроме того, Perl еще не так далек от ассемблера, как, например, PHP. Я так думаю.
Доброго времени суток, спасибо за примеры программ. Я только начал изучать ассемблер, с чего Вы бы посоветовали начать? Я так понял нужно выучить все команды и регистры? Как сделать первый шаг? Заранее спасибо)
Оооочень интересный способ отдыха! Редкая и очень специфическая штука, этот асм. Мы на нем писали в университете. На масме писали программы, использующие WinAPI, писали игры на масме. Сам собираюсь в скором времени стереть пыль с кода, слишком уж много сил и энергии на него было потрачено