Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции / GL17.doc
Скачиваний:
24
Добавлен:
20.05.2014
Размер:
81.41 Кб
Скачать

Void main()

{

char Str1[MAX_SIZE], Str2[MAX_SIZE];

char FinStr[ 2 * MAX_SIZE];

unsigned int lenstr;

puts("Введите первую строку: ");

gets( Str1);

puts("Введите вторую строку: ");

gets( Str2);

lenstr = ConStr( Str1, Str2, FinStr);

puts("Объединённая строка: ");

puts( FinStr);

printf( "Длина строки: %u\n", lenstr);

}

А теперь программу на языке Ассемблера. Сначала нарисуем стековый кадр.

bp

старое BP

адрес возврата

bp + 4

Str1

bp + 6

Str2

bp + 8

FinStr

Файл constr.asm

.MODEL small

.CODE

GLOBAL _ConStr:PROC

_ConStr PROC

push bp

mov bp, sp

push si di

cld

mov di,@data

mov es,di

mov si, [bp+4] ; Адрес Str1 - в SI

mov di, [bp+8] ; Адрес FinStr - в DI

Str1Loop:

lodsb ; В AL- элемент строки

and al,al ; Конец строки?

jz DoStr2 ; Да - будем присоединять Str2

stosb ; Нет - перепишем в FinStr

jmp Str1Loop

DoStr2:

mov si,[bp+6] ; Адрес Str2 - в SI

Str2Loop:

lodsb

stosb

and al,al ; Конец строки?

jnz Str2Loop ; Нет - повторять копирование

mov ax,di ; Адрес терминатора строки - в AX

dec ax ; Терминатор не включать

sub ax, [bp+8] ; Определить длину строки

pop di si bp

ret

_ConStr ENDP

END

В программе на языке Ассемблера для нас были неприятные моменты:

  • приходилось следить за подчёркиванием в начале глобальных имён, хотя в исходном модуле на Си этого не было,

  • приходилось тщательно выписывать пролог и эпилог программы, хотя они, очевидно стандартны,

  • приходилось рисовать стековый кадр и тщательно отслеживать смещения относительно bp, что чревато ошибками; хотелось бы использовать символические имена.

В TASMимеются средства для исправления положения.

Директива .MODEL.

Если в начале файла размещать директиву

.MODELsmall,C

то можно не указывать символ подчёркивания. Пролог и эпилог в подпрограммах генерируется автоматически.

Директива PROC

Знакомая нам директива расширяется так:

имя PROC USES список_сохранямого_в_стеке

как правило, в стеке сохраняются регистры, их разделяют пробелами.

(Но если не указать .MODEL small,Cто предупреждение

USES has no effect without language)

Директива ARG

В директиве ARGперечисляем параметры и их типы в порядке их расположения в списке формальных параметров, например

ARG Str1:word, Str2:word

Тогда вместо команды mov si, Str1 или mov si, OFFSET Str1 Ассемблер сгенерирует mov si, [bp+4]. Разумеется, при этом обязательно использование директивы.MODEL small,C.

Пример. Перепишем заново программуConStr

.MODEL small,C

.CODE

GLOBAL ConStr:PROC

ConStr PROC USES si di

ARG Str1:word, Str2:word, FinStr:word

cld

mov di,@data

mov es,di

mov si,Str1

mov di,FinStr

Str1Loop:

lodsb

and al,al

jz DoStr2

stosb

jmp Str1Loop

DoStr2:

mov si,Str2

Str2Loop:

lodsb

stosb

and al,al

jnz Str2Loop

mov ax,di

dec ax

sub ax, OFFSET FinStr

ret

ConStr ENDP

END

Она стала намного проще для восприятия.

Перепишем программу для использования большой модели памяти. Теперь стековый кадр имеет вид (адрес возврата и параметры — двойные слова — сегмент:смещение)

bp

старое BP

адрес возврата

bp + 6

Str1

bp + A

Str2

bp + E

FinStr

Но благодаря использованию символических имен, можно не рассчитывать смещения. Зато загрузку указателей теперь придётся осуществлять с помощью команд ldsиles.

файл constrl.asm

.MODEL large,C

.CODE

GLOBAL ConStr:PROC

ConStr PROC USES si di ds

ARG Str1:dword, Str2:dword, FinStr:dword

cld

lds si,Str1

les di,FinStr

Str1Loop:

lodsb

and al,al

jz DoStr2

stosb

jmp Str1Loop

DoStr2:

lds si,Str2

Str2Loop:

lodsb

stosb

and al,al

jnz Str2Loop

mov ax,di

dec ax

sub ax, OFFSET FinStr

ret

ConStr ENDP

END

Команда для создания exe-файла

tcc -ml test.c constrl.asm

Ключ -mlуказывает на использование большой модели памяти.

Особенности интерфейса при использовании C++.

Первый шаг в переходе от CкC++ — изменить расширение файла с .cна .cpp. В результате файл будет обрабатываться компиляторомC++, что повлечёт например более строгую проверку типов и т.д.

Проделаем это. Переименуем test.cвtest.cpp. При компоновке нас ожидает неудача:

tcc test.cpp constr.asm

Turbo Link Version 3.0 …

Error: Undefined symbol ConStr(char near*,char near*,char near*) in module test.cpp

Для разгадки столь неожиданного сообщения сгенерируем ассемблерный файл и посмотрим его:

tcc -S test.cpp

В файле test.asmмы найдём строку

call near ptr @ConStr$qpzct1t1

Оказывается, компилятор C++ изменяет имена функций, добавляя в них закодированную информацию о типах параметров и возвращаемого значения. Поэтому компоновщик не нашёл этого глобального имени в модулеconstr.obj.

Исправление несложно. В файле test.cppизменим описание прототипа:

extern "C" unsigned int ConStr( char*, char*, char*);

Теперь компилятор будет генерировать внешнее имя по правилам языка Си, а не C++.

Интерфейс Turbo Pascal и Assembler.

Кратко, не приводя примеров, рассмотрим особенности интерфейса меду модулями, написанными на Паскале и Ассемблере.

1) Передача параметров происходит не как в Си — справа налево, а наоборот — слева направо.

2) Уничтожение стекового кадра возложено на подпрограмму и выполняется командой ret n, гдеn— число байтов в стековом кадре (разумеется,n— чётное число).

Эти правила носят название — паскалевское соглашение о связях. В языке Си имеются модификаторы, которые используются при описании прототипов функций:

int pascal f( p1, p2, …)— передача параметров по правилам языка Паскаль,

int cdecl f( p1, p2, …)— передача параметров по правилам языка Си.

Итак, если прототип функции Constrимеет вид:

unsigned int pascal ConStr( char*, char*, char*);

то в программе надо сделать следующие изменения:

во втором варианте изменить одну директиву

.MODELsmall,pascal

а в первом варианте

  • убрать символ подчёркивания у глобальных имён и записать имена большими буквами,

  • заменить ret наret 6,

  • изменить ссылки на элементы стекового кадра: [bp+4]заменить на[bp+8]и наоборот.

При программировании для Win16 использовалось паскалевское соглашение о связях. Но вWin32 ввели новое соглашение о связях: модификатор__stdcall. Параметры передаются справа налево — как в Си, а уничтожение стекового кадра производится командойret n. Пример мы увидим ниже.

Соседние файлы в папке Лекции