+ |
+ |
+ |
+
+
+ Документация на C--.
+ |
+ |
+ |
+ |
+
+
+
+
+
+Содержание.
+
+
+
+
+1 Введение.
+1.1 История создания и развития.
+1.2 Что такое C--?
+1.3 Как установить C--.
+
+2. Управление компиляцией.
+2.1 Параметры командной строки компилятора C--.
+2.1.1 /ON - Оптимизация числовых выражений.
+2.1.2 /DE - Временное расширение разрядности переменной.
+2.1.3 /ARGC - Альтернативный обработчик командной строки.
+2.1.4 /OST - слияние одинаковых строковых констант.
+2.1.5 /D - установка идентификатора в TRUE из командной строки.
+2.1.6 /IA - упрощенный ввод ассемблерных инструкций.
+2.1.7 /CRI - пропуск повторно включаемого файла.
+2.1.8 /IND - импорт имен процедур из DLL.
+2.1.9 /WS - задать имя stub файла для программ под windows.
+2.1.10 /WBSS - разместить не инициализированные данные в отдельной секции.
+2.1.11 /DBG - создание отладочной информации.
+2.1.12 /J0 /J1 /J2.
+2.1.13 /LST - Создание ассемблерного листинга.
+2.1.14 /ENV - Сохранение адреса переменных окружения.
+2.1.15 /CPA - Очистка post-области данных.
+2.1.16 /W - вывод предупреждений.
+2.1.17 /NW - Выборочное отключение типов предупреждений.
+2.1.18 /WSI - короткая таблица импорта.
+2.2 Директивы транслятора.
+2.2.1 ?ifdef/?ifndef
+2.2.2 ?initallvar
+2.2.3 ?usestartup
+2.2.4 ?startusevar
+2.2.5 ?atexit
+2.2.6 ?startuptomain
+2.2.7 ?undef
+2.2.8 ?align и ?aligncode
+2.2.9 ?pragma
+
+3. Константы.
+3.1 Числовые константы.
+3.2 Символьные константы.
+3.3 Строковые константы.
+3.4 Постоянные выражения.
+
+4. Выражения.
+4.1 Типы выражений.
+4.2 Выражения типа EAX/AX/AL.
+4.3 Выражения использующие получатель при вычислении выражения.
+4.4 Не - EAX/AX/AL выражения.
+4.5 Условные выражения.
+4.5.1 Простые условные выражения.
+4.5.2 Сложные условные выражения.
+4.6 Изменение типа выражения при присваивании.
+4.7 Вычисление в регистры EAX/AX/AL со знаком.
+
+5. Идентификаторы.
+5.1 Формат идентификатора.
+5.2 Зарезервированные идентификаторы.
+5.3 Универсальные регистры для 16 и 32-битного режима.
+5.4 Предопределенные идентификаторы.
+
+6. Переменные.
+6.1 Типы переменных.
+6.2 Объявление переменных.
+6.3 Глобальные переменные.
+6.4 Локальные переменные.
+6.5 Динамические переменные и структуры.
+6.6 Присваивание одного значения нескольким переменным.
+6.7 Переменные типа float.
+6.7.1 Формат переменных типа float.
+6.7.2 Константы с плавающей точкой.
+6.7.3 Диапазон допустимых значений.
+6.7.4 Математические операции.
+6.7.5 Преобразования типов.
+6.7.6 Операции сравнения.
+6.7.7 Сравнение переменных типа float с 32-битным регистром.
+6.8 Указатели.
+
+7. Адресация.
+7.1 Относительная адресация.
+7.2 Абсолютная адресация.
+
+8. Работа с блоками данных.
+8.1 Структуры.
+8.1.1 Что такое структуры.
+8.1.2 Синтаксис.
+8.1.3 Инициализация структур при объявлении.
+8.1.4 Инициализация структуры при выполнении программы.
+8.1.5 Операции с элементами структур.
+8.1.6 Вложенные структуры.
+8.1.7 Отображение тега структуры на блок памяти.
+8.1.8 Битовые поля структур.
+8.2 Объединения.
+8.3 Команды FROM и EXTRACT.
+
+9. Операторы.
+9.1 Условные инструкции.
+9.2 Циклы do{} while.
+9.3 Циклы loop, LOOPNZ, loopnz.
+9.4 Цикл while, WHILE.
+9.5 Цикл for, FOR.
+9.6 Оператор переключатель switch.
+9.7 Оператор перехода goto, GOTO.
+9.8 Оператор разрыва break, BREAK.
+9.9 Оператор продолжения continue, CONTINUE.
+9.10 Логическое объединение условий.
+9.11 Переход через циклы.
+9.12 Инвертирование флага проверки условий.
+9.13 Вычисление выражения, а затем проверка условия.
+9.14 Проверка битов при операции сравнения.
+9.15 Оператор перестановки.
+9.16 Оператор отрицания.
+9.17 Оператор инверсии.
+9.18 Специальные условные выражения.
+9.19 Символ $ - вставляет текущий адрес программы.
+9.20 Ключевое слово static и оператор ::.
+9.21 Оператор sizeof.
+9.22 Метки перехода.
+
+10. Ассемблер.
+10.1 Поддержка команд ассемблера.
+10.2 Ключевое слово asm.
+10.3 Префикс dup - повторение инструкций DB/DW/DD.
+10.4 Инструкции процессора Pentium III.
+
+11. Процедуры.
+11.1 Типы процедур, функций и макрокоманд.
+11.2 Стековые процедуры.
+11.3 Регистровые процедуры.
+11.4 Динамические процедуры.
+11.4.1 Установка динамической процедуры в определенное место программы.
+11.5 inline-процедуры.
+11.5.1 Другое применение inline.
+11.6 Процедуры обработки прерываний.
+11.7 Замена return на goto.
+11.8 Возвращаемые значения.
+11.9 Объявление параметров в регистровых процедурах.
+11.10 Объявление параметров в стековых процедурах.
+11.11 Использование макрокоманд.
+11.12 Передача параметров в стековые процедуры через регистры.
+11.13 Вызов процедур с адресом в регистре.
+11.14 Встоенные в компилятор процедуры.
+11.14.1 Процедуры ABORT, ATEXIT и EXIT.
+11.14.2 Процедуры inp/inportb, inport, inportd, outp/outportb, outport и
+ outportd.
+11.14.3 Процедуры для работы с вещественными числами.
+11.15 Классы.
+11.15.1 Объявление процедур в структурах.
+11.15.2 Наследование.
+11.15.3 Наследование процедур.
+
+12. Типы выходных файлов.
+12.1 Выходные файлы типа COM.
+12.2 Выходные файлы типа EXE.
+12.3 Выходной файл *.EXE с моделью памяти tiny.
+12.4 Объектный выходной файл OBJ.
+12.5 COM файл symbiosis.
+12.5.1 СИМБИОЗ - что это такое?
+12.5.2 Как это делать.
+12.5.3 Использование.
+12.5.4 Злоупотребления.
+12.6 SYS - драйверы устройств.
+12.7 Компиляция кода расширителей ROM-BIOS.
+12.8 32-битные файлы.
+12.8.1 32-битный код под DOS.
+12.8.2 32-битный код под Windows.
+12.8.3 Вызов API процедур по ординалам.
+12.8.4 Создание DLL под Windows.
+12.8.5 Инициализация DLL при загрузке.
+12.8.6 Компиляция ресурсов.
+12.9 Выходные файлы для MeOS.
+
+13. Приложения.
+13.1 Поиск включаемых файлов.
+13.2 Регистры, которые должны быть сохранены.
+13.3 C--.ini файл.
+13.4 startup.h-- файл.
+13.5 mainlib.ldp файл.
+13.6 C-- символы.
+
+
+
+1. Вступление.
+
+ 1.1 История создания и развития.
+
+
+ Автором языка SPHINX C-- является Peter Cellik (CANADA). Последняя
+ авторская версия SPHINX C-- v0.203 от 28.Oct.96. К сожалению автор
+ отказался от дальнейшего развития языка. С 1998 года, уже почти умерший
+ проект, подхватил Михаил Шекер (Россия). Изначально компилятор был freeware
+ (и даже greenware, как его называл Peter Cellik). Таким статус компилятора
+ остался и поныне.
+
+ Первоначально компилятор мог создавать только *.com файлы и был
+ рассчитан на создание небольших demo-программ и резидентов (TSR). В
+ дальнейшем возможности компилятора расширялись, так как этого требовало
+ наше бурное время.
+
+ При развитии компилятора, было стремление придерживаться следующих
+ принципов:
+
+ 1. Максимально возможная совместимость синтаксиса с последней версией
+ компилятора написанного Peter Cellik. Это давало возможность с минимальными
+ затратами (а чаще всего без всяких затрат) адаптировать программы,
+ написанные для 0.203 версии компилятора, к последней на этот момент версии
+ компилятора.
+
+ 2. Сблизить синтаксис компилятора со стандартным языком C. Это могло
+ значительно облегчить перенос программ написанных на C.
+
+ 3. Также прилагались усилия, для того, чтобы человек знающий только
+ ассемблер мог бы с минимальными затратами освоить C--.
+
+ Вот эти, зачастую противоречащие друг другу принципы, влияли на выбор
+ реализации возможностей компилятора. Насколько это удалось - судить Вам.
+
+ Если у Вас есть предложения и идеи по улучшению компилятора - пишите.
+ Мой e-mail sheker@mail.ru . Я с удовольствием выслушаю Ваши предложения, но
+ не гарантирую, что все они будут реализованы. Если реализовывать все
+ поступающие предложения, то компилятор превратится в свалку. Но если Ваше
+ предложение будет ценным (на мой взгляд, так что Вам придется свое
+ предложение хорошо аргументировать) и его будет возможным реализовать, оно
+ без сомнения найдет место в компиляторе.
+Return to contents.
+
+
+
+ 1.2 Что такое C--?
+
+
+ C-- был разработан, для того чтобы строить маленькие и быстрые
+ программы. Это наиболее подходит для создания резидентных программ (TSR),
+ программ, требующих обработку прерываний или программ у которых ограничены
+ ресурсы.
+
+ C-- занимает промежуточное положение между си и ассемблером. В связи с
+ этим промежуточным положением, Вам, для того чтобы писать программы на C--,
+ необходимо знать и ассемблер и си. Если Вам надоело возиться с огромными
+ ассемблерными листингами, а излишняя строгость языка C Вас угнетает, то этот
+ язык для ВАС.
+
+ Сейчас компилятор C-- может создавать 32-битные программы под Windows
+ (EXE-файлы формата PE) и 32-битные программы под DOS (LE-формат). Имеет
+ встроенный компилятор ресурсов и дизассемблер для генерации листинга
+ откомпилированного файла. Поддерживает ассемблерные инструкции процессора
+ Pentium III и ассемблерные инструкции FPU. Компилятор может генерировать
+ отладочную информацию совместимую с отладчиками фирмы Borland. Компилятор
+ может создавать объектные файлы (obj), но только для DOS программ.
+
+ C-- разработан только для использования на компьютерах с процессорами
+ совместимыми с семейством 80x86. Компилятор может работать только с
+ операционными системами DOS и семейством Windows.
+Return to contents.
+
+
+
+ 1.3 Как установить C--.
+
+
+ Компилятору C-- для работы нужны совсем незначительные ресурсы:
+ процессор 386 или лучше, чуть более 1 Мб дискового пространства и 4Мб
+ оперативной памяти. Компилятор может быть установлен на компьютеры с
+ операционной системой Windows 95 или лучше. Компилятор также может работать
+ в среде чистого DOS. В основном пакете компилятора находится 32-битная DOS
+ версия компилятора. На сайте http://sheker.chat.ru или
+ http://c--sphinx.narod.ru можно найти и консольную версию компилятора.
+ Консольная версия компилятора может работать только в среде Windows, но
+ она, в отличие от DOS версии, может работать с длинными именами исходных
+ файлов.
+
+ Установить компилятор C-- на Ваш компьютер очень просто. Предположим,
+ что Вы решили установить C-- на диск C. Создайте на диске C директорию
+ (папку) с именем C-- или с другим, удобным и понятным для Вас именем
+ (например, ДОСовской командой: MD C-- или другим доступным Вам способом).
+ Затем с сайта http://sheker.chat.ru или http://c--sphinx.narod.ru скачайте
+ файлы full_c--.zip и ful_c--2.zip и разархивируйте их в этой директории.
+ Затем в файле autoexec.bat можно прописать путь к директории с
+ компилятором. И все. Компилятор готов к работе. Если Вы добавляли путь к
+ компилятору в файл autoexec.bat, то Вам придется перегрузить операционную
+ систему.
+
+ Переменная окружения для компилятора C-- задается либо из командной
+ строки либо из командного файла (лучше всего ее прописать в autoexec.bat).
+ Эта переменная должна указывать компилятору, где находятся его библиотечные
+ файлы. Пример:
+
+ set C--=c:\c--\lib
+
+ Большой необходимости в переменной окружения для сегодняшней версии
+ компилятора нет. Существует несколько других способов, указать компилятору
+ место расположения библиотек. Поэтому определять или не определять
+ переменную окружения дело вашего вкуса и привычек.
+Return to contents.
+
+
+
+2. Управление компиляцией.
+
+ 2.1 Параметры командной строки компилятора C--.
+
+
+ Формат командной строки вызова компилятора C--:
+
+ C-- [Параметры] [ИМЯ INI ФАЙЛА] [ИМЯ ИСХОДНОГО ФАЙЛА]
+
+ Имя исходного файла можно задавать без расширения. Компилятор ищет
+ файл с расширением c--, cmm, c.
+
+ Параметры выделяются предшествующим символом / или -.
+ Инвертировать функцию опции можно завершающим символом -.
+
+ Список поддерживаемых параметров:
+
+ /0 использовать только команды 8086/8088 процессора (установлено
+ по умолчанию при компиляции 16-битных программ).
+ /1 использовать команды 80186 процессора.
+ /2 использовать команды и оптимизацию для 80286 процессора.
+ /3 использовать команды и оптимизацию для 80386 процессора.
+ (установлено по умолчанию для 32-битных программ).
+ /4 использовать команды и оптимизацию для 80486 процессора.
+ /5 использовать команды и оптимизацию для Pentium процессора.
+ /6 использовать команды и оптимизацию для Pentium MMX процессора.
+ /7 использовать команды и оптимизацию для Pentium Pro процессора.
+ /8 использовать команды и оптимизацию для Pentium II процессора.
+ /9 использовать команды и оптимизацию для Pentium III процессора
+ (пока не реализовано из-за отсутствии информации).
+ /A выравнивание данных на четный адрес
+ по умолчанию разрешено, поддерживает инверсию
+ /AC выравнивание адреса начала циклов
+ по умолчанию отключено, поддерживает инверсию
+ имеет смысл только на процессорах Pentium+
+ /AL=## установить значение байта заполнения при выравнивании данных
+ по умолчанию 0.
+ /AP выравнивание адреса начала процедур.
+ по умолчанию отключено, поддерживает инверсию
+ имеет смысл только на процессорах Pentium и лучше
+ /ARGC вставить блок разбора командной строки
+ по умолчанию отключено, поддерживает инверсию
+ /AS выравнивание в структурах.
+ по умолчанию отключено, поддерживает инверсию
+ /AT вставить блок поддержки ATEXIT процедуры
+ по умолчанию отключено, поддерживает инверсию
+ /C вставить блок игнорирования CTRL-C
+ по умолчанию отключен, поддерживает инверсию
+ имеет смысл только под DOS программы
+ /CRI проверять включаемые файлы на повторную загрузку
+ по умолчанию включено, поддерживает инверсию
+ /CPA очистка post-области данных
+ /D32 создать EXE файл (32 битный код под DOS)
+ по умолчанию COM
+ /D=idname определить идентификатор для условной компиляции
+ по умолчанию нет
+ /DBG генерировать отладочную информацию
+ по умолчанию нет
+ /DE временное расширение разрядности после умножения
+ по умолчанию отключено, поддерживает инверсию
+ /DLL создать DLL для Windows32
+ по умолчанию COM
+ /ENV сохранение адреса переменных окружения
+ /EXE создать EXE файл для DOS (модель SMALL)
+ по умолчанию COM
+ /HELP /H /? справка, эта информация
+ /IA имена ассемблерных инструкций являются идентификаторами
+ по умолчанию отключено, поддерживает инверсию
+ /IND=name импорт имен из файла name.
+ /IP=path задать путь поиска включаемых файлов
+ по умолчанию нет
+ /IV инициализировать все переменные
+ по умолчанию отключено, поддерживает инверсию
+ /J0 не делать начальный jump на main()
+ по умолчанию отключено, поддерживает инверсию
+ В COM-файлах не создает jmp на main. В остальных не создается
+ блок начальной инициализации программы, а управление
+ передается сразу на main.
+ /J1 делать короткий jump на main()
+ по умолчанию нет
+ имеет смысл только в COM-файлах
+ /J2 делать jump на main()
+ по умолчанию да, поддерживает инверсию
+ имеет смысл только в COM-файлах
+ /LAI список поддерживаемых ассемблерных инструкций
+ /LRS загружать числовые константы через стек.
+ по умолчанию да, поддерживает инверсию
+ /LST создать ассемблерный листинг
+ /ME показать мой адрес и имя
+ /MEOS создать исполняемый файл для MeOS
+ по умолчанию COM
+ /MER=## установить максимальное число ошибок
+ по умолчанию 16
+ /MIF=file определить имя главного компилируемого файла
+ /NS запретить подключать stub файлов
+ по умолчанию нет, поддерживает инверсию
+ /NW=## выборочное отключение предупреждений
+ /OBJ создать OBJ файл
+ только 16 битный код.
+ по умолчанию COM
+ /OC оптимизировать по размеру кода
+ по умолчанию нет, поддерживает инверсию
+ /ON оптимизация чисел
+ по умолчанию нет, поддерживает инверсию
+ /OS оптимизация по скорости выполнения
+ по умолчанию да, поддерживает инверсию
+ /OST оптимизация строковых идентификаторов
+ по умолчанию отключено, поддерживает инверсию
+ /P вставить блок разборки командной строки
+ по умолчанию нет, поддерживает инверсию
+ /R вставить блок уменьшающий размер доступной памяти.
+ по умолчанию да, поддерживает инверсию
+ имеет смысл только в DOS-файлах
+ /S=##### установить размер стека
+ по умолчанию 2048
+ /SA=#### начальное смещение адреса запуска программы
+ имеет смысл только в COM-файлах, по умолчанию 0x100
+ /SOBJ создать ведомый OBJ файл
+ по умолчанию COM
+ /STM перенести блок startup кода в процедуру main
+ по умолчанию нет, поддерживает инверсию
+ имеет смысл только в COM-файлах
+ /SUV=#### начальный адрес не инициализированных переменных, при
+ использовании ими startup кода.
+ имеет смысл только в COM-файлах, по умолчанию равен /SA
+ /SYM надстройка для COM файла
+ по умолчанию COM
+ /SYS создать драйвер устройств (SYS)
+ по умолчанию COM
+ /TEXE создать EXE файл для DOS (модель TINY)
+ по умолчанию COM
+ /UL использовать lea при оптимизации сложения регистров.
+ по умолчанию да, поддерживает инверсию
+ /UST использовать startup код для переменных.
+ имеет смысл только в COM-файлах
+ по умолчанию нет, поддерживает инверсию
+ /W разрешить предупреждения
+ по умолчанию нет, поддерживает инверсию
+ /W32 создать EXE файл для Windows32 GUI
+ по умолчанию COM
+ /W32C создать EXE файл для Windows32 console
+ по умолчанию COM
+ /WBSS помещать не инициализированные данные в отдельную секцию.
+ по умолчанию для /w32 разрешено, для остальных запрещено.
+ поддерживает инверсию
+ /WF=file перенаправить вывод предупреждений в файл.
+ по умолчанию нет
+ /WFA использовать быстрые вызовы API процедур
+ по умолчанию да, поддерживает инверсию
+ только под windows
+ /WFU создавать таблицу перемещений (для Windows32)
+ по умолчанию нет, поддерживает инверсию
+ только под windows
+ для DLL устанавливается в да
+ /WIB=##### установить адрес image base
+ по умолчанию 0x400000
+ /WMB создавать Windows-файл с единым блоком
+ по умолчанию да, поддерживает инверсию
+ только под windows
+ для DLL устанавливается в нет
+ /WORDS выдать список зарезервированных идентификаторов
+ /WS=name указывает имя файла используемого в качестве stub под windows.
+ /X запретить вставлять в код SPHINXC-- сигнатуру
+ по умолчанию разрешено, поддерживает инверсию
+ отключается если есть J0
+
+ Примечание: выражение поддерживает инверсию означает, что для данной
+ опции можно использовать и противоположное значение с помощью символа -
+ после опции. Пример:
+
+ /WFA-
+
+ Параметры командной строки можно писать как большими, так и
+ маленькими буквами.
+Return to contents.
+
+
+
+ 2.1.1 /ON - Оптимизация числовых выражений.
+
+
+ При включении в командную строку опции /ON или в файл C--.INI строчки
+ ON, компилятор будет анализировать операции над числами и где это
+ можно, сокращать число операций. Например:
+
+ Строка до оптимизации | После оптимизации
+ -----------------------------------------------
+ AX = var + 7 - 3; | AX = var + 4;
+ AX = var * 2 * 5; | AX = var * 10;
+ AX = var * 2 / 4; | AX = var / 2;
+ AX = var * 10 / 2; | AX = var * 5;
+ AX = var / 2 / 3; | AX = var / 6;
+ AX = var / 4 * 8; | AX = var * 2;
+ AX = var / 16 * 16; | AX = var;
+
+ Возможные отрицательные последствия:
+ Применение этой оптимизации может иметь и негативные последствия.
+ Например, если Вам нужно выровнять значение переменной на границу
+ параграфа, Вы напишите строку:
+
+ var = var / 16 * 16;
+
+ но после оптимизации будет
+
+ var = var;
+
+ т.е. выравнивание не будет произведено. Этого можно избежать, если
+ разбить это выражение на два:
+
+ var = var / 16;
+ var = var * 16;
+
+ тогда оптимизация не будет произведена. Но для получения более
+ компактного кода лучше будет записать так:
+
+ AX = var;
+ AX = AX / 16;
+ AX = AX * 16;
+ var = AX;
+Return to contents.
+
+
+
+ 2.1.2 /DE - Временное расширение разрядности переменной.
+
+
+ Как известно, после умножения может произойти переполнение, т.е
+ разрядность результата может превысить разрядность исходных операндов и
+ произойдет искажение результата. Частично решить эту проблему Вам поможет
+ опция командной строки /DE или строка DE в файле C--.INI. После команды
+ умножения компилятор будет просматривать остаток строки и если обнаружит,
+ что расширение разрядности может быть востребовано (востребовать
+ расширенную разрядность могут операции деления и вычисления остатка), то
+ будут приняты меры по ее сохранению. Например:
+
+ a = b*c+d/e; //здесь будет включена поддержка расширения разрядности
+ a = b*c+d*e; //здесь поддержки расширения разрядности не будет.
+
+ Однако применение этой опции может иметь и негативные последствия.
+ Покажу это на примере:
+
+ пусть имеется выражение
+
+ a = b * c / d;
+
+ если значения переменных b = 0xC000, c = 0x1000, d=0x10, после запуска
+ такая программа зависнет с сообщением о том, что произошло переполнение
+ при делении.
+Return to contents.
+
+
+
+ 2.1.3 /ARGC - Альтернативный обработчик командной строки.
+
+
+ Отличие этого обработчика командной строки от parsecommandline
+ заключается в том, что при вызове PARAMSTR(0); Вы получите адрес строки в
+ которой указан путь и имя запущенной программы. Следующие вызовы этой
+ процедуры с увеличивающимся параметром будут возвращать адреса слов
+ командной строки. А вызов процедуры PARAMCOUNT вернет Вам число слов в
+ командной строке плюс один.
+
+ Альтернативный обработчик командной строки включается директивой
+ ?argc TRUE или из командной строки компилятора ключом /argc или
+ строчкой argc в файле C--.INI.
+Return to contents.
+
+
+
+ 2.1.4 /OST - слияние одинаковых строковых констант.
+
+
+ Если этот режим оптимизации будет активизирован, то компилятор будет
+ запоминать все строковые константы и при обнаружении одинаковых в код
+ файла не будет вставлена повторная строковая константа, а будет сделана
+ ссылка на первую, обнаруженную ранее строковую константу. В оптимизации
+ участвуют только неименованные строковые константы. Т.е. если массив или
+ структура будет инициализированы строкой, то такая строка не будет
+ участвовать в процессе инициализации, так эта строка может быть изменена
+ в процессе работы программы. Пример:
+
+ char var="test"; //эта строка не будет участвовать в процессе
+ //оптимизации.
+
+ void proc(){
+ WRITESTR("test"); // эта строка будет участвовать в оптимизации.
+ AX="test"; // переменной AX будет присвоен адрес строки,
+ // которая была вставлена в код программы в
+ // предыдущей строке.
+ }
+
+ Обо всех случаях обнаружения повторной строки компилятор будет
+ выдавать предупреждения.
+
+ Включается этот режим оптимизации либо с командной строки /ost, либо
+ директивой #pragma option ost, либо строкой в файле c--.ini - ost.
+ Отключить, включенный ранее, этот режим можно директивой #pragma option ost-.
+Return to contents.
+
+
+
+ 2.1.5 /D - установка идентификатора в TRUE из командной строки.
+
+
+ Если Вы написали программу, которая может компилироваться по разному,
+ в зависимости от состояния некоторых идентификаторов (используется режим
+ условной компиляции), то Вам очень может пригодится эта опция.
+ Устанавливая с командной строки различные идентификаторы, Вы можете
+ получать различные варианты программы, не редактируя исходный текст
+ программы.
+
+ Идентификатор вводится с командной строки ключом /d=idname.
+Return to contents.
+
+
+
+ 2.1.6 /IA - упрощенный ввод ассемблерных инструкций.
+
+
+ Стало возможным использовать ассемблерные инструкции без префикса $
+ и вне блока asm. Этот режим включается: с командной строки опцией /ia;
+ в файле конфигурации строкой ia или директивой #pragma option ia.
+
+ Когда этот режим включен, все имена ассемблерных инструкций становятся
+ зарезервированными словами, т.е. Вы не сможете эти имена использовать в
+ качестве имен переменных или процедур. Ассемблерные инструкции компилятор
+ распознает независимо от того, написаны они маленькими или большими
+ буквами.
+Return to contents.
+
+
+
+ 2.1.7 /CRI - пропуск повторно включаемого файла.
+
+
+ Чаще всего, повторно включать файл в компилируемый проект, нет
+ необходимости, но это иногда происходит из-за того, что некоторые
+ включаемые файлы сами включают другие файлы. Чтобы этого не происходило
+ приходится делать проверку на повторную загрузку файла. Теперь эту
+ функцию берет на себя компилятор и у Вас отпадает необходимость делать
+ эту проверку.
+
+ Но иногда (очень редко) возникает потребность сделать повторное
+ включение файла. Для этого в компиляторе есть опция командной строки
+ /cri-, которая запрещает компилятору делать проверку на повторное
+ включение. Соответственно, для c--.ini файла, это можно сделать строкой
+ cri- или директивой в компилируемом файле - #pragma option cri-.
+Return to contents.
+
+
+
+ 2.1.8 /IND - импорт имен процедур из DLL.
+
+
+ Если Вы хотите в своей программе использовать DLL, для которой нет
+ заголовочного файла с описанием процедур, то компилятор может
+ импортировать имена из этой DLL. Для этого Вам надо указать имя этой
+ библиотеки либо через опцию командной строки /ind=name.dll, либо в
+ файле INI строкой 'ind=name.dll', либо через директиву '#pragma option
+ ind=name.dll'.
+
+ К недостатком такого способа получения имен можно отнести то, что при
+ компиляции программы библиотека, из которой импортируются имена,
+ обязательно должна присутствовать в компьютере. Также, если имена в
+ библиотеке написаны без суффикса '@number', компилятор не будет
+ контролировать число параметров передаваемых процедуре. И, к сожалению,
+ компилятор умеет импортировать имена из библиотек имеющих только формат
+ PE-файла.
+Return to contents.
+
+
+
+ 2.1.9 /WS - задать имя stub файла для программ под windows.
+
+
+ Как известно, в программах под windows есть DOS заглушка, называемая
+ stub, которой передается управление при запуске такой программы в чистом
+ DOS-е. Обычно такая заглушка выводит на экран сообщение о том, что эту
+ программу надо запускать в среде windows.
+
+ Вы можете вместо стандартного stub использовать свой. Для этого Вам
+ необходимо указать имя 16-битного EXE-файла либо через опцию командной
+ строки /ws=filename, либо строкой в INI-файле ws=filename, либо
+ директивой #pragma option ws=filename.
+
+ Таким образом, у Вас появилась возможность создавать программы,
+ работающие и под DOS и под windows.
+Return to contents.
+
+
+
+ 2.1.10 /WBSS - разместить не инициализированные данные в отдельной секции.
+
+
+ Секция .bss создается автоматически при компиляции программ с ключом
+ /w32. Если Вы хотите иметь эту секцию и при компиляции программ с
+ ключами /w32c или /dll Вам необходимо добавить либо в командной
+ строке опцию /wbss, либо строку wbss в INI-файле, либо директиву
+ #pragma option wbss.
+
+ Использование секции .bss практически не влияет на размер получаемого
+ файла. Теоретически, для процессоров, у которых есть отдельный кэш для
+ данных, использование секции .bss, должно повышать скорость работы
+ программы.
+Return to contents.
+
+
+
+ 2.1.11 /DBG - создание отладочной информации.
+
+
+ Если при компиляции программы в командную строку добавить ключ /dbg,
+ или в файл конфигурации c--.ini добавить строку dbg, то компилятор после
+ окончания компиляции создаст файл с отладочной информацией. Этот файл
+ имеет имя главного модуля и имеет расширение *.tds.
+
+ Отладочная информация создаваемая компилятором C-- совместима с
+ отладочной информацией создаваемой компиляторами фирмы Borland. Но, пока,
+ эта информация реализована еще не в полном объеме. Создаваемой сейчас
+ отладочной информации достаточно для проведения простейшей отладки
+ программы.
+
+ Для 16-битных программ под DOS для отладки надо использовать Turbo
+ Debugger из пакета Borland C v4.5 или лучше (файл td.exe).
+
+ Для программ под Windows надо использовать 32-битный отладчик из этого
+ же пакета (файл td32.exe).
+
+ Для 32-битных программ, использующих расширитель DOS применять для
+ отладки Turbo Debugger невозможно. Но, может быть я не знаю, как это
+ делать. Если Вы знаете, как создавать 32-битные программы с
+ DOS-расширителем компиляторами фирмы Borland с включением в них отладочной
+ информации, то расскажите мне. А я попробую применить это для C--.
+Return to contents.
+
+
+
+ 2.1.12 /J0 /J1 /J2
+
+
+ Синонимом ключей /J0 /J1 /J2 является директива #jumptomain с
+ параметрами NONE, SHORT и NEAR соответственно.
+
+ Директива #jumptomain выполняет немного различные функции в
+ зависимости от типа выходного файла.
+
+ Компиляция файла типа *.com и *.exe модель памяти tiny:
+
+ #jumptomain NONE (-j0) - в этом случае по окончании кода начальной
+ инициализации программы не генерируется jmp на процедуру main. Эту
+ директиву следует использовать в случае, если до процедуры main нет других
+ не динамических процедур и инициализированных переменных.
+
+ #jumptomain SHORT (-j1) - в этом случае по окончании кода начальной
+ инициализации генерируется короткий jmp на процедуру main. Эту директиву
+ следует использовать, если до процедуры main находится не более 128 байт
+ кода и данных.
+
+ #jumptomain NEAR (-j2) - это состояние устанавливается по умолчанию. При
+ этом генерируется близкий jmp на процедуру main.
+
+ Компиляция файлов *.exe (ключи -exe -d32 -w32 -w32c):
+
+ #jumptomain NONE (-j0) - в этом случае код начальной инициализации
+ программы не генерируется и управление при запуске передается сразу на
+ процедуру main.
+
+ Во всех остальных случаях генерируется код начальной инициализации и
+ управление на процедуру main передается инструкцией call.
+
+ Компиляция файлов *.dll:
+
+ #jumptomain NONE (-j0) - в этом случае код начальной инициализации
+ программы не генерируется и управление при запуске передается сразу на
+ процедуру main.
+
+ Во всех остальных случаях генерируется код заглушки и управление на
+ процедуру main не передается. Фактически процедура main в этом случае не
+ нужна.
+
+ Процедура main при создании файлов DLL должна выглядеть немного иначе,
+ чем в других случаях:
+
+ dword main ( dword hInstDLL, reason, reserv )
+ {
+ ...
+ }
+Return to contents.
+
+
+
+ 2.1.13 /LST - Создание ассемблерного листинга.
+
+
+ С помощью дополнительной опции командной строки -lst Вы можете
+ получить вместе с исполнительным файлом и его ассемблерный листинг.
+ Листинг будет помещен в файл одноименный с исполнительным файлом и
+ имеющим расширение *.lst.
+
+ Ассемблерный листинг создается независимой от компилятора частью кода
+ с использованием информации накапливаемой при компиляции программы.
+Return to contents.
+
+
+
+ 2.1.14 /ENV - Сохранение адреса переменных окружения.
+
+
+ Если при компиляции программы Вы в командную строку добавите опцию
+ -ENV или в файл c--.ini строка ENV, то компилятор добавит в вашу
+ программу переменную environ, в которой при загрузке будет сохранятся
+ адрес переменных окружения запускаемой программы. Для программ под
+ Windows это будет полный адрес, а для остальных в этой переменной будет
+ сохраняться только адрес сегмента.
+Return to contents.
+
+
+
+ 2.1.15 /CPA - Очистка post-области данных.
+
+
+ Переменные, которым в теле программы не было присвоено никакое
+ значение, не включаются в тело скомпилированной программы. Для них
+ резервируется память за пределами программы. Но эта память может быть
+ заполнена произвольной информацией.
+
+ Если Вам необходимо, чтобы неинициализированные переменные при
+ загрузке программы всегда содержали одно и тоже значение (ноль) -
+ включите в командную строку опцию -CPA.
+Return to contents.
+
+
+
+ 2.1.16 /W - вывод предупреждений.
+
+
+ По умолчанию компилятор не выводит предупреждения и многие даже не
+ подозревают о существовании такой полезной опции. В C-- предупреждения
+ фактически являются подсказками для создания оптимальных программ и
+ зачастую облегчают отладку программ. В предупреждениях компилятор может
+ сообщить Вам о том, в каком месте можно использовать короткие формы
+ операторов IF, WHILE, FOR... О том, какие процедуры, переменные и
+ структуры определенные в вашей программе не были использованы. О том
+ какие регистры компилятор использовал без вашего ведома и много другой
+ полезной информации.
+
+ По умолчанию предупреждения выводятся на экран. Но их бывает так
+ много, что они могут не поместиться на экране. Поэтому в компиляторе есть
+ опция, по которой все предупреждения выводятся в файл. Имя этого файла
+ задается в той же опции. Поместив в свой c--.ini файл пару вот этих строк:
+
+ w
+ wf=warning
+
+ Вы будете получать в файле warning предупреждения.
+Return to contents.
+
+
+
+ 2.1.17 /NW - Выборочное отключение типов предупреждений.
+
+
+ Сейчас компилятор может выдавать 12 типов предупреждений и, иногда их
+ бывает так много, что становится трудно в них ориентироваться. Теперь
+ можно выборочно запрещать выдачу предупреждений. Для этого в командной
+ строке (или в файле C--.INI) можно установить опцию /nw=number, где
+ number - число от 1 до 12. Этим цифрам соответствуют следующие типы
+ предупреждений:
+
+ 1 - Optimize numerical expressions
+ 2 - Compiler used register ..."
+ 3 - Short operator '...' may be used
+ 4 - String '...' repeated
+ 5 - Expansion variable
+ 6 - Signed value returned
+ 7 - '...' defined above, therefore skipped.
+ 8 - Variable/structure/procedure '...' possible not used
+ 9 - Non-initialized variable may have been used
+ 10 - Return flag was destroyed
+ 11 - Code may not be executable
+ 12 - Don't use local/parametric values in inline procedures
+Return to contents.
+
+
+
+ 2.1.18 /WSI - короткая таблица импорта.
+
+
+ Таблица импорта обычно состоит в свою очередь из четырех таблиц. Две
+ таблицы LookUp Table и Import Address Table абсолютно одинаковы.
+
+ Опцией командной строки /WSI Вы можете заставить компилятор
+ генерировать только одну из этих двух одинаковых таблиц (генерируется
+ только Import Address Table). Тем самым у Вас получится более компактная
+ таблица импорта, что приведет, в некоторых случаях, к созданию более
+ компактного выходного файла.
+Return to contents.
+
+
+
+ 2.2 Директивы транслятора.
+
+
+ C-- не содержит препроцессор. Тем не менее, есть несколько функций
+ очень похожих на функции C препроцессора.
+
+ Они даются как директивы транслятора. Все директивы транслятора
+ начинаются с вопросительного знака ? либо с символа #. Вот список имеющихся
+ директив и их назначение:
+
+ ? align [val] Выровнять данные программы на четный по
+ умолчанию или на адрес кратный величине val.
+
+ ? aligncode [val] Выровнять код программы на четный по
+ умолчанию или на адрес кратный величине val.
+ Заполнение производится кодом 0x90.
+
+ ? aligner (aligner value) определить значение байта вставки.
+
+ ? alignword (TRUE or FALSE) разрешает или запрещает выравнивание на
+ четный адрес переменных типа word и int,
+ значение по умолчанию TRUE.
+
+ ? argc (TRUE or FALSE) Включить или отключить альтернативный
+ обработчик командной строки.
+
+ ? atexit Вставляет в startup код поддержки процедуры
+ ATEXIT().
+
+ ? code32 (TRUE/FALSE) разрешает/запрещает генерацию 32-битного
+ кода.
+
+ ? codesize оптимизация размера кода (в ущерб скорости).
+
+ ? compilerversion min-vers указывает, компилятор какой версии необходим
+ для компиляции данной программы.
+
+ ? ctrl_c (TRUE or FALSE ) разрешает или запрещает игнорирование
+ нажатия CTRL-C.
+
+ ? dataseg (value) указывает компилятору сегментный адрес ОЗУ
+ для переменных при компиляции ROM-BIOS.
+
+ ? define (identifier) (token) определяет идентификатор.
+
+ ? DOSrequired (номер) устанавливает минимальную требуемую версию
+ DOS: старший байт - номер версии,
+ младший байт - номер модификации:
+ 0x0101 для версии 1.1 DOS
+ 0x0315 для версии 3.21 DOS
+ 0x0303 для версии 3.3 DOS
+ 0x0600 для версии 6.0 DOS
+ 0x0602 для версии 6.2 DOS и т.д.
+
+ ? dosstring (TRUE/FALSE) указывает компилятору, что в качестве
+ терминатора строки надо использовать символ $
+
+ ? else генерирует альтернативный код если ?ifdef или
+ ?ifndef принимают значение FALSE (пример
+ использования смотрите в файле FPU.H--)
+
+ ? endif указывает на конец действия директив ifdef и
+ ifndef
+
+ ? fastcallapi (FALSE/TRUE) запретить/разрешить генерацию быстрого вызова
+ API-процедур (по умолчанию разрешено).
+ Директива работает при компиляции программ
+ под Windows.
+
+ ? fixuptable (TRUE/FALSE) разрешить/запретить создание FixUp таблицы
+ (по умолчанию запрещено). Директива работает
+ при компиляции программ под Windows.
+
+ ? ifdef (identifier) если идентификатор определен, то возвращает
+ TRUE иначе FALSE
+
+ ? imagebase value задает адрес Image Base. По умолчанию этот
+ адрес равен 0x400000. Директива работает при
+ компиляции программ под Windows.
+
+ ? ifndef (identifier) если идентификатор определен, то возвращает
+ FALSE иначе TRUE
+
+ ? include ("filename") включает другой файл.
+
+ ? includepath ("path") указание компилятору, в какой директории надо
+ искать включаемые файлы
+
+ ? initallvar инициализирует 0 все неинициализированные
+ переменные.
+
+ ? jumptomain (NONE, SHORT, NEAR or FALSE)
+ устанавливает тип перехода к main(),
+ значение по умолчанию - NEAR.
+
+ ? maxerrors (number) максимальное количество найденных ошибок,
+ превысив которое транслятор прекращает
+ работу, значение по умолчанию - 16.
+
+ ? movedatarom (TRUE/FALSE) указывает компилятору о необходимости
+ переноса данных из ПЗУ в ОЗУ.
+
+ ? parsecommandline (TRUE or FALSE)
+ включает в программу блок кода для
+ синтаксического анализа командной строки
+ значение по умолчанию FALSE.
+
+ ? pragma может объявлять несколько других директив
+
+ ? print (number or string) выводит на экран строку или число.
+
+ ? printhex (number) выводит на экран число в шестнадцатеричном
+ коде.
+
+ ? randombyte вставляет в код программы байт случайного
+ значения.
+
+ ? resize (TRUE or FALSE) включает функцию изменения после запуска
+ размера выделенного программе блока памяти
+ на минимально требуемый объем,
+ значение по умолчанию TRUE.
+
+ ? resizemessage (string) сообщение, выводимое на экран перед
+ аварийным прерыванием выполнения программы,
+ если изменение размера выделенного программе
+ блока памяти не выполнено.
+
+ ? setdinproc по этой директиве компилятор немедленно
+ вставляет в код компилируемой программы все
+ вызывавшиеся ранее динамические процедуры.
+
+ ? sizerom (value) указывает компилятору размер ПЗУ.
+
+ ? speed оптимизация быстродействия (значение
+ по умолчанию) в ущерб размеру кода.
+
+ ? stack (number) определяет размер стека программы в байтах.
+
+ ? startaddress (number) устанавливает стартовый адрес начала кода,
+ значение по умолчанию 0x100.
+
+ ? startuptomain в com-файлах размещает startup-код в
+ процедуре main().
+
+ ? startusevar (number) указывает адрес, с которого разрешено
+ использовать ячейки памяти под
+ неинициализированные переменные.
+
+ ? sysattribute (значение) эта директива передает компилятору атрибут
+ создаваемого драйвера. По умолчанию
+ устанавливается значение 0x2000.
+ Действует только с ключом /SYS.
+
+ ? sysname <текстовая строка> эта директива передает компилятору имя
+ будущего драйвера. По умолчанию
+ присваивается имя NO_NAME. Длина имени не
+ более 8 символов. Действует только с ключом
+ /SYS.
+
+ ? syscommand ,, ...; - эта директива
+ является обязательной при создании
+ драйверов. По этой директиве компилятору
+ передается список имен процедур обработки
+ команд драйвера. Действует только с ключом
+ /SYS.
+
+ ? warning (TRUE or FALSE) эта директива разрешает или запрещает выдачу
+ предупреждений. Директива действует только в
+ пределах текущего файла и не влияет на
+ включаемые файлы.
+
+ ? winmonoblock FALSE запрещает размещение таблиц файла формата PE
+ в одну секцию.
+
+ ? undef уничтожает константы объявленные директивой
+ ? define
+
+ ? use8086 ограничивается при генерации объектного кода
+ командами 8088/8086 (значение по умолчанию).
+
+ ? use8088 ограничивается при генерации объектного кода
+ командами 8088/8086 (значение по умолчанию).
+
+ ? use80186 допускает при генерации объектного кода
+ команды и оптимизацию для процессора 80186.
+
+ ? use80286 допускает при генерации объектного кода
+ команды и оптимизацию для процессора 80286.
+
+ ? use80386 допускает при генерации объектного кода
+ команды и оптимизацию для процессора 80386.
+
+ ? use80486 допускает при генерации объектного кода
+ команды и оптимизацию для процессора 80486.
+
+ ? usePentium допускает при генерации объектного кода
+ команды и оптимизацию для процессора Pentium.
+
+ ? useMMX допускает при генерации объектного кода
+ команды и оптимизацию для процессора Pentium
+ MMX.
+
+ ? usestartup разрешает компилятору использовать ячейки
+ памяти, занимаемые кодом начальной
+ инициализации программы.
+Return to contents.
+
+
+
+ 2.2.1 ?ifdef/?ifndef
+
+
+ Ранее директива ?ifdef срабатывала на наличие константы независимо
+ от значения ее величины, а директива ?ifndef срабатывала на отсутствие
+ константы в компилируемом файле. Теперь ?indef срабатывает лишь на
+ константу отличную от FALSE, а ?ifndef срабатывает как на отсутствие
+ константы в компилируемом файле, так и на константу имеющую значение
+ FALSE.
+
+ Для директив ?ifdef/?ifndef зарезервированы константы codesize и
+ speed, которые принимают значение TRUE или FALSE в зависимости от режима
+ оптимизации. Это будет полезным для создания более гибких библиотек.
+
+ Есть возможность проверки типа CPU для которого ведется компиляция.
+ Допустимые варианты синтаксиса:
+
+ ?ifdef cpu > 1 //если программа компилируется для CPU выше 80186
+ ?ifndef cpu >= 2 // -------//------------- не больше или равно 80286
+ ?ifdef cpu == 3 // -------//------------- равно 80386
+ ?ifdef cpu != 0 // -------//------------- не равен 8086
+ ?ifdef cpu < 3 // -------//------------- хуже 80386
+ ?ifdef cpu <= 2 // -------//------------- хуже или равен 80286
+
+ Эта директива позволит Вам писать одну процедуру для различных типов
+ CPU.
+Return to contents.
+
+
+
+ 2.2.2 ?initallvar
+
+
+ Директивой ?initallvar TRUE включается режим при котором всем
+ неинициализированным переменным будет присвоено нулевое значение и они
+ будут располагаться в том месте, где были объявлены. Т.е. практически
+ исчезнут неинициализированные переменные. Это может быть полезным при
+ написании драйверов и резидентных программ.
+
+ Параметр FALSE этой директивы отключает этот режим.
+ По умолчанию эта директива установлена в FALSE.
+Return to contents.
+
+
+
+ 2.2.3 ?usestartup
+
+
+ Директива ?usestartup разрешает компилятору использовать ячейки кода
+ начальной инициализации программы (startup) для последующего размещения в
+ них неинициализированных переменных. Это может быть полезным для получения
+ более компактного кода, как обычных программ, так и в особенности
+ резидентных.
+
+ Эту директиву применяют только для генерации *.COM файлов.
+Return to contents.
+
+
+
+ 2.2.4 ?startusevar
+
+
+ Директивой ?startusevar можно указать начальный адрес с которого
+ компилятор будет распределять память для неинициализированных переменных.
+ Например, получив директиву ?startusevar 0x53 компилятор будет
+ располагать неинициализированные переменные, начиная с адреса 0x53. Это
+ может быть полезным для получения более компактного кода как для
+ резидентных, так и для обычных программ.
+
+ Эту директиву применяют только для генерации *.COM файлов.
+Return to contents.
+
+
+
+ 2.2.5 ?atexit
+
+
+ Директива ?atexit добавляет в startup программы код поддержки
+ процедуры ATEXIT, резервирует место для хранения 16 адресов процедур и
+ изменяет код процедур ABORT и EXIT.
+
+ Процедура ATEXIT регистрирует процедуру, адрес которой передается ей в
+ качестве параметра, как процедуру завершения программы. Эта процедура
+ будет вызвана в момент завершения программы процедурами ABORT или EXIT
+ или инструкцией RET из main.
+
+ Всего можно зарегистрировать до 16 процедур. Процедуры вызываются в
+ порядке обратном порядку их регистрации.
+Return to contents.
+
+
+
+ 2.2.6 ?startuptomain
+
+
+ По этой директиве компилятор в начале файла делает jmp на начало
+ процедуры main(). Перед началом компиляции этой процедуры компилятор
+ начнет компиляцию startup кода и лишь затем будет продолжена компиляция
+ процедуры main(). Тем самым startup код окажется не в начале файла, как
+ это происходит обычно, а в теле процедуры main(). Это будет полезным при
+ компиляции резидентных программ (TSR).
+
+ Директива ?startuptomain работает только при компиляции com-файлов.
+Return to contents.
+
+
+
+ 2.2.7 ?undef
+
+
+ Эта директива уничтожает константы объявленные директивой ?define. Ее
+ можно применять для изменения в процессе компиляции значения какой-нибудь
+ константы.
+Return to contents.
+
+
+
+ 2.2.8 ?align и ?aligncode
+
+
+ В C-- существует директива ?align, которая делает однократное
+ выравнивание данных на четный адрес. Но если к этой директиве добавить
+ число, то выравнивание будет произведено на адрес кратный этому числу.
+ Например директива ?align 4 дополнит сегмент данных до адреса кратного
+ 4. При выравнивании будут вставляться байты, значения которых определяются
+ директивой ?aligner, по умолчанию это значение равно нулю. Директива
+ ?align производит выравнивание только в сегменте данных. В тех моделях
+ памяти, в которых сегмент данных и кода совпадают эту директиву можно
+ применять и для выравнивания начала процедур.
+
+ Директива ?aligncode [value] делает выравнивание в сегменте кода на
+ адрес кратный значению value, по умолчанию на четный адрес. Значение байта
+ заполнения в этой директиве является число 0x90 - код инструкции NOP.
+ Значение байта заполнения для этой директивы изменить нельзя. Т.о. эту
+ директиву можно применять и внутри исполняемого кода. Например, если Вы
+ хотите получить быстрый код на 486 процессоре, то рекомендуется делать
+ выравнивание начала процедур и циклов на адрес кратный 16.
+Return to contents.
+
+
+
+ 2.2.9 ?pragma
+
+
+ Директива #pragma это многофункциональнальная директива, которая в
+ свою очередь имеет свои директивы:
+
+ option
+ Директива option позволяет включить в Ваш код опции командной строки
+ компилятора. Некоторые опции не могут быть использованы в этой директиве;
+ другие должны помещаться в самом начале исходного текста. Пример:
+
+ #pragma option w32c
+
+ Эта директива объявляет компилятору, что надо создать консольный
+ 32-битный файл под windows.
+
+ startup
+ Директивой startup можно указать функцию, которая будет выполнена перед
+ запуском процедуры main. Эта директива имеет такой формат:
+
+ #pragma startup procname
+
+ Количество раз, которое можно применять эту директиву в одной
+ программе не ограничено, но реально можно использовать лишь несколько
+ тысяч раз.
+
+ line
+ Директива line выводит на экран номер текущей строки и имя файла.
+ Дополнительно может выводиться содержимое строки находящееся после слова
+ line. Пример:
+
+ #pragma line information
+
+ Встретив эту директиву, компилятор выведет на экран номер строки и имя
+ файла. Также будет выведено сообщение справа от слова line, если оно
+ есть.
+
+ resource
+ Эта директива может принимать значения start и end. Эти два
+ значения выделяют начало и конец блока ресурсов, если вы используете его
+ непосредственно в исходном коде файла, а не в отдельном файле. Пример:
+
+ #pragma resource start
+
+ MyMenu MENU DISCARDABLE
+ BEGIN POPUP "Files",HELP
+ BEGIN
+ MENUITEM "Open", ID_OPEN
+ MENUITEM "Save", ID_SAVE
+ MENUITEM SEPARATOR
+ MENUITEM "Exit", ID_EXIT
+ END
+ MENUITEM "Other", 65535
+ END
+
+ #pragma resource end
+Return to contents.
+
+
+
+3. Константы.
+
+ 3.1 Числовые константы.
+
+
+ Представление числовых констант в виде десятичных чисел (чисел с
+ основанием 10) и шестнадцатеричных чисел (основание счисления 16) полностью
+ аналогично языку C.
+
+ При двоичном представлении чисел (основание 2) число должно начинаться
+ с символов 0b, за которыми без пробела идет последовательность нулей и
+ единиц.
+
+ При восьмеричном представлении чисел (основание 8) число должно
+ начинаться с символов 0o, за которыми без пробела идет последовательность
+ цифр.
+
+ Вещественное число отличается от целого по наличию в нем точки.
+ Начинаться вещественное число должно либо цифрой от 0 до 9, либо знаком
+ минус. Необязательной частью вещественного числа является показатель
+ степени. Показатель степени отделяется от числа символом e или E.
+ Пробелы недопустимы.
+
+ Примеры:
+ 0b11111111 // двоичное представление числа 255
+ 0x00F // шестнадцатеричное представление числа 15
+ 0o10 // восьмеричное представление числа 8
+ 1.234567E-20 // вещественное число
+
+ C-- вместе с традиционным C-стилем шестнадцатеричных чисел понимает и
+ числа записанные в стиле ассемблера. Для тех, кто вдруг не знает, сообщаю,
+ что шестнадцатеричные числа в ассемблере имеют на конце символ h или H.
+ Если первый символ шестнадцатеричного числа больше 9, то перед ним
+ обязательно должен быть записан символ 0. Примеры:
+
+ 1234h
+ 0A000H
+
+ К числовым константам можно писать суффиксы L, U и F. Фактически
+ эти суффиксы в C-- не играют никакой роли, компилятор их просто
+ проглатывает. Пример:
+
+ #define DEF 1L
+ #define DEF2 2Lu
+ #define DEF3 3.0F
+
+ Эти суффиксы не зависят от регистра, т.е. их можно писать как
+ маленькими, так и большими буквами.
+Return to contents.
+
+
+
+ 3.2 Символьные константы.
+
+
+ Одиночные символьные константы, как и в C, должны заключаться в
+ одиночные кавычки '.
+
+ Также как и в C, для обозначения специальных символов служит обратная
+ наклонная черта вправо \ с последующим за ней ключевым символом (или
+ несколькими символами). Поддерживаются следующие специальные символы:
+
+ \a /* звуковой сигнал */
+ \b /* забой */
+ \f /* перевод страницы */
+ \l /* перевод строки */
+ \n /* возврат каретки*/
+ \r /* возврат каретки*/
+ \t /* табуляция */
+ \x?? /* символ ASCII, соответствующий байтовому представлению,
+ состоящему из двух шестнадцатеричных цифр, расположенных
+ на месте знаков вопроса */
+ \??? /* символ ASCII, соответствующий байтовому представлению,
+ состоящему из трех десятичных цифр, расположенных
+ на месте знаков вопроса */
+
+ Любой другой символ после обратной наклонной черты вправо будет принят
+ как простой символ.
+
+ Символ "Одиночная кавычка" ' может быть введен при помощи конструкции
+ \'
+
+ Символ NULL может быть введен как ''
+
+ В C-- поддерживаются и многобуквенные символьные константы. Примеры
+ многобуквенных символьных констант:
+
+ 'ab'
+ 'the'
+ 'this is large'
+
+ Никакого ограничения на число символов в символьной константе не
+ накладывается, но различаются только последние 4 символа. Это - максимум,
+ который может быть сохранен в 32-разрядной переменной. Например, константы
+ this is large и arge - одинаковы.
+
+ C-- обрабатывает все символьные константы как числовые значения ASCII
+ символов. Для многобуквенных символьных констант первый символ
+ соответствует старшим разрядам, таким образом, значение для ab будет
+ закодировано как a*256+b.
+Return to contents.
+
+
+
+ 3.3 Строковые константы.
+
+
+ Строковые константы, как и в C, заключаются в двойные кавычки (").
+ Специальные символы внутри строк обозначаются так же, как и в символьных
+ константах. Все специальные символы имеют то же значение, что и в
+ символьных константах за исключением \n, который имеет значение новая
+ строка и заменяет собой пару символов возврат каретки и перевод
+ строки.
+
+ В настоящее время наибольшая длина строковой константы - 2048 символов,
+ включая символ-ограничитель 0, таким образом, максимум 2047 значащих
+ символов.
+Return to contents.
+
+
+
+ 3.4 Постоянные выражения.
+
+
+ Постоянное выражение - одиночная числовая константа или несколько
+ числовых констант, связанных между собой операторами. Числовое значение
+ выражения вычисляется один раз во время компиляции и далее используется
+ только его постоянное значение.
+
+ Подобно всем выражениям в C--, постоянные выражения всегда вычисляются
+ слева направо, невзирая на правила арифметики! Это совершенно отлично от
+ других языков, и при написании выражений надо быть осторожным и помнить,
+ что 2+3*2=10 а не 8.
+
+ Некоторые примеры постоянных выражений:
+ 45 & 1 + 3 // равняется 4
+ 14 - 1 / 2 // равняется 6 (помните целочисленные значения)
+ 1 * 2 * 3 / 2 + 4 // равняется 7
+ Примеры с применением вещественных чисел:
+ 3.23*1.53+2.0E2 // равняется 204.9419
+Return to contents.
+
+
+
+4. Выражения.
+
+ 4.1 Типы выражений.
+
+
+ Имеются три типа выражений в C--, не считая постоянных выражений. Это
+ выражения типа EAX/AX/AL, выражения типа неEAX/AX/AL и условные выражения.
+ Все C-- выражения вычисляются слева направо, независимо от старшинства
+ входящих в выражение математических операций.
+Return to contents.
+
+
+
+ 4.2 Выражения типа EAX/AX/AL.
+
+
+ Этот тип выражений применяется в случае, когда его результат может быть
+ сохранен в переменной в памяти или в регистре EAX или AX или AL.
+
+ Если результат может быть сохранен в переменных типа byte или char,
+ используется нотация AL.
+
+ Если результат может быть сохранен в переменных типа word или int,
+ используется нотация AX.
+
+ Если результат может быть сохранен в переменных типа dword, long или
+ float, используется нотация EAX.
+Return to contents.
+
+
+
+ 4.3 Выражения использующие получатель при вычислении выражения.
+
+
+ Если в правой части выражения используется переменная являющаяся
+ одновременно и приемником, то такие выражения дают различные результаты в
+ зависимости от того является приемник регистром или переменной памяти. Это
+ связано с тем, что при вычислении выражения в переменную памяти, вычисление
+ производится сначала в регистр EAX/AX/AL, и лишь после окончания вычисления
+ результат будет записан в приемник. Если же приемником является регистр, то
+ его значение будет меняться после каждой операции вычисления. Пример:
+
+ int var;
+ var = BX = 2;
+ var = 3 + var; // результатом будет 5
+ BX = 3 + BX; // результатом будет 6
+Return to contents.
+
+
+
+ 4.4 Не - EAX/AX/AL выражения.
+
+
+ Этот тип выражений применяется в случае, когда его результат должен
+ быть сохранен в любом другом регистре, отличном от аккумулятора EAX, AX
+ или AL. В процессе вычисления выражения этого типа меняется только
+ содержимое указанного регистра-получателя, все другие регистры будут
+ сохранены. Если регистром-получателем служит байтовый регистр, а при
+ вычислении используются величины размером в слово, одновременно с записью в
+ младший байт может быть разрушено содержимое старшего байта
+ регистра-получателя.
+
+ Это обстоятельство накладывает некоторые ограничения на операции и
+ операнды, допустимые в выражениях типа не EAX/AX/AL. Внутри выражений
+ байтового типа не допускается:
+
+ - делать вызовы МАКРОКОМАНД,
+ - делать вызовы РЕГИСТРОВЫХ процедур
+ - делать вызовы СТЕКОВЫХ процедур
+
+ Ранее в не-EAX/AX/AL выражениях было можно использовать лишь
+ операции: сложения, вычитания, XOR, OR, AND. Теперь для 16 и 32 битных
+ регистров почти все ограничения сняты. Но есть еще ограничения на регистры.
+ Например, если в выражении используется сдвиг на значение переменной, а
+ приемником являются регистры CX/ECX, то такое выражение компилятор не будет
+ компилировать:
+
+ CX = var * SI * 3 * var >> 3; //вызовет сообщение об ошибке
+
+ Примечание: для 8 битных не-AL выражений умножать можно только на
+ числа: 0, 1, 2, 4, 8, 16, 32, 64 и 128. Все эти ограничения связаны со
+ стремлением не разрушать другие регистры при использовании не-EAX/AX/AL
+ выражений.
+Return to contents.
+
+
+
+ 4.5 Условные выражения.
+
+
+ Условные выражения - выражения, результатом вычисления которых является
+ логическое значение да или нет, используемое в операторе if и циклах do {}
+ while, while, for.
+
+ Имеются два типа условных выражений, простые и сложные.
+
+ Возможно логическое объединение условий.
+Return to contents.
+
+
+
+ 4.5.1 Простые условные выражения.
+
+
+ Простые условные выражения - одиночная лексема или выражение, которое
+ примет значение да, если расчетное значение отлично от нуля, или значение
+ нет, если расчетное значение равно нулю.
+Return to contents.
+
+
+
+ 4.5.2 Сложные условные выражения.
+
+
+ Сложные условные выражения имеют следующую форму:
+
+ (левая_часть оператор_отношения правая_часть)
+
+ Где:
+ левая_часть - любое выражение типа AL/AX/EAX или постоянное выражение.
+ Тип выражения определяется по типу первой лексемы
+ (регистра или переменной); значение типа по умолчанию -
+ word для 16-битных программ и dword для 32-битных. Если
+ желателен другой тип, перед выражением ставится
+ соответствующее ключевое слово, определяющее его тип:
+ byte, char, int, long, dword или float
+
+ оператор_отношения - любой из операторов отношения:
+ ==, !=, <>, <, >, <=, или >=.
+
+ правая_часть - любой одиночный регистр, одиночная переменная или
+ постоянное выражение.
+
+ Примеры правильных сложных условных выражений:
+
+ (X + y > z)
+ (int CX*DX < = 12*3)
+ (byte first*second+hold == cnumber)
+
+ Примеры недопустимых сложных условных выражений:
+
+ (x+y >= x-y) // правая часть не является одиночной лексемой или
+ постоянным выражением.
+ (Z = y) // вместо == ошибочно поставлен =
+Return to contents.
+
+
+
+ 4.6 Изменение типа выражения при присваивании.
+
+
+ Если после знака равенства написать тип отличный от типа вычисляемой
+ переменной, то все переменные участвующие в процессе вычисления, будут
+ преобразовываться к этому новому типу, и лишь конечный результат будет
+ преобразован к типу вычисляемой переменной. Пример:
+
+ int i, a;
+ long b;
+ char c;
+
+ i = a * b + c ;
+
+ Значения переменных a, b, и c в этом примере перед вычислением будут
+ преобразованы к типу int (типу переменной i). Но если записать это
+ выражение вот так:
+
+ i = long a * b + c ;
+
+ то переменные a, b, и c в этом примере перед вычислением будут
+ преобразованы к типу long, а конечный результат будет преобразован к типу
+ переменной i - int.
+Return to contents.
+
+
+
+ 4.7 Вычисление в регистры EAX/AX/AL со знаком.
+
+
+ По умолчанию все вычисления в регистры производятся как с без знаковыми
+ величинами.
+
+ Например:
+
+ int a,b,c;
+ AX = a * b / c ;
+
+ При этом компилятор генерировал без знаковые инструкции div и mul, так как
+ регистры считаются без знаковыми переменными. Если написать вот так:
+
+ AX = int a * b / c ;
+
+ то компилятор сгенерирует инструкции idiv и imul.
+
+ Обращаю ваше внимание, что для регистра AL можно использовать только
+ модификатор char, для AX соответственно только int, а для EAX - long. Для
+ остальных регистров подобное делать нельзя.
+Return to contents.
+
+
+
+5. Идентификаторы.
+
+ 5.1 Формат идентификатора.
+
+
+ Идентификаторы в C-- должны начинаться или с символа подчеркивания _
+ или заглавных или строчных букв. Следующие символы могут быть любой
+ комбинацией символов подчеркивания, заглавных или строчных букв или чисел
+ (от 0 до 9). Общая длина идентификатора не может превышать 64 символа.
+ Символы с кодом больше 0x7A (код символа z) недопустимы.
+
+ Примеры допустимых идентификаторов:
+
+ _DOG
+ Loony12
+ HowdYBoys_AND_Girls
+ WOW___
+ X
+
+ Примеры недопустимых идентификаторов:
+
+ 12bogus /* не может начинаться с числа */
+ WowisthisalongidentifieryupitsureisnotOyoulengthismorethat64chars
+ /*длина идентификатора превышает 64 */
+ Y_es sir /* пробелы недопустимы */
+ The-end /* дефисы недопустимы */
+Return to contents.
+
+
+
+ 5.2 Зарезервированные идентификаторы.
+
+
+ Список зарезервированных в C-- идентификаторов, которые не могут
+ использоваться как общие идентификаторы, поскольку они уже были определены
+ или зарезервированы для других целей:
+
+ BREAK CASE CONTINUE ELSE EXTRACT FALSE FOR
+ FROM GOTO IF LOOPNZ RETURN SWITCH TRUE
+ WHILE
+
+ CARRYFLAG MINUSFLAG NOTCARRYFLAG NOTOVERFLOW
+ NOTZEROFLAG OVERFLOW PLUSFLAG ZEROFLAG
+
+ __CODEPTR__ __COMPILER__ __DATAPTR__ __DATESTR__ __DATE__ __DAY__
+ __HOUR__ __LINE__ __MINUTE__ __MONTH__ __POSTPTR__ __SECOND__
+ __TIME__ __VER1__ __VER2__ __WEEKDAY__ __YEAR__
+
+ _export asm break byte case cdecl char continue
+ default do dword else enum extern far fastcall
+ float for goto if inline int interrupt long
+ loop loopnz pascal return short signed sizeof static
+ stdcall struct switch union unsigned void while word
+
+ ESCHAR ESBYTE ESINT ESWORD ESLONG ESDWORD ESFLOAT
+ CSCHAR CSBYTE CSINT CSWORD CSLONG CSDWORD CSFLOAT
+ SSCHAR SSBYTE SSINT SSWORD SSLONG SSDWORD SSFLOAT
+ DSCHAR DSBYTE DSINT DSWORD DSLONG DSDWORD DSFLOAT
+ FSCHAR FSBYTE FSINT FSWORD FSLONG FSDWORD FSFLOAT
+ GSCHAR GSBYTE GSINT GSWORD GSLONG GSDWORD GSFLOAT
+
+ AX CX DX BX SP BP SI DI
+ EAX ECX EDX EBX ESP EBP ESI EDI
+ AL CL DL BL AH CH DH BH
+ ES CS SS DS FS GS
+
+ ST(0) ST(1) ST(2) ST(3) ST(4) ST(5) ST(6) ST(7) ST
+ st(0) st(1) st(2) st(3) st(4) st(5) st(6) st(7) st
+
+ Этот список может быть получен из C-- транслятора в любое время,
+ запуском его с опцией /WORDS из командной строки.
+
+ Если Вы пользуетесь при компиляции опцией командной строки /ia, которая
+ позволяет использовать ассемблерные инструкции не заключая их в блоки asm и
+ без префикса $, то все имена ассемблерных инструкций становятся
+ зарезервированными словами. Причем имена ассемблерных инструкций компилятор
+ различает независимо от того, написаны они маленькими или большими буквами.
+
+ Список имен поддерживаемых компилятором ассемблерных инструкции можно
+ получить запустив компилятор с опцией /LAI.
+
+ Кроме этого в ассемблерных инструкциях становятся зарезервированными
+ следующие идентификаторы:
+
+ ax cx dx bx sp bp si di
+ eax ecx edx ebx esp ebp esi edi
+ al cl dl bl ah ch dh bh
+ es cs ss ds fs gs
+
+ DR0 DR1 DR2 DR3 DR4 DR5 DR6 DR7
+ CR0 CR1 CR2 CR3 CR4 CR5 CR6 CR7
+ TR0 TR1 TR2 TR3 TR4 TR5 TR6 TR7
+ MM0 MM1 MM2 MM3 MM4 MM5 MM6 MM7
+ XMM0 XMM1 XMM2 XMM3 XMM4 XMM5 XMM6 XMM7
+
+ dr0 dr1 dr2 dr3 dr4 dr5 dr6 dr7
+ cr0 cr1 cr2 cr3 cr4 cr5 cr6 cr7
+ tr0 tr1 tr2 tr3 tr4 tr5 tr6 tr7
+ mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7
+ xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7
+Return to contents.
+
+
+
+ 5.3 Универсальные регистры для 16 и 32-битного режима.
+
+
+ При создании библиотечных процедур очень часто приходится писать
+ варианты процедуры для работы в 16-битном и 32-битном режимах, которые
+ отличаются друг от друга лишь использованием в них либо 16-битных либо
+ 32-битных регистров соответственно. Но можно писать лишь одну процедуру,
+ используя в ней новый синтаксис регистров. Если компилятор встретит вот
+ такой синтаксис:
+
+ (E)AX=0;
+
+ то компилятор будет использовать при компиляции 16-битного кода регистр
+ AX, а при компиляции 32-битного кода регистр EAX.
+
+ Использование автоматических регистров позволит упростить библиотечные
+ файлы и сделать их более понятными.
+Return to contents.
+
+
+
+ 5.4 Предопределенные идентификаторы.
+
+
+ Идентификаторы, определяемые компилятором в зависимости от режима
+ компиляции:
+
+ __TLS__ идет компиляция под windows (w32, w32c, dll).
+ __DLL__ идет компиляция dll.
+ __CONSOLE__ идет компиляция консольного приложения windows
+ __WIN32__ идет компиляция GUI-шного приложения
+ __FLAT__ компилируется 32-битный код.
+ __MSDOS__ компилируется 16-битный код.
+ __TINY__ используется модель памяти tiny в 16-битном режиме
+ __SMALL__ используется модель памяти small в 16-битном режиме
+ __DOS32__ компилируется 32-битный код под DOS (d32)
+ __COM__ компилируется com-файл
+ __SYS__ компилируется sys-файл
+ __ROM__ компилируется rom-файл
+ __OBJ__ компилируется obj-файл
+ __TEXE__ компилируется exe-файл модели tiny
+ __EXE__ компилируется exe-файл модели small
+ __MEOS__ компилируется исполняемый файл для MenuetOS
+ codesize компиляция ведется с оптимизацией на размер кода
+ speed компиляция ведется с оптимизацией на быстродействие кода
+ cpu определяет тип процессора для которого ведется компиляция:
+ 0 - 8086
+ 1 - 80186
+ 2 - 80286
+ 3 - 80386
+ 4 - 80486
+ 5 - Pentium
+ 6 - Pentium MMX
+ 7 - Pentium II
+
+ Эти идентификаторы могут быть проверены директивами #ifdef или #ifndef.
+ Идентификатор cpu может быть использован лишь с операторами проверки
+ условий:
+
+ #ifdef cpu > 3 //если тип процессора больше 80386
+Return to contents.
+
+
+
+6. Переменные.
+
+ 6.1 Типы переменных.
+
+
+ В C-- имеется семь типов переменных (именованных областей памяти), это:
+ byte, word, dword, char, int, long, float.
+
+ Следующая таблица показывает размер и диапазон представляемых величин
+ каждого из типов переменной:
+
+ NAME | SIZE | VALUE RANGE | VALUE RANGE
+ тип |размер | диапазон представления | диапазон представления
+ |в байт.| в десятичной системе | в шестнадцатеричной системе
+ ---------------------------------------------------------------------------
+ byte | 1 | 0 to 255 | 0x00 to 0xFF
+ word | 2 | 0 to 65535 | 0x0000 to 0xFFFF
+ dword | 4 | 0 to 4294967295 | 0x00000000 to 0xFFFFFFFF
+ char | 1 | -128 to 127 | 0x80 to 0x7F
+ int | 2 | -32768 to 32767 | 0x8000 to 0x7FFF
+ long | 4 | -2147483648 to 2147483647 | 0x80000000 to 0x7FFFFFFF
+ float | 4 | -3,37E38 to +3,37E38 | 0xFF7FFFFF to 0x7FFFFFFF
+
+ Примечание: для работы с типами float, dword и long используются
+ 32-разрядные целочисленные команды, следовательно, для их выполнения нужно
+ иметь процессор не хуже 80386, что сейчас не является большой проблемой.
+
+ Для совместимости со стандартом, принятом в языке C, введены
+ новые зарезервированные слова: short, signed, unsigned. Для типа int
+ в 32-битном режиме изменена разрядность. Вот таблица всех вариантов новых
+ типов данных:
+
+ ---------------------------------------------------------
+ | полный тип |допустимые сокращения|старые аналоги|
+ ---------------------------------------------------------
+ |signed char |char | char |
+ |signed int |signed, int | int/long |
+ |signed short int |short, signed short | int |
+ |signed long int |long, signed long | long |
+ |unsigned char |--- | byte |
+ |unsigned int |unsigned | word/dword |
+ |unsigned short int|unsigned short | word |
+ |unsigned long int |unsigned long | dword |
+ ---------------------------------------------------------
+
+ Старые типы byte, word и dword поддерживаются по прежнему и имеют
+ функционально прежнее значение. Изменения коснулись лишь типа int. Он в
+ 16-битном режиме, также как и тип unsigned int, имеет 16-битный размер, а
+ в 32-битном режиме эти оба типа имеют размер в 32-бита. На первый взгляд
+ такие свойства типа int вносят некоторую путаницу, но это дает большой
+ выигрыш при использовании этого типа в библиотечных файлах, которые могут
+ быть использованы при компиляции 16-битных и 32-битных программ.
+Return to contents.
+
+
+
+ 6.2 Объявление переменных.
+
+
+ Синтаксис для объявления переменных следующий:
+
+ variable-type identifier;
+
+ где variable-type - char, byte, int, word, long, dword или float.
+
+ Одновременно могут быть объявлены несколько идентификаторов одного типа:
+
+ variable-type identifier1, identifier2, ... , identifierN;
+
+ Одномерные массивы могут быть объявлены следующим образом:
+
+ variable-type identifier[elements];
+
+ где elements - постоянное выражение для количества переменных этого типа,
+ объединенных в массив.
+
+ Инициализированные массивы можно объявлять без указания числа
+ элементов. При этом будет создан массив по фактическому числу элементов.
+
+ variable-type identifier[] = { const1, const2 };
+
+ Переменные при объявлении могут быть проинициализированы следующим
+ образом:
+
+ variable-type identifier = value;
+
+ Некоторые примеры глобальных объявлений:
+ byte i,j; /* объявляет две переменные типа byte с именами i и j */
+ word see[10] /* объявляет массив с именем see, состоящий из 10
+ элементов типа word */
+ int h,x[27] /* объявляет, переменную типа int с именем h,
+ и массив с именем x, состоящий из 27 элементов типа int */
+ long size=0; /* объявлена переменная типа long с именем size и ей присвоено
+ значение 0. */
+Return to contents.
+
+
+
+ 6.3 Глобальные переменные.
+
+
+ Глобальные переменные - это переменные, область действия которых
+ распространяется на всю программу. В C-- использовать глобальные переменные
+ можно в процедурах, расположенных ниже места ее объявления. Т.е. если Вы
+ пишите процедуру, в которой используете переменную var, а саму переменную
+ объявляете ниже текста процедуры, то компилятор выдаст ошибку. Это связано
+ с тем, что компилятор может знать тип переменной только после их
+ объявления. Но для таких переменных можно использовать взятие их адреса,
+ так как адрес переменной не зависит от его типа. Пример:
+
+ void Proc(){
+ gvar = 0; /* компилятор выдаст сообщение об ошибке, т.к. он еще не знает
+ типа переменной gvar */
+ AX = #gvar; /* несмотря на то, что компилятор не знает и адреса этой
+ переменной такое выражение будет откомпилировано */
+ }
+ int gvar;
+
+ Но все же ситуация не безнадежна и нам удастся добиться того, чего мы
+ задумали. В этом нам поможет альтернативный синтаксис обращения к
+ переменным:
+
+ void Proc(){
+ DSINT[#gvar] = 0; /* компилятор успешно откомпилирует это выражение т.к.
+ ему теперь известен тип переменной gvar */
+ }
+ int gvar;
+
+ Память под глобальные переменные выделяется в сегменте данных. Если
+ переменная при объявлении инициализируется (т.е. ей присвоено какое-то
+ значение), то переменная будет включена в код компилируемого файла. Если
+ переменная не инициализируется, то место для переменной будет
+ зарезервировано сразу же за последним байтом скомпилированной программы.
+Return to contents.
+
+
+
+ 6.4 Локальные переменные.
+
+
+ Локальные переменные - это переменные область действия которых
+ распространяется лишь в пределах одной процедуры. Объявлять локальные
+ переменные, в отличии от современных версий C, можно между именем процедуры
+ и первой открывающейся фигурной скобкой. Пример:
+
+ void PROC ()
+ int i; //объявлена локальная переменная типа int с именем i
+ {
+ for ( i=0; i<10; i++ ) WRITE(1);
+ }
+
+ Память под локальные переменные отводится в сегменте стека.
+
+ К локальным переменным можно отнести и параметры стековых процедур. Под
+ них также отводится память в стеке.
+
+ Можно инициализировать локальные переменные при их объявлении. Но есть
+ некоторые ограничения. Нельзя инициализировать массивы и многомерные
+ структуры. Инициализировать можно одним значением, т.е нельзя при
+ инициализации локальных переменных пользоваться перечислением заключенным в
+ фигурные скобки и операторами FROM и EXTRACT.
+
+ Имена локальных переменных могут совпадать с именами глобальных
+ переменных или процедур, но тогда Вы не сможете обратиться к глобальной
+ переменной или вызвать одноименную процедуру.
+
+ Локальные переменные можно объявлять и в начале блока процедуры. Но
+ только до начала тела процедуры. Пример:
+
+ void proc(){
+ int locproc; // объявление локальной процедуры
+ locproc=0; // а теперь пошло тело процедуры
+ int locproc; // а на это объявление переменной компилятор выдаст сообщение
+ // об ошибке, т.к. уже началось тело процедуры
+ }
+Return to contents.
+
+
+
+ 6.5 Динамические переменные и структуры.
+
+
+ Наряду с уже известными Вам динамическими процедурами в C-- есть
+ возможность использовать динамически и переменные и структуры. Динамические
+ переменные и структуры обозначаются также как и динамические процедуры -
+ символом двоеточия перед началом их объявления. И также как и динамическая
+ процедура, динамическая переменная или структура будет вставлена в код,
+ лишь в том случае, если она будет использована в программе.
+
+ Динамические переменные и структуры найдут применение в библиотеках.
+ Использовать их непосредственно в программах нет смысла.
+
+ У динамических переменных, структур также как и у процедур, есть один
+ недостаток - Вы не сможете знать, в каком месте откомпилированного кода они
+ будут расположены, и в каком порядке. Но необходимость это знать бывает
+ очень редко.
+
+ Динамические инициализированные переменные и структуры в файле будут
+ расположены в его самом конце, после динамических процедур. Эту их
+ особенность можно использовать, если Вам будет необходимо, чтобы данные не
+ были разбросаны среди кода, а были сгруппированы в одном месте.
+Return to contents.
+
+
+
+ 6.6 Присваивание одного значения нескольким переменным.
+
+
+ Если Вам необходимо присвоить нескольким переменным одинаковые значения:
+
+ var1=0;
+ var2=0;
+ var3=0;
+
+ то теперь это можно записать более коротко:
+
+ var1=var2=var3=0;
+
+ При использовании такой записи генерируется более компактный и более
+ быстрый код.
+Return to contents.
+
+
+
+ 6.7 Переменные типа float.
+
+ 6.7.1 Формат переменных типа float.
+
+
+ Для представления значений с плавающей точкой в язык C-- введен тип
+ float. Этому типу соответствует действительное число одинарной точности
+ FPU.
+
+ Формат представления данных с плавающей точкой включает три поля:
+ знака, мантиссы и порядка. Знак определяется старшим значащим разрядом.
+ Поле мантиссы содержит значащие биты числа, а поле порядка содержит
+ степень 2 и определяет масштабирующий множитель для мантиссы.
+
+ 31 30.....23 22........0
+ | | | | |
+ | | | -------------- - поле мантиссы
+ | ------------------------ - поле порядка
+ --------------------------- - бит знака
+Return to contents.
+
+
+
+ 6.7.2 Константы с плавающей точкой.
+
+
+ Компилятор отличает вещественное число от целого по наличию в нем
+ точки. Начинаться вещественное число должно либо цифрой от 0 до 9, либо
+ знаком минус. Необязательной частью вещественного числа является
+ показатель степени. Показатель степени отделяется от числа символом e или
+ E. Пробелы недопустимы. Вот примеры допустимого синтаксиса:
+
+ 0.98
+ -15.75
+ 3.14e2
+ 1.234567E-20
+Return to contents.
+
+
+
+ 6.7.3 Диапазон допустимых значений.
+
+
+ Вещественное число типа float может находиться в диапазоне от 3.37E38
+ до -3.37E38. Минимально близкое к нулю значение равняется 1.17E-38 и
+ -1.17E-38. Записывать вещественное число одинарной точности более чем 8
+ цифрами не имеет смысла. Показатель степени может принимать значения от
+ +38 до -38.
+Return to contents.
+
+
+
+ 6.7.4 Математические операции.
+
+
+ Компилятор поддерживает 4 основных действия над переменными типа
+ float: сложение, вычитание, умножение и деление. Поддерживается также
+ инкремент (var++ - увеличение на 1), декремент (var-- - уменьшение на 1),
+ смена знака (-var) и обмен значениями (var1 >< var2). Остальные
+ математические операции будут реализованы либо уже реализованы во внешних
+ библиотеках. При вычислении значения переменной float можно использовать
+ и переменные других типов, они будут автоматически преобразованы в тип
+ float.
+
+ ВНИМАНИЕ! Составные математические операции выполняются в том
+ порядке, в котором они записаны, невзирая на правила арифметики.
+Return to contents.
+
+
+
+ 6.7.5 Преобразования типов.
+
+
+ При математических операциях конечным итогом которых является
+ переменная типа float, все операнды других типов перед вычислением будут
+ преобразованы в тип float. При присваивании переменной типа float значения
+ переменной другого типа оно также будет преобразовано в тип float.
+
+ Если при целочисленных вычислениях одним из операндов будет переменная
+ типа float, то из него будет выделена целая часть, которая и примет
+ участие в вычислениях. При присваивании целочисленной переменной значения
+ переменной типа float, из нее также будет выделена целая часть, которая и
+ будет присвоена целочисленной переменной.
+Return to contents.
+
+
+
+ 6.7.6 Операции сравнения.
+
+
+ Если при операции сравнения левым операндом является переменная или
+ выражение типа float, а правым является целочисленное значение, то
+ целочисленное значение будет преобразовано в вещественный тип. Если же
+ левым операндом является целочисленное выражение или переменная, а правым
+ операндом значение типа float, то из правого операнда будет выделена целая
+ часть, которая и примет участие в сравнении.
+Return to contents.
+
+
+
+ 6.7.7 Сравнение переменных типа float с 32-битным регистром.
+
+
+ В регистрах могут содержаться знаковые, без знаковые и вещественные
+ данные. По умолчанию считается, что в регистре находится без знаковое целое
+ число. При сравнении переменных типа float с 32-битным регистром можно
+ указывать тип данных содержащихся в регистре. Для этой цели можно
+ использовать модификаторы: signed, unsigned, float. Примеры:
+
+ float f=1.0;
+
+ void PROC()
+ {
+ IF( f < signed ECX) //в регистре ECX находится знаковое число
+ IF( unsigned EBX > f) //в регистре EBX находится без знаковое число
+ IF( f == float EAX ) //в EAX находится число формата float
+ }
+
+ ВНИМАНИЕ! При операции сравнения с участием переменой типа float,
+ содержимое регистра AX будет разрушено.
+Return to contents.
+
+
+
+ 6.8 Указатели.
+
+
+ В C-- сейчас указатели реализованы не в полном объеме. Поэтому многие
+ вещи, которые возможны в обычных языках C, здесь будут недоступны.
+
+ Пример применения указателей в C--:
+
+ char *string[4]={"string1", "string2", "string3", 0}; //массив указателей
+ char *str="string4";
+
+ main()
+ int i;
+ char *tstr;
+ {
+ FOR(i=0; string[i]!=0; i++){
+ WRITESTR(string[i]);
+ WRITELN();
+ }
+ FOR(tstr=str;byte *tstr!=0; tstr++){
+ WRITE(byte *tstr);
+ }
+ }
+
+ Указатели можно использовать при передаче параметров процедурам, а в
+ самих процедурах в качестве как локальных, так и параметрических
+ переменных. Указатели можно также использовать в структурах. Можно
+ использовать указатели на указатели. Введена поддержка указателей на
+ процедуры:
+
+ void (*proc)(); //объявление указателя на процедуру
+
+ По умолчанию указатели на процедуру являются указателями на процедуру в
+ стиле pascal, независимо от регистра, в котором написано имя процедуры и
+ режима компиляции. Если Вам необходимо, чтобы был использован другой тип
+ вызова, то его необходимо указать при объявлении указателя на процедуру.
+
+ При инициализации указателей компилятор не контролирует то, чем
+ инициализируется указатель. Т.е. Вы можете указателю на char присвоить
+ указатель на int или указателю на процедуру присвоить адрес переменной.
+ Это может вызвать ошибку в работе программы.
+Return to contents.
+
+
+
+7. Адресация.
+
+ 7.1 Относительная адресация.
+
+
+ Изначально индексный доступ к элементам в массивах любого типа в
+ компиляторе осуществлялся побайтно, независимо от объявленного типа данных.
+ Индексы ограничены форматом поля RM процессора 8086, таким образом,
+ доступны только следующие форматы индексов (где индекс - значение
+ 16-разрядной константы или постоянного выражения):
+
+ variable[index]
+ variable[index+BX+SI]
+ variable[index+BX+DI]
+ variable[index+BP+SI]
+ variable[index+BP+DI]
+ variable[index+SI]
+ variable[index+DI]
+ variable[index+BP]
+ variable[index+BX]
+
+ Начиная с версии 0.210, появилась возможность использовать в качестве
+ индекса переменных типа char byte int word long dword. При этом
+ доступ к элементам массива осуществляется в зависимости от объявленного типа
+ массива.
+
+ Также начиная с версии 0.210 появилась возможность использовать в
+ качестве индексных и базовых регистров при относительной адресации любые
+ 32-битные регистры.
+
+ Если Вы для адресации к элементам массива будете использовать регистры и
+ числовые константы, из которых можно получить поле RM для инструкций 8086
+ процессора или комбинацию полей RM BASE и SIB для 80386 процессора, то
+ компилятор будет использовать эти регистры для генерации инструкции с этими
+ полями. В результате Вы получите относительную побайтную адресацию к
+ элементам массива.
+
+ Если же из этих регистров невозможно получить поля RM, BASE, SIB,
+ или для адресации будет использована переменная, то компилятор сначала
+ вычислит это выражение в регистр (E)SI или другой, подходящий регистр, а
+ затем умножит содержимое этого регистра на разрядность Вашего массива. Таким
+ образом, в этом случае вы будете иметь поэлементную адресацию в массиве.
+ Пример:
+
+ AX = var [ 5 ];
+ AX = var [ BX + 5 ];
+ AX = var [ BX + CX ];
+ AX = var [ i ];
+
+ Компилятор сгенерирует следующий код:
+ test.c-- 7: AX=var[5];
+ 0100 A12501 mov ax,[125h]
+
+ test.c-- 8: AX=var[BX+5];
+ 0103 8B872501 mov ax,[bx+125h]
+
+ test.c-- 9: AX=var[BX+CX];
+ 0107 89DE mov si,bx
+ 0109 01CE add si,cx
+ 010B 01F6 add si,si
+ 010D 8B842001 mov ax,[si+120h]
+
+ test.c-- 10: AX=var[i];
+ 0111 8B362201 mov si,[122h]
+ 0115 01F6 add si,si
+ 0117 8B842001 mov ax,[si+120h]
+
+ Как Вы видите, первые два выражения были преобразованы в одну
+ ассемблерную инструкцию, и получилась побайтная адресация. В двух следующих
+ выражениях получить одну ассемблерную инструкцию не удалось и компилятор
+ применил для этих выражений поэлементную адресацию.
+
+ Такой двойственный подход реализован с целью сохранения совместимости
+ новых возможностей с предыдущими.
+
+ Несмотря на кажущуюся для неискушенного пользователя путаницу, этот
+ механизм легко понять и запомнить по следующему простому правилу: если Вы
+ используете в качестве индекса только цифровое значение или регистр BX, SI,
+ DI, BP или любой 32-битный регистр, то компилятор сгенерирует код с
+ побайтной адресацией. Если же в качестве индекса будет использована
+ переменная, то компилятор сгенерирует код с поэлементной адресацией. Если
+ же Вы хорошо знакомы с ассемблером, то Вам не составит большого труда
+ понять в каких случаях Вы получите побайтную, а в каких поэлементную
+ адресацию.
+
+ Иногда требуется иметь побайтный доступ к элементам массива используя в
+ качестве индекса переменную. Например
+
+ AX=var[i];
+
+ Для этого выражения будет сгенерирована поэлементная адресация, а нам
+ нужна побайтовая. Для этого можно написать так:
+
+ SI=i;
+ AX=var[SI];
+
+ Но можно это записать короче:
+
+ AX=DSWORD[#var+i];
+
+ В обоих этих случаях Вы получите побайтную адресацию к элементам массива
+ var. В первом варианте Вы сможете контролировать какой регистр будет
+ использован в качестве индекса, а во втором варианте компилятор будет сам
+ выбирать регистр для использования в качестве индекса.
+
+ Важно всегда помнить о двойственном подходе компилятора к вычислению
+ адреса в массиве. Еще раз кратко: если Вы в массиве адресуетесь используя
+ числовую константу или регистры BX,DI,SI,BP компилятор использует эти
+ значения без изменения. Во всех других случаях будет коррекция значения в
+ зависимости от типа массива.
+Return to contents.
+
+
+
+ 7.2 Абсолютная адресация.
+
+
+ Абсолютная адресация также возможна. Действуют те же самые ограничения
+ на индексы, что и при относительной адресации.
+
+ Вычисленный индекс будет абсолютен в сегменте, регистр которого указан.
+ Можно указывать любой из регистров DS, CS, SS и ES. На процессорах 80386 и
+ более новых можно указывать также регистры FS и GS.
+
+ Синтаксис - точно такой же, как и в относительной адресации, за
+ исключением того, что указывается не переменная, а сегмент и тип данных.
+ Могут применяться следующие указатели:
+
+ // адресация в сегменте данных
+ DSBYTE [смещение] // адресует байт в сегменте DS
+ DSWORD [смещение] // адресует слово в сегменте DS
+ DSCHAR [смещение] // адресует char в сегменте DS
+ DSINT [смещение] // адресует int в сегменте DS
+ DSDWORD [смещение] // адресует dword в сегменте DS
+ DSLONG [смещение] // адресует long в сегменте DS
+ DSFLOAT [смещение] // адресует float в сегменте DS
+
+ // адресация в сегменте кода
+ CSBYTE [смещение] // адресует байт в сегменте CS
+ CSWORD [смещение] // адресует слово в сегменте CS
+ CSCHAR [смещение] // адресует char в сегменте CS
+ CSINT [смещение] // адресует int в сегменте CS
+ CSDWORD [смещение] // адресует dword в сегменте CS
+ CSLONG [смещение] // адресует long в сегменте CS
+ CSFLOAT [смещение] // адресует float в сегменте CS
+
+ // адресация в сегменте стека
+ SSBYTE [смещение] // адресует байт в сегменте SS
+ SSWORD [смещение] // адресует слово в сегменте SS
+ SSCHAR [смещение] // адресует char в сегменте SS
+ SSINT [смещение] // адресует int в сегменте SS
+ SSDWORD [смещение] // адресует dword в сегменте SS
+ SSLONG [смещение] // адресует long в сегменте SS
+ SSFLOAT [смещение] // адресует float в сегменте SS
+
+ // адресация в дополнительном сегменте данных
+ ESBYTE [смещение] // адресует байт в сегменте ES
+ ESWORD [смещение] // адресует слово в сегменте ES
+ ESCHAR [смещение] // адресует char в сегменте ES
+ ESINT [смещение] // адресует int в сегменте ES
+ ESDWORD [смещение] // адресует dword в сегменте ES
+ ESLONG [смещение] // адресует long в сегменте ES
+ ESFLOAT [смещение] // адресует float в сегменте ES
+
+ // адресация в дополнительном сегменте 2 (80386) +
+ FSBYTE [смещение] // адресует байт в сегменте FS
+ FSWORD [смещение] // адресует слово в сегменте FS
+ FSCHAR [смещение] // адресует char в сегменте FS
+ FSINT [смещение] // адресует int в сегменте FS
+ FSDWORD [смещение] // адресует dword в сегменте FS
+ FSLONG [смещение] // адресует long в сегменте FS
+ FSFLOAT [смещение] // адресует float в сегменте FS
+
+ // адресация в дополнительном сегменте 3 (80386) +
+ GSBYTE [смещение] // адресуют байт в сегменте GS
+ GSWORD [смещение] // адресуют слово в сегменте GS
+ GSCHAR [смещение] // адресуют char в сегменте GS
+ GSINT [смещение] // адресуют int в сегменте GS
+ GSDWORD [смещение] // адресуют dword в сегменте GS
+ GSLONG [смещение] // адресуют long в сегменте GS
+ GSFLOAT [смещение] // адресует float в сегменте GS
+
+ Примеры:
+ Загрузить в AL байт из ячейки с шестнадцатеричным адресом 0000:0417
+ ES = 0x0000;
+ AL = ESBYTE [0x417];
+
+ Переместить слово из ячейки с шестнадцатеричным адресом 2233:4455
+ в ячейку с шестнадцатеричным адресом A000:0002
+ $PUSH DS
+ DS = 0x2233;
+ ES = 0xA000;
+ ESWORD [0x0002] = DSWORD [0x4455];
+ $POP DS
+
+ Сохранить вычисленное значение выражения X + 2, имеющее
+ тип int в ячейке с шестнадцатеричным адресом FFFF:1234
+ ES = 0xFFFF;
+ ESINT [0x1234] = X + 2;
+
+ Сохранить BX в сегменте стека по смещению 42:
+ SSWORD [42] = BX;
+Return to contents.
+
+
+
+8. Работа с блоками данных.
+
+ 8.1 Структуры.
+
+ 8.1.1 Что такое структуры.
+
+
+ Структура позволяет объединить в одном объекте совокупность значений,
+ которые могут иметь различные типы.
+Return to contents.
+
+
+
+ 8.1.2 Синтаксис.
+
+
+ struct [<тег>] { <список-объявлений-элементов> }
+ <описатель>[,<описатель>...];
+ struct <тег> <описатель> [,<описатель>];
+
+ Объявление структуры начинается с ключевого слова struct и имеет две
+ формы записи.
+
+ В первой форме типы и имена элементов структуры специфицируются в
+ списке-объявлений-элементов. Необязательный в данном случае тег - это
+ идентификатор, который именует структурный тип, определенный данным
+ списком объявлений элементов. описатель специфицирует либо переменную
+ структурного типа, либо массив структур данного типа.
+
+ Вторая синтаксическая форма объявления использует тег структуры для
+ ссылки на структурный тип, определенный где-то в другом месте программы.
+
+ Список объявлений элементов представляет собой последовательность из
+ одной или более объявлений переменных. Каждая переменная, объявленная в
+ этом списке, называется элементом структуры.
+
+ Элементы структуры запоминаются в памяти последовательно в том
+ порядке, в котором они объявляются. Выравнивание элементов внутри
+ структуры по умолчанию не производится. Но существует опция, включение
+ которой в командную строку позволяет иметь выравнивание и внутри
+ структуры. Сама структура выравнивается на четный адрес если включено
+ выравнивание.
+
+ Примеры объявлений структур:
+
+ struct test
+ {
+ int a;
+ char b[8];
+ long c;
+ } rr, ff[4];
+
+ В этом примере объявлены структура с именем rr и массив из 4 структур
+ с именем ff. Всему набору переменных присвоено название (тег) test. Этот
+ тег можно использовать для объявления других структур. Например:
+
+ struct test dd;
+
+ Здесь объявлена структура с именем dd, имеющая набор элементов
+ описанных в теге test.
+
+ При объявлении структур с ранее объявленным тегом ключевое слово
+ struct можно не писать. Т.е можно написать вот так:
+
+ test dd;
+Return to contents.
+
+
+
+ 8.1.3 Инициализация структур при объявлении.
+
+
+ После объявления структуры ее элементы могут принимать произвольные
+ значения. Что бы этого не было надо структуры проинициализировать.
+ Инициализировать структуры при их объявлении можно только глобальные. C--
+ поддерживает несколько способов инициализации структур при их объявлении:
+
+ 1. Одним значением:
+
+ struct test dd=2;
+
+ В этом примере всем элементам структуры dd присваивается значение 2.
+
+ 2. Массивом значений:
+
+ struct test dd={1,2,,6};
+
+ В этом примере первому элементу структуры dd присваивается значение 1,
+ второму - 2, четвертому - 6. Пропущенным и не доинициализированным
+ значениям будет присвоено 0 значение.
+
+ 3. Командой FROM:
+
+ struct test dd=FROM "file.dat";
+
+ В этом примере на место где расположена структура dd при компиляции будет
+ загружено содержимое файла . Если размер файла больше чем размер
+ структуры, то лишние байты будут загружены в код программы, но они не
+ будут востребованы. Если размер файла меньше чем размер структуры, то
+ недостающие байты структуры будут заполнены нулями.
+
+ 4. Командой EXTRACT:
+
+ struct test dd=EXTRACT "file.dat", 24, 10;
+
+ В этом примере на место где расположена структура dd при компиляции будет
+ загружен фрагмент из файла file.dat длиной 10 байт со смещения 24.
+ Недостающие байты будут заполнены нулями.
+Return to contents.
+
+
+
+ 8.1.4 Инициализация структуры при выполнении программы.
+
+
+ При выполнении программы, кроме присвоения каждому элементу структуры
+ значения, можно проинициализировать всю структуру присвоением ей числа или
+ переменной. Примеры:
+
+ void proc()
+ struct test aa[5],rr;
+ int i;
+ {
+ aa[0]=0x12345678;
+ aa[i]=int 0x12345678;
+ aa=long 0x12345678;
+ rr=i;
+
+ В первом примере память, занимаемая первой структурой массива из 5
+ структур, будет заполнена байтом 0x78 (по умолчанию).
+
+ Во втором примере память, занимаемая (i+1)-вой структурой массива из 5
+ структур, будет заполнена словом 0x5678.
+
+ В третьем примере память, занимаемая всем массивом из 5 структур, будет
+ заполнена длинным словом 0x12345678.
+
+ В четвертом примере память, занимаемая структурой rr, будет заполнена
+ содержимым переменной i.
+
+ Можно также копировать содержимое одной структуры в другую. Например:
+
+ rr=aa[2];
+
+ Будет скопировано содержимое третьей структуры массива структур aa в
+ структуру rr.
+Return to contents.
+
+
+
+ 8.1.5 Операции с элементами структур.
+
+
+ С элементами структур можно выполнять все те операции, которые
+ доступны для переменных соответствующего типа. Например: Объявлена
+ структура:
+
+ struct test
+ {
+ int a;
+ char b[8];
+ long c;
+ } rr[3];
+ Пример допустимого синтаксиса:
+ rr.a = rr.b[i] * rr[1].c + i ;
+
+ Примечание:
+ При операциях с элементами массива структур и с индексированными
+ элементами, в которых в качестве индекса или номера структуры используется
+ переменная, компилятор может использовать регистры SI и DI, а в некоторых
+ ситуациях (например: rr[i].b[j] >< rr[i+1].b[j+2] ) будет задействован и
+ регистр DX.
+
+ Для отдельных элементов структуры, можно получать их адрес, размер
+ и смещение в теге структуры. Вот пример:
+
+ struct AA //объявление тега структуры
+ {
+ word a[3]; // первый элемент структуры
+ char b; // второй элемент структуры
+ long c; // третий элемент структуры
+ };
+
+ struct BB //тег второй структуры
+ {
+ word aa; // первый элемент
+ AA bb; // второй элемент - вложенная структура
+ }ss; // объявляем структуру с тегом BB
+
+ void proc()
+ {
+ AX=#ss.bb.b; // получить адрес элемента b структуры bb в структуре ss
+ AX=#BB.bb.b; // получить смещение этого же элемента в теге BB
+ AX=sizeof(ss.bb); // получить размер элемента bb в структуре ss
+ AX=sizeof(BB.bb); // получить размер элемента bb в теге BB
+ }
+Return to contents.
+
+
+
+ 8.1.6 Вложенные структуры.
+
+
+ При объявлении тегов структур можно использовать теги других,
+ объявленных ранее структур. Пример вложенных структур:
+
+ struct RGB
+ {
+ byte Red;
+ byte Green;
+ byte Blue;
+ byte Reserved;
+ };
+
+ struct BMPINFO
+ {
+ struct BMPHEADER header; //описание этой структуры пропущено
+ struct RGB color[256];
+ }info;
+
+ Предположим Вам нужно получить содержимое переменной Red десятого
+ элемента color. Это можно будет записать так:
+
+ AL=info.color[10].Red;
+
+ Но существует одно ограничение использования вложенных структур в C--.
+ Это невозможность использования переменной в качестве индекса более одного
+ раза при обращении к многоэкземплярным структурам. Поясним это на примере:
+
+ struct ABC
+ {
+ int a;
+ int b;
+ int c;
+ };
+
+ struct
+ {
+ struct ABC first[4]; //4 экземпляра структуры ABC
+ int d;
+ }second[4];
+
+ int i,j;
+
+ void proc()
+ {
+ AX=second[i].first[j].a; //такая запись вызовет сообщение об ошибка, так
+ //как переменная использовалась в двух местах
+ AX=second[2].first[j].a; //а этот синтаксис допустим.
+ AX=second[i].first[3].a;
+ }
+Return to contents.
+
+
+
+ 8.1.7 Отображение тега структуры на блок памяти.
+
+
+ Отображение тега структуры на блок памяти является альтернативой
+ указателям на структуры.
+
+ Альтернативный способ использования указателей на структуры позволит
+ Вам самим выбрать регистр, в котором будет хранится адрес структуры и
+ самим следить за его сохранностью и по мере необходимости восстанавливать
+ его содержимое.
+
+ Объяснить, как использовать отображение тега структуры на память,
+ наверное, будет проще на примере:
+
+ struct AA //объявление тега структуры
+ {
+ word a[3]; // первый элемент структуры
+ char b; // второй элемент структуры
+ long c; // третий элемент структуры
+ };
+
+ byte buf[256]; //блок памяти, на который будет отображен тег структуры
+
+ void proc1()
+ {
+ ...
+ proc2 ( #buf ); // вызов процедуры с передачей ей в качестве параметра
+ // адреса блока памяти
+ ...
+ }
+
+ long proc2 (unsigned int pointer_to_mem)
+ {
+ int i;
+ BX=pointer_to_mem; // в BX загрузим адрес блока памяти
+ FOR(i=0; i<3; i++){ // в массив элемента a записать -1
+ BX.AA.a[i]=-1;
+ }
+ BX.AA.b=0;
+ ES:BX.AA.c=EAX;
+ return BX.AA.c; // вернуть содержимое элемента c
+ }
+
+ В 16-битном режиме для хранения адреса структуры можно использовать
+ регистры: BX,DI,SI,BP. Но лучше для этого использовать регистр BX.
+ Регистры DI и SI может использовать компилятор при вычислении адреса
+ многоэлементных объектов. Регистр BP компилятор использует для работы с
+ локальными и параметрическими переменными. В 32-битном режиме можно
+ использовать любой кроме ESP и EBP регистр, а регистры EDI и ESI надо
+ использовать осторожно.
+Return to contents.
+
+
+
+ 8.1.8 Битовые поля структур.
+
+
+ Битовые поля структур используются для экономии памяти, поскольку
+ позволяют плотно упаковать значения, и для организации удобного доступа к
+ регистрам внешних устройств, в которых различные биты могут иметь
+ самостоятельное функциональное назначение.
+
+ Объявление битового поля имеет следующий синтаксис:
+
+ <тип> [<идентификатор>]:<константа>;
+
+ или на примере:
+
+ int var:5; //объявление битового поля размером 5 бит с именем var
+
+ Битовое поле состоит из некоторого числа битов, которое задается
+ числовым выражением константа. Его значение должно быть целым
+ положительным числом и его значение не должно превышать числа разрядов,
+ соответствующие типу определяемого битового поля. В C-- битовые поля
+ могут содержать только без знаковые значения. Нельзя использовать массивы
+ битовых полей, указатели на битовые поля.
+
+ идентификатор именует битовое поле. Его наличие необязательно.
+ Неименованное битовое поле означает пропуск соответствующего числа битов
+ перед размещением следующего элемента структуры. Неименованное битовое
+ поле, для которого указан нулевой размер, имеет специальное назначение:
+ оно гарантирует, что память для следующего битового поля будет начинаться
+ на границе того типа, который задан для неименованного битового поля.
+ Т.е. будет произведено выравнивание битового поля на 8/16/32 бита.
+
+ В C-- все битовые поля упаковываются одно за другим независимо от
+ границ типа идентификаторов. Если последующее поле не является битовым
+ полем, то оставшиеся до границы байта биты не будут использованы.
+ Максимальный размер битового поля равен 32 бита для типа dword/long, 16
+ бит для типа word/int и 8 бит для типа byte/char. Битовые поля можно
+ объединять, т.е. использовать их в операторе union. sizeof
+ примененный к битовому полю вернет размер этого поля в битах. При
+ использовании битового поля, его содержимое будет расширятся в регистр
+ как без знаковое целое число.
+Return to contents.
+
+
+
+ 8.2 Объединения.
+
+
+ Объединения позволяют в разные моменты времени хранить в одном объекте
+ значения различного типа.
+
+ Память, которая выделяется под объединение, определяется размером
+ наиболее длинного из элементов объединения. Все элементы объединения
+ размещаются в одной и той же области памяти с одного и того же адреса.
+ Значение текущего элемента объединения теряется, когда другому элементу
+ объединения присваивается значение.
+
+ В C-- реализованы так называемые анонимные объединения. Т.е.
+ объединениям не присваивается имя, а обращение к элементам объединения
+ происходит как к обычной переменной. Пример:
+
+ union
+ {
+ dword regEAX;
+ word regAX;
+ byte regAL;
+ }; // объявили, что 3 переменные расположены по одному и тому же
+ // физическому адресу
+
+ void test()
+ {
+ regEAX = 0x2C;
+ BL = regAL; //в регистре BL окажется значение 0x2C
+ }
+
+ Объединять можно переменные различных типов, массивы, строковые
+ переменные и структуры. Объединения могут быть глобальными и локальными, а
+ также располагаться внутри структур (пока в объединениях внутри структур
+ нельзя использовать структуры). Глобальные объединения могут быть
+ инициализированными и неинициализированными. Чтобы получить
+ инициализированное объединение достаточно проинициализировать лишь первый
+ элемент объединения. Если же первый элемент объединения не инициализирован,
+ а следующие элементы инициализированы, то это вызовет сообщение компилятора
+ об ошибке.
+Return to contents.
+
+
+
+ 8.3 Команды 'FROM' и 'EXTRACT'.
+
+
+ В C-- есть очень оригинальные команды, которых нет в других языках. Это
+ FROM и EXTRACT.
+
+ Команда FROM имеет синтаксис:
+
+ <тип_переменной> <имя_переменной> = FROM <имя_файла>;
+
+ Встретив эту команду при компиляции, компилятор загрузит в выходной
+ файл содержимое файла имя_файла, а имя_переменной будет идентификатором
+ начала загруженного кода. Вот пример использования этой команды из файла
+ tinydraw.c--:
+
+ byte palette[PALSIZE] = FROM "TINYDRAW.PAL"; // buffer for palette
+
+ Команда EXTRACT имеет синтаксис:
+
+ <тип_переменной> <имя_переменной> = EXTRACT <имя_файла>, <начало>, <длина>;
+
+ Встретив эту команду при компиляции, компилятор загрузит в выходной
+ файл из файла имя_файла число байт равное длина со смещения начало, а
+ имя_переменной будет идентификатором начала загруженного кода. Вот пример
+ использования этой команды:
+
+ byte LIT128 = EXTRACT "8X16.FNT", 16*128, 16;
+ byte LIT130 = EXTRACT "8X16.FNT", 16*130, 16;
+Return to contents.
+
+
+
+9. Операторы.
+
+ 9.1 Условные инструкции.
+
+
+ Условные инструкции, при помощи которых осуществляется ветвление, такие
+ же как в C.
+
+ C-- имеет две инструкции ветвления. if и IF.
+
+ if делает близкий условный переход, а IF делает короткий
+ (8-разрядный) условный переход. IF выполняется быстрее и может экономить
+ до 3 байт в размере кода, но может осуществлять переходы только в пределах
+ 127 байтов кода.
+
+ Условные инструкции, как и в C, могут сопровождаться, как одиночной
+ командой, так и блоком из нескольких команд, заключенных в фигурные скобки
+ { и }. Условные инструкции имеют те же ограничения, что и условные
+ выражения.
+
+ Если за инструкцией IF следует больше чем 127 байтов кода, транслятор
+ выдаст следующее сообщение об ошибке:
+
+ IF jump distance too far, use if.
+
+ Это можно просто исправить, заменив в этом месте инструкцию IF на if.
+
+ Команды else и ELSE используются точно так же, как в языке C.
+ Отличие их в том, что ELSE имеет ограничение адреса перехода 127 байт,
+ такое же как IF. else генерирует код на 1 байт длиннее, чем ELSE.
+
+ Команды IF и else, а также if и ELSE могут свободно смешиваться
+ как в следующем примере:
+
+ if( x == 2 )
+ WRITESTR("Two");
+ ELSE{ WRITESTR("not two.");
+ printmorestuff();
+ }
+
+ Если за инструкцией ELSE следует больше чем 127 байтов кода,
+ транслятор выдаст следующее сообщение об ошибке:
+
+ ELSE jump distance too far, use else.
+
+ Это можно просто исправить, заменив в этом месте инструкцию ELSE на
+ else.
+Return to contents.
+
+
+
+ 9.2 Циклы do{} while.
+
+
+ В таком цикле блок кода, составляющий тело цикла, будет повторяться,
+ пока условное выражение имеет значение истинно.
+
+ Истинность условного выражения проверяется после выполнения тела цикла,
+ поэтому блок кода будет выполнен, по крайней мере, один раз.
+
+ Пример do {} while цикла, в котором тело будет исполнено пять раз:
+
+ count = 0;
+ do {
+ count++;
+ WRITEWORD(count);
+ WRITELN();
+ } while (count < 5);
+
+ Условное выражение в do {} while инструкции должно соответствовать тем же
+ правилам, что и в инструкциях IF и if.
+Return to contents.
+
+
+
+ 9.3 Циклы loop, LOOPNZ, loopnz.
+
+
+ Циклы loop повторяют блок кода, пока определенная переменная или
+ регистр, выполняющие роль счетчика цикла, содержат значение, отличное от
+ нуля. В конце выполнения блока кода, составляющего тело цикла, указанная
+ переменная или регистр - уменьшается на 1, а затем проверяется на равенство
+ нулю. Если переменная (или регистр) не равна нулю, тело цикла будет
+ выполнено снова, и процесс повторится.
+
+ Пример использования цикла loop в котором в качестве счетчика цикла
+ использована переменная:
+
+ count = 5;
+ loop( count )
+ {WRITEWORD(count);
+ WRITELN();
+ }
+
+ Наибольший эффект дает использование регистра CX для циклов с небольшим
+ телом, поскольку в этом случае компилятором генерируется цикл с применением
+ машинной команды LOOP.
+
+ Если перед стартом счетчик циклов содержит нулевое значение, команды
+ тела цикла будут выполнены максимальное число раз для диапазона переменной
+ (256 раз для 8-битного счетчика (переменной типа byte или char), 65536 для
+ 16-битного счетчика (переменной типа word или int), и 4294967296 для
+ 32-битного счетчика (переменной типа dword или long).
+
+ В следующем примере цикл будет выполнен 256 раз:
+
+ BH = 0;
+ loop (BH)
+ {
+ }
+
+ Если в команде не указано никакого счетчика цикла, цикл будет
+ продолжаться бесконечно.
+
+ Следующий пример будет непрерывно выводить символ звездочки (*) на
+ экран:
+
+ loop()
+ WRITE('*');
+
+ Программист, если хочет, может использовать или изменять значение
+ переменной счетчика цикла внутри цикла.
+
+ Например, следующий цикл выполнится только 3 раза:
+
+ CX = 1000;
+ loop( CX )
+ {
+ IF( CX > 3 )
+ CX = 3;
+ }
+
+ Цикл можно также прервать оператором разрыва BREAK или break. Вот
+ тот же пример с использованием BREAK:
+
+ CX = 1000;
+ loop( CX )
+ {
+ IF( CX > 3 )
+ BREAK;
+ }
+
+ Циклы LOOPNZ/loopnz отличаются от цикла loop, тем, что перед входом
+ в цикл проверяется равенство нулю аргумента цикла. Если аргумент равен
+ нулю, то тело цикла ни разу не выполнится (в цикле loop в этом случае
+ тело цикла выполнится максимальное число раз). Цикл LOOPNZ получается
+ максимально эффективным при оптимизации на размер кода, если в качестве
+ параметра-счетчика используется регистр CX/ECX. При этом компилятор
+ использует ассемблерные инструкции JCXZ/JECXZ и LOOP.
+Return to contents.
+
+
+
+ 9.4 Цикл while, WHILE.
+
+
+ Синтаксис:
+ while(<выражение>)
+ <оператор>
+
+ Цикл выполняется до тех пор, пока значение выражения не станет
+ ложным. Вначале вычисляется выражение. Если выражение изначально ложно,
+ то тело оператора while вообще не выполняется и управление сразу
+ передается на следующий оператор программы.
+
+ Цикл WHILE аналогичен циклу while, но при этом генерируется код на
+ 3 байта короче. Размер сгенерированного кода в цикле WHILE должен быть
+ меньше 127 байт.
+
+ Примеры:
+ while ( i < 20 ){
+ WRITEWORD(i);
+ i++;
+ }
+
+ WHILE (i < 20 ) @WRITEWORD(i); //цикл либо будет бесконечным либо не
+ //выполнится ни разу
+Return to contents.
+
+
+
+ 9.5 Цикл for, FOR.
+
+
+ Синтаксис:
+ for ([<начальное выражение>]; [<условие>]; [<приращение>])
+ <оператор>
+
+ Цикл for выполняется до тех пор, пока значение условия не станет
+ ложным. Если условие изначально ложно, то тело оператора for вообще не
+ выполняется и управление сразу передается на следующий оператор программы.
+ Начальное выражение и приращение обычно используются для инициализации
+ и модификации параметров цикла.
+
+ Первым шагом при выполнении for является вычисление начального
+ выражения, если оно имеется. Затем вычисляется условие и производится
+ его оценка следующим образом:
+
+ 1) Если условие истинно, то выполняется тело оператора. Затем
+ вычисляется приращение (если оно есть), и процесс повторяется.
+
+ 2) Если условие опущено, то его значение принимается за истину. В
+ этом случае цикл for представляет бесконечный цикл, который может
+ завершиться только при выполнении в его теле операторов break, goto,
+ return.
+
+ 3) Если условие ложно, то выполнение цикла for заканчивается и
+ управление передается следующему оператору.
+
+ Цикл FOR аналогичен циклу for, но при этом генерируется код на 3
+ байта короче. Размер сгенерированного кода в цикле FOR должен быть меньше
+ 127 байт.
+
+ Примеры:
+ for(i=0;i<5;i++){
+ WRITESTR("СТРОКА ");
+ WRITEWORD(i);
+ WRITELN();
+ }
+
+ Число начальных выражений и число приращений не ограничено. Каждый
+ оператор в начальных выражениях и приращениях должен разделяться
+ запятой. Пример:
+
+ for ( a=1, b=2 ; a<5 ; a++, b+=a ) {...
+
+ Также есть возможность логического объединения условий. Объединять
+ можно до 32 условий. Каждое объединяемое условие должно быть заключено в
+ скобки. Пример:
+
+ for ( a=0 ; (a>=0) && (a<10) ; a++ ){...
+Return to contents.
+
+
+
+ 9.6 Оператор переключатель switch.
+
+
+ Синтаксис:
+ switch(<выражение>){
+ case <константа>:
+ <оператор>
+ ...
+ case <константа>:
+ <оператор>
+ ...
+ ...
+ default:
+ <оператор>
+ }
+ Оператор переключатель switch предназначен для выбора одного из
+ нескольких альтернативных путей выполнения программы. Выполнение начинается
+ с вычисления значения выражения. После этого управление передается одному
+ из операторов тела переключателя. В теле переключателя содержатся
+ конструкции: case константа:, которые синтаксически представляют собой
+ метки операторов. Оператор, получающий управление, - это тот оператор,
+ значение константы которого совпадают со значением выражения
+ переключателя. Значение константы должно быть уникальным.
+
+ Выполнение тела оператора-переключателя switch начинается с выбранного
+ таким образом оператора, и продолжается до конца тела или до тех пор, пока
+ какой-либо оператор не передаст управление за пределы тела.
+
+ Оператор, следующий за ключевым словом default, выполняется, если ни
+ одна из констант не равна значению выражения. Если default опущено, то
+ ни один оператор в теле переключателя не выполняется, и управление
+ передается на оператор, следующий за switch.
+
+ Для выхода из тела переключателя обычно используется оператор разрыва
+ break (BREAK).
+
+ Пример:
+ switch (i){
+ case 'A':
+ WRITE(i);
+ i++;
+ BREAK;
+ case 32:
+ WRITE('_');
+ i++;
+ BREAK;
+ default:
+ WRITE('i');
+ }
+
+ Оператор switch сейчас в компиляторе может реализовываться трем
+ способами: двухтабличным, табличным и методом последовательных проверок.
+
+ Табличный метод является самым быстрым, а при большом числе операторов
+ case и при незначительной разнице между максимальным и минимальным
+ значениями case он еще может быть и более компактным. Но у него есть и
+ недостатки: в 16-битном режиме компилятор всегда использует регистр BX, а в
+ 32-битном режиме, если операндом switch является регистр, то его значение
+ будет разрушено.
+
+ В методе последовательных проверок блок сравнений находится в начале
+ тела оператора switch, это позволяет избавиться от 1-2 лишних jmp. Но
+ компилятор не может определить, какой тип перехода использовать при
+ проверке значений case. Это будет Вашей заботой. Если размер кода от
+ начала тела оператора switch до места расположения оператора case
+ меньше 128 байт, можно использовать короткий переход. В этом случае Вы
+ можете указать оператор CASE, что приведет к генерации более компактного
+ кода. Компилятор в предупреждениях будет Вам подсказывать о возможности
+ использования операторов CASE. Использование оператора CASE в случаях,
+ когда размер блока кода более 128 байт приведет к выдаче компилятором
+ сообщения об ошибке.
+
+ При двухтабличном методе создаются две таблицы - таблица адресов входа в
+ тело оператора switch/SWITCH и таблица значений case. Генерируется
+ процедура сравнения входного значения со значениями во второй таблице. Если
+ есть совпадение, то делается переход по адресу из второй таблицы. Этот
+ метод является самым медленным, но при большом числе значений case (более
+ 15) он становится самым компактным.
+
+ При оптимизации кода на размер, компилятор предварительно вычисляет
+ размер кода, который может быть получен всеми методами и реализует самый
+ компактный. При оптимизации на скорость преимущество отдается табличному
+ методу, если размер таблицы получается не слишком большим.
+
+ Для оператора switch введена также и короткая его форма - SWITCH.
+ Ее можно применять в случае, если размер блока кода между началом тела
+ оператора и оператором default (если он отсутствует, то концом тела
+ оператора switch) меньше 128 байт. О возможности использования короткой
+ формы компилятор будет сообщать в предупреждениях.
+
+ Для оператора case/CASE, который может использоваться только в теле
+ блока оператора switch/SWITCH, можно указывать диапазон значений. Сначала
+ надо указывать меньшее значение, затем после многоточия большее. Пример:
+
+ switch(AX){
+ case 1...5:
+ WRITESTR("Range AX from 1 to 5");
+ BREAK;
+ };
+
+ Раньше Вам бы пришлось писать более громоздкую конструкцию:
+
+ switch(AX){
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ WRITESTR("Range AX from 1 to 5");
+ BREAK;
+ };
+
+ Кроме того, что новый формат записи более компактен и более читабелен,
+ но еще при этом компилятор создает более компактный и быстрый код.
+Return to contents.
+
+
+
+ 9.7 Оператор перехода goto, GOTO.
+
+
+ Синтаксис:
+ goto <метка>;
+ .
+ .
+ .
+ <метка>:
+
+ Оператор перехода goto передает управление на оператор помеченный
+ меткой. Аналогом в ассемблере оператору goto является команда jmp near.
+ Аналогом в ассемблере оператору GOTO является команда jmp short.
+Return to contents.
+
+
+
+ 9.8 Оператор разрыва break, BREAK.
+
+
+ Оператор разрыва break прерывает выполнение операторов do-while,
+ for, switch, while, loop, loopnz, LOOPNZ. Он может содержаться
+ только в теле этих операторов. Управление передается оператору, следующему
+ за прерванным циклом.
+
+ Оператор BREAK аналогичен break, но при этом генерируется код на 1
+ байт короче. Размер сгенерированного кода от места где применяется BREAK
+ до конца цикла должен быть меньше 127 байт.
+
+ Примеры:
+ FOR (i=0; ; i++){
+ FOR(j=0; j < WIDTH; j++){
+ IF(i==5)BREAK;
+ }
+ IF(i==10)BREAK;
+ }
+Return to contents.
+
+
+
+ 9.9 Оператор продолжения continue, CONTINUE.
+
+
+ Оператор продолжения continue передает управление на следующую
+ итерацию в циклах do-while, for, while, loop, loopnz. В циклах
+ do-while, while, loop следующая итерация начинается с вычисления
+ условного выражения. Для цикла for следующая итерация начинается с
+ вычисления выражения приращения, а затем происходит вычисление условного
+ выражения.
+
+ Оператор CONTINUE аналогичен continue, но при этом генерируется код на
+ 1 байт короче. Размер сгенерированного кода от места где применяется
+ CONTINUE до начала итерации должен быть меньше 127 байт.
+Return to contents.
+
+
+
+ 9.10 Логическое объединение условий.
+
+
+ Существует возможность логического объединения сравнений в условиях
+ IF и if, циклах do{}while, while{}, WHILE{}, for{} и FOR{}.
+ Каждое сравнение при их логическом объединении должно быть заключено в
+ скобки. Объединять можно не более 32 сравнений.
+
+ В отличие от C в C-- анализ логических объединений происходит слева
+ направо и все лишние скобки будут восприняты компилятором как ошибочные.
+ Это несколько снижает гибкость и возможности применения этих объединений,
+ но такова традиция и философия, заложенная в C--.
+
+ Пример:
+
+ if ( (a>3) && (b>4) || (c<8) ){
+
+ Т.е. если произвести расшифровку этого условия, то получится следующее:
+ условие выполнится если a>3 и b>4 или a>3 и c<8.
+Return to contents.
+
+
+
+ 9.11 Переход через циклы.
+
+
+ Для операторов BREAK, break, CONTINUE, continue введена
+ поддержка числового параметра, определяющего, сколько циклов надо
+ пропустить, прежде чем будет выполнен этот оператор. Например, мы имеем три
+ вложенных цикла:
+
+ do{
+ loop(CX){
+ for(BX=0;BX<10;BX++){
+ break; //стандартный оператор
+ break 0; //break с параметром - пропустить 0 циклов
+ break 1; //break с параметром - пропустить 1 цикл
+ break 2; //break с параметром - пропустить 2 цикла
+ }
+ LABL0:
+ }
+ LABL1:
+ }while (DX!=0);
+ LABL2:
+
+ В третьем цикле находится группа различных вариантов оператора break.
+ Первым стоит стандартный оператор break, при выполнении которого
+ управление будет передаваться за пределы третьего цикла - на метку LABL0.
+ Вторым идет оператор break 0, при выполнении которого будет пропущено 0
+ циклов и управление будет передано опять же на метку LABL0. Таким
+ образом, запись break и break 0 являются синонимами. Третьим идет
+ оператор break 1, при выполнении которого будет пропущен один цикл и
+ управление будет передано за пределы второго цикла на метку LABL1. Ну и
+ наконец, последним идет оператор break 2, при выполнении которого
+ компилятор пропустит два цикла и передаст управление за пределы третьего,
+ на метку LABL2. Метки в этом примере проставлены для удобства объяснения.
+ Ну и я надеюсь, Вам понятно, что значение параметра не может превышать
+ числа циклов находящихся перед текущим. Так для одиночного цикла этот
+ параметр может принимать максимальное и единственное значение - 0.
+Return to contents.
+
+
+
+ 9.12 Инвертирование флага проверки условий.
+
+
+ Инвертирование флага проверки условий в операциях сравнения if/IF
+ for/FOR while/WHILE происходит с помощью символа ! - not.
+
+ Выражени
+
+ IF ( NOTCARRYFLAG )... и IF ( ! CARRYFLAG )...
+ IF ( proc() == 0 )... и IF ( ! proc() ) ...
+
+ являются синонимами.
+Return to contents.
+
+
+
+ 9.13 Вычисление выражения, а затем проверка условия.
+
+
+ В операциях сравнения в левом операнде теперь допустимо использовать
+ вычисления выражения с присваиванием и операции инкремента, декремента.
+ Например:
+
+ IF (i=a+2 != 0 )...
+ IF ( i++ )...
+ IF ( a-- )...
+ IF ( i+=4 == 0 )...
+
+ Во всех этих примерах сначала произойдет вычисление выражения в левой
+ части операции сравнения, а потом будет произведено сравнение результата с
+ правой частью выражения сравнения.
+Return to contents.
+
+
+
+ 9.14 Проверка битов при операции сравнения.
+
+
+ Если в левой части выражения сравнения написано: BX & 5, то при
+ вычислении выражения содержимое регистра BX будет изменено инструкцией
+ and. Но иногда возникает необходимость в проверке битов без изменения
+ содержимого регистра BX. Для этих целей надо использовать инструкцию
+ test. Как же указать компилятору, в каких ситуациях использовать
+ инструкцию and, а в каких test? В стандартных языках C для этого
+ используется механизм приоритетов - если выражение заключено в скобки, то
+ производится его вычисление, если нет, то производится проверка. Но C-- не
+ поддерживает приоритетов. Для разрешения этой проблемы в C-- решено
+ использовать непосредственно саму инструкцию test. Вот допустимые
+ варианты синтаксиса:
+
+ IF ( $test AX,5 )
+ IF ( ! $test AX,5)
+ IF ( asm test AX,5)
+ IF ( ! asm { test AX,5 } )
+Return to contents.
+
+
+
+ 9.15 Оператор перестановки.
+
+
+ В C-- есть оператор, который не встречается в других языках, это
+ оператор перестановки. Оператор перестановки меняет местами содержимое двух
+ переменных. Символьное обозначение этого оператора ><. Переменные с обеих
+ сторон оператора перестановки должны иметь одинаковый размер, 8 бит и 8
+ бит, 16 бит и 16 бит, или 32 бита и 32 бита.
+
+ Вот некоторые примеры:
+
+ AX >< BX; // сохраняет значение BX в AX и значение AX в BX
+ CH >< BL; // меняет местами содержимое регистров CH и BL
+ dog >< cat; /* меняет местами значения переменной dog и переменной cat*/
+ counter >< CX; // меняет местами значения переменной counter
+ // и содержимое регистра CX
+
+ Если перестановка осуществляется между двумя 8-разрядными переменными в
+ памяти, будет разрушено содержимое регистра AL. Если перестановка - между
+ двумя 16-разрядными переменными в памяти, будет разрушено содержимое
+ регистра AX. Если перестановка - между двумя 32-разрядными переменными в
+ памяти, будет разрушено содержимое EAX. В любом другом случае, например,
+ между переменной в памяти и регистром, значения всех регистров будут
+ сохранены.
+Return to contents.
+
+
+
+ 9.16 Оператор отрицания.
+
+
+ C-- поддерживает быстрый синтаксис смены знака переменной - оператор
+ отрицания. Поставив - (знак минус) перед идентификатором переменной памяти
+ или регистра и ; (точку с запятой) после идентификатора, вы смените знак
+ переменной памяти или регистра.
+
+ Вот некоторые примеры:
+
+ -AX; // результат тот же, что и при 'AX = -AX;' ,но быстрее.
+ -tree; // то же самое, что 'tree = -tree;' ,но быстрее.
+ -BH; // меняет знак BH.
+Return to contents.
+
+
+
+ 9.17 Оператор инверсии.
+
+
+ C-- поддерживает быстрый синтаксис выполнения логической инверсии
+ значения переменной - оператор инверсии. Поставив ! (восклицательный знак)
+ перед идентификатором переменной памяти или регистром и ; (точку с
+ запятой) после идентификатора, вы выполните логическую (выполнится
+ ассемблерная команда NOT) инверсию текущего значения переменной. Вот
+ некоторые примеры:
+
+ !AX; // то же самое, что ' AX ^ = 0xFFFF; ' но быстрее.
+ !node; // заменяет значение 'node' его логической инверсией.
+ !CL; // то же самое, что ' CL ^ = 0xFF ' но быстрее.
+Return to contents.
+
+
+
+ 9.18 Специальные условные выражения.
+
+
+ C-- поддерживает восемь специальных условных выражений:
+
+ CARRYFLAG
+ NOTCARRYFLAG
+ OVERFLOW
+ NOTOVERFLOW
+ ZEROFLAG
+ NOTZEROFLAG
+ MINUSFLAG
+ PLUSFLAG
+
+ Они могут использоваться вместо любых нормальных условных выражений.
+ Если Вы желаете, например, выполнить блок кода только если установлен флаг
+ переноса, Вам следует использовать следующую последовательность команд:
+
+ IF( CARRYFLAG )
+ {
+ // здесь вы чего-то делаете
+ }
+
+ Если Вы желаете непрерывно выполнять блок кода до тех пор, пока не
+ установится флаг переполнения, Вам следует использовать нечто подобное
+ следующему куску кода:
+
+ do {
+ // здесь вы опять чего-то делаете
+ } while( NOTOVERFLOW );
+Return to contents.
+
+
+
+ 9.19 Символ $ - вставляет текущий адрес программы.
+
+
+ Символ $, кроме того, что является признаком последующей ассемблерной
+ инструкции, в языке C--, как и в языке Assembler может указывать текущий
+ адрес (смещение) компилируемой программы. Но в C-- он имел ограниченные
+ возможности. Он мог быть использован лишь как аргумент в операторах
+ GOTO/goto и ассемблерных инструкциях DW/DD/JMP.
+
+ Этот символ может находиться в любом месте вычисляемого числового
+ выражения и может быть применен в любом месте совместно с другими числовыми
+ выражениями.
+
+ Примеры применения:
+
+ DW #main-$ //записать расстояние от процедуры main до текущего места
+ GOTO $+2; //перейти по адресу на 2 больше, чем текущий адрес
+Return to contents.
+
+
+
+ 9.20 Ключевое слово static и оператор ::.
+
+
+ Если перед объявлением глобальной переменной, структуры или процедуры
+ указать слово static, то эти переменная, структура или процедура будут
+ доступны только в том файле, в котором они были объявлены. Т.е. если Вы
+ включите этот файл в другой директивой include, то переменные объявленные
+ во включаемом файле со словом static не будут доступны в основном файле,
+ и Вы можете в основном файле объявить другие переменные с такими же
+ именами.
+
+ Если Вы примените слово static при объявлении локальной переменной в
+ процедуре, то память для этой переменной будет выделена не в стеке, а в
+ области данных процедуры. Но эта переменная будет доступна только внутри
+ процедуры, в которой она была объявлена. Применение static к локальным
+ переменным дает возможность сохранять значение переменной для следующего
+ входа в процедуру.
+
+ Слово static можно применять к любому глобальному объекту
+ (переменной, структуре, процедуре). Для локального использования это слово
+ можно применять только к переменным.
+
+ Если в Вашей программе есть глобальная и локальная переменная с
+ одинаковыми именами, то в процедуре, в которой объявлена эта локальная
+ переменная, Вы не имели доступа к одноименной глобальной переменной.
+ Применив перед именем переменной оператор ::, Вы получите доступ к
+ глобальной переменной. Пример:
+
+ int var; //объявляем глобальную переменную
+
+ void proc()
+ int var; //объявляем локальную переменную с именем уже существующей
+ //глобальной переменной
+ {
+ (E)AX=var; //имеем доступ только к локальной переменной
+ (E)AX=::var; //а так можно получить доступ к глобальной переменной
+ }
+Return to contents.
+
+
+
+ 9.21 Оператор sizeof.
+
+
+ Операция sizeof определяет размер памяти, который соответствует объекту
+ или типу. Операция sizeof имеет следующий вид:
+
+ sizeof (<имя типа>)
+
+ Результатом операции sizeof является размер памяти в байтах,
+ соответствующий заданному объекту или типу.
+
+ В C-- оператор sizeof можно применять к переменным, регистрам, типам
+ переменных, структурам, процедурам, текстовым строкам и файлам.
+
+ Если операция sizeof применяется к типу структуры, то результатом
+ является размер тега данной структуры.
+
+ Если операция sizeof применяется к текстовой строке, то результатом
+ операции является размер строки плюс завершающий нуль. Например:
+
+ sizeof ("Test")
+
+ результатом этой операции будет число 5. Если Вы напишите такую
+ конструкцию:
+
+ char a="Test";
+
+ sizeof(a)
+
+ то результатом будет 5 - размер памяти, отведенный для переменной a.
+
+ При использовании оператора sizeof с именем структуры вставляет
+ фактический размер памяти, занимаемый структурой. Это особенно важно, если
+ Вы объявили массив структур.
+
+ Оператор sizeof можно применять и к имени определенной ранее
+ процедуры. Результатом будет размер этой процедуры. Но для динамических
+ процедур всегда будет ноль.
+
+ Операцию sizeof можно применять и к файлам. Это бывает очень полезным
+ при использовании оператора FROM, но может применяться и в других случаях.
+ Пример применения оператора sizeof к файлам:
+
+ sizeof ( file "filename.dat" )
+
+ Результатом этой операции будет размер файла "filename.dat".
+Return to contents.
+
+
+
+ 9.22 Метки перехода.
+
+
+ Метки перехода применяются для указания начальных точек участков кода,
+ используемых командами перехода встроенного ассемблера и операторами
+ goto/GOTO.
+
+ Имеются два типа меток перехода: глобальные и локальные. Глобальные
+ метки, как следует из названия, это метки, которые видимы из любого места в
+ программе. Локальные метки видны только в пределах своего процедурного
+ блока, и не определены за его пределами.
+
+ Метки определяются идентификатором, оканчивающимися двоеточием. Если
+ идентификатор содержит хотя бы один символ строчных букв (букв нижнего
+ регистра, маленьких букв), это глобальная метка перехода, в противном
+ случае, это локальная метка перехода.
+
+ Глобальные метки перехода не должны использоваться внутри динамических
+ процедур; там можно использовать только локальные метки. Это важно помнить,
+ поскольку, из-за применения такого средства как макрокоманды, динамическая
+ процедура может присутствовать в нескольких местах кода, что будет
+ означать, что метке соответствует больше чем один адрес.
+
+ Метки вне процедур фактически располагаются в области данных программы.
+ Если данные и код находятся в одном сегменте (а именно так организованна
+ программа, написанная на C--), то метки вне процедур становятся простым и
+ эффективным методом для получения расстояний между частями программы. В
+ качестве имен для меток вне процедур могут быть использованы уникальные
+ идентификаторы, в которых можно использовать большие, маленькие и смесь
+ больших и маленьких букв.
+Return to contents.
+
+
+
+10. Ассемблер.
+
+ 10.1 Поддержка команд ассемблера.
+
+
+ Встроенный в C-- ассемблер поддерживает все инструкции 8088/8086,
+ 80286, 80386, 80486, Pentium, Pentium II и Pentium III процессоров.
+
+ Все инструкции встроенного ассемблера должны начинаться с символа
+ доллара $. Поддерживается также ключевое слово asm, которое являясь
+ синонимом к символу доллара, еще и поддерживает объединение ассемблерных
+ инструкций в блоки.
+Return to contents.
+
+
+
+ 10.2 Ключевое слово asm.
+
+
+ Ключевое слово asm является синонимом к $ - префикс ассемблерной
+ команды. После слова asm можно писать блок ассемблерных команд. Пример:
+
+ asm {
+ .
+ .
+ push AX
+ labl:
+ push BX
+ mov AX,0x1234
+ jmp short labl
+ .
+ .
+ .
+ }
+
+ Метки внутри блока ассемблерных команд допустимы.
+Return to contents.
+
+
+
+ 10.3 Префикс dup - повторение инструкций DB/DW/DD.
+
+
+ Для ассемблерных инструкции DB, DW, DD введена возможность использовать
+ префикс повторений dup. Применение этого префикса имеет следующий
+ синтаксис:
+
+ $DW NUMREP dup VALTOREP
+
+ NUMREP - число повторов инструкции DW.
+ VALTOREP - величина, которая будет повторена NUMREP раз.
+
+ В отличие от аналога этого префикса из ассемблера повторяемую величину
+ заключать в скобки нельзя.
+Return to contents.
+
+
+
+ 10.4 Инструкции процессора Pentium III.
+
+
+ В компилятор добавлена поддержка 19 новых инструкций MMX расширения
+
+ MASKMOVQ mmx,mmx
+ MOVNTQ m64,mmx
+ PAVGB mmx,mmx/m64
+ PAVGW mmx,mmx/m64
+ PEXTRW r32,mmx,i8
+ PINSRW mmx,r32/m16,i8
+ PMAXUB mmx,mmx/m64
+ PMAXSW mmx,mmx/m64
+ PMINUB mmx,mmx/m64
+ PMINSW mmx,mmx/m64
+ PMOVMSKB r32,mmx
+ PMULHUW mmx,mmx/m64
+ PREFETCHT0 mem
+ PREFETCHT1 mem
+ PREFETCHT2 mem
+ PREFETCHNTA mem
+ SFENCE
+ PSADBW mmx,mmx/m64
+ PSHUFW mmx,mmx/m64,i8
+
+ и 46 инструкций SSE расширения.
+
+ ADDPS xmm,m128/xmm
+ ADDSS xmm,xmm/m32
+ ANDNPS xmm,xmm/m128
+ ANDPS xmm,xmm/m128
+ COMISS xmm,xmm/m32
+ DIVPS xmm,m128/xmm
+ DIVSS xmm,xmm/m32
+ MAXPS xmm,m128/xmm
+ MAXSS xmm,xmm/m32
+ MINPS xmm,m128/xmm
+ MINSS xmm,xmm/m32
+ MULPS xmm,m128/xmm
+ MULSS xmm,xmm/m32
+ ORPS xmm,xmm/m128
+ RCPPS xmm,xmm/m128
+ RCPSS xmm,xmm/m32
+ RSQRTPS xmm,xmm/m128
+ RSQRTSS xmm,xmm/m32
+ SQRTPS xmm,m128/xmm
+ SQRTSS xmm,xmm/m32
+ SUBPS xmm,m128/xmm
+ SUBSS xmm,xmm/m32
+ UCOMISS xmm,xmm/m32
+ UNPCKHPS xmm,xmm/m128
+ UNPCKLPS xmm,xmm/m128
+ XORPS xmm,xmm/m128
+ CMPPS xmm,xmm/m128,i8
+ CMPSS xmm,xmm/m32,i8
+ SHUFPS xmm,xmm/m128,i8
+ CVTPI2PS xmm,m64/mmx
+ CVTSI2SS xmm,m32/r32
+ CVTPS2PI mmx,m128/xmm
+ CVTTPS2PI mmx,xmm/m128
+ CVTSS2SI r32,xmm/m128
+ CVTTSS2SI r32,xmm/m128
+ LDMXCSR m32
+ STMXCSR m32
+ MOVHLPS xmm,xmm
+ MOVLHPS xmm,xmm
+ MOVMSKPS r32,xmm
+ MOVNTPS m128,xmm
+ MOVAPS m128/xmm,xmm/m128
+ MOVSS xmm/m32,xmm/m32
+ MOVUPS xmm/m128,m128/xmm
+ MOVHPS xmm/m64,m64/xmm
+ MOVLPS xmm/m64,m64/xmm
+
+ Многие из этих инструкций могут использовать в качестве операнда
+ 64-битные и 128-битные ячейки памяти. Компилятор C-- сейчас может работать
+ только с 32-битными переменными. Поэтому для инструкций использующих в
+ качестве операнда ячейки памяти размером больше 32-бит можно использовать
+ переменные любых типов. Компилятор не будет выдавать на это сообщений об
+ ошибке, будет использован адрес этой переменной, а сама инструкция будет
+ использовать нужное ей число битов памяти, начиная с адреса указанной
+ переменной. Например:
+
+ Для инструкции movaps один из операндов может быть 128-битной
+ ячейкой памяти. Для этой инструкции допустимы следующий синтаксис:
+
+ byte var8_128[16];
+ word var16_128[8];
+ dword var32_128[4];
+
+ void proc()
+ {
+ asm{
+ movaps var8_128,xmm0 //в массив из 16 байт будет записано содержимое XMM0
+ movaps xmm1,var16_128 //в XMM1 будет записано содержимое 8 слов
+ movaps var32_128,xmm1 //в массив из 4 двойных слов будет записано XMM1
+ }
+ }
+Return to contents.
+
+
+
+11. Процедуры.
+
+ 11.1 Типы процедур, функций и макрокоманд.
+
+
+ Сейчас C-- поддерживает 4 типа вызова процедур: cdecl, pascal, stdcall
+ и fastcall. Вот краткие характеристики этих типов вызовов процедур:
+
+ cdecl Этот тип вызова процедур является по умолчанию для языка С. Он
+ характеризуется тем, что параметры процедуры передаются в порядке обратном
+ их записи. Очистка стека от параметров производится после завершения работы
+ процедуры. Этот способ вызова процедур очень удобен для процедур с
+ переменным числом параметров.
+
+ pascal Этот тип вызова предполагает, что параметры передаются в том
+ порядке, в котором они записаны в программе. Освобождение стека от
+ параметров производит сама вызываемая процедура. Этот тип вызова является
+ более компактным, чем cdecl.
+
+ stdcall Этот тип вызова является гибридом первых двух. Параметры
+ передаются процедуре в порядке обратном, тому в котором они записаны в
+ программе. Освобождение стека от параметров производится в самой вызываемой
+ процедуре.
+
+ fastcall Этот тип вызова процедур предполагает что передача параметров
+ процедуре производится через регистры, тем самым отпадает необходимость
+ освобождения стека от параметров. Для этого типа вызова процедуры
+ существуют ограничения по числу передаваемых параметров. Для C это три
+ параметра, а для C-- шесть. В C-- параметры передаются по умолчанию в
+ следующем порядке: 1-й - AX/EAX, 2-й - BX/EBX, 3 - CX/ECX, 4 - DX/EDX, 5 -
+ DI/EDI, 6 - SI/ESI. Параметры типов char или byte могут передаваться в
+ количестве не более 4 или только в первых 4 регистрах: 1 - AL, 2 - BL, 3 -
+ CL, 4 - DL. Этот порядок регистров может быть изменен, если явно указать
+ его либо при объявлении процедуры, либо при ее определении. Процедуры типа
+ fastcall иногда еще называют регистровыми.
+
+ В C-- по умолчанию, если имя процедуры написано большими буквами, то
+ считается, что эта процедура имеет тип вызова fastcall. Если же в имени
+ процедуры есть хотя бы одна маленькая буква, то по умолчанию считается, что
+ эта процедура имеет тип вызова pascal, за исключением программ
+ компилируемых с ключом /w32 /w32c или /DLL. В них по умолчанию применяется
+ тип вызова процедур stdcall. Если же Вы хотите изменить тип вызова процедур
+ из по умолчанию на любой другой, то эту процедуру надо обязательно объявить
+ с указанием типа желаемого вызова.
+
+ Объявление процедур введено для того, чтобы сообщать компилятору о
+ типе возврата из процедур, способе передачи параметров процедуре и их числе.
+Return to contents.
+
+
+
+ 11.2 Стековые процедуры.
+
+
+ Стековые процедуры по умолчанию объявляются при помощи идентификатора,
+ который содержит, по крайней мере, один символ строчных букв (букв нижнего
+ регистра, маленьких букв). Таким образом, стековые процедуры легко отличимы
+ от регистровых процедур, поскольку для имен регистровых процедур символы
+ строчных букв запрещены.
+
+ Параметры для стековых процедур, если они есть, могут иметь любой тип
+ byte, char, word, int, dword, long или float.
+
+ Параметры передаются в соответствии с правилами, принятыми для данного
+ типа процедур. Если процедура не имеет объявления, то компилятор не следит
+ за числом и типом передаваемых параметров. В этом случае у Вас появляется
+ свобода в их использовании, но Вы должны осознавать и последстви
+ неправильного их использования.
+
+ В списке параметров для каждого параметра указывается его тип.
+ Параметры одного типа, идущие подряд, разделяются запятыми. Формальные
+ параметры разного типа в объявлении функции разделяются символом ;.
+
+ В следующем примере стековая процедура возвращает сумму всех своих
+ параметров (имеющих различные типы) как величину типа word:
+
+ word add_them_all (int a,b,c; byte d,e; word x,y)
+ {
+ return( a+b+c+d+e+x+y );
+ }
+
+ Ранее C-- делал вызовы стековых процедур лишь в стиле pascal.
+ Преимуществом этого способа вызова процедур является компактность и более
+ простой механизм генерации кода. К недостаткам, а соответственно и
+ преимуществам С-стиля, можно отнести жесткую привязанность паскалевских
+ процедур к числу и типу передаваемых параметров (попробуйте при вызове
+ процедуры в стиле pascal опустить один параметр и получите 100% зависание).
+ Напомню некоторые технические детали обоих типов вызовов процедур.
+
+ Кадр стека C-- для близких процедур стека в стиле pascal:
+ АДРЕС
+ ...
+ BP + FFFE предпоследний байта локальных переменных
+ BP + FFFF последний байт локальных переменных
+ BP + 0000 Сохраненный BP
+ BP + 0002 RET адрес
+ BP + 0004 последнее слово передаваемых процедуре параметров (если они
+ есть)
+ BP + 0006 предпоследнее слово передаваемых процедуре параметров
+ ...
+ BP + nnnn первое слово передаваемых процедуре параметров
+
+ Освобождение стека от переданных процедуре параметров происходит прямо
+ в самой процедуре командой RET nnnn - где nnnn является размером переданных
+ в стек параметров.
+
+ Кадр стека C-- для близких процедур стека в стиле си:
+ АДРЕС
+ ...
+ BP + FFFE предпоследний байта локальных переменных
+ BP + FFFF последний байт локальных переменных
+ BP + 0000 Сохраненный BP
+ BP + 0002 RET адрес
+ BP + 0004 первое слово передаваемых процедуре параметров (если они
+ есть)
+ BP + 0006 второе слово передаваемых процедуре параметров
+ ...
+ BP + nnnn последнее слово передаваемых процедуре параметров
+
+ Процедуры в стиле С заканчиваются командой RET. Освобождение стека от
+ параметров происходит в том месте откуда была вызвана процедура. Обычно это
+ делается командой ADD SP,nnnn. Т.е. компилятор может точно знать сколько и
+ каких параметров Вы передаете в данном случае процедуре и соответственно
+ освобождает стек после завершения процедуры. Это очень удобно для процедур,
+ которые могут обрабатывать переменное число параметров (например, процедуры
+ типа printf).
+
+ Объявление процедуры имеет следующий вид:
+
+ rettype modif procname();
+
+ Первым идет необязательный тип возврата из процедур. По умолчанию он
+ для 16-битных программ равен word, а для 32-битных dword. Затем должен идти
+ также необязательный модификатор. По умолчанию все стековые процедуры в C--
+ (за исключением режима компиляции программ под Windows, где по умолчанию
+ действует стиль вызова процедур stdcall) имеют стиль pascal. Далее идет им
+ процедуры со скобками, которые являются признаком того что Вы объявляете
+ процедуру, а не переменную. Завершает объявление символ точка с запятой.
+
+ При объявлении процедур в C-- прописывать параметры процедуры
+ необязательно (тогда компилятор не будет контролировать число и тип
+ передаваемых параметров), но если Вы их вставите, то включится механизм
+ контроля за числом и типом параметров.
+Return to contents.
+
+
+
+ 11.3 Регистровые процедуры.
+
+
+ Регистровые процедуры определяются, по умолчанию, при помощи
+ идентификатора, который не содержит символов строчных букв. Или же явным
+ указанием что это регистровая процедура с помощью ключевого слова fastcall.
+
+ Как уже было сказано, параметры (если они есть) для регистровой
+ процедуры передаются через регистры. Регистровые процедуры могут иметь не
+ более 6 параметров. Если параметры имеют тип int или word, регистры по
+ умолчанию используются в следующем порядке: AX, BX, CX, DX, DI, и SI.
+ Первые четыре параметра могут также иметь тип char или byte, в этом случае
+ задействуются регистры AL, BL, CL и DL соответственно. Любой из шести
+ параметров может иметь тип long, dword или float, тогда для него
+ используется регистр EAX, EBX, ECX, EDX, EDI, или ESI.
+
+ В следующем примере регистровая процедура с именем TOGETHER возвращает
+ значение типа word как результат умножения первого параметра, имеющего тип
+ word, на второй параметр того же типа:
+
+ word TOGETHER() /* AX = первый параметр, BX = второй параметр */
+ {
+ return (AX * BX);
+ }
+
+ В следующем примере регистровая процедура с именем SHOW_NUM, которая не
+ возвращает никакого значения, зато выводит на экран первый параметр
+ (имеющий тип int), затем разделительный знак в виде двоеточия :, а затем
+ второй параметр (имеющий тип byte) :
+
+ void SHOW_NUM () /* AX = первое число, BL = второе число */
+ {
+ $ PUSH BX
+ WRITEINT (int AX);
+ WRITE (':');
+ $ POP BX
+ WRITEWORD (BL);
+ }
+
+ Но если в процедуре сделать объявление порядка и типов используемых
+ регистров, то возможно произвольное использование регистров. Более подробно
+ об этом можно почитать в разделе об объявлениях параметров в регистровых
+ процедурах.
+
+ Для того, чтобы использовать регистровую процедуру как макрокоманду,
+ она должна быть объявлена как динамическая процедура. Динамические
+ процедуры описаны в следующем подразделе.
+Return to contents.
+
+
+
+ 11.4 Динамические процедуры.
+
+
+ Динамические процедуры - процедуры, которые определены, но вставляются
+ в код программы, только если есть вызов. Динамические процедуры могут
+ использоваться как макрокоманды.
+
+ Определение динамической процедуры начинается с символа двоеточия ':'.
+
+ Пример динамической процедуры стека:
+
+ : void setvideomode (byte mode)
+ {
+ AL = mode;
+ AH = 0;
+ $ INT 0x10
+ }
+
+ Пример динамической регистровой процедуры:
+
+ : int ABS () /* AX = число, абсолютное значение которого ищется*/
+ {
+ IF (int AX < 0)
+ -AX;
+ }
+Return to contents.
+
+
+
+ 11.4.1 Установка динамической процедуры в определенное место программы.
+
+
+ Динамические процедуры, если они не используются как макросы и если
+ они были востребованы в программе, вставляются в код программы в самом
+ конце компиляции. В каком точно месте Вашей программы они окажутся узнать
+ невозможно. Если же Вам необходимо, чтобы какая-то динамическая процедура
+ находилась в конкретном месте программы, то это можно сделать таким
+ образом:
+
+ :void proc ( int par1, par2)
+ {
+ ...
+ }
+
+ Мы имеем динамическую процедуру, код которой был бы расположен ранее
+ кода обычной процедуры нашей программы. Для этого перед определением этой
+ процедуры надо написать такую строку:
+
+ @ void proc ();
+
+ В итоге динамическая процедура будет вставлена в код программы не в
+ конце ее, как обычно, а в месте, где будет расположена эта строка. Если
+ динамическая процедура имеет параметры, то прописывать эти параметры
+ необязательно.
+
+ В компиляторе есть еще более мощное средство, позволяющее все
+ динамические объекты ( процедуры, переменные, структуры ) расположить в
+ указанном месте, а не в конце программы, как обычно. Это директива
+ #setdinproc. Встретив эту директиву, компилятор немедленно расположит все
+ известные ему на этот момент динамические объекты в месте объявления этой
+ директивы. Последующие динамические объекты будут располагаться как
+ обычно, в конце программы, если конечно, не будет повторно применена
+ директива #setdinproc.
+
+ Это может быть применено и быть полезным при создании резидентных
+ программ (TSR) и драйверов устройств.
+Return to contents.
+
+
+
+ 11.5 inline-процедуры.
+
+
+ inline-процедурами могут быть динамические процедуры, которые можно
+ использовать как макросы. Но в отличие от макросов, inline-процедуры, при
+ включенной оптимизации на скорость, автоматически вставляются в код, а при
+ оптимизации кода на размер, делается вызов их, как динамических процедур.
+
+ Но иногда бывает нужно при включенной оптимизации на размер кода, чтобы
+ процедуры вставлялись в код, а не делался их вызов. Для этих целей введена
+ директива #inline TRUE. Этой же директивой ( #inline FALSE ), можно при
+ оптимизации на скорость делать вызовы процедур, вместо их вставки.
+
+ Важно помнить, что статус директивы #inline автоматически меняется при
+ смене режима оптимизации. При установке оптимизации на скорость статус
+ директивы #inline устанавливается в TRUE, а при смене режима оптимизации по
+ размеру кода, устанавливается в FALSE. Поэтому применяйте директиву #inline
+ лишь после смены режима оптимизации.
+
+ Директивы меняющие режим оптимизации #codesize, #speed и директива
+ #inline, объявленные внутри процедуры распространяются только на оставшуюся
+ часть процедуры, т.е. они становятся локальными. Для того чтобы изменения
+ были глобальными эти директивы надо объявлять вне тела процедуры.
+
+ Для того чтобы определить inline-процедуру, надо в первой строке с
+ именем процедуры вместо символа динамической процедуры (:) написать
+ ключевое слово inline. Пример определения inline-процедуры:
+
+ inline int fastcall abs(AX)
+ {
+ IF ( int AX < 0 ) -AX ;
+ }
+Return to contents.
+
+
+
+ 11.5.1 Другое применение inline.
+
+
+ Ключевое слово inline имеет в процедурах и другое применение. Если
+ это слово расположено перед началом блока процедуры, то для такой
+ процедуры не создается кадр стека и не генерируется завершающий процедуру
+ ret. Пример:
+
+ void PROC ()
+ inline
+ {
+ ...
+ }
+
+ Такие процедуры не должны содержать локальных переменных. Если
+ процедура является регистровой (тип fastcall), то с передачей ей
+ параметров нет проблем. Если же процедура является стековой, то передать
+ в такую процедуру параметры Вы можете, но воспользоваться этими
+ параметрами используя их имена, Вы уже не сможете. Это происходит потому,
+ что в этих процедурах кадр стека не формируется. Пример:
+
+ void proc (int par1, par2)
+ inline
+ {
+ AX=par1; /* компилятор обратится с параметру 'par1' через регистр BP.
+ Но так как кадр стека не был создан, при выполнении этого
+ кода программа будет работать не правильно. */
+ ...
+ }
+
+ Встретив такое определение процедуры, компилятор выдаст предупреждение
+ о том, что в таких процедурах использовать локальные и параметрические
+ переменные нельзя.
+Return to contents.
+
+
+
+ 11.6 Процедуры обработки прерываний.
+
+
+ Процедуры обработки прерываний определяются следующим способом:
+
+ interrupt procedure_name ()
+ {
+ // put code here (здесь должен быть код обработки)
+ }
+
+ Процедуры обработки прерываний не сохраняют никаких регистров
+ автоматически, и никакие регистры сами по себе не загружаются перед
+ передачей управления обработчику прерывания, следовательно, на Вашей
+ совести сохранение значений регистров в стеке и последующий их возврат, а
+ также загрузка регистра DS нужным значением.
+
+ Вот пример обработчика прерывания, который сохраняет значения всех
+ регистров и загружает регистр DS:
+
+ interrupt safe_handle ()
+ {
+ $ PUSH DS
+ $ PUSH ES
+ $ PUSHA // для выполнения этой команды нужен процессор не хуже 80286
+ DS = CS; // здесь DS загружается для работы с моделью памяти типа tiny
+
+
+ /* do your thing here (здесь вы делаете свою обработку)*/
+
+ $ POPA // для выполнения этой команды нужен процессор не хуже 80286
+ $ POP ES
+ $ POP DS
+ }
+
+ При завершении процедуры прерывания будет автоматически сгенерирована
+ инструкция выхода из обработчика прерывания - IRET.
+Return to contents.
+
+
+
+ 11.7 Замена return на goto.
+
+
+ В некоторых ситуациях, при компиляции программы, оператор return
+ будет заменяться на goto. Это происходит при разрешенной оптимизации по
+ размеру кода для операторов return, которые расположены внутри процедуры
+ и, естественно, если размер кода для выполнения return больше, чем размер
+ кода для реализации goto. Для динамических процедур, которые используются
+ как макросы, такая замена будет производится всегда. Оператор goto будет
+ выполнен на конец процедуры, там, где будет располагаться единственный
+ выход из процедуры. В динамических процедурах, используемых в качестве
+ макросов, return в конце процедуры будет пропущен компилятором.
+
+ Таким образом, снято последнее ограничение на использование
+ динамических процедур в качестве макросов. Любая динамическая процедура
+ может быть использована как макрос.
+
+ Для оператора goto существует его более короткий аналог - GOTO.
+ Для получения более компактного кода для оператора return введен также
+ более короткий оператор RETURN. Его можно использовать, если от места
+ его применения до конца процедуры находится не более 128 байт. Если Вы
+ будете использовать RETURN на большем расстоянии до конца процедуры, то
+ компилятор выдаст сообщение об ошибке. При использовании return на
+ расстоянии меньше 128 байт до конца кода, компилятор выдаст вам
+ предупреждение о возможном использовании RETURN.
+Return to contents.
+
+
+
+ 11.8 Возвращаемые значения.
+
+
+ Возвращаемые из функций значения располагаются в регистрах. В таблице
+ показано, какой регистр используется для каждого из возвращаемых типов:
+
+ --------------------------------------------
+ | возвращаемый тип | используемый регистр |
+ --------------------------------------------
+ | byte | AL |
+ | word | AX |
+ | dword | EAX |
+ | char | AL |
+ | int | AX |
+ | long | EAX |
+ | float | EAX |
+ --------------------------------------------
+
+ Самый простой способ вернуть значение из функции состоит в том, чтобы
+ использовать команду return(), но вместо этого можно напрямую загрузить
+ возвращаемое значение в соответствующий регистр. Например, следующие две
+ функции возвращают одно и то же значение:
+
+ byte proc_one ()
+ {
+ return (42);
+ }
+
+ byte proc_two ()
+ {
+ AL = 42;
+ }
+
+ Многие DOS функции 0x21 прерывания в качестве индикатора успешного
+ выполнения используют установку или сброс carry флага. Использовать флаги
+ процессора при возврате из процедур можно и в других случаях, когда надо
+ иметь статус успешного или не успешного выполнения процедуры. Это позволит
+ более полно использовать возможности процессора и соответственно уменьшит
+ размер кода и повысит быстродействие программы.
+
+ Наряду с флагами, при возврате из процедур, по прежнему остается
+ возврат различных типов и через регистр AL/AX/EAX. Если для процедуры
+ объявлено, что она имеет тип возврата int и CARRYFLAG, то при использовании
+ такой процедуры в операциях сравнения IF, WHILE... будет делаться проверка
+ carry флага, а не сравнение регистра AX. Пример использования возврата
+ флагов из процедур:
+
+ int CARRYFLAG FOPEN(); // объявление процедуры
+
+ void proc()
+ {
+ IF ( FOPEN(name,0) ) Error ( "Not open file" );
+ }
+
+ Варианты допустимого синтаксиса для использования возврата флага:
+
+ IF ( ! FOPEN() )...
+ IF ( @ FOPEN() )...
+ IF ( ! @ FOPEN() )...
+ IF ( handl = FOPEN() )...
+ IF ( handl = @ FOPEN() )...
+ IF ( ! handl = FOPEN() )...
+ IF ( ! handl = @ FOPEN() )...
+
+ А вот варианты, в которых, несмотря на то, что для процедуры объявлен
+ возврат флага, будет производиться сравнение регистра AX:
+
+ IF ( FOPEN() == 5 )... // производится сравнение
+ IF ( FOPEN() + 2 )... // результат процедуры подвергается дальнейшему
+ // вычислению, в результате которого флаги будут
+ // изменены.
+Return to contents.
+
+
+
+ 11.9 Объявление параметров в регистровых процедурах.
+
+
+ Ранее каждому параметру регистровой процедуры соответствовал строго
+ определенный регистр. Например, для переменных типа int или word первый
+ параметр передавался через регистр AX, 2-й - BX, 3-й - CX, 4-й - DX, 5-й -
+ DI, 6-й - SI. Поэтому, если Вам было необходимо передать только один
+ параметр через регистр SI, то приходилось перед ним писать пять запятых.
+ Вот как, например, выглядит вызов процедуры STRCPY:
+
+ void main ()
+ {
+ STRCPY ( , , , , #dest, #sourc ) ;
+ }
+
+ Теперь регистры могут располагаться при передаче параметров
+ произвольным образом. Надо только объявить компилятору о том, какой регистр
+ закреплен за каким параметром данной процедуры. После такого объявления
+ компилятор будет сам следить за тем, через какой регистр передавать
+ параметр процедуре, его размерностью и числом передаваемых параметров. Вот
+ как будет выглядеть объявление и использование процедуры STRCPY:
+
+ void STRCPY ( DI, SI ) ; //это объявление процедуры
+
+ void main ()
+ {
+ STRCPY ( #dest, #sourc ) ; //а это вызов процедуры
+ }
+
+ Можно не делать объявления процедуры, а указать расположение регистров
+ в заголовке процедуры. Но тогда такая процедура должна вызываться только
+ после ее определения. Вот пример процедуры выводящей на экран несколько
+ одинаковых символов:
+
+ void PUTNCHAR(AL,CX,BL,BH)
+ /* 1 параметр в AL - код символа, который будет выведен
+ 2 параметр в CX - число выводимых символов
+ 3 параметр в BL - цветовой атрибут
+ 4 параметр в BH - номер видеостраницы
+ */
+ {
+ AH=9;
+ $INT 0x10
+ }
+
+ При объявлении регистровой процедуры можно также указывать какой тип
+ переменной ожидает процедура (знаковый/без знаковый или вещественный). По
+ умолчанию считается без знаковый тип. Однако знаковый тип указывать есть
+ смысл только если параметр передается через регистр AL/AX/EAX. Через другие
+ регистры переменная всегда передается как без знаковая. Пример объявления
+ регистровой процедуры с указанием типов:
+
+ int fastcall Exampl( word CX, int AX, DX, float ESI ) ;
+ | | | | | | |
+ | | | | | | |---- 4-й парам. имеет тип float и
+ | | | | | | перед. через регистр ESI.
+ | | | | | |-------- 3-й парам. имеет по умолч.
+ | | | | | тип word и перед. через DX.
+ | | | | |------------ 2-й парам. имеет тип int и
+ | | | | передается через регистр AX.
+ | | | |---------------------- 1-й парам. имеет тип word и
+ | | | передается через регистр CX.
+ | | |------------------------------- Имя объявляемой процедуры.
+ | |---------------------------------------- Модификатор, указывающий, что
+ | эта проц. явл. регистровой.
+ |--------------------------------------------- Процедура возвращает перемен.
+ типа int.
+
+ Если Вы сделали объявление регистров процедуры, то компилятор будет
+ строго следить за количеством указанных параметров при вызове этой
+ процедуры и выдавать сообщения об ошибке, если их будет меньше или больше.
+ С одной стороны это хорошо - есть контроль за тем, что Вы ничего не забыли
+ или не добавили лишнего при вызове процедуры. С другой стороны иногда
+ бывают необязательные параметры, а их теперь придется прописывать. Но если
+ Вы при вызове процедуры не укажете ни одного параметра, то компилятор не
+ будет Вам выдавать сообщение об ошибке. Это дает Вам возможность
+ проинициализировать регистры, через которые Вы передаете параметры, вне
+ вызова процедуры. Но если Вы укажете, хоть один параметр, то Вам придется
+ указывать и остальные, иначе компилятор будет считать, что Вы их случайно
+ пропустили и выдаст сообщение об ошибке.
+
+ Если Вы не объявили регистры ни при объявлении регистровой процедуры,
+ ни в заголовке самой процедуры, то компилятор будет считать, что параметры
+ в эту процедуру передаются старым способом. Таким образом, достигается
+ полная совместимость с предыдущими версиями компилятора.
+Return to contents.
+
+
+
+ 11.10 Объявление параметров в стековых процедурах.
+
+
+ Как известно, ранее в C-- контроль за числом и типом передаваемых
+ процедуре параметров возлагался на программиста. Поэтому возникла непростая
+ задача, совместить одновременно отсутствие контроля за параметрами (для
+ совместимости с предыдущими версиями) и ее наличие. В результате
+ компромиссов появился вариант немного отличающийся от традиционно принятого
+ в языках C.
+
+ Главное отличие - это то, что параметры, определяемые при определении
+ процедуры, не будут восприниматься компилятором для контроля за ними. Во
+ всех языках C допускается совмещение прототипа процедуры и ее объявления.
+ В C-- для того, чтобы включился контроль за параметрами стековой процедуры,
+ надо эту процедуру обязательно объявить. Но не всякое объявление процедуры
+ будет сигналом компилятору о включении контроля за параметрами этой
+ процедуры. Если при объявлении в круглых скобках ничего не будет, то
+ компилятор не будет отслеживать параметры, передаваемые этой процедуре. В
+ C++ такое объявление означает, что процедуре не передаются никакие
+ параметры. В C-- для этого надо при объявлении процедуры в круглых скобках
+ обязательно написать void. Например:
+
+ int proc ( void ) ;
+
+ Встретив такое объявление процедуры, компилятор будет следить за тем,
+ чтобы этой процедуре не были переданы параметры.
+
+ При объявлении процедуры имена параметров можно опускать. Как известно,
+ в C-- параметры процедуры одного типа записываются через запятую. Для смены
+ типа используют точку с запятой. При объявлении смену типа можно
+ производить и после запятой:
+
+ void ptoc ( int a, b, c; word d );
+ void proc ( int, int, int, word );
+ void proc ( int, int, int; word );
+
+ Все эти примеры объявлений являются идентичными и допустимыми.
+
+ Для контроля за процедурами с переменным числом параметров был введен
+ новый для C-- элемент синтаксиса - многоточие или его еще называют эллипс.
+ Вот как будет выглядеть объявление процедуры printf:
+
+ void cdecl printf ( word, ... );
+Return to contents.
+
+
+
+ 11.11 Использование макрокоманд.
+
+
+ Теперь любая динамическая процедура может быть использована как макрос.
+ Если перед вызовом динамической процедуры поставить символ @, то код этой
+ процедуры будет вставлен, а не вызван инструкцией CALL.
+
+ При использовании стековых динамических процедур в качестве макросов
+ очистка стека от переданных параметров производится ассемблерной
+ инструкцией ADD SP,SIZE_PARAMETRS сразу после окончания кода вставленного
+ макроса. Поэтому, если эта процедура использовала флаги в качестве
+ возврата, то они будут разрушены.
+Return to contents.
+
+
+
+ 11.12 Передача параметров в стековые процедуры через регистры.
+
+
+ При передаче параметров через регистры, чаще всего получается более
+ компактный и быстрый код. Но содержимое регистров может быть легко
+ разрушено. Если в Вашей процедуре, какой-то из параметров используется
+ однократно для того, чтобы в начале процедуры инициализировать какой-то
+ регистр, то Вы можете передать это значение в процедуру сразу через
+ регистр, минуя стадию засовывания и извлечения содержимого в стек. Пример:
+
+ int proc (int param1, param2, param3)
+ {
+ (E)BX = param3;
+ (E)BX.TEG_STRUCT.var = proc2 (param1,papra2);
+ proc3 (param1,param2);
+ }
+
+ В этом примере параметр param3 используется лишь для того, чтобы
+ инициализировать регистр (E)BX, поэтому его можно сразу передать через
+ регистр:
+
+ int proc (int param1, param2, (E)BX)
+ {
+ (E)BX.TEG_STRUCT.var = proc2 (param1,papra2);
+ proc3 (param1,param2);
+ }
+
+ Как Вы видите, процедура немного упростилась.
+
+ В принципе, порядок расположения стековых и регистровых параметров не
+ принципиален. Но надо помнить, что содержимое регистров может быть легко
+ разрушено, и поэтому лучше всего регистровые параметры инициализировать
+ лишь после того, как были засунуты в стек все стековые параметры. Для
+ процедур типа pascal регистровые параметры лучше располагать после
+ стековых параметров. Для процедур типа cdecl и stdcall сначала лучше
+ располагать регистровые параметры.
+Return to contents.
+
+
+
+ 11.13 Вызов процедур с адресом в регистре.
+
+
+ В C-- допустимо делать вызов процедуры, адрес которой находится в
+ регистре. Параметры для такого вызова передаются только через стек. Тип
+ вызова процедуры для программ под Windows stdcall, для остальных pascal.
+ Адрес процедуры для 32-битных программ должен находится в 32-битном
+ регистре, а для 16-битных программ в 16-битном регистре. Считается, что
+ такой вызов имеет возврат типа unsigned int. Пример:
+
+ BX = # proc;
+ BX (a);
+ IF ( BX(b) == 0 ) AX=2;
+
+ Вы получите следующий код:
+
+ test.c-- 8: BX=#proc;
+ 0104 BB1A01 mov bx,11Ah
+
+ test.c-- 9: BX(a);
+ 0107 FF76FC push word ptr [bp-4]
+ 010A FFD3 call near bx
+
+ test.c-- 10: IF (BX(b) == 0)AX=2;
+ 010C FF76FE push word ptr [bp-2]
+ 010F FFD3 call near bx
+ 0111 85C0 test ax,ax
+ 0113 7503 jne 118h
+ 0115 B80200 mov ax,2
+Return to contents.
+
+
+
+ 11.14 Встроенные в компилятор процедуры.
+
+
+ Для некоторых процедур Вы не найдете их исходные тексты в библиотеках
+ компилятора. Код этих процедур генерирует компилятор. Вот список этих
+ процедур:
+
+ ABORT Прекращение выполнения программы
+ atan Вычислить арктангенс числа
+ atan2 Вычислить арктангенс числа
+ ATEXIT Зарегистрировать функцию выполняющуюся при выходе.
+ cos Возвращает косинус угла
+ EXIT Закончить программу с кодом ошибки
+ exp Возвращает экспоненту числа
+ inp/inportb Считать один байт из порта
+ inport Считать слово из порта
+ inportd Считать двойное слово из порта
+ fabs Возвращает абсолютное значение числа
+ log Вычисляет натуральный логарифм числа
+ log10 Вычисляет десятичный логарифм числа
+ outp/outportb Записать один байт в порт
+ outport Записать слово в порт
+ outportd Записать двойное слово в порт
+ sin Возвращает синус угла
+ sqrt Извлечь квадратный корень через FPU.
+ tan Возвращает тангенс угла
+
+ Размещение этих процедур непосредственно в компиляторе, связано с тем,
+ что в настоящий момент компилятор может таким образом генерировать более
+ эффективный код, чем если бы эти процедуры располагались в библиотеках.
+ В будущем, по мере развития компилятора, эти процедуры постепенно будут
+ выносится из компилятора в библиотеки.
+
+ Но ничто не мешает Вам уже сейчас написать свои одноименные
+ библиотечные процедуры. Встретив определение такой процедуры, компилятор не
+ будет выдавать никаких сообщение, он просто будет применять Ваш вариант
+ процедуры.
+Return to contents.
+
+
+
+ 11.14.1 Процедуры ABORT, ATEXIT и EXIT.
+
+
+ Процедуры ABORT и EXIT связаны с работой директивы #atexit и
+ процедурой ATEXIT. Наиболее оптимальную их реализацию и взаимную
+ интеграцию может сделать только компилятор. Именно поэтому эти процедуры
+ поддерживаются компилятором.
+
+ Процедура ATEXIT - регистровая процедура, которая регистрирует
+ функцию, адрес которой передается ей в качестве параметра, т.е. через
+ регистр (E)AX, как функцию завершения программы. При успешной регистрации
+ ATEXIT возвращает 0. Всего можно зарегистрировать до 16 функций.
+
+ Завершающие функции не должны иметь параметров и возврата. Эти
+ функции будут выполняться в порядке обратном очередности регистрации в
+ случае, если Вы будете завершать работу программы через вызовы процедур
+ ABORT или EXIT или закончится работа процедуры main. Если Вы
+ завершите работу программы вызовом процедуры ExitProcess под Windows или
+ вызовом AH=0x4C; $int 0x21 под DOS, выход из программы произойдет без
+ запуска зарегистрированных функций.
+
+ Процедура ABORT и EXIT, если не включена директива #atexit делают
+ вызов процедуры ExitProcess под Windows и вызов AH=0x4C; $int 0x21 под
+ DOS. Процедуре ABORT не передаются никакие параметры, и она завершает
+ работу программы с кодом возврата 0. Процедуре EXIT передается в
+ качестве параметра код возврата, с которым она и завершает работу
+ программы.
+Return to contents.
+
+
+
+ 11.14.2 Процедуры inp/inportb, inport, inportd, outp/outportb, outport и
+ outportd
+
+
+ Эти процедуры всегда вставляются в код как макросы, т.е. для этих
+ процедур никогда не генерируется вызов процедуры. В зависимости от
+ значения порта, с которым работают эти процедуры, генерируется разный
+ код. Все это позволяет получать более компактный код.
+
+ Процедуры чтения из порта имеют такой прототип:
+
+ byte inp ( word port );
+ word inport ( word port );
+ dword inportd ( word port );
+
+ Процедуры записи в порт имеют такой прототип:
+
+ void outp ( byte val; word port );
+ void outport ( word val; word port );
+ void outportd ( dword val; word port );
+
+ Имена процедур inp и inportb, также как и имена outp и outportb
+ являются синонимами.
+Return to contents.
+
+
+
+ 11.14.3 Процедуры для работы с вещественными числами.
+
+
+ Эти процедуры реализуются компилятором и всегда вставляются в код как
+ макросы, т.е. для них никогда не генерируется вызов процедуры. Кроме
+ этого, если параметром одной процедуры является вызов другой, то
+ результат работы второй процедуры остается в стеке FPU, а первая
+ процедура использует этот результат непосредственно из стека. Таким
+ образом получаются более компактный код. Вот вымышленный пример:
+
+ test.c-- 7: f = sin( sqrt(1) );
+ 0100 D9061C01 fld [11Ch]
+ 0104 D9FA fsqrt
+ 0106 D9FE fsin
+ 0108 D91E2001 fstp [120h]
+ 010C 9B fwait
+
+ Эти процедуры имеют следующий прототип:
+
+ float atan ( float val );
+ float atan ( float val, val2 );
+ float cos ( float val );
+ float exp ( float val );
+ float fabs ( float val );
+ float log ( float val );
+ float log10 ( float val );
+ float sin ( float val );
+ float sqrt ( float val );
+ float tan ( float val );
+Return to contents.
+
+
+
+ 11.15 Классы.
+
+ 11.15.1 Объявление процедур в структурах.
+
+
+ С введение поддержки объявления процедур в структурах, структура
+ становится подобной классу в C++. Т.е. такая процедура становится методом
+ класса. Пример:
+
+ struct Point // объявление класса
+ {
+ int x; // элементы данных
+ int y; // класса типа Point
+ void SetX(int); // объявление методов
+ void SetY(int); // класса Point
+ };
+
+ void Point::SetX(int _x) //определение процедуры класса Point
+ {
+ IF((_x>=0)&&(_x<=MAX_X)) x=_x;
+ // переменные x, y являются членами этого класса и поэтому доступ к ним из
+ // процедур этого же класса осуществляется напрямую.
+ }
+
+ void main()
+ Point p; //определяем структуру в стеке
+ {
+ p.y = p.x = 0;
+ p.SetX(1);
+ }
+
+ При вызове процедуры являющейся методом класса ей неявным образом
+ передается адрес этого класса (структуры). В самой процедуре этот адрес
+ доступен через имя параметрической переменной this. Эту переменную
+ автоматически генерирует компилятор. Если в объявление процедуры в
+ структуре указать ключевое слово static, то такой процедуре адрес
+ класса не передается и переменная this не генерируется.
+
+ Процедура объявленная в структуре может быть динамической. Для этого,
+ при ее определении, в самом ее начале, надо написать символ двоеточия :
+ (также как и для обычных динамических процедур). Но такая динамическая
+ процедура не может быть использована как макрос.
+Return to contents.
+
+
+
+ 11.15.2 Наследование.
+
+
+ В C-- поддерживаются простые и множественные наследования. Объявление
+ структуры с наследованием имеет следующий синтаксис:
+
+ struct Derived : Base1, Base2, ... Basen
+ {
+ int x0;
+ };
+
+ Число базовых структур в производном не ограничено. При множественном
+ наследовании структура может наследовать два и более экземпляра базовой
+ структуры. При этом возникает неоднозначность. Пример:
+
+ struct A
+ {
+ int x,y;
+ . . .
+ };
+
+ struct B : A //структура B наследует A
+ {
+ . . .
+
+ };
+
+ struct C : A //структура C наследует A
+ {
+ . . .
+ };
+
+ struct D : B, C //структура D наследует B и C
+ {
+ . . .
+ };
+
+ void main()
+ D d; //выделяем для структуры D память в стеке и присваиваем ей имя d
+ {
+ d.x0=0;
+
+ В этом примере структура D наследует два экземпляра структуры A и
+ в ней находятся два элемента с именем x0. Компиляторы C++ при записи
+ типа d.x0=0 выдают сообщение об ошибке. C-- эту запись обрабатывает,
+ присваивание производится по умолчанию в элемент из последней базовой
+ структуры, имеющей элемент x0. Для того чтобы получить доступ ко
+ второму элементу x0 (физически этот элемент находится в структуре
+ первым), необходимо применить операцию разрешения видимости:
+
+ d.B::x0=0;
+
+ Из всего этого следует, что записи:
+
+ d.x0=0;
+ и
+ d.C::x0=0;
+
+ являются равнозначными.
+Return to contents.
+
+
+
+ 11.15.3 Наследование процедур.
+
+
+ Если в базовом классе есть процедура, а в производном классе Вы эту
+ процедуру переопределили, то эта процедура будет переопределена и в
+ базовом классе. Таким образом процедура определенная в базовом классе
+ будет потеряна. Пример:
+
+ struct Point // базовый класс
+ {
+ int x; // элементы данных
+ int y; // класса типа Point
+ void SetX(int); // объявление методов
+ void SetY(int); // класса Point
+ };
+
+ void Point::SetX(int _x) // определение процедуры класса Point
+ {
+ IF((_x>=0)&&(_x<=MAX_X)) x=_x;
+ }
+
+ struct Point2 : Point // производный класс
+ {
+ int x2;
+ }
+
+ struct Point3 : Point // еще один производный класс
+ {
+ int z;
+ }
+
+ void Point3::SetX(int _x) // в этом производном классе переопределяем
+ { // процедуру SetX
+ IF((_x>=80)&&(_x<=MAX_X)) x=_x;
+ }
+
+ Процедура SetX, определенная в базовом классе Point, теперь будет
+ недоступна. Вместо кода определенного в этом классе, будет вызываться код
+ процедуры, определенный в наследуемом классе Point3. При вызове процедуры
+ SetX из другого производного класса Point2 будет также вызываться код
+ процедуры, определенный в производном классе Point3. Переопределяя
+ процедуру таким образом, Вы замените код этой процедуры в базовом классе и
+ во всех его наследуемых классах.
+
+ Если Вам необходимо, чтобы код новой процедуры был доступен
+ одновременно с кодом старой процедуры, то в производном классе Вам
+ необходимо сделать еще одно объявление этой процедуры. Пример:
+
+ struct Point // базовый класс
+ {
+ int x; // элементы данных
+ int y; // класса типа Point
+ void SetX(int); // объявление методов
+ void SetY(int); // класса Point
+ };
+
+ void Point::SetX(int _x) // определение процедуры класса Point
+ {
+ IF((_x>=0)&&(_x<=MAX_X)) x=_x;
+ }
+
+ struct Point2 : Point // производный класс
+ {
+ int x2;
+ }
+
+ struct Point3 : Point // еще один производный класс
+ {
+ int z;
+ void SetX(int); // в наследуемом классе делаем еще одно объявление
+ // процедуры SetX
+ }
+
+ void Point3::SetX(int _x) // в этом производном классе переопределяем
+ { // процедуру SetX
+ IF((_x>=80)&&(_x<=MAX_X)) x=_x;
+ EDI=this;
+ EDI.Point.SetX(_x); // делаем вызов одноименной процедуры из
+ // базового класса
+ }
+
+ Теперь из производного класса Point3 Вам доступны две различные
+ процедуры с одним именем SetX. А из базового класса Point и из другого
+ производного класса Point2 будет по прежнему доступен только базовый
+ вариант процедуры SetX.
+Return to contents.
+
+
+
+12. Типы выходных файлов.
+
+ 12.1 Выходные файлы типа COM.
+
+
+ Этот тип выходного файла получается автоматически по умолчанию.
+
+ Изначально C-- мог делать только файлы формата типа COM. В настоящее
+ время появилась возможность получать файла типа EXE с моделями памяти tiny
+ и small для 16-битного кода, а также 32-битные для DOS и Windows. Также
+ есть возможность получения выходного файла в формате OBJ, что позволяет
+ связывать программы на C-- с программами на других языках.
+Return to contents.
+
+
+
+ 12.2 Выходные файлы типа EXE.
+
+
+ Этот формат файла можно получить, если компилировать с ключом командной
+ строки /exe или /e.
+
+ Возможно также поддержка EXE-формата через выходной файл формата OBJ,
+ который можно затем обработать линковщиком, не входящим в пакет C--.
+Return to contents.
+
+
+
+ 12.3 Выходной файл *.EXE с моделью памяти tiny.
+
+
+ Фактически код файла *.exe модели tiny ничем не отличается от кода
+ *.com. В сущности, это тот же com-файл, к которому добавлен 32-байтный
+ заголовок exe-файла. Единственное отличие возникает, когда Вы компилируете
+ файл с директивой ?resize TRUE. В com-файле, по этой директиве, в код
+ программы добавляется соответствующий код, изменяющий размер доступной
+ памяти. В exe-файле для этих целей будет скорректирован заголовок
+ exe-файла.
+
+ Чтобы получить exe-файл с моделью памяти tiny, надо запустить
+ компилятор с ключом в командной строке /TEXE.
+Return to contents.
+
+
+
+ 12.4 Объектный выходной файл OBJ.
+
+
+ В настоящее время C-- может только создавать OBJ-файлы, но не может их
+ компоновать.
+
+ Ранее C-- создавал obj-файлы, которые могли быть подключены к проектам
+ созданным на других языках, т.е. ведомые (slave) модули. Причем из C--
+ модулей для основного проекта были доступны только процедуры и эти
+ процедуры не должны были использовать глобальные переменные.
+
+ Теперь же C-- может создавать основной модуль (master), который может
+ быть слинкован в самостоятельный файл.
+
+ Для obj-файлов появилась возможность использовать внешние (extern)
+ процедуры, переменные или структуры. Для этого достаточно их объявить как
+ extern. Причем ключевое слово extern должно быть всегда первым. Пример
+ объявления внешних объектов:
+
+ extern void cdecl _printf(); // объявление внешней процедуры _printf имеющей
+ // тип cdecl и тип возврата void
+ extern int buts,cubs; // объявление двух внешних переменных типа int
+ extern struct IPXL ipxl; // объявление внешней структуры ipxl имеющей тег
+ // IPXL, причем тег этой структуры должен быть
+ // описан ранее.
+
+ Появление возможности объявлять внешние объекты позволяет подключать к
+ obj-модулю на C-- модули написанные на других языках или подключать к
+ программе на C-- процедуры из библиотек на других языках. При объявлении
+ внешних объектов очень важно правильно указать тип процедуры и ее имя. Если
+ Вы будете использовать внешние процедуры, написанные на C то чаще всего,
+ Вам нужно будет указывать модификатор cdecl, а к имени процедуры или
+ переменной добавлять префикс _.
+
+ Из основного (master) obj-файла написанного на C-- для других
+ obj-модулей доступны все процедуры, глобальные переменные и глобальные
+ структуры.
+
+ Чтобы получить ведомый obj-модуль при компиляции надо использовать ключ
+ /sobj.
+
+ C-- может создавать obj-файлы с моделью памяти tiny и small. По
+ умолчанию создаются модули с моделью tiny. Чтобы получить obj-файл с
+ моделью памяти small надо запустить компилятор с ключами /obj и /exe.
+
+ Для создания obj-файлов для 32-битного DOS в командной строке Вам
+ необходимо указать ключи /d32 и /obj. Использовать полученный obj-файл мне
+ удалось лишь с помощью wlink и расширителя zrdx.exe.
+
+ Создание obj-файлов под windows не предусмотрено.
+Return to contents.
+
+
+
+ 12.5 COM файл symbiosis.
+
+ 12.5.1 СИМБИОЗ - что это такое?
+
+
+ Транслятор C-- имеет ключ, позволяющий добавлять компилируемую
+ программу к концу уже имеющегося COM файла. Это называют COM-файл
+ Symbiosis. Когда такая программа запускается, управление сначала получает
+ добавленный код C--, и только после выполнения его процедуры main()
+ управление получит первоначальный код COM-файла.
+
+ Если добавленный вами код завершается EXIT() или ABORT(), программа
+ прекратится, и первоначальный код COM-файла не будет выполнен. Это
+ позволяет программе, добавленной к COM файлу, определять, будет ли
+ управление передано на первоначальный код.
+Return to contents.
+
+
+
+ 12.5.2 Как это делать.
+
+
+ Чтобы сделать это, Вы должны использовать ключ /SYM в командной
+ строке компилятора, в которой указывается полное имя COM-файла, к
+ которому что-то добавляется. При этом оригинал COM-файла не меняется, а
+ новый файл содержит его в себе. Например, чтобы откомпилировать программу
+ HELLO.C-- к концу копии C:\command.сом используют следующую команду:
+
+ C-- /SYM C:\COMMAND.COM HELLO.C--
+
+ Будет создан выходной файл HELLO.COM .
+Return to contents.
+
+
+
+ 12.5.3 Использование.
+
+
+ Вы можете, вероятно, придумать большое количество путей использования
+ этой функции, типа:
+
+ - Добавление защиты с использованием пароля к некоторым
+ специальным COM файлам.
+ - Уменьшение памяти, доступной COM файлу при запуске.
+ - Инициализация режима видео для COM файла.
+Return to contents.
+
+
+
+ 12.5.4 Злоупотребления.
+
+
+ Любой злоумышленник может придумать и вредные применения для этой
+ функции. Наиболее очевидное из них - создание троянских коней. Я хотел бы
+ указать, что это неконструктивное использование C--, и любое
+ разрушительное использование симбиозов COM-файлов запрещено.
+Return to contents.
+
+
+
+ 12.6 SYS - драйверы устройств.
+
+
+ Компилятор значительно облегчит Ваш труд при написании драйверов.
+ Компилятор сам создаст заголовок драйвера и процедуры СТРАТЕГИЯ и
+ ПРЕРЫВАНИЕ. Вам остается лишь написать код обработки команд.
+
+ Что бы откомпилировать файл драйвера устройства, надо добавить в
+ командную строку ключ /SYS. Кроме того, появились новые директивы
+ компилятору, которые действуют только с этим ключом. Вот они:
+
+ ?sysattribute значение - эта директива передает компилятору
+ атрибут создаваемого драйвера. По умолчанию устанавливается значение
+ 0x2000.
+
+ ?sysname <текстовая строка> - эта директива передает компилятору
+ имя будущего драйвера. По умолчанию присваивается имя "NO_NAME". Длина
+ имени не более 8 символов.
+
+ ?syscommand command_0,command_1, ... command_n; - эта директива
+ является обязательной. По этой директиве компилятору передается список имен
+ процедур обработки команд драйвера. Имена разделены запятыми. Список должен
+ заканчиваться символом точка-с-запятой. Можно передать не более 25 команд.
+ Если какая-то команда не имеет кода поддержки, то в список надо записать
+ слово NONE.
+
+ По умолчанию компилятор для драйвера не создает стек. Драйвер может
+ пользоваться системным стеком. Но, говорят, что он имеет маленькую глубину.
+ Если Ваши процедуры активно используют стек, и Вы не надеетесь на системный,
+ то директивой ?stack <величина> можно заставить драйвер пользоваться своим
+ стеком.
+
+ Вашим процедурам обработки команд при передаче управления в регистрах
+ ES:BX будет передан адрес заголовка запроса. Регистр DS равен CS. При
+ возврате управления ваши процедуры должны сохранить регистр DS. В регистре
+ AX должен находиться код возврата. Остальные регистры могут быть
+ использованы произвольным образом.
+
+ Процедуру обработки команды инициализации желательно располагать
+ последней (чтобы иметь возможность отдать адресное пространство занимаемое
+ этой процедурой операционной системе). Перед этой процедурой, если Вы в
+ других процедурах обработки команд используете динамические процедуры,
+ обязательно должна быть директива ?setdinproc. Глобальные переменные должны
+ быть обязательно проинициализированы.
+Return to contents.
+
+
+
+ 12.7 Компиляция кода расширителей ROM-BIOS.
+
+
+ Расширители ROM-BIOS (BIOS видеоконтроллеров, сетевых карт...) имеют
+ определенную структуру и требования. C-- теперь может облегчить Вам процесс
+ создания кода ROM-BIOS. Если запустить компилятор на компиляцию с ключом
+ командной строки /ROM, то компилятор создаст сигнатуру (заголовок)
+ ROM-BIOS, заполнит оставшееся свободное место до указанного размера ПЗУ
+ кодом заполнения, подсчитает и скорректирует контрольную сумму ПЗУ.
+
+ Для этого режима компиляции есть несколько специфических директив:
+
+ 1. ?sizerom value - эта директива сообщает компилятору размер ПЗУ в
+ байтах. Если эта директива не указана, то компилятор сам выберет
+ минимальный подходящий размер ПЗУ из ряда: 1024, 2048, 4096, 8192, 16384,
+ 32762 или 65536. Свободное от кода и данных место будут заполнены до конца
+ размера ПЗУ байтом заполнения определяемого директивой ?aligner. По
+ умолчанию он равен нулю, для РПЗУ типа 27ххх этот байт имеет смысл сделать
+ равным 0xFF. Последний байт ПЗУ будет скорректирован компилятором таким
+ образом, чтобы контрольная сумма равнялась нулю.
+
+ 2. ?movedatarom TRUE/FALSE - эта директива сообщает компилятору есть ли
+ необходимость копировать данные из ПЗУ в ОЗУ. По умолчанию она установлена
+ в FALSE. Если эту директиву определить TRUE, то компилятор вставит в
+ область инициализации код перемещающий данные из ПЗУ в ОЗУ. При этом
+ регистр DS будет установлен на сегмент ОЗУ. Стек также будет переустановлен
+ на этот сегмент. Таким образом, процедура main получит управление с
+ регистрами AX = ES = DS = SS = сегменту ОЗУ с перенесенными в него данными.
+ Если эту директиву установить в FALSE, регистр DS все равно будет
+ переустановлен на адрес сегмента ОЗУ, так как Ваш код будет использовать
+ этот сегмент для неинициализированных глобальных переменных.
+ Инициализированные переменные останутся в ПЗУ и все обращения к ним будут
+ производиться через регистр CS. Так же останется не тронутым (таким, каким
+ его установил главный BIOS) и стек.
+
+ 3. ?dataseg value - этой директивой компилятору сообщается сегментный
+ адрес ОЗУ, который может быть использован вашим кодом. По умолчанию он
+ равен 0x70. Этот адрес вы можете узнать в любой момент, считав его из вашего
+ кода по смещению 4. Например: DS = CSWORD[4];
+
+ Некоторые замечания:
+
+ 1. Не забывайте, что в момент инициализации ROM-BIOS, DOS еще не
+ загружен, и соответственно все процедуры использующие вызовы DOS работать
+ не будут.
+
+ 2. Нельзя завершать работу программы процедурами ABORT() или EXIT() и им
+ подобным. Работа расширителя ROM-BIOS должна завершаться только выходом из
+ процедуры main().
+
+ 3. Если директива ?movedatarom установлена в FALSE, то будьте внимательны
+ при работе с инициализированными переменными. Они в этом режиме доступны
+ только для чтения, и адресуются через регистр CS.
+Return to contents.
+
+
+
+ 12.8 32-битные файлы.
+
+ 12.8.1 32-битный код под DOS.
+
+
+ Для того чтобы откомпилировать 32-битную программу под DOS надо
+ запустить компилятор с ключом командной строки /d32. Но работа 32-битной
+ программы под DOS-ом невозможна без расширителя DOS. Для C-- можно
+ использовать DOS4GW или zrdx.exe или любой другой расширитель DOS. Чтобы
+ компилятор знал, где искать stub файл и его имя, надо в файл c--.ini
+ прописать строку stub=path_name_to_stub_file. Пример:
+
+ stub=c:\c--\zrdx.exe
+
+ Если не добавлять в c--.ini эту строку, то компилятор сгенерирует
+ 32-битный exe-файл, но без расширителя DOS. Если в командной строке
+ вместе с ключом /d32 указать и ключ /ns, то строка с переменной stub из
+ файла c--.ini будет аннулирована, и вы получите файл без расширителя DOS.
+
+ Для 32-битного DOS-файла можно использовать директивы компилятора
+ ?parsecommandline TRUE/FALSE или его расширенный вариант ?argc
+ TRUE/FALSE. Реализована и поддержка директивы ?atexit TRUE/FALSE.
+
+ Сейчас для 32-битных DOS-файлов используется LE-формат. Так как LE
+ формат является стандартным, то теперь можно использовать почти любой
+ stub, понимающий этот формат. Файлы LE формата можно сжимать программами
+ типа UPX.EXE и ей подобными.
+
+ Если Вы используете stub, который затем загружает DOS4GW.EXE, то
+ начало Вашей программы должно иметь специальную сигнатуру. Компилятор
+ автоматически сформирует ее, если Вы в командной строке или в c--.ini
+ файле укажете ключ /DOS4GW. Такой ключ Вам необходимо будет применять,
+ если Вы будете использовать в качестве stub 4gs.exe.
+
+ Существует также поддержка блока кода использующего для перехода и
+ работы в 32-битном режиме возможности DPMI сервиса. Исходный текст этого
+ блока находится в файле startup.h-- и компилируется, если в командной
+ строке указана опция /stub=dpmi или в файле c--.ini написать строку
+ stub=dpmi. Недостатком этого способа перехода и работы в 32-битном
+ режиме являются необходимость обязательного функционирования на
+ запускаемом компьютере DPMI сервиса. Так как, программа загружается как
+ обычная DOS программа, и лишь в процессе работы переходит в 32-битный
+ режим работы, размер программы ограничен размером свободной DOS памяти.
+ Ну а преимуществом его является компактный размер исполняемого файла.
+Return to contents.
+
+
+
+ 12.8.2 32-битный код под Windows.
+
+
+ Для того чтобы откомпилировать программу, написанную под Windows надо
+ запустить компилятор с ключом командной строки /w32.
+
+ Если Вы в своей программе используете вызовы API-процедур, то эти
+ процедуры надо предварительно обязательно объявить. Объявление процедур
+ имеет следующую форму:
+
+ extern WINAPI "DLL_name"
+ {
+ returncode procname1();
+ returncode procname2();
+ procname3();
+ }
+
+ где:
+ DLL_name - имя и расширение dll-библиотеки, в которой находятся эти
+ процедуры.
+ returncode - тип возврата из api-процедур. По умолчанию он равен dword.
+
+ Программы, написанные под Windows, имеют одну немаловажную
+ особенность - все параметры в стековые процедуры передаются в обратном
+ порядке (так называемый C-стиль), но очистка стека от параметров
+ происходит в самих процедурах. Получается своеобразный гибрид C и pascal
+ стилей - stdcall.
+
+ С помощю ключа /W32C компилятор создает консольный файл под Windows.
+
+ Если при компиляции указывали опцию командной строки /j0 или
+ директиву #jumptomain NONE, то Ваша программа будет компилироваться без
+ использования кода начальной инициализации, описание которого находится в
+ файле startup.h--.
+
+ Код начальной инициализации для программ под Windows имеет следующий
+ вид:
+
+ hThisInst=GetModuleHandleA(0);
+ #ifdef __CONSOLE__
+ hStdOut=GetStdHandle(-11);
+ #endif
+ lpszArgs=GetCommandLineA();
+ #ifdef __environ;
+ environ=GetEnvironmentStringsA();
+ #endif
+ main();
+ ExitProcess(EAX);
+
+ Таким образом, в глобальных переменных hThisInst будет находится
+ handl запущенного файла, а в lpszArgs адрес командной строки Вашего
+ файла. Если Вы в командной строке указали опции /p или /argc или в
+ начале вашего файла есть директивы #parsecommandline TRUE или argc TRUE,
+ то компилятор создаст дополнительный код сделающий разборку этой
+ командной строки на части. Если Вы компилируете консольную программу, то
+ в вашей программе будет еще одна глобальная переменная - hStdOut. В этой
+ переменной хранится handl стандартного вывода (экрана). Если Вы при
+ компиляции программы указали опцию /env, то в глобальной переменной
+ environ хранится адрес переменной окружения программы.
+
+ После завершения работы процедуры main выполнятся процедура
+ ExitProcess, которой в качестве параметра передается регистр EAX. Т.о.
+ Вам для завершения работы программы будет достаточно сделать выход из
+ процедуры main, предварительно загрузив в регистр EAX нужный Вам код
+ возврата.
+
+ Некоторые компиляторы создают DLL, в которых имена экспортируемых
+ процедур имеют такой формат:
+
+ ProcName@8
+
+ В этом имени после символа @ указывается размер стека с
+ параметрами, передаваемых процедуре.
+
+ Объявлять такие процедуры нужно так:
+
+ extern WINAPI "name.dll"
+ {
+ ProcName@8 ;
+ }
+
+ т.е. без круглых скобок. В программе, при обращении к такой процедуре, ее
+ имя надо писать без суффикса @8, т.е. вот так - ProcName(param1,param2);
+Return to contents.
+
+
+
+ 12.8.3 Вызов API процедур по ординалам.
+
+
+ В динамически подключаемых библиотеках (DLL) каждой процедуре, кроме
+ ее имени, соответствует уникальное число, которое называется ординалом. И
+ поэтому, кроме общепринятого вызова API-процедуры по имени, можно делать
+ вызов и по ординалу. Теоретически, при использовании вызова по ординалу,
+ загрузка файла должна происходить быстрее. Так как в выходной файл не
+ будут включены списки имен процедур, вызов которых производится по
+ ординалам, то выходной файл может получиться немного меньшим по размеру.
+
+ Чтобы компилятор создал файл, использующий вызов API-процедур по
+ ординалам, надо сделать две вещи:
+
+ 1. Разрешить компилятору это делать. Для этого надо в опциях командной
+ строки (или в файле C--.INI) указать ключ WO.
+
+ 2. Сообщить компилятору - какой номер ординала соответствует какому
+ имени процедуры. Процедуры, для которых не был указан ординал, будет
+ создан вызов по имени. Установить соответствие имен процедур ординалу
+ можно двумя способами:
+
+ a). Автоматически, с помощью опции командной строки IND=name.dll,
+ по которой компилятор просканирует эту библиотеку и импортирует из
+ нее все имена и ординалы процедур. (Импорт возможет только из
+ библиотек имеющих формат PE).
+
+ b). В ручную указать в объявлении API-процедур и ее ординал. Делается
+ это так: после имени процедуры ставится точка, а за ней указывается
+ номер ординала. Вот пример объявления API-процедуры с указанием ее
+ ординала:
+
+ extern WINAPI "user32.dll"
+ {
+ ............
+ long MessageBoxA.429();
+ ............
+ }
+
+ В библиотеках (DLL), иногда существуют процедуры, для которых не
+ указано их имя, но указан номер ординала. Вызов таких процедур по имени
+ не возможен, но можно это сделать по ординалу (если, конечно Вы знаете,
+ для чего эта процедура и что она делает). Для этого в объявлении
+ API-процедуры Вам надо придумать для этой процедуры уникальное имя и
+ указать реальный ординал. Затем в программе Вы будете обращаться к этой
+ процедуре по вымышленному имени. Но если Вы случайно откомпилируете такой
+ файл без ключа WO, то при запуске этой программы Вы получите сообщение,
+ о том, что данного имени в библиотеке нет.
+
+ К сожалению, нет никаких гарантий того, что номер ординала для данной
+ процедуры не изменится при смене версии динамической библиотеки. Поэтому
+ использовать ординалы надо осторожно.
+Return to contents.
+
+
+
+ 12.8.4 Создание DLL под Windows.
+
+
+ Динамически подключаемые библиотеки позволят получать более
+ компактные программы и ускорить процесс компиляции. К минусам
+ использования DLL можно отнести необходимость наличия самих файлов DLL на
+ запускаемом компьютере и немного увеличивается время запуска программы.
+
+ Для того чтобы процедура стала доступной для других программ надо в
+ исходном тексте перед именем процедуры прописать ключевое слово - _export.
+ Пример:
+
+ void _export testproc()
+ {
+ ....
+ }
+
+ Для того чтобы создать DLL, нужно написать файл, в котором будут
+ процедуры с ключевыми словами _export. Вспомогательные процедуры, которые
+ могут понадобиться для работы основных экспортируемых процедур, объявлять
+ как _export необязательно. Затем этот файл нужно откомпилировать с ключом
+ /dll. В результате Вы получите готовую динамически подключаемую
+ библиотеку.
+Return to contents.
+
+
+
+ 12.8.5 Инициализация DLL при загрузке.
+
+
+ Иногда, для работы процедур из динамических библиотек (DLL), бывает
+ необходимым инициализировать некоторые переменные значениями, зависящими
+ от текущего состояния операционной системы, например, получить дескриптор
+ этой библиотеки.
+
+ Директивой #jumptomain NONE (-j0) управление при запуске передается
+ сразу на процедуру main.
+
+ Во всех остальных случаях генерируется код заглушки и управление на
+ процедуру main не передается. Фактически процедура main в этом случае не
+ нужна.
+
+ Процедура main при создании файлов DLL должна выглядеть немного иначе,
+ чем в других случаях:
+
+ dword main ( dword hInstDLL, reason, reserv )
+ {
+ ...
+ }
+Return to contents.
+
+
+
+ 12.8.6 Компиляция ресурсов.
+
+
+ Встроенный в C-- компилятор ресурсов по своим возможностям уступает
+ специализированным компиляторам ресурсов, но этих возможностей, как мне
+ кажется, будет достаточно для большинства Ваших задач.
+
+ Будет проще перечислить то, что встроенный в C-- компилятор ресурсов
+ не умеет делать. Не обрабатываются операторы ресурсов: VERSION,
+ VERSIONINFO и определяемые пользователем ресурсы. При необходимости,
+ данные, вводимые с помощью этих операторов, можно ввести с помощью
+ оператора RCDATA. У многих операторов ресурсов есть необязательные
+ параметры loading и 'memory'. Поддержка этих параметров не
+ реализована. Встретив эти параметры, компилятор их просто пропустит.
+
+ Заставить компилятор C-- обрабатывать ресурсы можно двумя способами:
+
+ 1. Включить в свой проект директивой #include файл с расширением
+ .rc. Файлы с таким расширением компилятор считает файлом с ресурсами.
+ Файл ресурсов необходимо включать в Ваш проект лишь после включения
+ заголовочных файлов Windows.
+
+ 2. Ресурсы можно располагать в теле исходного текста программы в
+ произвольном месте. Текст ресурсов должен начинаться с директивы #pragma
+ resource start, а заканчиваться директивой #pragma resoutce end.
+ Ресурсы могут быть разделенными на части и эти части можно располагать в
+ любом удобном для Вас месте (глупо располагать ресурсы в блоке
+ комментариев и потом удивляться, почему они не были откомпилированы).
+ Компилятор соберет эти части и откомпилирует.
+
+ Имена операторов можно писать как большими, так и маленькими буквами,
+ но имена идентификаторов чувствительны к регистру. В тексте ресурсов
+ можно использовать директивы и комментарии.
+
+ Ничто не мешает Вам использовать компиляторы ресурсов от других
+ языков. Главное, чтобы синтаксис файла ресурсов соответствовал выбранному
+ компилятору.
+Return to contents.
+
+
+
+ 12.9 Выходные файлы для MeOS.
+
+
+ Исполняемые файлы для операционной системы MenuetOS поддерживаются
+ компилятором совсем недавно. Для того, чтобы откомпилировать файл для
+ MenuetOS, нужно в опциях компилятору указать /meos. Вы получите файл без
+ расширения, который потом можно будет выполнить в среде операционной
+ системы MenuetOS.
+
+ Если при компиляции файла Вы не указывали опцию /j0 или не
+ использовали директиву #jumptomain NONE, то компилятор будет использовать
+ файл начальной инициализации startup.h--, в котором для операционной
+ системы MenuetOS создан блок инициализации и завершения программы.
+ Завершать выполнение таких программ можно просто выйдя из процедуры main.
+Return to contents.
+
+
+
+13. Приложения.
+
+ 13.1 Поиск включаемых файлов.
+
+
+ Поиск включаемого в вашу программу файла, имя которого объявляется
+ директивой include и заключено в двойные кавычки "", производится
+ компилятором по такой схеме:
+
+ сначала делается попытка открыть файл в текущей директории. Если файла там
+ нет, то далее делается попытка открыть файл в директории указанной
+ директивой #includepath. Если директива не была задана или файла в этой
+ директории не оказалось, то делается попытка открыть файл в директории
+ указанной в командной строке командой /ip=path. Если эта команда не была
+ задана или файла в указанной директории не оказалось, то делается попытка
+ открыть файл в директории указанной в файле C--.INI командой ip=. Если эта
+ команда не была задана или файла в указанной директории не оказалось, то
+ делается попытка открыть файл в директории, на которую указывает переменная
+ окружения C--. Если переменная окружения не была задана или файла в этой
+ директории не оказалось, то делается последняя попытка открыть файл в
+ директории, откуда был запущен компилятор.
+
+ Если имя включаемого файла заключено в угловые скобки < >, то поиск
+ этого файла производится в противоположном направлении, за исключением
+ того, что поиск в текущей директории не производится.
+
+ Для консольной версии компилятора имена главного модуля и включаемых
+ файлов могут иметь длину более 8 символов.
+Return to contents.
+
+
+
+ 13.2 Регистры, которые должны быть сохранены.
+
+
+ Регистры, которые должны сохраняться - BP, DI, SI, DS, SS, SP, CS и IP.
+
+ BP используется как указатель на локальные и параметрические
+ переменные в стеке, что и требует его сохранения.
+
+ DI и SI сохранять не обязательно, если программист осознает
+ последствия. DI и SI часто используются для индексации массивов, как
+ например в формуле:
+
+ dog = firehydrant(1,red) + legs[DI];
+
+ Если DI не сохранялся в процедуре firehydrant, значение, присвоенное
+ переменной dog, скорее всего, будет неправильным, поскольку индекс для
+ массива legs был изменен. В сущности, для точного согласования все
+ процедуры должны иметь специальное указание в комментарии на то, что в них
+ не сохраняется содержимое регистров DI и/или SI.
+
+ DS указывает на сегмент данных, и все операции с глобальными
+ переменными пользуются этим значением.
+
+ SS хранит сегмент стека и должен сохраняться. SP указывает на текущую
+ позицию в стеке и тоже должен сохраняться.
+
+ CS хранит сегмент кода программы. Все команды выбираются с
+ использованием CS и IP, следовательно их значения должны сохраняться. IP,
+ как известно, указатель адреса команды, и CS и IP непосредственно не могут
+ изменяться в процессорах 8086, 8088, 80286, 80386, 80486,...
+Return to contents.
+
+
+
+ 13.3 C--.ini файл.
+
+
+ C--.ini файл предназначен для предустановки по умолчанию параметров
+ компилятора.
+
+ Сейчас компилятор поддерживает огромное число параметров командной
+ строки. Правильное их использование позволит Вам получать более компактный
+ код и может значительно облегчить Вам отладку программы. Но так как этих
+ параметров очень много набирать их каждый раз в командной строке бывает
+ утомительно и не исключена возможность пропустить какой-нибудь параметр.
+ Чтобы избавить Вас от всех этих напастей и был введен c--.ini файл.
+
+ Параметры командной строки прописываются в этом файле построчно.
+ Синтаксис тот же, что и в командной строке, но без ведущего обратного слэша
+ или минуса. Если файл расположен в директории, на которую указывает
+ переменная окружения set c--= или если эта переменная не определена,
+ то в той же директории где и файл c--.exe, то эти параметры
+ распространяются на все компилируемые программы. Если же файл c--.ini
+ расположен в текущей директории, то параметры считываются только из этого
+ файла и действуют только для текущего проекта.
+
+ Допустимо использование комментариев. Признаком начала комментария
+ является символ ;. Все последующие символы после ; и до конца строки
+ считаются комментарием.
+
+ Пример C--.ini файла:
+
+ r-
+ X
+ 3 ;это комментарий
+ os
+
+ ini-файл может иметь любое имя (но расширение должно быть обязательно
+ ini). Имя этого файла с расширением должно быть передано компилятору в
+ командной строке. Файл c--.ini загружается и обрабатывается автоматически
+ до загрузки файла указанного в командной строке.
+
+ Таким образом, файл *.ini можно использовать подобно make-файлу - в нем
+ Вы можете указать и имя главного компилируемого модуля, и все необходимые
+ для его компиляции настройки.
+
+ Как альтернативу c--.ini файлу, параметры командной строки можно
+ прописывать непосредственно в начале главного файла компилируемого проекта,
+ используя директиву pragma option. С одной стороны это обеспечит Вашему
+ проекту независимость от настроек компилятора, если Ваш проект будет
+ компилироваться на другом компьютере. Но с другой стороны некоторые
+ настройки являются индивидуальными для данного компьютера (это расположение
+ библиотек, имена и расположение stub-файлов). Какой вариант использовать
+ решать Вам, но как говорят, и я с этим согласен, лучше пользоваться золотой
+ серединой - Часть параметров прописать в c--.ini файле, а другую
+ непосредственно в компилируемом файле.
+Return to contents.
+
+
+
+ 13.4 startup.h-- файл.
+
+
+ В этом файле находятся исходные тексты, которые компилируются
+ компилятором в код начальной инициализации файла, для всех поддерживаемых
+ компилятором типов выходных файлов. Этот файл должен находится либо в
+ директории вместе с компилятором, либо в директории с библиотечными файлами.
+ Этот файл включается компилятором в проект автоматически, а включение его
+ директивой include может привести к нежелательным результатам.
+
+ В блоке начальной инициализации программы может производится (если Вы
+ это укажете с помощью опций командной строки или используя директивы),
+ разбор командной строки на параметры, сохранение переменой окружения,
+ поддержка работы процедуры ATEXIT, изменение размера доступной памяти для
+ *.com файлов и многие другие подготовительные операции. Если Вы
+ откомпилируете свой файл не используя никаких опций командной строки и у
+ Вас будет отсутствовать c--.ini файл, а в самом компилируемом файле у Вас
+ будут отсутствовать директивы, то при компиляции *.com файла в него будет
+ включен блок изменяющий размер доступной памяти и сигнатура SPHINXC--.
+
+ Если Вы компилируете файл типа *.exe (кроме файла модели tiny для DOS)
+ и используете директиву jumptomain NONE или ключ командной строки /j0,
+ то для этого проекта файл startup.h-- компилятором не используется. Не
+ используется этот файл также при компиляции *.com файлов если, кроме /j0,
+ в этом проекте не используется разбор командной строки (/p /argc), не
+ применяется процедура ATEXIT (/at), не используется адрес переменной
+ окружения (/env), не используется очистка области post-адресов (/cpa), не
+ используется уменьшение доступной программе памяти (/r) и не используется
+ заглушка нажатий CTRL-C (/c).
+
+ Кроме блока начальной инициализации программы в файле startup.h--
+ находятся динамические процедуры:
+
+ void CLEARPOSTAREA( (E)AX ); - очистка post-области данных.
+ unsigned int PARAMSTR( ECX ); - получить адрес элемента командной строки
+ unsigned int PARAMCOUNT(); - получить число элементов в командной строке
+
+ При разборе командной строки на составляющие ее элементы для 32-битных
+ программ реализована поддержка длинных имен. Для 16-битных программ
+ поддержка разбора командной строки с учетом длинных имен подключается, если
+ Вы в начале свой программы укажете директиву:
+
+ #define _USELONGNAME TRUE
+
+ либо в c--.ini файле или в командной строке компилятора укажете опцию
+ d=_USELONGNAME.
+Return to contents.
+
+
+
+ 13.5 mainlib.ldp файл.
+
+
+ В этом файле находится большое число процедур из основной библиотеки
+ компилятора в уже откомпилированном виде. Все процедуры откомпилированы в
+ 4-х различных режимах оптимизации. В этот файл также вынесены многие
+ процедуры, которые ранее были внутри компилятора. Использование ранее
+ откомпилированных процедур повышает скорость компиляции.
+
+ Эти процедуры откомпилированы только для 16-битного режима работы
+ программы. Если Вы будете использовать эти процедуры в 32-битной программе,
+ то компилятор на это не выдаст никаких сообщений и включит эту процедуру в
+ Ваш код. Но при запуске такой программы она неизбежно потерпит крах.
+
+ Использовать эту библиотеку очень просто. Все что нужно, это
+ расположить эту библиотеку в одной с компилятором директории. Тогда
+ компилятор, если встретит в вашей программе вызов процедуры, которая не
+ была определена ни во включаемых в программу библиотечных файлах, ни в
+ вашей программе, будет искать эту процедуру в файле mainlib.ldp. Если эта
+ процедура будет найдена в этом файле, то ее код будет перенесен в Ваш файл,
+ иначе будет выдано сообщение о неизвестной процедуре. Таким образом, чтобы
+ процедура была вставлена в вашу программу из библиотеки mainlib.ldp Вам
+ нужно в свою программу не включать библиотечный файл, содержащий процедуру с
+ таким же именем.
+
+ Список процедур находящихся в этой библиотеке можно получить с помощью
+ специальной программы cmmlib.exe. Эту программу можно найти в архиве
+ cmmlib.rar. Извлеките программу cmmlib.exe из этого архива и расположите ее
+ в одной с компилятором директории. Затем запустите эту программу с ключом
+ /L и Вы получите список процедур находящихся в этой библиотеке.
+Return to contents.
+
+
+
+ 13.6 C-- символы.
+
+
+ SYMBOL|FUNCTION |EXAMPLE
+ --------------------------------------------------------------------
+ /* |начинают блок комментария |/* комментарий */
+ */ |завершают блок комментария|/* комментарий */
+ | |
+ // |комментарий до конца линии|// комментарий
+ | |
+ = |присвоение |AX = 12;
+ + |сложение |AX = BX + 12;
+ - |вычитание |house = dog - church;
+ * |умножение или указатель |x = y * z; AL = * var;
+ / |деление |x1 = dog / legs;
+ & |поразрядное логическое И |polution = stupid & pointless;
+ | |поразрядное логическое ИЛИ|yes = i | mabe;
+ ^ |поразрядн. исключающее ИЛИ|snap = got ^ power;
+ << |битовый сдвиг влево |x = y << z;
+ >> |битовый сдвиг вправо |x = y >> z;
+ | |
+ += |сложение |fox += 12; // fox = fox +12;
+ -= |вычитание |cow -= BX; // cow = cow - BX;
+ *= |умножение |a *= b; // a = a * b;
+ /= |деление |a /= b; // a = a / b;
+ &= |поразрядное логическое И |p &= q; // p = p & q;
+ |= |поразрядное логическое ИЛИ|p |= z; // p = p | z;
+ ^= |поразрядн. исключающее ИЛИ|u ^= s; // u = u ^ s;
+ <<= |битовый сдвиг влево |x <<= z; // x = x << z
+ >>= |битовый сдвиг вправо |x >>= z; // x = x >> z
+ | |
+ >< |обмен значениями |x >< y; /* меняет местами значения x и y */
+ | |
+ == |проверка на равенство |IF(AX == 12)
+ > |проверка на больше чем |IF(junk > BOGUS)
+ < |проверка на меньше чем |if( x < y )
+ >= |проверка больше или равно |if(AX >= 12)
+ <= |проверка меньше или равно |IF(BL >= CH)
+ != |проверка на неравенство |IF(girl != boy)
+ <> |проверка на отличие |if (cat<>dog) /* та же функция что != */
+ | |
+ @ |вставка кода |@ COLDBOOT(); /* вставляет COLDBOOT код */
+ : |динамическая процедура |: functionname () //объявляет functionname
+ $ |ассемблерная команда |$ PUSH AX /* заносит AX в стек */
+ # |получение адреса(смещения)|loc = #cow; /* loc = address of cow */
+ |или директива | #resize FALSE
+ ! |оператор NOT или смена |!x_var; if(!proc())
+ |флага операции сравнения. |
+ ... |любое число параметров в | void proc(...);
+ :: |разрешение видимости | ::var=0;
+Return to contents.
+
+
+
+
+
+
+ |
+
+
+