Скрещиваем Денди и Тостер или Dentoaster своими руками. Как воплотить в жизнь мечту детства и запрограммировать что-нибудь для Dendy Самодельная денди на микропроцессоре

Как сделать игровую приставку своими руками aslan wrote in November 8th, 2017

В общем-то идея сделать такое у меня появилась довольно давно, но только сейчас программное обеспечение для подобных поделок пришло в более-менее приличное состояние.
Прежде я пробовал сделать такое из x86 неттопа, но штука выходила довольно громоздкая и капризная. Потом продал неттоп и купил б/у Android TV Box. Это куда компактнее и легче, но заставить эмулятор RetroArch корректно работать на Андроиде мне так и не удалось, там что ни сборка - сплошные баги.

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


Итак, нам понадобится:
- Raspberry Pi (подойдёт любая, я купил самую мощную, с запасом);
- Блок питания 5V 3A;
- Два USB джойстика;
- Корпус (можно использовать что угодно);
- Различные разъемы и крепеж (по вкусу);
- Клей (по вкусу, мне нравится двухкомпонентная эпоксидка).

Корпус я решил использовать от мертвой приставки, и на барахолке была найдена за пару баксов вот такая 16-битка, китайская копия SEGA Genesis 3.

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

Прикинул расположение элементов внутри корпуса. Как видите - купил блок разъёмов RCA и бесполезнейший в быту, но незаменимый для моих нужд переходник HDMI мама-папа. Он нужен, чтобы отодвинуть плату от края корпуса. А тут и приехала посылка с набором различных латунных стоек (на местном рынке они как-то нереально дорого стоят).

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

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

Но всё получилось практически идеально! Я более чем доволен.

Двигаемся дальше! Вырезал кусочек макетной платы и закрепил на нём гнездо питания. Можно было бы и micro-usb вывести, но так каноничнее. Само собой, крепиться будет на такие же стойки.

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

Подготовил плату и припаял два гнезда USB для джойстиков.

Закрепил на стойках в нужном месте корпуса. Но, само собой, разъемы по форме не совпадают с Сеговскими, и это мне очень не понравилось.

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

Распаял проводами питание, USB-гнезда и аналоговые выходы. Кстати, не знаю, программная это фишка или аппаратная, но в Raspberry Pi 3 определение выхода (аналог/цифра) автоматическое, а вот в первой версии компьютера мне приходилось переключать вручную.

Время делать кнопки включения и сброса. Поскольку штатных кнопок для этих дел в Raspberry Pi не предусмотрено, а выключать, жестким образом отрубая питание, мне не хотелось, я решил подключить кнопки к контактам GPIO и прописать в автозагрузку скрипты для выключения и перезагрузки. Кнопку включения нужно вешать на строго определённые GPIO, чтобы по нажатию кнопки наша приставка не только выключалась, но и включалась.
А скрипты легко найти в Интернетах. Тут меня ждал неприятный сюрприз. Для включения/выключения нужна кнопка без фиксации, а родной выключатель в приставке был простым ползунком. Пришлось сделать возвратный механизм, а ползунок намазать силиконовой смазкой.

А вот и второй сюрприз: какой поставить выключатель? В итоге я просто вытащил из мертвого принтера переключатель с лапкой и изогнул лапку. Теперь при нажатии на ползунок лапка нажимает на переключатель. Отлично. Припаял на плату и закрепил на стойках.

Кнопочку сброса вытащил из того же принтера и закрепил на плате. Однако сама кнопка (на корпусе) упиралась в разъём кнопки питания, а он должен быть строго на 5 и 6 GPIO. Пришлось подрезать разъем. Можно было и припаять напрямую, но не хотелось.

Гнездо для картриджей закрыл сеткой, купленной на барахолке и покрашенной баллончиком в белый цвет. Пришлось изнутри подровнять корпус, но это несложно.
А тут подъехали джойстики в стиле Sega Saturn. Почему они? Потому что у них 6 кнопок и две кнопки сверху, то есть функционал без проблем покрывает NES, SNES и Sega Mega Drive. Нужно только настроить джойстик при первом включении, а потом поправить конфиги для каждого эмулятора и раскидать по папкам.

Сами джойстики по качеству на 3 из 5, сборка отличная, но невнятно нажимаются крестовины. Это я про Retrolink. Можно найти лицензионные USB джойстики Sega, но цены на них ОЧЕНЬ кусаются.

Собственно, готово! Остаётся включить, настроить джойстики и подключить Wi-Fi (понадобится клавиатура), а потом зайти через Total Commander в расшаренные папки устройства и закинуть туда любимые игры.

Вот такая картинка при подключении по RCA. Сразу напрашивается мысль сделать шрифты побольше.

А вот так вот получается, если подключить HDMI. Гораздо лучше. Но аналоговые выходы пригодятся, чтобы играть с друзьями на даче под пивко.

Оговорка: имеется небольшая задержка ввода (Input Lag), это заметил не только я, и телевизор тут ни при чем. В Интернете описаны способы снизить задержку, но это уже другая история.

И еще один неприятный минус - при подключении питания приставка включается сразу же, а не ждёт нажатия кнопки. Это я пока не придумал, как победить.
В планах еще заказать на плоттерной резке плёночные надписи на корпус. А в остальном я доволен, да и друзья тоже.

  • Игры и игровые приставки ,
  • Электроника для начинающих
    • Tutorial

    Первым делом читаем, как происходит взаимодействием с ним. А происходит оно через запись по определённым адресам, их 8 групп: $8000-$9FFE (чётные), $8001-$9FFF (нечётные), $A000-$BFFE (чётные), $A001-$BFFF (нечётные), $C000-$DFFE (чётные), $C001-$DFFF (нечётные), $E000-$FFFE (чётные) и $E001-$FFFF (нечётные). Запись по любому адресу внутри группы равнозначна. Видите закономерность? Регистр выбирается с помощью трёх адресных бит: A0 , A13 и A14 , остальные же значения не имеют.

    Попробуем же имитировать работу маппера с помощью ПЛИС. Код я пишу на языке Verilog. Он тут не подсвечивается, прошу прощения за это.
    Сначала описываем наши регистры, которые хранят текущее состояние:
    reg bank_select; reg prg_mode; reg chr_mode; reg r ; reg mirroring; reg ram_protect; reg irq_latch; reg irq_counter; reg a12_low_time; reg irq_reload; reg irq_reload_clear; reg irq_enabled;

    Описываем реакцию на запись по соответствующим адресам. Возрастающий сигнал /ROMSEL говорим о том что было обращение к памяти картриджа, т.е. по адресам $8000-$FFFF , нам надо реагировать именно в этот момент.
    always @ (posedge romsel) begin // Но только если это была запись if (cpu_rw_in == 0) begin // Рассматриваем состояние A14, A13 и A0, обновляем соответствующие регистры case ({cpu_addr_in, cpu_addr_in}) 3"b000: begin // $8000-$9FFE, even bank_select <= cpu_data_in; prg_mode <= cpu_data_in; chr_mode <= cpu_data_in; end 3"b001: r <= cpu_data_in; // $8001-$9FFF, odd 3"b010: mirroring <= cpu_data_in; // $A000-$BFFE, even 3"b011: ram_protect <= cpu_data_in; // $A001-$BFFF, odd 3"b100: irq_latch <= cpu_data_in; // $C000-$DFFE, even 3"b101: irq_reload <= 1; // $C001-$DFFF, odd 3"b110: irq_enabled <= 0; // $E000-$FFFE, even 3"b111: irq_enabled <= 1; // $E001-$FFFF, odd endcase end if (irq_reload_clear) irq_reload <= 0; end

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

    Где $8000 & #$40 - это у нас prg_mode, а -2 и -1 - это предпоследний и последний банк соответственно. Получается такой код:
    // PRG banking always @ (*) begin case ({cpu_addr_in, prg_mode}) // $8000-$9FFF 3"b000: cpu_addr_out <= r; 3"b001: cpu_addr_out <= 6"b111110; // Предпоследний банк // $A000-$BFFF 3"b010, 3"b011: cpu_addr_out <= r; // $C000-$DFFF 3"b100: cpu_addr_out <= 6"b111110; // Предпоследний банк 3"b101: cpu_addr_out <= r; // $E000-$FFFF - всегда является последним банком default: cpu_addr_out <= 6"b111111; endcase // A12 у MMC3 на выходе всегда как на входе, он идёт напрямую в память cpu_addr_out <= cpu_addr_in; end

    Теперь CHR. Там такая схема:

    Где $8000 & #$40 - это chr_mode. Получается так:
    // CHR banking always @ (*) begin if (ppu_addr_in == chr_mode) ppu_addr_out <= {r, ppu_addr_in}; else ppu_addr_out <= r]; // Максимальный размер CHR у MMC3 - 256 килобайт, поэтому A18 всегда 0. ppu_addr_out <= 0; end

    Режим зеркалирования описывается всего одной строкой. В зависимости от него мы замыкаем вывод картриджа CIRAM A10 либо на A10 , либо на A11 :
    assign ppu_ciram_a10 = mirroring ? ppu_addr_in : ppu_addr_in;

    Дальше сложнее. MMC3 умеет генерировать прерывания, когда на экране рисуется определённая строка. Это весьма полезно, и игры часто это используют. Строки на экране считаются с помощью обращений к A12 у PPU. При типичных настройках сигнал на A12 переходит из логического 0 в логическую 1 ровно один раз за строку, если не считать кратковременные переходы в 0. А их надо не считать, это всё немного усложняет:

    // Включаем прерывания только тогда, когда на A12 низкий уровень always @ (*) begin if (!irq_enabled) begin irq_ready = 0; irq <= 1"bZ; end else if (irq_enabled && !irq_value) irq_ready = 1; else if (irq_ready && irq_value) irq <= 1"b0; end // Сам счётчик always @ (posedge ppu_addr_in) begin if (a12_low_time == 3) // Время низкого уровня A12 должно быть не менее 3 циклов CPU begin if ((irq_reload && !irq_reload_clear) || (irq_counter == 0)) begin irq_counter = irq_latch; if (irq_reload) irq_reload_clear <= 1; end else irq_counter = irq_counter-1; if (irq_counter == 0 && irq_enabled) irq_value = 1; else irq_value = 0; end if (!irq_reload) irq_reload_clear <= 0; end // Время низкого уровня A12 должно быть не менее 3 циклов CPU always @ (posedge m2, posedge ppu_addr_in) begin if (ppu_addr_in) a12_low_time <= 0; else if (a12_low_time < 3) a12_low_time <= a12_low_time + 1; end

    Ах да, MMC3 поддерживает ещё подключение дополнительной оперативной памяти по адресу $6000-$7FFF ! Надо не забыть и это описать:
    assign cpu_wr_out = cpu_rw_in || ram_protect; assign cpu_rd_out = ~cpu_rw_in; assign cpu_sram_ce = !(cpu_addr_in && cpu_addr_in && m2 && romsel && ram_protect);

    Вот и всё, наш MMC3 готов! Полный код можно посмотреть тут.

    Сегодня расскажу вам как совместить две обычные вещи в один оригинальный подарок. В сети есть такой иностранный персонаж как AVGN, который занимается обзорами старых игровых приставок и игр к ним. Так вот есть у него довольно забавный агрегат, под названием Nintoaster. Представляет он собой приставку NES в корпусе тостера. Почесав репу, я подумал, а чем я хуже него и решил собрать подобный девайс себе. Но так как про NES у нас в стране в основном никто и слыхом не слыхивал, а все знали только тайваньско-китайский клон японского Famicom под названием Dendy. Поэтому аппарат решено было назвать Dentoaster.

    Все началось с поисков подходящего тостера на корпус. Через месяц ожидания на барахолке нашелся старый российский тостер и выглядел он вот так (фото с объявления).

    На дне тостера красовались характеристики агрегата, что сделан он в 1995 году и мощностью на 800 Ватт. Несмотря на свой почтенный возраст, тостер исправно выполнял свою основную функцию.

    А вот годы использования сказались на нем довольно плачевно. Аппарат был весь в нагаре и каплях масла. Благо средство для чистки газовых плит с нагаром и жиром справилась на ура. Фото всего этого безобразия увы не сделал, ибо сразу побежал его от этого дела отмывать. Зато есть фото поддона с крошками, которые все 18 использования тостера копились и присыхали там навсегда. То еще зрелище, но все так же решилось простым отмыванием.

    Ладно, корпус пока оставим и займемся изъятием лишних потрохов. Убираем нагревательные элементы вместе со слюдяной термоизоляцией и направляющие грили для хлеба.

    Теперь удаляем часть механизма опускания хлеба – две волнообразные металлические полоски. Благо все было на точечной сварке и отделилось довольно легко.

    Временно отвинчиваем контактную группу с таймером.

    И в сухом остатке получаем вот такую конструкцию.

    Очищаем контактную группу от пыли и отпаиваем остатки родных проводов.

    И привинчиваем ее к конструкции обратно.

    Левая и правая стенки потрохов были скреплены продольными планками, но конструкция ходила ходуном. Поэтому было решено спаять планки боковушками. Сначала снаружи.

    А потом и изнутри.

    Порывшись в закромах нашел вот такие миниатюрные петли, которые как нельзя кстати подошли ко всему этому делу.

    Временно прихватил петли на пайку и суперклей.

    А в качестве механизма возврата приделал пружинку.

    Вот так это выглядело после первых прикидок.

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

    После перекраски стало выглядеть намного симпатичнее.

    Соответственно и заглушки было решено покрасить в тот же цвет.

    Панель под разъемы джойстиков и кнопки reset сделал из такой же заглушки.

    Отпиливаем от нее лишнее и размечаем будущие отверстия.

    И любым доступным способом проделываем их. Я пользовался дремелем и надфилями.

    А затем красим.

    В качестве декоративной части кнопки reset было решено пожертвовать единственным джоем, который у меня с 95 года остался. Все равно он уже свое отжил и половина кнопок не работала. Да и провода перепаивать лень было. Про саму же кнопку писать особо нечего, так как это просто китайская кнопка на замыкание, без фиксации положения. Такие например в китайские лазерные указки ставят.

    Короче приклеиваем на супреклей декоративную часть к кнопке.

    Из обрезков пластика и текстолита про помощи того же суперклея делаем импровизированное крепление для кнопки.

    И закрепляем всю конструкцию на положенном ей месте.

    В итоге получаем вот такой результат.

    Теперь разбираем нашу приставку.

    Потроха у новодела не чета старым консолям, но работаем с тем, что имеем. Хорошо видны контакты кнопок power и reset, к которым надо будет подпаяться.

    Временно отпаиваем шлейф от платы со стабилизатором напряжения и разъемами подключения кабелей.

    Закрепляем на термоклей плату с разъемами джойстиков и кнопками включения и reset. Родные кнопки решил не выпаивать, ибо они не мешались внутри корпуса.

    А плату с разъемом под картридж крепим сначала на винты (благо в родных боковушках тостера отверстия уже были), а потом закрепляем на “Полиморфус”.

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

    Для меня Dendy всегда была чем-то большим, чем просто приставкой. Я не только играл в неё, но и значительное время провёл внутри неё с паяльником в руках для некоторых простых модификаций. По дороге куда-нибудь я часто размышлял о том, как же создаются эти игры и как это работает внутри. Наверняка, многие из вас когда-то задавались подобными вопросами, такова уж натура будущих IT-шников.

    Прошли годы. С некоторой периодичностью погружался в эму-тему, изучая всё новое на тематических сайтах, но я не решался окунуться в изучение ассемблера 6502 и архитектуры NES. Внутренний конфликт рационального и иррационального. Я долго убеждал себя, что мне не нужно тратить на это время, но… сорвался. Глядя на то, какие интересные вещи делают энтузиасты эму-сцены, я взялся за свою давнюю идею со светлой мыслью: «Я тоже смогу!». Две недели пролетели незаметно, я еле смог остановить себя. И да, теперь я знаком с ассемблером без команд умножения, о чём раньше только слышал в песне о программистской молодости .

    Очень вероятно, что сейчас вы вспомнили свой первый картридж для Dendy и меню с романтическим сюжетом и приятной музыкой. На таких картриджах никогда не было «серьёзных» игр, и не глядя на громкие надписи типа 9999-in-1, их обычно было что-то около пяти. Но это меню… Разве это не шедевр китайской мысли? :) Мне с детства нравилась эта мелодия (Unchained Melody), а фоновые изображения сейчас навевают кучу ностальгических воспоминаний. Поэтому я взял IDA и дизассемблировал меню 300-in-1 , вырезал всё лишнее, исправил ошибки, добавил фейдинг да немного приятных мелочей - и получилась демка Unchained Nostalgia (для запуска нужен эмулятор, например, Nestopia), есть запись на YouTube .

    Хотите также окунуться в олдскульное программирование? Делюсь самым полезным и интересным, что я нашёл по теме.

    Архитектура, программирование и отладка

    Раньше для процессора 6502 писали только на ассемблере, выбор инструментов был маленьким, документации было немного и поведение железа было плохо изучено. Сегодня же таких проблем нет. В последние годы были даже разработаны библиотеки для C и полноценные игры на них, которые при этом быстро работают на скромном железе NES.

    Современные разработки для NES

    Если кто-то считает, что NES - мёртвая платформа, тот ошибается:) Достаточно регулярно выпускаются новые игры и демки. Понятно, что это не массовый рынок, и здесь крутятся, в основном, энтузиасты, но тем не менее… Различных релизов выходит достаточно много, я поделюсь самым интересным и забавным из того, что нашёл сам.
    • CMC 80"s (2000 год) - старая демка, с длинным ностальгическим текстом и даже скрытым посланием
    • High Hopes (2007 год) - наверное, лучшая демка для NES, стоит посмотреть
    • D-Pad Hero (две части, 2009 и 2010 годы) - Guitar Hero для NES:)
    • Zooming Secretary (2011 год) - симулятор секретарши, выполненный с хорошей долей чувства юмора (чего только стоит отвлекающий шеф!), написано отечественным разработчиком (Shiru) и имеются исходные коды на C
    • Lan Master (2011 год) - головоломка на сисадминскую тематику с атмосферным звуком, от Shiru, исходные коды
    • Lawn Mower (2011 год) - симулятор газонокосильщика, также от Shiru (талантливый разработчик, да), исходные коды
    • Alter Ego (2011 год) - логический платформер, порт с ZX Spectrum от Shiru, исходные коды на C
    • Chase (2012 год) - это пример простой игры на C к соответствующей статье от Shiru, но вышло так хорошо, что даже жалко, что в игре всего пять уровней (обратите внимание на классные огромные надписи, нехарактерные для NES)
    • Driar (2012 год) - просто приятно выполненная игрушка
    • Retro City Rampage (2012 год) - коммерческая игра для современных консолей, но внутри есть маленький секрет в виде игры для NES, посмотрите это видео о создании NES версии, посмотреть обзор и скачать ROM можно
    • Sir Ababol (2013 год) - платформер-лабиринт от испанской группы разработчиков игр Mojon Twins, написано на C