Ко всем страницам про Аскоту
К главной странице про механику (alple.net/arif-ru)
К главной странице моего сайта (alple.net)

Аскота 170: описание возможностей и языка программирования


Аскота 170 - программируемая механическая вычислительная машина с электроприводом, полная по Тьюрингу. Она построена на гарвардской архитектуре: имеет независимую память программы и память данных.
К сожалению, Аскота не способна автоматически производить операции умножения и деления, не имеет команд для поразрядного сдвига чисел и работы с массивами данных.


Аскота предназначена, в основном, для автоматизации бухгалтерских операций, к тому же её язык программирования разработан в начале 50-х годов, и непривычен для нас.
Ниже я постараюсь сделать пересказ инструкции по управлению Аскотой в более привычных для современного ассемблера терминах. Инструкция не претендует на полноту, многие детали, важные, в первую очередь, для обработки бухгалтерских таблиц, опущены. Для более подробного знакомства с темой рекомендую прочитать замечательную книгу Т.А.Поляковой и В.И.Тихомирова "Эксплуатация бухгалтерских машин Аскота класса 170" 1974 года издания.
Вопросы ассемблирования в данном руководстве также не поднимаются.


Память программы технически представляет собой программную доску со 159 колонками отверстий, по 46 отверстий в колонке. В отверстия устанавливаются металлические пластинки (стопсы) разных форм, установка считывается рядом щупов.
На практике программа может содержать примерно 50 шагов.
Шаги выполняются последовательно, у каждого шага есть признак номера программы (№1, №2 или одновременно №1+№2). В каждый момент времени машина настроена на выполнение программы 1 или программы 2, и шаги, не промаркированные как принадлежащие этой программе, игнорируются.
За каждый шаг выполняется одна или несколько команд, по одной команде каждого типа (см. список ниже).
После выполнения шага машина может остановиться, перейти к следующему шагу или перейти в указанную точку программы.
После выполнения программы машина автоматически возвращается к назад (к началу программы, если не выбрана иная точка), выполняя бесконечный цикл.


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


Память данных представляет собой 56 регистров по 12 десятичных знаков, поддержка нецелых чисел минимальна, отрицательные числа хранятся в виде дополнения (но изменение знака происходит только при переходе через ноль, не после переполнения).
У машины есть клавиатурный регистр, регистры I, II, K, работающие независимо, пара регистров III и IV и несколько (в моём случае 5) банков по 10 "накапливающих" регистров. Номер накапливающего регистра состоит из двух цифр - номера банка и номера регистра в банке, например, "00" или "38".
Клавиатурный регистр работает только на чтение (число вводится в него с клавиатуры), остальные регистры не могут быть записаны оператором напрямую, но работают на чтение и запись.
[1] В одной команде нельзя использовать одновременно III и IV регистры. В одной команде нельзя использовать одновременно два "накапливающих" регистра с разными номерами в банке (17 и 37 - можно, 17 и 18 нельзя, 17 и 38 нельзя), нельзя складывать и вычитать из "накапливающих" регистров одной командой (17+=, 37+= можно, 17-=, 37-= можно, 17+=, 37-= нельзя).


Одновременно с любой операцией чтения чисел может быть напечатано прочтённое число и код операции, формат печати гибко настраивается. Отрицательные числа из регистров I, II, K печатаются в естественной форме, со знаком минус, из остальных -- в виде дополнения.


Регистр K, формально, весьма удобен и функционален, но для его программирования используются стопсы 9 и 10, которых в комплекте машины обычно очень мало, поэтому лучше использовать его пореже.

 





За один шаг машина может выполнить следующий набор команд:

команды одного шага записываются в одну строку


0. Метка номера программы:


1: // шаг выполняется только по программе 1
2: // шаг выполняется только по программе 2
12: // шаг выполняется как по программе 1, так и по программе 2
Шаги, не имеющие метки номера программы, не выполняются вообще.


А. Вычислительные


1. Прочитать любой регистр:
read I;
read 48;
read kbd;


2. Прибавить и отнять прочитанное число к другим регистрам, в любой комбинации, кроме запрещённых (см. [1]):
I+=;
K-=;
II+=; III-=; 03+=; 23+=; 33+=; 43+=;
Нельзя: kbd+=;


3. Распечатать прочитанное число:
prn([options]);
По умолчанию - распечатать прочитанное число как целое, чёрным цветом, если оно положительное, и красным, если отрицательное.
Опции - см. раздел Г., "печать".


4. Обнулить прочитанный ранее регистр
clr;


Б. команды перехода:


Команда goto prog1/prog2 может быть скомбинирована с командой goto forward/backward. В таком случае сначала произойдёт переход на нужный номер программы, а затем - переход к метке. Команды goto forward и goto backward в одном шаге встречаться не могут.


5. Перейти к другой программе (от I к II или от II к I)
goto prog1;
goto prog2;


6. Перейти по программе вперёд, до метки:
goto forward;
...
forward1:
forward2:
Примечания:
Переход осуществляется до метки, соответствующей номеру исполняемой в данный момент программы (forward1 при выполнении программы 1, forward2 при выполнении программы 2).
В программе может быть любое количество меток forward1 и forward2, переход будет выполнен до ближайшей.
Технически, метка перехода находится на шаг раньше, чем указанно в программе. Поэтому, если метка перехода находится на шаге, сразу же следующем после команды Goto forward, метка игнорируется. Это бывает важно при создании условных конструкций:
goto forward:
if (I>0) goto forward;
forward1:
...
forward1:

В этом примере первая метка forward приводит к тому, что следующий оператор после условного перехода выполняется всегда, а переход ко второй метке выполняется по логическому условию.
Если нужная метка не будет найдена до конца программы, программа (как обычно) перейдёт к началу, и будет искать метку сначала, например:
forward1: 1: read I; prn();
1: goto forward1;
После выполнения команды "goto forward1;" программа долистается до конца, перейдёт к началу, долистается до метки "forward1:" и выполнит печать содержимого регистра I;
Внимание! Если между командой goto forward и соответствующей меткой forward находится команда if (><), эта команда if сработает независимо от номера программы! Ограничение не относится к случаю, когда if находится на участке, проходимом кареткой обратным ходом, а также к переходу по второй программе на машинах с установленной блокировкой if по второй программе.
При отсутствии соответствующей метки в программе машина зависает, бесконечно сканируя программу в поисках метки.


7. Перейти по программе назад, до метки:
backward1:
...
goto backward1;
или
backward2:
...
goto backward2;
Примечания:
При переходе назад номер метки не связан с номером программы, и определяется меткой в команде перехода.
Количество меток в программе может быть любым, переход осуществляется до ближайшей.
При отсутствии метки до начала программы, машина останавливается на первом шаге, и начинает выполнять программу с него.
Технически, автоматический возврат каретки после прохождения последнего шага программы является командой goto backward1 - поэтому, если нужно, чтобы после последнего шага программа переходила к первому, использовать метку backward1 нельзя! В смысле, можно, но только на первом шагу, а там каретка и так остановится, без метки.


В. Логические команды:


8. Выполнить по знаку:
goto forward;
if ({условие}) {набор команд шага по условию};
{следующие шаги по условию};
forward: {начиная с этой точки шаги выполняются всегда}
Примечания:
Для работы условного оператора необходима предварительная команда "goto forward;" - без неё команда "if(условие)" игнорируется, и набор команд по условию выполняется всегда.
Возможные условия:
I>0; II>0; (I>0)||(II>0)
I<0; II<0; (I<0)||(II<0)
При этом знак (> или <) определяется настройкой машины и не может быть изменён в ходе работы (то есть, в одном листинге программы не может быть условий по > и по < одновременно).
Если регистр равен нулю, то его знак остаётся тем же, каким он был при последнем ненулевом значении:
//предположим, что II==1
read I; clr; //Если до начала программы I>0, после этой операции I==+0. Если до начала программы I<0, после этой операции I==-0.
read II; I+=; // А дальше всё однозначно, I==+1
goto forward;
if (I>0) read I; prn(); // I==+1, I>0 выполняется.
forward: read II; I-=; // I==+0, I>0
if (I>0) read I; prn(); // I==+0, I>0 выполняется.
forward: read II; I-=; // I==-1, I<0
goto forward;
if (I>0) read I; prn(); // I==-1, не выполняется
forward: read II; I+=; // I==-0, I<0
goto forward;
if (I>0) read I; prn(); // I==-0, не выполняется
forward: read II; I+=; // I==+1, I>0
goto forward;
if (I>0) read I; prn(); // I==1, выполняется


9. Выполнить переход, вперёд/назад если не 0
{весь шаг, кроме переходов forward/backward} if(!=0) goto forward/backward1/backward2;
0 должно быть равно то число, которое было прочитано в данном шаге.
read k; if(!=0) goto forward;


1: goto forward;
1: if(I>0) read k; II+=; clr; prn(); goto prog2; if(!=0) goto backward1;
12: forward1: forward2: read 00; prn();
В данном случае, если I<0, то шаг с условием пропускается, и сразу распечатывается значение регистра 00.
Если I>0, то считывается k, его значение прибавляется к регистру II и распечатывается, затем k обнуляется, машина переключается на работу по второй программе. В случае, если считанный k был равен нулю и до обнуления, выполняется goto backward1; иначе машина переходит к следующему шагу, и распечатывается значение регистра 00.


Г. Печать.


10. Новая строка
newline;
Переход печати на новую строку; в остальном, на выполнение программы не влияет. Выполняется после печати всех символов, заданных в данном шаге.


11. Форматированная печать прочитанного числа
prn(options);
options:
frac - поставить точку после второго разряда справа (печатать число как дробное)
red - распечатать красным цветом
symbol - распечатать дополнительно специальный знак операции
no0 - не распечатывать 2, 3 и 4 разряды, если в них нули (3 разряд не печатается в случае, если ноль в 3 и 4 разряде одновременно)


12. Печать текста
text;
После выполнения этой команды оператор может напечатать текстовую запись. Текст вводится на клавиатуре пишущей машинки, доступны заглавные кириллические буквы, цифры, знаки '.', ',', '-', '/', '%', а также пробел.
После окончания печати текста для продолжения исполнения программы следует перевести вверх рычаг "B/S" и нажать на среднюю пусковую клавишу.


13. Печать двухзначного кода
code;
Распечатать код из двух цифр. Код устанавливается на дополнительной клавиатуре, печатается слева от зоны печати прочитанного числа.


14. Печать даты
date;
Распечатать дату. Дата устанавливается на специальном валике, распечатывается слева от двухзначного кода.



Д. Прочие команды


15. Остановка машины
stop;
В начале шага автоматическое выполнение программы останавливается, машина ждёт любых действий оператора. Наиболее очевидное действие - набрать на клавиатуре число и нажать на среднюю пусковую клавишу для дальнейшего выполнения программы.


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



Е. Рычаги управления.


У машины есть 9 двухпозиционных рычагов на панели управления. По умолчанию (в случае автоматического выполнения программы №1) все они должны быть в "верхнем положении".
Рычаг B/S переключается машину из режима вычисления (верхний, B) в режим печати текста пишущей машинкой (нижний, S).
Безымянный рычаг в правом верхнем углу панели управления переключает машину с программы №1 (верхнее положение) на программу №2 (нижнее положение).
Любой из остальных вертикально переключаемых рычагов при переводе вниз отключает автоматическое выполнение программы, что полезно для пошаговой отладки. Под каждым рычагом подписаны номера регистров, которые данным рычагом отключаются от доски автоматического управления, и становятся доступны для управления с клавиатуры на чтение и запись. Это также полезно для отладки, а также для ввода исходных данных в программу, если программа не предусматривает раздел ввода исходных данных.
Рычаг в левой нижней части каретки разблокирует программную доску, так что её можно снять. Остальные узлы управления каретки похожи на узлы управления обычной печатной машинкой, подробнее про них можно прочитать на стр. 15-17 книги Поляковой.
Сняв программную доску, можно получить доступ к рычагу управления условным оператором ("рычагу переключения печати сальдо") - он находится примерно в сантиметре справа от правых концов щупов, читающих программную доску. При положении рычага "на оператора" условные операторы будут работать по условию "if (... >0)", при положении "от оператора" - "if (... <0)".


Ё. Клавиши, используемые в машине в неавтоматическом режиме:


- две колонки цифр слева задают код, печатаемый командой "code".
- клавиатура пишущей машинки печатает текст; клавиша '→' на ней позволяет сдвинуть каретку на один шаг назад, как backspace.
- Клавиатура цифр от 0 до 9 + клавиши '00' и '000' служат для ввода чисел в клавиатурный регистр. Я не знаю, что будет, если ввести их во время автоматической работы машины, и не собираюсь проверять.
- клавиши 'C' и 'R' слева от этой клавиатуры управляют введённым числом: клавиша 'C' гасит, клавиша 'R' осуществляет чтение числа без гашения клавиатурного регистра (при других способах чтения в неавтоматическом режиме он гасится) (прочитанное число обычно печатается и/или попадает в другие регистры, согласно настройкам программы/набранным клавишам).
- рядом с клавишами 'C' и 'R' находятся клавиши '◊' и '='. Они позволяют, соответственно, прочитать или прочитать и очистить регистры III/IV - но только при условии, что в данный момент программа стоит на шаге, в котором заданно подключение одного из этих регистров.
- три вертикальных ряда цифр в правой части панели управления задают номер накапливающего регистра (левая колонка - номер барабана, две правые - номер регистра в барабане).
- Под ними находится три клавиши управления накапливающими регистрами - '◊', '=' и '-'. При их нажатии число в выбранном барабане читается, читается и гасится, или от него отнимается прочитываемое (в неавтоматическом режиме - обычно из клавиатурного регистра) число. Если ни одна из клавиш не нажата, то при следующем запуске машины прочитываемое число будет прибавлено к выбранному накапливающему регистру.
- Ещё ниже находятся три клавиши, 'T', '→|' и '→'. 'T' переносит каретку вправо (вперёд по программе), стрелки - влево (назад). '→|' переносит каретку до упора, остальные две двигают каретку только пока оператор не отпустит клавишу.
- Над тремя большими белыми клавишами находится два ряда клавиш - "I - ◊ *" и "II - ◊ *". Они выполняют прибавление, вычитание, чтение и чтение с одновременным сбросом регистров I и II соответственно.


Запускать машину рекомендуется средней из трёх больших белых клавиш. Многие из перечисленных выше клавиш (любое чтение регистров, сброс и чтение без повторения клавиатуры, вычитание из накапливающих регистров) также запускают машину на выполнение выбранного клавишей действия, остальные клавиши только выбирают список действий, которые будут выполнены при пуске машины.



Ж. Примеры программ:



Простейший расчёт ряда Фибоначчи:

1: read I; II+=; prn();
1: read II; I+=; prn(); newline; goto backward1;
Перед запуском программы следует ввести в регистры 1 и 2 единицу: перевести рычаги I и II вниз, прочитать с гашением регистры I и II (клавиши '*' над тремя белыми клавишами, нажать обе по очереди), нажать 'C', затем нажать цифру '1' на клавиатуре цифр, нажать клавиши 'I' и 'II' одновременно, нажать на центральную белую клавишу. Затем запустить автоматическое выполнение программы - перевести все рычаги вверх и ещё раз нажать центральную клавишу.
Код установки стопсов:

1 0/0 5/4 12/4 14/3 15/6R
1     5/4 12/3 14/4 15/6R 17/4 33/4 



Расчёт ряда Фибоначчи с автоматическим запросом первого и второго члена ряда, в один столбик, с указанием номера члена ряда.
В I и II - члены ряда, в III - печатаемый член ряда, в IV - единица, в K - номер члена ряда. 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; // Печатаем номер шага, переводим строку, возвращаемся назад.


Перед началом выполнения программы надо выставить каретку в крайнее правое положение.


Код установки стопсов:

01 1 0/0(+2)        5/3     10/4      14/3 15/6R                                  // 1: read I; clr; 
02 1                5/3     10/4 12/3      15/6R                                  // 1: read II; clr;
03 1                5/3     10/4           15/6R                     41/3         // 1: read III; clr;
04 1                5/3     10/4           15/6R                35/3 41/3         // 1: read IV; clr;
05 1         3/10   5/3                    15/6R                                  // 1: read K; clr;
06 1         3/4        8/3                                     35/3 41/4         // 1: stop; read kbd; IV+=; K+=; prn(); clr; 
07 1                    8/3           14/4                                        // 1: stop; read kbd; I+=; prn(); clr; 
08 1         3/4    5/3                    15/6R                35/3 41/3         // 1: read IV; K+=;
09 1         3/9    5/4                    15/6R                                  // 1: read K; prn();
10 1                    8/3      12/4 14/4                                        // 1: stop; read kbd; II+=; I+=; prn(); clr; 
11 2 0/0(+2) 3/4    5/3                    15/6R                35/3 41/4 43/7R(-2)//backward1: 12: read IV, K+=; 
12(1)               5/3          12/3 14/4 15/6R 16/3 19/3           41/4         // 2: read II; I+=; III+=; goto prog1; goto forward; 
13 1                5/3          12/4 14/3 15/6R      19/4 20/5      41/4         // 1: read I; II+=; III+=; goto prog2; 
14 2                5/4 8/3 10/4           15/6R                     41/3         // forward1: 12: read III; prn(); clr;
15 2         3/9    5/4 8/3               15/6R   17/4       33/4                // 12: read k; prn(); newline; goto backward1; 

З. Пример программы: вычисление квадратного корня


Ко всем страницам про Аскоту
К главной странице про механику (alple.net/arif-ru)
К главной странице моего сайта (alple.net)


:

3

.