- •Связь Ассемблера и Си
- •Void main()
- •Ограничения на встроенное ассемблирование.
- •Вызов функций, написанных на языке Ассемблера из Си-программ
- •Передача параметров в функциях Си
- •X dw 0fcd5h
- •Вызов функций Си из программ на языке Ассемблера
- •Include macro.Inc
- •If type number eq 0
- •If number le 0fffFh
- •Void main()
Вызов функций, написанных на языке Ассемблера из Си-программ
Предположим, у нас есть файл с главной программой на языке Си prim.cи файл с подпрограммой на языке Ассемблераsub.asm. Их трансляция и компоновка из командной строки осуществляется командой
tcc prim.c sub.asm
Тогда для файла prim.c вызывается компилятор Си образуется файл prim.obj. Для файла sub.asm вызывается tasm.exe и образуется sub.obj. Наконец, вызывается компоновщик tlink, который из объектных файлов создаёт загрузочный (исполняемый) файл prim.exe.
Сделаем важное замечание. При трансляции программы на языке Ассемблера её текст приводится к верхнему регистру и поэтому такие, например, имена, как Testиtestнеразличимы. Но в программе на языке Си такие имена различны. Поэтому при ассемблировании можно указать дляtasmключи, которые сделают имена различимыми.
Если запустить tasm.exeбез хвоста командной строки, то на экран будет выведена краткая справка по ключамtasm. Одна из строк такова
/ml,/mx,/mu Case sensitivity on symbols: ml=all, mx=globals, mu=none
/mu— преобразование всех имён к верхнему регистру (по умолчанию)
/ml— имена различаются регистром (мнемоники команд и директив это не касается:Movиmovнеразличимы)
/mx— различие в регистре только для глобальных имён.
tccавтоматически вызываетtasmс ключом /ml. Если пользователь хочет, чтобы чувствительность к регистру была только для глобальных имён, то следует применить команды
tasm /mx sub.asm
tcc prim.c sub.obj
Передача параметров в функциях Си
Описание функции выглядит так
тип_результата имя_функции(тип_параметра_1, тип_параметра_2, …)
Соответственно, вызов функции имеет вид
[возвращаемое_значение = ] имя_функции(параметр_1, параметр_2, …);
Для дальнейшего изложения введём обозначение v=f(p1,p2,…,pn).
Вызывающая программа начинает формировать стековый кадр, помещая параметры в стек справа налево. При переходе в подпрограмму в стеке запоминается адрес возврата (в малой модели памяти это одно слово). В подпрограмме необходимо выполнить команды
push bp
mov bp, sp
После этого стековый кадр приобретает вид (в предположении, что каждый параметр занимает слово)
|
bp |
|
старое BP |
|
|
|
адрес возврата |
|
bp + 4 |
|
p1 |
|
bp + 6 |
|
p2 |
|
|
|
… |
|
bp + 2 + 2*n |
|
pn |
После выполнения тела подпрограммы нужно выполнить команды
mov sp, bp
pop bp
ret
Уничтожением стекового кадра занимается вызывающая программа. В ней выполняется команда
add sp, 2*n, гдеn— количество параметров.
Теперь вам должно быть понятно, почему, если мы хотим, чтобы подпрограмма изменила значения переменной, то в качестве параметра нужно использовать адрес этой переменной. Дело в том, что содержимое стекового кадра немедленно уничтожается после того как функция отработала.
На рисунке изображён стековый кадр, где каждый параметр занимает слово. Можно передавать и двойные слова и другие данные (например, плавающее число с двойной точностью занимает 8 байтов).
Для возвращаемого значения действует очень простое правило. Если возвращаемое значение слово, то результат в регистре AX, если двойное слово — в пареDX:AX. Исключение составляют плавающие числа, но мы их не рассматриваем.
