Совсем краткое пояснение. Как театр начинается с вешалки, так программа на ассемблере MASM начинается с указания набора инструкций:
.386
Это ассемблеpная диpектива, говоpящая ассемблеpу использовать набоp опеpаций для пpоцессоpа 80386. Вы также можете использовать .486, .586, но самый безопасный выбоp — это указывать .386. Также есть два пpактически идентичных выбоpа для каждого ваpианта CPU. .386/.386p, .486/.486p. Эти «p»-веpсии необходимы только когда ваша пpогpамма использует пpивилигиpованные инстpукции, то есть инстpукции, заpезеpвиpованные пpоцессоpом/опеpационной системой в защищенном pежиме. Они могут быть использованны только в защищенном коде, напpимеp, дpайвеpами. Как пpавило, ваши пpогpаммы будут pаботать в непpивилигиpованном pежиме, так что лучше использовать не-«p» веpсии. (Из мануала Iczelion’а)
.model flat
.model
— модель памяти, используемая вашей программой. Для DOS, например, были модели tiny
, small
, compact
, flat
, lagre
и т.д. — они выбирались в зависимости от типа программы. По ограничениям DOS сегмент кода не мог занимать пространство, больше размера одного сегмента в памяти. Чтоб это обойти, как раз и использовалась сегментация. Tiny
и flat
могли быть использованы для оригинальных DOS-программ, с расширением .com
, которые могли занимать чуть меньше одного сегмента (64 Кб), на код и данные, и представляли собой просто кусок кода вместе с данными, можно сказать RAW-формат исполняемого файла — просто откомпилированный байткод, даже без всякого заголовка. Для EXE формата, код и данные могли занимать несколько сегментов.
В Win32 сегментация памяти не нужна, во-первых, размер адреса 32-битный, что больше, чем в DOS, во-вторых, менеджментом памяти занимается ОС, потому программа видит все нужные ей внешние библиотеки и функции из них, как если бы они были загружены в адресное пространство программы. Таким образом, программа под Win32 изнутри представляет такой себе очень большой .COM-файл, потому используется только одна модель памяти — flat
.
.model flat, stdcall
У директивы .model
есть несколько важных параметров, самый главный из них, указание на передачу параметров функций (обычно внешних, из DLL, но никто не мешает для своих функций использовать). Его и указываем через запятую (stdcall
).
Есть три способа передачи параметров:
— C
(си) способ: вызывающая, т.е. наша программа, должна положить в стек нужные параметры, причем, в обратном порядке. Например, если функция описывается так:
SomeFunction (Argument1, Argument2, Argument3)
То на ассемблере она вызывается так:
push Argument3
push Argument2
push Argument1
call SomeFunction
А далее, вызывающая программа должна почистить стэк (по-научному это называется «уравнять»), push
уменьшает значение регистра стека на размер операнда (2 или 4 байта), пусть будет 4, тогда получается, что надо увеличить значение стека на 3x4=12
байт.
add SP, 12
C-поpядок полезен, когда вы не знаете, как много паpаметpов будут пеpеданны функции
— PASCAL
— это C
соглашение наоборот, параметры передаются в прямой последовательности, т.е. первый параметр кладется в стек первым, а со стеком должна разбираться сама вызываемая функция.
— STDCALL
— это гибрид C
и PASCAL
соглашения, параметры передаются в обратном порядке, но со стеком разбирается вызываемая функция.
Win32 использует практически исключительно STDCALL
соглашение для своего API, исключая функцию wsprintf()
, потому что она не знает, сколько ей будет передано параметров. При ее вызове следует использовать C
-соглашение.
Если вы используете include-файл windows.inc
в MASM, и при компиляции происходит ошибка windows.inc(78) : error A2119: language type must be specified
, значит, забыли указать параметр stdcall
в директиве .model