Info Guide #11
05 июля 2015

Системки - Оберон для ZX Spectrum: Тонкости при разработке на Обероне в среде ZXDev (часть 1).

   Оберон для ZX Spectrum
    Тонкости при разработке на Обероне
              в среде ZXDev
Oleg N. Cher, VEDAsoft Oberon Club 

  Концепция  среды  XDev (и её подсистемы
ZXDev,нацеленной на разработку для Спект─ 
рума) формировалась  в течение  нескольких
лет, но обрела свою реализацию в виде пер─
вой  версии сравнительно недавно ─ в конце
января 2015 г. Основные особенности:
 1.В качестве входного языка применяется
язык Оберон и  его  надмножества ─ языки 
Оберон-2 иКомпонентный Паскаль(из после─
днего поддержаны только некоторые фичи). 
 2.Кодогенерация реализована через тран─
сляцию ОберонавСис последующим вызовом 
сишного  компилятора. В качестве основного 
используется  компилятор  SDCC,  но  путём 
редактирования сборочных скриптов возможно 
подключение  и других компиляторов, напри─ 
мер, z88d. 
 3.Мультитаргетность. В репозитории про─
екта XDev(https://github.com/Oleg-N-Cher/
XDev ) есть подсистемы  для  таргетов MSX,
MS-DOS, Windows  (32/64 bit), Linux, нахо─ 
дящиеся  в разной  степени  готовности. Не 
публиковались,но также намечены подсистемы 
для разработки под NES/Nintendo, Java ME и 
Android. Уже зарелижена первая версия под─ 
системы  ZXDev с набором библиотек в комп─ 
лекте,хотя и не очень богатым, и примерами 
простых игр на Обероне ─ 
https://sourceforge.net/projects/bb-xdev

  Оберон - это очень компактный модульный
язык программирования (компонентный,объек─
тно-ориентированный - в зависимости от ди─
алекта) со структурной парадигмой, строгой
типизацией  и  автоматическим  управлением
памятью,спроектированный для универсально─
го применения (в том числе системного, где
он заодно  играет  и роль скриптового язы─
ка  ─  ОС ETH Oberon, A2/Bluebottle ). Это
квинтэссенция творчества классика програм─
мирования  доктора  Никлауса Вирта, автора
Паскаля иМодулы-2. В противовес современ─ 
ным  средствам  "основного  потока"  (main
stream) ─ большим языкам, ещё и наращивае─
мым дальнейшим усложнением,Оберон основан
на  самых  ключевых понятиях информатики ─
модуль, процедура, структура  данных  (за─
пись).
   Оберон-парадигма так же подвержена фра─
гментации,как и другие области IT. Поэтому
и в Оберон-парадигме есть диалекты, напри─
мер,Oberon-07 (сверхминималистичная реви─
зия Оберона-1 ), OberonX  (математическое
расширение),Active Oberon  (многопоточный
диалект),Оберон-2 (с расширенными средст─
вами  ООП), а также особенно  любимый мною
Компонентный Паскаль  (надмножествоОберо─ 
на-2 для промышленного применения).

          Беззнаковые вычисления

   Язык Оберон не имеет беззнаковых типов
данных, это подобно тому,как процессор Z80
не  имеет  беззнаковых регистров или ячеек
памяти. Но их значения могут интерпретиро─
ваться как беззнаковые числа и быть подве─
ргнуты беззнаковым операциям.
   Это также  похоже на языкФорт ─ значе─
ния  на  стеке  не  обладают типом - могут
трактоваться как знаковые, так и беззнако─
вые (или даже как двойные - занимающие два
слова). И хотя большинство стековых опера─
ций  знаковые, но наряду со знаковым умно─
жением  (* ) есть и беззнаковое умножение
(U* ), так же  и со сравнением, и с деле─
нием, и т.п. В общем, системного  програм─
миста не должно смутить отсутствие беззна─
ковых типов,ему достаточно знать,что ZXDev
имеет  три  типа  для целочисленных данных
размером в 1,2 и 4 байта. Рассмотрим,каким
способом можно определить эффективные без─
знаковые операции в ZXDev.

   Беззнаковое сравнение байтов. Этот трюк
я  придумал  в процессе  работы над портом
игры "Дурак", где применяется много знако─
вых сравнений байтов на больше-меньше, ко─
торые можно свободно изменить на беззнако─
вые сравнения, тем самым повысив эффектив─
ность кода.

   Знаковое сравнение:

IF a > b THEN ...

   Беззнаковое сравнение:

IF CHR( a ) > CHR( b ) THEN ...

   Здесь  знаковые  значения  приводятся к
беззнаковому типуCHAR и потом сравнивают─
ся.

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

MODULE UMath; IMPORT SYSTEM, B := Basic;

VAR a, b: SHORTINT;

PROCEDURE -UMultBytes (a, b: SHORTINT):
 SHORTINT
 "( ((CHAR)a) * ((CHAR)b) )";
PROCEDURE -UMultWords (a, b: INTEGER):
 INTEGER
 "(((unsigned int)a)*((unsigned int)b))";
PROCEDURE -UDivBytes (a, b: SHORTINT):
 SHORTINT
 "( ((CHAR)a) / ((CHAR)b) )";
PROCEDURE -UDivWords (a, b: INTEGER):
 INTEGER
 "(((unsigned int)a)/((unsigned int)b))";

BEGIN (*$MAIN*)
 B.Init;
  (* Приводим, т.к. значение >
                        SIZE(SHORTINT): *)
 a := SYSTEM.VAL(SHORTINT, 255);
 b := SYSTEM.VAL(SHORTINT, 255);

  (* Печатает: 65025 (255*255): *)
 B.PRWORD( UMultBytes(a, b) );
 B.PRLN;

  (* Печатает: 1 ( (-1)*(-1) ): *)
 B.PRWORD( a * b );
 B.PRLN;
 a := SYSTEM.VAL(SHORTINT, 255); b := 5;

  (* Печатает: 51 (255 DIV 5): *)
 B.PRWORD( UDivBytes(a, b) );
 B.PRLN;

  (* Печатает: 0 (-1 DIV 5): *)
 B.PRWORD( a DIV b );
 B.Quit
END UMath.

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

#define UMath_UDivBytes(a, b)            
  ( ((CHAR)a) / ((CHAR)b) )
#define UMath_UDivWords(a, b)            
  (((unsigned int)a)/((unsigned int)b))
#define UMath_UMultBytes(a, b)           
  ( ((CHAR)a) * ((CHAR)b) )
#define UMath_UMultWords(a, b)           
  (((unsigned int)a)*((unsigned int)b))

   Здесь я применил при описании результа─
та короткий тип (байт),чтобы результат был
совместим  с коротким типом (в случае при─
сваивания результата переменной длиной в 1
байт) без удлинения, т.е.:

 short:=UMultBytes(short1,short2);

   вместо  характерного дляОберона явного
SHORT() для  уменьшения  разрядности  типа
(угадайте, какой  вариант  более  эффекти─
вен?):

 short:=SHORT(UMultBytes(short1,short2));

   Но  если  тип  результата имеет среднюю
разрядность (слово), то,как видите,старший
разряд результата не теряется:

 integer:=UMultBytes(short1,short2);
Обероне обязательно  явное указание
SHORT() для уменьшения  мощности числового
типа; так сделано,чтобы было легче контро─ 
лировать  возможное искажение результата в 
случае  приведения  бОльших  типов к мень─ 
шим). 

            Битовые вычисления

   Вирт  попытался придать работе с битами
более привлекательный вид, согласующийся с
математическими абстракциями, поэтому биты
машинного слова представлены как множество
целых чисел ─ номеров отдельных битов
(http://oberoncore.ru/library/wirth_sets).
Для Оберона вместо универсальных множеств
были  выбраны  множества  небольших  целых
чисел. Тип SET в Обероне можно рассматри─
вать  как  битовый набор, спроектированный
так,чтобы быть независимым от порядка сле─
дования  байтов  платформы (так называемый
byte order: most significant byte ─ MSB, и 
least significant byte ─ LSB). Именно поэ─ 
тому Оберон  не  поощряет насильственного
приведения  целых к множествам и наоборот,
т.к.такое системное приведение типа ─ опе─
рация достаточно низкоуровневая,чтобы учи─
тывать  порядок байтов, и её использование
может привести к непредсказуемым последст─
виям  на платформах с разным порядком сле─
дования байтов, хотя, конечно, для Z80 это
некритично.
   Размер  типаSET в Обероне зафиксирован
в соответствии с современными процессорами
и  составляет  4  байта  (в GPCP есть тип
LONGSET = 8 байт), но  в ZXDev  мы можем в
конфиге Ofront.par  указать  произвольный
размер множеств, и я настоятельно рекомен─
дую  1 байт, что  наиболее  эффективно для
процессора Z80.
   ОперацияMOD (остаток от целочисленного
деления) при  соответствующем делителе бу─
дет  оптимизирована до соответствующего ей
логического AND,  например,  обероновское
a MOD 8 будет транслировано в сишноеa&7. 

   Эквивалент  логических побитовых опера─
ций для целыхa и b:

a AND b = ORD(BITS(a) * BITS(b))
a XOR b = ORD(BITS(a) / BITS(b))
a OR b = ORD(BITS(a) + BITS(b))
NOT a = ORD(-BITS(a))

   Где ORD - это  преобразование битового
множества в целое, аBITS ─ целого во мно─
жество. Идеология  Оберона не поощряет ра─
боту с целыми как с битами и наоборот, ибо
это, по  мнению Вирта, ведёт к неряшливому
использованию типов и нивелирует преимуще─
ства  строгой  типизации, поэтому  функций
BITS() иORD(set) в стандарте Оберона нет, 
но  есть  вКомпонентном Паскале (и в XDev
тоже).

  IF 0 IN set THEN(* if(set & 1) ... *)

   (* if (set & 0x23) ... *)
  IF BITS(23H) * set # {} THEN(* ... *)

   (* if (set & 0x23) ... *)
  IF {0, 1, 5} * set # {} THEN(* ... *)

   Последний вариант,как мне кажется,более
наглядно показывает, что в наборе проверя─
ется состояние битов №№0,1 и 5. Пусть вас
не  вводит  в заблуждение некоторая вычур─
ность записи битовых операций,особенно это
кажущееся "умножить" ─  машкод  получается
что надо:

; if ((0x23 & _set) != 0x0) { 
       ld      a,(#_set + 0)
       and     a, #0x23
       jr      Z, ...

   Таким образом, наОбероне можно сделать
достаточно низкоуровневую программу,напри─
мер, эмулятор Спектрума.

           Константные массивы

   Для  включения ресурсов и двоичных дан─
ных прямо в кодОберон не предлагает ниче─
го лучше,чем поэлементное присваивание.И я
очень  благодарен Олегу Комлеву (Saferoll)
за его труд  по добавлению в ZXDev нестан─
дартного  языкового расширения ─ констант─
ных массивов. Дадим ему слово:

Saferoll: 
   Что удалось сделать по константным мас─
сивам на данный момент - май 2015 года. 
 1)Константные  массивы любой вложеннос─
ти. 
 2)Типы  элементов - линейка целых типов
(включаяBYTE),BOOLEANилиCHAR.Все эти 
типы в C-исходнике становятся целыми конс─ 
тантами. 
 3)Если массив состоит изCHARилиBYTE,
то элементы можно указывать либо как пере─ 
чень символов в скобках('f',20X,"7"),ли─ 
бо в виде строки"ab"без лишних скобок.Но 
строка  обязательно  подразумевает в конце 
символ0Х,для него тоже должно быть место 
в массиве! 
   Ставить в кавычках меньше символов мож─
но,тогда символы после0Xмогут быть запо─ 
лнены  мусором - зависит от реализации Си- 
компилера. Поэтому лучше считать,что неис─ 
пользуемый  строкой остаток массива запол─ 
нен неопределёнными символами. 
   Пустую  строку "" можно указывать для
любого  массиваARRAY N OF CHAR(илиARRAY
N OF BYTE).
   Примеры:

  TYPE
    MsgStr = ARRAY 3, 7 OF CHAR;
  CONST
    Way = MsgStr("Hello","Error","Try");
  TYPE
    Labirint = ARRAY 3, 16 OF CHAR;
  CONST
    Map = Labirint(
      "...o..##...oo12",
      "...o..##...oo35",
      "...o..##...oo78"
    );

   Пока  не  сделано: экспорт  константных
массивов (с этим  мы  ещё не разобрались), 
возможность опускать размер массива, чтобы 
Ofront  автоматически его рассчитал по ко─ 
личеству элементов, и указание$на фикси─ 
рованный  символьный  массив. Чую, что тут 
опять  полезут  проблемы с путаницей "сим─ 
вол или строка". Также есть куда развивать 
реализацию  в смысле эффективности. Но то, 
что сделано сейчас, уже весьма полезно. 

  Совместное использование Оберона и Си

   В Оберон-программы можно вставлять про─
извольные  части кода, написанные на языке
Си (и встроенном ассемблере).Есть несколь─
ко способов (http://zx.oberon2.ru/forum/
viewtopic.php?f=10&t=202 ),которые я крат─
ко перечислю.

     1. Прямая вставка сишного файла
            в Оберон-программу

IMPORT SYSTEM;
PROCEDURE -includemain
  '#include "Main.c"';

// --- Main.c --- 
void main (void) {
  Basic_Init();
  Laser_InitScroll(65392);
  Laser_InitSprites(Rsrc_SprStart, 4769);
  ...
  Basic_Quit();
}

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

   Как  мы  знаем, Оберон-строки  являются
нуль-терминированными, но в отличие от си─
шных к ним прикреплено значение максималь─
ной длины строки.Если процедуры для работы
со  строками  всегда  будут  действовать в
рамках этой длины ─ код всегда будет рабо─
тать  корректно. Но  можно  ли работать на
Обероне со строками целиком в сишном стиле
без  дополнительного поля макс. длины? Ко─
нечно, можно. Вот мы опишем  вызовы сишных
функций в обёртке обероновских процедур, и
они могут  даже не совпадать по параметрам
(см., например,IntToStr ):

TYPE
   (* C-like null-terminated string: *)
  CString = SYSTEM.PTR;

PROCEDURE -includestdlib
  "#include <stdlib.h>";
PROCEDURE -includestring
  "#include <string.h>";
PROCEDURE -Length (
  str: CString): INTEGER
    "strlen((char*)str)";
PROCEDURE -CopyStr (dest, src: CString)
  "strcpy((char*)dest, (char*)src)";
PROCEDURE -IntToStr (
  n: INTEGER; str: CString)
    "_itoa(n, (char*)str, 10)";
PROCEDURE -UIntToStr (
  u: INTEGER; s: CString)
"_uitoa((unsigned int)u, (char*)s, 10)";
PROCEDURE -Concat (dest, src: CString)
  "strcat((char*)dest, (char*)src)";

Пример использования:

IMPORT SYSTEM, B := Basic;
CONST
  MaxIntSize = 7;(* ~-12345~ + 0X. *)
VAR
  num: SHORTINT;
  strBuf: ARRAY MaxIntSize OF CHAR;
BEGIN
  num := B.RND(1, 4);
  UIntToStr(num, SYSTEM.VAL(
    CString, SYSTEM.ADR(strBuf))
  );
  Concat(SYSTEM.VAL(CString,
    SYSTEM.ADR(strBuf)),
    SYSTEM.VAL(CString,
      SYSTEM.ADR(" is my number"))
  );

                2. Биндинг

   Чтобы Оберон  умел взаимодействовать с
кодом  наСи ─ нужно как-то к нему прикре─
питься. Необходимо сделать описание интер─
фейса сишной библиотеки в стиле Оберон-мо─
дуля,чтобы другие модули могли вызывать из
него  процедуры, брать значение констант и
т.д. Для этого  мы должны подготовить бин─
динг-связку, в которой будет описан интер─
фейс чужеродного модуля,все константы,типы
и процедуры (в случае XDev ─ с пустыми те─
лами). Замечу,что это обычная практика для
связки модульных языков  с сишными библио─
теками (применяется  не только вОберонах,
но и в языкахАда, Модула-2, Модула-3 ).
   XDev  содержит достаточные средства для
создания биндингов,учитывающие возможность
использовать разные модели вызова функций,
замену одних вызовов другими,описание про─
тотипов  функций  и т.д. С их помощью соз─
даны биндинги  к WinAPI и libSDL для XDev/
WinDev. Приведу  пример простого биндинга, 
отсылая за деталями к форуму
http://zx.oberon2.ru/forum/
viewtopic.php?f=10&t=94.

MODULE Input; IMPORT SYSTEM;

CONST
  Backspace* = 0CX;
  Enter* = 0DX;
  Escape* = "E";
  Space* = " ";
   (* Arrows *)
  Up    * = "Q";
  Down  * = "A";
  Right * = "P";
  Left  * = "O";

TYPE
  Key* = CHAR;

(** Returns the number of keystrokes 
     in the keyboard input buffer. *)
PROCEDURE Available* (): SHORTINT;
BEGIN RETURN 0 END Available;

(** Read a key from the keyboard buffer. 
     Blocks if no key is available. *)
PROCEDURE Read* (): Key;
BEGIN RETURN 0X END Read;

PROCEDURE RunMe50Hz* ; END RunMe50Hz;

END Input.

   Транслятор сгенерирует из этого биндин─
га сишный файл с пустыми телами функций, а
также  более  нужные  нам 1) заголовочный
файлObj/Input.h, который будет  подключен
при компиляции, и 2) символьный файл Sym/
Input.sym, подобный таким же,сгенерирован─
ным  для родных Оберон-модулей. В нём хра─
нится  закодированное представление интер─
фейса  модуля. Автосгенерированную  пустую
сишную  реализацию мы игнорируем, подменяя
при  компиляции  на реализацию, написанную
ручками (на Си или асме), которая хранится
в папке/C и реализует заявленную в интер─
фейсе функциональность.

   3. Биндинг с использованием готового
      сишного стандартного заголовка
 и самодельного заголовка-переадресовщика

   Допускаю  и такую вариацию: при необхо─
димости можно игнорировать и автосгенерён─
ный  Оберон-транслятором  заголовок(*.h).
Делается  модуль-биндинг  на  Обероне (для
получения символьного файла), а при компи─
ляции подключается сделанный вручную заго─
ловочный сишный файл, из которого1) уже в
свою очередь инклюдится стандартный сишный
заголовок, не адаптированный к оберонскому
манглированию имён префиксом и т.п.;и2) в
котором  описывается переадресация Оберон-
процедур  в сишные функции или даже макро─
сы. Подобным  образом  устроен  биндинг  к
библиотеке trdos.lib (см. в  дистрибутиве
XDev ). 

(про сопряжение с ассемблером см.следующую 
статью) 




Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Home BBS - об уникальных возможностях Home BBS.
Форум - Процедура "цветные полосы на бордюре". Снижение шума FDD.
Юмор - анекдоты.

В этот день...   21 сентября