Язык программирования для ОЭВМ 1816BE5I

Микросхема 1816ВЕ51 [1] (аналог Intel 8051 [2]) помимо 8-разрядного процессора (МП) содержит ПЗУ, небольшое ОЗУ, два счета-таймера, четыре параллельных порта и один последовательный, новый процессор" и развитую систему прерываний, т.е. является стоящей однокристальной ЭВМ (ОЭВМ), способной обслуживать бытовые приборы, измерительное оборудование, телекоммуационные устройства, системы АСУ ТП и т.д.

Теоретически, использование ОЭВМ (вместо МП, микро- и даже -ш-ЭВМ) значительно повышает надежность этих систем, уменьшает их габариты, энергопотребление и стоимость. Однако итоговый выигрыш в значительной степени определяется качеством и стоимостью работки ПО.

Вопросам создания ПО посвящено много работ, реализованы жрасные языки и системы программирования, однако пресловутая гключительность" ОЭВМ до настоящего времени заставляет серьезных людей утверждать, что хорошее ПО для нее может быть разработанного только на языке ассемблера {!]. Па различных этапах развития числительной техники аналогичные категорические утверждения эвозглашались сначала для всех ЭВМ, затем для мини-компьютеров .д., по нисходящей.
После многих лет успешной разработки встроенных систем на юве микроЭВМ семейства "Электроника" и языка программировав я высокого уровня Модула-2 мрачная перспектива возвращения к ассемблеру для программирования ОЭВМ заставила меня искать более "интеллектуальные" и дешевые средства разработки ПО.

История
Выпуская в свет свою ОЭВМ, фирма Intel сопроводила ее внутрисхемным эмулятором и качественной кросс-системой с макроассемблером ASM-51 [3]. Через некоторое время стал доступен и язык высокого уровня PL/M-51 [4] - адаптированная версия языка PL/M, который, в свою очередь, был создан по образу и подобию "единого и универсального" языка программирования PL/1. Многократно обсуждавшиеся недостатки PL/1, усиленные при переходе к ОЭВМ, породили сложный, противоречивый и громоздкий язык, полностью освоить все правила (а главное, все исключения из них) которого чрезвычайно трудно.
Очевидно, для того чтобы все-таки убедить потенциальных покупателей в простоте программирования своего изделия, Intel отказывается от кросс-средств и выпускает ОЭВМ со встроенным интерпретатором языка Бейсик [5]. Для этого пришлось увеличить объем внутренней памяти, ввести дополнительные аппаратные средства (в результате получилась новая микросхема 8052) и подключить внешнее ОЗУ большого объема, реализовав классическую ЭВМ с архитектурой фон Неймана. Сам Бейсик практически не пригоден для создания надежного ПО. Встроенные инструментальные средства достаточно громоздки и обременительны для ОЭВМ и не обеспечивают разработчику минимально необходимого комфорта.
Таким образом, известные инструментальные средства для ОЭВМ (созданные 5-10 лет назад) совершенно не соответствуют современному уровню технологии программирования. Появления новых инструментальных систем за рубежом ожидать не следует, так как на смену 8-разрядным там уже пришли 16- и даже 32-разрядные ОЭВМ [6], на поддержку которых брошены лучшие программистские силы. В нашей стране, к сожалению, только сейчас налажен массовый выпуск ОЭВМ 1816ВЕ51 и совершенствование разработки их ПО приобретает особую актуальность.

Новый язык
Вышеизложенные мотивы побудили меня начать разработку нового языка для програмирования ОЭВМ 1816ВЕ51. Первоначально предполагалось продолжить ряд языков низкого уровня (или структурных ассемблеров): PL360 [7], PL11, ..., которые обеспечивают генерацию (к сожалению, только благодаря интеллекту программиста) максимально эффективного кода. Однако предварительные эксперименты показали, что достаточно эффективный код можно получить и при использовании "обычного" языка программирования высокого уровня, подобного Паскалю или Модуле-2. Это семейство языков, созданное Н.Виртом [8] (влючая Модулу, Оберон и многочисленные диалекты Паскаля и Модулы-2), завоевало наибольшую популярность и знакомо практически каждому программисту со студенческой скамьи.
Новый язык очень прост. Его полный синтаксис приведен в приложении. Так как основой языка послужила Модула-2, в данном тексте не описаны общие вопросы (словарь и изображение, правила видимости объектов, операторы и т.д.), а выделены лишь специфические особенности.
Данные. В языке используются только элементарные типы данных, поддерживаемые аппаратурой:
BIT - логическая переменная, принимающая значения TRUE и FALSE;
BYTE - целое число в диапазоне 0 ... 255.
Все используемые переменные и константы должны быть объявлены: глобальные - в начале программы, локальные - в начале процедуры. Здесь и далее будут использоваться объявления из следующего примера:
CONST max-7; cr-13; line-("1816BE5l",cr);
matrix-(3FH,44H,44H,44H,7FH),(43H,45H,49H,JlH,6IH);
VAR X,Y:BYTE; char:BYTES[8]; set:BITS;
Байты можно сгруппировать в одномерный массив (типа BYTES), число элементов которого не превышает 256, так как тип индекса - тоже BYTE (следует напомнить, что память данных имеет размер всего 128 байт). Индекс изменяется от нуля до максимального значения, указанного в объявлении.
Биты также можно объединить с помощью убогого симбиоза типов множества и записи - типа BITS. Переменные этого типа, с одной стороны, эквивалентны байтам (например, допустимо выражение set+X), а с другой, обеспечивают обращение к отдельным битам, явно указанным после разделительной точки (например, sel.3:=a). К сожалению, аппаратура ОЭВМ поддерживает эффективный доступ к битам, адреса которых известны еще на этапе генерации кода, поэтому номер бита должен быть константным выражением. Новые ключевые слова введены вместо традиционных BOOLEAN, ARRAY и SET, чтобы насторожить хорошо знакомых с Паскалем или Модулой-2 программистов: это совсем другие типы данных (в основном за счет особенностей архитектуры и системы команд так называемого "битового процессора").
Базовый тип всех констант - BYTE. Из-за ограниченного объема памяти данных постоянные наборы значений необходимо размещать в относительно большой памяти программ. Такую возможность предоставляют структурные константы, отсутствующие как в Паскале, так и в Модуле-2, но с завидным упорством возникающие во всех их коммерческих реализациях. Для ОЭВМ структурные константы просто необходимы. Язык допускает даже двумерные массивы констант отчасти для адекватного отражения двумерных объектов (например, матрицы отображения символов в дисплее или принтере), но в основном для создания наборов данных, превышающих 256 байт (например, 96 символов ASCII х 5 колонок - 480 байт).
Выражения. Операндами выражений являются простые константы (объявленные ранее или вводимые непосредственно: шах, "А", 33), элементы структурных констант (line[5], matrix ["A",4j), переменные (X, set.4) и элементы массивов (char[X]). Старшинство операций однозначно определяется синтаксисом по аналогии с Модулой-2:
отрицание (NOT) - высший приоритет; операции типа умножения (*, /, MOD, AND); операции типа сложения (+, -, OR, XOR); операции отношения (-, #, >, <, >=, <=-) - низший приоритет. Арифметические операции (сложение, вычитание, умножение, деление и вычисление остатка) выполняются над байтами и вычисляют результат типа BYTE. Логические операции (И, ИЛИ, исключающее ИЛИ и НЕ) применимы как к битовым операндам, так и к байтовым (что облегчает решение многих задач, например маскирование отдельных полей). Оба операнда должны иметь один и тот же тип, совпадающий с типом результата. Операции отношения сравнивают байтовые операнды и дают результат типа BIT.
Операторы. Реализованный набор операторов существенно богаче, чем типы данных. Кроме присваивания - основного простого оператора любого языка, реализованы следующие структурные операторы:
условный (TF ... THEN ... ELSIF ... THEN ... ELSE ... END); цикла с пред- (WHILE ... DO ... END) и пост-условиями
(REPEAT ...UNTIL ...); обобщенного цикла (LOOP ... EXIT ... END).
Процедуры. Следующий уровень структурирования программ - процедуры. Отсутствие в ОЭВМ эффективных косвенных и индексных методов адресации существенно ограничивает возможности языка: запрещен рекурсивный вызов процедур, параметры передаются только по значению, не используются вложенные процедуры.
Объявление процедуры должно текстуально предшествовать ее вызову. Пример объявления простой процедуры. PROCEDURE Put(x:BYTE); BEGIN Y:-x & 60H END; и ее вызова: Put(Char(Y]>.
Для эффективного выполнения часто используемых функций реализовано несколько стандартных процедур, поддерживаемых компилятором:

INC и DEC - инкремент и декремент байта; CPL - инверсия бита;
DELAY - программная задержка (аргумент задает задержку в машинных циклах).
Например, оператор INC(Y) выполняется в три раза быстрее оператора Y:-Y+l.
Средства низкого уровня. Переменные размещаются во внутренней памяти данных последовательно в порядке их объявления. Чтобы вмешаться в этот процесс, в квадратных скобках после имени переменной можно поставить ключевое слово F^XT для размещения переменной во внешней памяти данных (ее размер обычно существенно больше, чем внутренней, однако доступ сложнее и дольше) и (или) явно указать адрес переменной либо константным выражением, либо через ранее определенную переменную (но не константу). Следующий пример демонстрирует эти возможности:
VAR PI [90H] :BITS;
Buffer[EXT) :BYTES [256); outPin [—P1.3] :BIT;
Как избавить программиста от рутинной работы по объявлению аппаратных регистров ЭВМ и избежать естественных в этом случае ошибок? Стандартные символические имена можно сделать доступными с помощью механизма импорта. Например, предложение IMPORT PI; в начале программы заменяет объявление Р1 в предыдущем примере. Всего импортируются 63 переменных трех типов:
VAR ACC,B,PSW,PO,PI,P2,P3,IPC.IEC,TCON,SCON :BITS: SP,DPL,DPH,PCON,TMOD,TLO,TL1 .ТНО.ТН 1 ,SBUF :BYTE; IT0,IE0.IT1 .IE I ,TR0,TF0,TR 1 ,TF 1 .RI.TI. RB8,TB8,REN,SM2,SM 1 ,SMO,EXO,ETO,EX 1 ,ET 1, ES,EA,RXD,TXD,INT0.INT1,T0,T1,WR,RD.PX0, PTO,PX 1 ,PT 1 ,PS,OV,RSO,RS 1 ,FO,AC,CY,P :BIT;

Прерывания.

Прерывания трактуются, как обычный вызов процедуры, однако не по желанию программиста, а по инициативе внешнего окружения.
Псевдопроцедура обработки начинается с ключевого слова INTERRUPT, после которого следует идентификатор источника прерывания. В ОЭВМ их пять: ЕХТО и ЕХТ1 - два внешних запроса; TIMERO и TIMER 1 - два таймера-счетчика; SERIALPORT - последовательный порт. Используемые в программе источники прерываний должны быть включены в список импорта. Приведенный ниже обработчик прерываний последовательного порта посылает в линию символы из буфера и инвертирует сигнал на Р1.3 при приеме каждого байта:
INTERRUPT SERIALPORT;
BEGIN
IF TI THEN SBUF:-Buffer[X]; TI:-FALSE: INC(X) ELSE CPL(outPin); RI—FALSE END
END;
Ассемблер. Для повышения эффективности или выполнения специфических управляющих или вычислительных действий тело любой процедуры, обработчика прерывания или самой программы можно реализовать на ассемблере. При этом ключевое слово BEGIN заменяется на ASSEMBLER. Средства языка ограниченны. Поэтому сам программист должен создать необходимые для решения конкретной задачи сервисные функции в виде ассемблерной процедуры (для реализации арифметики многократной точности, указателей и адресных ссылок, обработки исключительных ситуаций и т.д.). Например, процедура add вычисляет сумму двух 16-разрядных слов:
VAR hX,lX,hY,!Y:B'YTE; PROCEDURE add;
ASSEMBLER MOV A,!X ADD A.IY MOV IX.A
MOV A.hX ADDC A.hY MOV hX.A RET
END;
При использовании ассемблера программист должен сам заботиться о выполнении сервисных функций: сохранение (восстановление) контекста при прерывании, возврат из процедуры и т.д. Программист-фанатик может реализовать всю программу на ассемблере.

Даже в этом случае часть конструкций продолжает работать (объявления переменных и констант, импорт, процедуры и обработка прерываний), а сам язык вырождается в структурный ассемблер.
Пример законченной программы. Краткое описание языка завершает небольшая программа воспроизведения простейшей мелодии, специально для сравнения заимствованная из [I], где она реализована на ассемблере. PROGRAM Music;
Таймер 0 используется для отсчета временных интервалов, таймер 1 формирует выходной сигнал, который через усилитель подается на громкоговоритель. В зависимости от используемого кварцевого резонатора мелодия транспонируется, что, впрочем, обычным человеком на слух не воспринимается. Остальные пояснения приведены в тексте самой программы в виде комментариев (заключенных в фигурные скобки).
Реализация. Компилятор реализован на языке программирования Модула-2, что позволяет легко переносить его на различные типы ЭВМ. Размер программы - около 2 тыс. строк исходного текста.
Эффективность генерируемого кода оценивалась для относительно больших программ, т.е. таких, в которых требуется достаточно серьезное преобразование данных и большинство переменных размещается в памяти, а не в специальных и общих регистрах ОЭВМ. По сравнению с ассемблерной версией программа на языке высокого уровня в среднем занимает в 1,3 - 1,5 раза больше программной памяти и работает в 1,2 - 1,3 раза медленнее (за счет того, что регистры общего назначения размещены в ОЗУ ОЭВМ, их использование существенно уменьшает размер поля для кодирования операнда команды и не влияет на время выполнения последней).


Так как программы для ОЭВМ относительно невелики, принято решение отказаться от раздельной компиляции частей программы. Компилятор сразу создает загрузочный код.
Отладка. Для поддержки процесса отладки ПО вместо обычно используемого сложного, дефицитного и дорогостоящего внутрисхемного эмулятора предлагается классическая технология инструментальной и целевой систем, соединенных линией связи. На инструментальной ЭВМ (IBM PC, ДВК или семейства "Электроника") функционирует редактор текста, компилятор и сервисная программа, которая обеспечивает:
загрузку разрабатываемой программы в целевую систему по линии связи;
прием по линии связи дампа памяти данных ОЭВМ и его анализ в терминах исходного текста;
декодирование загрузочного кода, выдачу карты памяти и т.д.
На целевой системе на период отладки устанавливается небольшое устройство, в котором размещается эмулятор памяти программ, резидентное ПЗУ для загрузки ОЗУ эмулятора по линии связи и преобразователь TTL-уровней сигналов последовательного порта ОЭВМ в сигналы, соответствующие стандарту RS-232, или "токовую петлю" 20 мА.
Средства языка позволяют создавать контрольные точки для отладки программ. Процедура DEBUG (имя которой должно быть указано в списке импорта) обеспечивает передачу по линии связи дампа памяти данных. Дамп будет передан также при завершении выполнения тела программы (что для встроенной системы является аварийной ситуацией).
Компилятор не предусматривает генерацию кода для проверки корректности вычислений на этапе выполнения программы (контроль арифметического переполнения, обращения к несуществующей памяти, выход индекса за границу массива и т.д.). Для облегчения отладки программ разработчик может сам включить такие проверки в исходный текст. Например:
PROGRAM prog;
IMPORT DEBUG.Pl;
VAR stack:BYTES[ 10]; i:BYTE;
PROCEDURE push (n;B"YTE);
BEGIN IF i>9 THEN {такого элемента в массиве нет} DEBUG END; stackli):—n; INC(i)
END;
END prog.


Так как ошибки, к глубокому сожалению разработчика, могут появляться не только в процессе отладки, но и при эксплуатации ПО, для повышения надежности программ не рекомендуется исключать проверки после окончания отладки. Несложные процедуры' обработки исключительных ситуаций позволяют восстанавливать работоспособность, в противном случае действия системы трудно предсказуемы.
Процесс разработки ПО протекает следующим образом: программист на инструментальной ЭВМ подготавливает исходный текст программы, компилирует ее и загружает в целевую систему для отладки. Функционирование целевой системы проверяется визуально, посредством приборов, а также с помощью дампа памяти, переданного процедурой DEBUG при возникновении непредвиденной или заранее спланированной аварийной ситуации. Анализ дампа на инструментальной ЭВМ выявляет последовательность вызова процедур и обслуживания прерываний, содержимое глобальных, локальных переменных и регистров специальных функций.


Заключение. С помощью описанных инструментальных средств были разработаны несколько экспериментальных встроенных систем и три законченных изделия: стенд для балансировки колес, многоканальный радиодозиметр и АСУ установкой изготовления эмали. Язык оказался очень удобным и достаточно гибким. Размер ассемблерных вставок не превышает 5%. Качество разработанных программ улучшается настолько, что для их отладки вполне достаточно предложенной простой и дешевой технологии. Затраты времени и средств при разработке ПО встроенных систем на основе ОЭВМ 1816ВЕ51 снижаются почти на порядок.
К сожалению, сам язык не получил названия. Вполне логичное PL51 не подходит, так как по традиции гак называют структурные ассемблеры. Рабочее название Z слишком сильно коррелирует с продукцией фирмы Zilog, которая ОЭВМ никогда не производила.
В заключение хотелось бы выразить благодарность Б.Львину за ряд полезных советов и помощь в оформлении статьи и сотрудникам сектора вычислительной техники Воронежского госуниверситетета за поддержку и помощь при создании аппаратных средств и реализации целевых систем.
 


Обсудить вопрос в студенческом форуме

 

Сайт содержит информацию о учебном заведении и студенческой общине и не является официальным