# D2 Programming Language (описание языка)

1. Введение
2. Синтаксис
3. Основные понятия (язык и представление)
4. Объявления и области видимости
   - Объявление глобальных переменных
   - Описание процедур
   - Экспорт процедур
   - Описание констант
5. Выражения
6. Операторы
   - ОператорIf
   - ОператорWhile
   - ОператорRepeat
   - ОператорDo
   - ОператорFor
   - ОператорCycle
   - ОператорInline
   - ОператорContinue
   - ОператорLeave
   - ОператорRecur
   - ОператорExit
7. Синтаксис d2
8. Пример игровой программы Tetris

## Введение

Д2 - это язык программирования высокого уровня. Предками языка, оказавшими на него наибольшее влияние, можно назвать Forth, Pascal (Modula-2, Oberon) и С. Язык был задуман как обладающий намеренно простым синтаксисом и возможно большей расширяемостью. Первое было достигнуто за счет сокращения до реального минимума управляющих конструкций, и отказа от такого понятия современных языков алголоидного типа как тип данных, однако же, оставить "классический" синтаксис языка типа Pascal. Формально язык содержит единственный тип данных - указатель. Косвенно поддерживаются такие типы как числа и строки символов. Второе, т.е. расширяемость, было достигнуто за счет введения понятия библиотеки или модуля, а также, в основном за счет внутреннего устройства ядра языка идею которого он позаимствовал у Forth, хотя это скорее вопросы реализации, чем синтаксиса... Язык поддерживает такие понятия как процедура (векторная процедура), локальные и глобальные переменные, параметры. В языке нет каких либо предопределенных операций и функций кроме, как я уже отмечал, операций управления процессом исполнения. Все функции ввода-вывода, математические, процедуры работы с графикой и т.д. поставляются в виде библиотек и в исходных текстах.

## 2. Синтаксис

Для описания синтаксиса Д-2 используются Расширенные Бэкуса-Наура Формы (РБНФ). Варианты разделяются знаком |. Квадратные скобки [ и ] означают необязательность записанного внутри них выражения, а фигурные скобки { и } означают его повторение (возможно 0 раз). Терминальные символы заключаются в кавычки.

## 3. Основные понятия (язык и представление)

3.1. Коментарий

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

Коментарий заключается в фигурные скобки { и }, и может занимать несколько строк.

Коментарий не может быть вложенным

Коментарий может содержать директивы компилятору. Но об этом позднее.

      Пример:

        {Это коментарий}

Комментарий = '{' ЛюбойСимвол '}'

3.2. Идентификатор

Идентификатор начинается с буквы ( a..z,A..Z), вторым своим символом он может иметь букву и цифру ( 0..9 ), а также символ подчеркивания _ . В некоторых случаях в качестве идентификатора могут выступать строки, эти случаи будут оговорены подробнее.

      Пример:
         this_is_D2_identifier

Идентификатор = буква { буква | цифра }

3.3. Строки

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

      Пример:

        'It''s a string'

Строка = "'" ЛюбойСимвол "'"

3.4. Числа

Числом является набор символов 0..9 образующих номер, лежащий в диапазоне 0..FFFFh. Основание числа можно указать явно в формате: 0 { x|h|d|e|o|b } { цифра }. Где символ определяет систему исчисления x,h - шестнадцатиричная, d,e - десятичная (по умолчанию) o - восмеричная и b - двоичная.

      Пример:

        345
        0x90
        0b10011

Число = [ 0 ] [ основание ] { цифра }
Цифра = 0..9,A..F
Основание = x|h|d|e|o|b

3.5. Зарезервированные слова

Д2 содержит следующие зарезервированные слова:

    	ASM
    	CASE CONST CONTINUE CYCLE
    	DO
     	ELSE ELSIF ELSIFN END EXIT
    	FI FOR
    	IF IFN IN INLINE
    	LEAVE LIBRARY LOOP
    	MODULE
    	NEW NEXT
    	OF
    	RECUR REPEAT RET
    	SUB
    	THEN TYPE
    	UNTIL USE
    	VAR VECT
    	WEND WHILE

3.6. Разделители

Разделителями в Д2 являются следующие символы: , ; ( ) [ ] .

Разделитель = ',' | ';' | '(' | ')' | '[' | ']'

3.7. Символы

Символами в д2 являются: ` ~ ! @ # $ % ^ & * - + = " ; : < > . \ | / ? .

Из них также можно составить идентификатор.

Идентификатор = Символ { Символ }
Символ = '`' | '!' | '@' | '#' | '$' | '%' | '^' | '&' | '*' | '-' | '+' | '=' | '"' | ';' | ':' | '<' | '>' | '.' | '\' | '|' | '/' | '?'

    Пример:
    	+
    	-
    	++
    	>>

3.8. Игнорируемые символы

Символы с ascii кодами 0..32,127 игнорируются, если они не являются в данном контексте разделителями между идентификаторами или какими-либо другими лексемами.

3.9. Другие символы

Появление во входном потоке какого-нибудь символа, не описанного выше, кроме как в коментарии, является ошибкой и приводит к выдаче соответствующего сообщения.

## 4. Объявления и области видимости

Программа на d2 называется модулем и содержит в себе описания переменных и процедур, а также списки импорта библиотек.

Программа начинается с зарезервированного слова MODULE или LIBRARY, следующего за ним имени модуля или библиотеки и заканчивается словом END, которое завершает процесс компиляции текущего модуля. Модуль должен содержать хотябы одну процедуру с именем main с которой и начинается выполнение.

Процедура или переменная видны с места их описания до конца модуля, локальные переменные видны только в процедуре, для которой описаны. Из библиотеки доступны только те процедуры, которые имели соответствующую экспортную метку. Глобальные переменные экспортироваться не могут.

Программа = MODULE | LIBRARY Идентификатор { Описание } END
Описание = ОписаниеПеременных | ОписаниеПроцедуры | ОписаниеЭкспорта | ОписаниеКонстант

    Пример (минимальная d2 программа):

       MODULE minimal_d2_program

         SUB main
           { do nothing }
         RET

       END

4.1. Объявление глобальных переменных

Обявление глобальной переменной начинается с зарезервированного слова VAR и последующего перечисления идетификаторов переменных через запятую. По умолчанию под переменную резервируется место равное машинному слову. Этот размер можно переопределить, указав новый в [] скобках: [ new_size ]. По умолчанию переменная инициализируется нулями, но ее можно инициализировать другими данными, перечислив их в <> скобках: < ... data >.

ОписаниеПеременных = ОписаниеПеременной { , ОписаниеПеременной } ;
ОписаниеПеременной = Идентификатор [ Размер ] [ Инициализатор ]
Размер = '[' Число ']'
Инициализатор = < { Числа | Строки | ИменаПроцедур } >

    Пример:

      VAR
        a,b,c,
        x<3>,y<5>,
        arr[10]<0 1 2 3 4 5 6 7 8 9>,
        ptrs[6]<sub_1 sub_2 sub_3>,
        str_ptr[4]<'Иванов' 'Иван' 'Иваныч'>;

4.2. Описание процедур

Описание процедуры начинается с зарезервированного слова SUB или VECT, затем следует имя процедуры. Заканчивается описание словом RET. Процедуры описанные как VECT являются векторными, обладающими векторным полем кода, и могут переопределяться в момент исполнения.

Процедуры могут перегружаться, глобальные переменные не могут перегружаться.

После имени процедуры может идти экспортная метка, расширяющая область видимости процедуры на модули, импортирующие данный модуль (библиотеку).

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

Процедура может содержать локальные переменные. Они описываются сразу после заголовка процедуры, аналогично описанию глобальных переменных, но могут содержать импортную метку, показывающую, что данная переменная является параметром.

Имена локальных переменных могут перегружать имена глобальных и имя самой процедуры.

ОписаниеПроцедуры = SUB [ ЭкспортнаяМетка ] [ Приоритет ] [ ОписаниеЛокальныхПеременных ] { Оператор | Выражение } RET
ЭкспортнаяМетка = '*'
Приоритет = '[' Число ']'
ОписаниеЛокальныхПеременных = VAR ОписаниеПеременной | ОписаниеПараметра;
ОписаниеПараметра = Идентификатор ИмпортнаяМетка
ИмпортнаяМетка = '*'

    Пример:

    MODULE lview;

    USE
      system,fio,iostd,param,putnum,math;

    CONST
      buf = 10000;

    SUB readlib VAR h;
      @h = fopen(paramstr(1));
      fread(h buf 0h7FFF);
      fclose(h);
    RET

    SUB checklib VAR ptr<0>,str<'lib2d'>;
      REPEAT
        IF byte(str+ptr) <> byte(buf+ptr) THEN
          FALSE;
          EXIT
        FI
        inc(@ptr)
      UNTIL ptr==length(str) LOOP
      TRUE
    RET

    SUB viewhdr VAR pos*;
      puthex(pos-buf);
      bl;
      putword(byte(pos+2));
      bl;
      putword(byte(pos+3));
      bl;
      putword(byte(pos+4));
      bl;
      putstring(pos+5);
      cr
    RET

    SUB showlib VAR next,old;
      @next=word(buf+5);
      WHILE next<>0 DO
        @old=next;
        @next=word(buf+next);
        viewhdr(old+buf);
      WEND
    RET

    SUB main
      IF paramcount THEN
        readlib
        IF checklib THEN
          showlib
        ELSE
          putstring 'unknown library format'
        FI
      ELSE
        cr;
        putstring 'usage: lview [filename''.''ext]';
        cr;
        cr
      FI
    RET

    END

4.3. Экспорт процедур

Процедуры могут экспортироваться из библиотек. Библиотека, процедуры из которой должны экспортироваться должна быть явно упомянута с помощью зарезервированного слова USE, при этом все процедуры, обладающие экспортными метками становятся видимыми в текущем модуле.

ОписаниеЭкспорта = USE ИмяБиблиотеки { , ИмяБиблиотеки }

    Пример:
      USE system, math, '..\my_libs\lib', lib;

4.4. Описание констант

Константы описываются с помощью зарезервированого слова CONST.

ОписаниКонстант = CONST ОписаниеКонстанты {, ОписаниеКонстанты }
ОписаниеКонстанты = Идетификатор '=' Терм

    Пример:

      CONST
        c1 = 12,
        str = 'Hello, world!',
        plus = + ;

## 5. Выражения

Язык d2 не имеет ни одной встроенной или стандартной функции, поэтому все функции создаются или на языке ассемблера или на самом d2. Описание процедуры подразумевает указание ее приоритета. Данный приоритет используется при разборе выражений. Выражение может содержать любую допустимую лексему кроме операторов.

Идентификатор, если это процедура - приводит к ее вызову, если это переменная - на стеке остается ее значение, для взятия адреса, как переменной (глобальной или локальной), так и процедуры, используется символ '@'.

Число оставляет на стеке свое значение.

Использование строки в выражении, подразумевает использование ее адреса в памяти.

В выражении могут быть использованы скобки, запятые и символ ';'. Символ ';', а также зарезервированное слово приводит к завершению вычисления текущего выражения. Выражение должно быть сбалансировано по скобкам.

Выражение = { Идентификатор | Строка | Число | Символ | '(' Выражение ')' }

    Пример написания библиотеки math:

    include	libmacro.inc

    beglib 'math'

    item 'inc','*'
      pop bx
      xchg ax, bx
      inc word ptr [bx]

    item 'dec','*'
      pop bx
      xchg ax, bx
      dec word ptr [bx]

    item '+','*',5
      pop bx
      add ax,bx

    item '-','*',5
      pop bx
      xchg ax, bx
      sub ax,bx

    item '*','*',4
      pop bx
      imul bx

    item '/','*',4
      cwd
      pop bx
      xchg ax,bx
      idiv bx

    item '%','*',4
      cwd
      pop bx
      xchg ax,bx
      div bx
      mov ax,dx

    item '>','*',6
      pop bx
      cmp bx,ax
      setg al

    item '<','*',6
      pop bx
      cmp bx,ax
      setl al

    item '=','*',10
      pop	bx
      mov	[bx], ax
      pop	ax

    item '<=','*',6
      pop bx
      cmp bx,ax
      setle al

    item 'byte','*',1
      mov bx,ax
      xor ah,ah
      mov al,[bx]

    item 'word','*',1
      mov bx,ax
      mov ax,[bx]

    item '>=','*',6
      pop bx
      cmp bx,ax
      setge al

    item '==','*',6
      pop bx
      cmp bx,ax
      sete al

    item '<>','*',6
      pop bx
      cmp bx,ax
      setne al

    item '&','*',7
      pop bx
      and ax,bx

    item '|','*',7
      pop bx
      or ax,bx

    item '^','*',7
      pop bx
      xor ax,bx

    endlib

## 6. Операторы

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

Оператор = ОператорIf | ОператорWhile | ОператорRepeat | ОператорDo | ОператорFor | ОператорCycle | ОператорInline | ОператорContinue | ОператорLeave | ОператорRecur | ОператорExit
Блок = { Оператор | Выражение }

6.1. Оператор If

ОператорIf = IF | IFN Блок THEN Блок { ELSIF | ELSIFN Блок THEN Блок } [ ELSE Блок ] FI

Оператор IF позволяет выполнять ветвление по условию, блок между IF|IFN и THEN должен оставлять значение, которое интерпретируется как булевое (TRUE, если не равно 0), при этом в случае успеха (или неуспеха для IFN) выполняется блок операторов THEN, иначе блок ELSIF|ELSIFN или ELSE в случае их наличия. Части ELSIF могут повторяться произвольное число раз.

    Пример:

      IF ch==cUP THEN
        @y=(y-1) & 2
      ELSIF ch==cDOWN THEN
        @y=(y+1) & 2
      ELSIF ch==cRIGHT THEN
        @x=(x+1) & 2
      ELSIF ch==cLEFT THEN
        @x=(x-1) & 2
      FI

6.2. Оператор While

ОператорWhile = WHILE Блок DO Блок WEND

Оператор организует цикл с предусловием.

    Пример:
      @i=15;
      WHILE i DO  { печать чисел от 15 до 1 }
        putword(i);
        bl;
        @i--
      WEND

6.3. Оператор Repeat

ОператорRepeat = REPEAT Блок UNTIL Блок LOOP

Оператор организует цикл с постусловием

    Пример:
      @i=1;
      REPEAT { печать чисел от 1 до 15 }
        putword(i);
        bl;
        @i++
      UNTIL i>15 LOOP

6.4. Оператор Do

ОператорDo = DO Блок LOOP

Оператор организует бесконечный цикл.

6.5. Оператор For

ОператорFor = FOR Идентификатор {, Идентификатор } IN Данные DO Блок NEXT

6.6. Оператор Cycle

ОператорCycle = CYCLE Блок DO Блок LOOP

Оператор позволяет выполнить группу операторов заданное на перед число раз.

6.7. Оператор Inline

ОператорInline = INLINE ( Данные )

Оператор INLINE позволяет делать вставки непосредственно в машинном коде. При этом он может содержать не только числа, но и другие элементы. В данном случае будут компилироваться адреса этих элементов.

    Пример:

      MODULE HelloWorld

      SUB main
        INLINE(
    	 	0xBA,'Hello, world!$',
    		0xB4,0x09,
    		0xCD,0x21
        )
      RET

    END

6.8. Оператор Continue

ОператорContinue = CONTINUE

Оператор осуществляет безусловный переход на начало цикла (начинается следующая итерация). Циклами являются операторы: WHILE, REPEAT, DO, FOR и CYCLE.

6.9. Оператор Leave

ОператорLeave = LEAVE

Оператор осуществляет безусловный выход из цикла. Выход осуществляется из самого внутреннего цикла содержащего оператор.

    Пример:
      DO
        @ch=readkey()
        IF ch==cESC THEN LEAVE FI
        execute(ch)
      LOOP

6.10. Оператор Recur

ОператорRecur = RECUR

Оператор осуществляет вызов текущей процедуры (рекурсия).

6.11. Оператор Exit

ОператорExit = EXIT

Оператор EXIT осуществляет выход из текущей процедуры.

## 7. Синтаксис d2.

Модуль = MODULE | LIBRARY Идентификатор { Описание } END
Описание = ОписаниеПеременных | ОписаниеПроцедуры | ОписаниеЭкспорта | ОписаниеКонстант
ОписаниеПеременных = ОписаниеПеременной { , ОписаниеПеременной } ;
ОписаниеПеременной = Идентификатор [ Размер ] [ Инициализатор ]
Размер = '[' Число ']'
Инициализатор = < { Числа | Строки | ИменаПроцедур } >
ОписаниеПроцедуры = SUB [ ЭкспортнаяМетка ] [ Приоритет ] [ ОписаниеЛокальныхПеременных ] { Оператор | Выражение } RET
ЭкспортнаяМетка = '*'
Приоритет = '[' Число ']'
ОписаниеЛокальныхПеременных = VAR ОписаниеПеременной | ОписаниеПараметра;
ОписаниеПараметра = Идентификатор ИмпортнаяМетка
ИмпортнаяМетка = '*'
ОписаниеЭкспорта = USE ИмяБиблиотеки { , ИмяБиблиотеки }
Выражение = { Число | Идентификатор | Строка | СпециальныйСимвол }
Оператор = ОператорIf | ОператорWhile | ОператорRepeat | ОператорDo | ОператорFor | ОператорCycle | ОператорInline | ОператорContinue | ОператорLeave | ОператорRecur | ОператорExit
Блок = { Оператор | Выражение }
ОператорIf = IF | IFN Блок THEN Блок { ELSIF | ELSIFN Блок THEN Блок } [ ELSE Блок ] FI
ОператорWhile = WHILE Блок DO Блок WEND
ОператорRepeat = REPEAT Блок UNTIL Блок LOOP
ОператорDo = DO Блок LOOP
ОператорFor = FOR Идентификатор IN Данные DO Блок NEXT
ОператорCycle = CYCLE Блок DO Блок LOOP
ОператорInline = INLINE ( Данные )
ОператорContinue = CONTINUE
ОператорLeave = LEAVE
ОператорRecur = RECUR
ОператорExit = EXIT
Идентификатор = буква { буква | цифра }
Строка = "'" ЛюбойСимвол "'"
Число = [ 0 ] [ основание ] { цифра }
Цифра = 0..9,A..F
Основание = x|h|d|e|o|b
Разделитель = ',' | ';' | '(' | ')' | '[' | ']'
Идентификатор = Символ { Символ }
Символ = '`' | '!' | '@' | '#' | '$' | '%' | '^' | '&' | '*' | '-' | '+' | '=' | '"' | ';' | ':' | '<' | '>' | '.' | '\' | '|' | '/' | '?'
Буква = 'a'..'z','A'..'Z','а'..'я','А'..'Я'
Цифра = '0'..'9','A'..'F'

## 8. Пример игровой программы Tetris

    MODULE	tetris;

    USE	system,crt,math,write,timehund,putnum,iostd;

    CONST

    	N = 4,
    	n_e = 8,
    	e_size = 25,

    	Y = 3, X = 2, _ = 0,

    	buf_addr = 5000;

    VAR
    	{ элементы 5x5 }

    	E1 [e_size]  <
    			_ _ X _ _
    			_ _ X _ _
    			_ _ X _ _
    			_ _ X _ _
    			_ _ X _ _ >,

    	E2 [e_size]  <
    			_ _ X _ _
    			_ _ X _ _
    			_ _ X _ _
    			_ X X _ _
    			_ _ _ _ _ >,

    	E2i [e_size]  <
    			_ _ X _ _
    			_ _ X _ _
    			_ _ X _ _
    			_ _ X X _
    			_ _ _ _ _ >,

    	E3 [e_size]  <
    			_ _ _ _ _
    			_ X X _ _
    			_ X X _ _
    			_ _ _ _ _
    			_ _ _ _ _ >,

    	E4 [e_size]  <
    	                _ _ _ _ _
    			X X X _ _
    			_ _ X X _
    			_ _ _ _ _
    			_ _ _ _ _ >,

    	E5 [e_size]  <
    			_ _ _ _ _
    			_ _ _ _ _
    			_ _ X X _
    			X X X _ _
    			_ _ _ _ _ >,

    	E6 [e_size]  <  _ _ _ _ _
    		        _ _ _ _ _
    		        _ X X X _
    		        _ _ X _ _
    		        _ _ _ _ _ >,

    	E7 [e_size]  <  _ _ _ _ _
    		        _ _ X _ _
    		        _ X X X _
    		        _ _ X _ _
    		        _ _ _ _ _ >,

    	E8 [e_size]  <  _ _ _ _ _
    		        _ X _ _ _
    		        _ X X X _
    		        _ _ _ X _
    		        _ _ _ _ _ >,

    	E [16]		< E1 E2 E3 E4 E5 E6 E2i E8 >,

    	score
    ;

    CONST	width = 17, height=24;

    { 17 x 22 }

    SUB	. [9]	VAR x*,y*;
    	buf_addr+pred(y)*width+pred(x)
    RET

    SUB not VAR predicat*;
      IF predicat THEN
        FALSE
      ELSE
        TRUE
      FI
    RET

    SUB draw VAR i,j;
      @i=width;
      WHILE i DO
        @j=height;
        WHILE j DO
          w_char40(10+i,j,byte(i.j),219);
          dec(@j)
        WEND
        dec(@i)
      WEND
    RET

    SUB w_string VAR x*,y*,i<0>,str*;
      WHILE byte(str+i) DO
        x+i.y letb byte(str+i);
        inc(@i)
      WEND
    RET

    SUB write_score VAR i<5>,s;
      @s=score;
      REPEAT
        w_char40(i,1,4,s%10+0h30);
        @s=s/10;
        dec(@i);
      UNTIL s==0 LOOP
    RET

    VAR
      ln[18]< Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y 0>;

    SUB init VAR i<1>,j;

      w_mode(0);
      cursoroff;
      write_score;

      WHILE i<=height DO
        w_string(1,i,@ln);
        inc(@i);
      WEND

      @i=2;
      WHILE i;
      FOR x,y IN 0..N DO
        IF byte(Xt+x.Yt+y) & byte(x!y) THEN
          @F=FALSE
        FI
      NEXT F
    RET

    SUB rotate VAR NE[25],i,j;
      FOR i,j IN 0..N DO
        @NE+5*i+j letb byte(i!j)
      NEXT
      FOR i,j IN 0..N DO
        i!j letb byte(@NE+5*j+4-i)
      NEXT
      IF not(can(Xe,Ye)) THEN
        FOR i,j IN 0..N DO
          i!j letb byte(@NE+5*i+j)
        NEXT
      FI
    RET

    SUB show VAR x,y;
      FOR x,y IN 0..N DO
        IF byte(x!y) THEN (Xe+x.Ye+y) letb byte(x!y) FI
      NEXT
    RET

    SUB hide VAR x,y;
      FOR x,y IN 0..N DO
        IF byte(x!y) THEN (Xe+x.Ye+y) letb 0 FI
      NEXT
    RET

    SUB move VAR Xt,Yt;
      @Xt=Xe+dX;
      @Yt=Ye+dY;
      IF can(Xt,Yt) THEN
        @Xe=Xt;
        @Ye=Yt;
        FALSE
      ELSE
        TRUE
      FI
    RET

    SUB shift VAR line*,row;
      WHILE line>1 DO
        @row=2;
        WHILE row1 DO
        WHILE full(line) DO
          shift(line)
        WEND
        dec(@line)
      WEND
      write_score
    RET

    SUB run	VAR ch, delay_factor;
      REPEAT

        {пустить элемент в стакан}
        put;
        @delay_factor=5;

        REPEAT

          clrkbdbuf;
          show;
          draw;
          delay(delay_factor);
          hide;

          @dX=0;

          { обработать нажатие клавиши, если оно есть}
          IF keypressed THEN
            @ch=scancode;
            @dY=0;
            IF ch==RIGHT THEN
              @dX=1
            ELSIF ch==LEFT THEN
              @dX=0hFFFF
            ELSIF ch==UP THEN
              rotate
            ELSIF ch==DOWN | ch==SPACE THEN
              @delay_factor=0
            ELSIF ch==ENTER THEN
              REPEAT
                @ch=scancode
              UNTIL ch==ENTER | ch==ESC LOOP
            FI

          { иначе элемент опускается вниз }
          ELSE
            @dY=1
          FI

          UNTIL ch==ESC | (move & dY) LOOP

          show;
          replace

      UNTIL ch==ESC LOOP

    RET

    SUB done
      w_mode(3);
      putstring('Eugene programming, KSU 2001');
    RET

    SUB main
      init
      run
      done
    RET

    END