Трудно ли программировать на «Спектруме»?
У каждого человека свои странные привычки. Кто-то любит чай с бергамотом, кто-то — пиццу с ананасами, а кто-то — манную кашу с сосисками. Другим нравится просматривать объявления о продаже чего-нибудь не очень обычного. Так мы наткнулись на несколько объявлений о продаже персональных электронных вычислительных машин «Нафаня». Вместе с командой инженеров «Авито» мы решили приобрести такой компьютер и вспомнить детство, в котором составление программ напоминало волшебство, а игры загружались с кассет по 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.
Василий Сычёв
Соберите идеальную мелодию морских глубин
Песни китов и дельфинов, шелест прибрежной гальки, крики чаек и довольное брюзжание моржа — все это великая песнь Мирового океана. Хотите почувствовать себя композитором пучин? Включайте наш сэмплер и творите!