(*~\Глагол\Отделы\Среда\~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*)
(**)                          ОТДЕЛ СРЕДА;
(*============================================================================*)
(* НАЗНАЧЕНИЕ: среда для выполнения Глагол-приложений.                        *)
(*   Распределяет память под создаваемые переменные и выполняет               *)
(*    уборку памяти (УП) от неиспользуемых.                                   *)
(*   В исключительных состояниях (АвОстах) сохраняет в файл "Срез.пер"        *)
(*    значения переменных у запущенных задач (из стека приложения).           *)
(* ПРИМЕЧАНИЯ:                                                                *)
(*   Свободная память запрашивается у ОС кусками. Для создаваемой             *)
(*   переменной из 'куска памяти' выбирается свободный (или нарезается новый) *)
(*   участок памяти необходимого размера. Свойства участка хранятся в         *)
(*   описателе участка (ОУ), расположенном до значения переменной.            *)
(*   Для уборки памяти используются Данные о Видах во время выполнения (ДВ),  *)
(*   которые создаются Преобразователем Глагола (ПГ).                         *)
(*  "Куски памяти" организованны в кольцевой список.                          *)
(*                                                                            *)
(* Размещение ДВ набора в области ПОСТоянных:                                 *)
(* ДВ->  вид:4;   |                (Данные о Видах во время выполнения)       *)
(*       имя:...; } размерДВ ячеек                                            *)
(*       ...      |                                                           *)
(*       размерДВ     :4;                                                     *)
(*       адр(ДВ.имя)  :4;                                                     *)
(*       размер набора:4;                                                     *)
(* ОВ->  ОВ1:4;   |                (Описатель Вида)                           *)
(*       ОВ2:4;   |                                                           *)
(*       ...      } ВсегоРасширений шт.                                       *)
(*       0  :4;   |                                                           *)
(*       ...      |                                                           *)
(*       0  :4;   |                                                           *)
(*============================================================================*)

ИСПОЛЬЗУЕТ
  ОБХОД,
(*          \Глагол\                   *)
  ОС      ИЗ "..\..\Отделы\Обмен\",
  Текст   ИЗ "..\..\Отделы\Обмен\",
(*          \Глагол\                   *)
  Асм     ИЗ "..\..\Отделы\Заграница\",  (* Асм - системные задачи на ASM x86 *)
(*          \Глагол\                   *)
 оБуквы   ИЗ "..\..\Отделы\Иное\",
(*          \Глагол\                   *)
  НаЭкран ИЗ "..\..\Отделы\Обмен\ВводВывод\",
   ВФайл  ИЗ "..\..\Отделы\Обмен\ВводВывод\";

ПОСТ    (* локальные постоянные и переменные в отделе СРЕДА.отд
         *)
                         (* размер запрашиваемого у ОС куска свободной памяти *)
  РазмерКуска=10000H;  (* 10000H = 1+FFFFH = 16*16*16*16 = 2^16 = 65536 = 64 Кбайт *)
        (* выделяется памяти перед принудительным запуском Уборки Памяти (УП) *)
  ЗапускУП=100*РазмерКуска;                    (* Запуск   У      П     = 6400 Кбайт *)
          (* предельный уровень вложения доступов при записи среза переменных *)
  УровеньД=4;
          (* предельный уровень вложения  задач   при записи среза переменных *)
  УровеньЗ=30;   

  (*-----------------------------------------------------------
   * количественные ограничители Преобразователя Глагола (ПГ)
   *-----------------------------------------------------------*)

        рНазвания = 32;(* наибольший размер/длина названия.      Из отдела НП *)
  ВсегоРасширений = 10;(* наибольший уровень расширения наборов. Из отдела ЗК *)
     ВсегоОтделов =128;(* количество отделов.                    Из отдела ВД *)

  (* Программные ловушки/(аварийные остановы).                   Из отдела СК *)

  пределыАвОст=1;
    номерАвОст=2;
    арифмАвОст=3;
   охранаАвОст=4;
    пустоАвОст=5;
      дляАвОст=6;
  выбратьАвОст=7;
    ответАвОст=8;
проверитьАвОст=9;
     нольАвОст=10;
  бегунокАвОст=11;  (* при использовании вида БЕГУНОК *)
    (*
     * Род видов (Вид.родв).                                    Из отдела ВД.
     *   Причём:
     * вЯчейка <= вЦел8 <= вЗнак <= вЦел16 <= вЦел32 <= вЦел64.
     *)
   вНеопр=0;
  вЯчейка=1;
    вКлюч=2;
    вЗнак=3;
    вЦел8=4;
   вЦел16=5;
   вЦел32=6;
   вЦел64=7;
   вВещ32=8;
   вВещ64=9;

   вМнож=10;
  вПЦепь=11;
  вПусто=12;
    вНет=13;
 вДоступ=14;
 вЗадача=15;
вБегунок=16;
    вРяд=17;
   вОРяд=18; (* при использовании вида Открытый Ряд *)
  вНабор=19;
    вКон=20;
 (*
  *               вКон для ДВ (Данных о Видах времени выполнения).
  * При изменении вКон нужно 3 преобразования с уборка:=ОТКЛ
  *)
ВИД
 (*
  * описатель Куска находится в начале каждого куска памяти,
  *                 полученного от ОС
  *)
  ДкОпКуска- = ДОСТУП К нОпКуска; (* доступ к НАБОРу - Описателю Куска памяти *)
                        нОпКуска =        (* вид-набор Описатель Куска памяти - 
                           звено в односвязном кольцевом списке кусков памяти *)
  НАБОР
    адрес_1гоСвобУч-:ЦЕЛ; (* адрес первого свободного участка в куске  памяти *)
    размер-:ЦЕЛ;          (* количество        ячеек-участков в куске  памяти *)
    адрес_1гоУч-:ЦЕЛ;     (* адрес первого            участка в куске  памяти *)
    следКусокД- :ДкОпКуска(* доступ к следующему куску - для организации
                                 односвязного кольцевого списка кусков памяти *)
  КОН;

ПОСТ           (* локальные в отделе СРЕДА.отд
                *)
  РазмерОпКуска=16;                (* размер в байтах Описателя Куска  памяти *)

ПОСТ                                                            (* глобальные *)
 (*
  * Позиции свойств Описателя Участка (ОУ) относительно его начала.
  * Описатель участка предшествует каждому участку памяти, выделенному
  * задачей СОЗДАТЬ. Т.к. преобразователь подразумевает, что 4 ячейки,
  * предшествующие данным, всегда содержат адрес описателя вида (ОписВ),
  * то <ПозУчОВ> всегда должен указывать на последнее свойство ОУ.       
  *)
  ПозРазмераУч-        =0; (* расположение/позиция размера участка (если < 0, то участок свободен) *)
  ПозАдресаСледСвобУч- =4; (* расположение/позиция адреса следующего свободного участка            *)
  ПозАдреса_ОВ_Уч-     =4; (* расположение/позиция адреса описателя вида (ОписВ) участка           *)
  РазмерОпУч-          =8; (* размера Описателя Участка                       *)

ВИД                                                             (* глобальные *)
  Название- = ЦЕПЬ[рНазвания];
 (*
  * ОпОтдела - Описатель Отдела (ОО) создаётся преобразователем Глагола
  *                                  в области постоянных
  *)
  ДкОпОтдела- = ДОСТУП К нОпОтдела;  (* доступ к НАБОРу - Описателю Отдела *)
                         нОпОтдела =         (* вид-набор Описатель Отдела *)
  НАБОР
    название-           :СРЕДА.Название;
    адр_Перем-          :ЦЕЛ;          (* адрес переменных уровня отдела      *)
    адр_1йЗадачи-       :ЦЕЛ;          (* адрес первой задачи                 *)
    адр_заПослЗадачей-  :ЦЕЛ;          (* адрес за последней задачей          *)
    адр_ДВ_перемОтд-    :ЦЕЛ;          (* адрес ДВ переменных уровня отдела   *)
    длина_ДВ_перемОтд-  :ЦЕЛ;          (* длина ДВ переменных уровня отдела   *)
    рОписанийЗадач-     :РЯД 1 ИЗ            (* начало ряда из описаний задач *)
                                  НАБОР       
               адрес_начала_задачи-   :ЦЕЛ;         (* адрес начала    задачи *)
               адрес_начала_ДВ_задачи-:ЦЕЛ;         (* адрес начала ДВ задачи *)
                                  КОН
  КОН;

ПЕР                                                             (* глобальные *)
  рАдреса_опОтделов-:РЯД ВсегоОтделов ИЗ ЦЕЛ;    (* адреса описателей отделов *)
  всего_Адресов_опОтделов-:ЦЕЛ;           (* всего адресов описателей отделов *)
  уборка+   :КЛЮЧ;    (* ВКЛ, если разрешена периодическая уборка памяти (УП) *)
  памПослеУП:ЦЕЛ;     (* выделенно ячеек памяти после прошлой УП              *)
  адрес_текКуска- :ЦЕЛ; (* адрес текущего куска в кольцевом списке кусков     *)
  адрес_текСвобУч-:ЦЕЛ; (* адрес свободного участка в тек.куске <теккус>      *)

   (*--- Запись среза переменных в исключительном состоянии (авост) ---*)
ПЕР                                                              (* локальные *)
  поток:ВФайл.ДкПотоку;                  (* для записи среза переменных *)
  отступ:ЦЕЛ;                          (* отступ на строке
                                                             при записи среза *)
  текУровеньВложенияДоступов:ЦЕЛ;      (* тек.уровень вложения доступов
                                                             при записи среза *)


(*-оооооооооооооо- опережающие определения/заготовки задач -ооооооооооооооооо-*)


(*-------------------------------------общедоступная ЗАДАЧА-------------------*)
ЗАДАЧА^ ИмяИзОВ-(адрес_ОВ:ЦЕЛ; имя+:ЦЕПЬ);
(*-------------------------------------общедоступная ЗАДАЧА-------------------*)
ЗАДАЧА^ ОВИзПер-(пер:ЦЕЛ; адрес_ОВ+:ЦЕЛ);
 (*
(*-------------------------------------общедоступная ЗАДАЧА-------------------*)
ЗАДАЧА^ ИмяВИзПер-(пер:ЦЕЛ; имя+:ЦЕПЬ);
  *)

(*----------------------------------------------------------------------------*
 * заготовки Задач для чтения ДВ (данных о видах времени выполнения)
 *----------------------------------------------------------------------------*)

(*-------------------------------------общедоступная ЗАДАЧА-------------------*)
ЗАДАЧА^ ЦелИзДВ-(текАдрес_чтения_ДВ+,
                                цел+:ЦЕЛ);
(* Цель:  прочитать целое число <цел> из ДВ
 * До:    <текАдрес_чтения_ДВ> - текущий адрес чтения ДВ
 *)
(*-------------------------------------общедоступная ЗАДАЧА-------------------*)
ЗАДАЧА^ ЦепьИзДВ-(текАдрес_чтения_ДВ+:ЦЕЛ;
                                цепь+:ЦЕПЬ);
(* Цель:  прочитать в <цепь> цепочку знаков
 * До:    <текАдрес_чтения_ДВ> - текущий адрес чтения ДВ
 *)
(*-------------------------------------общедоступная ЗАДАЧА-------------------*)
ЗАДАЧА^ ВидИмяИзДВ-(текАдрес_чтения_ДВ+,
                                   вид+:ЦЕЛ;
                                   имя+:ЦЕПЬ);
(* Цель:  прочитать в <вид> метку ДВ, а в <имя> - название ДВ
 * До:    <текАдрес_чтения_ДВ> - текущий адрес чтения ДВ
 *)
(*-------------------------------------общедоступная ЗАДАЧА-------------------*)
ЗАДАЧА^ НаборИзДВ-
       (                   начАдрес_ОблПерем,текАдрес_чтения_ДВ+:ЦЕЛ;
         Обработка:ЗАДАЧА (начАдрес_ОблПерем,текАдрес_чтения_ДВ+:ЦЕЛ)
       );
(* Цель:  обработать ДВ набора
 * До:    <начАдрес_ОблПерем>  - адрес начала области переменных
 *        <текАдрес_чтения_ДВ> - текущий адрес чтения ДВ
 *)
(*-------------------------------------общедоступная ЗАДАЧА-------------------*)
ЗАДАЧА^ ПропуститьДВ-(начАдрес_ОблПерем,
                      текАдрес_чтения_ДВ+:ЦЕЛ);
(* Цель:  увеличить <текАдрес_чтения_ДВ> на столько, чтобы пропустить один ДВ
 * До:    <начАдрес_ОблПерем>  - адрес начала области переменных
 *        <текАдрес_чтения_ДВ> - текущий адрес чтения ДВ
 *)

(*----------------------------------------------------------------------------*
 * Шаги по уборке памяти (УП):
 * а) все ранее распределённые участки памяти переводим в разряд
 *    неиспользуемых (выставляем их размеры отрицательными);
 * б) отмечаем участки памяти всех размещённых переменных доступных на
 *    данный момент как на уровне отдела так и через стек (в том числе и
 *    через переменные доступа), как используемые (выставляем их размеры
 *    положительными);
 * в) считаем, что оставшиеся участки с отрицательными размерами больше не
 *    используются приложением, и их можно использовать для новых переменных;
 * г) смежные свободные участки памяти объединяем;
 * д) целиком свободные куски памяти возвращаем ОС.
 *----------------------------------------------------------------------------*)

(*-----------------------------------------локальная ЗАДАЧА-------------------*)
ЗАДАЧА^ ОтметитьВПер(начАдрес_ОблПерем,
                     текАдрес_чтения_ДВ+:ЦЕЛ); (*- заготовка локальной задачи.

                                                  Вызов из рекурсивной задачи
        НаборИзД-(                  начАдрес_ОблПерем,адрес_ОВ:ЦЕЛ;
                  Обработка:ЗАДАЧА (начАдрес_ОблПерем,
                                    текАдрес_чтения_ДВ+:ЦЕЛ));
                                                -*)
(*-------------------------------------общедоступная ЗАДАЧА-------------------*)
ЗАДАЧА^ ОтметитьВРяду-(начАдрес_ПеремСлагаемого,
                       текАдрес_чтения_ДВ+,
                                     чслаг,
                                     рслаг:ЦЕЛ);
(*
 * Цель:  отметить слагаемые ряда
 * До:    <начАдрес_ПеремСлагаемого> - адрес начала переменной слагаемого
 *        <текАдрес_чтения_ДВ>       - текущий адрес чтения ДВ
 *        <чслаг> - число слагаемых ряда
 *        <рслаг> - размер одного слагаемого
 *)
(*-------------------------------------общедоступная ЗАДАЧА-------------------*)
                                              (* заготовка рекурсивной задачи *)
ЗАДАЧА^ НаборИзД-(                  начАдрес_ОблПерем,адрес_ОВ:ЦЕЛ;
                  Обработка:ЗАДАЧА (начАдрес_ОблПерем,
                                    текАдрес_чтения_ДВ+:ЦЕЛ)
                 );
(*
 * Цель:  найти размещённые переменные набора и обработать их
 * До:    <начАдрес_ОблПерем> - адрес начала области переменных
 *)

 (*=-
(*-------------------------------------общедоступная ЗАДАЧА-------------------*)
ЗАДАЧА^ ПроверитьКучу-();
 (*
  * Цель:  проверить строение всей кучи - динамическая память
  *)
  -=*)

(*-------------------------------------общедоступная ЗАДАЧА-------------------*)
ЗАДАЧА^ GetEBP-():ЦЕЛ;
 (*
  * Цель:  возвратить значение регистра EBP вызвавшей задачи
  *)
(*-------------------------------------общедоступная ЗАДАЧА-------------------*)
ЗАДАЧА^ НайтиДВЗадачи-(адр,
                       номерОтдела+,
                       адрес_начала_ДВ_задачи+:ЦЕЛ);
 (*
  * Цель:  найти задачу и её ДВ по внутреннему адресу задачи <адр>
  * После: <адрес_начала_ДВ_задачи> - начальный адрес ДВ задачи
  *        <номерОтдела> - номер отдела, в котором находится задача или 0
  *)
(*-------------------------------------общедоступная ЗАДАЧА-------------------*)
ЗАДАЧА^ Уборка-();
 (*
  * Цель:  найти и объединить неиспользуемые участки памяти
  * После: <адрес_текКуска>  - адрес начального куска в кольцевом списке кусков
  *        <адрес_текСвобУч> - адрес первого свободного участка <адрес_текКуска>           
  *)

(*----------------------------------------------------------------------------*
 * заготовки Задач, вызываемых из приложения Windows неявным образом
 *----------------------------------------------------------------------------*)

(*-------------------------------------общедоступная ЗАДАЧА-------------------*)
ЗАДАЧА^ Создать-(адрес_ОВ,
                   размер,
                    адрес+:ЦЕЛ);
 (*
  * Цель:  поддержка встроенных в Глагол задач:
  *        СОЗДАТЬ(<пер>+:вид)                      - для наборов и рядов
  *        СОЗДАТЬ(<пер>+:вид; <ДЛ1>,...,<ДЛn>:ЦЕЛ) - для открытых рядов
  * До:    <адрес_ОВ> - описатель вида переменной <пер>
  *                     для рядов и открытых рядов <адрес_ОВ> = 0
  *        <размер>   - размер данных у <пер>
  *                     для открытых рядов <размер> = 4+n*4+<ДЛ1>*...*<ДЛn>
  * После: <адрес>    - адрес выделенной памяти заполненной 0-ми,
  *                     четыре ячейки перед <адрес> всегда содержат <адрес_ОВ>
  *)
(*-------------------------------------общедоступная ЗАДАЧА-------------------*)
ЗАДАЧА^ Присоединить-(адрОО:ЦЕЛ);
 (*
  * Цель:  присоединить к среде отдел, где <адрОО> адрес описателя отдела,
  *        расположенного в постоянной области кода
  * Прим:  вызывается однократно для каждого отдела при запуске приложения
  *)


(*----------------------------------------------------------------------------*
 * заготовки Задач для Записи среза значений переменных у работающих задач
 *             в исключительном состоянии (при АвОсте) в файл "Срез.пер"
 *----------------------------------------------------------------------------*)

(*-----------------------------------------локальная ЗАДАЧА-------------------*)
ЗАДАЧА^ Пер(начАдрес_ОблПерем,
            текАдрес_чтения_ДВ+:ЦЕЛ); (*- заготовка локальной рекурсивной задачи.

                                                   Вызов из локальной задачи
        ПисатьРядЦел4числа(
                           начАдрес_ПеремСлагаемого,
                           текАдрес_чтения_ДВ+,чслаг,рслаг:ЦЕЛ
                          );
                                      -*)
 (*
  * Цель:  выписать значение переменной
  * До:    <начАдрес_ОблПерем>  - адрес начала области переменных
  *        <текАдрес_чтения_ДВ> - текущий адрес чтения ДВ
  *)
(*-------------------------------------общедоступная ЗАДАЧА-------------------*)
ЗАДАЧА^ Ловушка-(адрОО,
                 кодстрока:ЦЕЛ);
 (*
  * Цель:  ловушка исключений
  *)


(*=рррррррррррррррррррррррр= реализация задач =рррррррррррррррррррррррррррррр=*)


(*============================================================================*
 * Задачи-помощники для работы с (описателями) вида и переменными приложения
 *============================================================================*)


(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~общедоступная ЗАДАЧА~~~~~~~~~~~~~~~~~~~*)
ЗАДАЧА ИмяИзОВ-(адрес_ОВ:ЦЕЛ; имя+:ЦЕПЬ);
ПЕР
  названиеД:ДОСТУП К Название;                    (* Доступ к НАБОРу Название *)
УКАЗ
  ЕСЛИ адрес_ОВ = 0 ТО
    СПИСАТЬ("ПУСТО",имя)
  ИНАЧЕ
    ОБХОД.ИзПамяти(адрес_ОВ-8,названиеД);
       (* записать по адресу <названиеД>
                   из адреса <адрес_ОВ-8> *)
    СПИСАТЬ(названиеД^,имя)
  КОН
КОН ИмяИзОВ;

(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~общедоступная ЗАДАЧА~~~~~~~~~~~~~~~~~~~*)
ЗАДАЧА ОВИзПер-(пер:ЦЕЛ; адрес_ОВ+:ЦЕЛ);
УКАЗ
  ЕСЛИ пер = 0 ТО
    адрес_ОВ:=0
  ИНАЧЕ
    ОБХОД.ИзПамяти(пер-РазмерОпУч+ПозАдреса_ОВ_Уч,адрес_ОВ)
                         (* записать по адресу <адрес_ОВ>
                                     из адреса <пер-РазмерОпУч+ПозАдреса_ОВ_Уч> *)
  КОН
КОН ОВИзПер;

 (*=-
(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~общедоступная ЗАДАЧА~~~~~~~~~~~~~~~~~~~*)
ЗАДАЧА ИмяВИзПер-(пер:ЦЕЛ;
                 имя+:ЦЕПЬ);
ПЕР
  адрес_ОВ:ЦЕЛ;
УКАЗ
  ОВИзПер(пер,адрес_ОВ);
  ИмяИзОВ(адрес_ОВ,имя)
КОН ИмяВИзПер;
 -=*)


(*============================================================================*
 * Задачи для чтения ДВ (данных о видах времени выполнения)
 *============================================================================*)


(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~общедоступная ЗАДАЧА~~~~~~~~~~~~~~~~~~~*)
ЗАДАЧА ЦелИзДВ-(текАдрес_чтения_ДВ+,
                               цел+:ЦЕЛ);
(*
 * Цель:  прочитать целое число <цел> из ДВ
 * До:    <текАдрес_чтения_ДВ> - текущий адрес чтения ДВ 
 *)
УКАЗ
  ОБХОД.ИзПамяти(текАдрес_чтения_ДВ,цел);
             (* записать по адресу <цел>
                         из адреса <текАдрес_чтения_ДВ> *)
  УВЕЛИЧИТЬ(текАдрес_чтения_ДВ,4)
КОН ЦелИзДВ;

(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~общедоступная ЗАДАЧА~~~~~~~~~~~~~~~~~~~*)
ЗАДАЧА ЦепьИзДВ-(текАдрес_чтения_ДВ+:ЦЕЛ;
                               цепь+:ЦЕПЬ);
(*
 * Цель:  прочитать в <цепь> цепочку знаков
 * До:    <текАдрес_чтения_ДВ> - текущий адрес чтения ДВ
 *)
ПЕР
  поз:ЦЕЛ;
  зн:ЗНАК;
УКАЗ
  поз:=0;
  ПОВТОРЯТЬ
    ОБХОД.ИзПамяти(текАдрес_чтения_ДВ,зн);
               (* записать по адресу <зн>
                           из адреса <текАдрес_чтения_ДВ> *)
         УВЕЛИЧИТЬ(текАдрес_чтения_ДВ,2);
    ЕСЛИ поз < РАЗМЕР(цепь) ТО
      цепь[поз]:=зн
    КОН;
    УВЕЛИЧИТЬ(поз)
  ДО зн = 0X
КОН ЦепьИзДВ;

(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~общедоступная ЗАДАЧА~~~~~~~~~~~~~~~~~~~*)
ЗАДАЧА ВидИмяИзДВ-(текАдрес_чтения_ДВ+,
                                  вид+:ЦЕЛ;
                                  имя+:ЦЕПЬ);
(*
 * Цель:  прочитать в <вид> метку ДВ, а в <имя> - название ДВ
 * До:    <текАдрес_чтения_ДВ> - текущий адрес чтения ДВ
 *)
УКАЗ
   СРЕДА.ЦелИзДВ(текАдрес_чтения_ДВ,вид); (* +вид *)
  СРЕДА.ЦепьИзДВ(текАдрес_чтения_ДВ,имя)  (* +имя *)
КОН ВидИмяИзДВ;

(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~общедоступная ЗАДАЧА~~~~~~~~~~~~~~~~~~~*)
ЗАДАЧА НаборИзДВ-
      (                   начАдрес_ОблПерем,текАдрес_чтения_ДВ+:ЦЕЛ;
        Обработка:ЗАДАЧА (начАдрес_ОблПерем,текАдрес_чтения_ДВ+:ЦЕЛ)
      );
(*
 * Цель:  обработать ДВ набора
 * До:    <начАдрес_ОблПерем>  - адрес начала области переменных
 *        <текАдрес_чтения_ДВ> - текущий адрес чтения ДВ
 *)
ПЕР
  вид:ЦЕЛ;
УКАЗ
    ОБХОД.ИзПамяти(текАдрес_чтения_ДВ,вид);
               (* записать по адресу <вид>
                           из адреса <текАдрес_чтения_ДВ> *)
  ПОКА
       вид # вКон                                      (* ПОСТоянная вКон=20; *)
  ВЫП                           
    Обработка(начАдрес_ОблПерем,текАдрес_чтения_ДВ);
    ОБХОД.ИзПамяти(текАдрес_чтения_ДВ,вид)
               (* записать по адресу <вид>
                           из адреса <текАдрес_чтения_ДВ> *)
  КОН;
  УВЕЛИЧИТЬ(текАдрес_чтения_ДВ,4)             (* +вид *)
КОН НаборИзДВ;

(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~общедоступная ЗАДАЧА~~~~~~~~~~~~~~~~~~~*)
ЗАДАЧА ПропуститьДВ-(начАдрес_ОблПерем,
                     текАдрес_чтения_ДВ+:ЦЕЛ);
 (*
  * Цель:  увеличить <текАдрес_чтения_ДВ> на столько, чтобы пропустить один ДВ
  * До:    <начАдрес_ОблПерем>  - адрес начала области переменных
  *        <текАдрес_чтения_ДВ> - текущий адрес чтения ДВ
  *)
ПЕР
  вид:ЦЕЛ;
  имя:Название;
УКАЗ
                              СРЕДА.ВидИмяИзДВ(текАдрес_чтения_ДВ,вид,имя);       (* +вид *)
                                     УВЕЛИЧИТЬ(текАдрес_чтения_ДВ,4);   (* +отнАдресПерем *)
  ВЫБРАТЬ вид ИЗ
    вДоступ:
                              СРЕДА.ВидИмяИзДВ(текАдрес_чтения_ДВ,вид,имя);       (* +вид *)
                                     УВЕЛИЧИТЬ(текАдрес_чтения_ДВ,4);   (* +отнАдресПерем *)
      ВЫБРАТЬ вид ИЗ
        вНабор:
                                     УМЕНЬШИТЬ(текАдрес_чтения_ДВ,4);   (* -отнАдресПерем *)
      | вРяд:
                                     УВЕЛИЧИТЬ(текАдрес_чтения_ДВ,8);    (* +рслаг +чслаг *)
          СРЕДА.ПропуститьДВ(начАдрес_ОблПерем,текАдрес_чтения_ДВ)
      | вОРяд:
                                     УВЕЛИЧИТЬ(текАдрес_чтения_ДВ,4);    (* +рслаг *)
          СРЕДА.ПропуститьДВ(начАдрес_ОблПерем,текАдрес_чтения_ДВ)
      | вБегунок:
          СРЕДА.ПропуститьДВ(начАдрес_ОблПерем,текАдрес_чтения_ДВ)
      | вНеопр:
      КОН
  | вРяд:
                                     УВЕЛИЧИТЬ(текАдрес_чтения_ДВ,8);    (* +рслаг +чслаг *)
          СРЕДА.ПропуститьДВ(начАдрес_ОблПерем,текАдрес_чтения_ДВ)
  | вБегунок:
          СРЕДА.ПропуститьДВ(начАдрес_ОблПерем,текАдрес_чтения_ДВ)
  | вНабор:
             СРЕДА.НаборИзДВ(начАдрес_ОблПерем,текАдрес_чтения_ДВ,СРЕДА.ПропуститьДВ)
  ИНАЧЕ
  КОН
КОН ПропуститьДВ;


(*============================================================================*
 * Шаги по уборке памяти (УП):
 * а) все ранее распределённые участки памяти переводим в разряд
 *    неиспользуемых (выставляем их размеры отрицательными);
 * б) отмечаем участки памяти всех размещённых переменных доступных на
 *    данный момент как на уровне отдела так и через стек (в том числе и
 *    через переменные доступа), как используемые (выставляем их размеры
 *    положительными);
 * в) считаем, что оставшиеся участки с отрицательными размерами больше не
 *    используются приложением, и их можно использовать для новых переменных;
 * г) смежные свободные участки памяти объединяем;
 * д) целиком свободные куски памяти возвращаем ОС.
 *============================================================================*)

 (*
ЗАДАЧА^ ОтметитьВПер(начАдрес_ОблПерем,
                     текАдрес_чтения_ДВ+:ЦЕЛ); (*- заготовка локальной задачи.

                                                  Вызов из рекурсивной задачи
        НаборИзД-(                  начАдрес_ОблПерем,адрес_ОВ:ЦЕЛ;
                  Обработка:ЗАДАЧА (начАдрес_ОблПерем,текАдрес_чтения_ДВ+:ЦЕЛ));
                                                 -*)
 *)
(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~общедоступная ЗАДАЧА~~~~~~~~~~~~~~~~~~~*)
ЗАДАЧА ОтметитьВРяду-(начАдрес_ПеремСлагаемого,
                           текАдрес_чтения_ДВ+,
                                         чслаг,
                                         рслаг:ЦЕЛ);
(*
 * Цель:  отметить слагаемые ряда
 * До:    <начАдрес_ПеремСлагаемого> - адрес начала переменной слагаемого
 *        <текАдрес_чтения_ДВ>       - текущий адрес чтения ДВ
 *        <чслаг> - число слагаемых ряда
 *        <рслаг> - размер одного слагаемого
 *)
ПЕР
  текАдрес_чтения_ДВ_слагаемого:ЦЕЛ;    (* текущий адрес чтения ДВ слагаемого *)
  вид:ЦЕЛ;                              (* вид слагаемых *)
УКАЗ
  ОБХОД.ИзПамяти(текАдрес_чтения_ДВ,вид);
             (* записать по адресу <вид>
                         из адреса <текАдрес_чтения_ДВ> *)
  ЕСЛИ вид В {вДоступ,вНабор,вРяд,вОРяд} ТО
                                               (* пробегаем по всем слагаемым *)
    ПОКА чслаг > 0 ВЫП
                                                текАдрес_чтения_ДВ_слагаемого:=текАдрес_чтения_ДВ;
      ОтметитьВПер(начАдрес_ПеремСлагаемого,
                                                текАдрес_чтения_ДВ_слагаемого);
         УВЕЛИЧИТЬ(начАдрес_ПеремСлагаемого,рслаг);
         УМЕНЬШИТЬ(чслаг)
    КОН;
                                                текАдрес_чтения_ДВ:=текАдрес_чтения_ДВ_слагаемого
  ИНАЧЕ                                           (* пропускаем ДВ слагаемого *)
    СРЕДА.ПропуститьДВ(начАдрес_ПеремСлагаемого,текАдрес_чтения_ДВ)
  КОН
КОН ОтметитьВРяду;

(*-----------------------------------------локальная ЗАДАЧА-------------------*)
ЗАДАЧА ОтмечаемУчПер(пер:ЦЕЛ):КЛЮЧ;
(*
 * Цель:  искать в "куче" и отметить участок памяти переменной с адресом <пер>
 * Ответ: ВКЛ, если участок ещё не был отмечен
 *)
ПЕР
  описьНачКусокД    :ДкОпКуска;  (* начальный кусок в кольцевом списке кусков *)
  описьТекКусокД    :ДкОпКуска;  (* текущий кусок в кольцевом списке кусков   *)
  адресУч           :ЦЕЛ;        (* адрес участка *)
  адресУч_переменной:ЦЕЛ;        (* адрес похожий на адрес участка переменной *)
  размУч            :ЦЕЛ;        (* размер участка *)
УКАЗ
  ЕСЛИ пер # 0 ТО                (* не ПУСТО *)
    адресУч_переменной:=пер-РазмерОпУч;
                                 (* ищем кусок, содержащий адресУч_переменной *)
    описьТекКусокД:=ОБХОД.Значение(ДкОпКуска,адрес_текКуска);
            (* преобразование в вид нОпКуска
                                  по адресу <адрес_текКуска> *)
    описьНачКусокД:=описьТекКусокД;
    ПОВТОРЯТЬ
      ЕСЛИ (адресУч_переменной >= описьТекКусокД.адрес_1гоУч)
          И
           (адресУч_переменной <  описьТекКусокД.адрес_1гоУч+
                                  описьТекКусокД.размер)
      ТО                                                       (* нашли кусок *)
                                                (* ищем участок *)
               адресУч:=описьТекКусокД.адрес_1гоУч;
        ПОКА 
               адресУч <= адресУч_переменной
        ВЫП
          ОБХОД.ИзПамяти(адресУч+ПозРазмераУч,размУч);
                       (* записать по адресу <размУч>
                                   из адреса <адресУч+ПозРазмераУч> *)
          ЕСЛИ 
               адресУч = адресУч_переменной
          ТО                                    (* нашли участок *)
            ЕСЛИ размУч < 0 ТО               (* он ещё не отмечен как занятый *)
              ОБХОД.ВПамять(адресУч+ПозРазмераУч,-размУч);
                          (* записать из адреса <-размУч>
                                       в адрес  <адресУч+ПозРазмераУч> *)
              ВОЗВРАТ ВКЛ
            ИНАЧЕ
              ВОЗВРАТ ОТКЛ
            КОН
          КОН;
          УВЕЛИЧИТЬ(адресУч,МОДУЛЬ(размУч))
        КОН
      КОН;
       описьТекКусокД:=описьТекКусокД.следКусокД
    ДО описьТекКусокД = описьНачКусокД            (* обошли всё кольцо кусков *)
  КОН;
  ВОЗВРАТ ОТКЛ
КОН ОтмечаемУчПер;

 (*
                                              (* заготовка рекурсивной задачи *)
ЗАДАЧА^ НаборИзД-(                  начАдрес_ОблПерем,адрес_ОВ:ЦЕЛ;
                  Обработка:ЗАДАЧА (начАдрес_ОблПерем,
                                    текАдрес_чтения_ДВ+:ЦЕЛ));
 *)
(*-----------------------------------------локальная ЗАДАЧА-------------------*)
ЗАДАЧА ОтметитьВПер(начАдрес_ОблПерем,
                    текАдрес_чтения_ДВ+:ЦЕЛ);
(*
 * Цель: разбирая ДВ (Данные о Видах времени выполнения), найти
 *       размещённые переменные и отметить их участки памяти
 * До:   <начАдрес_ОблПерем>  - адрес начала области переменных
 *       <текАдрес_чтения_ДВ> - текущий адрес чтения ДВ
 *)
ПЕР
  отнАдресПерем   :ЦЕЛ; (* адрес переменной относительно 
                                         <начАдрес_ОблПерем>                  *)
  полныйАдресПерем:ЦЕЛ; (* полный адрес переменной равен
                                         <начАдрес_ОблПерем>+<отнАдресПерем>  *)
  адрес_ОВ        :ЦЕЛ;       (* адрес ОВ                           *)
  зДОСТУП         :ЦЕЛ;       (* значение ДОСТУП                    *)
  вид             :ЦЕЛ;       (* ДВ метка вида                      *)
  имя             :Название;  (* имя переменной (вида)              *)
  чслаг           :ЦЕЛ;       (* число слагаемых ряда               *)
  рслаг           :ЦЕЛ;       (* размер одного слагаемого           *)
  числоРазмерностей:ЦЕЛ;      (* число размерностей у вОРяд         *)
  номерРазмерности:ЦЕЛ;       (* номер размерности вОРяд            *)
  длинаРазмерности:ЦЕЛ;       (* длина размерности вОРяд            *)

УКАЗ
  СРЕДА.ВидИмяИзДВ(текАдрес_чтения_ДВ,вид,имя);     (* +вид *)
  СРЕДА.ЦелИзДВ(текАдрес_чтения_ДВ,отнАдресПерем);         (* +отнАдресПерем  *)

  ПРОВЕРИТЬ(вид # вНет);

  ВЫБРАТЬ вид ИЗ
    вДоступ:
      ОБХОД.ИзПамяти(начАдрес_ОблПерем+отнАдресПерем,зДОСТУП);
                              (* записать по адресу <зДОСТУП>
                                          из адреса <начАдрес_ОблПерем+отнАдресПерем> *)
      СРЕДА.ВидИмяИзДВ(текАдрес_чтения_ДВ,вид,имя); (* +вид *)
      СРЕДА.ЦелИзДВ(текАдрес_чтения_ДВ,отнАдресПерем);     (* +отнАдресПерем  *)

      ВЫБРАТЬ вид ИЗ
        вНабор:
          УМЕНЬШИТЬ(текАдрес_чтения_ДВ,4);                 (* -отнАдресПерем  *)
          ЕСЛИ
               ОтмечаемУчПер(зДОСТУП)
          ТО
            СРЕДА.ОВИзПер(зДОСТУП,адрес_ОВ);
                                                          (* рекурсивный вызов задачи
                                            ОтметитьВПер() *)
            СРЕДА.НаборИзД(зДОСТУП,адрес_ОВ,ОтметитьВПер)
          КОН
      | вРяд:
          СРЕДА.ЦелИзДВ(текАдрес_чтения_ДВ,рслаг);    (* +рслаг *)
          СРЕДА.ЦелИзДВ(текАдрес_чтения_ДВ,чслаг);    (* +чслаг  *)
          ЕСЛИ 
               ОтмечаемУчПер(зДОСТУП)
          ТО
            СРЕДА.ОтметитьВРяду(зДОСТУП,текАдрес_чтения_ДВ,чслаг,рслаг)
          ИНАЧЕ
            СРЕДА.ПропуститьДВ (зДОСТУП,текАдрес_чтения_ДВ)
          КОН
      | вОРяд:
          СРЕДА.ЦелИзДВ(текАдрес_чтения_ДВ,рслаг);    (* +рслаг *)
 (*
  * по адресу ОРЯД 0-е слово содержит его размерность,
  * а следующие слова содержат РАЗМЕР его измерений
  *)
          ЕСЛИ ОтмечаемУчПер(зДОСТУП) ТО
            ОБХОД.ИзПамяти(зДОСТУП,числоРазмерностей);
                              (* записать по адресу <числоРазмерностей>
                                          из адреса <зДОСТУП> *)
            чслаг:=1;
            номерРазмерности:=1;
            ПОКА номерРазмерности <= числоРазмерностей ВЫП
              ОБХОД.ИзПамяти(зДОСТУП+номерРазмерности*4,длинаРазмерности);
                                           чслаг:=чслаг*длинаРазмерности;
              УВЕЛИЧИТЬ(номерРазмерности)
            КОН;
            СРЕДА.ОтметитьВРяду(зДОСТУП+номерРазмерности*4,текАдрес_чтения_ДВ,чслаг,рслаг)
          ИНАЧЕ
            СРЕДА.ПропуститьДВ (зДОСТУП,текАдрес_чтения_ДВ)
          КОН
      | вБегунок:
          СРЕДА.ПропуститьДВ(начАдрес_ОблПерем,текАдрес_чтения_ДВ)
      | вНеопр:
      КОН
  | вРяд:
      СРЕДА.ЦелИзДВ(текАдрес_чтения_ДВ,рслаг);              (* +рслаг *)
      СРЕДА.ЦелИзДВ(текАдрес_чтения_ДВ,чслаг);              (* +чслаг  *)
      СРЕДА.ОтметитьВРяду(начАдрес_ОблПерем+отнАдресПерем,
                    текАдрес_чтения_ДВ,чслаг,рслаг)
  | вНабор:                                                 (* рекурсивный вызов задачи
                                                               ОтметитьВПер() *)
      СРЕДА.НаборИзДВ    (начАдрес_ОблПерем,текАдрес_чтения_ДВ,ОтметитьВПер)
  | вБегунок:
      СРЕДА.ПропуститьДВ (начАдрес_ОблПерем,текАдрес_чтения_ДВ)
  ИНАЧЕ
  КОН
КОН ОтметитьВПер;

(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~общедоступная ЗАДАЧА~~~~~~~~~~~~~~~~~~~*)
                                                        (* рекурсивная задача *)
ЗАДАЧА НаборИзД-(                  начАдрес_ОблПерем,адрес_ОВ:ЦЕЛ;
                 Обработка:ЗАДАЧА (начАдрес_ОблПерем,
                                   текАдрес_чтения_ДВ+:ЦЕЛ));
 (* Цель:  найти размещённые переменные набора и обработать их
  * До:    <начАдрес_ОблПерем> - адрес начала области переменных
  *)
ПЕР
  текАдрес_чтения_ДВ :ЦЕЛ; (* текущий адрес для чтения       *)
  вч                 :ЦЕЛ; (* всего ячеек для чтения         *)
  конАдрес_чтения    :ЦЕЛ; (* последний адрес для чтения     *)
  уровеньРасширНабора:ЦЕЛ; (* уровень расширения набора      *)
  адрес_ОВ_основания :ЦЕЛ; (* адрес описателя вида основания *)
  имя:Название;

УКАЗ
  конАдрес_чтения:=адрес_ОВ-12;
  ОБХОД.ИзПамяти(конАдрес_чтения,вч);
      СРЕДА.ИмяИзОВ(адрес_ОВ,имя);
  ЕСЛИ вч >= 0 ТО
         текАдрес_чтения_ДВ:=конАдрес_чтения-вч;
                                                (* просмотр ДВ в нашем наборе *)
    ПОКА
         текАдрес_чтения_ДВ < конАдрес_чтения
    ВЫП
      Обработка(начАдрес_ОблПерем,текАдрес_чтения_ДВ)
    КОН;
                                    (* просмотр ДВ в основаниях нашего набора *)
           уровеньРасширНабора:=ВсегоРасширений-1;
    КОЛЬЦО
      ЕСЛИ уровеньРасширНабора <= 0 ТО ВЫХОД КОН;
        ОБХОД.ИзПамяти(адрес_ОВ+уровеньРасширНабора*4,адрес_ОВ_основания);
      ЕСЛИ             адрес_ОВ_основания # 0 ТО
        (*
         * берём предпоследний ОВО, т.к. в последнем ОВО
         * хранится адрес своего ОВ
         *)
        ОБХОД.ИзПамяти(адрес_ОВ+(уровеньРасширНабора-1)*4,адрес_ОВ_основания);
        СРЕДА.НаборИзД(начАдрес_ОблПерем,адрес_ОВ_основания,Обработка);
        ВЫХОД
      КОН;
      УМЕНЬШИТЬ(уровеньРасширНабора)
    КОН
  КОН
КОН НаборИзД;

(*-----------------------------------------локальная ЗАДАЧА-------------------*)
ЗАДАЧА ОтметитьВЗадаче(начАдрес_ОблПерем,
                       текАдрес_чтения_ДВ:ЦЕЛ);
 (*
  * Цель: разбирает ДВ (Данные о Видах времени выполнения) задачи,
  *       находит размещённые переменные и
  *       отмечает их участки памяти как занятые.
  * До:   <начАдрес_ОблПерем>  - адрес начала переменных задачи (регистр EBP при работе задачи)
  *       <текАдрес_чтения_ДВ> - текущий адрес чтения ДВ
  *)
ПЕР
  вид:ЦЕЛ;    (* ДВ метка вида *)
  имя:Название;
УКАЗ
     СРЕДА.ЦепьИзДВ(текАдрес_чтения_ДВ,имя);
  КОЛЬЦО
      СРЕДА.ЦелИзДВ(текАдрес_чтения_ДВ,вид); (* +вид *)
    ЕСЛИ вид = вКон ТО                                 (* ПОСТоянная вКон=20; *)
      ВЫХОД
    КОН;
          УМЕНЬШИТЬ(текАдрес_чтения_ДВ,4);   (* -вид *)

    ОтметитьВПер(начАдрес_ОблПерем,текАдрес_чтения_ДВ)
  КОН
КОН ОтметитьВЗадаче;

(*-----------------------------------------локальная ЗАДАЧА-------------------*)
ЗАДАЧА ОтметитьВОтделе(адрОО:ЦЕЛ);
 (* Цель: разбирает ДВ (Данные о Видах времени выполнения) отдела,
  *       находит размещённые переменные и
  *       отмечает их участки памяти как занятые
  *)
ПЕР
  текАдрес_чтения_ДВ:ЦЕЛ;      (* текущий адрес для чтения          *)
  за_конАдресом_чтения:ЦЕЛ;    (* за последним адресом для чтения   *)
  описОтделаД:ДкОпОтдела;      (* Доступ к НАБОРу Описатель Отдела  *)
УКАЗ
  описОтделаД:=ОБХОД.Значение(ДкОпОтдела,адрОО); (* преобразование целого адреса <адрОО> в ДОСТУП *)
                                                   текАдрес_чтения_ДВ:=описОтделаД.адр_ДВ_перемОтд;
                             за_конАдресом_чтения:=текАдрес_чтения_ДВ+
                                                                       описОтделаД.длина_ДВ_перемОтд;
  ПОКА 
        текАдрес_чтения_ДВ < за_конАдресом_чтения
  ВЫП
    ОтметитьВПер(описОтделаД.адр_Перем,текАдрес_чтения_ДВ)
  КОН
КОН ОтметитьВОтделе;

(*-----------------------------------------локальная ЗАДАЧА-------------------*)
ЗАДАЧА УдалитьСвКуски();
 (*
  * Цель:  найти полностью свободные куски памяти и возвратить их в ОС
  * Прим:  из кольцевого списка кусков удобно удалять последующий кусок
  * После: <адрес_текКуска>  - адрес начального куска в кольцевом списке кусков
  *        <адрес_текСвобУч> - адрес первого свободного участка <текКусок>           
  *)
ПЕР
  описьТекКусокД:ДкОпКуска;  (*   текущий кусок в кольцевом списке кусков *)
  ск            :ДкОпКуска;  (* следующий кусок в кольцевом списке кусков *)
  описьНачКусокД:ДкОпКуска;  (* начальный неудаляемый кусок               *)
  размсв        :ЦЕЛ;        (* размер первого свободного участка в <ск>  *)
  удалёнск      :КЛЮЧ;       (* если был удалён <ск>, то ВКЛ              *)
  адрес         :ЦЕЛ;        (* адрес памяти, возвращаемой в ОС           *)
УКАЗ
      описьТекКусокД:=ОБХОД.Значение(ДкОпКуска,адрес_текКуска);
  ск:=описьТекКусокД.следКусокД;
  описьНачКусокД:=ПУСТО;

  ПОВТОРЯТЬ
    удалёнск:=ОТКЛ;
    ЕСЛИ ск.адрес_1гоСвобУч # 0 ТО
      ОБХОД.ИзПамяти(ск.адрес_1гоСвобУч+ПозРазмераУч,размсв);
      ЕСЛИ           ск.размер = -размсв ТО            (* весь кусок свободен *)
        адрес:=ОБХОД.Значение(ЦЕЛ,ск);
        ЕСЛИ 
             ск = описьТекКусокД
        ТО                         (* был последним куском в кольцевом списке *)
          ОС.ОтдатьПамять(адрес);
                 адрес_текКуска:=0;
          ВОЗВРАТ
        КОН;
                                         (* удаляем <ск> из кольцевого списка *)
                ск:=ск.следКусокД;
        описьТекКусокД.следКусокД:=ск;
        удалёнск:=ВКЛ;
        ОС.ОтдатьПамять(адрес)
      КОН
    КОН;
    ЕСЛИ НЕ удалёнск ТО
      ЕСЛИ описьНачКусокД = ПУСТО ТО
           описьНачКусокД:=ск
      КОН;
                                        (* продвигаем <описьТекКусокД> и <ск> *)
      описьТекКусокД:=ск;
      ск:=описьТекКусокД.следКусокД
    КОН
  ДО ск = описьНачКусокД;                  (* обошли всё кольцо кусков памяти *)

  адрес_текКуска:=ОБХОД.Значение(ЦЕЛ,описьНачКусокД);
  адрес_текСвобУч:=описьНачКусокД.адрес_1гоСвобУч
КОН УдалитьСвКуски;

 (*=-
(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~общедоступная ЗАДАЧА~~~~~~~~~~~~~~~~~~~*)
ЗАДАЧА ПроверитьКучу-();
(*
 * Цель:  проверить строение всей кучи
 *)
ПЕР
  описьНачКусокД :ДкОпКуска;     (* начальный кусок в кольцевом списке кусков *)
  описьТекКусокД :ДкОпКуска;     (* текущий кусок в кольцевом списке кусков   *)
  адрес_за_послУч:ЦЕЛ;           (* адрес за последним участком в куске       *)
  адрес_текУч    :ЦЕЛ;           (* адрес текущего участка                    *)
  адрес_свобУч   :ЦЕЛ;           (* адрес свободного участка                  *)
  размУч         :ЦЕЛ;           (* размер </адрес_/текУч>                    *)
УКАЗ
  ЕСЛИ адрес_текКуска = 0 ТО ВОЗВРАТ КОН;
  описьТекКусокД:=ОБХОД.Значение(ДкОпКуска,адрес_текКуска);
  описьНачКусокД:=описьТекКусокД;
  ПОВТОРЯТЬ
    ПРОВЕРИТЬ((описьТекКусокД.размер > 0)
             И
              ((описьТекКусокД.размер+РазмерОпКуска) ОСТАТОК РазмерКуска = 0)
             И
              (описьТекКусокД.адрес_1гоУч = (ОБХОД.Значение(ЦЕЛ,описьТекКусокД)+РазмерОпКуска))
             );
    адрес_текУч:=описьТекКусокД.адрес_1гоУч;
    адрес_за_послУч:=описьТекКусокД.адрес_1гоУч+
            описьТекКусокД.размер;
    адрес_свобУч:=описьТекКусокД.адрес_1гоСвобУч;
    ПОКА адрес_текУч < адрес_за_послУч ВЫП
      ОБХОД.ИзПамяти(адрес_текУч+ПозРазмераУч,размУч);
      ЕСЛИ размУч < 0 ТО (* свободный участок *)
        ПРОВЕРИТЬ(адрес_текУч = адрес_свобУч);
        ОБХОД.ИзПамяти(адрес_свобУч+ПозАдресаСледСвобУч,адрес_свобУч);
        УВЕЛИЧИТЬ(адрес_текУч,-размУч)
      ИНАЧЕ
        УВЕЛИЧИТЬ(адрес_текУч,размУч)
      КОН
    КОН;
    ПРОВЕРИТЬ((адрес_текУч = адрес_за_послУч)
              И
              (адрес_свобУч = 0)
             );
     описьТекКусокД:=описьТекКусокД.следКусокД
  ДО описьТекКусокД = описьНачКусокД              (* обошли всё кольцо кусков *)
КОН ПроверитьКучу;
 -=*)

(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~общедоступная ЗАДАЧА~~~~~~~~~~~~~~~~~~~*)
ЗАДАЧА GetEBP-():ЦЕЛ;
(* Цель:  возвратить значение регистра EBP вызвавшей задаче *)
ПЕР
  п:ЦЕЛ;
УКАЗ
  п:=ОБХОД.ПолучитьАдрес(п)+4+4; (* после 'п': свой EBP, пред. EBP, пред. EIP *)
     ОБХОД.ИзПамяти(п,п);
  ВОЗВРАТ п
КОН GetEBP;

(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~общедоступная ЗАДАЧА~~~~~~~~~~~~~~~~~~~*)
ЗАДАЧА НайтиДВЗадачи-(адр,
                      номерОтдела+,
           адрес_начала_ДВ_задачи+:ЦЕЛ);
 (*
  * Цель:  найти задачу и её ДВ по внутреннему адресу задачи <адр>
  * После: <адрес_начала_ДВ_задачи> - начальный адрес ДВ задачи
  *        <номерОтдела> - номер отдела, в котором находится задача или 0
  *)
ПЕР
  описОтделаД:ДкОпОтдела;      (* Доступ к НАБОРу Описатель Отдела  *)
  адрес_ОписанияЗадачи:ЦЕЛ;    (* адрес Описания Задачи *)
  адрес_начала_задачи:ЦЕЛ;     (* адрес начала задачи *)
УКАЗ
       номерОтдела:=0;
  ПОКА
       номерОтдела < всего_Адресов_опОтделов
  ВЫП
    описОтделаД:=ОБХОД.Значение(ДкОпОтдела,рАдреса_опОтделов[номерОтдела]);
    ЕСЛИ
         (адр >= описОтделаД.адр_1йЗадачи)
        И
         (адр <  описОтделаД.адр_заПослЗадачей)
    ТО                                                         (* нашли отдел *)
      адрес_ОписанияЗадачи:=ОБХОД.ПолучитьАдрес(описОтделаД.рОписанийЗадач[0].адрес_начала_задачи);
      КОЛЬЦО
          ОБХОД.ИзПамяти(адрес_ОписанияЗадачи,адрес_начала_задачи);
        ЕСЛИ
             адрес_начала_задачи <= адр
        ТО                                                    (* нашли задачу *)
          ОБХОД.ИзПамяти(адрес_ОписанияЗадачи+4,адрес_начала_ДВ_задачи);
          ВОЗВРАТ
        КОН;
               УВЕЛИЧИТЬ(адрес_ОписанияЗадачи,8)          (* следующая запись *)
      КОН;
    КОН;
    УВЕЛИЧИТЬ(номерОтдела)
  КОН;
КОН НайтиДВЗадачи;

(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~общедоступная ЗАДАЧА~~~~~~~~~~~~~~~~~~~*)
ЗАДАЧА Уборка-();
 (*
  * Цель:  найти и объединить неиспользуемые участки памяти
  * После: <адрес_текКуска>  - адрес начального куска в кольцевом списке кусков
  *        <адрес_текСвобУч> - адрес первого свободного участка <текКусок>    
  *)
ПЕР
  номерОтдела         :ЦЕЛ;      (* номер отдела *)
  адрес_свобУч        :ЦЕЛ;      (* адрес группы свободных участков *)
  размУч              :ЦЕЛ;      (* размер участка *)
  адрес_1йЯч_послеКуска:ЦЕЛ;     (* адрес первой ячейки после куска *)
  размерСвобУч        :ЦЕЛ;      (* размер группы свободных участков *)
  предсв              :ЦЕЛ;      (* адрес предыдущего свободного участка *)
  EBP                 :ЦЕЛ;      (* значение машинного регистра *)
  EIP                 :ЦЕЛ;      (* значение машинного регистра *)
  текАдрес_чтения_ДВ  :ЦЕЛ;      (* текущий адрес чтения ДВ *)
  описьНачКусокД      :ДкОпКуска;(* начальный кусок в кольцевом списке кусков *)
  описьТекКусокД      :ДкОпКуска;(*  текущий  кусок в кольцевом списке кусков *)
  адрес_текУч         :ЦЕЛ;      (* адрес текущего участка *)
  ц0                  :ЦЕЛ;      (* целый 0 *)

УКАЗ
  ЕСЛИ
       адрес_текКуска = 0
  ТО
    ВОЗВРАТ
  КОН;
  ц0:=0;
             (* размеры всех участков делаем отрицательными (как у свободных) *)
  описьНачКусокД:=ОБХОД.Значение(ДкОпКуска,адрес_текКуска);
  описьТекКусокД:=описьНачКусокД;

  ПОВТОРЯТЬ
                       адрес_текУч:=описьТекКусокД.адрес_1гоУч;
адрес_1йЯч_послеКуска:=адрес_текУч+ описьТекКусокД.размер;
    ПОКА               адрес_текУч < адрес_1йЯч_послеКуска ВЫП
        ОБХОД.ИзПамяти(адрес_текУч+ПозРазмераУч, размУч);
                                                 размУч:=МОДУЛЬ(размУч);
         ОБХОД.ВПамять(адрес_текУч+ПозРазмераУч,-размУч);
             УВЕЛИЧИТЬ(адрес_текУч,              размУч)
    КОН;
     описьТекКусокД:=описьТекКусокД.следКусокД
  ДО описьТекКусокД = описьНачКусокД;             (* обошли всё кольцо кусков *)

                                     (* отмечаем переменные всех отделов *)
       номерОтдела:=0;
  ПОКА номерОтдела < всего_Адресов_опОтделов ВЫП
           ОтметитьВОтделе(рАдреса_опОтделов[номерОтдела]);
                                   УВЕЛИЧИТЬ(номерОтдела)
  КОН;

        (* отмечаем переменные работающих задач (переменные стека приложения) *)
  EBP:=СРЕДА.GetEBP();
  КОЛЬЦО
    ОБХОД.ИзПамяти(EBP+4,EIP);       (* EIP вызвавшей задачи *)
    ОБХОД.ИзПамяти(EBP,EBP);         (* EBP вызвавшей задачи *)
    ЕСЛИ EBP = 0 ТО
      ВЫХОД
    КОН;                                 (* вызвавшая задача - Пускач.^Запуск *)
    СРЕДА.НайтиДВЗадачи(EIP,номерОтдела,текАдрес_чтения_ДВ);
    ЕСЛИ
                            номерОтдела < всего_Адресов_опОтделов
    ТО
              ОтметитьВЗадаче(EBP,текАдрес_чтения_ДВ)
    КОН
  КОН;
             (* объединяем смежные свободные участки, и строим для них список *)
  описьТекКусокД:=описьНачКусокД;
  ПОВТОРЯТЬ
    адрес_свобУч:=0;
    предсв:=0;
                       адрес_текУч:=описьТекКусокД.адрес_1гоУч;
адрес_1йЯч_послеКуска:=адрес_текУч+ описьТекКусокД.размер;
    ПОКА               адрес_текУч < адрес_1йЯч_послеКуска ВЫП    (* по всем участкам куска *)
        ОБХОД.ИзПамяти(адрес_текУч+ПозРазмераУч,размУч);
      ЕСЛИ размУч < 0 ТО                        (* для свободного участка *)
        ЕСЛИ       адрес_свобУч = 0 ТО          (* если это начало области *)
                   адрес_свобУч:=адрес_текУч;   (* сохраним её адрес *)
             размерСвобУч:=размУч               (* и начнем её замерять *)
        ИНАЧЕ                       (* продолжение области *)
   УВЕЛИЧИТЬ(размерСвобУч, размУч)  (* замеряем её дальше *)
        КОН
      АЕСЛИ        адрес_свобУч # 0 ТО          (* конец области *)
                                 (* заранее обнуляем весь участок *)
Асм.ОбнулитьПамять(адрес_свобУч+РазмерОпУч,
                  -размерСвобУч-РазмерОпУч);
                                 (* выставляем окончательный размер области *)
     ОБХОД.ВПамять(адрес_свобУч+ПозРазмераУч,размерСвобУч);
     ОБХОД.ВПамять(адрес_свобУч+ПозАдресаСледСвобУч,ц0);

        ЕСЛИ предсв = 0 ТО       (* первая область *)
          описьТекКусокД.адрес_1гоСвобУч:=адрес_свобУч      (* обновляем первсв в куске *)
        ИНАЧЕ                    (* уже была область *)
                                 (* то выставляем у неё ссылку на нашу область *)
          ОБХОД.ВПамять(предсв+ПозАдресаСледСвобУч,адрес_свобУч)
        КОН;
           предсв:=адрес_свобУч;             (* готовимся к следующей области *)
                   адрес_свобУч:=0
      КОН;
         УВЕЛИЧИТЬ(адрес_текУч,МОДУЛЬ(размУч))
    КОН;
                                          (* такая же обработка в конце куска *)
    ЕСЛИ           адрес_свобУч # 0 ТО           (* конец области *)
                                 (* заранее обнуляем весь участок *)
Асм.ОбнулитьПамять(адрес_свобУч+РазмерОпУч,
                  -размерСвобУч-РазмерОпУч);
                                 (* выставляем окончательный размер области *)
     ОБХОД.ВПамять(адрес_свобУч+ПозРазмераУч,размерСвобУч);
     ОБХОД.ВПамять(адрес_свобУч+ПозАдресаСледСвобУч,ц0);

      ЕСЛИ предсв = 0 ТО         (* первая область *)
        описьТекКусокД.адрес_1гоСвобУч:=адрес_свобУч        (* записываем ссылку в куске *)
      ИНАЧЕ                      (* уже была предобласть *)
                                 (* то выставляем у неё ссылку на область *)
        ОБХОД.ВПамять(предсв+ПозАдресаСледСвобУч,адрес_свобУч)
      КОН
    КОН;
        описьТекКусокД:=описьТекКусокД.следКусокД
  ДО    описьТекКусокД =описьНачКусокД;           (* обошли всё кольцо кусков *)

  УдалитьСвКуски();
  памПослеУП:=0
КОН Уборка;

(*-----------------------------------------локальная ЗАДАЧА-------------------*)
ЗАДАЧА НаходимСвобУч(размер:ЦЕЛ):КЛЮЧ;
 (*
  * Цель:  искать по всем кускам участок памяти не меньше <размер> ячеек
  * Прим:  если находит такой участок, то возвращает ВКЛ и
  * После: <адрес_текКуска>  - адрес куска из кольцевого списка
  *        <адрес_текСвобУч> - адрес свободного участка по <адрес_текКуска>  
  *)
ПЕР
  описьТекКусокД :ДкОпКуска;(* текущий кусок в кольцевом списке кусков        *)
  описьНачКусокД :ДкОпКуска;(* кусок с которого начинается поиск              *)
  адрес_свобУч   :ЦЕЛ;(* адрес свободного участка с которого начинается поиск *)
  размерСвобУч   :ЦЕЛ;(* размер свободного участка по <адрес_текСвобУч>       *)

УКАЗ
  ЕСЛИ
       адрес_текКуска = 0
  ТО
    ВОЗВРАТ ОТКЛ
  КОН;
  описьТекКусокД:=ОБХОД.Значение(ДкОпКуска,адрес_текКуска);
  описьНачКусокД:=описьТекКусокД;

  адрес_свобУч:=адрес_текСвобУч;
  ПОВТОРЯТЬ
    ПОКА
                адрес_текСвобУч = 0
    ВЫП                                                     (* кончился кусок *)
      описьТекКусокД:=описьТекКусокД.следКусокД;

      адрес_текКуска:=ОБХОД.Значение(ЦЕЛ,описьТекКусокД);

      адрес_текСвобУч:=описьТекКусокД.адрес_1гоСвобУч;

      ЕСЛИ
           (описьТекКусокД = описьНачКусокД)
          И
           (адрес_текСвобУч = адрес_свобУч)
      ТО                                            (* обошли уже все участки *)
        ВОЗВРАТ ОТКЛ
      КОН
    КОН;
    ОБХОД.ИзПамяти(адрес_текСвобУч+ПозРазмераУч,размерСвобУч);
    ЕСЛИ
         -размерСвобУч >= размер
    ТО
      ВОЗВРАТ ВКЛ
    КОН;
    ОБХОД.ИзПамяти(адрес_текСвобУч+ПозАдресаСледСвобУч,адрес_текСвобУч)
  ДО                                                             (* ПОВТОРЯТЬ *)
           (описьТекКусокД = описьНачКусокД)
          И
           (адрес_текСвобУч = адрес_свобУч);        (* обошли уже все участки *)

  ВОЗВРАТ ОТКЛ
КОН НаходимСвобУч;

(*-----------------------------------------локальная ЗАДАЧА-------------------*)
ЗАДАЧА СоздатьКусок(размер:ЦЕЛ);
(*
 * Цель:  запрос у ОС нового куска памяти с полезным размером <размер>
 * После: <адрес_текКуска>  - адрес нового текущего куска в кольцевом списке кусков
 *        <адрес_текСвобУч> - адрес первого (и единственного) участка по <адрес_текКуска> 
 *)
ПЕР
       описьНачКусокД:ДкОпКуска;   (*   новый кусок в кольцевом списке кусков *)
       описьТекКусокД:ДкОпКуска;   (* текущий кусок в кольцевом списке кусков *)
  ц0:ЦЕЛ;                          (* целый 0                                 *)
УКАЗ
  ц0:=0;
       описьТекКусокД:=ОБХОД.Значение(ДкОпКуска,адрес_текКуска);

  УВЕЛИЧИТЬ(размер,РазмерОпКуска);
  УВЕЛИЧИТЬ(размер,(-размер) ОСТАТОК РазмерКуска);  (* выравнивание на РазмерКуска *)

            адрес_текКуска:=ОС.ВзятьПамять(размер);
  ПРОВЕРИТЬ(адрес_текКуска # 0);

       описьНачКусокД:=ОБХОД.Значение(ДкОпКуска,адрес_текКуска);

  адрес_текСвобУч:=адрес_текКуска+РазмерОпКуска; (* оставляем место для <описьНачКусокД> *)
  УМЕНЬШИТЬ(размер,РазмерОпКуска);
       описьНачКусокД.размер:=размер;
       описьНачКусокД.адрес_1гоУч    :=адрес_текСвобУч;
       описьНачКусокД.адрес_1гоСвобУч:=адрес_текСвобУч;
  ОБХОД.ВПамять(адрес_текСвобУч+ПозАдресаСледСвобУч,ц0);
  ОБХОД.ВПамять(адрес_текСвобУч+ПозРазмераУч,-размер);
  ЕСЛИ
       описьТекКусокД # ПУСТО
  ТО         (* вставляем новый кусок в кольцевой список после текущего куска *)
       описьНачКусокД.следКусокД:=описьТекКусокД.следКусокД;
       описьТекКусокД.следКусокД:=описьНачКусокД
  ИНАЧЕ
       описьНачКусокД.следКусокД:=описьНачКусокД
  КОН
КОН СоздатьКусок;

(*-----------------------------------------локальная ЗАДАЧА-------------------*)
ЗАДАЧА СоздатьУч(размер:ЦЕЛ):ЦЕЛ;
(*
 * Цель:  создать участок с размером <размер>
 *              ( в конце <адрес_текСвобУч>
 * или взять целиком весь <адрес_текСвобУч> )
 *        и возвратить его адрес
 * До:     <описьТекКусокД> - адрес текущего куска (доступ) в кольцевом списке кусков
 *        <адрес_текСвобУч> - адрес достаточно большого  свободного участка в куске <описьТекКусокД>
 * После: <адрес_текСвобУч> - бывший или следующий адрес свободного участка 
 *)
ПЕР
  адрес_предСвобУч:ЦЕЛ;       (* адрес предыдущего свободного участка    *)
  размСвобУч      :ЦЕЛ;       (* размер свободного участка               *)
  описьТекКусокД  :ДкОпКуска; (* текущий кусок в кольцевом списке кусков *)
  адрес_свобУч    :ЦЕЛ;       (* адрес свободного участка                *)
  адрес_создУч    :ЦЕЛ;       (* адрес созданного участка                *)
УКАЗ
                 ОБХОД.ИзПамяти(адрес_текСвобУч+ПозРазмераУч, размСвобУч);

              размСвобУч:=-размСвобУч;     (* размер будет положительным *)
  ЕСЛИ
              размСвобУч > размер+РазмерОпУч+4
  ТО                                                 (* забираем хвост у <адрес_текСвобУч> *)
    УМЕНЬШИТЬ(размСвобУч,размер);
                  ОБХОД.ВПамять(адрес_текСвобУч+ПозРазмераУч,-размСвобУч);

                  адрес_создУч:=адрес_текСвобУч+размСвобУч;
    ОБХОД.ВПамять(адрес_создУч+ПозРазмераУч,размер);
                                      (* ссылки в списке свободных участков остались те же *)
  ИНАЧЕ               
                  адрес_создУч:=адрес_текСвобУч;     (* забираем целиком <адрес_текСвобУч> *)
    ОБХОД.ВПамять(адрес_создУч+ПозРазмераУч,размСвобУч);
                                                                (* новый <адрес_текСвобУч> *)
   ОБХОД.ИзПамяти(адрес_создУч+ПозАдресаСледСвобУч,адрес_текСвобУч);

      (* удаляем <адрес_создУч> из списка свободных участков *)

      описьТекКусокД:=ОБХОД.Значение(ДкОпКуска,адрес_текКуска);
    ЕСЛИ
            адрес_создУч = описьТекКусокД.адрес_1гоСвобУч
    ТО                                                    (* это <адрес_1гоСвобУч> в куске *)
      описьТекКусокД.адрес_1гоСвобУч:=адрес_текСвобУч
    ИНАЧЕ                                             (* ищем предыдущий свободный участок *)
              адрес_предСвобУч:=описьТекКусокД.адрес_1гоСвобУч;

            адрес_свобУч:=адрес_предСвобУч;
      ПОКА  адрес_свобУч # адрес_создУч ВЫП

              адрес_предСвобУч:= адрес_свобУч;
   ОБХОД.ИзПамяти(адрес_свобУч+ПозАдресаСледСвобУч,адрес_свобУч)
                            (* записать по адресу <адрес_свобУч>
                                        из адреса <адрес_свобУч+ПозАдресаСледСвобУч> *)
      КОН;
ОБХОД.ВПамять(адрес_предСвобУч+ПозАдресаСледСвобУч,адрес_текСвобУч)
    КОН
  КОН;
  ВОЗВРАТ адрес_создУч
КОН СоздатьУч;


(*============================================================================*
 * Задачи, вызываемые из приложения Windows неявным образом
 *============================================================================*)

(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~общедоступная ЗАДАЧА~~~~~~~~~~~~~~~~~~~*)
ЗАДАЧА Создать-(адрес_ОВ,
                  размер,
                   адрес+:ЦЕЛ);
 (*
  * Цель:  поддержка встроенных в Глагол задач:
  *        СОЗДАТЬ(<пер>+:вид)                      - для наборов и рядов
  *        СОЗДАТЬ(<пер>+:вид; <ДЛ1>,...,<ДЛn>:ЦЕЛ) - для открытых рядов
  * До:    <адрес_ОВ> - описатель вида переменной <пер>
  *                     для рядов и открытых рядов <адрес_ОВ> = 0
  *        <размер>   - размер данных у <пер>
  *                     для открытых рядов <размер> = 4+n*4+<ДЛ1>*...*<ДЛn>
  * После: <адрес>    - адрес выделенной памяти заполненной 0-ми,
  *                     четыре ячейки перед <адрес> всегда содержат <адрес_ОВ>
  *)
ПЕР
  адрес_свобУч:ЦЕЛ;                               (* адрес свободного участка *)
УКАЗ
  УВЕЛИЧИТЬ(размер,РазмерОпУч);
  УВЕЛИЧИТЬ(размер,(-размер) ОСТАТОК 4);                      (* выравнивание *)

  ЕСЛИ НЕ НаходимСвобУч(размер) ТО
    ЕСЛИ
          уборка
        И
         (памПослеУП >= ЗапускУП)
    ТО
      СРЕДА.Уборка();
      ЕСЛИ
           НЕ НаходимСвобУч(размер)
      ТО
        СоздатьКусок(размер)
      КОН
    ИНАЧЕ
        СоздатьКусок(размер)
    КОН
  КОН;

  ЕСЛИ
       размер < РазмерКуска
  ТО
    УВЕЛИЧИТЬ(памПослеУП,размер)
  КОН;
                адрес_свобУч:=СоздатьУч(размер);
  ОБХОД.ВПамять(адрес_свобУч+ПозАдреса_ОВ_Уч,адрес_ОВ);
         адрес:=адрес_свобУч+РазмерОпУч
КОН Создать;

(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~общедоступная ЗАДАЧА~~~~~~~~~~~~~~~~~~~*)
ЗАДАЧА Присоединить-(адрОО:ЦЕЛ);
(*
 * Цель:  присоединить к среде отдел, где <адрОО> адрес описателя отдела,
 *                            расположенного в постоянной области кода
 * Прим:  вызывается однократно для каждого отдела при запуске приложения
 *)
УКАЗ
          ПРОВЕРИТЬ(всего_Адресов_опОтделов < ВсегоОтделов);
  рАдреса_опОтделов[всего_Адресов_опОтделов]:=адрОО;
          УВЕЛИЧИТЬ(всего_Адресов_опОтделов)
КОН Присоединить;


(*============================================================================*
 * реализации Задач для Записи среза значений переменных у работающих задач
 *             в исключительном состоянии (при АвОсте) в файл "Срез.пер"
 *============================================================================*)
  (*
ЗАДАЧА^ Пер(начАдрес_ОблПерем,
            текАдрес_чтения_ДВ+:ЦЕЛ);         (*- заготовка локальной задачи.

                                                   Вызов из локальной задачи
       ПисатьРядЦел4числа(
                          начАдрес_ПеремСлагаемого,
                          текАдрес_чтения_ДВ+,чслаг,рслаг:ЦЕЛ
                         );
                                               -*)
  *)
(*-----------------------------------------локальная ЗАДАЧА-------------------*)
ЗАДАЧА ПишемВпотокЦепь(цепь-:ЦЕПЬ);
УКАЗ
   ВФайл.ЦепьЗнаков(поток,цепь)
КОН ПишемВпотокЦепь;

(*-----------------------------------------локальная ЗАДАЧА-------------------*)
ЗАДАЧА ПишемВпотокЦел4числа(описание-:ЦЕПЬ;
                                    ц:ШИРЦЕЛ);
УКАЗ
   ВФайл.Цел4числа(поток,описание,ц,0,0,0);
КОН ПишемВпотокЦел4числа;

(*-----------------------------------------локальная ЗАДАЧА-------------------*)
ЗАДАЧА ПишемВпотокВещ4числа(описание-:ЦЕПЬ;
                                    в:ШИРВЕЩ);
УКАЗ
   ВФайл.Вещ4числа(поток,описание,в,0,0,0);
КОН ПишемВпотокВещ4числа;

(*-----------------------------------------локальная ЗАДАЧА-------------------*)
ЗАДАЧА ПисатьРядЦел4числа(
                          начАдрес_ПеремСлагаемого,
                          текАдрес_чтения_ДВ+,чслаг,рслаг:ЦЕЛ
                         );
(*
 * Цель:  выписать значения всех слагаемых ряда
 * До:    <начАдрес_ПеремСлагаемого> - адрес начала переменной слагаемого
 *        <текАдрес_чтения_ДВ>       - текущий адрес чтения ДВ
 *        <чслаг> - число слагаемых в ряду
 *        <рслаг> - размер одного слагаемого
 *)
ПЕР
  зЗНАК:ЗНАК;                           (* ЗНАК значение                      *)
                   текАдрес_чтения_ДВ_слагаемого:ЦЕЛ;  (* текущий адрес чтения ДВ слагаемого *)
  вид:ЦЕЛ;                              (* ДВ метка вида                      *)
  имя:Название;                         (* название переменной (вида)         *)
УКАЗ
                   текАдрес_чтения_ДВ_слагаемого:=текАдрес_чтения_ДВ;
  СРЕДА.ВидИмяИзДВ(текАдрес_чтения_ДВ_слагаемого,вид,имя); 

  ЕСЛИ
       вид = вЗнак
  ТО                     (* цепочка знаков *)
    ПишемВпотокЦепь('"');
    КОЛЬЦО
      ЕСЛИ чслаг <= 0 ТО
        ВЫХОД
      КОН;
      ОБХОД.ИзПамяти(начАдрес_ПеремСлагаемого,зЗНАК);
                       (* записать по адресу <зЗНАК>
                                   из адреса <начАдрес_ПеремСлагаемого> *)
      ЕСЛИ
           зЗНАК = 0X
      ТО
        ВЫХОД
      АЕСЛИ
            ( (зЗНАК >= ' ') И (зЗНАК <= '#007F') )
           ИЛИ
               оБуквы.Известная(зЗНАК)
      ТО
        ПишемВпотокЦел4числа("%c",    ВЦЕЛ(зЗНАК))
      ИНАЧЕ
        ПишемВпотокЦел4числа("##%.4x",ВЦЕЛ(зЗНАК))
      КОН;
           УВЕЛИЧИТЬ(начАдрес_ПеремСлагаемого,рслаг);
      УМЕНЬШИТЬ(чслаг)
    КОН;
    ПишемВпотокЦепь('"');
             текАдрес_чтения_ДВ:=текАдрес_чтения_ДВ_слагаемого+4 (* +оп *)
  ИНАЧЕ
    УВЕЛИЧИТЬ(отступ,2);
    ПОКА        чслаг > 0 ВЫП
             текАдрес_чтения_ДВ_слагаемого:=текАдрес_чтения_ДВ;
(* рекурсия *)
            Пер(начАдрес_ПеремСлагаемого,
                                            текАдрес_чтения_ДВ_слагаемого);                 
      УВЕЛИЧИТЬ(начАдрес_ПеремСлагаемого,рслаг);
      УМЕНЬШИТЬ(чслаг)
    КОН;
    УМЕНЬШИТЬ(отступ,2);
             текАдрес_чтения_ДВ:=текАдрес_чтения_ДВ_слагаемого
  КОН;
КОН ПисатьРядЦел4числа;

(*-----------------------------------------локальная ЗАДАЧА-------------------*)
ЗАДАЧА ИмяЗадачи(аз,текАдрес_чтения_ДВ+:ЦЕЛ);
(*
 * Цель:  выписать полное название задачи
 * До:    <аз> - произвольный адрес внутри кода задачи
 *        <текАдрес_чтения_ДВ> - текущий адрес чтения ДВ
 *)
ПЕР
  описОтделаД:ДкОпОтдела;      (* Доступ к НАБОРу Описатель Отдела  *)
  номерОтдела:ЦЕЛ;             (* № отдела         *)
  имя        :Название;        (* имя задачи       *)
УКАЗ
  СРЕДА.НайтиДВЗадачи(аз,номерОтдела,текАдрес_чтения_ДВ);
  ЕСЛИ
       номерОтдела < всего_Адресов_опОтделов
  ТО
         описОтделаД:=ОБХОД.Значение(ДкОпОтдела,рАдреса_опОтделов[номерОтдела]);
    ПишемВпотокЦепь(описОтделаД.название);
    ПишемВпотокЦепь(".");
                      СРЕДА.ЦепьИзДВ(текАдрес_чтения_ДВ,имя);
    ПишемВпотокЦепь(имя)
  ИНАЧЕ
    ПишемВпотокЦепь("ПУСТО");
    текАдрес_чтения_ДВ:=0
  КОН
КОН ИмяЗадачи;

(*-----------------------------------------локальная ЗАДАЧА-------------------*)
ЗАДАЧА Пер(начАдрес_ОблПерем,
           текАдрес_чтения_ДВ+:ЦЕЛ);                    (* рекурсивная задача *)
(*
 * Цель:  выписать значение переменной
 *----------------------------------------------------------------------------
 * До:    <начАдрес_ОблПерем>  - адрес начала области переменных
 *        <текАдрес_чтения_ДВ> - текущий адрес чтения ДВ
 *)
ПЕР
  отнАдресПерем:ЦЕЛ;    (* адрес переменной относительно 
                                         <начАдрес_ОблПерем>                  *)
  полныйАдресПерем:ЦЕЛ; (* полный адрес переменной равен
                                         <начАдрес_ОблПерем>+<отнАдресПерем>  *)
  адрес_ОВ:ЦЕЛ;         (* адрес ОВ                           *)
  зЗНАК:ЗНАК;           (* ЗНАК значение                      *)
  зЦел8 :ОБХОД.Цел8;    (* Цел8 значение                      *)
  зЦел16:ОБХОД.Цел16;   (* Цел16 значение                     *)
  зЦел32:ОБХОД.Цел32;   (* Цел32 значение                     *)
  зЦел64:ОБХОД.Цел64;   (* Цел64 значение                     *)
  зВещ32:ОБХОД.Вещ32;   (* Вещ32 значение                     *)
  зВещ64:ОБХОД.Вещ64;   (* Вещ64 значение                     *)
  зЦЕЛ  :ЦЕЛ;           (* ЦЕЛ значение                       *)
  зДОСТУП:ЦЕЛ;          (* ДОСТУП значение                    *)
  зТекБегун:ЦЕЛ;        (* текущее значение бегунка           *)
  начРядаБегунка:ЦЕЛ;   (* начало ряда бегунка                *)
  конРядаБегунка:ЦЕЛ;   (* конец  ряда бегунка                *)
  вид:ЦЕЛ;              (* ДВ метка вида                      *)
  имя:Название;         (* название переменной (вида)         *)
  чслаг:ЦЕЛ;            (* число слагаемых ряда               *)
  рслаг:ЦЕЛ;            (* размер одного слагаемого           *)
  числоРазмерностей:ЦЕЛ;(* число размерностей у вОРяд - вид Открытый РЯД *)
  номерРазмерности:ЦЕЛ; (* номер размерности  у вОРяд - вид Открытый РЯД *)
  длинаРазмерности:ЦЕЛ; (* длина размерности  у вОРяд - вид Открытый РЯД *)

УКАЗ
  СРЕДА.ВидИмяИзДВ(текАдрес_чтения_ДВ,вид,имя);             (* +вид  *)
     СРЕДА.ЦелИзДВ(текАдрес_чтения_ДВ,отнАдресПерем);       (* +отнАдресПерем  *)

  полныйАдресПерем:=начАдрес_ОблПерем+отнАдресПерем;

  ПишемВпотокЦел4числа("^%*",отступ);
  ПишемВпотокЦепь(имя);
  ПишемВпотокЦепь("=");
  ВЫБРАТЬ вид ИЗ
  | вЯчейка:
      ОБХОД.ИзПамяти(полныйАдресПерем,зЦел8);
               (* записать по адресу <зЦел8>
                           из адреса <полныйАдресПерем> *)
      зЦЕЛ:=зЦел8;
      ЕСЛИ зЦЕЛ < 0 ТО
        УВЕЛИЧИТЬ(зЦЕЛ,100H)
      КОН;
      ПишемВпотокЦел4числа("%.2xH",зЦЕЛ)
  | вКлюч:
      ОБХОД.ИзПамяти(полныйАдресПерем,зЦел8);
               (* записать по адресу <зЦел8>
                           из адреса <полныйАдресПерем> *)
      ПишемВпотокЦел4числа("%d",зЦел8)
  | вЗнак:
      ОБХОД.ИзПамяти(полныйАдресПерем,зЗНАК);
      ЕСЛИ (
             (зЗНАК >= ' ')
            И
             (зЗНАК <= '#007F')
           )
          ИЛИ
              оБуквы.Известная(зЗНАК)
      ТО
        ПишемВпотокЦел4числа("'%c'", ВЦЕЛ(зЗНАК))
      ИНАЧЕ
        ПишемВпотокЦел4числа("%.4xX",ВЦЕЛ(зЗНАК))
      КОН
  | вЦел8:
      ОБХОД.ИзПамяти(полныйАдресПерем,зЦел8);
               (* записать по адресу <зЦел8>
                           из адреса <полныйАдресПерем> *)
      ПишемВпотокЦел4числа("%d",зЦел8)
  | вЦел16:
      ОБХОД.ИзПамяти(полныйАдресПерем,зЦел16);
               (* записать по адресу <зЦел16>
                           из адреса <полныйАдресПерем> *)
      ПишемВпотокЦел4числа("%d",зЦел16)
  | вЦел32:
      ОБХОД.ИзПамяти(полныйАдресПерем,зЦел32);
               (* записать по адресу <зЦел32>
                           из адреса <полныйАдресПерем> *)
      ПишемВпотокЦел4числа("%d",зЦел32)
  | вЦел64:
      ОБХОД.ИзПамяти(полныйАдресПерем,зЦел64);
               (* записать по адресу <зЦел64>
                           из адреса <полныйАдресПерем> *)
      ПишемВпотокЦел4числа("%d",зЦел64)
  | вВещ32:
      ОБХОД.ИзПамяти(полныйАдресПерем,зВещ32);
      ПишемВпотокВещ4числа("%g",зВещ32)
  | вВещ64:
      ОБХОД.ИзПамяти(полныйАдресПерем,зВещ64);
      ПишемВпотокВещ4числа("%g",зВещ64)
  | вМнож:
      ОБХОД.ИзПамяти(полныйАдресПерем,зЦел32);
               (* записать по адресу <зЦел32>
                           из адреса <полныйАдресПерем> *)
      ПишемВпотокЦел4числа("%.8xH",зЦел32)
  | вЗадача:
      ОБХОД.ИзПамяти(полныйАдресПерем,зДОСТУП);
               (* записать по адресу <зДОСТУП>
                           из адреса <полныйАдресПерем> *)
      ИмяЗадачи(зДОСТУП,зДОСТУП)
  | вБегунок:
      ОБХОД.ИзПамяти(полныйАдресПерем+0,зТекБегун);
                 (* записать по адресу <зТекБегун>
                             из адреса <полныйАдресПерем+0> *)
      ОБХОД.ИзПамяти(полныйАдресПерем+4,начРядаБегунка);
                 (* записать по адресу <конРядаБегунка>
                             из адреса <полныйАдресПерем+4> *)
      ОБХОД.ИзПамяти(полныйАдресПерем+8,конРядаБегунка);
                 (* записать по адресу <конРядаБегунка>
                             из адреса <полныйАдресПерем+8> *)
      ЕСЛИ
            текУровеньВложенияДоступов >= УровеньД
      ТО
        СРЕДА.ПропуститьДВ(начАдрес_ОблПерем,текАдрес_чтения_ДВ)
      АЕСЛИ
           зТекБегун = 0
      ТО
        ПишемВпотокЦепь("ПУСТО");
        СРЕДА.ПропуститьДВ(начАдрес_ОблПерем,текАдрес_чтения_ДВ)
      АЕСЛИ
           зТекБегун < начРядаБегунка
      ТО                                   (* работает, если ВКЛ проверки *)
        ПишемВпотокЦепь("до ряда");
        СРЕДА.ПропуститьДВ(начАдрес_ОблПерем,текАдрес_чтения_ДВ)
      АЕСЛИ
           зТекБегун >= конРядаБегунка
      ТО                                   (* работает, если ВКЛ проверки *)
        ПишемВпотокЦепь("после ряда");
        СРЕДА.ПропуститьДВ(начАдрес_ОблПерем,текАдрес_чтения_ДВ)
      ИНАЧЕ
        УВЕЛИЧИТЬ(текУровеньВложенияДоступов);

        УВЕЛИЧИТЬ(отступ,2);
(* рекурсия *)
        Пер(зТекБегун,текАдрес_чтения_ДВ); (* рекурсивный вызов самоё себя *)
        УМЕНЬШИТЬ(отступ,2);

        УМЕНЬШИТЬ(текУровеньВложенияДоступов)
      КОН;
  | вДоступ:
      ОБХОД.ИзПамяти(полныйАдресПерем,зДОСТУП);
               (* записать по адресу <зДОСТУП>
                           из адреса <полныйАдресПерем> *)
        СРЕДА.ВидИмяИзДВ(текАдрес_чтения_ДВ,вид,имя);          (* +вид *)
           СРЕДА.ЦелИзДВ(текАдрес_чтения_ДВ,отнАдресПерем);    (* +отнАдресПерем   *)
      ВЫБРАТЬ вид ИЗ
      | вНабор:
          УМЕНЬШИТЬ(текАдрес_чтения_ДВ,4);                (* -отнАдресПерем   *)
          ЕСЛИ зДОСТУП = 0 ТО
            ПишемВпотокЦепь("ПУСТО")
          ИНАЧЕ
            ЕСЛИ
                        текУровеньВложенияДоступов < УровеньД
            ТО
              УВЕЛИЧИТЬ(текУровеньВложенияДоступов);

           СРЕДА.ОВИзПер(зДОСТУП,адрес_ОВ);

              УВЕЛИЧИТЬ(отступ,2);

           СРЕДА.НаборИзД(зДОСТУП,адрес_ОВ,Пер);

              УМЕНЬШИТЬ(отступ,2);

              УМЕНЬШИТЬ(текУровеньВложенияДоступов)
            КОН
          КОН
      | вНеопр:
          ПишемВпотокЦепь("ОБХОД.Доступ")
      | вРяд:
         СРЕДА.ЦелИзДВ(текАдрес_чтения_ДВ,рслаг);    (* +рслаг   *)
         СРЕДА.ЦелИзДВ(текАдрес_чтения_ДВ,чслаг);    (* +чслаг  *)
          ЕСЛИ
               (зДОСТУП = 0)
              ИЛИ
               (текУровеньВложенияДоступов >= УровеньД)
          ТО
            ЕСЛИ зДОСТУП = 0 ТО
              ПишемВпотокЦепь("ПУСТО")
            КОН;
            СРЕДА.ПропуститьДВ(начАдрес_ОблПерем,текАдрес_чтения_ДВ)
          ИНАЧЕ
            УВЕЛИЧИТЬ(текУровеньВложенияДоступов);
            ПисатьРядЦел4числа(зДОСТУП,текАдрес_чтения_ДВ,чслаг,рслаг);
            УМЕНЬШИТЬ(текУровеньВложенияДоступов)
          КОН
      | вОРяд:
          СРЕДА.ЦелИзДВ(текАдрес_чтения_ДВ,рслаг);   (* +рслаг *)
          (*
           * по адресу ОРЯД 0-е слово содержит его размерность,
           * а следующие слова содержат РАЗМЕРы его измерений 
           *)
          ЕСЛИ
               (зДОСТУП = 0)
              ИЛИ
               (текУровеньВложенияДоступов >= УровеньД)
          ТО
            ЕСЛИ
                зДОСТУП = 0
            ТО
              ПишемВпотокЦепь("ПУСТО")
            КОН;
            СРЕДА.ПропуститьДВ(начАдрес_ОблПерем,текАдрес_чтения_ДВ)
          ИНАЧЕ
            УВЕЛИЧИТЬ(текУровеньВложенияДоступов);

            ОБХОД.ИзПамяти(зДОСТУП,числоРазмерностей);
            (* записать по адресу <числоРазмерностей>
                        из адреса <зДОСТУП> *)
            чслаг:=1;
            номерРазмерности:=1;
            ПОКА
                 номерРазмерности <= числоРазмерностей
            ВЫП
              ОБХОД.ИзПамяти(зДОСТУП+номерРазмерности*4,длинаРазмерности);
                                 (* записать по адресу <длинаРазмерности>
                                             из адреса <зДОСТУП+номерРазмерности*4> *)
              чслаг:=чслаг*длинаРазмерности;
              УВЕЛИЧИТЬ(номерРазмерности)
            КОН;
            ПисатьРядЦел4числа(зДОСТУП+номерРазмерности*4,текАдрес_чтения_ДВ,чслаг,рслаг);
            УМЕНЬШИТЬ(текУровеньВложенияДоступов)
          КОН
      | вБегунок:
          СРЕДА.ПропуститьДВ(начАдрес_ОблПерем,текАдрес_чтения_ДВ)
      КОН
  | вРяд:
      СРЕДА.ЦелИзДВ(текАдрес_чтения_ДВ,рслаг);      (* +рслаг  *)
      СРЕДА.ЦелИзДВ(текАдрес_чтения_ДВ,чслаг);      (* +чслаг  *)
      ПисатьРядЦел4числа(полныйАдресПерем,текАдрес_чтения_ДВ,чслаг,рслаг)
  | вНабор:
      УВЕЛИЧИТЬ(отступ,2);
      СРЕДА.НаборИзДВ(начАдрес_ОблПерем,текАдрес_чтения_ДВ,Пер);
      УМЕНЬШИТЬ(отступ,2)
  ИНАЧЕ
    ПишемВпотокЦепь("НЕИЗВЕСТНЫЙ вид")
  КОН
КОН Пер;

(*-----------------------------------------локальная ЗАДАЧА-------------------*)
ЗАДАЧА ПерСтека(EBP:ЦЕЛ);
ПЕР
  EIP:ЦЕЛ;                      (* значение машинного регистра *)
  текАдрес_чтения_ДВ: ЦЕЛ;      (* текущий адрес чтения ДВ *)
  вид:ЦЕЛ;                                           (* ДВ метка вида *)
  уровеньВложенияЗадач:ЦЕЛ;     (* уровень вложения задач *)
УКАЗ
         уровеньВложенияЗадач:=0;
  КОЛЬЦО
    ЕСЛИ
         уровеньВложенияЗадач >= УровеньЗ
    ТО
      ВЫХОД
    КОН;
    ОБХОД.ИзПамяти(EBP+4,EIP);    (* EIP вызвавшей задачи *)
  (* записать по адресу <EIP>
              из адреса <EBP+4> *)
    ОБХОД.ИзПамяти(EBP,EBP);      (* EBP вызвавшей задачи *)
(* записать по адресу <EBP>
            из адреса <EBP> *)
    ЕСЛИ EBP = 0 ТО
      ВЫХОД
    КОН;                              (* вызвавшая задача - Пускач._Nachalo *)

    ПишемВпотокЦепь("^^Переменные задачи ");
    ИмяЗадачи(EIP,текАдрес_чтения_ДВ);
    ЕСЛИ
                  текАдрес_чтения_ДВ # 0
    ТО
      отступ:=2;
      КОЛЬЦО
    СРЕДА.ЦелИзДВ(текАдрес_чтения_ДВ,вид); (* +вид *)
        ЕСЛИ вид = вКон ТО
          ВЫХОД
        КОН;                                         (* ПОСТоянная вКон=20; *)
        УМЕНЬШИТЬ(текАдрес_чтения_ДВ,4);   (* -вид *)
(* рекурсия *)
          Пер(EBP,текАдрес_чтения_ДВ)         (* рекурсивный вызов самоё себя *)
      КОН
    КОН;
    УВЕЛИЧИТЬ(уровеньВложенияЗадач)
  КОН
КОН ПерСтека;

(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~общедоступная ЗАДАЧА~~~~~~~~~~~~~~~~~~~*)
ЗАДАЧА
       Ловушка-(адрОО,кодстрока:ЦЕЛ);
(*
 * Цель:  ловушка исключений
 *)
ПЕР
  EBP:ЦЕЛ;
  код,строка:ЦЕЛ;
  описОтделаД:ДкОпОтдела;                (* Доступ к НАБОРу Описатель Отдела  *)
  текст:ЦЕПЬ[100];
УКАЗ
  EBP:=СРЕДА.GetEBP();
  описОтделаД:=ОБХОД.Значение(ДкОпОтдела,адрОО); (* преобразование целого адреса <адрОО> в ДОСТУП *)
     код:=кодстрока ОСТАТОК 10000H;
  строка:=кодстрока ДЕЛИТЬ 10000H;  (* 10000H = 1+FFFFH = 16*16*16*16 = 2^16 = 65536 = 64 Кбайт *)

  НаЭкран.ВсяЦепь('^Сработала ловушка в отделе "');
  НаЭкран.ВсяЦепь(описОтделаД.название);

  Текст.Из4хЦел('" на строке %d.^Причина: ',строка,0,0,0,текст);

  НаЭкран.ВсяЦепь(текст);
  ВЫБРАТЬ код ИЗ
  |   пределыАвОст: текст:='выход значения за допустимые пределы'
  |     номерАвОст: текст:='неверный номер переменной ряда'
  |     арифмАвОст: текст:='арифметическое переполнение'
  |    охранаАвОст: текст:='неверный размещённый вид'
  |     пустоАвОст: текст:='значение доступа "ПУСТО"'
  |       дляАвОст: текст:='у "ДЛЯ" нет "ИНАЧЕ"'
  |   выбратьАвОст: текст:='у "ВЫБРАТЬ" нет "ИНАЧЕ"'
  |     ответАвОст: текст:='не определён ответ'
  | проверитьАвОст: текст:='"ПРОВЕРИТЬ" выдаёт ошибку'
  |      нольАвОст: текст:='деление на ноль'
  |   бегунокАвОст: текст:='бегунок вне ряда'
  ИНАЧЕ
    Текст.Из4хЦел('код %d',код,0,0,0,текст)
  КОН;
  НаЭкран.ВсяЦепь(текст);
  НаЭкран.ВсяЦепь('.^Подробности см. в файле "Срез.пер".^');
  поток:=ВФайл.Открыть("Срез.пер");
  поток.цВидЗнаков:=ВФайл.знУни;
  ПишемВпотокЦепь(текст);
  ПерСтека(EBP);
  ВФайл.Закрыть(поток);
  СТОП(код)
КОН Ловушка;

(*-ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss-*)
УКАЗ
  уборка:=ВКЛ
КОН СРЕДА.
