Код персонального домового

Трудно ли программировать на «Спектруме»?

У каждого человека свои странные привычки. Кто-то любит чай с бергамотом, кто-то — пиццу с ананасами, а кто-то — манную кашу с сосисками. Другим нравится просматривать объявления о продаже чего-нибудь не очень обычного. Так мы наткнулись на несколько объявлений о продаже персональных электронных вычислительных машин «Нафаня». Вместе с командой инженеров «Авито» мы решили приобрести такой компьютер и вспомнить детство, в котором составление программ напоминало волшебство, а игры загружались с кассет по 10-30 минут. И этим текстом мы открываем рубрику «Найдено на «Авито», в которой будем искать и тестировать необычную и интересную компьютерную технику — от «Нафани» до Amstrad PCW512.

Больше, чем калькулятор

В 1961 году в Великобритании Клайв Синклер основал компанию Sinclair Research. Эта фирма занялась разработкой нескольких электронных устройств, полагаясь на принцип альтруизма, — Синклер был уверен, что людям можно дать возможность пользоваться благами прогресса за приемлемые деньги.

Благодаря такому подходу на свет появились карманный телевизор TV80, часы Black Watch со светодиодными цифровыми индикаторами и наручный калькулятор Wrist Calculator.

Подавляющее большинство электроники, разработанной Sinclair Research, можно было купить в двух вариантах: полностью собранном и в виде комплекта для сборки, причем второй был дешевле первого. Но настоящая слава к британской фирме пришла после выхода на рынок компьютеров серии ZX (ZX80, ZX81 и ZX82, он же ZX81 Colour и ZX Spectrum).

Эти компьютеры были привлекательны по нескольким причинам: они были компактны, подключались к телевизорам (а не специальным мониторам, как компьютеры того времени) и стоили относительно недорого. Например, ZX Spectrum с 48 килобайтами оперативной памяти на старте продаж стоил 175 фунтов стерлингов (около 490 фунтов стерлингов 2019 года, или 630 долларов), а немного позже — уже 129 фунтов стерлингов.

Эти компьютеры, а также более поздние и более мощные версии ZX Spectrum+2 и ZX Spectrum+3 и их модификации стали очень популярны и в Великобритании, и в остальных странах мира. В 1981 году Клайв Синклер был удостоен титула рыцаря за изобретения, популяризирующие британские технологии и прославляющие английскую корону.

Начиная с середины 1980-х годов в нескольких странах мира уже выпускались клоны компьютера ZX Spectrum и его версий. В Польше продавались Elwro Junior, в Германии — Spectral, в Чехословакии — Didaktik и Mistrum, а в Румынии — Cobra.

В СССР «дух» Spectrum проник, вероятнее всего, через Польшу. Уже к концу 1980-х годов в стране выпускались несколько десятков клонов ZX Spectrum с звучными именами: «Ленинград», «Балтик», «Байт», «Дубна», «Композит», «Кворум», «Красногорск», «НИС», «НЭТИ», Patisonic, «Робик», «Сантака», «Сириус», «Нафаня».

Советские конструкторы нашли способ упростить схему компьютера. В частности, некоторые версии вычислительной машины получили адресные мультиплексоры, буферы клавиатуры и логику оперативной памяти, выполненные из советских комплектующих. Наиболее серьезной советской доработкой «Спектрума» стала подпольная платформа «Ленинград-1», в которой общее число распаянных элементов сократилось с 47 до 44.

В результате «Спектрумы» в Советском Союзе стали настоящей находкой для энтузиастов. Такие компьютеры в зависимости от комплектации, версии и степени «клонированности» продавались по цене от 260 до 950 рублей — дорого, но не слишком. Но самое главное, для них вышло огромное количество программ и игр, распространявшихся на самых обычных аудиокассетах. Эти кассеты просто переписывались любителями.

Программы для «Спектрумов» были записаны на кассетах в виде модулированного аудиосигнала, который в компьютере при «прослушивании» преобразовывался в бинарный код. Типичная аудиокассета того времени вмещала на себе аудиозапись продолжительностью до 60 минут (по 30 минут на сторону): около 17 килобайт на минуту записи, или чуть больше 500 килобайт на 60 минут.

В среднем программы на ZX Spectrum с объемом памяти 48 килобайт загружались не дольше 4-5 минут, но у Spectrum со 128 килобайтами время загрузки могло достигать 20-30 минут — все зависело от скорости загрузки данных и качества записи. При этом большинство «Спектрумов» были крайне капризны: незначительный скачок напряжения в сети (заработал холодильник на кухне или соседи включили утюг) — и загрузку приходилось начинать заново.

«Нафаня»

В 1990-1993 году компания «Аксон» выпускала собственный клон ZX Spectrum 48K, получивший название «Нафаня» — в честь Нафани, друга домовенка Кузи. Компьютер имел оперативную память объемом 48 килобайт, стоил около 600 рублей и поставлялся в дипломате с блоком питания, джойстиком, кассетой с базовым набором программ и всеми кабелями, необходимыми для подключения.

«Нафаня» был аппаратом непредсказуемым: некоторые версии компьютера имели 5-пиновый выход под видеосигнал, другие 7-пиновый, третьи 7-пиновый со звуковым моновыходом. В некоторых таких компьютерах видеосигнал подавался в виде четырех сигналов: для красного, синего и зеленого цветов плюс сигнал синхронизации. Другие компьютеры по каналу синхронизации помимо обычной синхросмеси передавали и композитный видеосигнал.

По части программного обеспечения тоже все было непросто: какие-то компьютеры оснащались стандартной прошивкой Sinclair Research, какие-то — модифицированной с изображением Нафани. Кроме того, были версии вычислительной машины с пленочной клавиатурой, а были — с кнопочной (резинка, как у современных телевизионных пультов, у которой контактные площадки выполнены не в виде графитового напыления, а в виде медных пятачков).

Для питания «Нафани» требовались 5 вольт и от 136 до 340 миллиампер. Компьютеры комплектовались несколькими видами блоков питания: одни давали на выход только необходимые 5 вольт, на других внешний вольтаж можно было переключать от 5 до 12 (наверное, немало «Нафань» погибло от неверно выставленного напряжения на таких блоках).

Трудный домовой

По объявлению на «Авито» мы приобрели «Нафаню» с заводским номером 44709. В комплекте: инструкция (со списком научной литературы по программированию, серьезный подход), джойстик, кассета и компьютер. Блока питания и кабеля для подключения магнитофона не было. Компьютер достался в довольно потрепанном виде: одна кнопка выпала и потерялась, вторая — отвалилась от резинки и запала. «Нафаня» этот имеет 7-пиновый выход для подключения монитора или телевизора.

Поначалу казалось, что подключить компьютер к телевизору или монитору будет несложно: в инструкции четко написано, что контакт 1 — синхронизация, 2 — земля, 3 — красный, 4 — синий, 5 — зеленый. Что тут может быть сложного? Но переходник, спаянный для монитора на базе микросхемы LM1881, отвечающей за разделение синхросмеси на сигналы вертикальной и горизонтальной синхронизации, почему-то не заработал — три перепробованных монитора упорно не видели подключенное устройство.

Тогда пришлось «шаманить» подключение к телевизору по SCART. Это штекер унифицированного подключения мультимедийных устройств, поддерживающий и композитный видеосигнал, и RGB-сигнал, и аудиосигнал, и много всего остального. Что, опять же, может быть сложного: SCART принимает RGB, «Нафаня» его отдает.

Но и здесь возникли сложности. Схемы подключения «спектрум»-подобных компьютеров к телевизору, которая бы заработала в нашем случае, найти не удалось. Дело в том, что прямое подключение проводами RGB-SCART хотя и возможно, небезопасно для телевизора, выдерживающего на входе не более 0,7 вольта, тогда как «Нафаня» отдает целых 2! И еще нужны дополнительные 1-3 вольта для подачи на тот же SCART, чтобы телевизор переключился в RGB-режим, то есть выводимая картинка из черно-белой стала бы цветной.

Проблему удалось решить перебором резисторов. «Нафаня» подключился и выдал картинку при распаянных сопротивлениях с номиналом 150 ом. Помимо этого, недостающие кнопки были заменены тактовыми, а питание выведено через USB. Но при включении компьютера нас ждало разочарование: он смог выдать картинку белой рамки с черным квадратом посередине, в котором на равных расстояниях были изображены вертикальные красные линии. Это признак неправильно работающей памяти.

Пропайка контактов на случай, если причина была в растрескавшемся припое на плате, все только ухудшила — «Нафаня» умер окончательно, выдав дрожащую картинку с желтой рамкой и прерывающийся неравномерный писк.

Тогда по другому объявлению мы приобрели второго «Нафаню». В нем оказался 5-пиновый выход на телевизор. Быстрая замена штекера на кабеле результата не дала — компьютер очевидно выдавал картинку, но она дрожала, постоянно срывалась в черно-белый и вообще была не читаема. И снова началась игра с резисторами.

Выяснилось, что этого «Нафаню», с заводским номером 05521, можно подключить без резисторов вовсе — при включении в SCART напряжение на выходах снижалось с 2 вольт до 0,5 вольта (закон Ома в действии). На самом деле, какие-то резисторы все равно нужны, причем индивидуально подобранные под каждый цвет — выводимая картинка была с синим оттенком.

Но это уже было неважно. Экран телевизора показал заветную строчку: «(c) 1982 Sinclair Research Ltd.». При нажатии Enter строчка сменилась мигающим курсором с буквой K (это обозначение «раскладки» клавиатуры: есть также курсор типа L, E, C и G). Все «спектрумы» оснащались интерпретатором языка Sinclair BASIC, диалекта языка программирования BASIC для 8-битных компьютеров типа Spectrum.

Язык Sinclair BASIC от BASIC отличается уменьшенным количеством операторов, команд и ключевых слов. Это было сделано специально, чтобы программы могли умещаться в невеликой памяти компьютеров семейства ZX. Кроме того, в Sinclair BASIC оператор объявления переменной LET был обязательным. Если в обычном BASIC можно было написать A=0 и интерпретатор понимал, что объявляется переменная A со значением 0, то в Sinclair BASIC необходимо было писать LET A=0. К слову, уж не из BASIC ли в современный JavaScript пришло это let?

В Sinclair BASIC оператор ветвления IF THEN лишен добавочного оператора ELSE. Это означает, что если бы в обычном BASIC мы написали условие IF A=0 THEN GO TO 100 ELSE GO TO 200 (если переменная A равна 0, то перейти к нумерованной строке кода 100, иначе — к 200), то в Sinclair BASIC этот код был бы написан на двух строках:

10 IF A=0 THEN GO TO 100
20 GO TO 200

Наконец, в Sinclair BASIC были и уникальные команды для работы с внешним накопителем информации ZX Microdrive: CAT, CLOSE#, FORMAT и несколько других. В компьютерах ZX Spectrum все возможные операторы и команды присвоены клавишам клавиатуры по три-четыре команды на клавишу (выбираются сменой типа курсора и/или одновременным нажатием со вспомогательными клавишами SYMBOL SHIFT (SIMBOL SHIFT на «Нафане») или CAPS SHIFT. То есть для ввода оператора ветвления IF надо было нажать всего лишь кнопку U, а для LET — L. Набранные в коде просто буквами такие операторы выводят ошибку — интерпретатор подсвечивает их знаком вопроса.

Вот так все и просто и сложно одновременно. С одной стороны, набор кода сводится к нажатию отдельных клавиш или сочетаний клавиш, а с другой — при работе с теми же операторами ветвления код должен быть продуман на несколько строк вперед, чтобы знать, номер какой именно строки еще не написанного кода в директиве GO TO следует указать. На самом деле, код тогда писался отдельно, в домашних условиях — на листке бумаги, а потом уже вводился в компьютер.

Сохранить написанную программу на аудиокассету можно командой SAVE с добавлением названия в двойных кавычках. После ввода этой команды компьютер переходит в ждущий режим — пользователю необходимо нажать кнопку записи на магнитофоне, а затем любую клавишу на вычислительной машине. После того, как компьютер «просвистит» код на пленку, магнитофон нужно остановить.

Фактически, благодаря операторам и командам, жестко закрепленным за клавишами, благодаря аудиокассетам и относительной простоте освоения (например, инструкция к «Нафане» читается легко и приятно) программирование на компьютерах семейства ZX превращалось в игру, в которую некоторые пользователи с удовольствием и играли.

Получается, что своей разработкой Клайв Синклер популяризовал программирование, сделав его еще и простым — в Sinclair BASIC следование синтаксису было почти принудительным (операторы же закреплены за клавишами), что уменьшало количество ошибок.

В поисках Фибоначчи

Словом, мы тоже решили попробовать себя в программировании. Для начала была выбрана довольно распространенная задача: написать программу поиска числа Фибоначчи по его порядковому номеру в ряду. Такую задачку нередко дают нынешним программистам на собеседовании (наряду с еще одной, довольно дурацкой, — написать рекурсивную функцию возведения числа в степень).

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

Код вычисления числа Фибоначчи на Sinclair BASIC выглядит следующим образом:

10 INPUT N
20 PRINT INT (0.5+(((SQR 5+1)/2)^N)/SQR 5)

В нем используется формула Бине с вычислением числа Фибоначчи через «золотое сечение», возведенное в степень n, где n — порядковый номер числа: ((1 + v2) / 2)n (в коде это — (SQR 5 + 1) / 2)^N). Результат этого вычисления делится на квадратный корень из 5, а затем к итогу прибавляется 0,5. Алгебраическая функция INT округляет дробный результат вычисления до целого числа, а команда PRINT — выводит его на экран.

Код итеративного поиска числа Фибоначчи, предполагающий повторение определенных действий несколько раз, выглядит интереснее:

 10 INPUT N
 20 IF N=0 THEN GO TO 200
 30 LET A=0
 40 LET B=1
 50 FOR I=2 TO N
 60 LET C=B
 70 LET B=A+B
 80 LET A=C
 90 NEXT I
100 PRINT B
110 STOP
200 PRINT N

Построчно этот код читается так. Спрашиваем у пользователя номер числа Фибоначчи, которое требуется найти. Если пользователь указал 0, то переходим к строке 200, на которой выводим на экран введенное число — 0. Затем программа останавливается.

Если же число не 0, то есть не удовлетворяет условию оператора ветвления на строке 20, то объявляем переменную A и присваиваем ей значение 0. Затем объявляем переменную B со значением 1. Эти переменные будут использоваться при повторяющемся вычислении в теле цикла.

Этот цикл объявляем в следующей строке. Счетчик цикла — I — начинается с двух, причем тело цикла, следующее за строкой с оператором FOR будет повторяться (итерироваться) только в том случае, если переменная N (указанное пользователем число) будет больше счетчика цикла. Если же меньше, то код, следующий за объявлением цикла будет выполнен только один раз.

На строке 60 объявляем переменную C и записываем в нее значение переменной B. Шестой строкой в переменную B записываем результат сложения значений переменных A и B. Дальше переменной A присваиваем значение переменной C. На строке 90 оператор NEXT увеличит значение переменной I на 1 и цикл, если значение переменной N все еще больше I, повторится.

После того как тело цикла выполнится один или несколько раз и цикл завершится, программа перейдет к строке 100, выведет на экран найденное число Фибоначчи и на строке 110 остановится — сработает директива STOP.

Таким образом если пользователь, к примеру, укажет, что хочет найти число Фибоначчи с порядковым номером 3, программа войдет в цикл, запомнит начальное значение переменной B (1), прибавит 1 к 0, сохранит результат в переменную B, начальное значение C запишет в A, а затем повторит цикл, в середине которого значение B будет уже определено как 1 + 1. Цикл на этом будет прерван, поскольку I, единожды увеличенное на 1, будет равно 3, что уже равно введенному пользователем числу. На экран будет выведен ответ 2, что верно: число Фибоначчи с номером 3 в ряду равно 2.

В рекурсивном коде частично использован код итеративного метода. В Sinclair BASIC новый код можно начать с первой строки, отличающейся от предыдущего кода. В нашем случае — со строки 40. При вводе в память новая строка просто заменит старую, главное, чтобы назначенный ей номер совпал с уже имеющимся в памяти.

Рекурсия в программировании означает вызов функции из самой себя с передачей в новый вызов значений, полученных при предыдущем выполнении функции. Код выглядит так:

 10 INPUT N
 20 IF N=0 THEN GO TO 200
 30 LET A=0
 40 LET B=1
 50 GO SUB 80
 60 PRINT B
 70 STOP
 80 IF N=1 THEN RETURN
 90 LET C=B
100 LET B=A+B
110 LET A=C
120 LET N=N-1
130 GO SUB 80
140 RETURN
200 PRINT N

По мере выполнения, в зависимости от результатов, эта программа вызывает часть своего кода. Директива GO SUB 80 указывает, что программе нужно войти в подпрограмму, начинающуюся со строки с номером 80.

Следует отметить, что в BASIC есть две похожие директивы: GO TO и GO SUB. Первая просто указывает следующую строку кода, с которой нужно продолжить выполнение программы. Вторая же не только указывает строку для продолжения программы, но и сохраняет номер строки, на которой она сама была объявлена.

Таким образом, когда дело доходит до директивы RETURN, программа возвращается к строке, на которой впервые была объявлена директива GO SUB, и выполнение кода продолжается со следующей строки. Таким образом на 80 строке, если значение переменной N будет равно 1, RETURN вернет программу на 60 строку. На этой строке будет выведен результат на экран. За строкой 60 последует строка 70, содержащая директиву STOP.

На строке 130 директива GO SUB 80 является объявлением рекурсии, то есть программе дается указание вернуться к 80 строке и повторить подпрограмму. Рекурсия будет происходить до тех пор, пока значение переменной N не станет равно 1 (с каждой новой рекурсией оно уменьшается на 1 в соответствии с кодом на строке 120).

RETURN на 130 строке указывает на окончание подпрограммы и переход к 60-й строке кода с выводом результата. В остальном поиск числа Фибоначчи программой по рекурсивному методу очень похож на итеративный. В обоих случаях при введении числа 0, ни цикл, ни рекурсия выполнены не будут. Рекурсия также не случится при N равном 1.

Для сравнения, на языке Python 3 итеративное вычисление числа Фибоначчи выглядит так (не забудьте про четыре пробела в начале строк в теле цикла):

fn1 = fn2 = 1
n = int(input("n: ")) - 2
while n > 0:
    fn1, fn2 = fn2, fn1 + fn2
    n -= 1
print(fn2)

На диалекте языка запросов SQL для реляционной базы данных MySQL вычисление числа Фибоначчи выглядит монструозно по сравнению даже с кодом Sinclair BASIC. И это при том, что написанная функция всего лишь реализует математический метод, то есть вычисляет число Фибоначчи. Правда, к вычислению функция переходит только в том случае, если указанное пользователем число меньше 2:

DELIMITER //
CREATE FUNCTION FIBONACCI (fib BIGINT(20))
RETURNS BIGINT(20) DETERMINISTIC
MAIN: BEGIN
DECLARE g_ratio DOUBLE;
DECLARE found_fibonacci BIGINT(20) DEFAULT 0;
SELECT (1 + SQRT(5)) / 2 INTO g_ratio;
IF fib < 2 THEN
SELECT fib INTO found_fibonacci;
ELSE
SELECT FLOOR(POW(gratio , fib) / SQRT(5) + 0.5)
    INTO found_fibonacci;
END IF;
RETURN found_fibonacci;
END MAIN;
//
DELIMITER ;

К слову, вызов этой функции в MySQL будет не менее изощренным: сперва надо будет объявить переменную со значением, равным результату выполнения функции FIBONACCI, которой пользователь в качестве аргумента передал некое число. А затем уже нужно будет запросить содержимое переменной. Выглядит это, например, так:

SET @x = FIBONACCI(10);
SELECT @x;

Программирование как игра

В разные годы для ZX Spectrum и его клонов была выпущена масса книг, содержавших иногда короткий, иногда не очень код тех или иных игр. Мы решили проверить пару таких игр на «Нафане»: «Морской бой» и «Посадка на Луну». К слову, попробовать программы вычисления числа Фибоначчи, игры или свой собственный код, вы можете с помощью эмулятора ZX Spectrum в конце текста.

Вариант «Морского боя» сильно упрощенный. По программе, компьютер размещает на игровом поле 9 на 9 четыре четырехпалубных корабля. Эти корабли нужно подбить не больше, чем за 40 ходов. Если все корабли подбиты или пользователь использовал свой лимит ходов, игра начинается заново. Вот ее код:

 10 LET H=0
 20 DIM X(8)
 30 FOR R=1 TO 8 STEP 2
 40 LET X(R)=INT(RND*8+1)*10+INT(RND*9+1)
 50 LET X(R+1)=X(R)+10
 60 NEXT R
 70 CLS
 80 PRINT "  1 2 3 4 5 6 7 8 9"
 90 FOR R=1 TO 9
100 PRINT AT 2*R,R-R;R
110 NEXT R
120 INPUT M
130 FOR R=1 TO 8
140 IF M=X(R) THEN GO TO 200
150 NEXT R
160 PRINT AT INT(M/10)*2,(M-INT(M/10)*10)*2;"O"
170 GO TO 210
200 PRINT AT INT(M/10)*2,(M-INT(M/10)*10)*2;"X"
210 LET H=H+1
220 IF H=8 THEN RUN
230 GO TO 120

В игре «Посадка на Луну» код немного сложнее. По правилам игры, указывая тягу двигателей и время между отображаемыми на экране состояниями посадочного модуля, нужно совершить посадку на поверхность Луны. Игра ведет подсчет очков. В промежуточных состояниях она показывает важную информацию: скорость (если отрицательная — снижаемся, если положительная — взлетаем), расстояние до поверхности и запас топлива. Все параметры — в условных попугаях. Выглядит эта программа следующим образом:

110 LET V=INT(RND*500)
120 LET H=2000
130 LET R=6000
140 GOTO 260
150 PRINT AT 1,0;"THRUST (0-99)"
160 INPUT F
180 PRINT AT 1,0;"TIME (1-6)"
190 INPUT T
200 CLS
210 IF F*T>R/10 THEN LET F=R/(10*T)
220 LET R=R-F*T*10
230 LET A=F-32
240 LET H=A*T**2+V*T+H
250 LET V=2*A*T+V
260 PRINT,"MOON LANDER"
270 IF H<=0 THEN LET H=0
280 PRINT,"SPEED ";V
290 PRINT,"DISTANCE ";INT H
295 PRINT,"FUEL ";R
300 PRINT AT (H<2000)*(20-H/100),2;"__=__"
305 PRINT AT 21,1;"_____"
310 IF H>0 THEN GOTO 150
320 PRINT AT 5,0;"SCORE=";100+V
330 IF V<-100 THEN PRINT "CRASHED"
340 IF 100+V>0 THEN PRINT "LANDED"

Со времени появления первых «Спектрумов» прошло уже почти 40 лет. По своей конструкции персональные компьютеры шагнули далеко вперед. Программирование тоже не стояло на месте. Появился стиль объектно-ориентированного программирования с его абстракциями, наследованием, полиморфизмом и инкапсуляцией. Появились языки с «синтаксическим сахаром», поддерживающим в том числе и ООП-стиль. Были разработаны несколько парадигм проектирования, некоторые из которых, как, например, известная каждому веб-разработчику MVC, зарождались во времена ZX Spectrum.

И этому прогрессу, наверное даже в существенной мере, способствовало изобретение Клайва Синклера. Не зря же клоны «Спектрумов» выпускаются и по сей день: начиная с конструктора для сборки «Ленинград-1» и заканчивая модернизированными программируемыми версиями Harlequin.

Василий Сычёв

Нашли опечатку? Выделите фрагмент и нажмите Ctrl+Enter.
Подслушано под водой

Соберите идеальную мелодию морских глубин