Система: текст вместо кнопок

Самое непривычное в Обероне — не язык и не процессор, а интерфейс. Здесь нет кнопок, меню, диалогов и значков. Есть текст, и любой текст можно исполнить.

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

Средняя кнопка исполняет

Основное правило: средний щелчок по слову вида Модуль.Процедура запускает эту процедуру. Не по кнопке, не по пункту меню — по любому такому слову, где бы оно ни было написано: в списке команд, в письме, в комментарии, в собственном тексте, который вы только что набрали.

На мыши без средней кнопки её заменяет Alt с левой.

Отсюда следует то, что поначалу сбивает: в системе нет разницы между «документом» и «панелью инструментов». Правая нижняя область с текстом System.Open, ORP.Compile, Tools.Inspect — это обычный текстовый файл System.Tool. Его можно редактировать, дописывать в него свои команды и сохранять. То, что вы сами набрали, работает ровно так же, как то, что шло в комплекте.

Три кнопки

Каждая кнопка мыши делает своё:

кнопка действие
левая поставить курсор (место вставки)
средняя исполнить команду под указателем
правая выделить текст протяжкой

Выделение и курсор — разные вещи и существуют одновременно. Это важно: многие команды берут аргумент из выделения, а вставляют результат туда, где стоит курсор.

Стрелка и тильда

В списке команд встречаются два знака-подсказки.

Стрелка ↑ после имени команды означает: аргумент берётся из текущего выделения. System.Directory ↑ — выделите образец вроде *.Mod, затем щёлкните по команде.

Тильда ~ закрывает список параметров. ORP.Compile Math.Mod/s ~ — команда, имя файла, ключ и тильда. Без тильды команда прочитает лишнее и, скорее всего, пожалуется.

Окна

Экран разделён на вертикальные полосы, в каждой — стопка окон. У каждого окна своя чёрная полоска заголовка, и в ней — команды, относящиеся к этому окну:

System.Log | Edit.Locate Edit.Search System.Copy System.Grow System.Clear

System.Close закрывает, System.Grow растягивает, Edit.Store сохраняет. Это тот же механизм: слова в полоске — обычный текст, исполняемый средним щелчком.

Два окна открыты сразу после загрузки. Сверху — System.Log, куда пишут все сообщения: результаты компиляции, ошибки, отчёты команд. Снизу — System.Tool, список команд.

У журнала есть особенность, на которую стоит обратить внимание заранее: он показывает текст с начала и сам не прокручивается. Если вывод длиннее примерно восемнадцати строк, хвост просто не виден, хотя команда отработала. System.Clear в его заголовке очищает журнал — пользуйтесь им между экспериментами.

Обычный рабочий путь

Скомпилировать модуль:

  1. левым щелчком поставить курсор в конец текста System.Tool;
  2. набрать ORP.Compile Имя.Mod/s ~;
  3. средним щелчком по набранному запустить.

В журнале появится строка вида:

compiling Math  447     0 32C32F12

Это имя модуля, размер кода в словах, размер данных в байтах и ключ интерфейса. Про ключ — в главе про модули.

Ключ /s разрешает перезаписать символьный файл. Без него компилятор откажется менять интерфейс модуля, от которого кто-то зависит.

Открыть файл на правку: Edit.Open Имя.Mod, сохранить: Edit.Store в заголовке окна.

Одна вещь, которая удивляет

Система однозадачная в том смысле, что команда выполняется до конца, и всё это время интерфейс не отвечает. Но между командами работает сборщик мусора — и только между ними.

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

pos 6734  TRAP  4 in ORB at 0001EC10

Это не поломка компилятора и не ошибка в вашем коде: ловушка номер 4 — разыменование NIL, а NIL вернуло выделение памяти. Те же модули, собранные по одному за команду, проходят без единой жалобы.

Мы наступили на это дважды: сперва при сборке компилятора, потом при пересборке всей системы. Правило простое — один крупный модуль на команду.

Задачи вместо потоков

Почему именно между командами? Потому что в системе нет ни потоков, ни прерываний таймера — есть один цикл, Oberon.Loop. Он читает мышь и клавиатуру, а когда ввода нет, по кругу вызывает задачи: процедуры, поставленные в круг вызовом Oberon.Install. Задача обязана быстро вернуть управление — отобрать его у неё нечем. Пока работает команда или задача, не происходит больше ничего: не движется указатель мыши, не мигает курсор, не работают другие задачи. Бесконечный цикл в задаче убивает систему целиком. Всё это вы проделаете сами в лабораторной №11.

Сборщик мусора — тоже задача. Последние строки Oberon.Mod:

ActCnt := 0; CurTask := NewTask(GC, 1000); Install(CurTask);

Раз в секунду цикл вызывает GC, но убирает он только по одному из двух поводов: с прошлой уборки прошло BasicCycle = 20 действий человека (нажатий и щелчков) или до конца кучи осталось меньше 64 КБ. System.Collect ничего не убирает сам — только обнуляет счётчик действий.

И главное: отмечает сборщик от глобальных указателей модулей (Kernel.Mark(mod.ptr)), стек он не просматривает вовсе. Посреди команды живые объекты держатся её локальными переменными, и уборка выбросила бы их. Поэтому — только между командами, когда стек пуст. Кучу до и после уборки, по переменной Kernel.allocated, вы увидите в лабораторной №10.