Внимание!
~~~~~~~~~~
    В компиляторе исправлена ошибка в сравнениях для процедур возвращающих
флаги. Ранее (в 0.238 версии ) генерировался противоположный код сравнения.
Например:

void CARRYFLAG TEST();	//declaration procedure
...
{
  IF ( TEST() ) AX = 1 ;  //check carry flag returned procedure TEST
}
   Сейчас компилятор генерирует такой код:
     call    103h
     jnc     10Ch
     mov     ax,1
   Раньше было так:
     call    103h
     jc      10Ch
     mov     ax,1


Выводить предупреждения как ошибки.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    С помощью опции командной строки /we=#nn или директивой #pragma option
we=#nn можно указать компилятору, какие предупреждения выводить как ошибки.
На этот момент можно таким образом менять статус у 15 типов предупреждений.
Т.е. #nn может принимать числовые значения от 1 до 15.  Вывод этих же 15
типов предупреждений можно отключать опцией /nw=#nn.  Вот список
предупреждений, которые можно либо отключать, либо выводить как ошибки:

1 -  Optimize numerical expressions
2 -  'RegName' has been used by compiler
3 -  Short operator 'if | else | ...' may be used
4 -  String "..." repeated
5 -  Expansion variable
6 -  Signed value returned
7 -  'fun.name' defined above, therefore skipped
8 -  Variable | structure | ... 'name' possible not used
9 -  Non-initialized variable 'name' may have been used
10 - Return flag was destroyed
11 - Code may not be executable
12 - Don't use local/parametric values in inline functions
13 - Sources size exceed destination size
14 - Pragma pack pop with no matching pack push
15 - Missing parameter in function 'name'



Регистры для вычисления индекса.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    При вычислении индексных выражений компилятор по умолчанию может
использовать регистры: (E)SI, (E)DI, (E)BX, (E)DX.  Эти регистры указаны в
порядке приоритета выбора.  Чаще используется первый регистр.  Если при
вычислении индекса не хватает регистра, то компилятор использует следующий.

    Список этих регистров можно изменить директивой #pragma indexregs.  В
качестве параметра этой директиве надо указать список регистров разделенных
пробелом или запятой.  Нужно указать до четырех регистров. Пример:

#pragma indexregs ECX EDX

    Действие этой директивы, если она указана вне функций, распространяется
на все нижележащие функции.  Если директива указана внутри функции, то ее
действие распостраняется только на эту функцию.


Расположение локальных переменных в стеке.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Теперь локальные переменные располагаются в стеке в том порядке, в каком
они были объявлены - самая первая переменная находится в вершине выделенного
для локальных переменных фрагмента стека, следующие располагаются ниже ее.
Раньше в вершине находилась последняя объявленная локальная переменная.

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


Замена переменной на константу.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

    Компилятор отслеживает инициализацию переменных константами и, при
использовании таких переменных, компилятор заменяет обращение к ним на эту
константу (если эта переменная не была изменена).

    Этот метод оптимизации по умолчанию включен. Для его отключения надо
использовать ключ командной строки /orv-.


Директивы #if #elif.
~~~~~~~~~~~~~~~~~~~~~

    Директива #if в C-- аналогична директиве #ifdef.  После директивы #if
можно использовать операцию defined или !defined.

    Директива #elif может располагаться только между директивами #if/#ifdef и
#endif.  Число директив #elif может быть любым.

    После этих директив следуют либо идентификаторы, либо константное
выражение сравнения.  Идентификаторы и выражения сравнения могут объединяться
в сложное выражение операторами || и &&.  Но в C-- не поддерживается
приоритет скобок.  Проверка идентификаторов и выражений сравнения
производится последовательно, так как они записаны.


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

    Директива #pragma pack определяет выравнивание в тегах структур.  По
умолчанию в компиляторе это выравнивание равно 1.  Изменить значение
выравнивания по умолчанию можно опцией командной строки /as=n, n может
принимать значения 1, 2, 4, 8...

    Директива #pragma pack поддерживает следующий синтаксис:

  #pragma pack(n)
    Устанавливает новое значение выравнивания.

  #pragma pack(push)
    Запоминает текущее значение выравнивания в стеке.

  #pragma pack(push,n)
    Запоминает текущее значение выравнивания в стеке и устанавливает новое.

  #pragma pack(push,id)
    Запоминает текущее значение выравнивания в стеке и присваивает ему имя id.

  #pragma pack(push,id,n)
    Запоминает текущее значение выравнивания в стеке, присваивает ему имя id
и устанавливает новое значение выравнивания n.

  #pragma pack(pop)
    Извлекает из стека значение выравнивания и делает его текущим.

  #pragma pack(pop,n)
    Извлекает из стека значение выравнивания и устанавливает новое значение
выравнивания n.

  #pragma pack(pop,id)
    Извлекает из стека запомненные ранее значения выравнивания до тех, пор
пока его имя не будет равным id.  Затем будет установлено значение
выравнивания соответствующее идентификатору id.  Если в стеке такого
идентификатора не окажется, то стек будет очищен и установится значение
выравнивания по умолчанию.

  #pragma pack(pop,id,n)
    Извлекает из стека запомненные ранее значения выравнивания до тех, пор
пока его имя не будет равным id.  Если в стеке такого идентификатора не
окажется, то стек будет очищен.  Затем будет установлено новое значение
выравнивания n.


Передача структур в качестве параметров функции.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Некоторые API-функции принимают в качестве параметра содержимое структур.
Покажем на примере как это можно делать:

//объявляем тег структуры
struct TAG
{
  int var_a;
  int var_b;
};

//объявляем функцию принимающую структуру в качестве параметра
void fun1 ( TAG str);

//определяем структуру
TAG struct1;

//определяем функцию
void fun1 ( TAG str)
{
  EAX = str.var_a + str.var_b;
...
}

//делаем вызов функции с передачей структуры в качестве параметра
main()
{
  fun1(struct1);
...
}


Поэлементная адресация в массиве.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

    Применив ключ командной строки /ba-, или добавив в файл c--.ini строку
ba-, или через директиву #pragma option ba- можно получить только
поэлементную адресацию.  Исключения составляют выражения типа DSDWORD[...],
DSINT[...] ...  В этих выражениях адрес всегда будет вычисляться без
умножения на размерность массива.


'static' функции и переменные в структуре.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Поддержка 'static' функций в структурах/классах существует еще с прошлой
версии компилятора.  В этой версии добавлена поддержка 'static' переменных.

    'static' функции и переменные это обычные функции и переменные видимость
(доступность) которых ограничена методами (функциями) класса (структуры) в
которых они объявлены.

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

struct info
{
  info (int num); // объявляем конструктор класса info, принимающего один
                  // параметр.
  int number;
  static int svar = 5; // объявление и инициализация static переменной svar
};

    Доступ к 'static' переменным и функциям из функций одного и того же
класса осуществляется как к обычным переменным и функциям. Пример:

info::info (int num)
{
  IF ( this ) {
    svar = num ;
  }
  return this;
}

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

void main()
{
  EAX = info.svar;
}


Новые вещественные типы переменных.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Тип переменных 'double' активно используется и в ассемблере и в Си,
поэтому подробно про него не будем говорить.

    Функции, имеющие тип возврата 'double', оставляют результат в вершине
стека FPU.  Все другие типы возвратов из функций для этого используют
регистры (AL, AX, EAX, EDX:EAX).  Это позволяет избавится от, зачастую не
нужной, загрузки вещественных чисел в регистры.

    Другим новым типом в компиляторе являются регистры FPU.

    Всего существует 8 регистров FPU.  Обозначаются они так: st(0), st(1),
st(2)... st(7).  Регистры организованы в стек, вершиной которого является
регистр st(0).  Через него производится загрузка и выгрузка вещественных
чисел.  Этот регистр можно обозначать как st.  Более подробно о работе и
свойствах регистров FPU, Вы можете почитать в специальной литературе.

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

    Регистры FPU можно использовать в вычислениях выражений. Пример:

double d,d2;

  st(3) = st(2) + 1 * d;
  d2 = PI / st(3);

    Регистры FPU можно использовать в объявлениях функций в качестве
параметров в сочетании с другими типами переменных. Пример:

void cdecl proc (int, st(3), st(4) );
int fastcall func (ECX, st(3), st(4) );

    Регистр st(0) можно использовать в качестве возврата из функции.  При
этом будет генерироваться такой же код, как и при использовании возврата
double.  Пример:

st ctdcall func (dword, st(2));



64-битные переменные.
~~~~~~~~~~~~~~~~~~~~~~

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

    В С-- поддерживаются 64-битные беззнаковые типы переменных - 'qword'.
Для совместимости со стандартным Си поддерживается обозначение этого типа как
'long long', а к числовым константам можно добавлять суффикс 'I64'.  К
64-битным областям памяти можно обращаться с использованием зарезервированных
идентификаторов:  DSQWORD, ESQWORD, SSQWORD, CSQWORD, FSQWORD, GSQWORD.  Для
удобства работы с 64-битными переменными можно объединять 32-битные регистры
в пары. Пример:

  EBX:ECX = DSQWORD[EAX] + 1;

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

    Функции, имеющие тип возврата 'qword,' используют для возврата 64-битную
пару регистров EDX:EAX.

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


Операции сравнения с использованием флагов.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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


Очистка стека от параметров cdecl-функций.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

    Вернуться к старому способу очистки стека можно с помощью опции командной
строки /js-.

    Стек очищается ассемблерной инструкцией add esp,sizeparam и при этом
флаги портятся.  Отложенная очистка стека производится перед операторами
сравнения, а если этот оператор проверяет флаги, то программа будет работать
не правильно.  Чтобы этого не происходило надо делать очистку стека до места,
где формируются флаги.  Для этого достаточно перед местом формирования флагов
поставить локальную метку, которая также вызывает очистку стека. Пример:

  printf("Password: ";
  scanf("%s",#pword);
  ESI = #right;
  EDI = #pword;
  ECX = sizeof(right)-1;
LOCAL:        // перед этой меткой компилятор разгрузит стек
  $rep $cmpsb // Эти инструкции устанавливают флаг
              // Который может быть сброшен, если не будет метки
  IF (ZEROFLAG) printf("OK\n";
  ELSE printf("Incorrect Password!!!\n";


COFF объектные файлы.
~~~~~~~~~~~~~~~~~~~~~~

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

    Объектные файлы формата COFF можно включать в свой проект директивой
#include. Пример:

#include "filename.obj"

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


Директива #define.
~~~~~~~~~~~~~~~~~~~

    С помощью директивы #define в C-- можно было определять: одиночную
лексему, числовое выражение (которое всегда вычисляется в числовую константу)
либо строковую константу.  Теперь возможности этой директивы расширены.

    Выражение, определенное через #define, можно переопределить:

#define WORD word
#define VERSION 1
 . . .
#define WORD dword
#define VERSION 2

    Можно определять пустой идентификатор:

#define WORD
#define VERSION

    Для идентификатора определяемого через #define можно задавать аргументы:

#define SQR( v )         v * v
#define AREA( r , a )    2 * SQR ( r ) * a

 main ( )
{
  AX = AREA ( BX , CX ) ;
}

    Компилятор сгенерирует такой код:

test.c-- 6: AX = AREA ( BX , CX ) ;
0100 B80200                   mov     ax,2
0103 F7E3                     mul     bx
0105 F7E3                     mul     bx
0107 F7E1                     mul     cx
0109 C3                       ret

    Для #define с аргументами в его синтаксисе есть обязательные условия:
    1 - открывающаяся скобка со списком аргументов '(' должна идти сразу за
идентификатором без пробелов.
    2 - правая часть #define начинается после символа закрывающаяся скобка
')' и заканчивается символом перехода на новую строку (код 13).

    Для длинных выражений, занимающих несколько строк, можно использовать
символ '\' в качестве символа продолжения строки.

    Если Вам необходимо определить идентификатор, состоящий из нескольких
лексем, но без параметров, то необходимо обязательно после имени
идентификатора ставить пару круглых скобок:

int i,j;
#define ADDVAR( )    i + j

    Если Вы напишите этот #define без скобок, как это делается в языках Си,
то компилятор воспримет только одну лексему, а оставшаяся лексема вызовет
сообщение об ошибке.  Использовать такой идентификатор можно как со скобками,
так и без них:

  AX = ADDVAR ( ) ;
  BX = ADDVAR + 1 ;

    Изменения в директиве #define потребовали внести некоторые изменения в
директивы #undef и #ifdef/ifndef.  Также были убраны из списка
зарезервированных слов:  FALSE, TRUE, __SECOND__, __MINUTE__, __HOUR__,
__DAY__, __MONTH__, __YEAR__, __WEEKDAY__, __VER1__, __VER2__", __COMPILER__,
__DATESTR__, __DATE__, __TIME__.  Все эти константы теперь автоматически
объявляются компилятором через #define.  Вы можете их изменять либо удалить.


Директива #undef.
~~~~~~~~~~~~~~~~~~

    Директивой #undef можно удалять (разыменовывать) только идентификаторы
объявленные через #define.


Директивы #ifdef/ifndef.
~~~~~~~~~~~~~~~~~~~~~~~~~

    Если значение проверяемого идентификатора равно нулю, то результатом
применения к этому идентификатору директив #ifdef/ifndef будет соответственно
TRUE/FALSE.  Раньше было FALSE/TRUE.


Директива #inline.
~~~~~~~~~~~~~~~~~~~

    Директива #inline принимала два значения - TRUE и FALSE, которые
определяли поведение динамических функций, объявленных как inline.  При TRUE
эти функции вставлялись в код, а при FALSE делался вызов функции.  Если
директива #inline не использовалась, то эти функции при оптимизации на
скорость вставлялись в код, а при оптимизации на размер делался вызов.

    Теперь у директивы #inline появилось третье состояние - AUTO.  Этот режим
является основным по умолчанию.  При этом функции ведут себя, так как будто
директива #inline не использовалась ранее, т.е. при оптимизации на скорость
функции будут вставляться, а при оптимизации на размер будет делаться их
вызов.


Unicode строки.
~~~~~~~~~~~~~~~~

    Для того чтобы строка была сохранена в программе в формате unicode, перед
этой строкой должен стоять префикс из символа 'L'. Пример:

char string = L"This unicode string";

    Этим ограничивается поддержка unicode строк компилятором.  Манипуляции с
такими строками (копирование, сравнение, вывод на экран...) должны делаться
библиотечными функциями, которые Вам еще предстоит написать.


Новые возможности sizeof.
~~~~~~~~~~~~~~~~~~~~~~~~~~

    Для sizeof появились четыре новых операнда: __CODESIZE, __DATASIZE,
__POSTSIZE, __STACKSIZE, которые соответственно возвращают размеры кода,
инициализированных данных, неинициализированных данных и стека компилируемой
программы.  Для этих четырех констант доступна операция сложения друг с
другом и с числовыми константами.  Результат этих операций будет сохранен в
программе в виде одного числового значения. Пример:

  sizeCOMfile = sizeof(__CODESIZE) + sizeof(__DATASIZE);

    Операнды sizeof теперь можно не заключать в скобки:

int sizeCOMfile = sizeof __CODESIZE + sizeof __DATASIZE;

    Внимание: константа '__sizepostarea' более компилятором не
поддерживается.  Если Вы ее использовали в своих программах, то ее необходимо
заменить на выражение 'sizeof (__POSTSIZE)'.


Новый синтаксис для локальных меток.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Ранее локальные метки отличались от глобальных тем, что они писались
только большими буквами.  Теперь локальные метки можно писать любыми буквами.
Но для того, чтобы обозначить, что данная метка является локальной, перед ее
именем надо указать префикс - символ '@'.  Этот префикс должен быть только
перед именем самой метки.  Использовать имя такой локальной метки нужно без
этого префикса. Пример:

void proc()
{
  . . .
  goto local_label;
  . . .
@ local_label:
  . . .
}


Ключевое слово 'uses'.
~~~~~~~~~~~~~~~~~~~~~~~

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

    При объявлении функции ключевое слово 'uses' передает компилятору список
регистров, которые эта функция разрушит.  Этот список вместе с 'uses' должен
быть заключен в круглые скобки, и располагаться между списком параметров
функции и завершающим ';'. Вот пример:

void proc (int param) (uses EAX,ECX);

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

    По умолчанию, если для функции не существует объявления или не указан
список разрушаемых регистров, принимается, что функция разрушает все
регистры.  Исключение составляют функции Windows API.  Для них принято, что
они разрушают все регистры кроме EBX, EDI, ESI.

    При определении функции ключевое слово 'uses' передает список регистров,
которые будут сохранены в стеке, а при завершении функции будут
восстановлены.  Этот список должен располагаться сразу после имени функции со
списком параметров.  Заключать этот список в круглые скобки необязательно.
Имена регистров в списке разделяются пробелами или запятыми.  Для того чтобы
сохранить все регистры, можно использовать ключевое слово 'allregs'.  Для
сохранения и восстановления всех регистров компилятор будет использовать
ассемблерные инструкции PUSHA(D)/POPA(D).  Пример:

void proc (int param)
uses EBX ECX
int localvar;
{
int localvar2;
  EBX=param;
  . . .
}

    Если Вы будете завершать функцию ассемблерной инструкцией 'ret', то эти
регистры не будут восстановлены из стека, и Ваша программа будет работать
неправильно.


Использование локальных переменных других функций.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Использование локальных переменных других функций возможно только в
inline-функциях (inline-функции это те, для которых не формируется свой
стековый кадр и не генерируется код завершения функции).

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

void proc (int param)
uses EBX ECX
int localvar;
{
int localvar2;
  EBX=param;
  proc2();
  . . .
}

void proc2 (int param)
inline
int localvar;
{
int localvar2;
  localvar=param;
  $ret
}

    Для таких inline-функции нельзя использовать ключевое слово 'uses', при
объявлении локальных переменых инициализировать их.  Завершаться такие
функции должны ассемблерной инструкцией 'ret'.


Директива #print.
~~~~~~~~~~~~~~~~~~

    Директива #print, если за ней находится слово 'error', после вывода на
экран информационного сообщения завершает работу компилятора с кодом ошибки
10. Директива #print имеет следующий формат:

  #print [error] string or number


Директивы #ifdef #ifndef.
~~~~~~~~~~~~~~~~~~~~~~~~~~

    Директивы #ifdef и #ifndef поддерживают операции сравнения двух констант.
Пример:

  #define VERSION 1
  . . .
  #ifdef VERSION < 2
  . . .
  #else
  . . .
  #endif

    Левая часть выражения сравнения может быть составным константным
выражением. Правая часть может быть только константой.


Новые типы ресурсов.
~~~~~~~~~~~~~~~~~~~~~

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

  id_name new_type "filename"

  id_name    - идентификатор ресурса, может принимать цифровое или текстовое
               значение.
  new_type   - цифровое значение нового типа ресурсов.  Стандартные типы
	       ресурсов, поддерживаемые компилятором, имеют значения от 1 до
               22.
  "filename" - имя файла, в котором находится информация, определяемая этим
               ресурсом.

    Пример использования, определяемого пользователем ресурса, можно
посмотреть в файле control.zip.


Obj-файл для программ под Windows.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Используя сочетания ключей /obj и /w32 Вы получите объектный файл,
который потом, используя линковщик, можно соединить с другими объектными
файлами или библиотеками в программу для Windows.

    Линковщики по умолчанию создают секцию кода с атрибутами только чтение.
Компилятор C-- генерирует код и инициализированные данные в один блок и
поэтому для него необходим доступ для записи. Поэтому Вам необходимо будет
приложить усилия, чтобы получить секцию кода с атрибутами чтение-запись. Если
Вы для линковки используете tlink32.exe, то для этого Вам необходимо будет
создать файл с расширением *.def, который должен обязательно содержать
строки:

SECTIONS
    .text READ WRITE

    Имя этого def-файла потом надо будет указать в командной строке tlink32:

tlink32 *.obj [*.lib],,,,*.def


Новые возможности при работе с классами.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

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

 - конструктор имеет имя такое же, как и имя класса.
 - для конструктора нельзя указывать тип возврата и модификаторы.
 - так как оператор 'new' в C-- не проверяет, была ли выделена память
   операционной системой, то в конструкторе желательно сделать эту проверку.
 - завершаться конструктор обязательно должен возвратом переменной 'this'.

    Вот пример синтаксиса класса с использованием конструктора:

struct info
{
  info (int num); // объявляем конструктор класса info, принимающего один
                  // параметр.
  int number;
};

info::info (int num)
{
  IF ( this ) {
    number = num ;
  }
  return this;
}

 Оператор 'new' имеет следующий синтаксис:

  var = new info ( 5 );

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

void proc()
{
info s1; //выделяем память для класса в стеке
  s1.info ( 5 ); //вызываем конструктор
 ...
}

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

info::info (int num)
: info2 (int num);
{
  IF ( this ) {
    number = num ;
  }
  return this;
}

    Но можно инициализировать наследуюмые конструкторы вызовом их из
основного конструктора.

    Для того чтобы освободить память выделенную оператором 'new' введен
оператор 'delete', который сначала вызывает деструктор, а затем освобождает
блок памяти. Если для данного класса нет деструктора, то оператор 'delete'
просто освобождает память.

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

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

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

struct info
{
  info (int num); // объявляем конструктор класса info, принимающего один
                  // параметр.
  ~info();	//объявляем деструктор
  int number;
};

info::info (int num)
{
  IF ( this ) {
    number = num ;
    WRITESTR("Create new class\n");
  }
  return this;
}

info::~info ()
{
  WRITESTR("Release class\n");
}

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

void proc ()
{
info s1; //выделяем память для класса в стеке
word w1; //переменная для адреса класса в общей памяти
  s1.info ( 5 ); //вызываем конструктор
  w1 = new info (4); //выделяем память и вызываем конструктор
  delete s1; //вызываем деструктор для класса в стеке
  s1.info ( 3 ); //снова активизируем класс в стеке
  delete w1 info; //вызываем деструктор и освобождаем память класса в
                  //распределяемой памяти
}

    Оператор 'delete' для класса созданного с помощью оператора 'new', в C--
должен иметь второй обязательный параметр - имя класса к котору применяется
этот оператор.

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

    Для работы операторов 'new' и 'delete' Вам необходимо будет включать в
свой проект заголовочный файл class.h--, который вместе с примером
использования этих операторов находится в этом архиве.


Оптимизация повторной загрузки регистров.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

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

    Если Вы не хотите тестировать новый метод оптимизации или у Вас нет на
это времени, то опцией командной строки /oir- можно отключить эту
оптимизацию.


32-битные бинарные файлы.
~~~~~~~~~~~~~~~~~~~~~~~~~~

    Указав в командной строке опцию /b32, Вы получите 32-битный код без всяких
заголовков. По умолчанию, сгенерированый код буден настроен на нулевой
стартовый адрес. Если Вам необходим другой стартовый адрес, то его можно
указать с помощью директивы #imagebase startaddress или с помощью опции
командной строки /wib=startaddress. Нельзя указывать стартовый адрес до
указания типа генерируемого файла. Иначе компилятор сгенерирует код с
нулевого адреса.

    При компиляции 32-битного бинарного файла, заголовочный файл startup.h--
компилятором не используется.


Вызов процедур по адресу в регистре или переменной.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Ранее C-- позволял делать вызовы процедур по адресу, находящемуся в
регистре.  При этом параметры этой процедуре передавались в Windows
программах в соответствии с типом вызова 'stdcall' а в остальных программах в
соответствии с типом вызова 'pascal'.  Теперь, кроме этих, используемых по
умолчанию, типов, можно задавать любой другой тип передачи параметров:

  (E)AX fastcall (par1,par2);
  (E)AX cdecl (par1,par2);
  (E)AX stdcall (par1,par2);
  (E)AX pascal (par1,par2);

    При использовании типа вызова процедур 'fastcall', будьте внимательны,
чтобы передаваемые процедуре параметры не разрушили адрес вызываемой
процедуры.  При этом типе вызова процедур параметры передаются следующим
образом: 1-й - AX, 2-й - BX, 3-й - CX ...

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

unsigned int var;  //переменная, в которой находится адрес процедуры
. . .
  var cdecl (par1,par2);  // вызов процедуры, адрес которой находится в
                          // переменной 'var' и передачей ей параметров в
                          // соответствии с типом 'cdecl'


Суффиксы строковых констант.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

   суффикс|терминатор
  ------------------
    'z'   |  0
    '$'   |  $
    'n'   | нет

    Суффикс пишется сразу за символом двойной кавычки без пробелов. Пример:

    AX = "test"n; // эта строка не будет иметь терминатора
    BX = "test 2"$; // а у этой строки будет терминатором символ '$'

    Консольная версия компилятора поддерживает еще два суффикса, при наличии
которых компилятор осуществляет перекодировку строковых констант.

   суффикс|перекодировка
  -----------------------
    'o'   |из OEM в Windows
    'w'   |из Windows в OEM


Усеченная таблица импорта.
~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Таблица импорта обычно состоит в свою очередь из четырех таблиц. Две
таблицы LookUp Table и Import Address Table абсолютно одинаковы.

    Опцией командной строки /WSI Вы можете заставить компилятор генерировать
только одну из этих двух одинаковых таблиц (генерируется только Import
Address Table). Тем самым у Вас получится более компактная таблица импорта,
что приведет, в некоторых случаях, к созданию более компактного выходного
файла.


Генерация файлов для Menuet OS.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Исполняемые файлы для операционной системы Menuet имеют заголовок
следующего формата:

char  os_name[8]
dword os_version
dword start_addr
dword size_img
dword alloc_mem
dword esp_reg
dword I_Param
dword I_Icon

    os_name всегда содержит строку 'MENUET01'.

    os_version - компилятор C-- в это поле всегда записывает 1.

    start_addr содержит адрес точки входа в программу.

    size_img содержит размер файла.

    aloc_mem определяет размер памяти выделяемое операционной системой
    программе. Компилятор C-- заполняет это поле значение вычисляемым по
    формуле:

      aloc_mem = align on 16 (size_img + size_post + size_stack)

    esp_reg определяет вершину стека. Компилятор C-- устанавливает это
    значение в конец выделенной программе памяти.

    I_Param содержит адрес буфера размером 256 байт, в который записываются
    параметры передаваемые программе при запуске. Этот буфер создается если
    Вы в своей программе указали директивы #argc TRUE или #parsecommandline
    TRUE, иначе поле I_Param устанавливается в 0.

    I_Icon компилятором пока не поддерживается и всегда устанавливается в 0.

    Для создания программ под MeOS в командную строку компилятора C-- надо
добавить ключ '/meos'. При компиляции с этим ключом определяется
идентификатор __MEOS__, который можно использовать для создания библиотек.
Компилятор поддерживает для этой операционной системы внутренние функции:
ATEXIT(), EXIT(), PARAMCOUNT(), PARAMSTR().

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


Изменения в файле startup.h--.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    В файле startup.h--, кроме поддержки MeOS, переписаны блоки разбора
командной строки.  Для 16-битных режимов компиляции добавлена возможность, по
необходимости включать в программу разбор командной строки с учетом длинных
имен файлов.  Для активизации этого разбора Вам в начале компилируемого файла
определить идентификатор _USELONGNAME_ или в командной строке указать ключ
'/D=_USELONGNAME_'.

    Кроме этого в файл startup.h-- вынесены из компилятора процедуры
'PARAMCOUNT()' и 'PARAMSTR()'.


Третий алгоритм реализации оператора 'switch/SWITCH'.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Ранее оператор 'switch/SWITCH' мог быть реализован двумя способами:
методом последовательного сравнения значений 'case' и табличным методом.

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

    Новый метод является промежуточным между первыми двумя: создаются две
таблицы - таблица адресов входа в тело оператора 'switch/SWITCH' и таблица
значений 'case'.  Генерируется процедура сравнения входного значения со
значениями во второй таблице.  Если есть совпадение, то делается переход по
адресу из второй таблицы.  Новый метод является самым медленным, но при
большом числе значений 'case' (более 15) он становится самым компактным.



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

    Ранее, если в операторе сравнения указать числовое значение, например:

    WHILE (1) { code }

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

   WHILE () { code }

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

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

    Если в операции сравнения делается сравнение регистра (E)CX с нулем, то
компилятор может использовать ассемблерную инструкцию 'jcxz', что позволяет
получать более компактный код.



Смена сегмента переменной.
~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

   ES:var=1;

