Как засунуть ZX Spectrum в SEGA Mega Drive и не очуметь: Рассказ об аппаратных тупиках.
На первый взгляд задача кажется тривиальной: берём картридж на базе чипа FPGA Altera Cyclone IV (EP4CE15F23C8N), поднимаем внутри кристалла ядро Z80, подкидываем BIOS, мапим порты — и готово. Запускай Dizzy или Elite.
Воодушевлённый этой идеей, взялся за работу и действительно написал работающую базу всего за 3 дня! Логика ядра внутри ПЛИС работала, игры запускались, кнопки на геймпаде нажимались, а из телика «шёл звук». Победа казалась близкой, и я поспешил: «Почти готово, ZXS на Сеге работает!».
Но радовался таки не долго. А именно ровно до тех пор, пока не наткнулся на классический тестер ZXS, где классический спектрумовский эффект FLASH (мигание текста или элементов интерфейса за счет инверсии цвета чернил INK и бумаги PAPER). Как только игра попыталась мигнуть атрибутами, картинка развалилась на куски.
Я упёрся в глухую стену аппаратных ограничений видеопроцессора Сеги (SEGA VDP).
…
Немного лирики… Вот приблизительный внешний вид реализации на тот момент:

Снимем розовые очки… Да, внутри Sega Mega Drive физически распаян чип Z80. Но использовать его для прямого запуска ZXS не особо реально. У сеговского Z80 своя специфическая обвязка, изолированная шина и жёстко заданные порты данных, которые абсолютно несовместимы с архитектурой и таймингами оригинального ZXS. Попытка переключить его на рельсы ZXS без тотального контроля над логикой сигналов это провал.
Единственный рабочий путь — разворачивать полноценное ядро Z80 с нуля внутри кристалла FPGA, тактируя его от системного генератора.
Но как только ядро завелось, перед нами встаёт главная архитектурная проблема: как подружить пиксельный мир ZX Spectrum с тайловым миром Сеги.
Пиксели против тайлов: Логика проецирования кадра.
Спектрум рисует картинку классическим попиксельным методом (bitmap, где один байт задаёт строку из 8 пикселей). Сега же устроена принципиально иначе: она вообще не умеет выводить графику отдельными точками на экране — только готовыми тайлами (матрицами 8 × 8 пикселей).
Чтобы превратить один экран в другой, у нас есть только один путь: сделать всю полезную рабочую область ZX Spectrum набором уникальных тайлов для Сеги. Простая математика разрешения 256 × 192 пикселя даёт нам сетку 32 × 24 блока.
Итого рабочая область экрана: 32 × 24 = ровно 768 уникальных тайлов.
С областью бордюра (Border) на первый взгляд получается схитрить. Вместо того чтобы генерировать мегабайты графики, мы выделяем под бордюр всего один-единственный тайл Сеги. Видеоконтроллер консоли просто размножает его, заполняя всё внешнее пространство вокруг экрана.

План казался надёжным: мы просто гоним 768 тайлов рабочей области и 1 тайл бордюра в видеопамять (SEGA VDP) каждый VBLANK. И вот тут-то физика шины данных рушит всю задумку.
Три аппаратных тупика, которые топят эмулятор.
Попытка реализовать эту схему в лоб показала, что пропускная способность шины данных Сеги в эту математику не помещается.
1. Физический предел VBLANK.
Каждый тайл Сеги весит 32 байта. Массив из 768 тайлов — это 24 576 байт (24 КБ) чистых данных. В режиме NTSC (60 Гц) обратный ход луча VBLANK длится ничтожно мало, и DMA Сеги способен протолкнуть в VRAM максимум ~7.3 КБ. Нам нужно засунуть 24 КБ, а влезает 7. Мы отстаём более чем в три раза! (В PAL-регионе на 50 Гц окно пошире, но ситуацию в целом это не спасает).
Более того, чтобы на экране не возникало уродливых визуальных разрывов (tearing), копировать графику нужно строго синхронно с VBLANK самого Спектрума, попадая в его родные тайминги. При попытке размазать передачу 768 тайлов на несколько кадров Сеги в лоб, экран при первом же глобальном FLASH-мигании порвался ровно на четыре части.
2. «Пытка бордюром» HBLANK.
Тот самый единственный тайл бордюра должен уметь менять свой цвет на любой оттенок из палитры Спектрума (INK/PAPER). Причём делать это не раз в кадр, а на ходу — строго каждый HBLANK (во время обратного хода строки), чтобы отобразить кассетные полосы загрузки. Времени на строку у Сеги настолько мало, что обновить целый тайл в этот момент физически невозможно! Нужно подменять цвета палитры на лету, устраивая гонку за лучом.
3. Контрольный выстрел: Оверлей клавиатуры
На Спектруме без клавиатуры невозможно сделать вообще ничего — даже игру не запустишь (классическое Press ANY key или выбор джойстика в меню). А значит, нам нужно выводить поверх игры экранный оверлей с виртуальной клавиатурой. И эти ассеты — тоже тайлы! То есть к нашим несчастным, не влезающим в шину 24 КБ графики, добавляется необходимость успевать копировать ещё и оверлей с клавиатурой.
Эволюция багов:
Понимая, что 24 КБ за раз не пролазят, я попробовал применить промежуточный видеобуфер. Идея казалась логичной: пусть генератор в FPGA считает тайлы порциями, а мы будем скармливать их Сеге по частям.
Результат? Ошибка реализации и новый визуальный баг. Картинка перестала дробиться на 4 части, но теперь стало конкретно хуже! Вообще «ШЛЯПА»!!!
Как в итоге был обманут VBLANK Сеги и запущен AY-звук?
Разгадка крылась в создании специализированного видеоконтроллера ZX_SEGA_VIDEO прямо внутри ПЛИС, который взял на себя роль «переводчика» и буферного моста между двумя мирами. Вместо того чтобы гонять «сырые» тайлы по шине DMA Сеги каждый кадр, видеоядро внутри FPGA непрерывно слушает обращения Z80 к памяти, вычисляет изменения атрибутов и генерирует тайловую сетку аппаратно на лету.
Он напрямую подключен к физическим линиям слота Сеги (VA, VD), подсовывая консоли нужные данные ровно в те микросекунды, когда пришло время заваливать VDP данными.
Воодушевленный тем как это заработало, решил добавить в кристалл кастомное ядро YM2149, Для исключения цифрового хрипа и перегрузок (клиппинга) была реализована схема знакового стерео-микширования ABC с безопасным сдвигом амплитуды:
Заключение.
Система работает: ромы загружаются мгновенными снапшотами, музыка из AY-3-8910 звучит без перегрузок, а виртуальная экранная клавиатура позволяет запускать старую добрую классику прямо на 16-битной консоли.
Упрощенная схема реализации:

И в завершении: А как же честное «проигрывание кассет»?
Реализовав ядро Z80, победив экран и заставив «железку» стабильно переваривать мгновенные слепки памяти .sna, подумал… Постойте-ка… Я выстроил такой сложнейший и навороченный огород для отрисовки бордюра на HBLANK, но ведь в SNA-файлах картинка просто появляется на экране мгновенно! Весь этот аппаратный конвейер бордюра фактически простаивает. А как же честное, олдскульное проигрывание кассетных образов .tzx? Как же эти «ламповые», бегущие по бордюру полосы, под которые мы замирали в детстве (и ждать завершения которых бесило)?
Без этого Спектрум — не Спектрум.
Аппаратный магнитофон на FIFO
Чтобы реализовать честную загрузку с «кассеты» прямо внутри FPGA, программной реализации «парсера» TZX со стороны ARM было мало — из-за задержек шины и доступа к SDIO (чтение с SD карты) звук EAR уплывал, и загрузка сбоила. Тут необходим специализированный модуль с аппаратным проигрывающим FIFO-буфером, тактируемый строго от нашего системного метронома:
Теперь появился выбор. Хочется играть здесь и сейчас, — выбираем .Z80 или .SNA, система мгновенно заливает образ, и мы сразу в игре.
А вот если хочется «лампового» эффекта ностальгии, используем .TZX… Включается аппаратный плеер и по бордюру Сеги начинают бежать те самые сине-красные и желто-синие кассетные полосы, раздается тот самый незабываемый цифровой скрежет (LOAD «» и запускаем кассету). И всё это работает на реальном 16-битном Mega Drive.
Проект, который начинался как «прогулка» чуть не утонул в аппаратных ограничениях, однако превратился в полноценный, честный и хардкорный трибьют двум великим платформам нашего детства.
На этом закругляюсь. Удачного дня! Отличного настроения! И доброго здоровья!
ссылка на оригинал статьи https://habr.com/ru/articles/1059504/