PL/M — это язык программирования, который часто остаётся в тени своих более известных современников, таких как C или Pascal. Однако именно он стал мостом между низкоуровневым ассемблером и высокоуровневым программированием в эпоху зарождения микрокомпьютеров.
История создания: консультант Intel и первый язык для микропроцессоров
В начале 1970-х годов инженеры Intel столкнулись с проблемой: их новые микропроцессоры (8008, 8080) требовали программного обеспечения, но писать его на ассемблере было мучительно долго. Решение пришло от внешнего консультанта.
В 1973 году компания Intel наняла Гэри Килдалла (Gary Kildall), талантливого программиста и преподавателя Военно-морской аспирантской школы Монтерея, для написания программного обеспечения для демонстрации возможностей микропроцессора Intel 8008. Килдалл, имевший опыт работы с мейнфреймами Burroughs B5500 и языками высокого уровня (ESPOL), быстро понял, что ассемблер неэффективен. За несколько месяцев он создал совершенно новый язык — PL/M (Programming Language for Microcomputers).
Первый компилятор был готов уже в 1973 году для процессора Intel 8008. Вскоре после этого Килдалл основал собственную компанию Digital Research (изначально называвшуюся MAA — Microcomputer Applications Associates) и начал распространять PL/M и свои другие разработки (включая легендарную ОС CP/M), сделав их индустриальным стандартом.
Истоки синтаксиса: «Килдалл взял из PL/I только ключевое слово DCL»
Глядя на код, многие замечают сходство с языком PL/I. Однако, как отмечали историки, «это совершенно не так, поскольку PL/M заимствовал черты нескольких языков, в частности ALGOL, XPL и в том числе PL/I». Сам Килдалл признавался: «Я черпал вдохновение из PL/I, но не для синтаксиса — для идей системного программирования. При этом на синтаксис повлиял язык XPL». Ключевое слово DCL в PL/M и декларативный подход — действительно напоминают PL/I, но внутренняя логика работы с памятью, указателями и прерываниями была уникальной разработкой Килдалла.
Уровень языка
PL/M позиционировался как машинно-ориентированный язык высокого уровня. Это означает, что он был абстракцией над ассемблером, но при этом позволял программисту напрямую управлять аппаратными ресурсами (памятью, портами ввода-вывода, прерываниями).
Ключевая особенность: компилятор PL/M создавал очень компактный объектный код. Один оператор языка заменялся в среднем шестью ассемблерными командами, а результирующий машинный код занимал всего в 1,2 раза больше памяти, чем написанная вручную ассемблерная программа.
Уровень PL/M можно оценить как «ниже C, выше ассемблера». Он был идеальным инструментом для написания операционных систем, драйверов и встроенного ПО для ранних микропроцессоров.
Синтаксис и основные конструкции
Комментарии: /* комментарий */
Объявление переменных:
Переменные бывают двух базовых типов: byte (один 8-битный байт) и address (два байта). Из переменной типа address можно вычленить младший и старший байт, используя функции low() и high().
declare var byte; /* переменная var типа byte */
declare var byte at (0fh); /* разместить var по адресу 0Fh */
declare (x, y) byte; /* объявить несколько переменных сразу */
declare array(15) byte; /* массив из 16 элементов (0..15) */
declare x byte initial (255); /* объявление с инициализацией */
Работа с портами ввода-вывода:
Из порта данные получает функция input(), а записывается output().
declare x byte;
x = input(0); /* ввели данные с 0 порта */
x = x + 1;
output(1) = x; /* вывели значение на порт 1 */
Условный оператор и CASE:
if условие then
оператор;
else
оператор;
do case 3; /* количество вариантов = n-1 (от 0 до 2) */
x = -x * 2; /* при 0 */
x = x; /* при 1 */
x = x * 2; /* при 2 */
end;
Циклы:
do while (логическое выражение);
операторы;
end;
do переменная = начальное_значение to конечное_значение by шаг;
операторы;
end;
Структуры:
declare struct$name (element1 (8) word, element2 (8) word);
Структуры могут быть вложенными. Доступ к элементам структуры осуществляется через «.»:
declare date (day word initial (5), month word initial (5), year word initial (94));
output(0) = date.day;
output(0) = date.month;
output(0) = date.year;
Процедуры и функции:
qr: procedure (varible) word; /* процедура возвращает тип word */
return varible * varible; /* возвращает квадрат аргумента */
end qr;
Использование: call procedure(x).
Метки и GOTO:
Для использования оператора GOTO (или GO TO) необходимо объявить метку:
declare label$name label;
label$name:
...
goto label$name;
Макросы:
declare name literally 'string';
Указатели:
Указатели — тип данных, по длине аналогичный address:
declare x initial (255); /* объявляем переменную */
declare addx based x; /* теперь в addx — адрес переменной x */
В процессе работы адрес переменной можно получить, написав перед именем переменной «.»: .x.
Битовые функции:
scr()— циклический сдвиг вправоscl()— циклический сдвиг влевоshr()— сдвиг вправоshl()— сдвиг влевоrol()— прокрутка влевоror()— прокрутка вправо
Работа с прерываниями: В ранних версиях была очень простой, но в более поздних диалектах (например, PL/M-386) была значительно расширена поддержкой специальных регистров и системных переменных для работы с процессорами i386 и i486.
Диалекты и эволюция (PL/M-80, PL/M-86, PL/M-386)
Названия диалектов PL/M имели простую логику: номер в названии указывал на целевой микропроцессор. Компиляторы были созданы для Intel 4004, 8008, 8080/85, 8051, 8086/8088, 80186/80188, 80286 и 80386, 80486 и других.
- PL/M-80 (Intel 8080/85 и совместимые Z80). Исходная версия, определившая синтаксис языка. Работал под управлением ОС ISIS-II, CP/M-80, а затем и на PC в виде эмуляторов CP/M-80.
- PL/M-86 (Intel 8086). Использовался для разработки системного ПО для первых IBM PC-совместимых компьютеров. Сам CP/M-86 был написан на PL/M-86.
- PL/M-286 (Intel 80286). Поддержка 16-битного защищённого режима.
- PL/M-386 (Intel 80386). Самая продвинутая версия. Поддерживал 32-битные адреса, защищённый режим, виртуальную память, а также многозадачность. В PL/M-386 были добавлены новые типы данных (
CHARINT,SHORTINT,LONGINT,HWORD,QWORD), встроенные функции для работы со строками (FIND,CMP,SKIP) и множество расширений для поддержки 32-битной архитектуры.
Где применялся PL/M?
PL/M был не просто академическим языком, а работающим инструментом:
- Сердце CP/M: Операционная система CP/M, ставшая стандартом для 8-битных микрокомпьютеров, была написана на PL/M и ассемблере.
- Разработка для IBM PC: Раннее системное ПО, включая BIOS для некоторых клонов IBM PC, было создано с использованием PL/M-86.
- Встроенные системы: PL/M-51 (для Intel 8051) десятилетиями использовался в промышленных контроллерах, автомобильной электронике и бытовой технике.
- В СССР: Язык был адаптирован под названием ПЛ/М-80 и использовался в операционной системе ДОС 1800 для советских компьютеров, совместимых с IBM PC.
Наследие и влияние
Хотя сейчас PL/M почти забыт, его наследие огромно:
- Первая ласточка: PL/M стал первым языком высокого уровня для микропроцессоров. Он показал, что даже на скромных 8-битных машинах можно писать сложное системное ПО не только на ассемблере.
- Колыбель для CP/M: Без PL/M не было бы CP/M. А без CP/M не было бы того стремительного роста индустрии программного обеспечения в конце 1970-х годов. Именно CP/M стала основой для MS-DOS.
- Гибридность: PL/M был пионером «гибридного» программирования (PL/M + ассемблер), позволяя разработчикам писать 90% кода на высоком уровне, а критичные участки (обработка прерываний, работа с регистрами) — на ассемблере.
Интересные факты
- Конкуренция с Керниганом и Ритчи: В то время как Деннис Ритчи только начинал работу над C в Bell Labs, PL/M уже активно использовался в коммерческих проектах Intel.
- «Лишний» пробел: Килдалл специально добавил
(после ключевого словаDECLARE, чтобы его язык было легче распознавать в тексте. - PL/M в космосе: Некоторые источники утверждают, что модифицированные версии PL/M использовались в программном обеспечении космических шаттлов и межпланетных станций. Других подтверждений этому факту нет, но звучит правдоподобно для встроенных систем высокого уровня надёжности.