Скрещиваем Денди и Тостер или 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