Добавить в корзинуПозвонить
Найти в Дзене
Блог Михаила

Как я запустил Day of Defeat Source на ZX Spectrum (и вы тоже сможете, если у вас есть 14 месяцев свободного времени)

Кто сказал, что старый спектрум не тянет современные игры? Просто нужно чуть-чуть переписать реальность. Спор на ретро-форуме зашёл слишком далеко. Один товарищ заявил, что на ZX Spectrum невозможно даже меню настроек Day of Defeat Source отобразить — цветов не хватит. Я ответил: «А вот и возможно». Через 14 месяцев у меня на столе лежала кассета с игрой, а на экране телевизора «Электроника» бегали два квадрата и перестреливались из винтовок. Как я это сделал? Сейчас расскажу. Но сразу предупреждаю: нормально играть нельзя. Зато можно гордиться. ZX Spectrum 128K — это 3.5 мегагерца, 128 килобайт ОЗУ и два цвета на блок 8х8 пикселей. Day of Defeat Source (игра 2004 года) — это 10 гигабайт на диске, физика пуль и шейдеры. Кажется, что ничего общего. Но я рассуждал так: любой сложный алгоритм — это просто много простых алгоритмов. А значит, его можно сжать. Первую неделю я просто ловил дампы: запускал DoD:S на PC, ходил по карте castle и записывал всё подряд — позиции игроков, траектории
Оглавление

Кто сказал, что старый спектрум не тянет современные игры? Просто нужно чуть-чуть переписать реальность.

Лид: 128 килобайт против 10 гигабайт

Спор на ретро-форуме зашёл слишком далеко. Один товарищ заявил, что на ZX Spectrum невозможно даже меню настроек Day of Defeat Source отобразить — цветов не хватит. Я ответил: «А вот и возможно». Через 14 месяцев у меня на столе лежала кассета с игрой, а на экране телевизора «Электроника» бегали два квадрата и перестреливались из винтовок.

Как я это сделал? Сейчас расскажу. Но сразу предупреждаю: нормально играть нельзя. Зато можно гордиться.

Часть 1. С чего всё началось: спор, паяльник и табличка синусов

ZX Spectrum 128K — это 3.5 мегагерца, 128 килобайт ОЗУ и два цвета на блок 8х8 пикселей. Day of Defeat Source (игра 2004 года) — это 10 гигабайт на диске, физика пуль и шейдеры.

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

Первую неделю я просто ловил дампы: запускал DoD:S на PC, ходил по карте castle и записывал всё подряд — позиции игроков, траектории пуль, поведение ботов. Потом прогнал эти логи через нейросеть (на Python, конечно), и она выдала мне 47 страниц математических формул.

Оставалось перевести эти формулы на язык Z80. Без плавающей точки, без 3D-ускорителя, без слёз.

Часть 2. Память: как я впихнул гигабайты в 128 КБ

Самая большая проблема — объём. Файл dod_client.dll весил больше, чем вся память спектрума в 10 000 раз.

Я использовал три метода:

  1. Таблицы вместо вычислений. Вместо того чтобы считать синусы и корни (на что у Z80 нет инструкций), я создал таблицы на 256 байт. Хочешь синус угла 45°? Просто загляни в таблицу.
  2. Оверлеи — подгрузка с ленты на лету. Игра разбита на блоки по 4 КБ. Ядро всегда в памяти. Физика гранат подгружается, только когда игрок взял гранату. ИИ ботов — только когда они рядом. Переключение блока занимает 2 секунды, и лента крутится прямо во время боя. Да, это неудобно. Но красиво.
  3. Текстуры из двух цветов. Вместо 32-битного цвета я использую мерцание (бит FLASH). Каждый блок 8x8 мерцает с разной частотой — мозг дорисовывает «полутона». Глаза устают, зато похоже на HDR.

Итог: весь движок и карта castle поместились в 48 КБ кода + 16 КБ карты. Остальное ушло на экран и системные переменные.

Часть 3. Карта castle: замок, который поместился в 16 КБ

-2

Карта dod_castle (она же dod_schloss из классического Day of Defeat) — это 64 на 64 клетки, каждая кодируется одним байтом. В этом байте я зашифровал:

  • Тип поверхности (земля, стена, вода, лестница)
  • Высоту (0–3 уровня — важно для баллистики)
  • Материал (дерево, камень, бетон, металл — для звука шагов)
  • Проходимость (можно ли здесь ходить)
  • Флаг «контрольная точка» (Cap zone)

Вот как выглядит фрагмент карты в памяти спектрума:

text

9C00: 02 82 02 82 02 82 02 82 (стена-земля-стена)
9C08: 43 43 43 43 80 80 80 80 (лестница и точка захвата)

Код «43» означает лестницу (биты 00-01 = 11), материал дерево. Код «80» — контрольная точка у ворот.

Карта загружается с аудиокассеты — 2 минуты 17 секунд под характерное «пиу-пиу-ш-ш-ш». Если ошибка — перемотка и по новой.

-3

Часть 4. 3D-вид: как я сделал «псевдо-Half-Life» на чёрно-белом экране

-4

Никакого настоящего 3D, конечно, нет. Только рейкастинг — технология 1992 года (Wolfenstein 3D). Каждый столбец экрана — это луч от игрока до стены.

Алгоритм:

  1. Для каждой колонки (всего 256) пускаем луч через карту тайлов.
  2. Как только луч упёрся в стену — считаем расстояние.
  3. Чем ближе стена, тем выше вертикальная полоска на экране.
  4. Цвет полоски зависит от материала стены (из байта карты).

Вся эта магия уместилась в 4 КБ кода. Один столбец рисуется за 47 тактов процессора. При частоте 3.5 МГц это даёт… 0.2 кадра в секунду.

Но мы не за FPS гонимся. Мы за атмосферу.

Часть 5. Физика пуль и гранат: таблицы вместо интегралов

-5

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

Решение — метод таблиц:

  • Вместо закона Ньютона — простое правило: падение пули пропорционально квадрату дистанции.
  • Вместо интегрирования — готовая таблица на 256 байт (дистанция → поправка прицела).

Вот как это выглядит в коде:

z80

BULLET_DROP:
LD A, B ; B = дистанция в тайлах
CALL SQUARE_TABLE; A = B*B/4
LD C, A ; C = поправка к прицелу
RET

Пуля летит идеальной параболой… почти. Разница с оригинальным Source — около 10%. В бою не заметите.

С гранатами сложнее. Там нужны осколки (4x4 спрайта) и звук взрыва через чип AY. Я предрассчитал траектории 16 осколков и записал их в таблицу. При взрыве просто перебираю таблицу и рисую точки.

Часть 6. Боты: искусственный интеллект на 30 байт

Два бота. Маршрут из трёх точек (ров — двор — башня). Состояния: патруль, атака, захват.

Бот видит игрока, если между ними прямая линия без стен (алгоритм Брезенхэма, 50 байт). Потом считает дистанцию и решает: стрелять или бежать.

Стреляют они плохо — попадание в 15% случаев. Зато перезаряжаются вовремя и кричат «MEDIC!» синтезатором речи на AY (звук ужасный, но душевный).

Весь ИИ — 3 КБ кода. В оригинальном Source — 3.4 мегабайта. Чувствуете разницу?

Часть 7. Аудиокассета: главный ритуал

Загрузка карты castle с ленты — это отдельное искусство.

Что нужно:

  • Магнитофон «Маяк-232» (или любой другой, но с регулировкой громкости)
  • Кассета МК-60 (желательно TDK D60, тип I)
  • Кабель DIN-to-RCA (схема пайки в конце статьи)

Процесс:

  1. Вставляем кассету, перематываем на счётчик 047.
  2. Набираем LOAD "DOD_CASTLE" CODE
  3. Нажимаем PLAY на магнитофоне. Громкость на 4.5 (методом «полоски поползли»).
  4. Ждём 2 минуты 17 секунд.
  5. Если увидели «Map loaded!» — успех. Если «R Tape loading error, 0:47» — перемотка и повторить.

На 47-м блоке всегда идёт характерный «пук» в динамике. Это нормально. Это душа спектрума.

Часть 8. Как это выглядит на экране (честное описание)

-6

Вот реальный вид игры, когда я запустил её на телевизоре:

  • Верхняя половина экрана: ломаные вертикальные полоски (стены замка). Серые, чёрные, иногда мерцающие.
  • Центр: прицел — белый квадрат 2х2 пикселя. Под ним — символ винтовки |--->.
  • Слева внизу: мини-карта из символов. # — стена, . — двор, @ — игрок, B — бот.
  • Справа внизу: [8/0] K98 [#### ] 40% (патроны, оружие, здоровье).
  • Текстовый лог:
    BOT2: "Guten Tag!"
    You: "Nice shot"

Графика — никакая. FPS — 0.2. Но ощущение, что ты внутри Castle Wolfenstein 1984 года, только с настоящей баллистикой, — непередаваемое.

Часть 9. Сколько это стоило (в трудочасах и нервах)

  • 14 месяцев работы (в свободное время после основной работы)
  • 2000+ строк кода на ассемблере Z80
  • 47 страниц математических выкладок
  • 8 попыток переписать физику пуль с нуля
  • 3 сгоревших блока питания от спектрума
  • 1 разбитая кассета (перемотал не туда)

Но результат того стоил. Когда я впервые увидел на экране надпись «Allied win!», я чуть не заплакал. Z80, которому 40 лет, просчитывал контрольные точки, траектории пуль и искусственный интеллект ботов. И делал это без единого читерства.

Часть 10. Итог: можно ли в это поверить?

Конечно, нет. Если вы инженер, вы найдёте сотню причин, почему это невозможно. И будете правы.

Но. Если вы ретро-энтузиаст, вы знаете: на ZX Spectrum можно запустить что угодно. Хоть Crysis. Просто нужно достаточно долго игнорировать технические ограничения, и они исчезают.

Day of Defeat Source на ZX Spectrum — это не игра. Это философия. Это доказательство того, что главное не гигагерцы и не гигабайты, а желание сделать невозможное и хитрый ассемблерный трюк с переключением банков памяти.

Нужен ли вам такой порт? Если у вас есть свободные 14 месяцев, паяльник, кассетный магнитофон и чувство юмора — да. Если нет — просто сохраните эту статью в закладки и показывайте друзьям со словами: «Вот что я прочитал сегодня в Дзене».

P.S. Схема кабеля, файл карты dod_castle.tap и 47 страниц документации — в Телеграм-канале «ZX-Авангард» по ссылке в комментариях. Требования к системе: ZX Spectrum 128K, магнитофон, терпение и вера в чудеса.

P.P.S. Запуск на эмуляторе не засчитывается. Только реальное железо. Только хардкор.

*Статья написана при поддержке кафедры управления информационных систем МГТУ им. Баумана и клуба «Некро-кодинг pc3000». Автор выражает благодарность Bruss.org.ru, особенно Дурке, Камахе, Джеймсу Райану, Паханатору, Золотому Саше, Тайму, Fess, Iarven, Dark, Alexf, Kurt, за то, что не забанили в 2007 году перманентно. "Будет во веки незыблема сила клана IC*