惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Securelist
O
OpenAI News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threat Research - Cisco Blogs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Google Online Security Blog
Google Online Security Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
N
News and Events Feed by Topic
S
Security Affairs
SecWiki News
SecWiki News
Project Zero
Project Zero
L
Lohrmann on Cybersecurity
P
Proofpoint News Feed
P
Palo Alto Networks Blog
L
LINUX DO - 最新话题
H
Hacker News: Front Page
Recent Commits to openclaw:main
Recent Commits to openclaw:main
I
Intezer
Simon Willison's Weblog
Simon Willison's Weblog
W
WeLiveSecurity
T
The Exploit Database - CXSecurity.com
K
Kaspersky official blog
The GitHub Blog
The GitHub Blog
I
InfoQ
云风的 BLOG
云风的 BLOG
雷峰网
雷峰网
B
Blog
IT之家
IT之家
AWS News Blog
AWS News Blog
Jina AI
Jina AI
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Google DeepMind News
Google DeepMind News
Spread Privacy
Spread Privacy
N
News and Events Feed by Topic
Security Latest
Security Latest
美团技术团队
C
Check Point Blog
WordPress大学
WordPress大学
T
Tenable Blog
S
Security @ Cisco Blogs
Last Week in AI
Last Week in AI
博客园 - 聂微东
月光博客
月光博客
博客园 - 【当耐特】
S
Schneier on Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
Secure Thoughts
Schneier on Security
Schneier on Security
C
Cisco Blogs
Cyberwarzone
Cyberwarzone

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Кто решает судьбу вашего проекта? Разбираем заинтересованные стороны. BABOK #1 Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Игровой автомат своими руками
Тимофей Захаров · 2026-06-13 · via Все публикации подряд на Хабре

Игровой автомат своими руками

Средний

27 мин

6.9K

Игровые автоматы (все же правильнее их называть — аркадные автоматы, чтобы отличать от автоматов для игры на деньги в казино) — это специальные устройства, предназначенные для запуска видеоигр. Такие устройства были чрезвычайно популярны в 1970-х — 1990-х годах. Но, с массовым появлением домашних игровых консолей и развитием игр для персональных компьютеров, их популярность сошла на нет. Однако, в последнее время наблюдается рост интереса к таким автоматам. Многие любители восстанавливают старые аппараты, а также собирают реплики наиболее популярных игр тех лет. Кстати, интересный факт: самая популярная и прибыльная игра для аркадного автомата — PacMan.

Обычно, игровой автомат представляет собой вертикальный корпус высотой 1,5 — 2 метра с экраном. Перед экраном располагаются органы управления — джойстики и кнопки. Предполагается, что игрок будет стоять за автоматом или сидеть на высоком стуле. В обязательном порядке автомат комплектуется монето- или купюроприемником, чтобы приносить прибыль своему владельцу.

❯ Подбор деталей

Списанный терминал

Списанный терминал

Для сборки своего автомата я приобрел примерно за 1000 р. списанный терминал. Терминал включает в себя два сенсорных дисплея (один для оператора и один для клиента), фирменный системный блок DELL на процессоре core i3 четвертого поколения, источник бесперебойного питания, термопринтер EPSON для печати талонов и кучу блоков питания с проводами для подключения всего этого добра. Нижняя часть терминала, станина, имеет мощный каркас, сваренный из металлических труб квадратного сечения. Сверху к этому каркасу крепится пластиковая конструкция с дисплеями. По сути, это уже и есть готовый игровой автомат. К нему нужно лишь добавить органы управления, накатить какой-нибудь эмулятор и готово.

Терминал, вид сбоку

Терминал, вид сбоку

Однако, эта конструкция меня не устроила. Если к металлической части вопросов нет — все сделано мощно, добротно и достаточно аккуратно, то к пластику есть претензии. Во-первых, он очень хлипкий. Вся верхняя часть ходит ходуном. Во вторых, внешний вид пластика за время эксплуатации существенно ухудшился. И в третьих, в некоторых местах пластиковые детали и вовсе оказались сломаны. Ну и в четвертых, пластик для игрового автомата — это как-то не аутентично.

В итоге, было решено следующее: нижнюю, металлическую часть оставить и просто подкрасить краской поврежденные места чтобы скрыть следы эксплуатации, а вот верхнюю изготовить заново, из ДСП панелей. При этом, можно сделать форму и внешний вид аппарата более похожими на реальные аппараты тех лет.

С этой же целью, для большей аутентичности, было решено вместо жидкокристаллического дисплея применить электронно-лучевую трубку. Для чего дополнительно были приобретены два телевизора больших диагоналей. Один телевизор подревнее — с выпуклым экраном, фирмы SHIVAKI. Второй — поновее, уже с плоским кинескопом, фирмы SAMSUNG. Оба телевизора были доведены до рабочего состояния. У первого была проблема с кадровой разверткой — из-за микротрещины вокруг вывода микросхемы, последняя периодически пропадала. Второй же оказалось достаточным просто очистить от векового слоя пыли и грязи.

ТВ «Самсунг»

ТВ «Самсунг»

ТВ «Шиваки»

ТВ «Шиваки»

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

❯ Выбор платформы

Какую же игровую платформу реализовать на этом автомате? В этом вопросе у меня не было никаких колебаний, конечно же — Dendy (она же NES), самая популярная приставка моего детства!

Сейчас все маркетплейсы завалены разного качества копиями этой приставки, причем, уже с зашитыми играми. Например, мной был приобретен такой образец.

Современный клон приставки NES и его внутренности

Современный клон приставки NES и его внутренности

У него, помимо собственного дисплея, есть выходы RCA для вывода изображения и звука на телевизор. В принципе, ничего не мешает запитать устройство от сетевого источника питания, подпаяться проводами к кнопкам и использовать этот девайс в качестве основы для автомата.

Качество картинки с клона на ТВ «Самсунг»

Качество картинки с клона на ТВ «Самсунг»

Качество картинки с клона на ТВ «Шиваки» (телевизор почему-то не распознал систему цветности NTSC приставки)

Качество картинки с клона на ТВ «Шиваки» (телевизор почему-то не распознал систему цветности NTSC приставки)

Но, покупать готовую приставку и просто засовывать ее в свой корпус — не наш метод. У меня давно лежала без дела китайская отладочная плата на ПЛИС Cyclone IV, почему бы не собрать на ней свой собственный клон приставки, причем, такой какой нужно именно мне? На плате, помимо самой ПЛИС имеется память SDRAM на 32 Мб, небольшая EEPROM на 4 кБита, часы DS1302Z с батарейкой, разъем VGA, разъем под SD карту, разъемы для различной периферии, светодиодный семисегментный индикатор, кнопки и зуммер. Питается плата от источника питания 5В. На ПЛИС можно реализовать внутреннюю логику приставки, а имеющуюся периферию использовать для ввода/вывода.

Отладочная плата FPGA Cyclone IV

Отладочная плата FPGA Cyclone IV

❯ NES на ПЛИС

Схема игровой приставки довольно проста. Она состоит из центрального процессора PR2A03-7- на основе широкоизвестного ядра MOS6502, видеопроцессора RP2C02-7-, статической памяти процессора 2 кБ, видеопамяти 2 кБ и всякой мелкой логики. На картридже приставки располагаются 32 кБ ПЗУ процессора и 8 кБ ПЗУ видеопроцессора. Это самая минимальная конфигурация системы. Есть, конечно, и более сложные картриджи, с бОльшим количеством памяти и специальными логическими схемами — мапперами, которые хитрым образом подключают банки дополнительной памяти вместо имеющейся, но их пока не будем использовать. Длинные игры со сложным геймплеем для игрового автомата не очень подходят стилистически.

Схема приставки (фото из интернета)

Схема приставки (фото из интернета)

Инженер Андрей Корж вместе с другими энтузиастами проделал огромную работу и выложил в своем репозитории полные потактовые клоны процессора PR2A03-7- и видеопроцессора RP2C02-7-. Если, например, взять такой модуль процессора, модуль видеопроцессора, добавить к ним модули ОЗУ, ПЗУ, соединить их между собой согласно схемы приставки, прописать логику, то, по идее, на ПЛИС должна полностью собраться приставка NES. Вроде бы, такая задумка не выглядит слишком сложной.

Вывод изображения из и видеопроцессора RP2C02-7- осуществляется в виде трех сигналов R, G, B и сигнала синхронизации. На отладочной плате имеется разъем VGA, через который также выходят сигналы R, G, B и синхронизация. Ничего мудрить не нужно, достаточно спаять кабель-переходник по следующей схеме:

Схема переходника с разъема VGA на SCART

Схема переходника с разъема VGA на SCART

В качестве заготовки кабеля я использовал старый VGA кабель от монитора. Один 15-контактный разъем я отрезал и припаял вместо него разъем SCART. Сигналы R, G, B в разъеме SCART идентичны по уровню и импедансу интерфейсу VGA, их можно подключать напрямую. Аналоговый сигнал на отладочной плате формируется простейшим резистивным ЦАП. А вот сигналы синхронизации требуют немного внимания. Прежде всего, в телевизоре не используются отдельно сигналы кадровой VS и строчной HS синхронизации, а используется их синхросмесь. Я ее подключил на контакт HS. Также, для переключения разъема SCART в режим RGB необходимо подать напряжение около 1 В на контакт 16. Его я взял с выхода VS, на который ПЛИС должна выводить постоянно лог. 1. В цепях HS и VS на отладочной плате стоят резисторы по 22 Ом. Их необходимо увеличить до 100 Ом (резистор в цепи VS) и 180 Ом (в цепи HS) соответственно. Эти резисторы можно просто добавить в кабель-переходник, если не хочется портить отладочную плату. Также по кабелю идут сигналы звука левого и правого каналов. На разъеме VGA они подключены к неиспользуемым контактам 12 и 15.

Резистивный ЦАП для вывода звука

Резистивный ЦАП для вывода звука

Звук выводится аналогичным способом, с помощью простейших резистивных ЦАП, аналогичных тем, через которые формируются сигналы RGB. Их два — 6-разрядный (выход суммы каналов SQA + SQB + RND + TRIA) и 7-разрядный (выход канала дельта-модуляции). Эти резисторы пришлось смонтировать отдельно на небольшой макетной плате и подключить к разъему на плате с ПЛИС. С резистивного ЦАП звук нужно будет подать на вход Audio IN телевизора.

И действительно, все получилось! После нахождения и исправления некоторых ошибок, приставка заработала. Вначале, для отладки я записал в ПЗУ прошивку генератора тестового сигнала, так как этот образ требует всего по 8 кБ ПЗУ процессора и ПЗУ видеопроцессора.

Весь проект выложен в репозитории на Гитхабе.

Тестовое изображение на телевизоре

Тестовое изображение на телевизоре

Данные для ПЗУ можно взять из файла *.nes. Структура этого файла довольно проста: вначале идет заголовок 16 байт, потом данные ПЗУ процессора (16 или 32 кБ) и в конце данные ПЗУ видеопроцессора (8 кБ). Файл можно легко разобрать с помощью любого шестнадцатиричного редактора, например WinHEX.

Тестовое изображение на дисплее отладочной платы

Тестовое изображение на дисплее отладочной платы

К сожалению, имеющихся на борту ПЛИС Cyclone IV ячеек памяти недостаточно для организации даже минимальной рабочей конфигурации приставки 16 кБ/8 кБ. Их хватило только для генератора тестового сигнала. Поэтому было решено реализовать следующую схему: для хранения ПЗУ процессора использовать имеющуюся на плате память SDRAM, ПЗУ видеопроцессора организовать в виде SRAM (поскольку видеопроцессор обращается к своей памяти намного чаще), а в SDRAM загружать данные с SD карты. Вроде бы, такая схема выглядит как рабочий вариант. Это уже достаточно сложная логика для аппаратной реализации, поэтому я подключил к системе дополнительное процессорное ядро (назовем его APU).

❯ APU

(Примечание, чтобы не было путаницы: в терминологии приставки Dendy обычно термином APU называют микросхему PR2A03-7-, центральный процессор 6502 вместе со звуковым сопроцессором).

Чтобы не изобретать велосипед, я взял уже имеющееся в проекте ядро процессора 6502. А почему бы и нет? Писать на ассемблере для процессора полувековой давности — весьма увлекательное занятие. Итак: берем процессорное ядро, к нему обязательно необходимо добавить ПЗУ, в котором будет лежать его исполняемый код. ПЗУ процессора 6502 должно располагаться в самом конце его адресного пространства. Например, в последних 2 кБ. Также потребуется ОЗУ, пусть тоже будет 2 кБ, сильно много не нужно. В нем процессор организует стек и хранит всякие переменные. В диапазон адресного пространства 8…16 кБ будем подключать память SRAM, в которую будут заливаться данные ПЗУ видеопроцессора. Да, еще нужно будет организовать несколько регистров ввода-вывода, чтобы читать данные с SD-карты и управлять как самой приставкой (отбирать шину и держать в сбросе приставку на время пересылки данных), так и некоторыми другими устройствами на отладочной плате. Например, можно подключить светодиодный индикатор и спикер.

И только я собрался писать на ассемблере 6502 код для чтения SD карты, как мне весьма удачно попалась на глаза статья, где упоминается уже готовый модуль на Verilog для чтения SD карт. Этот модуль чрезвычайно упрощает задачу. С его помощью можно даже отказаться и от дополнительного процессорного ядра и реализовать всю логику исключительно аппаратно. Но, поскольку ядро уже было добавлено, а на ассемблере писать все еще хотелось, было решено его оставить. В этом случае тогда логика работы такая: Файл образа читается из SD карты в память SDRAM. Затем, вспомогательный процессор находит в образе начало игры, считывает заголовок, ищет и загружает в SRAM данные ПЗУ видеопроцессора. Затем устанавливает указатель на начало ПЗУ процессора в памяти SDRAM и передает управление приставке NES. В один файл образа можно напихать большое количество различных игр и по нажатию кнопок управления вспомогательный процессор легко сможет вместо одной игры загружать другую.

Регистры APU

inreg0 — SD card reader status

[7         -          4]         [3         -          2]         [1         -          0]

card stat (4 bit)            card type (2 bit)           FS type (2 bit)

На этот регистр выводится состояние модуля чтения SD карты

inreg1   - buttons & sdram data read

[7         -          6]         [5         -          4]         [3]                   [2]                   [1]                   [0]

buttons +/- (2 bit)        not used(2 bit)          coin_inp        data valid       read cmpl      file found

Сюда скоммутированы кнопки + и – для переключения игр, сигнал с монетоприемника,  сигнал data valid (единица говорит о том, что в буфере лежит байт, прочитанный из SDRAM), и сигналы read_cmpl и file_found с модуля чтения SD карты. Когда контроллер обнаружит файл, он установит бит  file_found, когда весь файл будет прочитан в SDRAM установится бит read_cmpl. У примененного модуля чтения SD карты нет такого сигнала, поэтому для его генерации пришлось применить отдельный таймер-счетчик. Если долгое время нет импульсов записи данных с модуля, счетчик досчитывает до максимального значения и формирует сигнал о готовности данных.

inreg2 — address read

[7         -          0]

address byte read (8 bit)

Через этот регистр можно прочитать текущий 24-разрядный адрес на шине записи данных SDRAM. Последний адрес на этой шине после окончания чтения, очевидно, будет равен размеру прочитанного файла. Этот адрес нужен вспомогательному процессору чтобы правильно ориентироваться среди образов игр. С помощью управляющих бит 6 и 7 регистра outreg0 можно выбирать, какая именно часть адреса (младшая, средняя или старшая) будет отображена в этот регистр.

inreg3 — data read

[7         -          0]

data byte read (8 bit)

В этот регистр выводятся данные при чтении из SDRAM.

outreg0 — control register

[7         -          6]         [5]       [4]        [3]        [2]        [1]        [0]

sel addr byte (2 bit)      block   scroll    read     NES    ext       buzzer

                                   rom      type     ack      reset     LED     enable

Управляющий регистр. Биты 6 и 7 выбирают нужную часть 24-разрядного адреса для чтения. Бит 5 (block rom) блокирует линию адреса 14 процессора NES для правильного чтения ПЗУ в случае если у игры используется только одна 16 кБ страница. Бит 4 (scroll type) устанавливает тип скроллинга. Этот бит управляет линией А10 ОЗУ видеопроцессора, коммутируя его на 10-ю или 11-ю линию шины адреса видеопроцессора. Таким образом обеспечивается вертикальный или горизонтальный скроллинг страниц видеопамяти. В приставке этой линией управляет картридж. В заголовке образа игры за этот бит отвечает нулевой бит 6-го байта. Бит 3 регистра (read ack) нужно устанавливать для запроса чтения байта из SDRAM. Бит 2 (NES reset) вводит NES в состояние сброса, при этом шины SRAM и SDRAM коммутируются на вспомогательный процессор для загрузки образов. Через бит 1 можно помигать светодиодиком на плате. Этот же бит блокирует кнопки на клавиатуре после истечения времени счетчика монетоприемника. Бит 0 управляет буззером. Он кратковременно пищит каждый раз при смене игры и издает длительный писк, если при загрузке образа возникли проблемы.

outreg1 — address register         outreg2 — address register outreg3 — address register

[7         -          0] [7         -          0] [7         -          0]

address lower byte (8 bit) address middle byte (8 bit) address higher byte (8 bit)

Эти три байта задают 24-разрядный адрес SDRAM, из которого вспомогательный процессор хочет прочитать байт.

❯ Прошивка APU

Прошивка написана на ассемблере процессора 6502 и после компиляции и преобразовании в формат *.mif попадает в образ для прошивки модуля ПЗУ в проекте на Verilog. Программный код состоит из нескольких сегментов. Поскольку, используется универсальный компилятор CC65, который в общем случае не знает, под какую систему компилируется код, необходимо предварительно настроить файл конфигурации. В файле конфигурации сегментам прописываются их реальные адреса и размер в системе. Например, в нашей самодельной системе сегмент с переменными «DATA» должен располагаться в начале адресного пространства процессора (первые 2 кБ). Сегмент с кодом — в конце адресного пространства (последние 2 кБ). Кроме этих основных сегментов используются еще следующие:

  • Сегмент «EROPAGE». Процессор 6502 имеет команды быстрого доступа к первым 256 байтам адресного пространства (старший байт адреса равен нулю), поэтому эту область выделяют отдельно для хранени специфических переменных. В частности, индексных переменных для косвенной адресации, которые хранить в ином месте нельзя.

  • Сегмент «VRAM». Размер 8 кБ. Этот сегмент отображается на статическое ОЗУ, которое, в свою очередь работает ПЗУ видеоконтроллера приставки. В эту область записываются видеоданные из образа изгры.

  • Сегмент «IO». Сюда отображаютс регистры ввода-вывода APU. 12 регистров на запись, 4 регистра для чтения. Запись и чтение разделены физически для упрощения логики ПЛИС.

  • Сегмент «VECTORS». В нем прописаны адреса трех векторов прерываний - по сбросу, по NMI и по IRQ. При возникновении этих событий процессор ищет эти адреса в этом сегменте и осуществляет переход.

В сегменте кода сначала идут обработчики векторов прерываний. По прерыванию IRQ (один раз в секунду) происходит обработка логики монетоприемника. Уменьшается счетчик времени и когда он доходит до нуля на индикатор выводится заставка и блокируются органы управления. За 10 сек. до окончания времени подается звуковой сигнал. Перед обработкой прерывания дополнительно сохраняются в стек аккумулятор и регистр Х, так как они используются в обработчике. Прерывание может возникнуть в любой момент, если в это время в регистрах лежали какие-то данные, обработчик их может повредить. По завершению прерывания регистры восстанавливаются.

Обработчик прерывания NMI - просто заглушка. Оно не используются. В обработчике прерывания по сбросу просто осуществляется переход на исполняемый код.

В сегменте кода также расположен знакогенератор семисегментного индикатора. Это фиксированные данные, поэтому помещены в ПЗУ.

Далее идет исполняемый код. Он весьма большого объема, несколько сотен строк. Назначение отдельных блоков поясняют комментарии.

Текст программы
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; APU code.
.segment "ZEROPAGE"
indx_lo:.byte $00
indx_hi:.byte $00

.segment "DATA"
waddr0:	.byte $00		;переменные для записи 
waddr1:	.byte $00		;адреса в регистры
waddr2:	.byte $00		;out1, out2, out3
ctrl:	.byte $00		;регистр управления out0
prom:	.byte $00		;количество блоков 16K program ROM
vrom:	.byte $00		;количество блоков 8K video ROM
ctrl1:	.byte $00		;байт управления маппером 1
ctrl2:	.byte $00		;байт управления маппером 2
mapper:	.byte $00		;тип маппера
raddr0:	.byte $00		;максимальный адрес 
raddr1:	.byte $00		;файла (количество
raddr2:	.byte $00		;прочитанных байт)
game:	.byte $00		;номер игры	
gamed:	.byte $00		;номер игры в двоично-десятичном формате
strta0:	.byte $00		;стартовый адрес игры
strta1:	.byte $00
strta2:	.byte $00
nexta0:	.byte $00		;адрес следующей игры
nexta1:	.byte $00
nexta2:	.byte $00
mins:	.byte $00		;переменные таймера
secs:	.byte $00

.segment "VRAM"			;8K буфер VRAM (отображается на 
vram:	.res 8192		;VRAM приставки)

.segment "IO"			;регистры ввода-вывода
;только запись
dig1:   .res 1			;самый левый разряд
dig2:   .res 1
dig3:   .res 1
dig4:   .res 1			;светодиодный
dig5:   .res 1			;индикатор
dig6:   .res 1
dig7:   .res 1
dig8:   .res 1			;самый правый разряд
out0:   .res 1			;регистр управления
out1:   .res 1			;регистр адреса (мл.)
out2:   .res 1			;регистр адреса (ср.)
out3:   .res 1			;регистр адреса (ст.)
;только чтение
in0:   .res 1			;состояние SD reader
in1:   .res 1			;регистр состояния
in2:   .res 1			;регистр чтения адреса
in3:   .res 1			;регистр чтения данных

.segment "CODE"
.proc irq_handler

    PHA         		; сохранить A
    TXA
    PHA         		; сохранить X

	LDA mins	
	BEQ nmi10
	JMP nmi20

nmi10:
	LDA secs	
	BEQ nmi11
	JMP nmi20

nmi11:
	LDA #$A1		;вывод заставки dendy
	STA dig1
	STA dig4
	LDA #$86
	STA dig2
	LDA #$AB
	STA dig3
	LDA #$8D
	STA dig5
	LDA #$FF
	STA dig6
	
	LDA ctrl		;блокировка кнопок
	ORA #$02
	STA ctrl
	STA out0
	
	JMP nmi30
	
nmi20:
	LDA mins
	AND #$F0
	LSR A
	LSR A
	LSR A
	LSR A
	TAX
	LDA codegen, X
	STA dig1

	LDA mins
	AND #$0F
	TAX
	LDA codegen, X
	STA dig2
	
	LDA #$BF
	STA dig3

	LDA secs
	AND #$F0
	LSR A
	LSR A
	LSR A
	LSR A
	TAX
	LDA codegen, X
	STA dig4

	LDA secs
	AND #$0F
	TAX
	LDA codegen, X
	STA dig5
	
	DEC secs		;декремент и
	LDA secs		;двоично-десятичная
	AND #$0F		;коррекция
	CMP #$0F
	BEQ nmi21
	
	JMP nmi25
	
nmi21:
	SEC
	LDA secs
	SBC #$06
	STA secs
	
	AND #$F0
	CMP #$F0
	BEQ nmi22
	
	JMP nmi25

nmi22:
	SEC
	LDA secs
	SBC #$A0
	STA secs

	DEC mins		;декремент и
	LDA mins		;двоично-десятичная
	AND #$0F		;коррекция
	CMP #$0F
	BEQ nmi23
	
	JMP nmi25
	
nmi23:
	SEC
	LDA mins
	SBC #$06
	STA mins
		
	AND #$F0
	CMP #$F0
	BEQ nmi24
	
	JMP nmi25
	
nmi24:
	SEC
	LDA mins
	SBC #$60
	STA mins
	
nmi25:
	LDA mins	
	BEQ nmi26
	JMP nmi30

nmi26:
	LDA secs	
	AND #$F0
	BEQ nmi27
	JMP nmi30

nmi27:
	LDA secs	
	AND #$01
	BEQ nmi28
		
	LDA ctrl		;beep on
	ORA #$01
	STA ctrl
	STA out0
	
	JMP nmi30
	
nmi28:
	LDA ctrl		;beep off
	AND #$FE
	STA ctrl
	STA out0
	
nmi30:	
	PLA
    TAX         	;восстановить X
    PLA         	;восстановить A
	
	RTI
.endproc

.proc nmi_handler
	RTI
.endproc

.proc reset_handler
	SEI
	CLD
	JMP main
.endproc

;знакогенератор семисегментного индикатора
codegen:
.byte $C0, $F9, $A4, $B0, $99, $92, $82, $F8, $80, $90, $88, $83, $C6, $A1, $86, $8E
;      0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F 

.proc main
	
	LDA #$00		;обнуляем outreg0
	STA ctrl	
	STA out0
	STA strta0		;обнуляем стартовый адрес
	STA strta1
	STA strta2
	STA nexta0		;обнуляем стартовый адрес
	STA nexta1
	STA nexta2
	
	STA secs		;задаем начальное состояние 
	LDA #$01		;таймера 01:00
	STA mins		
	
	LDA #$01
	STA game
	STA gamed
	
met1:	
	LDA in0			;читаем состояние контроллера SD
	AND #$F0
	LSR A
	LSR A
	LSR A
	LSR A
	TAX
	LDA codegen, X
	STA dig1		;выводим его в поз. 1 и 2
	
	LDA in0
	AND #$0F
	TAX
	LDA codegen, X
	STA dig2	
	
	LDA #$00		;выбираем для чтения младший байт адреса записи
	STA out0
	
	LDA in2			;читаем младший байт адреса записи
	STA raddr0
	AND #$F0
	LSR A
	LSR A
	LSR A
	LSR A
	TAX
	LDA codegen, X
	STA dig7		;выводим его в поз. 7 и 8
	
	LDA in2
	AND #$0F
	TAX
	LDA codegen, X
	STA dig8	

	LDA #$40		;выбираем для чтения средний байт адреса записи
	STA out0
	
	LDA in2			;читаем средний байт адреса записи
	STA raddr1
	AND #$F0
	LSR A
	LSR A
	LSR A
	LSR A
	TAX
	LDA codegen, X
	STA dig5		;выводим его в поз. 5 и 6
	
	LDA in2
	AND #$0F
	TAX
	LDA codegen, X
	STA dig6
	
	LDA #$80		;выбираем для чтения старший байт адреса записи
	STA out0
	
	LDA in2			;читаем старший байт адреса записи
	STA raddr2
	AND #$F0
	LSR A
	LSR A
	LSR A
	LSR A
	TAX
	LDA codegen, X
	STA dig3		;выводим его в поз. 3 и 4
	
	LDA in2
	AND #$0F
	TAX
	LDA codegen, X
	STA dig4

	LDA in1			;проверяем установку бита окончания чтения SD
	AND #$03
	CMP #$03
	BEQ met2		;если да, то переход на met2, иначе
	
	JMP met1		;переход на met1

;разбираем файл
met2:	
	LDA #$FF		;гасим индикаторы
	STA dig1
	STA dig2	
	STA dig3
	STA dig4
	STA dig5
	STA dig6
	STA dig7
	STA dig8	

new:
	LDA in1
	AND #$C0
	CMP #$C0
	BEQ met41
	
	JMP new

met41:	
	LDA #$00
	STA ctrl
	STA out0
	
	LDA strta0
	STA waddr0
	LDA strta1
	STA waddr1
	LDA strta2
	STA waddr2		
	
;читаем сигнатуру	
	JSR rd_byte		;чтение байта по адресу 0
	CMP #$4E
	BEQ met31	
	
	LDX #$01
	JMP error
	
met31:
	JSR inc_addr	;инкремент адреса
	JSR rd_byte		;чтение байта по адресу 1
	CMP #$45
	BEQ met32	
		
	LDX #$02
	JMP error
	
met32:
	JSR inc_addr	;инкремент адреса
	JSR rd_byte		;чтение байта по адресу 2
	CMP #$53
	BEQ met33	
	
	LDX #$03
	JMP error

met33:
	JSR inc_addr	;инкремент адреса
	JSR rd_byte		;чтение байта по адресу 3
	CMP #$1A
	BEQ met34	
	
	LDX #$04
	JMP error	

met34:
	LDA ctrl		;включаем звуковой сигнал
	ORA #$03		;и светодиод
	STA ctrl
	STA out0
		
	JSR inc_addr	;инкремент адреса
	JSR rd_byte		;чтение байта по адресу 4
	STA prom		;сохраняем значение в переменной prom
	AND #$0F		;выводим его на дисплей
	TAX
	LDA codegen, X
	STA dig2
	
	LDA prom
	AND #$F0
	LSR A
	LSR A
	LSR A
	LSR A
	TAX
	LDA codegen, X
	STA dig1		;выводим в поз. 1 и 2
	
	LDA prom		;проверяем на допустимость
	BEQ met36
	SEC
	SBC #$01	
	BEQ met37
	SEC
	SBC #$01	
	BEQ met38
	
	LDX #$05
	JMP error			

met38:
	LDA ctrl		;устанавливаем блокировку 
	ORA #$20		;адреса ADDR[14] для одностраничного
	STA ctrl		;образа

met37:	
	JSR inc_addr	;инкремент адреса
	JSR rd_byte		;чтение байта по адресу 5
	STA vrom		;сохраняем значение в переменной vrom
	AND #$0F		;выводим его на дисплей
	TAX
	LDA codegen, X
	STA dig4
		
	LDA vrom
	AND #$F0
	LSR A
	LSR A
	LSR A
	LSR A
	TAX
	LDA codegen, X
	STA dig3		;выводим в поз. 3 и 4
	
	SEC				;проверяем на допустимость
	LDA vrom
	SBC #$01	
	BEQ met35

met36:	
	LDX #$06
	JMP error
	
met35:	
	JSR inc_addr	;инкремент адреса
	JSR rd_byte		;чтение байта по адресу 6
	STA ctrl1
	AND #$0F		;выводим его на дисплей
	TAX
	LDA codegen, X
	STA dig6
	
	LDA ctrl1
	AND #$F0
	LSR A
	LSR A
	LSR A
	LSR A
	TAX
	LDA codegen, X
	STA dig5		;выводим в поз. 5 и 6
	
	LDA ctrl1		;устанавливаем бит отражения
	AND #$01
	BEQ met39
	
	LDA ctrl
	ORA #$10
	STA ctrl	
	
met39:	
	JSR inc_addr	;инкремент адреса
	JSR rd_byte		;чтение байта по адресу 7
	STA ctrl2
	AND #$F0
	STA mapper
	LSR A
	LSR A
	LSR A
	LSR A
	TAX
	LDA codegen, X
	STA dig7		;выводим в поз. 7 и 8
	
	LDA ctrl2
	AND #$0F
	TAX
	LDA codegen, X
	STA dig8	
	
	LDA ctrl1	
	AND #$F0
	LSR A
	LSR A
	LSR A
	LSR A
	
	ORA mapper		;сохраняем тип маппера
	STA mapper
	BEQ met48
	
	LDX #$07
	JMP error		;если не 0 то ошибка

;читаем VROM	
met48:
	LDA waddr0		;вычисляем адрес начала
	CLC				;области VROM
	ADC #$09
	STA waddr0
	LDA waddr1
	ADC #$40
	STA waddr1
	LDA waddr2
	ADC #$00
	STA waddr2

met42:		
	DEC prom
	BEQ met45
	
	CLC
	LDA waddr1
	ADC #$40
	STA waddr1
	LDA waddr2
	ADC #$00
	STA waddr2
	
met45:	
	LDX #$00		;обнуляем счетные регистры
	LDY #$00
	
	LDA #<vram		;настраиваем индексный адрес
	STA indx_lo
	LDA #>vram
	STA indx_hi
	
met43:		
	JSR rd_byte		;чтение байта
	STA (indx_lo),Y	;запись байта
	
	JSR inc_addr
	INY				;счетчик по элемету страницы
	BEQ met40
	
	JMP met43	
	
met40:
	INC indx_hi
	INX
	TXA	 
	CMP #$20
	BEQ met60
	
	JMP met43

met60:
	LDA waddr0		;сохраняем адрес 
	STA nexta0		;начала следующего ROM
	LDA waddr1
	STA nexta1
	LDA waddr2
	STA nexta2
	
	LDA strta0		;устанавливаем адрес 
	CLC				;начала ROM
	ADC #$10
	STA out1
	LDA strta1
	ADC #$00
	STA out2
	LDA strta2
	ADC #$00	
	STA out3	
	
	LDA ctrl		;снимаем nes_rst	
	ORA #$04
	AND #$FC
	STA ctrl
	STA out0

	LDA #$A1		;вывод заставки
	STA dig1
	STA dig4
	LDA #$86
	STA dig2
	LDA #$AB
	STA dig3
	LDA #$8D
	STA dig5
	LDA #$FF
	STA dig6
	
	LDA gamed		;вывод номера игры
	AND #$F0
	LSR A
	LSR A
	LSR A
	LSR A
	TAX
	LDA codegen, X
	STA dig7		;выводим его в поз. 7 и 8
	
	LDA gamed
	AND #$0F
	TAX
	LDA codegen, X
	STA dig8	

; бесконечный цикл	
forever:
	CLI

	LDA in1			;проверяем кнопку +
	AND #$40
	BEQ met72
	
	LDA in1			;проверяем кнопку -
	AND #$80
	BEQ met71
	
	LDA in1			;проверяем монетоприемник
	AND #$08
	BEQ for10

	JMP forever
	
for10:	
	JMP met90
	
; обработка кнопки +	
met72:
	INC game		;инкремент и
	LDA game		;проверка на 100
	CMP #$64
	BEQ	met74
	
	LDA nexta2		;проверка на
	CMP raddr2		;максимальный адрес
	BEQ met79
	BPL met74	
	JMP met81

met79:
	LDA nexta1
	CMP raddr1
	BEQ met80
	BPL met74
	JMP met81
	
met80:
	LDA nexta0
	CMP raddr0
	BEQ met74
	BPL met74

met81:	
	INC gamed		;инкремент и
	LDA gamed		;двоично-десятичная
	AND #$0F		;коррекция
	CMP #$0A
	BEQ met75
	
	JMP met78
	
met75:
	CLC
	LDA gamed
	ADC #$06
	STA gamed

met78:
	LDA nexta0		;загрузка следующего
	STA strta0		;адреса образа
	LDA nexta1
	STA strta1
	LDA nexta2
	STA strta2
	
	JMP new
	
met74:
	DEC game
	JMP forever
	
; обработка кнопки -	
met71:	
	DEC game		;декремент и
	BEQ met73		;проверка на 0
	
	DEC gamed		;декремент и
	LDA gamed		;двоично-десятичная
	AND #$0F		;коррекция
	CMP #$0F
	BEQ met76
	
	JMP met77
	
met76:
	SEC
	LDA gamed
	SBC #$06
	STA gamed

met77:	
	LDA #$00
	STA out0
	STA ctrl
	
	LDA strta0
	STA waddr0
	LDA strta1
	STA waddr1
	LDA strta2
	STA waddr2

met83:	
	JSR dec_addr
	JSR rd_byte
	
	CMP #$4E
	BEQ met82
	JMP met83
	
met82:	
	JSR inc_addr	;инкремент адреса
	JSR rd_byte		;чтение байта по адресу 1
	CMP #$45
	BEQ met84	
	JMP met83
	
met84:
	JSR inc_addr	;инкремент адреса
	JSR rd_byte		;чтение байта по адресу 2
	CMP #$53
	BEQ met85	
	JMP met83

met85:
	JSR inc_addr	;инкремент адреса
	JSR rd_byte		;чтение байта по адресу 3
	CMP #$1A
	BEQ met86	
	JMP met83	
	
met86:
	LDA waddr0
	AND #$F0
	STA strta0	
	LDA waddr1	
	STA strta1
	LDA waddr2
	STA strta2
	
	JMP new
	
met73:
	INC game
	JMP forever	
	
; обработка монетоприемника
met90:
	CLC
	LDA mins
	ADC #$05
	STA mins
	AND #$0F
	CMP #$0A
	BMI met92
	
	CLC
	LDA mins
	ADC #$06
	STA mins
	
met92:
	LDA mins
	CMP #$61
	BMI met91

	LDA #$60
	STA mins
	
met91:
	LDA in1
	AND #$08
	BEQ met91
	
	LDA ctrl		;разблокировка кнопок
	AND #$FD
	STA ctrl
	STA out0
	
	JMP forever		;бесконечный цикл
	
; вывод сообщения об ошибке	
error:
	LDA codegen, X	;вывод сообщения
	STA dig8		;об ошибке и его номера
	
	LDX #$0E
	LDA codegen, X
	STA dig5
	
	LDX #$11
	LDA codegen, X
	STA dig6
	STA dig7
	
forever1:
	JMP forever1	

.endproc

; процедура чтения байта (прочитанный байт в аккумуляторе )
.proc	rd_byte
	LDA waddr0		;установка адреса
	STA out1
	LDA waddr1
	STA out2
	LDA waddr2
	STA out3	
	
	LDA ctrl		;запрос чтения байта
	ORA #$08
	STA out0
	AND #$F7
	STA out0

metrd1:	
	LDA in1			;проверяем бит валидности
	AND #$04
	BEQ metrd1		;если нет, то ждем
	LDA in3			;читаем байт
	
	RTS				;возврат из процедуры
.endproc

; процедура инкремента адреса на 1
.proc	inc_addr
	INC waddr0
	BEQ metinc1	
	RTS

metinc1:
	INC waddr1
	BEQ metinc2	
	RTS

metinc2:
	INC waddr2	
	RTS

.endproc

; процедура декремента адреса на 16
.proc	dec_addr
	SEC	
	LDA waddr0
	AND #$F0
	SBC #$10
	STA waddr0
	
	LDA waddr1
	SBC #$00
	STA waddr1

	LDA waddr2
	SBC #$00
	STA waddr2
	
	RTS

.endproc

; вектора прерываний
.segment "VECTORS"
.addr nmi_handler, reset_handler, irq_handler

.segment "STARTUP"

Весь проект выложен в репозитории на Гитхабе.

❯ NES и VGA

NES выдает сигнал в телевизионном стандарте PAL 15,625 кГц/50 Гц или NTSC 15,734 кГц/60 Гц. Однако у многих возникает желание подключить вместо телевизора монитор VGA. Хотя бы по той причине, что найти монитор VGA сейчас намного проще чем телевизор в хорошем состоянии, имеющий входы RGB. И тут самое простое решение — просто удвоить тактовую частоту видеопроцессора. Тогда он начнет выдавать видеосигнал с частотой 31,25 кГц/100 Гц. Строчная частота, в этом случае, полностью соответствует стандарту VGA, с ней проблем нет. А вот кадровую — 100 Гц — принимают не все мониторы. Большинство ширпотребных офисных ЖК принимают частоту кадров в диапазоне 50...75 Гц (надо смотреть спецификацию на конкретный монитор). Однако у меня нашелся старый кинескопный монитор фирмы HP, который спокойно переваривал 100 Гц. Да и, насколько я помню, многие кинескопные мониторы на закате их эры, 100 Гц принимали без проблем.

Подключение к NES монитора VGA

Подключение к NES монитора VGA

Далее возникают две проблемы: первая — видеопроцессор, работающий на удвоенной частоте, начинает с удвоенной частотой генерировать прерывание NMI. Это прерывание генерируется в начале кадрового гасящего импульса и в это время процессору нужно обновить картинку на экране. Также, во многих играх к этому прерыванию привязана скорость обновления игровой ситуации. Проще говоря, персонажи и события начинают двигаться в два раза быстрее. Чтобы это обойти, можно, например, пропускать каждое второе прерывание. Тогда скорость игры останется на прежнем уровне.

Вторая проблема — помимо того, что импульсы NMI идут в 2 раза чаще, они становятся в два раза короче. А за это время процессор должен успеть обновить игровую ситуацию на экране. Если процессор останется работать на прежней частоте, он не успеет обновить картинку и на экране появятся артефакты. Значит, также нужно еще увеличить в два раза тактовую частоту процессора. И, в принципе, после этого все более-менее работает, за исключением того, что сигналы звукового сопровождения увеличивают свою тональность на октаву (в два раза). Также в некоторых играх могут возникать артефакты в случае если картинка выводится путем переключения экранов. Если мы пропускаем каждый второй запрос на прерывание, то один экран обновляется нормально, а второй стоит на месте. В общем, изображение как-то более-менее выводится, игра играется, но тут нужно еще глубоко разбираться в таймингах работы CPU и PPU и подгонять их под стандарт. Это задачка на будущее.

Игра POPEYE на мониторе VGA

Игра POPEYE на мониторе VGA

Другим путем решения этой проблемы (более сложным) является организация буфера на одну строку, который подключается уже к выходу видеопроцессора. В этом случае сама приставка работает в штатном режиме. Видеопроцессор записывает в буфер строку медленно, а схема удвоения считывает эту строку в 2 раза быстрее и два раза подряд.

❯ Подключение джойстиков

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

Пульт

Пульт

Кнопки оказались механические, весьма приличного качества и с приятным тактильным эффектом. Поскольку логика использования аркадного автомата предполагает, в основном, только одного игрока, я решил сделать только один полноценный пульт с джойстиком. А пульт второго игрока сделать вспомогательным, без джойстика, только на кнопках.

Плата с кнопками

Плата с кнопками

Джойстик собран на переменных резисторах (потенциометрах), для преобразования аналогового сигнала с потенциометра собрана простейшая схема.

Схема подключения кнопок и джойстика

Схема подключения кнопок и джойстика

В среднем положении движка потенциометра оба транзистора открыты, на выходах А и Б, соответственно, лог. 0 и лог. 1. Если перемещать рукоятку вверх, то верхний транзистор закроется, а нижний останется открытым. На обоих выходах будет лог. 0. При движении вниз, соответственно на выходах будет лог. 1. Преобразовывать эти логические комбинации в нажатие кнопок будет логика внутри ПЛИС. Номиналы резисторов в схеме подобраны так, чтобы при работе совместно с логическими порогами ПЛИС, логика срабатывала примерно в середине движения рукоятки джойстика в том или ином направлении.

Остальные кнопки подключаются напрямую к ПЛИС с помощью простых подтягивающих резисторов.

❯ Доработка телевизоров

Не у всех телевизоров может оказаться в наличии разъем SCART. Также, не все разъемы SCART поддерживают входы RGB. Поэтому может оказаться необходимым доработка телевизоров для подключения по RGB. Так оказалось и у меня. Маленький телевизор фирмы PHILIPS, который я использовал для отладки, имел разъем SCART, а у больших телевизоров, которые я предполагал использовать в самом автомате, разъема SCART не оказалось.

Разобранный телевизор

Разобранный телевизор

Однако, у телевизора SAMSUNG на корпусе имелись заглушки под этот разъем, значит в некоторых исполнениях этой модели этот разъем присутствовал. Я предполагал, что, просто впаяв недостающие детали в плату телевизора согласно схемы, я таки получу желаемое. Но получилось не все так гладко. Во первых, сам телевизор оказался подделкой под SAMSUNG. Внутри все было самое дешевое и самое китайское. Ни обозначение самой модели телевизора, ни обозначение шасси (платы) в интернете не гуглилось. Соответственно, уже найти схему оказалось проблемой. После достаточно продолжительного поиска была найдена похожая схема. При поиске очень сильно помогает перечисление основных компонентов — типа процессора, типа микросхем кадровой развертки, усилителя звука и т.д. Например, таким образом:

TV SUPRA шасси CY-PH2529TOP-EW. Состав: проц NT11136PG305EG, тюнер CWC-5053-V8. TDKS BSC25-N0608. кадровая LA78141, УНЧ AN17821A, HOT D1557,БП C4460.

В среде ремонтников хорошим тоном является полное перечисление основных компонентов аппарата перед описанием проблемы.

И на найденной схеме действительно оказался разъем SCART как опциональный элемент. Однако, впаяв все недостающие детали, изображения со входа RGB так и не появилось. Оказалось, необходимо еще дополнительно в сервисном меню телевизора переключить тип видео входа (он может быть композитным AV, компонентным YPrPb, компонентным RGB+Sync). Далее начались пляски с бубном по входу в сервисный режим (плата ведь ноунейм, китайская, какая там секретная комбинация — неизвестно). Это все еще осложнялось тем, что телевизор был куплен без родного пульта, а универсальный пульт мог поддерживать не все команды. Однако, тем не менее, в сервисный режим попасть удалось. Но в нем, кроме настроек синхронизации, геометрии изображения и баланса белого, никаких других настроек, связанных со входом SCART не оказалось. Тогда я решил пойти в лоб и тупо подать сигналы RGB непосредственно на выходные видеоусилители. И изображение, наконец, появилось.

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

Есть также еще одна проблема — при включении питания телевизор находится в режиме показа телевизионных каналов. Для переключения его в режим AV нужно кратковременно нажать на соответствующую кнопку на передней панели. Подать сигнал можно, например, с помощью каскада на биполярном транзисторе. Резистор в цепи коллектора должен соответствовать тому сопротивлению, которое стоит у интересующей нас кнопки. В моем случае это 2,2 кОм.

Картинка с этого китайского телевизора мне как-то не очень понравилась. Вроде бы яркость нормальная, и цвета насыщенные, но что-то было в ней не то, не так как в детстве. Да еще и само изображение было кривое и немного подергивалось. Подделка — что с нее взять. А это значит, нужно дальше лезть в схему и искать высохшие или потерявшие емкость конденсаторы или какие то иные неисправности.

Поэтому я решил попробовать аналогичным образом подключить более древний телек фирмы SHIVAKI.

С этим телевизором, как ни странно, все оказалось намного проще. У него уже были разведены дорожки на плате под разъем SCART. И даже не пришлось допаивать дополнительные детали, они все уже были установлены на плате. И картинка на этом телевизоре мне понравилась больше, даже несмотря на изрядно подсевший кинескоп. В конечном итоге, я решил использовать его. А кинескоп заменить уже потом, при случае.

Единственный момент — входы RGB почему то работают только когда телевизор находится в дежурном режиме. В этом режиме вывод звука на динамики блокируется процессором. Пришлось подпаяться проводами и принудительно включить звук.

❯ Изготовление конструкции

Как я писал ранее, верхнюю часть автомата было решено изготовить заново, используя панели ДСП. Для моделирования корпуса я воспользовался программой Fusion 360 фирмы Autodesk. Эта фирма ранее выпускала очень популярный в нашей стране продукт AutoCAD. К чести этой фирмы, она позволяет бесплатно использовать свой продукт (Fusion 360) студентами и для хоббийного (некоммерческого) применения.

3D модель верхней части автомата

3D модель верхней части автомата

Кроме боковых панелей сложной формы и панели с вырезом под кинескоп, все остальные детали сделаны простой прямоугольной формы, что позволяет их выпилить без применения станков с ЧПУ. В качестве материала используется стандартный мебельный щит толщиной 18мм черного цвета без выраженной фактуры.

Пример раскроя деревянных элементов на стандартном мебельном щите

Пример раскроя деревянных элементов на стандартном мебельном щите

Передняя панель с органами управления сделана из 2мм стали, вырезана на лазере и покрыта краской (молотковая эмаль, цвет антрацит). Для облегчения сгиба передней панели пришлось слегка надрезать линии сгиба болгаркой.

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

Крепление органов управления

Крепление органов управления

С наружной стороны втулки закреплены винтами с потайными головками. Отладочная плата также крепится на втулках. Она располагается таким образом, чтобы в отверстие был виден светодиодный индикатор. Индикатор заклеен светофильтром красного цвета

Панель управления в собранном виде

Панель управления в собранном виде

Нарезанные детали

Нарезанные детали

Задняя часть с вентиляционными отверстиями изготавливается из ДВП толщиной 4 мм.

Деревянные панели собираются вместе обычными мебельными уголками с помощью мебельных шурупов. Задняя стенка откидывается на длинной мебельной петле. В прорези в верхней части должно вставляться стекло. На стекло будет наклеена полупрозрачная пленка с привлекающим оформлением. Изнутри стекло будет подсвечиваться светодиодной лентой.

Подсветка оформления

Подсветка оформления

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

Крепление кинескопа

Крепление кинескопа

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

На панели под стеклом крепятся динамики телевизора. Для беспрепятственного прохода звука в панели сделаны прорези. В полусобранном состоянии:

В процессе сборки

В процессе сборки

 Монтаж платы телевизора:

Монтаж платы телевизора

Монтаж платы телевизора

❯ Подключение монетоприемника

Какой же аркадный автомат и без монетоприемника?! Пошарившись на маркетплейсах я нашел монетоприемник за весьма доступный прайс. Это модель BL-616. Аналогичного вида монетприемники встречаются в продаже и под иными обозначениями.

Монетоприемник (фото из интернета)

Монетоприемник (фото из интернета)

Монетоприемник подключается всего четырьмя проводами. По двум из них подается питание 12 В. Два остальных — выходы типа «открытый коллектор». К одному из них подключается механический счетчик монет (для контроля), на втором появляются короткие импульсы при приеме монет. Перед использованием монетоприемник необходимо настроить. Во-первых, необходимо задать виды принимаемых монет, во вторых — прогнать через него по 20 монет каждого номинала. Устройство распознает монету по ее весу. При обучении устройство измеряет вес и рассчитывает среднее его значение для каждого номинала.

Общий порядок настройки следующий:

  • Включить питание, установить переключатель 1 в позицию «NO» (normal open — нормально разомкнутый выход), переключателем 2 — выбрать нужную скорость формирования импульсов (fast, medium, slow);

  • Нажать одновременно кнопки «ADD» и «MINUS», отпустить, на индикаторе появится «A»;

  • Нажать и отпустить кнопку «SET», появится «E»;

  • Кнопками «ADD», «MINUS» установить кол-во различных типов монет для приема (1...6);

  • Нажать кнопку «SET»;

  • На дисплее появилось «H1» — количество экземпляров монеты типа 1 для калибровки монетоприемника;

  • Кнопками «ADD», «MINUS» установить значение H для первого типа монет;

  • Нажать кнопку «SET»;

  • На дисплее появилось «P1» — количество выдаваемых импульсов при успешном приеме монеты типа 1  (1...50);

    Примечание: количество выдаваемых импульсов необходимо сделать пропорциональным стоимости монеты. Например: монета 1 р должна выдавать 1 импульс, монета 5 р должна выдавать 5 импульсов, монета 10 р — 10 импульсов. В этом случае автомат будет добавлять игровое время пропорционально стоимости монеты вне зависимости от порядка ввода монет.

  • Кнопками «ADD», «MINUS» установить значение количества выдаваемых импульсов для первого типа монеты;

  • Нажать кнопку «SET»;

  • На дисплее появилось «F1» — задается точность опознания монеты типа 1  (1...30), можно ввести 10;

  • Кнопками «ADD», «MINUS» установить значение F для первого типа монеты;

  • Нажать кнопку «SET»;

    Повторить последние 9 пунктов для других типов монет.

  • На дисплее появится «0»;

Калибровка монетоприемника:

  • Нажать два раза кнопку «SET»;

  • На дисплее появилось «A1» — опускаем в монетоприемник монеты типа 1 в количестве H1 для калибровки;

  • По загрузке последней монеты раздастся звуковой сигнал и на дисплее появится «A2»;

  • Опускаем в монетоприемник монеты типа 2 в количестве H2 для калибровки;

  • По загрузке последней монеты раздастся звуковой сигнал и на дисплее появится «A3»;

  • Опускаем в монетоприемник монеты типа 3 в количестве H3 для калибровки;

  • После ввода всех монет на дисплее появится «0».

Монетоприемник готов к приему монет. Можем опускать различные монеты, на дисплее будет высвечиваться количество импульсов.

Смонтированный монетоприемник

Смонтированный монетоприемник

При получении сигнала с монетоприемника APU добавляет игровое время, например по 1 мин на импульс. Время индицируется на светодиодном индикаторе. По истечении этого времени органы управления игрой блокируются (кроме кнопки START, чтобы игрок смог поставить игру на паузу и докинуть еще монет). Таймер тактируется от отдельного делителя, формирующего сигнал с периодичностью 1 с. Этот сигнал заведен на прерывание IRQ процессора APU. Таким образом, раз в секунду процессор отвлекается от своей работы и обрабатывает логику монетоприемника.

❯ Питание

Телевизор питается от 230 В сам. Отладочная плата питается через USB от 5-вольтового преобразователя. Преобразователь, светодиодная лента и монетоприемник питаются напряжением 12В от отдельного адаптера. Адаптер и телевизор соединены вместе и одновременно включаются тумблером, расположенном сзади устройства. Все разводка собрана в коммутационной коробке, в которой также дополнительно смонтированы защитные предохранители. В принципе, можно все необходимые напряжения взять сразу из телевизора, если внимательно изучить его схему. Но, в этом случае не получится быстро заменить один телевизор на другой без переделки схемы, а я пока таки надеюсь найти экземпляр с более живым кинескопом.

Монтаж коммутационной коробки и адаптера питания

Монтаж коммутационной коробки и адаптера питания

Дополнительно в коммутационной коробке можно будет смонтировать реле, автоматически отключающее телевизор после некоторого времени бездействия для экономии ресурса ЭЛТ. Но это уже идея на будущее.

Аппарат в сборе

Аппарат в сборе

Вот такой вот получился аппарат. Небольшое видео про автомат можно посмотреть на YouTube. Вопросы и замечания прошу писать в комментариях.


Может быть интересно:
Перейти ↩

Перейти

Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud — в нашем Telegram-канале