В процессе поиска материалов для предыдущей заметки о Настоящих Программистах наткнулся на ещё один интересный образец того времени: “Историю Мэла”, опубликованную 21 мая 1983 года в сети Usenet Эдом Натером про свою работу программистом в начале 1960-х годов в компании Royal McBee Computer Corporation.

В поисках перевода наткнулся на неплохой, но, к сожалению, это практически подстрочник, из которого (да и из оригинала) не всегда понятно о чём речь вообще. Перевёл сам и добавил [пояснения]:

История Мэла

Недавно я видел заметку о мачизме в программировании, автор которой сделал довольно резкое заявление, что Настоящие Программисты пишут на Фортране.

Может сейчас так и есть, в наш век пива “лайт”, наладонных калькуляторов и программ, “дружелюбных к пользователю”. Но в те Старые Добрые Времена, когда сам термин software вызывал улыбку, а Настоящие Компьютеры были сделаны из барабанов и вакуумных ламп, Настоящие Программисты писали машинный код. Не Фортран, не Ратфор и не ассемблер. Машинный код. Голые, неприкрашенные, шестнадцатеричные числа. Напрямую.

Чтобы новое поколение программистов не выросло в незнании об этом великом прошлом, я чувствую себя обязанным рассказать о том, как Настоящий Программист писал код. Я буду называть его Мэл, потому, что это его имя.

Я познакомился с Мэлом когда устроился на работу в Royal McBee Computer Corporation, сейчас уже несуществующее подразделение компании по производству печатных машинок. Мы занимались сборкой и обслуживанием компьютеров LGP-30, небольших, дешёвых (для того времени) компьютеров с барабанной памятью, и только-только осваивали производство RPC-4000: значительно улучшенного, ускоренного, транзисторного, компьютера с барабанной памятью. Память на ферритах на тот момент стоила слишком дорого, да уже и устарела. (Поэтому вы ничего и не слышали ни о такой компании, ни о таких компьютерах.)

Меня наняли для разработки компилятора Фортрана для этого чуда, и Мэл был моим наставником. Но он не признавал компиляторов: “Если программа не может изменять свой код, на что она годится?” - спрашивал он.

Мэл написал, в шестнадцатеричных кодах, конечно, самую популярную программу компании. Она работала на LGP-30 и играла в блек-джек с потенциальными покупателями на компьютерных выставках. Эффект это имело потрясающий. Стенд с LGP-30 был постоянно забит людьми, а продавцы из IBM крутились вокруг и что-то обсуждали. Оказывало ли это хоть какое-то влияние на продажи - это был вопрос, который мы никогда не задавали.

Задачей Мэла было написать такую же программу для RPC-4000. (Портирование? Не, не слышали.) Новый компьютер имел систему адресации one-plus-one: к каждой машиной инструкции, в дополнение к её опкоду и к адресу операнда добавлялся ещё один адрес - адрес на магнитном барабане, с которого нужно считать следующую инструкцию.

Если сказать в современных терминах: каждая инструкция заканчивалась GOTO. Попробуйте такое на Паскале!

[прим.: Тут нужно пояснить, как работает барабанная память и компьютеры, построенные на ней. На вращающемся барабане находятся несколько магнитных дорожек, над каждой из которых закреплена отдельная неподвижная считывающая головка. Считать данные можно только те, которые находятся непосредственно под головками. Сам барабан при этом вращается с постоянной скоростью. Среднее время доступа к произвольной ячейке памяти, таким образом, составляет 1/2 полного оборота барабана, но, задавая адрес следующей команды, можно, зная скорость вращения барабана и скорость работы компьютера, управлять временем доступа к нужным данным.]

Мэлу нравился RPC-4000 за то, что у него появилась возможность оптимизировать код: он располагал инструкции на барабане таким образом, чтобы к моменту, когда компьютер только-только заканчивал исполнение предыдущей команды, следующая была на барабане в точности в позиции готовой к чтению. Существовала программа для выполнения этой работы, “оптимизирующий ассемблер”, но Мэл отказывался ею пользоваться. “Никогда не знаешь, куда она что положит и приходится отдельно задавать константы”, - объяснял он.

Прошло немало времени, прежде чем я понял смысл этого замечания. Так как Мэл знал числовое значение каждой инструкции, которая располагалась каждая по своему адресу, он мог её использовать как числовую константу. Он мог взять код предыдущей инструкции сложения и умножить на него, если его числовое значение оказывалось подходящим. Неудивительно, что другие сотрудники испытывали трудности, изменяя код Мэла.

Я сравнивал скорость работы программ, оптимизированных Мэлом, с кодом, который создавал оптимизирующий ассемблер, и Мэл всегда выигрывал. Метод написания программ “сверху вниз” ещё не был изобретён, да и Мэл всё равно не стал бы его использовать. Он писал снизу вверх - сначала самые важные циклы, располагая их на ещё свободном барабане и выбирая для них самые оптимальные адреса, так, чтобы минимизировать время доступа к ним. Оптимизирующий ассемблер не был настолько умён.

Мэл никогда не писал пустых циклов для задержки времени, даже при работе с непослушным терминалом Flexowriter, который требовал при выводе пауз между символами. Он просто располагал инструкции так, что та, которую нужно было выполнить следующей, находилась сразу позади читающего блока, и для её прочтения требовалось ждать полного оборота барабана. Он придумал незабываемый термин для этой процедуры. Хотя термин “оптимальный” и является абсолютной характеристикой, как и “уникальный”, стало общепринятым использовать его относительно: “не совсем оптимальный”, “менее оптимальный”, “не очень оптимальный”. Мэл называл адреса с максимальной временной задержкой доступа “самыми пессимальными”.

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

Мэл заартачился. Он чувствовал явный обман, которым это и было, что вступило в противоречие с его искренней честностью программиста, которым он был, и поэтому он отказался. Мэла принялись уговаривать глава отдела продаж, и Большой Босс, и, по научению босса, некоторые коллеги. В итоге Мэл сдался и написал код. Но переключатель заработал наоборот: при прикосновении, программа начинала жульничать и всегда выигрывала. Мэл ликовал. Он объявил, что его подсознание бесконтрольно этично и наотрез отказался исправлять программу.

После того, как Мэл оставил компанию ради большего количе$$тва зелени, Большой Босс попросил меня взглянуть на код и постараться исправить проверку. Я неохотно согласился. Разбираться в коде Мэла было приключением.

Я считаю программирование формой искусства, которое требует посвящения и по-настоящему оценено может быть только другими таким же посвящёнными. Некоторые сокровища и бриллианты навечно скрыты от глаз простых людей из-за самой природы процесса. Можно многое узнать о человеке, просто читая его код, даже в шестнадцатеричной системе счисления. Мэл был, я считаю, непризнанным гением.

Величайшим шоком для меня было найти невинный цикл, в котором не было условий для выхода. Вообще. Не было. Условий. Никаких. Здравый смысл подсказывал, что такая программа просто должна навечно зависать. Но она работала, каким-то образом проходя через этот код и благополучно завершаясь. У меня ушло две недели, чтобы понять в чём тут дело.

В компьютере RPC-4000 было применено новое веяние времени - индексный регистр. Он позволяет программисту писать циклы для обработки последовательных наборов данных: число в индексном регистре добавляется к операнду инструкции, и всё что нужно сделать - это только увеличивать значение в индексном регистре. Но Мэл никогда его не использовал.

Его код зачитывал инструкцию в машинный регистр, добавлял единицу к её адресу и сохранял обратно. Потом он выполнял модифицированную команду прямо из регистра. Цикл был написан с таким расчётом, что как только инструкция была выполнена, следующая на барабане уже была в позиции, готовой к чтению. Всё. Никаких проверок в этом цикле не было.

Важнейшим событием стало то, что я заметил у инструкции установленный флаг использования индексного регистра. Бит, который находился между опкодом операции и адресом. Но Мэл не пользовался индексным регистром, оставляя его всегда нулевым. И тут меня озарило.

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

[прим.: Поясню, как это работает, на псевдоассемблере для несуществующего компьютера. Допустим, что нам нужно обработать набор данных из 100 байт, которые расположены по адресу #4444, причём, так повезло, что у нужной инструкции обработки данных опкод на единицу меньше опкода инструкции jump (переход). Предположим, что это команда add. Пишем цикл, как у Мэла:

cmd: addi r, [#FF9B] ; #FFFF - 100 = #FF9B
...
loop: mov r1, [cmd]
add r1, 1
mov [cmd], r1
exec r1
jump loop
...
#4444 data...

На каждом шаге читаем значение из адреса cmd, увеличиваем на единицу, записываем обратно и выполняем. На каждом шаге адрес увеличивается на 1. Но нам нужно обработать данные по адресу #4444, поэтому положим перед циклом в индексный регистр #44A8 (#4444-#FF9B-1=#44A8). Таким образом, операция чтения первого значения произойдёт по адресу #FF9C+#44A8 = #4444. Потом команда изменяется и вместо addi r, [#FF9C] становится addi r, [#FF9D] и т.д. до тех пор, пока не добежит до addi r, [#FFFF]. После увеличения численного значения этой команды #FFFF превратится в #0000 а бит переполнения добавится к следующему разряду, где у нас лежит опкод операции add, что превратит её в jump #0000 и код продолжит выполнение с этого адреса.]

Я не поддерживал связи с Мэлом, и поэтому не знаю, изменился ли его подход к программированию с тех пор. Мне хочется верить, что нет. В любом случае, я был настолько поражён найденным фрагментом кода, что прекратил всякие поиски этой несчастной проверки и сказал Большому Боссу, что не могу её найти. А он и не удивился.

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


Крайний справа стоит Мэл. (Librascope Librazette, volume 4, number 6, August 1956)