Совсем краткое пояснение. Как театр начинается с вешалки, так программа на ассемблере 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
Это репост с сайта http://tolik-punkoff.com
Оригинал: https://tolik-punkoff.com/2022/05/30/mas