include('Scripts/Web.php'); MODULE('Проект Оберс — компилятор языка Oberon-2 для MS-DOS'); AUTHOR('2005 Юрий Бутенко, Харьков'); BEGIN('Проект Оберс', 'obers'); ?>Перевод с украинского CONTACTME('Oleg N. Cher'); ?>, 2012
Проект предназначен для тех программистов, которые хотели бы иметь простой и логичный высокоуровневый язык для решения своих нетривиальних задач. Убеждён, что этим языком программирования является Оберон-2. Аргументация следующая:
Кроме самого языка программирования важно то, как он реализован. Мои требования были такими:
Я испробовал три реализации компилятора Оберон-2, которые меня не удовлетворили. Поэтому я создал транслятор с языка программирования Оберон-2 на язык макроассемблера. Понятно, что свои требования я выполнил. О втором пункте скажу, что требования такие: процессор 8086, сопроцесор 8087, DOS 3.0, памяти от 256 Кб. Понятное дело, что на Pentium в Виндовс всё тоже будет работать.
По третьему пункту. Доступная DOS память (до 640 Кб) делится на узлы (Node) размером по 16 байт. Эти узлы и используются для всех потребностей (стек, строки, структуры). Буфер под ввод-вывод выделяется статически. Отсюда и вытекают ограничения. Если, например, в распоряжении транслятора есть 512*1024 байт, то вы не сможете объявить больше 16384 поименованных объектов в одном модуле (один узел на имя, второй на описатель).
Недостатки транслятора:
Интересно? — Хорошо. В разделе Технология объясняется как создать простую программу. Загрузка — загрузить транслятор (приблизительно 25Кб) и другие файлы. Транслятор — технические детали сердца проекта — транслятора. Ссылки — ресурсы, которые той или иной стороной касаются этого проекта: определение языка Оберон, ассемблер NASM, и т.д.
|
— Помни, Рэмбо, мы не заставляем тебя ехать в Афганистан; там тебе придётся очень туго; в тебя будут стрелять; а если тебя схватит советский ОМОН, мы будем всё отрицать...Генерал Пентагона
Если вы решите повторить нижеприведённые шаги по созданию программы, помните: я не отвечаю за последствия ваших действий. Я не могу знать ни какой у вас компьютер, ни какая операционная система. Более того, человеческий язык — не является алгоритмическим языком; я не знаю заранее, правильно ли вы меня поймёте. Если вы надумаете поиграться boot-record жёсткого диска, знайте, что это опасно для информации, находящейся на нём. Нижеприведённые действия надо выполнять на компьютере с процессором выше 80386 с сопроцесором 80387, памятью 4Mb (возможно, и меньше, не проверял). Чтобы запустить макроассемблер NASM вам нужен интерфейс DPMI и операционная система ДОС (в Виндовс 95 и выше всё должно работать).
Наша цель — программа, которая:
MODULE HELLO;
IMPORT vm86,PRIN;
VAR r:vm86.Регiстри;
BEGIN
PRIN.Очисти;
PRIN.Встанови_координати(34,12);
PRIN.Друк("Hello World!");
r.EAX:=0; r.int:=16H;
IF vm86.Виклик(r)=vm86.Наслiдок_норма THEN END;
vm86.перезавантаження
END HELLO.
Чтобы не было проблем с кодировкой букв рекомендую скачать этот файл
HELLO.O2
Как по мне, то не очень просто, особенно когда опыт в ІТ ещё небольшой. Появляется философский вопрос: каковы будут мои возможности при использовании этой технологии? Возможности ваши будут уникальны. Мы создали программу, которая работает в 32-разрядном защищённом режиме процессора i80386 и выполняет прерывания BIOS в режиме виртуального процессора 8086, размер программы вместе с загрузчиком менее 4K, а операционная система нам не понадобилась! Желаете написать BIOS? — немного перепишите макросы, и ваша программа сможет выполняться прямо из ROM. Пишете программы для контроллеров с процессорами 8086 без сопроцессора? — модифицируйте файл macro.mac и добавьте в него эмуляцию вычислений с плавающей запятой. Вас вообще не интересуют эти хилые PC? — переписываем файл macro.mac для вашего макроассемблера. Хотите создавать программы под разные операционные системы? — те же самые действия. Мечтаете создать свою операционную систему? Разработали свой процессор? Последние два вопроса — шутка :-). Учитывая написанное выше, я и писал на первой странице о нетривиальных задачах.
О загрузке с жёсткого диска, которую я вспоминал выше. Действия те же самые, кроме пункта, где мы модифицируем первый сектор диска. Необходимо указать диск C. Советую сначала программой LINKTO('savesect.com'); ?> (~315) создать его копию. Захотите вернуться к загрузке предыдущей ОС — запишите его на старое место. Диск должен быть с файловой системой FAT12 или FAT16 (loader поддерживает только эти форматы). Метка диска должна совпадать с именем вашей программы.
ENDB(); BEGIN('Проект Оберс => Транслятор', 'transl'); ?>Командная строка имеет следующий вид:
obers fname { -dDNAMEz }, где:
- fname — имя файла для трансляции, указанное без расширения;
- DNAME — имя директивы;
- z - знак ("+" — включить, "-" — выключить).
Транслятор всегда добавляет к fname расширение O2, поэтому файлы ваших программ должны его иметь.
Зарезервированные имена директив для транслятора следующие:
Вы можете добавлять собственные директивы. Они не повлияют на трансляцию, но будут перенесены в выходной файл на макроассемблере. Таким образом можно влиять на процесс ассемблирования программы.
Примеры:
Когда транслятор завершает работу, он возвращает операционной системе код завершения. Код ноль значит что всё в порядке, другой код — код ошибки. Чтобы получить объяснение по каждому конкретному коду предусмотрен пакетный файл obers.bat. При его использовании командная строка имеет следующий вид:
obers fname, где:
- fname — имя файла для трансляции, указанное без расширения.
Вы должны помнить, что расширение bat в ДОС имеет бо́льший приоритет, чем com, поэтому для вызова транслятора надо будет применять команду:
obers.com fname
Использование пакетного файла для распечатки текста ошибки имеет одно преимущество: вы легко можете перевести этот файл на свой национальный язык, например, турецкий.
Сначала открывается файл (модуль) X, заданный в командной строке. Если он имеет оператор IMPORT Y, то транслятор: сохраняет текущую позицию чтения в файле, сохраняет директивы трансляции, закрывает текущий файл, открывает файл модуля Y, устанавливает директивы трансляции, заданные в командной строке. После обработки файла Y происходит возвращение к файлу X. Вложенность модулей ограничивается только доступной памятью. Если модули X и Y импортируют модуль Z, то модуль (файл) Z будет обработан только первый раз, при этом сведения об экспортированных именах из него будут сохранены. При повторном включении модуля Z транслятор лишь позволит вызывающему модулю доступ к сохранённым данным.
Если в программе существуют выражения, в которых фигурируют лишь константы, то они будут вычислены транслятором. Например:
x:=1+3*5-ABS(-6);
и x:=10;
дают одинаковый код;x:=1+3*5-ABS(-6)+y;
и x:=10+y;
дают одинаковый код;x:=1+3*5-y-ABS(-6);
и x:=10-y;
дают разный код
(который вернёт одинаковый результат во время выполнения).Циклический импорт модулей, как известно из определения языка Оберон- 2, запрещён. То есть если в модуле X есть оператор IMPORT Y, то модуль Y не может содержать оператор IMPORT X.
Транслятор не поддерживает раздельную трансляцию, поэтому вы должны иметь тексты всех программных модулей, которые используете.
Транслятор не употребляет в выходном файле имён, которые вы дали программным объектам (переменным, подпрограммам, ...). Это связано с тем, что в модулях могут быть одинаковые имена, которые имеют разное содержание, а выходной файл создаётся один.
Хотя расширение языка программирования и не способствует переносу программ между разными компиляторами, но иногда оно упрощает жизнь. Поэтому окончательный выбор остаётся за вами.
"Главным расширением" транслятора obers является поддержка имён на национальных языках. Поэтому вы можете, например, написать такой оператор:
сумма:=слагаемое1+слагаемое2;
К разрешённым буквам попали также обратный апостроф (код 96) и знак подчёркивания (код 95). Все символы с 128 по 255 также являются разрешёнными. Мотивация расширения следующая: бо́льшая понятность программы.
Расширением в трансляторе также является работа стандартной функции CAP(x:CHAR) — перевести букву в верхний регистр. Проблема в том, чем будет её аргумент: константой или переменной. Если в вашей программе есть строка:
буква:=CAP("я");
,
то переменной типа CHAR будет присвоено значение "Я". Кодировка букв кириллицы в трансляторе происходит по кодовой странице 866 (украинский, русский и белорусский алфавиты). Если в вашей программе есть строка:
буква:=CAP(буква);
,
то перевод в верхний регистр будет выполнен (во время выполнения программы) по таблице, находившейся в файле macro.mac. Если вы изменили эту таблицу, ваши результаты не будут совпадать. Мотивация этого расширения: упрощение прикладных программ.
Транслятор obers предоставляет возможность создавать так называемые кодовые подпрограммы (code procedures). Это расширение рекомендуется в Oakwood Guidelines, раздел 3.4. Синтаксис их определён как:
PROCEDURE -ProcHeading byte {"," byte};
Следовательно, перед именем процедуры вы должны поставить знак "-", а после заголовка процедуры должна идти через запятую непустая последовательность байтов — чисел из диапазона [0..255]. Завершается такая процедура точкой с запятой. Например:
PROCEDURE -ln (x: REAL): REAL; (* log2(x)/log2(e) *)
0D9H,0EDH, (* fldln2 *)
0D9H,044H,024H,004H, (* fld dword[esp+4] *)
0D9H,0F1H, (* fyl2x *)
0C2H,004H,000H; (* ret 4 *)
Комментарии в скобках не обязательны.
Естественно, что такие подпрограммы не являются переносимыми. Например, вышеприведённая функция ln не будет работать в 16-разрядном режиме процессоров 80x86, кроме того, она использует инструкции сопроцесора 80x87. Мотивацией применения таких подпрограмм является бо́льшая гибкость и эффективность кода. Кодовыми процедурами можно воспользоваться для включения в программу ресурсов.
Ещё одним расширением является возможность руководить ходом трансляции. Она определена в разделе 4.0 Oakwood Guidelines. Подробнее она обсуждается в разделе Управление ходом трансляции и опции транслятора.
Последним расширением являются подпрограммы, добавленные в модуль SYSTEM. Этот модуль включён в программу транслятора и не является внешним файлом. Вызвано это тем, что процедуры этого модуля фактически являются макросами, и их заголовки не всегда можно определить средствами языка. Модуль служит для целей облегчения системного программирования. Имена добавленных процедур таковы: Install, CLI, STI, PORTIN, PORTOUT. Подробнее добавленные подпрограммы обсуждаются в разделе Расширение модуля SYSTEM.
Управление ходом трансляции и опции транслятора
Данное расширение описано в Oakwood Guidelines, разделах 4.3 и 4.4. Нужно оно для серьёзных проектов, а суть такова: в программе есть небольшие места, которые надо запрограммировать по-разному в зависимости от обстоятельств. При этом вам не хочется создавать новый файл. В текущей реализации транслятора obers не различаются опции (options) и селекторы (selectors), потому что синтаксис их использования тождествен, и все они попадают в выходной файл на ассемблере. Поэтому в дальнейшем я буду писать лишь о селекторах.
Для управления процессом трансляции нужные признаки — селекторы. В трансляторе obers они могут быть заданы двумя способами: из командной строки и непосредственно в тексте. Первый способ был описан выше. Второй способ имеет такой синтаксис:
<* NEW SelectorName *>
Селекторы, заданные в командной строке, являются действительными для всех транслируемых файлов. Заданные в тексте — только в текущем файле.
Селекторы могут иметь только два значения: включено (true) и выключено (false). Задать значение можно следующим образом:
<* SelectorName+ *>
— включить
<* SelectorName- *>
— выключить
Для проверки селекторов и выполнения действий по включению-выключению фрагментов текста вашей программы служит специальный условный оператор IF. По логике работы он не отличается от штатного условного оператора Оберона. Ниже схематически приведено его наиболее общее использование.
<* IF условие THEN *>
строки вашей программы
<* ELSIF условие THEN *>
строки вашей программы
<* ELSE *>
строки вашей программы
<* END *>
Допустимыми операторами в выражении условие являются ~, & и OR. Их действие аналогично действию операторов Оберона с теми же самыми именами.
Небольшой пример. Допустим, вам надо вычислять по формуле x = x * 2. Вы знаете, что на процессоре А быстрее выполняется вариант INC(x,x), а на процессоре Б — x:=ASH(x,1). Идти на компромисс вы не хотите, и иметь два разных файла для одной программы тоже. Выход такой:
<* IF ProcA THEN *>
INC(x,x);
<* ELSE *>
x:=ASH(x,1);
<* END *>
Транслируя программу командой
obers.com HELLO -dProcA+,
вы будете иметь первый вариант кода, а командой
obers.com HELLO -dProcA-
— второй вариант.
По мнению авторов языка Оберон-2 в модуль SYSTEM должны быть вынесены вещи, которые не являются стандартными для всех платформ. В Oakwood Guidelines, раздел 5.3 предлагается принять этот модуль за основу и расширять по мере необходимости. Я добавил в модуль такие процедуры.
Объявления этих процедур следующие:
PROCEDURE CLI;
PROCEDURE STI;
PROCEDURE Install (proc: PROCEDURE; num: байт);
PROCEDURE PORTIN (port: LONGINT; VAR x: Type1);
PROCEDURE PORTOUT (port: LONGINT; x: Type1);
Под Type1 понимается любой основной тип (кроме LONGREAL), указатель или процедурный тип. Под байт — целое число в диапазоне [0..255].
Подпрограмма Install выполняет важные системные вещи — устанавливает обработчик прерываний. Напомню, что в языке Оберон-2 подпрограмма, передающаяся как параметр, не может быть связанной с типом, встроенной в язык или локальной в другой подпрограмме.
Включение в программу ресурсов
Кодовые подпрограммы, упомянутые выше, можно использовать для включения в программу различных ресурсов, например, шрифтов или рисунков. Можете применить для этого программу createrm.com. Её командная строка:
createrm resursdef,
где resursdef — имя файла определения ресурсов.
Файл ресурсов должен быть составлен по следующим правилам:
Например, если задана командная строка:
createrm mres.res,
а в файле mres.res размещён такой текст:
proba _size
Resurs1 text.ico 0 0
Resurs2 text.ico 100 10H
Resurs3 readme.txt 0 0
то будет создан файл proba.O2, который будет иметь следующий текст (схематически):
MODULE proba;
CONST
Resurs1_size- = 766;
Resurs2_size- = 16;
Resurs3_size- = 888;
PROCEDURE -Resurs1-;
000H,000H,001H,...;
PROCEDURE -Resurs2-;
000H,000H,001H,...;
PROCEDURE -Resurs3-;
020H,020H,025H,...;
END proba.
Выражение
FOR i:=0 TO proba.Resurs3_size-1 DO SYSTEM.GET(SYSTEM.ADR(proba.Resurs3)+i,x) END
последовательно считывает все байты файла readme.txt, для того, например, чтобы отобразить их на экране.
ENDB(); BEGIN('Проект Оберс => Загрузка', 'load'); ?>Платформа транслятора: i80x86, i80x87, DOS 3.3 256Kb
Целевая платформа: i80386, i80387, без операционной системы
Автором транслятора Оберс и данного описания является Юрий Бутенко из Харькова. Работа по транслятору велась приблизительно с 2005 по 2007 годы. К сожалению, страничка i.html с оригинального веб-сайта не сохранилась, и это вся имеющаяся у меня информация об авторе. Также не сохранилась упомянутая выше утилита LINKTO('savesect.com'); ?>. Если у Вас есть что добавить, пишите.
Если Вам понравился Оберс и у Вас есть наработки для данного транслятора (исходники библиотек или отдельных процедур, идеи использования и дальнейшего совершенствования Оберса, ссылки), которыми не жалко поделиться, обязательно пишите тоже. Благодарю.