В мире наступили восьмидесятые. IBM захватывал рынок профессиональных компьютеров своими PC и PC XT — родоначальниками всех современных настольных компьютеров. Джобс одну за другой выпускал новые модели Apple. Commodore 64 и ZX Spectrum гремели по миру. А в это время в советском блоке продолжали выпускаться Ascota 170 — механические компьютеры родом из начала пятидесятых. Почему-то, в рунете (да и в остальном интернете тоже) мало говорят об этих удивительных машинах, едва ли не единственных серийно (больше трёхсот тысяч с 1955 до 1983 годов) выпускавшихся Тьюринг-полных механических компьютерах. Я и сам о них узнал только тогда, когда Аскота случайно попала мне в руки.
Надеюсь, моя статья сможет изменить это.
Моя Аскота закончила считать квадратный корень из 2.
Вообще, я — коллекционер механических счётных машин. К сожалению, машины эти обычно здоровенные, а музея бесконечного размера у меня нет, поэтому брать приходится только самое интересное. Вот и когда Сергей Фролов поинтересовался, ли нужна ли мне Аскота, я пару минут погуглил и выяснил, что Аскотами назывались весьма скучные и примитивные машинки, которые не стоят занимаемого ими места. Что Сергею и сказал. К счастью, Сергей ответил, что речь не просто об Аскоте, а об особо редкой модели 170. Я погуглил ещё раз, и был пленён.
Оказалось, что мне предлагают одну из сложнейших когда либо производившихся механических машин, бухгалтерский счётный автомат, программируемый с помощью «гвоздей», вбиваемых в специальную доску молотком. К сожалению, общение с продавцом на Авито показало, что Аскота некомплектная, поэтому брать её бесполезно. Потом на Авито появилась ещё одна половинка Аскоты (продавец мне больше часа пытался доказать, что она целая, а не половинка), потом вопрос завис на год… А потом мне написал первый продавец, и сказал, что нашёл недостающие части! Здесь заканчивается предыстория и начинается, собственно история.
Конечно, к этому моменту я прочитал, что Аскота-170 — вроде как программируемая машина. Но «программируемая» — понятие растяжимое, и перед покупкой я попытался что-нибудь узнать о её возможностях. К счастью, это оказалось несложно: Аскоты поставлялись в СССР массово, и про них выпускали десятки книг. Одну из них, тоненькую брошюрку про методы применения, я купил на Авито — но понял из неё, увы, немного — в основном, то, что машина постоянно работает в цикле и как-то поддерживает переходы. Не знаю, что бы я делал с этой информацией — но мне в голову пришла идея: на станции метро Библиотека имени Ленина находится Библиотека имени Ленина! Весь двадцатый век туда стекаются обязательные экземпляры выпускавшихся у нас книг, и книги про Аскоты там должны быть. Действительно, в электронном каталоге Ленинки нашлось больше трёх сотен книг с упоминанием Аскоты 170. Больше того, оказалось, что за последние десятилетия режим работы библиотеки сильно смягчился, и сегодня любой желающий может прийти и отфотографировать книги на непрофессиональный фотоаппарат.
На радостях я перефотографировал несколько десятков книг, и примерно понял из них, что Аскота может, и немедленно купил её. А сам взялся за детальное штудирование учебников.
Возможности Аскоты 170
Читать эти книги было непросто: мало того, что они описывали программирование машины, разработанной задолго до появления первого языка высокого уровня, Фортрана. Так они ещё и написаны были в бухгалтерских терминах. В общем, представьте, что вам нужно изучать учебник по 1С: Ассемблер, написанный на старославянском, и вы примерно поймёте масштаб стоявшей передо мной проблемы. Достаточно сказать, что факт наличия условного оператора я смог установить только потому, что специально его искал. А разобраться, как он работает, удалось только после изучения конструкции соответствующих узлов и ряда экспериментов.
Да! У Аскоты есть условные операторы! И команды перехода! И вообще всё, что нужно для работы полноценного компьютера общего назначения!
Давайте я расскажу об этом чуть подробнее. Пользоваться буду придуманным мною С-подобным псевдокодом — совершенно не похожим на тот, что использовался в старых учебниках, но куда более понятным сегодня.
Итак, при работе каретка машины движется справа налево относительно тела машины — как на обычной печатной машинке. При этом печатающий узел скользит вдоль бумаги, а узел чтения программной команды — относительно программной доски с дырочками, в которые вставлены шпиньки-стопсы — примерно как в барабане музыкальной шкатулки. В какой-то момент узел чтения находит колонный стопс, означающий «здесь надо остановиться и что-то сделать», останавливается и делает то, что требуют остальные стопсы этой колонки и нажатые оператором клавиши. А если узел чтения не находит стопс остановки до конца каретки, то каретка автоматически перемещается до упора вправо, и сканирование начинается заново — такой вот основной цикл, в котором бесконечно крутится программа.
Один столбец стопсов. Вертикальный сверху — колонный, остальные — программные.
На практике, таких остановок «надо что-то сделать», то есть шагов программы, может быть около 50 на одну доску. За один шаг может выполняться несколько команд. Например, вот такой код занимает всего один шаг:
if (I>0)
{
print(K);
I+=K;
II-=K;
III-=K;
goto program2; // После выполнения этого шага начнёт выполняться шаг программы, обозначенный как принадлежащий второй программе.
if (K!=0) goto forward; // Если K!=0 , то следующим выполняться шаг, находящийся после метки forward; иначе - ближайший шаг второй программы.
K=0;
}
Как видите, машина активно обращается к регистрам; регистры — это ряды шестерёнок в теле машины, так что память программы (доска со стопсами) отделена от памяти данных (рядов шестерёнок регистров). Итого, у машины ~50 шагов командной памяти (~0,3 кБ) и 56 целочисленных регистров по 12 десятичных разрядов (ещё 0.3 кБ).
За один шаг машина может прочитать один регистр, распечатать его содержимое, прибавить или отнять его к нескольким другим регистрам и обнулить. Одновременно выполняется пара вариантов сравнения с нулём и несколько вариантов команд перехода.
Увы, несмотря на сложность и богатство машинного кода, у него есть несколько неприятных ограничений:
1. У машины нет команд для умножения и деления.
Как ни странно, это не бага, это фича. Команда умножения есть у большинства более простых бухгалтерских машин но разработчики Аскоты уже в начале 1950-х понимали, что дни механики сочтены, и вместо медленного и неуклюжего механического умножителя предусмотрели возможность подключения внешнего математического модуля. И действительно, упоминания внешнего механического умножителя для Аскоты встречается в старых учебниках, но ни одной его реальной фотографии (или, тем более, реального экземпляра) не сохранилось. Уже в конце 1950-х вместо него стали использоваться ламповые модули R12 (монстры размером с небольшой шкаф, весом в 200 килограммов и потреблением в полтора киловатта), с 1962 выпускались небольшие (с маленький системный блок) транзисторные умножители, а к моменту схода с конвейера моего экземпляра Аскоты стали использоваться крошечные умножители-делители на микросхемах. В общем, хорошо, что мне достался экземпляр без умножения-деления: обидно было бы получить вместо полностью механического компьютера банальный электронный калькулятор на микросхемах.
2. Машина не умеет смещать число на разряд вправо или влево.
Вообще, организовать умножение, деление и другую хитрую математику можно и программно; собственно, многие современные RISC процессоры так и делают. Могли бы так делать и Аскоты — но увы, для этого нужно сдвигать число на разряд, а такая команда не предусмотрена. Это, на самом деле, очень странно, потому что умножающий модуль, особенно в первое время, был дорогим и редким, а всё, необходимое для организации сдвига, у машины есть. Я даже хочу добавить в свою Аскоту команду сдвига — но об этом в конце статьи.
3. Количество меток перехода ограничено.
В программе могут быть метки backward1, backward2, forward1 и forward2, причём каждая — по несколько раз, шаги программы могут быть помечены как выполняемые по программе 1, по программе 2 и по обеим. Звучит неплохо, но, при программировании достаточно сложного алгоритма хватает этого в обрез.
4. Нет поддержки массивов и непрямой адресации.
У Аскоты много регистров. Только вдумайтесь: есть 50 шагов программы, за каждый шаг можно прочитать (и распечатать) только один регистр — и регистров, при этом, 56! То есть, напрямую адресовать все регистры в автоматическом режиме, без выбора номера регистра с клавиатуры, невозможно в принципе.
Обычно это проблема решается с помощью разных вариантов непрямой адресации — например, сначала вы задаёте номер регистра (ptr=&R00), а затем, в цикле, раз за разом увеличиваете его (ptr++). И, в принципе, у Аскоты для этого всё есть: пятьдесят регистров организованы в виде пяти вращающихся барабанов по десять регистров, и для инкрементной адресации достаточно было добавить команду поворота барабана на один шаг вперёд. Для обсчёта стопок многострочных документов это была бы очень востребованная функция. Но не сделали, почему-то. Единственное объяснение, которое мне пришло в голову — возможно, к началу 1950-х ещё не изобрели инкрементную адресацию. Если кто-нибудь вспомнит более ранний немецкий компьютер с инкрементной адресацией — подскажите, пожалуйста.
Живая Аскота!
Пока я разбирался с особенностями программирования, компьютер пришёл ко мне из Омска. После неизбежной перебранки с грузчиками я поставил его посреди комнаты, обстучал молотком ржавчину, полил механизм смесью масла и керосина и попробовал включить.
Аскота (справа) была очень большая
И очень грязная
Ожидаемо он не заработал. Я залез внутрь, выгреб горсть мусора, покачал клинящий штифт, машина в первый раз запустилась, сделала пару ходов и заклинила.
Следующий месяц прошёл в ремонте. Я завесил половину интернета объявлениями о поиске опытного наладчика, нашёл нескольких человек, которые сорок лет назад немного имели с Аскотами дело, но, увы, чинить пришлось самому. Я напечатал на 3D принтере ручку для ручного вращения управляющего вала, научился отключать залипшие счётчики, выгреб из механизма ещё несколько кусков особо злостной еловой коры, нашёл и разработал полдюжины залипших соединений, починил полдюжины отказавших пружин.
Откуда тут кора?!
Я прогуливал работу неделями, с утра до вечера рылся в руководстве по наладке, ища причину очередной неисправности, ловил на лестничной площадке спамеров, за сто рублей помогавших мне временно перевернуть машину кверху ногами… И, наконец, дошёл до момента, когда на машине удалось запустить «Hello world!».
Переключаем Аскоту в режим печатной машинки, и набираем «НЕЛЛО ШОРЛД».
Хм, нет, это слишком просто. Давайте лучше бессмертных кроликов считать.
Предположим, месяц назад у нас не было кроликов, а сегодня мы купили на рынке пару крольчат.
Через месяц они вырастают в пару взрослых кроликов.
Ещё через месяц пара даёт потомство, и мы получаем пару взрослых кроликов и пару крольчат. Итого, две пары.
Ещё через месяц пара взрослых опять даёт потомство, а крольчата прошлой пары подрастают и становятся взрослыми. Итого, три пары, две взрослых и одна дети.
Ещё через месяц две пары взрослых дают две пары потомства, а крольчата прошлого месяца вырастают. Итого, у нас пять пар, все кролики, бывшие у нас месяц назад, взрослые, а все, которые были взрослыми, дали по паре потомства.
И так далее.
Эту задачку в начале XIII века исследовал итальянский математик Фибоначчи, в честь которого получившийся ряд чисел назван рядом Фибоначчи:
0 1 1 1+1=2 1+2=3 2+3=5 3+5=8 5+8=13 8+13=21…
Давайте рассчитаем его на Аскоте. Это просто. Пусть в регистре I у нас находится нечётный член ряда (для начала, первый), в регистре II — чётный (для начала, нулевой). Тогда программа будет выглядеть так:
1: read I; II+=; prn(); //Программа №1: Читаем регистр I, прибавляем его значение к регистру II и распечатываем.
1: read II; I+=; prn(); newline; goto backward1; // Программа №1: Читаем регистр II, прибавляем его значение к регистру I и распечатываем. Затем сдвигаем бумагу на следующую строку и возвращаемся к началу программы.
Программу надо вручную ассемблировать, а потом вбить стопсы в нужные дырочки программной доски. Получится вот что:
1 0/0 12/4 14/3 1 12/3 14/4 17/4 33/4
Здесь единичка в начале означает колонный стопс типа 1, заставляющий машину остановиться для выполнения этих шагов по программе №1, 0/0 (установленный в ряд №0 стопс типа 0) заставляет машину искать колонные стопсы начиная с этой точки. Стопс в ряду 12 управляют регистром II: 12/3 вызывает чтение регистра, а 12/4 — добавление к нему прочитанного числа. Аналогично, 14/3 вызывает чтение регистра I, а 14/4 — добавление к нему прочитанного. Печать прочитанного числа происходит автоматически, если только не установлен специальный стопс 5/3, отключающий печать. 17/4 проворачивает валик каретки на один шаг, а 33/4 запускает возврат каретки (этот стопс можно не ставить, тогда каретка проскользнёт до конца, и возврат каретки запустится автоматически).
По хорошему, в обе строки надо добавить ещё 15/6R, тогда сразу после окончания шага машина начинает выполнять следующий. Но в первый раз я этого не сделал, и мне пришлось постоянно жать клавишу запуска.
Как ни странно, программа запустилась с первого раза. Я был в восторге!
Но для запуска этой программы нужно сначала записать данные в нужные регистры, а потом результаты выводятся двумя неаккуратными колонками. Давайте лучше сделаем программу, которая будет запрашивать исходные данные, а потом выводить члены ряда и их номера.
Получится вот так:
1: read I; clr; //для начала очищаем все переменные
1: read II; clr;
1: read III; clr;
1: read IV; clr;
1: read K; clr;//останавливаемся и даём пользователю ввести единицу.
1: stop; read kbd; IV+=; K+=; prn(); clr;//записываем единицу в IV (она нужна будет для инкремента) и в K (начнём с первого члена ряда). Заодно печатаем её - она будет запросом для ввода первого члена ряда.
1: stop; read kbd; I+=; prn(); clr; // записали первый член ряда в I.
1: read IV; K+=;
1: read K; prn();// печатаем запрос на ввод второго члена ряда.
1: stop; read kbd; II+=; I+=; prn(); clr; // записали второй член ряда в II. Заодно в I теперь I+II, то есть третий член ряда.
backward1: 12: read IV, K+=; // увеличиваем на единицу номер текущего члена ряда - для начала, он станет равен 3. Обратите внимание, этот шаг выполняется и по программе №1, и по программе №2.
2: read II; I+=; III+=; goto prog1; goto forward; //на первом цикле этот шаг обходим, потому что у него метка программы №2, а не №1. А на втором цикле из II читается четвёртый член, который записывается в пустой III и прибавляется к I (в I был третий, стал пятый). После этого мы снова переходим к программе №1 и прыгаем вперёд, к этапу печати.
1: read I; II+=; III+=; goto prog2; // На первом цикле в I третий член, в II - второй. В сумме, соответственно, получается четвёртый (он оказывается в II). В III записываем текущий I - третий. И переходим к программе №2.
forward1: 12: read III; prn(); clr; // печатаем текущий член ряда - для начала I+II, а там видно будет. Обнуляем III. Обратите внимание, этот и следующий шаги выполняются и по программе №1, и по программе №2.
12: read k; prn(); newline; goto backward1; // Печатаем номер шага, переводим строку, возвращаемся назад.
Я запустил программу, и машину благополучно заклинило, потому что я до этого не пользовался регистром К, а в нём был очередной кусок коры. Нде. Кусок коры убрал, счётчик отключил, машину расклинил. И заработало же!
Я решил, что машина уже окончательно работает, нужно только набить какой-нибудь красивый алгоритм, чтобы похвастаться на Хабре, и можно писать отчёт. Например, Стэнли Френкель, если верить мистеру Фейнману, учил табулятор считать арктангенс. Чем я хуже? То есть понятно, чем хуже, но вдруг?
Увы, подходящих алгоритмов я не нашёл. На Stackowerflow предложили вариант расчёта логарифма, слишком сложный для Аскоты, в Библиотеке имени Ленина нашлись нашлась пара брошюрок с несколькими интересными алгоритмами — но они тоже оказались нам с Аскотой не по зубам. В конце концов, я плюнул на тригонометрию и логарифмы, и решил считать банальный квадратный корень.
Вообще, квадратный корень считается легко. Из формулы суммы членов арифметической прогрессии получается, что сумма первых n нечётных чисел = n^2. Из чего следует
Откуда сравнительно легко выводится алгоритм вычисления квадратного корня, использовавшийся с начала 1950-х годов арифмометрами с автоматическим вычислением квадратных корней фирмы Friden — вот тут немного подробнее. Правда, этот алгоритм тоже требует сдвигов на разряд, но только на разряд вверх (сдвиг на разряд вверх, то есть умножение на десять, можно организовать как ряд сложений, а сдвиг на разряд вниз с помощью возможностей Аскоты практически нереализуем).
Первый вариант программы я написал за вечер. Но из-за количества операций, требуемых при сдвиге вверх, в 50 шагов он не укладывался. Пришлось оптимизировать, вычисляя два разных сдвига одними командами, потом исправлять ошибки, потом отлаживать программу на листе бумаги, опять исправлять ошибки, опять отлаживать… Где-то через неделю дело дошло до ассемблирования, набивки в программную доску и пробного запуска. Не заработало. Перепроверил, нашёл несколько мелких ошибок. Исправил. Не заработало. Перечитал книжки, выяснил, что неправильно понял одну тонкость программирования. Исправил, не заработало. Перепроверил, нашёл ещё ошибку. Не заработало. Перепроверил, понял, что не исправил мелкую неисправность в машине. Исправил. Не заработало… В общем, ещё неделю я налаживал программу в железе. И отладил, наконец!
Надеюсь, после этого серьёзных непонятых тонкостей программирования и неполадок машины не осталось, потому что программа вычисления квадратного корня использует, кажется, все вычислительные и логические функции, которые есть в машине. Разве что регистры не все, но регистры я отдельно проверял.
Что в результате и что дальше?
В результате, во-первых, у меня есть рабочая Аскота-170.
Во-вторых, надеюсь, этой статьёй я сделал её более известной в рунете. Аскоты использовались у нас, минимум, до конца 80-х годов, и их малую известность я могу объяснить только тем, что мало кто понимал, насколько они круты, а владельцы складов не понимали, насколько ценные вещи им попадались. Возможно, статья исправит эту ситуацию.
В-третьих, я создал на своём сайте раздел про Аскоту, в котором выложил руководство по программированию, некоторые советы по наладке механизма, книги, модели для 3D печати, коды программ и так далее. Надеюсь, это тоже послужит популяризации Аскоты, и поможет наладчикам, которым удастся достать её после меня.
В-четвёртых, в ходе работы я старался учитывать потраченное Аскоту время в табличке. Так вот, получилось, что я потратил под две сотни рабочих часов без учёта прокрастинации. Наверное, можно в резюме вписать, да?
С другой стороны, осталась пара нерешённых задач.
Во-первых, я так и не сделал программы для расчета чего-нибудь крутого, вроде тригонометрических функций, рациональных степеней или логарифмов. Как я уже сказал, придумать или найти подходящий алгоритм сам я не смог, но если кто-нибудь из читателей статьи предложит мне (в комментариях, в личке или на почту me[а]alple.net) какой-нибудь подходящий алгоритм (в псевдокоде вроде того, что есть для квадратного корня), я обязательно постараюсь реализовать его в железе, и снять про это видео.
Во-вторых, как я уже говорил, Аскоте очень не хватает команды сдвига на разряд. При этом, у меня такое впечатление, что её собирались сделать, но так и не сделали — никак иначе я не могу объяснить существование регистров III/IV.
Регистр клавиатуры, в котором временно хранится введённое на клавиатуре число.
Регистры I и II — универсальные, полностью независимые, управляемые автоматически и с клавиатуры, поддерживающие отрицательные числа и логические операции.
Регистр K — контрольный, для контроля безошибочности работы оператора и машины. По функциям похож на I и II, плюс имеет защиту от случайного сброса. С клавиатуры не управляется, но для контрольного регистра это скорее достоинство.
Накапливающие регистры — те самые, в барабанах по 10 штук. Не работают с отрицательными числами и меньше используются в логических операциях, имеют некоторые технические ограничения взаимного подключения, нормально управляются с клавиатуры и автоматически.
И те самые загадочные регистры III/IV — по возможностям они близки к накапливающим, но с клавиатуры управляются очень-очень плохо.
При этом, регистры III/IV имеют свой собственный механизм подключения, независимый от накапливающих регистров. Собственно, эта система осталась от одной из более примитивных моделей Аскоты, у которой кроме регистров I, II, III, IV и K ничего не было — но зачем их оставили?
Я думаю, что они предполагались именно как регистры сдвига на разряд. Потому что достаточно сцепить их рядом диагональных шестерёнок, и мы получим устройство сдвига: записываем, скажем, в III число, и получаем в IV его же, но сдвинутым на разряд влево.
Я представляю себе это как-то так:
Вот тут вы можете покрутить 3D модель.
А вот тут — посмотреть, как счётчики выглядят вживую.
На картинке — жёлтые шестерёнки — счётчики III и IV (например к красным подключены первый разряд III-1 и второй разряд IV-2). Полупрозрачный клин — детали устройства счётчика. Красные шестерёнки — та самая отсутствующая система сдвига, поворачивающая шестерёнку III-1 при повороте IV-2, и наоборот. Если собрать такую систему, то для сдвига на разряд влево достаточно будет записать нужное число в IV и прочитать его из III.
Сложность в том, что при работе шестерёнки счётчиков не только вращаются, но и движутся вверх-вниз (примерно на свой радиус), и механизм должен это движение поддерживать. Получается достаточно сложная конструкция, придумать которую я не могу.
Было бы здорово, если бы кто-нибудь, умеющий проектировать лучше меня, за разумные средства взялся бы придумать мне такую систему для печати на принтере, или изготовил бы её в железе — с уточнениями и предложениями, опять же, пишите в комментарии, в личку или на почту me[а]alple.net.
Больше подробностей вы можете найти у меня на сайте, в разделе про Аскоту.
Автор: APLe
Набрела на Вашу статью. Почитала с интересом. Я работала в 80-е в МСБ на мол комбинате. В отделе было две Аскоты.На них работали опытные операторы, печатали зарплату и расчёты с покупателями/поставщиками.Машины часто ломались, помню, были два наладчика-слесаря🙂, которые приезжали по вызову. Одна из Аскот, несмотря на введённое название “батайский ОРС СКЖД” Упорно печатала “батайские суки”.А мебельную фабрику вообще переименовала🤭. Мы, молодые, дурачьё, ржали, а начальница психовала, вызывала наладчиков. Посмотрела фото агрегата, вспомнила юность, 1981 й год..