Приставка денди своими руками. Как воплотить в жизнь мечту детства и запрограммировать что-нибудь для Dendy

  • Игры и игровые приставки ,
  • Электроника для начинающих
    • 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. Родные кнопки решил не выпаивать, ибо они не мешались внутри корпуса.

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

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

    А вот так изнутри.

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

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

    Вот так все выглядит снаружи. Относительно терпимо. Могло бы быть и хуже.

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

    Как будто там и было.

    К плате с RCA-разъемами припаиваем шлейф, отпаянный ранее и провода питания.

    А к контактной группе тостера припаиваем провода включения, остальные их концы соответственно припаиваем к контактам кнопки ВКЛ, указанной ранее.

    Уже окончательно крепим на скрепки, пайку и полиморфус заглушку для картриджей.

    А для верности еще наляпываем на крепление самого разъема. Точнее просто лишние остатки пластика оставались, вот и приляпал для верности.

    Ну и для пущей страховки от перелома платы при вставке картриджа из остатков комповой заглушки и полиморфуса сделал основание.

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

    Ну и наконец, после сборки получаем вот такой агрегат. Вроде бы все и готово, но чего-то не хватало. А именно через краску цветочки старые проглядывали.

    Поэтому в фотошопе наваял и распечатал на самоклейке вот такие логотипы.

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

    Ну и видео с демонстрацией работы девайса.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Сначала я и не думал писать статью на эту тему, но похоже, что это уже часть целого цикла статей на Денди-тематику. И да, на этот раз речь в первую очередь именно про отечественную Денди, а не про оригинальные консоли - Famicom или NES. Просто я делал устройство в подарок одному человеку, который снимает очень интересные видеоролики про Денди, и ориентировался на совместимость именно с этим клоном.

    Дело в том, что и для Famicom, и для NES выходили самые разные аксессуары: 3D очки, клавиатуры, роботы, считыватели штрих-кодов, всякие игровые контроллеры и очень многое другое . До нас же дошёл только световой пистолет. Передо мной стояла задача собрать устройство, которое совмещало бы в себе разветвитель на четыре игрока (да, были такие игры) и Arkanoid-контроллер.

    Порты ввода-вывода

    Прежде всего стоит рассказать, как же работают с джойстиками игровыми контроллерами Famicom, NES и Dendy, и чем же они отличаются в этом плане.

    С точки зрения игр порты ввода-вывода представляют из себя два регистра с адресами $4016 и $4017 , которые ассоциированы соответственно с двумя портами, куда всё и подключается. Но на стандартных контроллерах для чтения данных используется только один провод - D0 , данные с которого соответственно доступны через младший (нулевой) бит в каждом из регистров: $4016.0 и $4017.0 . Аналогично используется один провод на запись, его обычно называют STROBE (или LATCH ), который сбрасывает счётчик внутри геймпада, и который доступен через запись в $4016.0 (да, для обоих контроллеров он общий).

    Проще говоря, чтобы получить состояние кнопок на первом контроллере надо сначала записать 1 в $4016.0 , сразу же записать туда же 0, сбросив таким образом счётчик, а потом прочитать $4016 и $4017 восемь раз (для каждой из кнопок), получая данные о кнопках из младшего бита. Но для чего же остальные биты в этих регистрах, куда идут эти линии? Рассмотрим порт контроллера у NES:

    Да, на него на самом деле идут D3 и D4 ! Именно они и доступны через $4016.3 , $4016.4 у первого порта и $4017.3 , $4017.4 у второго, и именно они используются для нестандартных контроллеров.

    Что же касается его японского собрата - Famicom, там нет этих портов, да и сами игровые контроллеры не отсоединяются от консоли, но у него есть порт расширения, который представляет из себя разъём DB-15.

    Знакомо выглядит, правда? Да, когда китайцы проектировали нашу Денди (я сомневаюсь, что её проектировали у нас), и им нужно было сделать отсоединяющиеся контроллеры, они решили взять за основу именно порт расширения, ведь в нём есть контакты для второго контроллера, и он на Famicom расположен чуть правее центра. Им тут даже распиновку менять не пришлось. Что же касается первого контроллера, они взяли тот же DB-15, расположили его слева и поменять распиновку так, чтобы можно было подключать первый контроллер. И только его.

    Сравните сами передние порты у Famicom и у нашей Денди:

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

    Но давайте посмотрим, что же выведено на этот порт расширения у Famicom?


    (скриншот с сайта wiki.nesdev.com)

    Да, на него идут ещё $4016.1 (на ввод), $4017.0-4 (на ввод), $4017.0-2 (на вывод), внешнее прерывание и даже звук! Я был очень приятно удивлён, когда разобрал Денди и увидел, что всё это есть и там:

    Правда, не во всех моделях, как выяснилось позже. Но если это есть, значит есть и полная совместимость с аксессуарами для Famicom, и их могут использовать соответствующие японские игры. Но напомню, что Денди - это очень странная смесь NES и Famicom, PAL и NTSC. Пираты выпускали для неё и японские, и американские игры, которые по сути на 100% совместимы, если не брать в рассчёт эти аксессуары и разный формат картриджей.

    Итого: в некоторых Денди есть все те же выводы, что и на Фамикоме, которые при этом включают в себя часть выводов доступных на NES. Отсутствует доступ к $4016.3 и $4016.4 , но они используются крайне редко. В виде таблички для наглядности:

    Принцип работы аксессуаров

    Американский разветвитель на четыре игрока для NES называется Four Score представляет из себя простой набор сдвиговых регистров. Т.е. первые восемь чтений из $4016.0 дают данные из первого контроллера, а вторые восемь - из третьего. Аналогично $4017.0 даёт данные о втором и четвёртом контроллерах. Помимо этого при продолжении чтения устройство выдаёт свою сигнатуру, с помощью которой игра определяет, что подключен именно Four Score , а не что-то ещё. Получается, что такое устройство можно собрать из шести сдвиговых регистров (4021 или 74165), и оно будет работать на любой Денди, ведь для него не требуются дополнительные линии данных. Само собой, только с американскими играми, которые выходили для NES.

    Японский аналог для Famicom устроен гораздо проще. Третий и четвёртый контроллеры подключаются напрямую в порт расширения и доступны через $4016.1 и $4017.1 . Соответственно для такого переходника нам уже нужен полноценный порт расширения у Денди, иначе поиграть вчетвером в японские игры не получится.

    Arkanoid-контроллер, как ясно из названия, используется для игры Arkanoid и представляет из себя ручку-крутилку и одну кнопку. Внутри же это аналого-цифровой преобразователь и сдвиговый регистр, который так же последовательно выдаёт положение ручки. Разница между японской и американской версией только в способе подключения. Японская версия игры читает положение ручки и состояние кнопки из $4016.1 и $4017.1 , а американская версия из $4016.3 и $4016.4 соответственно. Получается, что для японского Арканоида нужен полноценный порт расширения, а для американского подойдёт любая денди, где работает световой пистолет (он использует те же выводы).

    Создание своего аксессуара

    Хотя сами по себе вышеперечисленные устройства имеют простую схему и собираются из простейших логических компонентов, для сердца устройства типа «всё в одном» я решил использовать ПЛИС. Тем более мне было высказано пожелание сделать там ещё и простейший переключатель-свитч, а мне хотелось сделать возможность менять местами кнопки A и B. Сначала я выбрал Altera EPM3064ATC100 , но вскоре выяснилось, что 64 макроячейки мне не хватит, и выбор пал на EPM3128ATC100 , где уже 128 макроячеек.

    Если уж на то пошло, я решил совсем не мелочиться и поставить в устройство ещё и какой-то экран, на котором показывались бы текущий режим и меню с настройками, к тому же у меня давно валялся без дела один знакосинтезирующий «16x2» дисплей. Вот для работы с ним уже нужен микроконтроллер, и я выбрал ATMEGA16 .

    Мне всегда было сложнее всего придать устройству приятный внешний вид. Всё-таки я программист, а не дизайнер, но именно при изготовлении устройства в подарок хотелось сделать его максимально красивым и удобным. Тем более это чуть ли не единственный способ как-то показать другим своё произведение искусства: фотографии и видео - это не то, по готовым схемам и 3D моделькам такие вещи воссоздают единицы, серийное производство наладить тяжело, а вот подарок - самое то.

    Итак, требования к внешнему виду были такие: четыре порта для стандартных DB-15 контроллеров от Денди, четыре кнопки для их выбора и настройки, кнопка «режим», кнопка «настройки», удобная ручка для Arkanoid и кнопка для него же, которые должны располагаться достаточно удобно и не мешаться. Помимо этого хотелось сделать, чтобы активные порты подсвечивались светодиодами и как-то интуитивно связывались с соответствующими кнопками, логичнее всего при этом расположить разъёмы в ряд, но эти дурацкие DB-15 слишком огромные для этого. Помимо всего устройство должно удобно лежать в руках, ведь оно само по себе игровой контроллер для Arkanoid. В итоге я пришёл примерно к такому виду:

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

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

    Плата с ПЛИС (первая версия):

    Вторая плата:

    Код для ПЛИС я писал на Verilog. Для каждого режима он получается достаточно простым. В первую очередь для многих из них нам надо считать обращения к каждому из портов, т.е. импульсы на проводе clock:

    Reg counter1; reg counter2; always @ (posedge strobe_in, posedge clock1_in) begin if (strobe_in) counter1 <= 1; else if (counter1 < 31) counter1 <= counter1 + 1; end always @ (posedge strobe_in, posedge clock2_in) begin if (strobe_in) counter2 <= 1; else if (counter2 < 31) counter2 <= counter2 + 1; end
    (простите, хабр не умеет подсвечивать Verilog)

    Где strobe_in - это strobe (один на оба порта), а clock1_in и clock2_in - это соответственно clock на каждом из портов. Внутри консоли стоит логика: clock = R/W nand (адрес == $4016/$4017), т.е. на clock логический ноль, когда консоль читает данные по соответствующему адресу.

    Режим имитации американского разветвителя на четверых игроков выглядит так:
    always @ (*) begin // Strobe соединяем напрямую - входы с выходами assign strobe_out = strobe_in; assign strobe_out = strobe_in; assign strobe_out = strobe_in; assign strobe_out = strobe_in; // Дёргаем clock у каждого геймпада в зависимости от того, в который раз читает данные консоль clock_out <= (counter1 <= 8) ? clock1_in: 1; clock_out <= (counter2 <= 8) ? clock2_in: 1; clock_out <= (counter1 > 8 && counter1 <= 16) ? clock1_in: 1; clock_out <= (counter2 > 8 && counter2 <= 16) ? clock2_in: 1; if (counter1 <= 8) // Первый контроллер joy1_data_out <= joy_data; else if (counter1 <= 16) // Третий контроллер joy1_data_out <= joy_data; // Сигнатура else if (counter1 == 20) joy1_data_out <= 0; else joy1_data_out <= 1; // Второй контроллер if (counter2 <= 8) joy2_data_out <= joy_data; // Четвёртый контроллер else if (counter2 <= 16) joy2_data_out <= joy_data; // Сигнатура else if (counter2 == 19) joy2_data_out <= 0; else joy2_data_out <= 1; // Неиспользуемые выводы оставляем в высокоимпедансном состоянии, они подтягиваются к VCC внутри самой консоли joy1_data_out <= 1"bZ; joy2_data_out <= 4"bZZZZ; end

    В режиме японского же разветвителя на четверых нужно просто соединить входы с выходами напрямую:
    always @ (*) begin clock_out <= clock1_in; clock_out <= clock2_in; clock_out <= clock1_in; clock_out <= clock2_in; joy1_data_out <= joy_data; joy2_data_out <= joy_data; joy1_data_out <= joy_data; joy2_data_out <= joy_data; // Неиспользуемые выводы оставляем в высокоимпедансном состоянии, они подтягиваются к VCC внутри самой консоли joy2_data_out <= 3"bZZZ; end

    Самым сложным оказалось сделать возможность менять местами кнопки A и B, ведь считываются они последовательно, т.е. нужно заранее знать значение B, когда консоль запрашивает A, но оно выдаётся как раз только после A. Сначала я думал как-то ускоренно считывать данные с контроллера, используя какой-то внешний тактовый генератор, но в итоге решил просто брать значение от предыдущего считывания. Это даёт задержку, но она абсолютно незаметна. Тем более игры обычно читают состояние кнопок по несколько раз подряд.

    Само собой, все эти режимы и настройки надо как-то задавать. Для этого я определил 12-битный регистр control , данные в который записываются через последовательное соединение, с дополнительным битом для проверки чётности:
    reg control; reg control_parity; reg control_receiver; reg control_counter; always @ (posedge control_strobe, posedge control_clock) begin if (control_strobe) begin control_counter = 0; control_parity = 0; end else begin if (control_counter <= 11) begin control_receiver = control_data; control_parity = control_parity ^ control_data; end; if (control_counter < 12) control_counter = control_counter + 1; end end always @ (posedge strobe_in) begin if (control_counter == 12 && !control_parity) control = control_receiver; end

    Соответственно со стороны микроконтроллера код (весьма грязный) выглядит вот так:
    void control_send(uint16_t data) { set_bit(CTRL_PORT, CTRL_STROBE_PIN); // Strobe _delay_us(10); unset_bit(CTRL_PORT, CTRL_STROBE_PIN); // Strobe _delay_us(10); int b; char parity = 0; for (b = 0; b < 11; b++) { if (data & (1<

    В остальном в коде микроконтроллера нет ничего особенного: работа с дисплеем на контроллере HD44780, кнопки, светодиоды, простенькая менюшка и работа с аналого-цифровой преобразователем для определения угла поворота ручки.

    Я всё отладил, убедился в работоспособности и уже начал упихивать компоненты в корпус…

    Но перед закрытием крышки решил проверить на оригинальном Famicom, ведь с ним устройство тоже будет использоваться. Увы, режимы, где нужно было считать импульсы clock, работали неправильно. С помощью логического анализатора выяснилось, что с линии данных идут наводки на линию clock:

    Это помеха длительностью всего в несколько десятков наносекунд всё портит. Я решил посмотреть своим простеньким осциллографом, что же происходит на линии clock у Денди:

    А вот что там же у Фамикома:

    Видно, что эта линия подтянута к VCC, при чём очень сильно у Денди и весьма слабо у оригинального Фамикома. Я начал экспериментировать с обвеской. Вскоре стало ясно, что на результат лучше смотреть не логическим анализатором, а самой консолью. Пришлось вспоминать ассемблер для 6502 процессора, писать простенькую программу для тестирования и записать её на картридж:

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

    Сначала я и не думал писать статью на эту тему, но похоже, что это уже часть целого цикла статей на Денди-тематику. И да, на этот раз речь в первую очередь именно про отечественную Денди, а не про оригинальные консоли - Famicom или NES. Просто я делал устройство в подарок одному человеку, который снимает очень интересные видеоролики про Денди, и ориентировался на совместимость именно с этим клоном.

    Дело в том, что и для Famicom, и для NES выходили самые разные аксессуары: 3D очки, клавиатуры, роботы, считыватели штрих-кодов, всякие игровые контроллеры и очень многое другое . До нас же дошёл только световой пистолет. Передо мной стояла задача собрать устройство, которое совмещало бы в себе разветвитель на четыре игрока (да, были такие игры) и Arkanoid-контроллер.

    Порты ввода-вывода

    Прежде всего стоит рассказать, как же работают с джойстиками игровыми контроллерами Famicom, NES и Dendy, и чем же они отличаются в этом плане.

    С точки зрения игр порты ввода-вывода представляют из себя два регистра с адресами $4016 и $4017 , которые ассоциированы соответственно с двумя портами, куда всё и подключается. Но на стандартных контроллерах для чтения данных используется только один провод - D0 , данные с которого соответственно доступны через младший (нулевой) бит в каждом из регистров: $4016.0 и $4017.0 . Аналогично используется один провод на запись, его обычно называют STROBE (или LATCH ), который сбрасывает счётчик внутри геймпада, и который доступен через запись в $4016.0 (да, для обоих контроллеров он общий).

    Проще говоря, чтобы получить состояние кнопок на первом контроллере надо сначала записать 1 в $4016.0 , сразу же записать туда же 0, сбросив таким образом счётчик, а потом прочитать $4016 и $4017 восемь раз (для каждой из кнопок), получая данные о кнопках из младшего бита. Но для чего же остальные биты в этих регистрах, куда идут эти линии? Рассмотрим порт контроллера у NES:

    Да, на него на самом деле идут D3 и D4 ! Именно они и доступны через $4016.3 , $4016.4 у первого порта и $4017.3 , $4017.4 у второго, и именно они используются для нестандартных контроллеров.

    Что же касается его японского собрата - Famicom, там нет этих портов, да и сами игровые контроллеры не отсоединяются от консоли, но у него есть порт расширения, который представляет из себя разъём DB-15.

    Знакомо выглядит, правда? Да, когда китайцы проектировали нашу Денди (я сомневаюсь, что её проектировали у нас), и им нужно было сделать отсоединяющиеся контроллеры, они решили взять за основу именно порт расширения, ведь в нём есть контакты для второго контроллера, и он на Famicom расположен чуть правее центра. Им тут даже распиновку менять не пришлось. Что же касается первого контроллера, они взяли тот же DB-15, расположили его слева и поменять распиновку так, чтобы можно было подключать первый контроллер. И только его.

    Сравните сами передние порты у Famicom и у нашей Денди:

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

    Но давайте посмотрим, что же выведено на этот порт расширения у Famicom?


    (скриншот с сайта wiki.nesdev.com)

    Да, на него идут ещё $4016.1 (на ввод), $4017.0-4 (на ввод), $4017.0-2 (на вывод), внешнее прерывание и даже звук! Я был очень приятно удивлён, когда разобрал Денди и увидел, что всё это есть и там:

    Правда, не во всех моделях, как выяснилось позже. Но если это есть, значит есть и полная совместимость с аксессуарами для Famicom, и их могут использовать соответствующие японские игры. Но напомню, что Денди - это очень странная смесь NES и Famicom, PAL и NTSC. Пираты выпускали для неё и японские, и американские игры, которые по сути на 100% совместимы, если не брать в рассчёт эти аксессуары и разный формат картриджей.

    Итого: в некоторых Денди есть все те же выводы, что и на Фамикоме, которые при этом включают в себя часть выводов доступных на NES. Отсутствует доступ к $4016.3 и $4016.4 , но они используются крайне редко. В виде таблички для наглядности:

    Принцип работы аксессуаров

    Американский разветвитель на четыре игрока для NES называется Four Score представляет из себя простой набор сдвиговых регистров. Т.е. первые восемь чтений из $4016.0 дают данные из первого контроллера, а вторые восемь - из третьего. Аналогично $4017.0 даёт данные о втором и четвёртом контроллерах. Помимо этого при продолжении чтения устройство выдаёт свою сигнатуру, с помощью которой игра определяет, что подключен именно Four Score , а не что-то ещё. Получается, что такое устройство можно собрать из шести сдвиговых регистров (4021 или 74165), и оно будет работать на любой Денди, ведь для него не требуются дополнительные линии данных. Само собой, только с американскими играми, которые выходили для NES.

    Японский аналог для Famicom устроен гораздо проще. Третий и четвёртый контроллеры подключаются напрямую в порт расширения и доступны через $4016.1 и $4017.1 . Соответственно для такого переходника нам уже нужен полноценный порт расширения у Денди, иначе поиграть вчетвером в японские игры не получится.

    Arkanoid-контроллер, как ясно из названия, используется для игры Arkanoid и представляет из себя ручку-крутилку и одну кнопку. Внутри же это аналого-цифровой преобразователь и сдвиговый регистр, который так же последовательно выдаёт положение ручки. Разница между японской и американской версией только в способе подключения. Японская версия игры читает положение ручки и состояние кнопки из $4016.1 и $4017.1 , а американская версия из $4016.3 и $4016.4 соответственно. Получается, что для японского Арканоида нужен полноценный порт расширения, а для американского подойдёт любая денди, где работает световой пистолет (он использует те же выводы).

    Создание своего аксессуара

    Хотя сами по себе вышеперечисленные устройства имеют простую схему и собираются из простейших логических компонентов, для сердца устройства типа «всё в одном» я решил использовать ПЛИС. Тем более мне было высказано пожелание сделать там ещё и простейший переключатель-свитч, а мне хотелось сделать возможность менять местами кнопки A и B. Сначала я выбрал Altera EPM3064ATC100 , но вскоре выяснилось, что 64 макроячейки мне не хватит, и выбор пал на EPM3128ATC100 , где уже 128 макроячеек.

    Если уж на то пошло, я решил совсем не мелочиться и поставить в устройство ещё и какой-то экран, на котором показывались бы текущий режим и меню с настройками, к тому же у меня давно валялся без дела один знакосинтезирующий «16x2» дисплей. Вот для работы с ним уже нужен микроконтроллер, и я выбрал ATMEGA16 .

    Мне всегда было сложнее всего придать устройству приятный внешний вид. Всё-таки я программист, а не дизайнер, но именно при изготовлении устройства в подарок хотелось сделать его максимально красивым и удобным. Тем более это чуть ли не единственный способ как-то показать другим своё произведение искусства: фотографии и видео - это не то, по готовым схемам и 3D моделькам такие вещи воссоздают единицы, серийное производство наладить тяжело, а вот подарок - самое то.

    Итак, требования к внешнему виду были такие: четыре порта для стандартных DB-15 контроллеров от Денди, четыре кнопки для их выбора и настройки, кнопка «режим», кнопка «настройки», удобная ручка для Arkanoid и кнопка для него же, которые должны располагаться достаточно удобно и не мешаться. Помимо этого хотелось сделать, чтобы активные порты подсвечивались светодиодами и как-то интуитивно связывались с соответствующими кнопками, логичнее всего при этом расположить разъёмы в ряд, но эти дурацкие DB-15 слишком огромные для этого. Помимо всего устройство должно удобно лежать в руках, ведь оно само по себе игровой контроллер для Arkanoid. В итоге я пришёл примерно к такому виду:

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

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

    Плата с ПЛИС (первая версия):

    Вторая плата:

    Код для ПЛИС я писал на Verilog. Для каждого режима он получается достаточно простым. В первую очередь для многих из них нам надо считать обращения к каждому из портов, т.е. импульсы на проводе clock:

    Reg counter1; reg counter2; always @ (posedge strobe_in, posedge clock1_in) begin if (strobe_in) counter1 <= 1; else if (counter1 < 31) counter1 <= counter1 + 1; end always @ (posedge strobe_in, posedge clock2_in) begin if (strobe_in) counter2 <= 1; else if (counter2 < 31) counter2 <= counter2 + 1; end
    (простите, хабр не умеет подсвечивать Verilog)

    Где strobe_in - это strobe (один на оба порта), а clock1_in и clock2_in - это соответственно clock на каждом из портов. Внутри консоли стоит логика: clock = R/W nand (адрес == $4016/$4017), т.е. на clock логический ноль, когда консоль читает данные по соответствующему адресу.

    Режим имитации американского разветвителя на четверых игроков выглядит так:
    always @ (*) begin // Strobe соединяем напрямую - входы с выходами assign strobe_out = strobe_in; assign strobe_out = strobe_in; assign strobe_out = strobe_in; assign strobe_out = strobe_in; // Дёргаем clock у каждого геймпада в зависимости от того, в который раз читает данные консоль clock_out <= (counter1 <= 8) ? clock1_in: 1; clock_out <= (counter2 <= 8) ? clock2_in: 1; clock_out <= (counter1 > 8 && counter1 <= 16) ? clock1_in: 1; clock_out <= (counter2 > 8 && counter2 <= 16) ? clock2_in: 1; if (counter1 <= 8) // Первый контроллер joy1_data_out <= joy_data; else if (counter1 <= 16) // Третий контроллер joy1_data_out <= joy_data; // Сигнатура else if (counter1 == 20) joy1_data_out <= 0; else joy1_data_out <= 1; // Второй контроллер if (counter2 <= 8) joy2_data_out <= joy_data; // Четвёртый контроллер else if (counter2 <= 16) joy2_data_out <= joy_data; // Сигнатура else if (counter2 == 19) joy2_data_out <= 0; else joy2_data_out <= 1; // Неиспользуемые выводы оставляем в высокоимпедансном состоянии, они подтягиваются к VCC внутри самой консоли joy1_data_out <= 1"bZ; joy2_data_out <= 4"bZZZZ; end

    В режиме японского же разветвителя на четверых нужно просто соединить входы с выходами напрямую:
    always @ (*) begin clock_out <= clock1_in; clock_out <= clock2_in; clock_out <= clock1_in; clock_out <= clock2_in; joy1_data_out <= joy_data; joy2_data_out <= joy_data; joy1_data_out <= joy_data; joy2_data_out <= joy_data; // Неиспользуемые выводы оставляем в высокоимпедансном состоянии, они подтягиваются к VCC внутри самой консоли joy2_data_out <= 3"bZZZ; end

    Самым сложным оказалось сделать возможность менять местами кнопки A и B, ведь считываются они последовательно, т.е. нужно заранее знать значение B, когда консоль запрашивает A, но оно выдаётся как раз только после A. Сначала я думал как-то ускоренно считывать данные с контроллера, используя какой-то внешний тактовый генератор, но в итоге решил просто брать значение от предыдущего считывания. Это даёт задержку, но она абсолютно незаметна. Тем более игры обычно читают состояние кнопок по несколько раз подряд.

    Само собой, все эти режимы и настройки надо как-то задавать. Для этого я определил 12-битный регистр control , данные в который записываются через последовательное соединение, с дополнительным битом для проверки чётности:
    reg control; reg control_parity; reg control_receiver; reg control_counter; always @ (posedge control_strobe, posedge control_clock) begin if (control_strobe) begin control_counter = 0; control_parity = 0; end else begin if (control_counter <= 11) begin control_receiver = control_data; control_parity = control_parity ^ control_data; end; if (control_counter < 12) control_counter = control_counter + 1; end end always @ (posedge strobe_in) begin if (control_counter == 12 && !control_parity) control = control_receiver; end

    Соответственно со стороны микроконтроллера код (весьма грязный) выглядит вот так:
    void control_send(uint16_t data) { set_bit(CTRL_PORT, CTRL_STROBE_PIN); // Strobe _delay_us(10); unset_bit(CTRL_PORT, CTRL_STROBE_PIN); // Strobe _delay_us(10); int b; char parity = 0; for (b = 0; b < 11; b++) { if (data & (1<

    В остальном в коде микроконтроллера нет ничего особенного: работа с дисплеем на контроллере HD44780, кнопки, светодиоды, простенькая менюшка и работа с аналого-цифровой преобразователем для определения угла поворота ручки.

    Я всё отладил, убедился в работоспособности и уже начал упихивать компоненты в корпус…

    Но перед закрытием крышки решил проверить на оригинальном Famicom, ведь с ним устройство тоже будет использоваться. Увы, режимы, где нужно было считать импульсы clock, работали неправильно. С помощью логического анализатора выяснилось, что с линии данных идут наводки на линию clock:

    Это помеха длительностью всего в несколько десятков наносекунд всё портит. Я решил посмотреть своим простеньким осциллографом, что же происходит на линии clock у Денди:

    А вот что там же у Фамикома:

    Видно, что эта линия подтянута к VCC, при чём очень сильно у Денди и весьма слабо у оригинального Фамикома. Я начал экспериментировать с обвеской. Вскоре стало ясно, что на результат лучше смотреть не логическим анализатором, а самой консолью. Пришлось вспоминать ассемблер для 6502 процессора, писать простенькую программу для тестирования и записать её на картридж:

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