    0.238 от 28.03.2002


Компиляция ресурсов.
~~~~~~~~~~~~~~~~~~~~~
    Встроенный в С-- компилятор ресурсов по своим возможностям уступает
специализированным компиляторам ресурсов, но этих возможностей, как мне
кажется, будет достаточно для большинства Ваших задач.

    Будет проще перечислить то, что встроенный в C-- компилятор ресурсов не
умеет делать.  Не обрабатываются операторы ресурсов: 'VERSION', 'VERSIONINFO'
и определяемые пользователем ресурсы.  При необходимости, данные, вводимые с
помощью этих операторов, можно ввести с помощью оператора 'RCDATA'.  У многих
операторов ресурсов есть необязательные параметры 'loading' и 'memory'.
Поддержка этих параметров не реализована.  Встретив эти параметры, компилятор
их просто пропустит.

    Заставить компилятор C-- обрабатывать ресурсы можно двумя способами:

    1. Включить в свой проект директивой '#include' файл с расширением '.rc'.
Файлы с таким расширением компилятор считает файлом с ресурсами. Файл
ресурсов необходимо включать в Ваш проект лишь после включения заголовочных
файлов Windows.

    2. Ресурсы можно располагать в теле программы в произвольном месте, но
текст ресурсов должен начинаться с директивы '#pragma resource start', а
заканчиваться директивой '#pragma resoutce end'.  Ресурсы могут быть
разделенными на части и эти части можно располагать в любом удобном для Вас
месте (глупо располагать ресурсы в блоке комментариев и потом удивляться,
почему они не были откомпилированы).  Компилятор соберет эти части и
откомпилирует.

    Имена операторов можно писать как большими, так и маленькими буквами, но
имена идентификаторов чувствительны к регистру.  В тексте ресурсов можно
использовать директивы и комментарии.


Метки вне процедур.
~~~~~~~~~~~~~~~~~~~~
    Метки вне процедур фактически располагаются в области данных программы.
Если данные и код находятся в одном сегменте (а именно так организованна
программа, написанная на C--), то метки вне процедур становятся простым и
эффективным методом для получения расстояний между частями программы.

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


Расширение возможностей указателей.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Теперь указатели можно использовать при передаче параметров процедурам, а
в самих процедурах в качестве как локальных, так и параметрических
переменных.  Указатели можно также использовать в структурах.  Можно
использовать указатели на указатели.  Введена поддержка указателей на
процедуры:

void (*proc)();  //объявление указателя на процедуру

    По умолчанию указатели на процедуру являются указателями на процедуру в
стиле 'pascal', независимо от регистра, в котором написано имя процедуры и
режима компиляции.  Если Вам необходимо, чтобы был использован другой тип
вызова, то его необходимо указать при объявлении указателя на процедуру.

    При инициализации указателей компилятор не контролирует то, чем
инициализируется указатель.  Т.е. Вы можете указателю на 'char' присвоить
указатель на 'int' или указателю на процедуру присвоить адрес переменной.
Это может вызвать ошибку в работе программы.


Объявление процедур в структурах.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    С введение поддержки объявления процедуры в структуре, структура
становится подобной классу в 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' не генерируется.

    Процедура объявленная в структуре может быть динамической.  Для этого,
при ее определении, в самом ее начале, надо написать символ двоеточия ':'
(также как и для обычных динамических процедур).  Но такая динамическая
процедура не может быть использована как макрос.


Наследование.
~~~~~~~~~~~~~~
    В 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;

     являются равнозначными.


Передача параметров в стековые процедуры через регистры.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    При передаче параметров через регистры, чаще всего получается более
компактный и быстрый код.  Но содержимое регистров может быть легко
разрушено.  Если в Вашей процедуре, какой-то из параметров используется
однократно для того, чтобы в начале процедуры инициализировать какой-то
регистр, то Вы можете передать это значение в процедуру сразу через регистр,
минуя стадию засовывания и извлечения содержимого в стек.  Пример:

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' сначала лучше располагать
регистровые параметры.


Присваивание одного значения нескольким переменным.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Если Вам необходимо присвоить нескольким переменным одинаковые значения:

  var1=0;
  var2=0;
  var3=0;

    то теперь это можно записать более коротко:

  var1=var2=var3=0;

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


Ключевое слово 'static' и оператор '::'
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Если перед объявлением глобальной переменной, структуры или процедуры
указать слово 'static', то эти переменная, структура или процедура будут
доступны только в том файле, в котором они были объявлены.  Т.е. если Вы
включите этот файл в другой директивой 'include', то переменные объявленные
во включаемом файле со словом 'static' не будут доступны в основном файле, и
Вы можете в основном файле объявить другие переменные с такими же именами.

    Если Вы примените слово 'static' при объявлении локальной переменной в
процедуре, то память для этой переменной будет выделена не в стеке, а в
области данных процедуры.  Но эта переменная будет доступна только внутри
процедуры, в которой она была объявлена.  Применение 'static' к локальным
переменным дает возможность сохранять значение переменной для следующего
входа в процедуру.

    Слово 'static' можно применять к любому глобальному объекту (переменной,
структуре, процедуре).  Для локального использования это слово можно
применять только к переменным.

    Если в Вашей программе есть глобальная и локальная переменная с
одинаковыми именами, то в процедуре, в которой объявлена эта локальная
переменная, Вы не имели доступа к одноименной глобальной переменной.  Теперь,
применив перед именем переменной оператор '::', Вы получите доступ к
глобальной переменной.  Пример:

int var;  //объявляем глобальную переменную

void proc()
int var;  //объявляем локальную переменную с именем уже существующей
          //глобальной переменной
{
  (e)AX=var;	//имеем доступ только к локальной переменной
  (e)AX=::var;  //а так можно получить доступ к глобальной переменной
}


Замена 'return' на 'goto'.
~~~~~~~~~~~~~~~~~~~~~~~~~~~
    В некоторых ситуациях, при компиляции программы, оператор 'return' будет
заменяться на 'goto'.  Это происходит при разрешенной оптимизации по размеру
кода для операторов 'return', которые расположены внутри процедуры и,
естественно, если размер кода для выполнения 'return' больше, чем размер кода
для реализации 'goto'.  Для динамических процедур, которые используются как
макросы, такая замена будет производится всегда.  Оператор 'goto' будет
выполнен на конец процедуры, там, где будет располагаться единственный выход
из процедуры.  В динамических процедурах, используемых в качестве макросов,
'return' в конце процедуры будет пропущен компилятором.

    Таким образом, снято последнее ограничение на использование динамических
процедур в качестве макросов.  Теперь любая динамическая процедура может быть
использована как макрос.

    Для оператора 'goto' существует его более короткий аналог - 'GOTO'.  Для
получения более компактного кода для оператора 'return' введен также более
короткий оператор 'RETURN'.  Его можно использовать, если от места его
применения до конца процедуры находится не более 128 байт.  Если Вы будете
использовать 'RETURN' на большем расстоянии до конца процедуры, то компилятор
выдаст сообщение об ошибке.  При использовании 'return' на расстоянии меньше
128 байт до конца кода, компилятор выдаст вам предупреждение о возможном
использовании 'RETURN'.


Битовые поля структур.
~~~~~~~~~~~~~~~~~~~~~~~
    Битовые поля структур используются для экономии памяти, поскольку
позволяют плотно упаковать значения, и для организации удобного доступа к
регистрам внешних устройств, в которых различные биты могут иметь
самостоятельное функциональное назначение.

    Объявление битового имеет следующий синтаксис:

<тип> [<идентификатор>]:<константа>;

или на примере:

int var:5;  //объявление битового поля размером 5 бит с именем 'var'

    Битовое поле состоит из некоторого числа битов, которое задается числовым
выражением <константа>.  Его значение должно быть целым положительным числом
и его значение не должно превышать числа разрядов соответствующие <типу>
определяемого битового поля.  В C-- битовые поля могут содержать только
беззнаковые значения.  Нельзя использовать массивы битовых полей, указатели
на битовые поля.

    <идентификатор> именует битовое поле.  Его наличие необязательно.
Неименованное битовое поле означает пропуск соответствующего числа битов
перед размещением следующего элемента структуры.  Неименованное битовое поле,
для которого указан нулевой размер, имеет специальное назначение: оно
гарантирует, что память для следующего битового поля будет начинаться на
границе того типа, который задан для неименованного битового поля.  Т.е.
будет произведено выравнивание битового поля на 8/16/32 бита.

    В C-- все битовые поля упаковываются одно за другим независимо от границ
типа идентификаторов.  Если последующее поле не является битовым полем, то
оставшиеся до границы байта биты не будут использованы.  Максимальный размер
битового поля равен 32 бита для типа dword/long, 16 бит для типа word/int и 8
бит для типа byte/char.  Битовые поля можно объединять, т.е. использовать их
в операторе 'union'.  'sizeof' примененный к битовому полю вернет размер
этого поля в битах.  При использовании битового поля, его содержимое будет
расширятся в регистр как беззнаковое целое число.



    0.237 от 5.11.2001


Использование динамических процедур в качестве макросов.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Теперь любая динамическая процедура может быть использована как макрос.
Если перед вызовом динамической процедуры поставить символ '@', то код этой
процедуры будет вставлен, а не вызван инструкцией CALL.

    В динамических процедурах, которые будут использованы в качестве
макросов, по прежнему нельзя использовать оператор 'return'.

    При использовании стековых динамических процедур в качестве макросов
очистка стека от переданных параметров производится ассемблерной инструкцией
'ADD SP,SIZE_PARAMETRS' сразу после окончания кода вставленного макроса.  По
этому, если эта процедура использовала флаги в качестве возврата, то они
будут разрушены.



Новые ассемблерные инструкции.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    В компилятор добавлена поддержка 19 новых инструкций MMX расширения и 46
инструкций SSE расширения.  Многие из этих инструкций могут использовать в
качестве операнда 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
}
}



Автоматический выбор разрядности регистров.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    При создании библиотечных процедур очень часто приходится писать варианты
процедуры для работы в 16-битном и 32-битном режимах, которые отличаются друг
от друга лишь использованием в них либо 16-битных либо 32-битных регистров
соответственно.  Теперь можно писать лишь одну процедуру, используя в ней
новый синтаксис регистров. Если компилятор встретит вот такой синтаксис:

  (E)AX=0;

    то компилятор будет использовать при компиляции 16-битного кода регистр
AX, а при компиляции 32-битного кода регистр EAX.

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



Диапазоны значений для оператора case/CASE.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Для оператора 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;
};

    Кроме того, что новый формат записи более компактен и более читабелен, но
еще при этом компилятор создает более компактный и быстрый код.



Сохранение адреса переменных окружения.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Если при компиляции программы Вы в командную строку добавите опцию -ENV
или в файл c--.ini строка ENV, то компилятор добавит в вашу программу
переменную environ, в которой при загрузке будет сохранятся адрес переменных
окружения запускаемой программы.  Для программ под Windows это будет полный
адрес, а для остальных в этой переменной будет сохраняться только адрес
сегмента.



Возврат флагов из процедур.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Многие 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 )...   // результат процедуры подвергается дальнейшему
                        // вычислению, в результате которого флаги будут
			// изменены.



LE формат exe-файлов для DOS32.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    При использовании LE формата exe-файлов под DOS32, по сравнению с
использовавшимся ранее механизмом, отпадает необходимость в промежуточном
загрузчике, что увеличивает скорость загрузки и уменьшает размер файла (для
очень больших файлов, из-за наличия таблицы перемещений, размер файла нового
формата может быть размером больше предыдущего).  Так как LE формат является
стандартным, то теперь возможно использовать почти любой stub, понимающий
этот формат.  Файлы LE формата можно сжимать программами типа UPX.EXE и ей
подобными.

    Если Вы используете stub, который затем загружает DOS4GW.EXE, то начало
Вашей программы должно иметь специальную сигнатуру.  Компилятор автоматически
сформирует ее, если Вы в командной строке или в c--.ini файле укажете ключ
/DOS4GW.  Такой ключ Вам необходимо будет применять, если Вы будете
использовать в качестве stub 4gs.exe.



Ассемблерный листинг блока программы.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

#pragma option lst
...
#pragma option lst-

можно выделить фрагмент исходного текста программы, для которого компилятор
создаст ассемблерный листинг.  Число таких блоков не ограничено.



Расширение возможностей при вычислении в регистры.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Для выражений осуществляющих вычисление выражения в 16 и 32-битные
регистры увеличено число доступных операций.

    Наряду с использовавшимися ранее операторами += -= &= |= ^= теперь
возможно использовать и *= /=.  Если правая часть такого выражения в свою
очередь будет состоять из нескольких операторов, то компилятор сначала
вычислит эту правую часть, а затем будет произведена операция с присвоением.

    Вызов процедур теперь возможен в любом месте выражения (раньше это было
допустимо лишь в начале выражения).

    Я надеюсь, что теперь значительно реже Вам будут досаждать сообщения о
невозможности использовать операцию в не AX/EAX регистрах.



Оптимизация оператора 'switch'.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Оператор 'switch' сейчас в компиляторе может реализовываться двумя
способами: табличным и методом последовательных проверок.

    Табличный метод является самым быстрым, а при большом числе операторов
'case' и при незначительной разнице между максимальным и минимальным
значениями 'case' он еще может быть и более компактным.  Но у него есть и
недостатки: в 16-битном режиме компилятор всегда использует регистр BX, а в
32-битном режиме, если операндом 'switch' является регистр, то его значение
будет разрушено.

    Метод последовательных проверок использовался компилятором ранее, но в
этой версии он немного изменился: блок сравнений перенесен в начало тела
оператора 'switch', это позволило избавиться от 1-2 лишних 'jmp'.  Но теперь
компилятор не может определить, какой тип перехода использовать при проверке
значений 'case'.  Теперь это будет Вашей заботой.  Если размер кода от начала
тела оператора 'switch' до места расположения оператора 'case' меньше 128
байт, возможно использовать короткий переход.  В этом случае Вы можете
указать оператор 'CASE', что приведет к генерации более компактного кода.
Компилятор в предупреждениях будет Вам подсказывать о возможности
использования операторов 'CASE'.  Использование оператора 'CASE' в случаях,
когда размер блока кода более 128 байт приведет к выдаче компилятором
сообщения об ошибке.

    При оптимизации кода на размер, компилятор предварительно вычисляет
размер кода, который может быть получен обоими методами и реализует самый
компактный.  При оптимизации на скорость преимущество отдается табличному
методу, если размер таблицы получается не слишком большим.

    Для оператора 'switch' введена также и короткая его форма - 'SWITCH'.  Ее
можно применять в случае, если размер блока кода между началом тела оператора
и оператором 'default' (если он отсутствует, то концом тела оператора
'switch') меньше 128 байт.  О возможности использования короткой формы
компилятор будет сообщать в предупреждениях.



Оптимизация кода при сравнениях.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    При проверке равенства или не равенства нулю, если левая часть выражения
сравнения является составной, происходит вычисление этого выражения в регистр
AL/AX/EAX, а затем производится сравнение этого регистра с нулем. Но если
последняя операция вычисления выражения устанавливает или снимает флаг нуля
(ZF) в соответствии с результатом, то нет необходимости в последующем
сравнении. Именно это рассуждение стало основным для получения более
компактного и быстрого кода при операциях сравнения. Вот как это выглядит на
примере:

//исходный пример
#jumptomain NONE
word a,b;
main()
{
  IF ( a + b != 0 ) a = 0 ;
}

    Последняя 0.236 версия компилятора создает следующий код:
SPHINX/SHEKER C-- One Pass Disassembler. Version 0.236 Feb 13 2001

test.c-- 7: IF ( a + b != 0 ) a = 0 ;
0100 A11201                   mov     ax,[112h]
0103 03061401                 add     ax,[114h]
0107 85C0                     test    ax,ax
0109 7406                     je      0111h
010B C70612010000             mov     word ptr [112h],0
0111 C3                       ret

    Новая версия создает более компактный код:
SPHINX/SHEKER C-- One Pass Disassembler. Version 0.237.1 Feb 18 2001

test.c-- 7: IF ( a + b != 0 ) a = 0 ;
0100 A11001                   mov     ax,[110h]
0103 03061201                 add     ax,[112h]
0107 7406                     je      010Fh
0109 C70610010000             mov     word ptr [110h],0
010F C3                       ret



Проверка битов при операции сравнения.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Если в левой части выражения сравнения написано: AX & 5, то при
вычислении выражения содержимое регистра AX будет изменено инструкцией 'and'.
Но иногда возникает необходимость в проверке битов без изменения содержимого
регистра AX. Для этих целей надо использовать инструкцию 'test'. Как же
указать компилятору в каких ситуациях использовать инструкцию 'and', а в
каких 'test'? В стандартных языках Си для этого используется механизм
приоритетов - если выражение заключено в скобки, то производится его
вычисление, если нет, то производится проверка. Но C-- не поддерживает
приоритетов. Для разрешения этой проблемы в C-- решено использовать
непосредственно саму инструкцию 'test'. Вот допустимые варианты синтаксиса:

IF ( $test AX,5 )
IF ( ! $test AX,5)
IF ( asm test AX,5)
IF ( ! asm { test AX,5 } )




    0.236 от 13.02.2001


Создание ассемблерного листинга.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    С помощью дополнительной  опции командной строки  -lst Вы можете  получить
вместе с  исполнительным файлом  и его  ассемблерный листинг.   Листинг  будет
помещен  в  файл  одноименный  с  исполнительным  файлом  и имеющим расширение
*.lst.

    Ассемблерный листинг  создается независимой  от компилятора  частью кода с
использованием  информации  накапливаемой  при  компиляции  программы. Все это
привело  к  незначительному  росту  размера  компилятора  и  снижению скорости
компиляции.


Расширение возможностей при операциях сравнения.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    В  операциях  сравнения  в  левом  операнде  теперь допустимо использовать
вычисления  выражения  с  присваиванием  и  операции  инкремента,  декремента.
Например:

  IF (i=a+2 != 0 )...
  IF ( i++ )...
  IF ( a-- )...
  IF ( i+=4 == 0 )...

    Во  всех  этих  примерах  сначала  произойдет вычисление выражения в левой
части операции  сравнения, а  потом будет  произведено сравнение  результата с
правой частью выражения сравнения.


Отображение тега структуры на блок памяти.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Отображение  тега   структуры  на   блок  памяти   является  альтернативой
указателям на  структуры. Фактически,  в целом,  в обоих  этих случаях  должен
генерироваться одинаковый  код, но  при использовании  указателей на структуры
(гипотетическом,  т.к.  они  в  C--  не  реализованы) Вы не будете знать какой
регистр  будет  использовать  компилятор  (а  без  использования  регистра  не
обойтись). Да  и компилятор  не может  знать, будете  ли Вы  использовать этот
регистр, поэтому ему придется при каждом обращении к элементу структуры  через
указатель каждый раз заново загружать в регистр адрес этой структуры.

    Альтернативный способ использования  указателей на структуры  позволит Вам
самим  выбрать  регистр,  в  котором  будет  хранится  адрес структуры и самим
следить  за  его  сохранностью  и  по  мере  необходимости восстанавливать его
содержимое.

    Объяснить, как использовать отображение тега структуры на память,
наверное, будет проще на примере:

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;
  return BX.AA.c;  // вернуть содержимое элемента 'c'
}

    В  16-битном  режиме  для  хранения  адреса  структуры  можно использовать
регистры:  BX,  DI,  SI,  BP.  Но  лучше  для  этого  использовать регистр BX.
Регистры  DI  и  SI  может  использовать  компилятор  при  вычислении   адреса
многоэлементных  объектов.  Регистр  BP  компилятор  использует  для  работы с
локальными  и   параметрическими  переменными.   В  32-битном   режиме   можно
использовать  любой  кроме  ESP  и  EBP  регистр,  а  регистры  EDI и ESI надо
использовать осторожно.


Работа с элементами структуры.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Для  отдельных  элементов  структуры  стало  возможным  получать их адрес,
размер и смещение в теге структуры. Вот пример:

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'
}


Динамические переменные и структуры.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Наряду  с  уже  известными  Вам  динамическими процедурами в C-- появилась
возможность использовать  динамически и  переменные и  структуры. Динамические
переменные  и  структуры  обозначаются  также  как  и динамические процедуры -
символом двоеточия  перед началом  их объявления.  И также  как и динамическая
процедура, динамическая переменная или  структура будет вставлена в  код, лишь
в том случае, если она будет использована в программе.

    Динамические  переменные  и  структуры  найдут  применение  в библиотеках.
Использовать их непосредственно в программах нет смысла.

    У динамических  переменных, структур  также как  и у  процедур, есть  один
недостаток  -  Вы  не  сможете  знать  в  каком  месте  программы  они   будут
расположены, и в каком порядке.

    Динамические  инициализированные  переменные  и  структуры  в  файле будут
расположены  в  его   самом  конце,  после   динамических  процедур.  Эту   их
особенность можно  использовать, если  Вам будет  необходимо, чтобы  данные не
были разбросаны среди кода, а были сгруппированы в одном месте.


inline-процедуры.
~~~~~~~~~~~~~~~~~~

    inline-процедурами  могут  быть  динамические  процедуры,  которые   можно
использовать  как  макросы.  Но  в  отличие от макросов, inline-процедуры, при
включенной оптимизации  на скорость,  автоматически вставляются  в код,  а при
оптимизации кода на размер, делается вызов их, как динамических процедур.

    Но иногда бывает  нужно при включенной  оптимизации на размер  кода, чтобы
процедуры вставлялись в  код, а не  делался их вызов.  Для этих целей  введена
директива  #inline  TRUE.  Этой  же  директивой  (  #inline FALSE ), можно при
оптимизации на скорость делать вызовы процедур, вместо их вставки.

    Важно помнить,  что статус  директивы #inline  автоматически меняется  при
смене  режима  оптимизации.  При  установки  оптимизации  на  скорость  статус
директивы #inline устанавливается  в TRUE, а  при смене режима  оптимизации по
размеру кода,  устанавливается в  FALSE. Поэтому  применяйте директиву #inline
лишь после смены режима оптимизации.

    Еще одно  изменение в  компиляторе: директивы  меняющие режим  оптимизации
#codesize,  #speed   и  директива   #inline,  объявленные   внутри   процедуры
распространяются только  на оставшуюся  часть процедуры,  т.е. они  становятся
локальными.  Для  того  чтобы  изменения  были  глобальными эти директивы надо
объявлять вне тела процедуры.

    Не  всякая  процедура  может  быть  inline-процедурой.  Для них существуют
такие же, как и для макросов ограничения. Более подробно об этих  ограничениях
будет сказано позднее.

    Для того чтобы определить inline-процедуру, надо в первой строке с  именем
процедуры вместо символа динамической процедуры (':') написать ключевое  слово
'inline'. Пример определения inline-процедуры:

inline int fastcall abs(AX)
{
    IF ( int AX < 0 ) -AX ;
}


Выборочное отключение типов предупреждений.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Сейчас  компилятор  может  выдавать  8  типов  предупреждений и, иногда их
бывает так много,  что становится трудно  в них ориентироваться.  Теперь можно
выборочно запрещать выдачу предупреждений.  Для этого в командной  строке (или
в файле C--.INI) можно  установить опцию /nw=number, где  number - число от  1
до 8. Этим цифрам соответствуют следующие типы предупреждений:

 1 - "Opimization number"
 2 - "Compiler used the register ..."
 3 - "Possible use short operator '...'"
 4 - "Repeated string "...""
 5 - "Expansion variable"
 6 - "Returned signed value"
 7 - "'...' already defined. Skipped"
 8 - "[Variable/Structure/Procedure] '...' possible not used"


Вызов 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', то  при
запуске  этой  программы  Вы  получите  сообщение,  о том, что данного имени в
библиотеке нет.

    К сожалению,  нет никаких  гарантий того,  что номер  ординала для  данной
процедуры  не  изменится  при  смене  версии  динамической библиотеки. Поэтому
использовать ординалы надо осторожно.


Снятие ограничений на динамические процедуры.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Ограничения  на  динамические  процедуры  снижали эффективность и гибкость
библиотечных процедур компилятора. Теперь  с этим покончено. Теперь  Вы можете
из  динамических  процедур  делать  вызовы  других динамических и обыкновенных
процедур, в общем, делать все то, что можно делать в обыкновенных  статических
процедурах.

    Однако для динамических процедур, которые Вы собираетесь использовать  как
макро или inline процедуры остались некоторые ограничения. Вот они:

    1.  в  качестве  макро-процедур  можно  использовать  только   регистровые
       процедуры (процедуры типа 'fastcall').

    2. в макро-процедурах нельзя использовать оператор 'return'.

    3. локальные переменные также нельзя использовать в макро-процедурах.

    Ранее  динамические  процедуры  компилировались  сразу,  а  затем, по мере
необходимости,  готовый  код  этих  процедур  вставлялся  в  необходимые места
программы. Сейчас  компилятор сохраняет  в памяти  исходный текст динамической
процедуры  и,  по  мере  необходимости,  компилирует  ее  в том месте, где она
должна  находится.   Такой  подход   увеличивает  потребность   компилятора  в
оперативной   памяти   и,   возможно,   уменьшит   скорость   компиляции.   Но
преимущества,  которые  дает  такой  подход,  как  мне  кажется,  стоят  таких
незначительных жертв.


Директива #jumptomain (ключи -j0 -j1 -j2).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Директива #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 )
{
  ...
}





    0.235 от 13.11.2000


32-битный stub для DOS программ.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    В  этой  версии  компилятора   произошли  изменения  в  способе   передачи
информации   от   скомпилированной    32-битной   программы   к    stub-файлу.
Соответственно изменился и сам stub-файл - c--stub.exe.

    32-битный DOS-файл имеет  в начале стандартный  'MZ' exe-файл размером  32
байта.  Если  при   компиляции  программы  была   задана  опция   присоединять
stub-файл, то его поле по  смещению 0x14 (CS:IP в стандартном  'MZ' заголовке)
содержит значение регистра EIP,  т.е. адрес точки входа  в программу.  А  поле
по  смещению  0x1C  (в  стандартном  'MZ'  заголовке  не имеет четкой функции)
содержит размер  памяти в  байтах, необходимый  для работы  программы - кода +
post_данные + стек. Stub-файл присоединяется к началу этого файла.

    В этой версии компилятора  введена поддержка блока кода  использующего для
перехода и работы в 32-битном режиме возможности DPMI сервиса. Исходный  текст
этого блока находится  в файле startup.h--  и компилируется, если  в командной
строке  указана  опция  /stub=dpmi  или   в  файле  c--.ini  написать   строку
stub=dpmi.  Недостатком  этого  способа  перехода  и работы в 32-битном режиме
являются   необходимость   обязательного   функционирования   на   запускаемом
компьютере DPMI  сервиса. Также,  так как,  программа загружается  как обычная
DOS программа, и  лишь в процессе  работы переходит в  32-битный режим работы,
размер программы ограничен размером  свободной DOS памяти. Ну  а преимуществом
его является компактный размер исполняемого файла.


Сравнение переменных типа 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
}


Сближение C-- со стандартом на языки Си.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    В этой  версии компилятора  проведен ряд  изменений, сближающий  синтаксис
языка  C--  со  стандартом  на  языки  Си. Некоторые из этих изменений создают
избыточность, но зато Вам будет легче переносить программы в С--.

    1.   Локальные  переменные  теперь  можно  объявлять  и  в  начале   блока
процедуры. Пример:

void proc()
{
int locproc;  // объявление локальной процедуры
  locproc=0;  // а теперь пошло тело процедуры
int locproc;  // а на это объявление переменной компилятор выдаст сообщение
	      // об ошибке, т.к. уже началось тело процедуры
}

    Объявление  локальных  переменных  между  объявлением  имени  процедуры  и
блоком процедуры поддерживается по прежнему.

    2. К числовым константам можно писать суффиксы 'L', 'U' и 'F'.  Фактически
эти  суффиксы  в  C--  сейчас  не  играют  никакой  роли, компилятор их просто
проглатывает. Пример:

#define DEF  1L
#define DEF2 2Lu
#define DEF3 3.0F

    Эти суффиксы не зависят от регистра, т.е. их можно писать как  маленькими,
так и большими буквами.

    3.   Произошли  изменения  в  наборе  типов  данных.  Для совместимости со
стандартом  введены   новые  зарезервированные   слова:  '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-бита.

    Если Вы в 32-битных программах  использовали переменные типов int, то  для
компиляции этих программ новым  компилятором, Вам надо будет  переименовать их
в  тип  short.  Иначе  ваши  программы  могут работать не правильно. К счастью
старый тип int используется в 32-битных программах не так часто. Из  имеющихся
у меня программ  пришлось внести изменения  лишь в три  заголовочных файла для
windows.

    Выражения типа: DSINT[..], ESINT[..] работаю, так же, как и прежде.


Инициализация DLL при загрузке.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Иногда,  для  работы  процедур  из  динамических  библиотек  (DLL), бывает
необходимым инициализировать  некоторые переменные  значениями, зависящими  от
текущего состояния  операционной системы,  например, получить  дескриптор этой
библиотеки. Для  этого в  новой версии  компилятора при  создании DLL  введена
поддержка процедуры 'main'. Управление на эту процедуру передается  однократно
при загрузке библиотеки.

    Если  Ваша  библиотека  не  нуждается   в  инициализации,  и  у  Вас   нет
необходимости использовать процедуру  'main', директивой #jumptomain  NONE или
ключом  командной  строки  /j0   можно  отключить  требования  компилятора   в
необходимости процедуры 'main'.


Создание obj-файлов для 32-битного DOS.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Наконец то сделана  генерация obj-файлов для  32-битного DOS. Для  этого в
командной  строке  Вам  необходимо  указать  ключи  /d32  и /obj. Использовать
полученный obj-файл мне удалось лишь  с помощью wlink и расширителя  zrdx.exe.
Если Вам удастся использовать obj-файл с помощью tlink, или каким либо  другим
способом,  сообщите  об  этом  мне.   Пример  использования  obj-файла   можно
посмотреть в директории EXAMPLE.


Создание и использование отладочной информации.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Если при компиляции программы в  командную строку добавить ключ /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--.

    При  запуске  для  отладки  программ,  отладчик  должен  загрузить  ее   и
выполнить startup код, т.е.  дойти до процедуры main  и там остановиться.   По
каким-то непонятным  причинам, при  отладке программ  под DOS  (за исключением
файлов  откомпилированных   с  ключом   /texe),  отладчик,   только  загружает
программу и  останавливается на  первом байте  startup-кода. С  этим, конечно,
можно смириться, но это очень  не удобно. Чтобы не трассировать  startup-блок,
можно в  отладчике нажать  клавиши 'Alt+V',  затем 'M',  из списка  полученных
модулей,  выбрать  модуль  с  именем  отлаживаемого  файла. В открывшемся окне
установить курсор на  начало процедуры main  и нажать 'F4'.  После этого можно
начать отладку программы.

    Когда я разбирался со структурой отладочной информации под DOS,  создалось
впечатление, что разработчики делали ее с глубокого похмелья. Но, возможно,  я
их просто  не понял.  Если у  Вас есть  подробное описание  этой структуры, то
поделитесь  ею   со  мной.   Это  поможет   улучшить  качество    генерируемой
компилятором  отладочной   информации.  Отладочная   информация  под   Windows
выглядит более логичной и понятной.




    0.234 от 1.10.2000



Объявления API-процедур.
~~~~~~~~~~~~~~~~~~~~~~~~~

    Некоторые  компиляторы  создают  DLL,  в  которых  имена  экспортируемых
процедур имеют такой формат:

   ProcName@8

    В этом имени после символа  '@' указывается размер стека с  параметрами,
передаваемых процедуре.

    C-- теперь может работать с такими DLL. Объявлять такие процедуры  нужно
так:

extern WINAPI "name.dll"
{
   ProcName@8 ;
}

т.е. без круглых скобок. В программе, при обращении к такой процедуре, ее имя
надо писать без суффикса @8, т.е. вот так - ProcName(param1,param2);



Импорт имен процедур из DLL.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Если  Вы  хотите  в  своей  программе  использовать DLL, для которой нет
заголовочного файла с описанием процедур, то компилятор может  импортировать
имена из  этой DLL.  Для этого  Вам надо  указать имя  этой библиотеки  либо
через  опцию  командной  строки  /ind=name.dll,  либо  в  файле  INI строкой
ind=name.dll, либо через директиву #pragma option ind=name.dll.

    К недостатком такого  способа получения имен  можно отнести то,  что при
компиляции   программы   библиотека,   из   которой   импортируются   имена,
обязательно  должна  присутствовать  в  компьютере.  Также,  если  имена   в
библиотеке   написаны   без   суффикса   '@number',   компилятор   не  будет
контролировать  число  параметров  передаваемых  процедуре.  И, к сожалению,
компилятор  умеет  импортировать  имена  из  библиотек имеющих только формат
PE-файла.



Замена stub в программах под windows.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Как известно,  в программах  под windows  есть DOS  заглушка, называемая
stub, которой  передается управление  при запуске  такой программы  в чистом
DOS-е.  Обычно  такая  заглушка  выводит  на  экран сообщение о том, что эту
программу надо запускать в среде windows.

    Вы  можете  вместо  стандартного  stub  использовать свой. Для этого Вам
необходимо  указать  имя  16-битного  EXE-файла  либо  через опцию командной
строки /ws=filename, либо строкой  в INI-файле ws=filename, либо  директивой
#pragma option ws=filename.

    Таким  образом,  у  Вас   появилась  возможность  создавать   программы,
работающие и под DOS и под windows.



Запрет подключения stub к файлу.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    С помощью опции  командной строки /ns,  или строкой в  INI-файле ns, или
директивой #pragma  option ns  можно запретить  подключение stub  теперь и к
программам под windows (раньше это  было возможно только для DOS32).  Запуск
windows  программы  без  stub  в   среде  DOS  приведет  к   непредсказуемым
результатам.



Подключение ресурсов.
~~~~~~~~~~~~~~~~~~~~~~

    Наверное,  многие   уже  заметили,   что  если   к  windows    программе
откомпилированной  с  директивой  #winmonoblock  TRUE подключить ресурсы, то
такая программа, чаще всего, не работает.

    Причиной  этого  явилось  то,  что  программы  подключающие  ресурсы  не
смотрят  на  виртуальный  размер   последней  секции  таблицы  объектов,   а
вычисляют его сами  из физического размера  секции. Поэтому, если  последней
оказывалась секция кода, в которой есть большое число не  инициализированных
данных,   физический   размер   этой   секции   выравненый  на  Object_Align
оказывается меньше чем виртуальный  размер, указанный в поле  Virtual_Size в
таблице объектов, страница  с ресурсами в  памяти пересекалась со  страницей
кода.

    Чтобы  избежать  этого,  для  не  инициализированных  данных   создается
отдельная  секция  .bss,  которая  располагается  самой  первой  в   таблице
объектов.  Тем   самым  у   последующей  секции   кода  виртуальный   размер
оказывается равным  выравненому физическому  и не  происходит пересечения  с
секцией ресурсов.

    Секция .bss  создается автоматически  при компиляции  программ с  ключом
/w32. Если Вы хотите  иметь эту секцию и  при компиляции программ с  ключами
/w32c или /dll Вам необходимо добавить либо в командной строке опцию  /wbss,
либо строку wbss в INI-файле, либо директиву #pragma option wbss.

    Использование секции  .bss практически  не влияет  на размер получаемого
файла.  Теоретически,  для  процессоров,  у  которых  есть отдельный кэш для
данных,  использование   секции  .bss,   должно  повышать   скорость  работы
программы.



    0.233 от 11.09.2000


    При оптимизации на скорость, не спаривающаяся на процессорах Pentium
инструкция MOVZX заменяется на пару инструкций XOR и MOV. Но при этом иногда
возникали ситуации, когда получался ошибочный код. Пример:

   строка исходного текста:

   EBX = DSBYTE [ EBX ] ;

   при этом генерировался следующий код:

   XOR EBX,EBX
   MOV BL,DSBYTE[EBX]

   При выполнении такой код неизбежно вызовет сбой программы.

    В новой версии компилятора такие ситуации отслеживаются и не допускаются.



    Продолжена оптимизация генерируемого кода по размеру и на скорость
выполнения:

 - замена деления на умножение:
     Как известно, деление самая медленная инструкция процессора. При делении
     переменной на число, если его значение не превышает 65535, деление можно
     заменить на более быстро выполняющееся умножение. Пример:

	строка исходного текста:

	 dword a,b;
	 ....
	 a = b / 12 ;

	При этом будет генерироваться следующий код:

	 mov    eax,[b]
         mov    edx,15555556
         mul    edx
         mov    eax,edx
         mov    [a],eax

	Новое числовое значение, на которое будет умножена переменная,
        вычисляется компилятором по следующей формуле:

	  new_const = 0xFFFFFFFF / old_const + 1

	Для вычислений с переменными типов int и char применяется немного
        другой код, но алгоритм остается таким же.

 - сложение двух 32-битных регистров и числового значения производится
   инструкцией LEA. Примеры:

    EBX = EAX + EDX;         - будет получен код LEA EBX,[EAX+EDX]
    EAX = EBX*4+ECX+0x1234;  - будет получен код LEA EAX,[ECX+EBX*4+0x1234]

    Применение инструкции LEA уменьшает размер получаемого кода и увеличивает
    скорость выполнения, однако, для процессоров Pentium она увеличивает
    вероятность возникновения ситуаций AGI с предыдущим кодом.

 - при умножении числовой константы на переменную производится обмен местами
   сомножителей, что приводит к получению более компактного кода.






    0.232 от 6.09.2000


    Начиная с этой версии компилятор  С-- перестает выходить в двух  вариантах
(консольный  и  досовский).  Теперь  будет  только  одна  32-битная  версия  с
расширителем  ДОС.  В  связи  с  этим  увеличиваются  минимальные требования к
компьютеру на  котором способен  работать компилятор.  Эти требования  такие -
386 компьютер с двумя мегабайтами оперативной памяти (но лучше четыре).



		Более подробно об изменениях в компиляторе.


Изменения в параметрах командной строки.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Для  улучшения  взаимодействия  компилятора  с  рабочей  оболочкой (IDE) к
параметрам  командной  строки  добавлены  параметры, выполняющие функции ранее
определенных директив.

    Добавлены два совершенно новых параметра:

/AC  (Align Cycle) - делает выравнивание начала цикла на адрес кратный 8.  Это
     должно  повышать  быстродействие  кода  и  имеут  смысл  использовать для
     процессоров Pentium.

/MIF=<filename>  (Main  Input  File)  -  передает  компилятору  имя   главного
     компилируемого  модуля.  Этот  параметр  предназначен для использования в
     файлах начальной инициализации *.ini.

    Вот полный список параметров командной строки и их описание:

/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         выравнивание адреса начала процедур.
	    по умолчанию отключено, поддерживает инверсию
	    имеет смысл только на процессорах пентиум и лучше
/ARGC       вставить блок разбора командной строки
	    по умолчанию отключено, поддерживает инверсию
/AT         вставить блок поддержки ATEXIT процедуры
	    по умолчанию отключено, поддерживает инверсию
/C          вставить блок игнорирования CTRL<C>
	    по умолчанию отключен, поддерживает инверсию
	    имеет смысл только под DOS программы
/CRI        проверять включаемые файлы на повторную загрузку
	    по умолчанию включено, поддерживает инверсию
/D32        создать EXE файл (32 битный код под DOS)
	    по умолчанию COM
/D=<idname> определить идентификатор для условной компиляции
	    по умолчанию нет
/DE         временное расширение разрядности после умножения
	    по умолчанию отключено, поддерживает инверсию
/DLL        создать DLL для Windows32
	    по умолчанию COM
/EXE        создать EXE файл для DOS (модель SMALL)
	    по умолчанию COM
/HELP /H /? справка, эта информация
/IA         имена ассемблерных инструкций являются идентификаторами
	    по умолчанию отключено, поддерживает инверсию
/IP=<path>  задать путь поиска включаемых файлов
	    по умолчанию нет
/IV         инициализировать все переменные
	    по умолчанию отключено, поддерживает инверсию
/J0         не делать начальный jump на main()
	    по умолчанию отключено, поддерживает инверсию
	    В COM-файлах не создает jmp на main. В остальных не создается блок
	    начальной инициализации программы, а управление передается
	    сразу на main.
/J1         делать короткий jump на main()
	    по умолчанию нет
	    имеет смысл только в COM-файлах
/J2         делать jump на main()
	    по умолчанию да, поддерживает инверсию
	    имеет смысл только в COM-файлах
/LAI        список поддерживаемых ассемблерных инструкций
/ME         показать мой адрес и имя
/MER=##     установить максимальное число ошибок
	    по умолчанию 16
/MIF=<file> определить имя главного компилируемого файла
/NS         запретить подключать stub (для DOS 32bit)
	    если указано имя stub-файла - разрешено
/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
/UST        использовать startup код для переменных.
	    имеет смысл только в COM-файлах
	    по умолчанию нет, поддерживает инверсию
/W          разрешить предупреждения
	    по умолчанию нет, поддерживает инверсию
/W32        создать EXE файл для Windows32 GUI
	    по умолчанию COM
/W32C       создать EXE файл для Windows32 console
	    по умолчанию COM
/WF=<file>  перенаправить вывод предупреждений в файл.
	    по умолчанию нет
/WFA        использовать быстрые вызовы API процедур
	    по умолчанию нет, поддерживает инверсию
	    только под windows
/WFU        создавать таблицу перемещений (для Windows32)
	    по умолчанию нет, поддерживает инверсию
	    только под windows
	    для DLL устанавливается в да
/WIB=#####  установить адрес image base
	    по умолчанию 0x400000
/WMB        создават Windows-файл с единым блоком
	    по умолчанию да, поддерживает инверсию
	    только под windows
	    для DLL устанавливается в нет
/WORDS      выдать список зарезервированных идентификаторов
/X          запретить вставлять в код SPHINXC-- сигнатуру
	    по умолчанию разрешено, поддерживает инверсию
	    отключается если есть J0

Примечание: выражение "поддерживает инверсию" означает, что для данной опции
можно использовать и противоположное значение с помощью символа '-' после
опции.



Файл начальной инициализации *.ini.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Ранее   компилятор   автоматически   искал   и   загружал  файл  начальной
инициализации c--.ini  сначала в  текущей директории  и если  его там  не было
найдено, то в директории, из которой был запущен компилятор. Сейчас этот  файл
может иметь любое имя (но  расширение должно быть обязательно ini).  Имя этого
файла с расширением должно быть передано компилятору в командной строке.  Файл
c--.ini загружается и обрабатывается  по прежнему - автоматически  до загрузки
файла указанного в командной строке.

    Таким образом, файл  *.ini можно использовать  подобно make-файлу -  в нем
Вы можете указать и имя  главного компилируемого модуля и все  необходимые для
его компиляции настройки.



Оптимизация кода на быстродействие.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Для   процессора    Pentium   выполнен    ряд   рекомендаций    повышающий
быстродействие кода программы. А именно:

  - в операциях сравнения регистра с нулем вместо инструкции OR используется
    TEST.
  - заменены не спаривающиеся инструкции CDQ, NOT, NEG, MOVZX, LEAVE на
    спаривающиеся аналоги, там, где это было возможным.
  - при умножении на 2, 4, 8 вместо инструкции LEA используются сдвиги.
  - введено выравнивание начала процедур и циклов (включается опционально из
    командной строки).
  - при передачи параметров стековым процедурам не применяются не
    спаривающиеся инструкции PUSH NUMBER и PUSH VARIABLE.  Их значения
    переносятся в регистр AX/EAX а затем делается PUSH AX/EAX.

    Все эти модификации не применяются для кода генерируемого из ассемблерных
инструкций.



Проблема с созданием DLL.
~~~~~~~~~~~~~~~~~~~~~~~~~~

    Наконец  решена  проблема,  не  позволявшая  полноценно  использовать  DLL
созданные компилятором  C--. Благодаря  исследованиям Denis  Porfiryev (FIDO -
2:5066/68.2) было выяснено, что  при генерации таблицы перемещений  происходит
ошибка,  из-за  которой  и  не  происходила инициализация таблиц перемещений и
импорта. Не  происходит инициализация  этих таблиц  и в  случае, если  таблица
перемещений не  находится в  отдельной секции.  Поэтому при  компиляции DLL не
используйте директиву #winmonoblock TRUE.




    0.231 от 16.07.2000


Поддержка процедур 'ABORT', 'ATEXIT' и 'EXIT' под Windows.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Процедуры  'ABORT'  и  'EXIT'  связаны  с  работой  директивы  #atexit   и
процедурой 'ATEXIT'. Наиболее оптимальную их реализацию и взаимную  интеграцию
может сделать только компилятор.  Именно поэтому эти процедуры  поддерживаются
компилятором. Поддержка этих процедур под DOS уже была произведена ранее.

    Процедура 'ATEXIT' - регистровая процедура, которая регистрирует  функцию,
адрес которой передается ей в качестве параметра, т.е. через регистр EAX,  как
функцию завершения программы. При успешной регистрации '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'  передается  в   качестве
параметра код возврата, с которым она и завершает работу программы.


Директива #pragma strtup.
~~~~~~~~~~~~~~~~~~~~~~~~~~

    Директивой #pragma startup можно указать функцию, которая будет  выполнена
перед запуском процедуры 'main'. Эта директива имеет такой формат:

#pragma startup procname

    Количество раз, которое  можно применять эту  директиву в одной  программе
не ограничено, но реально можно использовать лишь несколько тысяч раз.


Расширение области использования символа '$'.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Символ '$',  кроме того,  что является  признаком последующей ассемблерной
инструкции,  в  языке  C--,  как  и  в языке Assembler может указывать текущий
адрес  (смещение)  компилируемой  программы.  Но  в  C--  он имел ограниченные
возможности. Он мог быть использован лишь как аргумент в операторах  GOTO/goto
и ассемблерных инструкциях  DW/DD/JMP и должен  был быть обязательно  первым в
вычисляемом выражении.

    Сейчас этот символ может  находиться в любом месте  вычисляемого числового
выражения и может  быть применен в  любом месте совместно  с другими числовыми
выражениями.


Расширения области использования оператора 'sizeof'.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Если оператору 'sizeof' в качестве параметра указать имя переменной, то  в
программу будет вставлен размер памяти, отведенный под эту переменную  (раньше
вставлялся размер типа переменной).

    Обращаю  Ваше  внимание  на  изменения,  произошедшие  при   использовании
оператора  'sizeof'  с  именем  структуры.  Сейчас  такое  выражение вставляет
фактический  размер  памяти,  занимаемый  структурой (раньше вставлялся размер
тега структуры). Это особенно важно, если Вы объявили массив структур.

    Оператор  'sizeof'  примененный  к  тегу  структуры  как и раньше, вставит
размер этого тега.

    Оператор  'sizeof'  теперь  можно  применять  и к имени определенной ранее
процедуры. Результатом будет размер этой процедуры.


Проблемы с компиляцией DLL.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    При использовании  DLL, написанных  на C--,  выяснилось, что  при загрузки
этих  библиотек  происходить  инициализация  только  таблицы экспорта. Таблицы
перемещений и импорта, по непонятным мне причинам не инициализируются. Я  буду
рад любой помощи от Вас, позволившей решить эту проблему.

    Я  предполагаю,  что  эту   инициализацию  производит  код   инициализации
библиотеки.  Хотелось   бы  увидеть   документальное  подтверждение   этого  и
подробное описание этого процесса.

    Эта проблема накладывает,  очень надеюсь что  это не надолго,  ограничения
на  использование  DLL  написанных  на  C--.  А  ограничения  эти  вот такие -
процедуры, находящиеся, в DLL должны соответствовать всем требования,  которые
предъявляются к динамическим процедурам, т.е.  они не должны содержать в  себе
вызовов других процедур и не обращаться к глобальным переменным. Они также  не
должны включать в себя строковые константы.






    0.230 от 13.06.2000


Файл конфигурации c--.ini
~~~~~~~~~~~~~~~~~~~~~~~~~~

    Теперь компилятор ищет файл  конфигурации сначала в текущей  директории и,
если его там не окажется, то в директории, где расположен сам компилятор.  Это
позволит использовать файл конфигурации для настройки конкретного проекта.


Упрощенный ввод ассемблерных инструкций.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Стало возможным  использовать ассемблерные  инструкции без  префикса '$' и
вне блока asm. Этот режим включается:  с командной строки опцией /ia; в  файле
конфигурации строкой ia или директивой #pragma option ia.

    Когда этот  режим включен,  все имена  ассемблерных инструкций  становятся
зарезервированными  словами,  т.е.  Вы  не  сможете  эти  имена использовать в
качестве  имен  переменных  или  процедур.  Ассемблерные инструкции компилятор
распознает независимо от того, написаны они маленькими или большими буквами.


Список поддерживаемых ассемблерных инструкций.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Получить  список  поддерживаемых   компилятором  ассемблерных   инструкций
можно, запустив компилятор с опцией /lia.


Директива include.
~~~~~~~~~~~~~~~~~~~

    Для директив include  и includepath пути  теперь записывается без  двойных
символов '\\'.  Для директивы  include появился  альтернативный вариант поиска
файла,  в  котором  поиск  включаемого  файла  происходит  в   противоположном
направлении - сначала  в директории, в  которой находится компилятор,  затем в
директории, на которую указывает переменная окружения C--, затем в  директории
введенной  строкой  ini-файла  ip=path,  затем  в  директории введенной опцией
командной строки /ip=path. Поиск  в текущей директории не  производится. Чтобы
поиск включаемого файла  происходил по этому  алгоритму, имя этого  файла надо
включить в угловые кавычки. Например:

 #include <system.h-->


Синтаксис параметров командной строки.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Опции компилятору в командной  строке теперь можно выделять  кроме символа
'/' еще и символом '-'.  Инвертировать функцию опции можно символом  '-' после
опции. Примеры:

Команды
/on  и  -on  являются допустимыми и функционально одинаковыми.
Для отмены этой опции можно записать:
/on-  или  -on-

    Соответственно изменился  и синтаксис  в файле  C--.INI -  команды в  этом
файле  записываются  точно  так  же  как  и  в командной строке за исключением
первого идентификационного символа '/' или '-'.



Пропуск повторно включаемого файла.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Чаще  всего,   повторно  включать   файл  в   компилируемый  проект,   нет
необходимости, но это иногда  происходит из-за того, что  некоторые включаемые
файлы  сами  включают  другие  файлы.  Чтобы  этого  не происходило приходится
делать проверку на повторную загрузку файла. Теперь эту функцию берет на  себя
компилятор и у Вас отпадает необходимость делать эту проверку.

    Но иногда (очень редко) возникает потребность сделать повторное  включение
файла. Для  этого в  компиляторе есть  опция командной  строки /cri-,  которая
запрещает компилятору делать проверку на повторное включение.  Соответственно,
для  c--.ini  файла,  это  можно   сделать  строкой  cri-  или  директивой   в
компилируемом файле - #pragma option cri-.


Пропуск ключевого слова struct.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Если  Вы  объявляете  структуру  с  уже  ранее  определенным  тегом   этой
структуры, то ключевое слово 'struct', так  же как и в других языках  C, можно
не писать, а указывать сразу имя тега. Например:

//объявляем тег структуры
struct EXAMPL
{
  int a;
  long b;
  char c[6];
};

...

//объявление структуры как было раньше
struct EXAMPL s1;
//сейчас можно делать и вот так
EXAMPL s2;



    0.229 от 06.04.00


Расширение имени файла с главным модулем исходной программы.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Раньше  главный  модуль  компилируемой  программы  мог  иметь расширения
*.c-- или *.cmm. Теперь компилятор поддерживает ещё четыре расширения  имени
файла. Это: *.c; *.h--; *.hmm; *.h. Это позволит Вам использовать  редакторы
от компиляторов  других языков.  Если Вы  в командной  строке укажете только
имя файла без расширения, то компилятор будет пробовать открыть файл с  этим
именем и с последовательно  подключаемыми расширениями в следующем  порядке:
*.c--; *.cmm; *.c; *.h--; *.hmm; *.h.


Масштабирование адреса.
~~~~~~~~~~~~~~~~~~~~~~~

    Для  того,  чтобы  облегчить   выбор  элемента  однотипного  массива   в
компилятор  ранее  было  введено  автоматическое  масштабирование  адреса  в
зависимости  от  объявленного  типа  массива.  Это позволяет при обращении к
элементам  массива   указывать  их   порядковый  номер   вместо  абсолютного
смещения. Это автоматическое масштабирование действует только в случае  если
в  качестве  индекса  используется  переменная  или  какое-то  выражение или
регистр, который  не может  быть использован  в генерации  косвенного адреса
ассемблерной инструкции (для 16-битного  режима это регистры AX,CX,DX).  При
использовании  в  качестве  индекса  числовой  константы  или  регистра  или
комбинации  регистров  из  которой  можно  получить  ассемблерную инструкцию
компилятор  этот  индекс  не  масштабирует  по  типу  массива, т.е. в данных
случаях используется абсолютная адресация в массиве. Например:

word buffer[10];  //массив из 10 элементов типа word
int i;

proc()
{
  ...
  buffer[i]=0;  //переменная i указывает номер элемента массива buffer
  buffer[3]=0;  //число 3 является абсолютным смещением в массива, т.е. нуль
		//будет записан по адресу #buffer+3
  buffer[BX]=0; //регистр BX также содержит абсолютное смещение в массиве
  buffer[AX]=0; //а регистр AX уже будет содержать порядковый номер элемента
		//массива
 ...
}

    Такая  двойственность  объясняется  тем,  что  ранее  в качестве индекса
можно было указывать  только числовое значение  или регистры, которые  можно
использовать  при   косвенной  адресации   в  инструкциях   ассемблера.  Для
совместимости со  старыми программами  это положение  было сохранено,  а все
последующие новые  типы индексов,  которые ранее  не поддерживались,  теперь
используются как номера элементов массива.

    Но это новшество имело и  отрицательный побочный эффект. В C--  элементы
массива buffer[idx] и абсолютная адресация через выражения типа  DSWORD[idx]
разбираются одной процедурой компилятора,  в результате чего и  в выражениях
типа DSWORD[idx] стало  происходить масштабирование, что  является логически
неверным.

    В этой версии в  эту процедуру внесены коррективы  позволяющие разделять
эти выражения, в  результате чего компилятор  стал выдавать более  логически
правильный код.

    Попутно  была  добавлена  возможность  отключать  масштабирование  и при
обращении к  элементу массива  (я не  представляю в  каких случаях это может
пригодится).  Это  можно  сделать   поставив  перед  индексом  символ   '*'.
Например:

  buffer[*i]=0;  //здесь переменная i будет содержать абсолютное смещение в
                 //массиве, а не номер элемента.



    0.228 от 08.03.00

    Новая группа внутренних процедур макросов.
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Использование внутренних  процедур-макросов позволяет  получать во  многих
случаях более  компактный и  быстрый код.  Это объясняется  тем, что  отпадает
необходимость  преобразовывать   передаваемые  и   возвращаемые  параметры   в
промежуточное, удобное для  передачи, состояние. В  этой версии компилятора  к
уже  существующей  группе  процедур  ввода-вывода  в  порты, добавлена большая
группа  математических   процедур  работающая   с  вещественными   числами   и
переменными типа float.

    Вот краткое описание этих процедур:

float atan(float x);    - вычисляет арктангенс числа x.
float atan2(float x,y); - вычисляет арктангенс отношения x/y.
float cos(float x);     - возвращает косинус угла x.
float exp(float x);     - возвращает экспоненту числа x (возводит основание
                          натуральных логарифмов в степень x).
float fabs(float x);    - получить абсолютное значение числа x.
float log(float x);     - вычисляет натуральный логарифм числа x.
float log10(float x);   - вычисляет десятичный логарифм числа x.
float sin(float x);     - возвращает синус угла x.
float sqrt(float x);    - извлекает квадратный корень из числа x.
float tan(float x);     - возвращает тангенс угла x.

    Все  углы  в  этих  процедурах  должны  быть заданы в радианах. В качестве
параметров  этим  процедурам  можно  использовать  как  целые  числа,  так   и
переменные целых  типов. Компилятор  автоматически будет  преобразовывать их в
вещественный тип.


    Слияние одинаковых строковых констант.
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    В  этой  версии  компилятора  введен  новый  режим  оптимизации  - слияние
одинаковых   строковых   констант.   Если   этот   режим   оптимизации   будет
активизирован, то компилятор  будет запоминать все  строковые константы и  при
обнаружении  одинаковых  в  код  файла  не будет вставлена повторная строковая
константа,  а  будет  сделана  ссылка  на первую, обнаруженную ранее строковую
константу. В оптимизации  участвуют только неименованные  строковые константы.
Т.е.  если  массив  или  структура  будет  инициализированы  строкой, то такая
строка не  будет участвовать  в процессе  инициализации, так  эта строка может
быть изменена в процессе работы программы. Пример:

char var="test";  //эта строка не будет участвовать в процессе оптимизации.

void proc()
{
    WRITESTR("test");	//эта строка будет участвовать в оптимизации.
    AX="test";          //переменной AX будет присвоен адрес строки, которая
                        //была вставлена в код программы в предыдущей строке.
}

    Обо всех  случаях обнаружения  повторной строки  компилятор будет выдавать
предупреждения.

    Включается  этот  режим  оптимизации  либо  с  командной строки /ost, либо
директивой #pragma option ost, либо строкой в файле c--.ini - ost.  Отключить,
включенный ранее, этот режим можно директивой #pragma option -ost.


    Установка идентификатора в TRUE из командной строки.
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Если Вы написали  программу, которая может  компилироваться по разному,  в
зависимости  от  состояния   некоторых  идентификаторов  (используется   режим
условной компиляции), то Вам очень может пригодится эта опция. Устанавливая  с
командной  строки  различные  идентификаторы  Вы  можете  получать   различные
варианты программы не редактируя исходный текст программы.

    Идентификатор вводится с командной строки ключом /d=idname.



    0.227 от 09.02.00

    Инициализация локальных переменных при их объявлении:
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Собственно,  мне  кажется,  объяснять  здесь  ничего  не  надо.  Все и так
понятно.   Единственно   скажу,   что   есть   некоторые  ограничения.  Нельзя
инициализировать  массивы  и  многомерные  структуры.  Инициализировать  можно
одним   значением,   т.е   нельзя   при   инициализации  локальных  переменных
пользоваться перечислением заключенным в фигурные скобки и операторами FROM  и
EXTRACT.


    Контроль за числом и типами передаваемых стековой процедуре параметров:
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Как известно,  в C--  контроль за  числом и  типом передаваемых  процедуре
параметров  возлагался  на  программиста.  Поэтому  возникла  непростая задача
совместить одновременно отсутствие контроля за параметрами (для  совместимости
с  предыдущими  версиями)  и  ее  наличие.  В результате компромиссов появился
вариант немного отличающийся от традиционно принятого в языках си.

    Главное  отличие  -  это  то,  что параметры, определяемые при определении
процедуры, не будут восприниматься компилятором для контроля за ними. Во  всех
языках си допускается  совмещение прототипа процедуры  и ее объявления.  В С--
для того,  чтобы включился  контроль за  параметрами стековой  процедуры, надо
эту процедуру обязательно  объявить. Но не  всякое объявление процедуры  будет
сигналом компилятору о включении контроля за параметрами этой процедуры.  Если
при  объявлении  в  круглых  скобках  ничего  не будет, то компилятор не будет
отслеживать  параметры  передаваемые  этой  процедуре.  В C++ такое объявление
означает, что процедуре не передаются никакие параметры. В C-- для этого  надо
при  объявлении  процедуры  в  круглых  скобках  обязательно  написать   void.
Например:

int proc ( void ) ;

Встретив такое  объявление процедуры  компилятор будет  следить за  тем, чтобы
этой процедуре не были переданы параметры.

    При объявлении процедуры имена параметров можно опускать. Как известно,  в
С--  параметры  процедуры  одного  типа  записываются через запятую. Для смены
типа используют точку с запятой.  При объявлении смену типа можно  производить
и после запятой:

void ptoc ( int a, b, c; word d );
void proc ( int, int, int, word );
void proc ( int, int, int; word );

    Все эти примеры объявлений являются идентичными и допустимыми.

    Для  контроля  за  процедурами  с  переменным числом параметров был введен
новый для  С-- элемент  синтаксиса -  многоточие или  его еще называют эллипс.
Вот как будет выглядеть объявление процедуры printf:

void cdecl printf ( word, ... );


    Операторы цикла LOOPNZ/loopnz:
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Циклы LOOPNZ/loopnz  отличаются от  цикла loop,  тем, что  перед входом  в
цикл проверяется равенство нулю аргумента цикла. Если аргумент равен нулю,  то
тело  цикла  ни  разу  не  выполнится  (в  цикле loop в этом случае тело цикла
выполнится максимальное число раз).



    0.226 от 10.01.00

   Устранены ошибки:
 - Выдавалось ложное сообщение об ошибке в выражениях типа:
    intvar = #ucnownlabel + number ;
 - Происходил сбой парсера при разборе выражений типа: $ push 8, AX, var
 - Генерировался неправильный месяц при использовании макроподстановки
   выражения __DATE__.

    Была  добавлена  упрощенная  поддержка  символа  !  -  not  в  операциях
сравнения if/IF for/FOR  while/WHILE.  Фактически  введение этого символа  в
операцию  сравнения  (символ  !  может   быть  только  первым)  приводит   к
инвертированию флага проверки условия. Т.о.  выражения:

  IF ( NOTCARRYFLAG )...   и  IF ( ! CARRYFLAG )...
  IF ( proc() == 0 )...    и IF ( ! proc() ) ...

являются синонимами.

    Также был немного оптимизирован код компилятора.



    0.224 от 28.10.99

    Консольный вариант компилятора с этой версии стал более мощным, чем  его
досовский аналог.  При компиляции  32-битных программ  на консольной  версии
компилятора  размер  выходного  файла  не  ограничивается. Также значительно
увеличены и некоторые другие параметры.


    При компиляции программ  под Windows теперь  все таблицы, которые  умеет
создавать компилятор (а он пока умеет создавать таблицы импорта, экспорта  и
таблицу перемещений) помещаются в одну секцию. Это позволяет получать  более
компактный  выходной  файл.  Если  же  Вас,  по  каким  то  причинам   такая
компоновка файла не устраивает,  то с помощью директивы  #winmonoblock FALSE
Вы будете получать файл со стандартной компоновкой таблиц.


    Добавлена также  директива #pragma,  которая в  свою очередь  имеет свои
директивы.  В  этой  версии   директива  #pragma  поддерживает  всего   одну
директиву -  option. Директива  option позволяет  включить в  Ваш код  опции
командной строки компилятора. Некоторые  опции не могут быть  использованы в
этой директиве; другие  должны помещаться в  самом начале исходного  текста.
Пример:

  #pragma option w32c

    Эта  директива  объявляет  компилятору,  что  надо  создать   консольный
32-битный файл под windows.


    Добавлена опция командной строки позволяющая делать выравнивание  начала
процедур  на  начало  параграфа,  что  должно  повысить быстродействие ваших
программ. Эта  опция работает  только при  компиляции 32-битного  кода.   По
умолчанию эта опция отключена. Включается она так:

  /ap=true


   Макросы __DATE__ и __TIME__ представляют текстовую строку даты и  времени
компиляции вашего файла.


   Для  операторов  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.


    Упрощен  синтаксис  файла  c--.ini.   Теперь  в  этом  файле   допустимо
использование  пробелов   и  комментариев.   Признаком  начала   комментария
является символ  ';'. Все  последующие символы  после ';'  и до конца строки
считаются комментарием. Но по прежнему, каждой опции должна  соответствовать
своя строка.


    Для программ под windows  стала доступной директива #argс,  включающая в
программу  код  разборки  командной  строки.  Соответственно  теперь   можно
использовать и встроенные процедуры PARAMCOUNT() и PARAMSTR().


    0.223 от 05.10.99

                           Вложенные структуры.
			  ~~~~~~~~~~~~~~~~~~~~~

    Теперь при  объявлении тегов  структур можно  использовать теги  других,
объявленных ранее структур. Пример вложенных структур:

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;
}


                 Новый синтаксис шестнадцатеричных чисел.
		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Теперь  C--  вместе  с  традиционным  C-стилем  шестнадцатеричных  чисел
понимает  и  числа  записанные  в  стиле  ассемблера.  Для тех, кто вдруг не
знает,  сообщаю,  что  шестнадцатеричные  числа  в ассемблере имеют на конце
символ 'h' или 'H'. Если первый символ шестнадцатеричного числа больше  '9',
то перед ним обязательно должен быть записан символ '0'. Примеры:

  1234h
  0A000H


                  Сближение синтаксиса с традиционным C.
		 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Для того, чтобы было легче переносить включаемые файлы языка C в C--,  в
синтаксисе директив компилятору теперь  вместо символа '?' можно  использовать
символ '#'.


    0.222 от 19.09.99

                Экспорт процедур и создание DLL под Windows.
	       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    В этой  версии появилась  возможность при  программировании под  windows
делать экспорт  процедур и,  соответственно, появилась  возможность получать
файлы динамически подключаемых библиотек - DLL.

    Динамически подключаемые библиотеки  позволят получать более  компактные
программы и ускорить процесс  компиляции. К минусам использования  DLL можно
отнести необходимость наличия самих  файлов DLL на запускаемом  компьютере и
немного увеличивается время запуска программы.

    Для того  чтобы процедура  стала доступной  для других  программ надо  в
исходнике  перед  именем  процедуры  прописать  ключевое  слово  -  _export.
Пример:

  void _export testproc()
  {
    ....
  }

    Для  того  чтобы  создать  DLL  нужно  написать  файл  в  котором  будут
процедуры  с  ключевыми  словами  _export.  Процедура  main в таком файле не
нужна.  Вспомогательные  процедуры,  которые  могут  понадобиться для работы
основных  экспортируемых  процедур,  объявлять  как  _export  необязательно.
Затем  этот  файл  нужно  откомпилировать  с  ключом  /dll.  В результате Вы
получите готовую динамически подключаемую библиотеку.

    Примечание:  Код  инициализации  DLL  находится  в  файле   STARTUP.H--,
    ~~~~~~~~~~~ поэтому  прежде чем  компилировать DLL  обязательно обновите
этот файл. Он  находится в файле  LIB.ZIP. Так как  я не нашел  документацию
описывающую   процесс   инициализации    DLL,   этот    код   был    получен
интуитивно-экспериментальным путем. Если у Вас есть такая документация,  то,
пожалуйста, пришлите ее мне по адресу sheker@mail.ru.


                 Альтернативное расширение исходных файлов.
		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    С этой версии  компилятор кроме расширения  *.C-- понимает и  *.CMM. Это
сделано потому, что некоторые редакторы не воспринимают расширение C--.


                     Синтаксис объявления API-процедур.
		    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    В  этой   версии  также   немного  изменился   и  синтаксис   объявления
API-процедур.  Теперь  после  имени  процедуры  надо обязательно прописывать
открывающуюся  и  закрывающуюся  скобки  и  точку  с запятой (ранее это было
необязательным). А для  процедур типа fastcall  в скобках можно  указывать и
регистры используемые при передаче параметров.


    0.221 от 10.09.99

              Объявление параметров в регистровых процедурах.
	     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Объявление параметров  регистровых процедур  введено, для  того чтобы  при
вызове  регистровых  процедур  не  надо  было  считать  число  запятых   перед
параметром процедуры.

    Ранее  каждому  параметру  регистровой  процедуры  соответствовал   строго
определенный  регистр.  Например,  для  переменных  типа  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.

    Если  Вы  сделали  объявление  регистров  процедуры,  то  компилятор будет
строго следить за количеством  указанных параметров при вызове  этой процедуры
и выдавать  сообщения об  ошибке, если  их будет  меньше или  больше. С  одной
стороны это  хорошо -  есть контроль  за тем,  что Вы  ничего не забыли или не
добавили  лишнего  при  вызове  процедуры.  С  другой  стороны  иногда  бывают
необязательные параметры, а их теперь придется прописывать.

    Если Вы не объявили регистры  ни при объявлении регистровой процедуры,  ни
в заголовке самой процедуры, то компилятор будет считать, что параметры в  эту
процедуру  передаются  старым  способом.  Таким  образом,  достигается  полная
совместимость с предыдущими версиями компилятора.


    0.220 от 23.08.99

                                  OBJ-файлы.
				 ~~~~~~~~~~~

    Для  получения  ведомых  (slave)  obj-модулей  в  компилятор добавлен ключ
командной  строки  /sobj.  Для  этого  типа  obj-модулей  сняты ограничения на
использование переменных.


                            Директива ?dosstring.
			   ~~~~~~~~~~~~~~~~~~~~~~

    Директива   ?dosstring   определяет   какой   символ   будет  терминатором
(признаком конца) строки.

    Если  ввести  директиву  ?dosstring  TRUE,  то  все последующие символьные
строки будут заканчиваться символом доллара - '$'.

    Если  же  установить  эту  директиву  в  FALSE,  то в качестве терминатора
символьных строк будет использован ноль.

    По умолчанию эта директива установлена в FALSE.


              Директивы для компиляции программ под Windows.
	     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Появилось  несколько  директив,  которые  работают  только  при компиляции
программ под Windows.

    ?imagebase value - задает адрес Image Base. По умолчанию этот адрес  равен
0x400000.

    ?fixuptable TRUE/FALSE  - разрешить/запретить  создание FixUp  таблицы (по
умолчанию  запрещено).  Как  выяснилось,   программы  под  Windows   прекрасно
работают без секции FixUp.

    - ?fastcallapi FALSE/TRUE - запретить/разрешить генерацию быстрого  вызова
API-процедур  (по  умолчанию  разрешено).  Микрософт  рекомендует  такую схему
вызова API-процедур  - делается  CALL на  косвенный JMP,  хотя сам  использует
сразу косвенный  CALL. Это  позволяет получать  немного более  быстрый код,  а
если вызов API-процедуры делается из менее,  чем 5 мест программы, то и  более
короткий код.


                     Консольная версия компилятора.
		    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Консольная  версия  компилятора   работает  только  в   среде  Windows   и
использует библиотеку cw3220.dll из пакета компилятора BC 5.0 или лучше.

    Функционально ДОСовская и консольная версия компилятора пока идентичны.


                         Замена умножения сдвигами.
			~~~~~~~~~~~~~~~~~~~~~~~~~~~

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


                       Оператора объединения - union.
		      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Объединения позволяют  в разные  моменты времени  хранить в  одном объекте
значения различного типа.

    Память,  которая   выделяется  под   объединение,  определяется   размером
наиболее  длинного   из  элементов   объединения.  Все   элементы  объединения
размещаются  в  одной  и  той  же  области  памяти  с одного и того же адреса.
Значение  текущего  элемента  объединения  теряется,  когда  другому  элементу
объединения присваивается значение.

    В C-- реализованы так называемые анонимные объединения. Т.е.  объединениям
не присваивается  имя, а  обращение к  элементам объединения  происходит как к
обычной переменной. Пример:

union
{
  dword regEAX;
  word  regAX;
  byte  regAL;
};  // объявили, что 3 переменные расположены по одному и тому же физическому
    // адресу

void test()
{
	regEAX = 0x2C;
	BL = regAL;	//в регистре BL окажется значение 0x2C
}

    Объединять   можно   переменные   различных   типов,   массивы,  строковые
переменные и  структуры. Объединения  могут быть  глобальными и  локальными, а
также  располагаться  внутри  структур  (пока  в  объединениях внутри структур
нельзя   использовать   структуры).   Глобальные   объединения   могут    быть
инициализированными     и      неинициализированными.      Чтобы      получить
инициализированное  объединение  достаточно  проинициализировать  лишь  первый
элемент объединения. Если же первый элемент объединения не инициализирован,  а
следующие элементы инициализированы, то  это вызовет сообщение компилятора  об
ошибке.


    0.219 от 06.07.99

                                  OBJ-файлы.
				 ~~~~~~~~~~~

    Ранее C--  создавал obj-файлы,  которые могли  быть подключены  к проектам
созданным  на  других  языках,  т.е.  ведомые  (slave)  модули.  Причем из C--
модулей для основного проекта были  доступны только процедуры и эти  процедуры
не должны были использовать глобальные переменные.

    Теперь  же  C--  может  создавать  основной модуль (master), который может
быть слинкован в самостоятельный файл. Это дает возможность упростить  отладку
программ.  Например  Вы  написали  программу,  которая  либо не работает, либо
работает не  так как  надо. Попытка  использовать для  отладки отладчик TD.EXE
осложняется тем, что очень тяжело сориентироваться в машинном коде. Теперь  же
Вы  можете  откомпилировать  этот  файл  с  ключом /obj, а полученный obj-файл
слинковать  tlink.exe  с  ключом  /v.  В  результате  Вы  получите  exe-файл с
включенной в  него минимальной  отладочной информацией.  Запустив этот  файл в
отладчике Вы  увидите в  машинных кодах  названия процедур  и переменных,  что
значительно облегчит отладку.

    Для  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--  процедуры  из  библиотек  на других языках. При объявлении
внешних объектов очень  важно правильно указать  тип процедуры и  ее имя. Если
Вы будете использовать внешние процедуры  написанные на Си то чаще  всего, Вам
нужно будет указывать  модификатор cdecl, а  к имени процедуры  или переменной
добавлять префикс '_'.

    Из основного (master) obj-файла написанного на C-- для других  obj-модулей
доступны все процедуры, глобальные переменные и глобальные структуры.

    Для ведомых (slave) obj-модулей положение пока практически не  изменилось.
Вы по прежнему можете использовать из модулей на C-- только процедуры.  Причем
эти процедуры не должны использовать  глобальные переменные, но зато теперь  в
них  Вы  можете  использовать  внешние  переменные  и делать вызовы как других
процедур, так  и внешних.  Чтобы получить  ведомый obj-модуль  в исходник надо
добавить директиву ?jumptomain NONE или  при компиляции к ключу /obj  добавить
ключ /j0.

    Если полученный obj-файл отлинковать с  ключом /s то Вы получите  map-файл
с подробной информацией. Т.о. отпала необходимость в генерации C--  map-файла,
который  фактически  был  бесполезным.  В  этой версии убрана поддержка ключей
командной строки /-map и /+map.

    С-- может создавать obj-файлы с моделью памяти tiny и small. По  умолчанию
создаются модули  с моделью  tiny. Чтобы  получить obj-файл  с моделью  памяти
small надо запустить компилятор с  ключами /obj и /exe. Теоретически  возможно
получать и 32-битные  obj-модули для ДОС  (я этот момент  не проверял, поэтому
могут  быть  какие-то  недоработки).   Создание  obj-файлов  под  windows   не
предусмотрено.


                  Новые модификаторы типа вызовов процедур.
		 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    В прошлой версии появился модификатор типа вызова процедуры cdecl. В  этой
версии к  нему добавлены  еще три  - pascal,  stdcall и  fastcall. Вот краткие
характеристики этих типов вызовов процедур:

cdecl   Этот  тип  вызова  процедур  является  по  умолчанию  для языка Си. Он
~~~~~~  характеризуется  тем,  что  параметры  процедуры  передаются в порядке
обратном их записи. Очистка стека от параметров производится после  завершения
работы процедуры.  Этот способ  вызова процедур  очень удобен  для процедур  с
переменным числом параметров.

pascal  Этот тип вызова предполагает, что параметры передаются в том  порядке,
~~~~~~~ в котором они записаны  в программе. Освобождение стека от  параметров
производит  сама  вызываемая  процедура.   Этот  тип  вызова  является   более
компактным чем cdecl.

stdcall  Этот тип вызова  является гибридом первых двух. Параметры  передаются
~~~~~~~~  процедуре  в  порядке  обратном,  тому  в  котором  они  записаны  в
программе. Освобождение  стека от  параметров производится  в самой вызываемой
процедуре.

fastcall   Этот  тип  вызова  процедур  предполагает  что  передача параметров
~~~~~~~~~  процедуре   производится  через   регистры,  тем   самым   отпадает
необходимость  освобождения  стека  от  параметров.  Для  этого  типа   вызова
процедуры существуют ограничения по числу передаваемых параметров. Для Си  это
три  параметра,  а  для  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.

    В  C--  по  умолчанию,  если  имя  процедуры написано большими буквами, то
считается,  что  эта  процедура  имеет  тип  вызова  fastcall. Если же в имени
процедуры есть хотя  бы одна маленькая  буква, то по  умолчанию считается, что
эта процедура имеет тип  вызова pascal, за исключением  программ компилируемых
с ключом /w32 или  /w32c. В них по  умолчанию применяется тип вызова  процедур
stdcall. Если же  Вы хотите изменить  тип вызова процедур  из по умолчанию  на
любой другой,  то эту  процедуру надо  обязательно объявить  с указанием  типа
желаемого вызова.


                        Выравнивание данных и кода.
		       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    В   C--   существует   директива   ?align,   которая   делает  однократное
выравнивание данных на  четный адрес. Теперь,  если к этой  директиве добавить
число,  то  выравнивание  будет  произведено  на  адрес  кратный  этому числу.
Например директива ?align 4 дополнит сегмент данных до адреса кратного 4.  При
выравнивании будут вставляться байты значения которых определяются  директивой
?aligner, по умолчанию  это значение равно  нулю. Директива ?align  производит
выравнивание  только  в  сегменте  данных.   В  тех  моделях памяти, в которых
сегмент  данных  и  кода  совпадают  эту  директиву  можно  применять  и   для
выравнивания начала процедур.

    В  этой  версии  добавлена  новая  директива - ?aligncode [value], которая
делает  выравнивание  в  сегменте  кода  на  адрес  кратный значению value, по
умолчанию  на  четный  адрес.  Значение  байта  заполнения  в  этой  директиве
является число 0x90 - код  инструкции NOP. Значение байта заполнения  для этой
директивы  изменить  нельзя.  Т.о.  эту  директиву  можно  применять  и внутри
исполняемого  кода.  Например,  если  Вы  хотите  получить  быстрый код на 486
процессоре, то рекомендуется делать  выравнивание начала процедур и  циклов на
адрес кратный 16. Для Pentium  у меня есть противоречивая информация.  В одних
источниках написано,  что для  Pentium выравнивать  код не  надо, а  в других,
сообщается  о  необходимости  выравнивания  на  адрес  кратный 32. Если кто-то
может уточнить эту информацию, то просьба сообщить об этом мне.


    0.218 от 14.06.99


                          Директива ?startuptomain
			 ~~~~~~~~~~~~~~~~~~~~~~~~~~

    По  этой  директиве  компилятор  в  начале  файла  делает  jmp  на  начало
процедуры main().  Перед началом  компиляции этой  процедуры компилятор начнет
компиляцию startup  кода и  лишь затем  будет продолжена  компиляция процедуры
main(). Тем самым startup код окажется  не в начале файла, как это  происходит
обычно,  а  в  теле  процедуры  main().  Это  будет  полезным  при  компиляции
резидентных программ (TSR).

    Директива ?startuptomain работает только при компиляции com-файлов.


                            Директива ?undef
			   ~~~~~~~~~~~~~~~~~~

    Эта  директива  уничтожает  константы  объявленные  директивой ?define. Её
можно  применять  для  изменения  в  процессе компиляции значения какой-нибудь
константы.


                      Startup-код в файле startup.h--
		     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Наверное  многие  из  Вас  были  несогласные  с тем startup кодом, который
генерил компилятор  и чесались  руки изменить  его. Но  это было очень сложно,
так как этот  код генерил компилятор  и не было  возможности повлиять на  это.
Теперь startup код  вынесен в отдельный  файл - startup.h--.  Этот файл должен
находится в одной директории с остальными библиотечными файлами. Включать  его
директивой ?include в  свою программу не  надо. Компилятор сам  загрузит его в
удобный для него момент.

    Теперь у  Вас появилась  возможность делать  startup код  на свой вкус. Но
без острой необходимости делать это нежелательно.

    Компилятор   управляет   генерацией   startup-код   посредством   создания
различных  констант.  По  окончании   генерации  startup-кода  эти   константы
удаляются директивой  ?undef, так  как они  больше не  нужны. Имена глобальных
меток  и  переменных  изменять  нельзя,  так  как  они  будут   использоваться
компилятором  в   процессе  дальнейшей   компиляции.  Чтобы   выделить   имена
(константы, метки, переменные), которые  нельзя изменять, в их  синтаксисе два
первых символа являются символами нижнего подчеркивания.


                   Префикс повторения инструкций DB/DW/DD
		  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Для ассемблерных  инструкции DB,  DW, DD  введена возможность использовать
префикс повторений dup. Применение этого префикса имеет следующий синтаксис:

  $DW NUMREP dup VALTOREP

NUMREP - число повторов инструкции DW.
VALTOREP - величина, которая будет повторена NUMREP раз.

    В отличие  от аналога  этого префикса  из ассемблера  повторяемую величину
заключать в скобки нельзя.


                        Вызов процедур в C-стиле.
		       ~~~~~~~~~~~~~~~~~~~~~~~~~~

    Для того чтобы можно было делать вызовы процедур в C-стиле в 0.212  версии
был введен префикс имени процедуры c_ , по которому компилятор определял,  что
эту процедуру надо воспринимать как  процедуру в C-стиле. На тот  момент такой
способ  определения  типа  вызова  процедуры  был более простым и оптимальным.
Сейчас,  когда  в  компиляторе  появился  механизм  объявления процедур, стало
возможным приблизить синтаксис к стандартному сишному.

    В  этой  версии   префикс  c_  отменен.   Его  функцию  теперь   выполняет
модификатор cdecl.  Если вы  используете процедуру  в си-стиле  раньше чем она
определена, то эту  процедуру надо обязательно  объявить раньше чем  она будет
использована. Если же  процедура будет определена  ранее ее применения,  то ее
объявлять необязательно. Объявление процедуры имеет следующий синтаксис:

rettype modif procname();

    Первым идет необязательный тип возврата  из процедур. По умолчанию он  для
16-битных программ равен word, а для 32-битных dword. Затем должен идти  также
необязательный  модификатор.  По  умолчанию  все  стековые  процедуры в С--(за
исключением  режима  компиляции  программ   под  Windows,  где  по   умолчанию
действует  стиль  вызова  процедур  stdcall)  имеют  стиль  pascal. Сейчас C--
поддерживает  всего  один  модификатор  -  cdecl, который может изменить стиль
вызова  процедур  из  по  умолчанию  в  си-стиль.  Далее идет имя процедуры со
скобками, которые являются  признаком того что  Вы объявляете процедуру,  а не
переменную. Завершает объявление символ точка с запятой.

    При   объявлении   процедур   в   C--   прописывать   параметры  процедуры
необязательно  (компилятор  пока  не  контролирует  число  и  тип передаваемых
параметров),  но  если  Вы  их  вставите  это  не будет ошибкой, компилятор их
просто проигнорирует.


    0.217 от 24.05.99

                                32-битный код.
			       ~~~~~~~~~~~~~~~

    В  этой  версии  компилятора  появилась  возможность  создавать  32-битные
программы  под  DOS  и  Windows.  32-битный  код  имеет  свои  особенности   и
требования.   Для   разъяснения   их   я   могу   Вам   посоветовать  почитать
соответствующую  литературу.  Из-за  этих  особенностей  практически почти все
процедуры  из   имеющихся  библиотек,   а  процедуры   находящиеся  в    файле
динамических библиотек абсолютно все (так как они откомпилированы в  16-битном
режиме),  не  будут  работать  в  32-битном  режиме.  Вероятно,  Вам предстоит
нелегкий труд в создании новых 32-битных библиотек. Но зато под Windows у  Вас
появился доступ к огромному количеству API-процедур.


    32-битный код под DOS.
    ~~~~~~~~~~~~~~~~~~~~~~~

    Для того чтобы откомпилировать 32-битную программу под DOS надо  запустить
компилятор с ключом командной строки  /d32. Но работа 32-битной программы  под
DOSом невозможна без расширителя DOS. Для C-- был выбран WDOSX.  Полный  пакет
этого     расширителя     DOS     можно      взять     в     интернете      на
http://www.geocities.com/SiliconValley/Park/4493.  Нам  же  из  этого   пакета
нужен будет лишь файл wdosx.dx. Чтобы компилятор знал, где искать этот файл  и
его имя,  надо в  файл c--.ini  прописать строку  stub=path_name_to_stub_file.
Пример:

    stub=c:\c--\wdosx.dx

    Если  не  добавлять  в  c--.ini  эту  строку,  то  компилятор  сгенерирует
32-битный exe-файл, но без расширителя  DOS. Чтобы к этому файлу  прилинковать
расширитель DOS надо  взять из пакета  WDOSX файл stubit.exe  и запустить его,
указав  ему  в  качестве  параметра  имя  откомпилированного файла. Если же Вы
добавили в файл  c--.ini строку с  переменной stub, то  компилятор создаст Вам
уже готовый к употреблению файл. Если в командной строке вместе с ключом  /d32
указать  и  ключ  /ns,  то  строка  с  переменной  stub из файла c--.ini будет
аннулирована, и вы получите файл без расширителя DOS.

    Для  32-битного   DOS-файла  можно   использовать  директивы   компилятора
?parsecommandline  TRUE/FALSE  или  его  расширенный вариант ?argc TRUE/FALSE.
Реализована и  поддержка директивы  ?atexit TRUE/FALSE.  Директива ?initallvar
компилятором  устанавливается  в   состояние  TRUE.  Ни   в  коем  случае   не
устанавливайте эту директиву в FALSE. Иначе Ваша программа не будет работать.


    32-битный код под Windows.
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Для  того  чтобы  откомпилировать  программу,  написанную под Windows надо
запустить   компилятор   с   ключом   командной   строки   /w32.  Затем  нужно
откомпилировать файл  ресурсов (если  он необходим  и существует).  О том  как
писать файл  ресурсов мне  остается отослать  Вас опять  же к  соответствующей
литературе. Для компиляции ресурсов  я использовал компилятор ресурсов  из BC.
Для этой компиляции Вам понадобятся следующие файлы из пакета BC v5.0:

  BRC32.EXE
  BRCC32.EXE
  TLINK32.EXE
  RW32CORE.DLL
  RLINK32.DLL

    Если  Вы  в  своей  программе  используете  вызовы  API-процедур,  то  эти
процедуры надо предварительно обязательно объявить. Объявление процедур  имеет
следующую форму:

extern WINAPI "DLL_name"
{
    returncode procname1
    returncode procname2
    procname3
}

  где:
       DLL_name - имя и расширение dll-библиотеки в которой находятся эти
                  процедуры.
     returncode - тип возврата из api-процедур. По умолчанию он равен dword.

    В принципе можно и нужно создать  файл, в котором будут описаны все  часто
используемые  API-процедуры.  Но  увлекаться  этим  не  стоит,  так как каждое
объявление  API-процедуры  требует  некоторого  количества  памяти, а с этим у
компилятора в последнее время стали появляться сложности.

    Вызовы  API-процедур   очень  часто   используют  в   качестве  параметров
константы. Эти константы описаны в  файлах winuser.h wingdi.h и др.  из пакета
BC 5.0.  Надо будет  эти константы  переписать на  C--. Но  опять же  не стоит
сильно увлекаться этим.

    Программы, написанные под Windows,  имеют одну немаловажную особенность  -
все  параметры  в  стековые  процедуры  передаются  в  обратном  порядке  (так
называемый  си-стиль),  но  очистка  стека  от  параметров  происходит в самих
процедурах. Получается  своеобразный гибрид  ci и  pascal стилей.  Это не  моя
выдумка - такое требование для вызовов api-процедур.

    В С-- существуют некоторые ограничения при написании программ под
Windows. Это:
1. Размер кода и данных не должен превышать 64 килобайт.
2. C-- пока не умеет экспортировать процедуры.
3. Пока не реализована поддержка startup-кода.


    Заключение.
    ~~~~~~~~~~~~

    Наверное,  лучше  моих  объяснений  Вам  помогут  разобраться со всем этим
примеры, которые идут вместе с этой версией компилятора.

    В  компиляторе  также  появилась   директива  ?code32  TRUE/FALSE, которая
разрешает/запрещает генерацию 32-битного кода. С помощью этой директивы  можно
получить и  32-битный com-файл.  Но как  потом можно  будет использовать  этот
файл  я  не  знаю.  Если  у  Вас  будут какие-то идеи по применению 32-битного
com-файла,  то  сообщите   мне.  Пользоваться  этой   директивой  без   особой
надобности я Вам не советую. Это для любителей эксперементировать.



    0.216 от 12.04.99


                   Компиляция кода расширителей ROM-BIOS.
		  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Расширители  ROM-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.
Так же останется не тронутым (таким каким его установил главный БИОС) и стек.
  3. ?dataseg value - этой директивой компилятору сообщается сегментный  адрес
ОЗУ, который может быть использован  вашим кодом. По умолчанию он  равен 0x70.
Этот  адрес  вы  можете  узнать  в  любой  момент считав его из вашего кода по
смещению 4. Например: DS = CSWORD[4];
    Некоторые замечания:
  1. Не забывайте, что в момент инициализации ROM-BIOS ДОС еще не загружен и
соответственно все процедуры использующие вызовы ДОС работать не будут.
  2. Нельзя  завершать работу  программы процедурами  ABORT() или  EXIT() и им
подобным. Работа  расширителя ROM-BIOS  должна завершаться  только выходом  из
процедуры main().
  3. Если директива  ?movedatarom установлена в  FALSE, то будьте  внимательны
при  работе  с  инициализированными  переменными.  Они  в этом режиме доступны
только для чтения, и адресуются через регистр CS.


               Выходной файл *.EXE с моделью памяти tiny.
	      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Фактически код файла *.exe модели tiny ничем не отличается от кода  *.com.
В  сущности  это  тот  же  com-файл  к  которому добавлен 32-байтный заголовок
exe-файла.  Единственное  отличие  возникает  когда  Вы  компилируете  файл  с
директивой  ?resize  TRUE.  В  com-файле  по  этой  директиве  в код программы
добавляется  соответствующий  код,  изменяющий  размер  доступной  памяти.   В
exe-файле  для  этих  целей  будет  скорректирован  заголовок exe-файла. Чтобы
получить exe-файл с моделью памяти tiny, надо запустить компилятор с ключом  в
командной строке /TEXE.


                                 Указатели.
				~~~~~~~~~~~~

    В этой версии сделана попытка научить 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);
	}
}


                 Вычисление в регистры 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. Для
остальных регистров подобное делать нельзя.


                  Исправленные ошибки предыдущих версий.
		 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    1. Директива ?setdinproc не вставляла динамические процедуры из библиотеки
динамических процедур.
    2.  При  генерации  кода  присваивания  содержимого  регистров AL/AX/EAX в
ячейку  памяти  определенной  нижележащей  меткой генерировался неверный адрес
этой ячейки.
    3.  При   вычислении  выражений   с  плавающей   точкой,  если   возникала
необходимость преобразовать число формата  float в целое число,  компилятор не
всегда   автоматически   вставлял   в   код   вызов   процедуры    конвертации
float2numer().
    4.  При  вычислении  значения  переменной  типа  byte или char, если среди
операций вычисления было деление, то компилятор иногда генерил неверный код  -
перед  операцией  деления  не  делалось  обнуление регистра AH (или расширение
знака AL в AH для типа char).



    0.215 от 14.03.99


                  Условные выражения MINUSFLAG и PLUSFLAG.
		 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Для  проверки  бита  знака  в  регистре  флагов  в  компилятор введены два
специальных условных  выражения -  MINUSFLAG и  PLUSFLAG. Выражение  MINUSFALG
принимает  значение  <истинно>  если  бит  SF  (флаг  знака)  регистра  флагов
установлен в 1.  Выражение PLUSFLAG принимает  значение <истинно> если  бит SF
(флаг знака) регистра флагов установлен в 0. Примеры:

1).
    i = a + b ;
    IF ( MINUSFLAG ) i = 0 ; //если i отрицательно - установить его в 0

2).
    do {
      i-=a;
    } while ( PLUSFLAG ) ; //цикл пока переменная i положительна


                           Объявление процедур.
			  ~~~~~~~~~~~~~~~~~~~~~

    Объявление процедур введено  для того, чтобы  сообщать компилятору о  типе
возврата из процедур. Если процедура имеет  тип возврата void или word или  не
участвует в операциях сравнения или присваивания, то такую процедуру можно  не
объявлять.


                Изменение типа выражения при присваивании.
	       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Как  известно,  в  C--  при  вычислении значения переменной все переменные
участвующие  в  процессе   вычисления  (т.е.  находящиеся   справа  от   знака
равенства)  преобразуются  к  типу  вычисляемой  переменной. Теперь если после
знака равенства написать тип отличный  от типа вычисляемой переменной, то  все
переменные участвующие в процессе вычисления, будут преобразовываться к  этому
новому типу, и лишь конечный  результат будет преобразован к типу  вычисляемой
переменной. Пример:

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.


                              Прочие изменения.
			     ~~~~~~~~~~~~~~~~~~

    Из  компилятора  убрана  поддержка  ключей  командной  строки /MACRO /PROC
/REGPROC, так как в них отпала необходимость.

    В  некоторых  ситуациях  при  компиляции  происходит переполнение стека, в
связи с  этим в  этой версии  компилятора размер  стека увеличен  в два  раза.
Теперь он равен 8192 байт.

    В библиотеку добавлено несколько процедур:

long filelength(word handle) - возвращает  длину файла (в отличие от  процедур
	      FILELENGTH  и   FILELENGTH32  положение   указателя  файла    не
	      меняется).

long  lseek(word  handle;  long  offset;  byte mode) - устанавливает указатель
	      файла  в  заданную  позицию.  В  отличие от аналогичных процедур
	      FSEEK  и  LSEEK  новая  позиция  указателя  передается  как одна
	      переменная типа long (отпадает необходимость разбивать  смещение
	      на две составляющие).

long tell(word handle) - возвращает текущее положение указателя файла.

long ATOL(,strofs) - преобразует строку в число типа long.

void  BYTE2STRBIT(val,strbuf)  -  преобразует  байт  в  строку  битов (нулей и
	      единиц).  Переменная  strbuf  должна  содержать адрес буфера для
	      строки размером не менее 9 байт.



    0.214 от 21.02.99


                      Библиотека динамических процедур.
		     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    В  этой  версии  компилятора  добавлена  поддержка библиотеки динамических
процедур. Сейчас компилятор может работать только с одной библиотекой  имеющей
название  MAINLIB  и  имеющей  расширение  LDP  (Library  Dynamic Procedures).
Библиотека должна находиться в одной директории с компилятором.

    Вы спросите - какие преимущества дает эта библиотека, по сравнению с
традиционным способом работы?
    Во первых отпадает необходимость писать многочисленные инклюды (не знаю
как Вас, а меня это немного раздражает).
    Во вторых, теоретически, должно уменьшиться время компиляции программ (я
не проверял, но кому интересно проверьте).
    Ну и в третьих, уменьшается загрузка процессора и потребность компилятора
в ресурсах.

    В библиотеку  перенесены почти  все внутренние  процедуры. Оставлены  лишь
макросы  для  работы  с  портами,  процедуры  для  работы  с командной строкой
PARAMCOUNT, PARAMSTR и процедуры  завершения работы ABORT, EXIT.  В компилятор
добавлена  поддержка  процедуры  ATEXIT.  В  библиотеку  добавлены  почти  все
динамические  процедуры  из  файлов  *.h--.  На  сегодняшний  день  библиотека
содержит 431 процедуру. Для работы с библиотекой написана утилита  CMMLIB.EXE.
Утилита  позволяет  получать  листинг  всех  процедур,  добавлять  и   удалять
процедуры.
    Процедуры  добавляются   к  компилируемому   файлу  после   окончания  его
компиляции,  поэтому  во  время  компиляции  компилятор  не  может  знать  тип
возврата  из  процедур  (как  Вы  знаете  в  C--  не  реализованы   объявления
процедур). Это иногда может привести к неверным результатам.
    Например:
    В библиотеке есть процедура GETVIDEOMODE которая имеет тип возврата  byte.
    Но на строку IF(GETVIDEOMODE()  == 0x54)... компилятор сгенерит  сравнение
    регистра  AX  (по  умолчанию  компилятор  считает, что процедура имеет при
    возврате тип  word). В  таком случае  надо или  явно указать  тип возврата
    процедуры - IF(byte GETVIDEOMODE()  == 0x54)... или до  использования этой
    процедуры сделать ее объявление-установку строчкой @byte  GETVIDEOMODE();.
    Также,  если  это  регистровая  процедура,  то  можно  использовать ее как
    макрос:  IF(@GETVIDEOMODE  ==  0x54)...  Тогда  компилятор  будет вынужден
    достать из библиотеки эту процедуру и тем самым узнает ее код возврата.


                         Поддержка процедуры ATEXIT.
			~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Процедура  ATEXIT   регистрирует  процедуру   адрес  которой    передается
процедуре ATEXIT  в качестве  параметра, как  процедуру завершения  программы.
Эта процедура будет  вызвана в момент  завершения программы процедурами  ABORT
или EXIT или инструкцией RET из main.
    Всего  можно  зарегистрировать  до  16  процедур.  Процедуры  вызываются в
порядке обратном порядку их регистрации.
    Поддержка процедуры ATEXIT включается директивой компилятору ?atexit.



    0.213 от 1.02.99


                  Символ '$' в ассемблерных инструкциях.
		 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Теперь  в  ассемблерных  инструкциях,  так  же  как  и в ассемблере, можно
использовать символ '$'. При компиляции  этот символ будет заменен на  текущее
значение  регистра  IP.  Его   можно  использовать  при  вычислении   числовых
значений, зависящих  от текущего  адреса, но  символ '$'  можно использовать в
них однократно и он должен быть первым. Пример:

  $JMP SHORT $+2   //передать управление на следующую команду
  $DW $		   //записать в слово текущий адрес


                       Макросы работы с портами.
		      ~~~~~~~~~~~~~~~~~~~~~~~~~~~

    В  компилятор  добавлена  поддержка  макросов: inp(), inpotr(), inportb(),
inportd(),  outp(),  outport(),  outportb(),  outportd().  Они  должны   стать
альтернативой  библиотеки  PORT.H--  и  позволят  получать  более  быстрый   и
компактный код.
Синтаксис:
    byte inp(potr)      - считать один байт из порта
    byte inportb(port)  - считать один байт из порта
    word inport(port)   - считать слово из порта
    dword inportd(port) - считать двойное слово из порта
	   port -  слово с  адресом порта  является необязательным параметром.
		  Если его величина не задана, то будет сгенерирована  команда
		  in  al,dx.  Если  величина  port  будет меньше 256, то будет
		  сгенерирована команда in al,port.
        outp(byte val,word port)      - записать байт в порт
    outportb(byte val,word port)  - записать байт в порт
    outport(word val,word port)   - записать слово в порт
    outportd(dword val,word port) - записать двойное слово в порт
	   val - записываемое значение
	   port -  слово с  адресом порта  является необязательным параметром.
		  Если его величина не задана, то будет сгенерирована  команда
		  out dx,al.  Если величина  port будет  меньше 256,  то будет
		  сгенерирована команда out port,al.


                Альтернативный обработчик командной строки.
	       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Альтернативность  этого  обработчика  командной  строки заключается в том,
что при вызове PARAMSTR(0); Вы получите  адрес строки в которой указан путь  и
имя  запущенной   программы.  Альтернативный   обработчик  командной    строки
включается директивой ?argc  TRUE или из  командной строки компилятора  ключом
/+argc или строчкой +argc в файле C--.INI.


               Директивы управляющие размещением переменных.
	      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    В этой  версии добавлен  ряд директив  позволяющих управлять расположением
неинициализированных  переменных.  Ранее  все  неинициализированные переменные
располагались за пределами файла.
    Теперь  директивой  ?initallvar  TRUE  включается  режим  при котором всем
неинициализированным переменным будет присвоено  нулевое значение и они  будут
располагаться в том  месте где были  объявлены. Параметр FALSE  этой директивы
отключает этот режим.
    Директива  ?usestartup  разрешает  компилятору  использовать  ячейки  кода
начальной  инициализации   программы  для   последующего  размещения   в   них
неинициализированных переменных.
    Директивой  ?startusevar   можно  указать   начальный  адрес   с  которого
компилятор  будет  распределять  память  для  неинициализированных переменных.
Например  получив  директиву  ?startusevar  0x53  компилятор будет располагать
неинициализированные переменные начиная с адреса 0x53.


                 Изменения в работе директив ?ifdef/ifndef.
		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Ранее  директива  ?ifdef  срабатывала  на  наличие константы независимо от
значения ее величины, а константа ?ifndef срабатывала на отсутствие  константы
в компилируемом  файле. Теперь  ?indef срабатывает  лишь на  константу имеющую
величину  TRUE,  а   ?ifndef  срабатывает  как   на  отсутствие  константы   в
компилируемом файле, так и на константу имеющую значение FALSE.
    Для директив  ?ifdef/?ifndef зарезервированы  константы codesize  и speed,
которые  принимают   значение  TRUE   или  FALSE   в  зависимости   от  режима
оптимизации. Это будет полезным для создания более гибких библиотек.





    0.212 от 7.12.98


                                 Структуры.
				~~~~~~~~~~~

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

Синтаксис:
~~~~~~~~~~
struct [<тег>] { <список-объявлений-элементов> } <описатель>[,<описатель>...];
struct <тег> <описатель> [,<описатель>];
    Объявление  структуры  начинается  с  ключевого  слова  struct и имеет две
формы записи.
    В первой форме типы и  имена элементов структуры специфицируются в  списке
объявлений   элементов.   Необязательный   в   данном   случае   <тег>  -  это
идентификатор, который  именует структурный  тип, определенный  данным списком
объявлений элементов. <описатель>  специфицирует либо переменную  структурного
типа, либо массив структур данного типа.
    Вторая  синтаксическая  форма  объявления  использует  тег  структуры  для
ссылки на структурный тип, определенный где-то в другом месте программы.
    Список  объявлений  элементов  представляет  собой  последовательность  из
одной или более объявлений  переменных. Каждая переменная, объявленная  в этом
списке, называется элементом структуры.
    Элементы структуры запоминаются в памяти последовательно в том порядке,  в
котором  они   объявляются.  Выравнивание   элементов  внутри   структуры   не
производится.  Сама  структура  выравнивается  на  четный  адрес если включено
выравнивание.
    Примеры объявлений структур:

struct test
{
  int a;
  char b[8];
  long c;
} rr, ff[4];

    В этом примере объявлены  структура с именем rr  и массив из 4  структур с
именем ff.  Всему набору  переменных присвоено  название (тег)  test. Этот тег
можно использовать для объявления других структур. Например:

struct test dd;

   Здесь объявлена структура с именем  dd имеющая набор элементов описанных  в
теге test.

Инициализация структур при объявлении.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    После  объявления  структуры  ее  элементы  могут  принимать  произвольные
значения.  Что   бы  этого   не  было   надо  структуры   проинициализировать.
Инициализировать  структуры  при  их  объявлении  можно только глобальные. 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 при  компиляции будет
загружено содержимое  файла <file.dat>.  Если размер  файла больше  чем размер
структуры, то лишние байты  будут загружены в код  программы, но они не  будут
востребованы. Если размер  файла меньше чем  размер структуры, то  недостающие
байты структуры будут заполнены нулями.

    4. Командой EXTRACT:
       struct test dd=EXTRACT "file.dat", 24, 10;
В этом  примере на  место где  расположена структура  dd при  компиляции будет
загружен  фрагмент  из  файла  <file.dat>  длиной  10  байт  со  смещения  24.
Недостающие байты будут заполнены нулями.

Инициализация структуры при выполнении программы.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    При  выполнении  программы,  кроме  присвоения  каждому элементу структуры
значения, можно  проинициализировать всю  структуру присвоением  ей числа  или
переменной. Примеры:

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.

Операции с элементами структур.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    С элементами структур  можно выполнять все  те операции, которые  доступны
для переменных соответствующего типа. Например:

Объявлена структура:
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.

    Это пока все - что можно делать со структурами на C--.


                            Оператор sizeof.
			   ~~~~~~~~~~~~~~~~~

    Операция sizeof  определяет размер  памяти, который  соответствует объекту
или типу. Операция sizeof имеет следующий вид:

 sizeof (<имя типа>)

    Результатом   операции   sizeof   является   размер   памяти   в   байтах,
соответствующий заданному объекту или типу.
    В  C--  оператор  sizeof  можно  применять  к переменным, регистрам, типам
переменных, структурам, текстовым строкам и файлам.
    Если  операция  sizeof  применяется  к  типу  структуры,  то   результатом
является размер тега данной структуры.
    Если  операция  sizeof  применяется  к  текстовой  строке,  то результатом
операции является размер строки плюс завершающий нуль. Например:

 sizeof ("Test")

результатом  этой  операции  будет  число  5.  Однако,  если Вы напишите такую
конструкцию:

char a="Test";

 sizeof(a)

то результатом  будет 1  - размер  типа переменной  a. Если  же Вам необходимо
иметь  размер  строки  и  иметь   возможность  манипулировать  ею,  то   можно
порекомендовать такой способ:

?define RR "Test"
char a=RR;

 sizeof(RR);

результатом  этой  операции  будет  число  5  - размер строки плюс завершающий
нуль.
    Операцию sizeof можно применять и к файлам. Это бывает очень полезным  при
использовании  оператора  FROM,  но  может  применяться  и  в  других случаях.
Пример применения оператора sizeof к файлам:

 sizeof ( file "filename.dat" )

Результатом этой операции будет размер файла "filename.dat".


                        Вызов процедур в си-стиле.
		       ~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Ранее  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).
    Что  бы  компилятор  C--  воспринимал  стековую  процедуру как процедуру в
си-стиле, первые  два символа  в имени  процедуры должны  быть: c_.  Например:
test();	- компилятор будет считать процедурой  в стиле pascal
c_test(); -  уже будет процедурой си-стиля.


                   Алгоритм поиска включаемых файлов.
		  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    В этой версии немного изменен  и расширен алгоритм включаемых файлов.  Для
этого  в  компилятор  добавлены:  директива  ?includepath  "filepath",   опция
командной  строки  /ip=filepath  и  строка  в c--.ini файл ip=filepath. Причем
можно  задавать  несколько  директив  и  команд  с  путями,  но  общее   число
просматриваемых путей ограничено 16.
    Теперь  поиск  включаемого  файла  производится  по  такой  схеме: сначала
делается попытка  открыть файл  в текущей  директории. Если  файла там нет, то
далее  делается  попытка  открыть  файл  в  директории  указанной   директивой
?includepath. Если  директива не  была задана  или файла  в этой директории не
оказалось,  то  делается  попытка  открыть  файл  в  директории  указанной   в
командной строке командой  /ip. Если эта  команда не была  задана или файла  в
указанной  директории  не  оказалось,  то  делается  попытка  открыть  файл  в
директории указанной  в файле  C--.INI командой  ip. Если  эта команда не была
задана или  файла в  указанной директории  не оказалось,  то делается  попытка
открыть файл в директории на которую указывает переменная окружения C--.  Если
переменная окружения не была задана или файла в этой директории не  оказалось,
то делается  последняя попытка  открыть файл  в директории  откуда был запущен
компилятор.
    Файл  C--.INI  должен  находиться  в  той  же  директории,  где расположен
компилятор.


    0.211 от 15.11.98


           Увеличение доступных операций в не-EAX/AX/AL выражениях.
          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Ранее в не-EAX/AX/AL выражениях было возможно использовать лишь  операции:
сложения,  вычитания,  XOR,  OR,  AND.  Теперь  для  CPU  286  или лучше можно
использовать умножение и сдвиги на число. Например:
     DX = var * CX * 3 * var >> 3;
будет компилироваться, а выражение
     DX = var * CX * 3 * var >> var;
вызовет сообщение об ошибке, так как в нем указан сдвиг на переменную  (сдвиги
можно делать только на непосредственное число).
    Примечание: для 8 битных не-AL  выражений умножать можно только на  числа:
0, 1, 2, 4, 8,  16, 32, 64 и 128.  Все эти ограничения связаны со  стремлением
не разрушать другие регистры при использовании не-EAX/AX/AL выражений.


                     Оптимизация числовых выражений.
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    При включении в командную строку опции /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;


                  Временное расширение разрядности переменной.
                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Как  известно,   после  умножения   может  произойти   переполнение,   т.е
разрядность  результата  может  превысить  разрядность  исходных  операндов  и
произойдет  искажение  результата.  Частично  решить  эту проблему Вам поможет
опция  командной  строки  /DE  или  строка  DE  в файле C--.INI. После команды
умножения компилятор будет просматривать остаток строки и если обнаружит,  что
расширение  разрядности  может  быть  востребовано  (востребовать  расширенную
разрядность могут  операции деления  и вычисления  остатка), то  будут приняты
меры по ее сохранению. Например:
    a = b*c+d/e; //здесь будет включена поддержка расширения разрядности
    a = b*c+d*e; //здесь поддержки расширения разрядности не будет.

    Однако применение этой опции может иметь и негативные последствия. Покажу
это на примере:
пусть имеется выражение
    a = b * c / d;
если значения переменных b = 0xC000,  c = 0x1000, d=0x10, после запуска  такая
программа зависнет с сообщением о том, что произошло переполнение при делении.


                        Операции с присвоением.
                       ~~~~~~~~~~~~~~~~~~~~~~~~~

    Ранее C-- уже умел делать  операции с присвоением: сложение +=;  вычитание
-=; or |=; and &=;  xor ^=; сдвиг в лево  <<= и сдвиг вправо >>=.  Теперь к их
числу добавилось  еще две  команды -  умножение с  присвоением *=  и деление с
присвоением.
    По поводу операций с присвоением хочу обратить Ваше внимание, что  сначала
будет  вычислено  выражение  справа  от  знака  равенства,  а  затем уже будет
выполнено действие слева  от него. При  делении с присвоением  будут разрушены
регистры EBX/BX/BL и EDX/DX.



    0.210 от 15.10.98


                    Поддержка ассемблерных инструкций.
                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    В этой версии закончена  реализация поддержки ассемблерных инструкций  CPU
вплоть до Pentium MMX. Вот список последней группы инструкций включенных в эту
версию:
  ARPL r/m16,r16  - коррекция затребованного уровня привилегий
  LAR r16,r/m16   - загрузить права доступа
  LAR r32,r/m32
  LGDT m64        - загрузить таблицу дескрипторов в GDTR
  LIDT m64        - загрузить таблицу дескрипторов в IDTR
  LLDT r/m16      - загрузить таблицу дескрипторов в LDTR
  LMSW r/m16      - загрузить слово состояния машины
  LSL r16,r/m16   - загрузить предел сегмента
  LSL r32,r/m32
  LTR r/m64       - загрузить регистр задачи
  SGDT m64        - запомнить таблицу дескрипторов в GDTR
  SIDT m64        - запомнить таблицу дескрипторов в IDTR
  SLDT r/m16      - запомнить таблицу дескрипторов в LDTR
  SMSW r/m16      - запомнить слово состояния машины
  VERR r/m16      - проверить селектор на доступ чтения
  VERW r/m16      - проверить селектор на доступ записи


                      32-битная адресация к памяти.
                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Наконец то C--  сделал робкий шаг  к 32-разрядности. Теперь  при адресации
можно использовать 32-битные регистры. Т.е. в ответ на синтаксис типа:
      AX = DSWORD [ECX+EDX*2+64];
компилятор сгенерит соответствующий ей код.


                      Предупреждения при компиляции.
                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Скорее всего, это можно  назвать не  предупреждения, а полезная информация
при  компиляции.  Сейчас  C--  может  выдавать  2  типа  предупреждений:   это
информация  о  самовольном  использовании  компилятором  регистров  CPU (кроме
регистров AL,AH,AX,EAX)  и информация  о возможности  применения коротких форм
языкового синтаксиса (IF, ELSE, JMP SHORT, FOR...).
    Разрешить вывод предупреждений можно с помощью опции командной строки  /+w
-  тогда   предупреждения  будут   выводиться  и   из  файлов   включаемых   в
компилируемый файл.   Разрешить или  запретить вывод  предупреждений только  в
одном  файле  можно  директивой  ?warning.   Параметр  FALSE  или  TRUE в этой
директиве позволит  разрешать или  запрещать вывод  предупреждений из текущего
файла.
    По  умолчанию  предупреждения  выводятся  на  экран.  Но  если в командную
строку  добавить   параметр  /wf=<file_name>   -  все   предупреждения   будут
выводиться в файл <file_name>.


                    Индексирование массива и переменных.
                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    В  предыдущих  версиях  была  попытка   сделать  это,  но  она   оказалась
неполноценной и  не всегда  компилировалось правильно.  Теперь, вроде  бы, все
проблемы разрешены.  В качестве  индекса компилятор  использует регистр  SI, а
при команде обмена >< может также быть задействован регистр DI.
    Пример:

int buf[20];
int a;
	AX=buf[a];
    Компилятор сгенерит код:
	mov SI,a
	shl SI,1
	mov AX,[#buf+SI]

    В качестве индекса могут использоваться все типы переменных кроме float.
    Допускается также применение вложенного индексирования. Например:

char buf[20];
int a[20];
int b;
	AX=buf[a[b]];
    Компилятор сгенерит код:
	mov SI,b
	shl SI,1
	mov SI,[#a+SI]
	mov AX,[#buf+SI]



    0.209 от 24.09.98


		     Компиляция драйверов устройств.
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    В этой  версии появилась  возможность получать  файлы драйверов устройств.
Компилятор значительно облегчит Ваш  труд при написании драйверов.  Компилятор
сам  создаст  заголовок  драйвера  и  процедуры  СТРАТЕГИЯ  и  ПРЕРЫВАНИЕ. Вам
остается лишь написать код обработки команд.
    Что  бы  откомпилировать  файл   драйвера  устройства,  надо  добавить   в
командную строку ключ /SYS. Кроме того появились новые директивы  компилятору,
которые действуют только с этим ключом. Вот они:

    ?sysattribute <значение>   - эта  директива передает  компилятору  атрибут
создаваемого драйвера. По умолчанию устанавливается значение 0x2000.

    ?sysname  <текстовая  строка>  -  эта  директива  передает компилятору имя
будущего драйвера. По  умолчанию присваивается имя  "NO_NAME". Длина имени  не
более 8 символов.

    ?syscommand  <command_0>,<command_1>,  ...  <command_n>;  -  эта директива
является обязательной.  По этой  директиве компилятору  передается список имен
процедур обработки  команд драйвера.  Имена разделены  запятыми. Список должен
заканчиваться символом  точка-с-запятой. Можно  передать не  более 25  команд.
Если  какая-то  команда  не  имеет  кода  поддержки, то в список надо записать
слово NONE.

    По  умолчанию  компилятор  для  драйвера  не  создает  стек. Драйвер может
пользоваться системным стеком.  Но, говорят, что  он имеет маленькую  глубину.
Если Ваши процедуры  активно используют стек  и Вы не  надеетесь на системный,
то директивой  ?stack <величина>  можно заставить  драйвер пользоваться  своим
стеком.
    Вашим  процедурам  обработки  команд  при  передачи управления в регистрах
ES:BX  будет  передан  адрес  заголовка  запроса.  Регистр  DS  равен  CS. При
возврате управления ваши процедуры должны сохранить регистр DS. В регистре  AX
должен находиться  код возврата.  Остальные регистры  могут быть  использованы
произвольным образом.
    Процедуру   обработки   команды   инициализации   желательно   располагать
последней  (чтобы  иметь  возможность  отдать адресное пространство занимаемое
этой  процедурой  операционной  системе).  Перед  этой  процедурой,  если Вы в
других  процедурах  обработки   команд  используете  динамические   процедуры,
обязательно  должна  быть  директива  ?setdinproc.  Об  этой  директиве  более
подробно  смотрите  ниже.  Глобальные   переменные  должны  быть   обязательно
проинициализированы.
    В  качестве  примера  предлагаю  Вам  драйвер  TIME2000, который на старых
компьютерах должен  помочь решить  проблему 2000  года. В  файле DRIVER.TXT Вы
можете найти немного полезной информации по драйверам.


		          Директива ?setdinproc.
                         ~~~~~~~~~~~~~~~~~~~~~~~~

    Эта  директива  появилась  для  того,  чтобы  облегчить  написание  на C--
драйверов и резидентов.  По этой директиве  компилятор немедленно вставляет  в
код  компилируемой  программы  все  вызывавшиеся ранее динамические процедуры.
Для любопытных поясню более подробно.
    Обычно, резидентные программы имеют такую схему: в самом начале  находится
jmp на код  инициализации, который заканчивается  вызовом прерывания INT  27 с
адресом последнего, оставляемого резидентным,  байта (обычно это адрес  начала
кода  инициализации).  Между  jmp  и  кодом  инициализации  располагается  код
остающийся  резидентно.  Если  Вы  в  этом  коде  делаете  вызов  динамических
процедур,  то  при  компиляции  эти  процедуры  будут добавлены в тело файла в
самом его конце,  за кодом инициализации.  Если вы запустите  такую программу,
то  код  динамических  процедур  окажется  за  пределами резидента и первое же
обращение к нему приведет к зависанию.
    Если  же  перед  кодом  инициализации  поставить директиву ?setdinproc, то
динамические процедуры будут  вставлены в этом  месте. Так как  компилятор C--
является однопроходным, не  ставьте эту директиву  в начале файла.  Компилятор
не может знать, какие процедуры Вы собираетесь использовать.
    Глобальные  переменные  при  создании  резидентов  и драйверов должны быть
обязательно  проинициализированы.  Для  неинициализированных переменных память
отводится за пределами файла и их  поджидает та же учесть, что и  динамических
процедур.


		     Логическое объединение сравнений.
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Наконец в C-- появилась  возможность логически объединять сравнения.  Т.е.
появилась возможность делать конструкции типа:

  IF( (AX > 2) && (CX == 0) || (DX < 8)){...

    Подобные конструкции можно теперь делать также и в циклах do{}while(),
while(), WHILE(), for() и FOR().
    Но не спишите радоваться. Есть несколько ограничений:
  1. Объединять можно лишь до 30 сравнений.
  2. Нельзя с помощью скобок задать приоритеты при проверке условий (как это
     делается в си). Все условия будут проверяться последовательно, в том
     порядке как они записаны.

    Синтаксически, каждое условие должно быть обязательно заключено в скобки.


		   Расширение возможностей циклов for и FOR.
                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Теперь в циклах for и FOR на этапе инициализации и этапе приращения  можно
писать несколько команд. Разделителем между командами на этих этапах  является
запятая. Например:

  for ( a=0, b=0, c=1; (a<12) && (c>b); a++, c+=4, b=b*a)

    Число команд на этапе инициализации  не ограничено. Длина строки на  этапе
приращения ограничена 1000 символами.



    0.208 от 6.09.98


			   Переменные типа float.
			   ~~~~~~~~~~~~~~~~~~~~~~
    Для  представления  значений  с  плавающей  точкой  в  язык C-- введен тип
float.  Этому типу соответствует действительное число одинарной точности FPU.
    Формат представления данных с  плавающей точкой включает три  поля: знака,
мантиссы  и  порядка.  Знак  определяется  старшим  значащим  разрядом.   Поле
мантиссы содержит  значащие биты  числа, а  поле порядка  содержит степень 2 и
определяет масштабирующий множитель для мантиссы.
    31 30.....23 22........0
    │  │      │  │         │
    │  │      │  └─────────┴─── - поле мантиссы
    │  └──────┴──────────────── - поле порядка
    └────────────────────────── - бит знака

		       Константы с плавающей точкой.
		       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Компилятор отличает вещественное число от  целого по наличию в нем  точки.
Начинаться вещественое число должно либо цифрой от 0 до 9, либо знаком  минус.
Необязательной  частью  вещественного   числа  является  показатель   степени.
Показатель  степени  отделяется  от  числа  символом  <e>  или  <E>.   Пробелы
недопустимы. Вот примеры допустимого синтаксиса:
   0.98
   -15.75
   3.14e2
   1.234567E-20

		       Диапазон допустимых значений.
		       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Вещественное число типа float может  находиться в диапазоне от 3.37E38  до
-3.37E38. Минимально близкое к  нулю значение равняется 1.17E-38  и -1.17E-38.
Записывать вещественное число одинарной точности более чем 8 цифрами не  имеет
смысла. Показатель степени может принимать значения от +38 до -38.

			  Математические операции.
			  ~~~~~~~~~~~~~~~~~~~~~~~~
    Компилятор поддерживает  4 основных  действия над  переменными типа float:
сложение,  вычитание,  умножение  и  деление.  Поддерживается  также инкремент
(var++ - увеличение на  1), декремент (var-- -  уменьшение на 1), смена  знака
(-var)  и  обмен  значениями  (var1><var2).  Остальные математические операции
будут  реализованы  во  внешних  библиотеках  (в  файле  fpumath.h-- некоторые
операции уже реализованы и я надеюсь,  что Вы примите участие в их  дальнейшем
пополнении).  При  вычислении значения переменной  float можно использовать  и
переменные других типов, они будут автоматически преобразованы в тип float.
    ВНИМАНИЕ! Составные математические операции  выполняются в том порядке,  в
котором они записаны, невзирая на правила арифметики.

			   Преобразования типов.
			   ~~~~~~~~~~~~~~~~~~~~~
    При математических операциях  конечным итогом которых  является переменная
типа float, все операнды других типов перед вычислением будут преобразованы  в
тип float. При присваивании переменной типа float значения переменной  другого
типа оно также будет преобразовано в тип float.
    Если при  целочисленных вычислениях  одним из  операндов будет  переменная
типа float, то из него будет выделена целая часть, которая и примет участие  в
вычислениях.  При  присваивании  целочисленной  переменной значения переменной
типа  float,  из  нее  также  будет  выделена  целая  часть,  которая  и будет
присвоена  целочисленной   переменной.  Существует   одно  исключение   -  это
32-битные регистры. При  присваивании 32-битному регистру  значения переменной
типа  float,  присваивание  произойдет  без  конвертации.  Для выделения целой
части из переменной  типа float будет  вызвана процедура float2numer  из файла
fpu.h--. Поэтому, если  в своей программе  используете переменные типа  float,
обязательно добавьте в свою программу строчку - ?include "fpu.h--" .
    ВНИМАНИЕ!  Если  Вы  собираетесь  писать  свои  динамические  процедуры  с
использованием переменных  типа float,  то пишите  их так,  чтобы не возникало
ситуаций, когда компилятор будет вынужден конвертировать вещественное число  в
целочисленное.  При   этом  будет   сгенерирован  код   вызова  несуществующей
процедуры. Это связано с тем, что в C-- не допускается делать вызовы  процедур
из динамических процедур. В ближайшее время я постараюсь решить эту проблему.

			    Операции сравнения.
			    ~~~~~~~~~~~~~~~~~~~
    Если  при  операции  сравнения  левым  операндом  является  переменная или
выражение  типа   float,  а   правым  является   целочисленное  значение,   то
целочисленное значение будет преобразовано  в вещественный тип. Если  же левым
операндом является целочисленное выражение или переменная, а правым  операндом
значение  типа  float,  то  из  правого  операнда  будет выделена целая часть,
которая и примет участие в сравнении.
    ВНИМАНИЕ!  При  операции  сравнения  с  участием  переменой  типа   float,
содержимое регистра AX будет разрушено.

			    О возможных багах...
			    ~~~~~~~~~~~~~~~~~~~~
    Переменная типа  float является  новшеством в  языке C--  и еще  не прошла
полного тестирования. Поэтому я допускаю что компилятор может генерировать  не
оптимальный код  а иногда  и ошибочный.  Также, возможно,  что еще охвачены не
все возможные случаи использования этой переменной.
    Если  Вы  написали  программу  с  использованием  переменной  float, а она
работает не  так как  надо, или  зависает, а  Вы уверены  в том, что программа
написана  верно,  то  пришлите  мне  ее  исходник  с  Вашими  пояснениями.   Я
постараюсь  разобраться  -  это   баг  компилятора  или  Ваше   недопонимание.
Присылайте  также  Ваши  предложения   по  оптимизации  кода  или   расширении
применения переменной float.


                     Директива компилятору assumeDSSS.
		     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     Незнаю, то ли Peter  Cellik (автор языка C--)  не знал, то ли  забыл, что
при базовой  адресации к  памяти, когда  в качестве  базы используется регистр
BP, по умолчанию адресация происходит  в сегменте SS. Тем не  менее компилятор
при использовании  стековых переменных  добросовестно штамповал  префикс SS. А
это лишний байт и такт при  каждом обращении к ним. Чтобы избегать  этого была
придумана директива компилятору  assumeDSSS, которая предпологала  что сегмент
данных  и  стека  совпадают  (что  в  большинстве  случаев  и  происходит)   и
компилятор не генерил префикс SS.
     Теперь  все  поставлено  на  свои  места  и  необходимость  в   директиве
assumeDSSS отпала. В этой  версии компилятор будет проглатывать  эту директиву
без предупреждений о неизвестной директиве,  но никаких действий она не  будет
производить.



    0.207a от 6.08.98


        Список поддерживаемых инструкций арифметического сопроцессора.
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

F2XM1         Вычисление ^b2x-1
FABS          Получить абсолютное значение
FADD ST,ST(i) Сложение вещественных чисел
FADD ST(i),ST Сложение вещественных чисел
FADD float    Сложеие float c ST(0) результат в ST(0).
FADDP ST(i)   Сложение вещественных чисел и извлечение из стека.
FBLD var80    Упакованная десятичная загрузка (BCD).
FBSTP var80   Запись упакованного десятичного значения (BCD) и извлечение
              из стека.
FCHS          Изменение знака.
FCLEX         Очистка исключительных прерываний.
FNCLEX        Очистка исключительных прерываний.
FCOM ST(i)    Сравнение вещественных чисел в ST(0) и ST(i).
FCOM float    Сравнение вещественных чисел в ST(0) и float.
FCOMP ST(i)   Сравнение вещественных чисел ST(0) и ST(i) и извлечение из стека.
FCOMP float   Сравнение вещественных чисел ST(0) и float и извлечение из стека.
FCOMPP        Сравнение вещественных чисел и извлечение из стека дважды.
FCOS          Косинус ST(0) (387+).
FDECSTP       Уменьшение указателя стека.
FDISI         Запрещение прерывания (только для сопроцессора 8087).
FDIV ST,ST(i) Деление вещественных чисел.
FDIV ST(i),ST Деление вещественных чисел.
FDIV float    Деление вещественных чисел.
FDIVP ST(i)   Деление вещественных чисел и извлечение из стека.
FDIVR ST,ST(i) Деление вещественных чисел с обращением.
FDIVR ST(i),ST Деление вещественных чисел с обращением.
FDIVR float    Деление вещественных чисел с обращением.
FDIVRP        Деление вещественных чисел с обращением и извлечение из стека.
FENI          Разрешение прерываний (только для сопроцессора 8087).
FFREE         Освобождение регистра.
FIADD ST(i)   Целочисленное сложение.
FIADD var32   Целочисленное сложение.
FICOM var16   Целочисленное сравнение.
FICOM var32   Целочисленное сравнение.
FICOMP var16  Целочисленное сравнение и извлечение из стека.
FICOMP var32  Целочисленное сравнение и извлечение из стека.
FIDIV var16   Деление целых чисел.
FIDIV var32   Деление целых чисел.
FIDIVR var16  Деление целых чисел с обращением.
FIDIVR var32  Деление целых чисел с обращением.
FILD var16    Загрузка целого слова.
FILD var32    Загрузка целого двойного слова.
FILDQ var64   Загрузка целого четверного слова.
FIMUL var16   Целочисленное умножение.
FIMUL var32   Целочисленное умножение.
FINCSTP       Увеличение указателя стека.
FINIT         Инициализация сопроцессора.
FNINIT        Инициализация сопроцессора.
FIST var16    Запись целого значения.
FIST var32    Запись целого значения.
FISTP var16   Запись целого значения и извлечение из стека.
FISTP var32   Запись целого значения и извлечение из стека.
FISUB var16   Целочисленное вычитание.
FISUB var32   Целочисленное вычитание.
FISUBR var16  Целочисленное вычитание с обращением.
FISUBR var32  Целочисленное вычитание с обращением.
FLD ST(i)     Загрузка вещественного значения.
FLD float     Загрузка вещественного значения.
FLDCW var16   Загрузка слова управления.
FLDENV var14b Загрузка операционной среды.
FLDLG2        Загрузка ^blg 2.
FLDLN2        Загрузка ^bln 2.
FLDL2E        Загрузка ^blog e.
FLDL2T        Загрузка ^blog 10.
FLDPI         Загрузка числа ^bPi.
FLDZ          Загрузка ^b+0.0.
FLD1          Загрузка ^b+1.0.
FMUL ST,ST(i) Умножение вещественных чисел.
FMUL ST(i),ST Умножение вещественных чисел.
FMUL float    Умножение вещественных чисел.
FMULP ST(i)   Умножение вещественных чисел и извлечение из стека.
FNOP          Нет операции.
FPATAN        Дробный арктангенс.
FPREM         Дробный остаток.
FPREM1        Дробный остаток (387+).
FPTAN         Дробный тангенс.
FRNDINT       Округление до целого.
FRSTOR var94b Восстановление сохраненного состояния.
FSAVE var94b  Сохранение состояния.
FNSAVE var94b Сохранение состояния.
FSCALE        Масштабирование.
FSETPM        Установка защищенного режима (287+).
FSIN          Синус ST(0) (387+).
FSINCOS       Синус и косинус ST(0) (387+).
FSQRT         Квадратный корень.
FST ST(i)     Запись вещественного значения.
FST float     Запись вещественного значения.
FSTCW var16   Запись слова управления.
FNSTCW var16  Запись слова управления.
FSTENV var14b Сохранение операционной среды.
FNSTENV var14b Сохранение операционной среды.
FSTP ST(i)    Сохранение вещественного значения и извлечение из стека
FSTP float    Сохранение вещественного значения и извлечение из стека
FSTSW var16   Запись слова состояния.
FNSTSW var16  Запись слова состояния.
FSTSW AX      Запись слова состояния.
FNSTSW AX     Запись слова состояния.
FSUB ST,ST(i) Вычитание вещественных значений.
FSUB ST(i),ST Вычитание вещественных значений.
FSUB float    Вычитание вещественных значений.
FSUBP ST(i)   Вычитание вещественных значений и извлечение из стека.
FSUBR ST,ST(i) Вычитание вещественных значений с обращением.
FSUBR ST(i),ST Вычитание вещественных значений с обращением.
FSUBR float   Вычитание вещественных значений с обращением.
FSUBRP ST(i)  Вычитание вещественных значений с обращением и извлечение из
              стека.
FTST          Проверка вершины стека на +0.0
FUCOM ST(i)   Неупорядоченное сравнение.
FUCOMP ST(i)  Неупорядоченное сравнение (387+).
FUCOMPP ST(i) Неупорядоченное сравнение (387+).
FWAIT         Ожидание.
FXAM          Проверка вершины стека.
FXCH ST(i)    Обмен содержимого регистров.
FXTRACT       Выделение экспоненты и значащей части.
FYL2X         ^bY * log2 X.
FYL2XP1       ^bY * log2 (X+1).

        		  Условные обозначения:
                         ~~~~~~~~~~~~~~~~~~~~~~~
ST      Вершина стека.
ST(i)   Регистр стека, где i=0-7.
var32   dword или long переменная памяти.
var16   word или int переменная памяти.
var64   qword 8-байтная переменная.
var80   10-байтная переменная памяти.
float   float переменная памяти.
var14b  14-байтная переменная памяти.
var94b  94-байтная переменная памяти.


                           Индексация массива.
                          ~~~~~~~~~~~~~~~~~~~~

    В этой версии появилась возможность использовать в качестве индекса к
массиву данных переменную типов: char, byte, int, word. При этом доступ к
элементам массива осуществляется в зависимости от объявленного типа массива.
Например:

?define COUNT 10
int array[COUNT];
int i;

void PROC()
{
	FOR ( i=0; i<COUNT; i++){
// Чтобы заполнить массив array нулями раньше нужно было сделать так:
		BX=i+i;	// доступ к элементам массива при использовании в
			// качестве индекса регистров или непосредственных
			// значений осуществляется побайтно.
		array[BX]=0;

// Теперь можно записать так:
		array[i]=0;	// при этом компилятор сам задействует
				// регистор BX и скорректирует его величину в
				// зависимости от размерности массива.


                     Директива компиллятору ?else.
		    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Директива ?else дополняет введеные ранее директивы ?ifdef ?ifndef ?endif.
Директива ?else распологается между директивами ?ifdef/?ifndef и ?endif.
    Если выражение следующее за ?ifdef определено в программе через ?define
или если выражение следующее за ?ifndef не определено в программе то будет
компиллироваться текст располложенный между ?ifdef/?ifndef и ?else. Текст
между ?else и ?endif компиллятором будет пропущен.
    Если же выражение следующее за ?ifdef не определено в программе или если
выражение следующее за ?ifndef определено в программе то будет компиллиро-
ваться текст располложенный между ?else и ?endif. Текст между ?ifdef/?ifndef
и ?else компиллятором будет пропущен.
    Пример использования ?else можно найти в файле FPU.H--.


    0.206 от 5.06.98


ini-файл
~~~~~~~~~
      Предназначен для предустановки по умолчанию параметров компилятора.
   Параметры прописываются построчно. Синтаксис тот же, что и в командной
   строке, но без ведущего обратного слэша.

Оператор goto, GOTO
~~~~~~~~~~~~~~~~~~~~
     Синтаксис:
     goto <метка>;
	.
	.
	.
<метка>:

  Оператор перехода goto передает управление на <оператор> помеченный
<меткой>. Аналогом в ассемблере оператору goto является команда jmp near.
Аналогом в ассемблере оператору GOTO является команда jmp short.

Цикл while, WHILE
~~~~~~~~~~~~~~~~~~
     Синтаксис:
  while(<выражение>)
       <оператор>

  Цикл выполняется до тех пор, пока значение <выражения> не станет ложным.
Вначале вычисляется <выражение>. Если <выражение> изначально ложно, то тело
оператора while вообще не выполняется и управление сразу передается на
следующий оператор программы.
  Цикл WHILE аналогичен циклу while, но при этом генерируется код на 3 байта
короче. Размер сгенерированного кода в цикле WHILE должен быть меньше 127
байт.
  Примеры:
	while ( i < 20 ){
		WRITEWORD(i);
		i++;
	}

	WHILE (i < 20 ) @WRITEWORD(i);	//цикл либо будет бесконечным либо не
                                        //выполнится ни разу
Цикл 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();
	}

Оператор break, BREAK
~~~~~~~~~~~~~~~~~~~~~~
  Оператор разрыва break прерывает выполнение операторов do-while, for,
while, loop. Он может содержаться только в теле этих операторов. Управление
передается оператору, следующему за прерванным циклом.
  Оператор BREAK аналогичен break, но при этом генерируется код на 1 байт
короче. Размер сгенерированного кода от места где применяется BREAK до конца
цикла должен быть меньше 127 байт.
  Примеры:
	FOR (i=0; ; i++){
		FOR(j=0; j<WIDTH; j++){
			IF(i==5)BREAK;
		}
		IF(i==10)BREAK;
	}

Оператор continue, CONTINUE
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Оператор продолжения continue передает управление на следующую итерацию в
циклах do-while, for, while, loop. В циклах do-while, while, loop следующая
итерация начинается с вычисления условного выражения. Для цикла for следующая
итерация начинается с вычисления выражения приращения, а затем происходит
вычисление условного выражения.
  Оператор CONTINUE аналогичен continue, но при этом генерируется код на 1
байт короче. Размер сгенерированного кода от места где применяется CONTINUE до
начала итерации должен быть меньше 127 байт.

Оператор 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');
  }

Ключевое слово asm
~~~~~~~~~~~~~~~~~~~
  Ключевое слово asm является синонимом к $ - префикс ассемблерной команды.
После слова asm можно писать блок ассемблерных команд.
  Пример:
	asm {
		.
		.
		push AX
labl:
		push BX
		mov AX,0x1234
		jmp short labl
		.
		.
		.
	}
  Метки внутри блока ассемблерных команд допустимы.


        	     Ассемблерные команды добавленные в C--.
                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BSF r16,r/m16  Сканирует операнд-источник на предмет первого бита равного 1.
BSF r32,r/m32  BSF сканирует от нулевого бита к старшим, а BSR в обратном по-
BSR r16,r/m16  рядке. Если 1 найдена, то устанавливается ZF и в операнд-прием-
BSR r32,r/m32  ник помещается номер найденного бита.

BT r16/r32,imm8              Копирует значение указанного бита во флаг переноса
BT r16/r32/m16/m32,r16/r32   CF. В операнде-приемнике нах. проверяемая послед.
BTC r16/r32,imm8             битов, в операнде-источнике - номер позиции. BT -
BTC r16/r32/m16/m32,r16/r32  просто копирует бит, BTC - копирует и после этого
BTR r16/r32,imm8             инвертирует значение в приемнике, BTR - копирует
BTR r16/r32/m16/m32,r16/r32  и после этого сбрасывает в 0, BTS - копирует и
BTS r16/r32,imm8             после этого устанавливает в 1 значение в операнде
BTS r16/r32/m16/m32,r16/r32  приемнике.

SHLD r/m16,r16,imm8  Сдвиг двойного (2x16 бит) или учетверенного (2x32 бит)
SHLD r/m32,r32,imm8  слова, составленного из двух операндов. SHLD сдвигает
SHLD r/m16,r16,cl    влево, SHRD - вправо. В SHLD первый операнд находится
SHLD r/m32,r32,cl    слева, второй - справа. В SHRD первый операнд находится
SHRD r/m16,r16,imm8  справа, второй - слева. Освобождаемые разряды первого
SHRD r/m32,r32,imm8  операнда заполняются битами второго операнда.
SHRD r/m16,r16,cl    Число сдвигов задается либо в CL, либо 8-битной констан-
SHRD r/m32,r32,cl    той.

SETcond r/m8  Устанавливает байт, указанный в качестве операнда, в 1, если
              условие имеет значение "истина", и в 0 в противном случае.
              Условие проверяется как комбинация флагов в соответствии с
              таблицей:
╔═══════════╤═════════════╤════════════════════════════════════════════╗
║ Мнемоника │Комбинация фл│        Описание условия                    ║
╠═══════════╪═════════════╪════════════════════════════════════════════╣
║SETO       │OF=1         │Установить, если переполнение               ║
║SETNO      │OF=0         │Установить, если не переполнение            ║
║SETC       │CF=1         │Установить, если перенос                    ║
║SETNC      │CF=0         │Установить, если не перенос                 ║
║SETB/SETNAE│CF=1         │Установить, если ниже/не выше и не равно    ║
║SETAE/SETNB│CF=0         │Установить, если выше или равно/не ниже     ║
║SETE/SETZ  │ZF=1         │Установить, если равно                      ║
║SETNE/SETNZ│ZF=0         │Установить, если не равно                   ║
║SETBE/SETNA│CF=1 | ZF=1  │Установить, если ниже или равно/не выше     ║
║SETA/SETNBE│CF=0 & ZF=0  │Установить, если выше/не ниже и не равно    ║
║SETS       │SF=1         │Установить, если знак                       ║
║SETNS      │SF=0         │Установить, если не знак                    ║
║SETP/SETPE │PF=1         │Установить, если четно                      ║
║SETNP/SETPO│PF=0         │Установить, если нечетно                    ║
║SETL/SETNGE│SF!=OF       │Установить, если меньше/не больше и не равно║
║SETGE/SETNL│SF=OF        │Установить, если больше или равно/не меньше ║
║SETLE/SETNG│ZF=1 | SF!=OF│Установить, если меньше или равно/не больше ║
║SETG/SETNLE│ZF=0 & SF=OF │Установить, если больше/не меньше и не равно║
╚═══════════╧═════════════╧════════════════════════════════════════════╝

		      Команды MMX-процессора.
                     ~~~~~~~~~~~~~~~~~~~~~~~~~

    Все MMX команды, за исключением EMMS, ссылаются и оперируют на двух
операндах: источник и адресата. Первый операнд - адресат а второй операнд -
источник. Операнд адресата может также быть вторым исходным операндом для
операции. Команда записывает результат поверх операнда - адресата.
    Исходный операнд для всех MMX команд (за исключением команд передачи
данных), может быть или в памяти или в регистре MMX. Операнд адресата всегда
в регистре MMX. Для команд передачи данных, операндом источника и адресата
может также быть целочисленный регистр (для команды MOVD) или память (для
MOVD и MOVQ команд).
    Если одним из операндов является память или переменная, то
инициализировать их надо типом dword, но не забывайте что MMX команды
используют 8 байт. Это ухищрение связано с тем, что C-- не поддерживает тип
переменных qword. Пример использования MMX команд в C--:

dword i[2];	//зарезервировать 8 байт под переменную i
int j[4];
main()
{
	asm{
		movq i,MM1
		movq MM0,DSDWORD[0x100]	/* в регистр MM0 будет записано 8 байт
					   с адреса 100h по 107h */
		movq MM2,DSDWORD[#j]
		emms
	}
}

    Команда EMMS должна использоваться в каждом из следующих случаев:
 - когда приложение, использующее команды с плавающей запятой обращается к
MMXT библиотеке/DLL. (Используйте команду EMMS конце MMX кода.)
 - когда приложение, использующее MMX команды вызывает библиотеку/DLL с
плавающей запятой. (Используйте команду EMMS перед вызовом кода с плавающей
запятой.)
 - когда произошло переключение между MMX кодом в задаче/нити и другими
задачами/нитями в совместных операционных системах, если не уверенны, что
большее количество MMX команд будет выполнено перед любым FPU кодом.
    Если приложение содержит команды с плавающей запятой и MMX команды,
следуйте этим принципам:
 - Выделять MMXT  модуль и модуль сопроцессора в отдельные потоки команд
(отдельные циклы или подпрограммы) так, чтобы они содержали только команды
одного типа.
 - Не полагаться на содержание регистров поперек переходов. Когда состояние
 MMX не требуется, очистьте состояние MMX, используя команду EMMS.
 - Выходите из раздела кода с плавающей запятой с пустым стеком.
EMMS - Освободить MMXT состояние. Команда EMMS освобождает состояние MMX. Эта
       команда должна использоваться, чтобы очистить состояние MMX (чтобы
       освободить tag- слово регистров для работы с плавающей запятой) в
       конце MMX подпрограммы перед вызовом других подпрограмм, которые могут
       выполнять операции с плавающей запятой.

MOVD mmxreg,mem   - Переместить 32 Бита, передает 32 бита
MOVD mem,mmxreg     упакованных данных из памяти в регистры MMX и обратно,
MOVD reg32,mmxreg   или из целочисленных регистров в регистры MMX и обратно.
MOVD mmxreg,reg32

MOVQ mmxreg,mmxreg/mem64 - Переместить 64 Бита, передает 64 бита упакованных
			   данных из памяти в регистры MMX и обратно, или
			   между регистрами MMX.

   УПАКОВАННОЕ СЛОЖЕНИЕ И ВЫЧИТАНИЕ. Команды PADDSB, PADDSW, и PADDWD
(упакованное сложение) и PSUBB, PSUBW, и PSUBD (упакованное вычитание)
добавляют или вычитают знаковые или беззнаковые элементы данных исходного
операнда к или (' операнда адресата в циклическом режиме. Эти команды
поддерживают упакованные байты, упакованные слова, и упакованные двойные
слова. Команд PADDSB и PADDSW (упакованное сложение с насыщенностью) и
PSUBSB, и PSUBSW (упакованное вычитание с насыщенностью) добавляют или
вычитают знаковые элементы данных исходного операнда к или из знаковых
элементов данных операнда адресата и обрезают результат к ограничениям
диапазона знакового типа данных. Эти команды поддерживают упакованные байты и
упакованные слова. Команды PADDUSB и PADDUSW (упакованное сложение без знака
с насыщенностью) и PSUBUSB, и PSUBUSW (упакованное вычитание без знака с
насыщенностью) добавляют или вычитают элементы данных без знака исходного
операнда к или из элементов данных без знака операнда адресата и обрезают
результат к ограничениям диапазона типа данных без знака. Эти команды
поддерживают упакованные байты и упакованные слова.
PADDB   - Add with wrap-around
PADDW   - Add with wrap-around
PADDD   - Add with wrap-around
PADDSB  - Add signed with saturation
PADDSW  - Add signed with saturation
PADDUSB - Add unsigned with saturation
PADDUSW - Add unsigned with saturation
PSUBSB  - Subtract signed with saturation
PSUBSW  - Subtract signed with saturation
PSUBUSB - Subtract unsigned with saturation
PSUBUSW - Subtract unsigned with saturation
PSUBB   - Subtract with wrap-around
PSUBW   - Subtract with wrap-around
PSUBD   - Subtract with wrap-around

    УПАКОВАННОЕ УМНОЖЕНИЕ. Команды упакованного умножения выполняют четыре
умножения на парах 16-разрядных знаковых операндов, производя 32-разрядные
промежуточные результаты. Пользователи могут выбирать старшие или младшие
части каждого 32-разрядного результа. Команды PMULHW (упакованное умножение
старший) и PMULLW (упакованное умножение младший) умножают знаковые слова
операндов источника и адресата и записывают старшую или младшую часть
результатата в операнд адресата.
PMULHW - Packed multiplic ation
PMULLW - Packed multiplic ation

    УПАКОВАННОЕ УМНОЖЕНИЕ/СЛОЖЕНИЕ. Команда PMADDWD (упакованное умножение и
сложение) вычисляет произведение знаковых слов операндов адресата и
источника. Четыре промежуточных 32-разрядных произведения суммируются в
парах, чтобы произвести два 32-разрядных результата.
PMADDWD - Packed multiply add

    КОМАНДЫ СРАВНЕНИЯ. Команды PCMPEQB, PCMPEQW, и PCMPEQD (упакованное
сравнение на равенство) и PCMPGTB, PCMPGTW, и PCMPGTD (упакованное сравнение
для большего чем) сравнивают соответствующие элементы данных в операндах
источника и адресата на равенство или оценивают больше чем, соответственно.
Эти команды генерируют маску единиц или нулей, которые записываются в операнд
адресата. Логические операции могут использовать маску, чтобы выбрать
элементы. Это может использоваться, чтобы выполнить упакованную условную
операцию пересылки без ветвления или набора команд ветвления. Никакие флажки
не устанавливаются. Эти команды поддерживают упакованные байты, упакованные
слова и упакованные двойные слова.
PCMPEQB - Packed compare for equality
PCMPEQW - Packed compare for equality
PCMPEQD - Packed compare for equality
PCMPGTB - Packed compare greater (signed)
PCMPGTW - Packed compare greater (signed)
PCMPGTD - Packed compare greater (signed)

    КОМАНДЫ ПРЕОБРАЗОВАНИЯ. Команды преобразования преобразовывают элементы
данных внутри упакованного типа данных. Команды PACKSSWB и PACKSSDW
(упакованный со знаковой насыщенностью) преобразовывает знаковые слова в
знаковые байты или знаковые двойные слова в знаковые слова, в режиме знаковой
насыщенности. Команда PACKUSWB (упакованный насыщенностью без знака)
преобразовывает знаковые слова в байты без знака, в режиме насыщенности без
знака. Команды PUNPCKHBW, PUNPCKHWD, и PUNPCKHDQ (распаковать старшие
упакованные данные) и PUNPCKLBW, PUNPCKLWD, и PUNPCKLDQ (распаковать младшие
упакованные данные) преобразовывают байты в слова, слова в двойные слова, или
двойные слова в четверное слово.
PACKSSDW  - Pack dword to word data (signed with saturation)
PACKSSWB  - Pack word to byte data (signed with saturation)
PACKUSWB  - Pack word to byte data (unsigned with saturation)
PUNPCKHBW - Unpack high data to next larger type
PUNPCKHDQ - Unpack high data to next larger type
PUNPCKHWD - Unpack high data to next larger type
PUNPCKLBW - Unpack low data to next larger type
PUNPCKLDQ - Unpack low data to next larger type
PUNPCKLWD - Unpack low data to next larger type

    ЛОГИЧЕСКИЕ КОМАНДЫ. Команде PAND (поразрядное логическое И), PANDN
(поразрядное логическое И-НЕ), POR (поразрядное логическое ИЛИ), и PXOR
(поразрядное логическое исключающее ИЛИ) выполняют поразрядные логические
операции над 64-разрядных данными.
PAND  - Bitwise And
PANDN - Bitwise AndNot
POR   - Bitwise Or
PXOR  - Bitwise Xor

    КОМАНДЫ СДВИГА. Команды логического сдвига влево, логического сдвига
вправо и арифметического сдвига право, сдвигают каждый элемент на
определенное число битов. Логические левые и правые сдвиги также дают
возможность перемещать 64-разрядное поле как один блок, что помогает в
преобразованиях типа данных и операциях выравнивания. Команды PSLLW и PSLLD
(упакованный логический сдвиг влево) и PSRLW и PSRLD (упакованный логический
сдвиг вправо) выполняют логический левый или правый сдвиг, и заполняют пустые
старшие или младшие битовые позиции нулями. Эти команды поддерживают
упакованные слова, упакованные двойные слова, и четверное слово. Команда
PSRAW и PSRAD (упакованный арифметический сдвиг вправо) выполняет
арифметический сдвиг вправо, копируя знаковый разряд в пустые разрядные
позиции на старшем конце операнда. Эта команда поддерживает упакованные слова
и упакованные двойные слова. Вторым операндом в этих командах может быть 8
битное число.
PSLLD - Packed shift left logical
PSLLQ - Packed shift left logical
PSLLW - Packed shift left logical
PSRAW - Packed shift right arithmetic
PSRAD - Packed shift right arithmetic
PSRLW - Packed shift right logical
PSRLD - Packed shift right logical
PSRLQ - Packed shift right logical

