CodeNet / Языки программирования / Ассемблер / Windows / Assembler & Win32. Курс молодого бойца
Первая программа
6 октября 2006 года
На этом уроке мы с вами напишем первую программу на ассемблере. Но сначала я вам объясню структуру программы на ассемблере. Самая первая программа будет написана на TASM, потому что язык TASM классический, стандартный, без наворотов, а о MASM (макроассемблер) я вам буду говорить на следующих уроках.
Общая структура программы.
.386p ;тип процессора .model flat, stdcall ; модель памяти и вызова подпрограмм ;объявление включаемых (заголовочных) файлов, макросов, макроопределений, ; также внешних определений .data ; Инициализированные данные .data? ; неинициализированные данные .const ; константы .code ; исполняемый код End "метка точки входа"
Процессоры могут быть 386, 486, 586, обычно всегда стоит 386p, но ничто вам не мешает поставить 486p или 586. Модель памяти всегда flat и никакой другой не может быть. Вызов подпрограмм обычно всегда stdcall, стандарт вызова почти всех API функций. Секция .data секция с инициализированными данными она включается в исполняемый файл. Секция .data? секция с неинициализированными данными, она не включается в исполняемый файл и появляется только тогда, когда программа загружается в память. Секция .const секция констант. Секция .code содержит исполняемый код программы. В конце программы всегда должно стоять слово end, которая задаёт точку входа программы, т.е. место с которого начнётся выполняться программа. Секции .data, .data? имеют полный доступ. Разумеется, секции .const и .code имеют атрибут доступа - только чтение. Секция .const наиболее редко встречается в программах, так как константы можно задавать с помощью макроопределений.
При программировании под Win32 мы не имеем доступ к портам ввода вывода и не можем вызывать прерывания (самые главные команды в DOS), у нас есть только WIN API, которые экспортируют системные библиотеки. Вызывая API функцию, вы просто передаёте управление точке входа функции (точка входа функции это первая инструкция функции), сама функция находится где-то в памяти вашего процесса внутри некоторой библиотеки. API функции находятся в библиотеках kernel32.dll, user32.dll, gdi32.dll (классика) advapi32.dll и т.д., для того чтобы использовать некоторую функцию этих библиотек надо сначала загрузить нужную библиотеку в свою память, либо включить используемые функции в таблицу импорта и тогда загрузчик загрузит библиотеки за вас. О kernel32.dll можно не волноваться, потому что эта библиотека есть в каждом процессе в любом случае, тем более она там существует ещё до запуска главной нити процесса, она даже проецируется в вашу память всегда по одинаковым адресам. Как я уже говорил все функции, используют модель вызова stdcall, а значит, параметры подпрограмме передаются "задом-на-перёд" и вызываемый очищает стек. Общий вид вызова API функции будет такой:
Push параметр 3 Push параметр 2 Push параметр 1 Call функция ; если быть точнее то здесь должен стоятб ажрес функции ; т.е. адрес её точки входа
Каждая программа в Win32 в конце своего выполнения ОБЯЗАТЕЛЬНО должна вызвать функцию ExitProcess. Сами подумайте, после выполнения последней инструкции программы, дальше идёт пустота, даже если там что-то осмысленное, все равно у выделенной памяти есть конец, а после конца идёт невыделенная (т.е. несуществующая) память. Процессор начнёт выполнять непоймёшь чё, пойдут глюки, и ваша программа даст в конце хорошо всем знакомый звук - "ДУНГ". А это уже бессмыслица. Вот определение ExitProcess из MS SDK:
VOID ExitProcess( UINT uExitCode // exit code for all threads );
uExitCode - это значение никогда не используется и всегда равно 0.
Первая программа.
Теперь я приведу вам программу, которая выводит легендарное сообщение "Hello, World!".
.386 .model flat extrn ExitProcess:PROC extrn MessageBoxA:PROC .data Ttl db "First ASSEMBLER program",0h Msg db 'Hello, World!!!!',0h .code start: push 0h push offset Msg push offset Ttl push 0h call MessageBoxA push 0h call ExitProcess end start
Мы вызываем функцию MessageBoxA, для вывода окна сообщения потом выходим из процесса. Теперь поподробнее.
Определение функции MessageBox из MS SDK в оригинале:
int MessageBox( HWND hWnd, // handle of owner window LPCTSTR lpText, // address of text in message box LPCTSTR lpCaption, // address of title of message box UINT uType // style of message box );
Мы указали только модель памяти и модель вызов подпрограмм не указали, так как это просто не к чему. В качестве первого параметра мы передаём ноль, поэтому у окна сообщения не будет родительского окна. В качестве параметров lpText и lpCaption мы передаём адреса соответствующих строк, заметьте, что мы передаём адрес строки (т.е. её первого символа) у которой в конце стоит символ #0, это нужно, для того чтобы функция смогла найти конец строки. Последний параметр определяет стиль окна сообщения, в данном случае мы передали 0, что обозначает, что там будет только одна кнопка OK, иначе этот стиль обозначается MB_OK. Функцию ExitProcess вы уже знаете. После директивы end мы указали метку, с которой начнётся выполнение ваша программа.
Зачем нужна буква A в конце функции?
В Win32 есть 2 типа функции заканчивающиеся на A и на W. Всё отличие в строках, которые принимают функции. А-функции принимают ANSI строки. W-функции принимают Unicode строки. У каждой функции, которая принимает строки, есть обе версии. Обычно все пользуются ANSI версиями функций. Но в любом случае при вызове ANSI функции все строки сначала преобразуются в Unicode строки, и будет вызвана Unicode функция.
Компиляция.
Чуть-чуть не забыл рассказать, как надо компилировать программы. Для этого вам надо написать текст программы на ассемблере и сохранить в файл с расширением *.asm. Потом надо ассемблировать его. Для этого вам понадобится программа tasm32.exe она нахидтся в папке %tasmdir%\bin\tasm32.exe, формат её вызова такой: TASM [options] source [,object] [,listing] [,xref] (доп сведения смотрите в tasm32 /?) для большинства программ ассемблирование проходит так:
%tasmdir%\BIN\tasm32 /m3 /ml asmfile,,;
Где asmfile - имя вашего файла БЕЗ расширения.
Потом будет создан объектный файл, файл листинга и т.д. Теперь вам надо всё это слинковать. Для Линковки вам понадобится программа tlink32.exe. Вот формат её вызова:
TLINK objfiles, exefile, mapfile, libfiles, deffile, resfiles
Для всех программ в этом туториале линковка будет такой:
%tasmdir%\BIN\tlink32 /Tpe /aa asmfile,asmfile,,%tasmdir% \Lib\import32.lib
Где asmfile - имя вашего файла БЕЗ расширения.
Статическая библиотека %tasmdir% \Lib\import32.lib содержит в себе все функции библиотек kernel32.dll, user32.dll, gdi32.dll и может быть других стандартных библиотек. Желательно чтобы asmfile находился в папке %tasmdir%.
Для быстрой компиляции я вам советую создать *.bat файл, который всё это делает за вас. Вот текст моего файла.
Compile.bat cls echo off d:\tasm_5\BIN\tasm32 /m3 /ml %1,,; d:\tasm_5\BIN\tlink32 /Tpe /aa %1,%1,,d:\TASM_5\Lib\import32.lib echo "Succesful"
Теперь после вызова
Compile sample
У вас готовый к выполнению экзешник.
Подошёл к концу четвёртый урок. На этом уроке мы написали первую программу на ассемблере. Но я вам почти не объяснил, как надо пользоваться меткам и данными. Именно, для этого и предназначены 2 следующий урока
Оставить комментарий
Комментарии
В итоге понимается только половина прочитанного.
Повторюсь, это просьба, а не претензия
Error: Unresolved external 'MESSAGEBOXA' referenced from module main.asm
Error: Unresolved external 'EXITPROCESS' referenced from module main.asm
почему? ((((
создаю бат файл перетаскиваю на него иконку *.asm файла и ничего
.code
start:
push 0h
push offset Ttl
push offset Msg
push 0h
call MessageBoxA
push 0h ---------Зачем нужно в стек запихивать 0? Без этой строчки программа работает также.
call ExitProcess
end start
.model flat
extrn ExitProcess:PROC
extrn MessageBoxA:PROC
.data
Ttl db 'First ASSEMBLER program',0h
Msg db "Hello, World!!!!",0h
.code
start:
push 0h
push offset Ttl
push offset Msg
push 0h
call MessageBoxA
push 0h
call ExitProcess
end start