Pull to refresh

Моя первая оверлейная программа

Reading time 6 min
Views 21K
Во времена повсеместного господства планшетов и смартфонов, сложно поверить, что совсем недавно, можно было получить немалое удовольствие, играя с калькулятором. Разумеется, я имею в виду не обычный (или даже инженерный) калькулятор, а программируемый. Я хочу рассказать об устройстве, которое буквально перевернуло мою жизнь и фактически подтолкнуло меня к тому, чтобы стать программистом.

В конце 70-ых годов прошлого века, с вычислительной техникой в СССР было туго. Мой отец работал программистом. В моей памяти навсегда останутся и походы на его работу в ЦУМ, где я играл в Ним с вычислительной машиной Минск и продолжительные набивания программ латунными кольцами на планшетах для Искр, использовавшихся в то время Сбербанком. Иметь, в своем личном распоряжении, что-то, способное выполнять программы, я, конечно, не мог даже мечтать.

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

Приложив немалые усилия, я убедил родителей приобрести совсем не дешевый по тем временам МК-61 и, на сдачу, подшивку старых журналов Квант, после чего, забаррикадировавшись на квартире у наших родственников, перестал реагировать на внешние раздражители, вплоть до самого отъезда.

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

Когда я уже довольно хорошо освоился с МК-61, одному моему другу приобрели улучшенную версию — МК-52. По системе команд она практически не отличалась от МК-61, но имела возможность сохранять программы в ППЗУ и поставлялась с несколькими картриджами с уже готовыми программами. Разумеется, я сразу захотел написать что-то специально для нее.

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

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

Выбор пал на «Морской бой». Я уже давно хотел его сделать, но никак не мог втиснуть весь требуемый функционал в куцее адресное пространство калькулятора. Возможность подгружать программы из ППЗУ позволила разбить программу на две части. Первый блок — расставлял корабли, второй (собственно игровой) обеспечивал диалог с пользователем. Часть подпрограмм использовалась обоими блоками.

Вот как выглядело это чудо (картинка не для слабонервных)
image

Поскольку в таком виде эта наскальная живопись расшифровке практически не поддается, приведу этот код в более читаемом виде:

Блок расстановки кораблей
00|46 01.07 02.12 03.08 04.11 05.34 06.15 07.45 08|53 09:75 
10.06 11.53 12:67 13.5E 14:08 15.62 16.53 17:37 18.6E 19.01 
20.11 21.4E 22|53 23:34 24.6E 25.02 26.10 27.4E 28.53 29:34
30.5D 31:08 32.63 33.50 34|62 35.02 36.10 37|65 38.12 39.01
40.10 41.DE 42.38 43.BE 44.52 45.11 46.4E 47.53 48:58 
                                                      49.6E
50.02 51.10 52.4E 53.53 54:58 55.5D 56:22 57.27 58|61 59|65
60.12 61.01 62.10 63.DE 64.38 65.BE 66.52 67|65 68.12 69.01
70.10 71.DE 72.37 73.35 74.52 75|66 76.01 77.01 78.12 79.20
80.10 81.35 82.46 83.07 84.12 85.07 86.10 87.4E 88.65 89.04
90.15 91.12 92.45 93.01 94.11 95.59 96:A2 97.65 98.07 99.15
A0.13 A1.45 A2|52
Подгружаемый игровой блок
00.40 01.50 02.57 03:41 04.0B 05.15 06.45 07.14 08.06 09.10
10.4E 11.02 12.53 13:67 14.57 15:22 16.0F 17.DE 18.39 19.BE
20.64 21.52 22|53 23:75 24.01 25.53 26:67 27.5E 28.22 29.15
30.53 31:59 32.6E 33.06 34.11 35.62 36.12 37.65 38.17 39.11
40.52 41|53 42:58 43.6E 44.01 45.11 46.4E 47.53 48:58

Здесь я использую коды команд, поскольку их обозначения, предложенные производителем ПМК, не очень удобны для распечатки «листингов». Соответствие кодов командам можно посмотреть по табличке (любезно нарисованной мной в те же годы):

Коды команд МК-52
image
Краткий экскурс в систему команд
При некоторой практике, коды команд легко запоминались (что было необходимо, так как введенные программы требовалось проверять). Наиболее часто употребляемыми были:

  1. Арифметические команды (10-13), выполняющие соответствующее действие с двумя нижними регистрами оперативного стека (X, Y) и помещавшими результат в X
  2. Команды записи и чтения из регистров памяти (40-4E и 60-6E соответственно)
  3. Команды, управляющие выполнением программы (50-5E), из числа которых следует отметить команду останова (50), а также команды вызова подпрограммы (53) и возврата из нее (52)
  4. Весьма полезными были команды косвенной адресации (B0-BE, D0-DE), позволявшие обращаться к регистру памяти, номер которого был записан в другой регистр

Хотя в кодах команд и использовались шестнадцатеричные цифры, их отображение на экране вызывало в памяти кадры из кинофильма «Хищник»:

image

Для удобства, я выделил в коде адреса команд переходов (двоеточием после адреса команды), а также точки входа (вертикальной чертой). Это позволяет более наглядно увидеть взаимосвязь подпрограмм (особенно интересна команда вызова подпрограммы, расположенная по адресу 22 и обеспечивавшая своего рода «полиморфизм». При обращении к этой подпрограмме, управление передавалось в различные места, в зависимости от того, какой именно блок был загружен).

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

Поскольку работающего калькулятора под рукой уже нет, я использовал эмулятор:

image

Все подготовленные образы были загружены на GitHub.

Первый образ можно использовать для расстановки «кораблей»:

image

Начальное значение для генератора псевдослучайных чисел (0.1234567) может быть изменено. После запуска на выполнение (в/о с/п), программа работает довольно долго, выводя, в конечном итоге, адрес для загрузки игрового модуля из ППЗУ (возможность автоматического обращения к ППЗУ из программы отсутствовала):

image

Так как эмулятор (по крайней мере та версия которую я использовал) полностью проигнорировала мои попытки обращения к ППЗУ, я подготовил образ с вбитым руками игровым модулем и уже выполненной расстановкой кораблей.

Сама расстановка хранится в регистрах памяти с 7-го по D. Вот пример второй строки (8-ой регистр):

image

Двойки кодируют положение «кораблей». На основе приведенного выше начального значения, была сгенерирована следующая расстановка:

0 0 0 0 1 0 1
0 0 1 0 0 0 0
1 0 0 0 0 1 0
0 0 1 0 0 0 0
0 0 0 0 0 0 0
0 1 0 0 0 0 1
0 0 0 1 0 0 0

Можно заметить, что программа разместила девять не соприкасающихся «однопалубных кораблей» на поле размером 7x7 клеток.

Чтобы начать игру, необходимо ввести количество кораблей (9 в/о с/п) после чего, вводить координаты «выстрелов» (например 2 В^ 2 с/п). Так как мы подсмотрели расположение «кораблей», нам не составило никакого труда потопить «однопалубник»:

image

Впрочем, второй раз попасть по нему уже не удастся:

image

Попробуем (2 В^ 2 с/п). Как и ожидалось, калькулятор наносит ответный удар:

image

Первая цифра здесь — координата по вертикали (начиная снизу-вверх), вторая — по горизонтали (слева-направо).

Чуть позже я заметил, что изменив всего пару команд (в самом первом фото с текстом программы, они отмечены красным), можно добиться того, чтобы размещаемые корабли могли соприкасаться, но не углами, а сторонами (образы 0003 и 0004). В результате получалась следующая расстановка «многопалубников»:

0 0 0 0 1 0 0
0 0 0 0 0 0 0
0 0 1 0 0 1 0
0 0 1 0 0 0 0
0 0 0 0 0 0 0
1 1 0 1 0 0 1
0 0 0 1 0 0 0

Не бог весть что, но неплохо, с учетом того, что изменить пришлось всего две команды.

Отдельно следует сказать о багах (даже скорее о Багах), хотя это тема для отдельной статьи, если не их цикла. Дело в том, что калькуляторы описываемой серии были буквально напичканы различными примерами ошибок и недокументированного поведения. В среде любителей, это привело к развитию культуры изучения этих недокументированных возможностей, известной под обобщенным названием ЕГГОГ-ология.

Некоторые образчики таких багов забавны, иные — опасны, а отдельные из них удивительно полезны. Хорошим примером такого потенциально полезного поведения является выполнение команды «ввод порядка» (ВП) после записи в регистр памяти. В режиме выполнения программы, она приводила к «отрезанию» первой цифры числа в регистре X (отображаемом на индикаторе), причем при пошаговом выполнении (да да, было и такое), команда отрабатывала вполне штатно. Добиться каким-либо другим вменяемым способом такого результата, было невозможно.

Напуганный столь нестандартным поведением команды ВП я внес исправление в программу, изменив последовательность команд 35.ВП 36.1, на 35.ИП2 36.x (во 2-ом регистре пришлось хранить константу 10). Поскольку живого калькулятора нет, я уже не могу проверить, приводила ли команда ВП, в этом контексте к ошибке. Под эмулятором оба варианта работают вполне штатно.

К сожалению, мне не известна реализация эмулятора воспроизводящая все примеры недокументированного поведения.

Уже после всего этого, были Микроши, БК-шки и Спектрумы. Была ЕС-1046, с которой я столкнулся в институте. Были PDP-шки и VAX-ы. До появления IBM PC, ждать оставалось недолго.
Tags:
Hubs:
+59
Comments 16
Comments Comments 16

Articles