Геймпад от Sega Mega Drive и Raspberry Pi Часть 1 (подготовительная и трёхкнопочная)

    Осень наступила, отцвела капуста, Уже почти середина зимы, а я только закончил с этим возиться. Но всё равно наступило время когда хочется поиграть во, что-нибудь старенькое, под шум метели за окном, например в Соника или червяка Джима. Внизу статьи видос с предварительными результатами.



    Если Вы играли на эмуляторе в игры от SMD, то наверно заметили, что самым удобным гейм-падом для этих игр является родной геймпад от SMD. Для большинства остальных приставок, при игре на эмуляторе, вполне можно обойтись тем же геймпадом от Xbox или Logitech, стандарт сформировался примерно в конце 90-х. А вот до конца 90-х, каждый изгалялся как мог.

    Приобрести геймпад от SMD не сложно, и как правило купить его можно там где продают сами клоны приставок, по достаточно демократичной цене, примерно в пределах 300 рублей.

    Подключение к Raspberry pi я, как и раньше, организовал при помощи usb шлейфа из списанного корпуса и разъёма DB-9 папа. А выводы GPIO расписал в программе. Геймпад прекрасно работает от 3,3 Вольт.



    Как всегда встал вопрос о выборе эмулятора, и наиболее лучшим вариантом стал эмулятор — Picodrive, он оптимизирован для ARM, хорошо структурирован и насколько я понял, он входит в состав сборки RetroPi. Но со сборкой пришлось немного повозиться. Располагается исходный код на сервисе Github, по этому адресу.

    Для сборки нам понадобятся 3 составляющие успеха из репозитория автора эмулятора:

    1. сам эмулятор Picodrive;
    2. эмулятор центрального процессора — cyclone68000;
    3. и FrontEnd — Libpicofe.

    Теперь это всё надо правильно скомпоновать. Распаковываем или не распаковываем Picodrive, в зависимости от того, как скачивали. Теперь открываем директорию с cyclone68000, её содержимое надо скопировать в директорию:

    /ваша директория/picodrive-master/cpu/cyclone
    

    Так же надо поступить с содержимым директории Libpicofe, его содержимое копируется в директорию:

    /ваша директория/picodrive-master/platform/libpicofe
    

    Теперь необходимо выполнить подготовку к сборке:
    производим конфигурацию

    sudo ./configure
    

    После того, как конфигурация будет закончена, будет создан файл — config.mak, в нём надо будет найти и изменить некоторые строки. Ниже приведён готовый результат:

    AS = arm-linux-as
    LDLIBS += -L/usr/lib/arm-linux-gnueabihf -lSDL -lasound -lpng  -lm -lz -lwiringPi
    ARCH = arm
    PLATFORM = rpi1
    

    Далее необходимо отредактировать файл — config.h. Он находится в директории:

    /ваша директория/picodrive-master/cpu/cyclone
    

    В нём надо проставить единички в переменных:

    #define HAVE_ARMv6                  1
    #define CYCLONE_FOR_GENESIS         1
    

    А теперь программная часть

    Как всегда надо было найти место, где обрабатывается информация о нажатых кнопках, понять и простить код и подменить его.

    Не нагоняя саспенса сразу скажу, что искомые файлы располагаются в директории:

    /ваша директория/picodrive-master/pico/
    

    Здесь нас интересуют 3 файла — pico.c, memory.c, memory.h. Наверно можно обойтись меньшим числом, и всё запихать в один, но мне так показалось проще.
    И так, в файле pico.c я произвожу инициализацию библиотеки и начальную настройку пинов GPIO.

    Сразу приведу часть заголовка файла:

    #include "pico_int.h"
    #include "sound/ym2612.h"
    #include <wiringPi.h>
    #define Data0 3
    #define Data1 4
    #define Data2 5
    #define Data3 12
    #define Data4 13
    #define Data5 10
    #define Select 6
    struct Pico Pico;
    struct PicoMem PicoMem;
    PicoInterface PicoIn;
    

    Как видно, задан заголовочник библиотеки WiringPi, и объявлены дефайны, которые пондобятся чуть ниже. Ну например сейчас в функции void PicoInit(void):

    void PicoInit(void)
    {
    ...
    ...
      PicoDraw2Init();
      wiringPiSetup ();
      pinMode (Select, OUTPUT);
      pinMode (Data0, INPUT);
      pinMode (Data1, INPUT);
      pinMode (Data2, INPUT);
      pinMode (Data3, INPUT);
      pinMode (Data4, INPUT);
      pinMode (Data5, INPUT);
      digitalWrite (Select, HIGH);
    }
    

    Это функция инициализации памяти эмулятора (вроде). И вот именно сюда я вставил все настройки выводов GPIO.Вот тут дана распиновка разъёма DB-9.

    Тут надо сказать, что у геймпада имеется 6 информационных контактов (Data0...Data5), один управляющий (Seleсt), и питание.

    Далее, нам эти же определения — define, нужно повторить ещё раз. Это можно сделать как и в memory.h, так и в memory.c. Я выбрал первый вариант. Нет смысла приводить листинг этого.

    Вот мы и подбираемся к самому интересному — файлу memory.c. В нём имеются 2 функции с красноречивыми названиями:

    static u32 read_pad_3btn(int i, u32 out_bits)
    static u32 read_pad_6btn(int i, u32 out_bits)
    

    Названия как бы ненавязчиво намекают на чтение состояния 3-х кнопочных и 6-и кнопочных геймпадов.

    Тут надо пояснить, что любой 6-и кнопочный геймпад может работать как 3-х кнопочный. И львиная часть игр работает именно с таким режимом геймпада. В этом режиме, один раз в 16 миллисекунд меняется состояние выхода Select. Когда Select = 0, читаются значения кнопок — UP, DOWN, A, Start. Когда Select = 1 читается состояние кнопок — UP, DOWN, LEFT, RIGHT, B, C. Ниже пример работы этого режима.



    Сразу приведу листинг этой функции с изменениями:

    static u32 read_pad_3btn(int i, u32 out_bits)
    {
        u32 pad = ~PicoIn.padInt[i]; // Get inverse of pad MXYZ SACB RLDU
        u32 value = 0;
    
        if (i == 0 && (out_bits & 0x40)) // TH
        {
          digitalWrite (Select, HIGH);
          delayMicroseconds (20);
          value ^= digitalRead(Data0) << 0; //read UP button
          value ^= digitalRead(Data1) << 1; //read DOWN button
          value ^= digitalRead(Data2) << 2; //read LEFT button
          value ^= digitalRead(Data3) << 3; //read RIGHT button
          value ^= digitalRead(Data4) << 4; //read B button
          value ^= digitalRead(Data5) << 5; //read C button
        }
        if (i == 0 && !(out_bits & 0x40))
        {
          digitalWrite (Select, LOW);
          delayMicroseconds (20);
          value ^= digitalRead(Data0) << 0; //read UP button
          value ^= digitalRead(Data1) << 1; //read DOWN button
          value ^= digitalRead(Data4) << 4; //read A button
          value ^= digitalRead(Data5) << 5; //read Start button
        }
    
        if (i == 1 && (out_bits & 0x40))// TH
        {
        value = pad & 0x3f;                      // ?1CB RLDU
        }
        if (i == 1 && !(out_bits & 0x40))
        {
        value = ((pad & 0xc0) >> 2) | (pad & 3); // ?0SA 00DU
        }
    
      return value;
    }
    

    Здесь i — это номер геймпада, а выражение if (out_bits & 0x40) // TH — как раз отвечает за состояние выхода Select. Стоит обратить внимание, что в эмуляторе состояние кнопок отдаётся в таком же виде, как и в приставке. Нажатая кнопка = 0.

    Вот результат работы:


    Продолжение в следующей серии, Пип-Пип-Пип
    • +18
    • 7,8k
    • 2
    Поделиться публикацией

    Комментарии 2

      +1
      А как же кнопки X,Y,Z? Без них в MK очень грустно играть :(
      Ой, сорри, это первая часть статьи. Ждём продолжения :)
        +1
        Продолжение опубликовано сразу за этой статьей)

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое